@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.
Files changed (160) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +297 -2
  3. package/dist/eslint.d.ts +120 -0
  4. package/dist/eslint.d.ts.map +1 -0
  5. package/dist/eslint.js +551 -0
  6. package/dist/eslint.js.map +1 -0
  7. package/dist/functions/pages-proxy.d.ts +113 -0
  8. package/dist/functions/pages-proxy.d.ts.map +1 -0
  9. package/dist/functions/pages-proxy.js +131 -0
  10. package/dist/functions/pages-proxy.js.map +1 -0
  11. package/dist/index.d.ts +31 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +28 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/scripts/backup/d1-target.d.ts +35 -0
  16. package/dist/scripts/backup/d1-target.d.ts.map +1 -0
  17. package/dist/scripts/backup/d1-target.js +31 -0
  18. package/dist/scripts/backup/d1-target.js.map +1 -0
  19. package/dist/scripts/backup/destination-config.d.ts +61 -0
  20. package/dist/scripts/backup/destination-config.d.ts.map +1 -0
  21. package/dist/scripts/backup/destination-config.js +57 -0
  22. package/dist/scripts/backup/destination-config.js.map +1 -0
  23. package/dist/scripts/backup/encrypt-backup.d.ts +35 -0
  24. package/dist/scripts/backup/encrypt-backup.d.ts.map +1 -0
  25. package/dist/scripts/backup/encrypt-backup.js +98 -0
  26. package/dist/scripts/backup/encrypt-backup.js.map +1 -0
  27. package/dist/scripts/backup/naming.d.ts +44 -0
  28. package/dist/scripts/backup/naming.d.ts.map +1 -0
  29. package/dist/scripts/backup/naming.js +58 -0
  30. package/dist/scripts/backup/naming.js.map +1 -0
  31. package/dist/scripts/check-god-files.d.ts +164 -0
  32. package/dist/scripts/check-god-files.d.ts.map +1 -0
  33. package/dist/scripts/check-god-files.js +271 -0
  34. package/dist/scripts/check-god-files.js.map +1 -0
  35. package/dist/scripts/ensure-spa-shell-stub.d.ts +3 -0
  36. package/dist/scripts/ensure-spa-shell-stub.d.ts.map +1 -0
  37. package/dist/scripts/ensure-spa-shell-stub.js +30 -0
  38. package/dist/scripts/ensure-spa-shell-stub.js.map +1 -0
  39. package/dist/scripts/init-secrets.d.ts +63 -0
  40. package/dist/scripts/init-secrets.d.ts.map +1 -0
  41. package/dist/scripts/init-secrets.js +240 -0
  42. package/dist/scripts/init-secrets.js.map +1 -0
  43. package/dist/scripts/lib/cli-args.d.ts +78 -0
  44. package/dist/scripts/lib/cli-args.d.ts.map +1 -0
  45. package/dist/scripts/lib/cli-args.js +116 -0
  46. package/dist/scripts/lib/cli-args.js.map +1 -0
  47. package/dist/scripts/lib/github-actions.d.ts +26 -0
  48. package/dist/scripts/lib/github-actions.d.ts.map +1 -0
  49. package/dist/scripts/lib/github-actions.js +38 -0
  50. package/dist/scripts/lib/github-actions.js.map +1 -0
  51. package/dist/scripts/lib/wrangler-table.d.ts +46 -0
  52. package/dist/scripts/lib/wrangler-table.d.ts.map +1 -0
  53. package/dist/scripts/lib/wrangler-table.js +99 -0
  54. package/dist/scripts/lib/wrangler-table.js.map +1 -0
  55. package/dist/scripts/migrations-lock.d.ts +3 -0
  56. package/dist/scripts/migrations-lock.d.ts.map +1 -0
  57. package/dist/scripts/migrations-lock.js +46 -0
  58. package/dist/scripts/migrations-lock.js.map +1 -0
  59. package/dist/scripts/prepare-wrangler-config.d.ts +3 -0
  60. package/dist/scripts/prepare-wrangler-config.d.ts.map +1 -0
  61. package/dist/scripts/prepare-wrangler-config.js +50 -0
  62. package/dist/scripts/prepare-wrangler-config.js.map +1 -0
  63. package/dist/scripts/spa-shell.d.ts +41 -0
  64. package/dist/scripts/spa-shell.d.ts.map +1 -0
  65. package/dist/scripts/spa-shell.js +155 -0
  66. package/dist/scripts/spa-shell.js.map +1 -0
  67. package/dist/scripts/validate-locales.d.ts +26 -0
  68. package/dist/scripts/validate-locales.d.ts.map +1 -0
  69. package/dist/scripts/validate-locales.js +350 -0
  70. package/dist/scripts/validate-locales.js.map +1 -0
  71. package/dist/scripts/verify-migrations.d.ts +62 -0
  72. package/dist/scripts/verify-migrations.d.ts.map +1 -0
  73. package/dist/scripts/verify-migrations.js +302 -0
  74. package/dist/scripts/verify-migrations.js.map +1 -0
  75. package/dist/scripts/verify-spa-shell.d.ts +3 -0
  76. package/dist/scripts/verify-spa-shell.d.ts.map +1 -0
  77. package/dist/scripts/verify-spa-shell.js +53 -0
  78. package/dist/scripts/verify-spa-shell.js.map +1 -0
  79. package/dist/scripts/wrangler-config/cli.d.ts +22 -0
  80. package/dist/scripts/wrangler-config/cli.d.ts.map +1 -0
  81. package/dist/scripts/wrangler-config/cli.js +51 -0
  82. package/dist/scripts/wrangler-config/cli.js.map +1 -0
  83. package/dist/scripts/wrangler-config/patches.d.ts +51 -0
  84. package/dist/scripts/wrangler-config/patches.d.ts.map +1 -0
  85. package/dist/scripts/wrangler-config/patches.js +140 -0
  86. package/dist/scripts/wrangler-config/patches.js.map +1 -0
  87. package/dist/scripts/wrangler-config/resources.d.ts +70 -0
  88. package/dist/scripts/wrangler-config/resources.d.ts.map +1 -0
  89. package/dist/scripts/wrangler-config/resources.js +290 -0
  90. package/dist/scripts/wrangler-config/resources.js.map +1 -0
  91. package/dist/scripts/wrangler-config/types.d.ts +103 -0
  92. package/dist/scripts/wrangler-config/types.d.ts.map +1 -0
  93. package/dist/scripts/wrangler-config/types.js +49 -0
  94. package/dist/scripts/wrangler-config/types.js.map +1 -0
  95. package/dist/test/integration-migrations.d.ts +167 -0
  96. package/dist/test/integration-migrations.d.ts.map +1 -0
  97. package/dist/test/integration-migrations.js +171 -0
  98. package/dist/test/integration-migrations.js.map +1 -0
  99. package/dist/test/mocks/cloudflare-workers.d.ts +106 -0
  100. package/dist/test/mocks/cloudflare-workers.d.ts.map +1 -0
  101. package/dist/test/mocks/cloudflare-workers.js +90 -0
  102. package/dist/test/mocks/cloudflare-workers.js.map +1 -0
  103. package/dist/vite.d.ts +117 -0
  104. package/dist/vite.d.ts.map +1 -0
  105. package/dist/vite.js +125 -0
  106. package/dist/vite.js.map +1 -0
  107. package/dist/vitest-web.d.ts +73 -0
  108. package/dist/vitest-web.d.ts.map +1 -0
  109. package/dist/vitest-web.js +72 -0
  110. package/dist/vitest-web.js.map +1 -0
  111. package/dist/vitest.d.ts +92 -0
  112. package/dist/vitest.d.ts.map +1 -0
  113. package/dist/vitest.js +128 -0
  114. package/dist/vitest.js.map +1 -0
  115. package/package.json +58 -3
  116. package/src/eslint.test.ts +175 -0
  117. package/src/eslint.ts +640 -0
  118. package/src/functions/pages-proxy.test.ts +72 -0
  119. package/src/functions/pages-proxy.ts +187 -0
  120. package/src/github/actions/retry-step/action.yml +39 -0
  121. package/src/github/actions/setup-env/action.yml +20 -0
  122. package/src/github/dependabot.yml +30 -0
  123. package/src/github/workflows/backup-main.yml +46 -0
  124. package/src/github/workflows/continuous-deployment.yml +188 -0
  125. package/src/github/workflows/continuous-integration.yml +259 -0
  126. package/src/github/workflows/scheduled-version-update.yml +38 -0
  127. package/src/github/workflows/upstream-sync.yml +56 -0
  128. package/src/index.ts +42 -0
  129. package/src/scripts/backup/backup-rules.test.ts +105 -0
  130. package/src/scripts/backup/d1-target.ts +54 -0
  131. package/src/scripts/backup/destination-config.ts +92 -0
  132. package/src/scripts/backup/encrypt-backup.ts +107 -0
  133. package/src/scripts/backup/naming.ts +62 -0
  134. package/src/scripts/check-god-files.test.ts +131 -0
  135. package/src/scripts/check-god-files.ts +327 -0
  136. package/src/scripts/ensure-spa-shell-stub.ts +34 -0
  137. package/src/scripts/init-secrets.ts +265 -0
  138. package/src/scripts/lib/cli-args.ts +154 -0
  139. package/src/scripts/lib/github-actions.ts +41 -0
  140. package/src/scripts/lib/wrangler-table.ts +105 -0
  141. package/src/scripts/migrations-lock.ts +52 -0
  142. package/src/scripts/prepare-wrangler-config.ts +51 -0
  143. package/src/scripts/spa-shell.test.ts +91 -0
  144. package/src/scripts/spa-shell.ts +179 -0
  145. package/src/scripts/validate-locales.test.ts +89 -0
  146. package/src/scripts/validate-locales.ts +380 -0
  147. package/src/scripts/verify-migrations.test.ts +71 -0
  148. package/src/scripts/verify-migrations.ts +364 -0
  149. package/src/scripts/verify-spa-shell.ts +56 -0
  150. package/src/scripts/wrangler-config/cli.ts +51 -0
  151. package/src/scripts/wrangler-config/patches.ts +157 -0
  152. package/src/scripts/wrangler-config/resources.ts +330 -0
  153. package/src/scripts/wrangler-config/types.ts +113 -0
  154. package/src/test/integration-migrations.test.ts +169 -0
  155. package/src/test/integration-migrations.ts +267 -0
  156. package/src/test/mocks/cloudflare-workers.ts +115 -0
  157. package/src/vite.test.ts +83 -0
  158. package/src/vite.ts +202 -0
  159. package/src/vitest-web.ts +109 -0
  160. package/src/vitest.ts +185 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The SPA's test and coverage gate.
3
+ *
4
+ * Provenance: converged from Durable-DAV-Router's and Mail-Otter's
5
+ * `vitest.web.config.mts`, which are the two repos that gave the frontend its own
6
+ * suite. Durable-DAV-Router's is the canonical shape — it is the one that records
7
+ * *why* the split exists; Mail-Otter's contributes the React plugin and the
8
+ * `apps/web/**` include.
9
+ *
10
+ * Canonical decisions:
11
+ *
12
+ * 1. **It is a separate config, not a `projects` entry in one.** With a single
13
+ * config the SPA suites ran under `node` and the worker suites under `jsdom`
14
+ * in the same pass, so a test could pass in one environment and fail in the
15
+ * other with neither run noticing.
16
+ * 2. **`pool: 'threads'`, bounded, rather than one forked worker per file.** Each
17
+ * jsdom document is a whole browser environment; a fork per file exhausts
18
+ * memory on a constrained machine, and when it does the run dies partway with
19
+ * a bare "Worker exited unexpectedly" and no test reports a failure — which
20
+ * reads as a flaky suite rather than as an out-of-memory kill.
21
+ * 3. **jsdom is set here for every file**, rather than a `@vitest-environment`
22
+ * pragma per file: these are the tests that need a DOM, and the pragma is one
23
+ * more place to forget.
24
+ * 4. **`include` covers both shapes the repos use** — the root-suite `web-*`
25
+ * naming convention and suites bundled under `apps/web` — and the same globs
26
+ * are what `defineUnitVitestConfig()` excludes.
27
+ */
28
+
29
+ import { defineConfig, type Plugin } from 'vitest/config';
30
+ import { fileURLToPath } from 'node:url';
31
+ import type { CoverageThresholds, VitestAlias } from './vitest';
32
+
33
+ /**
34
+ * What `defineWebVitestConfig` accepts.
35
+ */
36
+ export interface WebVitestConfigOptions {
37
+ /**
38
+ * Vite plugins the SPA needs — `@vitejs/plugin-react` for a React app. Passed
39
+ * by the consumer rather than imported here, because the kit must stay
40
+ * zero-dependency and the plugin belongs to the app, not the harness.
41
+ */
42
+ readonly plugins?: readonly Plugin[];
43
+ /**
44
+ * Extra resolver entries.
45
+ */
46
+ readonly aliases?: readonly VitestAlias[];
47
+ /**
48
+ * The repo root, as a file URL.
49
+ */
50
+ readonly root?: string;
51
+ /**
52
+ * Which suites to collect. Defaults to both shapes the source repos use.
53
+ */
54
+ readonly include?: readonly string[];
55
+ /**
56
+ * Coverage floors. Omitted by default; see `defineUnitVitestConfig`.
57
+ */
58
+ readonly thresholds?: CoverageThresholds;
59
+ /**
60
+ * Where the coverage report is written. Separate from the unit config's, so
61
+ * the two gates never overwrite each other's report.
62
+ */
63
+ readonly reportsDirectory?: string;
64
+ }
65
+
66
+ /**
67
+ * The default suite globs: the root-suite `web-*` convention and the
68
+ * bundled-under-`apps/web` convention, so either layout works unchanged.
69
+ */
70
+ const DEFAULT_INCLUDE: readonly string[] = ['test/**/web-*.test.{ts,tsx}', 'apps/web/**/*.test.{ts,tsx}'];
71
+
72
+ /**
73
+ * Build the SPA's test config.
74
+ *
75
+ * ```ts
76
+ * // vitest.web.config.mts
77
+ * import react from '@vitejs/plugin-react';
78
+ * import { defineWebVitestConfig } from '@rexezuge/tooling/vitest-web';
79
+ * export default defineWebVitestConfig({ plugins: [react()] });
80
+ * ```
81
+ */
82
+ export function defineWebVitestConfig(options: WebVitestConfigOptions = {}): ReturnType<typeof defineConfig> {
83
+ const root = options.root ?? new URL('.', import.meta.url).href;
84
+ const webSrc = fileURLToPath(new URL('apps/web/src', root));
85
+ const aliases: VitestAlias[] = [{ find: /^@\//, replacement: `${webSrc}/` }, ...(options.aliases ?? [])];
86
+
87
+ return defineConfig({
88
+ ...(options.plugins !== undefined && { plugins: [...options.plugins] }),
89
+ resolve: { alias: aliases },
90
+ test: {
91
+ globals: true,
92
+ environment: 'jsdom',
93
+ include: [...(options.include ?? DEFAULT_INCLUDE)],
94
+ // Threads, bounded, rather than one forked worker per file — see the
95
+ // header. A custom `exclude` replaces Vitest's defaults, so `node_modules`
96
+ // must be re-listed.
97
+ pool: 'threads',
98
+ exclude: ['**/node_modules/**', '**/dist/**', 'test/integration/**'],
99
+ coverage: {
100
+ provider: 'v8',
101
+ reporter: ['text', 'lcov'],
102
+ reportsDirectory: options.reportsDirectory ?? './coverage-spa',
103
+ include: ['apps/web/src/**/*.{ts,tsx}'],
104
+ exclude: ['**/*.test.{ts,tsx}', '**/*.d.ts', '**/types.ts', '**/generated/**'],
105
+ ...(options.thresholds !== undefined && { thresholds: { ...options.thresholds } }),
106
+ },
107
+ },
108
+ });
109
+ }
package/src/vitest.ts ADDED
@@ -0,0 +1,185 @@
1
+ /**
2
+ * The unit-test Vitest config for the worker-and-packages half of the suite.
3
+ *
4
+ * Provenance: converged from the eight repos' `vitest.config.mts`. The coverage
5
+ * block is Durable-DAV-Router's (the most complete: v8 provider, explicit
6
+ * include/exclude patterns, thresholds with the reasoning for why they sit just
7
+ * under the measured value); the alias block is Durable-DAV's, which is the only
8
+ * one that also aliases `cloudflare:workers` and `cloudflare:sockets` to the
9
+ * Node mocks.
10
+ *
11
+ * Canonical decisions:
12
+ *
13
+ * 1. **Every file matching `include` is reported** — Vitest 4 removed
14
+ * `coverage.all`, and an explicit `include` now covers files no test
15
+ * imported. That is the behaviour the source repos configured `all: true` for,
16
+ * and the reason it mattered: without it a brand-new module with no test is
17
+ * invisible rather than counted as zero, which is the exact regression this
18
+ * gate exists to catch.
19
+ * 2. **The SPA is excluded here**, so this config and `defineWebVitestConfig()`
20
+ * partition the suite rather than each running all of it. Every SPA suite is
21
+ * named `web-*`. One number could not honestly describe both halves: the SPA's
22
+ * presentational modules pulled a shared floor down 16 points, so a change
23
+ * adding legitimate code in `apps/api` could fail the gate on the strength of
24
+ * files two directories away.
25
+ * 3. **A custom `exclude` re-lists `node_modules`** — `test/` is a workspace
26
+ * project and therefore has its own, so without the re-list the glob reaches
27
+ * into `test/node_modules` and tries to run other packages' suites.
28
+ * 4. **Thresholds are a consumer argument**, not a kit default. A floor is a
29
+ * per-repo ratchet measured against that repo's suite; the kit cannot know a
30
+ * number, and guessing one is how floors get lowered.
31
+ */
32
+
33
+ import { defineConfig } from 'vitest/config';
34
+ import { fileURLToPath } from 'node:url';
35
+
36
+ /**
37
+ * One `{ find, replacement }` pair for Vitest's resolver.
38
+ */
39
+ export interface VitestAlias {
40
+ readonly find: string | RegExp;
41
+ readonly replacement: string;
42
+ }
43
+
44
+ /**
45
+ * Coverage thresholds, in the four figures Vitest gates on.
46
+ */
47
+ export interface CoverageThresholds {
48
+ readonly statements?: number;
49
+ readonly branches?: number;
50
+ readonly functions?: number;
51
+ readonly lines?: number;
52
+ }
53
+
54
+ /**
55
+ * What `defineUnitVitestConfig` accepts. Every field is optional: the defaults
56
+ * cover the canonical monorepo layout, and a consumer overrides only what its
57
+ * tree actually differs in.
58
+ */
59
+ export interface UnitVitestConfigOptions {
60
+ /**
61
+ * Extra resolver entries, applied after the canonical ones.
62
+ */
63
+ readonly aliases?: readonly VitestAlias[];
64
+ /**
65
+ * Where the repo root is, as a file URL. Defaults to the directory this
66
+ * factory is *called* from, which is the repo root's `vitest.config.mts`.
67
+ */
68
+ readonly root?: string;
69
+ /**
70
+ * Coverage `include` globs. Defaults to the worker plus every workspace
71
+ * package's `src`.
72
+ */
73
+ readonly coverageInclude?: readonly string[];
74
+ /**
75
+ * Coverage `exclude` globs, merged with the canonical set.
76
+ */
77
+ readonly coverageExclude?: readonly string[];
78
+ /**
79
+ * Coverage floors. Omitted by default; a repo that wants them sets them.
80
+ */
81
+ readonly thresholds?: CoverageThresholds;
82
+ /**
83
+ * Where the coverage report is written.
84
+ */
85
+ readonly reportsDirectory?: string;
86
+ }
87
+
88
+ /**
89
+ * The SPA's suites, excluded here so the two configs partition the suite.
90
+ *
91
+ * Every SPA suite is named `web-*`. `defineWebVitestConfig()` includes the same
92
+ * glob, so a file is collected by exactly one config.
93
+ */
94
+ const SPA_FILES = 'test/**/web-*.test.{ts,tsx}';
95
+
96
+ /**
97
+ * The canonical alias set.
98
+ *
99
+ * Built relative to the consumer's repo root rather than the kit's directory,
100
+ * so the factory describes *their* layout. A Vitest config is a standalone file
101
+ * with no module graph to share through, and a suite resolving a workspace
102
+ * import differently from its sibling fails in a way that looks like a product
103
+ * bug — which is why these are stated once, here, rather than duplicated.
104
+ */
105
+ function canonicalAliases(root: string): VitestAlias[] {
106
+ const at = (relative: string): string => fileURLToPath(new URL(relative, root));
107
+ return [
108
+ { find: 'cloudflare:workers', replacement: at('test/mocks/cloudflare-workers.ts') },
109
+ { find: 'cloudflare:sockets', replacement: at('test/mocks/cloudflare-sockets.ts') },
110
+ { find: /^@\//, replacement: `${at('apps/api/src')}/` },
111
+ { find: /^~\//, replacement: `${at('apps/web/src')}/` },
112
+ ];
113
+ }
114
+
115
+ /**
116
+ * Coverage `exclude` globs converged from the source repos.
117
+ *
118
+ * The categories: build and test tooling (`scripts/` grows with project surface,
119
+ * not complexity, and the god-file guard excludes it for the same reason), test
120
+ * files, declarations, re-export barrels (`index.ts` carries no logic of its
121
+ * own), model records, generated files (the SPA shell is a one-line HTML blob
122
+ * with no logic to exercise), and type-only modules.
123
+ */
124
+ const CANONICAL_COVERAGE_EXCLUDE: readonly string[] = [
125
+ 'scripts/**',
126
+ '**/*.test.ts',
127
+ '**/*.d.ts',
128
+ '**/index.ts',
129
+ '**/types.d.ts',
130
+ '**/model/**',
131
+ '**/generated/**',
132
+ '**/D1Types.ts',
133
+ '**/ServiceEnv.ts',
134
+ '**/env.d.ts',
135
+ ];
136
+
137
+ /**
138
+ * Build the unit-test config.
139
+ *
140
+ * ```ts
141
+ * // vitest.config.mts
142
+ * import { defineUnitVitestConfig } from '@rexezuge/tooling/vitest';
143
+ * export default defineUnitVitestConfig();
144
+ * ```
145
+ */
146
+ export function defineUnitVitestConfig(options: UnitVitestConfigOptions = {}): ReturnType<typeof defineConfig> {
147
+ const root = options.root ?? new URL('.', import.meta.url).href;
148
+ const coverageInclude = options.coverageInclude ?? ['apps/api/src/**/*.ts', 'apps/background/src/**/*.ts', 'packages/**/src/**/*.ts'];
149
+ const coverageExclude = [...CANONICAL_COVERAGE_EXCLUDE, ...(options.coverageExclude ?? [])];
150
+ const aliases = [...canonicalAliases(root), ...(options.aliases ?? [])];
151
+
152
+ return defineConfig({
153
+ resolve: { alias: aliases },
154
+ test: {
155
+ globals: true,
156
+ environment: 'node',
157
+ include: ['test/**/*.test.{ts,tsx}'],
158
+ // `test` is a workspace project and therefore has its own node_modules;
159
+ // without the re-list, the glob reaches into it.
160
+ exclude: ['**/node_modules/**', '**/dist/**', 'test/integration/**', SPA_FILES],
161
+ server: {
162
+ deps: {
163
+ // A `node_modules` dependency is externalized by default, so Node's
164
+ // own ESM loader resolves a `cloudflare:workers` specifier and rejects
165
+ // the scheme before `resolve.alias` is ever consulted. Inlining routes
166
+ // it back through Vite, where the alias applies.
167
+ inline: ['dofs'],
168
+ },
169
+ },
170
+ coverage: {
171
+ provider: 'v8',
172
+ // Vitest 4 removed `coverage.all`; an explicit `include` now reports
173
+ // every matching file whether or not a test imported it, which is the
174
+ // behaviour the source repos' `all: true` was there to get: a brand-new
175
+ // module with no test is counted as zero rather than being invisible,
176
+ // which is the exact regression this gate exists to catch.
177
+ reporter: ['text', 'lcov', 'html'],
178
+ reportsDirectory: options.reportsDirectory ?? './coverage',
179
+ include: [...coverageInclude],
180
+ exclude: coverageExclude,
181
+ ...(options.thresholds !== undefined && { thresholds: { ...options.thresholds } }),
182
+ },
183
+ },
184
+ });
185
+ }