@teamscala/service-boot 0.23.503 → 0.23.506

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 (69) hide show
  1. package/package.json +2 -1
  2. package/src/agreement-gate/signing-shell.test.ts +9 -15
  3. package/src/agreement-gate.test.ts +8 -15
  4. package/src/asset-proxy.test.ts +9 -9
  5. package/src/attach-portal-members.test.ts +9 -13
  6. package/src/attach-transfers-upload.test.ts +9 -10
  7. package/src/auth-gate.test.ts +8 -7
  8. package/src/bind-page-handler.ts +24 -8
  9. package/src/build-client-assets/manifest.test.ts +8 -14
  10. package/src/build-client-assets/source-candidates.test.ts +9 -11
  11. package/src/build-client-assets/sourcemaps-upload.test.ts +8 -15
  12. package/src/build-client-assets/styles-build.test.ts +9 -15
  13. package/src/build-client-assets.test.ts +8 -12
  14. package/src/capability-slug.test.ts +9 -0
  15. package/src/compose-page-title.test.ts +8 -9
  16. package/src/content-gate/access.ts +11 -0
  17. package/src/content-gate/catalog.test.ts +8 -8
  18. package/src/content-gate/config-types.ts +15 -0
  19. package/src/content-gate/gate-entry-clears.ts +38 -0
  20. package/src/content-gate/gate-entry-marker.test.ts +102 -0
  21. package/src/content-gate/gate.ts +16 -0
  22. package/src/content-gate/token.test.ts +8 -13
  23. package/src/content-page-floor.test.ts +9 -11
  24. package/src/csrf-body-limit.test.ts +9 -15
  25. package/src/handlers/messaging-send-reconcile-logic.test.ts +9 -11
  26. package/src/handlers/messaging-send-recording-logic.test.ts +9 -9
  27. package/src/handlers/messaging-send-recording-watch.test.ts +9 -11
  28. package/src/handlers/messaging-send-sweep-selection.test.ts +9 -7
  29. package/src/handlers/platform-fault-escalation/probe.test.ts +9 -10
  30. package/src/handlers/platform-fault-escalation-refresh.test.ts +9 -22
  31. package/src/handlers/platform-fault-escalation.test.ts +9 -10
  32. package/src/handlers/scala-tools-currency-watch.test.ts +9 -0
  33. package/src/handlers/singleton-tools-currency-watch.test.ts +9 -0
  34. package/src/handlers/whatsapp-liveness-handler.test.ts +9 -12
  35. package/src/handlers/whatsapp-liveness.test.ts +9 -9
  36. package/src/interpolate-meta.ts +123 -0
  37. package/src/is-react-element-type.test.ts +8 -9
  38. package/src/loader-errors.ts +41 -0
  39. package/src/module-not-found.test.ts +8 -12
  40. package/src/mount-orpc-router.test.ts +8 -9
  41. package/src/page-server-orpc/server-client.test.ts +8 -17
  42. package/src/quiz-gate.ts +10 -0
  43. package/src/read-html-template.test.ts +8 -12
  44. package/src/register-registry-page-route/apply-page-branding.test.ts +8 -19
  45. package/src/register-registry-page-route/captured-loader-config.test.ts +9 -13
  46. package/src/register-registry-page-route/graph-cache.test.ts +9 -12
  47. package/src/register-registry-page-route/live-loader-config.test.ts +8 -24
  48. package/src/register-registry-page-route/meta-title.ts +1 -1
  49. package/src/register-registry-page-route/page-data-shared.test.ts +9 -12
  50. package/src/register-registry-page-route/public-shell-page-data.test.ts +8 -19
  51. package/src/register-registry-page-route/resolve-entity-title.test.ts +8 -7
  52. package/src/register-registry-page-route/resolve-page-data.test.ts +9 -13
  53. package/src/register-registry-page-route/resolve-request-graph.test.ts +9 -13
  54. package/src/resolve-from-loader-config.test.ts +117 -14
  55. package/src/resolve-from-loader-config.ts +28 -115
  56. package/src/resolve-service-port.test.ts +8 -12
  57. package/src/resolvers/app-header.test.ts +9 -5
  58. package/src/resolvers/fetch-org-brand.ts +18 -2
  59. package/src/resolvers/portal-route-paths.test.ts +8 -13
  60. package/src/resource-chat-inject.page-assistant.test.ts +8 -28
  61. package/src/resource-chat-inject.test.ts +8 -25
  62. package/src/resource-not-found.test.ts +9 -8
  63. package/src/security-headers.test.ts +9 -12
  64. package/src/select-service-routes.test.ts +9 -0
  65. package/src/server-error-page.test.ts +8 -10
  66. package/src/theme-fonts-link.test.ts +8 -10
  67. package/src/two-phase-full-app.test.ts +8 -16
  68. package/src/two-phase-health-gate-client-only.test.ts +8 -28
  69. package/src/two-phase-health-gate.test.ts +8 -28
@@ -1,17 +1,12 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Round-trip + binding tests for the content-gate access token. Covers the
7
- * identity-carrying additions (signAccessToken `name` + verifyAccessTokenIdentity)
8
- * used by the public GPT lead-capture gate, plus the gate/resource/expiry
9
- * binding that every gate relies on to withhold content from a link holder.
10
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
11
9
 
12
- // A stable secret so secret() resolves deterministically in the test. Set BEFORE
13
- // importing the module under test (secret() reads it at call time, so order is
14
- // not strictly required, but this keeps the intent explicit).
15
10
  process.env.AUTH_SECRET = "test-secret-at-least-16-chars-long";
16
11
 
17
12
  import { describe, expect, test } from "bun:test";
@@ -1,14 +1,12 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Unit proof of the /roadmap fix's core decision (independent of the publish /
7
- * deploy — verify-before-done). Exercises the pure floor decision + the floor
8
- * render for every case: dead link (no row → 404), empty page (zero blocks →
9
- * 200), normal page (renders), resource_type route (handled elsewhere), and
10
- * region-slot shell (no page source — untouched).
11
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
12
10
  import { test, expect } from "bun:test";
13
11
  import {
14
12
  decideContentPageFloor,
@@ -1,18 +1,12 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Behavior proofs for the two boot-chain security invariants:
7
- * - security-csrf-protection: a state-changing form-element POST from an
8
- * untrusted Origin is refused 403; the managed-base-domain Origin (the
9
- * derived trusted set) is allowed; same-origin sec-fetch-site is allowed.
10
- * - security-body-limit: a body over the global limit is refused 413 BEFORE
11
- * any handler runs, and the transfers upload rejects an over-sized
12
- * Content-Length without buffering the body.
13
- * Proxy-is-not-behaviour guards: these exercise the real middleware through
14
- * real Hono requests.
15
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
16
10
  import { describe, expect, it } from "bun:test";
17
11
  import { Hono } from "hono";
18
12
  import { bodyLimit } from "hono/body-limit";
@@ -1,14 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Fixture pair for the outbound-dispatch reconciliation logic: the clean
7
- * fixture (every GOWA outbound id already recorded) and the violating fixture
8
- * (a raw-dispatched send GOWA knows about that left no messaging_logs row —
9
- * the 2026-08-26 measured class — which MUST come back as a backfill
10
- * candidate).
11
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
12
10
  import { describe, expect, test } from "bun:test";
13
11
  import {
14
12
  buildBackfillRow,
@@ -1,12 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Unit tests for the pure detection logic of the messaging send-recording
7
- * watch: healthy shape (zero rows) yields null; the failure shape yields the
8
- * aggregate with ordered bounds; the sample keeps the MOST RECENT rows.
9
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
10
10
  import { describe, expect, test } from "bun:test";
11
11
  import {
12
12
  buildUnrecordedPayload,
@@ -1,14 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Handler-level integration test for `messagingSendRecordingWatch`: mocks the
7
- * Prisma registry + recordEvent, drives the two observed live shapes (the
8
- * three pre-fix rows measured 2026-08-20, and the healthy all-recorded state),
9
- * and asserts the `messaging.send.unrecorded` durable event fires for exactly
10
- * the failure shape — without touching the real database.
11
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
12
10
  import { beforeEach, expect, mock, test } from "bun:test";
13
11
 
14
12
  const recordEventCalls: Array<{ kind: string; payload: Record<string, unknown> }> = [];
@@ -1,10 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Tests for the reconcile sweep's chat-page selection.
7
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
8
10
  import { describe, expect, test } from "bun:test";
9
11
  import {
10
12
  selectChatsToSweep,
@@ -1,13 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Behavioural tests for the capability probe behind the resolve gate
7
- * (operator ruling 2026-08-28): an episode whose subject is genuinely down
8
- * must FAIL to close. Health surfaces encode capability in the STATUS CODE
9
- * (llm-router /health answers 503 while degraded), so healthy means 2xx.
10
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
11
10
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
12
11
  import { probeHttpOk } from "./probe.ts";
13
12
 
@@ -1,25 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Unit tests for episode FACT CURRENCY — a separate concern from the
7
- * threshold/dedup semantics locked by `platform-fault-escalation.test.ts`,
8
- * and kept in its own file because that one already sits over the size
9
- * budget.
10
- *
11
- * WHAT THIS LOCKS. The sweep escalated once and then never wrote the row
12
- * again, so an open episode's window facts froze at their escalation-time
13
- * values while the fault kept firing. Measured 2026-08-28: the
14
- * `host-command-bus.command.failed` row read `last_seen_at` fifteen hours old
15
- * with 1,150 further events recorded after it, and a reader — this lane
16
- * included — concluded from the row that the fault had stopped.
17
- *
18
- * The stale value is not only cosmetic: `decideSweep` falls back to
19
- * `episode.lastSeenAt` once a kind drops out of the window, so a frozen
20
- * timestamp makes a just-gone-quiet fault look quiet for hours and closes the
21
- * episode immediately instead of after its configured resolve window.
22
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
23
10
  import { describe, expect, test } from "bun:test";
24
11
  import { episodeNeedsRefresh, pagesOperator } from "./platform-fault-escalation-logic.ts";
25
12
 
@@ -1,13 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Unit tests for the pure decision core of the platform-fault escalation
7
- * sweep (`platform-fault-escalation` handler). The handler's DB/WhatsApp I/O
8
- * is runtime-verified by the scheduled_job firing; this locks the threshold,
9
- * dedup and episode semantics from the 2026-08-15 incident brief.
10
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
11
10
  import { describe, expect, test } from "bun:test";
12
11
  import {
13
12
  composeEscalationMessage,
@@ -1,3 +1,12 @@
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
1
10
  import { describe, expect, test } from "bun:test";
2
11
  import { join } from "node:path";
3
12
 
@@ -1,3 +1,12 @@
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
1
10
  import { describe, expect, test } from "bun:test";
2
11
  import { join } from "node:path";
3
12
 
@@ -1,15 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Handler-level integration test for `pollWhatsAppLiveness`: mocks the central
7
- * /mcp forwarder (`forwardToolToCentralMcp`) + recordEvent, drives a
8
- * connected→disconnected transition across two polls, and asserts the
9
- * `messaging.whatsapp.device.disconnected` recordEvent fires — the "simulated
10
- * disconnect emitting recordEvent" of `whatsapp-remote-logout-monitor`, without
11
- * touching the real device or the real /mcp.
12
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
13
10
  import { beforeEach, expect, mock, test } from "bun:test";
14
11
 
15
12
  const recordEventCalls: Array<{ kind: string; payload: Record<string, unknown> }> = [];
@@ -1,12 +1,12 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Unit tests for the pure `detectLivenessTransitions` core of the
7
- * `whatsapp-liveness` monitor. The handler's GOWA poll + recordEvent emission is
8
- * runtime-verified by the scheduled_job firing; this locks the transition logic.
9
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
9
+
10
10
  import { expect, test } from "bun:test";
11
11
  import { detectLivenessTransitions } from "./whatsapp-liveness-logic.ts";
12
12
 
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @system ui-service-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Template interpolation against URL params and resolved loader data:
7
+ * resolveParamRef, interpolateTemplate, and the meta-context filler.
8
+ *
9
+ * interpolateTemplate moved here from resolve-from-loader-config.ts
10
+ * (file-size-budget): interpolation is its own canonical unit and the meta
11
+ * filler already consumed it, so the import direction now points at the
12
+ * family home instead of the wave resolver.
13
+ *
14
+ * An unresolved ref keeps its literal template rather than becoming
15
+ * undefined — a page whose brand failed should show a raw `:brand.companyName`
16
+ * somewhere visible rather than silently dropping its title.
17
+ */
18
+
19
+ export function resolveParamRef(
20
+ ref: string,
21
+ params: Record<string, unknown>,
22
+ resolved: Record<string, unknown>,
23
+ ): unknown {
24
+ const parts = ref.split(".");
25
+ // A bare (no-dot) ref can name either a resolved source key (e.g. `:response`
26
+ // passes the whole resolved source object to a resolver_fn) or a URL param
27
+ // (e.g. `:id`). Prefer the resolved source key, then fall back to the param.
28
+ // Dotted refs (`:response.total_score`) always read from `resolved`.
29
+ if (parts.length === 1) {
30
+ if (resolved != null && typeof resolved === "object" && ref in resolved) {
31
+ return (resolved as Record<string, unknown>)[ref];
32
+ }
33
+ if (params != null && typeof params === "object" && ref in params) {
34
+ return (params as Record<string, unknown>)[ref];
35
+ }
36
+ return undefined;
37
+ }
38
+ let current: unknown = resolved;
39
+ for (const part of parts) {
40
+ if (current == null || typeof current !== "object") return undefined;
41
+ current = readField(current as Record<string, unknown>, part);
42
+ }
43
+ return current;
44
+ }
45
+
46
+ /**
47
+ * A resource_loader row spells DATABASE COLUMN NAMES — `page_id`,
48
+ * `organisation_id`, `prepared_for` — because that is what the schema calls
49
+ * them. The ORM hands back CAMELCASE keys — `pageId`, `organisationId`,
50
+ * `preparedFor`. Nothing reconciled the two, so a `:proposal.page_id` ref read
51
+ * a key the object does not have and returned undefined.
52
+ *
53
+ * MEASURED 2026-09-03, and the consequence is far worse than a missing value.
54
+ * `interpolateTemplate` DROPS a key whose value is undefined, so a dependent
55
+ * source declared `where: { id: ":proposal.page_id" }` was issued as
56
+ * `where: {}` — and findFirst with an empty filter returns AN ARBITRARY FIRST
57
+ * ROW. A client proposal page therefore loaded an unrelated builda_page,
58
+ * rendered a stranger's content under the correct brand chrome, and answered
59
+ * 200 throughout. Instrumenting the live resolver showed every dependent
60
+ * source doing it: page, website, contact, partnerOrg and clientOrg all with
61
+ * `where={}`; only `proposal` was correct, because its ref is the URL param
62
+ * `:id` rather than a sibling source.
63
+ *
64
+ * Read the spelled key first, then its camelCase equivalent. Column names stay
65
+ * the authoring vocabulary and a row never has to know what the ORM did to its
66
+ * keys.
67
+ */
68
+ function readField(source: Record<string, unknown>, key: string): unknown {
69
+ if (key in source) return source[key];
70
+ const camel = key.replace(/_([a-z0-9])/g, (_m, c: string) => c.toUpperCase());
71
+ return camel === key ? undefined : source[camel];
72
+ }
73
+
74
+ export function interpolateTemplate(
75
+ template: unknown,
76
+ params: Record<string, unknown>,
77
+ resolved: Record<string, unknown>,
78
+ ): unknown {
79
+ if (typeof template === "string") {
80
+ const exactMatch = template.match(/^:([a-zA-Z0-9_.]+)$/);
81
+ if (exactMatch) {
82
+ return resolveParamRef(exactMatch[1], params, resolved);
83
+ }
84
+ if (template.includes(":")) {
85
+ return template.replace(
86
+ /:([a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*)/g,
87
+ (_match, ref: string) => {
88
+ const value = resolveParamRef(ref, params, resolved);
89
+ return value != null ? String(value) : _match;
90
+ },
91
+ );
92
+ }
93
+ return template;
94
+ }
95
+ if (Array.isArray(template)) {
96
+ return template.map((v) => interpolateTemplate(v, params, resolved));
97
+ }
98
+ if (template != null && typeof template === "object") {
99
+ const out: Record<string, unknown> = {};
100
+ for (const [k, v] of Object.entries(template)) {
101
+ const result = interpolateTemplate(v, params, resolved);
102
+ if (result !== undefined) out[k] = result;
103
+ }
104
+ return out;
105
+ }
106
+ return template;
107
+ }
108
+
109
+ export function interpolateMeta(
110
+ template: Record<string, unknown>,
111
+ data: Record<string, unknown>,
112
+ ): Record<string, unknown> {
113
+ const out: Record<string, unknown> = {};
114
+ for (const [k, v] of Object.entries(template)) {
115
+ if (v != null && typeof v === "object" && !Array.isArray(v)) {
116
+ out[k] = interpolateMeta(v as Record<string, unknown>, data);
117
+ } else {
118
+ const result = interpolateTemplate(v, {}, data);
119
+ out[k] = result ?? v;
120
+ }
121
+ }
122
+ return out;
123
+ }
@@ -1,12 +1,11 @@
1
- /**
2
- * @system ui-service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Pins the three React idioms a `typeof === "function"` check silently rejects.
7
- * The memo case is the measured one: PresenceAvatars rendered blank on every
8
- * scala-projects page carrying it, because memo() returns an object.
9
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
10
9
 
11
10
  import { expect, test } from "bun:test";
12
11
  import { forwardRef, lazy, memo } from "react";
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @system ui-service-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Error vocabulary for resource_loader source resolution. Split out of
7
+ * resolve-from-loader-config (file-size-budget): the classes are the
8
+ * name-matched contract other layers key on, and they belong where they
9
+ * can be imported without dragging the wave resolver with them.
10
+ */
11
+
12
+ export class MissingLoaderModelCapabilityError extends Error {
13
+ constructor(model: string, sourceKey: string) {
14
+ super(
15
+ `[resource_loader] source "${sourceKey}" requires model "${model}", but the generated server client does not expose findFirst for it — regenerate the service schema from its page loaders`,
16
+ );
17
+ this.name = "MissingLoaderModelCapabilityError";
18
+ }
19
+ }
20
+
21
+ /**
22
+ * A Prisma/pg query rejected an interpolated URL param as not a valid uuid
23
+ * (pg 22P02 surfaced as the generated client's SqlQueryError). The value came
24
+ * from the URL, so the URL names no row — the same state as a deleted row,
25
+ * not a renderer failure. Deliberately a tight name+message match: a
26
+ * genuinely malformed query is a config fault and must keep failing loudly.
27
+ *
28
+ * Measured 2026-09-12 on scala-quiz prod: scanner paths (/.env, /robots.txt,
29
+ * *.php) and slug links each threw `invalid input syntax for type uuid` as a
30
+ * 500 after a ~2.5s DB round trip, and the page-server:render-budget breaker
31
+ * counted them as CONSECUTIVE render failures — 53 in a row on 2026-09-11 —
32
+ * so the layer sat DEGRADED fail-closed and every real quiz page 503'd for
33
+ * hours while the only successful render in the window was a crawler.
34
+ */
35
+ export function isUrlRefNotUuidError(err: unknown): boolean {
36
+ return (
37
+ err instanceof Error &&
38
+ err.name === "SqlQueryError" &&
39
+ err.message.includes("invalid input syntax for type uuid")
40
+ );
41
+ }
@@ -1,15 +1,11 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Locks the mount candidate-chain regression (2026-08-24): scala-agents-ui
7
- * crash-looped at boot because @teamscala/cli-session exports ./boot/mount.ts
8
- * and not ./mount.ts. The chain already listed boot/mount.ts as a fallback,
9
- * but Bun names an unexported subpath by its BARE package, so the first
10
- * candidate's miss was classified as a real error and boot died before the
11
- * fallback ran. The whole value of the chain is that candidate 1 may be absent.
12
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
13
9
 
14
10
  import { describe, expect, it } from "bun:test";
15
11
  import { barePackageOf, isModuleNotFound } from "./module-not-found";
@@ -1,12 +1,11 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Regression coverage for the generated-router boot seam. A service schema
7
- * without its generated router is a codegen failure, not a daemon-only
8
- * service, so production boot must fail before it can report healthy.
9
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
10
9
 
11
10
  import { mkdir, mkdtemp, rm } from "node:fs/promises";
12
11
  import { afterEach, describe, expect, mock, test } from "bun:test";
@@ -1,20 +1,11 @@
1
- /**
2
- * @system page-server
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The empty-router substitute that hid a total outage.
7
- *
8
- * MEASURED 2026-08-20: scala-quiz and scala-forms served 500s on every
9
- * data-backed page for hours. /health returned 200 and boot logged "ORPC
10
- * serverClient registered", because an unregistered read was answered with
11
- * `createRouterClient({}, …)` — a client that looks registered and carries no
12
- * models. The fault only surfaced later and elsewhere, as
13
- * MissingLoaderModelCapabilityError during page render.
14
- *
15
- * These pin the contract: a missing client is an error at the point of the
16
- * fault, never a fabricated stand-in.
17
- */
1
+ // @system codegen
2
+ // @status generated
3
+ // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
+ //
5
+ // This suite's assertions are OWNED by the codegen band: the band module
6
+ // carries them verbatim, this file is the emission, and hand edits here are
7
+ // overwritten on the next run. The rationale each assertion carries moved
8
+ // with it into the band.
18
9
 
19
10
  import { afterEach, describe, expect, test } from "bun:test";
20
11
  import {