@fuzdev/fuz_app 0.62.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.
Files changed (45) hide show
  1. package/dist/actions/CLAUDE.md +15 -13
  2. package/dist/actions/action_rpc.d.ts +10 -0
  3. package/dist/actions/action_rpc.d.ts.map +1 -1
  4. package/dist/actions/action_rpc.js +1 -1
  5. package/dist/actions/action_spec.d.ts +1 -1
  6. package/dist/actions/action_spec.js +1 -1
  7. package/dist/actions/perform_action.d.ts.map +1 -1
  8. package/dist/actions/perform_action.js +1 -0
  9. package/dist/auth/CLAUDE.md +45 -24
  10. package/dist/auth/account_action_specs.d.ts +6 -0
  11. package/dist/auth/account_action_specs.d.ts.map +1 -1
  12. package/dist/auth/account_action_specs.js +11 -4
  13. package/dist/auth/account_actions.d.ts.map +1 -1
  14. package/dist/auth/account_actions.js +9 -4
  15. package/dist/auth/account_routes.d.ts.map +1 -1
  16. package/dist/auth/account_routes.js +8 -4
  17. package/dist/auth/account_schema.d.ts +2 -2
  18. package/dist/auth/account_schema.js +2 -2
  19. package/dist/auth/actor_lookup_actions.d.ts +1 -1
  20. package/dist/auth/actor_lookup_actions.js +1 -1
  21. package/dist/auth/actor_lookup_queries.d.ts +1 -1
  22. package/dist/auth/actor_lookup_queries.js +1 -1
  23. package/dist/auth/actor_search_action_specs.d.ts +1 -1
  24. package/dist/auth/actor_search_action_specs.js +1 -1
  25. package/dist/auth/actor_search_actions.d.ts +1 -1
  26. package/dist/auth/actor_search_actions.js +1 -1
  27. package/dist/auth/actor_search_queries.d.ts +1 -1
  28. package/dist/auth/actor_search_queries.js +1 -1
  29. package/dist/auth/all_action_spec_registries.d.ts +2 -2
  30. package/dist/auth/all_action_spec_registries.js +2 -2
  31. package/dist/auth/audit_log_routes.d.ts +1 -1
  32. package/dist/auth/audit_log_routes.js +1 -1
  33. package/dist/auth/audit_log_schema.d.ts +25 -0
  34. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  35. package/dist/auth/audit_log_schema.js +16 -0
  36. package/dist/auth/request_context.d.ts +1 -1
  37. package/dist/env/update_env_variable.js +1 -1
  38. package/dist/http/CLAUDE.md +15 -15
  39. package/dist/testing/CLAUDE.md +28 -44
  40. package/dist/testing/audit_completeness.d.ts.map +1 -1
  41. package/dist/testing/audit_completeness.js +17 -1
  42. package/dist/ui/CLAUDE.md +13 -18
  43. package/dist/ui/keyed_async_slot.svelte.d.ts +1 -1
  44. package/dist/ui/keyed_async_slot.svelte.js +1 -1
  45. package/package.json +1 -1
@@ -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
- (supersedes the old `AsyncSlot` + `SvelteSet<id>` pair) so concurrent rows
9
- don't abort each other and failures surface per-row via `slot.error(key)`.
10
- Payload lives as `$state.raw` fields on the class. Shared dependencies flow
11
- through Svelte context, never through props RPC adapters in particular
12
- are provisioned once at the admin shell and read by every `Admin*.svelte`.
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
- See ../../docs/usage.md for end-to-end wiring examples (sections "Role grant
15
- offer UI" and "Admin UI"). This file is a reference, not a tutorial.
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 `../auth/role_grant_offer_notifications.ts`
71
- (see `../auth/CLAUDE.md` §WS notifications).
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 `../auth/role_grant_offer_action_specs.js` (see
188
- `../auth/CLAUDE.md` for `role_grant_offer_action_specs.ts` +
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. `` `${account_id}:${role}` ``).
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. `` `${account_id}:${role}` ``).
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzdev/fuz_app",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
4
4
  "description": "fullstack app library",
5
5
  "glyph": "🗝",
6
6
  "logo": "logo.svg",