@teamscala/service-boot 0.23.329 → 0.23.331

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 (157) hide show
  1. package/package.json +538 -522
  2. package/src/agreement-gate.d.ts +19 -0
  3. package/src/asset-cache-key.d.ts +11 -0
  4. package/src/asset-optimize.d.ts +17 -0
  5. package/src/asset-proxy.d.ts +11 -0
  6. package/src/asset-social-card.d.ts +17 -0
  7. package/src/attach-agreement-magic.d.ts +20 -0
  8. package/src/attach-portal-members.d.ts +23 -0
  9. package/src/attach-proposal-accept.d.ts +26 -0
  10. package/src/attach-proposal-magic.d.ts +18 -0
  11. package/src/attach-transfers-upload.d.ts +27 -0
  12. package/src/auth-gate.d.ts +28 -0
  13. package/src/auth-gate.ts +6 -68
  14. package/src/bind-page-handler.d.ts +18 -0
  15. package/src/build-client-assets/input-hashing.d.ts +25 -0
  16. package/src/build-client-assets/source-candidates.d.ts +27 -0
  17. package/src/build-client-assets.d.ts +50 -0
  18. package/src/build-client-assets.test.ts +1 -1
  19. package/src/build-client-assets.ts +1 -1
  20. package/src/compose-page-title.d.ts +41 -0
  21. package/src/content-gate/access.d.ts +15 -0
  22. package/src/content-gate/config-types.d.ts +99 -0
  23. package/src/content-gate/gate.d.ts +13 -0
  24. package/src/content-gate/parse-cookie.d.ts +9 -0
  25. package/src/content-gate/token.d.ts +48 -0
  26. package/src/content-page-floor.d.ts +39 -0
  27. package/src/discover-blocks.d.ts +28 -0
  28. package/src/discover-blocks.ts +1 -1
  29. package/src/expand-roster.d.ts +34 -0
  30. package/src/fn-routers/agreement-completion-email.d.ts +14 -0
  31. package/src/fn-routers/agreement-send-router.d.ts +82 -0
  32. package/src/fn-routers/ai-health-router.d.ts +35 -0
  33. package/src/fn-routers/auth-router.d.ts +47 -0
  34. package/src/fn-routers/cache-admin-router.d.ts +57 -0
  35. package/src/fn-routers/chat-session-router.d.ts +219 -0
  36. package/src/fn-routers/email-defaults.d.ts +1 -0
  37. package/src/fn-routers/email-link-ttl.d.ts +1 -0
  38. package/src/fn-routers/index.d.ts +18 -0
  39. package/src/fn-routers/index.ts +0 -2
  40. package/src/fn-routers/panel-data-router.d.ts +19 -0
  41. package/src/fn-routers/persist-chat-messages.d.ts +36 -0
  42. package/src/fn-routers/plans-router.d.ts +25 -0
  43. package/src/fn-routers/proposal-send-router.d.ts +58 -0
  44. package/src/fn-routers/public-agreement-router.d.ts +289 -0
  45. package/src/fn-routers/public-assessment-router.d.ts +68 -0
  46. package/src/fn-routers/public-gpt-session-router.d.ts +143 -0
  47. package/src/fn-routers/sign-in-flow-router.d.ts +107 -0
  48. package/src/fn-routers/task-sync-assign.d.ts +305 -0
  49. package/src/fn-routers/task-sync-assign.test.ts +1 -2
  50. package/src/fn-routers/task-sync-assign.ts +4 -4
  51. package/src/fn-routers/task-sync-bind.test.ts +18 -18
  52. package/src/fn-routers/task-sync-brief.d.ts +96 -0
  53. package/src/fn-routers/task-sync-brief.test.ts +17 -20
  54. package/src/fn-routers/task-sync-brief.ts +14 -15
  55. package/src/fn-routers/task-sync-close.d.ts +41 -0
  56. package/src/fn-routers/task-sync-dispatch.d.ts +61 -0
  57. package/src/fn-routers/task-sync-dispatch.test.ts +33 -51
  58. package/src/fn-routers/task-sync-dispatch.ts +124 -0
  59. package/src/fn-routers/task-sync-lane.d.ts +49 -0
  60. package/src/fn-routers/task-sync-lane.ts +125 -0
  61. package/src/fn-routers/task-sync-projection.d.ts +121 -0
  62. package/src/fn-routers/task-sync-projection.test.ts +0 -26
  63. package/src/fn-routers/task-sync-projection.ts +0 -51
  64. package/src/fn-routers/task-sync-rename.d.ts +91 -0
  65. package/src/fn-routers/task-sync-router.d.ts +444 -0
  66. package/src/fn-routers/task-sync-router.test.ts +2 -14
  67. package/src/fn-routers/task-sync-router.ts +1111 -0
  68. package/src/fn-routers/tasks-router.d.ts +25 -0
  69. package/src/fn-routers/user-preferences-router.d.ts +77 -0
  70. package/src/fn-routers/work-items-router.d.ts +102 -0
  71. package/src/gate-ttls.d.ts +6 -0
  72. package/src/gpt-gate.d.ts +25 -0
  73. package/src/handlers/bun-stable-watch.d.ts +1 -0
  74. package/src/handlers/pane-outbox-drain.d.ts +1 -0
  75. package/src/handlers/projects-digest.d.ts +26 -0
  76. package/src/handlers/prune-stale-data.d.ts +30 -0
  77. package/src/handlers/registry-stale-inactive-sweep.d.ts +1 -0
  78. package/src/handlers/scala-tools-currency-watch.d.ts +1 -0
  79. package/src/handlers/singleton-tools-currency-watch.d.ts +1 -0
  80. package/src/handlers/sweep-event-log.d.ts +1 -0
  81. package/src/handlers/sweep-host-cache.d.ts +1 -0
  82. package/src/handlers/sweep-host-cache.ts +26 -143
  83. package/src/handlers/upstream-issue-sync.d.ts +1 -0
  84. package/src/handlers/whatsapp-liveness-logic.d.ts +34 -0
  85. package/src/handlers/whatsapp-liveness.d.ts +4 -0
  86. package/src/hono-error-handler.d.ts +25 -0
  87. package/src/install-harness.d.ts +45 -0
  88. package/src/install-logging.d.ts +34 -0
  89. package/src/load-service-install-row.d.ts +24 -0
  90. package/src/load-service-prisma-client.d.ts +18 -0
  91. package/src/mount-orpc-router.d.ts +15 -0
  92. package/src/mount-orpc-router.ts +1 -1
  93. package/src/mount-server-packages.d.ts +29 -0
  94. package/src/mount.d.ts +9 -0
  95. package/src/mutable-app-ref.d.ts +44 -0
  96. package/src/page-route-shell-types.d.ts +12 -0
  97. package/src/page-server-orpc/audit-revert-router.d.ts +37 -0
  98. package/src/page-server-orpc/hub-mcp-client.d.ts +12 -0
  99. package/src/page-server-orpc/portal-diagnostics-router.d.ts +49 -0
  100. package/src/page-server-orpc/puck-pages-router.d.ts +58 -0
  101. package/src/page-server-orpc/server-client.d.ts +16 -0
  102. package/src/page-server-orpc/templates-router.d.ts +34 -0
  103. package/src/page-server-orpc/user-preferences-router.d.ts +79 -0
  104. package/src/pg-boss-handler-registry.d.ts +27 -0
  105. package/src/pg-boss-mount.d.ts +42 -0
  106. package/src/proposal-authorized-emails.d.ts +28 -0
  107. package/src/proposal-chat-inject.d.ts +17 -0
  108. package/src/proposal-gate.d.ts +18 -0
  109. package/src/read-html-template.d.ts +38 -0
  110. package/src/read-viewport-from-request.d.ts +18 -0
  111. package/src/readiness-gate.d.ts +16 -0
  112. package/src/register-api-routes.d.ts +18 -0
  113. package/src/register-crash-handlers.d.ts +15 -0
  114. package/src/register-page-routes.d.ts +27 -0
  115. package/src/register-page-routes.ts +3 -0
  116. package/src/register-page-server-client.d.ts +11 -0
  117. package/src/register-registry-page-route/ai-chat-prefetch.d.ts +13 -0
  118. package/src/register-registry-page-route/graph-cache.d.ts +30 -0
  119. package/src/register-registry-page-route/meta-title.d.ts +2 -0
  120. package/src/register-registry-page-route/proposal-approval-email-token.d.ts +14 -0
  121. package/src/register-registry-page-route/proposal-brand-stamping.d.ts +28 -0
  122. package/src/register-registry-page-route/resolve-page-data.d.ts +1 -0
  123. package/src/register-registry-page-route.d.ts +27 -0
  124. package/src/register-static-assets.d.ts +13 -0
  125. package/src/register-ws-routes.d.ts +28 -0
  126. package/src/resolve-app-base-url.d.ts +13 -0
  127. package/src/resolve-brand-favicon.d.ts +28 -0
  128. package/src/resolve-brand-favicon.ts +1 -1
  129. package/src/resolve-from-loader-config.d.ts +32 -0
  130. package/src/resolve-service-context.d.ts +17 -0
  131. package/src/resolve-service-port.d.ts +75 -0
  132. package/src/resolve-theme-mode.d.ts +42 -0
  133. package/src/resolvers/fetch-org-brand.d.ts +2 -0
  134. package/src/resolvers/portal-route-paths.d.ts +39 -0
  135. package/src/resolvers/website-by-host-map.d.ts +12 -0
  136. package/src/resource-chat-inject.d.ts +46 -0
  137. package/src/resource-not-found.d.ts +60 -0
  138. package/src/run-boot-hook-list.d.ts +19 -0
  139. package/src/run-boot-hooks.d.ts +31 -0
  140. package/src/scala-tools-currency-watchdog.d.ts +10 -0
  141. package/src/security-headers.d.ts +12 -0
  142. package/src/serve/serve.d.ts +46 -0
  143. package/src/serve/stamp-transport-identity.d.ts +32 -0
  144. package/src/serve/stamp-transport-identity.ts +2 -14
  145. package/src/server-error-page.d.ts +29 -0
  146. package/src/start-scheduled-jobs.d.ts +18 -0
  147. package/src/two-phase-boot.d.ts +136 -0
  148. package/src/two-phase-boot.ts +17 -85
  149. package/src/two-phase-health-gate.test.ts +2 -13
  150. package/src/types.d.ts +46 -0
  151. package/src/ui-service.d.ts +46 -0
  152. package/src/ws-boot-self-test.d.ts +19 -0
  153. package/src/auth-gate.test.ts +0 -70
  154. package/src/fn-routers/task-sync-lane.test.ts +0 -36
  155. package/src/handlers/sweep-host-cache.test.ts +0 -133
  156. package/src/serve/stamp-transport-identity.test.ts +0 -24
  157. package/src/two-phase-health-gate-client-only.test.ts +0 -77
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @system server-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ * @indivisible-unit
6
+ *
7
+ * One-call install of the entire logging pipeline. After installLogging()
8
+ * runs, every error in the service — process-level, HTTP middleware, Hono
9
+ * handlers, Puck blocks, even third-party packages calling console.* — is
10
+ * captured by @teamscala/logger and flows to Sentry/GlitchTip (prod) or
11
+ * loga (dev) with full context. Application code never imports the logger
12
+ * or wraps anything in try/catch for telemetry purposes.
13
+ *
14
+ * Marked @indivisible-unit: installLogging (early bootstrap, hardcoded
15
+ * defaults) and applyLoggerConfig (late-stage, DB-driven) are the two
16
+ * phases of the same one-call install — the service boot pipeline calls
17
+ * them in sequence with the same serviceSlug.
18
+ */
19
+ import { attachRequestLogger } from "@teamscala/logger/install-logging";
20
+ export { attachRequestLogger };
21
+ export interface InstallLoggingOptions {
22
+ serviceSlug: string;
23
+ }
24
+ /** Initial bootstrap install — runs BEFORE DB/Registry are available.
25
+ * Uses hardcoded defaults (INFO level, /tmp/scala-logs). */
26
+ export declare function installLogging(opts: InstallLoggingOptions): void;
27
+ /** Late-stage configuration — runs AFTER Registry is loaded.
28
+ * Injects DB-driven settings into the logger primitive. */
29
+ export declare function applyLoggerConfig(config: {
30
+ minLevel?: "debug" | "info" | "warn" | "error";
31
+ logDir?: string;
32
+ maxFileSize?: number;
33
+ maxFiles?: number;
34
+ }): void;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @system server-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Loads the service_install registry row for a given service slug.
7
+ * Uses registry.require() per Constitution Rule 11.
8
+ * Schema is owned by @teamscala/db-validation (Rule 7).
9
+ */
10
+ import type { ServiceInstallConfig } from "@teamscala/db-validation/registry-schemas/service-install";
11
+ export { ServiceInstallConfigSchema } from "@teamscala/db-validation/registry-schemas/service-install";
12
+ export type { ServiceInstallConfig };
13
+ export interface LoadServiceInstallRowResult {
14
+ success: true;
15
+ config: ServiceInstallConfig;
16
+ }
17
+ export interface LoadServiceInstallRowError {
18
+ success: false;
19
+ error: "row_not_found" | "validation_failed";
20
+ slug: string;
21
+ message: string;
22
+ }
23
+ export type LoadServiceInstallRowOutcome = LoadServiceInstallRowResult | LoadServiceInstallRowError;
24
+ export declare function loadServiceInstallRow(serviceSlug: string): Promise<LoadServiceInstallRowOutcome>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @system server-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Loads the service's own generated PrismaClient from
7
+ * <cwd>/prisma/generated/prisma/client.ts. Each service repo generates its own
8
+ * client from its own schema.prisma; resolving a static import would pick up
9
+ * whichever @prisma/generated bun resolves first in a multi-service workspace,
10
+ * which is wrong.
11
+ *
12
+ * No fallback: if the generated client is missing the deploy is broken — fail
13
+ * loud with the remediation step.
14
+ */
15
+ export type ServicePrismaClientConstructor = new (args: {
16
+ adapter: unknown;
17
+ }) => unknown;
18
+ export declare function loadServicePrismaClient(serviceSlug: string): Promise<ServicePrismaClientConstructor>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @system service-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Mounts the composed ORPC app router (generated Prisma CRUD + custom fn routers)
7
+ * at /api/rpc/* over HTTP via @orpc/server/fetch's RPCHandler.
8
+ * Uses getAppRouter() from @teamscala/orpc/app-router which merges generated
9
+ * routers with custom routers registered via registerCustomRouter().
10
+ */
11
+ import type { Hono } from "hono";
12
+ export interface OrpcMountResult {
13
+ routeCount: number;
14
+ }
15
+ export declare function mountOrpcRouter(app: Hono, serviceSlug: string): Promise<OrpcMountResult | null>;
@@ -37,7 +37,7 @@ export async function mountOrpcRouter(
37
37
  );
38
38
 
39
39
  // No generated ORPC router is the EXPECTED state for daemon-only / boot-hook
40
- // services (session-picker, dep-sync, etc.) that own no DB schema or
40
+ // services (session-picker, package-poller, etc.) that own no DB schema or
41
41
  // api_routes — they serve no /api/rpc surface. Skip silently: importing a
42
42
  // nonexistent module would throw into the catch below and surface a
43
43
  // misleading "ORPC router not mounted" WARN on every boot. The catch is
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @system server-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Walks `service_install.server_packages[]`, dynamic-imports each, and calls
7
+ * its `mount(ctx)` function. Server packages register handlers / boot hooks /
8
+ * scheduled jobs / WS routes via the MountContext exposed in ctx; the
9
+ * resulting MountRegistry is consumed by register-api-routes /
10
+ * register-ws-routes / run-boot-hook-list / start-scheduled-jobs.
11
+ */
12
+ import type { ServiceInstallConfig } from "@teamscala/db/registry/loader/load-service-install-row";
13
+ import { type MountRegistry } from "@teamscala/os/runtime-contracts/mount-context";
14
+ import type { Hono } from "hono";
15
+ /**
16
+ * Speculatively import every server_package's mount module so Bun compiles
17
+ * the module graphs into its cache NOW, overlapping the (CPU-bound)
18
+ * compilation with whatever I/O the caller runs next. When mountServerPackages
19
+ * later imports the same paths they hit the cache (instant).
20
+ *
21
+ * Same pattern as create-boot-pipeline's speculative preloads — the
22
+ * cross-internet DB I/O in the gate chain (~3.4s) is dead time the compiler
23
+ * can use. Best-effort + non-fatal: a failed import here (wrong candidate
24
+ * path, transient resolution race) is silently swallowed because
25
+ * mountServerPackages will re-attempt with full error surfacing; the preload
26
+ * only warms the cache, never gates boot.
27
+ */
28
+ export declare function preloadServerPackages(packages: string[]): Promise<void>;
29
+ export declare function mountServerPackages(app: Hono, config: ServiceInstallConfig, serviceSlug: string): Promise<MountRegistry>;
package/src/mount.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @system server-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Server-package mount for opt-in service-boot watchdogs.
7
+ */
8
+ import type { MountContext } from "@teamscala/os/runtime-contracts/mount-context";
9
+ export declare function mount(ctx: MountContext): void;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @system service-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Mutable app reference for two-phase boot. Allows swapping the Hono app
7
+ * atomically after building the full app in the background. The initial app
8
+ * is a minimal tool-only surface (ORPC, data routes) that starts serving
9
+ * immediately; the full app (pages, hydration) is built off-to-the-side and
10
+ * swapped in once complete.
11
+ *
12
+ * Thread-safety: Bun is single-threaded, so a simple module-level variable
13
+ * is sufficient. Atomics are not needed.
14
+ */
15
+ import type { Hono } from "hono";
16
+ /**
17
+ * Create a new mutable app reference initialized with the provided app.
18
+ * This should be called once at boot time.
19
+ */
20
+ export declare function initMutableAppRef(initialApp: Hono, label: string): void;
21
+ /**
22
+ * Get the current app. Throws if the ref hasn't been initialized.
23
+ */
24
+ export declare function getApp(): Hono;
25
+ /**
26
+ * Get the current app label for logging.
27
+ */
28
+ export declare function getAppLabel(): string;
29
+ /**
30
+ * Atomically swap the app to a new instance. This is called after the
31
+ * full app is built in the background. Returns the previous label for logging.
32
+ */
33
+ export declare function swapApp(newApp: Hono, newLabel: string): string;
34
+ /**
35
+ * Fetch handler that routes through the mutable app reference.
36
+ * This replaces the direct app.fetch call in the serve layer.
37
+ *
38
+ * Stamps transport-derived identity (x-caller-remote-address/-port + the
39
+ * loopback local-caller comm) via the SAME stampTransportIdentity helper as
40
+ * direct mode, so two-phase-boot (mutable-ref) UI services carry identical
41
+ * identity on every request — closing the gap where the mutable-ref path
42
+ * previously served requests with no transport headers stamped at all.
43
+ */
44
+ export declare function fetchWithMutableApp(req: Request, server: ReturnType<typeof Bun.serve>): Promise<Response>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @system ui-service-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Render mode literal shared across page route registration. The old
7
+ * shell-variant types (LegacyShell, PageSlugShell, RegionsShell,
8
+ * PublicShell, ConfigPageRoute) were removed when the Phase 2 deploy-time
9
+ * `service_install.page_routes[]` path was eliminated — all routing is
10
+ * now registry-driven `page_route` rows (Phase 1).
11
+ */
12
+ export type RenderModeLiteral = "public" | "portals" | "utility";
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @system page-server
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Audit revert procedure — re-applies an audit_trail row's changes_before to
7
+ * the entity via the canonical generated Prisma procedure for the entity's
8
+ * table: INSERT→delete the created row, UPDATE→restore the old values,
9
+ * DELETE→recreate from the deleted snapshot. The revert write itself fires the
10
+ * audit extension + DB trigger, recording a NEW forward audit_trail row —
11
+ * history is never edited (audit-trail-permanent; the undo is itself audited).
12
+ *
13
+ * Gated to super-admin (reverting business data is privileged). The caller
14
+ * must pass the audit_trail row id; the procedure resolves table + action +
15
+ * before-snapshot from that row.
16
+ */
17
+ import * as v from "valibot";
18
+ export declare const auditRevertRouter: {
19
+ /**
20
+ * Revert a single audited change by re-applying changes_before through the
21
+ * generated Prisma model for the entity's table. Returns the action taken.
22
+ */
23
+ revert: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
24
+ readonly auditTrailId: v.StringSchema<undefined>;
25
+ }, undefined>, import("@orpc/contract").Schema<{
26
+ success: true;
27
+ action: string;
28
+ table: string;
29
+ entityId: string;
30
+ }, {
31
+ success: true;
32
+ action: string;
33
+ table: string;
34
+ entityId: string;
35
+ }>, Record<never, never>, Record<never, never>>;
36
+ };
37
+ export type AuditRevertRouter = typeof auditRevertRouter;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @system page-server
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Module-level singleton for the hub MCP client. setHubMcpClient(client)
7
+ * is called once at boot by the host service after creating the multi-
8
+ * tenant MCP client; createPageHandler's resolve-page-state reads it via
9
+ * getHubMcpClient to pass through to data loaders.
10
+ */
11
+ export declare function setHubMcpClient(client: unknown): void;
12
+ export declare function getHubMcpClient(): unknown | undefined;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @system page-server
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * ORPC procedures for portal diagnostics. These replace direct Prisma calls
7
+ * in the portal diagnostic section files with proper ORPC procedures that
8
+ * receive context.prisma from the router.
9
+ */
10
+ import * as v from "valibot";
11
+ export declare const portalDiagRouter: {
12
+ findPortalWebsite: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{}, undefined>, v.NullableSchema<v.ObjectSchema<{
13
+ readonly id: v.StringSchema<undefined>;
14
+ readonly name: v.StringSchema<undefined>;
15
+ readonly organisation_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
16
+ }, undefined>, undefined>, Record<never, never>, Record<never, never>>;
17
+ findMember: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
18
+ readonly organisationId: v.StringSchema<undefined>;
19
+ }, undefined>, v.NullableSchema<v.ObjectSchema<{
20
+ readonly user_id: v.StringSchema<undefined>;
21
+ }, undefined>, undefined>, Record<never, never>, Record<never, never>>;
22
+ countPages: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
23
+ readonly websiteId: v.StringSchema<undefined>;
24
+ }, undefined>, v.NumberSchema<undefined>, Record<never, never>, Record<never, never>>;
25
+ findPages: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
26
+ readonly websiteId: v.StringSchema<undefined>;
27
+ readonly path: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
28
+ readonly take: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
29
+ readonly orderBy: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
30
+ }, undefined>, v.ArraySchema<v.ObjectSchema<{
31
+ readonly id: v.StringSchema<undefined>;
32
+ readonly path: v.NullableSchema<v.StringSchema<undefined>, undefined>;
33
+ readonly name: v.StringSchema<undefined>;
34
+ readonly puck_data: v.UnknownSchema;
35
+ readonly updated_at: v.StringSchema<undefined>;
36
+ }, undefined>, undefined>, Record<never, never>, Record<never, never>>;
37
+ findBlockSchemas: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
38
+ readonly slugs: v.ArraySchema<v.StringSchema<undefined>, undefined>;
39
+ }, undefined>, v.ArraySchema<v.ObjectSchema<{
40
+ readonly slug: v.StringSchema<undefined>;
41
+ }, undefined>, undefined>, Record<never, never>, Record<never, never>>;
42
+ findWebsites: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
43
+ readonly take: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
44
+ }, undefined>, v.ArraySchema<v.ObjectSchema<{
45
+ readonly id: v.StringSchema<undefined>;
46
+ readonly name: v.StringSchema<undefined>;
47
+ }, undefined>, undefined>, Record<never, never>, Record<never, never>>;
48
+ };
49
+ export type PortalDiagRouter = typeof portalDiagRouter;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @system page-server
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * ORPC procedures for loading Puck pages. These replace the direct Prisma calls
7
+ * in load-puck-page-by-slug.ts with proper ORPC procedures that receive
8
+ * context.prisma from the router.
9
+ */
10
+ import * as v from "valibot";
11
+ export declare const puckPagesRouter: {
12
+ loadFromWebsite: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
13
+ readonly websiteSlug: v.StringSchema<undefined>;
14
+ readonly path: v.StringSchema<undefined>;
15
+ }, undefined>, v.NullableSchema<v.ObjectSchema<{
16
+ readonly id: v.StringSchema<undefined>;
17
+ readonly path: v.NullableSchema<v.StringSchema<undefined>, undefined>;
18
+ readonly puckData: v.UnknownSchema;
19
+ readonly mode: v.NullableSchema<v.StringSchema<undefined>, undefined>;
20
+ readonly render_mode: v.NullableSchema<v.StringSchema<undefined>, undefined>;
21
+ readonly website_slug: v.StringSchema<undefined>;
22
+ readonly organisation_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
23
+ readonly meta_title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
24
+ readonly meta_description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
25
+ readonly website: v.ObjectSchema<{
26
+ readonly slug: v.StringSchema<undefined>;
27
+ readonly name: v.StringSchema<undefined>;
28
+ readonly organisation_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
29
+ readonly website_type: v.NullableSchema<v.StringSchema<undefined>, undefined>;
30
+ readonly layout_settings: v.NullableSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
31
+ readonly header_settings: v.NullableSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
32
+ readonly footer_settings: v.NullableSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
33
+ }, undefined>;
34
+ }, undefined>, undefined>, Record<never, never>, Record<never, never>>;
35
+ loadById: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
36
+ readonly pageId: v.StringSchema<undefined>;
37
+ }, undefined>, v.NullableSchema<v.ObjectSchema<{
38
+ readonly id: v.StringSchema<undefined>;
39
+ readonly path: v.NullableSchema<v.StringSchema<undefined>, undefined>;
40
+ readonly puckData: v.UnknownSchema;
41
+ readonly mode: v.NullableSchema<v.StringSchema<undefined>, undefined>;
42
+ readonly render_mode: v.NullableSchema<v.StringSchema<undefined>, undefined>;
43
+ readonly website_slug: v.StringSchema<undefined>;
44
+ readonly organisation_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
45
+ readonly meta_title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
46
+ readonly meta_description: v.NullableSchema<v.StringSchema<undefined>, undefined>;
47
+ readonly website: v.ObjectSchema<{
48
+ readonly slug: v.StringSchema<undefined>;
49
+ readonly name: v.StringSchema<undefined>;
50
+ readonly organisation_id: v.NullableSchema<v.StringSchema<undefined>, undefined>;
51
+ readonly website_type: v.NullableSchema<v.StringSchema<undefined>, undefined>;
52
+ readonly layout_settings: v.NullableSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
53
+ readonly header_settings: v.NullableSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
54
+ readonly footer_settings: v.NullableSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
55
+ }, undefined>;
56
+ }, undefined>, undefined>, Record<never, never>, Record<never, never>>;
57
+ };
58
+ export type PuckPagesRouter = typeof puckPagesRouter;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @system page-server
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Server-side ORPC client for in-process direct invocation, per the canonical
7
+ * Vite/Next/Nuxt SSR pattern documented at https://orpc.dev/docs/best-practices/optimize-ssr
8
+ *
9
+ * The host service (scala-install) must call `setServerClient(client)` once
10
+ * at boot, after createRouterClient(appRouter, { context: { prisma } }).
11
+ * Then page-server's createPageHandler reads `getServerClient()` per request
12
+ * — no HTTP loopback, just a typed function call.
13
+ */
14
+ import { createRouterClient } from "@orpc/server";
15
+ export declare function getServerClient(): ReturnType<typeof createRouterClient>;
16
+ export declare function setServerClient(client: ReturnType<typeof createRouterClient>): void;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @system page-server
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * ORPC procedure: templates.getSettings(slug). Reads the registry_entries row
7
+ * of type='template_settings' for the requested slug, validates against the
8
+ * matching Valibot schema from db-validation, returns typed settings.
9
+ *
10
+ * Per data-access.md: every data read goes through ORPC. The Template
11
+ * components do not call Prisma or registry directly — they receive the
12
+ * typed result of this procedure as a prop.
13
+ *
14
+ * Server-side (SSR) calls land here in-process via createRouterClient.
15
+ * Client-side calls would land here over HTTP via createORPCClient.
16
+ */
17
+ import * as v from "valibot";
18
+ export declare const templatesRouter: {
19
+ getSettings: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
20
+ readonly slug: v.StringSchema<undefined>;
21
+ }, undefined>, import("@orpc/contract").Schema<unknown, unknown>, Record<never, never>, Record<never, never>>;
22
+ /**
23
+ * Patch a template's settings row. The patch is a deep partial applied
24
+ * over the existing config; the merged result is validated against the
25
+ * matching Valibot schema before write. Used by chrome layout (pane
26
+ * widths, toolbar height, etc.) so resizes persist server-side and the
27
+ * next SSR render picks them up directly — no localStorage, no flash.
28
+ */
29
+ updateSettings: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
30
+ readonly slug: v.StringSchema<undefined>;
31
+ readonly patch: v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>;
32
+ }, undefined>, import("@orpc/contract").Schema<unknown, unknown>, Record<never, never>, Record<never, never>>;
33
+ };
34
+ export type TemplatesRouter = typeof templatesRouter;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @system page-server
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * ORPC procedures for per-user portal UI preferences (sidebar collapse,
7
+ * panel sizes, active right panel, conversation ID, brand cache).
8
+ * Reads and writes the `portal` key inside `user.preferences` JSONB.
9
+ *
10
+ * The SSR page loads initial state via getPortalPreferences; mutations
11
+ * from usePortalActionHelpers call updatePortalPreferences. This replaces
12
+ * the injected localStorage pattern with DB-backed persistence.
13
+ */
14
+ import * as v from "valibot";
15
+ export declare const userPreferencesRouter: {
16
+ /**
17
+ * Read the current user's portal preferences from user.preferences.portal.
18
+ * Returns null portal if no preferences exist yet.
19
+ */
20
+ getPortalPreferences: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, import("@orpc/contract").Schema<unknown, unknown>, import("@orpc/contract").Schema<{
21
+ portal: null;
22
+ } | {
23
+ portal: Record<string, unknown>;
24
+ }, {
25
+ portal: null;
26
+ } | {
27
+ portal: Record<string, unknown>;
28
+ }>, Record<never, never>, Record<never, never>>;
29
+ /**
30
+ * Merge and persist a patch to the user's portal preferences.
31
+ * The patch is a deep partial of the portal object; missing keys
32
+ * are preserved from the existing value.
33
+ */
34
+ updatePortalPreferences: import("@orpc/server").DecoratedProcedure<Record<never, never>, Record<never, never>, v.ObjectSchema<{
35
+ readonly patch: v.ObjectSchema<{
36
+ readonly theme: v.OptionalSchema<v.NullableSchema<v.PicklistSchema<["light", "dark"], undefined>, undefined>, undefined>;
37
+ readonly paneLeftWidth: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
38
+ readonly paneRightWidth: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
39
+ readonly sidebarCollapsed: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
40
+ readonly activePanel: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
41
+ readonly panelSizes: v.OptionalSchema<v.ObjectSchema<{
42
+ readonly main: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
43
+ readonly right: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
44
+ }, undefined>, undefined>;
45
+ readonly conversationId: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
46
+ readonly brandCache: v.OptionalSchema<v.NullableSchema<v.ObjectSchema<{
47
+ readonly logo: v.ObjectSchema<{
48
+ readonly light: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
49
+ readonly dark: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
50
+ readonly brand: v.OptionalSchema<v.NullableSchema<v.StringSchema<undefined>, undefined>, undefined>;
51
+ }, undefined>;
52
+ readonly primaryColor: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
53
+ }, undefined>, undefined>, undefined>;
54
+ }, undefined>;
55
+ }, undefined>, import("@orpc/contract").Schema<{
56
+ portal: {
57
+ theme?: "dark" | "light" | null | undefined;
58
+ paneLeftWidth?: number | undefined;
59
+ paneRightWidth?: number | undefined;
60
+ sidebarCollapsed?: boolean | undefined;
61
+ activePanel?: string | null | undefined;
62
+ conversationId?: string | null | undefined;
63
+ panelSizes: unknown;
64
+ brandCache: unknown;
65
+ };
66
+ }, {
67
+ portal: {
68
+ theme?: "dark" | "light" | null | undefined;
69
+ paneLeftWidth?: number | undefined;
70
+ paneRightWidth?: number | undefined;
71
+ sidebarCollapsed?: boolean | undefined;
72
+ activePanel?: string | null | undefined;
73
+ conversationId?: string | null | undefined;
74
+ panelSizes: unknown;
75
+ brandCache: unknown;
76
+ };
77
+ }>, Record<never, never>, Record<never, never>>;
78
+ };
79
+ export type UserPreferencesRouter = typeof userPreferencesRouter;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @system server-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * The pg-boss scheduled-job handler registry — `handler_slug` → handler
7
+ * function. Built on `@teamscala/generic-registry` `createRegistry` (the mandated
8
+ * primitive per `generic-registry-is-the-only-registry-pattern`); a hand-rolled
9
+ * Map or a hardcoded switch in service-boot is the prohibited alternative.
10
+ *
11
+ * Handlers live in packages and self-register at boot (a boot hook / package
12
+ * subscriber shape) by calling `registerScheduledJobHandler`. `resolveHandler`
13
+ * in `pg-boss-mount.ts` reads this registry to dispatch a firing `scheduled_job`
14
+ * row to its registered function.
15
+ */
16
+ import type { ScheduledJobConfig } from "@teamscala/db-validation/registry-schemas/scheduled-job";
17
+ export type PgBossJobHandler = (ctx: {
18
+ config: ScheduledJobConfig;
19
+ /** Per-job data payload from a one-shot `boss.send()` — absent for scheduled jobs. */
20
+ jobData?: unknown;
21
+ }) => Promise<unknown> | unknown;
22
+ export declare const pgBossHandlerRegistry: import("@teamscala/generic-registry/create-registry").Registry<PgBossJobHandler>;
23
+ /**
24
+ * Register a handler for a `scheduled_job.handler_slug`. Idempotent on re-import
25
+ * (createRegistry.register overwrites silently — last registration wins).
26
+ */
27
+ export declare function registerScheduledJobHandler(slug: string, handler: PgBossJobHandler): void;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @system server-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Mounts pg-boss for a backend service. Reads all scheduled_job registry
7
+ * entries scoped to the current service from the registry cache, registers
8
+ * pg-boss workers per queue_name, and schedules cron/interval jobs.
9
+ * Per Constitution Rule 6: pg-boss is the single mechanism for all
10
+ * application background work.
11
+ */
12
+ import { PgBoss } from "pg-boss";
13
+ import "./handlers/prune-stale-data.ts";
14
+ import "./handlers/projects-digest.ts";
15
+ import "./handlers/sweep-event-log.ts";
16
+ import "./handlers/sweep-host-cache.ts";
17
+ import "./handlers/bun-stable-watch.ts";
18
+ import "./handlers/whatsapp-liveness.ts";
19
+ import "./handlers/upstream-issue-sync.ts";
20
+ import "./handlers/scala-tools-currency-watch.ts";
21
+ import "./handlers/singleton-tools-currency-watch.ts";
22
+ import "./handlers/registry-stale-inactive-sweep.ts";
23
+ import "./handlers/pane-outbox-drain.ts";
24
+ /** Returns the mounted PgBoss instance, or null if pg-boss is not running on this service. */
25
+ export declare function getPgBoss(): PgBoss | null;
26
+ export interface PgBossMountContext {
27
+ databaseUrl: string;
28
+ serviceSlug: string;
29
+ packages: string[];
30
+ }
31
+ export interface MountedJob {
32
+ queueName: string;
33
+ handlerSlug: string;
34
+ cron?: string;
35
+ intervalSeconds?: number;
36
+ }
37
+ export interface PgBossMountResult {
38
+ boss: PgBoss | null;
39
+ jobs: MountedJob[];
40
+ }
41
+ export declare function intervalToCron(intervalSeconds: number): string;
42
+ export declare function mountPgBoss(ctx: PgBossMountContext): Promise<PgBossMountResult>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @system service-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Resolves the set of emails authorized to view a proposal. A proposal is
7
+ * gated (proposal-access) — only these emails can unlock it:
8
+ *
9
+ * - the issuing-org team: every member of the proposal's organisation
10
+ * (better_auth_member → user.email). Lets the agency team open any of their
11
+ * own proposals AND covers existing proposals whose created_by is
12
+ * "system"/null.
13
+ * - the recipient: the proposal's CRM contact (client_database.primary_email),
14
+ * when contact_id is set.
15
+ * - the partner team: every CRM contact belonging to the proposal's partner
16
+ * org (client_database.company_id = proposals.partner_id → primary_email),
17
+ * when partner_id is set. Lets the co-delivery partner's team open a
18
+ * co-branded proposal. The partner is still branding-first; this is the one
19
+ * access edge it carries.
20
+ *
21
+ * Uses $queryRaw (raw SQL) deliberately: the per-service Prisma client is scoped
22
+ * to that service's own models, so the proposals service's client has no
23
+ * `member`/`user` model methods — but $queryRaw runs against the shared DB
24
+ * regardless of the client's model scope. Returns a lowercased Set, checked
25
+ * server-side only (emails never sent to the client). Fail-closed: any error
26
+ * yields an empty set (no access).
27
+ */
28
+ export declare function resolveProposalAuthorizedEmails(proposalId: string): Promise<Set<string>>;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @system service-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * Injects the ProposalChatLauncher block into an AUTHORISED proposal page (called
7
+ * from proposalGateConfig.onAuthorized — so the bubble lives strictly behind the
8
+ * email gate, and every proposal gets it with no per-page Puck edit). It builds
9
+ * the proposal's full page content into a runtime-context string and passes it to
10
+ * the launcher, which forwards it to the chat as <runtime_context> — so ONE shared
11
+ * "Proposal Assistant" agent answers about whichever proposal is being viewed.
12
+ *
13
+ * The agent id is config, not code (registries-are-source-of-truth): the
14
+ * `config / proposal-assistant-agent` row carries { agentId }. No row (or no
15
+ * agent) → no injection, the page renders exactly as before (fail-soft).
16
+ */
17
+ export declare function injectProposalChatLauncher(resolved: Record<string, unknown>, viewerEmail: string): Promise<void>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @system service-boot
3
+ * @status handwritten
4
+ * @edit edit directly
5
+ *
6
+ * The PROPOSAL content gate, expressed as config for the content-gate primitive
7
+ * (content-gate/). No bespoke gate plumbing — just the proposal-specific bits:
8
+ * which resource (resolved.proposal.id), who's authorized (issuing-org team +
9
+ * recipient), what to seed (initialVisitor for the chat), and how to withhold
10
+ * (replace the proposal body with an injected SignIn gate block + neutralize the
11
+ * meta tags). The engine handles cookie/token/verify/orchestration.
12
+ *
13
+ * Universal: every proposal route exposes resolved.proposal, so this gates every
14
+ * proposal (existing + new) with no per-page block or migration.
15
+ */
16
+ import type { AccessConfig, ContentGateConfig } from "./content-gate/config-types.ts";
17
+ export declare const proposalGateConfig: ContentGateConfig;
18
+ export declare const proposalAccessConfig: AccessConfig;