@fuzdev/fuz_app 0.139.0 → 0.141.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 +62 -17
- package/dist/actions/action_rpc.d.ts +5 -1
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +9 -1
- package/dist/actions/connection_closer.d.ts +25 -2
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +8 -1
- package/dist/actions/register_action_ws.d.ts +42 -6
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +87 -19
- package/dist/actions/register_ws_endpoint.d.ts +15 -6
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +5 -2
- package/dist/actions/socket.svelte.d.ts +19 -4
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +31 -4
- package/dist/actions/transports.d.ts +30 -1
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +30 -1
- package/dist/actions/transports_ws_auth_guard.d.ts +13 -6
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +24 -10
- package/dist/actions/transports_ws_backend.d.ts +51 -5
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +62 -9
- package/dist/actions/ws_endpoint_spec.d.ts +11 -10
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +11 -9
- package/dist/auth/account_route_schema.d.ts +1 -0
- package/dist/auth/account_route_schema.d.ts.map +1 -1
- package/dist/auth/account_route_schema.js +4 -0
- package/dist/auth/api_token.d.ts +20 -0
- package/dist/auth/api_token.d.ts.map +1 -1
- package/dist/auth/api_token.js +21 -1
- package/dist/auth/api_token_queries.d.ts +22 -4
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +11 -6
- package/dist/auth/audit_log_routes.d.ts +10 -0
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +20 -0
- package/dist/auth/audit_log_schema.d.ts +30 -5
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +30 -0
- package/dist/auth/bearer_auth.d.ts +4 -2
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +19 -8
- package/dist/auth/bootstrap_route_schema.d.ts +1 -0
- package/dist/auth/bootstrap_route_schema.d.ts.map +1 -1
- package/dist/auth/bootstrap_route_schema.js +1 -0
- package/dist/auth/cleanup.d.ts +9 -9
- package/dist/auth/cleanup.js +9 -9
- package/dist/auth/credential_deadline.d.ts +95 -0
- package/dist/auth/credential_deadline.d.ts.map +1 -0
- package/dist/auth/credential_deadline.js +139 -0
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +4 -1
- package/dist/auth/middleware.d.ts +90 -12
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +115 -16
- package/dist/auth/request_context.d.ts +4 -2
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +14 -3
- package/dist/auth/resolved_auth.d.ts +13 -2
- package/dist/auth/resolved_auth.d.ts.map +1 -1
- package/dist/auth/resolved_auth.js +5 -4
- package/dist/auth/session_cookie.d.ts +4 -0
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +3 -2
- package/dist/auth/session_middleware.d.ts +1 -1
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +4 -1
- package/dist/auth/session_queries.d.ts +19 -2
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +8 -2
- package/dist/auth/signup_route_schema.d.ts.map +1 -1
- package/dist/auth/signup_route_schema.js +1 -0
- package/dist/hono_context.d.ts +23 -0
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +19 -0
- package/dist/http/CLAUDE.md +41 -15
- package/dist/http/middleware_spec.d.ts +16 -0
- package/dist/http/middleware_spec.d.ts.map +1 -1
- package/dist/http/origin.d.ts +113 -18
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +247 -127
- package/dist/http/route_spec.d.ts +21 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/schema_helpers.d.ts +22 -0
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +24 -0
- package/dist/http/surface.d.ts +6 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +12 -5
- package/dist/realtime/sse_auth_guard.d.ts +22 -6
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +31 -12
- package/dist/realtime/subscriber_registry.d.ts +10 -1
- package/dist/realtime/subscriber_registry.d.ts.map +1 -1
- package/dist/realtime/subscriber_registry.js +10 -1
- package/dist/server/app_server.d.ts +20 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +8 -3
- package/dist/server/env.d.ts +2 -1
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +7 -17
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +1 -0
- package/dist/testing/CLAUDE.md +154 -22
- package/dist/testing/adversarial_headers.d.ts.map +1 -1
- package/dist/testing/adversarial_headers.js +13 -6
- package/dist/testing/connection_closer_helpers.d.ts +6 -1
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
- package/dist/testing/connection_closer_helpers.js +4 -0
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +14 -65
- package/dist/testing/cross_backend/capabilities.d.ts +26 -2
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +9 -3
- package/dist/testing/cross_backend/conformance_case.d.ts +11 -3
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_case.js +6 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +20 -19
- package/dist/testing/cross_backend/credential_deadline.d.ts +25 -0
- package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -0
- package/dist/testing/cross_backend/credential_deadline.js +185 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +13 -2
- package/dist/testing/cross_backend/full_spine_mount.d.ts +3 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +7 -1
- package/dist/testing/cross_backend/narrowed_api_token.d.ts +23 -0
- package/dist/testing/cross_backend/narrowed_api_token.d.ts.map +1 -0
- package/dist/testing/cross_backend/narrowed_api_token.js +40 -0
- package/dist/testing/cross_backend/origin.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin.js +6 -26
- package/dist/testing/cross_backend/origin_conformance.d.ts +152 -0
- package/dist/testing/cross_backend/origin_conformance.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin_conformance.js +136 -0
- package/dist/testing/cross_backend/origin_conformance.json +1679 -0
- package/dist/testing/cross_backend/origin_scope.d.ts +16 -0
- package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin_scope.js +140 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +35 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/role_grant_cross_helpers.js +64 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +8 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +44 -33
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +6 -4
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +19 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +22 -0
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +20 -7
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +180 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +154 -5
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_scope_surface.js +25 -21
- package/dist/testing/cross_backend/transport_composition.d.ts +60 -0
- package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -0
- package/dist/testing/cross_backend/transport_composition.js +364 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +21 -6
- package/dist/testing/cross_backend/ws_role_gated.d.ts +38 -0
- package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_role_gated.js +142 -0
- package/dist/testing/entities.d.ts +23 -0
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +37 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +3 -1
- package/dist/testing/middleware.d.ts +10 -0
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +31 -7
- package/dist/testing/stubs.d.ts +16 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +20 -7
- package/dist/testing/surface_invariants.js +2 -2
- package/package.json +12 -12
|
@@ -15,11 +15,17 @@ import '../assert_dev_env.js';
|
|
|
15
15
|
* of notifications onto the caller's own WebSocket connection, to push its
|
|
16
16
|
* outbound queue past its byte budget).
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
18
|
+
* Three further daemon-token actions live here but are **not** bundled by
|
|
19
|
+
* `create_testing_actions`, and are appended at the full-mount layer
|
|
20
|
+
* (`build_full_spine_rpc_actions`) instead: **`_testing_action_manifest`**
|
|
21
|
+
* (dump the live RPC registry for cross-impl method-set + auth-shape parity —
|
|
22
|
+
* it must enumerate *every* mounted method, so it goes where the complete list
|
|
23
|
+
* exists), **`_testing_origin_check`** (answer the origin allowlist's parse
|
|
24
|
+
* and request check for the cross-impl origin conformance table), and
|
|
25
|
+
* **`_testing_shorten_credential_expiry`** (pull an account's credential
|
|
26
|
+
* expiries in to seconds from now, shorten-only, for the credential-deadline
|
|
27
|
+
* cases) — the last two kept out of the bundle so a consumer whose Rust binary
|
|
28
|
+
* doesn't mount their mirrors keeps a green manifest gate.
|
|
23
29
|
*
|
|
24
30
|
* `_testing_reset` — full DB wipe + keeper re-seed + optional
|
|
25
31
|
* secondary-account seeding. The
|
|
@@ -466,6 +472,175 @@ export declare const testing_migration_tracker_action_spec: {
|
|
|
466
472
|
* `create_testing_actions`; mount directly for in-process use.
|
|
467
473
|
*/
|
|
468
474
|
export declare const create_testing_migration_tracker_action: () => RpcAction;
|
|
475
|
+
/** Input of `_testing_origin_check`. */
|
|
476
|
+
export declare const TestingOriginCheckInput: z.ZodObject<{
|
|
477
|
+
patterns: z.ZodString;
|
|
478
|
+
origin: z.ZodNullable<z.ZodString>;
|
|
479
|
+
}, z.core.$strict>;
|
|
480
|
+
export type TestingOriginCheckInput = z.infer<typeof TestingOriginCheckInput>;
|
|
481
|
+
/**
|
|
482
|
+
* Output of `_testing_origin_check`: the request-level answer when the list
|
|
483
|
+
* parses, else every refusal — `index` the 1-based entry position, absent for
|
|
484
|
+
* a list-level refusal.
|
|
485
|
+
*/
|
|
486
|
+
export declare const TestingOriginCheckOutput: z.ZodUnion<readonly [z.ZodObject<{
|
|
487
|
+
ok: z.ZodLiteral<true>;
|
|
488
|
+
allowed: z.ZodBoolean;
|
|
489
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
490
|
+
ok: z.ZodLiteral<false>;
|
|
491
|
+
errors: z.ZodArray<z.ZodObject<{
|
|
492
|
+
index: z.ZodOptional<z.ZodNumber>;
|
|
493
|
+
kind: z.ZodEnum<{
|
|
494
|
+
empty_entry: "empty_entry";
|
|
495
|
+
not_printable_ascii: "not_printable_ascii";
|
|
496
|
+
missing_scheme: "missing_scheme";
|
|
497
|
+
unsupported_scheme: "unsupported_scheme";
|
|
498
|
+
has_path: "has_path";
|
|
499
|
+
has_userinfo: "has_userinfo";
|
|
500
|
+
missing_host: "missing_host";
|
|
501
|
+
invalid_host_char: "invalid_host_char";
|
|
502
|
+
unbracketed_ipv6: "unbracketed_ipv6";
|
|
503
|
+
partial_wildcard: "partial_wildcard";
|
|
504
|
+
non_canonical_ipv4: "non_canonical_ipv4";
|
|
505
|
+
unclosed_ipv6: "unclosed_ipv6";
|
|
506
|
+
invalid_ipv6: "invalid_ipv6";
|
|
507
|
+
invalid_after_ipv6: "invalid_after_ipv6";
|
|
508
|
+
invalid_port: "invalid_port";
|
|
509
|
+
default_port: "default_port";
|
|
510
|
+
no_origins: "no_origins";
|
|
511
|
+
}>;
|
|
512
|
+
}, z.core.$strict>>;
|
|
513
|
+
}, z.core.$strict>]>;
|
|
514
|
+
export type TestingOriginCheckOutput = z.infer<typeof TestingOriginCheckOutput>;
|
|
515
|
+
/**
|
|
516
|
+
* Answer one `_testing_origin_check` input: parse `patterns` with
|
|
517
|
+
* `parse_allowed_origins`, then run the request-level check for one `Origin`
|
|
518
|
+
* carrying `origin` (`null` — no `Origin` — passes). The action handler and
|
|
519
|
+
* the conformance table's unit test both call this, so the table checks the
|
|
520
|
+
* same function the wire serves.
|
|
521
|
+
*
|
|
522
|
+
* @param input - the allowlist value and the `Origin` value
|
|
523
|
+
* @returns the answer the Rust twin returns for the same input
|
|
524
|
+
*/
|
|
525
|
+
export declare const run_testing_origin_check: (input: TestingOriginCheckInput) => TestingOriginCheckOutput;
|
|
526
|
+
/**
|
|
527
|
+
* `_testing_origin_check` — run the origin allowlist's parse and request
|
|
528
|
+
* check for one `{patterns, origin}` pair, so the shared conformance table
|
|
529
|
+
* (`origin_conformance.json`) checks each backend's grammar and matcher over
|
|
530
|
+
* the wire, row by row. Pure: no database, no state. The Rust mirror is
|
|
531
|
+
* `fuz_testing::create_testing_origin_check_action_spec`.
|
|
532
|
+
*
|
|
533
|
+
* Like `_testing_action_manifest`, it is **not** bundled by
|
|
534
|
+
* `create_testing_actions`: a consumer whose Rust binary doesn't mount the
|
|
535
|
+
* mirror would fail its action-manifest parity gate. The spine test binary
|
|
536
|
+
* appends it at the full-mount layer (`build_full_spine_rpc_actions`).
|
|
537
|
+
*
|
|
538
|
+
* `auth` gates on the daemon-token credential, matching `_testing_reset`.
|
|
539
|
+
*/
|
|
540
|
+
export declare const testing_origin_check_action_spec: {
|
|
541
|
+
readonly method: "_testing_origin_check";
|
|
542
|
+
readonly kind: "request_response";
|
|
543
|
+
readonly initiator: "frontend";
|
|
544
|
+
readonly auth: {
|
|
545
|
+
readonly account: "required";
|
|
546
|
+
readonly actor: "none";
|
|
547
|
+
readonly credential_types: readonly ["daemon_token"];
|
|
548
|
+
};
|
|
549
|
+
readonly side_effects: false;
|
|
550
|
+
readonly input: z.ZodObject<{
|
|
551
|
+
patterns: z.ZodString;
|
|
552
|
+
origin: z.ZodNullable<z.ZodString>;
|
|
553
|
+
}, z.core.$strict>;
|
|
554
|
+
readonly output: z.ZodUnion<readonly [z.ZodObject<{
|
|
555
|
+
ok: z.ZodLiteral<true>;
|
|
556
|
+
allowed: z.ZodBoolean;
|
|
557
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
558
|
+
ok: z.ZodLiteral<false>;
|
|
559
|
+
errors: z.ZodArray<z.ZodObject<{
|
|
560
|
+
index: z.ZodOptional<z.ZodNumber>;
|
|
561
|
+
kind: z.ZodEnum<{
|
|
562
|
+
empty_entry: "empty_entry";
|
|
563
|
+
not_printable_ascii: "not_printable_ascii";
|
|
564
|
+
missing_scheme: "missing_scheme";
|
|
565
|
+
unsupported_scheme: "unsupported_scheme";
|
|
566
|
+
has_path: "has_path";
|
|
567
|
+
has_userinfo: "has_userinfo";
|
|
568
|
+
missing_host: "missing_host";
|
|
569
|
+
invalid_host_char: "invalid_host_char";
|
|
570
|
+
unbracketed_ipv6: "unbracketed_ipv6";
|
|
571
|
+
partial_wildcard: "partial_wildcard";
|
|
572
|
+
non_canonical_ipv4: "non_canonical_ipv4";
|
|
573
|
+
unclosed_ipv6: "unclosed_ipv6";
|
|
574
|
+
invalid_ipv6: "invalid_ipv6";
|
|
575
|
+
invalid_after_ipv6: "invalid_after_ipv6";
|
|
576
|
+
invalid_port: "invalid_port";
|
|
577
|
+
default_port: "default_port";
|
|
578
|
+
no_origins: "no_origins";
|
|
579
|
+
}>;
|
|
580
|
+
}, z.core.$strict>>;
|
|
581
|
+
}, z.core.$strict>]>;
|
|
582
|
+
readonly async: true;
|
|
583
|
+
readonly description: string;
|
|
584
|
+
};
|
|
585
|
+
/** Build the standalone `_testing_origin_check` action (see the spec doc). */
|
|
586
|
+
export declare const create_testing_origin_check_action: () => RpcAction;
|
|
587
|
+
/** The shortest expiry `_testing_shorten_credential_expiry` sets, in seconds from now. */
|
|
588
|
+
export declare const TESTING_SHORTEN_CREDENTIAL_EXPIRY_MIN_SECONDS = 1;
|
|
589
|
+
/**
|
|
590
|
+
* The longest expiry `_testing_shorten_credential_expiry` sets, in seconds
|
|
591
|
+
* from now — a minute: the action exists to make a deadline arrive inside a
|
|
592
|
+
* test.
|
|
593
|
+
*/
|
|
594
|
+
export declare const TESTING_SHORTEN_CREDENTIAL_EXPIRY_MAX_SECONDS = 60;
|
|
595
|
+
/**
|
|
596
|
+
* `_testing_shorten_credential_expiry` — pull the expiry of an account's
|
|
597
|
+
* stored credentials (every `auth_session` and `api_token` row it owns) in to
|
|
598
|
+
* `seconds` from now, so the cross-backend credential-deadline suite can open
|
|
599
|
+
* a WebSocket or audit stream on a credential that expires a moment later and
|
|
600
|
+
* watch it close at the deadline (`ResolvedAuth.credential_deadline`) without
|
|
601
|
+
* waiting out a real lifetime.
|
|
602
|
+
*
|
|
603
|
+
* Applied **before the open**: it rewrites the stored rows, and a connection
|
|
604
|
+
* reads its deadline when its credential resolves, so only one opened
|
|
605
|
+
* afterwards sees the near deadline.
|
|
606
|
+
*
|
|
607
|
+
* **Shorten-only.** The new expiry is `LEAST(expires_at, NOW() + seconds)` —
|
|
608
|
+
* an eternal token gains one, and nothing already due sooner moves later, so
|
|
609
|
+
* the action can neither extend a credential's life nor revive an expired one;
|
|
610
|
+
* `seconds` is an integer from `TESTING_SHORTEN_CREDENTIAL_EXPIRY_MIN_SECONDS`
|
|
611
|
+
* to `TESTING_SHORTEN_CREDENTIAL_EXPIRY_MAX_SECONDS`. The Rust mirror is
|
|
612
|
+
* `fuz_testing::create_testing_shorten_credential_expiry_action_spec`.
|
|
613
|
+
*
|
|
614
|
+
* Like `_testing_origin_check`, it is **not** bundled by
|
|
615
|
+
* `create_testing_actions`: a consumer whose Rust binary doesn't mount the
|
|
616
|
+
* mirror would fail its action-manifest parity gate. The spine test binary
|
|
617
|
+
* appends it at the full-mount layer (`build_full_spine_rpc_actions`).
|
|
618
|
+
*
|
|
619
|
+
* `auth` gates on the daemon-token credential, matching `_testing_reset`.
|
|
620
|
+
*/
|
|
621
|
+
export declare const testing_shorten_credential_expiry_action_spec: {
|
|
622
|
+
readonly method: "_testing_shorten_credential_expiry";
|
|
623
|
+
readonly kind: "request_response";
|
|
624
|
+
readonly initiator: "frontend";
|
|
625
|
+
readonly auth: {
|
|
626
|
+
readonly account: "required";
|
|
627
|
+
readonly actor: "none";
|
|
628
|
+
readonly credential_types: readonly ["daemon_token"];
|
|
629
|
+
};
|
|
630
|
+
readonly side_effects: true;
|
|
631
|
+
readonly input: z.ZodObject<{
|
|
632
|
+
account_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
633
|
+
seconds: z.ZodNumber;
|
|
634
|
+
}, z.core.$strict>;
|
|
635
|
+
readonly output: z.ZodObject<{
|
|
636
|
+
sessions: z.ZodNumber;
|
|
637
|
+
api_tokens: z.ZodNumber;
|
|
638
|
+
}, z.core.$strict>;
|
|
639
|
+
readonly async: true;
|
|
640
|
+
readonly description: string;
|
|
641
|
+
};
|
|
642
|
+
/** Build the standalone `_testing_shorten_credential_expiry` action (see the spec doc). */
|
|
643
|
+
export declare const create_testing_shorten_credential_expiry_action: () => RpcAction;
|
|
469
644
|
/**
|
|
470
645
|
* `_testing_action_manifest` — dump the backend's live RPC method set as a
|
|
471
646
|
* normalized `ActionManifest` (one entry per method: `{method, side_effects,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B
|
|
1
|
+
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,11 +15,17 @@ import '../assert_dev_env.js';
|
|
|
15
15
|
* of notifications onto the caller's own WebSocket connection, to push its
|
|
16
16
|
* outbound queue past its byte budget).
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
18
|
+
* Three further daemon-token actions live here but are **not** bundled by
|
|
19
|
+
* `create_testing_actions`, and are appended at the full-mount layer
|
|
20
|
+
* (`build_full_spine_rpc_actions`) instead: **`_testing_action_manifest`**
|
|
21
|
+
* (dump the live RPC registry for cross-impl method-set + auth-shape parity —
|
|
22
|
+
* it must enumerate *every* mounted method, so it goes where the complete list
|
|
23
|
+
* exists), **`_testing_origin_check`** (answer the origin allowlist's parse
|
|
24
|
+
* and request check for the cross-impl origin conformance table), and
|
|
25
|
+
* **`_testing_shorten_credential_expiry`** (pull an account's credential
|
|
26
|
+
* expiries in to seconds from now, shorten-only, for the credential-deadline
|
|
27
|
+
* cases) — the last two kept out of the bundle so a consumer whose Rust binary
|
|
28
|
+
* doesn't mount their mirrors keeps a green manifest gate.
|
|
23
29
|
*
|
|
24
30
|
* `_testing_reset` — full DB wipe + keeper re-seed + optional
|
|
25
31
|
* secondary-account seeding. The
|
|
@@ -83,6 +89,7 @@ import { ActionManifest, build_action_manifest } from './action_manifest.js';
|
|
|
83
89
|
import { query_create_actor } from '../../auth/account_queries.js';
|
|
84
90
|
import { create_test_account_with_credentials, mint_test_session } from '../app_server.js';
|
|
85
91
|
import { DEFAULT_TEST_PASSWORD } from '../test_credentials.js';
|
|
92
|
+
import { AllowedOriginsErrorKind, parse_allowed_origins, should_allow_origin } from '../../http/origin.js';
|
|
86
93
|
/**
|
|
87
94
|
* Shared `auth` axis for every `_testing_*` action: keeper-only via the
|
|
88
95
|
* daemon-token credential, no acting actor. This is the entire structural
|
|
@@ -452,6 +459,148 @@ export const testing_migration_tracker_action_spec = {
|
|
|
452
459
|
* `create_testing_actions`; mount directly for in-process use.
|
|
453
460
|
*/
|
|
454
461
|
export const create_testing_migration_tracker_action = () => rpc_action(testing_migration_tracker_action_spec, async (_input, ctx) => query_migration_tracker(ctx.db));
|
|
462
|
+
/** Input of `_testing_origin_check`. */
|
|
463
|
+
export const TestingOriginCheckInput = z.strictObject({
|
|
464
|
+
/** An allowlist value, as `FUZ_ALLOWED_ORIGINS` would carry it. */
|
|
465
|
+
patterns: z.string(),
|
|
466
|
+
/** One `Origin` header's value, or `null` for a request without one. */
|
|
467
|
+
origin: z.string().nullable()
|
|
468
|
+
});
|
|
469
|
+
/**
|
|
470
|
+
* Output of `_testing_origin_check`: the request-level answer when the list
|
|
471
|
+
* parses, else every refusal — `index` the 1-based entry position, absent for
|
|
472
|
+
* a list-level refusal.
|
|
473
|
+
*/
|
|
474
|
+
export const TestingOriginCheckOutput = z.union([
|
|
475
|
+
z.strictObject({ ok: z.literal(true), allowed: z.boolean() }),
|
|
476
|
+
z.strictObject({
|
|
477
|
+
ok: z.literal(false),
|
|
478
|
+
errors: z.array(z.strictObject({ index: z.number().int().min(1).optional(), kind: AllowedOriginsErrorKind }))
|
|
479
|
+
})
|
|
480
|
+
]);
|
|
481
|
+
/**
|
|
482
|
+
* Answer one `_testing_origin_check` input: parse `patterns` with
|
|
483
|
+
* `parse_allowed_origins`, then run the request-level check for one `Origin`
|
|
484
|
+
* carrying `origin` (`null` — no `Origin` — passes). The action handler and
|
|
485
|
+
* the conformance table's unit test both call this, so the table checks the
|
|
486
|
+
* same function the wire serves.
|
|
487
|
+
*
|
|
488
|
+
* @param input - the allowlist value and the `Origin` value
|
|
489
|
+
* @returns the answer the Rust twin returns for the same input
|
|
490
|
+
*/
|
|
491
|
+
export const run_testing_origin_check = (input) => {
|
|
492
|
+
const parsed = parse_allowed_origins(input.patterns);
|
|
493
|
+
if (!parsed.ok) {
|
|
494
|
+
return {
|
|
495
|
+
ok: false,
|
|
496
|
+
errors: parsed.errors.map(({ index, kind }) => index === undefined ? { kind } : { index, kind })
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
ok: true,
|
|
501
|
+
allowed: input.origin === null || should_allow_origin(input.origin, parsed.origins)
|
|
502
|
+
};
|
|
503
|
+
};
|
|
504
|
+
/**
|
|
505
|
+
* `_testing_origin_check` — run the origin allowlist's parse and request
|
|
506
|
+
* check for one `{patterns, origin}` pair, so the shared conformance table
|
|
507
|
+
* (`origin_conformance.json`) checks each backend's grammar and matcher over
|
|
508
|
+
* the wire, row by row. Pure: no database, no state. The Rust mirror is
|
|
509
|
+
* `fuz_testing::create_testing_origin_check_action_spec`.
|
|
510
|
+
*
|
|
511
|
+
* Like `_testing_action_manifest`, it is **not** bundled by
|
|
512
|
+
* `create_testing_actions`: a consumer whose Rust binary doesn't mount the
|
|
513
|
+
* mirror would fail its action-manifest parity gate. The spine test binary
|
|
514
|
+
* appends it at the full-mount layer (`build_full_spine_rpc_actions`).
|
|
515
|
+
*
|
|
516
|
+
* `auth` gates on the daemon-token credential, matching `_testing_reset`.
|
|
517
|
+
*/
|
|
518
|
+
export const testing_origin_check_action_spec = {
|
|
519
|
+
method: '_testing_origin_check',
|
|
520
|
+
kind: 'request_response',
|
|
521
|
+
initiator: 'frontend',
|
|
522
|
+
auth: TESTING_ACTION_AUTH,
|
|
523
|
+
side_effects: false,
|
|
524
|
+
input: TestingOriginCheckInput,
|
|
525
|
+
output: TestingOriginCheckOutput,
|
|
526
|
+
async: true,
|
|
527
|
+
description: "Test-binary only — answer the origin allowlist's parse and request check for one " +
|
|
528
|
+
'{patterns, origin} pair, for the cross-impl origin conformance table.'
|
|
529
|
+
};
|
|
530
|
+
/** Build the standalone `_testing_origin_check` action (see the spec doc). */
|
|
531
|
+
export const create_testing_origin_check_action = () => rpc_action(testing_origin_check_action_spec, async (input) => run_testing_origin_check(input));
|
|
532
|
+
/** The shortest expiry `_testing_shorten_credential_expiry` sets, in seconds from now. */
|
|
533
|
+
export const TESTING_SHORTEN_CREDENTIAL_EXPIRY_MIN_SECONDS = 1;
|
|
534
|
+
/**
|
|
535
|
+
* The longest expiry `_testing_shorten_credential_expiry` sets, in seconds
|
|
536
|
+
* from now — a minute: the action exists to make a deadline arrive inside a
|
|
537
|
+
* test.
|
|
538
|
+
*/
|
|
539
|
+
export const TESTING_SHORTEN_CREDENTIAL_EXPIRY_MAX_SECONDS = 60;
|
|
540
|
+
/**
|
|
541
|
+
* The new expiry: `$2` seconds from now, unless the row is due sooner. The
|
|
542
|
+
* `COALESCE` gives an eternal token (`NULL`) the near expiry too.
|
|
543
|
+
*/
|
|
544
|
+
const SHORTEN_EXPIRES_AT_SQL = "LEAST(COALESCE(expires_at, 'infinity'::timestamptz), NOW() + ($2::int4 * INTERVAL '1 second'))";
|
|
545
|
+
/**
|
|
546
|
+
* `_testing_shorten_credential_expiry` — pull the expiry of an account's
|
|
547
|
+
* stored credentials (every `auth_session` and `api_token` row it owns) in to
|
|
548
|
+
* `seconds` from now, so the cross-backend credential-deadline suite can open
|
|
549
|
+
* a WebSocket or audit stream on a credential that expires a moment later and
|
|
550
|
+
* watch it close at the deadline (`ResolvedAuth.credential_deadline`) without
|
|
551
|
+
* waiting out a real lifetime.
|
|
552
|
+
*
|
|
553
|
+
* Applied **before the open**: it rewrites the stored rows, and a connection
|
|
554
|
+
* reads its deadline when its credential resolves, so only one opened
|
|
555
|
+
* afterwards sees the near deadline.
|
|
556
|
+
*
|
|
557
|
+
* **Shorten-only.** The new expiry is `LEAST(expires_at, NOW() + seconds)` —
|
|
558
|
+
* an eternal token gains one, and nothing already due sooner moves later, so
|
|
559
|
+
* the action can neither extend a credential's life nor revive an expired one;
|
|
560
|
+
* `seconds` is an integer from `TESTING_SHORTEN_CREDENTIAL_EXPIRY_MIN_SECONDS`
|
|
561
|
+
* to `TESTING_SHORTEN_CREDENTIAL_EXPIRY_MAX_SECONDS`. The Rust mirror is
|
|
562
|
+
* `fuz_testing::create_testing_shorten_credential_expiry_action_spec`.
|
|
563
|
+
*
|
|
564
|
+
* Like `_testing_origin_check`, it is **not** bundled by
|
|
565
|
+
* `create_testing_actions`: a consumer whose Rust binary doesn't mount the
|
|
566
|
+
* mirror would fail its action-manifest parity gate. The spine test binary
|
|
567
|
+
* appends it at the full-mount layer (`build_full_spine_rpc_actions`).
|
|
568
|
+
*
|
|
569
|
+
* `auth` gates on the daemon-token credential, matching `_testing_reset`.
|
|
570
|
+
*/
|
|
571
|
+
export const testing_shorten_credential_expiry_action_spec = {
|
|
572
|
+
method: '_testing_shorten_credential_expiry',
|
|
573
|
+
kind: 'request_response',
|
|
574
|
+
initiator: 'frontend',
|
|
575
|
+
auth: TESTING_ACTION_AUTH,
|
|
576
|
+
side_effects: true,
|
|
577
|
+
input: z.strictObject({
|
|
578
|
+
account_id: Uuid,
|
|
579
|
+
seconds: z
|
|
580
|
+
.number()
|
|
581
|
+
.int()
|
|
582
|
+
.min(TESTING_SHORTEN_CREDENTIAL_EXPIRY_MIN_SECONDS)
|
|
583
|
+
.max(TESTING_SHORTEN_CREDENTIAL_EXPIRY_MAX_SECONDS)
|
|
584
|
+
.meta({
|
|
585
|
+
description: 'Seconds from now the credentials expire, unless already due sooner — this ' +
|
|
586
|
+
'backdoor can only shorten a credential, never extend it.'
|
|
587
|
+
})
|
|
588
|
+
}),
|
|
589
|
+
output: z.strictObject({
|
|
590
|
+
sessions: z.number().int().min(0),
|
|
591
|
+
api_tokens: z.number().int().min(0)
|
|
592
|
+
}),
|
|
593
|
+
async: true,
|
|
594
|
+
description: "Test-binary only — pull an account's session and API token expiries in to seconds from " +
|
|
595
|
+
'now (shorten-only), for the cross-impl credential-deadline cases.'
|
|
596
|
+
};
|
|
597
|
+
/** Build the standalone `_testing_shorten_credential_expiry` action (see the spec doc). */
|
|
598
|
+
export const create_testing_shorten_credential_expiry_action = () => rpc_action(testing_shorten_credential_expiry_action_spec, async (input, ctx) => {
|
|
599
|
+
const params = [input.account_id, input.seconds];
|
|
600
|
+
const sessions = await ctx.db.query(`UPDATE auth_session SET expires_at = ${SHORTEN_EXPIRES_AT_SQL} WHERE account_id = $1 RETURNING id`, params);
|
|
601
|
+
const api_tokens = await ctx.db.query(`UPDATE api_token SET expires_at = ${SHORTEN_EXPIRES_AT_SQL} WHERE account_id = $1 RETURNING id`, params);
|
|
602
|
+
return { sessions: sessions.length, api_tokens: api_tokens.length };
|
|
603
|
+
});
|
|
455
604
|
/**
|
|
456
605
|
* `_testing_action_manifest` — dump the backend's live RPC method set as a
|
|
457
606
|
* normalized `ActionManifest` (one entry per method: `{method, side_effects,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token_scope_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_scope_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA2E9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAkB1E,wDAAwD;AACxD,MAAM,WAAW,iCAAkC,SAAQ,uCAAuC;IACjG,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"token_scope_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/token_scope_surface.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA2E9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAkB1E,wDAAwD;AACxD,MAAM,WAAW,iCAAkC,SAAQ,uCAAuC;IACjG,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AA6DD,eAAO,MAAM,wCAAwC,GACpD,SAAS,iCAAiC,KACxC,IA4HF,CAAC"}
|
|
@@ -63,10 +63,10 @@ import '../assert_dev_env.js';
|
|
|
63
63
|
* @module
|
|
64
64
|
*/
|
|
65
65
|
import { describe, assert } from 'vitest';
|
|
66
|
-
import { account_token_create_action_spec } from '../../auth/account_action_specs.js';
|
|
67
66
|
import { ERROR_CREDENTIAL_TYPE_REQUIRED, ERROR_TOKEN_SCOPE_REQUIRED } from '../../http/error_schemas.js';
|
|
68
67
|
import { test_if } from './capabilities.js';
|
|
69
68
|
import { cross_rpc_call } from './cell_cross_helpers.js';
|
|
69
|
+
import { mint_narrowed_api_token } from './narrowed_api_token.js';
|
|
70
70
|
import { SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
71
71
|
/** Default audit-log SSE path — the `audit_stream` surface. */
|
|
72
72
|
const DEFAULT_SSE_PATH = '/api/admin/audit/stream';
|
|
@@ -84,22 +84,6 @@ const surface_denial_body = (surface) => ({
|
|
|
84
84
|
error: ERROR_TOKEN_SCOPE_REQUIRED,
|
|
85
85
|
required_scope: `surface:${surface}`
|
|
86
86
|
});
|
|
87
|
-
/**
|
|
88
|
-
* Mint a narrowed bearer token over a session, through the production
|
|
89
|
-
* `account_token_create` path.
|
|
90
|
-
*
|
|
91
|
-
* Deliberately not a fixture-level credential: minting it here means the token
|
|
92
|
-
* is produced by the same handler a real minter would use, on whichever
|
|
93
|
-
* account the caller chose — which is what lets the ordering case put a
|
|
94
|
-
* narrowed token on an account that lacks the gating role.
|
|
95
|
-
*/
|
|
96
|
-
const mint_narrowed_token = async (transport, rpc_path, session_headers) => {
|
|
97
|
-
const res = await cross_rpc_call(transport, rpc_path, account_token_create_action_spec.method, { scope: { kind: 'methods', methods: [ADMITTED_METHOD] }, lifetime: { kind: 'eternal' } }, session_headers);
|
|
98
|
-
assert.ok(res.ok, `minting a narrowed token failed: ${JSON.stringify(res.error)}`);
|
|
99
|
-
const token = res.result?.token;
|
|
100
|
-
assert.ok(typeof token === 'string' && token.length > 0, 'mint returned no token');
|
|
101
|
-
return token;
|
|
102
|
-
};
|
|
103
87
|
/**
|
|
104
88
|
* Assert a bearer GET is refused with the canonical surface denial.
|
|
105
89
|
*
|
|
@@ -145,18 +129,33 @@ export const describe_token_scope_surface_cross_tests = (options) => {
|
|
|
145
129
|
*/
|
|
146
130
|
test_if(true, 'a narrowed token still reaches the method it names', async () => {
|
|
147
131
|
const fixture = await setup_test();
|
|
148
|
-
const token = await
|
|
132
|
+
const token = await mint_narrowed_api_token({
|
|
133
|
+
app: fixture.transport,
|
|
134
|
+
rpc_path,
|
|
135
|
+
session_headers: fixture.create_session_headers(),
|
|
136
|
+
methods: [ADMITTED_METHOD]
|
|
137
|
+
});
|
|
149
138
|
const res = await cross_rpc_call(fixture.fresh_transport({ origin: null }), rpc_path, ADMITTED_METHOD, undefined, { authorization: `Bearer ${token}` });
|
|
150
139
|
assert.ok(res.ok, `admitted method must succeed: ${JSON.stringify(res.error)}`);
|
|
151
140
|
});
|
|
152
141
|
test_if(capabilities.sse, 'the audit stream refuses a bearer at the channel gate (credential_types)', async () => {
|
|
153
142
|
const fixture = await setup_test();
|
|
154
|
-
const token = await
|
|
143
|
+
const token = await mint_narrowed_api_token({
|
|
144
|
+
app: fixture.transport,
|
|
145
|
+
rpc_path,
|
|
146
|
+
session_headers: fixture.create_session_headers(),
|
|
147
|
+
methods: [ADMITTED_METHOD]
|
|
148
|
+
});
|
|
155
149
|
await assert_channel_denied(fixture.fresh_transport, sse_path, token, ['session']);
|
|
156
150
|
});
|
|
157
151
|
test_if(capabilities.fact_serving, 'a narrowed token is refused the bare-hash fact read (surface:fact_bare)', async () => {
|
|
158
152
|
const fixture = await setup_test();
|
|
159
|
-
const token = await
|
|
153
|
+
const token = await mint_narrowed_api_token({
|
|
154
|
+
app: fixture.transport,
|
|
155
|
+
rpc_path,
|
|
156
|
+
session_headers: fixture.create_session_headers(),
|
|
157
|
+
methods: [ADMITTED_METHOD]
|
|
158
|
+
});
|
|
160
159
|
await assert_surface_denied(fixture.fresh_transport, `/api/facts/${ABSENT_FACT_HASH}`, token, 'fact_bare');
|
|
161
160
|
});
|
|
162
161
|
/**
|
|
@@ -189,7 +188,12 @@ export const describe_token_scope_surface_cross_tests = (options) => {
|
|
|
189
188
|
test_if(capabilities.fact_serving, 'scope outranks role — a narrowed token lacking the role hears about its scope', async () => {
|
|
190
189
|
const fixture = await setup_test();
|
|
191
190
|
const non_admin = await fixture.create_account({ username: 'scope_before_role' });
|
|
192
|
-
const token = await
|
|
191
|
+
const token = await mint_narrowed_api_token({
|
|
192
|
+
app: fixture.fresh_transport(),
|
|
193
|
+
rpc_path,
|
|
194
|
+
session_headers: non_admin.create_session_headers(),
|
|
195
|
+
methods: [ADMITTED_METHOD]
|
|
196
|
+
});
|
|
193
197
|
await assert_surface_denied(fixture.fresh_transport, `/api/facts/${ABSENT_FACT_HASH}`, token, 'fact_bare');
|
|
194
198
|
});
|
|
195
199
|
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { type BackendCapabilities } from './capabilities.js';
|
|
3
|
+
import type { RpcPathCapabilityGatedCrossSuiteOptions } from './setup.js';
|
|
4
|
+
/** A transport's front door, as the table's rows name it. */
|
|
5
|
+
export type TransportCompositionSurface = 'rest_post' | 'rest_password' | 'rpc_post' | 'rpc_get' | 'ws_upgrade' | 'sse';
|
|
6
|
+
/** What a probe sends, as the table's columns name it. */
|
|
7
|
+
export type TransportCompositionProbe = 'foreign_origin' | 'unreadable_origin' | 'duplicated_origin' | 'over_cap_foreign_origin' | 'no_credential' | 'narrowed_token';
|
|
8
|
+
/** The refusal a case expects. */
|
|
9
|
+
export interface TransportCompositionExpect {
|
|
10
|
+
readonly status: number;
|
|
11
|
+
/** The refusal reason — the flat body's `error`, or the envelope's `error.data.reason`. */
|
|
12
|
+
readonly error: string;
|
|
13
|
+
/** Further fields the refusal must carry, beside the reason. */
|
|
14
|
+
readonly fields?: Readonly<Record<string, unknown>>;
|
|
15
|
+
/** `true` when the refusal is a JSON-RPC error envelope rather than a flat `{error}` body. */
|
|
16
|
+
readonly envelope?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** One cell of the front-door table. */
|
|
19
|
+
export interface TransportCompositionCase {
|
|
20
|
+
readonly surface: TransportCompositionSurface;
|
|
21
|
+
readonly probe: TransportCompositionProbe;
|
|
22
|
+
readonly expect: TransportCompositionExpect;
|
|
23
|
+
/** The capability the surface needs; the case skips on a backend without it. */
|
|
24
|
+
readonly gate?: keyof BackendCapabilities;
|
|
25
|
+
}
|
|
26
|
+
/** The front-door table — one case per cell of the module doc's matrix. */
|
|
27
|
+
export declare const transport_composition_cases: ReadonlyArray<TransportCompositionCase>;
|
|
28
|
+
/** Options for `describe_transport_composition_cross_tests`. */
|
|
29
|
+
export interface TransportCompositionCrossTestOptions extends RpcPathCapabilityGatedCrossSuiteOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Base URL the spawned backend is reachable at, or `null` for an in-process
|
|
32
|
+
* run. Given a URL, every probe is written to the socket; with `null` the
|
|
33
|
+
* probes go through the fixture's transport and the WebSocket rows skip.
|
|
34
|
+
*/
|
|
35
|
+
readonly base_url: string | null;
|
|
36
|
+
/**
|
|
37
|
+
* An `Origin` the backend allows. Default: the origin of `base_url`, or
|
|
38
|
+
* `http://localhost:5173` in-process (which `create_test_app`'s allow list
|
|
39
|
+
* admits).
|
|
40
|
+
*/
|
|
41
|
+
readonly origin?: string;
|
|
42
|
+
/** Session-only REST `POST` route the REST row probes. Default `/api/account/logout`. */
|
|
43
|
+
readonly rest_path?: string;
|
|
44
|
+
/** Password-change route the `/password` row probes. Default `/api/account/password`. */
|
|
45
|
+
readonly password_path?: string;
|
|
46
|
+
/** WebSocket endpoint path. Default `/api/ws`. */
|
|
47
|
+
readonly ws_path?: string;
|
|
48
|
+
/** Audit-log SSE path. Default `/api/admin/audit/stream`. */
|
|
49
|
+
readonly sse_path?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Register the front-door table's cases — one test per cell, each on a fresh
|
|
53
|
+
* fixture.
|
|
54
|
+
*
|
|
55
|
+
* This is the front-door contract: a backend that wires it answers each
|
|
56
|
+
* transport's probes as the table says — when one request trips several
|
|
57
|
+
* refusals, the one the table names answers.
|
|
58
|
+
*/
|
|
59
|
+
export declare const describe_transport_composition_cross_tests: (options: TransportCompositionCrossTestOptions) => void;
|
|
60
|
+
//# sourceMappingURL=transport_composition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport_composition.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/transport_composition.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA+E9B,OAAO,EAAW,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EAAE,uCAAuC,EAAe,MAAM,YAAY,CAAC;AAGvF,6DAA6D;AAC7D,MAAM,MAAM,2BAA2B,GACtC,WAAW,GAAG,eAAe,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;AAE/E,0DAA0D;AAC1D,MAAM,MAAM,yBAAyB,GAClC,gBAAgB,GAChB,mBAAmB,GACnB,mBAAmB,GACnB,yBAAyB,GACzB,eAAe,GACf,gBAAgB,CAAC;AAEpB,kCAAkC;AAClC,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,8FAA8F;IAC9F,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wCAAwC;AACxC,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,OAAO,EAAE,2BAA2B,CAAC;IAC9C,QAAQ,CAAC,KAAK,EAAE,yBAAyB,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,gFAAgF;IAChF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,mBAAmB,CAAC;CAC1C;AAsCD,2EAA2E;AAC3E,eAAO,MAAM,2BAA2B,EAAE,aAAa,CAAC,wBAAwB,CA4C/E,CAAC;AAEF,gEAAgE;AAChE,MAAM,WAAW,oCAAqC,SAAQ,uCAAuC;IACpG;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,yFAAyF;IACzF,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,kDAAkD;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAwFD;;;;;;;GAOG;AACH,eAAO,MAAM,0CAA0C,GACtD,SAAS,oCAAoC,KAC3C,IA2KF,CAAC"}
|