@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
package/dist/testing/stubs.js
CHANGED
|
@@ -14,6 +14,8 @@ import { prefix_route_specs } from '../http/route_spec.js';
|
|
|
14
14
|
import { create_bootstrap_route_specs } from '../auth/bootstrap_routes.js';
|
|
15
15
|
import { create_rpc_endpoint } from '../actions/action_rpc.js';
|
|
16
16
|
import { create_app_surface_spec, } from '../http/surface.js';
|
|
17
|
+
import { AUDIT_LOG_SSE_MAX_PER_SCOPE } from '../realtime/sse_auth_guard.js';
|
|
18
|
+
import { SubscriberRegistry } from '../realtime/subscriber_registry.js';
|
|
17
19
|
import { BaseServerEnv } from '../server/env.js';
|
|
18
20
|
/* eslint-disable @typescript-eslint/require-await */
|
|
19
21
|
/**
|
|
@@ -88,6 +90,45 @@ export const stub_handler = () => new Response('stub');
|
|
|
88
90
|
/** Stub middleware that passes through. */
|
|
89
91
|
export const stub_mw = async (_c, next) => next();
|
|
90
92
|
const stub_db = create_noop_stub('stub_db');
|
|
93
|
+
/**
|
|
94
|
+
* Build a no-op `AuditEmitter` for tests that don't assert on audit fan-out.
|
|
95
|
+
*
|
|
96
|
+
* `emit` / `emit_role_grant_target` are no-ops; `emit_pool` resolves
|
|
97
|
+
* immediately; `notify` is a no-op; `on_event_chain` is a frozen empty
|
|
98
|
+
* array — pushing onto it throws at runtime, so a test that wires a
|
|
99
|
+
* listener fails loudly instead of silently never firing. Tests asserting
|
|
100
|
+
* on real audit-row persistence (or on listener fan-out) build a real
|
|
101
|
+
* emitter via `create_audit_emitter` against a stub or real DB —
|
|
102
|
+
* `create_test_app` already does this on the test backend.
|
|
103
|
+
*/
|
|
104
|
+
export const create_test_audit_emitter = () => ({
|
|
105
|
+
emit: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
106
|
+
emit_role_grant_target: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
107
|
+
emit_pool: async () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
108
|
+
notify: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
109
|
+
on_event_chain: Object.freeze([]),
|
|
110
|
+
});
|
|
111
|
+
/**
|
|
112
|
+
* Build a no-op `AuditLogSse` for tests that wire `audit_sse` into the
|
|
113
|
+
* surface helper but don't assert on SSE fan-out or subscriber state.
|
|
114
|
+
*
|
|
115
|
+
* `subscribe` returns a no-op cleanup; `on_audit_event` is a no-op; the
|
|
116
|
+
* `registry` is a fresh `SubscriberRegistry` instance (call sites that
|
|
117
|
+
* inspect `.size` or call `.close_*` see a real registry, so writes are
|
|
118
|
+
* isolated per test). Tests that need real SSE plumbing build it via
|
|
119
|
+
* `create_audit_log_sse` against `create_test_app`.
|
|
120
|
+
*/
|
|
121
|
+
export const create_stub_audit_sse = () => {
|
|
122
|
+
const registry = new SubscriberRegistry({
|
|
123
|
+
max_per_scope: AUDIT_LOG_SSE_MAX_PER_SCOPE,
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
subscribe: () => () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
127
|
+
log: new Logger('test:audit_sse', { level: 'off' }),
|
|
128
|
+
on_audit_event: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
129
|
+
registry,
|
|
130
|
+
};
|
|
131
|
+
};
|
|
91
132
|
/** Stub `AppDeps` for auth surface tests — throws on any method access. */
|
|
92
133
|
export const stub_app_deps = {
|
|
93
134
|
stat: create_throwing_stub('stat'),
|
|
@@ -97,7 +138,7 @@ export const stub_app_deps = {
|
|
|
97
138
|
password: create_throwing_stub('password'),
|
|
98
139
|
db: create_throwing_stub('db'),
|
|
99
140
|
log: create_throwing_stub('log'),
|
|
100
|
-
|
|
141
|
+
audit: create_test_audit_emitter(),
|
|
101
142
|
};
|
|
102
143
|
/**
|
|
103
144
|
* Create no-op `AppDeps` for auth surface testing.
|
|
@@ -110,7 +151,7 @@ export const create_stub_app_deps = () => ({
|
|
|
110
151
|
password: create_noop_stub('password'),
|
|
111
152
|
db: stub_db,
|
|
112
153
|
log: new Logger('test', { level: 'off' }),
|
|
113
|
-
|
|
154
|
+
audit: create_test_audit_emitter(),
|
|
114
155
|
});
|
|
115
156
|
/** Create the API middleware stub array matching `create_auth_middleware_specs` output. */
|
|
116
157
|
export const create_stub_api_middleware = (options) => {
|
|
@@ -97,8 +97,8 @@ export interface SurfaceSecurityPolicyOptions {
|
|
|
97
97
|
/**
|
|
98
98
|
* Path patterns for routes that should be rate-limited.
|
|
99
99
|
* Default: common sensitive REST patterns (login, password, bootstrap).
|
|
100
|
-
* `account_token_create`
|
|
101
|
-
*
|
|
100
|
+
* `account_token_create` lives on the RPC surface; per-method RPC rate
|
|
101
|
+
* limiting is a separate invariant if consumers want it.
|
|
102
102
|
*/
|
|
103
103
|
sensitive_route_patterns?: Array<string | RegExp>;
|
|
104
104
|
/**
|
|
@@ -43,7 +43,7 @@ import { type Uuid } from '@fuzdev/fuz_util/id.js';
|
|
|
43
43
|
import type { ActionSpecUnion } from '../actions/action_spec.js';
|
|
44
44
|
import type { Action } from '../actions/action_types.js';
|
|
45
45
|
import type { ActionEventEnvironment } from '../actions/action_event_types.js';
|
|
46
|
-
import { type
|
|
46
|
+
import { type RegisterActionWsOptions } from '../actions/register_action_ws.js';
|
|
47
47
|
import { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
|
|
48
48
|
import { type RequestContext } from '../auth/request_context.js';
|
|
49
49
|
import { type CredentialType } from '../hono_context.js';
|
|
@@ -124,7 +124,7 @@ export interface WsConnectIdentity {
|
|
|
124
124
|
session_id?: string;
|
|
125
125
|
/** Api token id; set for bearer connections, null otherwise. */
|
|
126
126
|
api_token_id?: string | null;
|
|
127
|
-
/** Roles to grant via active
|
|
127
|
+
/** Roles to grant via active role_grants. Pass `[ROLE_KEEPER]` for keeper actions. */
|
|
128
128
|
roles?: Array<string>;
|
|
129
129
|
}
|
|
130
130
|
/** A mock WS client: send requests, inspect/await incoming messages. */
|
|
@@ -214,15 +214,14 @@ export declare const is_notification_with: <P>(method: string, match: (params: P
|
|
|
214
214
|
/** Predicate matching a JSON-RPC response frame (success or error) for the given request id. */
|
|
215
215
|
export declare const is_response_for: (id: number | string) => (msg: unknown) => boolean;
|
|
216
216
|
/** Options for `create_ws_test_harness`. */
|
|
217
|
-
export interface CreateWsTestHarnessOptions
|
|
217
|
+
export interface CreateWsTestHarnessOptions {
|
|
218
218
|
/**
|
|
219
219
|
* The actions registered on this endpoint — matches the shape
|
|
220
220
|
* `register_action_ws` accepts. Each entry is a `{spec, handler?}` tuple;
|
|
221
221
|
* shared fuz_app primitives (like `heartbeat_action`) can be spread in
|
|
222
222
|
* alongside consumer-specific actions.
|
|
223
223
|
*/
|
|
224
|
-
actions: ReadonlyArray<Action
|
|
225
|
-
extend_context?: RegisterActionWsOptions<TCtx>['extend_context'];
|
|
224
|
+
actions: ReadonlyArray<Action>;
|
|
226
225
|
/** Pass a pre-created transport to share with a broadcast API. */
|
|
227
226
|
transport?: BackendWebsocketTransport;
|
|
228
227
|
/**
|
|
@@ -230,13 +229,13 @@ export interface CreateWsTestHarnessOptions<TCtx extends BaseHandlerContext> {
|
|
|
230
229
|
* fake timers + receive-silence detection need explicit opt-in and per-
|
|
231
230
|
* test tuning to avoid spurious closes.
|
|
232
231
|
*/
|
|
233
|
-
heartbeat?: RegisterActionWsOptions
|
|
232
|
+
heartbeat?: RegisterActionWsOptions['heartbeat'];
|
|
234
233
|
/** Optional logger. Defaults to a silent `[ws-test]` logger. */
|
|
235
234
|
log?: Logger;
|
|
236
235
|
/** Threaded straight through to `register_action_ws`. */
|
|
237
|
-
on_socket_open?: RegisterActionWsOptions
|
|
236
|
+
on_socket_open?: RegisterActionWsOptions['on_socket_open'];
|
|
238
237
|
/** Threaded straight through to `register_action_ws`. */
|
|
239
|
-
on_socket_close?: RegisterActionWsOptions
|
|
238
|
+
on_socket_close?: RegisterActionWsOptions['on_socket_close'];
|
|
240
239
|
}
|
|
241
240
|
/** A harness instance — transport handle + connection factory. */
|
|
242
241
|
export interface WsTestHarness {
|
|
@@ -259,24 +258,24 @@ export interface WsTestHarness {
|
|
|
259
258
|
* auth identity. Returned clients drive the real
|
|
260
259
|
* `onOpen`/`onMessage`/`onClose` path against a real `WSContext`.
|
|
261
260
|
*/
|
|
262
|
-
export declare const create_ws_test_harness:
|
|
261
|
+
export declare const create_ws_test_harness: (options: CreateWsTestHarnessOptions) => WsTestHarness;
|
|
263
262
|
/** Convenience: default identity for keeper-authenticated connections. */
|
|
264
263
|
export declare const keeper_identity: () => WsConnectIdentity;
|
|
265
264
|
/**
|
|
266
265
|
* Wire a typed broadcast API against the harness's transport, matching
|
|
267
266
|
* how a consumer's real backend composes the stack. Returns the typed
|
|
268
|
-
* API so tests can call `.
|
|
267
|
+
* API so tests can call `.zap_run_created(...)` / `.workspace_changed(...)`
|
|
269
268
|
* etc. directly.
|
|
270
269
|
*
|
|
271
270
|
* ```ts
|
|
272
|
-
* const harness = create_ws_test_harness
|
|
271
|
+
* const harness = create_ws_test_harness({actions});
|
|
273
272
|
* const broadcast = build_broadcast_api<MyBackendActionsApi>({
|
|
274
273
|
* harness,
|
|
275
274
|
* specs: my_broadcast_action_specs,
|
|
276
275
|
* });
|
|
277
276
|
* const client = await harness.connect(keeper_identity());
|
|
278
|
-
* await broadcast.
|
|
279
|
-
* await client.wait_for(is_notification('
|
|
277
|
+
* await broadcast.zap_run_created({run_id: '...', ...});
|
|
278
|
+
* await client.wait_for(is_notification('zap_run_created'));
|
|
280
279
|
* ```
|
|
281
280
|
*/
|
|
282
281
|
export declare const build_broadcast_api: <TApi extends object>(options: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/ws_round_trip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAO,MAAM,MAAM,CAAC;AACxC,OAAO,EACN,SAAS,EAET,KAAK,gBAAgB,EAErB,KAAK,QAAQ,EACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAc,KAAK,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,4BAA4B,CAAC;AAEvD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,kCAAkC,CAAC;AAE7E,OAAO,
|
|
1
|
+
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/ws_round_trip.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,14 +41,15 @@ import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
|
41
41
|
import { create_uuid } from '@fuzdev/fuz_util/id.js';
|
|
42
42
|
import { ActionPeer } from '../actions/action_peer.js';
|
|
43
43
|
import { create_broadcast_api } from '../actions/broadcast_api.js';
|
|
44
|
-
import { register_action_ws
|
|
44
|
+
import { register_action_ws } from '../actions/register_action_ws.js';
|
|
45
|
+
import { create_stub_db } from './stubs.js';
|
|
45
46
|
import { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
|
|
46
47
|
import { REQUEST_CONTEXT_KEY } from '../auth/request_context.js';
|
|
47
48
|
import { ROLE_KEEPER } from '../auth/role_schema.js';
|
|
48
49
|
import { ACCOUNT_ID_KEY, AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY, TEST_CONTEXT_PRESET_KEY, } from '../hono_context.js';
|
|
49
50
|
import { JSONRPC_VERSION } from '../http/jsonrpc.js';
|
|
50
51
|
import { create_jsonrpc_request, is_jsonrpc_error_response, is_jsonrpc_notification, is_jsonrpc_response, } from '../http/jsonrpc_helpers.js';
|
|
51
|
-
import { create_test_account, create_test_actor,
|
|
52
|
+
import { create_test_account, create_test_actor, create_test_role_grant } from './entities.js';
|
|
52
53
|
/**
|
|
53
54
|
* Build a real `WSContext` backed by in-memory `send`/`close` capture.
|
|
54
55
|
* Parsing of outgoing frames is left to the caller — `sends` holds the
|
|
@@ -160,7 +161,7 @@ export const is_notification_with = (method, match) => (msg) => is_jsonrpc_notif
|
|
|
160
161
|
export const is_response_for = (id) => (msg) => (is_jsonrpc_response(msg) || is_jsonrpc_error_response(msg)) && msg.id === id;
|
|
161
162
|
const DEFAULT_TIMEOUT_MS = 1000;
|
|
162
163
|
/**
|
|
163
|
-
* Build a `RequestContext` with a fresh UUID account/actor and
|
|
164
|
+
* Build a `RequestContext` with a fresh UUID account/actor and role_grants
|
|
164
165
|
* for the supplied roles. Used by the high-level harness so callers can
|
|
165
166
|
* pass `roles: [ROLE_KEEPER, 'admin']`.
|
|
166
167
|
*/
|
|
@@ -187,10 +188,11 @@ const build_multi_role_request_context = (account_id, roles) => {
|
|
|
187
188
|
updated_at: null,
|
|
188
189
|
updated_by: null,
|
|
189
190
|
},
|
|
190
|
-
|
|
191
|
+
role_grants: roles.map((role) => ({
|
|
191
192
|
id: create_uuid(),
|
|
192
193
|
actor_id,
|
|
193
194
|
role,
|
|
195
|
+
scope_kind: null,
|
|
194
196
|
scope_id: null,
|
|
195
197
|
created_at: now,
|
|
196
198
|
expires_at: null,
|
|
@@ -210,7 +212,7 @@ const build_multi_role_request_context = (account_id, roles) => {
|
|
|
210
212
|
const build_simple_request_context = (role) => ({
|
|
211
213
|
account: create_test_account({ id: 'acc_1', username: 'testuser' }),
|
|
212
214
|
actor: create_test_actor({ id: 'act_1', account_id: 'acc_1', name: 'testuser' }),
|
|
213
|
-
|
|
215
|
+
role_grants: role ? [create_test_role_grant({ id: 'perm_1', actor_id: 'act_1', role })] : [],
|
|
214
216
|
});
|
|
215
217
|
/**
|
|
216
218
|
* Create a WebSocket test harness for the given specs + handlers.
|
|
@@ -222,16 +224,22 @@ const build_simple_request_context = (role) => ({
|
|
|
222
224
|
* `onOpen`/`onMessage`/`onClose` path against a real `WSContext`.
|
|
223
225
|
*/
|
|
224
226
|
export const create_ws_test_harness = (options) => {
|
|
225
|
-
const { actions,
|
|
227
|
+
const { actions, transport = new BackendWebsocketTransport(), heartbeat = false, log = new Logger('[ws-test]', { level: 'off' }), on_socket_open, on_socket_close, } = options;
|
|
226
228
|
const stub = create_stub_upgrade();
|
|
227
229
|
// Minimal Hono stub — `register_action_ws` only needs `.get(path, handler)`.
|
|
228
230
|
const stub_app = { get: () => stub_app };
|
|
231
|
+
// Stub DB — the harness pre-bakes `RequestContext` via the test-preset
|
|
232
|
+
// escape hatch so `perform_action` skips the live authorization phase.
|
|
233
|
+
// `db.transaction(fn)` synchronously calls `fn(stub_db)` so handlers
|
|
234
|
+
// declaring `side_effects: true` execute under the same shape they
|
|
235
|
+
// would in production.
|
|
236
|
+
const stub_db = create_stub_db();
|
|
229
237
|
register_action_ws({
|
|
230
238
|
path: '/test/ws',
|
|
231
239
|
app: stub_app,
|
|
232
240
|
upgradeWebSocket: stub.upgradeWebSocket,
|
|
233
241
|
actions,
|
|
234
|
-
|
|
242
|
+
db: stub_db,
|
|
235
243
|
transport,
|
|
236
244
|
heartbeat,
|
|
237
245
|
log,
|
|
@@ -383,18 +391,18 @@ const make_peer = () => new ActionPeer({ environment: new MinimalActionEnvironme
|
|
|
383
391
|
/**
|
|
384
392
|
* Wire a typed broadcast API against the harness's transport, matching
|
|
385
393
|
* how a consumer's real backend composes the stack. Returns the typed
|
|
386
|
-
* API so tests can call `.
|
|
394
|
+
* API so tests can call `.zap_run_created(...)` / `.workspace_changed(...)`
|
|
387
395
|
* etc. directly.
|
|
388
396
|
*
|
|
389
397
|
* ```ts
|
|
390
|
-
* const harness = create_ws_test_harness
|
|
398
|
+
* const harness = create_ws_test_harness({actions});
|
|
391
399
|
* const broadcast = build_broadcast_api<MyBackendActionsApi>({
|
|
392
400
|
* harness,
|
|
393
401
|
* specs: my_broadcast_action_specs,
|
|
394
402
|
* });
|
|
395
403
|
* const client = await harness.connect(keeper_identity());
|
|
396
|
-
* await broadcast.
|
|
397
|
-
* await client.wait_for(is_notification('
|
|
404
|
+
* await broadcast.zap_run_created({run_id: '...', ...});
|
|
405
|
+
* await client.wait_for(is_notification('zap_run_created'));
|
|
398
406
|
* ```
|
|
399
407
|
*/
|
|
400
408
|
export const build_broadcast_api = (options) => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* Admin accounts table — users with their
|
|
3
|
+
* Admin accounts table — users with their role_grants and pending offers.
|
|
4
4
|
* Consumes `admin_accounts_rpc_context` (read via `AdminAccountsState`)
|
|
5
5
|
* and `format_scope_context` for label rendering. Per-row actions:
|
|
6
|
-
* grant role (`
|
|
7
|
-
* keyed by `actor_id`), retract pending offer (`
|
|
6
|
+
* grant role (`role_grant_offer_create`), revoke role_grant (`role_grant_revoke`,
|
|
7
|
+
* keyed by `actor_id`), retract pending offer (`role_grant_offer_retract`).
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
const get_format_scope = format_scope_context.get();
|
|
23
23
|
const format_scope = $derived(get_format_scope());
|
|
24
24
|
|
|
25
|
-
// `null` global label: global
|
|
25
|
+
// `null` global label: global role_grants render no scope chip — the implicit default in admin tables.
|
|
26
26
|
const scope_label = (scope_id: string | null, role: string): string | null =>
|
|
27
27
|
resolve_scope_label(scope_id, role, format_scope, null);
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
const columns: Array<DatatableColumn<AdminAccountEntryJson>> = [
|
|
32
32
|
{key: 'account', label: 'username', width: 180},
|
|
33
|
-
{key: '
|
|
33
|
+
{key: 'role_grants', label: 'role_grants', width: 240},
|
|
34
34
|
{key: 'actor', label: 'grant', width: 200},
|
|
35
35
|
];
|
|
36
36
|
</script>
|
|
@@ -72,31 +72,34 @@
|
|
|
72
72
|
updated {format_relative_time(row.account.updated_at)}
|
|
73
73
|
</div>
|
|
74
74
|
{/if}
|
|
75
|
-
{:else if column.key === '
|
|
76
|
-
{#each row.
|
|
77
|
-
{@const scope = scope_label(
|
|
75
|
+
{:else if column.key === 'role_grants'}
|
|
76
|
+
{#each row.role_grants as role_grant (role_grant.id)}
|
|
77
|
+
{@const scope = scope_label(role_grant.scope_id, role_grant.role)}
|
|
78
78
|
<div class="row">
|
|
79
|
-
<span class="chip color_b">{
|
|
79
|
+
<span class="chip color_b">{role_grant.role}</span>
|
|
80
80
|
{#if scope !== null}
|
|
81
|
-
<span class="text_50 font_size_sm" title={
|
|
81
|
+
<span class="text_50 font_size_sm" title={role_grant.scope_id ?? undefined}>
|
|
82
82
|
{scope}
|
|
83
83
|
</span>
|
|
84
84
|
{/if}
|
|
85
|
-
{#if
|
|
86
|
-
<span
|
|
87
|
-
|
|
85
|
+
{#if role_grant.expires_at}
|
|
86
|
+
<span
|
|
87
|
+
class="text_50 font_size_sm"
|
|
88
|
+
title={format_datetime_local(role_grant.expires_at)}
|
|
89
|
+
>
|
|
90
|
+
expires {format_relative_time(role_grant.expires_at)}
|
|
88
91
|
</span>
|
|
89
92
|
{/if}
|
|
90
93
|
{#if admin_accounts.has_rpc && row.actor}
|
|
91
94
|
{@const actor_id = row.actor.id}
|
|
92
95
|
<ConfirmButton
|
|
93
|
-
onconfirm={() => admin_accounts.
|
|
94
|
-
title="revoke {
|
|
96
|
+
onconfirm={() => admin_accounts.revoke_role_grant(actor_id, role_grant.id)}
|
|
97
|
+
title="revoke {role_grant.role}"
|
|
95
98
|
class="sm"
|
|
96
|
-
disabled={admin_accounts.revoking_ids.has(
|
|
99
|
+
disabled={admin_accounts.revoking_ids.has(role_grant.id)}
|
|
97
100
|
>
|
|
98
101
|
{#snippet children(_popover, _confirm)}
|
|
99
|
-
{admin_accounts.revoking_ids.has(
|
|
102
|
+
{admin_accounts.revoking_ids.has(role_grant.id) ? 'revoking…' : 'revoke'}
|
|
100
103
|
{/snippet}
|
|
101
104
|
</ConfirmButton>
|
|
102
105
|
{/if}
|
|
@@ -130,15 +133,15 @@
|
|
|
130
133
|
{/if}
|
|
131
134
|
</div>
|
|
132
135
|
{/each}
|
|
133
|
-
{#if row.
|
|
136
|
+
{#if row.role_grants.length === 0 && row.pending_offers.length === 0}
|
|
134
137
|
<span class="text_50">none</span>
|
|
135
138
|
{/if}
|
|
136
139
|
{:else if column.key === 'actor'}
|
|
137
140
|
{#if admin_accounts.has_rpc}
|
|
138
141
|
{#each admin_accounts.grantable_roles as role (role)}
|
|
139
|
-
{#if !row.
|
|
142
|
+
{#if !row.role_grants.some((p) => p.role === role) && !row.pending_offers.some((o) => o.role === role)}
|
|
140
143
|
<ConfirmButton
|
|
141
|
-
onconfirm={() => admin_accounts.
|
|
144
|
+
onconfirm={() => admin_accounts.create_role_grant(row.account.id, role)}
|
|
142
145
|
title="offer {role}"
|
|
143
146
|
class="sm"
|
|
144
147
|
disabled={admin_accounts.granting_keys.has(`${row.account.id}:${role}`)}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Admin dashboard — six summary panels (accounts, sessions, invites,
|
|
4
4
|
* recent activity, security, system) fed by parallel `fetch()` calls
|
|
5
5
|
* on mount. Consumes all four admin RPC contexts plus `auth_state_context`;
|
|
6
|
-
* derives `role_counts`, `failed_logins`, and `
|
|
6
|
+
* derives `role_counts`, `failed_logins`, and `role_grant_changes` from the
|
|
7
7
|
* audit log slice.
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
@@ -39,14 +39,16 @@
|
|
|
39
39
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
40
40
|
const counts = new Map<string, number>();
|
|
41
41
|
for (const entry of accounts.accounts) {
|
|
42
|
-
const roles = new Set(entry.
|
|
42
|
+
const roles = new Set(entry.role_grants.map((p) => p.role));
|
|
43
43
|
for (const role of roles) {
|
|
44
44
|
counts.set(role, (counts.get(role) || 0) + 1);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
return Array.from(counts.entries());
|
|
48
48
|
});
|
|
49
|
-
const unroled_count = $derived(
|
|
49
|
+
const unroled_count = $derived(
|
|
50
|
+
accounts.accounts.filter((a) => a.role_grants.length === 0).length,
|
|
51
|
+
);
|
|
50
52
|
|
|
51
53
|
// sessions
|
|
52
54
|
const unique_users = $derived(new Set(sessions.sessions.map((s) => s.username)).size);
|
|
@@ -63,9 +65,9 @@
|
|
|
63
65
|
const failed_logins = $derived(
|
|
64
66
|
audit_log.events.filter((e) => e.event_type === 'login' && e.outcome === 'failure'),
|
|
65
67
|
);
|
|
66
|
-
const
|
|
68
|
+
const role_grant_changes = $derived(
|
|
67
69
|
audit_log.events.filter(
|
|
68
|
-
(e) => e.event_type === '
|
|
70
|
+
(e) => e.event_type === 'role_grant_create' || e.event_type === 'role_grant_revoke',
|
|
69
71
|
),
|
|
70
72
|
);
|
|
71
73
|
|
|
@@ -108,10 +110,10 @@
|
|
|
108
110
|
{#each accounts.accounts.slice(0, 6) as entry (entry)}
|
|
109
111
|
<li>
|
|
110
112
|
<strong>{entry.account.username}</strong>
|
|
111
|
-
{#each entry.
|
|
112
|
-
<span class="chip font_size_sm">{
|
|
113
|
+
{#each entry.role_grants as role_grant (role_grant.id)}
|
|
114
|
+
<span class="chip font_size_sm">{role_grant.role}</span>
|
|
113
115
|
{/each}
|
|
114
|
-
{#if entry.
|
|
116
|
+
{#if entry.role_grants.length === 0}
|
|
115
117
|
<span class="text_50 font_size_sm">no roles</span>
|
|
116
118
|
{/if}
|
|
117
119
|
</li>
|
|
@@ -244,17 +246,17 @@
|
|
|
244
246
|
<span class="text_50">failed logins</span>
|
|
245
247
|
</div>
|
|
246
248
|
<div class="baseline-row gap_xs">
|
|
247
|
-
<strong class="font_size_lg">{
|
|
248
|
-
<span class="text_50">
|
|
249
|
+
<strong class="font_size_lg">{role_grant_changes.length}</strong>
|
|
250
|
+
<span class="text_50">role_grant changes</span>
|
|
249
251
|
</div>
|
|
250
|
-
{#if
|
|
252
|
+
{#if role_grant_changes.length > 0}
|
|
251
253
|
<ul class="compact-list">
|
|
252
|
-
{#each
|
|
254
|
+
{#each role_grant_changes.slice(0, 4) as event (event.id)}
|
|
253
255
|
<li class="font_size_sm">
|
|
254
256
|
<span class="text_50" title={format_datetime_local(event.created_at)}
|
|
255
257
|
>{format_relative_time(event.created_at)}</span
|
|
256
258
|
>
|
|
257
|
-
<code>{event.event_type === '
|
|
259
|
+
<code>{event.event_type === 'role_grant_create' ? 'grant' : 'revoke'}</code>
|
|
258
260
|
{#if event.metadata?.role}
|
|
259
261
|
<span class="chip font_size_sm">{event.metadata.role}</span>
|
|
260
262
|
{/if}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminOverview.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminOverview.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminOverview.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminOverview.svelte"],"names":[],"mappings":"AA4UA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* calls `audit_log.
|
|
5
|
-
* `
|
|
3
|
+
* Role grant create/revoke history table. Consumes `audit_log_rpc_context`,
|
|
4
|
+
* calls `audit_log.fetch_role_grant_history()` once on mount (the
|
|
5
|
+
* `audit_log_role_grant_history` RPC). Uses `format_scope_context` to render
|
|
6
6
|
* scope ids as human labels.
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import {format_relative_time, format_datetime_local, truncate_uuid} from './ui_format.js';
|
|
13
13
|
import Datatable from './Datatable.svelte';
|
|
14
14
|
import type {DatatableColumn} from './datatable.js';
|
|
15
|
-
import type {
|
|
15
|
+
import type {RoleGrantHistoryEventJson} from '../auth/audit_log_schema.js';
|
|
16
16
|
import {format_scope_context, resolve_scope_label} from './format_scope.js';
|
|
17
17
|
|
|
18
18
|
const get_rpc = audit_log_rpc_context.get();
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
const get_format_scope = format_scope_context.get();
|
|
21
21
|
const format_scope = $derived(get_format_scope());
|
|
22
22
|
|
|
23
|
-
void audit_log.
|
|
23
|
+
void audit_log.fetch_role_grant_history();
|
|
24
24
|
|
|
25
|
-
const columns: Array<DatatableColumn<
|
|
25
|
+
const columns: Array<DatatableColumn<RoleGrantHistoryEventJson>> = [
|
|
26
26
|
{key: 'event_type', label: 'action', width: 100},
|
|
27
27
|
{key: 'metadata', label: 'role', width: 160},
|
|
28
28
|
{key: 'username', label: 'by', width: 140},
|
|
@@ -38,22 +38,22 @@
|
|
|
38
38
|
</script>
|
|
39
39
|
|
|
40
40
|
<section>
|
|
41
|
-
<h1>
|
|
41
|
+
<h1>role_grant history</h1>
|
|
42
42
|
|
|
43
43
|
{#if audit_log.loading}
|
|
44
|
-
<p class="text_50">loading
|
|
44
|
+
<p class="text_50">loading role_grant history...</p>
|
|
45
45
|
{:else if audit_log.error}
|
|
46
46
|
<p class="color_c_50">{audit_log.error}</p>
|
|
47
47
|
{:else}
|
|
48
|
-
<Datatable {columns} rows={audit_log.
|
|
48
|
+
<Datatable {columns} rows={audit_log.role_grant_history_events} height="400px" row_key="id">
|
|
49
49
|
{#snippet cell(column, row)}
|
|
50
50
|
{#if column.key === 'event_type'}
|
|
51
51
|
<span
|
|
52
52
|
class="chip"
|
|
53
|
-
class:color_b={row.event_type === '
|
|
54
|
-
class:color_c={row.event_type === '
|
|
53
|
+
class:color_b={row.event_type === 'role_grant_create'}
|
|
54
|
+
class:color_c={row.event_type === 'role_grant_revoke'}
|
|
55
55
|
>
|
|
56
|
-
{row.event_type === '
|
|
56
|
+
{row.event_type === 'role_grant_create' ? 'grant' : 'revoke'}
|
|
57
57
|
</span>
|
|
58
58
|
{:else if column.key === 'metadata'}
|
|
59
59
|
{#if row.metadata}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminRoleGrantHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminRoleGrantHistory.svelte"],"names":[],"mappings":"AAiGA,QAAA,MAAM,qBAAqB,2DAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import PendingButton from '@fuzdev/fuz_ui/PendingButton.svelte';
|
|
18
18
|
import {autofocus} from '@fuzdev/fuz_ui/autofocus.svelte.js';
|
|
19
19
|
|
|
20
|
-
import {Username} from '../
|
|
20
|
+
import {Username} from '../primitive_schemas.js';
|
|
21
21
|
import {PASSWORD_LENGTH_MIN} from '../auth/password.js';
|
|
22
22
|
import {auth_state_context} from './auth_state.svelte.js';
|
|
23
23
|
import {FormState} from './form_state.svelte.js';
|