@fuzdev/fuz_app 0.5.0 → 0.6.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/action_bridge.d.ts +3 -3
- package/dist/actions/action_bridge.d.ts.map +1 -1
- package/dist/actions/action_bridge.js +4 -3
- package/dist/actions/action_rpc.d.ts +89 -0
- package/dist/actions/action_rpc.d.ts.map +1 -0
- package/dist/actions/action_rpc.js +248 -0
- package/dist/http/jsonrpc.d.ts +62 -0
- package/dist/http/jsonrpc.d.ts.map +1 -0
- package/dist/http/jsonrpc.js +49 -0
- package/dist/http/jsonrpc_errors.d.ts +132 -0
- package/dist/http/jsonrpc_errors.d.ts.map +1 -0
- package/dist/http/jsonrpc_errors.js +197 -0
- package/dist/http/route_spec.d.ts +2 -1
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +43 -7
- package/dist/http/surface.d.ts +25 -0
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +16 -1
- package/dist/server/app_server.d.ts +3 -1
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +2 -1
- package/dist/testing/adversarial_input.d.ts.map +1 -1
- package/dist/testing/adversarial_input.js +22 -7
- package/dist/testing/stubs.d.ts +3 -1
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +2 -1
- package/dist/testing/surface_invariants.d.ts +4 -0
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +4 -0
- 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 `
|
|
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
|
|
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
|
|
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,
|
|
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 `
|
|
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
|
|
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
|
|
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"}
|