@endora-commerce/mod-i18n 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/backend/cli/coverage.d.ts +29 -0
  4. package/dist/backend/cli/coverage.d.ts.map +1 -0
  5. package/dist/backend/cli/coverage.js +84 -0
  6. package/dist/backend/cli/coverage.js.map +1 -0
  7. package/dist/backend/cli/reload.d.ts +35 -0
  8. package/dist/backend/cli/reload.d.ts.map +1 -0
  9. package/dist/backend/cli/reload.js +18 -0
  10. package/dist/backend/cli/reload.js.map +1 -0
  11. package/dist/backend/entities/translation-bundle.entity.d.ts +23 -0
  12. package/dist/backend/entities/translation-bundle.entity.d.ts.map +1 -0
  13. package/dist/backend/entities/translation-bundle.entity.js +69 -0
  14. package/dist/backend/entities/translation-bundle.entity.js.map +1 -0
  15. package/dist/backend/index.d.ts +164 -0
  16. package/dist/backend/index.d.ts.map +1 -0
  17. package/dist/backend/index.js +134 -0
  18. package/dist/backend/index.js.map +1 -0
  19. package/dist/backend/routes.admin.d.ts +41 -0
  20. package/dist/backend/routes.admin.d.ts.map +1 -0
  21. package/dist/backend/routes.admin.js +74 -0
  22. package/dist/backend/routes.admin.js.map +1 -0
  23. package/dist/backend/services/bundle-loader.d.ts +49 -0
  24. package/dist/backend/services/bundle-loader.d.ts.map +1 -0
  25. package/dist/backend/services/bundle-loader.js +84 -0
  26. package/dist/backend/services/bundle-loader.js.map +1 -0
  27. package/dist/backend/services/bundle-reconciler.d.ts +82 -0
  28. package/dist/backend/services/bundle-reconciler.d.ts.map +1 -0
  29. package/dist/backend/services/bundle-reconciler.js +38 -0
  30. package/dist/backend/services/bundle-reconciler.js.map +1 -0
  31. package/dist/backend/services/i18n-service.d.ts +67 -0
  32. package/dist/backend/services/i18n-service.d.ts.map +1 -0
  33. package/dist/backend/services/i18n-service.js +323 -0
  34. package/dist/backend/services/i18n-service.js.map +1 -0
  35. package/dist/backend/services/missing-key-logger.d.ts +52 -0
  36. package/dist/backend/services/missing-key-logger.d.ts.map +1 -0
  37. package/dist/backend/services/missing-key-logger.js +37 -0
  38. package/dist/backend/services/missing-key-logger.js.map +1 -0
  39. package/dist/manifest.d.ts +214 -0
  40. package/dist/manifest.d.ts.map +1 -0
  41. package/dist/manifest.js +245 -0
  42. package/dist/manifest.js.map +1 -0
  43. package/dist/migrations/20260507T091405_i18n_admin_i18n_init.d.ts +30 -0
  44. package/dist/migrations/20260507T091405_i18n_admin_i18n_init.d.ts.map +1 -0
  45. package/dist/migrations/20260507T091405_i18n_admin_i18n_init.js +50 -0
  46. package/dist/migrations/20260507T091405_i18n_admin_i18n_init.js.map +1 -0
  47. package/dist/migrations/index.d.ts +23 -0
  48. package/dist/migrations/index.d.ts.map +1 -0
  49. package/dist/migrations/index.js +23 -0
  50. package/dist/migrations/index.js.map +1 -0
  51. package/docs/i18n.md +166 -0
  52. package/i18n/en.json +1957 -0
  53. package/i18n/pl.json +1957 -0
  54. package/package.json +68 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ <!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
2
+
3
+ # @endora-commerce/mod-i18n
4
+
5
+ Per-user Admin UI language preference and module-scoped translation bundles. English is the platform-wide fallback (FR-013 / FR-016).
6
+
7
+ ## What this is
8
+
9
+ An **Endora Commerce module package**. Its module id is `_i18n` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
10
+
11
+ A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
12
+
13
+ ## Entry points
14
+
15
+ | Import | Contents |
16
+ | --- | --- |
17
+ | `@endora-commerce/mod-i18n` | the module manifest — its id, version, dependencies, settings and activation |
18
+ | `@endora-commerce/mod-i18n/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
19
+ | `@endora-commerce/mod-i18n/migrations` | the module’s own schema migrations, in the order the platform runs them |
20
+
21
+ ## Depends on
22
+
23
+ Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
24
+
25
+ **Endora packages**
26
+
27
+ - `@endora-commerce/contracts`
28
+ - `@endora-commerce/platform`
29
+
30
+ **Third-party**
31
+
32
+ - `@mikro-orm/core` ^6
33
+ - `@mikro-orm/migrations` ^6
34
+ - `@mikro-orm/postgresql` ^6
35
+ - `fastify` ^5
36
+ - `zod` ^4
37
+
38
+ ## What the tarball carries
39
+
40
+ - `dist/` — the compiled JavaScript and its type declarations
41
+ - `i18n/` — the translation bundles this package ships (`en`, `pl`), loaded by the platform
42
+ - `docs/` — the package’s own pages in the Endora Commerce documentation site
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ pnpm add @endora-commerce/mod-i18n
48
+ ```
49
+
50
+ ## Licence
51
+
52
+ MIT — the text is in `LICENSE`, beside this file.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `_i18n coverage` — the translation-coverage snapshot (feature 021), as a
3
+ * host-run command (feature 080, T042b / D-160.9).
4
+ *
5
+ * Prints the same payload the admin route returns. Useful for developers and
6
+ * for CI; in `--strict` mode it exits non-zero when any module in the filtered
7
+ * set has `missingCount > 0`.
8
+ *
9
+ * Flags:
10
+ * --json Emit JSON (default: human-readable table)
11
+ * --strict Exit 1 when any missingCount > 0 for the filtered set
12
+ * --modules a,b,c Filter by module id (repeatable as CSV)
13
+ * --languages pl,en Filter by supported admin language code (repeatable as CSV)
14
+ * --include-keys all Match the route's includeKeys=all
15
+ */
16
+ import { type SupportedAdminLanguage } from '@endora-commerce/contracts';
17
+ import type { ModuleCliCommandContext } from '@endora-commerce/contracts';
18
+ import { type ModuleContext } from '@endora-commerce/platform/kernel';
19
+ interface ParsedArgs {
20
+ json: boolean;
21
+ strict: boolean;
22
+ modules?: string[];
23
+ languages?: SupportedAdminLanguage[];
24
+ includeKeys?: 'all' | 'missing';
25
+ }
26
+ export declare function parseArgs(argv: readonly string[]): ParsedArgs;
27
+ export declare function coverage({ ctx, argv, out, err, }: ModuleCliCommandContext<ModuleContext>): Promise<number>;
28
+ export {};
29
+ //# sourceMappingURL=coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.d.ts","sourceRoot":"","sources":["../../../src/backend/cli/coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAuB,KAAK,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAGhF,UAAU,UAAU;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACrC,WAAW,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;CACjC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAsB7D;AA+BD,wBAAsB,QAAQ,CAAC,EAC7B,GAAG,EACH,IAAI,EACJ,GAAG,EACH,GAAG,GACJ,EAAE,uBAAuB,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CA4B1D"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `_i18n coverage` — the translation-coverage snapshot (feature 021), as a
3
+ * host-run command (feature 080, T042b / D-160.9).
4
+ *
5
+ * Prints the same payload the admin route returns. Useful for developers and
6
+ * for CI; in `--strict` mode it exits non-zero when any module in the filtered
7
+ * set has `missingCount > 0`.
8
+ *
9
+ * Flags:
10
+ * --json Emit JSON (default: human-readable table)
11
+ * --strict Exit 1 when any missingCount > 0 for the filtered set
12
+ * --modules a,b,c Filter by module id (repeatable as CSV)
13
+ * --languages pl,en Filter by supported admin language code (repeatable as CSV)
14
+ * --include-keys all Match the route's includeKeys=all
15
+ */
16
+ import { SUPPORTED_LANGUAGES } from '@endora-commerce/contracts';
17
+ import { lazyPort } from '@endora-commerce/platform/kernel';
18
+ export function parseArgs(argv) {
19
+ const out = { json: false, strict: false };
20
+ for (let i = 0; i < argv.length; i += 1) {
21
+ const a = argv[i];
22
+ if (a === '--json')
23
+ out.json = true;
24
+ else if (a === '--strict')
25
+ out.strict = true;
26
+ else if (a === '--modules' || a === '--module') {
27
+ out.modules = (argv[i + 1] ?? '').split(',').filter(Boolean);
28
+ i += 1;
29
+ }
30
+ else if (a === '--languages' || a === '--language') {
31
+ const supported = new Set(SUPPORTED_LANGUAGES);
32
+ out.languages = (argv[i + 1] ?? '')
33
+ .split(',')
34
+ .filter((v) => supported.has(v));
35
+ i += 1;
36
+ }
37
+ else if (a === '--include-keys') {
38
+ const v = argv[i + 1];
39
+ if (v === 'all' || v === 'missing')
40
+ out.includeKeys = v;
41
+ i += 1;
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+ function printTable(snapshot, out) {
47
+ out(`Coverage snapshot @ ${snapshot.capturedAt}`);
48
+ if (snapshot.modules.length === 0) {
49
+ out(' (no modules in the filtered set)');
50
+ return;
51
+ }
52
+ for (const m of snapshot.modules) {
53
+ out(` ${m.moduleId}`);
54
+ for (const l of m.languages) {
55
+ out(` ${l.languageCode} missing=${String(l.missingCount).padStart(3, ' ')} ` +
56
+ `fellBackToEn=${String(l.fellBackToEnCount).padStart(3, ' ')}`);
57
+ }
58
+ }
59
+ }
60
+ export async function coverage({ ctx, argv, out, err, }) {
61
+ const args = parseArgs(argv);
62
+ // This module's own port, resolved exactly as `backend.ts` resolves it — one
63
+ // `I18nService` for the composition rather than a second one built here.
64
+ const i18nService = lazyPort(ctx, 'adminI18nService');
65
+ const snapshot = await i18nService.getCoverageSnapshot({
66
+ ...(args.modules ? { moduleIds: args.modules } : {}),
67
+ ...(args.languages ? { languageCodes: args.languages } : {}),
68
+ ...(args.includeKeys ? { includeKeys: args.includeKeys } : {}),
69
+ });
70
+ if (args.json)
71
+ out(JSON.stringify({ data: snapshot }, null, 2));
72
+ else
73
+ printTable(snapshot, out);
74
+ if (args.strict) {
75
+ const violators = snapshot.modules.flatMap((m) => m.languages.filter((l) => l.missingCount > 0).map((l) => ({ m, l })));
76
+ if (violators.length > 0) {
77
+ err(`[i18n coverage] strict mode: ${violators.length} (module, language) pair(s) are ` +
78
+ `missing entries.`);
79
+ return 1;
80
+ }
81
+ }
82
+ return 0;
83
+ }
84
+ //# sourceMappingURL=coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../../src/backend/cli/coverage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `_i18n reload` — hot-reload translations (feature 080, T042b / D-160.9).
3
+ *
4
+ * Re-reads every module's on-disk i18n JSON bundles into the
5
+ * `translation_bundles` table, bumping each row's version. A running backend
6
+ * serves the new strings on its next read (the per-language cache rebuilds when
7
+ * it sees a newer `MAX(version)`), so no restart is needed — admins just refresh
8
+ * the page, or the Admin UI "Reload translations" button does it for them.
9
+ *
10
+ * Note: the storefront ships its UI strings compiled in-tree
11
+ * (`storefront/lib/i18n/messages.ts`); those are not DB-backed and require a
12
+ * storefront rebuild/redeploy. This command covers the admin bundles.
13
+ *
14
+ * ## What composing bought here
15
+ *
16
+ * This was `scripts/reload.ts`, and it imported `resolvedManifestEntries` from
17
+ * `_lifecycle` to get the module list — the single entry in this module's
18
+ * `check:module-boundary` shard, escalated rather than cut because *"a
19
+ * module-owned CLI entry point has no container and no `ModuleContext` … and
20
+ * what it needs is the deployment's module registry, which is a
21
+ * composition-root input by nature"*. Both halves of that sentence are answered
22
+ * by the host running the command: `resolvedModuleRegistry` is a
23
+ * `PLATFORM_OWNED_NAMES` entry any module may read off its cradle, and the
24
+ * `I18nService` is this module's own registration rather than a second instance
25
+ * built here.
26
+ *
27
+ * It is deliberately the **composition's** registry rather than a fresh
28
+ * `resolvedManifestEntries()` call, which is what makes the command's answer
29
+ * the same as the running platform's: core, this deployment's overlay modules
30
+ * and every installed package (feature 080, T032).
31
+ */
32
+ import type { ModuleCliCommandContext } from '@endora-commerce/contracts';
33
+ import { type ModuleContext } from '@endora-commerce/platform/kernel';
34
+ export declare function reload({ ctx, out, err, }: ModuleCliCommandContext<ModuleContext>): Promise<number>;
35
+ //# sourceMappingURL=reload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reload.d.ts","sourceRoot":"","sources":["../../../src/backend/cli/reload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAYhF,wBAAsB,MAAM,CAAC,EAC3B,GAAG,EACH,GAAG,EACH,GAAG,GACJ,EAAE,uBAAuB,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB1D"}
@@ -0,0 +1,18 @@
1
+ import { lazyPort } from '@endora-commerce/platform/kernel';
2
+ import { reconcileBundles, } from '../services/bundle-reconciler.js';
3
+ export async function reload({ ctx, out, err, }) {
4
+ const entries = ctx.cradle().resolvedModuleRegistry;
5
+ // This module's own port, resolved exactly as `backend.ts` resolves it.
6
+ const i18nService = lazyPort(ctx, 'adminI18nService');
7
+ const result = await reconcileBundles(entries, i18nService, {
8
+ info: (msg) => out(msg),
9
+ warn: (msg) => err(msg),
10
+ });
11
+ out(`[i18n reload] done — installed=${result.installed} skipped=${result.skipped} ` +
12
+ `failed=${result.failed}`);
13
+ for (const failure of result.failures) {
14
+ err(`[i18n reload] "${failure.moduleId}" (${failure.reason}) at ${failure.modulePath}`);
15
+ }
16
+ return result.failed > 0 ? 1 : 0;
17
+ }
18
+ //# sourceMappingURL=reload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reload.js","sourceRoot":"","sources":["../../../src/backend/cli/reload.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAE,QAAQ,EAAsB,MAAM,kCAAkC,CAAC;AAChF,OAAO,EACL,gBAAgB,GAEjB,MAAM,kCAAkC,CAAC;AAQ1C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,EAC3B,GAAG,EACH,GAAG,EACH,GAAG,GACoC;IACvC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,EAAoB,CAAC,sBAAsB,CAAC;IACtE,wEAAwE;IACxE,MAAM,WAAW,GAAG,QAAQ,CAAc,GAAG,EAAE,kBAAkB,CAAC,CAAC;IAEnE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE;QAC1D,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;QACvB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;KACxB,CAAC,CAAC;IACH,GAAG,CACD,kCAAkC,MAAM,CAAC,SAAS,YAAY,MAAM,CAAC,OAAO,GAAG;QAC7E,UAAU,MAAM,CAAC,MAAM,EAAE,CAC5B,CAAC;IACF,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,GAAG,CAAC,kBAAkB,OAAO,CAAC,QAAQ,MAAM,OAAO,CAAC,MAAM,QAAQ,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { OptionalProps } from '@mikro-orm/core';
2
+ import type { SupportedAdminLanguage, TranslationBundleEntries } from '@endora-commerce/contracts';
3
+ /**
4
+ * Translation Bundle — feature 019 / data-model.md §1.1.
5
+ *
6
+ * One row per `(module_id, language_code)`. JSONB `entries` carries the
7
+ * flat translation key → string map. `version` defaults to
8
+ * `nextval(translation_bundles_version_seq)` so every UPSERT bumps the
9
+ * vector and the in-process resolver cache invalidates on its next
10
+ * `MAX(version)` check (research §R9).
11
+ *
12
+ * No FK on `module_id` — see migration 040 for the rationale.
13
+ */
14
+ export declare class TranslationBundle {
15
+ [OptionalProps]?: 'version' | 'installedAt' | 'updatedAt';
16
+ moduleId: string;
17
+ languageCode: SupportedAdminLanguage;
18
+ entries: TranslationBundleEntries;
19
+ version: number;
20
+ installedAt: Date;
21
+ updatedAt: Date;
22
+ }
23
+ //# sourceMappingURL=translation-bundle.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation-bundle.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/translation-bundle.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAwB,MAAM,iBAAiB,CAAC;AAErF,OAAO,KAAK,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEnG;;;;;;;;;;GAUG;AACH,qBAMa,iBAAiB;IAC5B,CAAC,aAAa,CAAC,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;IAG1D,QAAQ,EAAG,MAAM,CAAC;IAGlB,YAAY,EAAG,sBAAsB,CAAC;IAGtC,OAAO,EAAE,wBAAwB,CAAM;IAOvC,OAAO,EAAG,MAAM,CAAC;IAGjB,WAAW,EAAE,IAAI,CAAc;IAG/B,SAAS,EAAE,IAAI,CAAc;CAC9B"}
@@ -0,0 +1,69 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Entity, Index, OptionalProps, PrimaryKey, Property } from '@mikro-orm/core';
11
+ import { GlobalEntity } from '@endora-commerce/platform/tenancy';
12
+ /**
13
+ * Translation Bundle — feature 019 / data-model.md §1.1.
14
+ *
15
+ * One row per `(module_id, language_code)`. JSONB `entries` carries the
16
+ * flat translation key → string map. `version` defaults to
17
+ * `nextval(translation_bundles_version_seq)` so every UPSERT bumps the
18
+ * vector and the in-process resolver cache invalidates on its next
19
+ * `MAX(version)` check (research §R9).
20
+ *
21
+ * No FK on `module_id` — see migration 040 for the rationale.
22
+ */
23
+ let TranslationBundle = class TranslationBundle {
24
+ [OptionalProps];
25
+ moduleId;
26
+ languageCode;
27
+ entries = {};
28
+ version;
29
+ installedAt = new Date();
30
+ updatedAt = new Date();
31
+ };
32
+ __decorate([
33
+ PrimaryKey({ type: 'string', length: 64 }),
34
+ __metadata("design:type", String)
35
+ ], TranslationBundle.prototype, "moduleId", void 0);
36
+ __decorate([
37
+ PrimaryKey({ type: 'string', length: 12 }),
38
+ __metadata("design:type", String)
39
+ ], TranslationBundle.prototype, "languageCode", void 0);
40
+ __decorate([
41
+ Property({ type: 'json', columnType: 'jsonb' }),
42
+ __metadata("design:type", Object)
43
+ ], TranslationBundle.prototype, "entries", void 0);
44
+ __decorate([
45
+ Property({
46
+ type: 'bigint',
47
+ columnType: 'bigint',
48
+ defaultRaw: "nextval('translation_bundles_version_seq')",
49
+ }),
50
+ __metadata("design:type", Number)
51
+ ], TranslationBundle.prototype, "version", void 0);
52
+ __decorate([
53
+ Property({ type: 'Date' }),
54
+ __metadata("design:type", Date)
55
+ ], TranslationBundle.prototype, "installedAt", void 0);
56
+ __decorate([
57
+ Property({ type: 'Date', onUpdate: () => new Date() }),
58
+ __metadata("design:type", Date)
59
+ ], TranslationBundle.prototype, "updatedAt", void 0);
60
+ TranslationBundle = __decorate([
61
+ GlobalEntity(),
62
+ Entity({ tableName: 'translation_bundles' }),
63
+ Index({
64
+ properties: ['languageCode'],
65
+ name: 'idx_translation_bundles_language_code',
66
+ })
67
+ ], TranslationBundle);
68
+ export { TranslationBundle };
69
+ //# sourceMappingURL=translation-bundle.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation-bundle.entity.js","sourceRoot":"","sources":["../../../src/backend/entities/translation-bundle.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAGjE;;;;;;;;;;GAUG;AAOI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC5B,CAAC,aAAa,CAAC,CAA2C;IAG1D,QAAQ,CAAU;IAGlB,YAAY,CAA0B;IAGtC,OAAO,GAA6B,EAAE,CAAC;IAOvC,OAAO,CAAU;IAGjB,WAAW,GAAS,IAAI,IAAI,EAAE,CAAC;IAG/B,SAAS,GAAS,IAAI,IAAI,EAAE,CAAC;CAC9B,CAAA;AApBC;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;mDACzB;AAGlB;IADC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;;uDACL;AAGtC;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;;kDACT;AAOvC;IALC,QAAQ,CAAC;QACR,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,QAAQ;QACpB,UAAU,EAAE,4CAA4C;KACzD,CAAC;;kDACe;AAGjB;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8BACd,IAAI;sDAAc;AAG/B;IADC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;8BAC5C,IAAI;oDAAc;AAvBlB,iBAAiB;IAN7B,YAAY,EAAE;IACd,MAAM,CAAC,EAAE,SAAS,EAAE,qBAAqB,EAAE,CAAC;IAC5C,KAAK,CAAC;QACL,UAAU,EAAE,CAAC,cAAc,CAAC;QAC5B,IAAI,EAAE,uCAAuC;KAC9C,CAAC;GACW,iBAAiB,CAwB7B"}
@@ -0,0 +1,164 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql';
2
+ import type { FastifyRequest } from 'fastify';
3
+ import { type ModuleContext } from '@endora-commerce/platform/kernel';
4
+ import type { RequireAdminFactory } from '@endora-commerce/platform/kernel';
5
+ import { I18nService } from './services/i18n-service.js';
6
+ import { type I18nReconcileEntry, type I18nReconcileResult } from './services/bundle-reconciler.js';
7
+ import { TranslationBundle } from './entities/translation-bundle.entity.js';
8
+ /**
9
+ * `_i18n` — the module whose boot-time work does not run in `ctx.onBoot`
10
+ * (feature 072, wave 1, T089).
11
+ *
12
+ * Seventeen conversions before this one put their boot-time work in
13
+ * `ctx.onBoot`. This one does not, and the reason is worth stating because the
14
+ * natural move is to make it uniform with the other seventeen.
15
+ *
16
+ * This module walks the lifecycle manifest registry to refresh every module's
17
+ * `translation_bundles` rows from disk. It reaches that registry through a lazy
18
+ * accessor, because `_lifecycle`'s orchestrator is constructed *after* it — the
19
+ * chicken-and-egg the original `registry?: T | (() => T | undefined)` option
20
+ * shape exists to break. The reconcile therefore runs from the `ctx.routes`
21
+ * callback, at **plugin attach**, which is the point at which every root has
22
+ * finished composing and the accessor is guaranteed to answer.
23
+ *
24
+ * D-45 made the boot phase the last thing a root does before it builds the
25
+ * server, so `ctx.onBoot` would find the registry today too. The reconcile stays
26
+ * where it is all the same: the two placements are not equivalent in general (a
27
+ * root that composes without building a server runs one and not the other), and
28
+ * the failure mode if the accessor ever answered `undefined` again is silent —
29
+ * the reconciler's documented behaviour for an absent registry is to return
30
+ * `{installed: 0, skipped: 0, failed: 0}`, i.e. success. Every module's
31
+ * translation bundles would quietly stop being refreshed, with no exception and
32
+ * no warning, and the first symptom would be screens rendering raw i18n keys
33
+ * some time after somebody edits a JSON bundle. Moving it therefore needs
34
+ * evidence, not tidiness. `test/unit/_i18n/reconcile-timing.test.ts` pins the
35
+ * placement, because the harness cannot: since issue #158 `setupBackendServer`
36
+ * does pass the resolved registry and this reconcile does run there, but a
37
+ * composed server can only observe that it happened — never that it happened at
38
+ * plugin attach rather than during registration or boot.
39
+ *
40
+ * **The third `AdminUserService` goes away.** `composition.ts` built one
41
+ * specially for this module — `new AdminUserService(em)`, with no audit writer
42
+ * — while `admin_users` built its own with one, and the harness a third.
43
+ * Nothing was being lost: the only method reached from here is
44
+ * `setPreferredLanguage`, which is explicitly marked `command-coverage-ignore`
45
+ * as a personal UI setting rather than an audited mutation. But it is the same
46
+ * shape this wave has now removed six times — an optional constructor argument,
47
+ * silently omitted, defaulting the unsafe way — and resolving the service from
48
+ * the container means there is one of it, and it is the audited one.
49
+ *
50
+ * `deps.orm` was in the option interface and read nowhere in the factory body.
51
+ * It does not reappear here.
52
+ */
53
+ /**
54
+ * The slice of the lifecycle registry this module reads: every registered
55
+ * module's manifest and the path it was loaded from, which is all
56
+ * `reconcileBundles` needs to find `<module>/<bundlesDir>/<lang>.json`.
57
+ *
58
+ * Declared here rather than imported from `_lifecycle` (feature 075, Phase C).
59
+ * `lifecycleManifestRegistry` is a name a **composition root** supplies —
60
+ * which modules a deployment ships is a root's input, and `_lifecycle`'s own
61
+ * `LoadedManifestRegistry` additionally carries a dependency graph and each
62
+ * module's install hooks, none of which a bundle reconcile has any business
63
+ * seeing. Stating the demand is what keeps the two apart.
64
+ */
65
+ export interface I18nManifestRegistryView {
66
+ readonly modules: {
67
+ values(): Iterable<I18nReconcileEntry>;
68
+ };
69
+ }
70
+ /** Reads the lifecycle registry lazily; `undefined` until `_lifecycle` exists. */
71
+ export type LifecycleManifestRegistryAccessor = () => I18nManifestRegistryView | undefined;
72
+ export interface AdminI18nCradle {
73
+ readonly emFactory: () => EntityManager;
74
+ readonly requireAdmin: RequireAdminFactory;
75
+ readonly adminContextResolver: (req: FastifyRequest) => {
76
+ adminUserId: string;
77
+ };
78
+ readonly lifecycleManifestRegistry: LifecycleManifestRegistryAccessor;
79
+ readonly adminI18nService: I18nService;
80
+ readonly adminI18nReconciler: AdminI18nReconciler;
81
+ }
82
+ /** The lifecycle-orchestrator-shaped hook surface (feature 019 FR-016). */
83
+ export interface AdminI18nReconciler {
84
+ install(args: {
85
+ moduleId: string;
86
+ modulePath: string;
87
+ bundlesDir: string;
88
+ }): Promise<{
89
+ installed: string[];
90
+ }>;
91
+ remove(moduleId: string): Promise<{
92
+ removed: number;
93
+ }>;
94
+ /** Re-read every module's on-disk bundles without a restart (`i18n:reload`). */
95
+ reloadAll(): Promise<I18nReconcileResult>;
96
+ }
97
+ export declare function registerModule(ctx: ModuleContext): void;
98
+ /**
99
+ * The module's own entity classes, as one array — D-168.
100
+ *
101
+ * This is the shape the platform reads when the package is *installed*: the
102
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
103
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
104
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
105
+ * the package pass of `check-entity-tenant-classification`. A missing array is
106
+ * answered with `[]` — zero entities registered, no error anywhere.
107
+ *
108
+ * The class is **not** exported by name beside it (D-168): the ORM registers
109
+ * whichever copy this array carries, and a second, source-reached copy of a
110
+ * MikroORM entity is a lookup miss rather than a type error. A test that needs
111
+ * the runtime class takes it out of this array by name
112
+ * (`backend/test/helpers/package-entities.ts`).
113
+ */
114
+ export declare const entities: (typeof TranslationBundle)[];
115
+ /**
116
+ * The rest of this module's published surface, and why each name is on it.
117
+ *
118
+ * Every one of these was reached relatively from `backend/` before the move,
119
+ * and a relative reach into a package's `src` is the defect
120
+ * `check:singleton-identity` refuses: the composed platform loads this
121
+ * package's `dist`, so a second evaluation of the same file is a second copy of
122
+ * everything module-scoped in it. Publishing the names is what gives those
123
+ * callers a spelling that lands on the copy the platform holds.
124
+ *
125
+ * * `buildErrorTranslationTargets` and its shapes **stood here and are gone**
126
+ * (`specs/117-instance-bring-up/` FR-030). The routing map is now
127
+ * `@endora-commerce/platform`'s, at `kernel/i18n/error-translation.ts`,
128
+ * beside `request-language.ts` — the producer of the other
129
+ * `ErrorEnvelopeOptions` member a composition root injects.
130
+ *
131
+ * The line it moved across is worth stating here as well as there, because
132
+ * this is where a reader looks for it: *the routing is derived from
133
+ * manifests, the translation is a service*. `I18nService.translate` — what
134
+ * the envelope's `translateErrorMessage` closure calls — resolves a key
135
+ * against a bundle and is `_i18n`'s. The derivation translated nothing; it
136
+ * read `manifest.errorCodes` off the **resolved manifest set**, which is a
137
+ * composition-root input, and it had no consumer inside this package at
138
+ * all: this barrel re-exported it and nothing here called it. That is
139
+ * T040b's criterion 8, the test `absolutizePublicUrl` moved out of `email`
140
+ * under, met exactly.
141
+ *
142
+ * `ERROR_TRANSLATION_KEYS` and `composeErrorTranslationTargets` stood here
143
+ * too and were **deleted** in feature 090 Phase 4: the first was the prefix
144
+ * chain's static table, the second the transitional composition that laid
145
+ * the declarations over it so the migration could be delivered one module
146
+ * per merge request. All eighteen owners have declared, so there is nothing
147
+ * left to lay over.
148
+ * * `I18nService` — the resolver the reconciler, the CLI commands and the
149
+ * acceptance probe construct over an `EntityManager` of their own.
150
+ * * `MissingKeyLogger` — the resolver's collaborator, constructed the same way.
151
+ * * `loadModuleBundles` / `BundleLoadError` and their shapes — the filesystem
152
+ * reader every module's bundles arrive through.
153
+ * * `reconcileBundles` and its shapes — the boot pass itself, which the
154
+ * acceptance probe runs against a live instance.
155
+ * * `registerI18nAdminRoutes` / `I18nAdminDeps` — the HTTP surface, registered
156
+ * against a bare Fastify instance by its own contract test.
157
+ */
158
+ export { I18nService } from './services/i18n-service.js';
159
+ export { MissingKeyLogger } from './services/missing-key-logger.js';
160
+ export { BundleLoadError, loadModuleBundles, type BundleLoaderOptions, type LoadedBundles, } from './services/bundle-loader.js';
161
+ export { reconcileBundles, type I18nReconcileFailure, } from './services/bundle-reconciler.js';
162
+ export { registerI18nAdminRoutes, type I18nAdminDeps } from './routes.admin.js';
163
+ export type { I18nReconcileEntry, I18nReconcileResult };
164
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE;QAAE,MAAM,IAAI,QAAQ,CAAC,kBAAkB,CAAC,CAAA;KAAE,CAAC;CAC9D;AAED,kFAAkF;AAClF,MAAM,MAAM,iCAAiC,GAAG,MAC5C,wBAAwB,GACxB,SAAS,CAAC;AAEd,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,oBAAoB,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAChF,QAAQ,CAAC,yBAAyB,EAAE,iCAAiC,CAAC;IACtE,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAC;IACvC,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;CACnD;AAED,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,IAAI,EAAE;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACrC,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD,gFAAgF;IAChF,SAAS,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC3C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA6DvD;AAiBD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,8BAEpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,gBAAgB,EAChB,KAAK,oBAAoB,GAC1B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChF,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,CAAC"}