@fuzdev/fuz_app 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/actions/action_bridge.d.ts +3 -3
  2. package/dist/actions/action_bridge.d.ts.map +1 -1
  3. package/dist/actions/action_bridge.js +4 -3
  4. package/dist/actions/action_rpc.d.ts +89 -0
  5. package/dist/actions/action_rpc.d.ts.map +1 -0
  6. package/dist/actions/action_rpc.js +248 -0
  7. package/dist/http/jsonrpc.d.ts +62 -0
  8. package/dist/http/jsonrpc.d.ts.map +1 -0
  9. package/dist/http/jsonrpc.js +49 -0
  10. package/dist/http/jsonrpc_errors.d.ts +132 -0
  11. package/dist/http/jsonrpc_errors.d.ts.map +1 -0
  12. package/dist/http/jsonrpc_errors.js +197 -0
  13. package/dist/http/route_spec.d.ts +2 -1
  14. package/dist/http/route_spec.d.ts.map +1 -1
  15. package/dist/http/route_spec.js +43 -7
  16. package/dist/http/surface.d.ts +25 -0
  17. package/dist/http/surface.d.ts.map +1 -1
  18. package/dist/http/surface.js +16 -1
  19. package/dist/server/app_server.d.ts +3 -1
  20. package/dist/server/app_server.d.ts.map +1 -1
  21. package/dist/server/app_server.js +2 -1
  22. package/dist/testing/adversarial_input.d.ts.map +1 -1
  23. package/dist/testing/adversarial_input.js +22 -7
  24. package/dist/testing/app_server.d.ts +2 -1
  25. package/dist/testing/app_server.d.ts.map +1 -1
  26. package/dist/testing/app_server.js +1 -0
  27. package/dist/testing/rpc_attack_surface.d.ts +23 -0
  28. package/dist/testing/rpc_attack_surface.d.ts.map +1 -0
  29. package/dist/testing/rpc_attack_surface.js +376 -0
  30. package/dist/testing/rpc_helpers.d.ts +44 -0
  31. package/dist/testing/rpc_helpers.d.ts.map +1 -0
  32. package/dist/testing/rpc_helpers.js +74 -0
  33. package/dist/testing/rpc_round_trip.d.ts +41 -0
  34. package/dist/testing/rpc_round_trip.d.ts.map +1 -0
  35. package/dist/testing/rpc_round_trip.js +163 -0
  36. package/dist/testing/stubs.d.ts +3 -1
  37. package/dist/testing/stubs.d.ts.map +1 -1
  38. package/dist/testing/stubs.js +2 -1
  39. package/dist/testing/surface_invariants.d.ts +4 -0
  40. package/dist/testing/surface_invariants.d.ts.map +1 -1
  41. package/dist/testing/surface_invariants.js +4 -0
  42. package/package.json +1 -1
@@ -39,7 +39,7 @@ export declare const derive_http_method: (side_effects: ActionSideEffects) => Ro
39
39
  * Derive a `RouteSpec` from an `ActionSpec` and options.
40
40
  *
41
41
  * Only `request_response` actions (which require non-null `auth`) can become routes.
42
- * `remote_notification` actions (auth null) should use `event_spec_from_action`.
42
+ * `remote_notification` actions (auth null) should use `create_action_event_spec`.
43
43
  * `local_call` actions are not for HTTP transport.
44
44
  *
45
45
  * Error schemas are transport-specific (keyed by HTTP status codes) and belong
@@ -51,7 +51,7 @@ export declare const derive_http_method: (side_effects: ActionSideEffects) => Ro
51
51
  * @returns a `RouteSpec` ready for `apply_route_specs`
52
52
  * @throws if `spec.auth` is null
53
53
  */
54
- export declare const route_spec_from_action: (spec: ActionSpec, options: ActionRouteOptions) => RouteSpec;
54
+ export declare const create_action_route_spec: (spec: ActionSpec, options: ActionRouteOptions) => RouteSpec;
55
55
  /**
56
56
  * Derive an `SseEventSpec` from an `ActionSpec`.
57
57
  *
@@ -61,5 +61,5 @@ export declare const route_spec_from_action: (spec: ActionSpec, options: ActionR
61
61
  * @param options - optional SSE-specific options (channel)
62
62
  * @returns an `SseEventSpec` ready for `create_validated_broadcaster`
63
63
  */
64
- export declare const event_spec_from_action: (spec: ActionSpec, options?: ActionEventOptions) => SseEventSpec;
64
+ export declare const create_action_event_spec: (spec: ActionSpec, options?: ActionEventOptions) => SseEventSpec;
65
65
  //# sourceMappingURL=action_bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action_bridge.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,UAAU,EAAE,UAAU,IAAI,cAAc,EAAE,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAClG,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAC3F,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAEhE,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,uGAAuG;IACvG,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACrB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,mFAAmF;IACnF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,+IAA+I;IAC/I,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,8GAA8G;IAC9G,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kDAAkD;AAClD,eAAO,MAAM,eAAe,GAAI,MAAM,cAAc,KAAG,SAKtD,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,GAAI,cAAc,iBAAiB,KAAG,WAEpE,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,UAAU,EAChB,SAAS,kBAAkB,KACzB,SAkBF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,UAAU,EAChB,UAAU,kBAAkB,KAC1B,YAYF,CAAC"}
1
+ {"version":3,"file":"action_bridge.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,UAAU,EAAE,UAAU,IAAI,cAAc,EAAE,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAClG,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAC,MAAM,uBAAuB,CAAC;AAC3F,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAEhE,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,uGAAuG;IACvG,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACrB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACpB,mFAAmF;IACnF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,+IAA+I;IAC/I,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,8GAA8G;IAC9G,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,kDAAkD;AAClD,eAAO,MAAM,eAAe,GAAI,MAAM,cAAc,KAAG,SAKtD,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,GAAI,cAAc,iBAAiB,KAAG,WAEpE,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,wBAAwB,GACpC,MAAM,UAAU,EAChB,SAAS,kBAAkB,KACzB,SAmBF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GACpC,MAAM,UAAU,EAChB,UAAU,kBAAkB,KAC1B,YAYF,CAAC"}
@@ -25,7 +25,7 @@ export const derive_http_method = (side_effects) => {
25
25
  * Derive a `RouteSpec` from an `ActionSpec` and options.
26
26
  *
27
27
  * Only `request_response` actions (which require non-null `auth`) can become routes.
28
- * `remote_notification` actions (auth null) should use `event_spec_from_action`.
28
+ * `remote_notification` actions (auth null) should use `create_action_event_spec`.
29
29
  * `local_call` actions are not for HTTP transport.
30
30
  *
31
31
  * Error schemas are transport-specific (keyed by HTTP status codes) and belong
@@ -37,7 +37,7 @@ export const derive_http_method = (side_effects) => {
37
37
  * @returns a `RouteSpec` ready for `apply_route_specs`
38
38
  * @throws if `spec.auth` is null
39
39
  */
40
- export const route_spec_from_action = (spec, options) => {
40
+ export const create_action_route_spec = (spec, options) => {
41
41
  if (spec.auth === null) {
42
42
  throw new Error(`Cannot derive route spec from action '${spec.method}': auth is null (only request_response actions with non-null auth can become routes)`);
43
43
  }
@@ -52,6 +52,7 @@ export const route_spec_from_action = (spec, options) => {
52
52
  input: spec.input,
53
53
  output: spec.output,
54
54
  ...(options.errors ? { errors: options.errors } : {}),
55
+ transaction: spec.side_effects,
55
56
  };
56
57
  };
57
58
  /**
@@ -63,7 +64,7 @@ export const route_spec_from_action = (spec, options) => {
63
64
  * @param options - optional SSE-specific options (channel)
64
65
  * @returns an `SseEventSpec` ready for `create_validated_broadcaster`
65
66
  */
66
- export const event_spec_from_action = (spec, options) => {
67
+ export const create_action_event_spec = (spec, options) => {
67
68
  if (spec.kind !== 'remote_notification') {
68
69
  throw new Error(`Cannot derive event spec from action '${spec.method}': kind is '${spec.kind}' (must be 'remote_notification')`);
69
70
  }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Single JSON-RPC 2.0 endpoint from action specs.
3
+ *
4
+ * `create_rpc_endpoint` produces `RouteSpec[]` (GET + POST on one path)
5
+ * with an internal dispatcher. Method name lives in the JSON-RPC envelope
6
+ * (POST body or GET query string), not the URL. Auth is checked per-action
7
+ * inside the dispatcher.
8
+ *
9
+ * Handler signature: `(input: TInput, ctx: ActionContext) => TOutput`
10
+ * where `ActionContext` provides auth identity, DB, and framework context.
11
+ *
12
+ * @module
13
+ */
14
+ import type { Logger } from '@fuzdev/fuz_util/log.js';
15
+ import type { RequestResponseActionSpec } from './action_spec.js';
16
+ import { type RouteSpec } from '../http/route_spec.js';
17
+ import { type RequestContext } from '../auth/request_context.js';
18
+ import type { Db } from '../db/db.js';
19
+ /**
20
+ * Per-request context provided to RPC action handlers.
21
+ *
22
+ * Extends `RouteContext` with auth identity and logger.
23
+ * `auth` is `RequestContext | null` — handlers for authenticated
24
+ * actions can narrow via the auth middleware guarantee.
25
+ */
26
+ export interface ActionContext {
27
+ /** The authenticated identity, or `null` for public routes. */
28
+ auth: RequestContext | null;
29
+ /** Transaction-scoped for mutations, pool-level for reads. */
30
+ db: Db;
31
+ /** Always pool-level — for fire-and-forget effects that outlive the transaction. */
32
+ background_db: Db;
33
+ /** Fire-and-forget side effects — push here for post-response flushing. */
34
+ pending_effects: Array<Promise<void>>;
35
+ /** Logger instance. */
36
+ log: Logger;
37
+ }
38
+ /**
39
+ * Handler function for an RPC action.
40
+ *
41
+ * Receives validated input and an `ActionContext` with per-request deps.
42
+ * Returns the output value (serialized to JSON by the wrapper).
43
+ */
44
+ export type ActionHandler<TInput = any, TOutput = any> = (input: TInput, ctx: ActionContext) => TOutput | Promise<TOutput>;
45
+ /**
46
+ * An RPC action — combines an action spec with its handler.
47
+ *
48
+ * The spec defines the contract (method, auth, schemas, side effects).
49
+ * The handler implements the behavior.
50
+ */
51
+ export interface RpcAction {
52
+ spec: RequestResponseActionSpec;
53
+ handler: ActionHandler;
54
+ }
55
+ /** Options for `create_rpc_endpoint`. */
56
+ export interface CreateRpcEndpointOptions {
57
+ /** Mount path for the endpoint (e.g., `/api/rpc`). */
58
+ path: string;
59
+ /** RPC actions to serve. */
60
+ actions: Array<RpcAction>;
61
+ /** Logger instance for handler context. */
62
+ log: Logger;
63
+ }
64
+ /**
65
+ * Single JSON-RPC 2.0 endpoint — the canonical RPC transport binding.
66
+ *
67
+ * Returns two `RouteSpec` entries (GET + POST on the same path) for
68
+ * `apply_route_specs`. The internal dispatcher handles:
69
+ *
70
+ * 1. **Parse envelope** — POST: JSON body as `JsonrpcRequest`. GET: `method`
71
+ * and `params` from query string.
72
+ * 2. **Lookup method** — find the `RpcAction` by method name.
73
+ * 3. **Auth check** — verify identity against the action's `auth` requirement.
74
+ * 4. **Validate params** — parse input against the action's `input` schema.
75
+ * 5. **Dispatch** — acquire DB handle (transaction for mutations, pool for reads),
76
+ * construct `ActionContext`, call handler, return JSON-RPC response.
77
+ *
78
+ * GET is restricted to `side_effects: false` actions (cacheable reads).
79
+ * All errors use JSON-RPC format: `{jsonrpc, id, error: {code, message, data?}}`.
80
+ *
81
+ * The RouteSpecs use `auth: {type: 'none'}` because auth is checked per-action
82
+ * inside the dispatcher, and `transaction: false` because transaction scope
83
+ * is per-action (mutations get a transaction, reads get pool).
84
+ *
85
+ * @param options - endpoint path, actions, and logger
86
+ * @returns route specs (GET + POST) ready for `apply_route_specs`
87
+ */
88
+ export declare const create_rpc_endpoint: (options: CreateRpcEndpointOptions) => Array<RouteSpec>;
89
+ //# sourceMappingURL=action_rpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action_rpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/actions/action_rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAgC,KAAK,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAWpC;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,+DAA+D;IAC/D,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,8DAA8D;IAC9D,EAAE,EAAE,EAAE,CAAC;IACP,oFAAoF;IACpF,aAAa,EAAE,EAAE,CAAC;IAClB,2EAA2E;IAC3E,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CACxD,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,aAAa,KACd,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,yBAAyB,CAAC;IAChC,OAAO,EAAE,aAAa,CAAC;CACvB;AAED,yCAAyC;AACzC,MAAM,WAAW,wBAAwB;IACxC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;CACZ;AA4CD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,wBAAwB,KAAG,KAAK,CAAC,SAAS,CA6NtF,CAAC"}
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Single JSON-RPC 2.0 endpoint from action specs.
3
+ *
4
+ * `create_rpc_endpoint` produces `RouteSpec[]` (GET + POST on one path)
5
+ * with an internal dispatcher. Method name lives in the JSON-RPC envelope
6
+ * (POST body or GET query string), not the URL. Auth is checked per-action
7
+ * inside the dispatcher.
8
+ *
9
+ * Handler signature: `(input: TInput, ctx: ActionContext) => TOutput`
10
+ * where `ActionContext` provides auth identity, DB, and framework context.
11
+ *
12
+ * @module
13
+ */
14
+ import { z } from 'zod';
15
+ import { DEV } from 'esm-env';
16
+ import {} from '../http/route_spec.js';
17
+ import { get_request_context, has_role } from '../auth/request_context.js';
18
+ import { is_null_schema } from '../http/schema_helpers.js';
19
+ import { JSONRPC_VERSION, JsonrpcRequest } from '../http/jsonrpc.js';
20
+ import { jsonrpc_error_messages, jsonrpc_error_code_to_http_status, JSONRPC_ERROR_CODES, ThrownJsonrpcError, } from '../http/jsonrpc_errors.js';
21
+ /**
22
+ * Format a JSON-RPC error response.
23
+ *
24
+ * @param id - the request id (null if unknown)
25
+ * @param error - the error object
26
+ * @returns a JSON-RPC error response object
27
+ */
28
+ const jsonrpc_error_response = (id, error) => ({
29
+ jsonrpc: JSONRPC_VERSION,
30
+ id,
31
+ error,
32
+ });
33
+ /**
34
+ * Check auth for an action spec against the request context.
35
+ *
36
+ * @param auth - the action's auth requirement
37
+ * @param request_context - the resolved identity (null if unauthenticated)
38
+ * @returns an error json if auth fails, or null if authorized
39
+ */
40
+ const check_action_auth = (auth, request_context) => {
41
+ if (auth === 'public')
42
+ return null;
43
+ if (!request_context)
44
+ return jsonrpc_error_messages.unauthenticated();
45
+ if (auth === 'authenticated')
46
+ return null;
47
+ if (auth === 'keeper') {
48
+ // keeper requires the keeper role
49
+ if (!has_role(request_context, 'keeper'))
50
+ return jsonrpc_error_messages.forbidden();
51
+ return null;
52
+ }
53
+ // role check
54
+ if (!has_role(request_context, auth.role)) {
55
+ return jsonrpc_error_messages.forbidden(`requires role: ${auth.role}`);
56
+ }
57
+ return null;
58
+ };
59
+ /**
60
+ * Single JSON-RPC 2.0 endpoint — the canonical RPC transport binding.
61
+ *
62
+ * Returns two `RouteSpec` entries (GET + POST on the same path) for
63
+ * `apply_route_specs`. The internal dispatcher handles:
64
+ *
65
+ * 1. **Parse envelope** — POST: JSON body as `JsonrpcRequest`. GET: `method`
66
+ * and `params` from query string.
67
+ * 2. **Lookup method** — find the `RpcAction` by method name.
68
+ * 3. **Auth check** — verify identity against the action's `auth` requirement.
69
+ * 4. **Validate params** — parse input against the action's `input` schema.
70
+ * 5. **Dispatch** — acquire DB handle (transaction for mutations, pool for reads),
71
+ * construct `ActionContext`, call handler, return JSON-RPC response.
72
+ *
73
+ * GET is restricted to `side_effects: false` actions (cacheable reads).
74
+ * All errors use JSON-RPC format: `{jsonrpc, id, error: {code, message, data?}}`.
75
+ *
76
+ * The RouteSpecs use `auth: {type: 'none'}` because auth is checked per-action
77
+ * inside the dispatcher, and `transaction: false` because transaction scope
78
+ * is per-action (mutations get a transaction, reads get pool).
79
+ *
80
+ * @param options - endpoint path, actions, and logger
81
+ * @returns route specs (GET + POST) ready for `apply_route_specs`
82
+ */
83
+ export const create_rpc_endpoint = (options) => {
84
+ const { path: endpoint_path, actions, log } = options;
85
+ // build action lookup map
86
+ const action_map = new Map();
87
+ for (const action of actions) {
88
+ if (action_map.has(action.spec.method)) {
89
+ throw new Error(`Duplicate RPC action method: ${action.spec.method}`);
90
+ }
91
+ action_map.set(action.spec.method, action);
92
+ }
93
+ /**
94
+ * Core dispatcher — shared by GET and POST handlers.
95
+ *
96
+ * @param c - Hono context
97
+ * @param route - route context with db and pending_effects
98
+ * @param method_name - the JSON-RPC method name
99
+ * @param raw_params - the raw params (parsed from body or query string)
100
+ * @param id - the request id
101
+ * @param restrict_to_reads - true for GET requests (reject side_effects actions)
102
+ * @returns a Response
103
+ */
104
+ const dispatch = async (c, route, method_name, raw_params, id, restrict_to_reads) => {
105
+ // step 2: lookup method
106
+ const action = action_map.get(method_name);
107
+ if (!action) {
108
+ const error = jsonrpc_error_response(id, jsonrpc_error_messages.method_not_found(method_name));
109
+ return c.json(error, jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.method_not_found));
110
+ }
111
+ // GET restriction: only side_effects:false actions
112
+ if (restrict_to_reads && action.spec.side_effects) {
113
+ const error = jsonrpc_error_response(id, jsonrpc_error_messages.invalid_request({
114
+ reason: `method '${method_name}' has side effects and must use POST`,
115
+ }));
116
+ return c.json(error, jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.invalid_request));
117
+ }
118
+ // step 3: auth check
119
+ const request_context = get_request_context(c);
120
+ const auth_error = check_action_auth(action.spec.auth, request_context);
121
+ if (auth_error) {
122
+ const error = jsonrpc_error_response(id, auth_error);
123
+ return c.json(error, jsonrpc_error_code_to_http_status(auth_error.code));
124
+ }
125
+ // step 4: validate params
126
+ const params = raw_params ?? (is_null_schema(action.spec.input) ? null : undefined);
127
+ const parse_result = action.spec.input.safeParse(params);
128
+ if (!parse_result.success) {
129
+ const error = jsonrpc_error_response(id, jsonrpc_error_messages.invalid_params('invalid params', {
130
+ issues: parse_result.error.issues,
131
+ }));
132
+ return c.json(error, jsonrpc_error_code_to_http_status(JSONRPC_ERROR_CODES.invalid_params));
133
+ }
134
+ // step 5: dispatch — transaction for mutations, pool for reads
135
+ const use_transaction = action.spec.side_effects;
136
+ const execute = async (db) => {
137
+ const action_context = {
138
+ auth: request_context,
139
+ db,
140
+ background_db: route.background_db,
141
+ pending_effects: route.pending_effects,
142
+ log,
143
+ };
144
+ const output = await action.handler(parse_result.data, action_context);
145
+ // DEV-only output validation
146
+ if (DEV) {
147
+ const output_result = action.spec.output.safeParse(output);
148
+ if (!output_result.success) {
149
+ log.warn(`RPC output schema mismatch: ${method_name}`, output_result.error.issues);
150
+ }
151
+ }
152
+ return c.json({ jsonrpc: JSONRPC_VERSION, id, result: output });
153
+ };
154
+ // error handling wraps the transaction boundary so handler throws
155
+ // cause rollback before the error is formatted as a JSON-RPC response
156
+ try {
157
+ if (use_transaction) {
158
+ return await route.db.transaction((tx) => execute(tx));
159
+ }
160
+ return await execute(route.db);
161
+ }
162
+ catch (err) {
163
+ if (err instanceof ThrownJsonrpcError) {
164
+ const status = jsonrpc_error_code_to_http_status(err.code);
165
+ const error_json = { code: err.code, message: err.message };
166
+ if (err.data !== undefined)
167
+ error_json.data = err.data;
168
+ return c.json(jsonrpc_error_response(id, error_json), status);
169
+ }
170
+ // generic error
171
+ log.error(`Unhandled RPC handler error: ${method_name}`, err);
172
+ const message = DEV && err instanceof Error ? err.message : 'internal server error';
173
+ return c.json(jsonrpc_error_response(id, jsonrpc_error_messages.internal_error(message)), 500);
174
+ }
175
+ };
176
+ // POST handler — parse JSON-RPC envelope from body
177
+ const post_handler = async (c, route) => {
178
+ // step 1: parse envelope
179
+ let body;
180
+ try {
181
+ body = await c.req.json();
182
+ }
183
+ catch {
184
+ const error = jsonrpc_error_response(null, jsonrpc_error_messages.parse_error());
185
+ return c.json(error, 400);
186
+ }
187
+ const envelope = JsonrpcRequest.safeParse(body);
188
+ if (!envelope.success) {
189
+ // try to extract id even from an invalid envelope
190
+ const raw_id = typeof body === 'object' && body !== null && 'id' in body
191
+ ? body.id
192
+ : null;
193
+ const id = typeof raw_id === 'string' || typeof raw_id === 'number' ? raw_id : null;
194
+ const error = jsonrpc_error_response(id, jsonrpc_error_messages.invalid_request({ issues: envelope.error.issues }));
195
+ return c.json(error, 400);
196
+ }
197
+ return dispatch(c, route, envelope.data.method, envelope.data.params, envelope.data.id, false);
198
+ };
199
+ // GET handler — extract method and params from query string
200
+ const get_handler = async (c, route) => {
201
+ // step 1: parse from query string
202
+ const method_name = c.req.query('method');
203
+ if (!method_name) {
204
+ const error = jsonrpc_error_response(null, jsonrpc_error_messages.invalid_request({ reason: 'missing method query parameter' }));
205
+ return c.json(error, 400);
206
+ }
207
+ const id_raw = c.req.query('id');
208
+ if (!id_raw) {
209
+ const error = jsonrpc_error_response(null, jsonrpc_error_messages.invalid_request({ reason: 'missing id query parameter' }));
210
+ return c.json(error, 400);
211
+ }
212
+ // parse params from query string (optional — null input schemas need no params)
213
+ const params_raw = c.req.query('params');
214
+ let params;
215
+ if (params_raw !== undefined) {
216
+ try {
217
+ params = JSON.parse(params_raw);
218
+ }
219
+ catch {
220
+ const error = jsonrpc_error_response(id_raw, jsonrpc_error_messages.invalid_params('params query parameter is not valid JSON'));
221
+ return c.json(error, 400);
222
+ }
223
+ }
224
+ return dispatch(c, route, method_name, params, id_raw, true);
225
+ };
226
+ return [
227
+ {
228
+ method: 'POST',
229
+ path: endpoint_path,
230
+ auth: { type: 'none' }, // per-action auth inside dispatcher
231
+ handler: post_handler,
232
+ description: `JSON-RPC 2.0 endpoint — ${actions.length} method${actions.length === 1 ? '' : 's'}`,
233
+ input: z.null(), // dispatcher owns body parsing; rpc_endpoints surface has the real schemas
234
+ output: z.any(), // varies by method
235
+ transaction: false, // per-action inside dispatcher
236
+ },
237
+ {
238
+ method: 'GET',
239
+ path: endpoint_path,
240
+ auth: { type: 'none' }, // per-action auth inside dispatcher
241
+ handler: get_handler,
242
+ description: `JSON-RPC 2.0 endpoint (cacheable reads) — ${actions.length} method${actions.length === 1 ? '' : 's'}`,
243
+ input: z.null(), // params from query string, validated by dispatcher
244
+ output: z.any(), // varies by method
245
+ transaction: false, // per-action inside dispatcher
246
+ },
247
+ ];
248
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * JSON-RPC 2.0 envelope schemas for the single RPC endpoint dispatcher.
3
+ *
4
+ * Minimal subset extracted from zzz's `jsonrpc.ts` — only what the
5
+ * `create_rpc_endpoint` dispatcher needs to parse incoming requests
6
+ * and format outgoing responses. Full JSON-RPC schemas (batching,
7
+ * MCP extensions, notification types) remain in zzz.
8
+ *
9
+ * Following MCP, params and result are object-only (no positional arrays).
10
+ *
11
+ * @source https://github.com/modelcontextprotocol/typescript-sdk
12
+ * @see https://www.jsonrpc.org/specification
13
+ * @module
14
+ */
15
+ import { z } from 'zod';
16
+ export declare const JSONRPC_VERSION = "2.0";
17
+ /** A uniquely identifying id for a request in JSON-RPC. Like MCP, excludes null. */
18
+ export declare const JsonrpcRequestId: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
19
+ export type JsonrpcRequestId = z.infer<typeof JsonrpcRequestId>;
20
+ /** A JSON-RPC method name. */
21
+ export declare const JsonrpcMethod: z.ZodString;
22
+ export type JsonrpcMethod = z.infer<typeof JsonrpcMethod>;
23
+ /** Request params — loose object to allow additional properties. */
24
+ export declare const JsonrpcRequestParams: z.ZodObject<{}, z.core.$loose>;
25
+ export type JsonrpcRequestParams = z.infer<typeof JsonrpcRequestParams>;
26
+ /** Result — loose object to allow additional properties. */
27
+ export declare const JsonrpcResult: z.ZodObject<{}, z.core.$loose>;
28
+ export type JsonrpcResult = z.infer<typeof JsonrpcResult>;
29
+ /** A request that expects a response. */
30
+ export declare const JsonrpcRequest: z.ZodObject<{
31
+ jsonrpc: z.ZodLiteral<"2.0">;
32
+ id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
33
+ method: z.ZodString;
34
+ params: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
35
+ }, z.core.$loose>;
36
+ export type JsonrpcRequest = z.infer<typeof JsonrpcRequest>;
37
+ /** A successful (non-error) response to a request. */
38
+ export declare const JsonrpcResponse: z.ZodObject<{
39
+ jsonrpc: z.ZodLiteral<"2.0">;
40
+ id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
41
+ result: z.ZodObject<{}, z.core.$loose>;
42
+ }, z.core.$loose>;
43
+ export type JsonrpcResponse = z.infer<typeof JsonrpcResponse>;
44
+ /** Error object within a JSON-RPC error response. */
45
+ export declare const JsonrpcErrorObject: z.ZodObject<{
46
+ code: z.ZodNumber;
47
+ message: z.ZodString;
48
+ data: z.ZodOptional<z.ZodUnknown>;
49
+ }, z.core.$loose>;
50
+ export type JsonrpcErrorObject = z.infer<typeof JsonrpcErrorObject>;
51
+ /** A response that indicates an error occurred. */
52
+ export declare const JsonrpcErrorResponse: z.ZodObject<{
53
+ jsonrpc: z.ZodLiteral<"2.0">;
54
+ id: z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
55
+ error: z.ZodObject<{
56
+ code: z.ZodNumber;
57
+ message: z.ZodString;
58
+ data: z.ZodOptional<z.ZodUnknown>;
59
+ }, z.core.$loose>;
60
+ }, z.core.$loose>;
61
+ export type JsonrpcErrorResponse = z.infer<typeof JsonrpcErrorResponse>;
62
+ //# sourceMappingURL=jsonrpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonrpc.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,iDAAoC,CAAC;AAClE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,8BAA8B;AAC9B,eAAO,MAAM,aAAa,aAAa,CAAC;AACxC,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,oEAAoE;AACpE,eAAO,MAAM,oBAAoB,gCAAoB,CAAC;AACtD,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE,4DAA4D;AAC5D,eAAO,MAAM,aAAa,gCAAoB,CAAC;AAC/C,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,yCAAyC;AACzC,eAAO,MAAM,cAAc;;;;;iBAKzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,sDAAsD;AACtD,eAAO,MAAM,eAAe;;;;iBAI1B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,qDAAqD;AACrD,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,mDAAmD;AACnD,eAAO,MAAM,oBAAoB;;;;;;;;iBAI/B,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * JSON-RPC 2.0 envelope schemas for the single RPC endpoint dispatcher.
3
+ *
4
+ * Minimal subset extracted from zzz's `jsonrpc.ts` — only what the
5
+ * `create_rpc_endpoint` dispatcher needs to parse incoming requests
6
+ * and format outgoing responses. Full JSON-RPC schemas (batching,
7
+ * MCP extensions, notification types) remain in zzz.
8
+ *
9
+ * Following MCP, params and result are object-only (no positional arrays).
10
+ *
11
+ * @source https://github.com/modelcontextprotocol/typescript-sdk
12
+ * @see https://www.jsonrpc.org/specification
13
+ * @module
14
+ */
15
+ import { z } from 'zod';
16
+ export const JSONRPC_VERSION = '2.0';
17
+ /** A uniquely identifying id for a request in JSON-RPC. Like MCP, excludes null. */
18
+ export const JsonrpcRequestId = z.union([z.string(), z.number()]);
19
+ /** A JSON-RPC method name. */
20
+ export const JsonrpcMethod = z.string();
21
+ /** Request params — loose object to allow additional properties. */
22
+ export const JsonrpcRequestParams = z.looseObject({});
23
+ /** Result — loose object to allow additional properties. */
24
+ export const JsonrpcResult = z.looseObject({});
25
+ /** A request that expects a response. */
26
+ export const JsonrpcRequest = z.looseObject({
27
+ jsonrpc: z.literal(JSONRPC_VERSION),
28
+ id: JsonrpcRequestId,
29
+ method: JsonrpcMethod,
30
+ params: JsonrpcRequestParams.optional(),
31
+ });
32
+ /** A successful (non-error) response to a request. */
33
+ export const JsonrpcResponse = z.looseObject({
34
+ jsonrpc: z.literal(JSONRPC_VERSION),
35
+ id: JsonrpcRequestId,
36
+ result: JsonrpcResult,
37
+ });
38
+ /** Error object within a JSON-RPC error response. */
39
+ export const JsonrpcErrorObject = z.looseObject({
40
+ code: z.number(),
41
+ message: z.string(),
42
+ data: z.unknown().optional(),
43
+ });
44
+ /** A response that indicates an error occurred. */
45
+ export const JsonrpcErrorResponse = z.looseObject({
46
+ jsonrpc: z.literal(JSONRPC_VERSION),
47
+ id: JsonrpcRequestId.nullable(),
48
+ error: JsonrpcErrorObject,
49
+ });
@@ -0,0 +1,132 @@
1
+ /**
2
+ * JSON-RPC error infrastructure for fuz_app routes.
3
+ *
4
+ * Provides error types, named constructors, and HTTP status mapping
5
+ * for the throw/catch error pattern used by `apply_route_specs`.
6
+ * Extracted from zzz's `jsonrpc_errors.ts` — only core error codes
7
+ * (5 standard + 8 general application). Domain-specific codes stay
8
+ * in consumers. `JSONRPC_ERROR_CODES` is extensible — consumers
9
+ * add their own codes by casting `as JsonrpcErrorCode`.
10
+ *
11
+ * Complementary to `error_schemas.ts`: that module is declarative
12
+ * (Zod schemas for surface introspection), this one is runtime
13
+ * (throw + catch + map).
14
+ *
15
+ * @module
16
+ */
17
+ /** Branded number type for JSON-RPC error codes. */
18
+ export type JsonrpcErrorCode = number & {
19
+ readonly __brand: 'JsonrpcErrorCode';
20
+ };
21
+ /** JSON-RPC error response object — code, message, and optional data. */
22
+ export interface JsonrpcErrorJson {
23
+ code: JsonrpcErrorCode;
24
+ message: string;
25
+ data?: unknown;
26
+ }
27
+ /** Names of standard and general application JSON-RPC error codes. */
28
+ export type JsonrpcErrorName = 'parse_error' | 'invalid_request' | 'method_not_found' | 'invalid_params' | 'internal_error' | 'unauthenticated' | 'forbidden' | 'not_found' | 'conflict' | 'validation_error' | 'rate_limited' | 'service_unavailable' | 'timeout';
29
+ /**
30
+ * Standard JSON-RPC error codes (5) plus general application codes (8).
31
+ *
32
+ * Extensible — consumers add domain-specific codes to their own objects
33
+ * by casting `as JsonrpcErrorCode`. Application codes use the -32000 to
34
+ * -32099 range reserved by the JSON-RPC spec.
35
+ */
36
+ export declare const JSONRPC_ERROR_CODES: {
37
+ /** -32700 */
38
+ readonly parse_error: JsonrpcErrorCode;
39
+ /** -32600 */
40
+ readonly invalid_request: JsonrpcErrorCode;
41
+ /** -32601 */
42
+ readonly method_not_found: JsonrpcErrorCode;
43
+ /** -32602 */
44
+ readonly invalid_params: JsonrpcErrorCode;
45
+ /** -32603 */
46
+ readonly internal_error: JsonrpcErrorCode;
47
+ /**
48
+ * Same as HTTP 401 "unauthorized", but correctly named.
49
+ *
50
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status#client_error_responses
51
+ */
52
+ readonly unauthenticated: JsonrpcErrorCode;
53
+ /**
54
+ * Named to match HTTP 403 — avoids confusion with 401 which
55
+ * is incorrectly named "unauthorized" in HTTP.
56
+ */
57
+ readonly forbidden: JsonrpcErrorCode;
58
+ readonly not_found: JsonrpcErrorCode;
59
+ readonly conflict: JsonrpcErrorCode;
60
+ /**
61
+ * Application-level validation failures (business logic).
62
+ * Use `invalid_params` (-32602) for schema/parsing failures.
63
+ */
64
+ readonly validation_error: JsonrpcErrorCode;
65
+ readonly rate_limited: JsonrpcErrorCode;
66
+ readonly service_unavailable: JsonrpcErrorCode;
67
+ readonly timeout: JsonrpcErrorCode;
68
+ };
69
+ /**
70
+ * Named constructors for `JsonrpcErrorJson` objects.
71
+ *
72
+ * Each function creates a JSON-RPC error response object with the correct
73
+ * code and a sensible default message. Used by the catch layer in
74
+ * `apply_route_specs` to build response bodies.
75
+ */
76
+ export declare const jsonrpc_error_messages: {
77
+ readonly parse_error: (data?: unknown) => JsonrpcErrorJson;
78
+ readonly invalid_request: (data?: unknown) => JsonrpcErrorJson;
79
+ readonly method_not_found: (method?: string, data?: unknown) => JsonrpcErrorJson;
80
+ readonly invalid_params: (message?: string, data?: unknown) => JsonrpcErrorJson;
81
+ readonly internal_error: (message?: string, data?: unknown) => JsonrpcErrorJson;
82
+ readonly unauthenticated: (message?: string, data?: unknown) => JsonrpcErrorJson;
83
+ readonly forbidden: (message?: string, data?: unknown) => JsonrpcErrorJson;
84
+ readonly not_found: (resource?: string, data?: unknown) => JsonrpcErrorJson;
85
+ readonly conflict: (message?: string, data?: unknown) => JsonrpcErrorJson;
86
+ readonly validation_error: (message?: string, data?: unknown) => JsonrpcErrorJson;
87
+ readonly rate_limited: (message?: string, data?: unknown) => JsonrpcErrorJson;
88
+ readonly service_unavailable: (message?: string, data?: unknown) => JsonrpcErrorJson;
89
+ readonly timeout: (message?: string, data?: unknown) => JsonrpcErrorJson;
90
+ };
91
+ /**
92
+ * Error class carrying a JSON-RPC error code — thrown by handlers,
93
+ * caught by `apply_route_specs` and mapped to HTTP status + JSON-RPC error response.
94
+ *
95
+ * Named for what it is: an error with a JSON-RPC error code that gets thrown.
96
+ */
97
+ export declare class ThrownJsonrpcError extends Error {
98
+ code: JsonrpcErrorCode;
99
+ data?: unknown;
100
+ constructor(code: JsonrpcErrorCode, message: string, data?: unknown, options?: ErrorOptions);
101
+ }
102
+ /**
103
+ * Named constructors for `ThrownJsonrpcError`.
104
+ *
105
+ * Usage: `throw jsonrpc_errors.not_found('user')` or `throw jsonrpc_errors.forbidden()`.
106
+ */
107
+ export declare const jsonrpc_errors: {
108
+ readonly parse_error: (data?: unknown) => ThrownJsonrpcError;
109
+ readonly invalid_request: (data?: unknown) => ThrownJsonrpcError;
110
+ readonly method_not_found: (method?: string | undefined, data?: unknown) => ThrownJsonrpcError;
111
+ readonly invalid_params: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
112
+ readonly internal_error: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
113
+ readonly unauthenticated: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
114
+ readonly forbidden: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
115
+ readonly not_found: (resource?: string | undefined, data?: unknown) => ThrownJsonrpcError;
116
+ readonly conflict: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
117
+ readonly validation_error: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
118
+ readonly rate_limited: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
119
+ readonly service_unavailable: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
120
+ readonly timeout: (message?: string | undefined, data?: unknown) => ThrownJsonrpcError;
121
+ };
122
+ /**
123
+ * Map a JSON-RPC error code to an HTTP status code.
124
+ *
125
+ * Returns 500 for unrecognized codes (consumer-defined codes
126
+ * without a mapping default to internal server error).
127
+ *
128
+ * @param code - the JSON-RPC error code
129
+ * @returns the corresponding HTTP status code
130
+ */
131
+ export declare const jsonrpc_error_code_to_http_status: (code: JsonrpcErrorCode) => number;
132
+ //# sourceMappingURL=jsonrpc_errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonrpc_errors.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/jsonrpc_errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,oDAAoD;AACpD,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG;IAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;CAAC,CAAC;AAE/E,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,gBAAgB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CACf;AAED,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GACzB,aAAa,GACb,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,gBAAgB,GAChB,iBAAiB,GACjB,WAAW,GACX,WAAW,GACX,UAAU,GACV,kBAAkB,GAClB,cAAc,GACd,qBAAqB,GACrB,SAAS,CAAC;AAEb;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;IAE/B,aAAa;0BACU,gBAAgB;IACvC,aAAa;8BACc,gBAAgB;IAC3C,aAAa;+BACe,gBAAgB;IAC5C,aAAa;6BACa,gBAAgB;IAC1C,aAAa;6BACa,gBAAgB;IAG1C;;;;OAIG;8BACwB,gBAAgB;IAC3C;;;OAGG;wBACkB,gBAAgB;wBAChB,gBAAgB;uBACjB,gBAAgB;IACpC;;;OAGG;+BACyB,gBAAgB;2BACpB,gBAAgB;kCACT,gBAAgB;sBAC5B,gBAAgB;CAC2B,CAAC;AAEhE;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;kCACb,OAAO,KAAG,gBAAgB;sCAMtB,OAAO,KAAG,gBAAgB;yCAMvB,MAAM,SAAS,OAAO,KAAG,gBAAgB;wCAM1C,MAAM,SAAS,OAAO,KAAG,gBAAgB;wCAO1D,MAAM,SACR,OAAO,KACZ,gBAAgB;yCAMQ,MAAM,SAA6B,OAAO,KAAG,gBAAgB;mCAMnE,MAAM,SAAuB,OAAO,KAAG,gBAAgB;oCAMrD,MAAM,SAAS,OAAO,KAAG,gBAAgB;kCAM5C,MAAM,SAAsB,OAAO,KAAG,gBAAgB;0CAM9C,MAAM,SAA8B,OAAO,KAAG,gBAAgB;sCAMlE,MAAM,SAA0B,OAAO,KAAG,gBAAgB;6CAOxE,MAAM,SACR,OAAO,KACZ,gBAAgB;iCAMA,MAAM,SAAqB,OAAO,KAAG,gBAAgB;CAKe,CAAC;AAEzF;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC5C,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,CAAC,EAAE,OAAO,CAAC;gBAEH,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY;CAK3F;AAWD;;;;GAIG;AACH,eAAO,MAAM,cAAc;8CAXQ,kBAAkB;kDAAlB,kBAAkB;gFAAlB,kBAAkB;+EAAlB,kBAAkB;+EAAlB,kBAAkB;gFAAlB,kBAAkB;0EAAlB,kBAAkB;2EAAlB,kBAAkB;yEAAlB,kBAAkB;iFAAlB,kBAAkB;6EAAlB,kBAAkB;oFAAlB,kBAAkB;wEAAlB,kBAAkB;CAyBqC,CAAC;AAoB3F;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,GAAI,MAAM,gBAAgB,KAAG,MACjB,CAAC"}