@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
package/dist/vite.d.ts ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * The SPA's Vite config.
3
+ *
4
+ * Provenance: converged from the `apps/web/vite.config.ts` of the eight repos
5
+ * that build a Vite SPA. They are one file with three variables: the plugin list
6
+ * (`@vitejs/plugin-react` + `@tailwindcss/vite`), the dev-server proxy target,
7
+ * and the `spa-shell-embed` plugin that copies `dist/index.html` into
8
+ * `apps/api/src/generated/spa-shell.ts`.
9
+ *
10
+ * Canonical decisions:
11
+ *
12
+ * 1. **The plugins are passed in, not imported.** The kit stays zero-dependency:
13
+ * `react`, `@vitejs/plugin-react` and `@tailwindcss/vite` belong to the app
14
+ * that uses them, and importing them here would make the tooling package
15
+ * undownloadable for a consumer without a React SPA.
16
+ * 2. **The `spa-shell-embed` hook stays per-repo, and is passed in.** It is the
17
+ * one genuinely repo-specific piece: each repo names its own worker and its
18
+ * own generated-module path. What the kit provides is the *contract* — see
19
+ * `spaShellEmbedPlugin`, which is the converged body of all eight copies.
20
+ * 3. **`server.proxy` is data, not code.** The paths differ per repo
21
+ * (`/user` + `/rest` in Edge-Sonic, `/api` in CalDAV-Bridge, `/tracker` in
22
+ * ChordDHT-Tracker) but the shape never does, so it is an option with a
23
+ * default.
24
+ * 4. **`build.emptyOutDir: true`** is the default, because a stale asset in
25
+ * `dist/` is served as a live bundle — the same half-refreshed build
26
+ * `scripts/verify-spa-shell.ts` rejects.
27
+ *
28
+ * The consumer's `apps/web/vite.config.ts` becomes a handful of lines — see the
29
+ * README.
30
+ */
31
+ import { defineConfig, type Plugin } from 'vitest/config';
32
+ /**
33
+ * One dev-server proxy entry, as Vite's `server.proxy` accepts it.
34
+ */
35
+ export interface ProxyEntry {
36
+ readonly target: string;
37
+ readonly changeOrigin?: boolean;
38
+ readonly bypass?: (request: {
39
+ headers: Record<string, string | string[] | undefined>;
40
+ }) => string | undefined;
41
+ }
42
+ /**
43
+ * Where the SPA's built `index.html` is copied to, and under what module name.
44
+ */
45
+ export interface SpaShellTarget {
46
+ /**
47
+ * Path of the generated module, relative to the repository root.
48
+ *
49
+ * `apps/api/src/generated/spa-shell.ts` in every repo that embeds a shell; the
50
+ * parameter exists because the path is the one thing that genuinely differs.
51
+ */
52
+ readonly path: string;
53
+ /**
54
+ * The repository root the path is resolved against, as a file URL or a path.
55
+ *
56
+ * Defaults to the directory the build runs in (`process.cwd()`), which is the
57
+ * repo root when the build is `pnpm --filter @scope/web build`. Passed in by the
58
+ * Vite config, not derived from `import.meta.url`: this plugin object is created
59
+ * inside the kit's own module, so a URL relative to it points into
60
+ * `node_modules/@rexezuge/tooling` and writes the shell somewhere no compiler
61
+ * will ever look.
62
+ */
63
+ readonly root?: string | URL;
64
+ /**
65
+ * The built `index.html`, as a path or file URL.
66
+ *
67
+ * Defaults to `dist/index.html` under the same root. Vite's `closeBundle` runs
68
+ * after the bundle is written, so this is the file the build just produced.
69
+ */
70
+ readonly distIndex?: string | URL;
71
+ }
72
+ /**
73
+ * What `defineWebViteConfig` accepts.
74
+ */
75
+ export interface WebViteConfigOptions {
76
+ /**
77
+ * The SPA's plugins — `react()`, `tailwindcss()`, and anything else the app
78
+ * needs. Applied before the spa-shell plugin so the shell is written after the
79
+ * bundle exists.
80
+ */
81
+ readonly plugins?: readonly Plugin[];
82
+ /**
83
+ * Dev-server proxy entries. Keyed by path prefix.
84
+ */
85
+ readonly proxy?: Readonly<Record<string, ProxyEntry>>;
86
+ /**
87
+ * Where the built shell is embedded. Omit to skip the embedding entirely.
88
+ */
89
+ readonly spaShell?: SpaShellTarget;
90
+ /**
91
+ * The build output directory.
92
+ */
93
+ readonly outDir?: string;
94
+ /**
95
+ * The directory this config is resolved from, as a file URL. Defaults to the
96
+ * caller's location.
97
+ */
98
+ readonly root?: string;
99
+ }
100
+ export declare function spaShellEmbedPlugin(target: SpaShellTarget): Plugin;
101
+ /**
102
+ * Build the SPA's Vite config.
103
+ *
104
+ * ```ts
105
+ * // apps/web/vite.config.ts
106
+ * import react from '@vitejs/plugin-react';
107
+ * import { defineWebViteConfig, spaShellEmbedPlugin } from '@rexezuge/tooling/vite';
108
+ *
109
+ * export default defineWebViteConfig({
110
+ * plugins: [react()],
111
+ * proxy: { '/user': { target: 'http://localhost:8787', changeOrigin: true } },
112
+ * spaShell: { path: '../../apps/api/src/generated/spa-shell.ts' },
113
+ * });
114
+ * ```
115
+ */
116
+ export declare function defineWebViteConfig(options?: WebViteConfigOptions): ReturnType<typeof defineConfig>;
117
+ //# sourceMappingURL=vite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;KAAE,KAAK,MAAM,GAAG,SAAS,CAAC;CAC/G;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;OAEG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACtD;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;IACnC;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AA0CD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAyBlE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAcvG"}
package/dist/vite.js ADDED
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The SPA's Vite config.
3
+ *
4
+ * Provenance: converged from the `apps/web/vite.config.ts` of the eight repos
5
+ * that build a Vite SPA. They are one file with three variables: the plugin list
6
+ * (`@vitejs/plugin-react` + `@tailwindcss/vite`), the dev-server proxy target,
7
+ * and the `spa-shell-embed` plugin that copies `dist/index.html` into
8
+ * `apps/api/src/generated/spa-shell.ts`.
9
+ *
10
+ * Canonical decisions:
11
+ *
12
+ * 1. **The plugins are passed in, not imported.** The kit stays zero-dependency:
13
+ * `react`, `@vitejs/plugin-react` and `@tailwindcss/vite` belong to the app
14
+ * that uses them, and importing them here would make the tooling package
15
+ * undownloadable for a consumer without a React SPA.
16
+ * 2. **The `spa-shell-embed` hook stays per-repo, and is passed in.** It is the
17
+ * one genuinely repo-specific piece: each repo names its own worker and its
18
+ * own generated-module path. What the kit provides is the *contract* — see
19
+ * `spaShellEmbedPlugin`, which is the converged body of all eight copies.
20
+ * 3. **`server.proxy` is data, not code.** The paths differ per repo
21
+ * (`/user` + `/rest` in Edge-Sonic, `/api` in CalDAV-Bridge, `/tracker` in
22
+ * ChordDHT-Tracker) but the shape never does, so it is an option with a
23
+ * default.
24
+ * 4. **`build.emptyOutDir: true`** is the default, because a stale asset in
25
+ * `dist/` is served as a live bundle — the same half-refreshed build
26
+ * `scripts/verify-spa-shell.ts` rejects.
27
+ *
28
+ * The consumer's `apps/web/vite.config.ts` becomes a handful of lines — see the
29
+ * README.
30
+ */
31
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
32
+ import { dirname } from 'node:path';
33
+ import { fileURLToPath, pathToFileURL } from 'node:url';
34
+ import { defineConfig } from 'vitest/config';
35
+ /**
36
+ * The `spa-shell-embed` plugin, converged from all eight copies.
37
+ *
38
+ * It exists because the API worker serves `SPA_HTML` from a module in its own
39
+ * source tree, and both that module and `dist/` are gitignored — so a fresh
40
+ * clone gets an empty stub from `postinstall` and every `GET /` returns a blank
41
+ * page until someone runs a build. This plugin is what makes the build write the
42
+ * real artefact, and `scripts/verify-spa-shell.ts` is what refuses to deploy
43
+ * without it.
44
+ *
45
+ * Written as a factory rather than inlined so the exit conditions are in one
46
+ * place: the `closeBundle` hook runs on every build, including the ones where
47
+ * `dist/index.html` was never produced, and a plugin that throws there fails
48
+ * builds that legitimately emit nothing.
49
+ */
50
+ /**
51
+ * Normalise a path-or-URL option to a URL, so `new URL(...)` can be used on it.
52
+ *
53
+ * Small, and here because two options accept either form: a path is what a consumer
54
+ * naturally writes in a config, and a URL is what `import.meta.url` hands them.
55
+ * Accepting both is one line and saves a `fileURLToPath` at every call site.
56
+ */
57
+ function toUrl(value) {
58
+ return typeof value === 'string' ? pathToFileURL(value) : value;
59
+ }
60
+ /**
61
+ * The same, as a **directory** URL — with the trailing separator `new URL(rel, base)`
62
+ * needs.
63
+ *
64
+ * `new URL('apps/api/x.ts', 'file:///repo')` resolves to `/apps/api/x.ts`, silently
65
+ * dropping the base. The base has to end in a separator, and a path from
66
+ * `process.cwd()` never does, so this is where the plugin would otherwise write the
67
+ * shell into the filesystem root.
68
+ */
69
+ function toDirUrl(value) {
70
+ const url = toUrl(value);
71
+ return url.href.endsWith('/') ? url : new URL(`${url.href}/`);
72
+ }
73
+ export function spaShellEmbedPlugin(target) {
74
+ return {
75
+ name: 'spa-shell-embed',
76
+ closeBundle() {
77
+ // Resolved from the directory the build runs in, never from this module's own
78
+ // URL: the plugin object is created here but executes in the consumer's Vite
79
+ // process, and `new URL(path, import.meta.url)` would resolve against
80
+ // `node_modules/@rexezuge/tooling`. `process.cwd()` is the repo root for
81
+ // `pnpm --filter @scope/web build`, which is how every consumer runs it.
82
+ const root = toDirUrl(target.root ?? process.cwd());
83
+ const outputPath = fileURLToPath(new URL(target.path, root));
84
+ const indexPath = target.distIndex === undefined ? fileURLToPath(new URL('dist/index.html', root)) : fileURLToPath(toUrl(target.distIndex));
85
+ if (!existsSync(indexPath)) {
86
+ console.warn('spa-shell-embed: dist/index.html not found, skipping');
87
+ return;
88
+ }
89
+ const html = readFileSync(indexPath, 'utf8');
90
+ const output = `// Auto-generated by Vite build plugin - DO NOT EDIT.\nexport const SPA_HTML: string = ${JSON.stringify(html)};\n`;
91
+ mkdirSync(dirname(outputPath), { recursive: true });
92
+ writeFileSync(outputPath, output);
93
+ },
94
+ };
95
+ }
96
+ /**
97
+ * Build the SPA's Vite config.
98
+ *
99
+ * ```ts
100
+ * // apps/web/vite.config.ts
101
+ * import react from '@vitejs/plugin-react';
102
+ * import { defineWebViteConfig, spaShellEmbedPlugin } from '@rexezuge/tooling/vite';
103
+ *
104
+ * export default defineWebViteConfig({
105
+ * plugins: [react()],
106
+ * proxy: { '/user': { target: 'http://localhost:8787', changeOrigin: true } },
107
+ * spaShell: { path: '../../apps/api/src/generated/spa-shell.ts' },
108
+ * });
109
+ * ```
110
+ */
111
+ export function defineWebViteConfig(options = {}) {
112
+ const plugins = [...(options.plugins ?? [])];
113
+ if (options.spaShell !== undefined) {
114
+ plugins.push(spaShellEmbedPlugin(options.spaShell));
115
+ }
116
+ return defineConfig({
117
+ ...(plugins.length > 0 && { plugins }),
118
+ ...(options.proxy !== undefined && { server: { proxy: { ...options.proxy } } }),
119
+ build: {
120
+ outDir: options.outDir ?? 'dist',
121
+ emptyOutDir: true,
122
+ },
123
+ });
124
+ }
125
+ //# sourceMappingURL=vite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
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
+ import { defineConfig, type Plugin } from 'vitest/config';
29
+ import type { CoverageThresholds, VitestAlias } from './vitest';
30
+ /**
31
+ * What `defineWebVitestConfig` accepts.
32
+ */
33
+ export interface WebVitestConfigOptions {
34
+ /**
35
+ * Vite plugins the SPA needs — `@vitejs/plugin-react` for a React app. Passed
36
+ * by the consumer rather than imported here, because the kit must stay
37
+ * zero-dependency and the plugin belongs to the app, not the harness.
38
+ */
39
+ readonly plugins?: readonly Plugin[];
40
+ /**
41
+ * Extra resolver entries.
42
+ */
43
+ readonly aliases?: readonly VitestAlias[];
44
+ /**
45
+ * The repo root, as a file URL.
46
+ */
47
+ readonly root?: string;
48
+ /**
49
+ * Which suites to collect. Defaults to both shapes the source repos use.
50
+ */
51
+ readonly include?: readonly string[];
52
+ /**
53
+ * Coverage floors. Omitted by default; see `defineUnitVitestConfig`.
54
+ */
55
+ readonly thresholds?: CoverageThresholds;
56
+ /**
57
+ * Where the coverage report is written. Separate from the unit config's, so
58
+ * the two gates never overwrite each other's report.
59
+ */
60
+ readonly reportsDirectory?: string;
61
+ }
62
+ /**
63
+ * Build the SPA's test config.
64
+ *
65
+ * ```ts
66
+ * // vitest.web.config.mts
67
+ * import react from '@vitejs/plugin-react';
68
+ * import { defineWebVitestConfig } from '@rexezuge/tooling/vitest-web';
69
+ * export default defineWebVitestConfig({ plugins: [react()] });
70
+ * ```
71
+ */
72
+ export declare function defineWebVitestConfig(options?: WebVitestConfigOptions): ReturnType<typeof defineConfig>;
73
+ //# sourceMappingURL=vitest-web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest-web.d.ts","sourceRoot":"","sources":["../src/vitest-web.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C;;OAEG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;OAEG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IACzC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAQD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CA2B3G"}
@@ -0,0 +1,72 @@
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
+ import { defineConfig } from 'vitest/config';
29
+ import { fileURLToPath } from 'node:url';
30
+ /**
31
+ * The default suite globs: the root-suite `web-*` convention and the
32
+ * bundled-under-`apps/web` convention, so either layout works unchanged.
33
+ */
34
+ const DEFAULT_INCLUDE = ['test/**/web-*.test.{ts,tsx}', 'apps/web/**/*.test.{ts,tsx}'];
35
+ /**
36
+ * Build the SPA's test config.
37
+ *
38
+ * ```ts
39
+ * // vitest.web.config.mts
40
+ * import react from '@vitejs/plugin-react';
41
+ * import { defineWebVitestConfig } from '@rexezuge/tooling/vitest-web';
42
+ * export default defineWebVitestConfig({ plugins: [react()] });
43
+ * ```
44
+ */
45
+ export function defineWebVitestConfig(options = {}) {
46
+ const root = options.root ?? new URL('.', import.meta.url).href;
47
+ const webSrc = fileURLToPath(new URL('apps/web/src', root));
48
+ const aliases = [{ find: /^@\//, replacement: `${webSrc}/` }, ...(options.aliases ?? [])];
49
+ return defineConfig({
50
+ ...(options.plugins !== undefined && { plugins: [...options.plugins] }),
51
+ resolve: { alias: aliases },
52
+ test: {
53
+ globals: true,
54
+ environment: 'jsdom',
55
+ include: [...(options.include ?? DEFAULT_INCLUDE)],
56
+ // Threads, bounded, rather than one forked worker per file — see the
57
+ // header. A custom `exclude` replaces Vitest's defaults, so `node_modules`
58
+ // must be re-listed.
59
+ pool: 'threads',
60
+ exclude: ['**/node_modules/**', '**/dist/**', 'test/integration/**'],
61
+ coverage: {
62
+ provider: 'v8',
63
+ reporter: ['text', 'lcov'],
64
+ reportsDirectory: options.reportsDirectory ?? './coverage-spa',
65
+ include: ['apps/web/src/**/*.{ts,tsx}'],
66
+ exclude: ['**/*.test.{ts,tsx}', '**/*.d.ts', '**/types.ts', '**/generated/**'],
67
+ ...(options.thresholds !== undefined && { thresholds: { ...options.thresholds } }),
68
+ },
69
+ },
70
+ });
71
+ }
72
+ //# sourceMappingURL=vitest-web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest-web.js","sourceRoot":"","sources":["../src/vitest-web.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAe,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAoCzC;;;GAGG;AACH,MAAM,eAAe,GAAsB,CAAC,6BAA6B,EAAE,6BAA6B,CAAC,CAAC;AAE1G;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAAkC,EAAE;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAChE,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAkB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;IAEzG,OAAO,YAAY,CAAC;QAClB,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACvE,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;QAC3B,IAAI,EAAE;YACJ,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,OAAO;YACpB,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC,CAAC;YAClD,qEAAqE;YACrE,2EAA2E;YAC3E,qBAAqB;YACrB,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC,oBAAoB,EAAE,YAAY,EAAE,qBAAqB,CAAC;YACpE,QAAQ,EAAE;gBACR,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;gBAC1B,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,gBAAgB;gBAC9D,OAAO,EAAE,CAAC,4BAA4B,CAAC;gBACvC,OAAO,EAAE,CAAC,oBAAoB,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,CAAC;gBAC9E,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;aACnF;SACF;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,92 @@
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
+ import { defineConfig } from 'vitest/config';
33
+ /**
34
+ * One `{ find, replacement }` pair for Vitest's resolver.
35
+ */
36
+ export interface VitestAlias {
37
+ readonly find: string | RegExp;
38
+ readonly replacement: string;
39
+ }
40
+ /**
41
+ * Coverage thresholds, in the four figures Vitest gates on.
42
+ */
43
+ export interface CoverageThresholds {
44
+ readonly statements?: number;
45
+ readonly branches?: number;
46
+ readonly functions?: number;
47
+ readonly lines?: number;
48
+ }
49
+ /**
50
+ * What `defineUnitVitestConfig` accepts. Every field is optional: the defaults
51
+ * cover the canonical monorepo layout, and a consumer overrides only what its
52
+ * tree actually differs in.
53
+ */
54
+ export interface UnitVitestConfigOptions {
55
+ /**
56
+ * Extra resolver entries, applied after the canonical ones.
57
+ */
58
+ readonly aliases?: readonly VitestAlias[];
59
+ /**
60
+ * Where the repo root is, as a file URL. Defaults to the directory this
61
+ * factory is *called* from, which is the repo root's `vitest.config.mts`.
62
+ */
63
+ readonly root?: string;
64
+ /**
65
+ * Coverage `include` globs. Defaults to the worker plus every workspace
66
+ * package's `src`.
67
+ */
68
+ readonly coverageInclude?: readonly string[];
69
+ /**
70
+ * Coverage `exclude` globs, merged with the canonical set.
71
+ */
72
+ readonly coverageExclude?: readonly string[];
73
+ /**
74
+ * Coverage floors. Omitted by default; a repo that wants them sets them.
75
+ */
76
+ readonly thresholds?: CoverageThresholds;
77
+ /**
78
+ * Where the coverage report is written.
79
+ */
80
+ readonly reportsDirectory?: string;
81
+ }
82
+ /**
83
+ * Build the unit-test config.
84
+ *
85
+ * ```ts
86
+ * // vitest.config.mts
87
+ * import { defineUnitVitestConfig } from '@rexezuge/tooling/vitest';
88
+ * export default defineUnitVitestConfig();
89
+ * ```
90
+ */
91
+ export declare function defineUnitVitestConfig(options?: UnitVitestConfigOptions): ReturnType<typeof defineConfig>;
92
+ //# sourceMappingURL=vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;OAEG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;OAEG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IACzC;;OAEG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAmDD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAuC7G"}
package/dist/vitest.js ADDED
@@ -0,0 +1,128 @@
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
+ import { defineConfig } from 'vitest/config';
33
+ import { fileURLToPath } from 'node:url';
34
+ /**
35
+ * The SPA's suites, excluded here so the two configs partition the suite.
36
+ *
37
+ * Every SPA suite is named `web-*`. `defineWebVitestConfig()` includes the same
38
+ * glob, so a file is collected by exactly one config.
39
+ */
40
+ const SPA_FILES = 'test/**/web-*.test.{ts,tsx}';
41
+ /**
42
+ * The canonical alias set.
43
+ *
44
+ * Built relative to the consumer's repo root rather than the kit's directory,
45
+ * so the factory describes *their* layout. A Vitest config is a standalone file
46
+ * with no module graph to share through, and a suite resolving a workspace
47
+ * import differently from its sibling fails in a way that looks like a product
48
+ * bug — which is why these are stated once, here, rather than duplicated.
49
+ */
50
+ function canonicalAliases(root) {
51
+ const at = (relative) => fileURLToPath(new URL(relative, root));
52
+ return [
53
+ { find: 'cloudflare:workers', replacement: at('test/mocks/cloudflare-workers.ts') },
54
+ { find: 'cloudflare:sockets', replacement: at('test/mocks/cloudflare-sockets.ts') },
55
+ { find: /^@\//, replacement: `${at('apps/api/src')}/` },
56
+ { find: /^~\//, replacement: `${at('apps/web/src')}/` },
57
+ ];
58
+ }
59
+ /**
60
+ * Coverage `exclude` globs converged from the source repos.
61
+ *
62
+ * The categories: build and test tooling (`scripts/` grows with project surface,
63
+ * not complexity, and the god-file guard excludes it for the same reason), test
64
+ * files, declarations, re-export barrels (`index.ts` carries no logic of its
65
+ * own), model records, generated files (the SPA shell is a one-line HTML blob
66
+ * with no logic to exercise), and type-only modules.
67
+ */
68
+ const CANONICAL_COVERAGE_EXCLUDE = [
69
+ 'scripts/**',
70
+ '**/*.test.ts',
71
+ '**/*.d.ts',
72
+ '**/index.ts',
73
+ '**/types.d.ts',
74
+ '**/model/**',
75
+ '**/generated/**',
76
+ '**/D1Types.ts',
77
+ '**/ServiceEnv.ts',
78
+ '**/env.d.ts',
79
+ ];
80
+ /**
81
+ * Build the unit-test config.
82
+ *
83
+ * ```ts
84
+ * // vitest.config.mts
85
+ * import { defineUnitVitestConfig } from '@rexezuge/tooling/vitest';
86
+ * export default defineUnitVitestConfig();
87
+ * ```
88
+ */
89
+ export function defineUnitVitestConfig(options = {}) {
90
+ const root = options.root ?? new URL('.', import.meta.url).href;
91
+ const coverageInclude = options.coverageInclude ?? ['apps/api/src/**/*.ts', 'apps/background/src/**/*.ts', 'packages/**/src/**/*.ts'];
92
+ const coverageExclude = [...CANONICAL_COVERAGE_EXCLUDE, ...(options.coverageExclude ?? [])];
93
+ const aliases = [...canonicalAliases(root), ...(options.aliases ?? [])];
94
+ return defineConfig({
95
+ resolve: { alias: aliases },
96
+ test: {
97
+ globals: true,
98
+ environment: 'node',
99
+ include: ['test/**/*.test.{ts,tsx}'],
100
+ // `test` is a workspace project and therefore has its own node_modules;
101
+ // without the re-list, the glob reaches into it.
102
+ exclude: ['**/node_modules/**', '**/dist/**', 'test/integration/**', SPA_FILES],
103
+ server: {
104
+ deps: {
105
+ // A `node_modules` dependency is externalized by default, so Node's
106
+ // own ESM loader resolves a `cloudflare:workers` specifier and rejects
107
+ // the scheme before `resolve.alias` is ever consulted. Inlining routes
108
+ // it back through Vite, where the alias applies.
109
+ inline: ['dofs'],
110
+ },
111
+ },
112
+ coverage: {
113
+ provider: 'v8',
114
+ // Vitest 4 removed `coverage.all`; an explicit `include` now reports
115
+ // every matching file whether or not a test imported it, which is the
116
+ // behaviour the source repos' `all: true` was there to get: a brand-new
117
+ // module with no test is counted as zero rather than being invisible,
118
+ // which is the exact regression this gate exists to catch.
119
+ reporter: ['text', 'lcov', 'html'],
120
+ reportsDirectory: options.reportsDirectory ?? './coverage',
121
+ include: [...coverageInclude],
122
+ exclude: coverageExclude,
123
+ ...(options.thresholds !== undefined && { thresholds: { ...options.thresholds } }),
124
+ },
125
+ },
126
+ });
127
+ }
128
+ //# sourceMappingURL=vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"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"}