@teamscala/service-boot 0.23.317 → 0.23.319

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 (165) hide show
  1. package/package.json +526 -526
  2. package/src/agreement-gate.ts +12 -0
  3. package/src/auth-gate.test.ts +70 -0
  4. package/src/auth-gate.ts +68 -6
  5. package/src/build-client-assets.test.ts +1 -1
  6. package/src/build-client-assets.ts +1 -1
  7. package/src/discover-blocks.ts +1 -1
  8. package/src/fn-routers/index.ts +2 -0
  9. package/src/fn-routers/task-sync-assign.test.ts +123 -8
  10. package/src/fn-routers/task-sync-assign.ts +105 -18
  11. package/src/fn-routers/task-sync-bind.test.ts +18 -18
  12. package/src/fn-routers/task-sync-brief.test.ts +65 -22
  13. package/src/fn-routers/task-sync-brief.ts +77 -23
  14. package/src/fn-routers/task-sync-close.test.ts +44 -0
  15. package/src/fn-routers/task-sync-close.ts +82 -0
  16. package/src/fn-routers/task-sync-dispatch.test.ts +51 -33
  17. package/src/fn-routers/task-sync-hook-lifecycle.test.ts +204 -0
  18. package/src/fn-routers/task-sync-lane.test.ts +36 -0
  19. package/src/fn-routers/task-sync-projection.test.ts +26 -0
  20. package/src/fn-routers/task-sync-projection.ts +51 -0
  21. package/src/fn-routers/task-sync-rename.test.ts +53 -0
  22. package/src/fn-routers/task-sync-rename.ts +111 -0
  23. package/src/fn-routers/task-sync-router.test.ts +14 -2
  24. package/src/handlers/singleton-tools-currency-watch.ts +114 -11
  25. package/src/handlers/sweep-host-cache.test.ts +133 -0
  26. package/src/handlers/sweep-host-cache.ts +143 -26
  27. package/src/install-logging.ts +3 -0
  28. package/src/mount-orpc-router.ts +1 -1
  29. package/src/register-api-routes.ts +8 -2
  30. package/src/register-page-routes.ts +0 -3
  31. package/src/resolve-brand-favicon.ts +1 -1
  32. package/src/resource-not-found.ts +10 -0
  33. package/src/serve/stamp-transport-identity.test.ts +24 -0
  34. package/src/serve/stamp-transport-identity.ts +20 -7
  35. package/src/two-phase-boot.ts +85 -17
  36. package/src/two-phase-health-gate-client-only.test.ts +77 -0
  37. package/src/two-phase-health-gate.test.ts +13 -2
  38. package/src/agreement-gate.d.ts +0 -19
  39. package/src/asset-cache-key.d.ts +0 -11
  40. package/src/asset-optimize.d.ts +0 -17
  41. package/src/asset-proxy.d.ts +0 -11
  42. package/src/asset-social-card.d.ts +0 -17
  43. package/src/attach-agreement-magic.d.ts +0 -20
  44. package/src/attach-portal-members.d.ts +0 -23
  45. package/src/attach-proposal-accept.d.ts +0 -26
  46. package/src/attach-proposal-magic.d.ts +0 -18
  47. package/src/attach-transfers-upload.d.ts +0 -27
  48. package/src/auth-gate.d.ts +0 -28
  49. package/src/bind-page-handler.d.ts +0 -18
  50. package/src/build-client-assets/input-hashing.d.ts +0 -25
  51. package/src/build-client-assets/source-candidates.d.ts +0 -27
  52. package/src/build-client-assets.d.ts +0 -50
  53. package/src/compose-page-title.d.ts +0 -41
  54. package/src/content-gate/access.d.ts +0 -15
  55. package/src/content-gate/config-types.d.ts +0 -99
  56. package/src/content-gate/gate.d.ts +0 -13
  57. package/src/content-gate/parse-cookie.d.ts +0 -9
  58. package/src/content-gate/token.d.ts +0 -48
  59. package/src/content-page-floor.d.ts +0 -39
  60. package/src/discover-blocks.d.ts +0 -28
  61. package/src/expand-roster.d.ts +0 -34
  62. package/src/fn-routers/agreement-completion-email.d.ts +0 -14
  63. package/src/fn-routers/agreement-send-router.d.ts +0 -82
  64. package/src/fn-routers/ai-health-router.d.ts +0 -35
  65. package/src/fn-routers/auth-router.d.ts +0 -47
  66. package/src/fn-routers/cache-admin-router.d.ts +0 -57
  67. package/src/fn-routers/chat-session-router.d.ts +0 -219
  68. package/src/fn-routers/email-defaults.d.ts +0 -1
  69. package/src/fn-routers/email-link-ttl.d.ts +0 -1
  70. package/src/fn-routers/index.d.ts +0 -18
  71. package/src/fn-routers/panel-data-router.d.ts +0 -19
  72. package/src/fn-routers/persist-chat-messages.d.ts +0 -36
  73. package/src/fn-routers/plans-router.d.ts +0 -25
  74. package/src/fn-routers/proposal-send-router.d.ts +0 -58
  75. package/src/fn-routers/public-agreement-router.d.ts +0 -289
  76. package/src/fn-routers/public-assessment-router.d.ts +0 -68
  77. package/src/fn-routers/public-gpt-session-router.d.ts +0 -143
  78. package/src/fn-routers/sign-in-flow-router.d.ts +0 -107
  79. package/src/fn-routers/task-sync-assign.d.ts +0 -276
  80. package/src/fn-routers/task-sync-brief.d.ts +0 -69
  81. package/src/fn-routers/task-sync-dispatch.d.ts +0 -61
  82. package/src/fn-routers/task-sync-dispatch.ts +0 -124
  83. package/src/fn-routers/task-sync-lane.d.ts +0 -28
  84. package/src/fn-routers/task-sync-lane.ts +0 -91
  85. package/src/fn-routers/task-sync-projection.d.ts +0 -121
  86. package/src/fn-routers/task-sync-router.d.ts +0 -375
  87. package/src/fn-routers/task-sync-router.ts +0 -1011
  88. package/src/fn-routers/tasks-router.d.ts +0 -25
  89. package/src/fn-routers/user-preferences-router.d.ts +0 -77
  90. package/src/fn-routers/work-items-router.d.ts +0 -102
  91. package/src/gate-ttls.d.ts +0 -6
  92. package/src/gpt-gate.d.ts +0 -25
  93. package/src/handlers/bun-stable-watch.d.ts +0 -1
  94. package/src/handlers/pane-outbox-drain.d.ts +0 -1
  95. package/src/handlers/projects-digest.d.ts +0 -26
  96. package/src/handlers/prune-stale-data.d.ts +0 -30
  97. package/src/handlers/registry-stale-inactive-sweep.d.ts +0 -1
  98. package/src/handlers/scala-tools-currency-watch.d.ts +0 -1
  99. package/src/handlers/singleton-tools-currency-watch.d.ts +0 -1
  100. package/src/handlers/sweep-event-log.d.ts +0 -1
  101. package/src/handlers/sweep-host-cache.d.ts +0 -1
  102. package/src/handlers/upstream-issue-sync.d.ts +0 -1
  103. package/src/handlers/whatsapp-liveness-logic.d.ts +0 -34
  104. package/src/handlers/whatsapp-liveness.d.ts +0 -4
  105. package/src/hono-error-handler.d.ts +0 -25
  106. package/src/install-harness.d.ts +0 -45
  107. package/src/install-logging.d.ts +0 -34
  108. package/src/load-service-install-row.d.ts +0 -24
  109. package/src/load-service-prisma-client.d.ts +0 -18
  110. package/src/mount-orpc-router.d.ts +0 -15
  111. package/src/mount-server-packages.d.ts +0 -29
  112. package/src/mount.d.ts +0 -9
  113. package/src/mutable-app-ref.d.ts +0 -44
  114. package/src/page-route-shell-types.d.ts +0 -12
  115. package/src/page-server-orpc/audit-revert-router.d.ts +0 -37
  116. package/src/page-server-orpc/hub-mcp-client.d.ts +0 -12
  117. package/src/page-server-orpc/portal-diagnostics-router.d.ts +0 -49
  118. package/src/page-server-orpc/puck-pages-router.d.ts +0 -58
  119. package/src/page-server-orpc/server-client.d.ts +0 -16
  120. package/src/page-server-orpc/templates-router.d.ts +0 -34
  121. package/src/page-server-orpc/user-preferences-router.d.ts +0 -79
  122. package/src/pg-boss-handler-registry.d.ts +0 -27
  123. package/src/pg-boss-mount.d.ts +0 -42
  124. package/src/proposal-authorized-emails.d.ts +0 -28
  125. package/src/proposal-chat-inject.d.ts +0 -17
  126. package/src/proposal-gate.d.ts +0 -18
  127. package/src/read-html-template.d.ts +0 -38
  128. package/src/read-viewport-from-request.d.ts +0 -18
  129. package/src/readiness-gate.d.ts +0 -16
  130. package/src/register-api-routes.d.ts +0 -18
  131. package/src/register-crash-handlers.d.ts +0 -15
  132. package/src/register-page-routes.d.ts +0 -27
  133. package/src/register-page-server-client.d.ts +0 -11
  134. package/src/register-registry-page-route/ai-chat-prefetch.d.ts +0 -13
  135. package/src/register-registry-page-route/graph-cache.d.ts +0 -30
  136. package/src/register-registry-page-route/meta-title.d.ts +0 -2
  137. package/src/register-registry-page-route/proposal-approval-email-token.d.ts +0 -14
  138. package/src/register-registry-page-route/proposal-brand-stamping.d.ts +0 -28
  139. package/src/register-registry-page-route/resolve-page-data.d.ts +0 -1
  140. package/src/register-registry-page-route.d.ts +0 -27
  141. package/src/register-static-assets.d.ts +0 -13
  142. package/src/register-ws-routes.d.ts +0 -28
  143. package/src/resolve-app-base-url.d.ts +0 -13
  144. package/src/resolve-brand-favicon.d.ts +0 -28
  145. package/src/resolve-from-loader-config.d.ts +0 -32
  146. package/src/resolve-service-context.d.ts +0 -17
  147. package/src/resolve-service-port.d.ts +0 -75
  148. package/src/resolve-theme-mode.d.ts +0 -42
  149. package/src/resolvers/fetch-org-brand.d.ts +0 -2
  150. package/src/resolvers/portal-route-paths.d.ts +0 -39
  151. package/src/resolvers/website-by-host-map.d.ts +0 -12
  152. package/src/resource-chat-inject.d.ts +0 -46
  153. package/src/resource-not-found.d.ts +0 -60
  154. package/src/run-boot-hook-list.d.ts +0 -19
  155. package/src/run-boot-hooks.d.ts +0 -31
  156. package/src/scala-tools-currency-watchdog.d.ts +0 -10
  157. package/src/security-headers.d.ts +0 -12
  158. package/src/serve/serve.d.ts +0 -46
  159. package/src/serve/stamp-transport-identity.d.ts +0 -32
  160. package/src/server-error-page.d.ts +0 -29
  161. package/src/start-scheduled-jobs.d.ts +0 -18
  162. package/src/two-phase-boot.d.ts +0 -136
  163. package/src/types.d.ts +0 -46
  164. package/src/ui-service.d.ts +0 -46
  165. package/src/ws-boot-self-test.d.ts +0 -19
@@ -1,50 +0,0 @@
1
- /**
2
- * @system server-handlers
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Builds the per-service client bundle and stylesheet into `dist/client/`
7
- * at UI-service boot time. Outputs are content-hashed so deployments
8
- * naturally cache-bust:
9
- *
10
- * src/entry-client.ts ──bun build──> dist/client/hydrate-<hash>.js
11
- * src/app.css ──tailwindcss──> dist/client/styles-<hash>.css
12
- * (and) dist/client/asset-manifest.json
13
- *
14
- * The HTML template references `{{stylesPath}}` / `{{hydratePath}}` —
15
- * read-html-template.ts substitutes from the manifest at template-read
16
- * time. Bytes-level changes → new hash → new URL → browser refetch.
17
- * Without this, `cache-control: immutable` on a fixed filename means
18
- * returning visitors keep their stale copy forever.
19
- *
20
- * Content-fingerprint cache: the CLIENT-BUNDLE DEPENDENCY CLOSURE (source
21
- * entrypoints + every @teamscala/* module/stylesheet the bundle reaches + the
22
- * resolved versions of the third-party deps the closure imports) is hashed by
23
- * CONTENT and stored in the manifest; the rebuild is skipped only when the
24
- * current closure hashes to the same fingerprint (cache-key-hashes-source-
25
- * inputs-only — never mtime; see computeInputFingerprint for why mtime silently
26
- * served stale bundles on Railway). bun.lock is deliberately NOT hashed — it
27
- * encodes every @teamscala/* version including backend packages outside this
28
- * service's client bundle, so folding it in would rebuild on every publish
29
- * (the publish→rebuild storm; `no-uncontrolled-repetition-or-cascade`). First
30
- * pm2 restart after a real closure change pays the full build cost; unchanged
31
- * restarts skip. Stale hashed outputs from a prior build are removed in the
32
- * same pass so dist/client/ doesn't accumulate dead files.
33
- */
34
- /**
35
- * Read ONLY the tombstone state of the current manifest — the runtime
36
- * (read-html-template, ui-service) calls this to decide whether to surface a
37
- * build-failure signal. Returns null when the manifest is fresh (no
38
- * `buildFailed`). Decoupled from the full AssetManifest read so the runtime
39
- * never depends on the build internals.
40
- */
41
- export declare function readClientBuildStatus(cwd?: string): Promise<{
42
- failed: true;
43
- reason?: string;
44
- at?: string;
45
- } | {
46
- failed: false;
47
- }>;
48
- export declare function stripRemoteImports(css: string): string;
49
- export declare function computeInputFingerprint(cwd: string): Promise<string>;
50
- export declare function buildClientAssets(serviceSlug: string): Promise<void>;
@@ -1,41 +0,0 @@
1
- /**
2
- * @system ui-service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The single canonical page-title composer. Every SSR page handler routes
7
- * through this (portal/region-shell, public shell, single-page resource). A
8
- * page title is DERIVED from data that already exists — the website name +
9
- * the page/entity name — with `builda_pages.meta_title` as an explicit
10
- * per-page OVERRIDE only, never the primary source.
11
- *
12
- * Mirrors the brand-is-org-owned-full-scheme precedent: a value that should
13
- * be DERIVED was instead hand-set per row, and when unset it silently fell
14
- * back to something wrong. For themes the wrong fallback was the generic
15
- * light palette; for titles it was the bare org name — the `default`
16
- * page_meta_template `titleTemplate: "{org.name}"` produced "GBG Indo" with
17
- * no site or page context, and per-row `meta_title` carried stale product
18
- * literals like "Dashboard | scala Hub". The fix is identical in shape:
19
- * derive by default, override explicitly.
20
- *
21
- * Canonical format: `<website.name>: <subject>` (subject = entity title or
22
- * page name), site-first so a browser tab reads the surface then the page.
23
- * A resource surface (forms/quiz/gpt) may supply a curated `templateTitle`
24
- * (e.g. "{agent.name} — {org.name}"); it wins over the default derivation
25
- * but NOT over an explicit per-page override. The composer NEVER returns a
26
- * bare org name — that is the regression this closes, and the
27
- * `page-title-not-bare-org` guard backstops it at the template layer.
28
- */
29
- export interface TitleContext {
30
- /** Per-page override (builda_pages.meta_title). Wins outright when set. */
31
- pageMetaTitle?: string | null;
32
- /** Resolved resource-template string (forms/quiz/gpt). Curated per-surface shape. */
33
- templateTitle?: string | null;
34
- /** builda_websites.name — the surface, the primary site context. */
35
- websiteName?: string | null;
36
- /** builda_pages.name — the page (e.g. "Dashboard"). */
37
- pageName?: string | null;
38
- /** Resource entity title (proposal/form/quiz/agent name) — preferred subject. */
39
- entityTitle?: string | null;
40
- }
41
- export declare function composePageTitle(ctx: TitleContext): string;
@@ -1,15 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The content-gate ACCESS endpoint mounter. One generic POST handler for every
7
- * gate: a plain <form> posts {<recordIdField>, email}; on a match against the
8
- * resource's authorized set it sets the signed access cookie and redirects back,
9
- * so the page loader's gate then releases the content. No bespoke endpoint per
10
- * type — each gate is one AccessConfig. Mounted BEFORE the auth gate so it is
11
- * publicly reachable.
12
- */
13
- import type { Hono } from "hono";
14
- import type { AccessConfig } from "./config-types.ts";
15
- export declare function attachContentGateAccess(app: Hono, serviceSlug: string, config: AccessConfig): Promise<void>;
@@ -1,99 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The config contracts that PROGRAM a content gate. Each gated content type
7
- * (agreement, proposal, …) is one ContentGateConfig + one AccessConfig — no
8
- * bespoke gate functions per type; the engine (gate.ts) and the access mounter
9
- * (access.ts) are the single shared primitive driven by these.
10
- */
11
- type Resolved = Record<string, unknown>;
12
- /** Drives the SSR-time gate (gate.ts / runContentGate). */
13
- export interface ContentGateConfig {
14
- /** Cookie that carries this gate's access token (e.g. "ag_token"). */
15
- cookieName: string;
16
- /**
17
- * The gated resource id from the resolved graph, or "" when this gate does
18
- * not apply to the current page (the engine then no-ops). This is also how a
19
- * gate detects "is this my kind of page". Async to allow DB-backed resolution
20
- * (e.g. agreement gate resolving the UUID from the page→agreement association
21
- * when no pre-placed gate block exists).
22
- */
23
- getResourceId(resolved: Resolved): string | Promise<string>;
24
- /**
25
- * Verify the cookie's token for this resource → the verified identity
26
- * ({email, optional display name}), or null. Token-agnostic by design:
27
- * proposals use the shared content-gate token; agreements keep their existing
28
- * token (also minted by the signing ORPC), so the engine never assumes one
29
- * token format. Allow-list gates return just {email}; the GPT lead-capture
30
- * gate also returns the captured name (carried in its token).
31
- */
32
- verifyToken(token: string | undefined, resourceId: string, nowMs: number): {
33
- email: string;
34
- name?: string;
35
- } | null;
36
- /**
37
- * BAKED-IN unified auth. When a gate provides this resolver, the engine
38
- * AUTOMATICALLY releases a logged-in viewer whose session email is in the
39
- * authorized set — with no valid token and no per-gate code. This is the
40
- * platform default for EVERY gated resource: any application surface with
41
- * authentication gets "a signed-in, authorized viewer is never re-asked for
42
- * their email/OTP" for free, just by exposing its authorized-email set (the
43
- * same resolver its AccessConfig uses). Not called when the session is
44
- * anonymous. Omit only for open-capture gates that have no authorized set.
45
- */
46
- resolveAuthorizedEmails?(resourceId: string): Promise<Set<string>>;
47
- /**
48
- * OPTIONAL OVERRIDE of the baked-in session check above — for the rare gate
49
- * whose session→identity rule isn't simply "session email ∈ authorized set".
50
- * When present it wins; otherwise the engine uses `resolveAuthorizedEmails`.
51
- * Called only when no valid access token is present.
52
- */
53
- sessionAuthorize?(resolved: Resolved, resourceId: string): {
54
- email: string;
55
- name?: string;
56
- } | null | Promise<{
57
- email: string;
58
- name?: string;
59
- } | null>;
60
- /** Reveal + seed the verified viewer's identity/session (token was valid). */
61
- onAuthorized(resolved: Resolved, resourceId: string, identity: {
62
- email: string;
63
- name?: string;
64
- }): void | Promise<void>;
65
- /** Withhold content (strip/inject the gate) — token absent/invalid. Async-
66
- * capable so a gate can load its display/behaviour settings from a registry
67
- * row (DB-driven, not hardcoded); sync implementations remain valid. */
68
- onUnauthorized(resolved: Resolved, resourceId: string): void | Promise<void>;
69
- }
70
- /** Drives the public access endpoint (access.ts / attachContentGateAccess). */
71
- export interface AccessConfig {
72
- /** Route to mount, e.g. "/api/proposal-access". */
73
- path: string;
74
- /** Cookie to set on a match. */
75
- cookieName: string;
76
- /** Cookie lifetime in ms (Max-Age). */
77
- ttlMs: number;
78
- /** Form field carrying the resource id, e.g. "proposalId". */
79
- recordIdField: string;
80
- /**
81
- * Open lead-capture gate: ANY non-empty email is accepted (no allow-list).
82
- * For surfaces whose "gate" captures a visitor rather than authorizing one
83
- * against a fixed set (the public GPT). When true, `resolveAuthorizedEmails`
84
- * is not called. Default/omitted = allow-list mode (proposals, agreements).
85
- */
86
- openCapture?: boolean;
87
- /**
88
- * The authorized email set for a resource (lowercased), checked server-side.
89
- * Not called when `openCapture` is true.
90
- */
91
- resolveAuthorizedEmails(resourceId: string): Promise<Set<string>>;
92
- /**
93
- * Mint the access token for a matched email (token-agnostic per gate).
94
- * `name` is the optional captured display name (open-capture gates carry it
95
- * in the token; allow-list gates ignore it).
96
- */
97
- signToken(resourceId: string, email: string, nowMs: number, name?: string): string | null;
98
- }
99
- export {};
@@ -1,13 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The content-gate ENGINE. Runs in the page loader after the resource graph
7
- * resolves, BEFORE SSR. Orchestration only — the per-type behaviour (which
8
- * resource, who's authorized, what to seed, how to withhold) is supplied by a
9
- * ContentGateConfig, so agreements/proposals/future gates are configs, not new
10
- * functions. The single place that decides whether gated bytes reach the HTML.
11
- */
12
- import type { ContentGateConfig } from "./config-types.ts";
13
- export declare function runContentGate(resolved: Record<string, unknown>, cookieHeader: string | null, nowMs: number, config: ContentGateConfig): Promise<void>;
@@ -1,9 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Parse a single cookie value out of a raw Cookie header — shared by the
7
- * content-gate engine and any gate config.
8
- */
9
- export declare function parseCookie(header: string | null, name: string): string | undefined;
@@ -1,48 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The single signed, short-lived access token for EVERY content gate (agreement,
7
- * proposal, and any future gated content). A gate withholds page content
8
- * server-side until the request carries a valid token for that resource; this is
9
- * the only thing that releases the content into the SSR, so without it a gated
10
- * page is never readable/scrapable by a link holder.
11
- *
12
- * HMAC-SHA256 (Bun-native) over {c:cookieName, r:resourceId, e:email, x:exp}.
13
- * `cookieName` is bound INTO the payload so a token minted for one gate
14
- * (e.g. ag_token) can never satisfy another gate's check (e.g. prop_token).
15
- *
16
- * Secret resolver, in preference order: AUTH_SECRET → ENCRYPTION_KEY →
17
- * DATABASE_URL (first one ≥16 chars). A gate signs AND verifies inside one
18
- * service, so it only needs a stable per-deployment secret; the fallback keeps a
19
- * gate working on a service that was never given AUTH_SECRET (e.g. the former
20
- * public-only proposals viewer) instead of failing closed and locking out every
21
- * authorized viewer. Any service that has AUTH_SECRET transparently prefers it.
22
- * Fail-closed: no usable secret yields null (no token, no access).
23
- */
24
- /** Mint a token proving `email` is authorized for `resourceId` under `cookieName`. */
25
- export declare function signAccessToken(cookieName: string, resourceId: string, email: string, nowMs: number, ttlMs: number,
26
- /**
27
- * Optional display name to carry in the token (the public GPT lead-capture
28
- * gate uses this so a returning visitor's name is known at SSR — first paint
29
- * renders the full identity, not just the email). Allow-list gates
30
- * (proposals/agreements) omit it.
31
- */
32
- name?: string): string | null;
33
- /**
34
- * Validate a token for `cookieName` + `resourceId`. Returns the verified email
35
- * on success, null otherwise (bad signature, wrong gate, wrong resource,
36
- * expired, no secret).
37
- */
38
- export declare function verifyAccessToken(token: string | undefined | null, cookieName: string, resourceId: string, nowMs: number): string | null;
39
- /**
40
- * Like `verifyAccessToken` but returns the full verified identity ({email, name})
41
- * so a gate that carries a display name in the token (the public GPT lead-capture
42
- * gate) can seed the visitor's name — not just the email — at SSR. Same
43
- * signature/expiry/gate-binding checks; null on any failure.
44
- */
45
- export declare function verifyAccessTokenIdentity(token: string | undefined | null, cookieName: string, resourceId: string, nowMs: number): {
46
- email: string;
47
- name?: string;
48
- } | null;
@@ -1,39 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Pure decision: does a CONTENT route (no resource_type) whose loader resolved
7
- * a `page` body source warrant the branded not-found floor? Extracted from
8
- * register-registry-page-route so the rule is unit-testable independent of the
9
- * Hono/DB/graph machinery that wraps it (the fix's correctness does not depend
10
- * on the publish — verify-before-done).
11
- *
12
- * Three outcomes:
13
- * - no page row (deleted/unmatched path, e.g. /roadmap) → dead-link 404 floor
14
- * - page row exists but zero content blocks → empty-page 200 floor
15
- * - otherwise → null (render normally)
16
- *
17
- * The "Vision Toolbar" leak this closes: a content catchall synthesized an empty
18
- * page for an unmatched path and served a cacheable 200; resource-not-found-is-
19
- * the-root-floor only fired for resource_type routes, so dead content links fell
20
- * through. See resource-not-found.ts for the floor + operator ruling 2026-07-21.
21
- */
22
- import { PrimaryResourceMissingError } from "./resource-not-found.ts";
23
- /**
24
- * A page ROW exists but its body has zero content blocks — a content gap, not a
25
- * dead link. True only when the row is present yet `puck_data` is absent or its
26
- * `content` array is empty. Handles string (raw) and object (normalized) puck_data.
27
- */
28
- export declare function pageBodyIsEmpty(page: unknown): boolean;
29
- /**
30
- * Decide whether a content route should fall to the branded not-found floor.
31
- * Returns the error to throw (mapped to the floor by bind-page-handler), or null
32
- * to render normally. `hasResourceType` routes are handled by the existing
33
- * resource_type missing check (caller-side) — they return null here.
34
- */
35
- export declare function decideContentPageFloor(opts: {
36
- hasResourceType: boolean;
37
- hasPageContentSource: boolean;
38
- page: unknown;
39
- }): PrimaryResourceMissingError | null;
@@ -1,28 +0,0 @@
1
- /**
2
- * @system ui-service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Surgical block discovery: at boot, queries every block_schema row in the
7
- * registry whose componentPath starts with one of the service's declared
8
- * block_packages, then dynamic-imports each one. Per-block try/catch — one
9
- * dead componentPath logs and skips, the rest still register and the page
10
- * renders the surviving blocks. This replaces the static
11
- * `.scala-install/page-manifest.ts` codegen output, which had the failure
12
- * mode of a single dead block_schema row crashing the whole manifest at
13
- * compile time.
14
- *
15
- * Empty block_packages is NOT fatal here: it falls back to scanning all
16
- * active block_schema (uninstalled packages skip via the per-block try/catch),
17
- * so a service whose row is unpopulated but whose blocks are installed still
18
- * renders. The real "booted with zero blocks" fail-closed is at the call site
19
- * (loaded.length === 0 → BootFatalError). A DB-unreachable error still throws —
20
- * no fallback to a package-side puckBlocks scan, which masked blockless boots.
21
- */
22
- import type { RendererComponentEntry } from "./types.ts";
23
- export interface DiscoveredBlocks {
24
- components: Record<string, RendererComponentEntry>;
25
- loaded: string[];
26
- skipped: string[];
27
- }
28
- export declare function discoverBlocks(blockPackages: string[]): Promise<DiscoveredBlocks>;
@@ -1,34 +0,0 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Server-side roster expansion — the executor that handles "render N of block X
7
- * per data row" so the page never needs a domain-specific panel/list block. A
8
- * block authored with `props.dataRepeat: "parties"` is a TEMPLATE; before SSR
9
- * this clones it once per roster item into its parent slot (top-level content or
10
- * any Container's `content` slot), then drops the marker. Each clone:
11
- * - sets `role` + `dataKey` to the item's key, so a prop-driven block (the
12
- * SignatureField cell) finds its own party with no per-clone wiring;
13
- * - substitutes `{{name}}` / `{{role}}` / `{{company}}` / `{{date}}` /
14
- * `{{status}}` placeholders in any string prop (so a recipient row is plain
15
- * `Container` + `Text` primitives — the copy lives on the page, not here).
16
- * `{{status}}` resolves from the template's own `signedLabel` /
17
- * `awaitingLabel` props (DB-driven copy), never hardcoded.
18
- * Generic over any roster; the agreement page passes its parties.
19
- */
20
- export interface RosterItem {
21
- key: string;
22
- role: string;
23
- name: string;
24
- title: string | null;
25
- company: string | null;
26
- signedAt: string | null;
27
- signature: string | null;
28
- }
29
- /** Agreement-level tokens (one substitution, not per-party). */
30
- export interface RosterMeta {
31
- [token: string]: string;
32
- }
33
- /** Expand roster templates + bind agreement-meta placeholders across regions. */
34
- export declare function expandRosterTemplates(resolved: Record<string, unknown>, parties: RosterItem[], meta?: RosterMeta): void;
@@ -1,14 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Completion delivery. When an agreement is fully EXECUTED (every party has
7
- * signed), email each party their executed copy + a note to download the
8
- * Certificate of Completion. Each link is a per-party magic-link (no email gate,
9
- * no OTP — see attach-agreement-magic.ts). Best-effort and self-contained: it
10
- * loads its own data and swallows every error, so a send failure is logged and
11
- * NEVER undoes the signature (the payment-setup non-fatal rule, applied to mail).
12
- * Fire-and-forget from the sign handler: `void sendAgreementCompletionEmails(...)`.
13
- */
14
- export declare function sendAgreementCompletionEmails(agreementId: string, baseUrl: string): Promise<void>;
@@ -1,82 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * fn.agreementSend.send — authenticated "send for signature" procedure. Emails
7
- * each party of an agreement a link to its signing page, stamps the lifecycle
8
- * (sent_to / sent_at, status draft→sent), and returns a per-recipient result.
9
- *
10
- * Off-the-shelf path per building-applications: this is server logic beyond
11
- * CRUD, so it is a typed ORPC procedure (not a bespoke endpoint), and it sends
12
- * through the shared Resend adapter (@teamscala/api-resend) — the same surface
13
- * @teamscala/auth uses for OTP — never a new email integration.
14
- *
15
- * Per-org sender: emails go FROM the org's display name on the platform's
16
- * verified Resend domain ("<Org> via Sign <verified-addr>"), with reply_to set
17
- * to the org's contact_email, so replies reach the org without each org needing
18
- * its own verified domain. When an org later verifies its own sending domain,
19
- * resolveSender is the single place to switch the FROM address.
20
- *
21
- * Sending is privileged (it reaches real inboxes), so the procedure requires an
22
- * authenticated caller — the localhost super-admin bypass authorises operator /
23
- * portal-SSR callers; the public signing page cannot invoke it.
24
- */
25
- import * as v from "valibot";
26
- interface User {
27
- id: string;
28
- organisationId: string | null;
29
- }
30
- interface Context {
31
- user: User | null | undefined;
32
- headers: Headers;
33
- resolveUser?: (headers: Headers, prisma: any) => Promise<User | null>;
34
- prisma: any;
35
- }
36
- export declare const agreementSendRouter: {
37
- send: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<Context & Record<never, never>, Context, Context>, import("@orpc/server").MergedCurrentContext<Context, {
38
- headers: Headers;
39
- resolveUser?: (headers: Headers, prisma: any) => Promise<User | null>;
40
- prisma: any;
41
- user: User;
42
- }>, v.ObjectSchema<{
43
- readonly agreementId: v.StringSchema<undefined>;
44
- /** Public base URL of the signing site, e.g. https://sign-dev.scala.business (no trailing slash). */
45
- readonly baseUrl: v.StringSchema<undefined>;
46
- }, undefined>, import("@orpc/contract").Schema<{
47
- ok: false;
48
- error: string;
49
- sentAt?: undefined;
50
- url?: undefined;
51
- sent?: undefined;
52
- } | {
53
- error?: undefined;
54
- ok: boolean;
55
- sentAt: string | null;
56
- url: string;
57
- sent: {
58
- email: string;
59
- ok: boolean;
60
- id?: string;
61
- error?: string;
62
- }[];
63
- }, {
64
- ok: false;
65
- error: string;
66
- sentAt?: undefined;
67
- url?: undefined;
68
- sent?: undefined;
69
- } | {
70
- error?: undefined;
71
- ok: boolean;
72
- sentAt: string | null;
73
- url: string;
74
- sent: {
75
- email: string;
76
- ok: boolean;
77
- id?: string;
78
- error?: string;
79
- }[];
80
- }>, Record<never, never>, Record<never, never>>;
81
- };
82
- export {};
@@ -1,35 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * `fn.aiHealth` — the AI-availability probe the shared chat hook
7
- * (`useAIHealthCheck`) polls before showing the chat's health dot. It resolves
8
- * the same DB-driven default model the chat engine uses (`ai-default-model`
9
- * config → an active `llm_model` row) and reports `{status, mcp, gateway}`;
10
- * `status:"ok"` means the chat has a usable model configured.
11
- *
12
- * Unauthed (a public GPT surface polls it too) and a LIGHTWEIGHT registry read
13
- * via the request `prisma` — deliberately NO `@teamscala/chat-completion` import,
14
- * so wiring this universal procedure never pulls the AI stack into every
15
- * service. Registered into the `fn` namespace by registerFnRouters, so any
16
- * chat-capable service answers it instead of 404-ing.
17
- */
18
- interface Context {
19
- prisma: {
20
- registry_entries: {
21
- findFirst: (args: unknown) => Promise<{
22
- config?: unknown;
23
- id?: string;
24
- } | null>;
25
- };
26
- };
27
- }
28
- export interface AiHealthResponse {
29
- status: "ok" | "error";
30
- mcp: boolean;
31
- gateway: boolean;
32
- error?: string;
33
- }
34
- export declare const aiHealthHandler: import("@orpc/server").DecoratedProcedure<Context & Record<never, never>, Context, import("@orpc/contract").Schema<unknown, unknown>, import("@orpc/contract").Schema<AiHealthResponse, AiHealthResponse>, Record<never, never>, Record<never, never>>;
35
- export {};
@@ -1,47 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * ORPC procedures for auth checks (isOrgAdmin, resolveAdminOverride).
7
- * Extracted from @teamscala/orpc/fn-router — primitives tier must not
8
- * contain application-specific auth logic.
9
- */
10
- interface User {
11
- id: string;
12
- name?: string | null;
13
- email?: string;
14
- organisationId: string | null;
15
- }
16
- interface Context {
17
- user: User | null | undefined;
18
- headers: Headers;
19
- resolveUser?: (headers: Headers, prisma: any) => Promise<User | null>;
20
- prisma: any;
21
- orgId?: string | null;
22
- }
23
- declare const authed: import("@orpc/server").BuilderWithMiddlewares<import("@orpc/server").MergedInitialContext<Context & Record<never, never>, Context, Context>, import("@orpc/server").MergedCurrentContext<Context, {
24
- headers: Headers;
25
- resolveUser?: (headers: Headers, prisma: any) => Promise<User | null>;
26
- prisma: any;
27
- user: User;
28
- orgId: string | null | undefined;
29
- }>, import("@orpc/contract").Schema<unknown, unknown>, import("@orpc/contract").Schema<unknown, unknown>, Record<never, never>, Record<never, never>>;
30
- export declare const authRouter: {
31
- isOrgAdmin: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<Context & Record<never, never>, Context, Context>, import("@orpc/server").MergedCurrentContext<Context, {
32
- headers: Headers;
33
- resolveUser?: (headers: Headers, prisma: any) => Promise<User | null>;
34
- prisma: any;
35
- user: User;
36
- orgId: string | null | undefined;
37
- }>, import("@orpc/contract").Schema<unknown, unknown>, import("@orpc/contract").Schema<boolean, boolean>, Record<never, never>, Record<never, never>>;
38
- resolveAdminOverride: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<Context & Record<never, never>, Context, Context>, import("@orpc/server").MergedCurrentContext<Context, {
39
- headers: Headers;
40
- resolveUser?: (headers: Headers, prisma: any) => Promise<User | null>;
41
- prisma: any;
42
- user: User;
43
- orgId: string | null | undefined;
44
- }>, import("@orpc/contract").Schema<unknown, unknown>, import("@orpc/contract").Schema<string | null, string | null>, Record<never, never>, Record<never, never>>;
45
- };
46
- export type { Context, User };
47
- export { authed };
@@ -1,57 +0,0 @@
1
- /**
2
- * @system service-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * `fn.cacheAdmin` — ORPC procedures for in-process cache enable/disable/flush.
7
- * Replaces the former `/api/data/cache-monitor/*` HANDLER_MAP entries
8
- * (orpc-is-the-only-api: every API surface is a typed ORPC procedure, never a
9
- * hand-written Hono route). Calls through @teamscala/cache/registry — the
10
- * platform-singleton cache surface (cache-is-the-only-cache).
11
- */
12
- import * as v from "valibot";
13
- export declare const cacheAdminRouter: {
14
- enable: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<import("./auth-router.ts").Context & Record<never, never>, import("./auth-router.ts").Context, import("./auth-router.ts").Context>, import("@orpc/server").MergedCurrentContext<import("./auth-router.ts").Context, {
15
- headers: Headers;
16
- resolveUser?: (headers: Headers, prisma: any) => Promise<import("./auth-router.ts").User | null>;
17
- prisma: any;
18
- user: import("./auth-router.ts").User;
19
- orgId: string | null | undefined;
20
- }>, v.ObjectSchema<{
21
- readonly name: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
22
- }, undefined>, import("@orpc/contract").Schema<{
23
- ok: boolean;
24
- name: string;
25
- }, {
26
- ok: boolean;
27
- name: string;
28
- }>, Record<never, never>, Record<never, never>>;
29
- disable: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<import("./auth-router.ts").Context & Record<never, never>, import("./auth-router.ts").Context, import("./auth-router.ts").Context>, import("@orpc/server").MergedCurrentContext<import("./auth-router.ts").Context, {
30
- headers: Headers;
31
- resolveUser?: (headers: Headers, prisma: any) => Promise<import("./auth-router.ts").User | null>;
32
- prisma: any;
33
- user: import("./auth-router.ts").User;
34
- orgId: string | null | undefined;
35
- }>, v.ObjectSchema<{
36
- readonly name: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
37
- }, undefined>, import("@orpc/contract").Schema<{
38
- ok: boolean;
39
- name: string;
40
- }, {
41
- ok: boolean;
42
- name: string;
43
- }>, Record<never, never>, Record<never, never>>;
44
- flush: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<import("./auth-router.ts").Context & Record<never, never>, import("./auth-router.ts").Context, import("./auth-router.ts").Context>, import("@orpc/server").MergedCurrentContext<import("./auth-router.ts").Context, {
45
- headers: Headers;
46
- resolveUser?: (headers: Headers, prisma: any) => Promise<import("./auth-router.ts").User | null>;
47
- prisma: any;
48
- user: import("./auth-router.ts").User;
49
- orgId: string | null | undefined;
50
- }>, v.ObjectSchema<{
51
- readonly name: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
52
- }, undefined>, import("@orpc/contract").Schema<{
53
- flushed: number;
54
- }, {
55
- flushed: number;
56
- }>, Record<never, never>, Record<never, never>>;
57
- };