@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,15 +1,11 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The load-bearing case is the contradiction one: a PORT env that disagrees
7
- * with the service's own declared port must NOT be obeyed. Obeying it put
8
- * mcp-ai-chat-system on session-picker's port with a loopback pointing at a
9
- * port nothing was listening on, and the failure surfaced as broken tool
10
- * curation rather than as a port problem. It then did it again on 2026-07-31
11
- * to scala-quiz — see the kind:ui block below.
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, test } from "bun:test";
15
11
  import { resolveServicePort } from "./resolve-service-port.ts";
@@ -1,8 +1,12 @@
1
- /**
2
- * @system ui-service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- */
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
+
6
10
  import { describe, expect, test } from "bun:test";
7
11
  import { resolveAppHeader } from "./app-header.ts";
8
12
 
@@ -12,17 +12,33 @@
12
12
  * hardcoded `fetchOrgBrand` arm in resolve-from-loader-config.ts.
13
13
  */
14
14
  import { fetchOrgBrand } from "@teamscala/resolver-engine/data-resolvers/fetch-org-brand";
15
+ import { getAppLogger } from "@teamscala/logger/app-loggers";
15
16
  import type { ResolverArgs } from "./portal-route-paths.ts";
16
17
 
18
+ /** A null brand renders a page with no logo, colours or name — so each failure names which of the three it was. */
17
19
  export async function fetchOrgBrandWithFallback({ input, client }: ResolverArgs): Promise<unknown> {
18
20
  const orgId = input.orgId as string | undefined;
21
+ const fallbackOrgId = input.fallbackOrgId as string | undefined;
19
22
  if (orgId) {
20
23
  const result = await fetchOrgBrand(client as Parameters<typeof fetchOrgBrand>[0], orgId);
21
24
  if (result) return result;
25
+ getAppLogger().warn(
26
+ `[fetch-org-brand] org "${orgId}" resolved NO brand row — the page renders with no logo, colours or company name. Either organisation_profile has no row for that id, or the server client exposes no organisation_profile.findFirst.`,
27
+ );
28
+ } else {
29
+ getAppLogger().warn(
30
+ `[fetch-org-brand] no orgId reached the resolver (input keys: ${Object.keys(input).join(", ") || "none"}) — the resource_loader's resolver_input ref did not interpolate, so the page renders unbranded.`,
31
+ );
22
32
  }
23
- const fallbackOrgId = input.fallbackOrgId as string | undefined;
24
33
  if (fallbackOrgId && fallbackOrgId !== orgId) {
25
- return fetchOrgBrand(client as Parameters<typeof fetchOrgBrand>[0], fallbackOrgId);
34
+ const fallback = await fetchOrgBrand(
35
+ client as Parameters<typeof fetchOrgBrand>[0],
36
+ fallbackOrgId,
37
+ );
38
+ if (fallback) return fallback;
39
+ getAppLogger().warn(
40
+ `[fetch-org-brand] fallback org "${fallbackOrgId}" also resolved no brand row.`,
41
+ );
26
42
  }
27
43
  return null;
28
44
  }
@@ -1,16 +1,11 @@
1
- /**
2
- * @system ui-service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * portal-route-paths.test.ts — locks the portal-path shape contract that both
7
- * the route resolver and the /sign-in brand extractor (bind-page-handler) share:
8
- * the canonical portal URL is /<websiteId>/<orgId>/<path>, so the ORG is segment
9
- * 1, never segment 0. The fixture pairs below are the regression guard for the
10
- * two cross-tenant defects this centralises (found 2026-07-21): reading segment 0
11
- * fed a website id into brand resolution and silently rendered org-less portal
12
- * URLs unscoped.
13
- */
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.
14
9
 
15
10
  import { describe, expect, test } from "bun:test";
16
11
  import {
@@ -1,31 +1,11 @@
1
- /**
2
- * @system ui-service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * resource-chat-inject.page-assistant.test.ts — the page assistant must be
7
- * PRESENT IN RENDERED OUTPUT on every link shape a page resolves.
8
- *
9
- * THE DEFECT THIS PINS (found 2026-09-08, quiz report page): the page_route's
10
- * assistant named resource_key "response" — the loader's RAW response hit.
11
- * On /report/<response-id> that hit exists and the chat bubble injected. On
12
- * anchor links (a research-record id, live or superseded) the raw hit is
13
- * null, injectResourceChatLauncher read no organisation_id off it, returned
14
- * SILENTLY, and the page rendered a complete report at 200 with the
15
- * assistant simply absent — 552KB of script gone, no error anywhere. The
16
- * page's reportData was byte-identical across all three shapes, which is
17
- * exactly why a data-equality check could not see it: the launcher is a
18
- * separate BLOCK whose presence depends on resolved[resourceKey], not on the
19
- * report data.
20
- *
21
- * THE FIX SHAPE: the page's resource_key names the PAIRED key ("pair"),
22
- * which exists on every link shape and carries organisation_id (the pair
23
- * resolver returns the anchor half's organisation). These tests drive the
24
- * injector with graphs shaped exactly like the loader produces for each of
25
- * the three link shapes and assert the launcher lands in the rendered
26
- * content tree — the thing that becomes markup — for ALL THREE. A
27
- * data-equality assertion cannot pass in their place.
28
- */
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.
29
9
 
30
10
  import { describe, expect, mock, test } from "bun:test";
31
11
 
@@ -1,28 +1,11 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Regression test for the block NAME the chat injectors push.
7
- *
8
- * THE DEFECT THIS PINS (found 2026-08-19): the floating assistant bubble was
9
- * silently absent on every gated proposal and agreement. discover-blocks keys
10
- * the Puck component registry off `block_schema.slug` and nothing else
11
- * (`components[row.slug] = …`), so a pushed block `type` must EQUAL an active
12
- * slug. The launcher block was consolidated to the single `ResourceLauncher`
13
- * row, but the injectors still pushed the pre-consolidation names —
14
- * `ResourceChatLauncher` here, and `ChatLauncherBlock` in the bespoke proposal
15
- * injector that has since been deleted in favour of this one. Neither name
16
- * resolves, and because injection is deliberately fail-soft the page
17
- * rendered unchanged with no error: the classic shape of
18
- * `a-deleted-producer-leaves-its-consumers-executing`.
19
- *
20
- * Asserting the literal is the WEAK half of the fix — it catches a regression
21
- * of this exact string, not a future rename of the block. The strong half is
22
- * single-sourcing the name so it cannot be spelled at all
23
- * (`identity-is-imported-never-spelled`); until that lands, this test is what
24
- * stands between the bubble and another silent disappearance.
25
- */
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.
26
9
 
27
10
  import { describe, expect, mock, test } from "bun:test";
28
11
 
@@ -1,11 +1,12 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Co-located tests for the resource-not-found public API: the error class +
7
- * the response builder are pure (no S3), so the behaviour is exercised directly.
8
- */
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
+
9
10
  import { describe, expect, it } from "bun:test";
10
11
  import {
11
12
  PrimaryResourceMissingError,
@@ -1,15 +1,12 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Behavior proof for the `security-content-type-nosniff` +
7
- * `security-permissions-policy` invariants: exercise injectSecurityHeaders
8
- * through a real Hono request and assert the headers on the RESPONSE —
9
- * a registry row naming a header is not evidence the header is served.
10
- * Covers both modes: the seeded-registry path (config row applied, CSP
11
- * {{nonce}} substituted) and the DB-less fallback (secureHeaders defaults).
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 { describe, expect, it } from "bun:test";
14
11
  import { Hono } from "hono";
15
12
  import { injectSecurityHeaders } from "./security-headers.ts";
@@ -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 { selectServiceRoutes } from "./select-service-routes.ts";
3
12
 
@@ -1,13 +1,11 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Fixture suite for the branded HTML render-error page. Asserts the
7
- * user-observable outcome (error-responses-via-error-report): a page render
8
- * failure serves a branded HTML page — never JSON — carrying the status,
9
- * Retry-After, and the errorId that ties the screen to GlitchTip + the log.
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
10
  import { describe, expect, test } from "bun:test";
13
11
  import type { ErrorReport } from "@teamscala/os/errors/error-report";
@@ -1,13 +1,11 @@
1
- /**
2
- * @system ui-service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The served <head> carries every font family the css_theme row declares —
7
- * derived from the row (one source), never a hard-coded family list. The old
8
- * envelope loaded only Inter, so the proposal heading variant's manuscript
9
- * serif never loaded and fell back to Times (2026-09-02).
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
10
  import { describe, expect, mock, test } from "bun:test";
13
11
 
@@ -1,19 +1,11 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Phase-2 step isolation: a client-asset build failure must not prevent
7
- * pg-boss from mounting.
8
- *
9
- * Asserted against the SOURCE rather than by booting a service, because the
10
- * behaviour is an ordering/error-handling property of buildFullApp and booting
11
- * it here would need a database, a registry, a Vite build and a live pg-boss.
12
- * The property is structural: buildClientAssets must be inside a try/catch that
13
- * appears BEFORE the mountPgBoss call. That is exactly
14
- * what regressed, and a source assertion fails the moment someone re-awaits it
15
- * unguarded — which is the regression this locks.
16
- */
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.
17
9
 
18
10
  import { describe, expect, test } from "bun:test";
19
11
 
@@ -1,31 +1,11 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * A CLIENT-ONLY service (`render_mode=client`) must reach ready, even though it
7
- * never registers an SSR page handler.
8
- *
9
- * Separate file from `two-phase-health-gate.test.ts` on purpose: `fullAppReady`
10
- * is module state that only transitions false -> true and has no reset hook, so
11
- * the two ready-paths cannot both be exercised in one module instance. A reset
12
- * hook would exist only for tests.
13
- *
14
- * THE REGRESSION THIS LOCKS (measured 2026-08-07 on scala-agents-ui). Readiness
15
- * used to be flipped INSIDE `setPageHandler`, which was honest while every
16
- * service was SSR. `render_mode=client` made a service that completes phase 2
17
- * and never calls it, so `/health` answered `{"status":"starting"}` FOREVER
18
- * while the process was healthy and serving — boot completed in 5023ms, "Full
19
- * app built: 50 block(s)" logged, `GET /` correctly 302'd to sign-in, and three
20
- * untouched UI services reported ok beside it.
21
- *
22
- * That is precisely what `readiness-gated-boot` forbids ("binary: fully-ready or
23
- * cleanly-unavailable"): a permanently-un-ready health signal makes the
24
- * activator's wake gate and every health-gated router treat a working service as
25
- * down. Exercised through a real Hono request against the REAL exported handler
26
- * (`proxy-is-not-behaviour`) — a source grep would prove the call exists, never
27
- * that the endpoint answers 200.
28
- */
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.
29
9
 
30
10
  import { beforeAll, describe, expect, test } from "bun:test";
31
11
  import { Hono } from "hono";
@@ -1,31 +1,11 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The wake gate for every scale-to-zero app: `/health` must not report ready
7
- * while the service still 503s pages.
8
- *
9
- * Exercised through a real Hono request against the REAL exported handler, not
10
- * asserted against source text — `proxy-is-not-behaviour`: a source grep proves
11
- * a line exists, never that the endpoint answers 503. This test fails if anyone
12
- * restores the flat `{status:"ok"}`, which is the regression it locks.
13
- *
14
- * THE MEASURED OUTAGE (2026-07-31, quiz-dev + sign-dev returning 503 to
15
- * customers). `scala-activator` gates a cold wake on `isHealthy(port)` — a
16
- * plain `GET /health` — then pipes the held client connection the moment it
17
- * returns ok. `/health` answered ok as soon as the MINIMAL app served, but the
18
- * phase-1 page handler returns 503 until the phase-2 swap, so the activator
19
- * piped the customer's first request into that window:
20
- *
21
- * 05:58:25.446 activator: awake scala-quiz (4227ms)
22
- * 05:58:25.452 scala-quiz: GET / 503 3ms <- the customer's request
23
- * 05:58:26.456 scala-quiz: Swapped from minimal app to full app
24
- *
25
- * Nine such 503s on scala-quiz, two on scala-sign, zero on scala-public — the
26
- * gap is a race whose width is the phase-1→phase-2 build time (1420ms cold,
27
- * 137ms warm), which is why one app looked healthy and the others did not.
28
- */
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.
29
9
 
30
10
  import { beforeAll, describe, expect, test } from "bun:test";
31
11
  import { Hono } from "hono";