@rexezuge/tooling 0.0.0-stage → 1.0.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.
- package/LICENSE +21 -0
- package/README.md +297 -2
- package/dist/eslint.d.ts +120 -0
- package/dist/eslint.d.ts.map +1 -0
- package/dist/eslint.js +551 -0
- package/dist/eslint.js.map +1 -0
- package/dist/functions/pages-proxy.d.ts +113 -0
- package/dist/functions/pages-proxy.d.ts.map +1 -0
- package/dist/functions/pages-proxy.js +131 -0
- package/dist/functions/pages-proxy.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/scripts/backup/d1-target.d.ts +35 -0
- package/dist/scripts/backup/d1-target.d.ts.map +1 -0
- package/dist/scripts/backup/d1-target.js +31 -0
- package/dist/scripts/backup/d1-target.js.map +1 -0
- package/dist/scripts/backup/destination-config.d.ts +61 -0
- package/dist/scripts/backup/destination-config.d.ts.map +1 -0
- package/dist/scripts/backup/destination-config.js +57 -0
- package/dist/scripts/backup/destination-config.js.map +1 -0
- package/dist/scripts/backup/encrypt-backup.d.ts +35 -0
- package/dist/scripts/backup/encrypt-backup.d.ts.map +1 -0
- package/dist/scripts/backup/encrypt-backup.js +98 -0
- package/dist/scripts/backup/encrypt-backup.js.map +1 -0
- package/dist/scripts/backup/naming.d.ts +44 -0
- package/dist/scripts/backup/naming.d.ts.map +1 -0
- package/dist/scripts/backup/naming.js +58 -0
- package/dist/scripts/backup/naming.js.map +1 -0
- package/dist/scripts/check-god-files.d.ts +164 -0
- package/dist/scripts/check-god-files.d.ts.map +1 -0
- package/dist/scripts/check-god-files.js +271 -0
- package/dist/scripts/check-god-files.js.map +1 -0
- package/dist/scripts/ensure-spa-shell-stub.d.ts +3 -0
- package/dist/scripts/ensure-spa-shell-stub.d.ts.map +1 -0
- package/dist/scripts/ensure-spa-shell-stub.js +30 -0
- package/dist/scripts/ensure-spa-shell-stub.js.map +1 -0
- package/dist/scripts/init-secrets.d.ts +63 -0
- package/dist/scripts/init-secrets.d.ts.map +1 -0
- package/dist/scripts/init-secrets.js +240 -0
- package/dist/scripts/init-secrets.js.map +1 -0
- package/dist/scripts/lib/cli-args.d.ts +78 -0
- package/dist/scripts/lib/cli-args.d.ts.map +1 -0
- package/dist/scripts/lib/cli-args.js +116 -0
- package/dist/scripts/lib/cli-args.js.map +1 -0
- package/dist/scripts/lib/github-actions.d.ts +26 -0
- package/dist/scripts/lib/github-actions.d.ts.map +1 -0
- package/dist/scripts/lib/github-actions.js +38 -0
- package/dist/scripts/lib/github-actions.js.map +1 -0
- package/dist/scripts/lib/wrangler-table.d.ts +46 -0
- package/dist/scripts/lib/wrangler-table.d.ts.map +1 -0
- package/dist/scripts/lib/wrangler-table.js +99 -0
- package/dist/scripts/lib/wrangler-table.js.map +1 -0
- package/dist/scripts/migrations-lock.d.ts +3 -0
- package/dist/scripts/migrations-lock.d.ts.map +1 -0
- package/dist/scripts/migrations-lock.js +46 -0
- package/dist/scripts/migrations-lock.js.map +1 -0
- package/dist/scripts/prepare-wrangler-config.d.ts +3 -0
- package/dist/scripts/prepare-wrangler-config.d.ts.map +1 -0
- package/dist/scripts/prepare-wrangler-config.js +50 -0
- package/dist/scripts/prepare-wrangler-config.js.map +1 -0
- package/dist/scripts/spa-shell.d.ts +41 -0
- package/dist/scripts/spa-shell.d.ts.map +1 -0
- package/dist/scripts/spa-shell.js +155 -0
- package/dist/scripts/spa-shell.js.map +1 -0
- package/dist/scripts/validate-locales.d.ts +26 -0
- package/dist/scripts/validate-locales.d.ts.map +1 -0
- package/dist/scripts/validate-locales.js +350 -0
- package/dist/scripts/validate-locales.js.map +1 -0
- package/dist/scripts/verify-migrations.d.ts +62 -0
- package/dist/scripts/verify-migrations.d.ts.map +1 -0
- package/dist/scripts/verify-migrations.js +302 -0
- package/dist/scripts/verify-migrations.js.map +1 -0
- package/dist/scripts/verify-spa-shell.d.ts +3 -0
- package/dist/scripts/verify-spa-shell.d.ts.map +1 -0
- package/dist/scripts/verify-spa-shell.js +53 -0
- package/dist/scripts/verify-spa-shell.js.map +1 -0
- package/dist/scripts/wrangler-config/cli.d.ts +22 -0
- package/dist/scripts/wrangler-config/cli.d.ts.map +1 -0
- package/dist/scripts/wrangler-config/cli.js +51 -0
- package/dist/scripts/wrangler-config/cli.js.map +1 -0
- package/dist/scripts/wrangler-config/patches.d.ts +51 -0
- package/dist/scripts/wrangler-config/patches.d.ts.map +1 -0
- package/dist/scripts/wrangler-config/patches.js +140 -0
- package/dist/scripts/wrangler-config/patches.js.map +1 -0
- package/dist/scripts/wrangler-config/resources.d.ts +70 -0
- package/dist/scripts/wrangler-config/resources.d.ts.map +1 -0
- package/dist/scripts/wrangler-config/resources.js +290 -0
- package/dist/scripts/wrangler-config/resources.js.map +1 -0
- package/dist/scripts/wrangler-config/types.d.ts +103 -0
- package/dist/scripts/wrangler-config/types.d.ts.map +1 -0
- package/dist/scripts/wrangler-config/types.js +49 -0
- package/dist/scripts/wrangler-config/types.js.map +1 -0
- package/dist/test/integration-migrations.d.ts +167 -0
- package/dist/test/integration-migrations.d.ts.map +1 -0
- package/dist/test/integration-migrations.js +171 -0
- package/dist/test/integration-migrations.js.map +1 -0
- package/dist/test/mocks/cloudflare-workers.d.ts +106 -0
- package/dist/test/mocks/cloudflare-workers.d.ts.map +1 -0
- package/dist/test/mocks/cloudflare-workers.js +90 -0
- package/dist/test/mocks/cloudflare-workers.js.map +1 -0
- package/dist/vite.d.ts +117 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +125 -0
- package/dist/vite.js.map +1 -0
- package/dist/vitest-web.d.ts +73 -0
- package/dist/vitest-web.d.ts.map +1 -0
- package/dist/vitest-web.js +72 -0
- package/dist/vitest-web.js.map +1 -0
- package/dist/vitest.d.ts +92 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +128 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +58 -3
- package/src/eslint.test.ts +175 -0
- package/src/eslint.ts +640 -0
- package/src/functions/pages-proxy.test.ts +72 -0
- package/src/functions/pages-proxy.ts +187 -0
- package/src/github/actions/retry-step/action.yml +39 -0
- package/src/github/actions/setup-env/action.yml +20 -0
- package/src/github/dependabot.yml +30 -0
- package/src/github/workflows/backup-main.yml +46 -0
- package/src/github/workflows/continuous-deployment.yml +188 -0
- package/src/github/workflows/continuous-integration.yml +259 -0
- package/src/github/workflows/scheduled-version-update.yml +38 -0
- package/src/github/workflows/upstream-sync.yml +56 -0
- package/src/index.ts +42 -0
- package/src/scripts/backup/backup-rules.test.ts +105 -0
- package/src/scripts/backup/d1-target.ts +54 -0
- package/src/scripts/backup/destination-config.ts +92 -0
- package/src/scripts/backup/encrypt-backup.ts +107 -0
- package/src/scripts/backup/naming.ts +62 -0
- package/src/scripts/check-god-files.test.ts +131 -0
- package/src/scripts/check-god-files.ts +327 -0
- package/src/scripts/ensure-spa-shell-stub.ts +34 -0
- package/src/scripts/init-secrets.ts +265 -0
- package/src/scripts/lib/cli-args.ts +154 -0
- package/src/scripts/lib/github-actions.ts +41 -0
- package/src/scripts/lib/wrangler-table.ts +105 -0
- package/src/scripts/migrations-lock.ts +52 -0
- package/src/scripts/prepare-wrangler-config.ts +51 -0
- package/src/scripts/spa-shell.test.ts +91 -0
- package/src/scripts/spa-shell.ts +179 -0
- package/src/scripts/validate-locales.test.ts +89 -0
- package/src/scripts/validate-locales.ts +380 -0
- package/src/scripts/verify-migrations.test.ts +71 -0
- package/src/scripts/verify-migrations.ts +364 -0
- package/src/scripts/verify-spa-shell.ts +56 -0
- package/src/scripts/wrangler-config/cli.ts +51 -0
- package/src/scripts/wrangler-config/patches.ts +157 -0
- package/src/scripts/wrangler-config/resources.ts +330 -0
- package/src/scripts/wrangler-config/types.ts +113 -0
- package/src/test/integration-migrations.test.ts +169 -0
- package/src/test/integration-migrations.ts +267 -0
- package/src/test/mocks/cloudflare-workers.ts +115 -0
- package/src/vite.test.ts +83 -0
- package/src/vite.ts +202 -0
- package/src/vitest-web.ts +109 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|