@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 @@
1
+ {"version":3,"file":"github-actions.js","sourceRoot":"","sources":["../../../src/scripts/lib/github-actions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAa;IACnD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;IACT,CAAC;IACD,cAAc,CAAC,UAAU,EAAE,GAAG,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC;AACnD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,OAAO,IAAI,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Parsers for the human-readable tables `wrangler` prints.
3
+ *
4
+ * Provenance: ported from Edge-Sonic's `scripts/lib/wrangler-table.ts`, which was
5
+ * factored out of two scripts that had each grown their own copy.
6
+ *
7
+ * `wrangler secrets-store …` renders through `cli-table3`, which draws a
8
+ * box-drawing frame and separates cells with U+2502 (`│`) padded by exactly one
9
+ * space. Columns are therefore *not* space-aligned, and a parser written for
10
+ * aligned output silently matches nothing — which reads as "the resource is
11
+ * absent" and makes provisioning create a duplicate.
12
+ *
13
+ * ### Why membership is exact and never a substring
14
+ *
15
+ * Durable-DAV's `init-secrets.ts` used `output.includes(secretName)`, and that is
16
+ * the defect this port replaces: a store holding both
17
+ * `durable-dav-replication-encryption-key` and a `…-key-backup` would satisfy a
18
+ * substring search for the first, so the real secret was reported as already
19
+ * present and never created. A substring match is not a membership test.
20
+ */
21
+ /**
22
+ * Extracts the data rows of a `wrangler` table, dropping the frame, the header,
23
+ * and any surrounding log lines.
24
+ *
25
+ * Rows are returned in print order with cells trimmed and empty cells removed,
26
+ * so a blank optional column (`Comment`, typically unset) does not shift the
27
+ * columns a caller indexes into.
28
+ */
29
+ export declare function parseWranglerTableRows(output: string): string[][];
30
+ /**
31
+ * The exact names in the first column of a `wrangler secrets-store secret list`
32
+ * table.
33
+ *
34
+ * Exact values rather than a substring search — see the header.
35
+ */
36
+ export declare function parseWranglerNames(output: string): Set<string>;
37
+ /**
38
+ * True when a failed `secrets-store secret list` means the store is empty
39
+ * rather than the listing having failed.
40
+ *
41
+ * Callers must treat this as a confirmed absence; every other failure has to
42
+ * stay indistinguishable from "unknown", or a transient CLI error would be read
43
+ * as permission to create a secret that already holds a live value.
44
+ */
45
+ export declare function isEmptySecretsStoreListing(error: unknown): boolean;
46
+ //# sourceMappingURL=wrangler-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrangler-table.d.ts","sourceRoot":"","sources":["../../../src/scripts/lib/wrangler-table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAiBH;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAyBjE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAO9D;AASD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGlE"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Parsers for the human-readable tables `wrangler` prints.
3
+ *
4
+ * Provenance: ported from Edge-Sonic's `scripts/lib/wrangler-table.ts`, which was
5
+ * factored out of two scripts that had each grown their own copy.
6
+ *
7
+ * `wrangler secrets-store …` renders through `cli-table3`, which draws a
8
+ * box-drawing frame and separates cells with U+2502 (`│`) padded by exactly one
9
+ * space. Columns are therefore *not* space-aligned, and a parser written for
10
+ * aligned output silently matches nothing — which reads as "the resource is
11
+ * absent" and makes provisioning create a duplicate.
12
+ *
13
+ * ### Why membership is exact and never a substring
14
+ *
15
+ * Durable-DAV's `init-secrets.ts` used `output.includes(secretName)`, and that is
16
+ * the defect this port replaces: a store holding both
17
+ * `durable-dav-replication-encryption-key` and a `…-key-backup` would satisfy a
18
+ * substring search for the first, so the real secret was reported as already
19
+ * present and never created. A substring match is not a membership test.
20
+ */
21
+ /**
22
+ * Cell separator emitted by `cli-table3`.
23
+ */
24
+ const COLUMN_SEPARATOR = String.fromCodePoint(0x25_02);
25
+ /**
26
+ * Horizontal rule drawn by `cli-table3`; a row made of these is not data.
27
+ */
28
+ const RULE_CHARACTER = String.fromCodePoint(0x25_00);
29
+ /**
30
+ * Header row of every `wrangler … list` table.
31
+ */
32
+ const HEADER_FIRST_CELL = 'Name';
33
+ /**
34
+ * Extracts the data rows of a `wrangler` table, dropping the frame, the header,
35
+ * and any surrounding log lines.
36
+ *
37
+ * Rows are returned in print order with cells trimmed and empty cells removed,
38
+ * so a blank optional column (`Comment`, typically unset) does not shift the
39
+ * columns a caller indexes into.
40
+ */
41
+ export function parseWranglerTableRows(output) {
42
+ const rows = [];
43
+ for (const line of output.split('\n')) {
44
+ // The frame rows use U+253C (`┼`) at the joints rather than U+2502, so they
45
+ // are already excluded here; the rule-character guard below additionally
46
+ // covers borders that happen to contain a separator.
47
+ if (!line.includes(COLUMN_SEPARATOR)) {
48
+ continue;
49
+ }
50
+ const cells = line
51
+ .split(COLUMN_SEPARATOR)
52
+ .map((cell) => cell.trim())
53
+ .filter(Boolean);
54
+ const firstCell = cells[0];
55
+ if (firstCell === undefined || cells.length < 2) {
56
+ continue;
57
+ }
58
+ // The header row and the horizontal rules are frame, not data.
59
+ if (firstCell === HEADER_FIRST_CELL || firstCell.includes(RULE_CHARACTER)) {
60
+ continue;
61
+ }
62
+ rows.push(cells);
63
+ }
64
+ return rows;
65
+ }
66
+ /**
67
+ * The exact names in the first column of a `wrangler secrets-store secret list`
68
+ * table.
69
+ *
70
+ * Exact values rather than a substring search — see the header.
71
+ */
72
+ export function parseWranglerNames(output) {
73
+ const names = new Set();
74
+ for (const row of parseWranglerTableRows(output)) {
75
+ const name = row[0];
76
+ if (name !== undefined)
77
+ names.add(name);
78
+ }
79
+ return names;
80
+ }
81
+ /**
82
+ * wrangler exits non-zero when a Secrets Store holds no secrets
83
+ * (`secrets-store secret list` throws this `FatalError`), which is
84
+ * indistinguishable by exit code from a genuine failure.
85
+ */
86
+ const EMPTY_STORE_MESSAGE = 'List request returned no secrets';
87
+ /**
88
+ * True when a failed `secrets-store secret list` means the store is empty
89
+ * rather than the listing having failed.
90
+ *
91
+ * Callers must treat this as a confirmed absence; every other failure has to
92
+ * stay indistinguishable from "unknown", or a transient CLI error would be read
93
+ * as permission to create a secret that already holds a live value.
94
+ */
95
+ export function isEmptySecretsStoreListing(error) {
96
+ const message = error instanceof Error ? error.message : String(error);
97
+ return message.includes(EMPTY_STORE_MESSAGE);
98
+ }
99
+ //# sourceMappingURL=wrangler-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrangler-table.js","sourceRoot":"","sources":["../../../src/scripts/lib/wrangler-table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;GAEG;AACH,MAAM,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAEvD;;GAEG;AACH,MAAM,cAAc,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAErD;;GAEG;AACH,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,4EAA4E;QAC5E,yEAAyE;QACzE,qDAAqD;QACrD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACrC,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAa,IAAI;aACzB,KAAK,CAAC,gBAAgB,CAAC;aACvB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;aAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;QACnB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,SAAS;QACX,CAAC;QACD,+DAA+D;QAC/D,IAAI,SAAS,KAAK,iBAAiB,IAAI,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1E,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,sBAAsB,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,IAAI,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,kCAAkC,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACvD,MAAM,OAAO,GAAW,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/E,OAAO,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env tsx
2
+ export {};
3
+ //# sourceMappingURL=migrations-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations-lock.d.ts","sourceRoot":"","sources":["../../src/scripts/migrations-lock.ts"],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Record newly-added migration files in `migrations/migrations.lock.json`.
4
+ *
5
+ * Provenance: Edge-Sonic's `scripts/migrations/verify-migrations.ts --write`,
6
+ * which is the only repo that splits the two operations into two commands. The
7
+ * other repos have one script with a flag; the split is kept here because the two
8
+ * calls have different audiences and different blast radii — `verify-migrations`
9
+ * runs in CI and changes nothing, while this one is an operator action that writes
10
+ * a file into version control.
11
+ *
12
+ * The rules live once, in `verify-migrations.ts`, so the two entrypoints cannot
13
+ * disagree. `--write` is add-only: it records unlocked files, refreshes the
14
+ * baseline's own entry, and drops entries the baseline absorbed. It never touches
15
+ * another existing entry, so an incremental migration that has drifted still
16
+ * fails afterwards — re-baselining one means deleting its entry by hand, which
17
+ * shows up in the diff.
18
+ *
19
+ * There is deliberately no `--force`, because a write that could bless a drift
20
+ * without reading the drift is the operator blessing their own edit, and they are
21
+ * by definition the one making it.
22
+ *
23
+ * Usage, from the repo root:
24
+ *
25
+ * ```bash
26
+ * pnpm exec tsx scripts/migrations-lock.ts # ./migrations
27
+ * pnpm exec tsx scripts/migrations-lock.ts path/to/migrations
28
+ * ```
29
+ */
30
+ import path from 'node:path';
31
+ import { fileURLToPath } from 'node:url';
32
+ import { LOCK_NAME, verifyMigrations } from './verify-migrations';
33
+ const REPOSITORY_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
34
+ const migrationsDir = process.argv[2] === undefined ? path.join(REPOSITORY_ROOT, 'migrations') : path.resolve(process.argv[2]);
35
+ const report = verifyMigrations(migrationsDir, { write: true });
36
+ for (const problem of report.problems) {
37
+ console.error(`FAIL: ${problem.problem}`);
38
+ }
39
+ console.log(`${report.lock === null ? 'left' : 'wrote'} ${path.join(migrationsDir, LOCK_NAME)} ` +
40
+ `(${report.files.length} migration(s) recorded)`);
41
+ // The lock is re-checked by the caller: `verify-migrations.ts` re-reads from disk,
42
+ // so running it after this command reports the same verdict CI will. A `--write`
43
+ // that could not resolve everything still exits non-zero here, which is why an
44
+ // incremental `edited` finding does not vanish just because someone wrote.
45
+ process.exit(report.problems.length === 0 ? 0 : 1);
46
+ //# sourceMappingURL=migrations-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations-lock.js","sourceRoot":"","sources":["../../src/scripts/migrations-lock.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAElE,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AACzF,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/H,MAAM,MAAM,GAAG,gBAAgB,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAEhE,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;IACtC,OAAO,CAAC,KAAK,CAAC,SAAS,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,OAAO,CAAC,GAAG,CACT,GAAG,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG;IAClF,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,yBAAyB,CACnD,CAAC;AAEF,mFAAmF;AACnF,iFAAiF;AACjF,+EAA+E;AAC/E,2EAA2E;AAC3E,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env tsx
2
+ export {};
3
+ //# sourceMappingURL=prepare-wrangler-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare-wrangler-config.d.ts","sourceRoot":"","sources":["../../src/scripts/prepare-wrangler-config.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Materialize `wrangler.jsonc` and provision what it refers to.
4
+ *
5
+ * Provenance: Durable-DAV's `scripts/prepare-wrangler-config.ts` and Edge-Sonic's
6
+ * `scripts/deploy/prepare-wrangler-config.ts`, converged. The five ordered calls
7
+ * live in `wrangler-config/`: read or copy the config, refuse one older than the
8
+ * template requires, apply the caller's patches, then create whatever resources it
9
+ * names and write their real ids over the placeholders. This file is only the
10
+ * order; it has nothing else to do.
11
+ *
12
+ * Run by the deploy job before `wrangler deploy`, and by the backup workflow
13
+ * before `wrangler d1 export` — so the database a backup covers is resolved from
14
+ * the same config the deploy targets, with no second `D1_DATABASE_ID` secret to
15
+ * keep in sync.
16
+ *
17
+ * The one thing it does beyond calling is publish what
18
+ * `provisionWranglerResources` created, which the backup workflow reads to refuse
19
+ * exporting a database that did not exist until this run.
20
+ *
21
+ * Usage, from the repo root:
22
+ *
23
+ * ```bash
24
+ * pnpm exec tsx scripts/prepare-wrangler-config.ts
25
+ * ```
26
+ *
27
+ * Environment:
28
+ *
29
+ * - `WRANGLER_JSONC` — the whole config, as a repository variable. When set it is
30
+ * written verbatim instead of copying the template.
31
+ * - `WRANGLER_PATCH_JSON` — top-level keys merged into the config.
32
+ * - `WRANGLER_VARS_PATCH_JSON` — `vars` entries merged into the config.
33
+ */
34
+ import { setOutput } from './lib/github-actions';
35
+ import { applyTopLevelPatch, applyVarsPatch, ensureMinimumConfigVersion, prepareConfigFile } from './wrangler-config/patches';
36
+ import { provisionWranglerResources } from './wrangler-config/resources';
37
+ prepareConfigFile();
38
+ ensureMinimumConfigVersion();
39
+ applyTopLevelPatch();
40
+ applyVarsPatch();
41
+ const created = provisionWranglerResources();
42
+ // Published as a step output rather than only printed. The backup workflow needs
43
+ // to know whether the D1 database *existed* or was created moments ago: a fresh one
44
+ // holds no user data, and uploading that empty dump every night is a false sense
45
+ // of safety. The alternative — re-reading the placeholder id out of
46
+ // `wrangler.jsonc` — is unreachable, because `provisionWranglerResources` patches
47
+ // the placeholder away before the reader ever runs.
48
+ setOutput('created', created.join(','));
49
+ console.log('Wrangler configuration is ready.');
50
+ //# sourceMappingURL=prepare-wrangler-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare-wrangler-config.js","sourceRoot":"","sources":["../../src/scripts/prepare-wrangler-config.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9H,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAEzE,iBAAiB,EAAE,CAAC;AACpB,0BAA0B,EAAE,CAAC;AAC7B,kBAAkB,EAAE,CAAC;AACrB,cAAc,EAAE,CAAC;AACjB,MAAM,OAAO,GAAG,0BAA0B,EAAE,CAAC;AAE7C,iFAAiF;AACjF,oFAAoF;AACpF,iFAAiF;AACjF,oEAAoE;AACpE,kFAAkF;AAClF,oDAAoD;AACpD,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACxC,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ interface SpaShellPaths {
2
+ /** Repo root. */
3
+ root: string;
4
+ /** Generated TS module path, relative to root. */
5
+ shellPath?: string;
6
+ /** Built SPA index.html path, relative to root. */
7
+ distIndexPath?: string;
8
+ }
9
+ interface VerifyResult {
10
+ problems: string[];
11
+ }
12
+ /**
13
+ * Create the empty stub when (and only when) nothing is there yet, so a fresh
14
+ * clone typechecks and `pnpm run build` regenerates the real thing. Never
15
+ * overwrites an existing file — overwriting would erase a real build with a
16
+ * stub on the next `pnpm install`.
17
+ */
18
+ declare function ensureSpaShellStub(options: SpaShellPaths): {
19
+ created: boolean;
20
+ path: string;
21
+ };
22
+ /**
23
+ * Fail when the shell the API worker serves is missing or self-inconsistent:
24
+ *
25
+ * - the postinstall stub (or no build at all) still in place;
26
+ * - `/assets/...` bundles referenced but not emitted (a half-cleaned dist,
27
+ * which serves a shell whose JS 404s);
28
+ * - `spa-shell.ts` and `dist/index.html` disagreeing — only one half of the
29
+ * build was refreshed.
30
+ *
31
+ * Known limit, stated rather than papered over: a *stale but self-consistent*
32
+ * pair passes. That class is covered at the source level by each repo's SPA
33
+ * parser integration test; the remaining duty is operational — run the build
34
+ * after touching `apps/web`, before deploy.
35
+ */
36
+ declare function verifySpaShell(options: SpaShellPaths): VerifyResult;
37
+ /** CLI entry: `spaShellCli(['verify'|'ensure', 'repo-root?'])`. */
38
+ declare function spaShellCli(argv?: readonly string[]): number;
39
+ export { ensureSpaShellStub, spaShellCli, verifySpaShell };
40
+ export type { SpaShellPaths, VerifyResult };
41
+ //# sourceMappingURL=spa-shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spa-shell.d.ts","sourceRoot":"","sources":["../../src/scripts/spa-shell.ts"],"names":[],"mappings":"AAmBA,UAAU,aAAa;IACrB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAiBD;;;;;GAKG;AACH,iBAAS,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAQtF;AAsCD;;;;;;;;;;;;;GAaG;AACH,iBAAS,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAsB5D;AAID,mEAAmE;AACnE,iBAAS,WAAW,CAAC,IAAI,GAAE,SAAS,MAAM,EAA0B,GAAG,MAAM,CAwB5E;AAED,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AAC3D,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * The SPA shell pair: `ensure-spa-shell-stub` + `verify-spa-shell`.
3
+ *
4
+ * Provenance: the `scripts/ensure-spa-shell-stub.mjs` +
5
+ * `scripts/verify-spa-shell.mjs` pair (Durable-DAV) and Edge-Sonic's typed
6
+ * `scripts/build/{ensure-spa-shell-stub,spa-shell-checks,verify-spa-shell}.ts`.
7
+ * The generate step itself is a Vite plugin (`spaShellEmbedPlugin` in
8
+ * `vite.ts`) because it must run on every build; this module holds the
9
+ * freshness contract around the generated file.
10
+ *
11
+ * Why the pair exists: `apps/api/src/generated/spa-shell.ts` and
12
+ * `apps/web/dist/` are both gitignored build artifacts. A fresh clone gets the
13
+ * empty postinstall stub and every browser navigation returns a blank page,
14
+ * with nothing in the source tree saying so.
15
+ */
16
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
17
+ import { pathToFileURL } from 'node:url';
18
+ import { dirname, join, resolve } from 'node:path';
19
+ const DEFAULT_SHELL = 'apps/api/src/generated/spa-shell.ts';
20
+ const DEFAULT_DIST_INDEX = 'apps/web/dist/index.html';
21
+ const STUB_CONTENT = "// Auto-generated stub - real content is produced by the build step via the Vite plugin.\nexport const SPA_HTML: string = '';\n";
22
+ /** A built shell is a full Vite index.html; any small threshold rejects the stub. */
23
+ const MIN_HTML_CHARS = 200;
24
+ function resolvePaths(options) {
25
+ const root = resolve(options.root);
26
+ const shell = join(root, options.shellPath ?? DEFAULT_SHELL);
27
+ const distIndex = join(root, options.distIndexPath ?? DEFAULT_DIST_INDEX);
28
+ return { shell, distIndex, distAssets: join(dirname(distIndex), 'assets') };
29
+ }
30
+ /**
31
+ * Create the empty stub when (and only when) nothing is there yet, so a fresh
32
+ * clone typechecks and `pnpm run build` regenerates the real thing. Never
33
+ * overwrites an existing file — overwriting would erase a real build with a
34
+ * stub on the next `pnpm install`.
35
+ */
36
+ function ensureSpaShellStub(options) {
37
+ const { shell } = resolvePaths(options);
38
+ if (existsSync(shell)) {
39
+ return { created: false, path: shell };
40
+ }
41
+ mkdirSync(dirname(shell), { recursive: true });
42
+ writeFileSync(shell, STUB_CONTENT, 'utf8');
43
+ return { created: true, path: shell };
44
+ }
45
+ /** The `SPA_HTML` string literal, decoded from the generated TS module. */
46
+ function readEmbeddedHtml(shell, problems) {
47
+ if (!existsSync(shell)) {
48
+ problems.push(`${shell} is missing — run the build (only apps/web has a build step).`);
49
+ return null;
50
+ }
51
+ const source = readFileSync(shell, 'utf8');
52
+ const marker = 'export const SPA_HTML: string = ';
53
+ const at = source.indexOf(marker);
54
+ if (at === -1) {
55
+ problems.push(`${shell} does not export SPA_HTML — regenerate it with the build.`);
56
+ return null;
57
+ }
58
+ const literal = source.slice(at + marker.length).replace(/;\s*$/, '');
59
+ if (literal === "''" || literal === '""') {
60
+ problems.push(`${shell} still holds the empty postinstall stub — run the build. Until then every browser navigation served by the API worker is a blank page.`);
61
+ return null;
62
+ }
63
+ let html;
64
+ try {
65
+ html = JSON.parse(literal);
66
+ }
67
+ catch {
68
+ problems.push(`${shell} is not the generated shape — delete it and run the build.`);
69
+ return null;
70
+ }
71
+ if (typeof html !== 'string' || html.length < MIN_HTML_CHARS) {
72
+ problems.push(`${shell} holds no usable HTML — run the build. Until then every browser navigation is a blank page.`);
73
+ return null;
74
+ }
75
+ return html;
76
+ }
77
+ /**
78
+ * Fail when the shell the API worker serves is missing or self-inconsistent:
79
+ *
80
+ * - the postinstall stub (or no build at all) still in place;
81
+ * - `/assets/...` bundles referenced but not emitted (a half-cleaned dist,
82
+ * which serves a shell whose JS 404s);
83
+ * - `spa-shell.ts` and `dist/index.html` disagreeing — only one half of the
84
+ * build was refreshed.
85
+ *
86
+ * Known limit, stated rather than papered over: a *stale but self-consistent*
87
+ * pair passes. That class is covered at the source level by each repo's SPA
88
+ * parser integration test; the remaining duty is operational — run the build
89
+ * after touching `apps/web`, before deploy.
90
+ */
91
+ function verifySpaShell(options) {
92
+ const { shell, distIndex, distAssets } = resolvePaths(options);
93
+ const problems = [];
94
+ const html = readEmbeddedHtml(shell, problems);
95
+ if (html === null) {
96
+ return { problems };
97
+ }
98
+ const referenced = [...html.matchAll(/(?:src|href)="\/assets\/([^"]+)"/g)].map((match) => match[1]);
99
+ if (referenced.length === 0) {
100
+ problems.push('The embedded shell references no /assets/ bundles — it is not a real build output.');
101
+ }
102
+ for (const asset of new Set(referenced)) {
103
+ if (!existsSync(join(distAssets, asset))) {
104
+ problems.push(`The embedded shell references /assets/${asset}, which is not in ${distAssets} — run the build.`);
105
+ }
106
+ }
107
+ if (!existsSync(distIndex)) {
108
+ problems.push(`${distIndex} is missing — run the build.`);
109
+ }
110
+ else if (readFileSync(distIndex, 'utf8') !== html) {
111
+ problems.push(`${shell} does not match ${distIndex} — run the build.`);
112
+ }
113
+ return { problems };
114
+ }
115
+ const CLI_SHAPES = 'verify|ensure';
116
+ /** CLI entry: `spaShellCli(['verify'|'ensure', 'repo-root?'])`. */
117
+ function spaShellCli(argv = process.argv.slice(2)) {
118
+ const mode = argv[0] ?? 'verify';
119
+ if (!CLI_SHAPES.split('|').includes(mode)) {
120
+ console.error(`Usage: tsx spa-shell.ts <${CLI_SHAPES}> [repo-root]`);
121
+ return 1;
122
+ }
123
+ const options = { root: argv[1] ?? process.cwd() };
124
+ if (mode === 'ensure') {
125
+ const result = ensureSpaShellStub(options);
126
+ if (result.created) {
127
+ console.log(`ensure-spa-shell-stub: created stub at ${result.path}`);
128
+ }
129
+ else {
130
+ console.log(`ensure-spa-shell-stub: ${result.path} already present, left untouched`);
131
+ }
132
+ return 0;
133
+ }
134
+ const { problems } = verifySpaShell(options);
135
+ if (problems.length > 0) {
136
+ console.error('SPA shell check failed:');
137
+ for (const problem of problems)
138
+ console.error(` - ${problem}`);
139
+ return 1;
140
+ }
141
+ console.log('SPA shell check passed.');
142
+ return 0;
143
+ }
144
+ export { ensureSpaShellStub, spaShellCli, verifySpaShell };
145
+ // Run only when this file is the entrypoint.
146
+ //
147
+ // The rules above are pure and the tests import them directly, so a module-level
148
+ // call would shell out (and exit) during a test run. The URL comparison rather than
149
+ // an unconditional call is the same guard Durable-DAV's init-secrets.ts uses, and
150
+ // it is what keeps the rule modules importable — which is the whole reason they are
151
+ // separate modules in the first place.
152
+ if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
153
+ process.exit(spaShellCli(process.argv.slice(2)));
154
+ }
155
+ //# sourceMappingURL=spa-shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spa-shell.js","sourceRoot":"","sources":["../../src/scripts/spa-shell.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /** The bundle layouts the checker understands. */
2
+ type LocaleShape = 'auto' | 'dir' | 'file' | 'module';
3
+ /** One defect, kept as a locale plus a message so a caller can group them. */
4
+ interface LocaleProblem {
5
+ locale: string;
6
+ problem: string;
7
+ }
8
+ /** The outcome of one run. */
9
+ interface LocaleReport {
10
+ locales: string[];
11
+ keys: number;
12
+ problems: LocaleProblem[];
13
+ }
14
+ declare function parseObjectLiteral(source: string, path: string): unknown;
15
+ /**
16
+ * Validate a directory of locale bundles against `baseLocale`'s key set.
17
+ *
18
+ * Two-way parity: a key the base has and a bundle lacks renders the fallback, and
19
+ * a key a bundle has that the base lacks is dead weight nothing can translate from.
20
+ */
21
+ declare function validateLocales(localesDir: string, baseLocale?: string, shape?: LocaleShape): LocaleReport;
22
+ /** CLI entry: `validateLocalesCli(['[locales-dir]?', '--shape=auto|dir|file|module'?, '--base=<tag>'?])`. */
23
+ declare function validateLocalesCli(argv?: readonly string[]): number;
24
+ export { parseObjectLiteral, validateLocales, validateLocalesCli };
25
+ export type { LocaleProblem, LocaleReport, LocaleShape };
26
+ //# sourceMappingURL=validate-locales.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-locales.d.ts","sourceRoot":"","sources":["../../src/scripts/validate-locales.ts"],"names":[],"mappings":"AAyCA,kDAAkD;AAClD,KAAK,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtD,8EAA8E;AAC9E,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,8BAA8B;AAC9B,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAkGD,iBAAS,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CA4EjE;AA8DD;;;;;GAKG;AACH,iBAAS,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,SAAO,EAAE,KAAK,GAAE,WAAoB,GAAG,YAAY,CAoDzG;AAED,6GAA6G;AAC7G,iBAAS,kBAAkB,CAAC,IAAI,GAAE,SAAS,MAAM,EAA0B,GAAG,MAAM,CAanF;AAED,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC"}