@fuzdev/fuz_app 0.112.0 → 0.114.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 +46 -20
- package/dist/actions/action_dispatcher.d.ts +1 -1
- package/dist/actions/action_dispatcher.d.ts.map +1 -1
- package/dist/actions/action_dispatcher.js +22 -16
- package/dist/actions/action_event.d.ts +18 -2
- package/dist/actions/action_event.d.ts.map +1 -1
- package/dist/actions/action_event.js +63 -19
- package/dist/actions/action_event_types.d.ts +22 -0
- package/dist/actions/action_event_types.d.ts.map +1 -1
- package/dist/actions/action_event_types.js +19 -0
- package/dist/actions/compile_action_registry.d.ts +4 -2
- package/dist/actions/compile_action_registry.d.ts.map +1 -1
- package/dist/actions/compile_action_registry.js +8 -3
- package/dist/actions/frontend_rpc_client.d.ts +6 -0
- package/dist/actions/frontend_rpc_client.d.ts.map +1 -1
- package/dist/actions/frontend_rpc_client.js +2 -1
- package/dist/actions/heartbeat.d.ts +8 -4
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +8 -4
- package/dist/actions/peer_ping.d.ts.map +1 -1
- package/dist/actions/peer_ping.js +3 -1
- package/dist/actions/perform_action.d.ts +1 -1
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +6 -4
- package/dist/actions/register_action_ws.d.ts +13 -0
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +23 -2
- package/dist/actions/rpc_client.d.ts +1 -1
- package/dist/actions/rpc_client.d.ts.map +1 -1
- package/dist/actions/rpc_client.js +2 -1
- package/dist/actions/socket.svelte.d.ts +53 -10
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +84 -23
- package/dist/actions/transports.d.ts +24 -0
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +30 -0
- package/dist/actions/transports_http.d.ts +11 -4
- package/dist/actions/transports_http.d.ts.map +1 -1
- package/dist/actions/transports_http.js +63 -18
- package/dist/actions/transports_ws.d.ts +28 -3
- package/dist/actions/transports_ws.d.ts.map +1 -1
- package/dist/actions/transports_ws.js +33 -4
- package/dist/actions/ws_endpoint_spec.d.ts +7 -0
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +6 -1
- package/dist/auth/audit_log_route_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_route_schema.js +12 -1
- package/dist/auth/role_grant_offer_action_specs.d.ts +2 -1
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +2 -1
- package/dist/db/fact_disk_storage.js +1 -1
- package/dist/http/CLAUDE.md +10 -6
- package/dist/http/auth_shape.d.ts +19 -6
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/auth_shape.js +19 -6
- package/dist/http/jsonrpc_helpers.d.ts +0 -5
- package/dist/http/jsonrpc_helpers.d.ts.map +1 -1
- package/dist/http/jsonrpc_helpers.js +0 -13
- package/dist/http/schema_helpers.d.ts +56 -0
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +91 -0
- package/dist/http/surface_query.d.ts +29 -4
- package/dist/http/surface_query.d.ts.map +1 -1
- package/dist/http/surface_query.js +30 -5
- package/dist/realtime/sse_auth_guard.d.ts +9 -3
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +9 -3
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +1 -0
- package/dist/testing/CLAUDE.md +18 -13
- package/dist/testing/attack_surface.d.ts +5 -2
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +45 -17
- package/dist/testing/auth_apps.d.ts +25 -0
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +34 -5
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.js +18 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +3 -2
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_scope_surface.js +36 -7
- package/dist/testing/cross_backend/ws_round_trip.d.ts +11 -3
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.js +74 -12
- package/dist/testing/surface_invariants.d.ts +7 -1
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +11 -5
- package/dist/testing/transports/ws_client.d.ts +6 -0
- package/dist/testing/transports/ws_client.d.ts.map +1 -1
- package/dist/testing/transports/ws_transport.d.ts.map +1 -1
- package/dist/testing/transports/ws_transport.js +6 -1
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +5 -0
- package/dist/ui/AdminAccounts.svelte +6 -4
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminInvites.svelte +3 -3
- package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
- package/dist/ui/AdminOverview.svelte +10 -10
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/AdminRoleGrantHistory.svelte +7 -7
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSettings.svelte +1 -1
- package/dist/ui/RoleGrantOfferInbox.svelte +4 -1
- package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -1
- package/dist/ui/SurfaceExplorer.svelte +33 -17
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/form_state.svelte.d.ts +9 -4
- package/dist/ui/form_state.svelte.d.ts.map +1 -1
- package/dist/ui/form_state.svelte.js +28 -8
- package/package.json +10 -10
- package/dist/actions/request_tracker.svelte.d.ts +0 -77
- package/dist/actions/request_tracker.svelte.d.ts.map +0 -1
- package/dist/actions/request_tracker.svelte.js +0 -169
|
@@ -32,11 +32,22 @@ export const create_audit_log_route_shape = (required_role = DEFAULT_AUDIT_STREA
|
|
|
32
32
|
account: 'required',
|
|
33
33
|
actor: 'required',
|
|
34
34
|
roles: [required_role],
|
|
35
|
+
// The stream is a session channel. A bearer that opens one cannot be
|
|
36
|
+
// closed when it is revoked: `token_revoke` closes WS sockets keyed by
|
|
37
|
+
// token, and SSE subscribers are not keyed that way, so a revoked
|
|
38
|
+
// token would keep receiving audit rows for the life of the
|
|
39
|
+
// connection. Gating the channel is what makes
|
|
40
|
+
// `realtime/sse_auth_guard.ts`'s omission of `token_revoke` correct
|
|
41
|
+
// rather than merely asserted.
|
|
42
|
+
credential_types: ['session'],
|
|
35
43
|
// Rule 3 — a narrowed token holds no audit feed. Same reasoning as the
|
|
36
44
|
// WS upgrade: this is a long-lived server→client stream whose contents
|
|
37
45
|
// are decided by the account's role, not by any per-message scope
|
|
38
46
|
// check, so there is no point after open at which a narrowing could
|
|
39
|
-
// apply.
|
|
47
|
+
// apply. Unreachable behind the credential gate above on the default
|
|
48
|
+
// mount, and kept deliberately: it is the rule-3 declaration the
|
|
49
|
+
// surface census matches on, and it still governs a consumer that
|
|
50
|
+
// widens the channel gate.
|
|
40
51
|
required_scope: 'surface:audit_stream'
|
|
41
52
|
},
|
|
42
53
|
description: 'Subscribe to realtime audit log events',
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* required (no roles) and rely on `query_*` IDOR guards or in-handler
|
|
12
12
|
* policy checks (e.g. `role_grant_offer_list`/`_history` elevate to admin only
|
|
13
13
|
* when inspecting another account — an input-dependent check that can't be
|
|
14
|
-
* expressed at the spec level). `role_grant_revoke`
|
|
14
|
+
* expressed at the spec level). `role_grant_revoke` and `role_grant_assign` add
|
|
15
|
+
* `roles: ['admin']` —
|
|
15
16
|
* the RPC dispatcher's per-spec post-authorization auth gate
|
|
16
17
|
* (`check_action_auth_post_authorization`) rejects non-admin callers before
|
|
17
18
|
* the handler runs even though the endpoint hosts non-admin methods
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_action_specs.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"role_grant_offer_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_action_specs.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* required (no roles) and rely on `query_*` IDOR guards or in-handler
|
|
12
12
|
* policy checks (e.g. `role_grant_offer_list`/`_history` elevate to admin only
|
|
13
13
|
* when inspecting another account — an input-dependent check that can't be
|
|
14
|
-
* expressed at the spec level). `role_grant_revoke`
|
|
14
|
+
* expressed at the spec level). `role_grant_revoke` and `role_grant_assign` add
|
|
15
|
+
* `roles: ['admin']` —
|
|
15
16
|
* the RPC dispatcher's per-spec post-authorization auth gate
|
|
16
17
|
* (`check_action_auth_post_authorization`) rejects non-admin callers before
|
|
17
18
|
* the handler runs even though the endpoint hosts non-admin methods
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { createHash } from 'node:crypto';
|
|
31
31
|
import { join } from 'node:path';
|
|
32
|
-
import { Blake3Hasher } from '@fuzdev/
|
|
32
|
+
import { Blake3Hasher } from '@fuzdev/blake3-wasm';
|
|
33
33
|
import { to_error_message } from '@fuzdev/fuz_util/error.ts';
|
|
34
34
|
import { blake3_ready } from '@fuzdev/fuz_util/hash_blake3.ts';
|
|
35
35
|
import { to_hex } from '@fuzdev/fuz_util/hex.ts';
|
package/dist/http/CLAUDE.md
CHANGED
|
@@ -17,7 +17,7 @@ effects, see ../../../docs/architecture.md.
|
|
|
17
17
|
- `http/route_spec.ts` — `RouteSpec` + `apply_route_specs`, validation pipeline, transactions.
|
|
18
18
|
- `http/auth_shape.ts` — canonical `RouteAuth` Zod schema + cross-axis invariants + predicates.
|
|
19
19
|
- `http/error_schemas.ts` — `ERROR_*` constants, standard error shapes, `derive_error_schemas`.
|
|
20
|
-
- `http/schema_helpers.ts` — shared Zod introspection (null/strict/surface/merge/middleware-applies)
|
|
20
|
+
- `http/schema_helpers.ts` — shared Zod introspection (null/void/strict/surface/merge/middleware-applies) + `to_void_params` / `to_input_params` / `safe_parse_dropping_unknown_keys`.
|
|
21
21
|
- `http/middleware_spec.ts` — `MiddlewareSpec` interface.
|
|
22
22
|
- `http/surface.ts` — `AppSurface`, `AppSurfaceSpec`, `generate_app_surface`, diagnostics.
|
|
23
23
|
- `http/surface_query.ts` — pure filters/groupings over `AppSurface`.
|
|
@@ -220,7 +220,7 @@ confirmation.
|
|
|
220
220
|
— extracted to break a circular dependency between `http/route_spec.ts`
|
|
221
221
|
(input validation) and `http/surface.ts` (surface generation).
|
|
222
222
|
|
|
223
|
-
**Import `is_null_schema`, `is_strict_object_schema`, `schema_to_surface`,
|
|
223
|
+
**Import `is_null_schema`, `is_void_schema`, `to_void_params`, `to_input_params`, `is_strict_object_schema`, `schema_to_surface`,
|
|
224
224
|
`middleware_applies`, and `merge_error_schemas` from `http/schema_helpers.ts`,
|
|
225
225
|
not from `http/surface.ts`.** The helpers were moved; `http/surface.ts`
|
|
226
226
|
only imports and re-uses them for generation logic.
|
|
@@ -228,6 +228,10 @@ only imports and re-uses them for generation logic.
|
|
|
228
228
|
Key helpers:
|
|
229
229
|
|
|
230
230
|
- `is_null_schema(schema)` — `instanceof z.ZodNull` (not parse-null, to avoid false positives from `z.nullable(z.string())`)
|
|
231
|
+
- `is_void_schema(schema)` — `instanceof z.ZodVoid`; a `z.void()` input declares a parameterless RPC method
|
|
232
|
+
- `to_void_params(raw_params)` — reads absent, `null`, and `{}` params of a `z.void()` input as the no-arg call (`undefined`), passing any other shape through for `z.void()` to refuse — the twin of the Rust spine's `require_void_params`
|
|
233
|
+
- `to_input_params(input, raw_params)` — the one params normalizer every receiving end runs before `input.safeParse`: `to_void_params` for a `z.void()` input, else absent/`null` → `{}`; used by both the server dispatch core (`perform_action`) and the peer receive path (`ActionDispatcher`)
|
|
234
|
+
- `safe_parse_dropping_unknown_keys(schema, value)` — the forward-compatible read of a remote-authored payload: when every issue is an unrecognized key, strips those keys from a copy and reparses; any other issue still fails. Used for response `result`s (`ActionEvent`) and peer replies (`peer/ping`); matches serde's ignore-unknown-fields default on the Rust spine
|
|
231
235
|
- `is_strict_object_schema(schema)` — detects `z.strictObject()` by checking `schema.def.catchall instanceof z.ZodNever`
|
|
232
236
|
- `schema_to_surface(schema)` — Zod → JSON Schema, with `$schema` and `default` keys stripped recursively (defaults may be non-deterministic; `$schema` is snapshot noise)
|
|
233
237
|
- `middleware_applies(mw_path, route_path)` — Hono pattern matching: `'*'`, exact, `'/api/*'` prefix (handles `prefix.slice(0, -1)` so `/api/*` also matches the bare `/api`)
|
|
@@ -264,11 +268,11 @@ the source specs, for tests that need to iterate over raw specs.
|
|
|
264
268
|
No side effects, no state — filters and groupings over `AppSurface`:
|
|
265
269
|
|
|
266
270
|
- `filter_protected_routes` / `filter_public_routes`
|
|
267
|
-
- `filter_role_routes` / `filter_authenticated_routes` / `filter_keeper_routes` / `filter_routes_for_role(role)`
|
|
271
|
+
- `filter_role_routes` / `filter_authenticated_routes` / `filter_keeper_routes` / `filter_credential_gated_routes` (any `auth.credential_types` gate — superset of keeper) / `filter_routes_for_role(role)`
|
|
268
272
|
- `filter_routes_by_prefix(prefix)` / `filter_routes_with_input` / `filter_routes_with_params` / `filter_routes_with_query` / `filter_mutation_routes` / `filter_rate_limited_routes`
|
|
269
|
-
- `routes_by_auth_type(surface)` — `Map<RouteAuthCategory, Array<AppSurfaceRoute>>` where `RouteAuthCategory = 'none' | 'authenticated' | 'optional' | 'keeper' | 'role:<name>' | 'other'`. Multi-role specs appear under each role bucket
|
|
273
|
+
- `routes_by_auth_type(surface)` — `Map<RouteAuthCategory, Array<AppSurfaceRoute>>` where `RouteAuthCategory = 'none' | 'authenticated' | 'optional' | 'keeper' | 'role:<name>' | 'credential:<type>' | 'other'`. Multi-role / multi-channel specs appear under each bucket. Keeper is tested before the role bucket and the general credential bucket after it — keeper is an authority _level_ and names the route, a session gate is a _channel_ restriction over the authority the route already declares (so the audit SSE stream buckets as `role:admin`)
|
|
270
274
|
- `format_route_key(route)` → `'METHOD /path'`
|
|
271
|
-
- `surface_auth_summary(surface)` — counts per auth type, roles broken out by name
|
|
275
|
+
- `surface_auth_summary(surface)` — counts per auth type, roles and credential channels broken out by name; buckets exactly as `routes_by_auth_type`
|
|
272
276
|
|
|
273
277
|
The per-route auth predicates these filters compose over (`is_public_auth`,
|
|
274
278
|
`is_role_auth`, `is_credential_gated_auth`, `is_keeper_auth`,
|
|
@@ -401,7 +405,7 @@ to a JSON-RPC code.
|
|
|
401
405
|
- Envelope schemas in `http/jsonrpc.ts`: `JsonrpcRequest`, `JsonrpcNotification`, `JsonrpcResponse`, `JsonrpcErrorResponse`, `JsonrpcResponseOrError`, `JsonrpcMessage`, `JsonrpcMessageFromClientToServer`, `JsonrpcMessageFromServerToClient`. Also `JsonrpcRequestId`, `JsonrpcMethod`, `JsonrpcProgressToken`, `JsonrpcMcpMeta`, `JsonrpcRequestParamsMeta`
|
|
402
406
|
- Builders in `http/jsonrpc_helpers.ts`: `create_jsonrpc_request`, `create_jsonrpc_notification`, `create_jsonrpc_response`, `create_jsonrpc_error_response`, `create_jsonrpc_error_response_from_thrown` (preserves code/message/data on `ThrownJsonrpcError`; plain `Error` → `internal_error` with `{stack}` in DEV)
|
|
403
407
|
- Type guards: `is_jsonrpc_request_id` (rejects NaN/Infinity), `is_jsonrpc_object`, `is_jsonrpc_message`, `is_jsonrpc_request` / `_notification` / `_response` / `_error_response`
|
|
404
|
-
- Converters: `to_jsonrpc_message_id`, `to_jsonrpc_params` (normalizes primitives to `{value}`)
|
|
408
|
+
- Converters: `to_jsonrpc_message_id`, `to_jsonrpc_params` (normalizes primitives to `{value}`)
|
|
405
409
|
|
|
406
410
|
Handlers can `throw jsonrpc_errors.*` — `apply_route_specs`' error-catch
|
|
407
411
|
layer converts to `{error: JsonrpcErrorObject}` at the correct HTTP status.
|
|
@@ -189,16 +189,29 @@ export declare const is_role_auth: (auth: RouteAuth) => boolean;
|
|
|
189
189
|
/** True iff the route declares any credential-type gate (`auth.credential_types?.length`). */
|
|
190
190
|
export declare const is_credential_gated_auth: (auth: RouteAuth) => boolean;
|
|
191
191
|
/**
|
|
192
|
-
* True iff the route is the keeper bucket — credential gate
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
192
|
+
* True iff the route is the keeper bucket — a credential gate admitting
|
|
193
|
+
* `daemon_token`. Knows the literal directly, because keeper is a named
|
|
194
|
+
* authority level rather than one channel among many: it is the ceiling
|
|
195
|
+
* sessions and api tokens cannot reach.
|
|
196
|
+
*
|
|
197
|
+
* Narrower than `is_credential_gated_auth`, which is true of every channel
|
|
198
|
+
* gate — including the session-only gates pointing the other way (the
|
|
199
|
+
* credential-minting actions, `POST /logout` / `POST /password`, the audit
|
|
200
|
+
* SSE stream). Reach for the general predicate when the question is "does a
|
|
201
|
+
* channel gate answer here"; reach for this one only when the question is
|
|
202
|
+
* about keeper specifically.
|
|
196
203
|
*/
|
|
197
204
|
export declare const is_keeper_auth: (auth: RouteAuth) => boolean;
|
|
198
205
|
/**
|
|
199
206
|
* True iff the route is plain authenticated — `account === 'required'`
|
|
200
|
-
* with no role gate and no credential gate
|
|
201
|
-
*
|
|
207
|
+
* with no role gate and no credential gate: any credential the auth
|
|
208
|
+
* middleware resolved gets in.
|
|
209
|
+
*
|
|
210
|
+
* A channel gate takes a route out of this bucket, which is why the
|
|
211
|
+
* account-grain routes one might expect here are not — `POST /logout`,
|
|
212
|
+
* `POST /password`, and the `account_*` credential-lifecycle actions all
|
|
213
|
+
* declare `credential_types: ['session']` and answer to
|
|
214
|
+
* `is_credential_gated_auth` instead.
|
|
202
215
|
*/
|
|
203
216
|
export declare const is_plain_authenticated_auth: (auth: RouteAuth) => boolean;
|
|
204
217
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/auth_shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,6DAGtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAGtD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;;;;EAA2C,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;IAMpB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;;kBAyCF,CAAC;AACJ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAUlD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACA,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,KAAG,OAAgC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAAkC,CAAC;AAEnF,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,KAAG,OAA+B,CAAC;AAE/E,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,OAC3B,CAAC;AAEjC
|
|
1
|
+
{"version":3,"file":"auth_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/auth_shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,6DAGtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAGtD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;;;;EAA2C,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;IAMpB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;;kBAyCF,CAAC;AACJ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAUlD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACA,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,KAAG,OAAgC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAAkC,CAAC;AAEnF,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,KAAG,OAA+B,CAAC;AAE/E,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,OAC3B,CAAC;AAEjC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACQ,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,SAAS,KAAG,OACwB,CAAC;AAYvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAIhE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,MAAM,SAAS,EACf,OAAO,WAAW,EAClB,SAAS,MAAM,KACb,IAmBF,CAAC"}
|
package/dist/http/auth_shape.js
CHANGED
|
@@ -224,16 +224,29 @@ export const is_role_auth = (auth) => !!auth.roles?.length;
|
|
|
224
224
|
/** True iff the route declares any credential-type gate (`auth.credential_types?.length`). */
|
|
225
225
|
export const is_credential_gated_auth = (auth) => !!auth.credential_types?.length;
|
|
226
226
|
/**
|
|
227
|
-
* True iff the route is the keeper bucket — credential gate
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
227
|
+
* True iff the route is the keeper bucket — a credential gate admitting
|
|
228
|
+
* `daemon_token`. Knows the literal directly, because keeper is a named
|
|
229
|
+
* authority level rather than one channel among many: it is the ceiling
|
|
230
|
+
* sessions and api tokens cannot reach.
|
|
231
|
+
*
|
|
232
|
+
* Narrower than `is_credential_gated_auth`, which is true of every channel
|
|
233
|
+
* gate — including the session-only gates pointing the other way (the
|
|
234
|
+
* credential-minting actions, `POST /logout` / `POST /password`, the audit
|
|
235
|
+
* SSE stream). Reach for the general predicate when the question is "does a
|
|
236
|
+
* channel gate answer here"; reach for this one only when the question is
|
|
237
|
+
* about keeper specifically.
|
|
231
238
|
*/
|
|
232
239
|
export const is_keeper_auth = (auth) => auth.credential_types?.includes('daemon_token') ?? false;
|
|
233
240
|
/**
|
|
234
241
|
* True iff the route is plain authenticated — `account === 'required'`
|
|
235
|
-
* with no role gate and no credential gate
|
|
236
|
-
*
|
|
242
|
+
* with no role gate and no credential gate: any credential the auth
|
|
243
|
+
* middleware resolved gets in.
|
|
244
|
+
*
|
|
245
|
+
* A channel gate takes a route out of this bucket, which is why the
|
|
246
|
+
* account-grain routes one might expect here are not — `POST /logout`,
|
|
247
|
+
* `POST /password`, and the `account_*` credential-lifecycle actions all
|
|
248
|
+
* declare `credential_types: ['session']` and answer to
|
|
249
|
+
* `is_credential_gated_auth` instead.
|
|
237
250
|
*/
|
|
238
251
|
export const is_plain_authenticated_auth = (auth) => auth.account === 'required' && !is_role_auth(auth) && !is_credential_gated_auth(auth);
|
|
239
252
|
// --- Registry-time invariant 2 enforcement ---
|
|
@@ -61,9 +61,4 @@ export declare const to_jsonrpc_envelope_id: (frame: unknown) => JsonrpcRequestI
|
|
|
61
61
|
* Returns `undefined` for null/undefined, wraps primitives in `{value}`.
|
|
62
62
|
*/
|
|
63
63
|
export declare const to_jsonrpc_params: (input: unknown) => Record<string, any> | undefined;
|
|
64
|
-
/**
|
|
65
|
-
* Normalizes output to JSON-RPC result format.
|
|
66
|
-
* Returns empty object for null/undefined, wraps primitives in `{value}`.
|
|
67
|
-
*/
|
|
68
|
-
export declare const to_jsonrpc_result: (output: unknown) => Record<string, any>;
|
|
69
64
|
//# sourceMappingURL=jsonrpc_helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonrpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,eAAe,EACf,MAAM,cAAc,CAAC;AAKtB,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,GAClC,QAAQ,aAAa,EACrB,QAAQ,oBAAoB,GAAG,SAAS,EACxC,IAAI,gBAAgB,KAClB,cAUF,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,uBAAuB,GACnC,IAAI,gBAAgB,EACpB,QAAQ,aAAa,KACnB,eAID,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,GACvC,QAAQ,aAAa,EACrB,QAAQ,yBAAyB,GAAG,SAAS,KAC3C,mBASF,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,6BAA6B,GACzC,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAC9B,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAClC,oBAID,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,IAAI,gBAAgB,GAAG,IAAI,EAC3B,OAAO,OAAO,KACZ,oBAyBF,CAAC;AAIF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,GAAI,IAAI,OAAO,KAAG,EAAE,IAAI,gBAGzD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,GAC7B,SAAS,OAAO,KACd,OAAO,IAAI;IAAE,OAAO,EAAE,OAAO,eAAe,CAAA;CAIF,CAAC;AAE9C,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,GAC9B,SAAS,OAAO,KACd,OAAO,IAAI,cAAc,GAAG,KAAK,CAAC,cAAc,CAGrB,CAAC;AAE/B,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,cACI,CAAC;AAEtE,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,mBACE,CAAC;AAEzE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,eACG,CAAC;AAEtE,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,oBACJ,CAAC;AAIrE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,eAAe,OAAO,KAAG,gBAAgB,GAAG,IAOjF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,gBAAgB,GAAG,IACX,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAQxE,CAAC
|
|
1
|
+
{"version":3,"file":"jsonrpc_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,eAAe,EACf,MAAM,cAAc,CAAC;AAKtB,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,GAClC,QAAQ,aAAa,EACrB,QAAQ,oBAAoB,GAAG,SAAS,EACxC,IAAI,gBAAgB,KAClB,cAUF,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,uBAAuB,GACnC,IAAI,gBAAgB,EACpB,QAAQ,aAAa,KACnB,eAID,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,GACvC,QAAQ,aAAa,EACrB,QAAQ,yBAAyB,GAAG,SAAS,KAC3C,mBASF,CAAC;AAEF,iDAAiD;AACjD,eAAO,MAAM,6BAA6B,GACzC,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAC9B,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAClC,oBAID,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,IAAI,gBAAgB,GAAG,IAAI,EAC3B,OAAO,OAAO,KACZ,oBAyBF,CAAC;AAIF,kFAAkF;AAClF,eAAO,MAAM,qBAAqB,GAAI,IAAI,OAAO,KAAG,EAAE,IAAI,gBAGzD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,GAC7B,SAAS,OAAO,KACd,OAAO,IAAI;IAAE,OAAO,EAAE,OAAO,eAAe,CAAA;CAIF,CAAC;AAE9C,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,GAC9B,SAAS,OAAO,KACd,OAAO,IAAI,cAAc,GAAG,KAAK,CAAC,cAAc,CAGrB,CAAC;AAE/B,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,cACI,CAAC;AAEtE,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,mBACE,CAAC;AAEzE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,eACG,CAAC;AAEtE,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,GAAI,SAAS,OAAO,KAAG,OAAO,IAAI,oBACJ,CAAC;AAIrE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,eAAe,OAAO,KAAG,gBAAgB,GAAG,IAOjF,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,gBAAgB,GAAG,IACX,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAQxE,CAAC"}
|
|
@@ -136,16 +136,3 @@ export const to_jsonrpc_params = (input) => {
|
|
|
136
136
|
}
|
|
137
137
|
return { value: input };
|
|
138
138
|
};
|
|
139
|
-
/**
|
|
140
|
-
* Normalizes output to JSON-RPC result format.
|
|
141
|
-
* Returns empty object for null/undefined, wraps primitives in `{value}`.
|
|
142
|
-
*/
|
|
143
|
-
export const to_jsonrpc_result = (output) => {
|
|
144
|
-
if (output === null || output === undefined) {
|
|
145
|
-
return {};
|
|
146
|
-
}
|
|
147
|
-
if (typeof output === 'object' && !Array.isArray(output)) {
|
|
148
|
-
return output;
|
|
149
|
-
}
|
|
150
|
-
return { value: output };
|
|
151
|
-
};
|
|
@@ -27,6 +27,62 @@ export declare const is_null_schema: (schema: z.ZodType) => boolean;
|
|
|
27
27
|
* and the dispatcher maps absent params to `undefined` for these specs.
|
|
28
28
|
*/
|
|
29
29
|
export declare const is_void_schema: (schema: z.ZodType) => boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Normalize the raw `params` of a `z.void()` input: an absent `params`, an
|
|
32
|
+
* empty object, and the `null` a GET `?params=null` parses to are all the
|
|
33
|
+
* no-arg call (`undefined`); anything else passes through for `z.void()` to
|
|
34
|
+
* refuse.
|
|
35
|
+
*
|
|
36
|
+
* JSON-RPC 2.0 lets a parameterless call omit `params`, and an empty by-name
|
|
37
|
+
* structure carries no parameters, so refusing `{}` would only break clients
|
|
38
|
+
* that send it for a parameterless call. Arrays — `[]` included — are not
|
|
39
|
+
* normalized: RPC params are by-name only. The Rust spine's
|
|
40
|
+
* `require_void_params` accepts the same shapes. Receiving ends reach it
|
|
41
|
+
* through `to_input_params`.
|
|
42
|
+
*
|
|
43
|
+
* @param raw_params - the request's `params` as parsed off the wire
|
|
44
|
+
* @returns `undefined` for an empty shape, else `raw_params` unchanged
|
|
45
|
+
*/
|
|
46
|
+
export declare const to_void_params: (raw_params: unknown) => unknown;
|
|
47
|
+
/**
|
|
48
|
+
* Normalize the raw `params` of an RPC call for `input` to validate: for a
|
|
49
|
+
* `z.void()` input, `to_void_params`; for any other input, an absent or
|
|
50
|
+
* `null` `params` reads as `{}`, so an all-optional object input accepts a
|
|
51
|
+
* call that omits them. Every receiving end — the server dispatch
|
|
52
|
+
* (`perform_action`) and a peer receiving a request or notification
|
|
53
|
+
* (`ActionDispatcher`) — reads params through this, so a peer is never
|
|
54
|
+
* stricter than the server.
|
|
55
|
+
*
|
|
56
|
+
* @param input - the action's input schema
|
|
57
|
+
* @param raw_params - the call's `params` as parsed off the wire
|
|
58
|
+
* @returns the value to run `input.safeParse` on
|
|
59
|
+
*/
|
|
60
|
+
export declare const to_input_params: (input: z.ZodType, raw_params: unknown) => unknown;
|
|
61
|
+
/**
|
|
62
|
+
* `schema.safeParse(value)`, except that keys the schema doesn't declare are
|
|
63
|
+
* dropped rather than refused — the forward-compatible read of a payload a
|
|
64
|
+
* remote authored.
|
|
65
|
+
*
|
|
66
|
+
* Output schemas are strict so the server's DEV output validation catches a
|
|
67
|
+
* handler leaking fields it shouldn't. The receiving side, though, is often
|
|
68
|
+
* older than the remote it talks to — a deploy lands before open tabs reload
|
|
69
|
+
* — and a remote that adds a field is making a compatible change. So when
|
|
70
|
+
* every issue is an unrecognized key, those keys are stripped from a copy of
|
|
71
|
+
* `value` and it's parsed again; any other issue (a wrong type, a missing
|
|
72
|
+
* field, `null` where a value is required) still fails. `value` itself is
|
|
73
|
+
* never mutated. This matches serde's default on the Rust spine, which
|
|
74
|
+
* ignores unknown fields. Keys nested in a `z.union` branch surface as a
|
|
75
|
+
* union issue and aren't stripped; a `z.discriminatedUnion` reports its
|
|
76
|
+
* matched branch's issues directly, so those are.
|
|
77
|
+
*
|
|
78
|
+
* Used for a response's `result` (`ActionEvent`) and for a peer's reply to a
|
|
79
|
+
* server-initiated request (`peer/ping`).
|
|
80
|
+
*
|
|
81
|
+
* @param schema - the declared shape
|
|
82
|
+
* @param value - the remote-authored payload
|
|
83
|
+
* @returns the parse result, with undeclared keys dropped on success
|
|
84
|
+
*/
|
|
85
|
+
export declare const safe_parse_dropping_unknown_keys: <T extends z.ZodType>(schema: T, value: unknown) => z.ZodSafeParseResult<z.output<T>>;
|
|
30
86
|
/**
|
|
31
87
|
* Check if a schema is a strict object (`z.strictObject()`).
|
|
32
88
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/schema_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OACe,CAAC;AAE5E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAQrD,CAAC;AAoBF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,YAAY,MAAM,KAAG,OAQxE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM;IACL,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B,EACD,oBAAoB,iBAAiB,GAAG,IAAI,KAC1C,iBAAiB,GAAG,IAUtB,CAAC"}
|
|
1
|
+
{"version":3,"file":"schema_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/schema_helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAAsC,CAAC;AAE1F;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,GAAI,YAAY,OAAO,KAAG,OAUpD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,CAAC,CAAC,OAAO,EAAE,YAAY,OAAO,KAAG,OACA,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,gCAAgC,GAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EACnE,QAAQ,CAAC,EACT,OAAO,OAAO,KACZ,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAclC,CAAC;AAWF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OACe,CAAC;AAE5E;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAQrD,CAAC;AAoBF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,EAAE,YAAY,MAAM,KAAG,OAQxE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM;IACL,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B,EACD,oBAAoB,iBAAiB,GAAG,IAAI,KAC1C,iBAAiB,GAAG,IAUtB,CAAC"}
|
|
@@ -26,6 +26,97 @@ export const is_null_schema = (schema) => schema instanceof z.ZodNull;
|
|
|
26
26
|
* and the dispatcher maps absent params to `undefined` for these specs.
|
|
27
27
|
*/
|
|
28
28
|
export const is_void_schema = (schema) => schema instanceof z.ZodVoid;
|
|
29
|
+
/**
|
|
30
|
+
* Normalize the raw `params` of a `z.void()` input: an absent `params`, an
|
|
31
|
+
* empty object, and the `null` a GET `?params=null` parses to are all the
|
|
32
|
+
* no-arg call (`undefined`); anything else passes through for `z.void()` to
|
|
33
|
+
* refuse.
|
|
34
|
+
*
|
|
35
|
+
* JSON-RPC 2.0 lets a parameterless call omit `params`, and an empty by-name
|
|
36
|
+
* structure carries no parameters, so refusing `{}` would only break clients
|
|
37
|
+
* that send it for a parameterless call. Arrays — `[]` included — are not
|
|
38
|
+
* normalized: RPC params are by-name only. The Rust spine's
|
|
39
|
+
* `require_void_params` accepts the same shapes. Receiving ends reach it
|
|
40
|
+
* through `to_input_params`.
|
|
41
|
+
*
|
|
42
|
+
* @param raw_params - the request's `params` as parsed off the wire
|
|
43
|
+
* @returns `undefined` for an empty shape, else `raw_params` unchanged
|
|
44
|
+
*/
|
|
45
|
+
export const to_void_params = (raw_params) => {
|
|
46
|
+
if (raw_params === undefined || raw_params === null)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (typeof raw_params === 'object' &&
|
|
49
|
+
!Array.isArray(raw_params) &&
|
|
50
|
+
Object.keys(raw_params).length === 0) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return raw_params;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Normalize the raw `params` of an RPC call for `input` to validate: for a
|
|
57
|
+
* `z.void()` input, `to_void_params`; for any other input, an absent or
|
|
58
|
+
* `null` `params` reads as `{}`, so an all-optional object input accepts a
|
|
59
|
+
* call that omits them. Every receiving end — the server dispatch
|
|
60
|
+
* (`perform_action`) and a peer receiving a request or notification
|
|
61
|
+
* (`ActionDispatcher`) — reads params through this, so a peer is never
|
|
62
|
+
* stricter than the server.
|
|
63
|
+
*
|
|
64
|
+
* @param input - the action's input schema
|
|
65
|
+
* @param raw_params - the call's `params` as parsed off the wire
|
|
66
|
+
* @returns the value to run `input.safeParse` on
|
|
67
|
+
*/
|
|
68
|
+
export const to_input_params = (input, raw_params) => is_void_schema(input) ? to_void_params(raw_params) : (raw_params ?? {});
|
|
69
|
+
/**
|
|
70
|
+
* `schema.safeParse(value)`, except that keys the schema doesn't declare are
|
|
71
|
+
* dropped rather than refused — the forward-compatible read of a payload a
|
|
72
|
+
* remote authored.
|
|
73
|
+
*
|
|
74
|
+
* Output schemas are strict so the server's DEV output validation catches a
|
|
75
|
+
* handler leaking fields it shouldn't. The receiving side, though, is often
|
|
76
|
+
* older than the remote it talks to — a deploy lands before open tabs reload
|
|
77
|
+
* — and a remote that adds a field is making a compatible change. So when
|
|
78
|
+
* every issue is an unrecognized key, those keys are stripped from a copy of
|
|
79
|
+
* `value` and it's parsed again; any other issue (a wrong type, a missing
|
|
80
|
+
* field, `null` where a value is required) still fails. `value` itself is
|
|
81
|
+
* never mutated. This matches serde's default on the Rust spine, which
|
|
82
|
+
* ignores unknown fields. Keys nested in a `z.union` branch surface as a
|
|
83
|
+
* union issue and aren't stripped; a `z.discriminatedUnion` reports its
|
|
84
|
+
* matched branch's issues directly, so those are.
|
|
85
|
+
*
|
|
86
|
+
* Used for a response's `result` (`ActionEvent`) and for a peer's reply to a
|
|
87
|
+
* server-initiated request (`peer/ping`).
|
|
88
|
+
*
|
|
89
|
+
* @param schema - the declared shape
|
|
90
|
+
* @param value - the remote-authored payload
|
|
91
|
+
* @returns the parse result, with undeclared keys dropped on success
|
|
92
|
+
*/
|
|
93
|
+
export const safe_parse_dropping_unknown_keys = (schema, value) => {
|
|
94
|
+
const parsed = schema.safeParse(value);
|
|
95
|
+
if (parsed.success)
|
|
96
|
+
return parsed;
|
|
97
|
+
const { issues } = parsed.error;
|
|
98
|
+
if (!issues.every((issue) => issue.code === 'unrecognized_keys'))
|
|
99
|
+
return parsed;
|
|
100
|
+
const stripped = structuredClone(value);
|
|
101
|
+
for (const issue of issues) {
|
|
102
|
+
const target = get_at_path(stripped, issue.path);
|
|
103
|
+
if (typeof target !== 'object' || target === null)
|
|
104
|
+
return parsed;
|
|
105
|
+
for (const key of issue.keys) {
|
|
106
|
+
Reflect.deleteProperty(target, key);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return schema.safeParse(stripped);
|
|
110
|
+
};
|
|
111
|
+
const get_at_path = (value, path) => {
|
|
112
|
+
let current = value;
|
|
113
|
+
for (const segment of path) {
|
|
114
|
+
if (typeof current !== 'object' || current === null)
|
|
115
|
+
return undefined;
|
|
116
|
+
current = current[segment];
|
|
117
|
+
}
|
|
118
|
+
return current;
|
|
119
|
+
};
|
|
29
120
|
/**
|
|
30
121
|
* Check if a schema is a strict object (`z.strictObject()`).
|
|
31
122
|
*
|
|
@@ -27,6 +27,17 @@ export declare const filter_role_routes: (surface: AppSurface) => Array<AppSurfa
|
|
|
27
27
|
export declare const filter_authenticated_routes: (surface: AppSurface) => Array<AppSurfaceRoute>;
|
|
28
28
|
/** Filter routes that require keeper credentials (`daemon_token`). */
|
|
29
29
|
export declare const filter_keeper_routes: (surface: AppSurface) => Array<AppSurfaceRoute>;
|
|
30
|
+
/**
|
|
31
|
+
* Filter routes gated to a credential channel (any non-empty
|
|
32
|
+
* `auth.credential_types`), whichever channel that is.
|
|
33
|
+
*
|
|
34
|
+
* Superset of `filter_keeper_routes`: keeper is the `daemon_token` gate, and
|
|
35
|
+
* the same axis points the other way on the session-only routes (the
|
|
36
|
+
* credential-minting actions, `POST /logout` / `POST /password`, the audit
|
|
37
|
+
* SSE stream). Adversarial runners need the whole bucket, since every route
|
|
38
|
+
* in it answers `credential_type_required` to a channel it does not admit.
|
|
39
|
+
*/
|
|
40
|
+
export declare const filter_credential_gated_routes: (surface: AppSurface) => Array<AppSurfaceRoute>;
|
|
30
41
|
/** Filter routes whose `auth.roles` includes the named role. */
|
|
31
42
|
export declare const filter_routes_for_role: (surface: AppSurface, role: string) => Array<AppSurfaceRoute>;
|
|
32
43
|
/**
|
|
@@ -38,11 +49,22 @@ export declare const filter_routes_for_role: (surface: AppSurface, role: string)
|
|
|
38
49
|
* - `'role:<name>'` for each role declared on `auth.roles` (multi-role specs
|
|
39
50
|
* are emitted multiple times; callers that need single-bucket grouping
|
|
40
51
|
* should pre-collapse)
|
|
52
|
+
* - `'credential:<type>'` for each channel a role-less route's
|
|
53
|
+
* `credential_types` admits — the session-only gates (`POST /logout`,
|
|
54
|
+
* `POST /password`, the `account_*` credential-lifecycle actions), plus any
|
|
55
|
+
* channel a consumer registers
|
|
41
56
|
* - `'authenticated'` for `account === 'required'` without role / credential gate
|
|
42
57
|
* - `'optional'` when either axis is `'optional'` and no other bucket fits
|
|
43
58
|
* - `'other'` as a last-resort bucket for shapes that don't match above
|
|
59
|
+
*
|
|
60
|
+
* Keeper is tested before the role bucket and the general credential bucket
|
|
61
|
+
* after it, which is not an inconsistency: keeper is an authority *level* — a
|
|
62
|
+
* ceiling no session or api token reaches — so it names the route, while a
|
|
63
|
+
* session gate is a *channel* restriction layered over whatever authority the
|
|
64
|
+
* route already declares. So the audit SSE stream, gated to the session
|
|
65
|
+
* channel and to the admin role, buckets as `role:admin`.
|
|
44
66
|
*/
|
|
45
|
-
export type RouteAuthCategory = 'none' | 'authenticated' | 'optional' | 'keeper' | `role:${string}` | 'other';
|
|
67
|
+
export type RouteAuthCategory = 'none' | 'authenticated' | 'optional' | 'keeper' | `role:${string}` | `credential:${string}` | 'other';
|
|
46
68
|
/**
|
|
47
69
|
* Group routes by auth category (see `RouteAuthCategory`). Multi-role specs
|
|
48
70
|
* appear under each of their role buckets.
|
|
@@ -65,16 +87,19 @@ export declare const format_route_key: (route: AppSurfaceRoute) => string;
|
|
|
65
87
|
/**
|
|
66
88
|
* Summarize route auth distribution across the surface.
|
|
67
89
|
*
|
|
68
|
-
* Categorical view over the flat auth record
|
|
69
|
-
*
|
|
90
|
+
* Categorical view over the flat auth record, bucketing exactly as
|
|
91
|
+
* `routes_by_auth_type`. Multi-role and multi-channel specs contribute one
|
|
92
|
+
* count per role / channel they admit.
|
|
70
93
|
*
|
|
71
|
-
* @returns counts by auth category, with role counts broken out
|
|
94
|
+
* @returns counts by auth category, with role and channel counts broken out
|
|
95
|
+
* by name
|
|
72
96
|
*/
|
|
73
97
|
export declare const surface_auth_summary: (surface: AppSurface) => {
|
|
74
98
|
none: number;
|
|
75
99
|
authenticated: number;
|
|
76
100
|
optional: number;
|
|
77
101
|
role: Map<string, number>;
|
|
102
|
+
credential: Map<string, number>;
|
|
78
103
|
keeper: number;
|
|
79
104
|
other: number;
|
|
80
105
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surface_query.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/surface_query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"surface_query.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/surface_query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAShE,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC7B,CAAC;AAEvD,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEtD,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CACrB,CAAC;AAEnE,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEtD;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC3B,CAAC;AAEhE,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,EAAE,MAAM,MAAM,KAAG,KAAK,CAAC,eAAe,CAC5B,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,iBAAiB,GAC1B,MAAM,GACN,eAAe,GACf,UAAU,GACV,QAAQ,GACR,QAAQ,MAAM,EAAE,GAChB,cAAc,MAAM,EAAE,GACtB,OAAO,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,UAAU,KAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAwC3F,CAAC;AAEF,qDAAqD;AACrD,eAAO,MAAM,uBAAuB,GACnC,SAAS,UAAU,EACnB,QAAQ,MAAM,KACZ,KAAK,CAAC,eAAe,CAA4D,CAAC;AAErF,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEvD,wDAAwD;AACxD,eAAO,MAAM,yBAAyB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAExD,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEvD,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CACtC,CAAC;AAE7C,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,GAAI,SAAS,UAAU,KAAG,KAAK,CAAC,eAAe,CAC9B,CAAC;AAEzD,iEAAiE;AACjE,eAAO,MAAM,gBAAgB,GAAI,OAAO,eAAe,KAAG,MAAyC,CAAC;AAEpG;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,GAChC,SAAS,UAAU,KACjB;IACF,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CA4Cd,CAAC"}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @module
|
|
15
15
|
*/
|
|
16
|
-
import { is_keeper_auth, is_plain_authenticated_auth, is_public_auth, is_role_auth } from './auth_shape.js';
|
|
16
|
+
import { is_credential_gated_auth, is_keeper_auth, is_plain_authenticated_auth, is_public_auth, is_role_auth } from './auth_shape.js';
|
|
17
17
|
/** Filter routes that require any form of authentication. */
|
|
18
18
|
export const filter_protected_routes = (surface) => surface.routes.filter((r) => !is_public_auth(r.auth));
|
|
19
19
|
/** Filter routes that are publicly accessible (no auth surface at all). */
|
|
@@ -27,6 +27,17 @@ export const filter_role_routes = (surface) => surface.routes.filter((r) => is_r
|
|
|
27
27
|
export const filter_authenticated_routes = (surface) => surface.routes.filter((r) => is_plain_authenticated_auth(r.auth));
|
|
28
28
|
/** Filter routes that require keeper credentials (`daemon_token`). */
|
|
29
29
|
export const filter_keeper_routes = (surface) => surface.routes.filter((r) => is_keeper_auth(r.auth));
|
|
30
|
+
/**
|
|
31
|
+
* Filter routes gated to a credential channel (any non-empty
|
|
32
|
+
* `auth.credential_types`), whichever channel that is.
|
|
33
|
+
*
|
|
34
|
+
* Superset of `filter_keeper_routes`: keeper is the `daemon_token` gate, and
|
|
35
|
+
* the same axis points the other way on the session-only routes (the
|
|
36
|
+
* credential-minting actions, `POST /logout` / `POST /password`, the audit
|
|
37
|
+
* SSE stream). Adversarial runners need the whole bucket, since every route
|
|
38
|
+
* in it answers `credential_type_required` to a channel it does not admit.
|
|
39
|
+
*/
|
|
40
|
+
export const filter_credential_gated_routes = (surface) => surface.routes.filter((r) => is_credential_gated_auth(r.auth));
|
|
30
41
|
/** Filter routes whose `auth.roles` includes the named role. */
|
|
31
42
|
export const filter_routes_for_role = (surface, role) => surface.routes.filter((r) => r.auth.roles?.includes(role) ?? false);
|
|
32
43
|
/**
|
|
@@ -58,6 +69,11 @@ export const routes_by_auth_type = (surface) => {
|
|
|
58
69
|
push(`role:${role}`, r);
|
|
59
70
|
continue;
|
|
60
71
|
}
|
|
72
|
+
if (is_credential_gated_auth(auth)) {
|
|
73
|
+
for (const credential_type of auth.credential_types)
|
|
74
|
+
push(`credential:${credential_type}`, r);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
61
77
|
if (is_plain_authenticated_auth(auth)) {
|
|
62
78
|
push('authenticated', r);
|
|
63
79
|
continue;
|
|
@@ -87,16 +103,19 @@ export const format_route_key = (route) => `${route.method} ${route.path}`;
|
|
|
87
103
|
/**
|
|
88
104
|
* Summarize route auth distribution across the surface.
|
|
89
105
|
*
|
|
90
|
-
* Categorical view over the flat auth record
|
|
91
|
-
*
|
|
106
|
+
* Categorical view over the flat auth record, bucketing exactly as
|
|
107
|
+
* `routes_by_auth_type`. Multi-role and multi-channel specs contribute one
|
|
108
|
+
* count per role / channel they admit.
|
|
92
109
|
*
|
|
93
|
-
* @returns counts by auth category, with role counts broken out
|
|
110
|
+
* @returns counts by auth category, with role and channel counts broken out
|
|
111
|
+
* by name
|
|
94
112
|
*/
|
|
95
113
|
export const surface_auth_summary = (surface) => {
|
|
96
114
|
let none = 0;
|
|
97
115
|
let authenticated = 0;
|
|
98
116
|
let optional = 0;
|
|
99
117
|
const role = new Map();
|
|
118
|
+
const credential = new Map();
|
|
100
119
|
let keeper = 0;
|
|
101
120
|
let other = 0;
|
|
102
121
|
for (const r of surface.routes) {
|
|
@@ -115,6 +134,12 @@ export const surface_auth_summary = (surface) => {
|
|
|
115
134
|
}
|
|
116
135
|
continue;
|
|
117
136
|
}
|
|
137
|
+
if (is_credential_gated_auth(auth)) {
|
|
138
|
+
for (const credential_type of auth.credential_types) {
|
|
139
|
+
credential.set(credential_type, (credential.get(credential_type) ?? 0) + 1);
|
|
140
|
+
}
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
118
143
|
if (is_plain_authenticated_auth(auth)) {
|
|
119
144
|
authenticated++;
|
|
120
145
|
continue;
|
|
@@ -125,5 +150,5 @@ export const surface_auth_summary = (surface) => {
|
|
|
125
150
|
}
|
|
126
151
|
other++;
|
|
127
152
|
}
|
|
128
|
-
return { none, authenticated, optional, role, keeper, other };
|
|
153
|
+
return { none, authenticated, optional, role, credential, keeper, other };
|
|
129
154
|
};
|
|
@@ -31,9 +31,15 @@ export declare const AUDIT_LOG_CHANNEL = "audit_log";
|
|
|
31
31
|
* `session_revoke` closes only the stream tied to the specific revoked session
|
|
32
32
|
* (matched by the blake3 session hash in `event.metadata.session_id`) — closing
|
|
33
33
|
* all of a user's streams for a single-session revoke would be over-aggressive.
|
|
34
|
-
* The single `token_revoke` the WS half handles is omitted
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
* The single `token_revoke` the WS half handles is omitted, and what makes that
|
|
35
|
+
* correct is a gate rather than an assumption: the audit route declares
|
|
36
|
+
* `credential_types: ['session']` (`auth/audit_log_route_schema.ts`), so a
|
|
37
|
+
* bearer is refused at the channel and no stream is ever keyed by a single
|
|
38
|
+
* token id. **A consumer that widens that gate must key its subscribers by
|
|
39
|
+
* api-token id and add `token_revoke` here** — otherwise a revoked token keeps
|
|
40
|
+
* receiving audit rows for the life of the connection. The Rust twin enforces
|
|
41
|
+
* the same pairing structurally, via the `RevocationScope` column on
|
|
42
|
+
* `AUDIT_EVENT_SPECS`.
|
|
37
43
|
*/
|
|
38
44
|
export declare const disconnect_event_types: ReadonlySet<string>;
|
|
39
45
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse_auth_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse_auth_guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAGN,KAAK,aAAa,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEtE,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C
|
|
1
|
+
{"version":3,"file":"sse_auth_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse_auth_guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAGN,KAAK,aAAa,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEtE,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAOrD,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,EACtC,UAAU,kBAAkB,CAAC,CAAC,CAAC,EAC/B,eAAe,MAAM,GAAG,IAAI,EAC5B,KAAK,MAAM,KACT,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CA6CjC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,8FAA8F;IAC9F,SAAS,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,KAAK,MAAM,IAAI,CAAC;IAC1F,kFAAkF;IAClF,GAAG,EAAE,MAAM,CAAC;IACZ,yJAAyJ;IACzJ,cAAc,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,yEAAyE;IACzE,QAAQ,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;CAC9C;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,SAAS,CAOlD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS;IAC7C,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,KAAG,WAgBH,CAAC"}
|