@fuzdev/fuz_app 0.111.0 → 0.112.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.
- package/dist/actions/CLAUDE.md +15 -11
- package/dist/actions/action_rpc.d.ts +1 -1
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +4 -6
- package/dist/actions/peer_ping.d.ts +8 -0
- package/dist/actions/peer_ping.d.ts.map +1 -1
- package/dist/actions/peer_ping.js +8 -0
- package/dist/actions/peer_request.d.ts +1 -1
- package/dist/actions/peer_request.js +1 -1
- package/dist/actions/perform_action.d.ts +16 -9
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +30 -21
- package/dist/actions/register_action_ws.d.ts +1 -1
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +88 -21
- package/dist/auth/CLAUDE.md +37 -7
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +10 -1
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +36 -16
- package/dist/auth/account_schema.d.ts +20 -0
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +22 -1
- package/dist/auth/actor_search_actions.d.ts +3 -3
- package/dist/auth/actor_search_actions.d.ts.map +1 -1
- package/dist/auth/actor_search_actions.js +10 -6
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +11 -5
- package/dist/auth/app_settings_queries.d.ts.map +1 -1
- package/dist/auth/app_settings_queries.js +6 -4
- package/dist/auth/audit_log_queries.d.ts +6 -0
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +11 -5
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +3 -8
- package/dist/auth/cell_audit_actions.d.ts.map +1 -1
- package/dist/auth/cell_audit_actions.js +1 -1
- package/dist/auth/cell_field_actions.js +1 -1
- package/dist/auth/cell_grant_actions.js +1 -1
- package/dist/auth/cell_item_actions.js +1 -1
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +10 -8
- package/dist/auth/role_grant_offer_ddl.d.ts +11 -0
- package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_ddl.js +20 -2
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +29 -15
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +21 -7
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +9 -4
- package/dist/db/CLAUDE.md +21 -2
- package/dist/db/cell_audit_queries.js +2 -2
- package/dist/db/cell_field_queries.d.ts +1 -1
- package/dist/db/cell_field_queries.d.ts.map +1 -1
- package/dist/db/cell_field_queries.js +8 -6
- package/dist/db/cell_grant_queries.d.ts +1 -1
- package/dist/db/cell_grant_queries.d.ts.map +1 -1
- package/dist/db/cell_grant_queries.js +12 -10
- package/dist/db/cell_item_queries.d.ts +1 -1
- package/dist/db/cell_item_queries.d.ts.map +1 -1
- package/dist/db/cell_item_queries.js +9 -7
- package/dist/db/cell_queries.d.ts +3 -3
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +6 -4
- package/dist/db/fact_queries.d.ts.map +1 -1
- package/dist/db/fact_queries.js +9 -3
- package/dist/db/sql_columns.d.ts +80 -3
- package/dist/db/sql_columns.d.ts.map +1 -1
- package/dist/db/sql_columns.js +91 -4
- package/dist/http/CLAUDE.md +3 -1
- package/dist/http/jsonrpc_helpers.d.ts +13 -0
- package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
- package/dist/http/jsonrpc_helpers.js +13 -0
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +7 -2
- package/dist/testing/CLAUDE.md +53 -27
- package/dist/testing/admin_integration.d.ts +0 -3
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +0 -1
- package/dist/testing/adversarial_404.d.ts.map +1 -1
- package/dist/testing/adversarial_404.js +4 -1
- package/dist/testing/adversarial_input.d.ts.map +1 -1
- package/dist/testing/adversarial_input.js +5 -4
- package/dist/testing/app_server.d.ts +4 -3
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +11 -5
- package/dist/testing/attack_surface.d.ts +16 -0
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +36 -13
- package/dist/testing/audit_completeness.d.ts +0 -3
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +0 -1
- package/dist/testing/auth_apps.d.ts +9 -2
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +9 -2
- package/dist/testing/cross_backend/account_lifecycle.d.ts +5 -4
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +7 -0
- package/dist/testing/cross_backend/action_manifest.d.ts +19 -3
- package/dist/testing/cross_backend/action_manifest.d.ts.map +1 -1
- package/dist/testing/cross_backend/action_manifest.js +10 -4
- package/dist/testing/cross_backend/action_manifest_parity.d.ts +9 -3
- package/dist/testing/cross_backend/action_manifest_parity.d.ts.map +1 -1
- package/dist/testing/cross_backend/action_manifest_parity.js +6 -0
- package/dist/testing/cross_backend/actor_lookup.d.ts +3 -2
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.d.ts +4 -2
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +1 -1
- package/dist/testing/cross_backend/cell_crud.d.ts +2 -2
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +10 -0
- package/dist/testing/cross_backend/cell_gated_create.d.ts +3 -3
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_grant_role.d.ts +2 -2
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.d.ts +2 -2
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/fact_serving.d.ts +2 -2
- package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
- package/dist/testing/cross_backend/make_cross_backend_project.d.ts +17 -2
- package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -1
- package/dist/testing/cross_backend/make_cross_backend_project.js +23 -3
- package/dist/testing/cross_backend/origin.d.ts +4 -2
- package/dist/testing/cross_backend/origin.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin.js +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.js +11 -7
- package/dist/testing/cross_backend/query_shape.d.ts +6 -5
- package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/ready.d.ts +2 -2
- package/dist/testing/cross_backend/ready.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_enumeration.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_enumeration.js +7 -1
- package/dist/testing/cross_backend/setup.d.ts +26 -6
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.d.ts +5 -1
- package/dist/testing/cross_backend/standard.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.js +1 -5
- package/dist/testing/cross_backend/testing_backdoor.js +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +5 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_lifetime.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_lifetime.js +6 -0
- package/dist/testing/cross_backend/token_scope_surface.d.ts +2 -2
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/wire_shapes.d.ts +22 -0
- package/dist/testing/cross_backend/wire_shapes.d.ts.map +1 -0
- package/dist/testing/cross_backend/wire_shapes.js +41 -0
- package/dist/testing/data_exposure.d.ts +0 -3
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +0 -1
- package/dist/testing/db.d.ts +44 -7
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +62 -27
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +15 -2
- package/dist/testing/round_trip.d.ts +0 -3
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +0 -4
- package/dist/testing/rpc_attack_surface.js +2 -2
- package/dist/testing/rpc_round_trip.d.ts +0 -3
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +0 -1
- package/dist/testing/schema_generators.d.ts +15 -0
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +45 -11
- package/dist/testing/standard.d.ts +5 -1
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +2 -7
- package/dist/timestamp.d.ts +58 -0
- package/dist/timestamp.d.ts.map +1 -0
- package/dist/timestamp.js +57 -0
- package/dist/ui/RoleGrantOfferHistory.svelte +3 -1
- package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.js +6 -2
- package/package.json +1 -1
|
@@ -7,6 +7,14 @@ export interface AdversarialTestOptions {
|
|
|
7
7
|
build: () => AppSurfaceSpec;
|
|
8
8
|
/** All roles in the app (e.g. `['admin', 'keeper']`). */
|
|
9
9
|
roles: Array<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Routes to skip, in `'METHOD /path'` form (the surface key) — the escape
|
|
12
|
+
* hatch every sibling suite carries. Reach for it only when a route
|
|
13
|
+
* cannot be driven generically at all (a handler needing real seeded
|
|
14
|
+
* state, a path segment no schema can describe); a route whose params
|
|
15
|
+
* merely have a format is handled by the synthesizer.
|
|
16
|
+
*/
|
|
17
|
+
skip_routes?: Array<string>;
|
|
10
18
|
}
|
|
11
19
|
/**
|
|
12
20
|
* Generate adversarial HTTP auth enforcement test suites.
|
|
@@ -63,6 +71,14 @@ export interface StandardAttackSurfaceOptions {
|
|
|
63
71
|
* to skip the assertion and keep the audit log informational-only.
|
|
64
72
|
*/
|
|
65
73
|
error_schema_tightness?: ErrorSchemaTightnessOptions | null;
|
|
74
|
+
/**
|
|
75
|
+
* Routes the three adversarial sub-suites skip, in `'METHOD /path'` form
|
|
76
|
+
* — the escape hatch every sibling suite carries (`round_trip`,
|
|
77
|
+
* `data_exposure`). Reach for it only when a route cannot be driven
|
|
78
|
+
* generically at all; a route whose path params merely carry a format is
|
|
79
|
+
* handled by the params-schema-aware synthesizer.
|
|
80
|
+
*/
|
|
81
|
+
skip_routes?: Array<string>;
|
|
66
82
|
}
|
|
67
83
|
/**
|
|
68
84
|
* Run the standard attack surface test suite.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAQN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAQN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAqBjC,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAoDpF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IAsI3E,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uCAAuC,GACnD,UAAU,2BAA2B,GAAG,IAAI,GAAG,SAAS,KACtD,2BAA2B,GAAG,IAWhC,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,gHAAgH;IAChH,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C;;;;;;;;;;;OAWG;IACH,sBAAsB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC5D;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,SAAS,4BAA4B,KACnC,IA4EF,CAAC"}
|
|
@@ -18,7 +18,8 @@ import { test, assert, describe } from 'vitest';
|
|
|
18
18
|
import { assert_surface_invariants, assert_rpc_ws_surface_invariants, assert_surface_security_policy, audit_error_schema_tightness, assert_error_schema_tightness, default_error_schema_tightness, fuz_app_stock_route_tightness_allowlist } from './surface_invariants.js';
|
|
19
19
|
import { describe_adversarial_input } from './adversarial_input.js';
|
|
20
20
|
import { describe_adversarial_404 } from './adversarial_404.js';
|
|
21
|
-
import { create_test_app_from_specs, create_test_request_context, create_auth_test_apps,
|
|
21
|
+
import { create_test_app_from_specs, create_test_request_context, create_auth_test_apps, create_route_skip_filter, select_auth_app } from './auth_apps.js';
|
|
22
|
+
import { resolve_valid_path } from './schema_generators.js';
|
|
22
23
|
import { assert_surface_matches_snapshot, assert_surface_deterministic, assert_only_expected_public_routes, assert_full_middleware_stack, assert_error_schema_valid } from './assertions.js';
|
|
23
24
|
import { merge_error_schemas } from '../http/schema_helpers.js';
|
|
24
25
|
import { collect_middleware_errors } from '../http/surface.js';
|
|
@@ -45,6 +46,19 @@ const build_error_schema_lookup = (route_specs, middleware_specs) => {
|
|
|
45
46
|
}
|
|
46
47
|
return lookup;
|
|
47
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* Build a lookup from `"METHOD /path"` to the route's own `RouteSpec` — the
|
|
51
|
+
* companion of `build_error_schema_lookup`, so a generated request can reach
|
|
52
|
+
* the route's `params` schema and synthesize path segments the route will
|
|
53
|
+
* actually accept.
|
|
54
|
+
*/
|
|
55
|
+
const build_route_spec_lookup = (route_specs) => {
|
|
56
|
+
const lookup = new Map();
|
|
57
|
+
for (const spec of route_specs) {
|
|
58
|
+
lookup.set(`${spec.method} ${spec.path}`, spec);
|
|
59
|
+
}
|
|
60
|
+
return lookup;
|
|
61
|
+
};
|
|
48
62
|
/**
|
|
49
63
|
* Generate adversarial HTTP auth enforcement test suites.
|
|
50
64
|
*
|
|
@@ -57,19 +71,26 @@ const build_error_schema_lookup = (route_specs, middleware_specs) => {
|
|
|
57
71
|
export const describe_adversarial_auth = (options) => {
|
|
58
72
|
const { build, roles } = options;
|
|
59
73
|
const { surface, route_specs, middleware_specs } = build();
|
|
60
|
-
const
|
|
74
|
+
const is_skipped = create_route_skip_filter(options.skip_routes);
|
|
75
|
+
const protected_routes = filter_protected_routes(surface).filter((r) => !is_skipped(r));
|
|
61
76
|
if (protected_routes.length === 0)
|
|
62
77
|
return;
|
|
63
|
-
const role_routes = filter_role_routes(surface);
|
|
64
|
-
const keeper_routes = filter_keeper_routes(surface);
|
|
78
|
+
const role_routes = filter_role_routes(surface).filter((r) => !is_skipped(r));
|
|
79
|
+
const keeper_routes = filter_keeper_routes(surface).filter((r) => !is_skipped(r));
|
|
65
80
|
// merged error schemas (auto-derived + middleware + handler-specific) for response validation
|
|
66
81
|
const error_schema_lookup = build_error_schema_lookup(route_specs, middleware_specs);
|
|
82
|
+
// The route's own spec, so a path param with a declared format (a
|
|
83
|
+
// `blake3:…` fact hash, a `tok_…` id) is synthesized into a value params
|
|
84
|
+
// validation accepts. Otherwise the generated request 400s on the path
|
|
85
|
+
// before ever reaching the 401 / 403 gate the case is asserting.
|
|
86
|
+
const spec_lookup = build_route_spec_lookup(route_specs);
|
|
87
|
+
const test_path = (route) => resolve_valid_path(route.path, spec_lookup.get(`${route.method} ${route.path}`)?.params);
|
|
67
88
|
const apps = create_auth_test_apps(route_specs, roles);
|
|
68
89
|
describe('adversarial HTTP auth enforcement', () => {
|
|
69
90
|
describe('unauthenticated → 401', () => {
|
|
70
91
|
for (const route of protected_routes) {
|
|
71
92
|
test(`${route.method} ${route.path}`, async () => {
|
|
72
|
-
const res = await apps.public.request(
|
|
93
|
+
const res = await apps.public.request(test_path(route), {
|
|
73
94
|
method: route.method
|
|
74
95
|
});
|
|
75
96
|
assert.strictEqual(res.status, 401, `${route.method} ${route.path}`);
|
|
@@ -93,7 +114,7 @@ export const describe_adversarial_auth = (options) => {
|
|
|
93
114
|
const app = apps.by_role.get(wrong_role);
|
|
94
115
|
if (!app)
|
|
95
116
|
throw new Error(`No test app for role '${wrong_role}'`);
|
|
96
|
-
const res = await app.request(
|
|
117
|
+
const res = await app.request(test_path(route), {
|
|
97
118
|
method: route.method
|
|
98
119
|
});
|
|
99
120
|
assert.strictEqual(res.status, 403, `${route.method} ${route.path}`);
|
|
@@ -108,7 +129,7 @@ export const describe_adversarial_auth = (options) => {
|
|
|
108
129
|
describe('authenticated without role → 403', () => {
|
|
109
130
|
for (const route of role_only_routes) {
|
|
110
131
|
test(`${route.method} ${route.path}`, async () => {
|
|
111
|
-
const res = await apps.authed.request(
|
|
132
|
+
const res = await apps.authed.request(test_path(route), {
|
|
112
133
|
method: route.method
|
|
113
134
|
});
|
|
114
135
|
assert.strictEqual(res.status, 403, `${route.method} ${route.path}`);
|
|
@@ -126,7 +147,7 @@ export const describe_adversarial_auth = (options) => {
|
|
|
126
147
|
const app_session_keeper = create_test_app_from_specs(route_specs, create_test_request_context('keeper'), 'session');
|
|
127
148
|
for (const route of keeper_routes) {
|
|
128
149
|
test(`${route.method} ${route.path}`, async () => {
|
|
129
|
-
const res = await app_session_keeper.request(
|
|
150
|
+
const res = await app_session_keeper.request(test_path(route), {
|
|
130
151
|
method: route.method
|
|
131
152
|
});
|
|
132
153
|
assert.strictEqual(res.status, 403, `${route.method} ${route.path}`);
|
|
@@ -141,7 +162,9 @@ export const describe_adversarial_auth = (options) => {
|
|
|
141
162
|
describe('correct auth passes guard', () => {
|
|
142
163
|
for (const route of protected_routes) {
|
|
143
164
|
test(`${route.method} ${route.path}`, async () => {
|
|
144
|
-
const res = await select_auth_app(apps, route.auth).request(
|
|
165
|
+
const res = await select_auth_app(apps, route.auth).request(test_path(route), {
|
|
166
|
+
method: route.method
|
|
167
|
+
});
|
|
145
168
|
// handler may error (500) or return 404 (stub deps) — that's fine, we only verify auth passed
|
|
146
169
|
assert.notStrictEqual(res.status, 401, 'should not be 401 (auth rejected)');
|
|
147
170
|
assert.notStrictEqual(res.status, 403, 'should not be 403 (role rejected)');
|
|
@@ -210,7 +233,7 @@ export const resolve_standard_error_schema_tightness = (consumer) => {
|
|
|
210
233
|
* any project-specific assertions in additional `describe` blocks.
|
|
211
234
|
*/
|
|
212
235
|
export const describe_standard_attack_surface_tests = (options) => {
|
|
213
|
-
const { build, snapshot_path, expected_public_routes, expected_api_middleware, roles, api_path_prefix = '/api/', security_policy } = options;
|
|
236
|
+
const { build, snapshot_path, expected_public_routes, expected_api_middleware, roles, api_path_prefix = '/api/', security_policy, skip_routes } = options;
|
|
214
237
|
const error_schema_tightness = resolve_standard_error_schema_tightness(options.error_schema_tightness);
|
|
215
238
|
const built = build();
|
|
216
239
|
const { surface } = built;
|
|
@@ -254,7 +277,7 @@ export const describe_standard_attack_surface_tests = (options) => {
|
|
|
254
277
|
}
|
|
255
278
|
});
|
|
256
279
|
});
|
|
257
|
-
describe_adversarial_auth({ build: () => built, roles });
|
|
258
|
-
describe_adversarial_input({ build: () => built, roles });
|
|
259
|
-
describe_adversarial_404({ build: () => built, roles });
|
|
280
|
+
describe_adversarial_auth({ build: () => built, roles, skip_routes });
|
|
281
|
+
describe_adversarial_input({ build: () => built, roles, skip_routes });
|
|
282
|
+
describe_adversarial_404({ build: () => built, roles, skip_routes });
|
|
260
283
|
};
|
|
@@ -2,7 +2,6 @@ import './assert_dev_env.js';
|
|
|
2
2
|
import type { SessionOptions } from '../auth/session_cookie.js';
|
|
3
3
|
import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
4
4
|
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
5
|
-
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
6
5
|
import type { SetupTest } from './cross_backend/setup.js';
|
|
7
6
|
/**
|
|
8
7
|
* Configuration for `describe_audit_completeness_tests`.
|
|
@@ -23,8 +22,6 @@ export interface AuditCompletenessTestOptions {
|
|
|
23
22
|
* options uniformity.
|
|
24
23
|
*/
|
|
25
24
|
surface_source: AppSurfaceSpec;
|
|
26
|
-
/** Backend capability declarations. */
|
|
27
|
-
capabilities: BackendCapabilities;
|
|
28
25
|
/** Session config — needed for factory-form rpc_endpoints resolution. */
|
|
29
26
|
session_options: SessionOptions<string>;
|
|
30
27
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AA2B1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,0BAA0B,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AAgGD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAgnBzF,CAAC"}
|
|
@@ -111,7 +111,6 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
111
111
|
// confusing test failure when `rpc_endpoints` is missing.
|
|
112
112
|
const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
|
|
113
113
|
const rpc_path = require_rpc_endpoint_path(rpc_endpoints_for_setup);
|
|
114
|
-
void options.capabilities;
|
|
115
114
|
describe('audit_log_completeness', () => {
|
|
116
115
|
// --- Account routes ---
|
|
117
116
|
describe('account mutation audit events', () => {
|
|
@@ -46,6 +46,13 @@ export declare const create_auth_test_apps: (route_specs: Array<RouteSpec>, role
|
|
|
46
46
|
* `create_auth_test_apps`.
|
|
47
47
|
*/
|
|
48
48
|
export declare const select_auth_app: (apps: AuthTestApps, auth: RouteAuth) => Hono;
|
|
49
|
-
/**
|
|
50
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Build the skip predicate an adversarial suite applies from its
|
|
51
|
+
* `skip_routes` option — routes named in `'METHOD /path'` form, the surface
|
|
52
|
+
* key. `undefined` skips nothing.
|
|
53
|
+
*/
|
|
54
|
+
export declare const create_route_skip_filter: (skip_routes: Array<string> | undefined) => ((route: {
|
|
55
|
+
method: string;
|
|
56
|
+
path: string;
|
|
57
|
+
}) => boolean);
|
|
51
58
|
//# sourceMappingURL=auth_apps.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth_apps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/auth_apps.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,oBAAoB,CAAC;AAI5B;;GAEG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,MAAM,KAAG,cAI1D,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACtC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,WAAW,cAAc,EACzB,kBAAkB,cAAc,KAC9B,IAuBF,CAAC;AAEF,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,OAAO,KAAK,CAAC,MAAM,CAAC,KAClB,YAeF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,YAAY,EAAE,MAAM,SAAS,KAAG,IAarE,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"auth_apps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/auth_apps.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,oBAAoB,CAAC;AAI5B;;GAEG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,MAAM,KAAG,cAI1D,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACtC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,WAAW,cAAc,EACzB,kBAAkB,cAAc,KAC9B,IAuBF,CAAC;AAEF,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,OAAO,KAAK,CAAC,MAAM,CAAC,KAClB,YAeF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,YAAY,EAAE,MAAM,SAAS,KAAG,IAarE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACpC,aAAa,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,KACpC,CAAC,CAAC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAGvD,CAAC"}
|
|
@@ -90,5 +90,12 @@ export const select_auth_app = (apps, auth) => {
|
|
|
90
90
|
}
|
|
91
91
|
return apps.authed;
|
|
92
92
|
};
|
|
93
|
-
/**
|
|
94
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Build the skip predicate an adversarial suite applies from its
|
|
95
|
+
* `skip_routes` option — routes named in `'METHOD /path'` form, the surface
|
|
96
|
+
* key. `undefined` skips nothing.
|
|
97
|
+
*/
|
|
98
|
+
export const create_route_skip_filter = (skip_routes) => {
|
|
99
|
+
const skip_set = new Set(skip_routes);
|
|
100
|
+
return (route) => skip_set.has(`${route.method} ${route.path}`);
|
|
101
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { RpcPathCapabilityGatedCrossSuiteOptions } from './setup.js';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the account-lifecycle parity suite. The standard
|
|
5
|
-
* RPC-dispatched cross-suite shape (`setup_test` /
|
|
6
|
-
* `rpc_path`); aliases
|
|
5
|
+
* capability-gating RPC-dispatched cross-suite shape (`setup_test` /
|
|
6
|
+
* `capabilities` / `rpc_path`); aliases
|
|
7
|
+
* `RpcPathCapabilityGatedCrossSuiteOptions` rather than duplicating.
|
|
7
8
|
*/
|
|
8
|
-
export type AccountLifecycleCrossTestOptions =
|
|
9
|
+
export type AccountLifecycleCrossTestOptions = RpcPathCapabilityGatedCrossSuiteOptions;
|
|
9
10
|
export declare const describe_account_lifecycle_cross_tests: (options: AccountLifecycleCrossTestOptions) => void;
|
|
10
11
|
//# sourceMappingURL=account_lifecycle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account_lifecycle.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/account_lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"account_lifecycle.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/account_lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAsC9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAG1E;;;;;GAKG;AACH,MAAM,MAAM,gCAAgC,GAAG,uCAAuC,CAAC;AAEvF,eAAO,MAAM,sCAAsC,GAClD,SAAS,gCAAgC,KACvC,IAqUF,CAAC"}
|
|
@@ -22,6 +22,7 @@ import { AccountDeleteOutput, AccountUndeleteOutput, AccountPurgeOutput, AdminAc
|
|
|
22
22
|
import { ERROR_ACCOUNT_NOT_FOUND, ERROR_AUTHENTICATION_REQUIRED } from '../../http/error_schemas.js';
|
|
23
23
|
import { test_if } from './capabilities.js';
|
|
24
24
|
import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
|
|
25
|
+
import { assert_iso8601_seconds } from './wire_shapes.js';
|
|
25
26
|
import { SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
26
27
|
export const describe_account_lifecycle_cross_tests = (options) => {
|
|
27
28
|
const { setup_test, capabilities } = options;
|
|
@@ -99,6 +100,9 @@ export const describe_account_lifecycle_cross_tests = (options) => {
|
|
|
99
100
|
const failure = listed.events.find((e) => e.outcome === 'failure' &&
|
|
100
101
|
e.metadata?.reason === ERROR_CANNOT_DELETE_KEEPER);
|
|
101
102
|
assert.ok(failure, 'keeper-removal guard must emit an account_delete outcome=failure audit row with reason cannot_delete_keeper');
|
|
103
|
+
// Byte-shape gate on the audit row's timestamp — `z.string()` in
|
|
104
|
+
// `AuditLogEventWithUsernamesJson` accepts a millisecond stamp too.
|
|
105
|
+
assert_iso8601_seconds(failure.created_at, 'audit_log_list.created_at');
|
|
102
106
|
});
|
|
103
107
|
test_if(capabilities.account_lifecycle, 'deterministic: double-undelete → second call is not_found', async () => {
|
|
104
108
|
const fixture = await setup_test();
|
|
@@ -139,6 +143,9 @@ export const describe_account_lifecycle_cross_tests = (options) => {
|
|
|
139
143
|
const row = with_deleted.accounts.find((a) => a.account.id === victim.account.id);
|
|
140
144
|
assert.ok(row, 'include_deleted surfaces the tombstoned row');
|
|
141
145
|
assert.ok(row.account.deleted_at !== null, 'tombstoned row carries a non-null deleted_at on both spines');
|
|
146
|
+
assert_iso8601_seconds(row.account.created_at, 'admin_account_list.created_at');
|
|
147
|
+
assert_iso8601_seconds(row.account.updated_at, 'admin_account_list.updated_at');
|
|
148
|
+
assert_iso8601_seconds(row.account.deleted_at, 'admin_account_list.deleted_at');
|
|
142
149
|
});
|
|
143
150
|
});
|
|
144
151
|
};
|
|
@@ -2,7 +2,7 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Cross-impl RPC **action-manifest** introspection — a normalized,
|
|
4
4
|
* JSON-serializable dump of a backend's live RPC method set, one entry per
|
|
5
|
-
* method carrying its auth shape
|
|
5
|
+
* method carrying its auth shape, side-effect flag, and rate-limit class.
|
|
6
6
|
*
|
|
7
7
|
* The sibling of `schema_introspect.ts`'s `SchemaSnapshot`: where that
|
|
8
8
|
* captures the live *database* shape for the schema-parity gate, this
|
|
@@ -35,13 +35,16 @@ import '../assert_dev_env.js';
|
|
|
35
35
|
*/
|
|
36
36
|
import { z } from 'zod';
|
|
37
37
|
import { type RouteAuth } from '../../http/auth_shape.js';
|
|
38
|
+
import { RateLimitKey } from '../../http/error_schemas.js';
|
|
38
39
|
import type { RequestResponseActionSpec } from '../../actions/action_spec.js';
|
|
39
40
|
/**
|
|
40
41
|
* One method's normalized RPC metadata — the cross-impl-comparable unit.
|
|
41
42
|
* `roles` / `credential_types` are always present + sorted (an absent gate
|
|
42
43
|
* and an empty list both serialize to `[]`) so the diff never trips on a
|
|
43
44
|
* `undefined`-vs-`[]` or declaration-order difference between impls; the
|
|
44
|
-
* auth axes reuse the canonical `AuthAxisState` enum.
|
|
45
|
+
* auth axes reuse the canonical `AuthAxisState` enum. `rate_limit` is
|
|
46
|
+
* nullable rather than optional for the same reason — an unthrottled action
|
|
47
|
+
* serializes an explicit `null`, so a missing key is drift, not a default.
|
|
45
48
|
*/
|
|
46
49
|
export declare const ActionManifestEntry: z.ZodObject<{
|
|
47
50
|
/** RPC method name (`spec.method`). */
|
|
@@ -64,6 +67,12 @@ export declare const ActionManifestEntry: z.ZodObject<{
|
|
|
64
67
|
roles: z.ZodArray<z.ZodString>;
|
|
65
68
|
/** Permitted credential channels, sorted; `[]` when ungated (any credential). */
|
|
66
69
|
credential_types: z.ZodArray<z.ZodString>;
|
|
70
|
+
/** Per-action rate-limit class, or `null` when the action declares none. */
|
|
71
|
+
rate_limit: z.ZodNullable<z.ZodEnum<{
|
|
72
|
+
account: "account";
|
|
73
|
+
ip: "ip";
|
|
74
|
+
both: "both";
|
|
75
|
+
}>>;
|
|
67
76
|
}, z.core.$strict>;
|
|
68
77
|
export type ActionManifestEntry = z.infer<typeof ActionManifestEntry>;
|
|
69
78
|
/** The full action manifest — every entry, sorted by `method`. */
|
|
@@ -89,6 +98,12 @@ export declare const ActionManifest: z.ZodObject<{
|
|
|
89
98
|
roles: z.ZodArray<z.ZodString>;
|
|
90
99
|
/** Permitted credential channels, sorted; `[]` when ungated (any credential). */
|
|
91
100
|
credential_types: z.ZodArray<z.ZodString>;
|
|
101
|
+
/** Per-action rate-limit class, or `null` when the action declares none. */
|
|
102
|
+
rate_limit: z.ZodNullable<z.ZodEnum<{
|
|
103
|
+
account: "account";
|
|
104
|
+
ip: "ip";
|
|
105
|
+
both: "both";
|
|
106
|
+
}>>;
|
|
92
107
|
}, z.core.$strict>>;
|
|
93
108
|
}, z.core.$strict>;
|
|
94
109
|
export type ActionManifest = z.infer<typeof ActionManifest>;
|
|
@@ -101,6 +116,7 @@ export declare const action_manifest_entry: (spec: {
|
|
|
101
116
|
readonly method: string;
|
|
102
117
|
readonly auth: RouteAuth;
|
|
103
118
|
readonly side_effects: boolean;
|
|
119
|
+
readonly rate_limit?: RateLimitKey;
|
|
104
120
|
}) => ActionManifestEntry;
|
|
105
121
|
/**
|
|
106
122
|
* Build the normalized `ActionManifest` from a list of request-response
|
|
@@ -109,5 +125,5 @@ export declare const action_manifest_entry: (spec: {
|
|
|
109
125
|
* it passes (the TS spine passes its full mount; the Rust stub filters
|
|
110
126
|
* `PROTOCOL_ACTION_SPECS` first) — see the module doc.
|
|
111
127
|
*/
|
|
112
|
-
export declare const build_action_manifest: (specs: ReadonlyArray<Pick<RequestResponseActionSpec, "method" | "auth" | "side_effects">>) => ActionManifest;
|
|
128
|
+
export declare const build_action_manifest: (specs: ReadonlyArray<Pick<RequestResponseActionSpec, "method" | "auth" | "side_effects" | "rate_limit">>) => ActionManifest;
|
|
113
129
|
//# sourceMappingURL=action_manifest.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_manifest.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/action_manifest.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAE9E
|
|
1
|
+
{"version":3,"file":"action_manifest.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/action_manifest.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAE9E;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB;IAC/B,uCAAuC;;IAEvC,iGAAiG;;IAEjG,+BAA+B;;;;;;IAE/B,6BAA6B;;;;;;IAE7B,mFAAmF;;IAEnF,iFAAiF;;IAEjF,4EAA4E;;;;;;kBAE3E,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,kEAAkE;AAClE,eAAO,MAAM,cAAc;;QAlB1B,uCAAuC;;QAEvC,iGAAiG;;QAEjG,+BAA+B;;;;;;QAE/B,6BAA6B;;;;;;QAE7B,mFAAmF;;QAEnF,iFAAiF;;QAEjF,4EAA4E;;;;;;;kBAQ3E,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAM5D;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC;CACnC,KAAG,mBAQF,CAAC;AAMH;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GACjC,OAAO,aAAa,CACnB,IAAI,CAAC,yBAAyB,EAAE,QAAQ,GAAG,MAAM,GAAG,cAAc,GAAG,YAAY,CAAC,CAClF,KACC,cAED,CAAC"}
|
|
@@ -2,7 +2,7 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Cross-impl RPC **action-manifest** introspection — a normalized,
|
|
4
4
|
* JSON-serializable dump of a backend's live RPC method set, one entry per
|
|
5
|
-
* method carrying its auth shape
|
|
5
|
+
* method carrying its auth shape, side-effect flag, and rate-limit class.
|
|
6
6
|
*
|
|
7
7
|
* The sibling of `schema_introspect.ts`'s `SchemaSnapshot`: where that
|
|
8
8
|
* captures the live *database* shape for the schema-parity gate, this
|
|
@@ -35,12 +35,15 @@ import '../assert_dev_env.js';
|
|
|
35
35
|
*/
|
|
36
36
|
import { z } from 'zod';
|
|
37
37
|
import { AuthAxisState } from '../../http/auth_shape.js';
|
|
38
|
+
import { RateLimitKey } from '../../http/error_schemas.js';
|
|
38
39
|
/**
|
|
39
40
|
* One method's normalized RPC metadata — the cross-impl-comparable unit.
|
|
40
41
|
* `roles` / `credential_types` are always present + sorted (an absent gate
|
|
41
42
|
* and an empty list both serialize to `[]`) so the diff never trips on a
|
|
42
43
|
* `undefined`-vs-`[]` or declaration-order difference between impls; the
|
|
43
|
-
* auth axes reuse the canonical `AuthAxisState` enum.
|
|
44
|
+
* auth axes reuse the canonical `AuthAxisState` enum. `rate_limit` is
|
|
45
|
+
* nullable rather than optional for the same reason — an unthrottled action
|
|
46
|
+
* serializes an explicit `null`, so a missing key is drift, not a default.
|
|
44
47
|
*/
|
|
45
48
|
export const ActionManifestEntry = z.strictObject({
|
|
46
49
|
/** RPC method name (`spec.method`). */
|
|
@@ -54,7 +57,9 @@ export const ActionManifestEntry = z.strictObject({
|
|
|
54
57
|
/** Required roles (any-of), sorted; `[]` when the action declares no role gate. */
|
|
55
58
|
roles: z.array(z.string()),
|
|
56
59
|
/** Permitted credential channels, sorted; `[]` when ungated (any credential). */
|
|
57
|
-
credential_types: z.array(z.string())
|
|
60
|
+
credential_types: z.array(z.string()),
|
|
61
|
+
/** Per-action rate-limit class, or `null` when the action declares none. */
|
|
62
|
+
rate_limit: RateLimitKey.nullable()
|
|
58
63
|
});
|
|
59
64
|
/** The full action manifest — every entry, sorted by `method`. */
|
|
60
65
|
export const ActionManifest = z.strictObject({
|
|
@@ -73,7 +78,8 @@ export const action_manifest_entry = (spec) => ({
|
|
|
73
78
|
account: spec.auth.account,
|
|
74
79
|
actor: spec.auth.actor,
|
|
75
80
|
roles: sorted_strings(spec.auth.roles),
|
|
76
|
-
credential_types: sorted_strings(spec.auth.credential_types)
|
|
81
|
+
credential_types: sorted_strings(spec.auth.credential_types),
|
|
82
|
+
rate_limit: spec.rate_limit ?? null
|
|
77
83
|
});
|
|
78
84
|
/** Deterministic byte-lexicographic method order — matches the Rust `str::cmp` the stub sorts by. */
|
|
79
85
|
const compare_method = (a, b) => a.method < b.method ? -1 : a.method > b.method ? 1 : 0;
|
|
@@ -7,8 +7,8 @@ import '../assert_dev_env.js';
|
|
|
7
7
|
* fuz_app spine and the Rust `testing_spine_stub`) are each other's parity
|
|
8
8
|
* reference. After both bootstrap, dump each one's live RPC method set, diff,
|
|
9
9
|
* fail loudly on drift. The diff entries name the specific divergence (a
|
|
10
|
-
* method only one impl mounts, a per-method auth-axis
|
|
11
|
-
* mismatch) so the error message points at the source.
|
|
10
|
+
* method only one impl mounts, a per-method auth-axis, side-effect, or
|
|
11
|
+
* rate-limit-class mismatch) so the error message points at the source.
|
|
12
12
|
*
|
|
13
13
|
* ```ts
|
|
14
14
|
* const manifest_a = await capture_action_manifest(ts_handle);
|
|
@@ -17,13 +17,14 @@ import '../assert_dev_env.js';
|
|
|
17
17
|
* ```
|
|
18
18
|
*
|
|
19
19
|
* Non-coverage: the manifest captures `{method, side_effects, account, actor,
|
|
20
|
-
* roles, credential_types}` — the wire-relevant auth shape. It does **not**
|
|
20
|
+
* roles, credential_types, rate_limit}` — the wire-relevant auth shape. It does **not**
|
|
21
21
|
* capture input/output schemas (the declared-surface wire shape is gated by
|
|
22
22
|
* `rpc_round_trip`) nor the protocol actions `heartbeat` / `cancel` (excluded
|
|
23
23
|
* by construction — see `action_manifest.ts`).
|
|
24
24
|
*
|
|
25
25
|
* @module
|
|
26
26
|
*/
|
|
27
|
+
import type { RateLimitKey } from '../../http/error_schemas.js';
|
|
27
28
|
import type { ActionManifest } from './action_manifest.js';
|
|
28
29
|
/** Structured drift entry. `where` is the named source impl ('a' or 'b'). */
|
|
29
30
|
export type ActionManifestDiff = {
|
|
@@ -35,6 +36,11 @@ export type ActionManifestDiff = {
|
|
|
35
36
|
readonly method: string;
|
|
36
37
|
readonly a: boolean;
|
|
37
38
|
readonly b: boolean;
|
|
39
|
+
} | {
|
|
40
|
+
readonly kind: 'rate_limit_differs';
|
|
41
|
+
readonly method: string;
|
|
42
|
+
readonly a: RateLimitKey | null;
|
|
43
|
+
readonly b: RateLimitKey | null;
|
|
38
44
|
} | {
|
|
39
45
|
readonly kind: 'auth_field_differs';
|
|
40
46
|
readonly method: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_manifest_parity.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/action_manifest_parity.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,sBAAsB,CAAC;AAWhF,6EAA6E;AAC7E,MAAM,MAAM,kBAAkB,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvF;IACA,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;CACnB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,kBAAkB,CAAC;IACnE,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAML;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,GAAG,cAAc,EACjB,GAAG,cAAc,KACf,KAAK,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"action_manifest_parity.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/action_manifest_parity.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,sBAAsB,CAAC;AAWhF,6EAA6E;AAC7E,MAAM,MAAM,kBAAkB,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvF;IACA,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;CACnB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAC/B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,kBAAkB,CAAC;IACnE,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAML;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,GAAG,cAAc,EACjB,GAAG,cAAc,KACf,KAAK,CAAC,kBAAkB,CAkC1B,CAAC;AAEF,qEAAqE;AACrE,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,OAAO,aAAa,CAAC,kBAAkB,CAAC,EACxC,SAAQ,wBAA6B,KACnC,MAqCF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,GAAG,cAAc,EACjB,GAAG,cAAc,EACjB,SAAQ,wBAA6B,KACnC,IAUF,CAAC"}
|
|
@@ -30,6 +30,9 @@ export const diff_action_manifests = (a, b) => {
|
|
|
30
30
|
if (ea.side_effects !== eb.side_effects) {
|
|
31
31
|
diffs.push({ kind: 'side_effects_differ', method, a: ea.side_effects, b: eb.side_effects });
|
|
32
32
|
}
|
|
33
|
+
if (ea.rate_limit !== eb.rate_limit) {
|
|
34
|
+
diffs.push({ kind: 'rate_limit_differs', method, a: ea.rate_limit, b: eb.rate_limit });
|
|
35
|
+
}
|
|
33
36
|
for (const field of AUTH_SCALAR_FIELDS) {
|
|
34
37
|
if (ea[field] !== eb[field]) {
|
|
35
38
|
diffs.push({ kind: 'auth_field_differs', method, field, a: ea[field], b: eb[field] });
|
|
@@ -62,6 +65,9 @@ export const format_action_manifest_diffs = (diffs, labels = {}) => {
|
|
|
62
65
|
case 'side_effects_differ':
|
|
63
66
|
lines.push(` ${d.method} side_effects differs: ${label_a}=${d.a}, ${label_b}=${d.b}`);
|
|
64
67
|
break;
|
|
68
|
+
case 'rate_limit_differs':
|
|
69
|
+
lines.push(` ${d.method} rate_limit differs: ${label_a}=${JSON.stringify(d.a)}, ${label_b}=${JSON.stringify(d.b)}`);
|
|
70
|
+
break;
|
|
65
71
|
case 'auth_field_differs':
|
|
66
72
|
lines.push(` ${d.method} auth.${d.field} differs: ${label_a}=${JSON.stringify(d.a)}, ${label_b}=${JSON.stringify(d.b)}`);
|
|
67
73
|
break;
|
|
@@ -2,8 +2,9 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
import type { RpcPathCrossSuiteOptions } from './setup.js';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the actor-lookup parity suite. The standard RPC-dispatched
|
|
5
|
-
* cross-suite shape (`setup_test` / `
|
|
6
|
-
*
|
|
5
|
+
* cross-suite shape (`setup_test` / `rpc_path`); aliases the shared
|
|
6
|
+
* `RpcPathCrossSuiteOptions` rather than minting a duplicate. No case here
|
|
7
|
+
* is capability-gated, so the flag bundle is not on the shape.
|
|
7
8
|
*/
|
|
8
9
|
export type ActorLookupCrossTestOptions = RpcPathCrossSuiteOptions;
|
|
9
10
|
export declare const describe_actor_lookup_cross_tests: (options: ActorLookupCrossTestOptions) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actor_lookup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/actor_lookup.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAqC9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D
|
|
1
|
+
{"version":3,"file":"actor_lookup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/actor_lookup.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAqC9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AASnE,eAAO,MAAM,iCAAiC,GAAI,SAAS,2BAA2B,KAAG,IAkDxF,CAAC"}
|
|
@@ -2,8 +2,10 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
import type { RpcPathCrossSuiteOptions } from './setup.js';
|
|
3
3
|
/**
|
|
4
4
|
* Options for the body-size parity suite — the standard RPC-dispatched
|
|
5
|
-
* cross-suite shape (`setup_test` / `
|
|
6
|
-
*
|
|
5
|
+
* cross-suite shape (`setup_test` / `rpc_path`); aliases the shared
|
|
6
|
+
* `RpcPathCrossSuiteOptions` rather than minting a duplicate. The limit is
|
|
7
|
+
* on every spine, so no case is capability-gated and the flag bundle is not
|
|
8
|
+
* on the shape.
|
|
7
9
|
*/
|
|
8
10
|
export type BodySizeCrossTestOptions = RpcPathCrossSuiteOptions;
|
|
9
11
|
export declare const describe_body_size_cross_tests: (options: BodySizeCrossTestOptions) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body_size.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/body_size.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6D9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D
|
|
1
|
+
{"version":3,"file":"body_size.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/body_size.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6D9B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAG3D;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,GAAG,wBAAwB,CAAC;AAmDhE,eAAO,MAAM,8BAA8B,GAAI,SAAS,wBAAwB,KAAG,IA0ClF,CAAC"}
|
|
@@ -39,7 +39,7 @@ import '../assert_dev_env.js';
|
|
|
39
39
|
* Like origin/payload rejection, this is middleware-level flat REST — not the
|
|
40
40
|
* JSON-RPC envelope the conformance-table runner expects — so it's an
|
|
41
41
|
* imperative suite, not a `conformance_table` row. Runs both legs via the
|
|
42
|
-
* shared `
|
|
42
|
+
* shared `setup_test` protocol: the in-process leg
|
|
43
43
|
* (`auth/body_size_parity.db.test.ts`, plain `gro test`) and the cross-process
|
|
44
44
|
* leg (`cross_backend/body_size.cross.test.ts`, the TS spine binaries + Rust
|
|
45
45
|
* `testing_spine_stub` over real HTTP). The body-size limit is on every spine,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
|
-
import type {
|
|
3
|
-
export declare const describe_cell_crud_cross_tests: (options:
|
|
2
|
+
import type { RpcPathCapabilityGatedCrossSuiteOptions } from './setup.js';
|
|
3
|
+
export declare const describe_cell_crud_cross_tests: (options: RpcPathCapabilityGatedCrossSuiteOptions) => void;
|
|
4
4
|
//# sourceMappingURL=cell_crud.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_crud.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_crud.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"cell_crud.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_crud.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA4D9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAG1E,eAAO,MAAM,8BAA8B,GAC1C,SAAS,uCAAuC,KAC9C,IA+cF,CAAC"}
|
|
@@ -48,6 +48,7 @@ import { describe, assert } from 'vitest';
|
|
|
48
48
|
import { CellCreateOutput, CellDeleteOutput, CellGetOutput, CellListOutput, CellUpdateOutput } from '../../auth/cell_action_specs.js';
|
|
49
49
|
import { test_if } from './capabilities.js';
|
|
50
50
|
import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
|
|
51
|
+
import { assert_iso8601_seconds, assert_iso8601_seconds_nullable } from './wire_shapes.js';
|
|
51
52
|
import { SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
52
53
|
export const describe_cell_crud_cross_tests = (options) => {
|
|
53
54
|
const { setup_test, capabilities } = options;
|
|
@@ -64,6 +65,12 @@ export const describe_cell_crud_cross_tests = (options) => {
|
|
|
64
65
|
assert.strictEqual(created.cell.created_by, owner.actor.id);
|
|
65
66
|
assert.strictEqual(created.cell.updated_by, null);
|
|
66
67
|
assert.strictEqual(created.cell.grant_count, 0);
|
|
68
|
+
// Byte-shape gate: `CellJson`'s `z.string()` accepts a millisecond
|
|
69
|
+
// stamp too, so `expect_output` alone can't tell the two spines
|
|
70
|
+
// apart on the timestamp columns.
|
|
71
|
+
assert_iso8601_seconds(created.cell.created_at, 'cell_create.created_at');
|
|
72
|
+
assert_iso8601_seconds_nullable(created.cell.updated_at, 'cell_create.updated_at');
|
|
73
|
+
assert_iso8601_seconds_nullable(created.cell.deleted_at, 'cell_create.deleted_at');
|
|
67
74
|
const cell_id = created.cell.id;
|
|
68
75
|
// get by id — full CellGetOutput envelope; relations empty in the first cut
|
|
69
76
|
const got = expect_output(await cross_rpc_call(t, rpc_path, 'cell_get', { id: cell_id }, owner_headers), CellGetOutput);
|
|
@@ -74,10 +81,13 @@ export const describe_cell_crud_cross_tests = (options) => {
|
|
|
74
81
|
assert.strictEqual(got.items_truncated, false);
|
|
75
82
|
assert.strictEqual(got.can_edit, true);
|
|
76
83
|
assert.strictEqual(got.can_grant, true); // owner is manage-tier
|
|
84
|
+
assert_iso8601_seconds(got.cell.created_at, 'cell_get.created_at');
|
|
85
|
+
assert_iso8601_seconds_nullable(got.cell.updated_at, 'cell_get.updated_at');
|
|
77
86
|
// update data + flip to public (owner is manage-tier, so visibility write is allowed)
|
|
78
87
|
const updated = expect_output(await cross_rpc_call(t, rpc_path, 'cell_update', { cell_id, data: { label: 'hi2' }, visibility: 'public' }, owner_headers), CellUpdateOutput);
|
|
79
88
|
assert.strictEqual(updated.cell.visibility, 'public');
|
|
80
89
|
assert.strictEqual(updated.cell.updated_by, owner.actor.id);
|
|
90
|
+
assert_iso8601_seconds(updated.cell.updated_at, 'cell_update.updated_at');
|
|
81
91
|
// list includes it
|
|
82
92
|
const listed = expect_output(await cross_rpc_call(t, rpc_path, 'cell_list', {}, owner_headers), CellListOutput);
|
|
83
93
|
assert.ok(listed.cells.some((c) => c.id === cell_id), 'owner cell_list omitted the cell');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
|
-
import type {
|
|
3
|
-
export declare const describe_cell_gated_create_cross_tests: (options:
|
|
2
|
+
import type { RpcPathCapabilityGatedCrossSuiteOptions } from './setup.js';
|
|
3
|
+
export declare const describe_cell_gated_create_cross_tests: (options: RpcPathCapabilityGatedCrossSuiteOptions) => void;
|
|
4
4
|
/**
|
|
5
5
|
* Cross-backend parity for the **`cell_moderate` verb** (the `pending →
|
|
6
6
|
* approved | rejected` transition) — root-authority-gated.
|
|
@@ -24,5 +24,5 @@ export declare const describe_cell_gated_create_cross_tests: (options: RpcPathCr
|
|
|
24
24
|
*
|
|
25
25
|
* `$lib`-free by contract (relative specifiers only).
|
|
26
26
|
*/
|
|
27
|
-
export declare const describe_cell_moderate_cross_tests: (options:
|
|
27
|
+
export declare const describe_cell_moderate_cross_tests: (options: RpcPathCapabilityGatedCrossSuiteOptions) => void;
|
|
28
28
|
//# sourceMappingURL=cell_gated_create.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_gated_create.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_gated_create.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiD9B,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"cell_gated_create.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/cell_gated_create.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiD9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAkB1E,eAAO,MAAM,sCAAsC,GAClD,SAAS,uCAAuC,KAC9C,IAwPF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,kCAAkC,GAC9C,SAAS,uCAAuC,KAC9C,IAwHF,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { RpcPathCapabilityGatedCrossSuiteOptions } from './setup.js';
|
|
3
3
|
/** App role the holder is seeded with; matches the spine's registered role. */
|
|
4
4
|
export declare const CELL_EDITOR_ROLE = "cell_editor";
|
|
5
5
|
/** Username the fixture seeds (via `extra_accounts`) holding `CELL_EDITOR_ROLE`. */
|
|
6
6
|
export declare const CELL_ROLE_HOLDER_USERNAME = "cell_role_holder";
|
|
7
|
-
export declare const describe_cell_grant_role_cross_tests: (options:
|
|
7
|
+
export declare const describe_cell_grant_role_cross_tests: (options: RpcPathCapabilityGatedCrossSuiteOptions) => void;
|
|
8
8
|
//# sourceMappingURL=cell_grant_role.d.ts.map
|