|
| 1 | +# TypeScript Style & Ways of Working (Agents & Copilot) |
| 2 | + |
| 3 | +> Extracted from root `agents.md` Section 16 (2025-11-07). Terraform-specific guidance now lives in `infrastructure/terraform/agents-terraform.md`. |
| 4 | +
|
| 5 | +This document defines conventions for TypeScript across Lambdas, shared packages and the Next.js frontend. It complements Terraform guidance (security, determinism, least privilege) with code-level consistency, testability and readability. Use these rules for both autonomous agent code generation touching TypeScript and interactive AI pair programming (Copilot). |
| 6 | + |
| 7 | +## 1. Project Structure & Module Boundaries |
| 8 | +- Each Lambda function lives in `lambdas/<function-name>` with `src/`, `__tests__/`, build script (`build.sh` using `esbuild`), output in `dist/`. |
| 9 | +- Backend API Lambdas: thin entry file delegating to API handler factory. |
| 10 | +```ts |
| 11 | +export const handler = createHandler(templatesContainer()); |
| 12 | +``` |
| 13 | +- Domain logic separated from transport (e.g. `api/` vs `domain/`). Keep handler composition pure/injectable. |
| 14 | +- Shared packages expose `index.ts` re-export barrels. |
| 15 | + |
| 16 | +## 2. Naming Conventions (ESLint-Enforced) |
| 17 | +Follow the repository ESLint configuration rather than relying on this document for exhaustive rules. Key points are enforced automatically: |
| 18 | +| Concern | Enforcement Source | |
| 19 | +|---------|--------------------| |
| 20 | +| Filenames (kebab-case for files) | Custom/file naming lint rules & PR review | |
| 21 | +| Exported functions/constants camelCase | ESLint stylistic rules | |
| 22 | +| Types & interfaces PascalCase | `@typescript-eslint/naming-convention` | |
| 23 | +| Handler export name `handler` | Manual review + tests referencing handler | |
| 24 | +| Zod schemas prefixed with `$` | Convention (add if missing); not auto-enforced | |
| 25 | +| Factory functions suffix `create`/`make` | Convention to enhance intent | |
| 26 | + |
| 27 | +Agent guidance: Run `npm run lint` and fix reported issues; do not duplicate rule text here. If a required naming change would create wide churn, limit edits to touched lines and add a `// agent: rationale partial naming normalisation` comment if inconsistent legacy remains. |
| 28 | + |
| 29 | +## 3. Import Ordering (ESLint-Enforced) |
| 30 | +Ordering (built-ins → external → internal aliases → relative) is governed by ESLint (`import/order`). Agents should: |
| 31 | +1. Avoid manual reordering of untouched blocks unless lint fails. |
| 32 | +2. Preserve grouping and let `--fix` handle spacing/blank lines. |
| 33 | +3. If adding a new import, place it in the correct group; run `npm run lint:fix` to auto-format. |
| 34 | + |
| 35 | +Do not include example blocks; the linter output will indicate misplacement. Add rationale comment only if a deliberate cycle prevents ideal ordering. |
| 36 | + |
| 37 | +## 4. Types vs Interfaces |
| 38 | +Use `type` for unions/intersections; `interface` for extensible object shapes. Use `z.infer<typeof $Schema>` for types derived from Zod. |
| 39 | + |
| 40 | +## 5. Runtime Validation & Schema Design |
| 41 | +- Validate all external input with Zod at the boundary; parse early. |
| 42 | +- Use `.extend`, `.intersection`, `.omit` to compose. |
| 43 | +- Include `.meta({ id: '...' })` for identification. |
| 44 | + |
| 45 | +## 6. Environment Variables & Configuration |
| 46 | +- Validate env vars (Zod object). Coerce numeric values. |
| 47 | +- Parse complex JSON only after validation. |
| 48 | +- Throw early if required vars absent. |
| 49 | + |
| 50 | +## 7. Error Handling Patterns |
| 51 | +- API handlers return structured success/failure objects; avoid throwing for expected validation failures. |
| 52 | +- Batch SQS: validate each record; DLQ or skip unprocessable payloads. |
| 53 | + |
| 54 | +## 8. Functional Composition & Dependency Injection |
| 55 | +- Use factory functions returning handlers. |
| 56 | +- Containers construct dependency graph (clients, repositories, loggers) and return pure objects. |
| 57 | +- Avoid global singletons except for AWS SDK efficiency; pass constructed clients. |
| 58 | + |
| 59 | +## 9. CSV / Data Processing |
| 60 | +- Deterministic header set from union of record keys; sorted alphabetically. |
| 61 | +- RFC4180 escaping (double quotes, wrap fields containing comma/quote/newline). |
| 62 | +- Keep transformation functions pure and test them. |
| 63 | + |
| 64 | +## 10. Testing Conventions |
| 65 | +- Jest config per package; test files `__tests__/<name>.test.ts`. |
| 66 | +- Mock AWS SDK v3 clients by overriding `send`. |
| 67 | +- Use shared test helper factories (e.g. `makeSQSRecord`, `createMockLogger`). |
| 68 | +- Assert observable response shapes; avoid internal implementation details. |
| 69 | + |
| 70 | +## 11. Async & Promise Handling |
| 71 | +Use `async/await`. Sequential `for...of` loops for ordered processing; `Promise.all` only for independent operations. |
| 72 | + |
| 73 | +## 12. Logging |
| 74 | +- Use structured `winston` logger; inject via container. |
| 75 | +- Log identifiers (templateId, clientId) not full sensitive payloads. |
| 76 | + |
| 77 | +## 13. Security & PII |
| 78 | +- Never log secrets or personal data; redact or summarise. |
| 79 | +- Centralise CSP construction with nonce in frontend middleware. |
| 80 | + |
| 81 | +## 14. Performance & Memory |
| 82 | +- Lambda memory: 512 MB default (light); 2048 MB for heavy tasks (PDF, complex transform). Justify deviations with comment. |
| 83 | +- Avoid unnecessary JSON (de)serialization cycles. |
| 84 | + |
| 85 | +## 15. Formatting & Lint |
| 86 | +Formatting (quotes, semicolons, trailing commas, spacing) and stylistic concerns are entirely delegated to ESLint/Prettier configuration in the repo. Agent process: |
| 87 | +1. After code changes run: `npm run lint` and `npm run lint:fix` (if permitted) to apply canonical style. |
| 88 | +2. Do not handcraft style changes beyond what the linter requires for the modified lines. |
| 89 | +3. If large unrelated formatting noise appears, revert non-essential edits to minimise diff surface. |
| 90 | + |
| 91 | +Examples are intentionally omitted; rely on linter output for corrections. |
| 92 | + |
| 93 | +## 16. Export Strategy |
| 94 | +- Prefer named exports. Use barrel `index.ts` for re-exports. Avoid CommonJS patterns. |
| 95 | + |
| 96 | +## 17. Utility Patterns |
| 97 | +- Inline single-use pure utilities; extract to `utils/` if reused >2 times. |
| 98 | +- Group regex or security policies in top-level constants. |
| 99 | + |
| 100 | +## 18. Testing Edge Cases (Minimum) |
| 101 | +For each handler include: happy path, missing/invalid auth/env, validation failure, empty batch (no-op). |
| 102 | + |
| 103 | +## 19. Error Object Shape |
| 104 | +Standard failure JSON: `{ statusCode, technicalMessage, details? }`. No internal stack traces to clients. |
| 105 | + |
| 106 | +## 20. Comment Standards |
| 107 | +- Use `//` for brief notes; block comments only for multi-line protocol references. |
| 108 | +- Link external specs (e.g. CloudEvents) where relevant. |
| 109 | + |
| 110 | +## 21. AI / Agent Usage Notes (TypeScript) |
| 111 | +- Provide a mini contract before requesting generation: inputs, outputs, error modes, key edge cases. |
| 112 | +- Reject suggestions lacking types or validation. |
| 113 | +- Ensure new handlers follow dependency injection & logging patterns. |
| 114 | +- Use commit convention `<JIRA-ID> <imperative summary>`. |
| 115 | +- Annotate any deviation with `// agent: rationale <brief>`. |
| 116 | + |
| 117 | +## 22. Quality Gates (When TS code is generated/modified) |
| 118 | +Run locally before handover: |
| 119 | +```bash |
| 120 | +npm run typecheck |
| 121 | +npm run lint |
| 122 | +npm run test:unit |
| 123 | +``` |
| 124 | +(Include accessibility tests if frontend changes: `npm run test:accessibility`.) |
0 commit comments