@fuzdev/fuz_app 0.140.0 → 0.142.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 +72 -22
- 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 +44 -4
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +33 -3
- package/dist/actions/perform_action.d.ts +10 -0
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +23 -9
- 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 +10 -4
- 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 +44 -22
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +12 -1
- 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 +12 -0
- package/dist/auth/account_routes.d.ts +7 -2
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +240 -187
- package/dist/auth/admin_action_specs.d.ts +6 -1
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +6 -1
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +14 -2
- 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/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +85 -76
- package/dist/auth/cell_action_specs.d.ts +7 -3
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +9 -5
- package/dist/auth/cell_actions.d.ts +3 -3
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +67 -38
- package/dist/auth/cell_authorize.d.ts +69 -8
- package/dist/auth/cell_authorize.d.ts.map +1 -1
- package/dist/auth/cell_authorize.js +89 -9
- package/dist/auth/cell_field_action_specs.d.ts +5 -4
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_field_action_specs.js +7 -7
- package/dist/auth/cell_field_actions.d.ts +7 -5
- package/dist/auth/cell_field_actions.d.ts.map +1 -1
- package/dist/auth/cell_field_actions.js +24 -22
- package/dist/auth/cell_grant_action_specs.d.ts +6 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_grant_action_specs.js +6 -0
- package/dist/auth/cell_grant_actions.d.ts +3 -1
- package/dist/auth/cell_grant_actions.d.ts.map +1 -1
- package/dist/auth/cell_grant_actions.js +16 -2
- package/dist/auth/cell_item_action_specs.d.ts +5 -4
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_item_action_specs.js +7 -7
- package/dist/auth/cell_item_actions.d.ts +6 -5
- package/dist/auth/cell_item_actions.d.ts.map +1 -1
- package/dist/auth/cell_item_actions.js +23 -22
- package/dist/auth/cell_relation_list_limit.d.ts +20 -0
- package/dist/auth/cell_relation_list_limit.d.ts.map +1 -0
- package/dist/auth/cell_relation_list_limit.js +27 -0
- package/dist/auth/cell_relation_visibility.d.ts +4 -3
- package/dist/auth/cell_relation_visibility.d.ts.map +1 -1
- package/dist/auth/cell_relation_visibility.js +6 -5
- package/dist/auth/cell_steward.d.ts +45 -0
- package/dist/auth/cell_steward.d.ts.map +1 -0
- package/dist/auth/cell_steward.js +53 -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 +8 -2
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +20 -4
- 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/role_grant_offer_action_specs.d.ts +5 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +6 -1
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +23 -16
- package/dist/auth/role_grant_offer_queries.d.ts +8 -6
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +16 -8
- 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/auth/signup_routes.d.ts +4 -3
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +157 -134
- package/dist/auth/token_scope.d.ts +7 -1
- package/dist/auth/token_scope.d.ts.map +1 -1
- package/dist/auth/token_scope.js +8 -2
- package/dist/db/CLAUDE.md +32 -19
- package/dist/db/cell_ddl.d.ts +1 -2
- package/dist/db/cell_ddl.d.ts.map +1 -1
- package/dist/db/cell_ddl.js +1 -2
- package/dist/db/cell_field_queries.d.ts +20 -18
- package/dist/db/cell_field_queries.d.ts.map +1 -1
- package/dist/db/cell_field_queries.js +28 -22
- package/dist/db/cell_item_queries.d.ts +20 -14
- package/dist/db/cell_item_queries.d.ts.map +1 -1
- package/dist/db/cell_item_queries.js +28 -18
- package/dist/db/cell_moderation.d.ts +67 -0
- package/dist/db/cell_moderation.d.ts.map +1 -0
- package/dist/db/cell_moderation.js +66 -0
- package/dist/db/cell_queries.d.ts +44 -14
- package/dist/db/cell_queries.d.ts.map +1 -1
- package/dist/db/cell_queries.js +68 -45
- package/dist/db/cell_view_sql.d.ts +102 -0
- package/dist/db/cell_view_sql.d.ts.map +1 -0
- package/dist/db/cell_view_sql.js +112 -0
- package/dist/db/db_pg.d.ts +1 -1
- package/dist/db/db_pg.js +1 -1
- 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/auth_shape.d.ts +5 -2
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/auth_shape.js +5 -2
- package/dist/http/ip_canonical.d.ts +30 -3
- package/dist/http/ip_canonical.d.ts.map +1 -1
- package/dist/http/ip_canonical.js +50 -3
- 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/primitive_schemas.d.ts +10 -1
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +11 -2
- package/dist/rate_limiter.d.ts +179 -12
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +352 -42
- 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 +8 -4
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +16 -6
- package/dist/testing/CLAUDE.md +164 -25
- 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/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +37 -2
- 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/cell_gated_create.d.ts +38 -0
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +401 -5
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.js +145 -2
- 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/expected_schema.json +0 -1
- 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/login_security.d.ts +16 -0
- package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
- package/dist/testing/cross_backend/login_security.js +154 -0
- 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/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/query_shape.js +24 -2
- 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/role_grant_participation.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_participation.js +64 -0
- 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 +20 -1
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +23 -1
- 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_action_rate_limit.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.js +21 -1
- 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/schema_introspect.js +1 -1
- package/dist/testing/schema_parity.d.ts +4 -3
- package/dist/testing/schema_parity.d.ts.map +1 -1
- package/dist/testing/schema_parity.js +4 -3
- package/dist/testing/schema_ready_fixture.d.ts +2 -4
- package/dist/testing/schema_ready_fixture.d.ts.map +1 -1
- package/dist/testing/schema_ready_fixture.js +2 -4
- 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/dist/ui/auth_state.svelte.d.ts +10 -0
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +22 -4
- package/package.json +1 -1
- package/dist/db/cell_history_ddl.d.ts +0 -38
- package/dist/db/cell_history_ddl.d.ts.map +0 -1
- package/dist/db/cell_history_ddl.js +0 -61
|
@@ -16,11 +16,23 @@ import '../assert_dev_env.js';
|
|
|
16
16
|
* - **grant lifecycle** — owner grants an actor-shaped editor; the grantee
|
|
17
17
|
* gains edit (was 404 before the grant); `cell_grant_list` is manage-tier
|
|
18
18
|
* (owner sees it, the editor gets the IDOR 404); revoke drops the edit path.
|
|
19
|
+
* - **no owner principal** — a grant naming the cell's owner is refused
|
|
20
|
+
* `cell_grant_principal_is_owner`, with the owner's actor id sent lowercase
|
|
21
|
+
* or uppercase.
|
|
19
22
|
* - **`cell_visibility_manage_only`** — the editor-grant holder can edit
|
|
20
23
|
* content but flipping `visibility` is 403 (the case the 5-verb CRUD cut
|
|
21
24
|
* couldn't reach without a grant principal).
|
|
22
25
|
* - **fields** — `cell_field_set` UPSERT, forward + reverse `cell_field_list`,
|
|
23
26
|
* idempotent `cell_field_delete`.
|
|
27
|
+
* - **relation-list caps** — past the default page of inbound edges, a
|
|
28
|
+
* reverse `cell_item_list` / `cell_field_list` with no `limit` answers the
|
|
29
|
+
* default page, an explicit `limit` caps it in either direction, and the
|
|
30
|
+
* forward list defaults the same — the same counts on both spines. A page is
|
|
31
|
+
* visible edges: a page's worth of private ends older than one public end
|
|
32
|
+
* still leaves the public one on an anonymous page, in all four lists. An
|
|
33
|
+
* out-of-range or mistyped `limit` (`0`, `-1`, `1.5`, past the max, `null`,
|
|
34
|
+
* a string) is `invalid_params` in every direction, and the reverse lists
|
|
35
|
+
* read oldest edge first.
|
|
24
36
|
* - **items** — `cell_item_insert` at fractional-index positions, forward
|
|
25
37
|
* (lex-ordered) + reverse `cell_item_list`, `cell_item_move`, idempotent
|
|
26
38
|
* `cell_item_delete`.
|
|
@@ -55,13 +67,15 @@ import '../assert_dev_env.js';
|
|
|
55
67
|
* @module
|
|
56
68
|
*/
|
|
57
69
|
import { describe, assert } from 'vitest';
|
|
58
|
-
import { fractional_index_between } from '@fuzdev/fuz_util/fractional_index.ts';
|
|
70
|
+
import { fractional_index_between, fractional_indices_between } from '@fuzdev/fuz_util/fractional_index.ts';
|
|
59
71
|
import { CellCreateOutput, CellUpdateOutput, CellCloneOutput, CellGetOutput, CellListOutput } from '../../auth/cell_action_specs.js';
|
|
60
72
|
import { AuditLogListOutput } from '../../auth/admin_action_specs.js';
|
|
61
|
-
import { CellGrantCreateOutput, CellGrantListOutput, CellGrantRevokeOutput } from '../../auth/cell_grant_action_specs.js';
|
|
73
|
+
import { ERROR_CELL_GRANT_PRINCIPAL_IS_OWNER, CellGrantCreateOutput, CellGrantListOutput, CellGrantRevokeOutput } from '../../auth/cell_grant_action_specs.js';
|
|
62
74
|
import { CellFieldDeleteOutput, CellFieldListOutput, CellFieldSetOutput } from '../../auth/cell_field_action_specs.js';
|
|
63
75
|
import { CellItemDeleteOutput, CellItemInsertOutput, CellItemListOutput, CellItemMoveOutput } from '../../auth/cell_item_action_specs.js';
|
|
64
76
|
import { CellAuditListOutput } from '../../auth/cell_audit_action_specs.js';
|
|
77
|
+
import { JSONRPC_INVALID_PARAMS } from '../../http/jsonrpc.js';
|
|
78
|
+
import { CELL_RELATION_LIST_LIMIT_DEFAULT, CELL_RELATION_LIST_LIMIT_MAX } from '../../auth/cell_relation_list_limit.js';
|
|
65
79
|
import { test_if } from './capabilities.js';
|
|
66
80
|
import { cross_rpc_call, error_reason, expect_output } from './cell_cross_helpers.js';
|
|
67
81
|
import { SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
@@ -134,6 +148,20 @@ export const describe_cell_relations_cross_tests = (options) => {
|
|
|
134
148
|
assert.ok(!post.ok, 'revoked editor still edited');
|
|
135
149
|
assert.strictEqual(error_reason(post), 'cell_not_found');
|
|
136
150
|
});
|
|
151
|
+
test_if(capabilities.cell_relations, 'a grant naming the owner is refused cell_grant_principal_is_owner, whatever the id case', async () => {
|
|
152
|
+
const fixture = await setup_test();
|
|
153
|
+
const owner = await fixture.create_account({ username: 'cell_grant_self_owner' });
|
|
154
|
+
const t = fixture.fresh_transport();
|
|
155
|
+
const owner_h = owner.create_session_headers();
|
|
156
|
+
const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, owner_h), CellCreateOutput).cell;
|
|
157
|
+
// both spines canonicalize the id before comparing it with
|
|
158
|
+
// `created_by`, so the uppercased owner id is the owner too
|
|
159
|
+
for (const actor_id of [owner.actor.id, owner.actor.id.toUpperCase()]) {
|
|
160
|
+
const res = await cross_rpc_call(t, rpc_path, 'cell_grant_create', { cell_id: cell.id, level: 'viewer', principal: { kind: 'actor', actor_id } }, owner_h);
|
|
161
|
+
assert.ok(!res.ok, `a grant to the owner must be refused (${actor_id})`);
|
|
162
|
+
assert.strictEqual(error_reason(res), ERROR_CELL_GRANT_PRINCIPAL_IS_OWNER, actor_id);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
137
165
|
test_if(capabilities.cell_relations, "cell_list shared_with: 'me' lists grant-admitted cells, excludes owned, enriches cell_grants", async () => {
|
|
138
166
|
const fixture = await setup_test();
|
|
139
167
|
const owner = await fixture.create_account({ username: 'cell_shared_owner' });
|
|
@@ -219,6 +247,121 @@ export const describe_cell_relations_cross_tests = (options) => {
|
|
|
219
247
|
const del2 = expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_delete', { source_id: source.id, name: 'cover' }, h), CellFieldDeleteOutput);
|
|
220
248
|
assert.strictEqual(del2.deleted, false);
|
|
221
249
|
});
|
|
250
|
+
test_if(capabilities.cell_relations, 'relation lists default to a capped page and honor limit, forward and reverse', async () => {
|
|
251
|
+
const fixture = await setup_test();
|
|
252
|
+
const owner = await fixture.create_account({ username: 'cell_list_cap_owner' });
|
|
253
|
+
const t = fixture.fresh_transport();
|
|
254
|
+
const h = owner.create_session_headers();
|
|
255
|
+
// one past the default page, so an omitted limit shows the cap
|
|
256
|
+
const inbound = CELL_RELATION_LIST_LIMIT_DEFAULT + 1;
|
|
257
|
+
const make = async () => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell.id;
|
|
258
|
+
const hub = await make();
|
|
259
|
+
const parent = await make();
|
|
260
|
+
const source = await make();
|
|
261
|
+
const positions = fractional_indices_between(null, null, inbound);
|
|
262
|
+
for (let i = 0; i < inbound; i++) {
|
|
263
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_insert', { parent_id: parent, child_id: hub, position: positions[i] }, h), CellItemInsertOutput);
|
|
264
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_set', { source_id: source, name: `link_${i}`, target_id: hub }, h), CellFieldSetOutput);
|
|
265
|
+
}
|
|
266
|
+
const items = async (params) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_list', params, h), CellItemListOutput).items.length;
|
|
267
|
+
const fields = async (params) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_list', params, h), CellFieldListOutput).fields.length;
|
|
268
|
+
for (const [label, count] of [
|
|
269
|
+
['reverse items, no limit', await items({ child_id: hub })],
|
|
270
|
+
['reverse fields, no limit', await fields({ target_id: hub })],
|
|
271
|
+
['forward items, no limit', await items({ parent_id: parent })],
|
|
272
|
+
['forward fields, no limit', await fields({ source_id: source })]
|
|
273
|
+
]) {
|
|
274
|
+
assert.strictEqual(count, CELL_RELATION_LIST_LIMIT_DEFAULT, label);
|
|
275
|
+
}
|
|
276
|
+
assert.strictEqual(await items({ child_id: hub, limit: 3 }), 3, 'reverse items, limit 3');
|
|
277
|
+
assert.strictEqual(await fields({ target_id: hub, limit: 3 }), 3, 'reverse fields, limit 3');
|
|
278
|
+
assert.strictEqual(await items({ child_id: hub, limit: inbound }), inbound, 'reverse items, limit past the default');
|
|
279
|
+
assert.strictEqual(await fields({ target_id: hub, limit: inbound }), inbound, 'reverse fields, limit past the default');
|
|
280
|
+
});
|
|
281
|
+
test_if(capabilities.cell_relations, 'a relation page is visible edges: hidden ends never crowd out a visible one', async () => {
|
|
282
|
+
const fixture = await setup_test();
|
|
283
|
+
const owner = await fixture.create_account({ username: 'cell_list_pad_owner' });
|
|
284
|
+
const t = fixture.fresh_transport();
|
|
285
|
+
const h = owner.create_session_headers();
|
|
286
|
+
const anon = fixture.fresh_transport({ origin: null });
|
|
287
|
+
const make = async (visibility) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {}, visibility }, h), CellCreateOutput).cell.id;
|
|
288
|
+
const call = async (method, params) => cross_rpc_call(t, rpc_path, method, params, h);
|
|
289
|
+
// A full default page of private edges, older (and, forward, named
|
|
290
|
+
// earlier) than one public edge, in all four lists.
|
|
291
|
+
const target = await make('public');
|
|
292
|
+
const child = await make('public');
|
|
293
|
+
const source = await make('public');
|
|
294
|
+
const parent = await make('public');
|
|
295
|
+
const padding = CELL_RELATION_LIST_LIMIT_DEFAULT + 1;
|
|
296
|
+
const positions = fractional_indices_between(null, null, padding + 1);
|
|
297
|
+
for (let i = 0; i <= padding; i++) {
|
|
298
|
+
const end = await make(i < padding ? 'private' : 'public');
|
|
299
|
+
const name = `link_${String(i).padStart(3, '0')}`;
|
|
300
|
+
expect_output(await call('cell_field_set', { source_id: end, name: 'link', target_id: target }), CellFieldSetOutput);
|
|
301
|
+
expect_output(await call('cell_item_insert', { parent_id: end, child_id: child, position: 'a0' }), CellItemInsertOutput);
|
|
302
|
+
expect_output(await call('cell_field_set', { source_id: source, name, target_id: end }), CellFieldSetOutput);
|
|
303
|
+
expect_output(await call('cell_item_insert', {
|
|
304
|
+
parent_id: parent,
|
|
305
|
+
child_id: end,
|
|
306
|
+
position: positions[i]
|
|
307
|
+
}), CellItemInsertOutput);
|
|
308
|
+
}
|
|
309
|
+
const anon_fields = async (params) => expect_output(await cross_rpc_call(anon, rpc_path, 'cell_field_list', params, {}), CellFieldListOutput).fields;
|
|
310
|
+
const anon_items = async (params) => expect_output(await cross_rpc_call(anon, rpc_path, 'cell_item_list', params, {}), CellItemListOutput).items;
|
|
311
|
+
assert.strictEqual((await anon_fields({ target_id: target })).length, 1, 'reverse fields: the public source, past a page of private ones');
|
|
312
|
+
assert.strictEqual((await anon_items({ child_id: child })).length, 1, 'reverse items: the public parent, past a page of private ones');
|
|
313
|
+
assert.strictEqual((await anon_fields({ source_id: source })).length, 1, 'forward fields: the public target, past a page of private ones');
|
|
314
|
+
assert.strictEqual((await anon_items({ parent_id: parent })).length, 1, 'forward items: the public child, past a page of private ones');
|
|
315
|
+
// the owner views every end: a full default page
|
|
316
|
+
const owned = expect_output(await call('cell_field_list', { target_id: target }), CellFieldListOutput).fields;
|
|
317
|
+
assert.strictEqual(owned.length, CELL_RELATION_LIST_LIMIT_DEFAULT, "the owner's page");
|
|
318
|
+
});
|
|
319
|
+
test_if(capabilities.cell_relations, 'relation-list limit: out-of-range and mistyped values refused alike, forward and reverse', async () => {
|
|
320
|
+
const fixture = await setup_test();
|
|
321
|
+
const owner = await fixture.create_account({ username: 'cell_list_limit_owner' });
|
|
322
|
+
const t = fixture.fresh_transport();
|
|
323
|
+
const h = owner.create_session_headers();
|
|
324
|
+
const cell = expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell.id;
|
|
325
|
+
const directions = [
|
|
326
|
+
['cell_field_list', { source_id: cell }],
|
|
327
|
+
['cell_field_list', { target_id: cell }],
|
|
328
|
+
['cell_item_list', { parent_id: cell }],
|
|
329
|
+
['cell_item_list', { child_id: cell }]
|
|
330
|
+
];
|
|
331
|
+
for (const [method, base] of directions) {
|
|
332
|
+
for (const limit of [0, -1, 1.5, CELL_RELATION_LIST_LIMIT_MAX + 1, null, '5']) {
|
|
333
|
+
const res = await cross_rpc_call(t, rpc_path, method, { ...base, limit }, h);
|
|
334
|
+
const label = `${method} ${JSON.stringify(base)} limit ${JSON.stringify(limit)}`;
|
|
335
|
+
assert.ok(!res.ok, `${label} must be refused`);
|
|
336
|
+
assert.strictEqual(res.error?.code, JSONRPC_INVALID_PARAMS, label);
|
|
337
|
+
assert.strictEqual(error_reason(res), undefined, label);
|
|
338
|
+
}
|
|
339
|
+
for (const limit of [1, CELL_RELATION_LIST_LIMIT_MAX]) {
|
|
340
|
+
const res = await cross_rpc_call(t, rpc_path, method, { ...base, limit }, h);
|
|
341
|
+
assert.ok(res.ok, `${method} ${JSON.stringify(base)} limit ${limit}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
test_if(capabilities.cell_relations, 'reverse relation lists read oldest edge first, so a page of one is the oldest', async () => {
|
|
346
|
+
const fixture = await setup_test();
|
|
347
|
+
const owner = await fixture.create_account({ username: 'cell_list_order_owner' });
|
|
348
|
+
const t = fixture.fresh_transport();
|
|
349
|
+
const h = owner.create_session_headers();
|
|
350
|
+
const make = async () => expect_output(await cross_rpc_call(t, rpc_path, 'cell_create', { kind: 'note', data: {} }, h), CellCreateOutput).cell.id;
|
|
351
|
+
const hub = await make();
|
|
352
|
+
// each edge its own call, so each has its own `created_at`, in order
|
|
353
|
+
const ends = [await make(), await make(), await make()];
|
|
354
|
+
for (const end of ends) {
|
|
355
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_insert', { parent_id: end, child_id: hub, position: 'a0' }, h), CellItemInsertOutput);
|
|
356
|
+
expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_set', { source_id: end, name: 'link', target_id: hub }, h), CellFieldSetOutput);
|
|
357
|
+
}
|
|
358
|
+
const parents = async (limit) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_item_list', { child_id: hub, limit }, h), CellItemListOutput).items.map((i) => i.parent_id);
|
|
359
|
+
const sources = async (limit) => expect_output(await cross_rpc_call(t, rpc_path, 'cell_field_list', { target_id: hub, limit }, h), CellFieldListOutput).fields.map((f) => f.source_id);
|
|
360
|
+
assert.deepStrictEqual(await parents(), ends, 'reverse items, oldest first');
|
|
361
|
+
assert.deepStrictEqual(await parents(1), [ends[0]], 'reverse items, a page of one');
|
|
362
|
+
assert.deepStrictEqual(await sources(), ends, 'reverse fields, oldest first');
|
|
363
|
+
assert.deepStrictEqual(await sources(1), [ends[0]], 'reverse fields, a page of one');
|
|
364
|
+
});
|
|
222
365
|
test_if(capabilities.cell_relations, 'item insert → ordered forward + reverse list → move → idempotent delete', async () => {
|
|
223
366
|
const fixture = await setup_test();
|
|
224
367
|
const owner = await fixture.create_account({ username: 'cell_item_owner' });
|
|
@@ -66,6 +66,11 @@ import { z } from 'zod';
|
|
|
66
66
|
* be replayed from a browser — wire-indistinguishable from sending no
|
|
67
67
|
* credential (the `token` principal is the honored counterpart: it suppresses
|
|
68
68
|
* `Origin`, so the same token authenticates).
|
|
69
|
+
* - `malformed_bearer` — the keeper's bearer api-token with its last character
|
|
70
|
+
* dropped, non-browser context, fresh jar. A value that cannot be a token
|
|
71
|
+
* fails each spine's shape precheck and soft-fails before any database work,
|
|
72
|
+
* so the request arrives anonymous — wire-indistinguishable from sending no
|
|
73
|
+
* credential.
|
|
69
74
|
* - `scoped_token` — the keeper authenticated via a bearer api-token minted
|
|
70
75
|
* with a **narrowed** `TokenScope` (`{kind: 'methods', methods:
|
|
71
76
|
* [SCOPED_TOKEN_ADMITTED_METHOD]}`), non-browser context. Minted through the
|
|
@@ -92,12 +97,13 @@ import { z } from 'zod';
|
|
|
92
97
|
export declare const ConformancePrincipal: z.ZodEnum<{
|
|
93
98
|
keeper: "keeper";
|
|
94
99
|
token: "token";
|
|
95
|
-
anonymous: "anonymous";
|
|
96
100
|
daemon: "daemon";
|
|
97
101
|
invalid_daemon: "invalid_daemon";
|
|
98
102
|
daemon_browser: "daemon_browser";
|
|
99
103
|
bearer_browser: "bearer_browser";
|
|
104
|
+
malformed_bearer: "malformed_bearer";
|
|
100
105
|
scoped_token: "scoped_token";
|
|
106
|
+
anonymous: "anonymous";
|
|
101
107
|
fresh_non_admin: "fresh_non_admin";
|
|
102
108
|
role_holder: "role_holder";
|
|
103
109
|
wrong_role: "wrong_role";
|
|
@@ -122,12 +128,13 @@ export declare const ConformanceCaseRequest: z.ZodObject<{
|
|
|
122
128
|
as: z.ZodEnum<{
|
|
123
129
|
keeper: "keeper";
|
|
124
130
|
token: "token";
|
|
125
|
-
anonymous: "anonymous";
|
|
126
131
|
daemon: "daemon";
|
|
127
132
|
invalid_daemon: "invalid_daemon";
|
|
128
133
|
daemon_browser: "daemon_browser";
|
|
129
134
|
bearer_browser: "bearer_browser";
|
|
135
|
+
malformed_bearer: "malformed_bearer";
|
|
130
136
|
scoped_token: "scoped_token";
|
|
137
|
+
anonymous: "anonymous";
|
|
131
138
|
fresh_non_admin: "fresh_non_admin";
|
|
132
139
|
role_holder: "role_holder";
|
|
133
140
|
wrong_role: "wrong_role";
|
|
@@ -179,12 +186,13 @@ export declare const ConformanceCase: z.ZodObject<{
|
|
|
179
186
|
as: z.ZodEnum<{
|
|
180
187
|
keeper: "keeper";
|
|
181
188
|
token: "token";
|
|
182
|
-
anonymous: "anonymous";
|
|
183
189
|
daemon: "daemon";
|
|
184
190
|
invalid_daemon: "invalid_daemon";
|
|
185
191
|
daemon_browser: "daemon_browser";
|
|
186
192
|
bearer_browser: "bearer_browser";
|
|
193
|
+
malformed_bearer: "malformed_bearer";
|
|
187
194
|
scoped_token: "scoped_token";
|
|
195
|
+
anonymous: "anonymous";
|
|
188
196
|
fresh_non_admin: "fresh_non_admin";
|
|
189
197
|
role_holder: "role_holder";
|
|
190
198
|
wrong_role: "wrong_role";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance_case.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_case.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB
|
|
1
|
+
{"version":3,"file":"conformance_case.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_case.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;EAc/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAE7D,6CAA6C;AAC7C,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;kBAgBjC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE5E,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B;;;;;;;kBA+DrC,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEpF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB;;;kBAK/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAS1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -66,6 +66,11 @@ import { z } from 'zod';
|
|
|
66
66
|
* be replayed from a browser — wire-indistinguishable from sending no
|
|
67
67
|
* credential (the `token` principal is the honored counterpart: it suppresses
|
|
68
68
|
* `Origin`, so the same token authenticates).
|
|
69
|
+
* - `malformed_bearer` — the keeper's bearer api-token with its last character
|
|
70
|
+
* dropped, non-browser context, fresh jar. A value that cannot be a token
|
|
71
|
+
* fails each spine's shape precheck and soft-fails before any database work,
|
|
72
|
+
* so the request arrives anonymous — wire-indistinguishable from sending no
|
|
73
|
+
* credential.
|
|
69
74
|
* - `scoped_token` — the keeper authenticated via a bearer api-token minted
|
|
70
75
|
* with a **narrowed** `TokenScope` (`{kind: 'methods', methods:
|
|
71
76
|
* [SCOPED_TOKEN_ADMITTED_METHOD]}`), non-browser context. Minted through the
|
|
@@ -96,6 +101,7 @@ export const ConformancePrincipal = z.enum([
|
|
|
96
101
|
'daemon_browser',
|
|
97
102
|
'token',
|
|
98
103
|
'bearer_browser',
|
|
104
|
+
'malformed_bearer',
|
|
99
105
|
'scoped_token',
|
|
100
106
|
'anonymous',
|
|
101
107
|
'fresh_non_admin',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,
|
|
1
|
+
{"version":3,"file":"conformance_table.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/conformance_table.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA8B9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAOnE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEN,KAAK,eAAe,EAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAIzD;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,uBAAuB;IACvC,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAmPD;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAIrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,GACzC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,MAAM,KACX,IAQF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GACnC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EACvC,OAAO,MAAM,KACX,IAYF,CAAC;AAwKF;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,uBAAuB,KAAG,IAkEnF,CAAC"}
|
|
@@ -27,9 +27,10 @@ import '../assert_dev_env.js';
|
|
|
27
27
|
import { assert, describe, test } from 'vitest';
|
|
28
28
|
import { DAEMON_TOKEN_HEADER } from '../../auth/daemon_token.js';
|
|
29
29
|
import { find_auth_route, rest_auth_route_suffixes } from '../integration_helpers.js';
|
|
30
|
-
import { find_rpc_action, headers_to_record, rpc_call,
|
|
30
|
+
import { find_rpc_action, headers_to_record, rpc_call, resolve_rpc_endpoints_for_setup } from '../rpc_helpers.js';
|
|
31
31
|
import { account_token_create_action_spec } from '../../auth/account_action_specs.js';
|
|
32
32
|
import { SCOPED_TOKEN_ADMITTED_METHOD } from './conformance_case.js';
|
|
33
|
+
import { mint_narrowed_api_token } from './narrowed_api_token.js';
|
|
33
34
|
import { xfail_until } from './xfail.js';
|
|
34
35
|
/**
|
|
35
36
|
* A deliberately malformed `X-Daemon-Token` value for the `invalid_daemon`
|
|
@@ -61,26 +62,13 @@ const mint_scoped_token = async (fixture, resolved_rpc_endpoints) => {
|
|
|
61
62
|
`on the surface to mint its narrowed token, but the method is not registered on ` +
|
|
62
63
|
`rpc_endpoints.`);
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
// `TokenCreateInput` (so dropping `scope` is a compile error, not a runtime
|
|
66
|
-
// 400 in every test at once), and `result.token` comes back typed and
|
|
67
|
-
// output-validated — no cast, no hand-rolled presence check.
|
|
68
|
-
const res = await rpc_call_for_spec({
|
|
65
|
+
return mint_narrowed_api_token({
|
|
69
66
|
app: fixture.transport,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
scope: { kind: 'methods', methods: [SCOPED_TOKEN_ADMITTED_METHOD] },
|
|
75
|
-
lifetime: { kind: 'eternal' }
|
|
76
|
-
},
|
|
77
|
-
headers: fixture.create_session_headers()
|
|
67
|
+
rpc_path: found.path,
|
|
68
|
+
session_headers: fixture.create_session_headers(),
|
|
69
|
+
methods: [SCOPED_TOKEN_ADMITTED_METHOD],
|
|
70
|
+
name: 'conformance scoped token'
|
|
78
71
|
});
|
|
79
|
-
if (!res.ok) {
|
|
80
|
-
throw new Error(`conformance: minting the 'scoped_token' principal failed with ${res.status}: ` +
|
|
81
|
-
JSON.stringify(res.error));
|
|
82
|
-
}
|
|
83
|
-
return res.result.token;
|
|
84
72
|
};
|
|
85
73
|
/**
|
|
86
74
|
* Map a `ConformancePrincipal` onto the transport + headers it
|
|
@@ -159,6 +147,19 @@ const resolve_principal = async (fixture, as, principals, resolved_rpc_endpoints
|
|
|
159
147
|
transport: fixture.fresh_transport(),
|
|
160
148
|
headers: fixture.create_bearer_headers()
|
|
161
149
|
};
|
|
150
|
+
case 'malformed_bearer': {
|
|
151
|
+
// The keeper's valid token minus its last character — prefixed, one char
|
|
152
|
+
// short — over the same no-Origin, empty-jar transport as `token`, so the
|
|
153
|
+
// shape precheck (not the browser-context discard) is what refuses it.
|
|
154
|
+
const valid = fixture.create_bearer_headers().authorization;
|
|
155
|
+
if (!valid)
|
|
156
|
+
throw new Error(`conformance: principal 'malformed_bearer' found no bearer header`);
|
|
157
|
+
return {
|
|
158
|
+
transport: fixture.fresh_transport({ origin: null }),
|
|
159
|
+
headers: { authorization: valid.slice(0, -1) },
|
|
160
|
+
suppress_default_origin: true
|
|
161
|
+
};
|
|
162
|
+
}
|
|
162
163
|
case 'scoped_token': {
|
|
163
164
|
// A narrowed bearer, otherwise identical to `token` — empty jar + no
|
|
164
165
|
// Origin, so the credential is honored rather than browser-discarded
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { type BackendCapabilities } from './capabilities.js';
|
|
3
|
+
import type { SetupTest } from './setup.js';
|
|
4
|
+
/** Configuration for `describe_credential_deadline_cross_tests`. */
|
|
5
|
+
export interface CredentialDeadlineCrossTestOptions {
|
|
6
|
+
/** Per-test fixture producer (`default_cross_process_setup(handle)`). */
|
|
7
|
+
readonly setup_test: SetupTest;
|
|
8
|
+
/** Backend capability flags — the WebSocket cases gate on `ws`, the stream case on `sse`. */
|
|
9
|
+
readonly capabilities: BackendCapabilities;
|
|
10
|
+
/** Base URL the backend is reachable at. */
|
|
11
|
+
readonly base_url: string;
|
|
12
|
+
/** The WebSocket endpoint path (e.g. `/api/ws`). */
|
|
13
|
+
readonly ws_path: string;
|
|
14
|
+
/** The audit-log stream path. Default `/api/admin/audit/stream`. */
|
|
15
|
+
readonly sse_path?: string;
|
|
16
|
+
/** JSON-RPC endpoint path. Default `SPINE_RPC_PATH`. */
|
|
17
|
+
readonly rpc_path?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Register the credential-deadline suite: a WebSocket on a session or an API
|
|
21
|
+
* token closes with 4001 "Credential expired" at its credential's expiry, an
|
|
22
|
+
* audit stream on a session ends there, and the expiry seam never extends.
|
|
23
|
+
*/
|
|
24
|
+
export declare const describe_credential_deadline_cross_tests: (options: CredentialDeadlineCrossTestOptions) => void;
|
|
25
|
+
//# sourceMappingURL=credential_deadline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential_deadline.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/credential_deadline.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmD9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAmC,MAAM,YAAY,CAAC;AAsB7E,oEAAoE;AACpE,MAAM,WAAW,kCAAkC;IAClD,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,6FAA6F;IAC7F,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,eAAO,MAAM,wCAAwC,GACpD,SAAS,kCAAkC,KACzC,IAoKF,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-process **credential deadline** suite — a long-lived connection
|
|
4
|
+
* closes when the credential it was opened with expires.
|
|
5
|
+
*
|
|
6
|
+
* A WebSocket or an audit stream is authorized once, at open, and closed on
|
|
7
|
+
* revocation by identity — but expiry writes nothing a revocation listener
|
|
8
|
+
* could hear. So each spine carries the credential's deadline onto the
|
|
9
|
+
* connection (`ResolvedAuth.credential_deadline`: the earlier of a session's
|
|
10
|
+
* cookie and row expiry, an API token's row expiry) and closes it there: a
|
|
11
|
+
* socket with `WS_CLOSE_SESSION_REVOKED` and
|
|
12
|
+
* `WS_CLOSE_CREDENTIAL_EXPIRED_REASON`, a stream by ending it as a revoked one
|
|
13
|
+
* ends.
|
|
14
|
+
*
|
|
15
|
+
* Each case pulls a fresh account's credential expiries in to
|
|
16
|
+
* `expiry_seconds` from now with the daemon-token
|
|
17
|
+
* `_testing_shorten_credential_expiry` seam, **before** opening the
|
|
18
|
+
* connection (the deadline is read when the credential resolves), then checks
|
|
19
|
+
* the connection is open short of the deadline and closed at it:
|
|
20
|
+
*
|
|
21
|
+
* 1. WebSocket × session — 4001, "Credential expired".
|
|
22
|
+
* 2. WebSocket × API token — the same.
|
|
23
|
+
* 3. Audit stream × session — the stream ends.
|
|
24
|
+
* 4. The seam only shortens: a second call asking for a later expiry moves
|
|
25
|
+
* nothing, and the socket still closes at the first deadline.
|
|
26
|
+
*
|
|
27
|
+
* The fourth reachable combination, API token × audit stream, is not here:
|
|
28
|
+
* the stock audit stream admits sessions only on both spines. Each spine
|
|
29
|
+
* covers it below the wire — the Rust stream route on a widened credential
|
|
30
|
+
* gate, the TS deadline wait shared by both transports.
|
|
31
|
+
*
|
|
32
|
+
* Gated on `capabilities.ws` (cases 1, 2, 4) and `capabilities.sse` (case 3);
|
|
33
|
+
* cross-process only — the seam is mounted on the spine test binaries.
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
import { assert, describe } from 'vitest';
|
|
38
|
+
import { heartbeat_action_spec } from '../../actions/heartbeat.js';
|
|
39
|
+
import { WS_CLOSE_CREDENTIAL_EXPIRED_REASON, WS_CLOSE_SESSION_REVOKED } from '../../actions/transports.js';
|
|
40
|
+
import { ROLE_ADMIN } from '../../auth/role_schema.js';
|
|
41
|
+
import { SSE_CONNECTED_COMMENT } from '../../realtime/sse_constants.js';
|
|
42
|
+
import { rpc_call_non_browser } from '../rpc_helpers.js';
|
|
43
|
+
import { create_sse_transport } from '../transports/sse_transport.js';
|
|
44
|
+
import { create_admitted_ws_transport } from '../transports/ws_transport.js';
|
|
45
|
+
import { test_if } from './capabilities.js';
|
|
46
|
+
import { SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
47
|
+
/** Default audit-log stream path. */
|
|
48
|
+
const DEFAULT_SSE_PATH = '/api/admin/audit/stream';
|
|
49
|
+
/** The shortened lifetime each case gives its credentials, in seconds. */
|
|
50
|
+
const EXPIRY_SECONDS = 2;
|
|
51
|
+
/**
|
|
52
|
+
* How long past the deadline a close may take before the case fails — wide
|
|
53
|
+
* for a loaded machine, and bounded so a missing close fails rather than
|
|
54
|
+
* hangs.
|
|
55
|
+
*/
|
|
56
|
+
const CLOSE_GRACE_MS = 4000;
|
|
57
|
+
/**
|
|
58
|
+
* The deadline counts from the seam's call, and the connection opens after
|
|
59
|
+
* it; a close sooner than this after the call came before the deadline.
|
|
60
|
+
*/
|
|
61
|
+
const EARLIEST_CLOSE_MS = EXPIRY_SECONDS * 1000 - 300;
|
|
62
|
+
/**
|
|
63
|
+
* Register the credential-deadline suite: a WebSocket on a session or an API
|
|
64
|
+
* token closes with 4001 "Credential expired" at its credential's expiry, an
|
|
65
|
+
* audit stream on a session ends there, and the expiry seam never extends.
|
|
66
|
+
*/
|
|
67
|
+
export const describe_credential_deadline_cross_tests = (options) => {
|
|
68
|
+
const { setup_test, capabilities, base_url, ws_path, sse_path = DEFAULT_SSE_PATH, rpc_path = SPINE_RPC_PATH } = options;
|
|
69
|
+
/**
|
|
70
|
+
* Pull `account`'s credential expiries in to `seconds` from now through the
|
|
71
|
+
* daemon-token seam, returning when it was called.
|
|
72
|
+
*/
|
|
73
|
+
const shorten = async (fixture, account, seconds) => {
|
|
74
|
+
const called_at = performance.now();
|
|
75
|
+
const result = await rpc_call_non_browser({
|
|
76
|
+
app: fixture.fresh_transport({ origin: null }),
|
|
77
|
+
path: rpc_path,
|
|
78
|
+
method: '_testing_shorten_credential_expiry',
|
|
79
|
+
params: { account_id: account.account.id, seconds },
|
|
80
|
+
headers: fixture.create_daemon_token_headers()
|
|
81
|
+
});
|
|
82
|
+
if (!result.ok) {
|
|
83
|
+
assert.fail(`_testing_shorten_credential_expiry failed: ${JSON.stringify(result)}`);
|
|
84
|
+
}
|
|
85
|
+
const counts = result.result;
|
|
86
|
+
return { called_at, ...counts };
|
|
87
|
+
};
|
|
88
|
+
const cookie_of = (account) => {
|
|
89
|
+
const { cookie } = account.create_session_headers();
|
|
90
|
+
assert.ok(cookie, 'expected a session cookie');
|
|
91
|
+
return cookie;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Assert `client` is open short of the deadline, then closes with 4001
|
|
95
|
+
* "Credential expired" at it.
|
|
96
|
+
*/
|
|
97
|
+
const assert_closes_at_deadline = async (client, called_at) => {
|
|
98
|
+
// open and dispatching before the deadline
|
|
99
|
+
assert.deepStrictEqual(await client.request(1, heartbeat_action_spec.method, {}), {});
|
|
100
|
+
assert.ok(await client.wait_for_close(EXPIRY_SECONDS * 1000 + CLOSE_GRACE_MS), 'the socket did not close at its credential deadline');
|
|
101
|
+
const elapsed = performance.now() - called_at;
|
|
102
|
+
assert.ok(elapsed >= EARLIEST_CLOSE_MS, `closed ${Math.round(elapsed)}ms in, before the deadline`);
|
|
103
|
+
assert.strictEqual(client.close_code, WS_CLOSE_SESSION_REVOKED);
|
|
104
|
+
assert.strictEqual(client.close_reason, WS_CLOSE_CREDENTIAL_EXPIRED_REASON);
|
|
105
|
+
};
|
|
106
|
+
describe('credential deadline (cross-process)', () => {
|
|
107
|
+
test_if(capabilities.ws, 'a session socket closes with 4001 "Credential expired" at the deadline', async () => {
|
|
108
|
+
const fixture = await setup_test();
|
|
109
|
+
const account = await fixture.create_account();
|
|
110
|
+
const { called_at, sessions } = await shorten(fixture, account, EXPIRY_SECONDS);
|
|
111
|
+
assert.ok(sessions >= 1, 'the seam shortened the account session');
|
|
112
|
+
const client = await create_admitted_ws_transport({
|
|
113
|
+
base_url,
|
|
114
|
+
ws_path,
|
|
115
|
+
cookies: [cookie_of(account)],
|
|
116
|
+
origin: base_url
|
|
117
|
+
});
|
|
118
|
+
try {
|
|
119
|
+
await assert_closes_at_deadline(client, called_at);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
await client.close();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
test_if(capabilities.ws, 'an API token socket closes with 4001 "Credential expired" at the deadline', async () => {
|
|
126
|
+
const fixture = await setup_test();
|
|
127
|
+
const account = await fixture.create_account();
|
|
128
|
+
const { called_at, api_tokens } = await shorten(fixture, account, EXPIRY_SECONDS);
|
|
129
|
+
assert.ok(api_tokens >= 1, 'the seam shortened the account API token');
|
|
130
|
+
// a bearer outside a browser context: no Origin, no cookie
|
|
131
|
+
const client = await create_admitted_ws_transport({
|
|
132
|
+
base_url,
|
|
133
|
+
ws_path,
|
|
134
|
+
cookies: [],
|
|
135
|
+
origin: null,
|
|
136
|
+
headers: { Authorization: `Bearer ${account.api_token}` }
|
|
137
|
+
});
|
|
138
|
+
try {
|
|
139
|
+
await assert_closes_at_deadline(client, called_at);
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
await client.close();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
test_if(capabilities.sse, 'a session audit stream ends at the deadline', async () => {
|
|
146
|
+
const fixture = await setup_test();
|
|
147
|
+
const account = await fixture.create_account({ roles: [ROLE_ADMIN] });
|
|
148
|
+
const { called_at } = await shorten(fixture, account, EXPIRY_SECONDS);
|
|
149
|
+
const sse = await create_sse_transport({
|
|
150
|
+
base_url,
|
|
151
|
+
sse_path,
|
|
152
|
+
cookies: [cookie_of(account)],
|
|
153
|
+
origin: base_url
|
|
154
|
+
});
|
|
155
|
+
try {
|
|
156
|
+
assert.strictEqual((await sse.read_frame()) + '\n\n', SSE_CONNECTED_COMMENT, 'the stream opened');
|
|
157
|
+
assert.ok(await sse.wait_for_close(EXPIRY_SECONDS * 1000 + CLOSE_GRACE_MS), 'the stream did not end at its credential deadline');
|
|
158
|
+
const elapsed = performance.now() - called_at;
|
|
159
|
+
assert.ok(elapsed >= EARLIEST_CLOSE_MS, `ended ${Math.round(elapsed)}ms in, before the deadline`);
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
await sse.close();
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
test_if(capabilities.ws, 'the expiry seam only shortens: a later expiry moves nothing', async () => {
|
|
166
|
+
const fixture = await setup_test();
|
|
167
|
+
const account = await fixture.create_account();
|
|
168
|
+
const { called_at } = await shorten(fixture, account, EXPIRY_SECONDS);
|
|
169
|
+
// asks for a minute; the credentials stay due at the first deadline
|
|
170
|
+
await shorten(fixture, account, 60);
|
|
171
|
+
const client = await create_admitted_ws_transport({
|
|
172
|
+
base_url,
|
|
173
|
+
ws_path,
|
|
174
|
+
cookies: [cookie_of(account)],
|
|
175
|
+
origin: base_url
|
|
176
|
+
});
|
|
177
|
+
try {
|
|
178
|
+
await assert_closes_at_deadline(client, called_at);
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
await client.close();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAA4B,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAYhG,2CAA2C;AAC3C,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,yBAAyB,KAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAKtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,
|
|
1
|
+
{"version":3,"file":"default_backend_configs.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/default_backend_configs.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAA4B,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAYhG,2CAA2C;AAC3C,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,SAAS,yBAAyB,KAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAKtB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAyCpC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,mBAkCtC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,iBAInC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,EAAE,iBAIrC,CAAC;AAeH,MAAM,WAAW,iCAAiC;IACjD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,mDAAmD;IACnD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,6CAA6C;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,GAC1C,MAAM,iCAAiC,KACrC,aAoCF,CAAC;AAEF,MAAM,WAAW,mCAAmC;IACnD,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,+CAA+C;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC/D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,mCAAmC,KACvC,aA4CF,CAAC"}
|
|
@@ -53,7 +53,13 @@ export const ts_default_capabilities = Object.freeze({
|
|
|
53
53
|
// Off by default — the `cell_gated_create` test policy is a fuz_app test
|
|
54
54
|
// fixture, mounted only on the spine binaries (`ts_spine_*`), not generic
|
|
55
55
|
// consumers.
|
|
56
|
-
cell_gated_create: false
|
|
56
|
+
cell_gated_create: false,
|
|
57
|
+
// Off by default — the second, role-gated WS endpoint is a spine-binary
|
|
58
|
+
// mount (`SPINE_ROLE_GATED_WS_PATH`); fuz_app's spine configs opt in.
|
|
59
|
+
ws_role_gated: false,
|
|
60
|
+
// Off by default: `@hono/node-ws` drops a refused upgrade's body, so a
|
|
61
|
+
// Node-served consumer sends none. A Deno- or Bun-served one may opt in.
|
|
62
|
+
ws_refusal_body: false
|
|
57
63
|
});
|
|
58
64
|
/**
|
|
59
65
|
* Capabilities for the Rust family. The non-gating wiring facts live in
|
|
@@ -88,7 +94,12 @@ export const rust_default_capabilities = Object.freeze({
|
|
|
88
94
|
ws_self_revocation_reply: false,
|
|
89
95
|
// Off by default — the `testing_spine_stub` preset opts in (it mounts the
|
|
90
96
|
// `TestCellGatedCreateAuthorize` policy); a generic Rust consumer doesn't.
|
|
91
|
-
cell_gated_create: false
|
|
97
|
+
cell_gated_create: false,
|
|
98
|
+
// Off by default — the `testing_spine_stub` preset opts in (it mounts the
|
|
99
|
+
// role-gated WS endpoint at `SPINE_ROLE_GATED_WS_PATH`).
|
|
100
|
+
ws_role_gated: false,
|
|
101
|
+
// axum sends a refused upgrade's response as built, body included.
|
|
102
|
+
ws_refusal_body: true
|
|
92
103
|
});
|
|
93
104
|
/**
|
|
94
105
|
* Shape notes for TS-family backends — wiring facts, not gating flags.
|
|
@@ -76,7 +76,6 @@
|
|
|
76
76
|
"role",
|
|
77
77
|
"scope_id"
|
|
78
78
|
],
|
|
79
|
-
"cell_history": ["action_id", "cell_id", "created_at", "fact_hash", "id"],
|
|
80
79
|
"cell_item": ["child_id", "created_at", "parent_id", "position"],
|
|
81
80
|
"fact": ["bytes", "content_type", "created_at", "external_url", "hash", "size"],
|
|
82
81
|
"fact_ref": ["source_hash", "target_hash"],
|
|
@@ -14,6 +14,9 @@ import '../assert_dev_env.js';
|
|
|
14
14
|
* - the `_testing_*` daemon-token backdoors (`create_testing_actions`),
|
|
15
15
|
* - the full cell verb set (CRUD + grant + field + item + audit),
|
|
16
16
|
* - the opt-in `actor_lookup` / `actor_search` resolvers,
|
|
17
|
+
* - the `_testing_origin_check` probe the origin conformance table drives,
|
|
18
|
+
* - the `_testing_shorten_credential_expiry` seam the credential-deadline
|
|
19
|
+
* suite drives,
|
|
17
20
|
* - the `_testing_action_manifest` backdoor, appended last (it dumps the live
|
|
18
21
|
* method set for the cross-impl manifest-parity gate, so it must enumerate
|
|
19
22
|
* every method above it).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"full_spine_mount.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/full_spine_mount.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B
|
|
1
|
+
{"version":3,"file":"full_spine_mount.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/full_spine_mount.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAG5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAcvF,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CA0CjB,CAAC;AAEF,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,qBAAqB,KAC5B,cAUF,CAAC"}
|