@ultimat3/cli 23.0.0 → 25.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 (296) hide show
  1. package/CLAUDE.md +28 -13
  2. package/README.md +32 -7
  3. package/package.json +29 -29
  4. package/src/admin-catalog-keys.ts +2 -2
  5. package/src/affected.ts +30 -36
  6. package/src/api-registration.ts +89 -0
  7. package/src/app-artifacts.ts +1 -1
  8. package/src/app-auth.ts +4 -27
  9. package/src/app-boundaries.ts +17 -4
  10. package/src/app-config-load.ts +51 -0
  11. package/src/app-env.ts +26 -25
  12. package/src/app-load.ts +38 -14
  13. package/src/app-manifest.ts +4 -10
  14. package/src/app-mcp.ts +31 -46
  15. package/src/app-name.ts +52 -0
  16. package/src/app-permissions-borrowed.ts +123 -0
  17. package/src/app-permissions-site.ts +48 -0
  18. package/src/app-permissions.ts +7 -17
  19. package/src/app-root.ts +9 -5
  20. package/src/bin.ts +3 -7
  21. package/src/browser-launcher.ts +4 -2
  22. package/src/budgets-charged.ts +60 -0
  23. package/src/budgets-scan.ts +76 -0
  24. package/src/budgets-unmeasured.ts +91 -0
  25. package/src/budgets.ts +117 -154
  26. package/src/cdp-shot-driver.ts +20 -1
  27. package/src/cdp-shot-watch.ts +35 -31
  28. package/src/ci-log.ts +0 -0
  29. package/src/cmd-auth-spec.ts +16 -0
  30. package/src/cmd-auth.ts +65 -0
  31. package/src/cmd-build-spec.ts +7 -1
  32. package/src/cmd-build.ts +151 -22
  33. package/src/cmd-db-backfill.ts +26 -13
  34. package/src/cmd-db-branch.ts +2 -2
  35. package/src/cmd-db.ts +10 -6
  36. package/src/cmd-deploy-compose.ts +94 -0
  37. package/src/cmd-deploy-drain.ts +48 -0
  38. package/src/cmd-deploy-helm.ts +7 -6
  39. package/src/cmd-deploy-start-first.ts +178 -0
  40. package/src/cmd-deploy.ts +158 -36
  41. package/src/cmd-dev.ts +18 -356
  42. package/src/cmd-doctor-spec.ts +2 -9
  43. package/src/cmd-doctor.ts +57 -80
  44. package/src/cmd-env.ts +55 -16
  45. package/src/cmd-generate.ts +89 -32
  46. package/src/cmd-help.ts +19 -6
  47. package/src/cmd-i18n.ts +21 -17
  48. package/src/cmd-jobs-spec.ts +7 -22
  49. package/src/cmd-jobs.ts +33 -140
  50. package/src/cmd-manifest-spec.ts +1 -1
  51. package/src/cmd-manifest.ts +6 -25
  52. package/src/cmd-mcp.ts +71 -22
  53. package/src/cmd-new.ts +36 -8
  54. package/src/cmd-planned.ts +24 -8
  55. package/src/cmd-routes.ts +15 -5
  56. package/src/cmd-secrets.ts +47 -6
  57. package/src/cmd-shot-matrix.ts +22 -7
  58. package/src/cmd-shot-spec.ts +10 -2
  59. package/src/cmd-shot.ts +46 -41
  60. package/src/cmd-tasks.ts +2 -2
  61. package/src/cmd-test-spec.ts +1 -7
  62. package/src/cmd-test.ts +14 -30
  63. package/src/cmd-verify.ts +4 -4
  64. package/src/coverage-lcov.ts +4 -2
  65. package/src/db-backfill.ts +2 -2
  66. package/src/db-replay-engine.ts +4 -10
  67. package/src/db-subscribes.ts +27 -16
  68. package/src/dev-boot.ts +290 -0
  69. package/src/dev-child-watch.ts +63 -12
  70. package/src/dev-dashboard-guard.ts +76 -0
  71. package/src/dev-dashboard.ts +56 -30
  72. package/src/dev-lock.ts +100 -31
  73. package/src/dev-rebuild.ts +80 -0
  74. package/src/dev-restart-retire.ts +46 -0
  75. package/src/dev-route-table.ts +30 -8
  76. package/src/dev-supervisor.ts +114 -22
  77. package/src/dev-traces.ts +53 -20
  78. package/src/dispatch.ts +17 -13
  79. package/src/doctor-app-url.ts +39 -0
  80. package/src/doctor-auth.ts +95 -0
  81. package/src/doctor-offline.ts +6 -5
  82. package/src/document-options.ts +7 -0
  83. package/src/drift-replica-identity.ts +42 -0
  84. package/src/drift.ts +29 -6
  85. package/src/error-catalog.ts +6 -9
  86. package/src/error-codes.ts +29 -3
  87. package/src/error-page-csp.ts +14 -15
  88. package/src/error-unthrown.ts +29 -11
  89. package/src/errors.ts +54 -83
  90. package/src/exec.ts +8 -0
  91. package/src/favicon.ts +2 -2
  92. package/src/fix-command.ts +9 -1
  93. package/src/framework-env.ts +148 -0
  94. package/src/framework-schema-apply.ts +78 -0
  95. package/src/framework-schema-stamp.ts +96 -0
  96. package/src/framework-schema.ts +25 -3
  97. package/src/generate-catalog-locales.ts +79 -0
  98. package/src/generate-files.ts +4 -3
  99. package/src/generate-kinds.ts +18 -0
  100. package/src/generate-plural.ts +50 -0
  101. package/src/generate-shadow.ts +95 -0
  102. package/src/generate-write.ts +47 -13
  103. package/src/gh-target.ts +12 -7
  104. package/src/hold.ts +56 -1
  105. package/src/i18n-audit.ts +25 -9
  106. package/src/i18n-index.ts +5 -6
  107. package/src/i18n-registration.ts +7 -9
  108. package/src/import-scan.ts +28 -3
  109. package/src/index.ts +2 -6
  110. package/src/island-bundle.ts +57 -79
  111. package/src/island-capture.ts +1 -1
  112. package/src/island-duplicate-refusal.ts +57 -0
  113. package/src/island-duplicates.ts +221 -0
  114. package/src/island-entry-missing.ts +64 -0
  115. package/src/island-harness-route.ts +20 -4
  116. package/src/island-harness-script.ts +23 -2
  117. package/src/island-identity.ts +17 -14
  118. package/src/island-package-dedupe.ts +150 -0
  119. package/src/island-realtime.ts +36 -16
  120. package/src/island-routes.ts +2 -2
  121. package/src/island-runtime.ts +241 -0
  122. package/src/island-sources.ts +105 -0
  123. package/src/island-states-load.ts +6 -0
  124. package/src/island-store.ts +90 -6
  125. package/src/jobs-json.ts +0 -26
  126. package/src/jobs-report.ts +36 -20
  127. package/src/local-cli-handoff.ts +90 -0
  128. package/src/loopback-host.ts +13 -0
  129. package/src/manifest-staleness.ts +62 -0
  130. package/src/mcp-errors.ts +20 -4
  131. package/src/mcp-host.ts +22 -14
  132. package/src/mcp-test-run.ts +38 -0
  133. package/src/mcp-ui-inspect.ts +3 -2
  134. package/src/mcp-ui-interact.ts +3 -2
  135. package/src/mcp-ui.ts +8 -7
  136. package/src/measure-scope.ts +15 -10
  137. package/src/messages.ts +29 -16
  138. package/src/metrics-endpoint.ts +28 -3
  139. package/src/migration-extensions.ts +1 -1
  140. package/src/otlp-export.ts +2 -2
  141. package/src/output.ts +24 -0
  142. package/src/page-navigation.ts +7 -23
  143. package/src/page-speculation.ts +19 -31
  144. package/src/parse.ts +0 -8
  145. package/src/permission-grants.ts +3 -3
  146. package/src/posix-path.ts +30 -0
  147. package/src/prerender-locales.ts +6 -4
  148. package/src/prerender-out.ts +34 -7
  149. package/src/prerender-report.ts +58 -0
  150. package/src/prerender-served.ts +52 -0
  151. package/src/prerender.ts +71 -103
  152. package/src/pwa-artifacts.ts +33 -94
  153. package/src/pwa-manifest-locales.ts +9 -9
  154. package/src/registry.ts +2 -0
  155. package/src/retry-memo.ts +1 -1
  156. package/src/revalidated-response.ts +4 -3
  157. package/src/role-realtime.ts +7 -1
  158. package/src/role-replicator.ts +124 -29
  159. package/src/role-start.ts +69 -85
  160. package/src/role-sync.ts +119 -25
  161. package/src/root-env.ts +157 -28
  162. package/src/route-islands.ts +69 -0
  163. package/src/runtime-admin.ts +4 -1
  164. package/src/runtime-assets.ts +26 -9
  165. package/src/runtime-bindings.ts +27 -7
  166. package/src/runtime-cache.ts +46 -55
  167. package/src/runtime-hooks.ts +2 -1
  168. package/src/runtime-idempotency-scope.ts +23 -0
  169. package/src/runtime-isr-outcome.ts +100 -0
  170. package/src/runtime-isr.ts +36 -0
  171. package/src/runtime-jobs.ts +76 -41
  172. package/src/runtime-live-feed.ts +1 -1
  173. package/src/runtime-mail.ts +26 -0
  174. package/src/runtime-mfa-warning.ts +29 -0
  175. package/src/runtime-notify-retention.ts +10 -46
  176. package/src/runtime-overrides.ts +4 -4
  177. package/src/runtime-purge.ts +38 -16
  178. package/src/runtime-queue.ts +43 -22
  179. package/src/runtime-realtime.ts +11 -45
  180. package/src/runtime-render.ts +136 -114
  181. package/src/runtime-replica.ts +3 -8
  182. package/src/runtime-services.ts +49 -26
  183. package/src/runtime-storage-upload.ts +2 -2
  184. package/src/runtime-storage.ts +13 -8
  185. package/src/schema-diff.ts +16 -0
  186. package/src/schema-drift.ts +45 -15
  187. package/src/secrets-rotation.ts +8 -9
  188. package/src/seo-routes.ts +80 -9
  189. package/src/serve-boot.ts +51 -12
  190. package/src/serve-drain.ts +11 -33
  191. package/src/serve-env.ts +9 -6
  192. package/src/serve-retire.ts +95 -0
  193. package/src/serve-web.ts +31 -15
  194. package/src/serve.ts +27 -10
  195. package/src/server-barrels.ts +6 -3
  196. package/src/shot-browser.ts +1 -1
  197. package/src/shot-locale.ts +6 -33
  198. package/src/shot-settle.ts +3 -3
  199. package/src/shot-theme.ts +1 -1
  200. package/src/shot-viewport.ts +152 -0
  201. package/src/signal-shred.ts +26 -12
  202. package/src/site-asset-routes.ts +2 -2
  203. package/src/site-assets.ts +3 -5
  204. package/src/site-config.ts +9 -48
  205. package/src/site-seo.ts +7 -5
  206. package/src/sitemap-lastmod.ts +5 -0
  207. package/src/solid-loader.ts +12 -3
  208. package/src/source-files.ts +19 -3
  209. package/src/static-report.ts +5 -0
  210. package/src/stored-object-headers.ts +43 -0
  211. package/src/style-routes.ts +2 -2
  212. package/src/sw-artifacts.ts +8 -8
  213. package/src/sw-precache-plan.ts +34 -4
  214. package/src/sync-authenticator.ts +3 -3
  215. package/src/sync-url.ts +3 -3
  216. package/src/tasks-facts.ts +3 -29
  217. package/src/templates/action.ts +7 -4
  218. package/src/templates/backfill.ts +4 -4
  219. package/src/templates/entity.ts +2 -2
  220. package/src/templates/github/ci.yml.ts +28 -18
  221. package/src/templates/github/image.yml.ts +92 -0
  222. package/src/templates/github/runner.ts +18 -0
  223. package/src/templates/guard-bare-error.ts +16 -2
  224. package/src/templates/guard-unzoned-date.ts +56 -6
  225. package/src/templates/index.ts +1 -1
  226. package/src/templates/job-body-test.ts +2 -2
  227. package/src/templates/job.ts +6 -6
  228. package/src/templates/naming.ts +28 -0
  229. package/src/templates/query.ts +9 -8
  230. package/src/templates/resource-page.ts +9 -7
  231. package/src/templates/resource-service.ts +2 -2
  232. package/src/templates/route.ts +26 -14
  233. package/src/templates/scaffold-api.ts +2 -3
  234. package/src/templates/scaffold-app.ts +8 -3
  235. package/src/templates/scaffold-auth.ts +205 -70
  236. package/src/templates/scaffold-bin.ts +122 -0
  237. package/src/templates/scaffold-claude-commands.ts +11 -11
  238. package/src/templates/scaffold-container-compose.ts +10 -1
  239. package/src/templates/scaffold-container.ts +16 -8
  240. package/src/templates/scaffold-dashboard-bare.ts +8 -4
  241. package/src/templates/scaffold-dashboard-example-tests.ts +208 -0
  242. package/src/templates/scaffold-dashboard-example.ts +112 -172
  243. package/src/templates/scaffold-dashboard-shared.ts +62 -1
  244. package/src/templates/scaffold-dashboard.ts +9 -5
  245. package/src/templates/scaffold-db-client.ts +4 -2
  246. package/src/templates/scaffold-db-package.ts +2 -2
  247. package/src/templates/scaffold-docs.ts +20 -77
  248. package/src/templates/scaffold-domain-package.ts +0 -4
  249. package/src/templates/scaffold-entries.ts +4 -3
  250. package/src/templates/scaffold-env.ts +6 -3
  251. package/src/templates/scaffold-gitattributes.ts +59 -0
  252. package/src/templates/scaffold-helm-fences.ts +76 -0
  253. package/src/templates/scaffold-helm-templates.ts +100 -19
  254. package/src/templates/scaffold-helm.ts +62 -5
  255. package/src/templates/scaffold-http.ts +1 -1
  256. package/src/templates/scaffold-i18n.ts +15 -2
  257. package/src/templates/scaffold-icon.ts +2 -2
  258. package/src/templates/scaffold-repo.ts +27 -16
  259. package/src/templates/scaffold-shell.ts +55 -37
  260. package/src/test-counts.ts +2 -6
  261. package/src/test-ignores.ts +25 -0
  262. package/src/test-only-shipped.ts +193 -0
  263. package/src/test-passes.ts +0 -15
  264. package/src/test-select.ts +7 -15
  265. package/src/test-shards.ts +40 -95
  266. package/src/test-workers.ts +26 -4
  267. package/src/theme-boot.ts +8 -33
  268. package/src/theme-brand.ts +66 -0
  269. package/src/trusted-hops-error.ts +17 -0
  270. package/src/trusted-hops.ts +49 -0
  271. package/src/unscanned-admin.ts +2 -2
  272. package/src/unthrown-codes.ts +26 -0
  273. package/src/verify-checks.ts +29 -48
  274. package/src/verify-coverage-run.ts +3 -2
  275. package/src/verify-deadline.ts +207 -33
  276. package/src/verify-merge.ts +108 -43
  277. package/src/verify-part-step.ts +98 -0
  278. package/src/verify-role-load-probe.ts +12 -2
  279. package/src/verify-role-load.ts +9 -4
  280. package/src/verify-run.ts +32 -8
  281. package/src/verify-shard.ts +13 -2
  282. package/src/verify-stalled.ts +152 -0
  283. package/src/verify-step.ts +2 -0
  284. package/src/verify-test-run.ts +3 -2
  285. package/src/verify-tests.ts +5 -3
  286. package/src/worker-bundle.ts +2 -2
  287. package/src/workspace-checks.ts +53 -7
  288. package/src/workspace-graph.ts +3 -2
  289. package/src/write-line.ts +82 -7
  290. package/src/browser-launcher-fake-html.ts +0 -80
  291. package/src/browser-launcher-fake.ts +0 -168
  292. package/src/build-errors.ts +0 -54
  293. package/src/jobs-drain.ts +0 -163
  294. package/src/scaffold-typecheck.ts +0 -227
  295. package/src/thrown-by.ts +0 -24
  296. package/src/verify-errors.ts +0 -58
@@ -1,14 +1,10 @@
1
1
  // The public origin and the crawler rules out of `app.config.ts` — `site.origin` and
2
2
  // `seo.robots.disallow` — and the one resolution of "which origin is this site served on" every
3
- // absolute URL a document, a sitemap or a robots file carries is built against. Sibling of
4
- // `app-auth.ts`'s `loadSignInPath`, and it imports the config for the same reason.
3
+ // absolute URL a document, a sitemap or a robots file carries is built against. Read off the one
4
+ // loader (`app-config-load.ts`).
5
5
 
6
- // why: Bun exposes no path-join primitive, and the config path is app-root-relative.
7
- import { join } from 'node:path';
8
- import type { AppConfig, Environment, SeoSitemapConfig, SitemapLastmod } from '@ultimat3/core';
9
- import { SITEMAP_LASTMOD_SOURCES } from '@ultimat3/core';
10
- import { APP_CONFIG_EXPORT } from './app-auth';
11
- import { APP_CONFIG_FILE } from './app-root';
6
+ import type { Environment, SeoSitemapConfig } from '@ultimat3/core';
7
+ import { loadAppConfig } from './app-config-load';
12
8
 
13
9
  export interface SiteSettings {
14
10
  /** `site.origin`, trailing slash removed; `null` when the app declares none. */
@@ -25,51 +21,16 @@ export const NO_SITE_SETTINGS: SiteSettings = {
25
21
  sitemap: { extra: [], lastmod: 'none' },
26
22
  };
27
23
 
28
- const isRecord = (value: unknown): value is Record<string, unknown> =>
29
- typeof value === 'object' && value !== null;
30
-
31
- /**
32
- * Structural, never `instanceof`, for `loadSignInPath`'s reason: a config that resolved through an
33
- * older core simply has no `site` section, and that is the "not declared" answer, not a crash.
34
- */
35
- const hasSiteSections = (value: unknown): value is Pick<AppConfig, 'site' | 'seo'> =>
36
- isRecord(value) &&
37
- isRecord(value['site']) &&
38
- isRecord(value['seo']) &&
39
- isRecord(value['seo']['robots']);
40
-
41
24
  const trimSlash = (origin: string): string => origin.replace(/\/+$/, '');
42
25
 
43
26
  export async function loadSiteSettings(root: string): Promise<SiteSettings> {
44
- const configPath = join(root, APP_CONFIG_FILE);
45
- if (!(await Bun.file(configPath).exists())) return NO_SITE_SETTINGS;
46
- const module = (await import(configPath)) as Record<string, unknown>;
47
- const config = module[APP_CONFIG_EXPORT];
48
- if (!hasSiteSections(config)) return NO_SITE_SETTINGS;
49
- const origin = config.site.origin;
27
+ const config = await loadAppConfig(root);
28
+ if (config === undefined) return NO_SITE_SETTINGS;
29
+ const { origin } = config.site;
50
30
  return {
51
- origin: typeof origin === 'string' && origin !== '' ? trimSlash(origin) : null,
31
+ origin: origin === null ? null : trimSlash(origin),
52
32
  disallow: [...config.seo.robots.disallow],
53
- sitemap: sitemapOf(config.seo),
54
- };
55
- }
56
-
57
- /**
58
- * Structural for `hasSiteSections`' reason: a config resolved through a core older than
59
- * `seo.sitemap` has no such key, and that is the default, not a crash.
60
- */
61
- function sitemapOf(seo: unknown): SeoSitemapConfig {
62
- const declared = isRecord(seo) ? seo['sitemap'] : undefined;
63
- if (!isRecord(declared)) return NO_SITE_SETTINGS.sitemap;
64
- const extra = declared['extra'];
65
- const lastmod = declared['lastmod'];
66
- return {
67
- extra: Array.isArray(extra)
68
- ? extra.filter((path): path is string => typeof path === 'string')
69
- : [],
70
- lastmod: SITEMAP_LASTMOD_SOURCES.includes(lastmod as SitemapLastmod)
71
- ? (lastmod as SitemapLastmod)
72
- : 'none',
33
+ sitemap: config.seo.sitemap,
73
34
  };
74
35
  }
75
36
 
package/src/site-seo.ts CHANGED
@@ -4,7 +4,8 @@
4
4
 
5
5
  import type { Environment, SeoSitemapConfig } from '@ultimat3/core';
6
6
  import { UltimateError } from '@ultimat3/core';
7
- import { localeConfig, localizedPath, routedLocales, unlocalizedPath } from '@ultimat3/i18n';
7
+ import { localizedPath, unlocalizedPath } from '@ultimat3/i18n';
8
+ import { appLocaleSet } from '@ultimat3/i18n/app-catalogs';
8
9
  import type { RouteEntry } from '@ultimat3/render';
9
10
  import { routeEntries } from '@ultimat3/render';
10
11
  import { enumeratePrerender, fillPath } from '@ultimat3/render/server';
@@ -150,10 +151,11 @@ export async function siteSeo(options: SiteSeoOptions): Promise<SiteSeo> {
150
151
  // Every routed locale, with `xhtml:link` alternates: one `<url>` per page per locale, each
151
152
  // naming the whole cluster and `x-default`. A single-locale app passes none and gets the plain
152
153
  // urlset it always had — an alternates cluster of one is noise.
153
- const locales = routedLocales();
154
- const defaultLocale = localeConfig().fallback;
155
- const settings = options.sitemap ?? NO_SITEMAP_SETTINGS;
154
+ // The app's declared set — the one the manifest, the worker and the prerender read — never the
155
+ // ambient locale config, which a build process has not configured.
156
156
  const root = options.root ?? process.cwd();
157
+ const { locales, defaultLocale } = await appLocaleSet(root);
158
+ const settings = options.sitemap ?? NO_SITEMAP_SETTINGS;
157
159
  const stamp: Stamp = (file) => {
158
160
  const lastmod = lastmodOf(file, settings.lastmod, root);
159
161
  return lastmod === undefined ? {} : { lastmod };
@@ -173,7 +175,7 @@ export async function siteSeo(options: SiteSeoOptions): Promise<SiteSeo> {
173
175
  localizedPath(path, locale, defaultLocale),
174
176
  }),
175
177
  });
176
- // Past 50,000 URLs `files` are `/sitemap-N.xml` and the index is `/sitemap.xml`; below it,
178
+ // Past 50,000 URLs `files` are `/sitemaps/<n>.xml` and the index is `/sitemap.xml`; below it,
177
179
  // `files` is that one file and there is no index.
178
180
  const sitemaps = sitemap.index === undefined ? sitemap.files : [sitemap.index, ...sitemap.files];
179
181
  const robots = buildRobots({
@@ -14,6 +14,9 @@ let builtAt: string | undefined;
14
14
 
15
15
  const cache = new Map<string, string | undefined>();
16
16
 
17
+ /** One `git log -1` over one path; a synchronous wait past this blocks the request serving it. */
18
+ const GIT_DATE_TIMEOUT_MS = 5_000;
19
+
17
20
  /**
18
21
  * The `<lastmod>` for one route file, W3C Datetime (ISO 8601, UTC), or `undefined` when the source
19
22
  * yields none — a sitemap entry without `<lastmod>` is valid, a wrong one misleads a crawler.
@@ -46,6 +49,8 @@ function gitDate(file: string, root: string): string | undefined {
46
49
  cwd: root,
47
50
  stdout: 'pipe',
48
51
  stderr: 'ignore',
52
+ // A wedged `git` (a lock, a credential prompt) answers no date: the mtime fallback below.
53
+ timeout: GIT_DATE_TIMEOUT_MS,
49
54
  });
50
55
  if (run.exitCode !== 0) return undefined;
51
56
  return isoOf(run.stdout.toString().trim());
@@ -73,6 +73,10 @@ interface CacheEntry {
73
73
  */
74
74
  const cache = new Map<string, CacheEntry>();
75
75
 
76
+ /** Transforms running now, and the `prepareStackTrace` the first of them found (see below). */
77
+ let inFlight = 0;
78
+ let foundStackTrace: typeof Error.prepareStackTrace;
79
+
76
80
  /** Test seam: the cache is process-global because the dev server it serves is too. */
77
81
  export function clearIslandTransformCache(): void {
78
82
  cache.clear();
@@ -92,8 +96,12 @@ export async function transformIslandTsx(source: string, path: string): Promise<
92
96
 
93
97
  // Babel installs its own `prepareStackTrace` on the first TRANSFORM — not on import, which is
94
98
  // where this guard was first put — and leaving it installed makes `Error.captureStackTrace`
95
- // strict for every unrelated module loaded later in the same process.
96
- const saved = Error.prepareStackTrace;
99
+ // strict for every unrelated module loaded later in the same process, and names every error in
100
+ // it `Error` (a `TypeError`'s stack then opens `Error: …`). Saved by the FIRST transform in
101
+ // flight and restored by the LAST: `Bun.build` loads a second `.tsx` while the first compiles,
102
+ // and a per-call save taken inside that window saved Babel's own rewriter and restored it last.
103
+ if (inFlight === 0) foundStackTrace = Error.prepareStackTrace;
104
+ inFlight += 1;
97
105
  let code: string | null | undefined;
98
106
  try {
99
107
  // `transformAsync` and never `transformFileAsync`: the latter is gated behind `@babel/core`'s
@@ -111,7 +119,8 @@ export async function transformIslandTsx(source: string, path: string): Promise<
111
119
  });
112
120
  code = result?.code;
113
121
  } finally {
114
- Error.prepareStackTrace = saved;
122
+ inFlight -= 1;
123
+ if (inFlight === 0) Error.prepareStackTrace = foundStackTrace;
115
124
  }
116
125
  // A parse error is NOT caught here: Babel's own message already names the file, the line and the
117
126
  // column ("a.island.tsx: Unexpected token (2:23)"), and `buildOne` wraps whatever escapes in
@@ -3,6 +3,7 @@
3
3
  // means a finding one of them can never see.
4
4
 
5
5
  import { hasPathSegment } from './path-segments';
6
+ import { toPosix } from './posix-path';
6
7
 
7
8
  export const SOURCE_GLOBS = [
8
9
  'packages/*/src/**/*.{ts,tsx}',
@@ -16,6 +17,12 @@ export const SOURCE_GLOBS = [
16
17
  'api/**/*.{ts,tsx}',
17
18
  'shared/**/*.{ts,tsx}',
18
19
  'apps/*/{app,site,api,shared}/**/*.{ts,tsx}',
20
+ // An app's own entry files: authored, shipped, and outside every surface glob above — a 900-line
21
+ // `server.ts` or an unrunnable `fix:` in it was invisible to `filesize` and `errors`.
22
+ // And its guards: run on the gate, held to the error contract, and invisible to `filesize`.
23
+ 'guards/*.ts',
24
+ 'apps/*/server.ts',
25
+ 'apps/*/prerender.ts',
19
26
  ] as const;
20
27
 
21
28
  /**
@@ -32,7 +39,17 @@ export const isGenerated = (path: string): boolean => path.endsWith('.d.ts');
32
39
  export const isTest = (path: string): boolean => /\.test\.tsx?$/.test(path);
33
40
 
34
41
  /**
35
- * Every source file under `root`, repo-relative and deduplicated across the globs — in a SORTED
42
+ * Glob answers as the POSIX paths every consumer keys by, in code-unit order of that spelling.
43
+ * The glob answers in the host's separator: on Windows `guards\untranslated-string.ts` missed
44
+ * `i18n`'s `startsWith('guards/')`, so the example `t('…')` in its fix line became a key every
45
+ * fresh scaffold was missing — and `^packages\/` rules matched nothing at all.
46
+ */
47
+ export function sortedPosix(paths: readonly string[]): string[] {
48
+ return paths.map(toPosix).sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));
49
+ }
50
+
51
+ /**
52
+ * Every source file under `root`, repo-relative, POSIX and deduplicated across the globs — in a SORTED
36
53
  * order per pattern, `As of 2026-09-05`. `Bun.Glob.scan` yields in the filesystem's `readdir`
37
54
  * order, which ext4 hashes: the first offender a scan names was `index.ts` on one machine and
38
55
  * `tools.ts` on a GitHub runner, so a finding's `cause` depended on which disk held the checkout
@@ -46,8 +63,7 @@ export async function* eachSourceFile(root: string): AsyncGenerator<string> {
46
63
  for await (const path of new Bun.Glob(pattern).scan({ cwd: root, absolute: false })) {
47
64
  if (!isVendored(path)) matches.push(path);
48
65
  }
49
- matches.sort((left, right) => (left < right ? -1 : left > right ? 1 : 0));
50
- for (const path of matches) {
66
+ for (const path of sortedPosix(matches)) {
51
67
  if (seen.has(path)) continue;
52
68
  seen.add(path);
53
69
  yield path;
@@ -71,6 +71,11 @@ export type UnmeasuredRoute = {
71
71
  readonly code?: string;
72
72
  readonly cause?: string;
73
73
  readonly fix?: string;
74
+ /**
75
+ * Who the measurement render ran as, labelled (`measure-scope.ts`'s `measurementActorLabel`) —
76
+ * so a refusal OF that actor names it, and the edit that changes it (#675). Never its facts.
77
+ */
78
+ readonly actor?: string;
74
79
  /**
75
80
  * `false` when no build can weigh the route by construction — a `render: 'ssr'` page with params,
76
81
  * which may not declare `prerender()`, so the build holds no value to render it at. Not a defect
@@ -0,0 +1,43 @@
1
+ // How a stored object may be presented when the app's own origin serves it (plan 101, s1-sec L6):
2
+ // inline only for a type a browser renders without running anything, a sandboxed download otherwise. Read by
3
+ // both surfaces that serve stored bytes — `/_storage` (`runtime-storage.ts`) and `/media`.
4
+
5
+ /**
6
+ * Raster images, audio and video: what an `<img>`, `<audio>` or `<video>` presents and nothing
7
+ * executes. NOT `image/svg+xml` (it carries `<script>`), `text/html`, `application/xml` or PDF —
8
+ * any of them opened from the app's origin runs with the app's cookies and its `'self'` sources.
9
+ */
10
+ const INLINE_TYPES: ReadonlySet<string> = new Set([
11
+ 'image/png',
12
+ 'image/jpeg',
13
+ 'image/gif',
14
+ 'image/webp',
15
+ 'image/avif',
16
+ 'audio/mpeg',
17
+ 'audio/ogg',
18
+ 'audio/wav',
19
+ 'audio/webm',
20
+ 'video/mp4',
21
+ 'video/webm',
22
+ 'video/ogg',
23
+ ]);
24
+
25
+ /** `image/PNG; charset=…` is `image/png`: parameters and case never widen the list. */
26
+ const essenceOf = (contentType: string): string =>
27
+ (contentType.split(';')[0] ?? '').trim().toLowerCase();
28
+
29
+ export const isInlineSafe = (contentType: string): boolean =>
30
+ INLINE_TYPES.has(essenceOf(contentType));
31
+
32
+ /**
33
+ * The headers a stored object is served with beyond its type. `attachment` makes a navigation to an
34
+ * uploaded `text/html` a download instead of a page on the app's origin; an embedding element
35
+ * (`<img>`, `<video>`) ignores it, so nothing that displays an object inline stops working. The
36
+ * `sandbox` policy is for the browser that renders it anyway: an opaque origin, no script, no
37
+ * cookies. `@ultimat3/http`'s security stage keeps it and adds the app's policy beside it.
38
+ */
39
+ export function storedObjectHeaders(contentType: string): Readonly<Record<string, string>> {
40
+ return isInlineSafe(contentType)
41
+ ? {}
42
+ : { 'content-disposition': 'attachment', 'content-security-policy': 'sandbox' };
43
+ }
@@ -4,7 +4,7 @@
4
4
  // image.
5
5
 
6
6
  import type { Route, UltimateRequest } from '@ultimat3/http';
7
- import { applyCacheHeaders, json, NO_STORE } from '@ultimat3/http';
7
+ import { applyCacheHeaders, jsonResponse, NO_STORE } from '@ultimat3/http';
8
8
  import type { StyleBundle } from './style-bundle';
9
9
  import { STYLE_BASE_PATH } from './style-bundle';
10
10
 
@@ -32,7 +32,7 @@ export function styleRoutes(source: StyleSource): readonly Route[] {
32
32
  handler: (request: UltimateRequest): Response => {
33
33
  const chunk = source().chunkAt(request.pathname);
34
34
  if (chunk === undefined) {
35
- const missing = json(
35
+ const missing = jsonResponse(
36
36
  {
37
37
  ok: false,
38
38
  error: {
@@ -3,7 +3,6 @@
3
3
  // until #390. Beside `pwa-artifacts.ts` and not inside it: that file needs a root and a config
4
4
  // file, this one needs a booted app and a finished build.
5
5
 
6
- import { localeConfig, routedLocales } from '@ultimat3/i18n';
7
6
  import type { PrecacheManifest } from '@ultimat3/pwa';
8
7
  import { generateServiceWorker } from '@ultimat3/pwa';
9
8
  import type { RouteDescriptor } from '@ultimat3/render';
@@ -12,7 +11,7 @@ import { ISLAND_BASE_PATH } from './island-bundle';
12
11
  import { msg } from './messages';
13
12
  import type { PwaArtifacts } from './pwa-artifacts';
14
13
  import type { StyleBundle } from './style-bundle';
15
- import type { RenderedDocument, RoutedLocales } from './sw-precache-plan';
14
+ import type { RenderedDocument } from './sw-precache-plan';
16
15
  import { localePrefixes, precacheAssets, pwaRoutes } from './sw-precache-plan';
17
16
 
18
17
  export type { RenderedDocument, RoutedLocales } from './sw-precache-plan';
@@ -73,11 +72,6 @@ export interface ServiceWorkerInput {
73
72
  * offline reload that cannot load the boot restores no record and shows the old count.
74
73
  */
75
74
  readonly scripts?: readonly { readonly url: string; readonly bytes: number }[];
76
- /**
77
- * The routed locales, default first. Absent, `@ultimat3/i18n`'s own — the answer the server routes
78
- * with, set once the app's catalogs are loaded, which every caller has done by now.
79
- */
80
- readonly locales?: RoutedLocales;
81
75
  }
82
76
 
83
77
  /**
@@ -157,7 +151,9 @@ export function serviceWorkerArtifacts(
157
151
  // Absent when this pass did not render the fallback (no route serves it — `x doctor` reports
158
152
  // that as `X_PWA_NO_OFFLINE_FALLBACK`), and then `@ultimat3/pwa` falls back to the build id.
159
153
  const fallbackDocument = documents.get(fallback);
160
- const locales = input.locales ?? { routed: routedLocales(), fallback: localeConfig().fallback };
154
+ // The set the manifests were built from (`PwaArtifacts.locales`), never the ambient locale
155
+ // config: one answer, so a locale an installed manifest names is always one the worker prefixes.
156
+ const locales = pwa.locales;
161
157
  const output = generateServiceWorker(
162
158
  pwaRoutes(input.routes, documents, locales),
163
159
  {
@@ -187,6 +183,10 @@ export function serviceWorkerArtifacts(
187
183
  styles: input.styles,
188
184
  scripts: input.scripts ?? [],
189
185
  fallback,
186
+ placeholders: {
187
+ urls: [pwa.offline.image, pwa.offline.font].filter((url): url is string => url !== null),
188
+ buildId: input.buildId,
189
+ },
190
190
  }),
191
191
  },
192
192
  input.buildId,
@@ -7,6 +7,7 @@ import type { PrecacheAsset, PwaRoute } from '@ultimat3/pwa';
7
7
  import { strategyFor } from '@ultimat3/pwa';
8
8
  import type { RouteDescriptor } from '@ultimat3/render';
9
9
  import type { IslandBundle } from './island-bundle';
10
+ import { parseHashedAssetUrl } from './site-assets';
10
11
  import type { StyleBundle } from './style-bundle';
11
12
 
12
13
  /**
@@ -94,6 +95,12 @@ export interface PrecacheAssetInput {
94
95
  readonly scripts: readonly { readonly url: string; readonly bytes: number }[];
95
96
  /** The offline document's path: its islands ride the precache whatever its own route says. */
96
97
  readonly fallback: string;
98
+ /**
99
+ * `pwa.offline.image` / `pwa.offline.font`: the worker answers a failed image or font request
100
+ * from the PRECACHE only, so a placeholder nothing precached is a branch that matches nothing.
101
+ * `buildId` is the revision of one whose bytes this build cannot hash — the offline document's.
102
+ */
103
+ readonly placeholders?: { readonly urls: readonly string[]; readonly buildId: string };
97
104
  }
98
105
 
99
106
  /**
@@ -140,12 +147,35 @@ export function precacheAssets(input: PrecacheAssetInput): readonly PrecacheAsse
140
147
  for (const chunk of input.islands.chunks) {
141
148
  if (wanted.has(chunk.url)) for (const url of chunk.imports) wanted.add(url);
142
149
  }
143
- return [
150
+ const chunks: PrecacheAsset[] = [
144
151
  ...input.islands.chunks.filter((chunk) => wanted.has(chunk.url)),
145
152
  ...input.islands.shared.filter((chunk) => wanted.has(chunk.url)),
146
153
  ...input.styles.chunks,
147
154
  ...input.scripts,
148
- ]
149
- .map((chunk) => ({ url: chunk.url, revision: chunk.url, bytes: chunk.bytes }))
150
- .sort((a, b) => (a.url < b.url ? -1 : a.url > b.url ? 1 : 0));
155
+ ].map((chunk) => ({ url: chunk.url, revision: chunk.url, bytes: chunk.bytes }));
156
+ return [...chunks, ...placeholderAssets(input, new Set(chunks.map((chunk) => chunk.url)))].sort(
157
+ (a, b) => (a.url < b.url ? -1 : a.url > b.url ? 1 : 0),
158
+ );
159
+ }
160
+
161
+ /**
162
+ * The placeholders on the offline document's terms (`buildPrecacheManifest`'s `fallback` entry):
163
+ * never refused here — this pass cannot see the site assets, and the worker installs entry by
164
+ * entry, so a URL that 404s costs that entry and the branch falls through to the 503 — and
165
+ * stamped with the build id unless the URL is content-addressed, which is its own revision. Bytes
166
+ * unknown count 0, as the offline document's do. A chunk already listed keeps its own entry.
167
+ */
168
+ function placeholderAssets(
169
+ input: PrecacheAssetInput,
170
+ listed: ReadonlySet<string>,
171
+ ): PrecacheAsset[] {
172
+ const out = new Map<string, PrecacheAsset>();
173
+ const placeholders = input.placeholders;
174
+ if (placeholders === undefined) return [];
175
+ for (const url of placeholders.urls) {
176
+ if (listed.has(url) || out.has(url)) continue;
177
+ const hashed = parseHashedAssetUrl(url) !== undefined;
178
+ out.set(url, { url, revision: hashed ? url : placeholders.buildId, bytes: 0 });
179
+ }
180
+ return [...out.values()];
151
181
  }
@@ -9,8 +9,8 @@ import { finiteCount, systemClock } from '@ultimat3/core';
9
9
  import type { HttpConfig } from '@ultimat3/http';
10
10
  import {
11
11
  configuredAuthenticator,
12
- createRequestContext,
13
12
  defineHttpConfig,
13
+ requestContext,
14
14
  UltimateRequest,
15
15
  } from '@ultimat3/http';
16
16
  import type { SyncAuthenticator, SyncGrant } from '@ultimat3/realtime/server';
@@ -84,7 +84,7 @@ export interface SyncAuthenticatorOptions {
84
84
  /**
85
85
  * What the sync node is given when the app configured an authenticator, and `undefined` when it
86
86
  * did not — which keeps `x dev` anonymous and makes the node log that it is, exactly as
87
- * `createSyncNode` documents. A stub that answered `{ actor: anonymous }` would look configured.
87
+ * `syncNode` documents. A stub that answered `{ actor: anonymous }` would look configured.
88
88
  *
89
89
  * The grant carries an `expiresAt` and a `refresh`, and both are the app's own resolver asked
90
90
  * again: `configureAuthenticator` says who is dialling, and the only honest way to learn that it
@@ -115,7 +115,7 @@ export function syncAuthenticator(
115
115
  method: credential.method,
116
116
  headers: credential.headers,
117
117
  });
118
- const ctx = createRequestContext({
118
+ const ctx = requestContext({
119
119
  url: new URL(credential.url),
120
120
  method: credential.method,
121
121
  role: 'sync',
package/src/sync-url.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  import { ConfigInvalidError } from '@ultimat3/core';
6
6
 
7
7
  /**
8
- * The sync node's own path (`createSyncNode`'s default in `@ultimat3/realtime`). Same origin by
8
+ * The sync node's own path (`syncNode`'s default in `@ultimat3/realtime`). Same origin by
9
9
  * default because every rung already serves it there: `x dev` and a combined-role container mount
10
10
  * the node on the web port, and `docker/helm`'s ingress routes `/_x/sync` to the `sync` service.
11
11
  */
@@ -43,8 +43,8 @@ export function syncConnectSources(env: Readonly<Record<string, string | undefin
43
43
  }
44
44
 
45
45
  /**
46
- * The page origins a sync node admits besides its own host name: `APP_URL`'s, when set. Only a
47
- * deployment that serves the page on another host than the node needs it; an unparsable value is
46
+ * The page origins a sync node admits besides its own origin: `APP_URL`'s, when set. A deployment
47
+ * that serves the page on another port or host than the node sees needs it; an unparsable value is
48
48
  * refused rather than read as "no page origin", which would refuse every socket in silence.
49
49
  */
50
50
  export function syncOriginsFrom(env: Readonly<Record<string, string | undefined>>): string[] {
@@ -5,38 +5,12 @@
5
5
  import type { TaskDescriptor, TaskHandle } from '@ultimat3/jobs';
6
6
  import { getTask, registeredTasks } from '@ultimat3/jobs';
7
7
  import type { CronPhrases } from '@ultimat3/time';
8
- import {
9
- describeCron,
10
- fromEpochMs,
11
- nextCronOccurrenceMs,
12
- offsetLabel,
13
- toZoned,
14
- } from '@ultimat3/time';
8
+ import { describeCron, fromEpochMs, isoInZone, nextCronOccurrenceMs } from '@ultimat3/time';
15
9
  import { BadFlagError } from './errors';
16
10
 
17
11
  const DEFAULT_COUNT = 5;
18
12
  const MAX_COUNT = 50;
19
13
 
20
- const pad2 = (value: number): string => String(value).padStart(2, '0');
21
-
22
- /**
23
- * ISO-8601 rendered with the ZONE's OWN offset (`2026-03-08T03:00:00-04:00`), never collapsed to
24
- * UTC `Z` — an ambient zone is exactly the bug a task's `tz` exists to prevent. No single
25
- * "instant in a zone, as ISO" export exists in `@ultimat3/time` to call instead: `toIso` is
26
- * UTC-`Z` only, `isoDateInZone` is date-only, and `formatWithOffset` renders locale prose, not
27
- * ISO. So this composes the package's own instant→zoned-wall-clock conversion (`toZoned`) and
28
- * zone-label helper (`offsetLabel`) rather than a fresh `Intl.DateTimeFormat` call here.
29
- *
30
- * Exported for `cmd-tasks.ts`'s `last` column: the occurrence a task last fired and the one it
31
- * fires next are one clock, so they are one function.
32
- */
33
- export function isoInZone(ms: number, zone: string): string {
34
- const zoned = toZoned(fromEpochMs(ms), zone);
35
- const date = `${String(zoned.year).padStart(4, '0')}-${pad2(zoned.month)}-${pad2(zoned.day)}`;
36
- const time = `${pad2(zoned.hour)}:${pad2(zoned.minute)}:${pad2(zoned.second)}`;
37
- return `${date}T${time}${offsetLabel(zoned.offsetMinutes)}`;
38
- }
39
-
40
14
  /** `x tasks list` row: the descriptor plus the next occurrence, ms and rendered alike. */
41
15
  export interface TaskFact extends TaskDescriptor {
42
16
  readonly nextMs: number;
@@ -46,7 +20,7 @@ export interface TaskFact extends TaskDescriptor {
46
20
  function toFact(handle: TaskHandle, nowMs: number): TaskFact {
47
21
  const descriptor = handle.describe();
48
22
  const nextMs = nextCronOccurrenceMs(descriptor.cron, descriptor.tz, nowMs);
49
- return { ...descriptor, nextMs, next: isoInZone(nextMs, descriptor.tz) };
23
+ return { ...descriptor, nextMs, next: isoInZone(fromEpochMs(nextMs), descriptor.tz) };
50
24
  }
51
25
 
52
26
  export function listTaskFacts(nowMs: number): readonly TaskFact[] {
@@ -110,7 +84,7 @@ export function taskShowFacts(
110
84
  let cursor = nowMs;
111
85
  for (let i = 0; i < count; i += 1) {
112
86
  cursor = nextCronOccurrenceMs(descriptor.cron, descriptor.tz, cursor);
113
- upcoming.push({ ms: cursor, at: isoInZone(cursor, descriptor.tz) });
87
+ upcoming.push({ ms: cursor, at: isoInZone(fromEpochMs(cursor), descriptor.tz) });
114
88
  }
115
89
  return { descriptor, describe: describeCron(descriptor.cron, 'en-US', phrases), upcoming };
116
90
  }
@@ -82,6 +82,9 @@ export const ${name.camel} = mutator({
82
82
  input: t.object({ id: t.uuid, orgId: t.uuid, title: t.string }),
83
83
  output: t.object({ id: t.uuid, title: t.string }),
84
84
  policy: can${feature.pascal}Write,
85
+ // Required on a mutator (X_MUTATOR_NOT_IDEMPOTENT): the client replays this write under one
86
+ // Idempotency-Key after a dropped response, and the replay answers the first result.
87
+ idempotent: true,
85
88
  // No \`mcp\` until a real description is written: see \`x g action\`.
86
89
  // tx.table(name) rather than tx.${feature.plural}: the typed accessor exists only once the app
87
90
  // augments LocalTables, and generated code cannot assume that has happened yet. The name is the
@@ -151,7 +154,7 @@ const input = { id, orgId${isMutator ? ", title: 'a title'" : ''} };
151
154
 
152
155
  // Named here because every projection needs a stable name and this file does not boot the app.
153
156
  // At boot \`registerActions(await import('./actions'))\` stamps the same name onto the same
154
- // object, so \`${name.camel}.tool()\` works there with nothing to remember.
157
+ // object, so each projection of it works there with nothing to remember.
155
158
  const target = ${name.camel}.named('${name.camel}');
156
159
  ${
157
160
  outsider
@@ -231,7 +234,7 @@ ${
231
234
  ? `
232
235
  unitTest('the handler answers the row it found', async () => {
233
236
  const draft = { orgId, title: 'kept', price: { minor: 0, currency: 'USD' } };
234
- const row = await runWithContext(createContext({ actor: writer }), () => repo.insert(draft));
237
+ const row = await runWithContext(ctxOf({ actor: writer }), () => repo.insert(draft));
235
238
  const answer = await target.as(writer, ${call});
236
239
  expect(answer).toEqual(${found});
237
240
  });
@@ -260,7 +263,7 @@ const actionUnitTest = (name: NameSet, feature: NameSet, shape: HandlerShape): s
260
263
  return `// ${name.camel}: its declared shape, the input it refuses, and what its handler answers — in
261
264
  // process, so all three belong to the \`unit\` step. The contract projections are next door.
262
265
  ${sortedImports([
263
- ...(stores ? ["import { createContext, runWithContext } from '@ultimat3/core';"] : []),
266
+ ...(stores ? ["import { ctxOf, runWithContext } from '@ultimat3/core';"] : []),
264
267
  "import { testActor } from '@ultimat3/policy';",
265
268
  `import { ${testing} } from '@ultimat3/testing';`,
266
269
  ...(stores ? [`import { driver } from '${shape.dbModule}';`] : []),
@@ -273,7 +276,7 @@ const input = { id, orgId${shape.isMutator ? ", title: 'a title'" : ''} };
273
276
  ${shape.isMutator ? `\n// The client store's table: the entity's own, so the twin and the server row share one key.\nconst TABLE = '${feature.table}';\n` : ''}
274
277
  // Named here because every projection needs a stable name and this file does not boot the app.
275
278
  // At boot \`registerActions(await import('./actions'))\` stamps the same name onto the same
276
- // object, so \`${name.camel}.tool()\` works there with nothing to remember.
279
+ // object, so each projection of it works there with nothing to remember.
277
280
  const target = ${name.camel}.named('${name.camel}');
278
281
 
279
282
  // Holds the grant in the org the input names: past the policy, so what answers is the handler.
@@ -107,14 +107,14 @@ const backfillTest = (
107
107
  ): string => `// ${name.camel}'s durable identity: one live run per name, retried under the same key. The work
108
108
  // itself — the pass over real rows and the projection it applies — is the unit suite's, next door.
109
109
 
110
- import { createMemoryDriver, resetJobDriver, setJobDriver } from '@ultimat3/jobs';
110
+ import { memoryJobDriver, resetJobDriver, setJobDriver } from '@ultimat3/jobs';
111
111
  import { afterAll, beforeAll, expect, jobTest } from '@ultimat3/testing';
112
112
  import { ${name.camel} } from './${name.kebab}';
113
113
 
114
114
  // The driver is process-global, so it is installed and released around this file rather than
115
115
  // left behind for whichever test happens to run next.
116
116
  beforeAll(() => {
117
- setJobDriver(createMemoryDriver());
117
+ setJobDriver(memoryJobDriver());
118
118
  });
119
119
  afterAll(resetJobDriver);
120
120
 
@@ -162,7 +162,7 @@ const backfillUnitTest = (
162
162
  // driver. A sweep rewrites rows no user asked it to, so what is worth failing on is that it reaches
163
163
  // every tenant's rows, writes the projection and nothing else, and that a replay changes nothing.
164
164
  ${sortedImports([
165
- "import { createContext, runWithContext } from '@ultimat3/core';",
165
+ "import { ctxOf, runWithContext } from '@ultimat3/core';",
166
166
  "import { testActor } from '@ultimat3/policy';",
167
167
  "import { afterEach, expect, unitTest } from '@ultimat3/testing';",
168
168
  `import { db, driver } from '${dbModule}';`,
@@ -175,7 +175,7 @@ const orgB = '00000000-0000-4000-8000-000000000009';
175
175
 
176
176
  /** What a request is to the handle: an actor, whose org every read and write runs under. */
177
177
  const inOrg = <T>(orgId: string, run: () => Promise<T>): Promise<T> =>
178
- runWithContext(createContext({ actor: testActor('member', { orgId }).actor }), run);
178
+ runWithContext(ctxOf({ actor: testActor('member', { orgId }).actor }), run);
179
179
 
180
180
  const store = (orgId: string, title: string): Promise<${feature.pascal}> => {
181
181
  const draft = { orgId, title, price: { minor: 1000, currency: 'USD' } };
@@ -156,7 +156,7 @@ const repoTest = (
156
156
  ): string => `// The ${name.kebab} repo against the in-memory driver: the contract Postgres serves, with no
157
157
  // database. What it pins is what the repo cannot show by being read — whose rows a call reaches.
158
158
  ${sortedImports([
159
- "import { createContext, frozenClock, runWithContext } from '@ultimat3/core';",
159
+ "import { ctxOf, frozenClock, runWithContext } from '@ultimat3/core';",
160
160
  "import { testActor } from '@ultimat3/policy';",
161
161
  "import { afterEach, expect, unitTest } from '@ultimat3/testing';",
162
162
  `import { driver } from '${dbModule}';`,
@@ -171,7 +171,7 @@ const clock = frozenClock('2026-01-01T00:00:00.000Z');
171
171
 
172
172
  /** What a request is to the handle: an actor, whose org every read and write runs under. */
173
173
  const inOrg = <T>(org: string, run: () => Promise<T>): Promise<T> =>
174
- runWithContext(createContext({ actor: testActor('member', { orgId: org }).actor, clock }), run);
174
+ runWithContext(ctxOf({ actor: testActor('member', { orgId: org }).actor, clock }), run);
175
175
 
176
176
  const draft = (title: string) => ({ orgId, title, price: { minor: 1200, currency: 'USD' } });
177
177