@teamscala/service-boot 0.23.522 → 0.23.524

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 (119) hide show
  1. package/package.json +1 -1
  2. package/src/agreement-gate/agreement-lookup.ts +0 -7
  3. package/src/agreement-gate/authorized-emails.ts +0 -7
  4. package/src/agreement-gate/placeholder-strip.ts +0 -8
  5. package/src/agreement-gate/resolution-caches.ts +0 -5
  6. package/src/agreement-gate/settings.ts +0 -7
  7. package/src/agreement-gate/signing-session.ts +0 -8
  8. package/src/agreement-gate/signing-shell.ts +0 -9
  9. package/src/agreement-gate.ts +0 -18
  10. package/src/apply-logger-config.ts +1 -9
  11. package/src/asset-cache-key.ts +1 -5
  12. package/src/asset-optimize.ts +1 -7
  13. package/src/asset-proxy.ts +15 -9
  14. package/src/asset-social-card.ts +1 -9
  15. package/src/attach-agreement-magic.ts +0 -14
  16. package/src/attach-portal-members.ts +0 -17
  17. package/src/attach-proposal-accept.ts +0 -20
  18. package/src/attach-proposal-magic.ts +0 -12
  19. package/src/attach-transfers-upload.ts +0 -21
  20. package/src/auth/sign-out.ts +1 -42
  21. package/src/auth-gate/public-paths.ts +1 -10
  22. package/src/auth-gate.ts +1 -22
  23. package/src/bind-page-handler.ts +1 -7
  24. package/src/build-client-assets/build-lock.ts +1 -11
  25. package/src/build-client-assets/hashed-output.ts +1 -11
  26. package/src/build-client-assets/hydrate-bundle.ts +1 -11
  27. package/src/build-client-assets/manifest.ts +1 -12
  28. package/src/build-client-assets/source-candidates.ts +0 -17
  29. package/src/build-client-assets/sourcemaps-upload.ts +1 -10
  30. package/src/build-client-assets/styles-build.ts +1 -21
  31. package/src/build-client-assets.ts +1 -30
  32. package/src/capability-slug.ts +1 -35
  33. package/src/compose-page-title.ts +1 -25
  34. package/src/content-gate/access.ts +0 -8
  35. package/src/content-gate/catalog.ts +0 -23
  36. package/src/content-gate/config-types.ts +0 -6
  37. package/src/content-gate/gate-entry-clears.ts +0 -18
  38. package/src/content-gate/gate.ts +0 -7
  39. package/src/content-gate/parse-cookie.ts +0 -4
  40. package/src/content-gate/token.ts +0 -22
  41. package/src/content-page-floor.ts +0 -17
  42. package/src/csrf-protection.ts +1 -28
  43. package/src/error-page-logo.ts +0 -23
  44. package/src/expand-roster.ts +1 -16
  45. package/src/gate-ttls.ts +0 -12
  46. package/src/gpt-gate.ts +0 -18
  47. package/src/hono-error-handler.ts +0 -15
  48. package/src/install-harness.ts +1 -12
  49. package/src/install-logging.ts +1 -15
  50. package/src/is-react-element-type.ts +1 -11
  51. package/src/load-page-manifest.ts +1 -23
  52. package/src/load-service-install-row.ts +1 -6
  53. package/src/module-not-found.ts +0 -6
  54. package/src/mount-orpc-router.ts +0 -6
  55. package/src/mount-server-packages.ts +1 -8
  56. package/src/mount.ts +1 -4
  57. package/src/mutable-app-ref.ts +0 -10
  58. package/src/orpc-openapi-document.ts +1 -13
  59. package/src/orpc-openapi-gate.ts +1 -13
  60. package/src/page-route-shell-types.ts +1 -8
  61. package/src/page-server-loader-graph.ts +0 -10
  62. package/src/page-server-orpc/hub-mcp-client.ts +1 -7
  63. package/src/page-server-orpc/server-client.ts +1 -23
  64. package/src/pg-boss-handler-registry.ts +1 -12
  65. package/src/pg-boss-mount.ts +1 -8
  66. package/src/proposal-authorized-emails.ts +0 -32
  67. package/src/proposal-gate.ts +0 -11
  68. package/src/quiz-gate.ts +0 -22
  69. package/src/read-html-template.ts +1 -49
  70. package/src/readiness-gate.ts +0 -8
  71. package/src/register-api-routes.ts +1 -11
  72. package/src/register-crash-handlers.ts +1 -6
  73. package/src/register-page-routes.ts +1 -12
  74. package/src/register-page-server-client.ts +1 -13
  75. package/src/register-registry-page-route/ai-chat-prefetch.ts +1 -6
  76. package/src/register-registry-page-route/apply-page-branding.ts +1 -10
  77. package/src/register-registry-page-route/captured-loader-config.ts +1 -11
  78. package/src/register-registry-page-route/graph-cache.ts +1 -21
  79. package/src/register-registry-page-route/live-loader-config.ts +1 -22
  80. package/src/register-registry-page-route/meta-title.ts +1 -12
  81. package/src/register-registry-page-route/page-data-shared.ts +1 -12
  82. package/src/register-registry-page-route/prefetch-org-id.ts +1 -7
  83. package/src/register-registry-page-route/proposal-approval-email-token.ts +1 -11
  84. package/src/register-registry-page-route/proposal-brand-stamping.ts +1 -11
  85. package/src/register-registry-page-route/public-shell-page-data.ts +1 -8
  86. package/src/register-registry-page-route/region-slots-page-data.ts +1 -8
  87. package/src/register-registry-page-route/resolve-page-data.ts +1 -10
  88. package/src/register-registry-page-route/resolve-panel-page.ts +1 -10
  89. package/src/register-registry-page-route/resolve-request-graph.ts +1 -11
  90. package/src/register-registry-page-route/run-content-gates.ts +1 -10
  91. package/src/register-registry-page-route/single-page-data.ts +1 -9
  92. package/src/register-registry-page-route/stamp-session-context.ts +1 -10
  93. package/src/register-registry-page-route/warn-uncovered-blocks.ts +1 -8
  94. package/src/register-registry-page-route.ts +1 -18
  95. package/src/register-static-assets.ts +1 -8
  96. package/src/register-ws-routes.ts +1 -21
  97. package/src/resolve-brand-favicon.ts +0 -13
  98. package/src/resolve-service-port.ts +1 -50
  99. package/src/resolve-theme-mode.ts +1 -22
  100. package/src/resolvers/app-header.ts +1 -19
  101. package/src/resolvers/fetch-org-brand.ts +1 -10
  102. package/src/resolvers/portal-route-paths.ts +1 -12
  103. package/src/resolvers/website-by-host-map.ts +1 -7
  104. package/src/resource-chat-inject.ts +0 -17
  105. package/src/resource-not-found.ts +0 -24
  106. package/src/run-boot-hook-list.ts +1 -13
  107. package/src/run-boot-hooks.ts +1 -19
  108. package/src/scala-tools-currency-watchdog.ts +1 -7
  109. package/src/security-headers.ts +1 -7
  110. package/src/select-service-routes.ts +1 -17
  111. package/src/server-error-page.ts +0 -18
  112. package/src/theme-fonts-link.ts +1 -16
  113. package/src/transfers/presigned-upload.ts +1 -27
  114. package/src/two-phase-full-app.ts +0 -6
  115. package/src/two-phase-minimal-app.ts +0 -6
  116. package/src/two-phase-readiness.ts +0 -6
  117. package/src/types.ts +0 -3
  118. package/src/ui-service.ts +1 -6
  119. package/src/ws-boot-self-test.ts +1 -14
package/package.json CHANGED
@@ -302,5 +302,5 @@
302
302
  "repository": "https://github.com/teamgbg/service-boot",
303
303
  "sideEffects": false,
304
304
  "type": "module",
305
- "version": "0.23.522"
305
+ "version": "0.23.524"
306
306
  }
@@ -1,13 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Agreement lookup: typed first-match query over the agreements model via
7
- * the page-server ORPC client.
8
- *
9
- * Split out of agreement-gate.ts (909 lines) — byte-for-byte move; import
10
- * paths adjusted only.
11
4
  */
12
5
 
13
6
  import { getServerClient } from "../page-server-orpc/server-client.ts";
@@ -1,13 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The agreement's authorized email sets: the signer roster (who may sign) and
7
- * the roster ∪ owning/party-org members (who may view past the gate).
8
- *
9
- * Split out of agreement-gate.ts (909 lines) — byte-for-byte move; import
10
- * paths adjusted only.
11
4
  */
12
5
 
13
6
  import { getAppLogger } from "@teamscala/logger/app-loggers";
@@ -1,14 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The withholding transform: drop blocks whose own text still carries an
7
- * {{agreement.*}} token, recursively, so unauthed viewers never see raw
8
- * placeholders.
9
- *
10
- * Split out of agreement-gate.ts (909 lines) — byte-for-byte move; import
11
- * paths adjusted only.
12
4
  */
13
5
 
14
6
  import type { ContentBlock } from "./signing-session.ts";
@@ -1,11 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The agreement gate's resolution caches: the page→agreement association
7
- * cache, the owning/party org member-directory caches, and the event-driven
8
- * invalidation (LISTEN registry_config_changed) that clears them.
9
4
  */
10
5
 
11
6
  import { getAppLogger } from "@teamscala/logger/app-loggers";
@@ -1,13 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * DB-driven display + behaviour settings for the AGREEMENT content gate
7
- * (config/content-gate-agreement), plus its cookie name.
8
- *
9
- * Split out of agreement-gate.ts (909 lines) — byte-for-byte move; import
10
- * paths adjusted only.
11
4
  */
12
5
 
13
6
  import { loadRegistryConfig } from "@teamscala/db/registry/load-config";
@@ -1,14 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The verified signer's signing session: gate-block discovery across regions,
7
- * roster/party session construction (signer vs view-only org member), and
8
- * seeding of the session into every SignatureField.
9
- *
10
- * Split out of agreement-gate.ts (909 lines) — byte-for-byte move; import
11
- * paths adjusted only.
12
4
  */
13
5
 
14
6
  import { getAppLogger } from "@teamscala/logger/app-loggers";
@@ -1,15 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * New-flow signing-shell injection (no pre-placed gate required): agreement
7
- * meta stashing, body-content discovery, the SignIn gate block builder, and
8
- * the DB-driven signing shell (config/agreement-signing-shell) with its
9
- * hardcoded fallback.
10
- *
11
- * Split out of agreement-gate.ts (909 lines) — byte-for-byte move; import
12
- * paths adjusted only.
13
4
  */
14
5
 
15
6
  /**
@@ -1,24 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The AGREEMENT content gate, expressed as config for the content-gate primitive
7
- * (content-gate/). The agreement-specific bits: which resource (the placed
8
- * SignIn gate block's id), who's authorized (the signer roster), what to seed
9
- * (the verified signer's signing session into every SignatureField), and how to
10
- * withhold (strip the gate region to gate-only + strip {{agreement.*}} chrome).
11
- * The engine handles cookie/token/verify/orchestration.
12
- *
13
- * The signing logic (buildSigningSession, seedSigningBlocks, roster expansion,
14
- * placeholder strip) is preserved verbatim from the former gate-agreement-
15
- * content.ts — only the cookie/token orchestration moved into the shared engine.
16
- *
17
- * This module holds the two exported configs (agreementGateConfig +
18
- * agreementAccessConfig). The machinery lives as siblings under
19
- * ./agreement-gate/ (settings, agreement-lookup, signing-session,
20
- * placeholder-strip, signing-shell, authorized-emails) — split when the
21
- * parent held them all at 909 lines.
22
4
  */
23
5
 
24
6
  import type { AccessConfig, ContentGateConfig } from "./content-gate/config-types.ts";
@@ -1,14 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Late-stage logger configuration — runs AFTER Registry is loaded (the boot
7
- * pipeline's `apply-logger-config` phase), injecting the DB-driven settings
8
- * into the logger primitive. Split from install-logging.ts under
9
- * single-purpose-file: the early bootstrap (hardcoded defaults, primitive
10
- * injection) and this late reconfiguration are two phases, and the guard
11
- * counts them as two export groups in one file.
12
4
  */
13
5
 
14
6
  import { setMinLevel } from "@teamscala/logger/creator";
@@ -1,10 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * ETag derivation for the asset proxy that incorporates the negotiated
7
- * image format so per-Accept variants cache independently.
8
4
  */
9
5
 
10
6
  const SKIP_FORMATS = new Set(["webp", "avif", "gif", "svg", "ico"]);
@@ -1,12 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * On-the-fly image optimisation for the asset proxy. Converts stored
7
- * PNG/JPEG to WebP or AVIF when the browser signals support, using
8
- * @teamscala/image-optimizer's compressImage primitive. Returns null
9
- * on failure so the caller can fall back to the original stream.
10
4
  */
11
5
 
12
6
  import { getAppLogger } from "@teamscala/logger/app-loggers";
@@ -1,14 +1,9 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Same-origin S3 asset proxy with on-the-fly image optimisation.
7
- * Streams objects from the configured S3 bucket, converting PNG/JPEG
8
- * to WebP/AVIF when the browser declares support via Accept header.
9
- * Returns 404 for missing keys, 502 for S3 failures. Never redirects.
10
4
  */
11
5
 
6
+ import { getAppLogger } from "@teamscala/logger/app-loggers";
12
7
  import { getClient } from "@teamscala/object-storage/create/client/get-client";
13
8
  import {
14
9
  buildEtag,
@@ -37,6 +32,9 @@ export async function handleAssetProxy(req: Request): Promise<Response> {
37
32
 
38
33
  const s3 = await getClient();
39
34
  if (!s3) {
35
+ getAppLogger().error("[asset-proxy] no storage client — credential resolution returned no client", {
36
+ key,
37
+ });
40
38
  return new Response("", { status: 502 });
41
39
  }
42
40
 
@@ -45,7 +43,11 @@ export async function handleAssetProxy(req: Request): Promise<Response> {
45
43
  let stats: Awaited<ReturnType<typeof file.stat>>;
46
44
  try {
47
45
  stats = await file.stat();
48
- } catch {
46
+ } catch (err) {
47
+ getAppLogger().error("[asset-proxy] store stat failed", {
48
+ error: err,
49
+ key,
50
+ });
49
51
  return new Response("Not Found", { status: 404 });
50
52
  }
51
53
 
@@ -117,7 +119,11 @@ export async function handleAssetProxy(req: Request): Promise<Response> {
117
119
  let body: ReadableStream<Uint8Array<ArrayBuffer>>;
118
120
  try {
119
121
  body = file.stream();
120
- } catch {
122
+ } catch (err) {
123
+ getAppLogger().error("[asset-proxy] store stream failed", {
124
+ error: err,
125
+ key,
126
+ });
121
127
  return new Response("", { status: 502 });
122
128
  }
123
129
 
@@ -1,14 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Dynamic-import wrapper around image-optimizer's in-process sharp
7
- * social-card op. Mirrors asset-optimize.ts's optional-dependency shape:
8
- * services without image-optimizer installed transparently get null and
9
- * fall back to the raw asset stream. In-process (not HTTP) because the
10
- * og:image transform is a cold path per shared link — no auth handshake,
11
- * no per-consumer service config.
12
4
  */
13
5
 
14
6
  import { getAppLogger } from "@teamscala/logger/app-loggers";
@@ -1,20 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The agreement MAGIC-LINK endpoint. A recipient who clicks an emailed
7
- * "Review & sign" link — which carries a pre-authorised, signed ag_token for
8
- * THEIR party — lands here: the token is verified for the agreement, the
9
- * ag_token cookie is set on the shared parent domain, and the browser is
10
- * 302-redirected straight into the signing page. No email-entry gate, no OTP —
11
- * "came from our email" IS the proof of identity, exactly like the
12
- * proposal-accept hand-off (attach-proposal-accept.ts).
13
- *
14
- * The token is the same HMAC ag_token the gate verifies (30-min TTL, scoped to
15
- * {agreementId, email}); an invalid/expired/forged token sets no cookie and the
16
- * destination page simply shows the normal email gate. GET so it is a plain
17
- * clickable link in an email.
18
4
  */
19
5
 
20
6
  import type { Hono } from "hono";
@@ -1,23 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Portal-secured member directory endpoint. Resolves members of an org via a
7
- * parameterized raw join over better_auth_member + "user" — NOT the typed
8
- * `member` ORPC model, which is deliberately omitted from the portal's
9
- * service_schema (security: the raw table is not exposed as a typed CRUD
10
- * surface). Mirrors the agreement-gate.ts:109-150 precedent.
11
- *
12
- * The portal's auth middleware (better-auth-cookie) gates the endpoint — only
13
- * authenticated users can call it. The orgId comes from the caller's context
14
- * (the portal resolves it from the session + passes it).
15
- *
16
- * Endpoint: GET /api/portal/members?orgId=<uuid>
17
- * - orgId omitted: super-admin all-orgs mode (returns every member across
18
- * all orgs, strictly gated to super-admin via getSession; used by the
19
- * "View As" impersonation dropdown).
20
- * Response: { members: Array<{ id, user_id, organization_id, role, email, name, image }> }
21
4
  */
22
5
 
23
6
  import type { Hono } from "hono";
@@ -1,26 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The proposal ACCEPT endpoint. A viewer who has passed the proposal gate (holds
7
- * a valid prop_token) POSTs {proposalId} here to accept; the server generates a
8
- * signing document from the proposal (createAgreementFromProposal) and
9
- * 302-redirects them straight into signing it on the sign site. This replaces
10
- * the old mailto CTA. Mounted BEFORE the auth gate so it is publicly reachable —
11
- * the prop_token (not a login) is the proof of identity, exactly like the
12
- * content-gate access endpoint.
13
- *
14
- * Single sign-on across the journey: the viewer already proved their email at
15
- * the proposal gate (prop_token), so we MUST NOT make them re-enter it on the
16
- * sign site. Before redirecting, we mint the agreement's ag_token for that same
17
- * verified email and set it on the SHARED parent domain (Domain=.scala.business)
18
- * so the browser carries it from proposals.scala.business to sign.scala.business
19
- * — the agreement gate then auto-reveals + seeds the signing session with zero
20
- * extra prompts. This requires both hosts to resolve the SAME token secret
21
- * (AUTH_SECRET→ENCRYPTION_KEY→DATABASE_URL): scala-sign must carry ENCRYPTION_KEY
22
- * (also required to decrypt the org's Stripe key on full sign), so both hosts
23
- * land on ENCRYPTION_KEY and the carried token verifies.
24
4
  */
25
5
 
26
6
  import type { Hono } from "hono";
@@ -1,18 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The proposal MAGIC-LINK endpoint — the proposal twin of attach-agreement-magic.
7
- * A recipient who clicks an emailed "View your proposal" link (carrying a signed,
8
- * per-recipient prop_token) lands here: the token is verified for the proposal,
9
- * the prop_token cookie is set, and the browser is 302-redirected straight into
10
- * the proposal page. No email-entry gate, no OTP. An invalid/expired token sets
11
- * no cookie and the page falls through to the normal email gate.
12
- *
13
- * The prop_token is host-scoped (proposals are served on the proposals host only),
14
- * unlike the agreement ag_token which spans .scala.business for the proposal→sign
15
- * hand-off. GET so it is a plain clickable link in an email.
16
4
  */
17
5
 
18
6
  import type { Hono } from "hono";
@@ -1,27 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Authenticated asset upload — POST /api/transfers/upload. Restores the
7
- * handler deleted with @teamscala/server-files (commit ca5643ef); avatar/logo
8
- * upload had 404'd on every service since. Writes to S3 via
9
- * @teamscala/object-storage/create/write-ops and returns the SERVABLE URL
10
- * (`/api/assets/<key>`) the client persists into avatar_url/logo_url — the
11
- * stored value is the servable value (single-logo-pathway), so display code
12
- * reads the column with no rewriting. Self-authenticates (getSession) so it
13
- * mounts BEFORE the auth gate.
14
- *
15
- * Key scheme is USER-scoped (`users/<userId>/<type>/<file>-<ts>.<ext>`) per
16
- * the operator ruling: a profile picture belongs to the individual, not any
17
- * org (the prior handler's org-scoped `<orgId>/avatars/...` encoded the
18
- * uploading org into a personal asset). Spec + history:
19
- * `reference/asset-upload-system.md`.
20
- *
21
- * `thumbUrl` mirrors `url` — the full image is served for both; a separate
22
- * generated thumbnail is a future refinement (the prior handler generated
23
- * one, but the image-resize dependency is not wired here yet and a missing
24
- * thumb must not block the upload restoring).
25
4
  */
26
5
  import type { Hono } from "hono";
27
6
  import { getAppLogger } from "@teamscala/logger/app-loggers";
@@ -1,47 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
- * @status handwritten — none derivable: the api_route row (fn-auth-sign-out) declares the path, auth and input, not a body; host-bus-handler-contracts emits the client node + wiring FROM the row, never a procedure body; the sign-out mechanism is BetterAuth's `auth.api.signOut` on the app's own session + the /api/auth/* → scala-otp forward that auth-gate already performs (the body composes those two, it is not generated from either).
4
- * @edit edit directly
5
- *
6
- * The fn.auth.signOut procedure body (api_route `fn-auth-sign-out`, authed:
7
- * context). It is the host-side sign-out the Button logout action calls
8
- * through the generated ORPC client node, replacing the raw browser POST to
9
- * /api/auth/sign-out. It takes no input — sign-out has no fields — so the
10
- * generated node is called as `orpcClient.fn.auth.signOut()`, the same no-arg
11
- * shape as fn.chat.health.
12
- *
13
- * WHY A PROCEDURE, NOT THE RAW FETCH. The old client called
14
- * `/api/auth/sign-out` directly. That path is only served when the host is
15
- * running the auth gate's `/api/auth/*` forward or a local BetterAuth handler,
16
- * and it is a bare string path the client hard-codes — a second, unnamed
17
- * statement of "where sign-out lives". Declaring it as an api_route procedure
18
- * gives it the one home (the registry row) the rest of the auth surface uses,
19
- * and the generated node carries the path.
20
- *
21
- * WHAT IT DOES, IN ORDER, so server-side behaviour is UNCHANGED from the
22
- * forwarded POST:
23
- * 1. Revoke the caller's session through the SAME BetterAuth instance the
24
- * session-read path uses (getAuth() — the bridge auth-gate installs, so
25
- * it validates through the identical factory). This deletes the session
26
- * row and returns the Set-Cookie headers that clear it.
27
- * 2. When SCALA_OTP_URL is set — centralised auth, per otp-centralised-via-
28
- * otp-service — forward the same POST to scala-otp's /api/auth/sign-out
29
- * with the caller's cookie, so the central service clears the shared
30
- * Domain=.scala.business session too. The local revoke alone would leave
31
- * the central session alive. The caller's cookie is carried forward and
32
- * scala-otp's response status is checked, so a central refusal is reported
33
- * rather than swallowed — the same POST the /api/auth/* forward in
34
- * auth-gate performs.
35
- *
36
- * HONEST RESIDUAL: the ORPC response is JSON, so the Set-Cookie headers
37
- * BetterAuth returns on its Response cannot ride back to the browser through
38
- * this procedure. The session is revoked in the database on both sides, and
39
- * the caller redirects to the sign-in path, so the browser presents a
40
- * revoked-and-deleted session token on its next request and the auth gate
41
- * rejects it — the same observable outcome as the forwarded POST, whose
42
- * cleared cookie was an optimisation. Clearing the cookie explicitly is the
43
- * caller's business (the raw /api/auth/clear-stale-cookies route), not this
44
- * procedure's.
3
+ * @status handwritten
45
4
  */
46
5
  import { getAppLogger } from "@teamscala/logger/app-loggers";
47
6
 
@@ -1,15 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The auth gate's public-path surface: sign-in paths, the static-asset
7
- * extension predicate, and the public infra prefixes/paths (self-authenticating
8
- * service-to-service surfaces, dev-host infra reads, and the access-gate
9
- * rejection target).
10
- *
11
- * Split out of auth-gate.ts (436 lines) — byte-for-byte move; import paths
12
- * adjusted only.
13
4
  */
14
5
 
15
6
  export const SIGN_IN_PATHS = new Set([
package/src/auth-gate.ts CHANGED
@@ -1,27 +1,6 @@
1
1
  /**
2
- * @system server-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Auth middleware factory. Three modes, same BetterAuth email-OTP mechanism:
7
- *
8
- * service_install.auth values:
9
- * - omitted | "none" → no gate, no handler (scala-public, scala-sign)
10
- * - "better-auth-cookie" → full session auth: mount handler + session gate
11
- * - "email-otp-only" → mount handler (OTP send/verify), no session gate
12
- *
13
- * The sign-in block determines the UX per mode:
14
- * - "full" → email → OTP → session → portal selection (portal, agents, docs, gpt)
15
- * - "email-gate" → email → OTP → verify-only, no session (forms, proposals, quiz)
16
- * - "capture-only" → email field only, no OTP (sign)
17
- *
18
- * There is NO localhost bypass: 127.0.0.1 is gated identically to the tunnel
19
- * (no session → /sign-in). Tools needing an authed view sign in
20
- * (getSuperAdminSessionCookie); the dev OTP is DB-readable.
21
- *
22
- * When SCALA_OTP_URL is set, /api/auth/* forwards to the centralised
23
- * scala-otp service (per otp-centralised-via-otp-service). Otherwise
24
- * the local better-auth handler is used (dev-host fallback).
25
4
  */
26
5
 
27
6
  /**
@@ -1,12 +1,6 @@
1
1
  /**
2
- * @system ui-service-boot
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Calls a createPageHandler() handler with the per-request extras the
7
- * SSR pipeline needs: the resolved userId/orgId (from localhost-bypass
8
- * or better-auth-cookie), and the htmlTemplate. Shared by the regions
9
- * and page_slug shell variants.
10
4
  */
11
5
 
12
6
  // Per-page root.props.maxWidth (the canonical mechanism) centers any page at the chosen width via inline styles — already hydration-safe
@@ -1,16 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The host-wide client-build lock of the client-asset build: serialises the
7
- * heavy boot-time client build across ALL kind:ui services
8
- * (client-asset-builds-serialize-via-lock), with a DB-driven TTL that
9
- * fail-softs to a hardcoded default.
10
- *
11
- * Split out of build-client-assets.ts (962 lines) alongside its build
12
- * siblings. Byte-for-byte move from the parent — imports adjusted, nothing
13
- * else.
14
4
  */
15
5
 
16
6
  import { join } from "node:path";
@@ -1,16 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Content-hashed output housekeeping for the client-asset build: the
7
- * short content hash stamped into hashed filenames, and removal of stale
8
- * hashed files from a previous build so dist/client/ never accumulates
9
- * dead outputs.
10
- *
11
- * Split out of build-client-assets.ts (962 lines) alongside its build
12
- * siblings. Byte-for-byte move from the parent except createHash →
13
- * Bun.CryptoHasher (bun-native-crypto guard; identical digest output).
14
4
  */
15
5
 
16
6
  import { readdir, rm } from "node:fs/promises";
@@ -1,16 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The hydrate JS bundle unit of the client-asset build: Bun.build over
7
- * src/entry-client.ts with the resilient-block-imports plugin (dead block
8
- * imports degrade at runtime instead of crash-looping the service), the
9
- * post-build dead-facade gate, stale-chunk removal, and source-map upload.
10
- *
11
- * Split out of build-client-assets.ts (962 lines) alongside its build
12
- * siblings. Byte-for-byte move from the parent — imports adjusted, nothing
13
- * else.
14
4
  */
15
5
 
16
6
  // Externalize an unresolvable @teamscala/* block import (a deactivated block_schema whose manifest regen never propagated) or a broken
@@ -1,17 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The asset-manifest unit of the client-asset build: the AssetManifest shape,
7
- * its read/write, the last-failure tombstone status read the runtime surfaces,
8
- * and the first-boot fallback paths.
9
- *
10
- * Split out of build-client-assets.ts (962 lines) alongside its build
11
- * siblings — the file held six concerns (manifest, hashed output, hydrate
12
- * bundle, source-map upload, stylesheet build, fingerprint, lock, orchestration).
13
- *
14
- * Byte-for-byte move from the parent — imports adjusted, nothing else.
15
4
  */
16
5
 
17
6
  import { join } from "node:path";
@@ -1,23 +1,6 @@
1
1
  /**
2
2
  * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Tailwind source-candidate scanning for the client CSS build.
7
- *
8
- * Split out of build-client-assets.ts (1221 lines, six concerns). Owns ONE
9
- * concern: turning the resolved `@source` directories into the candidate token
10
- * set Tailwind's `build()` consumes.
11
- *
12
- * Why it exists at all, preserved from the original: Tailwind v4's low-level
13
- * `compile()` does NOT scan content, and @tailwindcss/oxide's native Scanner is
14
- * not a dependency of the bare `tailwindcss` lib — only of the integration
15
- * packages. So the tokens are extracted here. Over-extraction is safe, because
16
- * `build()` ignores any token that is not a valid utility. Dropping this scan is
17
- * what shipped an unstyled page to the operator's tablet on 2026-06-13; it is
18
- * enforced by `css-build-scans-source-candidates`.
19
- *
20
- * Byte-for-byte move — imports adjusted, nothing else.
21
4
  */
22
5
 
23
6
  import { resolve } from "node:path";
@@ -1,15 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * Upload source maps to GlitchTip via the official `glitchtip-cli` debug-ID
7
- * flow, then RETAIN the maps outside the served root so a failed upload is
8
- * retryable on a later boot.
9
- *
10
- * Split out of build-client-assets.ts (962 lines) alongside its build
11
- * siblings. Byte-for-byte move from the parent — imports adjusted, nothing
12
- * else.
13
4
  */
14
5
 
15
6
  /**
@@ -1,26 +1,6 @@
1
1
  /**
2
- * @system server-handlers
2
+ * @system service-boot
3
3
  * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The Tailwind stylesheet unit of the client-asset build: Bun-native
7
- * stylesheet resolution returning RAW content, stripping of remote
8
- * (CSP-forbidden) @imports, and the content-hashed styles-<hash>.css
9
- * build.
10
- *
11
- * NESTED @IMPORTS ARE THE SUPPLIER'S JOB: tailwind 4.3.3's compile()
12
- * re-invokes loadStylesheet for every @import INSIDE loaded content and
13
- * splices the loaded nodes into the tree (verified in the installed
14
- * dist source — the dr() recursion, depth-capped at 100), so a present
15
- * nested leaf is inlined by the supplier and a missing one throws at
16
- * compile; the suite pins both. The 2026-07-05 dropped-leaf outage was
17
- * guarded by an in-process flattener here; with the supplier recursion
18
- * verified the flattener is a second owner of the same fact and is
19
- * deleted.
20
- *
21
- * Split out of build-client-assets.ts (962 lines) alongside its build
22
- * siblings. Byte-for-byte move from the parent — imports adjusted, nothing
23
- * else.
24
4
  */
25
5
 
26
6
  import { dirname, join, resolve } from "node:path";