@fuzdev/fuz_app 0.90.0 → 0.91.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 (32) hide show
  1. package/dist/auth/CLAUDE.md +7 -0
  2. package/dist/auth/all_cell_actions.d.ts +35 -0
  3. package/dist/auth/all_cell_actions.d.ts.map +1 -0
  4. package/dist/auth/all_cell_actions.js +32 -0
  5. package/dist/testing/CLAUDE.md +82 -9
  6. package/dist/testing/cross_backend/action_manifest.d.ts +113 -0
  7. package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -0
  8. package/dist/testing/cross_backend/action_manifest.js +89 -0
  9. package/dist/testing/cross_backend/action_manifest_parity.d.ts +67 -0
  10. package/dist/testing/cross_backend/action_manifest_parity.d.ts.map +1 -0
  11. package/dist/testing/cross_backend/action_manifest_parity.js +88 -0
  12. package/dist/testing/cross_backend/capabilities.d.ts +47 -30
  13. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  14. package/dist/testing/cross_backend/capabilities.js +19 -4
  15. package/dist/testing/cross_backend/default_backend_configs.d.ts +31 -15
  16. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  17. package/dist/testing/cross_backend/default_backend_configs.js +30 -14
  18. package/dist/testing/cross_backend/full_spine_mount.d.ts +81 -0
  19. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -0
  20. package/dist/testing/cross_backend/full_spine_mount.js +49 -0
  21. package/dist/testing/cross_backend/method_coverage.d.ts +71 -0
  22. package/dist/testing/cross_backend/method_coverage.d.ts.map +1 -0
  23. package/dist/testing/cross_backend/method_coverage.js +89 -0
  24. package/dist/testing/cross_backend/setup.d.ts +12 -0
  25. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  26. package/dist/testing/cross_backend/setup.js +19 -0
  27. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  28. package/dist/testing/cross_backend/testing_backdoor.js +9 -5
  29. package/dist/testing/cross_backend/testing_reset_actions.d.ts +76 -2
  30. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  31. package/dist/testing/cross_backend/testing_reset_actions.js +63 -2
  32. package/package.json +2 -2
@@ -1,40 +1,45 @@
1
1
  import '../assert_dev_env.js';
2
2
  /**
3
- * Optional behaviors a backend may support. Each flag's TSDoc names the
4
- * tests that gate on it; add a new flag here before referencing it from
5
- * a suite body, and document the gating tests inline.
3
+ * Backend wiring facts recorded for documentation — **not** gating flags.
4
+ *
5
+ * The companion to `BackendCapabilities`: where each capability flag has a
6
+ * `test_if(capabilities.X, ...)` reader that skips a suite the backend doesn't
7
+ * implement, nothing reads these. They record middleware / limiter wiring that
8
+ * differs between the TS and Rust families (a backend-shape record) but gates
9
+ * no cross test today. They live in their own type precisely so
10
+ * `BackendCapabilities` stops claiming gating power it doesn't have — fold a
11
+ * flag in here the moment it has no `test_if` reader, and promote it back the
12
+ * day a suite genuinely gates on it.
6
13
  */
7
- export interface BackendCapabilities {
14
+ export interface BackendShapeNotes {
8
15
  /**
9
- * Bearer token auth (`Authorization: Bearer <token>`) is wired through
10
- * the backend's middleware stack.
11
- *
12
- * **Declared for backend-shape documentation, not gating.** No suite reads
13
- * this flag — the bearer-token cases in `describe_standard_integration_tests`
14
- * / `describe_rate_limiting_tests` run unconditionally (every spine wires
15
- * bearer auth). Fold into a typed capability taxonomy if these gain real
16
- * gating readers.
16
+ * Bearer-token auth (`Authorization: Bearer <token>`) is wired through the
17
+ * backend's middleware stack. `true` on every spine — the bearer-token cases
18
+ * in `describe_standard_integration_tests` / `describe_rate_limiting_tests`
19
+ * run unconditionally.
17
20
  */
18
21
  readonly bearer_auth: boolean;
19
22
  /**
20
- * Trusted-proxy XFF parsing is wired (`X-Forwarded-For` etc.).
21
- *
22
- * **Declared for backend-shape documentation, not gating.** No suite reads
23
- * this flag (there is no cross-process proxy-resolution suite); it records
24
- * the proxy-default difference between the TS family (`false`) and the Rust
25
- * family (`true`). Fold into a typed capability taxonomy if it gains real
26
- * gating readers.
23
+ * Trusted-proxy XFF parsing (`X-Forwarded-For` etc.) is wired. Records the
24
+ * proxy-default difference between the TS family (`false` — the test binary
25
+ * leaves proxy parsing off) and the Rust family (`true` — the client-IP
26
+ * middleware is always wired; the env-gate only chooses XFF vs the TCP peer).
27
27
  */
28
28
  readonly trusted_proxy: boolean;
29
29
  /**
30
- * Per-account login rate limiting is wired.
31
- *
32
- * **Declared for backend-shape documentation, not gating.** No suite reads
33
- * this flag; the `describe_rate_limiting_tests` per-account cases are
34
- * in-process-only and don't cross a process boundary. Fold into a typed
35
- * capability taxonomy if it gains real gating readers.
30
+ * Per-account login rate limiting is wired. `false` for the TS family (the
31
+ * canonical path leaves the limiter null in test mode), `true` for the Rust
32
+ * family (env-gated bucket on `/login` + `/password`).
36
33
  */
37
34
  readonly login_rate_limit: boolean;
35
+ }
36
+ /**
37
+ * Optional behaviors a backend may support. Each flag's TSDoc names the
38
+ * tests that gate on it; add a new flag here before referencing it from
39
+ * a suite body, and document the gating tests inline. Wiring facts that gate
40
+ * nothing belong in `BackendShapeNotes`, not here.
41
+ */
42
+ export interface BackendCapabilities {
38
43
  /**
39
44
  * WebSocket transport is reachable end-to-end. Gates the cross-process
40
45
  * WS round-trip suite; the in-process `describe_ws_round_trip_tests`
@@ -73,10 +78,16 @@ export interface BackendCapabilities {
73
78
  * The account-lifecycle admin verbs (`account_delete` soft-delete,
74
79
  * `account_undelete` reactivation, `account_purge` keeper hard-delete)
75
80
  * are live-mounted on the backend's RPC path. Gates the dedicated
76
- * `describe_account_lifecycle_cross_tests` suite. Like cells, these
77
- * destructive/stateful verbs stay off the standard declared surface (the
78
- * generic round-trip can't drive them — they delete the subject), so
79
- * this flag opts a backend into the lifecycle parity coverage.
81
+ * `describe_account_lifecycle_cross_tests` suite.
82
+ *
83
+ * Unlike cells / fact-serving / ws / sse, these verbs **are** on the
84
+ * standard declared surface — they live in `create_admin_actions`, so
85
+ * `create_spine_surface_spec` carries them and the spec-derived round-trip
86
+ * + attack-surface suites already auto-enumerate their wire shape + auth.
87
+ * This flag gates the *behavioral* parity the generic round-trip can't
88
+ * provide: it can't drive verbs that delete their own subject (soft-delete →
89
+ * undelete round-trip, keeper-confirmed purge, the keeper-guard refusal), so
90
+ * the dedicated cross suite adds them.
80
91
  */
81
92
  readonly account_lifecycle: boolean;
82
93
  /**
@@ -136,13 +147,19 @@ export interface BackendCapabilities {
136
147
  * declare each flag explicitly per backend.
137
148
  */
138
149
  export declare const in_process_capabilities: BackendCapabilities;
150
+ /**
151
+ * Shape notes for the in-process Hono transport — every wiring fact present
152
+ * (the in-process app exercises the full middleware stack). Documentation
153
+ * only, like every `BackendShapeNotes` (nothing reads it).
154
+ */
155
+ export declare const in_process_shape_notes: BackendShapeNotes;
139
156
  /**
140
157
  * Conditional `test()` wrapper — registers a vitest case only when
141
158
  * `cond` is true; otherwise registers it as `.skip` so the run still
142
159
  * surfaces the gated coverage in the report.
143
160
  *
144
161
  * Thin wrapper around vitest's `test.skipIf(!cond)` with the argument
145
- * order flipped to match the more readable `test_if(capabilities.bearer_auth, ...)`
162
+ * order flipped to match the more readable `test_if(capabilities.ws, ...)`
146
163
  * call pattern.
147
164
  */
148
165
  export declare const test_if: (cond: boolean, name: string, fn: () => void | Promise<void>) => void;
@@ -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;AAgC9B;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;;;;OAOG;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;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,kCAAkC,EAAE,OAAO,CAAC;CACrD;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAapC,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;AAwC9B;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;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;;;;;;;;;;;;;;OAcG;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;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,kCAAkC,EAAE,OAAO,CAAC;CACrD;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAUpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAInC,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"}
@@ -24,6 +24,14 @@ import '../assert_dev_env.js';
24
24
  * - `rust_spine_stub_capabilities` — fuz_app's Rust spine-stub preset, in
25
25
  * `rust_spine_stub_backend_config.ts` (delta off the rust family default).
26
26
  *
27
+ * **Gating flags vs shape notes.** `BackendCapabilities` holds only flags a
28
+ * suite actually gates on (each has a `test_if(capabilities.X, ...)` reader).
29
+ * Wiring facts that gate nothing — `bearer_auth` / `trusted_proxy` /
30
+ * `login_rate_limit` — live in the parallel `BackendShapeNotes` record
31
+ * (`in_process_shape_notes` here, `ts_default_shape_notes` /
32
+ * `rust_default_shape_notes` in `default_backend_configs.ts`) so the
33
+ * capability type never claims gating power it doesn't have.
34
+ *
27
35
  * @module
28
36
  */
29
37
  import { test } from 'vitest';
@@ -34,9 +42,6 @@ import { test } from 'vitest';
34
42
  * declare each flag explicitly per backend.
35
43
  */
36
44
  export const in_process_capabilities = Object.freeze({
37
- bearer_auth: true,
38
- trusted_proxy: true,
39
- login_rate_limit: true,
40
45
  ws: true,
41
46
  sse: true,
42
47
  cell_crud: true,
@@ -47,13 +52,23 @@ export const in_process_capabilities = Object.freeze({
47
52
  account_status: true,
48
53
  oversized_reject_closes_connection: true,
49
54
  });
55
+ /**
56
+ * Shape notes for the in-process Hono transport — every wiring fact present
57
+ * (the in-process app exercises the full middleware stack). Documentation
58
+ * only, like every `BackendShapeNotes` (nothing reads it).
59
+ */
60
+ export const in_process_shape_notes = Object.freeze({
61
+ bearer_auth: true,
62
+ trusted_proxy: true,
63
+ login_rate_limit: true,
64
+ });
50
65
  /**
51
66
  * Conditional `test()` wrapper — registers a vitest case only when
52
67
  * `cond` is true; otherwise registers it as `.skip` so the run still
53
68
  * surfaces the gated coverage in the report.
54
69
  *
55
70
  * Thin wrapper around vitest's `test.skipIf(!cond)` with the argument
56
- * order flipped to match the more readable `test_if(capabilities.bearer_auth, ...)`
71
+ * order flipped to match the more readable `test_if(capabilities.ws, ...)`
57
72
  * call pattern.
58
73
  */
59
74
  export const test_if = (cond, name, fn) => {
@@ -11,13 +11,15 @@ import '../assert_dev_env.js';
11
11
  *
12
12
  * Defaults baked in by family:
13
13
  *
14
- * - **TS** — `'memory://'` PGlite, 30s startup window, capabilities
15
- * without trusted_proxy/login_rate_limit. The TS canonical path
16
- * leaves these limiters null in test mode.
14
+ * - **TS** — `'memory://'` PGlite, 30s startup window. Its shape notes
15
+ * (`ts_default_shape_notes`) record `trusted_proxy: false` /
16
+ * `login_rate_limit: false` — the TS canonical path leaves those limiters
17
+ * null in test mode.
17
18
  * - **Rust** — caller-supplied real Postgres URL (PGlite isn't reachable
18
- * from `tokio-postgres`), 120s startup window (cargo first-build cost),
19
- * capabilities including trusted_proxy + login_rate_limit + the
20
- * `FUZ_TESTING_RESET_DB_ON_STARTUP=true` self-wipe gate.
19
+ * from `tokio-postgres`), 120s startup window (cargo first-build cost), the
20
+ * `FUZ_TESTING_RESET_DB_ON_STARTUP=true` self-wipe gate, and shape notes
21
+ * (`rust_default_shape_notes`) recording `trusted_proxy: true` +
22
+ * `login_rate_limit: true`.
21
23
  *
22
24
  * Both builders default `port_env_var` to `'PORT'`. Consumers whose
23
25
  * binary reads a different name (e.g. zzz's `ZZZ_PORT`) override.
@@ -31,22 +33,36 @@ import '../assert_dev_env.js';
31
33
  * @module
32
34
  */
33
35
  import type { BackendBootstrapConfig, BackendConfig } from './backend_config.js';
34
- import type { BackendCapabilities } from './capabilities.js';
36
+ import type { BackendCapabilities, BackendShapeNotes } from './capabilities.js';
35
37
  import { type TestBackendPaths } from './build_test_backend_paths.js';
36
38
  /**
37
- * Capabilities shared by TS-family backends — same canonical
38
- * implementation, same feature set. No trusted-proxy phase (the test
39
- * binary doesn't enable proxy parsing) and no per-account login rate
40
- * limit (the TS canonical path leaves the limiter null in test mode).
39
+ * Capabilities shared by TS-family backends — same canonical implementation,
40
+ * same feature set. The non-gating wiring facts (`bearer_auth` /
41
+ * `trusted_proxy` / `login_rate_limit`) live in `ts_default_shape_notes`.
41
42
  */
42
43
  export declare const ts_default_capabilities: BackendCapabilities;
43
44
  /**
44
- * Capabilities for the Rust family. Adds `trusted_proxy: true` (the
45
- * Rust spine's client-IP middleware is always wired; the env-gate just
46
- * controls whether XFF is consulted vs the TCP peer IP) and
47
- * `login_rate_limit: true` (env-gated bucket on `/login` + `/password`).
45
+ * Capabilities for the Rust family. The non-gating wiring facts live in
46
+ * `rust_default_shape_notes` (which records the family's `trusted_proxy` +
47
+ * `login_rate_limit` differences from the TS family).
48
48
  */
49
49
  export declare const rust_default_capabilities: BackendCapabilities;
50
+ /**
51
+ * Shape notes for TS-family backends — wiring facts, not gating flags.
52
+ * `trusted_proxy: false` (the test binary doesn't enable proxy parsing) and
53
+ * `login_rate_limit: false` (the TS canonical path leaves the limiter null in
54
+ * test mode); bearer auth is always wired. Documentation only — see
55
+ * `BackendShapeNotes`.
56
+ */
57
+ export declare const ts_default_shape_notes: BackendShapeNotes;
58
+ /**
59
+ * Shape notes for Rust-family backends — wiring facts, not gating flags. Adds
60
+ * `trusted_proxy: true` (the Rust spine's client-IP middleware is always wired;
61
+ * the env-gate only chooses XFF vs the TCP peer IP) and `login_rate_limit: true`
62
+ * (env-gated bucket on `/login` + `/password`). Documentation only — see
63
+ * `BackendShapeNotes`.
64
+ */
65
+ export declare const rust_default_shape_notes: BackendShapeNotes;
50
66
  export interface MakeDefaultTsBackendConfigOptions {
51
67
  /** Diagnostic label; also used as the tmpdir prefix when `paths` is omitted. */
52
68
  readonly name: string;
@@ -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,mBAoBpC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAmBtC,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAC,mBAAmB,EAAE,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAA2B,KAAK,gBAAgB,EAAC,MAAM,+BAA+B,CAAC;AAQ9F;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAiBpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAgBtC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAInC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAIrC,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"}
@@ -2,15 +2,11 @@ import '../assert_dev_env.js';
2
2
  import { build_test_backend_paths } from './build_test_backend_paths.js';
3
3
  import { default_test_bootstrap_token, default_test_cookie_keys, default_test_keeper_password, default_test_keeper_username, } from './default_secrets.js';
4
4
  /**
5
- * Capabilities shared by TS-family backends — same canonical
6
- * implementation, same feature set. No trusted-proxy phase (the test
7
- * binary doesn't enable proxy parsing) and no per-account login rate
8
- * limit (the TS canonical path leaves the limiter null in test mode).
5
+ * Capabilities shared by TS-family backends — same canonical implementation,
6
+ * same feature set. The non-gating wiring facts (`bearer_auth` /
7
+ * `trusted_proxy` / `login_rate_limit`) live in `ts_default_shape_notes`.
9
8
  */
10
9
  export const ts_default_capabilities = Object.freeze({
11
- bearer_auth: true,
12
- trusted_proxy: false,
13
- login_rate_limit: false,
14
10
  ws: true,
15
11
  sse: false,
16
12
  cell_crud: true,
@@ -29,15 +25,11 @@ export const ts_default_capabilities = Object.freeze({
29
25
  oversized_reject_closes_connection: true,
30
26
  });
31
27
  /**
32
- * Capabilities for the Rust family. Adds `trusted_proxy: true` (the
33
- * Rust spine's client-IP middleware is always wired; the env-gate just
34
- * controls whether XFF is consulted vs the TCP peer IP) and
35
- * `login_rate_limit: true` (env-gated bucket on `/login` + `/password`).
28
+ * Capabilities for the Rust family. The non-gating wiring facts live in
29
+ * `rust_default_shape_notes` (which records the family's `trusted_proxy` +
30
+ * `login_rate_limit` differences from the TS family).
36
31
  */
37
32
  export const rust_default_capabilities = Object.freeze({
38
- bearer_auth: true,
39
- trusted_proxy: true,
40
- login_rate_limit: true,
41
33
  ws: true,
42
34
  sse: false,
43
35
  cell_crud: true,
@@ -54,6 +46,30 @@ export const rust_default_capabilities = Object.freeze({
54
46
  // and the pipelined request is never reached.
55
47
  oversized_reject_closes_connection: true,
56
48
  });
49
+ /**
50
+ * Shape notes for TS-family backends — wiring facts, not gating flags.
51
+ * `trusted_proxy: false` (the test binary doesn't enable proxy parsing) and
52
+ * `login_rate_limit: false` (the TS canonical path leaves the limiter null in
53
+ * test mode); bearer auth is always wired. Documentation only — see
54
+ * `BackendShapeNotes`.
55
+ */
56
+ export const ts_default_shape_notes = Object.freeze({
57
+ bearer_auth: true,
58
+ trusted_proxy: false,
59
+ login_rate_limit: false,
60
+ });
61
+ /**
62
+ * Shape notes for Rust-family backends — wiring facts, not gating flags. Adds
63
+ * `trusted_proxy: true` (the Rust spine's client-IP middleware is always wired;
64
+ * the env-gate only chooses XFF vs the TCP peer IP) and `login_rate_limit: true`
65
+ * (env-gated bucket on `/login` + `/password`). Documentation only — see
66
+ * `BackendShapeNotes`.
67
+ */
68
+ export const rust_default_shape_notes = Object.freeze({
69
+ bearer_auth: true,
70
+ trusted_proxy: true,
71
+ login_rate_limit: true,
72
+ });
57
73
  /** Bootstrap block built from the default secrets + supplied paths. */
58
74
  const build_default_bootstrap = (paths, overrides) => ({
59
75
  token_path: paths.bootstrap_token_path,
@@ -0,0 +1,81 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * The **full** live RPC mount for fuz_app's own spine test binary — the
4
+ * complete action set `testing_spine_server.ts` exposes on a single RPC
5
+ * endpoint, in one place.
6
+ *
7
+ * Where `default_spine_surface.ts` defines the **declared** surface
8
+ * (`create_spine_surface_spec` / `spine_rpc_endpoints` — the
9
+ * `create_standard_rpc_actions` bundle the spec-derived suites
10
+ * auto-enumerate), this module defines its superset: the standard bundle
11
+ * **plus** the families the binary live-mounts but keeps off the declared
12
+ * surface —
13
+ *
14
+ * - the `_testing_*` daemon-token backdoors (`create_testing_actions`),
15
+ * - the full cell verb set (CRUD + grant + field + item + audit),
16
+ * - the opt-in `actor_lookup` / `actor_search` resolvers,
17
+ * - the `_testing_action_manifest` backdoor, appended last (it dumps the live
18
+ * method set for the cross-impl manifest-parity gate, so it must enumerate
19
+ * every method above it).
20
+ *
21
+ * Single-sourcing the mount here lets the binary, the in-process parity
22
+ * setup, and the `spine_method_coverage` reconciliation test all build the
23
+ * same list — so a method can never be mounted in one place and forgotten
24
+ * in another. The reconciliation test enumerates `build_full_spine_rpc_actions`
25
+ * with stub deps and asserts the live method set equals the tagged coverage
26
+ * manifest; see `src/test/cross_backend/spine_method_coverage.ts`.
27
+ *
28
+ * **`$lib`-free by contract** — like `default_spine_surface.ts`, this module
29
+ * is reached by the spawned TS binary under Gro's loader (which resolves
30
+ * `.js`→`.ts` but not the `$lib` alias), so every import is relative. Keep
31
+ * it that way.
32
+ *
33
+ * @module
34
+ */
35
+ import type { RpcAction } from '../../actions/action_rpc.js';
36
+ import type { AppDeps } from '../../auth/deps.js';
37
+ import type { DaemonTokenState } from '../../auth/daemon_token.js';
38
+ import type { NotificationSender } from '../../auth/role_grant_offer_notifications.js';
39
+ import type { RpcEndpointSpec } from '../../http/surface.js';
40
+ import type { AppServerContext } from '../../server/app_server_context.js';
41
+ /** Options for {@link build_full_spine_rpc_actions} / {@link full_spine_rpc_endpoints}. */
42
+ export interface FullSpineMountOptions {
43
+ /**
44
+ * Daemon-token runtime state threaded into `create_testing_actions` — the
45
+ * `_testing_reset` handler mutates `keeper_account_id` after re-seeding.
46
+ * Pass the same instance the daemon-token middleware reads. For the
47
+ * coverage test (method enumeration only, handlers never run) any stub
48
+ * state satisfies it.
49
+ */
50
+ readonly daemon_token_state: DaemonTokenState;
51
+ /**
52
+ * WS notification sender for the role-grant-offer fan-out. Pass the SAME
53
+ * `BackendWebsocketTransport` the WS endpoint registers connections against
54
+ * (the transport is the connection registry). Omitted for enumeration.
55
+ */
56
+ readonly notification_sender?: NotificationSender | null;
57
+ }
58
+ /**
59
+ * Build the complete live RPC action list the spine test binary mounts on
60
+ * its single endpoint: the declared `create_standard_rpc_actions` bundle plus
61
+ * the off-surface families (`_testing_*` backdoors, cells, actor resolvers).
62
+ *
63
+ * Mirrors the previous inline assembly in `testing_spine_server.ts` exactly —
64
+ * `session_options` is pinned to `spine_session_options` (the binary's cookie
65
+ * config) and `roles` to `spine_roles` (carrying `cell_editor`), so the only
66
+ * runtime-varying inputs are the daemon-token state + notification sender.
67
+ *
68
+ * @param deps - the backend `AppDeps` (stub deps suffice for method enumeration)
69
+ * @param options - daemon-token state + optional WS notification sender
70
+ * @returns every `RpcAction` the binary exposes, in mount order
71
+ */
72
+ export declare const build_full_spine_rpc_actions: (deps: AppDeps, options: FullSpineMountOptions) => Array<RpcAction>;
73
+ /**
74
+ * Factory-form full mount at {@link SPINE_RPC_PATH}, the shape
75
+ * `create_app_server`'s `rpc_endpoints` slot accepts. The spine binary wires
76
+ * this directly; the surface builder (`create_spine_surface_spec`) keeps using
77
+ * the narrower `spine_rpc_endpoints` so the declared surface stays the
78
+ * standard bundle only.
79
+ */
80
+ export declare const full_spine_rpc_endpoints: (ctx: AppServerContext, options: FullSpineMountOptions) => Array<RpcEndpointSpec>;
81
+ //# sourceMappingURL=full_spine_mount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"full_spine_mount.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/full_spine_mount.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,8CAA8C,CAAC;AAKrF,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,oCAAoC,CAAC;AAOzE,2FAA2F;AAC3F,MAAM,WAAW,qBAAqB;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAoBjB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACpC,KAAK,gBAAgB,EACrB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,eAAe,CAEvB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import '../assert_dev_env.js';
2
+ import { create_standard_rpc_actions } from '../../auth/standard_rpc_actions.js';
3
+ import { create_all_cell_actions } from '../../auth/all_cell_actions.js';
4
+ import { create_actor_lookup_actions } from '../../auth/actor_lookup_actions.js';
5
+ import { create_actor_search_actions } from '../../auth/actor_search_actions.js';
6
+ import { create_testing_action_manifest_action, create_testing_actions, } from './testing_reset_actions.js';
7
+ import { SPINE_RPC_PATH, spine_roles, spine_session_options } from './default_spine_surface.js';
8
+ /**
9
+ * Build the complete live RPC action list the spine test binary mounts on
10
+ * its single endpoint: the declared `create_standard_rpc_actions` bundle plus
11
+ * the off-surface families (`_testing_*` backdoors, cells, actor resolvers).
12
+ *
13
+ * Mirrors the previous inline assembly in `testing_spine_server.ts` exactly —
14
+ * `session_options` is pinned to `spine_session_options` (the binary's cookie
15
+ * config) and `roles` to `spine_roles` (carrying `cell_editor`), so the only
16
+ * runtime-varying inputs are the daemon-token state + notification sender.
17
+ *
18
+ * @param deps - the backend `AppDeps` (stub deps suffice for method enumeration)
19
+ * @param options - daemon-token state + optional WS notification sender
20
+ * @returns every `RpcAction` the binary exposes, in mount order
21
+ */
22
+ export const build_full_spine_rpc_actions = (deps, options) => {
23
+ const actions = [
24
+ ...create_standard_rpc_actions({ ...deps, notification_sender: options.notification_sender ?? null }, { roles: spine_roles }),
25
+ ...create_testing_actions(deps, {
26
+ session_options: spine_session_options,
27
+ daemon_token_state: options.daemon_token_state,
28
+ }),
29
+ ...create_all_cell_actions(deps, { roles: spine_roles }),
30
+ ...create_actor_lookup_actions(deps),
31
+ ...create_actor_search_actions(deps),
32
+ ];
33
+ // Append the `_testing_action_manifest` backdoor last — it closes over the
34
+ // complete `actions` list (plus its own spec) to dump the live method set
35
+ // for the cross-impl manifest-parity gate, so it must come after every
36
+ // method it enumerates.
37
+ actions.push(create_testing_action_manifest_action(actions));
38
+ return actions;
39
+ };
40
+ /**
41
+ * Factory-form full mount at {@link SPINE_RPC_PATH}, the shape
42
+ * `create_app_server`'s `rpc_endpoints` slot accepts. The spine binary wires
43
+ * this directly; the surface builder (`create_spine_surface_spec`) keeps using
44
+ * the narrower `spine_rpc_endpoints` so the declared surface stays the
45
+ * standard bundle only.
46
+ */
47
+ export const full_spine_rpc_endpoints = (ctx, options) => [
48
+ { path: SPINE_RPC_PATH, actions: build_full_spine_rpc_actions(ctx.deps, options) },
49
+ ];
@@ -0,0 +1,71 @@
1
+ import '../assert_dev_env.js';
2
+ import type { BackendCapabilities } from './capabilities.js';
3
+ /** How a live-mounted RPC method earns its cross-backend coverage. */
4
+ export type MethodCoverageTier =
5
+ /**
6
+ * On the declared surface (`create_spine_surface_spec`). Auto-enumerated by
7
+ * the spec-derived round-trip + attack-surface suites — no bespoke suite
8
+ * needed.
9
+ */
10
+ 'declared'
11
+ /**
12
+ * Live-mounted but deliberately off the declared surface (stateful or
13
+ * opt-in). Covered only by a dedicated imperative `describe_*_cross_tests`
14
+ * suite, so the entry must name it.
15
+ */
16
+ | 'off_surface'
17
+ /**
18
+ * A `_testing_*` daemon-token backdoor — live-mounted on the test binary,
19
+ * never on the declared surface. Coverage is the credential-gate suite plus
20
+ * the in-process spec-level gate test.
21
+ */
22
+ | 'backdoor';
23
+ /** One row of the live-RPC-method coverage manifest. */
24
+ export interface MethodCoverageEntry {
25
+ /** The RPC method name (`action.spec.method`). */
26
+ readonly method: string;
27
+ /** How this method is covered. */
28
+ readonly tier: MethodCoverageTier;
29
+ /**
30
+ * The `BackendCapabilities` flag gating this method's cross suite, when it
31
+ * is capability-gated. Typed against the real interface so a stale flag is a
32
+ * compile error. Omit for ungated off-surface families (always-mounted, the
33
+ * suite runs unconditionally) and for declared / backdoor tiers.
34
+ */
35
+ readonly capability?: keyof BackendCapabilities;
36
+ /**
37
+ * The cross-backend suite (or test file) that covers this method.
38
+ * **Required** for `off_surface` — the manifest's whole point is to name the
39
+ * suite a method without auto-enumeration relies on. Optional for
40
+ * `declared` (auto-enumerated) / `backdoor` (infra).
41
+ */
42
+ readonly suite?: string;
43
+ /** Optional free-text note printed nowhere — documentation for the reader. */
44
+ readonly note?: string;
45
+ }
46
+ /** Inputs for {@link assert_rpc_method_coverage}. */
47
+ export interface RpcMethodCoverageInput {
48
+ /** Every method the live RPC endpoint mounts (`action.spec.method`). */
49
+ readonly live_methods: ReadonlyArray<string>;
50
+ /** The declared-surface method names (from `create_*_surface_spec`). */
51
+ readonly declared_methods: ReadonlyArray<string>;
52
+ /** The tagged manifest the live set must reconcile against. */
53
+ readonly manifest: ReadonlyArray<MethodCoverageEntry>;
54
+ /** Backdoor method prefix. Defaults to `'_testing_'`. */
55
+ readonly testing_method_prefix?: string;
56
+ }
57
+ /**
58
+ * Assert the live RPC method set reconciles exactly with the coverage
59
+ * manifest, and that every manifest entry's tier is internally consistent.
60
+ *
61
+ * Fails loud on: a live method missing from the manifest (mounted-but-unclaimed),
62
+ * a manifest row naming a method the live mount no longer exposes (stale row),
63
+ * a declared-surface method absent from the live mount (the full mount must be
64
+ * a superset), a duplicate manifest entry, or any tier/declared/backdoor
65
+ * inconsistency (e.g. an `off_surface` row that is actually on the declared
66
+ * surface, or one missing its `suite`).
67
+ *
68
+ * @throws AssertionError naming the specific divergence.
69
+ */
70
+ export declare const assert_rpc_method_coverage: (input: RpcMethodCoverageInput) => void;
71
+ //# sourceMappingURL=method_coverage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"method_coverage.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/method_coverage.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiC9B,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAE3D,sEAAsE;AACtE,MAAM,MAAM,kBAAkB;AAC7B;;;;GAIG;AACD,UAAU;AACZ;;;;GAIG;GACD,aAAa;AACf;;;;GAIG;GACD,UAAU,CAAC;AAEd,wDAAwD;AACxD,MAAM,WAAW,mBAAmB;IACnC,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,mBAAmB,CAAC;IAChD;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qDAAqD;AACrD,MAAM,WAAW,sBAAsB;IACtC,wEAAwE;IACxE,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjD,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;IACtD,yDAAyD;IACzD,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CACxC;AAID;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,sBAAsB,KAAG,IAsF1E,CAAC"}
@@ -0,0 +1,89 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Reconcile a backend's **live-mounted** RPC method set against a tagged
4
+ * coverage manifest, so the off-declared-surface methods stay as drift-proof
5
+ * as the declared ones.
6
+ *
7
+ * The spec-derived suites (`describe_rpc_round_trip_tests` /
8
+ * `describe_rpc_attack_surface_tests`) auto-enumerate the **declared**
9
+ * surface — add a method to a registry in `create_spine_surface_spec` and it
10
+ * is tested automatically. But a backend's live RPC endpoint also mounts
11
+ * methods kept *off* that surface (stateful cell verbs, opt-in resolvers,
12
+ * `_testing_*` backdoors); those rely on hand-wired imperative suites and get
13
+ * no auto-enumeration. Nothing structurally guaranteed every live method was
14
+ * actually claimed by a suite — so a newly mounted-but-untested method could
15
+ * ship silently.
16
+ *
17
+ * {@link assert_rpc_method_coverage} closes that gap: it diffs the live method
18
+ * set against a {@link MethodCoverageEntry} manifest (both directions — a
19
+ * mounted-but-unclaimed method *and* a stale manifest row both fail loud) and
20
+ * checks each entry's tier is consistent with the declared surface + the
21
+ * backdoor prefix. The manifest becomes the forcing function — a new method
22
+ * can't reach the live mount without a manifest row naming the suite that
23
+ * covers it.
24
+ *
25
+ * Pairs with `surface_invariants.ts` `assert_no_testing_methods` (which guards
26
+ * the *reverse* — a backdoor must never leak *onto* the declared surface).
27
+ *
28
+ * @module
29
+ */
30
+ import { assert } from 'vitest';
31
+ const sorted = (set) => [...set].sort().join('\n ');
32
+ /**
33
+ * Assert the live RPC method set reconciles exactly with the coverage
34
+ * manifest, and that every manifest entry's tier is internally consistent.
35
+ *
36
+ * Fails loud on: a live method missing from the manifest (mounted-but-unclaimed),
37
+ * a manifest row naming a method the live mount no longer exposes (stale row),
38
+ * a declared-surface method absent from the live mount (the full mount must be
39
+ * a superset), a duplicate manifest entry, or any tier/declared/backdoor
40
+ * inconsistency (e.g. an `off_surface` row that is actually on the declared
41
+ * surface, or one missing its `suite`).
42
+ *
43
+ * @throws AssertionError naming the specific divergence.
44
+ */
45
+ export const assert_rpc_method_coverage = (input) => {
46
+ const prefix = input.testing_method_prefix ?? '_testing_';
47
+ const live = new Set(input.live_methods);
48
+ const declared = new Set(input.declared_methods);
49
+ const manifest_methods = new Set();
50
+ for (const entry of input.manifest) {
51
+ assert.ok(!manifest_methods.has(entry.method), `method coverage: duplicate manifest entry for '${entry.method}'`);
52
+ manifest_methods.add(entry.method);
53
+ }
54
+ const unclaimed = [...live].filter((m) => !manifest_methods.has(m));
55
+ assert.ok(unclaimed.length === 0, `method coverage: ${unclaimed.length} live-mounted RPC method(s) absent from the coverage ` +
56
+ `manifest — add a {method, tier, suite} entry for each (or stop mounting it):\n ${sorted(unclaimed)}`);
57
+ const stale = [...manifest_methods].filter((m) => !live.has(m));
58
+ assert.ok(stale.length === 0, `method coverage: ${stale.length} manifest entr(y/ies) name a method the live mount no longer ` +
59
+ `exposes — remove the stale row(s):\n ${sorted(stale)}`);
60
+ const declared_not_live = [...declared].filter((m) => !live.has(m));
61
+ assert.ok(declared_not_live.length === 0, `method coverage: ${declared_not_live.length} declared-surface method(s) not present in the ` +
62
+ `live mount — the full mount must be a superset of the declared surface:\n ${sorted(declared_not_live)}`);
63
+ for (const entry of input.manifest) {
64
+ const is_backdoor = entry.method.startsWith(prefix);
65
+ const is_declared = declared.has(entry.method);
66
+ switch (entry.tier) {
67
+ case 'declared':
68
+ assert.ok(is_declared, `method coverage: '${entry.method}' is tagged 'declared' but is not on the declared ` +
69
+ `surface (create_*_surface_spec) — retag it 'off_surface' or 'backdoor'`);
70
+ assert.ok(!is_backdoor, `method coverage: '${entry.method}' is tagged 'declared' but starts with '${prefix}' ` +
71
+ `(backdoors must never reach the declared surface) — retag it 'backdoor'`);
72
+ break;
73
+ case 'backdoor':
74
+ assert.ok(is_backdoor, `method coverage: '${entry.method}' is tagged 'backdoor' but does not start with ` +
75
+ `'${prefix}'`);
76
+ assert.ok(!is_declared, `method coverage: '${entry.method}' is tagged 'backdoor' but appears on the declared ` +
77
+ `surface — a '${prefix}*' action must be live-mounted only`);
78
+ break;
79
+ case 'off_surface':
80
+ assert.ok(!is_declared, `method coverage: '${entry.method}' is tagged 'off_surface' but is on the declared ` +
81
+ `surface — retag it 'declared' (the spec-derived suites already enumerate it)`);
82
+ assert.ok(!is_backdoor, `method coverage: '${entry.method}' is tagged 'off_surface' but starts with '${prefix}' ` +
83
+ `— retag it 'backdoor'`);
84
+ assert.ok(!!entry.suite, `method coverage: off-surface method '${entry.method}' must name the cross-backend ` +
85
+ `'suite' that covers it — it gets no auto-enumeration`);
86
+ break;
87
+ }
88
+ }
89
+ };