@burdenoff/fe-libs 2026.1021.8 → 2026.1021.9

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.
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Shared types for the fleet i18n toolkit (`burdenoff-i18n`).
3
+ *
4
+ * The bundle shape is the one every app shell emits as `/i18n-bundle.json` and
5
+ * global-i18n-svc's `seed:translations` reads (gitops `--bundle-url`): one
6
+ * entry per key, with its value per locale. Everything here is the published
7
+ * contract, so a shell's own builder and this toolkit agree on what a row is.
8
+ */
9
+
10
+ /** One key of an emitted bundle: its value per locale code. */
11
+ export interface BundleKey {
12
+ key: string;
13
+ namespace?: string;
14
+ values: Readonly<Record<string, string>>;
15
+ }
16
+
17
+ /** The emitted `/i18n-bundle.json`. Only `keys` is read; the header is passed through. */
18
+ export interface I18nBundle {
19
+ $schema?: string;
20
+ product?: { name?: string; displayName?: string };
21
+ languages?: ReadonlyArray<{ code: string }>;
22
+ keys: readonly BundleKey[];
23
+ }
24
+
25
+ /** One seeded row: `[key, locale, value]`. */
26
+ export type BundleRow = readonly [key: string, locale: string, value: string];
27
+
28
+ /**
29
+ * How row and key lines are ordered before hashing.
30
+ *
31
+ * - `utf8` sorts by UTF-8 bytes (Buffer.compare), which is Unicode code-point
32
+ * order. FluidGrids' served-bundle fixture was generated this way.
33
+ * - `utf16` sorts by JavaScript's default string comparison (UTF-16 code
34
+ * units). VibeControls' seed-plan fixtures were generated this way.
35
+ *
36
+ * The two orders agree unless a line contains a character outside the Basic
37
+ * Multilingual Plane (an emoji, for example), so a digest names its order.
38
+ */
39
+ export type DigestOrder = 'utf8' | 'utf16';
40
+
41
+ /** Counts and set digests of a bundle: the served-bundle contract. */
42
+ export interface BundleDigest {
43
+ keys: number;
44
+ rows: number;
45
+ order: DigestOrder;
46
+ /** sha256 of the keys, one per line, sorted in `order`. */
47
+ keySetSha256: string;
48
+ /** sha256 of `JSON.stringify([key, locale, value])` lines sorted in `order`. */
49
+ rowSetSha256: string;
50
+ rowsByLocale: Record<string, number>;
51
+ }
52
+
53
+ /** A row the proposed bundle changes relative to the base. */
54
+ export interface RowChange {
55
+ key: string;
56
+ locale: string;
57
+ before: string | null;
58
+ after: string | null;
59
+ }
60
+
61
+ /** What seeding `proposed` over `base` would do, row by row. */
62
+ export interface BundleDiff {
63
+ base: { keys: number; rows: number };
64
+ proposed: { keys: number; rows: number };
65
+ inserts: RowChange[];
66
+ updates: RowChange[];
67
+ deletions: RowChange[];
68
+ newKeys: string[];
69
+ removedKeys: string[];
70
+ insertsByLocale: Record<string, number>;
71
+ }
72
+
73
+ /** A key the shipped (compiled) code requests, with where it was found. */
74
+ export interface ShippedKey {
75
+ pkg: string;
76
+ fallback: string;
77
+ }
78
+
79
+ /** What a shell's adapter returns when it builds its bundle in-process. */
80
+ export interface AdapterBuild {
81
+ bundle: I18nBundle;
82
+ /**
83
+ * Keys the builder leaves out ON PURPOSE, with the reason. `verify-shipped`
84
+ * lists them and does not fail on them.
85
+ */
86
+ heldBack?: ReadonlyMap<string, string> | Readonly<Record<string, string>>;
87
+ }
88
+
89
+ /**
90
+ * The shell-specific half of the toolkit. A shell points `adapter` in its
91
+ * `i18n.config.json` at a module exporting these (named exports or a default
92
+ * object). Both members are optional; a shell without an adapter can point
93
+ * `bundleFile` at its emitted bundle instead.
94
+ */
95
+ export interface I18nAdapter {
96
+ /** Build the bundle exactly as the shell's build does. */
97
+ buildBundle?: () => Promise<AdapterBuild> | AdapterBuild;
98
+ /**
99
+ * Keys the shipped app requests that a scan of compiled `tr(key, fallback)`
100
+ * pairs cannot see (runtime-prefixed assistant keys, for example):
101
+ * key -> { source, fallback }.
102
+ */
103
+ requestedKeys?: () =>
104
+ | Promise<Readonly<Record<string, ShippedKey>>>
105
+ | Readonly<Record<string, ShippedKey>>;
106
+ }
107
+
108
+ /** `i18n.config.json`, as written by a shell. */
109
+ export interface I18nToolsConfig {
110
+ /** Product slug, used in messages only. */
111
+ product: string;
112
+ /** Where production serves the bundle, e.g. `https://app.vibecontrols.com/i18n-bundle.json`. */
113
+ servedUrl?: string;
114
+ /** A built bundle on disk, relative to the config file (e.g. `dist/i18n-bundle.json`). */
115
+ bundleFile?: string;
116
+ /** Module implementing `I18nAdapter`, relative to the config file. */
117
+ adapter?: string;
118
+ shipped?: {
119
+ /** npm scope whose installed packages are scanned. Default `@burdenoff`. */
120
+ scope?: string;
121
+ /** Package names (without scope) to scan; a trailing `*` matches a prefix. Default `["microfe-*"]`. */
122
+ packages?: string[];
123
+ };
124
+ /** Whether the shell's fe-libs reads a branch's `_` slot (fe-libs >= 2026.919.1). Default true. */
125
+ readsLeafSlot?: boolean;
126
+ /** Line order for digests. Default `utf8`. */
127
+ digestOrder?: DigestOrder;
128
+ }
129
+
130
+ /** The config after defaults, with paths made absolute. */
131
+ export interface ResolvedI18nToolsConfig {
132
+ product: string;
133
+ configDir: string;
134
+ servedUrl?: string;
135
+ bundleFile?: string;
136
+ adapter?: string;
137
+ shipped: { scope: string; packages: string[] };
138
+ readsLeafSlot: boolean;
139
+ digestOrder: DigestOrder;
140
+ }