@fuzdev/fuz_app 0.54.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 +214 -103
- 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 +32 -0
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +35 -15
- 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 +141 -22
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +106 -187
- 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 +46 -40
- 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 +15 -10
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +54 -7
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +0 -4
- 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 +794 -410
- 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 +7 -13
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +26 -35
- package/dist/auth/account_queries.d.ts +52 -16
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +87 -38
- package/dist/auth/account_routes.d.ts +9 -11
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +118 -46
- package/dist/auth/account_schema.d.ts +46 -35
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +21 -28
- package/dist/auth/admin_action_specs.d.ts +100 -32
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +64 -33
- package/dist/auth/admin_actions.d.ts +13 -19
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +37 -41
- 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 -48
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +20 -56
- 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 +92 -32
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +75 -46
- 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/bearer_auth.d.ts +9 -7
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +13 -21
- 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 -42
- 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 +23 -11
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +28 -22
- 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 -18
- 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 +9 -4
- package/dist/auth/migrations.d.ts +37 -14
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +79 -32
- package/dist/auth/request_context.d.ts +331 -61
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +378 -95
- package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +163 -94
- 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/role_grant_offer_actions.js +473 -0
- package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +90 -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/role_grant_offer_queries.d.ts +242 -0
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_queries.js +533 -0
- 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} +60 -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 +6 -1
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +3 -1
- package/dist/auth/self_service_role_actions.d.ts +34 -27
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +68 -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 +12 -8
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +10 -7
- package/dist/dev/setup.d.ts +2 -2
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +9 -7
- package/dist/env/load.d.ts +1 -1
- package/dist/env/load.js +1 -1
- package/dist/hono_context.d.ts +64 -5
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +38 -2
- package/dist/http/CLAUDE.md +264 -87
- 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 +132 -19
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +132 -40
- package/dist/http/jsonrpc_errors.d.ts +27 -2
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +26 -2
- 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 +113 -41
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +130 -52
- package/dist/http/schema_helpers.d.ts +3 -2
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +9 -2
- package/dist/http/surface.d.ts +2 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +1 -2
- 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 +36 -31
- package/dist/server/validate_nginx.d.ts +1 -1
- package/dist/server/validate_nginx.js +1 -1
- package/dist/testing/CLAUDE.md +73 -55
- 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 +100 -96
- package/dist/testing/adversarial_headers.js +1 -1
- 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 +18 -17
- 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 +53 -39
- 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 +28 -22
- 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 +10 -8
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +22 -18
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +13 -14
- package/dist/testing/integration_helpers.d.ts +8 -6
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +29 -23
- package/dist/testing/middleware.d.ts +15 -11
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +75 -32
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +40 -24
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +3 -1
- 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 +24 -12
- 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 +65 -59
- package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +37 -22
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts +20 -0
- 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 +25 -18
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +28 -17
- 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} +39 -31
- 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} +25 -19
- 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 -227
- 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_actions.js +0 -452
- 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 +0 -183
- package/dist/auth/permit_offer_queries.d.ts.map +0 -1
- package/dist/auth/permit_offer_queries.js +0 -408
- package/dist/auth/permit_offer_schema.d.ts +0 -103
- package/dist/auth/permit_offer_schema.d.ts.map +0 -1
- package/dist/auth/permit_queries.d.ts +0 -210
- package/dist/auth/permit_queries.d.ts.map +0 -1
- package/dist/auth/permit_queries.js +0 -294
- 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 -21
- package/dist/auth/route_guards.d.ts.map +0 -1
- package/dist/auth/route_guards.js +0 -32
- 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 +0 -14
- 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
|
@@ -15,14 +15,15 @@ import { z } from 'zod';
|
|
|
15
15
|
import { DEV } from 'esm-env';
|
|
16
16
|
import {} from '../http/route_spec.js';
|
|
17
17
|
import { get_client_ip } from '../http/proxy.js';
|
|
18
|
-
import { get_request_context,
|
|
19
|
-
import { CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
20
|
-
import {
|
|
18
|
+
import { get_request_context, } from '../auth/request_context.js';
|
|
19
|
+
import { ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY, TEST_CONTEXT_PRESET_KEY } from '../hono_context.js';
|
|
20
|
+
import { compile_action_registry } from './compile_action_registry.js';
|
|
21
21
|
import { JSONRPC_VERSION, JsonrpcRequest, } from '../http/jsonrpc.js';
|
|
22
22
|
import { jsonrpc_error_messages, jsonrpc_error_code_to_http_status, JSONRPC_ERROR_CODES, } from '../http/jsonrpc_errors.js';
|
|
23
|
-
import {
|
|
23
|
+
import { perform_action, perform_action_result_to_envelope } from './perform_action.js';
|
|
24
24
|
/**
|
|
25
|
-
* Pair a spec with a handler while preserving per-method input/output types
|
|
25
|
+
* Pair a spec with a handler while preserving per-method input/output types
|
|
26
|
+
* and selecting the narrowest `ctx.auth` shape the spec literal admits.
|
|
26
27
|
*
|
|
27
28
|
* Constructing `{spec, handler}` literals widens `handler` to
|
|
28
29
|
* `ActionHandler<any, any>`, so spec/handler drift (renamed Zod schema,
|
|
@@ -31,60 +32,57 @@ import { ERROR_INSUFFICIENT_PERMISSIONS, ERROR_KEEPER_REQUIRES_DAEMON_TOKEN, } f
|
|
|
31
32
|
* `(input: z.infer<spec.input>, ctx) => z.infer<spec.output>` via the
|
|
32
33
|
* generic spec parameter — drift surfaces at the call site.
|
|
33
34
|
*
|
|
35
|
+
* The `ctx.auth` narrowing follows the spec's `auth.account` /
|
|
36
|
+
* `auth.actor` literals (see `HandlerForSpec`): an actor-implying spec
|
|
37
|
+
* gets `ctx.auth: RequestActorContext`; an account-grain spec gets
|
|
38
|
+
* `ctx.auth: RequestContext`; everything else stays `ctx.auth:
|
|
39
|
+
* RequestContext | null`. Handlers can rely on the dispatcher's
|
|
40
|
+
* runtime guarantee without a manual narrowing call.
|
|
41
|
+
*
|
|
34
42
|
* Fits fuz_app's factory-closure pattern (handlers close over
|
|
35
43
|
* `grantable_roles`, `app_settings` ref, `notification_sender`, etc.).
|
|
36
44
|
* zzz uses a different shape — a codegen-keyed `Record<Method, Handler>`
|
|
37
45
|
* map typed via generated `ActionInputs`/`ActionOutputs` — which works when
|
|
38
46
|
* handlers are pure (no closure state) and specs are codegen-enumerated.
|
|
39
|
-
* fuz_app's admin +
|
|
47
|
+
* fuz_app's admin + role-grant-offer actions have neither, so per-pair typing
|
|
40
48
|
* at the registration site is the right fit.
|
|
49
|
+
*
|
|
50
|
+
* Spec-literal preservation is load-bearing: declare specs with
|
|
51
|
+
* `satisfies RequestResponseActionSpec` (canonical) so `auth.actor`
|
|
52
|
+
* keeps its `'required'` / `'none'` literal type. A spec typed
|
|
53
|
+
* directly as `RequestResponseActionSpec` widens the axes to
|
|
54
|
+
* `AuthAxisState` and the handler defaults to the loosest tier — sound,
|
|
55
|
+
* but loses the ergonomic narrowing.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* // actor-implying spec → ctx.auth: RequestActorContext
|
|
60
|
+
* rpc_action(role_grant_revoke_action_spec, async (input, ctx) => {
|
|
61
|
+
* const revoker_id = ctx.auth.actor.id; // no narrowing needed
|
|
62
|
+
* });
|
|
63
|
+
*
|
|
64
|
+
* // account-grain spec → ctx.auth: RequestContext (actor: null)
|
|
65
|
+
* rpc_action(account_verify_action_spec, (_input, ctx) => {
|
|
66
|
+
* return to_session_account(ctx.auth.account); // no narrowing needed
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
41
69
|
*/
|
|
42
70
|
export const rpc_action = (spec, handler) => ({
|
|
43
71
|
spec,
|
|
44
72
|
handler: handler,
|
|
45
73
|
});
|
|
46
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Build a JSON-RPC error envelope for transport-shape errors (envelope
|
|
76
|
+
* parse failures, method-not-found, GET-on-mutation rejections). The
|
|
77
|
+
* dispatch core in `perform_action` returns a `PerformActionResult`
|
|
78
|
+
* directly; this helper covers only the wire-shape errors the HTTP shim
|
|
79
|
+
* emits before / after the core runs.
|
|
80
|
+
*/
|
|
81
|
+
const jsonrpc_error_envelope = (id, error) => ({
|
|
47
82
|
jsonrpc: JSONRPC_VERSION,
|
|
48
83
|
id,
|
|
49
84
|
error,
|
|
50
85
|
});
|
|
51
|
-
/**
|
|
52
|
-
* Check auth for an action spec against the request context.
|
|
53
|
-
*
|
|
54
|
-
* @returns a JSON-RPC error object if auth fails, or `null` if authorized
|
|
55
|
-
*/
|
|
56
|
-
const check_action_auth = (auth, request_context, credential_type) => {
|
|
57
|
-
if (auth === 'public')
|
|
58
|
-
return null;
|
|
59
|
-
if (!request_context)
|
|
60
|
-
return jsonrpc_error_messages.unauthenticated();
|
|
61
|
-
if (auth === 'authenticated')
|
|
62
|
-
return null;
|
|
63
|
-
if (auth === 'keeper') {
|
|
64
|
-
// keeper requires daemon_token credential type AND the keeper role.
|
|
65
|
-
// API tokens and session cookies cannot access keeper actions even
|
|
66
|
-
// if the account has the keeper permit. Attach the credential type
|
|
67
|
-
// under `data` so clients can distinguish "wrong credential shape"
|
|
68
|
-
// from "missing keeper role" — mirrors REST 403 semantics.
|
|
69
|
-
if (credential_type !== 'daemon_token' || !has_role(request_context, 'keeper')) {
|
|
70
|
-
return jsonrpc_error_messages.forbidden('forbidden', {
|
|
71
|
-
reason: ERROR_KEEPER_REQUIRES_DAEMON_TOKEN,
|
|
72
|
-
credential_type,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
// role check — attach `required_role` under `data.required_role` so
|
|
78
|
-
// clients can render targeted copy (matches the former REST `PermissionError`
|
|
79
|
-
// shape that exposed `required_role` as a top-level field).
|
|
80
|
-
if (!has_role(request_context, auth.role)) {
|
|
81
|
-
return jsonrpc_error_messages.forbidden(`requires role: ${auth.role}`, {
|
|
82
|
-
reason: ERROR_INSUFFICIENT_PERMISSIONS,
|
|
83
|
-
required_role: auth.role,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
return null;
|
|
87
|
-
};
|
|
88
86
|
/**
|
|
89
87
|
* Single JSON-RPC 2.0 endpoint — the canonical RPC transport binding.
|
|
90
88
|
*
|
|
@@ -94,9 +92,18 @@ const check_action_auth = (auth, request_context, credential_type) => {
|
|
|
94
92
|
* 1. **Parse envelope** — POST: JSON body as `JsonrpcRequest`. GET: `method`
|
|
95
93
|
* and `params` from query string.
|
|
96
94
|
* 2. **Lookup method** — find the `RpcAction` by method name.
|
|
97
|
-
* 3. **
|
|
98
|
-
*
|
|
99
|
-
*
|
|
95
|
+
* 3. **Pre-validation auth** — short-circuit `unauthenticated` when no
|
|
96
|
+
* account is on the request, before input validation runs.
|
|
97
|
+
* 4. **Authorization phase** — resolve the acting actor (when the action's
|
|
98
|
+
* auth requires role_grants or its input declares `acting?: ActingActor`)
|
|
99
|
+
* and build the request context. Runs before input validation so
|
|
100
|
+
* role-grant-grain auth checks return 403 before 400 invalid_params;
|
|
101
|
+
* `acting` is read from raw params via a string typeguard.
|
|
102
|
+
* 5. **Post-authorization auth** — enforce role / keeper requirements
|
|
103
|
+
* against the request context.
|
|
104
|
+
* 6. **Validate params** — parse input against the action's `input` schema.
|
|
105
|
+
* 7. **Rate limit** — per-action IP / account throttling.
|
|
106
|
+
* 8. **Dispatch** — acquire DB handle (transaction for mutations, pool for reads),
|
|
100
107
|
* construct `ActionContext`, call handler, return JSON-RPC response.
|
|
101
108
|
*
|
|
102
109
|
* GET is restricted to `side_effects: false` actions (cacheable reads).
|
|
@@ -115,156 +122,68 @@ const check_action_auth = (auth, request_context, credential_type) => {
|
|
|
115
122
|
*/
|
|
116
123
|
export const create_rpc_endpoint = (options) => {
|
|
117
124
|
const { path: endpoint_path, actions, log, action_ip_rate_limiter = null, action_account_rate_limiter = null, } = options;
|
|
118
|
-
|
|
119
|
-
const action_map = new Map();
|
|
120
|
-
for (const action of actions) {
|
|
121
|
-
if (action_map.has(action.spec.method)) {
|
|
122
|
-
throw new Error(`Duplicate RPC action method: ${action.spec.method}`);
|
|
123
|
-
}
|
|
124
|
-
if (is_null_schema(action.spec.input)) {
|
|
125
|
-
throw new Error(`RPC action "${action.spec.method}" uses z.null() for input — JSON-RPC 2.0 §4.2 forbids "params": null on the wire (must be omitted or be a Structured value). Use z.void() for parameterless methods.`);
|
|
126
|
-
}
|
|
127
|
-
// Reject account-keyed rate limiting on public actions — there's no
|
|
128
|
-
// actor post-auth, so the account bucket has no key to consume.
|
|
129
|
-
if ((action.spec.rate_limit === 'account' || action.spec.rate_limit === 'both') &&
|
|
130
|
-
action.spec.auth === 'public') {
|
|
131
|
-
throw new Error(`RPC 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.`);
|
|
132
|
-
}
|
|
133
|
-
action_map.set(action.spec.method, action);
|
|
134
|
-
}
|
|
125
|
+
const { action_map } = compile_action_registry(actions, 'RPC action');
|
|
135
126
|
/**
|
|
136
|
-
*
|
|
127
|
+
* HTTP-shape dispatch shim — the GET/POST entry points share this:
|
|
128
|
+
*
|
|
129
|
+
* 1. Resolve the action by method name (HTTP-shape `method_not_found` envelope).
|
|
130
|
+
* 2. Reject GET requests for `side_effects: true` actions (HTTP-only constraint).
|
|
131
|
+
* 3. Hand off to `perform_action` for the post-parse pipeline.
|
|
132
|
+
* 4. Bind the result to `c.json` — `'ok'` returns the result envelope,
|
|
133
|
+
* `'error'` returns the error envelope at the `result.status` HTTP code.
|
|
137
134
|
*
|
|
138
135
|
* @param restrict_to_reads - `true` for GET (rejects `side_effects: true` actions)
|
|
139
136
|
*/
|
|
140
137
|
const dispatch = async (c, route, method_name, raw_params, id, restrict_to_reads) => {
|
|
141
|
-
// step 2: lookup method
|
|
142
138
|
const action = action_map.get(method_name);
|
|
143
139
|
if (!action) {
|
|
144
|
-
|
|
145
|
-
return c.json(error, jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.method_not_found));
|
|
140
|
+
return c.json(jsonrpc_error_envelope(id, jsonrpc_error_messages.method_not_found(method_name)), jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.method_not_found));
|
|
146
141
|
}
|
|
147
|
-
// GET restriction: only side_effects:false actions
|
|
148
142
|
if (restrict_to_reads && action.spec.side_effects) {
|
|
149
|
-
|
|
143
|
+
return c.json(jsonrpc_error_envelope(id, jsonrpc_error_messages.invalid_request({
|
|
150
144
|
reason: `method '${method_name}' has side effects and must use POST`,
|
|
151
|
-
}));
|
|
152
|
-
return c.json(error, jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.invalid_request));
|
|
153
|
-
}
|
|
154
|
-
// step 3: auth check
|
|
155
|
-
const request_context = get_request_context(c);
|
|
156
|
-
const credential_type = c.get(CREDENTIAL_TYPE_KEY) ?? null;
|
|
157
|
-
const auth_error = check_action_auth(action.spec.auth, request_context, credential_type);
|
|
158
|
-
if (auth_error) {
|
|
159
|
-
const error = jsonrpc_error_response(id, auth_error);
|
|
160
|
-
return c.json(error, jsonrpc_error_code_to_http_status(auth_error.code));
|
|
145
|
+
})), jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.invalid_request));
|
|
161
146
|
}
|
|
162
|
-
//
|
|
163
|
-
// invocation, no success-reset). Suits admin mutation oracles where
|
|
164
|
-
// the *successful* call is the threat. Different from REST login's
|
|
165
|
-
// throttle-failures pattern that resets on success. Silent partial
|
|
166
|
-
// enforcement: a key is checked iff its bucket's limiter is wired —
|
|
167
|
-
// `rate_limit: 'both'` with only one limiter set runs only that side.
|
|
168
|
-
const rate_limit = action.spec.rate_limit;
|
|
169
|
-
const client_ip = get_client_ip(c);
|
|
170
|
-
if (rate_limit) {
|
|
171
|
-
const ip_check = action_ip_rate_limiter && (rate_limit === 'ip' || rate_limit === 'both');
|
|
172
|
-
const account_check = action_account_rate_limiter &&
|
|
173
|
-
request_context &&
|
|
174
|
-
(rate_limit === 'account' || rate_limit === 'both');
|
|
175
|
-
const reject = (retry_after) => {
|
|
176
|
-
const error = jsonrpc_error_response(id, jsonrpc_error_messages.rate_limited('rate limited', { retry_after }));
|
|
177
|
-
return c.json(error, jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.rate_limited));
|
|
178
|
-
};
|
|
179
|
-
if (ip_check) {
|
|
180
|
-
const result = action_ip_rate_limiter.check(client_ip);
|
|
181
|
-
if (!result.allowed)
|
|
182
|
-
return reject(result.retry_after);
|
|
183
|
-
}
|
|
184
|
-
if (account_check) {
|
|
185
|
-
const result = action_account_rate_limiter.check(request_context.actor.id);
|
|
186
|
-
if (!result.allowed)
|
|
187
|
-
return reject(result.retry_after);
|
|
188
|
-
}
|
|
189
|
-
if (ip_check)
|
|
190
|
-
action_ip_rate_limiter.record(client_ip);
|
|
191
|
-
if (account_check)
|
|
192
|
-
action_account_rate_limiter.record(request_context.actor.id);
|
|
193
|
-
}
|
|
194
|
-
// step 4: validate params
|
|
195
|
-
// Missing `params` on the envelope maps to `undefined` for `z.void()`
|
|
196
|
-
// input schemas and `{}` for object inputs (matches HTTP's "empty
|
|
197
|
-
// body = empty object" convention so callers of all-optional-object
|
|
198
|
-
// RPC methods can omit `params` on the wire). JSON-RPC 2.0 §4.2
|
|
199
|
-
// forbids `params: null`, so `z.void()` is the spec-correct schema
|
|
200
|
-
// for parameterless methods — registration above rejects `z.null()`
|
|
201
|
-
// inputs to keep this branch from having to consider that legacy
|
|
202
|
-
// shape. When `raw_params` is present it flows through unchanged so
|
|
203
|
-
// contract-violating shapes still fail validation.
|
|
204
|
-
const params = is_void_schema(action.spec.input) ? raw_params : (raw_params ?? {});
|
|
205
|
-
const parse_result = action.spec.input.safeParse(params);
|
|
206
|
-
if (!parse_result.success) {
|
|
207
|
-
const error = jsonrpc_error_response(id, jsonrpc_error_messages.invalid_params('invalid params', {
|
|
208
|
-
issues: parse_result.error.issues,
|
|
209
|
-
}));
|
|
210
|
-
return c.json(error, jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.invalid_params));
|
|
211
|
-
}
|
|
212
|
-
// step 5: dispatch — transaction for mutations, pool for reads
|
|
213
|
-
const use_transaction = action.spec.side_effects;
|
|
147
|
+
// HTTP RPC has no streaming channel; `ctx.notify` warn-and-drops in DEV.
|
|
214
148
|
const notify = (notify_method, _notify_params) => {
|
|
215
149
|
if (DEV) {
|
|
216
150
|
log.warn(`ctx.notify('${notify_method}') called on non-streaming transport; notification dropped (method=${method_name})`);
|
|
217
151
|
}
|
|
218
152
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// Duck-type check: Error with numeric `code` signals a JSON-RPC error.
|
|
252
|
-
// Avoids instanceof which fails when consumers throw their own ThrownJsonrpcError
|
|
253
|
-
// (structurally identical but different class identity, e.g. zzz's copy).
|
|
254
|
-
if (err instanceof Error && typeof err.code === 'number') {
|
|
255
|
-
const code = err.code;
|
|
256
|
-
const data = err.data;
|
|
257
|
-
const status = jsonrpc_error_code_to_http_status(code);
|
|
258
|
-
const error_json = { code, message: err.message };
|
|
259
|
-
if (data !== undefined)
|
|
260
|
-
error_json.data = data;
|
|
261
|
-
return c.json(jsonrpc_error_response(id, error_json), status);
|
|
262
|
-
}
|
|
263
|
-
// generic error
|
|
264
|
-
log.error(`Unhandled RPC handler error: ${method_name}`, err);
|
|
265
|
-
const message = DEV && err instanceof Error ? err.message : 'internal server error';
|
|
266
|
-
return c.json(jsonrpc_error_response(id, jsonrpc_error_messages.internal_error(message)), 500);
|
|
153
|
+
// Test escape hatch: harnesses pre-populate `REQUEST_CONTEXT_KEY` and
|
|
154
|
+
// flag `TEST_CONTEXT_PRESET_KEY = true`. Production middleware never
|
|
155
|
+
// sets the flag; the shim honors it and `perform_action` trusts the
|
|
156
|
+
// pre-baked context instead of running the live authorization phase.
|
|
157
|
+
const preset = c.get(TEST_CONTEXT_PRESET_KEY)
|
|
158
|
+
? { request_context: get_request_context(c) }
|
|
159
|
+
: undefined;
|
|
160
|
+
const result = await perform_action({
|
|
161
|
+
action,
|
|
162
|
+
raw_params,
|
|
163
|
+
request_id: id,
|
|
164
|
+
account_id: c.get(ACCOUNT_ID_KEY) ?? null,
|
|
165
|
+
credential_type: c.get(CREDENTIAL_TYPE_KEY) ?? null,
|
|
166
|
+
client_ip: get_client_ip(c),
|
|
167
|
+
signal: c.req.raw.signal,
|
|
168
|
+
notify,
|
|
169
|
+
preset,
|
|
170
|
+
}, {
|
|
171
|
+
db: route.db,
|
|
172
|
+
pending_effects: route.pending_effects,
|
|
173
|
+
post_commit_effects: route.post_commit_effects,
|
|
174
|
+
log,
|
|
175
|
+
action_ip_rate_limiter,
|
|
176
|
+
action_account_rate_limiter,
|
|
177
|
+
});
|
|
178
|
+
const envelope = perform_action_result_to_envelope(id, result);
|
|
179
|
+
if (result.kind === 'error') {
|
|
180
|
+
// `result.status` is one of the JSON-RPC → HTTP status codes the
|
|
181
|
+
// dispatcher emits; Hono types `c.json`'s second arg as
|
|
182
|
+
// `ContentfulStatusCode`, which the cast bridges (the value space
|
|
183
|
+
// is verified by `jsonrpc_error_code_to_http_status`).
|
|
184
|
+
return c.json(envelope, result.status);
|
|
267
185
|
}
|
|
186
|
+
return c.json(envelope);
|
|
268
187
|
};
|
|
269
188
|
// POST handler — parse JSON-RPC envelope from body
|
|
270
189
|
const post_handler = async (c, route) => {
|
|
@@ -274,7 +193,7 @@ export const create_rpc_endpoint = (options) => {
|
|
|
274
193
|
body = await c.req.json();
|
|
275
194
|
}
|
|
276
195
|
catch {
|
|
277
|
-
const error =
|
|
196
|
+
const error = jsonrpc_error_envelope(null, jsonrpc_error_messages.parse_error());
|
|
278
197
|
return c.json(error, 400);
|
|
279
198
|
}
|
|
280
199
|
const envelope = JsonrpcRequest.safeParse(body);
|
|
@@ -284,7 +203,7 @@ export const create_rpc_endpoint = (options) => {
|
|
|
284
203
|
? body.id
|
|
285
204
|
: null;
|
|
286
205
|
const id = typeof raw_id === 'string' || typeof raw_id === 'number' ? raw_id : null;
|
|
287
|
-
const error =
|
|
206
|
+
const error = jsonrpc_error_envelope(id, jsonrpc_error_messages.invalid_request({ issues: envelope.error.issues }));
|
|
288
207
|
return c.json(error, 400);
|
|
289
208
|
}
|
|
290
209
|
return dispatch(c, route, envelope.data.method, envelope.data.params, envelope.data.id, false);
|
|
@@ -294,12 +213,12 @@ export const create_rpc_endpoint = (options) => {
|
|
|
294
213
|
// step 1: parse from query string
|
|
295
214
|
const method_name = c.req.query('method');
|
|
296
215
|
if (!method_name) {
|
|
297
|
-
const error =
|
|
216
|
+
const error = jsonrpc_error_envelope(null, jsonrpc_error_messages.invalid_request({ reason: 'missing method query parameter' }));
|
|
298
217
|
return c.json(error, 400);
|
|
299
218
|
}
|
|
300
219
|
const id_raw = c.req.query('id');
|
|
301
220
|
if (!id_raw) {
|
|
302
|
-
const error =
|
|
221
|
+
const error = jsonrpc_error_envelope(null, jsonrpc_error_messages.invalid_request({ reason: 'missing id query parameter' }));
|
|
303
222
|
return c.json(error, 400);
|
|
304
223
|
}
|
|
305
224
|
// parse integer ids so GET ?id=42 matches POST {id: 42} behavior
|
|
@@ -314,7 +233,7 @@ export const create_rpc_endpoint = (options) => {
|
|
|
314
233
|
params = JSON.parse(params_raw);
|
|
315
234
|
}
|
|
316
235
|
catch {
|
|
317
|
-
const error =
|
|
236
|
+
const error = jsonrpc_error_envelope(id, jsonrpc_error_messages.invalid_params('params query parameter is not valid JSON'));
|
|
318
237
|
return c.json(error, 400);
|
|
319
238
|
}
|
|
320
239
|
}
|
|
@@ -324,7 +243,7 @@ export const create_rpc_endpoint = (options) => {
|
|
|
324
243
|
{
|
|
325
244
|
method: 'POST',
|
|
326
245
|
path: endpoint_path,
|
|
327
|
-
auth: {
|
|
246
|
+
auth: { account: 'none', actor: 'none' }, // per-action auth inside dispatcher
|
|
328
247
|
handler: post_handler,
|
|
329
248
|
description: `JSON-RPC 2.0 endpoint — ${actions.length} method${actions.length === 1 ? '' : 's'}`,
|
|
330
249
|
input: z.null(), // dispatcher owns body parsing; rpc_endpoints surface has the real schemas
|
|
@@ -334,7 +253,7 @@ export const create_rpc_endpoint = (options) => {
|
|
|
334
253
|
{
|
|
335
254
|
method: 'GET',
|
|
336
255
|
path: endpoint_path,
|
|
337
|
-
auth: {
|
|
256
|
+
auth: { account: 'none', actor: 'none' }, // per-action auth inside dispatcher
|
|
338
257
|
handler: get_handler,
|
|
339
258
|
description: `JSON-RPC 2.0 endpoint (cacheable reads) — ${actions.length} method${actions.length === 1 ? '' : 's'}`,
|
|
340
259
|
input: z.null(), // params from query string, validated by dispatcher
|
|
@@ -23,10 +23,6 @@ export declare const ActionInitiator: z.ZodEnum<{
|
|
|
23
23
|
backend: "backend";
|
|
24
24
|
}>;
|
|
25
25
|
export type ActionInitiator = z.infer<typeof ActionInitiator>;
|
|
26
|
-
export declare const ActionAuth: z.ZodUnion<readonly [z.ZodLiteral<"public">, z.ZodLiteral<"authenticated">, z.ZodLiteral<"keeper">, z.ZodObject<{
|
|
27
|
-
role: z.ZodString;
|
|
28
|
-
}, z.core.$strict>]>;
|
|
29
|
-
export type ActionAuth = z.infer<typeof ActionAuth>;
|
|
30
26
|
export declare const ActionSideEffects: z.ZodBoolean;
|
|
31
27
|
export type ActionSideEffects = z.infer<typeof ActionSideEffects>;
|
|
32
28
|
export declare const ActionSpec: z.ZodObject<{
|
|
@@ -41,9 +37,29 @@ export declare const ActionSpec: z.ZodObject<{
|
|
|
41
37
|
frontend: "frontend";
|
|
42
38
|
backend: "backend";
|
|
43
39
|
}>;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
/**
|
|
41
|
+
* The four-axis auth shape (canonical schema in `http/auth_shape.ts`).
|
|
42
|
+
* `null` for `remote_notification` and `local_call` — those don't
|
|
43
|
+
* dispatch through the request/response auth gate.
|
|
44
|
+
*
|
|
45
|
+
* See `../http/auth_shape.ts` for the design rationale (orthogonal
|
|
46
|
+
* authentication / account-resolution / actor-resolution / role-and-
|
|
47
|
+
* credential authorization axes).
|
|
48
|
+
*/
|
|
49
|
+
auth: z.ZodNullable<z.ZodObject<{
|
|
50
|
+
account: z.ZodEnum<{
|
|
51
|
+
optional: "optional";
|
|
52
|
+
none: "none";
|
|
53
|
+
required: "required";
|
|
54
|
+
}>;
|
|
55
|
+
actor: z.ZodEnum<{
|
|
56
|
+
optional: "optional";
|
|
57
|
+
none: "none";
|
|
58
|
+
required: "required";
|
|
59
|
+
}>;
|
|
60
|
+
roles: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
61
|
+
credential_types: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
62
|
+
}, z.core.$strict>>;
|
|
47
63
|
side_effects: z.ZodBoolean;
|
|
48
64
|
input: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
49
65
|
output: z.ZodCustom<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
@@ -62,7 +78,7 @@ export declare const ActionSpec: z.ZodObject<{
|
|
|
62
78
|
* failure. Declarative metadata mirroring the `streams` precedent —
|
|
63
79
|
* codegen, UI form-state matching, and docs read it off the spec instead
|
|
64
80
|
* of scanning handler implementations. Reuses the same `as const` string
|
|
65
|
-
* constants the handler throws (e.g. `
|
|
81
|
+
* constants the handler throws (e.g. `ERROR_ROLE_GRANT_OFFER_*`) so call sites can
|
|
66
82
|
* import either side. Optional — actions that surface only standard
|
|
67
83
|
* transport errors leave it unset.
|
|
68
84
|
*/
|
|
@@ -72,8 +88,9 @@ export declare const ActionSpec: z.ZodObject<{
|
|
|
72
88
|
* actions without it skip the rate-limit hook entirely.
|
|
73
89
|
*
|
|
74
90
|
* - `'ip'` — keyed on the resolved client IP (`get_client_ip(c)`).
|
|
75
|
-
* - `'account'` — keyed on the post-auth
|
|
76
|
-
* Registration-time error if paired with `auth
|
|
91
|
+
* - `'account'` — keyed on the post-auth account id (`request_context.account.id`).
|
|
92
|
+
* Registration-time error if paired with `auth.account !== 'required'`
|
|
93
|
+
* (no account to key on).
|
|
77
94
|
* - `'both'` — both checks run; either can block.
|
|
78
95
|
*
|
|
79
96
|
* Throttle-requests semantics — every invocation records, regardless of
|
|
@@ -112,9 +129,20 @@ export declare const RequestResponseActionSpec: z.ZodObject<{
|
|
|
112
129
|
both: "both";
|
|
113
130
|
}>>;
|
|
114
131
|
kind: z.ZodDefault<z.ZodLiteral<"request_response">>;
|
|
115
|
-
auth: z.
|
|
116
|
-
|
|
117
|
-
|
|
132
|
+
auth: z.ZodObject<{
|
|
133
|
+
account: z.ZodEnum<{
|
|
134
|
+
optional: "optional";
|
|
135
|
+
none: "none";
|
|
136
|
+
required: "required";
|
|
137
|
+
}>;
|
|
138
|
+
actor: z.ZodEnum<{
|
|
139
|
+
optional: "optional";
|
|
140
|
+
none: "none";
|
|
141
|
+
required: "required";
|
|
142
|
+
}>;
|
|
143
|
+
roles: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
144
|
+
credential_types: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
145
|
+
}, z.core.$strict>;
|
|
118
146
|
async: z.ZodDefault<z.ZodLiteral<true>>;
|
|
119
147
|
}, z.core.$strict>;
|
|
120
148
|
export type RequestResponseActionSpec = z.infer<typeof RequestResponseActionSpec>;
|
|
@@ -187,9 +215,20 @@ export declare const ActionSpecUnion: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
187
215
|
both: "both";
|
|
188
216
|
}>>;
|
|
189
217
|
kind: z.ZodDefault<z.ZodLiteral<"request_response">>;
|
|
190
|
-
auth: z.
|
|
191
|
-
|
|
192
|
-
|
|
218
|
+
auth: z.ZodObject<{
|
|
219
|
+
account: z.ZodEnum<{
|
|
220
|
+
optional: "optional";
|
|
221
|
+
none: "none";
|
|
222
|
+
required: "required";
|
|
223
|
+
}>;
|
|
224
|
+
actor: z.ZodEnum<{
|
|
225
|
+
optional: "optional";
|
|
226
|
+
none: "none";
|
|
227
|
+
required: "required";
|
|
228
|
+
}>;
|
|
229
|
+
roles: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
230
|
+
credential_types: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
|
|
231
|
+
}, z.core.$strict>;
|
|
193
232
|
async: z.ZodDefault<z.ZodLiteral<true>>;
|
|
194
233
|
}, z.core.$strict>, z.ZodObject<{
|
|
195
234
|
method: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"action_spec.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAKtB,eAAO,MAAM,UAAU;;;;EAAoE,CAAC;AAC5F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,eAAO,MAAM,eAAe;;;;EAA0C,CAAC;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAC7C,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAElE,eAAO,MAAM,UAAU;;;;;;;;;;;;IAItB;;;;;;;;OAQG;;;;;;;;;;;;;;;;;;;;IAOH;;;;;;OAMG;;IAEH;;;;;;;;OAQG;;IAEH;;;;;;;;;;;;;;;;;;;OAmBG;;;;;;kBAEF,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAEpD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAIpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;kBAMvC,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAExF;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;kBAG9B,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAI1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,gHAAgH;AAChH,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAKoB,CAAC;AAE9E,eAAO,MAAM,gBAAgB;;;;;;;;;;EAU3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
|
@@ -12,20 +12,24 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { RateLimitKey } from '../http/error_schemas.js';
|
|
15
|
+
import { RouteAuth } from '../http/auth_shape.js';
|
|
15
16
|
export const ActionKind = z.enum(['request_response', 'remote_notification', 'local_call']);
|
|
16
17
|
export const ActionInitiator = z.enum(['frontend', 'backend', 'both']);
|
|
17
|
-
export const ActionAuth = z.union([
|
|
18
|
-
z.literal('public'),
|
|
19
|
-
z.literal('authenticated'),
|
|
20
|
-
z.literal('keeper'),
|
|
21
|
-
z.strictObject({ role: z.string() }),
|
|
22
|
-
]);
|
|
23
18
|
export const ActionSideEffects = z.boolean();
|
|
24
19
|
export const ActionSpec = z.strictObject({
|
|
25
20
|
method: z.string(),
|
|
26
21
|
kind: ActionKind,
|
|
27
22
|
initiator: ActionInitiator,
|
|
28
|
-
|
|
23
|
+
/**
|
|
24
|
+
* The four-axis auth shape (canonical schema in `http/auth_shape.ts`).
|
|
25
|
+
* `null` for `remote_notification` and `local_call` — those don't
|
|
26
|
+
* dispatch through the request/response auth gate.
|
|
27
|
+
*
|
|
28
|
+
* See `../http/auth_shape.ts` for the design rationale (orthogonal
|
|
29
|
+
* authentication / account-resolution / actor-resolution / role-and-
|
|
30
|
+
* credential authorization axes).
|
|
31
|
+
*/
|
|
32
|
+
auth: RouteAuth.nullable(),
|
|
29
33
|
side_effects: ActionSideEffects,
|
|
30
34
|
input: z.custom((v) => v instanceof z.ZodType),
|
|
31
35
|
output: z.custom((v) => v instanceof z.ZodType),
|
|
@@ -44,7 +48,7 @@ export const ActionSpec = z.strictObject({
|
|
|
44
48
|
* failure. Declarative metadata mirroring the `streams` precedent —
|
|
45
49
|
* codegen, UI form-state matching, and docs read it off the spec instead
|
|
46
50
|
* of scanning handler implementations. Reuses the same `as const` string
|
|
47
|
-
* constants the handler throws (e.g. `
|
|
51
|
+
* constants the handler throws (e.g. `ERROR_ROLE_GRANT_OFFER_*`) so call sites can
|
|
48
52
|
* import either side. Optional — actions that surface only standard
|
|
49
53
|
* transport errors leave it unset.
|
|
50
54
|
*/
|
|
@@ -54,8 +58,9 @@ export const ActionSpec = z.strictObject({
|
|
|
54
58
|
* actions without it skip the rate-limit hook entirely.
|
|
55
59
|
*
|
|
56
60
|
* - `'ip'` — keyed on the resolved client IP (`get_client_ip(c)`).
|
|
57
|
-
* - `'account'` — keyed on the post-auth
|
|
58
|
-
* Registration-time error if paired with `auth
|
|
61
|
+
* - `'account'` — keyed on the post-auth account id (`request_context.account.id`).
|
|
62
|
+
* Registration-time error if paired with `auth.account !== 'required'`
|
|
63
|
+
* (no account to key on).
|
|
59
64
|
* - `'both'` — both checks run; either can block.
|
|
60
65
|
*
|
|
61
66
|
* Throttle-requests semantics — every invocation records, regardless of
|
|
@@ -72,7 +77,7 @@ export const ActionSpec = z.strictObject({
|
|
|
72
77
|
});
|
|
73
78
|
export const RequestResponseActionSpec = ActionSpec.extend({
|
|
74
79
|
kind: z.literal('request_response').default('request_response'),
|
|
75
|
-
auth:
|
|
80
|
+
auth: RouteAuth,
|
|
76
81
|
async: z.literal(true).default(true),
|
|
77
82
|
});
|
|
78
83
|
export const RemoteNotificationActionSpec = ActionSpec.extend({
|