Files
processor/package.json
T
julian 95efc23139 Implement Phase 1 tasks 1.1-1.4 (scaffold + core types + config + Postgres)
Scaffold mirrors tcp-ingestion conventions: ESM, strict TS, pnpm, vitest
with unit/integration split, ESLint flat config with no-floating-promises
+ no-misused-promises + import/no-restricted-paths (the new src/core/ →
src/domain/ boundary that protects Phase 1 from Phase 2 churn).

Core types in src/core/types.ts (Position, StreamRecord, DeviceState,
Metrics, AttributeValue) — Position is byte-equivalent to tcp-ingestion's
output. Codec in src/core/codec.ts implements sentinel reversal:
{__bigint:"..."} → bigint, {__buffer_b64:"..."} → Buffer, ISO timestamp
string → Date. CodecError surfaces malformed payload reasons with the
failing field named.

Config in src/config/load.ts (zod schema, all 13 env vars with defaults
and bounded numerics). Logger in src/observability/logger.ts matches
tcp-ingestion exactly: ISO timestamps, string level labels, pino-pretty
in development.

Postgres in src/db/: createPool with sane defaults and application_name,
connectWithRetry mirroring the ioredis retry pattern, a 30-line
migration runner using a schema_migrations table, and 0001_positions.sql
with the hypertable + (device_id, ts) unique index + ts DESC index.
Migration runner unit-tested against a mocked pg.Pool; the real
TimescaleDB round-trip is deferred to task 1.10 per spec.

Verification: typecheck, lint, build all clean; 73 unit tests passing
across 4 files. import/no-restricted-paths verified live by temporarily
adding a forbidden src/domain/ import.
2026-04-30 21:35:59 +02:00

44 lines
1.2 KiB
JSON

{
"name": "processor",
"version": "0.1.0",
"description": "Worker service that consumes Position records from Redis Streams and writes durable state to Postgres/TimescaleDB",
"type": "module",
"engines": {
"node": ">=22"
},
"scripts": {
"build": "tsc --project tsconfig.json",
"dev": "tsx watch src/main.ts",
"start": "node dist/main.js",
"test": "vitest run",
"test:watch": "vitest",
"test:integration": "vitest run --config vitest.integration.config.ts",
"lint": "eslint .",
"format": "prettier --write .",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"ioredis": "^5.3.2",
"pg": "^8.13.0",
"pino": "^9.5.0",
"prom-client": "^15.1.3",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^22.10.0",
"@types/pg": "^8.11.10",
"@typescript-eslint/eslint-plugin": "^8.19.0",
"@typescript-eslint/parser": "^8.19.0",
"@vitest/coverage-v8": "^2.1.8",
"eslint": "^9.17.0",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.31.0",
"pino-pretty": "^13.0.0",
"prettier": "^3.4.2",
"testcontainers": "^11.14.0",
"tsx": "^4.19.2",
"typescript": "^5.7.2",
"vitest": "^2.1.8"
}
}