testing/cross_backend/action_manifest.ts

Cross-impl RPC action-manifest introspection — a normalized, JSON-serializable dump of a backend's live RPC method set, one entry per method carrying its auth shape + side-effect flag.

The sibling of schema_introspect.ts's SchemaSnapshot: where that captures the live *database* shape for the schema-parity gate, this captures the live *RPC registry* shape for the action-manifest parity gate. Both are dumped over a daemon-token _testing_* introspection action (_testing_action_manifest here, _testing_schema_snapshot there) and diffed across the TS spine and the Rust testing_spine_stub so a method-set or per-method auth-shape divergence fails loud. This complements the in-repo spine_method_coverage gate: that proves *mounted ⟹ covered*; this proves *TS-mount-set ≡ Rust-mount-set* (method set + auth shape).

Scope — domain + testing surface, not wire protocol. Protocol actions (heartbeat / cancel / peer/ping) are excluded: on the TS spine create_testing_action_manifest_action filters them via the protocol_action_specs method set; the Rust stub drops PROTOCOL_ACTION_SPECS. The two impls organize protocol actions differently (peer/ping is on the TS spine's WS and HTTP-RPC endpoints — it must answer peer_no_transport over HTTP — while heartbeat/cancel stay WS-only; the Rust stub compiles one shared registry serving both transports), so including them would be a spurious cross-impl diff. This matches the scope of the in-repo spine_method_coverage gate (also over build_full_spine_rpc_actions).

Paired with action_manifest_parity.ts for the diff + assertion helpers.

$lib-free by contract — reached by the spawned TS binary (via testing_reset_actions.tsfull_spine_mount.ts), so every import is relative.

view source

Declarations
#

4 declarations

action_manifest_entry
#

testing/cross_backend/action_manifest.ts view source

(spec: { readonly method: string; readonly auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }; readonly side_effects: boolean; }): { ...; } import {action_manifest_entry} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest.js';

Normalize one spec's auth + side-effects into a manifest entry. Pulls the four auth axes off RouteAuth (the same shape the Rust AuthSpec mirrors) and flattens optional roles / credential_types to sorted arrays.

spec

type { readonly method: string; readonly auth: { account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles?: readonly string[] | undefined; credential_types?: readonly string[] | undefined; }; readonly side_effects: boolean; }

returns

{ method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }

ActionManifest
#

testing/cross_backend/action_manifest.ts view source

ZodObject<{ methods: ZodArray<ZodObject<{ method: ZodString; side_effects: ZodBoolean; account: ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>; actor: ZodEnum<{ ...; }>; roles: ZodArray<...>; credential_types: ZodArray<...>; }, $strict>>; }, $strict> import type {ActionManifest} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest.js';

The full action manifest — every entry, sorted by method.

ActionManifestEntry
#

testing/cross_backend/action_manifest.ts view source

ZodObject<{ method: ZodString; side_effects: ZodBoolean; account: ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>; actor: ZodEnum<{ none: "none"; optional: "optional"; required: "required"; }>; roles: ZodArray<...>; credential_types: ZodArray<...>; }, $strict> import type {ActionManifestEntry} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest.js';

One method's normalized RPC metadata — the cross-impl-comparable unit. roles / credential_types are always present + sorted (an absent gate and an empty list both serialize to []) so the diff never trips on a undefined-vs-[] or declaration-order difference between impls; the auth axes reuse the canonical AuthAxisState enum.

build_action_manifest
#

testing/cross_backend/action_manifest.ts view source

(specs: readonly Pick<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }, "method" | ... 1 more ... | "side_effects">[]): { ...; } import {build_action_manifest} from '@fuzdev/fuz_app/testing/cross_backend/action_manifest.js';

Build the normalized ActionManifest from a list of request-response specs. Entries are sorted by method so two impls producing the same set serialize identically regardless of mount order. The caller owns the scope it passes (the TS spine passes its full mount; the Rust stub filters PROTOCOL_ACTION_SPECS first) — see the module doc.

specs

type readonly Pick<{ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; }, "metho...

returns

{ methods: { method: string; side_effects: boolean; account: "none" | "optional" | "required"; actor: "none" | "optional" | "required"; roles: string[]; credential_types: string[]; }[]; }

Depends on
#

Imported by
#