@fuzdev/fuz_app 0.55.0 → 0.56.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 +211 -155
- package/dist/actions/action_bridge.d.ts +8 -5
- package/dist/actions/action_bridge.d.ts.map +1 -1
- package/dist/actions/action_bridge.js +1 -11
- package/dist/actions/action_codegen.d.ts +19 -0
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +20 -14
- package/dist/actions/action_registry.d.ts.map +1 -1
- package/dist/actions/action_registry.js +5 -2
- package/dist/actions/action_rpc.d.ts +110 -44
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +92 -287
- package/dist/actions/action_spec.d.ts +55 -16
- package/dist/actions/action_spec.d.ts.map +1 -1
- package/dist/actions/action_spec.js +16 -11
- package/dist/actions/action_types.d.ts +28 -60
- package/dist/actions/action_types.d.ts.map +1 -1
- package/dist/actions/action_types.js +13 -5
- package/dist/actions/broadcast_api.d.ts +2 -2
- package/dist/actions/broadcast_api.js +2 -2
- package/dist/actions/compile_action_registry.d.ts +50 -0
- package/dist/actions/compile_action_registry.d.ts.map +1 -0
- package/dist/actions/compile_action_registry.js +69 -0
- package/dist/actions/heartbeat.d.ts +8 -4
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +5 -4
- package/dist/actions/perform_action.d.ts +145 -0
- package/dist/actions/perform_action.d.ts.map +1 -0
- package/dist/actions/perform_action.js +258 -0
- package/dist/actions/register_action_ws.d.ts +44 -38
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +101 -159
- package/dist/actions/register_ws_endpoint.d.ts +2 -10
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +32 -10
- package/dist/actions/transports_ws_auth_guard.d.ts +1 -1
- package/dist/actions/transports_ws_auth_guard.js +1 -1
- package/dist/actions/transports_ws_backend.d.ts +1 -1
- package/dist/actions/transports_ws_backend.js +1 -1
- package/dist/auth/CLAUDE.md +673 -442
- package/dist/auth/account_action_specs.d.ts +28 -7
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +7 -7
- package/dist/auth/account_actions.d.ts +8 -14
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +26 -32
- package/dist/auth/account_queries.d.ts +46 -13
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +73 -33
- package/dist/auth/account_routes.d.ts +4 -3
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +58 -33
- package/dist/auth/account_schema.d.ts +46 -54
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +21 -48
- package/dist/auth/admin_action_specs.d.ts +55 -21
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +42 -26
- package/dist/auth/admin_actions.d.ts +14 -21
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +47 -44
- package/dist/auth/audit_emitter.d.ts +160 -0
- package/dist/auth/audit_emitter.d.ts.map +1 -0
- package/dist/auth/audit_emitter.js +83 -0
- package/dist/auth/audit_log_queries.d.ts +17 -87
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +17 -96
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +7 -3
- package/dist/auth/audit_log_schema.d.ts +48 -42
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +56 -43
- package/dist/auth/auth_guard_resolver.d.ts +44 -0
- package/dist/auth/auth_guard_resolver.d.ts.map +1 -0
- package/dist/auth/auth_guard_resolver.js +56 -0
- package/dist/auth/bootstrap_account.d.ts +7 -7
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +7 -7
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +11 -10
- package/dist/auth/cleanup.d.ts +20 -26
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +33 -47
- package/dist/auth/credential_type_schema.d.ts +115 -0
- package/dist/auth/credential_type_schema.d.ts.map +1 -0
- package/dist/auth/credential_type_schema.js +127 -0
- package/dist/auth/daemon_token_middleware.d.ts +1 -1
- package/dist/auth/daemon_token_middleware.js +3 -3
- package/dist/auth/ddl.d.ts +2 -2
- package/dist/auth/ddl.d.ts.map +1 -1
- package/dist/auth/ddl.js +6 -6
- package/dist/auth/deps.d.ts +7 -32
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/grant_path_schema.d.ts +117 -0
- package/dist/auth/grant_path_schema.d.ts.map +1 -0
- package/dist/auth/grant_path_schema.js +137 -0
- package/dist/auth/invite_queries.d.ts +12 -1
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +12 -1
- package/dist/auth/invite_schema.d.ts +1 -1
- package/dist/auth/invite_schema.d.ts.map +1 -1
- package/dist/auth/invite_schema.js +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +5 -2
- package/dist/auth/migrations.d.ts +22 -7
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +64 -25
- package/dist/auth/request_context.d.ts +157 -170
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +224 -268
- package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +130 -100
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_action_specs.js +262 -0
- package/dist/auth/role_grant_offer_actions.d.ts +104 -0
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -0
- package/dist/auth/{permit_offer_actions.js → role_grant_offer_actions.js} +153 -140
- package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +80 -70
- package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_notifications.js +182 -0
- package/dist/auth/{permit_offer_queries.d.ts → role_grant_offer_queries.d.ts} +64 -64
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
- package/dist/auth/{permit_offer_queries.js → role_grant_offer_queries.js} +136 -123
- package/dist/auth/role_grant_offer_schema.d.ts +150 -0
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -0
- package/dist/auth/{permit_offer_schema.js → role_grant_offer_schema.js} +55 -36
- package/dist/auth/role_grant_queries.d.ts +231 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -0
- package/dist/auth/role_grant_queries.js +320 -0
- package/dist/auth/role_schema.d.ts +150 -40
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +144 -45
- package/dist/auth/scope_kind_schema.d.ts +96 -0
- package/dist/auth/scope_kind_schema.d.ts.map +1 -0
- package/dist/auth/scope_kind_schema.js +94 -0
- package/dist/auth/self_service_role_action_specs.d.ts +4 -1
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +2 -2
- package/dist/auth/self_service_role_actions.d.ts +35 -29
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +58 -48
- package/dist/auth/session_cookie.d.ts +43 -6
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +31 -5
- package/dist/auth/session_middleware.d.ts +37 -3
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +33 -7
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +48 -19
- package/dist/auth/standard_action_specs.d.ts +2 -2
- package/dist/auth/standard_action_specs.js +4 -4
- package/dist/auth/standard_rpc_actions.d.ts +23 -19
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +12 -12
- package/dist/db/migrate.d.ts +1 -1
- package/dist/db/migrate.js +1 -1
- package/dist/dev/setup.d.ts +2 -2
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +4 -4
- package/dist/env/load.d.ts +1 -1
- package/dist/env/load.js +1 -1
- package/dist/hono_context.d.ts +27 -45
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +14 -28
- package/dist/http/CLAUDE.md +235 -121
- package/dist/http/auth_shape.d.ts +191 -0
- package/dist/http/auth_shape.d.ts.map +1 -0
- package/dist/http/auth_shape.js +237 -0
- package/dist/http/common_routes.js +3 -3
- package/dist/http/db_routes.d.ts +4 -0
- package/dist/http/db_routes.d.ts.map +1 -1
- package/dist/http/db_routes.js +44 -7
- package/dist/http/error_schemas.d.ts +56 -34
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +63 -28
- package/dist/http/pending_effects.d.ts +71 -18
- package/dist/http/pending_effects.d.ts.map +1 -1
- package/dist/http/pending_effects.js +87 -18
- package/dist/http/proxy.d.ts +52 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +92 -14
- package/dist/http/route_spec.d.ts +89 -75
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +54 -72
- package/dist/http/schema_helpers.d.ts +3 -14
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +2 -14
- package/dist/http/surface.d.ts +2 -10
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +3 -4
- package/dist/http/surface_query.d.ts +39 -35
- package/dist/http/surface_query.d.ts.map +1 -1
- package/dist/http/surface_query.js +79 -36
- package/dist/primitive_schemas.d.ts +39 -0
- package/dist/primitive_schemas.d.ts.map +1 -0
- package/dist/primitive_schemas.js +40 -0
- package/dist/realtime/sse_auth_guard.d.ts +5 -5
- package/dist/realtime/sse_auth_guard.js +9 -9
- package/dist/runtime/mock.d.ts +1 -1
- package/dist/runtime/mock.js +1 -1
- package/dist/server/app_backend.d.ts +14 -11
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +12 -8
- package/dist/server/app_server.d.ts +7 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +35 -40
- package/dist/server/validate_nginx.d.ts +1 -1
- package/dist/server/validate_nginx.js +1 -1
- package/dist/testing/CLAUDE.md +50 -38
- package/dist/testing/admin_integration.d.ts +5 -6
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +87 -85
- package/dist/testing/app_server.d.ts +11 -14
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +16 -15
- package/dist/testing/assertions.d.ts.map +1 -1
- package/dist/testing/assertions.js +2 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +15 -9
- package/dist/testing/audit_completeness.d.ts +2 -2
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +36 -36
- package/dist/testing/auth_apps.d.ts +5 -4
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +22 -19
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +5 -5
- package/dist/testing/db.d.ts +1 -1
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +4 -4
- package/dist/testing/db_entities.d.ts +22 -0
- package/dist/testing/db_entities.d.ts.map +1 -0
- package/dist/testing/db_entities.js +28 -0
- package/dist/testing/entities.d.ts +8 -7
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +21 -18
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +13 -14
- package/dist/testing/integration_helpers.d.ts +4 -4
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +20 -18
- package/dist/testing/middleware.d.ts +4 -4
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +12 -11
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +40 -24
- package/dist/testing/rpc_round_trip.d.ts +1 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +14 -13
- package/dist/testing/sse_round_trip.d.ts +3 -4
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +7 -11
- package/dist/testing/standard.d.ts +1 -1
- package/dist/testing/stubs.d.ts +25 -0
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +43 -2
- package/dist/testing/surface_invariants.d.ts +2 -2
- package/dist/testing/ws_round_trip.d.ts +12 -13
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +19 -11
- package/dist/ui/AdminAccounts.svelte +23 -20
- package/dist/ui/AdminOverview.svelte +15 -13
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
- package/dist/ui/BootstrapForm.svelte +1 -1
- package/dist/ui/CLAUDE.md +60 -60
- package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +27 -26
- package/dist/ui/{PermitOfferForm.svelte.d.ts → RoleGrantOfferForm.svelte.d.ts} +7 -7
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
- package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
- package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
- package/dist/ui/SignupForm.svelte +1 -1
- package/dist/ui/SurfaceExplorer.svelte +35 -15
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +2 -3
- package/dist/ui/admin_accounts_state.svelte.d.ts +18 -18
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +16 -16
- package/dist/ui/admin_rpc_adapters.d.ts +20 -20
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +17 -17
- package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
- package/dist/ui/admin_sessions_state.svelte.js +2 -2
- package/dist/ui/audit_log_state.svelte.d.ts +7 -7
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +6 -6
- package/dist/ui/auth_state.svelte.d.ts +3 -3
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +6 -6
- package/dist/ui/format_scope.d.ts +2 -2
- package/dist/ui/format_scope.js +2 -2
- package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +30 -30
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
- package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +18 -18
- package/dist/ui/ui_format.js +2 -2
- package/package.json +3 -3
- package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
- package/dist/auth/permit_offer_action_specs.js +0 -258
- package/dist/auth/permit_offer_actions.d.ts +0 -110
- package/dist/auth/permit_offer_actions.d.ts.map +0 -1
- package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
- package/dist/auth/permit_offer_notifications.js +0 -182
- package/dist/auth/permit_offer_queries.d.ts.map +0 -1
- package/dist/auth/permit_offer_schema.d.ts +0 -125
- package/dist/auth/permit_offer_schema.d.ts.map +0 -1
- package/dist/auth/permit_queries.d.ts +0 -222
- package/dist/auth/permit_queries.d.ts.map +0 -1
- package/dist/auth/permit_queries.js +0 -305
- package/dist/auth/require_keeper.d.ts +0 -20
- package/dist/auth/require_keeper.d.ts.map +0 -1
- package/dist/auth/require_keeper.js +0 -35
- package/dist/auth/route_guards.d.ts +0 -27
- package/dist/auth/route_guards.d.ts.map +0 -1
- package/dist/auth/route_guards.js +0 -38
- package/dist/auth/session_lifecycle.d.ts +0 -37
- package/dist/auth/session_lifecycle.d.ts.map +0 -1
- package/dist/auth/session_lifecycle.js +0 -29
- package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
- package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
|
@@ -2,63 +2,64 @@
|
|
|
2
2
|
* WebSocket JSON-RPC dispatch — the low-level WS transport binding.
|
|
3
3
|
*
|
|
4
4
|
* Most consumers should mount WS endpoints via `register_ws_endpoint`
|
|
5
|
-
* (`actions/register_ws_endpoint.ts`), which wraps this function with the
|
|
6
|
-
* upgrade stack (origin check + auth + optional role). This
|
|
7
|
-
* exported as the lower-level entry point for tests that
|
|
8
|
-
* dispatcher directly via `create_ws_test_harness`.
|
|
5
|
+
* (`actions/register_ws_endpoint.ts`), which wraps this function with the
|
|
6
|
+
* standard upgrade stack (origin check + auth + optional role). This
|
|
7
|
+
* module stays exported as the lower-level entry point for tests that
|
|
8
|
+
* drive the dispatcher directly via `create_ws_test_harness`.
|
|
9
9
|
*
|
|
10
10
|
* Symmetric to `create_rpc_endpoint` (from `actions/action_rpc.ts`):
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* socket-scoped `ctx.notify` + per-socket `ctx.signal`. See
|
|
19
|
-
* `BackendWebsocketTransport.send` for broadcast.
|
|
11
|
+
* both transports parse their wire envelope, then call the shared
|
|
12
|
+
* `perform_action` core (`actions/perform_action.ts`) for the post-parse
|
|
13
|
+
* pipeline. WS-specific concerns — connection lifecycle, heartbeat,
|
|
14
|
+
* cancel-notification interception, socket-scoped notify — stay in this
|
|
15
|
+
* module; everything else (auth gates, input validation, authorization
|
|
16
|
+
* phase, rate limiting, transactional dispatch, DEV output validation,
|
|
17
|
+
* thrown-error normalization) is shared.
|
|
20
18
|
*
|
|
21
19
|
* ## Auth expectations
|
|
22
20
|
*
|
|
23
|
-
* The consumer is responsible for rejecting unauthenticated upgrades
|
|
24
|
-
* routing to this handler (fuz_app's `require_auth` middleware,
|
|
25
|
-
* `register_ws_endpoint` which wires it for you).
|
|
26
|
-
* `
|
|
27
|
-
*
|
|
21
|
+
* The consumer is responsible for rejecting unauthenticated upgrades
|
|
22
|
+
* *before* routing to this handler (fuz_app's `require_auth` middleware,
|
|
23
|
+
* or `register_ws_endpoint` which wires it for you). Per-action auth
|
|
24
|
+
* runs inside `perform_action` on every message via the same gates HTTP
|
|
25
|
+
* RPC uses.
|
|
28
26
|
*
|
|
29
27
|
* @module
|
|
30
28
|
*/
|
|
31
|
-
import { DEV } from 'esm-env';
|
|
32
29
|
import { wait } from '@fuzdev/fuz_util/async.js';
|
|
33
30
|
import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
34
|
-
import {
|
|
31
|
+
import { get_request_context, require_request_context, } from '../auth/request_context.js';
|
|
35
32
|
import { hash_session_token } from '../auth/session_queries.js';
|
|
36
|
-
import { ROLE_KEEPER } from '../auth/role_schema.js';
|
|
37
33
|
import { get_client_ip } from '../http/proxy.js';
|
|
38
|
-
import {
|
|
39
|
-
import { jsonrpc_error_messages
|
|
40
|
-
import { create_jsonrpc_error_response,
|
|
41
|
-
import { CREDENTIAL_TYPE_KEY, AUTH_API_TOKEN_ID_KEY } from '../hono_context.js';
|
|
34
|
+
import { flush_pending_effects, flush_post_commit_effects } from '../http/pending_effects.js';
|
|
35
|
+
import { jsonrpc_error_messages } from '../http/jsonrpc_errors.js';
|
|
36
|
+
import { create_jsonrpc_error_response, create_jsonrpc_notification, to_jsonrpc_message_id, to_jsonrpc_params, is_jsonrpc_request, } from '../http/jsonrpc_helpers.js';
|
|
37
|
+
import { CREDENTIAL_TYPE_KEY, AUTH_API_TOKEN_ID_KEY, TEST_CONTEXT_PRESET_KEY, } from '../hono_context.js';
|
|
42
38
|
import {} from './action_types.js';
|
|
39
|
+
import { compile_action_registry } from './compile_action_registry.js';
|
|
43
40
|
import { cancel_action_spec, CancelNotificationParams } from './cancel.js';
|
|
44
41
|
import { WS_CLOSE_SERVER_HEARTBEAT_TIMEOUT } from './transports.js';
|
|
45
42
|
import { BackendWebsocketTransport } from './transports_ws_backend.js';
|
|
43
|
+
import { perform_action, perform_action_result_to_envelope } from './perform_action.js';
|
|
46
44
|
/** Default inactivity window before the server closes a silent socket. */
|
|
47
45
|
export const DEFAULT_SERVER_HEARTBEAT_TIMEOUT = 60_000;
|
|
48
46
|
/**
|
|
49
|
-
* Mount a JSON-RPC WebSocket endpoint that dispatches
|
|
50
|
-
*
|
|
51
|
-
* `RegisterActionWsOptions.extend_context`.
|
|
47
|
+
* Mount a JSON-RPC WebSocket endpoint that dispatches via the shared
|
|
48
|
+
* `perform_action` core.
|
|
52
49
|
*
|
|
53
50
|
* Wire behavior:
|
|
54
51
|
* - Batch JSON-RPC is rejected (single-message only).
|
|
55
52
|
* - Notifications (method + no id) are silently dropped per JSON-RPC spec.
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
53
|
+
* Exception: `cancel` notifications abort the matching pending request's
|
|
54
|
+
* `ctx.signal` before bubbling out.
|
|
55
|
+
* - Per-message dispatch goes through `perform_action`: pre-validation
|
|
56
|
+
* auth (401) → input validation (400) → authorization phase →
|
|
57
|
+
* post-authorization auth (403) → rate limit (429) → handler (with
|
|
58
|
+
* transaction wrap iff `spec.side_effects: true`) → DEV output validation.
|
|
59
|
+
* - Authorization phase runs **per message** — role_grant changes during a
|
|
60
|
+
* connection lifetime are picked up on the next message without any
|
|
61
|
+
* in-place refresh. Authentication invalidation closes the socket via
|
|
62
|
+
* `create_ws_auth_guard`.
|
|
62
63
|
*
|
|
63
64
|
* @returns the transport (supplied or freshly created) — retain it to wire
|
|
64
65
|
* `create_ws_auth_guard` or broadcast on audit events.
|
|
@@ -67,23 +68,14 @@ export const DEFAULT_SERVER_HEARTBEAT_TIMEOUT = 60_000;
|
|
|
67
68
|
* in the transport's internal maps via `add_connection` / `remove_connection`
|
|
68
69
|
*/
|
|
69
70
|
export const register_action_ws = (options) => {
|
|
70
|
-
const { path, app, upgradeWebSocket, actions,
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (action.handler)
|
|
79
|
-
handlers[action.spec.method] = action.handler;
|
|
80
|
-
// Reject account-keyed rate limiting on public actions — the dispatcher
|
|
81
|
-
// has no actor to key on. Mirrors the HTTP RPC registration check.
|
|
82
|
-
if ((action.spec.rate_limit === 'account' || action.spec.rate_limit === 'both') &&
|
|
83
|
-
action.spec.auth === 'public') {
|
|
84
|
-
throw new Error(`WS action "${action.spec.method}" declares rate_limit: '${action.spec.rate_limit}' but auth: 'public' — no actor available for account-keyed limiting. Use 'ip' or change auth.`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
71
|
+
const { path, app, upgradeWebSocket, actions, db, transport = new BackendWebsocketTransport(), heartbeat = true, artificial_delay = 0, log = new Logger('[ws]'), on_socket_open, on_socket_close, action_ip_rate_limiter = null, action_account_rate_limiter = null, } = options;
|
|
72
|
+
// Build the dispatcher's per-method lookup. Only request_response
|
|
73
|
+
// specs with a handler reach `action_map` — perform_action is the
|
|
74
|
+
// only site that calls handlers, and it requires an `RpcAction`.
|
|
75
|
+
// Other kinds (`remote_notification` like `cancel`, `local_call`)
|
|
76
|
+
// are registry-only on WS; the cancel handler reads
|
|
77
|
+
// `cancel_action_spec.method` directly.
|
|
78
|
+
const { action_map } = compile_action_registry(actions, 'WS action');
|
|
87
79
|
const heartbeat_enabled = heartbeat !== false;
|
|
88
80
|
const heartbeat_config = typeof heartbeat === 'object' ? heartbeat : {};
|
|
89
81
|
const heartbeat_timeout = heartbeat_config.timeout ?? DEFAULT_SERVER_HEARTBEAT_TIMEOUT;
|
|
@@ -92,14 +84,14 @@ export const register_action_ws = (options) => {
|
|
|
92
84
|
// dead-because-unresponsive that closing is arguably correct.
|
|
93
85
|
const heartbeat_tick_interval = Math.max(100, Math.floor(heartbeat_timeout / 2));
|
|
94
86
|
app.get(path, upgradeWebSocket((c) => {
|
|
95
|
-
// Upgrade-time
|
|
96
|
-
// rejected unauthenticated
|
|
97
|
-
// non-null
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
87
|
+
// Upgrade-time identity capture. `require_auth` middleware has
|
|
88
|
+
// already rejected unauthenticated upgrades, so request_context is
|
|
89
|
+
// non-null here. Per-message dispatch reads `account_id` +
|
|
90
|
+
// `credential_type` from this closure; the live request_context is
|
|
91
|
+
// only used by the test-preset escape hatch (perform_action runs
|
|
92
|
+
// the authorization phase fresh on every message in production).
|
|
93
|
+
const upgrade_context = require_request_context(c);
|
|
94
|
+
const account_id = upgrade_context.account.id;
|
|
103
95
|
const client_ip = get_client_ip(c);
|
|
104
96
|
const credential_type = c.get(CREDENTIAL_TYPE_KEY);
|
|
105
97
|
// Session-based connections have a token hash for targeted revocation.
|
|
@@ -110,6 +102,12 @@ export const register_action_ws = (options) => {
|
|
|
110
102
|
// `close_sockets_for_token` to tear down just this socket on
|
|
111
103
|
// `token_revoke` without affecting the account's other sockets.
|
|
112
104
|
const api_token_id = c.get(AUTH_API_TOKEN_ID_KEY);
|
|
105
|
+
// Test escape hatch — captured once at upgrade. perform_action
|
|
106
|
+
// honors it per-message so harnesses with pre-baked
|
|
107
|
+
// `RequestContext` skip the live authorization phase.
|
|
108
|
+
const upgrade_preset = c.get(TEST_CONTEXT_PRESET_KEY)
|
|
109
|
+
? { request_context: get_request_context(c) }
|
|
110
|
+
: undefined;
|
|
113
111
|
// Per-socket abort controller — fires on socket close, chained into
|
|
114
112
|
// every in-flight handler's per-request controller via
|
|
115
113
|
// `AbortSignal.any`. Keeping both signals lets the client
|
|
@@ -128,9 +126,9 @@ export const register_action_ws = (options) => {
|
|
|
128
126
|
// down; `BackendWebsocketTransport.#revoke_connection` clears the
|
|
129
127
|
// identity map before Hono fires onClose.
|
|
130
128
|
const identity = { token_hash, account_id, api_token_id };
|
|
131
|
-
// Captured on open, consumed on close.
|
|
132
|
-
// when a consumer never opens (e.g. immediate disconnect).
|
|
133
|
-
let captured_connection_id
|
|
129
|
+
// Captured on open, consumed on close. Undefined before onOpen
|
|
130
|
+
// fires or when a consumer never opens (e.g. immediate disconnect).
|
|
131
|
+
let captured_connection_id;
|
|
134
132
|
// Receive-silence watchdog. Seeded to open-time so the first window is
|
|
135
133
|
// exempt (cold-start grace — avoid killing mid-handshake sockets).
|
|
136
134
|
// Bumped by onMessage. Any incoming activity counts, not just
|
|
@@ -240,80 +238,19 @@ export const register_action_ws = (options) => {
|
|
|
240
238
|
return;
|
|
241
239
|
}
|
|
242
240
|
const { method, id, params } = json;
|
|
243
|
-
// Per-action
|
|
244
|
-
|
|
245
|
-
|
|
241
|
+
// Per-action method lookup — return method_not_found before
|
|
242
|
+
// we engage the dispatch machinery. Specs without a handler
|
|
243
|
+
// (client-only / dispatcher-handled) miss action_map and
|
|
244
|
+
// surface as method_not_found just like unknown methods.
|
|
245
|
+
const action = action_map.get(method);
|
|
246
|
+
if (!action) {
|
|
246
247
|
ws.send(JSON.stringify(create_jsonrpc_error_response(id, jsonrpc_error_messages.method_not_found(method))));
|
|
247
248
|
return;
|
|
248
249
|
}
|
|
249
|
-
const { auth } = spec;
|
|
250
|
-
if (auth === 'keeper') {
|
|
251
|
-
if (credential_type !== 'daemon_token' || !has_role(request_context, ROLE_KEEPER)) {
|
|
252
|
-
ws.send(JSON.stringify(create_jsonrpc_error_response(id, jsonrpc_error_messages.forbidden('keeper actions require daemon_token credential with keeper role'))));
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
else if (typeof auth === 'object' && auth !== null) {
|
|
257
|
-
if (!has_role(request_context, auth.role)) {
|
|
258
|
-
ws.send(JSON.stringify(create_jsonrpc_error_response(id, jsonrpc_error_messages.forbidden(`requires role: ${auth.role}`))));
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
// Rate limit — throttle-requests semantics, mirrors the HTTP RPC
|
|
263
|
-
// dispatcher. Same limiters are shared across transports so an
|
|
264
|
-
// attacker can't bypass the budget by switching from RPC to WS.
|
|
265
|
-
const rate_limit = spec.rate_limit;
|
|
266
|
-
if (rate_limit) {
|
|
267
|
-
const ip_check = action_ip_rate_limiter && (rate_limit === 'ip' || rate_limit === 'both');
|
|
268
|
-
const account_check = action_account_rate_limiter && (rate_limit === 'account' || rate_limit === 'both');
|
|
269
|
-
const send_rate_limited = (retry_after) => {
|
|
270
|
-
ws.send(JSON.stringify(create_jsonrpc_error_response(id, jsonrpc_error_messages.rate_limited('rate limited', { retry_after }))));
|
|
271
|
-
};
|
|
272
|
-
if (ip_check) {
|
|
273
|
-
const result = action_ip_rate_limiter.check(client_ip);
|
|
274
|
-
if (!result.allowed) {
|
|
275
|
-
send_rate_limited(result.retry_after);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
if (account_check) {
|
|
280
|
-
const result = action_account_rate_limiter.check(request_context.account.id);
|
|
281
|
-
if (!result.allowed) {
|
|
282
|
-
send_rate_limited(result.retry_after);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
if (ip_check)
|
|
287
|
-
action_ip_rate_limiter.record(client_ip);
|
|
288
|
-
if (account_check)
|
|
289
|
-
action_account_rate_limiter.record(request_context.account.id);
|
|
290
|
-
}
|
|
291
|
-
// Look up handler — method is validated against spec_by_method above.
|
|
292
|
-
const handler = handlers[method];
|
|
293
|
-
if (!handler) {
|
|
294
|
-
ws.send(JSON.stringify(create_jsonrpc_error_response(id, jsonrpc_error_messages.method_not_found(method))));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
// Validate input against spec schema.
|
|
298
|
-
const parsed = spec.input.safeParse(params);
|
|
299
|
-
if (!parsed.success) {
|
|
300
|
-
ws.send(JSON.stringify(create_jsonrpc_error_response(id, jsonrpc_error_messages.invalid_params(`invalid params for ${method}`, {
|
|
301
|
-
issues: parsed.error.issues,
|
|
302
|
-
}))));
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
const validated_input = parsed.data;
|
|
306
250
|
if (artificial_delay > 0) {
|
|
307
251
|
log.debug(`throttling ${artificial_delay}ms`);
|
|
308
252
|
await wait(artificial_delay);
|
|
309
253
|
}
|
|
310
|
-
// Socket-scoped notification — routes to originator only, not
|
|
311
|
-
// broadcast. Same helper used in `on_socket_open` so both
|
|
312
|
-
// paths share one code path for send-and-log-on-failure.
|
|
313
|
-
// Future work: other audiences — account-scoped,
|
|
314
|
-
// ACL-filtered, broadcast — likely via a transport-level
|
|
315
|
-
// policy hook.
|
|
316
|
-
const notify = notify_socket(ws);
|
|
317
254
|
// Per-request controller — fires on explicit `cancel` or on
|
|
318
255
|
// socket close (via the socket_abort_controller chain below).
|
|
319
256
|
// Registered before dispatch so a cancel arriving mid-handler
|
|
@@ -322,40 +259,45 @@ export const register_action_ws = (options) => {
|
|
|
322
259
|
// null-abort the wrong handler.
|
|
323
260
|
const request_controller = new AbortController();
|
|
324
261
|
pending_controllers.set(id, request_controller);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const
|
|
262
|
+
// Per-message side-effect queues. `pending_effects` collects
|
|
263
|
+
// eager fire-and-forget pool writes (audit emits, etc.);
|
|
264
|
+
// `post_commit_effects` collects deferred thunks pushed
|
|
265
|
+
// via `emit_after_commit` (WS notifications). Both flush
|
|
266
|
+
// in the same try/finally so the next message sees a clean
|
|
267
|
+
// slate.
|
|
268
|
+
const pending_effects = [];
|
|
269
|
+
const post_commit_effects = [];
|
|
270
|
+
const notify = notify_socket(ws);
|
|
271
|
+
const signal = AbortSignal.any([
|
|
272
|
+
socket_abort_controller.signal,
|
|
273
|
+
request_controller.signal,
|
|
274
|
+
]);
|
|
334
275
|
try {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
log
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
ws.send(JSON.stringify(create_jsonrpc_error_response_from_thrown(id, error)));
|
|
276
|
+
const result = await perform_action({
|
|
277
|
+
action,
|
|
278
|
+
raw_params: params,
|
|
279
|
+
request_id: id,
|
|
280
|
+
account_id,
|
|
281
|
+
credential_type,
|
|
282
|
+
client_ip,
|
|
283
|
+
signal,
|
|
284
|
+
notify,
|
|
285
|
+
connection_id: captured_connection_id,
|
|
286
|
+
preset: upgrade_preset,
|
|
287
|
+
}, {
|
|
288
|
+
db,
|
|
289
|
+
pending_effects,
|
|
290
|
+
post_commit_effects,
|
|
291
|
+
log,
|
|
292
|
+
action_ip_rate_limiter,
|
|
293
|
+
action_account_rate_limiter,
|
|
294
|
+
});
|
|
295
|
+
ws.send(JSON.stringify(perform_action_result_to_envelope(id, result)));
|
|
356
296
|
}
|
|
357
297
|
finally {
|
|
358
298
|
pending_controllers.delete(id);
|
|
299
|
+
await flush_pending_effects(pending_effects, log);
|
|
300
|
+
await flush_post_commit_effects(post_commit_effects, log);
|
|
359
301
|
}
|
|
360
302
|
},
|
|
361
303
|
onClose: async (event, ws) => {
|
|
@@ -21,23 +21,15 @@
|
|
|
21
21
|
* @module
|
|
22
22
|
*/
|
|
23
23
|
import type { RoleName } from '../auth/role_schema.js';
|
|
24
|
-
import type { Db } from '../db/db.js';
|
|
25
24
|
import { type RegisterActionWsOptions, type RegisterActionWsResult } from './register_action_ws.js';
|
|
26
|
-
import type { BaseHandlerContext } from './action_types.js';
|
|
27
25
|
/** Options for `register_ws_endpoint`. */
|
|
28
|
-
export interface RegisterWsEndpointOptions
|
|
26
|
+
export interface RegisterWsEndpointOptions extends RegisterActionWsOptions {
|
|
29
27
|
/**
|
|
30
28
|
* Origin allowlist regexes — typically parsed from the `ALLOWED_ORIGINS`
|
|
31
29
|
* env var via `parse_allowed_origins`. Passed straight to
|
|
32
30
|
* `verify_request_source`.
|
|
33
31
|
*/
|
|
34
32
|
allowed_origins: Array<RegExp>;
|
|
35
|
-
/**
|
|
36
|
-
* Pool-level database used for upgrade-time actor resolution + permit
|
|
37
|
-
* load. Ran once per connection, then the result is reused for every
|
|
38
|
-
* message on the socket.
|
|
39
|
-
*/
|
|
40
|
-
db: Db;
|
|
41
33
|
/**
|
|
42
34
|
* Role required to upgrade. Omit for any authenticated account
|
|
43
35
|
* (`require_auth` + actor resolution alone); set to e.g. `ROLE_ADMIN`
|
|
@@ -58,5 +50,5 @@ export interface RegisterWsEndpointOptions<TCtx extends BaseHandlerContext> exte
|
|
|
58
50
|
* @mutates options.app - applies origin/auth/authorization/role middleware via `app.use`,
|
|
59
51
|
* then registers the `GET path` route via the inner `register_action_ws`
|
|
60
52
|
*/
|
|
61
|
-
export declare const register_ws_endpoint:
|
|
53
|
+
export declare const register_ws_endpoint: (options: RegisterWsEndpointOptions) => RegisterActionWsResult;
|
|
62
54
|
//# sourceMappingURL=register_ws_endpoint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register_ws_endpoint.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_ws_endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"register_ws_endpoint.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/register_ws_endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAYH,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAGrD,OAAO,EAEN,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,yBAAyB,CAAC;AAEjC,0CAA0C;AAC1C,MAAM,WAAW,yBAA0B,SAAQ,uBAAuB;IACzE;;;;OAIG;IACH,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,QAAQ,CAAC;CACzB;AAgDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,yBAAyB,KAChC,sBAmBF,CAAC"}
|
|
@@ -21,25 +21,47 @@
|
|
|
21
21
|
* @module
|
|
22
22
|
*/
|
|
23
23
|
import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
24
|
-
import { apply_authorization_phase, require_auth, require_role } from '../auth/request_context.js';
|
|
24
|
+
import { apply_authorization_phase, REQUEST_CONTEXT_KEY, require_auth, require_role, } from '../auth/request_context.js';
|
|
25
25
|
import { verify_request_source } from '../http/origin.js';
|
|
26
|
+
import { ACCOUNT_ID_KEY, TEST_CONTEXT_PRESET_KEY } from '../hono_context.js';
|
|
26
27
|
import { register_action_ws, } from './register_action_ws.js';
|
|
28
|
+
/** Synthesized auth shape for WS upgrade: account + actor both required. */
|
|
29
|
+
const WS_UPGRADE_AUTH = {
|
|
30
|
+
account: 'required',
|
|
31
|
+
actor: 'required',
|
|
32
|
+
};
|
|
27
33
|
/**
|
|
28
34
|
* Upgrade-time authorization middleware. Resolves the acting actor for
|
|
29
35
|
* the WS connection (single-actor default; multi-actor must supply
|
|
30
36
|
* `?acting=<uuid>`) and builds the `RequestContext` that per-message
|
|
31
37
|
* dispatch reads. Returns 400 on resolution failure.
|
|
38
|
+
*
|
|
39
|
+
* Sets `REQUEST_CONTEXT_KEY` on resolved outcomes so the inner
|
|
40
|
+
* `register_action_ws` reads the upgrade-time context via
|
|
41
|
+
* `require_request_context(c)`. Honors the test-preset escape hatch the
|
|
42
|
+
* same way the REST and HTTP RPC binders do.
|
|
32
43
|
*/
|
|
33
44
|
const create_ws_authorization_middleware = (db) => {
|
|
34
45
|
return async (c, next) => {
|
|
46
|
+
// Test escape hatch — harnesses pre-populate `REQUEST_CONTEXT_KEY`
|
|
47
|
+
// + flag `TEST_CONTEXT_PRESET_KEY = true`. Production middleware
|
|
48
|
+
// never sets this flag.
|
|
49
|
+
if (c.get(TEST_CONTEXT_PRESET_KEY)) {
|
|
50
|
+
await next();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
35
53
|
const acting_param = c.req.query('acting');
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
const account_id = c.get(ACCOUNT_ID_KEY) ?? null;
|
|
55
|
+
const result = await apply_authorization_phase({ db }, account_id, WS_UPGRADE_AUTH, acting_param ?? undefined);
|
|
56
|
+
if (!result.ok)
|
|
57
|
+
return c.json(result.body, result.status);
|
|
58
|
+
if (result.request_context !== null) {
|
|
59
|
+
c.set(REQUEST_CONTEXT_KEY, result.request_context);
|
|
60
|
+
}
|
|
61
|
+
// `request_context: null` is unreachable here — `WS_UPGRADE_AUTH` is
|
|
62
|
+
// `account: 'required', actor: 'required'`, and `require_auth` ran
|
|
63
|
+
// upstream, so neither the public nor the unauthenticated branch
|
|
64
|
+
// resolves through this middleware.
|
|
43
65
|
await next();
|
|
44
66
|
};
|
|
45
67
|
};
|
|
@@ -60,7 +82,7 @@ export const register_ws_endpoint = (options) => {
|
|
|
60
82
|
app.use(path, require_auth);
|
|
61
83
|
app.use(path, create_ws_authorization_middleware(db));
|
|
62
84
|
if (required_role !== undefined) {
|
|
63
|
-
app.use(path, require_role(required_role));
|
|
85
|
+
app.use(path, require_role([required_role]));
|
|
64
86
|
}
|
|
65
|
-
return register_action_ws({ app, path, log, ...rest });
|
|
87
|
+
return register_action_ws({ app, path, db, log, ...rest });
|
|
66
88
|
};
|
|
@@ -31,7 +31,7 @@ export type AuditEventHandler = (event: AuditLogEvent) => void;
|
|
|
31
31
|
* - `session_revoke_all` / `token_revoke_all` / `password_change` — close every socket
|
|
32
32
|
* for the affected account (all credentials invalidated).
|
|
33
33
|
*
|
|
34
|
-
* `
|
|
34
|
+
* `role_grant_revoke` is intentionally omitted: the WS transport does not track
|
|
35
35
|
* per-connection role requirements, so role-scoped disconnection would
|
|
36
36
|
* require either closing all sockets (too aggressive) or new tracking
|
|
37
37
|
* (out of scope). Consumers that need it compose their own callback.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* - `session_revoke_all` / `token_revoke_all` / `password_change` — close every socket
|
|
19
19
|
* for the affected account (all credentials invalidated).
|
|
20
20
|
*
|
|
21
|
-
* `
|
|
21
|
+
* `role_grant_revoke` is intentionally omitted: the WS transport does not track
|
|
22
22
|
* per-connection role requirements, so role-scoped disconnection would
|
|
23
23
|
* require either closing all sockets (too aggressive) or new tracking
|
|
24
24
|
* (out of scope). Consumers that need it compose their own callback.
|
|
@@ -97,7 +97,7 @@ export declare class BackendWebsocketTransport implements FilterableBroadcastTra
|
|
|
97
97
|
* Broadcast to connections whose identity satisfies a predicate.
|
|
98
98
|
*
|
|
99
99
|
* Used by the broadcast API when a consumer supplies a subscription ACL hook
|
|
100
|
-
* (e.g.
|
|
100
|
+
* (e.g. zap's `zap_run_created` only reaches the account that owns the run).
|
|
101
101
|
* When no ACL is needed, callers should prefer `send(message)` / `#broadcast`
|
|
102
102
|
* to skip the per-connection predicate overhead.
|
|
103
103
|
*
|
|
@@ -146,7 +146,7 @@ export class BackendWebsocketTransport {
|
|
|
146
146
|
* Broadcast to connections whose identity satisfies a predicate.
|
|
147
147
|
*
|
|
148
148
|
* Used by the broadcast API when a consumer supplies a subscription ACL hook
|
|
149
|
-
* (e.g.
|
|
149
|
+
* (e.g. zap's `zap_run_created` only reaches the account that owns the run).
|
|
150
150
|
* When no ACL is needed, callers should prefer `send(message)` / `#broadcast`
|
|
151
151
|
* to skip the per-connection predicate overhead.
|
|
152
152
|
*
|