@teamscala/service-boot 0.23.516 → 0.23.518

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 (86) hide show
  1. package/package.json +232 -1127
  2. package/src/agreement-gate/settings.ts +8 -8
  3. package/src/apply-logger-config.test.ts +46 -0
  4. package/src/apply-logger-config.ts +31 -0
  5. package/src/asset-proxy.test.ts +9 -5
  6. package/src/auth/sign-out.ts +122 -0
  7. package/src/build-client-assets/hydrate-bundle.ts +14 -4
  8. package/src/build-client-assets/manifest.ts +5 -18
  9. package/src/build-client-assets/styles-build.test.ts +11 -8
  10. package/src/build-client-assets/styles-build.ts +14 -63
  11. package/src/build-client-assets.test.ts +0 -161
  12. package/src/build-client-assets.ts +20 -67
  13. package/src/content-gate/token.ts +9 -2
  14. package/src/install-logging.ts +10 -26
  15. package/src/mount-orpc-router.ts +8 -2
  16. package/src/mutable-app-ref.ts +0 -7
  17. package/src/page-server-loader-graph.test.ts +31 -0
  18. package/src/page-server-loader-graph.ts +21 -0
  19. package/src/pg-boss-mount.ts +3 -16
  20. package/src/readiness-gate.ts +0 -4
  21. package/src/register-api-routes.ts +8 -17
  22. package/src/register-page-server-client.ts +6 -0
  23. package/src/register-registry-page-route/meta-title.test.ts +50 -0
  24. package/src/register-registry-page-route/meta-title.ts +1 -1
  25. package/src/register-registry-page-route/page-data-shared.ts +7 -11
  26. package/src/register-registry-page-route/resolve-request-graph.test.ts +1 -1
  27. package/src/register-registry-page-route/resolve-request-graph.ts +1 -2
  28. package/src/register-registry-page-route.ts +14 -16
  29. package/src/resolve-brand-favicon.test.ts +63 -0
  30. package/src/resolve-theme-mode.test.ts +51 -0
  31. package/src/resolvers/fetch-org-brand.test.ts +48 -0
  32. package/src/resolvers/website-by-host-map.test.ts +63 -0
  33. package/src/resolvers/website-by-host-map.ts +15 -3
  34. package/src/run-boot-hook-list.test.ts +47 -0
  35. package/src/run-boot-hooks.test.ts +56 -0
  36. package/src/scala-tools-currency-watchdog.test.ts +43 -0
  37. package/src/scala-tools-currency-watchdog.ts +0 -5
  38. package/src/transfers/presigned-upload.ts +165 -0
  39. package/src/two-phase-minimal-app.test.ts +24 -0
  40. package/src/two-phase-readiness.test.ts +102 -0
  41. package/src/types.test.ts +20 -0
  42. package/src/ui-service.test.ts +24 -0
  43. package/src/ws-boot-self-test.test.ts +43 -0
  44. package/src/build-client-assets/input-fingerprint.ts +0 -92
  45. package/src/build-client-assets/input-hashing.ts +0 -206
  46. package/src/handlers/bun-stable-watch.ts +0 -86
  47. package/src/handlers/messaging-send-reconcile-logic.test.ts +0 -79
  48. package/src/handlers/messaging-send-reconcile-logic.ts +0 -83
  49. package/src/handlers/messaging-send-recording-logic.test.ts +0 -86
  50. package/src/handlers/messaging-send-recording-logic.ts +0 -68
  51. package/src/handlers/messaging-send-recording-watch.test.ts +0 -91
  52. package/src/handlers/messaging-send-recording-watch.ts +0 -244
  53. package/src/handlers/messaging-send-sweep-selection.test.ts +0 -47
  54. package/src/handlers/messaging-send-sweep-selection.ts +0 -35
  55. package/src/handlers/platform-fault-escalation/config.ts +0 -95
  56. package/src/handlers/platform-fault-escalation/delivery.ts +0 -327
  57. package/src/handlers/platform-fault-escalation/event-log-queries.ts +0 -182
  58. package/src/handlers/platform-fault-escalation/orpc-client.ts +0 -114
  59. package/src/handlers/platform-fault-escalation/probe.test.ts +0 -63
  60. package/src/handlers/platform-fault-escalation/probe.ts +0 -19
  61. package/src/handlers/platform-fault-escalation-logic.ts +0 -382
  62. package/src/handlers/platform-fault-escalation-refresh.test.ts +0 -72
  63. package/src/handlers/platform-fault-escalation.test.ts +0 -497
  64. package/src/handlers/platform-fault-escalation.ts +0 -246
  65. package/src/handlers/projects-digest.ts +0 -198
  66. package/src/handlers/quiz-research.ts +0 -178
  67. package/src/handlers/registry-stale-inactive-sweep.ts +0 -82
  68. package/src/handlers/scala-tools-currency-watch.test.ts +0 -65
  69. package/src/handlers/scala-tools-currency-watch.ts +0 -162
  70. package/src/handlers/singleton-tools-currency-watch.test.ts +0 -51
  71. package/src/handlers/singleton-tools-currency-watch.ts +0 -380
  72. package/src/handlers/sweep-event-log.ts +0 -98
  73. package/src/handlers/upstream-issue-sync.ts +0 -220
  74. package/src/handlers/whatsapp-liveness-handler.test.ts +0 -56
  75. package/src/handlers/whatsapp-liveness-logic.ts +0 -53
  76. package/src/handlers/whatsapp-liveness.test.ts +0 -78
  77. package/src/handlers/whatsapp-liveness.ts +0 -138
  78. package/src/interpolate-meta.ts +0 -123
  79. package/src/load-service-prisma-client.ts +0 -53
  80. package/src/loader-errors.ts +0 -41
  81. package/src/read-viewport-from-request.ts +0 -36
  82. package/src/register-page-server-client.test.ts +0 -185
  83. package/src/resolve-app-base-url.ts +0 -35
  84. package/src/resolve-from-loader-config.test.ts +0 -429
  85. package/src/resolve-from-loader-config.ts +0 -316
  86. package/src/resolve-service-context.ts +0 -88
@@ -1,82 +0,0 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The `registry-stale-inactive-sweep` pg-boss handler. Surfaces registry
7
- * rows that have been inactive beyond a threshold with no deliberate-pause
8
- * metadata — the "deactivated and forgotten" class that caused the
9
- * fleet_agent_slots miscount (operator directive 2026-07-20).
10
- *
11
- * This is a MONITOR (detection), not enforcement: it records an event_log
12
- * entry so the operator sees stale rows accumulate, but never blocks or
13
- * auto-reaps. The structural prevention is the `deactivated_at` stamp the
14
- * registry_edit deactivate path writes; this sweep is the detection
15
- * backstop that surfaces rows whose `updated_at` (the deactivation
16
- * timestamp) has aged past the threshold.
17
- *
18
- * Excluded from the sweep (deliberate deactivation — NOT stale):
19
- * - config.pause_owner / config.reactivate_when (progressive-activation paused guards)
20
- * - config.operator_deactivated = true (operator emergency off)
21
- *
22
- * Registered under slug `@teamscala/service-boot:registry-stale-inactive-sweep`.
23
- */
24
- import type { ScheduledJobConfig } from "@teamscala/db-validation/registry-schemas/scheduled-job";
25
- import { getPrisma } from "@teamscala/db/prisma-registry";
26
- import { recordEvent } from "@teamscala/event-log/record-event";
27
- import { getAppLogger } from "@teamscala/logger/app-loggers";
28
- import { pgBossHandlerRegistry } from "../pg-boss-handler-registry.ts";
29
-
30
- const STALE_AFTER_DAYS = 30;
31
- const SAMPLE_LIMIT = 20;
32
-
33
- interface StaleRow {
34
- type: string;
35
- slug: string;
36
- updated_at: Date;
37
- }
38
-
39
- async function registryStaleInactiveSweep(): Promise<void> {
40
- const log = getAppLogger();
41
- const prisma = getPrisma();
42
-
43
- const rows = await prisma.$queryRawUnsafe<StaleRow[]>(`
44
- SELECT type, slug, updated_at
45
- FROM registry_entries
46
- WHERE is_active = false
47
- AND updated_at < NOW() - INTERVAL '${STALE_AFTER_DAYS} days'
48
- AND NOT (config ? 'pause_owner')
49
- AND NOT (config ? 'reactivate_when')
50
- AND NOT (config->>'operator_deactivated' = 'true')
51
- ORDER BY updated_at ASC
52
- `);
53
-
54
- const byType = new Map<string, number>();
55
- for (const r of rows) {
56
- byType.set(r.type, (byType.get(r.type) ?? 0) + 1);
57
- }
58
-
59
- const samples = rows
60
- .slice(0, SAMPLE_LIMIT)
61
- .map((r) => `${r.type}/${r.slug}(${r.updated_at.toISOString().slice(0, 10)})`);
62
-
63
- recordEvent({
64
- kind: "registry.stale-inactive.detected",
65
- payload: {
66
- totalStale: rows.length,
67
- staleAfterDays: STALE_AFTER_DAYS,
68
- byType: Object.fromEntries(byType),
69
- samples,
70
- },
71
- });
72
-
73
- log.info(
74
- `[registry-stale-inactive-sweep] ${rows.length} stale inactive row(s) (>${STALE_AFTER_DAYS}d, no pause metadata) — recorded to event_log`,
75
- { byType: Object.fromEntries(byType), samples },
76
- );
77
- }
78
-
79
- pgBossHandlerRegistry.register(
80
- "@teamscala/service-boot:registry-stale-inactive-sweep",
81
- registryStaleInactiveSweep as (ctx: { config: ScheduledJobConfig }) => Promise<void>,
82
- );
@@ -1,65 +0,0 @@
1
- // @system codegen
2
- // @status generated
3
- // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
- //
5
- // This suite's assertions are OWNED by the codegen band: the band module
6
- // carries them verbatim, this file is the emission, and hand edits here are
7
- // overwritten on the next run. The rationale each assertion carries moved
8
- // with it into the band.
9
-
10
- import { describe, expect, test } from "bun:test";
11
- import { join } from "node:path";
12
-
13
- /**
14
- * Regression lock: this watch must never restart scala-sentinel again.
15
- *
16
- * It used to reinstall @teamscala/scala-tools and then `pm2 restart scala-sentinel`
17
- * "so the fresh scala-tools loads". That premise is false for this package —
18
- * sentinel SPAWNS `node_modules/.bin/scala-tools` per invocation rather than
19
- * importing it, so the reinstall alone is what takes effect. The restart's only
20
- * real consequence was taking enforcement offline: every guard-daemon shutdown
21
- * logs "152 guard(s) failed to run (fail-open)", so each publish of scala-tools
22
- * opened a window where tool-check, turn-end and pre-push silently did not run.
23
- *
24
- * The assertion is on the SOURCE rather than on a mocked run, and that is
25
- * deliberate rather than a shortcut: the property being locked is the ABSENCE of
26
- * a side effect. A behavioural test can only prove the restart did not fire for
27
- * the paths it happens to drive, whereas the absence of any pm2-restart call in
28
- * the module proves it cannot fire on any path. It also stays honest if someone
29
- * reintroduces the restart behind a new condition.
30
- */
31
- const SOURCE = await Bun.file(join(import.meta.dir, "scala-tools-currency-watch.ts")).text();
32
-
33
- /** Comment prose legitimately discusses the removed restart; code must not do it. */
34
- const CODE = SOURCE.split("\n")
35
- .filter((l) => !l.trimStart().startsWith("//") && !l.trimStart().startsWith("*"))
36
- .join("\n");
37
-
38
- describe("scala-tools-currency-watch never restarts the guard daemon", () => {
39
- test("issues no pm2 restart command", () => {
40
- expect(CODE).not.toContain('"restart"');
41
- });
42
-
43
- test("never names scala-sentinel as a spawn argument", () => {
44
- // The reinstall targets the sentinel DIRECTORY (a path constant); what must
45
- // not exist is scala-sentinel as a process argument to a supervisor call.
46
- expect(CODE).not.toContain('"scala-sentinel"');
47
- });
48
-
49
- test("uses no bare pm2 invocation", () => {
50
- // forbid-bare-pm2-restart: a bare `pm2` call bypasses the currency-gated
51
- // canonical pipeline, and the guard blocks agents from making this exact
52
- // call — the handler was doing what agents are prevented from doing.
53
- expect(CODE).not.toContain('"pm2"');
54
- });
55
-
56
- test("still performs the reinstall, which is the watch's actual job", () => {
57
- // Removing the restart must not turn this into a no-op watch.
58
- expect(CODE).toContain("deps");
59
- expect(CODE).toContain("install");
60
- });
61
-
62
- test("still records the reinstall event for observability", () => {
63
- expect(CODE).toContain("scala-tools.currency.reinstalled");
64
- });
65
- });
@@ -1,162 +0,0 @@
1
- /**
2
- * @system server-boot
3
- * @status handwritten
4
- * @edit edit directly
5
- *
6
- * The `scala-tools-currency-watch` pg-boss handler — keeps scala-sentinel's
7
- * INSTALLED @teamscala/scala-tools at registry `latest` (open migration
8
- * `daemon-scala-tools-currency`). scala-sentinel resolves scala-tools via its
9
- * own node_modules (the installed copy), which goes STALE on every
10
- * @teamscala/scala-tools publish until a manual deps install + restart — so the
11
- * daemon's publish-chain + codegen trio run OLD scala-tools while workspace
12
- * source is current (incident #58). This watch polls the registry dist-tag and,
13
- * when scala-sentinel's installed copy lags, REINSTALLS it. It does not restart
14
- * anything: sentinel spawns `node_modules/.bin/scala-tools` per invocation
15
- * rather than importing it, so the next spawn picks up the new binary. See the
16
- * no-restart note at the call site for the evidence and for why the restart that
17
- * used to live there was disabling enforcement on every publish.
18
- *
19
- * Boot-safety (protective-layers-fail-explicitly): every step is wrapped — a
20
- * dist-tag fetch failure, a version-parse failure, OR an install failure NEVER
21
- * crashes the job or loga; each records a failure event + returns. Repeated
22
- * reinstalls are prevented by strict semver-lt (re-installs only when installed
23
- * is genuinely behind latest) + the idempotent --force-resolve install.
24
- * Subprocesses go through @teamscala/os spawn (the single spawn surface).
25
- *
26
- * Registered under slug `@teamscala/service-boot:scala-tools-currency-watch`.
27
- */
28
- // NO RESTART. The reinstall alone is sufficient, and restarting here was actively harmful.
29
- //
30
- // The premise this watch was written on — "restart so the fresh scala-tools loads" — does not hold for THIS package. scala-sentinel never
31
- // imports `@teamscala/scala-tools`; it SPAWNS `node_modules/.bin/scala-tools` per invocation, so a freshly installed binary is picked up by the next
32
- // spawn with no process restart at all. Verified 2026-07-27: the package is declared in scala-sentinel/package.json, yet scala-guard/src contains zero
33
- // import statements resolving to it — it is spawned, never linked. dep-sync already encodes the same conclusion — HARNESS_CRITICAL_FAST_PATH
34
- // maps this exact pair to install-WITHOUT-restart, while its PM2_CACHED sibling map exists for packages a daemon really does load at boot
35
- // (@teamscala/codegen → codegen-daemon). This handler was the odd one out.
36
- //
37
- // The cost was not theoretical. Restarting the guard daemon takes enforcement offline for its warmup, and every shutdown logs "152 guard(s)
38
- // failed to run (fail-open)" — so each unnecessary restart opened a window in which tool-check, turn-end and pre-push silently did not run. Three
39
- // scala-tools versions published on 2026-07-26 alone, and this watch polls, so it re-fired for each. That inverts
40
- // `infrastructure-outranks-application-durability`: a currency convenience was repeatedly disabling the thing it exists to keep current.
41
- //
42
- // It also used a bare `pm2 restart`, which is forbidden (`forbid-bare-pm2-restart`) precisely because it bypasses the
43
- // currency-gated canonical pipeline — the guard blocks agents from that exact call.
44
- //
45
- // If a future package genuinely needs a restart to take effect, it does not belong here: add it to dep-sync's PM2_CACHED map, which already owns
46
- // install-plus-restart and coalesces them.
47
-
48
- import type { ScheduledJobConfig } from "@teamscala/db-validation/registry-schemas/scheduled-job";
49
- import { recordEvent } from "@teamscala/event-log/record-event";
50
- import { getAppLogger } from "@teamscala/logger/app-loggers";
51
- import { workspaceRoot } from "@teamscala/os/host-paths";
52
- import { spawn } from "@teamscala/os/spawn";
53
- import { pgBossHandlerRegistry } from "../pg-boss-handler-registry.ts";
54
-
55
- const PKG = "@teamscala/scala-tools";
56
- const SENTINEL_DIR = `${workspaceRoot()}/scala-sentinel`;
57
- const INSTALLED_PKG_JSON = `${SENTINEL_DIR}/node_modules/@teamscala/scala-tools/package.json`;
58
-
59
- interface PkgJson {
60
- version: string;
61
- }
62
-
63
- function parseSemver(v: string): [number, number, number] | null {
64
- const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v.trim());
65
- return m ? [Number(m[1]!), Number(m[2]!), Number(m[3]!)] : null;
66
- }
67
-
68
- /** Strict a < b on major.minor.patch (pre-releases ignored — latest is a stable dist-tag). */
69
- function semverLt(a: string, b: string): boolean {
70
- const pa = parseSemver(a);
71
- const pb = parseSemver(b);
72
- if (!pa || !pb) return false;
73
- for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return pa[i]! < pb[i]!;
74
- return false;
75
- }
76
-
77
- async function scalaToolsCurrencyWatch(_ctx: { config: ScheduledJobConfig }): Promise<void> {
78
- const log = getAppLogger();
79
- try {
80
- const installedPkg = await Bun.file(INSTALLED_PKG_JSON).json() as PkgJson;
81
- const installed = installedPkg.version;
82
-
83
- const distTag = await spawn({
84
- name: "scala-tools-currency-watch:dist-tag",
85
- command: ["scala-tools", "packages", "dist-tag", "get", PKG, "latest"],
86
- timeoutMs: 30_000,
87
- });
88
- // dist-tag get prints the version (last non-empty line).
89
- const latest =
90
- (distTag.stdout ?? "")
91
- .split("\n")
92
- .map((l) => l.trim())
93
- .filter(Boolean)
94
- .pop() ?? "";
95
- if (!latest || !parseSemver(latest)) {
96
- log.warn(
97
- `[scala-tools-currency-watch] no readable latest (exit=${distTag.exitCode}, stdout=${(distTag.stdout ?? "").slice(0, 80)}) — skip`,
98
- );
99
- recordEvent({
100
- kind: "scala-tools.currency.tick",
101
- payload: { installed, latest: null, note: "dist-tag fetch unreadable" },
102
- });
103
- return;
104
- }
105
-
106
- if (!semverLt(installed, latest)) {
107
- recordEvent({
108
- kind: "scala-tools.currency.tick",
109
- payload: { installed, latest, status: "current" },
110
- });
111
- return;
112
- }
113
-
114
- // STALE — reinstall + restart.
115
- log.warn(
116
- `[scala-tools-currency-watch] scala-sentinel ${PKG} ${installed} < latest ${latest} — reinstalling + restarting`,
117
- );
118
- const install = await spawn({
119
- name: "scala-tools-currency-watch:deps-install",
120
- // A plain install fetches the current version, which is the whole point
121
- // of this watch. Re-resolving the entire tree is an emergency hatch, not
122
- // a currency step (operator ruling 2026-07-31).
123
- command: ["scala-tools", "deps", "install"],
124
- cwd: SENTINEL_DIR,
125
- timeoutMs: 300_000,
126
- });
127
- if ((install.exitCode ?? -1) !== 0) {
128
- log.error(
129
- `[scala-tools-currency-watch] deps install failed (exit ${install.exitCode}) — NOT restarting to avoid boot-looping the enforcement daemon: ${(install.stderr ?? "").slice(0, 200)}`,
130
- );
131
- recordEvent({
132
- kind: "scala-tools.currency.install-failed",
133
- payload: {
134
- installed,
135
- latest,
136
- exitCode: install.exitCode,
137
- stderr: (install.stderr ?? "").slice(0, 300),
138
- },
139
- });
140
- return;
141
- }
142
- recordEvent({
143
- kind: "scala-tools.currency.reinstalled",
144
- payload: { from: installed, to: latest },
145
- });
146
-
147
- log.info(
148
- `[scala-tools-currency-watch] reinstalled ${PKG} ${installed} → ${latest} (no restart: sentinel spawns the binary, it does not import it)`,
149
- );
150
- } catch (err) {
151
- // Boot-safety: never propagate — record + return (a watch error must not crash loga).
152
- log.error(
153
- `[scala-tools-currency-watch] caught — fail-open (no restart): ${err instanceof Error ? err.message : String(err)}`,
154
- );
155
- recordEvent({
156
- kind: "scala-tools.currency.watch-error",
157
- payload: { error: err instanceof Error ? err.message : String(err) },
158
- });
159
- }
160
- }
161
-
162
- pgBossHandlerRegistry.register("@teamscala/service-boot:scala-tools-currency-watch", scalaToolsCurrencyWatch);
@@ -1,51 +0,0 @@
1
- // @system codegen
2
- // @status generated
3
- // @edit change the suite in the owned-suites band, then re-run codegen. Hand-edits are overwritten.
4
- //
5
- // This suite's assertions are OWNED by the codegen band: the band module
6
- // carries them verbatim, this file is the emission, and hand edits here are
7
- // overwritten on the next run. The rationale each assertion carries moved
8
- // with it into the band.
9
-
10
- import { describe, expect, test } from "bun:test";
11
- import { join } from "node:path";
12
-
13
- const SOURCE = await Bun.file(join(import.meta.dir, "singleton-tools-currency-watch.ts")).text();
14
-
15
- const CODE = SOURCE.split("\n")
16
- .filter((l) => !l.trimStart().startsWith("//") && !l.trimStart().startsWith("*"))
17
- .join("\n");
18
-
19
- describe("singleton-tools-currency-watch", () => {
20
- test("reads version_source from config/scala-singleton-tools", () => {
21
- expect(CODE).toContain("scala-singleton-tools");
22
- });
23
-
24
- test("reinstalls stale tools", () => {
25
- expect(CODE).toContain("reinstall");
26
- });
27
-
28
- test("skips bare tools (bun) with a deferred event", () => {
29
- expect(CODE).toContain("deferred-to-bun-stable-watch");
30
- });
31
-
32
- test("records tick events for observability", () => {
33
- expect(CODE).toContain("singleton-tools.currency.tick");
34
- });
35
-
36
- test("records reinstall events", () => {
37
- expect(CODE).toContain("singleton-tools.currency.reinstalled");
38
- });
39
-
40
- test("records install-failed events", () => {
41
- expect(CODE).toContain("singleton-tools.currency.install-failed");
42
- });
43
-
44
- test("uses semver comparison (not string comparison)", () => {
45
- expect(CODE).toContain("semverLt");
46
- });
47
-
48
- test("handles unreadable installed version gracefully", () => {
49
- expect(CODE).toContain("unreadable");
50
- });
51
- });