first commit
This commit is contained in:
130
docs/localization/i18n/i18n-backend-storage/README.md
Normal file
130
docs/localization/i18n/i18n-backend-storage/README.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# @suaveplan/i18n-backend-storage - Universal translation backend backed by any AbstractStorage from @suaveplan/storage. Three wire layouts: single-key-per-namespace (default), single-key-per-locale, flat-keys. Pluggable codec, AbortSignal cancellation, OpenTelemetry instrumentation under i18n.backend.storage.*. Zero runtime dependencies on @suaveplan/i18n core.
|
||||
|
||||
## Summary
|
||||
|
||||
Universal translation backend for the `@suaveplan/i18n` family,
|
||||
backed by any `AbstractStorage` implementation from
|
||||
`@suaveplan/storage`. Implements the full `Backend` contract
|
||||
(`read`, `readMulti`, `save`, `lastModified`) over three wire
|
||||
layouts: `single-key-per-namespace` (default),
|
||||
`single-key-per-locale`, and `flat-keys`. Pluggable codec, optional
|
||||
`AbortSignal` cancellation, OpenTelemetry instrumentation under the
|
||||
`i18n.backend.storage.*` prefix. Zero runtime dependencies on
|
||||
`@suaveplan/i18n` core.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [backend](./src/backend/backend.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [backend](./src/backend/backend.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
||||
<!-- end:auto-docs --> - [backend](./src/backend/backend.md) — <!-- begin:auto-docs --> _(no submodules)_- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_- [backend](./src/backend/backend.md) — <!-- begin:auto-docs --> _(no submodules)_- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_- [backend](./src/backend/backend.md) — <!-- begin:auto-docs --> _(no submodules)_- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_- [backend](./src/backend/backend.md) — <!-- begin:auto-docs --> _(no submodules)_- [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_ - [factory](./src/factory/factory.md) — <!-- begin:auto-docs --> _(no submodules)_- [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_ - [observability](./src/observability/observability.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
## Why this exists
|
||||
|
||||
The Wave 5 modular split lifts each backend out of the
|
||||
`@suaveplan/i18n` monolith into its own sibling so consumers can
|
||||
install only the backends they need. The storage backend is the
|
||||
canonical persistence layer: pair it with `MemoryStorage` for tests,
|
||||
`OpfsStorage` / `IndexedDBStorage` for offline-first browser apps,
|
||||
or `S3Storage` / `GarageStorage` / `NodeFsStorage` /
|
||||
`SqliteStorage` for server-tier deployments. Splitting it out also
|
||||
isolates `@suaveplan/storage` as a transitive dep — consumers using
|
||||
only the HTTP or memory backends no longer pull it in.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/i18n-backend-storage @suaveplan/storage
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```ts
|
||||
import { storageBackend } from "@suaveplan/i18n-backend-storage";
|
||||
import { MemoryStorage } from "@suaveplan/storage";
|
||||
|
||||
const backend = storageBackend({ storage: new MemoryStorage() });
|
||||
|
||||
await backend.save("en", "auth", { login: { title: "Sign in" } });
|
||||
const record = await backend.read("en", "auth");
|
||||
// → { login: { title: "Sign in" } }
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
| Subpath | Description |
|
||||
|---|---|
|
||||
| `@suaveplan/i18n-backend-storage` | Root barrel — class + factory + types + schemas + observability |
|
||||
| `@suaveplan/i18n-backend-storage/backend` | `StorageBackend` class + `defaultCodec` |
|
||||
| `@suaveplan/i18n-backend-storage/factory` | `storageBackend` factory function only |
|
||||
| `@suaveplan/i18n-backend-storage/schemas` | Zod boundary schemas |
|
||||
| `@suaveplan/i18n-backend-storage/observability` | OTel instruments + in-memory state ring |
|
||||
|
||||
## Modules Documentation
|
||||
|
||||
- [backend.md](src/backend/backend.md) — `StorageBackend` class, layouts, codec
|
||||
- [factory.md](src/factory/factory.md) — `storageBackend` factory function
|
||||
- [observability.md](src/observability/observability.md) — OTel instruments + cardinality
|
||||
|
||||
## Capabilities
|
||||
|
||||
| Flag | Value | Notes |
|
||||
|---|---|---|
|
||||
| `canRead` | `true` | always |
|
||||
| `canReadMulti` | `true` | per-layout dispatch |
|
||||
| `canSave` | `true` | persists into the wrapped storage |
|
||||
| `canTrackModification` | `true` | consults `getMetadata().modifiedAt` |
|
||||
| `canWatch` | `false` | the storage contract has no change feed |
|
||||
|
||||
## Layouts
|
||||
|
||||
- `single-key-per-namespace` (default) — one key per `(locale, ns)`
|
||||
at `<prefix>/<locale>/<namespace>.json`. Best for HTTP-CDN-style
|
||||
storages.
|
||||
- `single-key-per-locale` — one key per locale holding the whole
|
||||
namespace map. Best when namespaces are small and read together.
|
||||
- `flat-keys` — one key per leaf string at
|
||||
`<prefix>:<locale>:<namespace>:<dotPath>`. Best for content-
|
||||
addressed storages with cheap per-leaf delta shipping.
|
||||
|
||||
## Testing
|
||||
|
||||
The package's own tests use the shared `runBackendConformance`
|
||||
harness from `@suaveplan/i18n-shared/testing` so the universal
|
||||
`Backend` contract is exercised against a fresh storage backend
|
||||
per assertion (against all three layouts):
|
||||
|
||||
```ts
|
||||
import { runBackendConformance } from "@suaveplan/i18n-shared/testing";
|
||||
import { StorageBackend } from "@suaveplan/i18n-backend-storage";
|
||||
import { MemoryStorage } from "@suaveplan/storage";
|
||||
|
||||
runBackendConformance("StorageBackend", async () => {
|
||||
const backend = new StorageBackend({ storage: new MemoryStorage() });
|
||||
return {
|
||||
backend,
|
||||
async seed(locale, namespace, record) {
|
||||
await backend.save(locale, namespace, record as never);
|
||||
},
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
## Versioning
|
||||
|
||||
This package is part of the i18n modular-split rollout. Stays at
|
||||
`0.1.0` until the slim core (Wave 6) cuts the family `1.0.0`
|
||||
release. The metric names under `i18n.backend.storage.*` are part of
|
||||
the public API contract and will not change without a major bump.
|
||||
|
||||
## Wave 5 Scope
|
||||
|
||||
This package is Wave 5b of the 14-package i18n modular split. It
|
||||
ships pure additions only — `packages/i18n/i18n/src/core/backend/storage/`
|
||||
retains its copy of the backend until Wave 6 deletes it. Both
|
||||
copies coexist during Wave 5; downstream consumers continue to
|
||||
import from the monolith and observe no behaviour change.
|
||||
|
||||
Reference in New Issue
Block a user