@fuzdev/fuz_app 0.58.0 → 0.60.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 +13 -8
- package/dist/actions/action_codegen.d.ts +1 -1
- package/dist/actions/action_codegen.js +2 -2
- package/dist/actions/action_event_helpers.d.ts +3 -3
- package/dist/actions/action_event_helpers.js +8 -8
- package/dist/actions/action_event_types.d.ts +3 -3
- package/dist/actions/action_event_types.js +3 -3
- package/dist/actions/transports_ws_auth_guard.d.ts +2 -2
- package/dist/actions/transports_ws_auth_guard.js +3 -3
- package/dist/auth/CLAUDE.md +215 -45
- package/dist/auth/account_action_specs.d.ts +9 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +9 -0
- package/dist/auth/actor_lookup_action_specs.d.ts +127 -0
- package/dist/auth/actor_lookup_action_specs.d.ts.map +1 -0
- package/dist/auth/actor_lookup_action_specs.js +93 -0
- package/dist/auth/actor_lookup_actions.d.ts +19 -0
- package/dist/auth/actor_lookup_actions.d.ts.map +1 -0
- package/dist/auth/actor_lookup_actions.js +32 -0
- package/dist/auth/actor_lookup_queries.d.ts +44 -0
- package/dist/auth/actor_lookup_queries.d.ts.map +1 -0
- package/dist/auth/actor_lookup_queries.js +42 -0
- package/dist/auth/actor_search_action_specs.d.ts +166 -0
- package/dist/auth/actor_search_action_specs.d.ts.map +1 -0
- package/dist/auth/actor_search_action_specs.js +139 -0
- package/dist/auth/actor_search_actions.d.ts +31 -0
- package/dist/auth/actor_search_actions.d.ts.map +1 -0
- package/dist/auth/actor_search_actions.js +61 -0
- package/dist/auth/actor_search_queries.d.ts +75 -0
- package/dist/auth/actor_search_queries.d.ts.map +1 -0
- package/dist/auth/actor_search_queries.js +91 -0
- package/dist/auth/admin_action_specs.d.ts +35 -0
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +35 -0
- package/dist/auth/admin_actions.js +2 -2
- package/dist/auth/all_action_spec_registries.d.ts +55 -0
- package/dist/auth/all_action_spec_registries.d.ts.map +1 -0
- package/dist/auth/all_action_spec_registries.js +59 -0
- package/dist/auth/audit_emitter.d.ts +1 -1
- package/dist/auth/audit_emitter.js +2 -2
- package/dist/auth/audit_log_queries.d.ts +1 -1
- package/dist/auth/audit_log_queries.js +3 -3
- 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 +5 -5
- package/dist/auth/audit_log_schema.js +7 -7
- package/dist/auth/auth_ddl.d.ts +7 -0
- package/dist/auth/auth_ddl.d.ts.map +1 -1
- package/dist/auth/auth_ddl.js +8 -0
- package/dist/auth/credential_type_schema.d.ts +1 -1
- package/dist/auth/credential_type_schema.js +3 -3
- package/dist/auth/grant_path_schema.d.ts +1 -1
- package/dist/auth/grant_path_schema.js +3 -3
- package/dist/auth/migrations.d.ts +4 -4
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +7 -6
- package/dist/auth/role_grant_offer_action_specs.d.ts +17 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +17 -0
- package/dist/auth/role_grant_offer_actions.js +2 -2
- package/dist/auth/role_grant_offer_notifications.d.ts +2 -2
- package/dist/auth/role_grant_offer_notifications.js +2 -2
- package/dist/auth/role_grant_queries.d.ts +21 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +31 -0
- package/dist/auth/role_schema.d.ts +2 -2
- package/dist/auth/role_schema.js +3 -3
- package/dist/auth/self_service_role_action_specs.d.ts +8 -0
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +8 -0
- package/dist/auth/self_service_role_actions.d.ts +1 -1
- package/dist/auth/self_service_role_actions.js +2 -2
- package/dist/auth/session_cookie.d.ts +1 -1
- package/dist/auth/session_cookie.js +1 -1
- package/dist/auth/session_middleware.d.ts +1 -1
- package/dist/auth/session_middleware.js +5 -5
- package/dist/rate_limiter.d.ts +5 -5
- package/dist/rate_limiter.js +6 -6
- package/dist/realtime/sse_auth_guard.d.ts +3 -3
- package/dist/realtime/sse_auth_guard.js +4 -4
- package/dist/server/app_backend.d.ts +3 -3
- package/dist/server/app_backend.js +4 -4
- package/dist/server/app_server.d.ts +1 -1
- package/dist/server/app_server.js +10 -10
- package/dist/testing/CLAUDE.md +22 -12
- package/dist/testing/admin_integration.js +4 -4
- package/dist/testing/app_server.d.ts +1 -1
- package/dist/testing/app_server.js +2 -2
- package/dist/testing/attack_surface.d.ts +4 -4
- package/dist/testing/attack_surface.js +6 -6
- package/dist/testing/audit_completeness.js +4 -4
- package/dist/testing/data_exposure.d.ts +2 -2
- package/dist/testing/data_exposure.js +7 -7
- package/dist/testing/db.d.ts +8 -8
- package/dist/testing/db.js +11 -11
- package/dist/testing/integration.js +4 -4
- package/dist/testing/integration_helpers.d.ts +6 -6
- package/dist/testing/integration_helpers.js +7 -7
- package/dist/testing/rate_limiting.js +4 -4
- package/dist/testing/round_trip.js +2 -2
- package/dist/testing/rpc_round_trip.js +2 -2
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +23 -2
- package/dist/testing/sse_round_trip.js +2 -2
- package/dist/testing/surface_invariants.d.ts +4 -4
- package/dist/testing/surface_invariants.js +5 -5
- package/package.json +1 -1
package/dist/actions/CLAUDE.md
CHANGED
|
@@ -71,9 +71,14 @@ resolved) and is rejected at registration when paired with
|
|
|
71
71
|
`auth.account !== 'required'` (no account to key on); `'both'` runs
|
|
72
72
|
both checks. **Throttle-requests semantics** — every invocation records,
|
|
73
73
|
regardless of outcome (different from REST login's throttle-failures
|
|
74
|
-
that resets on success). The motivating threat is admin
|
|
75
|
-
(`invite_create` account-existence probe) where the
|
|
76
|
-
invocation is the threat
|
|
74
|
+
that resets on success). The originally motivating threat is admin
|
|
75
|
+
mutation oracles (`invite_create` account-existence probe) where the
|
|
76
|
+
_successful_ invocation is the threat; the same shape extends to
|
|
77
|
+
authed-spam oracles (`role_grant_offer_create` iterating
|
|
78
|
+
`to_account_id` to probe `ERROR_ACCOUNT_NOT_FOUND`) and to paginated
|
|
79
|
+
cross-account reads (`admin_account_list`, `audit_log_list`,
|
|
80
|
+
`audit_log_role_grant_history`) where every successful page is an
|
|
81
|
+
enumeration step. Limiters are configured at server-assembly
|
|
77
82
|
time via `AppServerOptions.action_ip_rate_limiter` /
|
|
78
83
|
`action_account_rate_limiter` and threaded into both dispatchers
|
|
79
84
|
automatically; consumers wiring `register_action_ws` directly forward
|
|
@@ -459,7 +464,7 @@ persistence + rehydration by the consumer.
|
|
|
459
464
|
wireable via `CreateAppBackendOptions.on_audit_event`. Mirrors the SSE
|
|
460
465
|
guard in `realtime/sse_auth_guard.ts` but targets the WS transport.
|
|
461
466
|
|
|
462
|
-
`
|
|
467
|
+
`ws_disconnect_event_types` (ReadonlySet): `session_revoke`,
|
|
463
468
|
`token_revoke`, `session_revoke_all`, `token_revoke_all`, `password_change`.
|
|
464
469
|
`role_grant_revoke` is intentionally **omitted** — the WS transport does not
|
|
465
470
|
track per-connection role requirements, so role-scoped disconnection would
|
|
@@ -479,7 +484,7 @@ caller close another user's socket by guessing a session hash or token id.
|
|
|
479
484
|
|
|
480
485
|
`create_ws_logout_closer(transport, log)` is the sibling helper for
|
|
481
486
|
user-initiated `logout` events — kept separate because
|
|
482
|
-
`
|
|
487
|
+
`ws_disconnect_event_types` deliberately omits `logout` (admin-initiated
|
|
483
488
|
revocations use `session_revoke`, while `logout` is the user-initiated
|
|
484
489
|
case). Compose the two on `on_audit_event`:
|
|
485
490
|
|
|
@@ -595,9 +600,9 @@ an action through its lifecycle.
|
|
|
595
600
|
|
|
596
601
|
- `ActionExecutor` — `'frontend' | 'backend'`
|
|
597
602
|
- `ActionEventStep` — `'initial' | 'parsed' | 'handling' | 'handled' | 'failed'`
|
|
598
|
-
- `
|
|
599
|
-
- `
|
|
600
|
-
- `
|
|
603
|
+
- `action_event_step_transitions` — valid next-steps: `initial → parsed | failed`, `parsed → handling | failed`, `handling → handled | failed`, `handled`/`failed` terminal.
|
|
604
|
+
- `action_event_phase_by_kind` — valid phases per kind (`request_response` has 6, `remote_notification` has 2, `local_call` has 1).
|
|
605
|
+
- `action_event_phase_transitions` — chained phases: `send_request → receive_response`; `receive_request → send_response`; everything else terminal.
|
|
601
606
|
- `ActionEventEnvironment` — `{executor, lookup_action_handler, lookup_action_spec, log?}`. The ambient registry + handler resolver for an `ActionEvent`.
|
|
602
607
|
|
|
603
608
|
### `action_event_data.ts`
|
|
@@ -182,7 +182,7 @@ export declare const generate_actions_api_method_signature: (spec: ActionSpecUni
|
|
|
182
182
|
/** Discriminator for `generate_action_method_enums` — which method-set enums to emit. */
|
|
183
183
|
export type ActionMethodEnumKind = 'all' | 'request_response' | 'remote_notification' | 'local_call' | 'frontend' | 'backend' | 'frontend_handled' | 'backend_handled' | 'broadcast';
|
|
184
184
|
/** Default emit set — every enum kind. */
|
|
185
|
-
export declare const
|
|
185
|
+
export declare const action_method_enum_kinds_all: ReadonlySet<ActionMethodEnumKind>;
|
|
186
186
|
/**
|
|
187
187
|
* Resolve a per-spec identifier qualifier with the standard default-vs-callback
|
|
188
188
|
* dance. When `qualify_spec` is set, returns the caller's callback verbatim
|
|
@@ -416,7 +416,7 @@ ${lines}
|
|
|
416
416
|
export type ${name} = z.infer<typeof ${name}>;`;
|
|
417
417
|
};
|
|
418
418
|
/** Default emit set — every enum kind. */
|
|
419
|
-
export const
|
|
419
|
+
export const action_method_enum_kinds_all = new Set([
|
|
420
420
|
'all',
|
|
421
421
|
'request_response',
|
|
422
422
|
'remote_notification',
|
|
@@ -483,7 +483,7 @@ export const resolve_spec_qualifier = (imports, options) => {
|
|
|
483
483
|
* `cancel` in the emitted enums. Default `false`.
|
|
484
484
|
*/
|
|
485
485
|
export const generate_action_method_enums = (specs, imports, options) => {
|
|
486
|
-
const emit = options?.emit ??
|
|
486
|
+
const emit = options?.emit ?? action_method_enum_kinds_all;
|
|
487
487
|
const filtered = filter_protocol_actions(specs, options?.include_protocol_actions);
|
|
488
488
|
const registry = new ActionRegistry([...filtered]);
|
|
489
489
|
const blocks = [];
|
|
@@ -59,20 +59,20 @@ export declare const is_notification_send_with_parsed_input: <TMethod extends st
|
|
|
59
59
|
input: unknown;
|
|
60
60
|
};
|
|
61
61
|
/**
|
|
62
|
-
* Validate that a step transition is legal per `
|
|
62
|
+
* Validate that a step transition is legal per `action_event_step_transitions`.
|
|
63
63
|
*
|
|
64
64
|
* @throws Error if `from → to` is not a permitted transition
|
|
65
65
|
*/
|
|
66
66
|
export declare const validate_step_transition: (from: ActionEventStep, to: ActionEventStep) => void;
|
|
67
67
|
/**
|
|
68
68
|
* Validate that `phase` is one of the phases allowed for `kind` per
|
|
69
|
-
* `
|
|
69
|
+
* `action_event_phase_by_kind`.
|
|
70
70
|
*
|
|
71
71
|
* @throws Error if `phase` is not valid for `kind`
|
|
72
72
|
*/
|
|
73
73
|
export declare const validate_phase_for_kind: (kind: ActionKind, phase: ActionEventPhase) => void;
|
|
74
74
|
/**
|
|
75
|
-
* Validate that a phase chain is legal per `
|
|
75
|
+
* Validate that a phase chain is legal per `action_event_phase_transitions`.
|
|
76
76
|
*
|
|
77
77
|
* @throws Error if `from → to` is not the permitted next phase (or `from` is terminal)
|
|
78
78
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { action_event_step_transitions, action_event_phase_by_kind, action_event_phase_transitions, } from './action_event_types.js';
|
|
7
7
|
// Type guards for action kinds
|
|
8
8
|
export const is_request_response = (data) => data.kind === 'request_response';
|
|
9
9
|
export const is_remote_notification = (data) => data.kind === 'remote_notification';
|
|
@@ -28,33 +28,33 @@ export const is_failed = (data) => data.step === 'failed';
|
|
|
28
28
|
export const is_send_request_with_parsed_input = (data) => is_send_request(data) && (data.step === 'parsed' || data.step === 'handling');
|
|
29
29
|
export const is_notification_send_with_parsed_input = (data) => is_notification_send(data) && (data.step === 'parsed' || data.step === 'handling');
|
|
30
30
|
/**
|
|
31
|
-
* Validate that a step transition is legal per `
|
|
31
|
+
* Validate that a step transition is legal per `action_event_step_transitions`.
|
|
32
32
|
*
|
|
33
33
|
* @throws Error if `from → to` is not a permitted transition
|
|
34
34
|
*/
|
|
35
35
|
export const validate_step_transition = (from, to) => {
|
|
36
|
-
if (!
|
|
36
|
+
if (!action_event_step_transitions[from].includes(to)) {
|
|
37
37
|
throw new Error(`Invalid step transition from '${from}' to '${to}'`);
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
40
|
/**
|
|
41
41
|
* Validate that `phase` is one of the phases allowed for `kind` per
|
|
42
|
-
* `
|
|
42
|
+
* `action_event_phase_by_kind`.
|
|
43
43
|
*
|
|
44
44
|
* @throws Error if `phase` is not valid for `kind`
|
|
45
45
|
*/
|
|
46
46
|
export const validate_phase_for_kind = (kind, phase) => {
|
|
47
|
-
if (!
|
|
47
|
+
if (!action_event_phase_by_kind[kind].includes(phase)) {
|
|
48
48
|
throw new Error(`Invalid phase '${phase}' for ${kind} action`);
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
/**
|
|
52
|
-
* Validate that a phase chain is legal per `
|
|
52
|
+
* Validate that a phase chain is legal per `action_event_phase_transitions`.
|
|
53
53
|
*
|
|
54
54
|
* @throws Error if `from → to` is not the permitted next phase (or `from` is terminal)
|
|
55
55
|
*/
|
|
56
56
|
export const validate_phase_transition = (from, to) => {
|
|
57
|
-
const expected =
|
|
57
|
+
const expected = action_event_phase_transitions[from];
|
|
58
58
|
if (expected !== to) {
|
|
59
59
|
throw new Error(`Invalid phase transition from '${from}' to '${to}'`);
|
|
60
60
|
}
|
|
@@ -79,7 +79,7 @@ export const is_action_complete = (data) => {
|
|
|
79
79
|
if (data.step !== 'handled')
|
|
80
80
|
return false;
|
|
81
81
|
// Check if in terminal phase
|
|
82
|
-
const next_phase =
|
|
82
|
+
const next_phase = action_event_phase_transitions[data.phase];
|
|
83
83
|
return next_phase === null;
|
|
84
84
|
};
|
|
85
85
|
export const create_initial_data = (kind, phase, method, executor, input) => ({
|
|
@@ -19,9 +19,9 @@ export declare const ActionEventStep: z.ZodEnum<{
|
|
|
19
19
|
failed: "failed";
|
|
20
20
|
}>;
|
|
21
21
|
export type ActionEventStep = z.infer<typeof ActionEventStep>;
|
|
22
|
-
export declare const
|
|
23
|
-
export declare const
|
|
24
|
-
export declare const
|
|
22
|
+
export declare const action_event_step_transitions: Record<ActionEventStep, ReadonlyArray<ActionEventStep>>;
|
|
23
|
+
export declare const action_event_phase_by_kind: Record<ActionKind, ReadonlyArray<ActionEventPhase>>;
|
|
24
|
+
export declare const action_event_phase_transitions: Record<ActionEventPhase, ActionEventPhase | null>;
|
|
25
25
|
export interface ActionEventEnvironment {
|
|
26
26
|
readonly executor: ActionExecutor;
|
|
27
27
|
lookup_action_handler: (method: string, phase: ActionEventPhase) => ((event: any) => any) | undefined;
|
|
@@ -12,14 +12,14 @@ export const ActionEventStep = z.enum(['initial', 'parsed', 'handling', 'handled
|
|
|
12
12
|
// value types on the object literal) without narrowing lookups to literal
|
|
13
13
|
// tuple types — a `satisfies` shape forces every `X[k]` reader to widen
|
|
14
14
|
// back to `ReadonlyArray<V>` themselves to call `.includes(...)`.
|
|
15
|
-
export const
|
|
15
|
+
export const action_event_step_transitions = {
|
|
16
16
|
initial: ['parsed', 'failed'],
|
|
17
17
|
parsed: ['handling', 'failed'],
|
|
18
18
|
handling: ['handled', 'failed'],
|
|
19
19
|
handled: [],
|
|
20
20
|
failed: [],
|
|
21
21
|
};
|
|
22
|
-
export const
|
|
22
|
+
export const action_event_phase_by_kind = {
|
|
23
23
|
request_response: [
|
|
24
24
|
'send_request',
|
|
25
25
|
'receive_request',
|
|
@@ -31,7 +31,7 @@ export const ACTION_EVENT_PHASE_BY_KIND = {
|
|
|
31
31
|
remote_notification: ['send', 'receive'],
|
|
32
32
|
local_call: ['execute'],
|
|
33
33
|
};
|
|
34
|
-
export const
|
|
34
|
+
export const action_event_phase_transitions = {
|
|
35
35
|
send_request: 'receive_response',
|
|
36
36
|
receive_request: 'send_response',
|
|
37
37
|
send_response: null,
|
|
@@ -36,7 +36,7 @@ export type AuditEventHandler = (event: AuditLogEvent) => void;
|
|
|
36
36
|
* require either closing all sockets (too aggressive) or new tracking
|
|
37
37
|
* (out of scope). Consumers that need it compose their own callback.
|
|
38
38
|
*/
|
|
39
|
-
export declare const
|
|
39
|
+
export declare const ws_disconnect_event_types: ReadonlySet<string>;
|
|
40
40
|
/**
|
|
41
41
|
* Create an audit event handler that closes WebSocket connections on auth changes.
|
|
42
42
|
*
|
|
@@ -56,7 +56,7 @@ export declare const create_ws_auth_guard: (transport: BackendWebsocketTransport
|
|
|
56
56
|
* user-initiated logout.
|
|
57
57
|
*
|
|
58
58
|
* Sibling helper to `create_ws_auth_guard` — kept separate because
|
|
59
|
-
* `
|
|
59
|
+
* `ws_disconnect_event_types` deliberately omits `logout` (admin-initiated
|
|
60
60
|
* revocations use `session_revoke`, while `logout` is the user-initiated
|
|
61
61
|
* case). Three consumers (tx, undying, zzz) hand-rolled this same branch
|
|
62
62
|
* before extraction.
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* require either closing all sockets (too aggressive) or new tracking
|
|
24
24
|
* (out of scope). Consumers that need it compose their own callback.
|
|
25
25
|
*/
|
|
26
|
-
export const
|
|
26
|
+
export const ws_disconnect_event_types = new Set([
|
|
27
27
|
'session_revoke',
|
|
28
28
|
'token_revoke',
|
|
29
29
|
'session_revoke_all',
|
|
@@ -45,7 +45,7 @@ export const WS_DISCONNECT_EVENT_TYPES = new Set([
|
|
|
45
45
|
*/
|
|
46
46
|
export const create_ws_auth_guard = (transport, log) => {
|
|
47
47
|
return (event) => {
|
|
48
|
-
if (!
|
|
48
|
+
if (!ws_disconnect_event_types.has(event.event_type))
|
|
49
49
|
return;
|
|
50
50
|
// Failed mutations carry attacker-controlled metadata — never act on them.
|
|
51
51
|
if (event.outcome === 'failure')
|
|
@@ -90,7 +90,7 @@ export const create_ws_auth_guard = (transport, log) => {
|
|
|
90
90
|
* user-initiated logout.
|
|
91
91
|
*
|
|
92
92
|
* Sibling helper to `create_ws_auth_guard` — kept separate because
|
|
93
|
-
* `
|
|
93
|
+
* `ws_disconnect_event_types` deliberately omits `logout` (admin-initiated
|
|
94
94
|
* revocations use `session_revoke`, while `logout` is the user-initiated
|
|
95
95
|
* case). Three consumers (tx, undying, zzz) hand-rolled this same branch
|
|
96
96
|
* before extraction.
|