Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
# `backend.ts` — `MultiFileFsBackend`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Server-tier translation `Backend` that reads and writes
|
||||
`<root>/<locale>/<namespace>.<ext>` (or one of the alternate `flat` /
|
||||
`glob` layouts) on the local filesystem via `node:fs/promises`. The
|
||||
class implements the universal `Backend` contract from
|
||||
`@suaveplan/types` and conforms to the
|
||||
`runBackendConformance` harness shipped from
|
||||
`@suaveplan/i18n-shared/testing`. It exists as its own package — and
|
||||
not part of the universal `@suaveplan/i18n` core — because the
|
||||
default code path performs `await import("node:fs/promises")` which
|
||||
fails at module evaluation in browsers and non-Node runtimes that do
|
||||
not expose Node built-ins.
|
||||
|
||||
## Features
|
||||
|
||||
- Three wire layouts: `nested` (default), `flat`, `glob` (with
|
||||
`{locale}` / `{namespace}` substitution into a caller-supplied
|
||||
pattern).
|
||||
- `read` / `save` / `lastModified` over a `MultiFileFsLike` shim that
|
||||
defaults to `node:fs/promises` and is fully injectable for tests.
|
||||
- JSON built in. YAML and JSON5 are auto-detected via dynamic
|
||||
`import()` so the package stays free of mandatory parser deps.
|
||||
- Optional `fs.watch` loop emits `loaded` events into the
|
||||
`BackendInitContext.emitLoaded` channel for HMR / Suspense refresh.
|
||||
- Pre-aborted `AbortSignal` short-circuits with `BACKEND_TIMEOUT`
|
||||
without touching the filesystem. ENOENT / ENOTDIR fall through as
|
||||
`BACKEND_LOCALE_NOT_AVAILABLE`; other errno codes surface as
|
||||
`BACKEND_READ_FAILED`.
|
||||
- Zod-validated options at the public boundary — bad `layout` /
|
||||
missing `globPattern` / non-string `name` rejected as
|
||||
`INVALID_OPTIONS`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { MultiFileFsBackend } from "@suaveplan/i18n-backend-fs-multifile";
|
||||
|
||||
const backend = new MultiFileFsBackend({ root: "./locales" });
|
||||
const record = await backend.read("en", "auth");
|
||||
// → { login: { title: "Sign in" } }
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { MultiFileFsBackend } from "@suaveplan/i18n-backend-fs-multifile";
|
||||
|
||||
const backend = new MultiFileFsBackend({
|
||||
root: "./locales",
|
||||
layout: "glob",
|
||||
globPattern: "{locale}/{namespace}.json",
|
||||
extension: "json",
|
||||
watch: true,
|
||||
});
|
||||
|
||||
await backend.init({
|
||||
catalogName: "app",
|
||||
emitLoaded: (locale, ns) => {
|
||||
console.log(`reloaded ${locale}/${ns}`);
|
||||
},
|
||||
});
|
||||
|
||||
// AbortSignal short-circuits with BACKEND_TIMEOUT.
|
||||
const ctrl = new AbortController();
|
||||
ctrl.abort();
|
||||
await backend.read("en", "auth", ctrl.signal); // throws I18N.BACKEND_TIMEOUT
|
||||
|
||||
// YAML peer auto-detection — install `yaml` to enable.
|
||||
const yamlBackend = new MultiFileFsBackend({
|
||||
root: "./locales",
|
||||
extension: "yaml",
|
||||
});
|
||||
const fr = await yamlBackend.read("fr", "auth");
|
||||
|
||||
await backend.dispose();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `new MultiFileFsBackend(options: MultiFileFsBackendOptions)`:
|
||||
Constructs and validates options. Throws
|
||||
`I18nError(I18N.INVALID_OPTIONS)` on Zod failure or when
|
||||
`layout: "glob"` is supplied without `globPattern`.
|
||||
- `read(locale, namespace, signal?)`: returns the parsed
|
||||
`TranslationRecord`. Throws
|
||||
`BACKEND_LOCALE_NOT_AVAILABLE` on ENOENT/ENOTDIR,
|
||||
`BACKEND_READ_FAILED` on other fs / parse / unknown-extension /
|
||||
missing-peer failures, `BACKEND_TIMEOUT` on a pre-aborted signal.
|
||||
- `save(locale, namespace, record, signal?)`: ensures the parent
|
||||
directory and writes the encoded payload. Throws
|
||||
`BACKEND_WRITE_FAILED` on writeFile failure or unsupported
|
||||
extension; `BACKEND_TIMEOUT` on pre-aborted signal.
|
||||
- `lastModified(locale, namespace)`: returns `mtimeMs` or `undefined`
|
||||
(never throws).
|
||||
- `init(context)`: stores the `BackendInitContext` and starts the
|
||||
`fs.watch` loop when `watch: true`.
|
||||
- `dispose()`: aborts the watcher; safe to call multiple times.
|
||||
- `MultiFileFsBackendOptionsSchema`: exported Zod schema for the
|
||||
options bag.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- A leading source-comment marks the package as server-tier so
|
||||
consumers do not accidentally land it in a browser bundle. Wave 6's
|
||||
slim universal `@suaveplan/i18n` core does NOT re-export this
|
||||
package.
|
||||
- The `#getFs` method dynamically imports `node:fs/promises` only
|
||||
when no `fs` shim was injected, keeping injected-fs tests
|
||||
hermetic and avoiding the cost when consumers supply their own
|
||||
filesystem.
|
||||
- `parsePayload` routes JSON through built-in `JSON.parse`. Unknown
|
||||
extensions throw `BACKEND_READ_FAILED` with the extension name in
|
||||
the error message and context. Peer parsers (`yaml`, `json5`) are
|
||||
loaded via dynamic `import()`; missing peers surface as
|
||||
`BACKEND_READ_FAILED` with `{ peer }` in the context so consumers
|
||||
can install the right package.
|
||||
- `joinPath` collapses leading and trailing slashes so
|
||||
`root: ""` (relative to cwd) and `root: "/abs"` both produce well-formed
|
||||
paths.
|
||||
@@ -0,0 +1,72 @@
|
||||
# `factory.ts` — `multiFileFsBackend()`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Builder-style entry point that returns a fresh `MultiFileFsBackend`
|
||||
per call. Functionally identical to `new MultiFileFsBackend(options)`,
|
||||
but reads naturally in pipelines and configuration objects where the
|
||||
caller is composing a backend chain alongside `memoryBackend()`,
|
||||
`storageBackend()`, `httpBackend()`, and `chainedBackend()` — every
|
||||
backend in the family ships a matching factory function for symmetry.
|
||||
|
||||
The factory has no internal state — every invocation returns a brand-new
|
||||
instance. Options are validated by the same Zod schema as the
|
||||
constructor; bad options throw `I18nError(I18N.INVALID_OPTIONS)`. There
|
||||
is no caching, no singleton, and no hidden configuration: the factory
|
||||
exists purely to flip the calling style from `new C(opts)` to
|
||||
`factory(opts)` for ergonomic chain composition.
|
||||
|
||||
## Features
|
||||
|
||||
- Returns a fresh `MultiFileFsBackend` on every call.
|
||||
- Forwards all options unchanged into the class constructor.
|
||||
- Same validation surface — bad options throw `INVALID_OPTIONS`.
|
||||
- Zero shared state, zero side effects.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { multiFileFsBackend } from "@suaveplan/i18n-backend-fs-multifile";
|
||||
|
||||
const backend = multiFileFsBackend({ root: "./locales" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { chainedBackend } from "@suaveplan/i18n-backend-chained";
|
||||
import { multiFileFsBackend } from "@suaveplan/i18n-backend-fs-multifile";
|
||||
import { memoryBackend } from "@suaveplan/i18n-backend-memory";
|
||||
|
||||
// Server-side: hot in-memory cache → fall through to disk on miss.
|
||||
const chain = chainedBackend([
|
||||
memoryBackend(),
|
||||
multiFileFsBackend({ root: "./locales", watch: true }),
|
||||
]);
|
||||
|
||||
await chain.init({
|
||||
catalogName: "app",
|
||||
emitLoaded: () => {},
|
||||
});
|
||||
|
||||
const record = await chain.read("en", "auth");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `multiFileFsBackend(options: MultiFileFsBackendOptions): MultiFileFsBackend`:
|
||||
returns a new `MultiFileFsBackend` instance. Throws
|
||||
`I18nError(I18N.INVALID_OPTIONS)` on schema failure or when
|
||||
`layout: "glob"` is supplied without `globPattern`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The factory is a thin wrapper — the line count is intentional. The
|
||||
factory exists for symmetry with the rest of the i18n backend
|
||||
family, where every concrete backend ships both a class export and
|
||||
a `<thing>Backend()` factory function.
|
||||
- The factory is server-tier — declaring this package's `engines.node`
|
||||
at the package boundary covers the factory and the class equally.
|
||||
@@ -0,0 +1,92 @@
|
||||
# `observability.ts`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Self-contained OpenTelemetry instrument set for the
|
||||
`@suaveplan/i18n-backend-fs-multifile` package. The module wires every
|
||||
public counter and histogram emitted by `MultiFileFsBackend` and exposes
|
||||
an in-memory state ring used by spec-conformance tests so production
|
||||
code paths can be exercised without standing up a full OTel SDK.
|
||||
|
||||
The module is loaded lazily by `getMultiFileFsBackendMetrics()` — the
|
||||
first call to any production path (`recordRead`, `recordWrite`) builds a
|
||||
single shared `Meter` and caches the resulting `Histogram`/`Counter`
|
||||
instances. Subsequent calls hit the cache. When no `MeterProvider` is
|
||||
registered (the default in production), every instrument is a no-op,
|
||||
keeping the cost path-neutral when telemetry is off.
|
||||
|
||||
## Features
|
||||
|
||||
- Lazily-built histograms and counters under the
|
||||
`i18n.backend.fs_multifile.*` prefix.
|
||||
- Five instruments: `read_duration_ms`, `read_total`,
|
||||
`read_errors_total`, `write_duration_ms`, `write_total`.
|
||||
- Every emission carries a `backend` label plus a `layout` label
|
||||
(`nested` / `flat` / `glob`) so consumers can size watchers per
|
||||
layout.
|
||||
- In-memory state ring (`getMultiFileFsBackendMetricsState`,
|
||||
`resetMultiFileFsBackendMetricsState`) so spec-conformance tests can
|
||||
assert exact attribute payloads without an exporter.
|
||||
- `resetMultiFileFsBackendMetricsCache` test hook to drop the cached
|
||||
instruments between cases when a different MeterProvider is required.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
recordRead,
|
||||
recordWrite,
|
||||
} from "@suaveplan/i18n-backend-fs-multifile/observability";
|
||||
|
||||
recordRead("multi-file-fs", "en", "auth", "nested", "hit", 0.42);
|
||||
recordWrite("multi-file-fs", "nested", "ok", 0.31);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
getMultiFileFsBackendMetricsState,
|
||||
resetMultiFileFsBackendMetricsCache,
|
||||
resetMultiFileFsBackendMetricsState,
|
||||
} from "@suaveplan/i18n-backend-fs-multifile/observability";
|
||||
|
||||
resetMultiFileFsBackendMetricsCache();
|
||||
resetMultiFileFsBackendMetricsState();
|
||||
|
||||
// ... run code that hits the backend ...
|
||||
|
||||
const state = getMultiFileFsBackendMetricsState();
|
||||
const outcomes = state.readTotal.map((s) => s.attributes["outcome"]);
|
||||
expect(outcomes).toContain("hit");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `recordRead(backend, locale, namespace, layout, outcome, durationMs, errorCode?)`:
|
||||
emits the `read_duration_ms` histogram and `read_total` counter; if
|
||||
`errorCode` is supplied, also increments `read_errors_total{code,layout}`.
|
||||
- `recordWrite(backend, layout, outcome, durationMs)`: emits the
|
||||
`write_duration_ms` histogram and `write_total` counter.
|
||||
- `getMultiFileFsBackendMetrics()`: lazy build / return cached
|
||||
instruments — idempotent.
|
||||
- `getMultiFileFsBackendMetricsState()`: return the in-memory state
|
||||
ring snapshot.
|
||||
- `resetMultiFileFsBackendMetricsState()`: clear the in-memory state
|
||||
ring.
|
||||
- `resetMultiFileFsBackendMetricsCache()`: drop the cached instruments
|
||||
so a fresh MeterProvider can take over (test-only escape hatch).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `layout` attribute is mandatory on every counter and histogram so
|
||||
operators can split SLOs per wire layout — `glob` patterns tend to
|
||||
hide expensive resolutions behind dynamic substitution.
|
||||
- The state ring is unbounded by design and is the test-only path. The
|
||||
`resetMultiFileFsBackendMetricsState` helper is the supported way to
|
||||
prevent ring growth across tests.
|
||||
- The OTel meter is named `suaveplan.i18n-backend-fs-multifile` so
|
||||
consumers can apply per-package views.
|
||||
Reference in New Issue
Block a user