@fuzdev/fuz_app 0.83.0 → 0.84.0

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 (38) hide show
  1. package/dist/auth/migrations.d.ts +21 -18
  2. package/dist/auth/migrations.d.ts.map +1 -1
  3. package/dist/auth/migrations.js +22 -19
  4. package/dist/db/CLAUDE.md +6 -0
  5. package/dist/db/schema_ready.d.ts +83 -0
  6. package/dist/db/schema_ready.d.ts.map +1 -0
  7. package/dist/db/schema_ready.js +103 -0
  8. package/dist/http/CLAUDE.md +3 -2
  9. package/dist/http/common_routes.d.ts +49 -0
  10. package/dist/http/common_routes.d.ts.map +1 -1
  11. package/dist/http/common_routes.js +92 -0
  12. package/dist/testing/CLAUDE.md +35 -6
  13. package/dist/testing/cross_backend/backend_config.d.ts +2 -2
  14. package/dist/testing/cross_backend/capabilities.d.ts +10 -0
  15. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  16. package/dist/testing/cross_backend/capabilities.js +1 -0
  17. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  18. package/dist/testing/cross_backend/default_backend_configs.js +6 -0
  19. package/dist/testing/cross_backend/default_spine_surface.d.ts +48 -0
  20. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  21. package/dist/testing/cross_backend/default_spine_surface.js +24 -24
  22. package/dist/testing/cross_backend/expected_schema.json +113 -0
  23. package/dist/testing/cross_backend/ready.d.ts +14 -0
  24. package/dist/testing/cross_backend/ready.d.ts.map +1 -0
  25. package/dist/testing/cross_backend/ready.js +50 -0
  26. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +39 -0
  27. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -0
  28. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +103 -0
  29. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +1 -1
  30. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  31. package/dist/testing/cross_backend/ts_spine_backend_config.js +6 -2
  32. package/dist/testing/schema_ready_fixture.d.ts +46 -0
  33. package/dist/testing/schema_ready_fixture.d.ts.map +1 -0
  34. package/dist/testing/schema_ready_fixture.js +48 -0
  35. package/package.json +7 -3
  36. package/dist/testing/cross_backend/spine_stub_backend_config.d.ts +0 -66
  37. package/dist/testing/cross_backend/spine_stub_backend_config.d.ts.map +0 -1
  38. package/dist/testing/cross_backend/spine_stub_backend_config.js +0 -53
@@ -76,6 +76,16 @@ export interface BackendCapabilities {
76
76
  * the serve routes stay off the standard declared surface.
77
77
  */
78
78
  readonly fact_serving: boolean;
79
+ /**
80
+ * The `/ready` readiness deploy gate (`GET /ready`) is live-mounted on the
81
+ * backend — the public column-presence schema-drift probe over the committed
82
+ * `expected_schema.json`. Gates `describe_ready_cross_tests` (anonymous
83
+ * `GET /ready` → `200 {ready: true}` on a clean spine bootstrap). Like
84
+ * ws/sse/cells, the route stays off the standard declared surface
85
+ * (`create_spine_surface_spec`); this flag opts a backend into the dedicated
86
+ * readiness parity coverage. The drift → `503` path stays per-impl unit tests.
87
+ */
88
+ readonly ready: boolean;
79
89
  }
80
90
  /**
81
91
  * Capability declarations for the in-process Hono transport. Every flag
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmB9B;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC;;;;;;;;OAQG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAUpC,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,EAAE,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAMrF,CAAC"}
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmB9B;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC;;;;;;;;OAQG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAWpC,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,EAAE,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAMrF,CAAC"}
@@ -30,6 +30,7 @@ export const in_process_capabilities = Object.freeze({
30
30
  cell_relations: true,
31
31
  account_lifecycle: true,
32
32
  fact_serving: true,
33
+ ready: true,
33
34
  });
34
35
  /**
35
36
  * Conditional `test()` wrapper — registers a vitest case only when
@@ -1 +1 @@
1
- {"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAUpC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAUtC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAoCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA4CF,CAAC"}
1
+ {"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAapC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAatC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAoCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA4CF,CAAC"}
@@ -17,6 +17,9 @@ export const ts_default_capabilities = Object.freeze({
17
17
  cell_relations: true,
18
18
  account_lifecycle: true,
19
19
  fact_serving: true,
20
+ // Off by default like `sse` — a generic TS consumer backend may not mount
21
+ // `/ready`. fuz_app's own spine configs (`ts_spine_*`) opt in.
22
+ ready: false,
20
23
  });
21
24
  /**
22
25
  * Capabilities for the Rust family. Adds `trusted_proxy: true` (the
@@ -34,6 +37,9 @@ export const rust_default_capabilities = Object.freeze({
34
37
  cell_relations: true,
35
38
  account_lifecycle: true,
36
39
  fact_serving: true,
40
+ // Off by default like `sse`; the spine-stub preset opts in (it mounts
41
+ // `/ready` over the env-supplied fixture path).
42
+ ready: false,
37
43
  });
38
44
  /** Bootstrap block built from the default secrets + supplied paths. */
39
45
  const build_default_bootstrap = (paths, overrides) => ({
@@ -1,4 +1,29 @@
1
1
  import '../assert_dev_env.js';
2
+ /**
3
+ * Canonical no-domain spine surface — the standard fuz_app
4
+ * auth/account/admin/audit surface with no consumer domain layer on top.
5
+ *
6
+ * This is the single source of truth for "the standard spine surface",
7
+ * shared by:
8
+ *
9
+ * - the Rust `testing_spine_stub` cross-process self-tests (which build the
10
+ * `AppSurfaceSpec` via `create_spine_surface_spec` and drive the binary's
11
+ * wire shape),
12
+ * - the TS `testing_spine_server` cross-process binary (which feeds
13
+ * `create_spine_route_specs` + `spine_rpc_endpoints` into a live
14
+ * `create_app_server`), and
15
+ * - the `cross_backend_ts_*` self-test projects.
16
+ *
17
+ * **`$lib`-free by contract.** This module and everything it imports use
18
+ * relative specifiers (no `$lib` SvelteKit alias) so the spawned TS test
19
+ * binary — run under Gro's loader, which resolves `.js`→`.ts` and package
20
+ * imports but **not** the `$lib` alias — can import it transitively. Keep
21
+ * it that way: a `$lib` import anywhere in this graph breaks the binary
22
+ * spawn while still typechecking under vitest.
23
+ *
24
+ * @module
25
+ */
26
+ import type { Logger } from '@fuzdev/fuz_util/log.js';
2
27
  import type { NotificationSender } from '../../auth/role_grant_offer_notifications.js';
3
28
  import { type RoleSchemaResult } from '../../auth/role_schema.js';
4
29
  import { type SessionOptions } from '../../auth/session_cookie.js';
@@ -84,6 +109,29 @@ export declare const spine_rpc_endpoints: (ctx: AppServerContext, options?: Spin
84
109
  * the stream at `SPINE_SSE_PATH`.
85
110
  */
86
111
  export declare const create_spine_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
112
+ /**
113
+ * Committed expected-schema fixture for the spine `/ready` deploy gate — the
114
+ * column map a fresh full spine bootstrap (auth + cell + cell_history + fact)
115
+ * produces. Resolved relative to this module so the spawned TS binary (which
116
+ * imports this source under its loader) reads it off disk via `node:fs`.
117
+ * Regenerated + guarded by `src/test/cross_backend/spine_expected_schema.db.test.ts`.
118
+ *
119
+ * The Rust `testing_spine_stub` reads the **same** committed file (its absolute
120
+ * path passed via env by `rust_spine_stub_backend_config`) — column-presence is
121
+ * engine-portable, so one fixture is the cross-impl contract.
122
+ */
123
+ export declare const SPINE_EXPECTED_SCHEMA_URL: URL;
124
+ /**
125
+ * The spine's `/ready` route spec — the column-presence schema-drift deploy
126
+ * gate, reading {@link SPINE_EXPECTED_SCHEMA_URL}. Mounted **live** by the TS
127
+ * spine binary (in `build_spine_app`) and the in-process readiness parity leg,
128
+ * but kept **off** the declared surface (`create_spine_surface_spec`) like the
129
+ * fact-serving / ws / sse behaviors — `describe_ready_cross_tests` (gated on
130
+ * `capabilities.ready`) is its explicit coverage, not the generic round-trip.
131
+ *
132
+ * @param log - optional logger for server-side drift diagnostics
133
+ */
134
+ export declare const create_spine_ready_route_spec: (log?: Logger) => RouteSpec;
87
135
  /**
88
136
  * The `AppSurfaceSpec` for the standard spine surface — the wire-shape
89
137
  * source the cross-process round-trip + RPC-round-trip suites validate
@@ -1 +1 @@
1
- {"version":3,"file":"default_spine_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_spine_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6B9B,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAwB,KAAK,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAGxF,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAC,cAAc,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AAGzE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAElG;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,gBAExB,CAAC;AAEH,iEAAiE;AACjE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,4BAA4B,CAAC;AAExD,+CAA+C;AAC/C,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAC/B,KAAK,gBAAgB,EACrB,UAAU,wBAAwB,KAChC,KAAK,CAAC,eAAe,CAQvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,KAAK,gBAAgB,KAAG,KAAK,CAAC,SAAS,CAiB/E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,QAAO,cAM1C,CAAC"}
1
+ {"version":3,"file":"default_spine_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_spine_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAIpD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,8CAA8C,CAAC;AACrF,OAAO,EAAqB,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAwB,KAAK,cAAc,EAAC,MAAM,8BAA8B,CAAC;AAIxF,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAC,cAAc,EAAE,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AAGzE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,MAAM,CAAwC,CAAC;AAElG;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,gBAExB,CAAC;AAEH,iEAAiE;AACjE,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,4BAA4B,CAAC;AAExD,+CAA+C;AAC/C,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAC/B,KAAK,gBAAgB,EACrB,UAAU,wBAAwB,KAChC,KAAK,CAAC,eAAe,CAQvB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,KAAK,gBAAgB,KAAG,KAAK,CAAC,SAAS,CAiB/E,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,EAAE,GAAwD,CAAC;AAEjG;;;;;;;;;GASG;AACH,eAAO,MAAM,6BAA6B,GAAI,MAAM,MAAM,KAAG,SAC6B,CAAC;AAE3F;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,QAAO,cAM1C,CAAC"}
@@ -1,34 +1,11 @@
1
1
  import '../assert_dev_env.js';
2
- /**
3
- * Canonical no-domain spine surface — the standard fuz_app
4
- * auth/account/admin/audit surface with no consumer domain layer on top.
5
- *
6
- * This is the single source of truth for "the standard spine surface",
7
- * shared by:
8
- *
9
- * - the Rust `testing_spine_stub` cross-process self-tests (which build the
10
- * `AppSurfaceSpec` via `create_spine_surface_spec` and drive the binary's
11
- * wire shape),
12
- * - the TS `testing_spine_server` cross-process binary (which feeds
13
- * `create_spine_route_specs` + `spine_rpc_endpoints` into a live
14
- * `create_app_server`), and
15
- * - the `cross_backend_ts_*` self-test projects.
16
- *
17
- * **`$lib`-free by contract.** This module and everything it imports use
18
- * relative specifiers (no `$lib` SvelteKit alias) so the spawned TS test
19
- * binary — run under Gro's loader, which resolves `.js`→`.ts` and package
20
- * imports but **not** the `$lib` alias — can import it transitively. Keep
21
- * it that way: a `$lib` import anywhere in this graph breaks the binary
22
- * spawn while still typechecking under vitest.
23
- *
24
- * @module
25
- */
26
2
  import { create_account_route_specs } from '../../auth/account_routes.js';
27
3
  import { create_audit_log_route_specs } from '../../auth/audit_log_routes.js';
28
4
  import { create_role_schema } from '../../auth/role_schema.js';
29
5
  import { create_session_config } from '../../auth/session_cookie.js';
30
6
  import { create_signup_route_specs } from '../../auth/signup_routes.js';
31
7
  import { create_standard_rpc_actions } from '../../auth/standard_rpc_actions.js';
8
+ import { create_ready_route_spec, load_expected_schema } from '../../http/common_routes.js';
32
9
  import { prefix_route_specs } from '../../http/route_spec.js';
33
10
  import { create_test_app_surface_spec } from '../stubs.js';
34
11
  /**
@@ -114,6 +91,29 @@ export const create_spine_route_specs = (ctx) => [
114
91
  ? prefix_route_specs('/api/admin', create_audit_log_route_specs({ stream: ctx.audit_sse }))
115
92
  : []),
116
93
  ];
94
+ /**
95
+ * Committed expected-schema fixture for the spine `/ready` deploy gate — the
96
+ * column map a fresh full spine bootstrap (auth + cell + cell_history + fact)
97
+ * produces. Resolved relative to this module so the spawned TS binary (which
98
+ * imports this source under its loader) reads it off disk via `node:fs`.
99
+ * Regenerated + guarded by `src/test/cross_backend/spine_expected_schema.db.test.ts`.
100
+ *
101
+ * The Rust `testing_spine_stub` reads the **same** committed file (its absolute
102
+ * path passed via env by `rust_spine_stub_backend_config`) — column-presence is
103
+ * engine-portable, so one fixture is the cross-impl contract.
104
+ */
105
+ export const SPINE_EXPECTED_SCHEMA_URL = new URL('./expected_schema.json', import.meta.url);
106
+ /**
107
+ * The spine's `/ready` route spec — the column-presence schema-drift deploy
108
+ * gate, reading {@link SPINE_EXPECTED_SCHEMA_URL}. Mounted **live** by the TS
109
+ * spine binary (in `build_spine_app`) and the in-process readiness parity leg,
110
+ * but kept **off** the declared surface (`create_spine_surface_spec`) like the
111
+ * fact-serving / ws / sse behaviors — `describe_ready_cross_tests` (gated on
112
+ * `capabilities.ready`) is its explicit coverage, not the generic round-trip.
113
+ *
114
+ * @param log - optional logger for server-side drift diagnostics
115
+ */
116
+ export const create_spine_ready_route_spec = (log) => create_ready_route_spec({ expected: load_expected_schema(SPINE_EXPECTED_SCHEMA_URL), log });
117
117
  /**
118
118
  * The `AppSurfaceSpec` for the standard spine surface — the wire-shape
119
119
  * source the cross-process round-trip + RPC-round-trip suites validate
@@ -0,0 +1,113 @@
1
+ {
2
+ "account": [
3
+ "created_at",
4
+ "created_by",
5
+ "deleted_at",
6
+ "deleted_by",
7
+ "email",
8
+ "email_verified",
9
+ "id",
10
+ "password_hash",
11
+ "updated_at",
12
+ "updated_by",
13
+ "username"
14
+ ],
15
+ "actor": [
16
+ "account_id",
17
+ "created_at",
18
+ "deleted_at",
19
+ "deleted_by",
20
+ "id",
21
+ "name",
22
+ "updated_at",
23
+ "updated_by"
24
+ ],
25
+ "api_token": [
26
+ "account_id",
27
+ "created_at",
28
+ "expires_at",
29
+ "id",
30
+ "last_used_at",
31
+ "last_used_ip",
32
+ "name",
33
+ "token_hash"
34
+ ],
35
+ "app_settings": ["id", "open_signup", "updated_at", "updated_by"],
36
+ "audit_log": [
37
+ "account_id",
38
+ "actor_id",
39
+ "created_at",
40
+ "event_type",
41
+ "id",
42
+ "ip",
43
+ "metadata",
44
+ "outcome",
45
+ "seq",
46
+ "target_account_id",
47
+ "target_actor_id"
48
+ ],
49
+ "auth_session": ["account_id", "created_at", "expires_at", "id", "last_seen_at"],
50
+ "bootstrap_lock": ["bootstrapped", "id"],
51
+ "cell": [
52
+ "created_at",
53
+ "created_by",
54
+ "data",
55
+ "deleted_at",
56
+ "id",
57
+ "path",
58
+ "refs",
59
+ "updated_at",
60
+ "updated_by",
61
+ "visibility"
62
+ ],
63
+ "cell_field": ["created_at", "name", "source_id", "target_id"],
64
+ "cell_grant": [
65
+ "actor_id",
66
+ "cell_id",
67
+ "created_at",
68
+ "granted_by",
69
+ "id",
70
+ "level",
71
+ "role",
72
+ "scope_id"
73
+ ],
74
+ "cell_history": ["action_id", "cell_id", "created_at", "fact_hash", "id"],
75
+ "cell_item": ["child_id", "created_at", "parent_id", "position"],
76
+ "fact": ["bytes", "content_type", "created_at", "external_url", "hash", "size"],
77
+ "fact_ref": ["source_hash", "target_hash"],
78
+ "invite": ["claimed_at", "claimed_by", "created_at", "created_by", "email", "id", "username"],
79
+ "memo": ["created_at", "fn_id", "input_hash", "output_hash"],
80
+ "role_grant": [
81
+ "actor_id",
82
+ "created_at",
83
+ "expires_at",
84
+ "granted_by",
85
+ "id",
86
+ "revoked_at",
87
+ "revoked_by",
88
+ "revoked_reason",
89
+ "role",
90
+ "scope_id",
91
+ "scope_kind",
92
+ "source_offer_id"
93
+ ],
94
+ "role_grant_offer": [
95
+ "accepted_at",
96
+ "created_at",
97
+ "decline_reason",
98
+ "declined_at",
99
+ "expires_at",
100
+ "from_actor_id",
101
+ "id",
102
+ "message",
103
+ "resulting_role_grant_id",
104
+ "retracted_at",
105
+ "role",
106
+ "scope_id",
107
+ "scope_kind",
108
+ "superseded_at",
109
+ "to_account_id",
110
+ "to_actor_id"
111
+ ],
112
+ "schema_version": ["applied_at", "name", "namespace", "sequence"]
113
+ }
@@ -0,0 +1,14 @@
1
+ import '../assert_dev_env.js';
2
+ import { type BackendCapabilities } from './capabilities.js';
3
+ import type { SetupTest } from './setup.js';
4
+ /** Options for the readiness-probe parity suite. */
5
+ export interface ReadyCrossTestOptions {
6
+ /** Per-test fixture-producing function (fresh keeper + db per call). */
7
+ readonly setup_test: SetupTest;
8
+ /** Backend capability declarations — the suite gates on `capabilities.ready`. */
9
+ readonly capabilities: BackendCapabilities;
10
+ /** Readiness probe path. Default `/ready`. */
11
+ readonly ready_path?: string;
12
+ }
13
+ export declare const describe_ready_cross_tests: (options: ReadyCrossTestOptions) => void;
14
+ //# sourceMappingURL=ready.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ready.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ready.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiC9B,OAAO,EAAU,KAAK,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAE1C,oDAAoD;AACpD,MAAM,WAAW,qBAAqB;IACrC,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,0BAA0B,GAAI,SAAS,qBAAqB,KAAG,IAuB3E,CAAC"}
@@ -0,0 +1,50 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Cross-backend parity suite for the `/ready` schema-drift deploy gate.
4
+ *
5
+ * The `/ready` mechanism already ships on both twins (TS `create_ready_route_spec`
6
+ * / `db/schema_ready.ts`; Rust `fuz_http::ready` / `fuz_db::schema_ready`), each
7
+ * with its own drift → `503` unit tests. This suite is the missing automated
8
+ * cross-impl gate: an anonymous `GET /ready` returns `200 {ready: true}` over
9
+ * real HTTP on **both** spine test servers, proving the success path is wire-
10
+ * identical and that both backends read the same committed `expected_schema.json`
11
+ * (column-presence is engine-portable, so one fixture is the cross-impl contract).
12
+ *
13
+ * `/ready` is a plain public REST route — not an RPC method, not one of the six
14
+ * REST auth routes — and it's deliberately **off** the declared spine surface
15
+ * (`create_spine_surface_spec`), like ws/sse/cells/fact-serving. So it needs a
16
+ * bespoke imperative suite (à la `origin.cross.test.ts`), gated on
17
+ * `capabilities.ready`, rather than a `conformance_table` row or generic
18
+ * round-trip enumeration. The drift → `503` path stays per-impl unit tests.
19
+ *
20
+ * Runs both legs via the shared `{setup_test, capabilities}` protocol: the
21
+ * in-process leg (`cross_backend/ready_parity.db.test.ts`, plain `gro test`) and
22
+ * the cross-process leg (`cross_backend/ready.cross.test.ts`, the TS spine
23
+ * binaries + Rust `testing_spine_stub` over real HTTP).
24
+ *
25
+ * `$lib`-free by contract (relative specifiers only), like the sibling
26
+ * cross-backend suites.
27
+ *
28
+ * @module
29
+ */
30
+ import { describe, assert } from 'vitest';
31
+ import { test_if } from './capabilities.js';
32
+ export const describe_ready_cross_tests = (options) => {
33
+ const { setup_test, capabilities } = options;
34
+ const ready_path = options.ready_path ?? '/ready';
35
+ describe('readiness probe parity', () => {
36
+ test_if(capabilities.ready, 'anonymous GET /ready → 200 {ready: true} on a clean spine bootstrap', async () => {
37
+ const fixture = await setup_test();
38
+ // Anonymous deploy-poll shape: cookie-jar-free, no Origin, no auth.
39
+ // `/ready` is public and outside `/api`, so neither the session /
40
+ // bearer middleware nor the RPC dispatcher sits in the path — exactly
41
+ // how a deploy gate (zap) polls it post-deploy. A freshly bootstrapped
42
+ // spine covers the committed expected column map, so the drift check
43
+ // passes.
44
+ const res = await fixture.fresh_transport({ origin: null })(ready_path, { method: 'GET' });
45
+ assert.strictEqual(res.status, 200, 'a clean spine bootstrap must report ready');
46
+ const body = (await res.json().catch(() => undefined));
47
+ assert.strictEqual(body?.ready, true);
48
+ });
49
+ });
50
+ };
@@ -0,0 +1,39 @@
1
+ import '../assert_dev_env.js';
2
+ import type { BackendConfig } from './backend_config.js';
3
+ /** Env var naming the prebuilt `testing_spine_stub` binary. Required when `binary_path` is omitted. */
4
+ export declare const RUST_SPINE_STUB_BIN_ENV = "FUZ_TESTING_RUST_SPINE_STUB_BIN";
5
+ /**
6
+ * Env var the stub reads for the absolute path of the committed
7
+ * `expected_schema.json` its `/ready` gate introspects against. Pointed at the
8
+ * **same** fixture the TS spine reads ({@link SPINE_EXPECTED_SCHEMA_URL}) —
9
+ * column-presence is engine-portable, so one file is the cross-impl contract.
10
+ */
11
+ export declare const RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV = "FUZ_RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH";
12
+ /** Default listening port — slots beside zzz's 1175/1176; matches the binary's `DEFAULT_PORT`. */
13
+ export declare const RUST_SPINE_STUB_DEFAULT_PORT = 1177;
14
+ /** Default Postgres database — real PG (PGlite isn't reachable from `tokio-postgres`). */
15
+ export declare const RUST_SPINE_STUB_DEFAULT_DATABASE_URL = "postgres://localhost/fuz_app_test_rust_spine_stub";
16
+ export interface SpineStubBackendConfigOptions {
17
+ /** Listening port. Default `RUST_SPINE_STUB_DEFAULT_PORT`. */
18
+ readonly port?: number;
19
+ /** Postgres connection URL. Default `RUST_SPINE_STUB_DEFAULT_DATABASE_URL`. */
20
+ readonly database_url?: string;
21
+ /**
22
+ * Prebuilt binary path. Overrides the `FUZ_TESTING_RUST_SPINE_STUB_BIN` env
23
+ * var. When neither is set the preset throws.
24
+ */
25
+ readonly binary_path?: string;
26
+ }
27
+ /**
28
+ * Build the `BackendConfig` for `testing_spine_stub`. Resolves the binary
29
+ * from `options.binary_path` or `FUZ_TESTING_RUST_SPINE_STUB_BIN`; throws when
30
+ * neither is set so a missing build surfaces as a clear error rather than
31
+ * a confusing spawn failure. Reconciles the binary's env contract: port
32
+ * via `--port` (and `FUZ_RUST_SPINE_STUB_PORT`), daemon-token dir via
33
+ * `FUZ_RUST_SPINE_STUB_DIR` (anchored to `paths.root` so the written
34
+ * `{dir}/run/daemon_token` matches the path `spawn_backend` reads).
35
+ *
36
+ * @throws Error when no binary path is available.
37
+ */
38
+ export declare const rust_spine_stub_backend_config: (options?: SpineStubBackendConfigOptions) => BackendConfig;
39
+ //# sourceMappingURL=rust_spine_stub_backend_config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuC9B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAQvD,uGAAuG;AACvG,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,6CAA6C,CAAC;AAEnG,kGAAkG;AAClG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,sDACG,CAAC;AAErD,MAAM,WAAW,6BAA6B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aA6CF,CAAC"}
@@ -0,0 +1,103 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Cross-process `BackendConfig` preset for the non-domain spine consumer,
4
+ * `testing_spine_stub` — a Rust binary that mounts only the spine surface
5
+ * (auth / account / admin / audit / role-grant offers) with no domain
6
+ * layer. fuz_app drives it from `src/test/cross_backend/*.cross.test.ts`
7
+ * to verify its TS spec against the Rust spine end-to-end with no domain
8
+ * implementation in the loop — drift becomes a fuz_app failure rather than
9
+ * a downstream consumer's failure with mixed signals.
10
+ *
11
+ * **Binary discovery — env-supplied, never hardcoded.** The binary lives
12
+ * in a sibling Rust workspace, not in fuz_app, so the preset never bakes a
13
+ * path in. `FUZ_TESTING_RUST_SPINE_STUB_BIN` (or the `binary_path` option) must
14
+ * point at a prebuilt binary; the preset throws a clear error when neither
15
+ * is set rather than guessing. Build once with
16
+ * `cargo build -p testing_spine_stub --release` and point the env var at
17
+ * the resulting `target/release/testing_spine_stub`; operators / CI cache
18
+ * the binary across runs for fast spawns.
19
+ *
20
+ * **Operator setup** — the target Postgres database must exist before the
21
+ * harness runs (the harness never issues `CREATE DATABASE`, to avoid
22
+ * forcing a `CREATEDB` grant on the test role):
23
+ *
24
+ * ```bash
25
+ * createdb fuz_app_test_rust_spine_stub 2>/dev/null || true
26
+ * ```
27
+ *
28
+ * The binary self-wipes the auth-namespace schema on every boot
29
+ * (`FUZ_TESTING_RESET_DB_ON_STARTUP=true`, set by the Rust-family builder),
30
+ * so no manual `DROP TABLE` between sessions is needed; per-test reset is
31
+ * the orthogonal `_testing_reset` RPC action `default_cross_process_setup`
32
+ * fires.
33
+ *
34
+ * @module
35
+ */
36
+ import { fileURLToPath } from 'node:url';
37
+ import { build_test_backend_paths } from './build_test_backend_paths.js';
38
+ import { SPINE_EXPECTED_SCHEMA_URL } from './default_spine_surface.js';
39
+ import { make_default_rust_backend_config, rust_default_capabilities, } from './default_backend_configs.js';
40
+ /** Env var naming the prebuilt `testing_spine_stub` binary. Required when `binary_path` is omitted. */
41
+ export const RUST_SPINE_STUB_BIN_ENV = 'FUZ_TESTING_RUST_SPINE_STUB_BIN';
42
+ /**
43
+ * Env var the stub reads for the absolute path of the committed
44
+ * `expected_schema.json` its `/ready` gate introspects against. Pointed at the
45
+ * **same** fixture the TS spine reads ({@link SPINE_EXPECTED_SCHEMA_URL}) —
46
+ * column-presence is engine-portable, so one file is the cross-impl contract.
47
+ */
48
+ export const RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV = 'FUZ_RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH';
49
+ /** Default listening port — slots beside zzz's 1175/1176; matches the binary's `DEFAULT_PORT`. */
50
+ export const RUST_SPINE_STUB_DEFAULT_PORT = 1177;
51
+ /** Default Postgres database — real PG (PGlite isn't reachable from `tokio-postgres`). */
52
+ export const RUST_SPINE_STUB_DEFAULT_DATABASE_URL = 'postgres://localhost/fuz_app_test_rust_spine_stub';
53
+ /**
54
+ * Build the `BackendConfig` for `testing_spine_stub`. Resolves the binary
55
+ * from `options.binary_path` or `FUZ_TESTING_RUST_SPINE_STUB_BIN`; throws when
56
+ * neither is set so a missing build surfaces as a clear error rather than
57
+ * a confusing spawn failure. Reconciles the binary's env contract: port
58
+ * via `--port` (and `FUZ_RUST_SPINE_STUB_PORT`), daemon-token dir via
59
+ * `FUZ_RUST_SPINE_STUB_DIR` (anchored to `paths.root` so the written
60
+ * `{dir}/run/daemon_token` matches the path `spawn_backend` reads).
61
+ *
62
+ * @throws Error when no binary path is available.
63
+ */
64
+ export const rust_spine_stub_backend_config = (options = {}) => {
65
+ const { port = RUST_SPINE_STUB_DEFAULT_PORT, database_url = RUST_SPINE_STUB_DEFAULT_DATABASE_URL, binary_path = process.env[RUST_SPINE_STUB_BIN_ENV], } = options;
66
+ if (!binary_path) {
67
+ throw new Error(`rust_spine_stub_backend_config: no binary path — set ${RUST_SPINE_STUB_BIN_ENV} to a prebuilt ` +
68
+ '`testing_spine_stub` binary (build it with `cargo build -p testing_spine_stub --release`) ' +
69
+ 'or pass `binary_path`.');
70
+ }
71
+ const name = 'spine_stub';
72
+ const paths = build_test_backend_paths(name);
73
+ return make_default_rust_backend_config({
74
+ name,
75
+ port,
76
+ // `--port` is the binary's authoritative port input; the
77
+ // `FUZ_RUST_SPINE_STUB_PORT` env the builder also sets (via `port_env_var`)
78
+ // is the lower-precedence fallback — both carry the same value.
79
+ start_command: [binary_path, '--port', String(port)],
80
+ database_url,
81
+ // The stub serves `GET /api/admin/audit/stream` (the spine
82
+ // `fuz_realtime::SseRegistry` + audit listener), so it advertises `sse`
83
+ // like the TS spines — the cross-process SSE suite's three cases run. It
84
+ // also live-mounts `/ready` over the env-supplied fixture path, so it
85
+ // advertises `ready` for `describe_ready_cross_tests`.
86
+ capabilities: { ...rust_default_capabilities, sse: true, ready: true },
87
+ port_env_var: 'FUZ_RUST_SPINE_STUB_PORT',
88
+ rust_log: 'info,testing_spine_stub=info',
89
+ paths,
90
+ extra_env: {
91
+ // The binary writes its daemon-token JSON to
92
+ // `{FUZ_RUST_SPINE_STUB_DIR}/run/daemon_token`; anchoring the dir to
93
+ // `paths.root` makes that equal `paths.daemon_token_path`, which
94
+ // `spawn_backend` reads after the health probe.
95
+ FUZ_RUST_SPINE_STUB_DIR: paths.root,
96
+ // Absolute path to the committed spine `expected_schema.json` — the
97
+ // stub's `/ready` gate introspects the live DB against it. The SAME
98
+ // file the TS spine reads, so the two backends share one cross-impl
99
+ // contract.
100
+ [RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV]: fileURLToPath(SPINE_EXPECTED_SCHEMA_URL),
101
+ },
102
+ });
103
+ };
@@ -3,7 +3,7 @@ import '../assert_dev_env.js';
3
3
  * Cross-process `BackendConfig` presets for fuz_app's domain-free **TS**
4
4
  * spine test binary (`src/test/cross_backend/testing_spine_server_{node,deno,bun}.ts`).
5
5
  *
6
- * The TS analog of `spine_stub_backend_config` (which spawns the Rust spine):
6
+ * The TS analog of `rust_spine_stub_backend_config` (which spawns the Rust spine):
7
7
  * these spawn fuz_app's own TS impl over real HTTP with no domain layer, so
8
8
  * the `cross_backend_ts_node` / `cross_backend_ts_deno` / `cross_backend_ts_bun`
9
9
  * self-test projects verify fuz_app's wire path in its own repo across all
@@ -1 +1 @@
1
- {"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAOvD,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAE3D;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,4BAA4B,CAAC;AAK3D,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAgBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAgBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aA6BF,CAAC"}
1
+ {"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAOvD,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAE3D;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,4BAA4B,CAAC;AAS3D,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAgBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAgBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aA6BF,CAAC"}
@@ -11,8 +11,12 @@ export const TS_SPINE_DIR_ENV = 'FUZ_TESTING_TS_SPINE_DIR';
11
11
  * the stream, so the shared `ts_default_capabilities` stays `sse: false`.
12
12
  */
13
13
  export const TS_SPINE_SSE_PATH = '/api/admin/audit/stream';
14
- /** Capabilities for the TS spine binary — `ts_default_capabilities` plus `sse` (the binary wires `audit_log_sse`). */
15
- const ts_spine_capabilities = Object.freeze({ ...ts_default_capabilities, sse: true });
14
+ /**
15
+ * Capabilities for the TS spine binary — `ts_default_capabilities` plus `sse`
16
+ * (the binary wires `audit_log_sse`) and `ready` (the binary live-mounts the
17
+ * `/ready` deploy gate in `build_spine_app`).
18
+ */
19
+ const ts_spine_capabilities = Object.freeze({ ...ts_default_capabilities, sse: true, ready: true });
16
20
  /** Default port for the Node TS spine binary — slots beside the Rust `spine_stub` (1177). */
17
21
  export const TS_SPINE_NODE_DEFAULT_PORT = 1178;
18
22
  /** Default port for the Deno TS spine binary. */
@@ -0,0 +1,46 @@
1
+ import './assert_dev_env.js';
2
+ import type { Db } from '../db/db.js';
3
+ import { type ExpectedSchema } from '../db/schema_ready.js';
4
+ /** Options for `sync_expected_schema_fixture`. */
5
+ export interface SyncExpectedSchemaFixtureOptions {
6
+ /** A bootstrapped DB — the consumer has run its full migration chain on it. */
7
+ db: Db;
8
+ /** Committed fixture location — an `import.meta.url`-relative URL or a path. */
9
+ fixture_url: URL | string;
10
+ /**
11
+ * When true, overwrite the fixture with the live column map instead of just
12
+ * reading it. Drive from an env flag (e.g. `UPDATE_SCHEMA_READY === '1'`).
13
+ */
14
+ update?: boolean;
15
+ }
16
+ /** The live column map and the committed fixture, for a `deepEqual` assertion. */
17
+ export interface SyncExpectedSchemaFixtureResult {
18
+ /** Columns introspected from the live, freshly-bootstrapped DB. */
19
+ live: Record<string, Array<string>>;
20
+ /** The committed fixture (re-read after writing when `update`). */
21
+ committed: ExpectedSchema;
22
+ }
23
+ /**
24
+ * Introspect the live (bootstrapped) DB's columns, write them to the committed
25
+ * fixture when `update`, then read the committed fixture back. The caller
26
+ * asserts `deepEqual(live, committed)`:
27
+ *
28
+ * ```ts
29
+ * const {live, committed} = await sync_expected_schema_fixture({
30
+ * db,
31
+ * fixture_url: new URL('../../lib/server/expected_schema.json', import.meta.url),
32
+ * update: process.env.UPDATE_SCHEMA_READY === '1',
33
+ * });
34
+ * assert.deepEqual(live, committed);
35
+ * ```
36
+ *
37
+ * When `update` writes the fixture it emits raw `JSON.stringify` (one array
38
+ * element per line); Prettier collapses short arrays inline, so run `gro format`
39
+ * after `UPDATE_SCHEMA_READY=1` before committing or the format check will flag
40
+ * the regenerated file. (The content is identical either way — the regen test
41
+ * compares values, not formatting.)
42
+ *
43
+ * @returns the live column map and the committed map (post-write when `update`)
44
+ */
45
+ export declare const sync_expected_schema_fixture: (options: SyncExpectedSchemaFixtureOptions) => Promise<SyncExpectedSchemaFixtureResult>;
46
+ //# sourceMappingURL=schema_ready_fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema_ready_fixture.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_ready_fixture.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,EAAuB,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAEhF,kDAAkD;AAClD,MAAM,WAAW,gCAAgC;IAChD,+EAA+E;IAC/E,EAAE,EAAE,EAAE,CAAC;IACP,gFAAgF;IAChF,WAAW,EAAE,GAAG,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,WAAW,+BAA+B;IAC/C,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACpC,mEAAmE;IACnE,SAAS,EAAE,cAAc,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,gCAAgC,KACvC,OAAO,CAAC,+BAA+B,CAQzC,CAAC"}