@fuzdev/fuz_app 0.61.0 → 0.63.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 +43 -35
- package/dist/actions/action_rpc.d.ts +10 -0
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +1 -1
- package/dist/actions/action_spec.d.ts +1 -1
- package/dist/actions/action_spec.js +1 -1
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +1 -0
- package/dist/auth/CLAUDE.md +47 -26
- package/dist/auth/account_action_specs.d.ts +6 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +11 -4
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +9 -4
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +8 -4
- package/dist/auth/account_schema.d.ts +2 -2
- package/dist/auth/account_schema.js +2 -2
- package/dist/auth/actor_lookup_actions.d.ts +1 -1
- package/dist/auth/actor_lookup_actions.js +1 -1
- package/dist/auth/actor_lookup_queries.d.ts +1 -1
- package/dist/auth/actor_lookup_queries.js +1 -1
- package/dist/auth/actor_search_action_specs.d.ts +1 -1
- package/dist/auth/actor_search_action_specs.js +1 -1
- package/dist/auth/actor_search_actions.d.ts +1 -1
- package/dist/auth/actor_search_actions.js +1 -1
- package/dist/auth/actor_search_queries.d.ts +1 -1
- package/dist/auth/actor_search_queries.js +1 -1
- package/dist/auth/all_action_spec_registries.d.ts +2 -2
- package/dist/auth/all_action_spec_registries.js +2 -2
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.js +1 -1
- package/dist/auth/audit_log_schema.d.ts +25 -0
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +16 -0
- package/dist/auth/request_context.d.ts +1 -1
- package/dist/env/update_env_variable.js +1 -1
- package/dist/http/CLAUDE.md +15 -15
- package/dist/server/app_server.d.ts +54 -6
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +32 -4
- package/dist/testing/CLAUDE.md +34 -50
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +17 -1
- package/dist/ui/CLAUDE.md +13 -18
- package/dist/ui/keyed_async_slot.svelte.d.ts +1 -1
- package/dist/ui/keyed_async_slot.svelte.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,IAAI,EAAE,KAAK,OAAO,EAAC,MAAM,MAAM,CAAC;AAGxC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAEN,KAAK,cAAc,EAEnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAGN,KAAK,WAAW,EAChB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,7 @@ import { bodyLimit } from 'hono/body-limit';
|
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { session_cookie_options, } from '../auth/session_cookie.js';
|
|
15
15
|
import { create_audit_log_sse, audit_log_event_specs, } from '../realtime/sse_auth_guard.js';
|
|
16
|
+
import { BaseServerEnv } from './env.js';
|
|
16
17
|
import { query_app_settings_load } from '../auth/app_settings_queries.js';
|
|
17
18
|
import { create_rate_limiter, default_login_account_rate_limit, default_action_account_rate_limit, default_action_ip_rate_limit, } from '../rate_limiter.js';
|
|
18
19
|
// Side-effect import: augments Hono's ContextVariableMap so consumers
|
|
@@ -31,6 +32,29 @@ import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
|
|
|
31
32
|
import { create_fuz_authorization_handler } from '../auth/request_context.js';
|
|
32
33
|
import { ERROR_PAYLOAD_TOO_LARGE } from '../http/error_schemas.js';
|
|
33
34
|
import { create_rpc_endpoint } from '../actions/action_rpc.js';
|
|
35
|
+
/**
|
|
36
|
+
* Assert that `audit_sse` was wired by `create_app_server` and return it
|
|
37
|
+
* as a non-null `AuditLogSse`. Throws a labelled error when the
|
|
38
|
+
* `audit_log_sse` option was not passed to `create_app_server`.
|
|
39
|
+
*
|
|
40
|
+
* Use in route factories that depend on factory-managed audit SSE:
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* create_route_specs: (ctx) => create_audit_log_route_specs({
|
|
44
|
+
* stream: require_audit_sse(ctx),
|
|
45
|
+
* }),
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* Preferred over `ctx.audit_sse!` — `!` lies to the type system and
|
|
49
|
+
* produces a downstream cannot-read-property crash if a consumer wires
|
|
50
|
+
* the route without enabling the option.
|
|
51
|
+
*/
|
|
52
|
+
export const require_audit_sse = (source) => {
|
|
53
|
+
if (!source.audit_sse) {
|
|
54
|
+
throw new Error('audit_sse is null — pass `audit_log_sse: true` (or `{role}`) in `AppServerOptions`');
|
|
55
|
+
}
|
|
56
|
+
return source.audit_sse;
|
|
57
|
+
};
|
|
34
58
|
/** Default maximum request body size: 1 MiB. */
|
|
35
59
|
export const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
|
|
36
60
|
/**
|
|
@@ -42,7 +66,11 @@ export const DEFAULT_MAX_BODY_SIZE = 1024 * 1024;
|
|
|
42
66
|
* pass `migration_namespaces` to `create_app_backend`.
|
|
43
67
|
*
|
|
44
68
|
* When `audit_log_sse` is set, the SSE registry's listener is appended to
|
|
45
|
-
* `backend.deps.audit.on_event_chain` — no shallow-copy of `AppDeps`.
|
|
69
|
+
* `backend.deps.audit.on_event_chain` — no shallow-copy of `AppDeps`. The
|
|
70
|
+
* `audit_sse` field on the returned `AppServer` (and the
|
|
71
|
+
* `AppServerContext` passed to `create_route_specs`) is non-null in that
|
|
72
|
+
* case; consumers can call `require_audit_sse(ctx)` / `require_audit_sse(server)`
|
|
73
|
+
* to assert the invariant.
|
|
46
74
|
*
|
|
47
75
|
* @returns assembled Hono app, backend, surface build, and bootstrap status
|
|
48
76
|
*/
|
|
@@ -161,7 +189,7 @@ export const create_app_server = async (options) => {
|
|
|
161
189
|
const surface_spec = create_app_surface_spec({
|
|
162
190
|
middleware_specs: surface_middleware,
|
|
163
191
|
route_specs,
|
|
164
|
-
env_schema: options.env_schema,
|
|
192
|
+
env_schema: options.env_schema ?? BaseServerEnv,
|
|
165
193
|
event_specs: all_event_specs,
|
|
166
194
|
rpc_endpoints: resolved_rpc_endpoints,
|
|
167
195
|
});
|
|
@@ -269,8 +297,8 @@ export const create_app_server = async (options) => {
|
|
|
269
297
|
}
|
|
270
298
|
// Static file serving
|
|
271
299
|
if (options.static_serving) {
|
|
272
|
-
const { serve_static, spa_fallback } = options.static_serving;
|
|
273
|
-
for (const mw of create_static_middleware(serve_static, { spa_fallback })) {
|
|
300
|
+
const { serve_static, root, spa_fallback, is_spa_route } = options.static_serving;
|
|
301
|
+
for (const mw of create_static_middleware(serve_static, { root, spa_fallback, is_spa_route })) {
|
|
274
302
|
app.use('/*', mw);
|
|
275
303
|
}
|
|
276
304
|
}
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -6,23 +6,17 @@ round-trip harnesses. Consumers import these to assemble their own test suites
|
|
|
6
6
|
against a fuz_app-derived server.
|
|
7
7
|
|
|
8
8
|
For narrative wiring examples (how to call these from a consumer's vitest
|
|
9
|
-
setup), see
|
|
10
|
-
conventions
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
setup), see ../../../docs/testing.md. For fuz_app's own test suite
|
|
10
|
+
conventions, see ../../test/CLAUDE.md. For shared testing conventions
|
|
11
|
+
(`.db.test.ts`, `assert` from vitest, `assert_rejects`, `vi.mock` caveats),
|
|
12
|
+
see Skill(fuz-stack) testing-patterns. This file is a reference index for
|
|
13
|
+
the helpers themselves.
|
|
13
14
|
|
|
14
15
|
## Production guard — always the first import
|
|
15
16
|
|
|
16
|
-
Every module
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
bundles. SvelteKit and Vite set `DEV` correctly for dev + tests; the
|
|
20
|
-
production code path explodes at the first testing-module import.
|
|
21
|
-
|
|
22
|
-
When adding a new module to this directory, make this import the first
|
|
23
|
-
line. The convention is enforced by grep, not by a linter — break it and
|
|
24
|
-
the production bundle still builds, then crashes at runtime on first
|
|
25
|
-
module load.
|
|
17
|
+
Every module here starts with `import './assert_dev_env.js';` — reads `DEV`
|
|
18
|
+
from `esm-env` and throws if false, preventing production-bundle inclusion.
|
|
19
|
+
Enforced by grep, not a linter; make this the first line in new modules.
|
|
26
20
|
|
|
27
21
|
## Stubs, factories, mocks
|
|
28
22
|
|
|
@@ -40,7 +34,7 @@ module load.
|
|
|
40
34
|
| `create_stub_app_deps()` | Factory returning fresh `AppDeps` with no-op FS/keyring/password, a `create_noop_stub` DB, silent `Logger`, no-op `audit`. |
|
|
41
35
|
| `create_test_audit_emitter()` | No-op `AuditEmitter` for tests that don't assert on audit fan-out. `emit` / `emit_role_grant_target` are no-ops; `emit_pool` resolves immediately; `notify` is a no-op; `on_event_chain` is empty. |
|
|
42
36
|
| `create_stub_audit_sse()` | No-op `AuditLogSse` for surface-test wiring without booting real SSE. `subscribe` returns a no-op cleanup; `on_audit_event` is a no-op; the `registry` is a fresh `SubscriberRegistry` (live `.size` / `.close_*` for tests touching registry state, isolated per call). For real SSE plumbing, build via `create_audit_log_sse` against `create_test_app`. |
|
|
43
|
-
| `create_stub_api_middleware({include_daemon_token?})` | Stub `MiddlewareSpec[]` matching `create_auth_middleware_specs`'s output (origin/session/request_context/bearer_auth, optional daemon_token) for surface generation without booting real auth. See
|
|
37
|
+
| `create_stub_api_middleware({include_daemon_token?})` | Stub `MiddlewareSpec[]` matching `create_auth_middleware_specs`'s output (origin/session/request_context/bearer_auth, optional daemon_token) for surface generation without booting real auth. See `auth/CLAUDE.md` §Middleware for the real stack. |
|
|
44
38
|
| `create_stub_app_server_context(session_options)` | Stub `AppServerContext` — rate limiters null, `bootstrap_status.available: false`, `app_settings.open_signup: false`. |
|
|
45
39
|
| `create_test_app_surface_spec(options)` | Builds an `AppSurfaceSpec` that mirrors `create_app_server`'s route assembly: consumer routes + factory-managed bootstrap routes (prefixed via `bootstrap_route_prefix`, default `'/api/account'`) + stub middleware + surface generation. `CreateTestAppSurfaceSpecOptions` accepts `session_options`, `create_route_specs`, `env_schema?`, `event_specs?`, `rpc_endpoints?`, `transform_middleware?`, `bootstrap_route_prefix?`. Single source of truth for attack-surface tests — track `create_app_server` wiring changes here. |
|
|
46
40
|
|
|
@@ -572,7 +566,7 @@ Options: `{session_options, create_route_specs, app_options?, db_factories?}`.
|
|
|
572
566
|
|
|
573
567
|
7 test groups covering admin surface: account listing, role_grant grant
|
|
574
568
|
lifecycle (via `role_grant_offer_create` + `role_grant_revoke` RPC flows —
|
|
575
|
-
**not** REST; see
|
|
569
|
+
**not** REST; see `auth/CLAUDE.md` for `role_grant_offer_action_specs.ts` + `role_grant_offer_actions.ts`), session / token management, audit log reads (RPC),
|
|
576
570
|
admin-to-admin isolation, error coverage, response schema validation.
|
|
577
571
|
|
|
578
572
|
Required options: `{session_options, create_route_specs, roles: RoleSchemaResult, rpc_endpoints: RpcEndpointsSuiteOption, admin_prefix?, app_options?, db_factories?}`.
|
|
@@ -688,7 +682,7 @@ Registry lookups:
|
|
|
688
682
|
- unauthenticated → `unauthenticated` (code -32001)
|
|
689
683
|
- wrong role → `forbidden` (-32002)
|
|
690
684
|
- authenticated without role → `forbidden`
|
|
691
|
-
- **keeper rejects non-daemon credentials** — session and api_token credentials are rejected even when the account has the keeper role (only `daemon_token` passes). The credential-type gate fires before the role gate (see
|
|
685
|
+
- **keeper rejects non-daemon credentials** — session and api_token credentials are rejected even when the account has the keeper role (only `daemon_token` passes). The credential-type gate fires before the role gate (see `auth/CLAUDE.md` §`request_context.ts` for `require_credential_types`).
|
|
692
686
|
- correct auth passes (not 401/403)
|
|
693
687
|
- GET unauthenticated for `side_effects: false` reads
|
|
694
688
|
2. **RPC adversarial envelopes** — fixed set exercising dispatcher steps 1–2: non-JSON body, wrong `jsonrpc` version, missing `jsonrpc` / `method` / `id`, batch array, unknown method, GET missing `method`/`id`, GET invalid JSON params, GET non-object params, GET mutation method → `invalid_request`.
|
|
@@ -700,47 +694,37 @@ deps — no DB needed.
|
|
|
700
694
|
Options: `{build: () => AppSurfaceSpec, roles: Array<string>}`.
|
|
701
695
|
|
|
702
696
|
**Opt-in bundles need their own per-bundle suite file.** Action bundles
|
|
703
|
-
not folded into `create_standard_rpc_actions` (today `self_service_role_actions
|
|
704
|
-
and `
|
|
705
|
-
from `describe_rpc_attack_surface_tests` +
|
|
706
|
-
unless the consumer ships a
|
|
707
|
-
opt-in factory on the RPC
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
697
|
+
not folded into `create_standard_rpc_actions` (today `self_service_role_actions`,
|
|
698
|
+
`actor_lookup_actions`, and `actor_search_actions`) get zero adversarial
|
|
699
|
+
/ round-trip coverage from `describe_rpc_attack_surface_tests` +
|
|
700
|
+
`describe_rpc_round_trip_tests` unless the consumer ships a
|
|
701
|
+
`<module>.rpc_suites.db.test.ts` mounting the opt-in factory on the RPC
|
|
702
|
+
endpoint and calling both suites. See ../../test/CLAUDE.md §Composable
|
|
703
|
+
Test Suites for the obligation note; existing
|
|
704
|
+
../../test/auth/\*.rpc_suites.db.test.ts files are templates.
|
|
711
705
|
|
|
712
706
|
## Cross-cutting conventions
|
|
713
707
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
-
|
|
718
|
-
|
|
719
|
-
`create_pglite_factory`). The suffix opts the file into the `db`
|
|
720
|
-
vitest project (`isolate: false`, `fileParallelism: false`) so the
|
|
721
|
-
PGlite WASM cache is shared across every DB test file.
|
|
708
|
+
Shared conventions (`.db.test.ts` suffix, `isolate: false` semantics,
|
|
709
|
+
`assert` from vitest, `assert_rejects`, `vi.mock` avoidance under
|
|
710
|
+
`isolate: false`) live in Skill(fuz-stack) testing-patterns.
|
|
711
|
+
fuz_app-specific points:
|
|
712
|
+
|
|
722
713
|
- **`await_pending_effects: true`** is set by `create_test_app`.
|
|
723
714
|
Fire-and-forget effects (audit logs, session touches, WS fan-out via
|
|
724
715
|
`emit_after_commit`) resolve before the response returns, so tests
|
|
725
716
|
can assert on side effects inline without manual flushing.
|
|
726
|
-
- **
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
ship one.
|
|
735
|
-
- **DI via small `*Deps` interfaces.** Stub factories here accept the
|
|
736
|
-
same narrow `*Deps` contracts production code uses — never
|
|
737
|
-
`Pick<GodType, ...>`. New helpers that need env/fs/logger access
|
|
738
|
-
should take `EnvDeps` / `FsReadDeps` / `Logger` from
|
|
739
|
-
`runtime/deps.ts` or `@fuzdev/fuz_util/log.js`.
|
|
717
|
+
- **Deep-path imports only.** Import from the canonical module
|
|
718
|
+
(`testing/db.js`, `testing/rpc_helpers.js`, etc.); fuz_app's `dist/` ships no
|
|
719
|
+
barrel.
|
|
720
|
+
- **DI via small `*Deps` interfaces.** Stub factories accept the same
|
|
721
|
+
narrow `*Deps` contracts production code uses — never
|
|
722
|
+
`Pick<GodType, ...>`. New helpers needing env/fs/logger take
|
|
723
|
+
`EnvDeps` / `FsReadDeps` / `Logger` from `runtime/deps.ts` or
|
|
724
|
+
`@fuzdev/fuz_util/log.js`.
|
|
740
725
|
- **Keep the shared echo routes in sync with public surface.** When
|
|
741
726
|
middleware or public API gains a new context variable, header, or
|
|
742
727
|
field, update the echo in `middleware.ts`
|
|
743
728
|
(`create_bearer_auth_test_app`, `create_test_middleware_stack_app`)
|
|
744
|
-
alongside the assertions in `src/test/auth/*.test.ts`.
|
|
745
|
-
|
|
746
|
-
test failure.
|
|
729
|
+
alongside the assertions in `src/test/auth/*.test.ts`. Drift surfaces
|
|
730
|
+
as a missed assertion, not a test failure.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAGN,KAAK,eAAe,EAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAqB1B;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;;;;;;;OAWG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;
|
|
1
|
+
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAGN,KAAK,eAAe,EAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAqB1B;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;;;;;;;OAWG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AA2ED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAihBzF,CAAC"}
|
|
@@ -27,12 +27,23 @@ import { admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spe
|
|
|
27
27
|
import { account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from '../auth/account_action_specs.js';
|
|
28
28
|
/** Query audit log events from the database. */
|
|
29
29
|
const query_audit_events = async (db) => {
|
|
30
|
-
return db.query('SELECT event_type, seq FROM audit_log ORDER BY seq');
|
|
30
|
+
return db.query('SELECT event_type, seq, metadata FROM audit_log ORDER BY seq');
|
|
31
31
|
};
|
|
32
32
|
/** Assert that audit events contain the expected event type. */
|
|
33
33
|
const assert_has_event = (events, expected, context) => {
|
|
34
34
|
assert.ok(events.some((e) => e.event_type === expected), `Expected '${expected}' audit event after ${context}`);
|
|
35
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Assert that an event type was emitted with the expected `credential_type`
|
|
38
|
+
* recorded in metadata — defense-in-depth coverage for the spec gate
|
|
39
|
+
* documented in `docs/security.md` §Credential-channel gating.
|
|
40
|
+
*/
|
|
41
|
+
const assert_event_credential_type = (events, expected, credential_type, context) => {
|
|
42
|
+
const match = events.find((e) => e.event_type === expected);
|
|
43
|
+
assert.ok(match, `Expected '${expected}' audit event after ${context}`);
|
|
44
|
+
const recorded = (match.metadata ?? {}).credential_type;
|
|
45
|
+
assert.strictEqual(recorded, credential_type, `Expected '${expected}' audit metadata.credential_type === '${credential_type}' after ${context} (got ${JSON.stringify(recorded)})`);
|
|
46
|
+
};
|
|
36
47
|
/** Build CreateTestAppOptions with admin+keeper roles. */
|
|
37
48
|
const build_options = (options, db) => ({
|
|
38
49
|
session_options: options.session_options,
|
|
@@ -138,6 +149,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
138
149
|
assert.ok(res.ok, `account_token_create failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
139
150
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
140
151
|
assert_has_event(events, 'token_create', 'account_token_create RPC');
|
|
152
|
+
assert_event_credential_type(events, 'token_create', 'session', 'account_token_create RPC');
|
|
141
153
|
});
|
|
142
154
|
test('token revoke produces token_revoke event', async () => {
|
|
143
155
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
@@ -162,6 +174,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
162
174
|
assert.ok(res.ok, `account_token_revoke failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
163
175
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
164
176
|
assert_has_event(events, 'token_revoke', 'account_token_revoke RPC');
|
|
177
|
+
assert_event_credential_type(events, 'token_revoke', 'session', 'account_token_revoke RPC');
|
|
165
178
|
});
|
|
166
179
|
test('session revoke produces session_revoke event', async () => {
|
|
167
180
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
@@ -198,6 +211,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
198
211
|
assert.ok(res.ok, `account_session_revoke failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
199
212
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
200
213
|
assert_has_event(events, 'session_revoke', 'account_session_revoke RPC');
|
|
214
|
+
assert_event_credential_type(events, 'session_revoke', 'session', 'account_session_revoke RPC');
|
|
201
215
|
});
|
|
202
216
|
test('session revoke-all produces session_revoke_all event', async () => {
|
|
203
217
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
@@ -211,6 +225,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
211
225
|
assert.ok(res.ok, `account_session_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
212
226
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
213
227
|
assert_has_event(events, 'session_revoke_all', 'account_session_revoke_all RPC');
|
|
228
|
+
assert_event_credential_type(events, 'session_revoke_all', 'session', 'account_session_revoke_all RPC');
|
|
214
229
|
});
|
|
215
230
|
test('password change produces password_change event', async () => {
|
|
216
231
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
@@ -227,6 +242,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
227
242
|
assert.strictEqual(res.status, 200);
|
|
228
243
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
229
244
|
assert_has_event(events, 'password_change', 'POST /password');
|
|
245
|
+
assert_event_credential_type(events, 'password_change', 'session', 'POST /password');
|
|
230
246
|
});
|
|
231
247
|
});
|
|
232
248
|
// --- Admin routes ---
|
package/dist/ui/CLAUDE.md
CHANGED
|
@@ -5,14 +5,16 @@ utilities. Cookie-based SPA auth; prerendered static HTML served by Hono
|
|
|
5
5
|
(no SvelteKit SSR for sessions). State classes hold one or more `AsyncSlot`s
|
|
6
6
|
via composition (one per distinct async operation — e.g. `list` + `create` +
|
|
7
7
|
`revoke`); per-row write ops use `KeyedAsyncSlot<K, T = void, E = string>`
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
so concurrent rows don't abort each other and failures surface per-row via
|
|
9
|
+
`slot.error(key)`. Payload lives as `$state.raw` fields on the class.
|
|
10
|
+
Shared dependencies flow through Svelte context, never through props —
|
|
11
|
+
RPC adapters are provisioned once at the admin shell and read by every
|
|
12
|
+
`Admin*.svelte`.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
For Svelte 5 patterns (runes, inline `$props`, contexts, snippets,
|
|
15
|
+
attachments), see Skill(fuz-stack) svelte-patterns. See ../../docs/usage.md
|
|
16
|
+
for end-to-end wiring examples ("Role grant offer UI", "Admin UI"). This
|
|
17
|
+
file is a reference, not a tutorial.
|
|
16
18
|
|
|
17
19
|
## Key patterns
|
|
18
20
|
|
|
@@ -67,15 +69,8 @@ it. Six methods land on the reducer: `role_grant_offer_received` /
|
|
|
67
69
|
`_retracted` / `_accepted` / `_declined` / `_supersede` all merge a
|
|
68
70
|
`{offer}` payload; `role_grant_revoke` is ignored at this layer (role_grant
|
|
69
71
|
lifecycle lives in auth/role_grants state). The six notification specs and
|
|
70
|
-
their payload shapes are defined in
|
|
71
|
-
(see
|
|
72
|
-
|
|
73
|
-
### Svelte 5 inline `$props` shape
|
|
74
|
-
|
|
75
|
-
Always `const {...}: {...} = $props()` — never `interface Props`.
|
|
76
|
-
Destructure defaults in the binding list; put the type literal inline.
|
|
77
|
-
This matches the user-memory Svelte props rule and the existing file
|
|
78
|
-
conventions.
|
|
72
|
+
their payload shapes are defined in `auth/role_grant_offer_notifications.ts`
|
|
73
|
+
(see `auth/CLAUDE.md` §WS notifications).
|
|
79
74
|
|
|
80
75
|
### Context over props for shared deps
|
|
81
76
|
|
|
@@ -184,8 +179,8 @@ destructive actions.
|
|
|
184
179
|
reason codes with friendly copy: `ERROR_ROLE_GRANT_OFFER_SELF_TARGET`,
|
|
185
180
|
`ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE`, `ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED`,
|
|
186
181
|
`ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH`, `ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH`
|
|
187
|
-
— imported from
|
|
188
|
-
|
|
182
|
+
— imported from `auth/role_grant_offer_action_specs.js` (see
|
|
183
|
+
`auth/CLAUDE.md` for `role_grant_offer_action_specs.ts` +
|
|
189
184
|
`role_grant_offer_actions.ts`).
|
|
190
185
|
- `RoleGrantOfferHistory.svelte` — both-directions history (recipient +
|
|
191
186
|
grantor, including terminal). Props: `current_actor_id: string | null`
|
|
@@ -56,7 +56,7 @@ export type KeyedAsyncSlotOptions<T, E = string> = Omit<AsyncSlotOptions<T, E>,
|
|
|
56
56
|
*
|
|
57
57
|
* @typeParam K - The key type. Map identity is SameValueZero — branded
|
|
58
58
|
* strings (`Uuid`) work directly. For composite keys, stringify at
|
|
59
|
-
* the call site (e.g.
|
|
59
|
+
* the call site (e.g. "`${account_id}:${role}`").
|
|
60
60
|
* @typeParam T - The success payload type. Use `void` for write-only
|
|
61
61
|
* actions whose response isn't worth retaining.
|
|
62
62
|
* @typeParam E - The shape of per-key `error(key)`. Defaults to
|
|
@@ -48,7 +48,7 @@ import { AsyncSlot } from './async_slot.svelte.js';
|
|
|
48
48
|
*
|
|
49
49
|
* @typeParam K - The key type. Map identity is SameValueZero — branded
|
|
50
50
|
* strings (`Uuid`) work directly. For composite keys, stringify at
|
|
51
|
-
* the call site (e.g.
|
|
51
|
+
* the call site (e.g. "`${account_id}:${role}`").
|
|
52
52
|
* @typeParam T - The success payload type. Use `void` for write-only
|
|
53
53
|
* actions whose response isn't worth retaining.
|
|
54
54
|
* @typeParam E - The shape of per-key `error(key)`. Defaults to
|