@rexezuge/tooling 0.0.0-stage → 1.0.1

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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +297 -2
  3. package/dist/eslint-boundaries.d.ts +75 -0
  4. package/dist/eslint-boundaries.d.ts.map +1 -0
  5. package/dist/eslint-boundaries.js +138 -0
  6. package/dist/eslint-boundaries.js.map +1 -0
  7. package/dist/eslint-rules.d.ts +41 -0
  8. package/dist/eslint-rules.d.ts.map +1 -0
  9. package/dist/eslint-rules.js +273 -0
  10. package/dist/eslint-rules.js.map +1 -0
  11. package/dist/eslint.d.ts +96 -0
  12. package/dist/eslint.d.ts.map +1 -0
  13. package/dist/eslint.js +189 -0
  14. package/dist/eslint.js.map +1 -0
  15. package/dist/functions/pages-proxy.d.ts +113 -0
  16. package/dist/functions/pages-proxy.d.ts.map +1 -0
  17. package/dist/functions/pages-proxy.js +131 -0
  18. package/dist/functions/pages-proxy.js.map +1 -0
  19. package/dist/index.d.ts +31 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +28 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/scripts/backup/d1-target.d.ts +35 -0
  24. package/dist/scripts/backup/d1-target.d.ts.map +1 -0
  25. package/dist/scripts/backup/d1-target.js +31 -0
  26. package/dist/scripts/backup/d1-target.js.map +1 -0
  27. package/dist/scripts/backup/destination-config.d.ts +61 -0
  28. package/dist/scripts/backup/destination-config.d.ts.map +1 -0
  29. package/dist/scripts/backup/destination-config.js +57 -0
  30. package/dist/scripts/backup/destination-config.js.map +1 -0
  31. package/dist/scripts/backup/encrypt-backup.d.ts +35 -0
  32. package/dist/scripts/backup/encrypt-backup.d.ts.map +1 -0
  33. package/dist/scripts/backup/encrypt-backup.js +98 -0
  34. package/dist/scripts/backup/encrypt-backup.js.map +1 -0
  35. package/dist/scripts/backup/naming.d.ts +44 -0
  36. package/dist/scripts/backup/naming.d.ts.map +1 -0
  37. package/dist/scripts/backup/naming.js +58 -0
  38. package/dist/scripts/backup/naming.js.map +1 -0
  39. package/dist/scripts/check-god-files.d.ts +164 -0
  40. package/dist/scripts/check-god-files.d.ts.map +1 -0
  41. package/dist/scripts/check-god-files.js +271 -0
  42. package/dist/scripts/check-god-files.js.map +1 -0
  43. package/dist/scripts/ensure-spa-shell-stub.d.ts +3 -0
  44. package/dist/scripts/ensure-spa-shell-stub.d.ts.map +1 -0
  45. package/dist/scripts/ensure-spa-shell-stub.js +30 -0
  46. package/dist/scripts/ensure-spa-shell-stub.js.map +1 -0
  47. package/dist/scripts/init-secrets.d.ts +63 -0
  48. package/dist/scripts/init-secrets.d.ts.map +1 -0
  49. package/dist/scripts/init-secrets.js +240 -0
  50. package/dist/scripts/init-secrets.js.map +1 -0
  51. package/dist/scripts/lib/cli-args.d.ts +78 -0
  52. package/dist/scripts/lib/cli-args.d.ts.map +1 -0
  53. package/dist/scripts/lib/cli-args.js +116 -0
  54. package/dist/scripts/lib/cli-args.js.map +1 -0
  55. package/dist/scripts/lib/github-actions.d.ts +26 -0
  56. package/dist/scripts/lib/github-actions.d.ts.map +1 -0
  57. package/dist/scripts/lib/github-actions.js +38 -0
  58. package/dist/scripts/lib/github-actions.js.map +1 -0
  59. package/dist/scripts/lib/wrangler-table.d.ts +46 -0
  60. package/dist/scripts/lib/wrangler-table.d.ts.map +1 -0
  61. package/dist/scripts/lib/wrangler-table.js +99 -0
  62. package/dist/scripts/lib/wrangler-table.js.map +1 -0
  63. package/dist/scripts/migrations-lock.d.ts +3 -0
  64. package/dist/scripts/migrations-lock.d.ts.map +1 -0
  65. package/dist/scripts/migrations-lock.js +46 -0
  66. package/dist/scripts/migrations-lock.js.map +1 -0
  67. package/dist/scripts/prepare-wrangler-config.d.ts +3 -0
  68. package/dist/scripts/prepare-wrangler-config.d.ts.map +1 -0
  69. package/dist/scripts/prepare-wrangler-config.js +50 -0
  70. package/dist/scripts/prepare-wrangler-config.js.map +1 -0
  71. package/dist/scripts/spa-shell.d.ts +41 -0
  72. package/dist/scripts/spa-shell.d.ts.map +1 -0
  73. package/dist/scripts/spa-shell.js +155 -0
  74. package/dist/scripts/spa-shell.js.map +1 -0
  75. package/dist/scripts/validate-locales.d.ts +26 -0
  76. package/dist/scripts/validate-locales.d.ts.map +1 -0
  77. package/dist/scripts/validate-locales.js +350 -0
  78. package/dist/scripts/validate-locales.js.map +1 -0
  79. package/dist/scripts/verify-migrations.d.ts +62 -0
  80. package/dist/scripts/verify-migrations.d.ts.map +1 -0
  81. package/dist/scripts/verify-migrations.js +302 -0
  82. package/dist/scripts/verify-migrations.js.map +1 -0
  83. package/dist/scripts/verify-spa-shell.d.ts +3 -0
  84. package/dist/scripts/verify-spa-shell.d.ts.map +1 -0
  85. package/dist/scripts/verify-spa-shell.js +53 -0
  86. package/dist/scripts/verify-spa-shell.js.map +1 -0
  87. package/dist/scripts/wrangler-config/cli.d.ts +22 -0
  88. package/dist/scripts/wrangler-config/cli.d.ts.map +1 -0
  89. package/dist/scripts/wrangler-config/cli.js +51 -0
  90. package/dist/scripts/wrangler-config/cli.js.map +1 -0
  91. package/dist/scripts/wrangler-config/patches.d.ts +51 -0
  92. package/dist/scripts/wrangler-config/patches.d.ts.map +1 -0
  93. package/dist/scripts/wrangler-config/patches.js +140 -0
  94. package/dist/scripts/wrangler-config/patches.js.map +1 -0
  95. package/dist/scripts/wrangler-config/resources.d.ts +70 -0
  96. package/dist/scripts/wrangler-config/resources.d.ts.map +1 -0
  97. package/dist/scripts/wrangler-config/resources.js +290 -0
  98. package/dist/scripts/wrangler-config/resources.js.map +1 -0
  99. package/dist/scripts/wrangler-config/types.d.ts +103 -0
  100. package/dist/scripts/wrangler-config/types.d.ts.map +1 -0
  101. package/dist/scripts/wrangler-config/types.js +49 -0
  102. package/dist/scripts/wrangler-config/types.js.map +1 -0
  103. package/dist/test/integration-migrations.d.ts +167 -0
  104. package/dist/test/integration-migrations.d.ts.map +1 -0
  105. package/dist/test/integration-migrations.js +171 -0
  106. package/dist/test/integration-migrations.js.map +1 -0
  107. package/dist/test/mocks/cloudflare-workers.d.ts +106 -0
  108. package/dist/test/mocks/cloudflare-workers.d.ts.map +1 -0
  109. package/dist/test/mocks/cloudflare-workers.js +90 -0
  110. package/dist/test/mocks/cloudflare-workers.js.map +1 -0
  111. package/dist/vite.d.ts +117 -0
  112. package/dist/vite.d.ts.map +1 -0
  113. package/dist/vite.js +125 -0
  114. package/dist/vite.js.map +1 -0
  115. package/dist/vitest-web.d.ts +73 -0
  116. package/dist/vitest-web.d.ts.map +1 -0
  117. package/dist/vitest-web.js +72 -0
  118. package/dist/vitest-web.js.map +1 -0
  119. package/dist/vitest.d.ts +92 -0
  120. package/dist/vitest.d.ts.map +1 -0
  121. package/dist/vitest.js +128 -0
  122. package/dist/vitest.js.map +1 -0
  123. package/package.json +58 -3
  124. package/src/eslint-boundaries.ts +169 -0
  125. package/src/eslint-rules.ts +276 -0
  126. package/src/eslint.test.ts +175 -0
  127. package/src/eslint.ts +257 -0
  128. package/src/functions/pages-proxy.test.ts +72 -0
  129. package/src/functions/pages-proxy.ts +187 -0
  130. package/src/github/actions/retry-step/action.yml +39 -0
  131. package/src/github/actions/setup-env/action.yml +20 -0
  132. package/src/github/dependabot.yml +30 -0
  133. package/src/github/workflows/backup-main.yml +46 -0
  134. package/src/github/workflows/continuous-deployment.yml +188 -0
  135. package/src/github/workflows/continuous-integration.yml +259 -0
  136. package/src/github/workflows/scheduled-version-update.yml +38 -0
  137. package/src/github/workflows/upstream-sync.yml +56 -0
  138. package/src/index.ts +42 -0
  139. package/src/scripts/backup/backup-rules.test.ts +105 -0
  140. package/src/scripts/backup/d1-target.ts +54 -0
  141. package/src/scripts/backup/destination-config.ts +92 -0
  142. package/src/scripts/backup/encrypt-backup.ts +107 -0
  143. package/src/scripts/backup/naming.ts +62 -0
  144. package/src/scripts/check-god-files.test.ts +131 -0
  145. package/src/scripts/check-god-files.ts +327 -0
  146. package/src/scripts/ensure-spa-shell-stub.ts +34 -0
  147. package/src/scripts/init-secrets.ts +265 -0
  148. package/src/scripts/lib/cli-args.ts +154 -0
  149. package/src/scripts/lib/github-actions.ts +41 -0
  150. package/src/scripts/lib/wrangler-table.ts +105 -0
  151. package/src/scripts/migrations-lock.ts +52 -0
  152. package/src/scripts/prepare-wrangler-config.ts +51 -0
  153. package/src/scripts/spa-shell.test.ts +91 -0
  154. package/src/scripts/spa-shell.ts +179 -0
  155. package/src/scripts/validate-locales.test.ts +89 -0
  156. package/src/scripts/validate-locales.ts +380 -0
  157. package/src/scripts/verify-migrations.test.ts +71 -0
  158. package/src/scripts/verify-migrations.ts +364 -0
  159. package/src/scripts/verify-spa-shell.ts +56 -0
  160. package/src/scripts/wrangler-config/cli.ts +51 -0
  161. package/src/scripts/wrangler-config/patches.ts +157 -0
  162. package/src/scripts/wrangler-config/resources.ts +330 -0
  163. package/src/scripts/wrangler-config/types.ts +113 -0
  164. package/src/test/integration-migrations.test.ts +169 -0
  165. package/src/test/integration-migrations.ts +267 -0
  166. package/src/test/mocks/cloudflare-workers.ts +115 -0
  167. package/src/vite.test.ts +83 -0
  168. package/src/vite.ts +202 -0
  169. package/src/vitest-web.ts +109 -0
  170. package/src/vitest.ts +185 -0
@@ -0,0 +1,350 @@
1
+ /**
2
+ * Validate that every locale bundle carries exactly the base locale's keys.
3
+ *
4
+ * Provenance: `scripts/validate_locales.{mjs,ts}` in Durable-DAV, Edge-Git and
5
+ * Mail-Meow, and Edge-Sonic's `scripts/i18n/locale-checks.ts` +
6
+ * `validate_locales.ts`, converged. All four copies agree on the contract:
7
+ * flattened dotted key paths, two-way parity (missing AND extra), no empty values,
8
+ * no stray placeholders.
9
+ *
10
+ * ### What the convergence added
11
+ *
12
+ * Edge-Sonic's is the only copy that checks what a value *is*. The rest compare
13
+ * bundle to bundle, so with one shipped language the whole per-tag body is skipped
14
+ * and the script reports `ALL OK` having checked nothing about the application — it
15
+ * could not see that `libraries.scanPausedRequests` was used and absent, because
16
+ * the string in the bundle did not exist to be wrong *about* anything. Hence
17
+ * `--call-sites`: every `t('key')` under `apps/web/src` must resolve.
18
+ *
19
+ * Key **order** drift against the base is a warning, not a failure: locale files
20
+ * are maintained by mirroring new keys into every file, and failing on order would
21
+ * turn an otherwise-correct translation update into a CI failure about formatting.
22
+ *
23
+ * ### Three on-disk shapes, one checker
24
+ *
25
+ * The family ships bundles in three shapes, and the shape is an argument rather
26
+ * than a fork of the script:
27
+ *
28
+ * - `auto` (default) — every layout below, tried in order, so a repo that has
29
+ * half-migrated is reported rather than silently skipped;
30
+ * - `dir` — `<locales-dir>/<tag>/translation.json`, the i18next layout used by
31
+ * seven repos;
32
+ * - `file` — `<locales-dir>/<tag>.json`, one flat file per tag;
33
+ * - `module` — `<locales-dir>/<tag>.ts` exporting an object literal, which is the
34
+ * shape `@rexezuge/i18n` uses for the backend string bundles. The literal is
35
+ * extracted and normalised here, so the same gate covers the backend catalogs
36
+ * the kit ships and the SPA bundles the repos do.
37
+ */
38
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
39
+ import { pathToFileURL } from 'node:url';
40
+ import { join } from 'node:path';
41
+ /** Every dotted key path in a bundle, depth first. */
42
+ function flatten(value, prefix = '') {
43
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
44
+ return prefix ? [prefix] : [];
45
+ }
46
+ const entries = Object.entries(value);
47
+ if (entries.length === 0)
48
+ return prefix ? [prefix] : [];
49
+ return entries.flatMap(([key, child]) => flatten(child, prefix ? `${prefix}.${key}` : key));
50
+ }
51
+ /**
52
+ * Read a bundle as a value, in whichever shape it is written.
53
+ *
54
+ * `module` is the shape `@rexezuge/i18n` ships: `const enStrings: BackendStrings =
55
+ * { common: { unauthorized: '…' } };`. Extracting the literal rather than importing
56
+ * the module is the point — a validator that imported the bundle would typecheck it
57
+ * against the kit's own types, and therefore agree with whatever it was given.
58
+ */
59
+ function readBundle(path) {
60
+ const source = readFileSync(path, 'utf8');
61
+ // Decided by the file's extension, not by the caller's `shape`: under `auto` a
62
+ // `.ts` bundle is found, and reading it as JSON would report a parse error for a
63
+ // file that is perfectly well-formed.
64
+ return path.endsWith('.ts') ? parseObjectLiteral(source, path) : JSON.parse(source);
65
+ }
66
+ /**
67
+ * The first balanced object literal in a TypeScript module, as a value.
68
+ *
69
+ * Brace-matched outside strings and comments, and started at the **first
70
+ * assignment** rather than at the first brace — see `objectLiteralStart` for why the
71
+ * difference is the whole rule. Single-quoted strings are normalised to
72
+ * double-quoted JSON, bare keys are quoted, and trailing commas are tolerated: that
73
+ * is all the shape this needs, and a translator file that needs more is not a
74
+ * translator file.
75
+ */
76
+ /**
77
+ * Where the object literal begins: the first brace **after the first assignment**.
78
+ *
79
+ * The assignment matters. `const enStrings: BackendStrings = { ... }` is preceded by
80
+ * an import line whose own braces open earlier in the file, so a plain
81
+ * `indexOf('{')` returns the import's pair and the "literal" extracted is
82
+ * `{ BackendStrings }` — an empty object that parses cleanly and then reports the
83
+ * base bundle as having no keys. Finding the assignment first skips every brace that
84
+ * is not the value being bound to it.
85
+ */
86
+ function objectLiteralStart(source) {
87
+ let quote = '';
88
+ let inLineComment = false;
89
+ let inBlockComment = false;
90
+ for (let index = 0; index < source.length; index += 1) {
91
+ const char = source[index];
92
+ const next = source[index + 1];
93
+ if (inLineComment) {
94
+ if (char === '\n')
95
+ inLineComment = false;
96
+ continue;
97
+ }
98
+ if (inBlockComment) {
99
+ if (char === '*' && next === '/') {
100
+ inBlockComment = false;
101
+ index += 1;
102
+ }
103
+ continue;
104
+ }
105
+ if (quote !== '') {
106
+ if (char === '\\') {
107
+ index += 1;
108
+ continue;
109
+ }
110
+ if (char === quote)
111
+ quote = '';
112
+ continue;
113
+ }
114
+ if (char === '-' && next === '-') {
115
+ inLineComment = true;
116
+ continue;
117
+ }
118
+ if (char === '/' && next === '*') {
119
+ inBlockComment = true;
120
+ continue;
121
+ }
122
+ if (char === "'" || char === '"' || char === '`') {
123
+ quote = char;
124
+ continue;
125
+ }
126
+ // The assignment, and not a comparison or an arrow: `=>` and `==` both contain an
127
+ // `=`, and neither is where a bundle's object literal is attached.
128
+ const previous = source[index - 1];
129
+ if (char === '=' && next !== '=' && next !== '>' && previous !== '=' && previous !== '!' && previous !== '<' && previous !== '>') {
130
+ const brace = source.indexOf('{', index);
131
+ return brace === -1 ? -1 : brace;
132
+ }
133
+ }
134
+ return -1;
135
+ }
136
+ function parseObjectLiteral(source, path) {
137
+ const start = objectLiteralStart(source);
138
+ if (start === -1) {
139
+ throw new Error(`${path} has no object literal`);
140
+ }
141
+ let depth = 0;
142
+ let end = -1;
143
+ let quote = '';
144
+ let inLineComment = false;
145
+ let inBlockComment = false;
146
+ for (let index = start; index < source.length; index += 1) {
147
+ const char = source[index];
148
+ const next = source[index + 1];
149
+ if (inLineComment) {
150
+ if (char === '\n')
151
+ inLineComment = false;
152
+ continue;
153
+ }
154
+ if (inBlockComment) {
155
+ if (char === '*' && next === '/') {
156
+ inBlockComment = false;
157
+ index += 1;
158
+ }
159
+ continue;
160
+ }
161
+ if (quote !== '') {
162
+ // An escape consumes the next character, so `\'` does not close the string.
163
+ if (char === '\\') {
164
+ index += 1;
165
+ continue;
166
+ }
167
+ if (char === quote)
168
+ quote = '';
169
+ continue;
170
+ }
171
+ if (char === '-' && next === '-') {
172
+ inLineComment = true;
173
+ continue;
174
+ }
175
+ if (char === '/' && next === '*') {
176
+ inBlockComment = true;
177
+ continue;
178
+ }
179
+ if (char === "'" || char === '"' || char === '`') {
180
+ quote = char;
181
+ continue;
182
+ }
183
+ if (char === '{') {
184
+ depth += 1;
185
+ }
186
+ else if (char === '}') {
187
+ depth -= 1;
188
+ if (depth === 0) {
189
+ end = index;
190
+ break;
191
+ }
192
+ }
193
+ }
194
+ if (end === -1) {
195
+ throw new Error(`${path} has an unterminated object literal`);
196
+ }
197
+ const literal = source.slice(start, end + 1);
198
+ // Three normalisations, each of which a JSON parser needs and a TS literal does not:
199
+ // single-quoted strings become double-quoted (applied only to string literals, so a
200
+ // value containing an apostrophe keeps it), bare keys get quoted, and trailing
201
+ // commas are dropped. A translator file that needs more than that is not a
202
+ // translator file.
203
+ const doubleQuoted = literal.replaceAll(/'((?:[^'\\]|\\.)*)'/g, (_match, body) => JSON.stringify(unescapeLiteral(body)));
204
+ const quotedKeys = doubleQuoted.replaceAll(/([{,]\s*)([A-Za-z_$][\w$]*)(\s*:)/g, '$1"$2"$3');
205
+ const withoutTrailingCommas = quotedKeys.replaceAll(/,(\s*[}\]])/g, '$1');
206
+ try {
207
+ return JSON.parse(withoutTrailingCommas);
208
+ }
209
+ catch (error) {
210
+ throw new Error(`${path} object literal could not be parsed: ${error instanceof Error ? error.message : String(error)}`);
211
+ }
212
+ }
213
+ /** The escapes a translator file actually uses; anything else is passed through. */
214
+ function unescapeLiteral(body) {
215
+ return body
216
+ .replaceAll(String.raw `\'`, "'")
217
+ .replaceAll(String.raw `\"`, '"')
218
+ .replaceAll(String.raw `\n`, '\n')
219
+ .replaceAll(String.raw `\t`, '\t')
220
+ .replaceAll(String.raw `\\`, '\\');
221
+ }
222
+ /** The extensions a flat layout uses, keyed by shape. */
223
+ const FLAT_EXTENSION = { file: 'json', module: 'ts' };
224
+ /** The layouts `shape` covers. `auto` covers all three, in the order they are tried. */
225
+ function layouts(shape) {
226
+ return shape === 'auto' ? ['dir', 'file', 'module'] : [shape];
227
+ }
228
+ /** The tags on disk, in whichever layouts `shape` covers. */
229
+ function tagsOnDisk(localesDir, shape) {
230
+ const tags = [];
231
+ if (layouts(shape).includes('dir')) {
232
+ tags.push(...readdirSync(localesDir, { withFileTypes: true })
233
+ .filter((entry) => entry.isDirectory())
234
+ .map((entry) => entry.name));
235
+ }
236
+ for (const layout of layouts(shape)) {
237
+ if (layout === 'dir')
238
+ continue;
239
+ const extension = FLAT_EXTENSION[layout];
240
+ tags.push(...readdirSync(localesDir)
241
+ .filter((name) => name.endsWith(`.${extension}`))
242
+ .map((name) => name.slice(0, -extension.length - 1)));
243
+ }
244
+ return tags;
245
+ }
246
+ /**
247
+ * The file a tag's bundle lives in, or `undefined` when it is not there.
248
+ *
249
+ * Every candidate is tried rather than only the one `shape` names, because a repo
250
+ * with a half-migrated tree is a repo whose missing half should be reported. A
251
+ * checker that only looked where it was told would read a bundle that exists as
252
+ * absent, and a missing bundle is the defect this script exists to find.
253
+ */
254
+ function bundleFor(localesDir, locale, shape) {
255
+ const candidates = [];
256
+ if (layouts(shape).includes('dir')) {
257
+ candidates.push(join(localesDir, locale, 'translation.json'));
258
+ }
259
+ for (const layout of layouts(shape)) {
260
+ if (layout === 'dir')
261
+ continue;
262
+ candidates.push(join(localesDir, `${locale}.${FLAT_EXTENSION[layout]}`));
263
+ }
264
+ return candidates.find((candidate) => existsSync(candidate));
265
+ }
266
+ /**
267
+ * Validate a directory of locale bundles against `baseLocale`'s key set.
268
+ *
269
+ * Two-way parity: a key the base has and a bundle lacks renders the fallback, and
270
+ * a key a bundle has that the base lacks is dead weight nothing can translate from.
271
+ */
272
+ function validateLocales(localesDir, baseLocale = 'en', shape = 'auto') {
273
+ const problems = [];
274
+ if (!existsSync(localesDir)) {
275
+ problems.push({ locale: baseLocale, problem: `${localesDir} does not exist` });
276
+ return { locales: [], keys: 0, problems };
277
+ }
278
+ const baseFile = bundleFor(localesDir, baseLocale, shape);
279
+ if (baseFile === undefined) {
280
+ problems.push({ locale: baseLocale, problem: `no bundle found for the base locale (expected ${baseLocale}/translation.json)` });
281
+ return { locales: [], keys: 0, problems };
282
+ }
283
+ let baseKeys;
284
+ try {
285
+ baseKeys = new Set(flatten(readBundle(baseFile)));
286
+ }
287
+ catch (error) {
288
+ problems.push({ locale: baseLocale, problem: `${baseFile}: ${error instanceof Error ? error.message : String(error)}` });
289
+ return { locales: [], keys: 0, problems };
290
+ }
291
+ if (baseKeys.size === 0) {
292
+ // An empty base is the one defect that reaches the most users: it is the fallback
293
+ // for every missing key, so an empty value here is indistinguishable from a
294
+ // missing bundle at runtime.
295
+ problems.push({ locale: baseLocale, problem: `${baseFile} has no keys — the base bundle is empty or unreadable` });
296
+ return { locales: [baseLocale], keys: 0, problems };
297
+ }
298
+ const locales = [...new Set([...tagsOnDisk(localesDir, shape), baseLocale])].sort();
299
+ for (const locale of locales) {
300
+ const file = bundleFor(localesDir, locale, shape);
301
+ if (file === undefined) {
302
+ problems.push({ locale, problem: 'no bundle found' });
303
+ continue;
304
+ }
305
+ let keys;
306
+ try {
307
+ keys = new Set(flatten(readBundle(file)));
308
+ }
309
+ catch (error) {
310
+ problems.push({ locale, problem: `${file}: ${error instanceof Error ? error.message : String(error)}` });
311
+ continue;
312
+ }
313
+ for (const key of baseKeys) {
314
+ if (!keys.has(key))
315
+ problems.push({ locale, problem: `missing key: ${key}` });
316
+ }
317
+ for (const key of keys) {
318
+ if (!baseKeys.has(key))
319
+ problems.push({ locale, problem: `extra key not in ${baseLocale}: ${key}` });
320
+ }
321
+ }
322
+ return { locales, keys: baseKeys.size, problems };
323
+ }
324
+ /** CLI entry: `validateLocalesCli(['[locales-dir]?', '--shape=auto|dir|file|module'?, '--base=<tag>'?])`. */
325
+ function validateLocalesCli(argv = process.argv.slice(2)) {
326
+ const dir = argv.find((arg) => !arg.startsWith('--')) ?? 'apps/web/src/locales';
327
+ const shape = (argv.find((arg) => arg.startsWith('--shape='))?.slice('--shape='.length) ?? 'auto');
328
+ const base = argv.find((arg) => arg.startsWith('--base='))?.slice('--base='.length) ?? 'en';
329
+ const report = validateLocales(dir, base, shape);
330
+ if (report.problems.length > 0) {
331
+ console.error(`Locale validation failed for ${dir} (${report.problems.length} problem(s)):`);
332
+ for (const problem of report.problems)
333
+ console.error(` - ${problem.locale}: ${problem.problem}`);
334
+ return 1;
335
+ }
336
+ console.log(`Locale validation passed (${report.locales.length} locales, ${report.keys} keys each, shape ${shape}).`);
337
+ return 0;
338
+ }
339
+ export { parseObjectLiteral, validateLocales, validateLocalesCli };
340
+ // Run only when this file is the entrypoint.
341
+ //
342
+ // The rules above are pure and the tests import them directly, so a module-level
343
+ // call would shell out (and exit) during a test run. The URL comparison rather than
344
+ // an unconditional call is the same guard Durable-DAV's init-secrets.ts uses, and
345
+ // it is what keeps the rule modules importable — which is the whole reason they are
346
+ // separate modules in the first place.
347
+ if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
348
+ process.exit(validateLocalesCli(process.argv.slice(2)));
349
+ }
350
+ //# sourceMappingURL=validate-locales.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-locales.js","sourceRoot":"","sources":["../../src/scripts/validate-locales.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /** One migration as the lock records it. */
2
+ interface MigrationEntry {
3
+ name: string;
4
+ sha256: string;
5
+ }
6
+ /** The parsed `migrations/migrations.lock.json`. */
7
+ interface MigrationLockFile {
8
+ version: 1;
9
+ /**
10
+ * The squashed-baseline filename, when the repo has squashed.
11
+ *
12
+ * Everything at or before it is exempt from the `edited` and `orphan` checks,
13
+ * because that is exactly the set a squash rewrites or absorbs.
14
+ */
15
+ baseline?: string;
16
+ migrations: MigrationEntry[];
17
+ }
18
+ /** One failure, phrased for the operator. */
19
+ interface MigrationProblem {
20
+ problem: string;
21
+ }
22
+ /** The outcome of one run. */
23
+ interface MigrationReport {
24
+ /** Migration filenames, in apply order. */
25
+ files: string[];
26
+ problems: MigrationProblem[];
27
+ /** The lock as it reads after the run. */
28
+ lock: MigrationLockFile | null;
29
+ }
30
+ /**
31
+ * The lock's filename, beside the `.sql` files.
32
+ *
33
+ * Named `migrations.lock.json` rather than `migrations.json` so it cannot be
34
+ * mistaken for a migration: wrangler applies `*.sql` only, and a lock named like
35
+ * one would be a deploy-time mystery.
36
+ */
37
+ declare const LOCK_NAME = "migrations.lock.json";
38
+ /** The lock's format version. Bumped only on an incompatible shape change. */
39
+ declare const LOCK_VERSION = 1;
40
+ /**
41
+ * The highest-numbered `NNNN_squash.sql` on disk, or `undefined` when the repo
42
+ * has not squashed — in which case every migration is incremental and therefore
43
+ * every one of them is immutable.
44
+ */
45
+ export declare function baselineOf(files: readonly string[]): string | undefined;
46
+ /**
47
+ * Read the migration set and validate its shape without consulting the lock.
48
+ *
49
+ * The lock is read here too, so one walk answers both "what is on disk" and "what
50
+ * is recorded"; a lock that is merely *absent* is reported by `verifyMigrations`
51
+ * rather than here, because that is the bootstrap case rather than a defect.
52
+ */
53
+ declare function inspectMigrations(migrationsDir: string): MigrationReport;
54
+ /** Verify (or with `write: true`, record) the lock against the files. */
55
+ declare function verifyMigrations(migrationsDir: string, options?: {
56
+ write?: boolean;
57
+ }): MigrationReport;
58
+ /** CLI entry: `verifyMigrationsCli(['[migrations-dir]?', '--write'?'])`. */
59
+ declare function verifyMigrationsCli(argv?: readonly string[]): number;
60
+ export { inspectMigrations, verifyMigrations, verifyMigrationsCli, LOCK_NAME, LOCK_VERSION };
61
+ export type { MigrationEntry, MigrationLockFile, MigrationProblem, MigrationReport };
62
+ //# sourceMappingURL=verify-migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-migrations.d.ts","sourceRoot":"","sources":["../../src/scripts/verify-migrations.ts"],"names":[],"mappings":"AAiEA,4CAA4C;AAC5C,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,UAAU,iBAAiB;IACzB,OAAO,EAAE,CAAC,CAAC;IACX;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,6CAA6C;AAC7C,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,8BAA8B;AAC9B,UAAU,eAAe;IACvB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,0CAA0C;IAC1C,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAChC;AAED;;;;;;GAMG;AACH,QAAA,MAAM,SAAS,yBAAyB,CAAC;AAQzC,8EAA8E;AAC9E,QAAA,MAAM,YAAY,IAAI,CAAC;AAMvB;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAQvE;AAOD;;;;;;GAMG;AACH,iBAAS,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,eAAe,CA6DjE;AAuDD,yEAAyE;AACzE,iBAAS,gBAAgB,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,eAAe,CAmEnG;AAED,4EAA4E;AAC5E,iBAAS,mBAAmB,CAAC,IAAI,GAAE,SAAS,MAAM,EAA0B,GAAG,MAAM,CAepF;AAED,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAC7F,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC"}