Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,90 @@
# Tasks — `@suaveplan/i18n-backend-chained` (chained-fallback backend orchestrator)
> Every checkbox MUST reflect work actually done in this change. Do not retroactively tick.
## 1. Spec authoring
- [x] 1.1 Read `openspec/cross-cutting-designs/2026-05-05-i18n-modular-split.md` end-to-end (especially §3, §6, §6.1)
- [x] 1.2 Read `openspec/AGENTS.md` Sections 0, 0a, 6a, 7, 9, 13
- [x] 1.3 Read source: `packages/i18n/i18n/src/core/backend/chained/chained-backend.ts`, `chained-backend.test.ts`, `chained-backend.spec-conformance.test.ts`, `chained-backend.schemas.ts`, `chained-backend.md`, plus `packages/i18n/i18n/src/core/backend/backend.types.ts`
- [x] 1.4 Confirm Wave 1b (`@suaveplan/i18n-shared`) registry includes `I18N.CHAINED_BACKEND_ALL_FAILED` so this package can throw it without a registry-change dependency
- [x] 1.5 Draft `proposal.md` from `openspec/templates/proposal.md`
- [x] 1.6 Fill `## Source mapping` table with verified line ranges from monolith
- [x] 1.7 Draft `specs/i18n-backend-chained/spec.md` from `openspec/templates/spec-delta.md`
- [x] 1.8 Every requirement carries a `**Verified by:**` line naming concrete `it(...)` titles
- [x] 1.9 `openspec change validate i18n-backend-chained` exits with no errors
## 2. Package scaffold
- [x] 2.1 `mkdir -p packages/i18n/i18n-backend-chained/src`
- [x] 2.2 Author `package.json` — name `@suaveplan/i18n-backend-chained`, version `0.1.0`, type `module`, sideEffects `false`. Deps: `@suaveplan/i18n-shared workspace:^`, `@suaveplan/types workspace:^`, `@suaveplan/error workspace:^`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`, `zod catalog:forms`. devDeps: `@suaveplan/testing workspace:^`, `@suaveplan/typescript-config workspace:^`, `@vitest/coverage-istanbul catalog:testing`, `vitest catalog:testing`. **No** workspace dep on any other backend package.
- [x] 2.3 Author `tsconfig.json` extending `@suaveplan/typescript-config/library.json`
- [x] 2.4 Author `vite.config.ts` using `createTestConfig()` from `@suaveplan/testing/config`
- [x] 2.5 Author `README.md` (≥200 words, includes Modules table linking to each `.md`)
- [x] 2.6 Add `package.json` `exports` map with subpaths `.`, `./backend`, `./factory`, `./write-through`, `./merge`
## 3. Implementation
- [x] 3.1 Author `src/backend.types.ts` — port `ChainedBackendOptions` and `MergeStrategy` from monolith L15-L28 verbatim
- [x] 3.2 Author `src/backend.schemas.ts` — port `ChainedBackendOptionsSchema` Zod schema verbatim (11 lines); validates `backends` is a non-empty array
- [x] 3.3 Author `src/write-through.ts` — extract `#writeThrough` logic into a standalone `writeThrough(backends, successIndex, locale, namespace, record): Promise<void>` (best-effort; swallows errors)
- [x] 3.4 Author `src/merge.ts` — extract inline merge logic from `readMulti` into a standalone `mergeReadMulti(perBackend, strategy): Record<string, TranslationRecord>`
- [x] 3.5 Author `src/observability.ts` — inline equivalents of `recordRead` / `recordWrite`; export `i18n.backend.chained.read_duration_ms`, `i18n.backend.chained.write_duration_ms` histograms; counters `read_total{outcome}`, `read_errors_total{code}`, `write_total{outcome}`, `fallthrough_total{from_backend,to_backend}` (low-cardinality — backends identified by their `.name`)
- [x] 3.6 Author `src/backend.ts` — port `ChainedBackend` class from monolith L43-L230; rewire imports; use `writeThrough` and `mergeReadMulti` helpers; emit metrics including the new `fallthrough_total` counter on every `BACKEND_LOCALE_NOT_AVAILABLE`-driven `continue`; add optional `signal?: AbortSignal` parameter on `read` / `readMulti` / `save` and thread it into every child call; throw `I18nError(I18N.CHAINED_BACKEND_ALL_FAILED)` if `backends.length === 0` is reached at runtime (defence-in-depth past the schema check)
## 4. Factory function
- [x] 4.1 Author `src/factory.ts` — `chainedBackend(backendsOrOptions: readonly Backend[] | ChainedBackendOptions): ChainedBackend` supporting both array shorthand and full options object (per monolith L233-L242)
## 5. Documentation (sibling `.md` per implementation file)
- [x] 5.1 `src/backend.md` — port from `packages/i18n/i18n/src/core/backend/chained/chained-backend.md`; ≥200 words; document fallthrough semantics, write-through cache pattern, BACKEND_LOCALE_NOT_AVAILABLE vs CHAINED_BACKEND_ALL_FAILED distinction
- [x] 5.2 `src/factory.md` — ≥200 words; basic + advanced examples (array shorthand vs options object)
- [x] 5.3 `src/write-through.md` — best-effort semantics; per-backend save-capability gating
- [x] 5.4 `src/merge.md` — first-wins vs last-wins examples; gap-filling behaviour
- [x] 5.5 `src/observability.md` — emitted metrics + units; `fallthrough_total` cardinality (= number of unique chain links observed)
- [x] 5.6 `README.md` `## Modules` section links every `.md`
## 6. Tests (Vitest, runner-neutral imports per CLAUDE.md §11)
- [x] 6.1 `src/backend.test.ts` — port every test from `packages/i18n/i18n/src/core/backend/chained/chained-backend.test.ts` (495 lines); same `it(...)` titles for conformance traceability; cover fallthrough, propagation of `BACKEND_READ_FAILED`, write-through, readMulti merge strategies, save fan-out, lastModified first-defined, init/dispose lifecycle, capabilities union
- [x] 6.2 `src/backend.spec-conformance.test.ts` — port from monolith spec-conformance suite (102 lines); add new requirements (CHAINED_BACKEND_ALL_FAILED on empty backends, AbortSignal cancellation, fallthrough_total counter emission)
- [x] 6.3 `src/factory.test.ts` — array form returns `ChainedBackend`; options form returns `ChainedBackend`; default `name === "chained"`
- [x] 6.4 `src/write-through.test.ts` — writes only to backends with `save`; saves at indices 0..N-1; never writes to the success backend; failures swallowed (best-effort)
- [x] 6.5 `src/merge.test.ts` — first-wins picks earlier; last-wins picks later; gaps filled from later
- [x] 6.6 `src/index.test.ts` — every documented public export resolves through `.`, `./backend`, `./factory`, `./write-through`, `./merge`
- [x] 6.7 Add an AbortSignal cancellation test — pre-aborted signal causes `read` to throw `I18nError(I18N.BACKEND_TIMEOUT)`; signal threaded into child reads (verify via stub backend that records its received signal)
- [x] 6.8 Add a `CHAINED_BACKEND_ALL_FAILED` test — bypass schema validation via `as unknown as ChainedBackendOptions` cast and pass `backends: []`; runtime throw on first `read` carries the new code
- [x] 6.9 Every `it(...)` carries `{ timeout: 5000 }`
- [x] 6.10 All test imports go through `@suaveplan/testing/runner` (CLAUDE.md §11) — verified by `grep -rnE "from ['\"](vitest|bun:test)['\"]" packages/i18n/i18n-backend-chained/src/` returning zero hits
- [x] 6.11 No `// biome-ignore` suppressions (CLAUDE.md §9)
- [x] 6.12 No `export *` in any non-root barrel (CLAUDE.md §10)
## 7. Pre-archive verification (5-gate triple)
- [x] 7.1 `bun run check` — Biome 0/0/0 across the monorepo
- [x] 7.2 `bunx turbo lint typecheck test --filter=@suaveplan/i18n-backend-chained` — green
- [x] 7.3 `bunx turbo test:coverage --filter=@suaveplan/i18n-backend-chained` — 100/100/100/100
- [x] 7.4 `bun scripts/openspec-conformance.ts @suaveplan/i18n-backend-chained` — exit 0
- [x] 7.5 `bunx turbo build --filter=@suaveplan/i18n-backend-chained` — green
## 8. Pre-archive 7-row checklist (AGENTS.md Section 13)
- [x] 8.1 Generics threaded — `Backend[]` correctly typed; `MergeStrategy` discriminator narrowed exhaustively
- [x] 8.2 Zod schemas wired at the public boundary — `ChainedBackendOptionsSchema` parses constructor input AND rejects empty `backends` array with `INVALID_OPTIONS`
- [x] 8.3 Every error code thrown by at least one production path: `INVALID_OPTIONS` (constructor), `BACKEND_LOCALE_NOT_AVAILABLE` (all children miss with non-empty chain), `CHAINED_BACKEND_ALL_FAILED` (defence-in-depth empty-chain throw), `BACKEND_READ_FAILED` (propagation from child), `BACKEND_WRITE_FAILED` (save fan-out failure), `BACKEND_TIMEOUT` (signal aborted)
- [x] 8.4 Strict-mode tested — exhaustive switch on `MergeStrategy` enforced via `expectTypeOf`
- [x] 8.5 OTel histograms emitted from production paths: `i18n.backend.chained.read_duration_ms`, `i18n.backend.chained.write_duration_ms`; counters `read_total`, `read_errors_total`, `write_total`, `fallthrough_total{from_backend,to_backend}`
- [x] 8.6 Lifecycle dedup — `init` is idempotent (re-calling forwards to children but does not duplicate state); `dispose` is idempotent and reverse-order
- [x] 8.7 No retroactive tick — every box reflects work done in this change
## 9. Manifest + ecosystem awareness
- [x] 9.1 Add `@suaveplan/i18n-backend-chained` entry to root `CLAUDE.md` package listing
- [x] 9.2 Run `bun scripts/folder-reorg/generate-manifest.ts` to refresh `MANIFEST.md`
- [x] 9.3 Append `@suaveplan/i18n-backend-chained` to `STATUS.md` Completed list
## 10. Archive
- [x] 10.1 Fold spec delta into `openspec/specs/i18n-backend-chained/spec.md` (NEW capability spec — first delta becomes canonical)
- [x] 10.2 Move `openspec/changes/i18n-backend-chained/` → `openspec/archive/<yyyy-mm-dd>-i18n-backend-chained/`