Files
tcp-ingestion/.planning/phase-1-telemetry/01-project-scaffold.md
T
julian c8a5f4cd68 Add Phase 1 and Phase 2 planning documents
ROADMAP plus granular task files per phase. Phase 1 (12 tasks + 1.13
device authority) covers Codec 8/8E/16 telemetry ingestion; Phase 2
(6 tasks) covers Codec 12/14 outbound commands; Phase 3 enumerates
deferred items.
2026-04-30 15:50:49 +02:00

4.0 KiB

Task 1.1 — Project scaffold

Phase: 1 — Inbound telemetry Status: Not started Depends on: None Wiki refs: docs/wiki/sources/teltonika-ingestion-architecture.md § Project location and layout

Goal

Initialize the Node.js / TypeScript project with the directory layout from the wiki, install the agreed tooling, and produce a "hello world" main.ts that the rest of Phase 1 builds on.

Deliverables

  • package.json declaring:
    • Type: "module" (ESM only).
    • Engines: "node": ">=22".
    • Scripts: build, dev, start, test, test:watch, lint, format, typecheck.
    • Dependencies (production): ioredis, pino, pino-pretty (dev-only via NODE_ENV check), prom-client, zod.
    • Dev dependencies: typescript, @types/node, vitest, @vitest/coverage-v8, eslint, @typescript-eslint/parser, @typescript-eslint/eslint-plugin, prettier, tsx (for dev watch).
  • tsconfig.json with strict: true, target: ES2022, module: NodeNext, moduleResolution: NodeNext, outDir: dist, rootDir: src, declaration: false, noUncheckedIndexedAccess: true.
  • eslint.config.js (flat config) with @typescript-eslint/recommended-type-checked plus a small project-specific allow-list.
  • .prettierrc — 2 spaces, single quotes, no semis at line end OR keep semis (pick one and stay consistent — recommend keep semis to match Node convention).
  • .gitignorenode_modules/, dist/, coverage/, .env, .env.local, *.log.
  • .dockerignore — same as .gitignore plus .git/, .planning/, test/, *.md except README.md.
  • Empty directories with .gitkeep files where Phase 1 will fill them in:
    • src/core/, src/adapters/teltonika/codec/data/, src/adapters/teltonika/codec/command/, src/config/, src/observability/
    • test/fixtures/teltonika/codec8/, test/fixtures/teltonika/codec8e/, test/fixtures/teltonika/codec16/
  • src/main.ts — minimal stub: imports a logger (placeholder until task 1.3), prints "tcp-ingestion starting" and exits with code 0.
  • README.md — short description pointing at .planning/ROADMAP.md for the work plan, and at ../docs/wiki/ for the architectural specification.

Specification

  • Package manager: pnpm. Commit pnpm-lock.yaml. The Dockerfile in task 1.11 will use pnpm fetch for layer-cache friendliness.
  • Module style: ESM throughout. No CJS interop hacks. All files use import/export and .js suffix on relative imports per Node ESM resolution rules.
  • TypeScript path style: Use relative imports for now. No paths aliases — they add a bundler dependency at runtime that we don't want.
  • No bundler. The build is tsc only. Runtime is plain Node consuming dist/. The Dockerfile will copy dist/ and node_modules/.
  • Linting style: Configure ESLint to enforce @typescript-eslint/no-floating-promises and @typescript-eslint/no-misused-promises — both are critical in a TCP server where unhandled promise rejections will silently lose work.

Acceptance criteria

  • pnpm install succeeds with no warnings other than peer deps.
  • pnpm typecheck succeeds on the empty project.
  • pnpm lint succeeds.
  • pnpm build produces dist/main.js.
  • pnpm start runs the compiled output and prints the startup message.
  • pnpm test runs (with no tests) and exits successfully.
  • pnpm dev runs main.ts via tsx and prints the startup message.
  • Repository builds reproducibly: deleting node_modules and dist, then pnpm install --frozen-lockfile && pnpm build produces identical output.

Risks / open questions

  • Pinning Node 22 LTS vs 20 LTS: 22 is current LTS in 2026 and has stable native fetch + better worker thread perf. Stay with 22 unless deployment infra forces 20.
  • ESLint v9 flat config: ensure the version is compatible with @typescript-eslint/* v8+. If issues arise, fall back to legacy .eslintrc.json until upstream catches up.

Done

(Fill in once complete: commit SHA, brief notes.)