@fuzdev/fuz_app 0.62.0 → 0.64.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 +139 -24
- 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/connection_closer.d.ts +68 -0
- package/dist/actions/connection_closer.d.ts.map +1 -0
- package/dist/actions/connection_closer.js +41 -0
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +1 -0
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +23 -2
- package/dist/actions/register_ws_endpoint.d.ts +11 -9
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +5 -5
- package/dist/actions/transports_ws_auth_guard.d.ts +24 -8
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +23 -7
- package/dist/actions/ws_endpoint_spec.d.ts +119 -0
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -0
- package/dist/actions/ws_endpoint_spec.js +13 -0
- package/dist/auth/CLAUDE.md +124 -39
- package/dist/auth/account_action_specs.d.ts +7 -1
- 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 +13 -0
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +40 -5
- package/dist/auth/account_routes.d.ts +12 -2
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +63 -12
- package/dist/auth/account_schema.d.ts +5 -5
- 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/admin_action_specs.d.ts +8 -8
- package/dist/auth/admin_actions.d.ts +11 -0
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +25 -0
- 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_emitter.d.ts +56 -12
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +38 -12
- 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 +30 -3
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +21 -3
- package/dist/auth/bootstrap_routes.d.ts +1 -1
- package/dist/auth/invite_schema.d.ts +2 -2
- package/dist/auth/request_context.d.ts +1 -1
- package/dist/auth/signup_routes.d.ts +1 -1
- package/dist/auth/standard_rpc_actions.d.ts +1 -0
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +1 -0
- package/dist/env/update_env_variable.js +1 -1
- package/dist/http/CLAUDE.md +42 -26
- package/dist/http/ip_canonical.d.ts +99 -0
- package/dist/http/ip_canonical.d.ts.map +1 -0
- package/dist/http/ip_canonical.js +191 -0
- package/dist/http/origin.d.ts +13 -5
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +13 -31
- package/dist/http/pending_effects.d.ts +1 -1
- package/dist/http/pending_effects.js +1 -1
- package/dist/http/proxy.d.ts +13 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +15 -23
- package/dist/http/surface.d.ts +50 -0
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +27 -1
- package/dist/primitive_schemas.d.ts +20 -4
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +25 -4
- package/dist/realtime/sse_auth_guard.d.ts +16 -4
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +15 -3
- package/dist/server/app_backend.d.ts +66 -19
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +57 -34
- package/dist/server/app_server.d.ts +60 -0
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +95 -2
- package/dist/server/startup.d.ts.map +1 -1
- package/dist/server/startup.js +12 -0
- package/dist/testing/CLAUDE.md +91 -71
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +4 -5
- package/dist/testing/adversarial_headers.d.ts +6 -0
- package/dist/testing/adversarial_headers.d.ts.map +1 -1
- package/dist/testing/adversarial_headers.js +13 -5
- package/dist/testing/app_server.d.ts +33 -32
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +4 -13
- package/dist/testing/attack_surface.d.ts +8 -7
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +12 -8
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +20 -6
- package/dist/testing/audit_drift_guard.d.ts +116 -0
- package/dist/testing/audit_drift_guard.d.ts.map +1 -0
- package/dist/testing/audit_drift_guard.js +134 -0
- package/dist/testing/connection_closer_helpers.d.ts +44 -0
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -0
- package/dist/testing/connection_closer_helpers.js +48 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +7 -9
- package/dist/testing/rate_limiting.js +4 -4
- package/dist/testing/rpc_helpers.d.ts +2 -1
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +6 -8
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +12 -6
- package/dist/testing/stubs.d.ts +11 -0
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +4 -0
- package/dist/testing/surface_invariants.d.ts +66 -1
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +103 -1
- package/dist/ui/CLAUDE.md +13 -18
- package/dist/ui/SurfaceExplorer.svelte +161 -2
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- 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
|
@@ -19,6 +19,7 @@ import './assert_dev_env.js';
|
|
|
19
19
|
import { assert } from 'vitest';
|
|
20
20
|
import { middleware_applies } from '../http/schema_helpers.js';
|
|
21
21
|
import { filter_protected_routes, filter_role_routes, filter_keeper_routes, filter_routes_with_input, filter_routes_with_params, filter_routes_with_query, filter_public_routes, format_route_key, } from '../http/surface_query.js';
|
|
22
|
+
import { PROTOCOL_ACTION_METHODS } from '../actions/action_codegen.js';
|
|
22
23
|
// --- Structural invariants ---
|
|
23
24
|
/**
|
|
24
25
|
* Every protected route has 401 in `error_schemas`.
|
|
@@ -374,6 +375,83 @@ export const audit_error_schema_tightness = (surface) => {
|
|
|
374
375
|
}
|
|
375
376
|
return entries;
|
|
376
377
|
};
|
|
378
|
+
// --- RPC / WS structural invariants ---
|
|
379
|
+
/**
|
|
380
|
+
* Every RPC method on every endpoint has a non-empty `description`.
|
|
381
|
+
*
|
|
382
|
+
* Parallel of `assert_descriptions_present` over `surface.rpc_endpoints`.
|
|
383
|
+
* Empty descriptions on RPC methods leak through `library.json` codegen and
|
|
384
|
+
* consumer-facing docs without the route-level check catching them.
|
|
385
|
+
*/
|
|
386
|
+
export const assert_rpc_method_descriptions_present = (surface) => {
|
|
387
|
+
for (const ep of surface.rpc_endpoints) {
|
|
388
|
+
for (const method of ep.methods) {
|
|
389
|
+
assert.ok(method.description.length > 0, `RPC method '${method.name}' on endpoint '${ep.path}' has empty description`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
/**
|
|
394
|
+
* Every WS method on every endpoint has a non-empty `description`.
|
|
395
|
+
*
|
|
396
|
+
* Parallel of `assert_descriptions_present` over `surface.ws_endpoints`.
|
|
397
|
+
* Same rationale as `assert_rpc_method_descriptions_present` — codegen +
|
|
398
|
+
* surface explorer consume the description, blank values surface as `''`.
|
|
399
|
+
*/
|
|
400
|
+
export const assert_ws_method_descriptions_present = (surface) => {
|
|
401
|
+
for (const ep of surface.ws_endpoints) {
|
|
402
|
+
for (const method of ep.methods) {
|
|
403
|
+
assert.ok(method.description.length > 0, `WS method '${method.name}' on endpoint '${ep.path}' has empty description`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
/**
|
|
408
|
+
* Every WS endpoint's `methods` includes every protocol action method
|
|
409
|
+
* (`heartbeat`, `cancel`).
|
|
410
|
+
*
|
|
411
|
+
* Consumers register WS endpoints by spreading `protocol_actions` from
|
|
412
|
+
* `actions/protocol.ts` before their own actions:
|
|
413
|
+
*
|
|
414
|
+
* ```ts
|
|
415
|
+
* ws_endpoints: [{path: '/api/ws', actions: [...protocol_actions, ...consumer_actions], ...}]
|
|
416
|
+
* ```
|
|
417
|
+
*
|
|
418
|
+
* Forgetting the spread compiles cleanly but breaks at runtime: client-side
|
|
419
|
+
* heartbeats and `cancel` notifications get `method_not_found` from the
|
|
420
|
+
* dispatcher, so disconnect detection silently regresses and per-request
|
|
421
|
+
* cancel never aborts the matching handler. Catch the mistake at the
|
|
422
|
+
* surface layer rather than at runtime.
|
|
423
|
+
*/
|
|
424
|
+
export const assert_ws_endpoints_include_protocol_actions = (surface) => {
|
|
425
|
+
for (const ep of surface.ws_endpoints) {
|
|
426
|
+
const method_names = new Set(ep.methods.map((m) => m.name));
|
|
427
|
+
for (const expected of PROTOCOL_ACTION_METHODS) {
|
|
428
|
+
assert.ok(method_names.has(expected), `WS endpoint '${ep.path}' is missing protocol action method '${expected}' — ` +
|
|
429
|
+
`spread \`protocol_actions\` from 'actions/protocol.js' into \`actions\``);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
/**
|
|
434
|
+
* WS methods follow the kind ⇔ auth biconditional emitted by surface
|
|
435
|
+
* generation: `kind === 'remote_notification' ⟺ auth === null`.
|
|
436
|
+
*
|
|
437
|
+
* `generate_app_surface` produces this shape directly from the action
|
|
438
|
+
* spec union (notifications carry `auth: null` per `ActionSpecUnion`;
|
|
439
|
+
* `request_response` carries a `RouteAuth`). The assertion guards against
|
|
440
|
+
* drift if a future surface emitter, transform, or test fixture violates
|
|
441
|
+
* it — and gives consumers a clear failure message when a hand-built
|
|
442
|
+
* surface mocks the shape incorrectly.
|
|
443
|
+
*/
|
|
444
|
+
export const assert_ws_notifications_have_null_auth = (surface) => {
|
|
445
|
+
for (const ep of surface.ws_endpoints) {
|
|
446
|
+
for (const method of ep.methods) {
|
|
447
|
+
const is_notification = method.kind === 'remote_notification';
|
|
448
|
+
const has_null_auth = method.auth === null;
|
|
449
|
+
assert.ok(is_notification === has_null_auth, `WS method '${method.name}' on endpoint '${ep.path}' violates kind ⇔ auth: ` +
|
|
450
|
+
`kind='${method.kind}', auth=${has_null_auth ? 'null' : 'non-null'} ` +
|
|
451
|
+
`(notifications must have auth: null; request_response must have a RouteAuth)`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
};
|
|
377
455
|
/** Default patterns for sensitive REST routes that should be rate-limited. */
|
|
378
456
|
const DEFAULT_SENSITIVE_PATTERNS = [
|
|
379
457
|
/\/login$/,
|
|
@@ -424,7 +502,9 @@ export const assert_no_unexpected_public_mutations = (surface, allowlist = []) =
|
|
|
424
502
|
*
|
|
425
503
|
* Note: RPC endpoints (`create_rpc_endpoint`) use `input: z.null()` on their
|
|
426
504
|
* route specs — the dispatcher handles body/query parsing internally. Real input
|
|
427
|
-
* schemas live in `rpc_endpoints` surface, not on routes
|
|
505
|
+
* schemas live in `rpc_endpoints` surface, not on routes; see
|
|
506
|
+
* `assert_rpc_ws_surface_invariants` for the parallel checks over RPC/WS
|
|
507
|
+
* method shapes.
|
|
428
508
|
*/
|
|
429
509
|
export const assert_mutation_routes_use_post = (surface) => {
|
|
430
510
|
const input_routes = filter_routes_with_input(surface);
|
|
@@ -546,6 +626,28 @@ export const assert_surface_invariants = (surface) => {
|
|
|
546
626
|
assert_error_code_status_consistency(surface);
|
|
547
627
|
assert_404_schemas_use_specific_errors(surface);
|
|
548
628
|
};
|
|
629
|
+
/**
|
|
630
|
+
* Run all RPC / WS structural invariants. Options-free — applies
|
|
631
|
+
* universally to the `surface.rpc_endpoints` and `surface.ws_endpoints`
|
|
632
|
+
* slots produced by `generate_app_surface`.
|
|
633
|
+
*
|
|
634
|
+
* Parallel of `assert_surface_invariants` for the non-REST surfaces.
|
|
635
|
+
* Within-endpoint duplicate method names and the auth-shape biconditional
|
|
636
|
+
* are already enforced at startup by `compile_action_registry` (see
|
|
637
|
+
* `actions/CLAUDE.md` §Registry compile) — these assertions cover only
|
|
638
|
+
* the contract-surface concerns that a runtime registration check
|
|
639
|
+
* cannot: empty descriptions, missing protocol-action spread on WS
|
|
640
|
+
* endpoints, and kind ⇔ auth drift on WS methods.
|
|
641
|
+
*
|
|
642
|
+
* @throws AssertionError on the first invariant violation; the message
|
|
643
|
+
* names the offending endpoint, method, and field.
|
|
644
|
+
*/
|
|
645
|
+
export const assert_rpc_ws_surface_invariants = (surface) => {
|
|
646
|
+
assert_rpc_method_descriptions_present(surface);
|
|
647
|
+
assert_ws_method_descriptions_present(surface);
|
|
648
|
+
assert_ws_endpoints_include_protocol_actions(surface);
|
|
649
|
+
assert_ws_notifications_have_null_auth(surface);
|
|
650
|
+
};
|
|
549
651
|
/**
|
|
550
652
|
* Run security policy invariants. Configurable with sensible defaults.
|
|
551
653
|
*
|
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`
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
is_plain_authenticated_auth,
|
|
18
18
|
is_public_auth,
|
|
19
19
|
is_role_auth,
|
|
20
|
+
type RouteAuth,
|
|
20
21
|
} from '../http/auth_shape.js';
|
|
21
22
|
|
|
22
23
|
const {surface}: {surface: AppSurface} = $props();
|
|
@@ -28,6 +29,13 @@
|
|
|
28
29
|
|
|
29
30
|
const summary = $derived(surface_auth_summary(surface));
|
|
30
31
|
|
|
32
|
+
const rpc_method_count = $derived(
|
|
33
|
+
surface.rpc_endpoints.reduce((sum, ep) => sum + ep.methods.length, 0),
|
|
34
|
+
);
|
|
35
|
+
const ws_method_count = $derived(
|
|
36
|
+
surface.ws_endpoints.reduce((sum, ep) => sum + ep.methods.length, 0),
|
|
37
|
+
);
|
|
38
|
+
|
|
31
39
|
const auth_matches_filter = (
|
|
32
40
|
auth: AppSurfaceRoute['auth'],
|
|
33
41
|
filter: (typeof auth_types)[number],
|
|
@@ -51,6 +59,8 @@
|
|
|
51
59
|
);
|
|
52
60
|
|
|
53
61
|
let expanded_event: string | null = $state.raw(null);
|
|
62
|
+
let expanded_rpc_method: string | null = $state.raw(null);
|
|
63
|
+
let expanded_ws_method: string | null = $state.raw(null);
|
|
54
64
|
|
|
55
65
|
const toggle_route = (key: string): void => {
|
|
56
66
|
expanded_route = expanded_route === key ? null : key;
|
|
@@ -60,7 +70,15 @@
|
|
|
60
70
|
expanded_event = expanded_event === method ? null : method;
|
|
61
71
|
};
|
|
62
72
|
|
|
63
|
-
const
|
|
73
|
+
const toggle_rpc_method = (key: string): void => {
|
|
74
|
+
expanded_rpc_method = expanded_rpc_method === key ? null : key;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const toggle_ws_method = (key: string): void => {
|
|
78
|
+
expanded_ws_method = expanded_ws_method === key ? null : key;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const format_auth = (auth: RouteAuth): string => {
|
|
64
82
|
if (is_public_auth(auth)) return 'none';
|
|
65
83
|
if (is_keeper_auth(auth)) return 'keeper';
|
|
66
84
|
if (is_role_auth(auth)) return `role:${auth.roles!.join('|')}`;
|
|
@@ -68,7 +86,7 @@
|
|
|
68
86
|
return 'other';
|
|
69
87
|
};
|
|
70
88
|
|
|
71
|
-
const auth_chip_class = (auth:
|
|
89
|
+
const auth_chip_class = (auth: RouteAuth): string => {
|
|
72
90
|
if (is_public_auth(auth)) return 'chip color_b';
|
|
73
91
|
if (is_keeper_auth(auth)) return 'chip color_c';
|
|
74
92
|
if (is_role_auth(auth)) return 'chip color_d';
|
|
@@ -89,6 +107,8 @@
|
|
|
89
107
|
{#if role_count > 0}<span class="chip color_d">{role_count} role</span>{/if}
|
|
90
108
|
{#if summary.keeper > 0}<span class="chip color_c">{summary.keeper} keeper</span>{/if}
|
|
91
109
|
<span class="chip">{surface.middleware.length} middleware</span>
|
|
110
|
+
{#if rpc_method_count > 0}<span class="chip">{rpc_method_count} rpc methods</span>{/if}
|
|
111
|
+
{#if ws_method_count > 0}<span class="chip">{ws_method_count} ws methods</span>{/if}
|
|
92
112
|
{#if surface.env.length}<span class="chip">{surface.env.length} env</span>{/if}
|
|
93
113
|
{#if surface.events.length}<span class="chip">{surface.events.length} events</span>{/if}
|
|
94
114
|
{#if surface.diagnostics.length}{@const warnings = surface.diagnostics.filter(
|
|
@@ -283,6 +303,145 @@
|
|
|
283
303
|
</div>
|
|
284
304
|
{/if}
|
|
285
305
|
|
|
306
|
+
{#if surface.rpc_endpoints.length}
|
|
307
|
+
<h3>rpc endpoints</h3>
|
|
308
|
+
{#each surface.rpc_endpoints as endpoint (endpoint.path)}
|
|
309
|
+
<div class="row" style:gap="var(--space_sm)" style:align-items="center">
|
|
310
|
+
<code>{endpoint.path}</code>
|
|
311
|
+
<span class="chip">{endpoint.methods.length} methods</span>
|
|
312
|
+
</div>
|
|
313
|
+
{#if endpoint.methods.length === 0}
|
|
314
|
+
<p class="text_50">no methods</p>
|
|
315
|
+
{:else}
|
|
316
|
+
<div style:overflow-x="auto">
|
|
317
|
+
<table>
|
|
318
|
+
<thead>
|
|
319
|
+
<tr>
|
|
320
|
+
<th>method</th>
|
|
321
|
+
<th>auth</th>
|
|
322
|
+
<th>side effects</th>
|
|
323
|
+
<th>rate limit</th>
|
|
324
|
+
<th>description</th>
|
|
325
|
+
</tr>
|
|
326
|
+
</thead>
|
|
327
|
+
<tbody>
|
|
328
|
+
{#each endpoint.methods as method (method.name)}
|
|
329
|
+
{@const key = `${endpoint.path}|${method.name}`}
|
|
330
|
+
<tr onclick={() => toggle_rpc_method(key)} style:cursor="pointer">
|
|
331
|
+
<td><code>{method.name}</code></td>
|
|
332
|
+
<td>
|
|
333
|
+
<span class={auth_chip_class(method.auth)}>{format_auth(method.auth)}</span>
|
|
334
|
+
</td>
|
|
335
|
+
<td>{method.side_effects ? 'yes' : 'no'}</td>
|
|
336
|
+
<td class="text_50">{method.rate_limit_key ?? '-'}</td>
|
|
337
|
+
<td class="text_50">{method.description}</td>
|
|
338
|
+
</tr>
|
|
339
|
+
{#if expanded_rpc_method === key}
|
|
340
|
+
<tr transition:slide>
|
|
341
|
+
<td colspan="5">
|
|
342
|
+
<div class="column" style:gap="var(--space_sm)">
|
|
343
|
+
<div>
|
|
344
|
+
<strong>input</strong>
|
|
345
|
+
{#if method.input_schema}
|
|
346
|
+
<pre>{JSON.stringify(method.input_schema, null, 2)}</pre>
|
|
347
|
+
{:else}
|
|
348
|
+
<span class="text_50">none (z.void)</span>
|
|
349
|
+
{/if}
|
|
350
|
+
</div>
|
|
351
|
+
<div>
|
|
352
|
+
<strong>output</strong>
|
|
353
|
+
<pre>{JSON.stringify(method.output_schema, null, 2)}</pre>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
</td>
|
|
357
|
+
</tr>
|
|
358
|
+
{/if}
|
|
359
|
+
{/each}
|
|
360
|
+
</tbody>
|
|
361
|
+
</table>
|
|
362
|
+
</div>
|
|
363
|
+
{/if}
|
|
364
|
+
{/each}
|
|
365
|
+
{/if}
|
|
366
|
+
|
|
367
|
+
{#if surface.ws_endpoints.length}
|
|
368
|
+
<h3>websocket endpoints</h3>
|
|
369
|
+
{#each surface.ws_endpoints as endpoint (endpoint.path)}
|
|
370
|
+
<div
|
|
371
|
+
class="row"
|
|
372
|
+
style:gap="var(--space_sm)"
|
|
373
|
+
style:align-items="center"
|
|
374
|
+
style:flex-wrap="wrap"
|
|
375
|
+
>
|
|
376
|
+
<code>{endpoint.path}</code>
|
|
377
|
+
<span class="chip">{endpoint.methods.length} methods</span>
|
|
378
|
+
{#each endpoint.required_roles as role (role)}
|
|
379
|
+
<span class="chip color_d">role:{role}</span>
|
|
380
|
+
{/each}
|
|
381
|
+
{#each endpoint.allowed_origins as origin (origin)}
|
|
382
|
+
<span class="chip color_b"><code>{origin}</code></span>
|
|
383
|
+
{/each}
|
|
384
|
+
</div>
|
|
385
|
+
{#if endpoint.methods.length === 0}
|
|
386
|
+
<p class="text_50">no methods</p>
|
|
387
|
+
{:else}
|
|
388
|
+
<div style:overflow-x="auto">
|
|
389
|
+
<table>
|
|
390
|
+
<thead>
|
|
391
|
+
<tr>
|
|
392
|
+
<th>method</th>
|
|
393
|
+
<th>kind</th>
|
|
394
|
+
<th>auth</th>
|
|
395
|
+
<th>side effects</th>
|
|
396
|
+
<th>rate limit</th>
|
|
397
|
+
<th>description</th>
|
|
398
|
+
</tr>
|
|
399
|
+
</thead>
|
|
400
|
+
<tbody>
|
|
401
|
+
{#each endpoint.methods as method (method.name)}
|
|
402
|
+
{@const key = `${endpoint.path}|${method.name}`}
|
|
403
|
+
<tr onclick={() => toggle_ws_method(key)} style:cursor="pointer">
|
|
404
|
+
<td><code>{method.name}</code></td>
|
|
405
|
+
<td><span class="chip">{method.kind}</span></td>
|
|
406
|
+
<td>
|
|
407
|
+
{#if method.auth}
|
|
408
|
+
<span class={auth_chip_class(method.auth)}>{format_auth(method.auth)}</span>
|
|
409
|
+
{:else}
|
|
410
|
+
<span class="text_50">—</span>
|
|
411
|
+
{/if}
|
|
412
|
+
</td>
|
|
413
|
+
<td>{method.side_effects ? 'yes' : 'no'}</td>
|
|
414
|
+
<td class="text_50">{method.rate_limit_key ?? '-'}</td>
|
|
415
|
+
<td class="text_50">{method.description}</td>
|
|
416
|
+
</tr>
|
|
417
|
+
{#if expanded_ws_method === key}
|
|
418
|
+
<tr transition:slide>
|
|
419
|
+
<td colspan="6">
|
|
420
|
+
<div class="column" style:gap="var(--space_sm)">
|
|
421
|
+
<div>
|
|
422
|
+
<strong>input</strong>
|
|
423
|
+
{#if method.input_schema}
|
|
424
|
+
<pre>{JSON.stringify(method.input_schema, null, 2)}</pre>
|
|
425
|
+
{:else}
|
|
426
|
+
<span class="text_50">none (z.void)</span>
|
|
427
|
+
{/if}
|
|
428
|
+
</div>
|
|
429
|
+
<div>
|
|
430
|
+
<strong>output</strong>
|
|
431
|
+
<pre>{JSON.stringify(method.output_schema, null, 2)}</pre>
|
|
432
|
+
</div>
|
|
433
|
+
</div>
|
|
434
|
+
</td>
|
|
435
|
+
</tr>
|
|
436
|
+
{/if}
|
|
437
|
+
{/each}
|
|
438
|
+
</tbody>
|
|
439
|
+
</table>
|
|
440
|
+
</div>
|
|
441
|
+
{/if}
|
|
442
|
+
{/each}
|
|
443
|
+
{/if}
|
|
444
|
+
|
|
286
445
|
{#if surface.diagnostics.length}
|
|
287
446
|
<h3>diagnostics</h3>
|
|
288
447
|
<div style:overflow-x="auto">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SurfaceExplorer.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/SurfaceExplorer.svelte"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAC,UAAU,EAAwC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"SurfaceExplorer.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/SurfaceExplorer.svelte"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAC,UAAU,EAAwC,MAAM,oBAAoB,CAAC;AAUzF,KAAK,gBAAgB,GAAI;IAAC,OAAO,EAAE,UAAU,CAAA;CAAC,CAAC;AAgchD,QAAA,MAAM,eAAe,sDAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -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
|