functionalscript 0.24.0 → 0.26.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 (62) hide show
  1. package/fs/asserts/proof.f.d.ts +8 -0
  2. package/fs/asserts/proof.f.js +51 -0
  3. package/fs/bnf/data/proof.f.js +7 -17
  4. package/fs/bnf/module.f.d.ts +10 -0
  5. package/fs/bnf/module.f.js +10 -0
  6. package/fs/bnf/testlib.f.js +4 -9
  7. package/fs/cas/module.f.d.ts +2 -10
  8. package/fs/cas/module.f.js +48 -52
  9. package/fs/cas/proof.f.js +21 -12
  10. package/fs/ci/common/module.f.d.ts +2 -0
  11. package/fs/ci/common/module.f.js +4 -1
  12. package/fs/ci/config/module.f.d.ts +2 -2
  13. package/fs/ci/config/module.f.js +2 -2
  14. package/fs/ci/module.f.d.ts +2 -4
  15. package/fs/ci/module.f.js +11 -9
  16. package/fs/ci/node/module.f.js +4 -2
  17. package/fs/ci/proof.f.js +8 -5
  18. package/fs/cli/module.f.d.ts +9 -0
  19. package/fs/cli/module.f.js +24 -0
  20. package/fs/cli/proof.f.d.ts +9 -0
  21. package/fs/cli/proof.f.js +86 -0
  22. package/fs/crypto/sha2/module.f.js +32 -36
  23. package/fs/crypto/vdf/module.f.d.ts +21 -0
  24. package/fs/crypto/vdf/module.f.js +57 -0
  25. package/fs/crypto/vdf/proof.f.d.ts +32 -0
  26. package/fs/crypto/vdf/proof.f.js +135 -0
  27. package/fs/dev/index/module.f.d.ts +1 -8
  28. package/fs/dev/index/module.f.js +1 -1
  29. package/fs/effects/node/module.f.d.ts +1 -1
  30. package/fs/emergent_testing/module.f.d.ts +1 -1
  31. package/fs/emergent_testing/module.f.js +2 -2
  32. package/fs/emergent_testing/proof.f.js +1 -1
  33. package/fs/fjs/module.f.js +34 -24
  34. package/fs/json/module.f.d.ts +25 -9
  35. package/fs/json/module.f.js +25 -3
  36. package/fs/json/rpc/module.f.d.ts +109 -0
  37. package/fs/json/rpc/module.f.js +84 -0
  38. package/fs/json/rpc/proof.f.d.ts +35 -0
  39. package/fs/json/rpc/proof.f.js +68 -0
  40. package/fs/json/schema/module.f.d.ts +76 -0
  41. package/fs/json/schema/module.f.js +112 -0
  42. package/fs/json/schema/proof.f.d.ts +33 -0
  43. package/fs/json/schema/proof.f.js +71 -0
  44. package/fs/mcp/module.f.d.ts +88 -0
  45. package/fs/mcp/module.f.js +69 -0
  46. package/fs/sul/level/literal/proof.f.d.ts +1 -0
  47. package/fs/sul/level/literal/proof.f.js +6 -0
  48. package/fs/types/array/module.f.d.ts +7 -0
  49. package/fs/types/array/module.f.js +7 -0
  50. package/fs/types/prime_field/module.f.d.ts +8 -0
  51. package/fs/types/prime_field/module.f.js +37 -5
  52. package/fs/types/prime_field/proof.f.d.ts +3 -0
  53. package/fs/types/prime_field/proof.f.js +27 -2
  54. package/fs/types/rtti/parse/module.f.d.ts +28 -0
  55. package/fs/types/rtti/parse/module.f.js +35 -5
  56. package/fs/types/rtti/ts/module.f.d.ts +46 -3
  57. package/fs/types/rtti/validate/module.f.js +5 -0
  58. package/fs/website/module.f.d.ts +1 -2
  59. package/fs/website/module.f.js +1 -1
  60. package/fs/website/proof.f.d.ts +1 -1
  61. package/fs/website/proof.f.js +3 -3
  62. package/package.json +4 -4
@@ -0,0 +1,109 @@
1
+ import type { Ts } from '../../types/rtti/ts/module.f.ts';
2
+ import type { Result } from '../../types/result/module.f.ts';
3
+ import { type Unknown } from '../module.f.ts';
4
+ /** Request/response identifier: a string, a number, or `null`. */
5
+ declare const id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
6
+ /**
7
+ * A request or notification envelope. `id` present → request (a response is
8
+ * expected); `id` absent → notification (no response). `params` is optional.
9
+ */
10
+ export declare const request: {
11
+ readonly jsonrpc: "2.0";
12
+ readonly method: import("../../types/rtti/module.f.ts").String;
13
+ readonly params: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
14
+ readonly id: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>, undefined]>;
15
+ };
16
+ /** The JSON-RPC error object. */
17
+ export declare const error: {
18
+ readonly code: import("../../types/rtti/module.f.ts").Number;
19
+ readonly message: import("../../types/rtti/module.f.ts").String;
20
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
21
+ };
22
+ export type Id = Ts<typeof id>;
23
+ export type Request = Ts<typeof request>;
24
+ export type RpcError = Ts<typeof error>;
25
+ export declare const successResponse: {
26
+ readonly jsonrpc: "2.0";
27
+ readonly result: () => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>];
28
+ readonly id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
29
+ };
30
+ export declare const errorResponse: {
31
+ readonly jsonrpc: "2.0";
32
+ readonly error: {
33
+ readonly code: import("../../types/rtti/module.f.ts").Number;
34
+ readonly message: import("../../types/rtti/module.f.ts").String;
35
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
36
+ };
37
+ readonly id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
38
+ };
39
+ /**
40
+ * A response envelope: either a success (`result`) or an error (`error`).
41
+ * Derived from the rtti schema via `Ts<>` — the same declaration is the
42
+ * runtime decoder and the static type, with no drift. rtti structs are open
43
+ * (extra keys allowed), so "result XOR error" is not enforced at runtime; in
44
+ * practice the dispatcher only ever constructs one or the other.
45
+ */
46
+ export declare const response: import("../../types/rtti/module.f.ts").Or<[{
47
+ readonly jsonrpc: "2.0";
48
+ readonly result: () => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>];
49
+ readonly id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
50
+ }, {
51
+ readonly jsonrpc: "2.0";
52
+ readonly error: {
53
+ readonly code: import("../../types/rtti/module.f.ts").Number;
54
+ readonly message: import("../../types/rtti/module.f.ts").String;
55
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
56
+ };
57
+ readonly id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
58
+ }]>;
59
+ export type Response = Ts<typeof response>;
60
+ /** Decodes an untrusted value as a JSON-RPC request / notification. */
61
+ export declare const decodeRequest: import("../../types/rtti/common/module.f.ts").Validate<{
62
+ readonly jsonrpc: "2.0";
63
+ readonly method: import("../../types/rtti/module.f.ts").String;
64
+ readonly params: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
65
+ readonly id: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>, undefined]>;
66
+ }>;
67
+ /** A method implementation: maps `params` to a result or an `RpcError`. */
68
+ export type Handler = (params: Unknown | undefined) => Result<Unknown, RpcError>;
69
+ /** A `method` name → `Handler` map. */
70
+ export type Handlers = {
71
+ readonly [method: string]: Handler;
72
+ };
73
+ /** Builds an `RpcError` with the given `code` and `message` (no `data`). */
74
+ export declare const rpcError: (code: number) => (message: string) => RpcError;
75
+ export declare const parseError: import("../../types/rtti/ts/module.f.ts").StructTs<{
76
+ readonly code: import("../../types/rtti/module.f.ts").Number;
77
+ readonly message: import("../../types/rtti/module.f.ts").String;
78
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
79
+ }>;
80
+ export declare const invalidRequest: import("../../types/rtti/ts/module.f.ts").StructTs<{
81
+ readonly code: import("../../types/rtti/module.f.ts").Number;
82
+ readonly message: import("../../types/rtti/module.f.ts").String;
83
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
84
+ }>;
85
+ export declare const methodNotFound: import("../../types/rtti/ts/module.f.ts").StructTs<{
86
+ readonly code: import("../../types/rtti/module.f.ts").Number;
87
+ readonly message: import("../../types/rtti/module.f.ts").String;
88
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
89
+ }>;
90
+ export declare const invalidParams: import("../../types/rtti/ts/module.f.ts").StructTs<{
91
+ readonly code: import("../../types/rtti/module.f.ts").Number;
92
+ readonly message: import("../../types/rtti/module.f.ts").String;
93
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
94
+ }>;
95
+ export declare const internalError: import("../../types/rtti/ts/module.f.ts").StructTs<{
96
+ readonly code: import("../../types/rtti/module.f.ts").Number;
97
+ readonly message: import("../../types/rtti/module.f.ts").String;
98
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
99
+ }>;
100
+ /**
101
+ * Dispatches an already-parsed JSON-RPC value against `handlers`.
102
+ *
103
+ * - invalid envelope → `Invalid Request` (`-32600`) with `id: null`
104
+ * - notification (no `id`) → `null` (never a response)
105
+ * - unknown method → `Method not found` (`-32601`)
106
+ * - otherwise the handler's `ok` / `error` result becomes a success / error response
107
+ */
108
+ export declare const dispatch: (handlers: Handlers) => (value: Unknown) => Response | null;
109
+ export {};
@@ -0,0 +1,84 @@
1
+ /**
2
+ * JSON-RPC 2.0 envelopes and a pure dispatcher.
3
+ *
4
+ * The envelopes are rtti schemas, so one declaration yields both a runtime
5
+ * decoder (`validate(request)`) and the static type (`Ts<typeof request>`) — no
6
+ * drift between them. The dispatcher is pure: it maps an already-parsed request
7
+ * value to a response value (or `null` for a notification) and performs no I/O.
8
+ *
9
+ * Out of scope here (follow-ups): transports — stdio / HTTP framing — over
10
+ * `fs/effects/node`, and concrete method sets such as MCP (i665-mcp), which layer
11
+ * on top of `dispatch`.
12
+ *
13
+ * @module
14
+ */
15
+ import { number, string, or, option } from "../../types/rtti/module.f.js";
16
+ import { validate } from "../../types/rtti/validate/module.f.js";
17
+ import { unknown } from "../module.f.js";
18
+ const jsonrpc = '2.0';
19
+ /** Request/response identifier: a string, a number, or `null`. */
20
+ const id = or(string, number, null);
21
+ /**
22
+ * A request or notification envelope. `id` present → request (a response is
23
+ * expected); `id` absent → notification (no response). `params` is optional.
24
+ */
25
+ export const request = {
26
+ jsonrpc,
27
+ method: string,
28
+ params: option(unknown),
29
+ id: option(id),
30
+ };
31
+ /** The JSON-RPC error object. */
32
+ export const error = {
33
+ code: number,
34
+ message: string,
35
+ data: option(unknown),
36
+ };
37
+ export const successResponse = { jsonrpc, result: unknown, id };
38
+ export const errorResponse = { jsonrpc, error, id };
39
+ /**
40
+ * A response envelope: either a success (`result`) or an error (`error`).
41
+ * Derived from the rtti schema via `Ts<>` — the same declaration is the
42
+ * runtime decoder and the static type, with no drift. rtti structs are open
43
+ * (extra keys allowed), so "result XOR error" is not enforced at runtime; in
44
+ * practice the dispatcher only ever constructs one or the other.
45
+ */
46
+ export const response = or(successResponse, errorResponse);
47
+ /** Decodes an untrusted value as a JSON-RPC request / notification. */
48
+ export const decodeRequest = validate(request);
49
+ /** Builds an `RpcError` with the given `code` and `message` (no `data`). */
50
+ export const rpcError = (code) => (message) => ({ code, message });
51
+ // The standard JSON-RPC 2.0 errors.
52
+ export const parseError = rpcError(-32700)('Parse error');
53
+ export const invalidRequest = rpcError(-32600)('Invalid Request');
54
+ export const methodNotFound = rpcError(-32601)('Method not found');
55
+ export const invalidParams = rpcError(-32602)('Invalid params');
56
+ export const internalError = rpcError(-32603)('Internal error');
57
+ const errorResponseOf = (id) => (e) => ({ jsonrpc, error: e, id });
58
+ /**
59
+ * Dispatches an already-parsed JSON-RPC value against `handlers`.
60
+ *
61
+ * - invalid envelope → `Invalid Request` (`-32600`) with `id: null`
62
+ * - notification (no `id`) → `null` (never a response)
63
+ * - unknown method → `Method not found` (`-32601`)
64
+ * - otherwise the handler's `ok` / `error` result becomes a success / error response
65
+ */
66
+ export const dispatch = (handlers) => (value) => {
67
+ const decoded = decodeRequest(value);
68
+ if (decoded[0] === 'error') {
69
+ return errorResponseOf(null)(invalidRequest);
70
+ }
71
+ const message = decoded[1];
72
+ const id = message.id;
73
+ if (id === undefined) {
74
+ return null;
75
+ }
76
+ const handler = handlers[message.method];
77
+ if (handler === undefined) {
78
+ return errorResponseOf(id)(methodNotFound);
79
+ }
80
+ const result = handler(message.params);
81
+ return result[0] === 'ok'
82
+ ? { jsonrpc, result: result[1], id }
83
+ : errorResponseOf(id)(result[1]);
84
+ };
@@ -0,0 +1,35 @@
1
+ export declare const proof: {
2
+ schema: {
3
+ request: {
4
+ full: () => void;
5
+ notification: () => void;
6
+ nullId: () => void;
7
+ stringId: () => void;
8
+ badVersion: () => void;
9
+ missingMethod: () => void;
10
+ badId: () => void;
11
+ notObject: () => void;
12
+ };
13
+ error: {
14
+ ok: () => void;
15
+ withData: () => void;
16
+ missingMessage: () => void;
17
+ };
18
+ };
19
+ errors: {
20
+ factory: () => void;
21
+ parseError: () => void;
22
+ invalidRequest: () => void;
23
+ methodNotFound: () => void;
24
+ invalidParams: () => void;
25
+ internalError: () => void;
26
+ };
27
+ dispatch: {
28
+ success: () => void;
29
+ paramsPassed: () => void;
30
+ handlerError: () => void;
31
+ methodNotFound: () => void;
32
+ invalidRequest: () => void;
33
+ notification: () => void;
34
+ };
35
+ };
@@ -0,0 +1,68 @@
1
+ import { assert, assertEq } from "../../asserts/module.f.js";
2
+ import { ok, error } from "../../types/result/module.f.js";
3
+ import { validate } from "../../types/rtti/validate/module.f.js";
4
+ import { error as errorSchema, decodeRequest, dispatch, rpcError, parseError, invalidRequest, methodNotFound, invalidParams, internalError, } from "./module.f.js";
5
+ const isOk = (r) => r[0] === 'ok';
6
+ const handlers = {
7
+ ping: () => ok('pong'),
8
+ echo: params => ok(params ?? null),
9
+ boom: () => error(invalidParams),
10
+ };
11
+ const d = dispatch(handlers);
12
+ export const proof = {
13
+ schema: {
14
+ request: {
15
+ full: () => assert(isOk(decodeRequest({ jsonrpc: '2.0', method: 'm', params: [1], id: 1 }))),
16
+ notification: () => assert(isOk(decodeRequest({ jsonrpc: '2.0', method: 'm' }))),
17
+ nullId: () => assert(isOk(decodeRequest({ jsonrpc: '2.0', method: 'm', id: null }))),
18
+ stringId: () => assert(isOk(decodeRequest({ jsonrpc: '2.0', method: 'm', id: 'abc' }))),
19
+ badVersion: () => assert(!isOk(decodeRequest({ jsonrpc: '1.0', method: 'm', id: 1 }))),
20
+ missingMethod: () => assert(!isOk(decodeRequest({ jsonrpc: '2.0', id: 1 }))),
21
+ badId: () => assert(!isOk(decodeRequest({ jsonrpc: '2.0', method: 'm', id: true }))),
22
+ notObject: () => assert(!isOk(decodeRequest(42))),
23
+ },
24
+ error: {
25
+ ok: () => assert(isOk(validate(errorSchema)({ code: -1, message: 'x' }))),
26
+ withData: () => assert(isOk(validate(errorSchema)({ code: -1, message: 'x', data: [1, 2] }))),
27
+ missingMessage: () => assert(!isOk(validate(errorSchema)({ code: -1 }))),
28
+ },
29
+ },
30
+ errors: {
31
+ factory: () => {
32
+ const e = rpcError(-7)('boom');
33
+ assertEq(e.code, -7);
34
+ assertEq(e.message, 'boom');
35
+ },
36
+ parseError: () => { assertEq(parseError.code, -32700); assertEq(parseError.message, 'Parse error'); },
37
+ invalidRequest: () => { assertEq(invalidRequest.code, -32600); assertEq(invalidRequest.message, 'Invalid Request'); },
38
+ methodNotFound: () => { assertEq(methodNotFound.code, -32601); assertEq(methodNotFound.message, 'Method not found'); },
39
+ invalidParams: () => { assertEq(invalidParams.code, -32602); assertEq(invalidParams.message, 'Invalid params'); },
40
+ internalError: () => { assertEq(internalError.code, -32603); assertEq(internalError.message, 'Internal error'); },
41
+ },
42
+ dispatch: {
43
+ success: () => {
44
+ const r = d({ jsonrpc: '2.0', method: 'ping', id: 1 });
45
+ assert(r !== null && 'result' in r && r.result === 'pong' && r.id === 1);
46
+ },
47
+ paramsPassed: () => {
48
+ const r = d({ jsonrpc: '2.0', method: 'echo', params: 'hi', id: 2 });
49
+ assert(r !== null && 'result' in r && r.result === 'hi');
50
+ },
51
+ handlerError: () => {
52
+ const r = d({ jsonrpc: '2.0', method: 'boom', id: 3 });
53
+ assert(r !== null && 'error' in r && r.error.code === -32602 && r.id === 3);
54
+ },
55
+ methodNotFound: () => {
56
+ const r = d({ jsonrpc: '2.0', method: 'nope', id: 4 });
57
+ assert(r !== null && 'error' in r && r.error.code === -32601);
58
+ },
59
+ invalidRequest: () => {
60
+ const r = d({ jsonrpc: '1.0', method: 'ping', id: 5 });
61
+ assert(r !== null && 'error' in r && r.error.code === -32600 && r.id === null);
62
+ },
63
+ notification: () => {
64
+ const r = d({ jsonrpc: '2.0', method: 'ping' });
65
+ assertEq(r, null);
66
+ },
67
+ },
68
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Converts an rtti schema to a JSON Schema (draft 2020-12) object.
3
+ *
4
+ * Mirrors the visitor structure of `fs/types/rtti/ts/module.f.ts` (`printer` / `toTs`),
5
+ * but emits a JSON Schema object instead of a TypeScript type string.
6
+ *
7
+ * @module
8
+ */
9
+ import { type Type as RttiType } from '../../types/rtti/module.f.ts';
10
+ import type { Ts, WithOut } from '../../types/rtti/ts/module.f.ts';
11
+ declare const unknownThunk: () => readonly ["const", {
12
+ readonly type: import("../../types/rtti/module.f.ts").Or<["boolean", "number", "string", "integer", "array", "object", undefined]>;
13
+ readonly const: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
14
+ readonly not: import("../../types/rtti/module.f.ts").Or<readonly [WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>, undefined]>;
15
+ readonly anyOf: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"array", WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>>, undefined]>;
16
+ readonly items: import("../../types/rtti/module.f.ts").Or<[WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>, false, undefined]>;
17
+ readonly prefixItems: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"array", WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>>, undefined]>;
18
+ readonly properties: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"record", WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>>, undefined]>;
19
+ readonly required: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"array", import("../../types/rtti/module.f.ts").String>, undefined]>;
20
+ readonly additionalProperties: import("../../types/rtti/module.f.ts").Or<readonly [WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>, undefined]>;
21
+ }];
22
+ /** rtti schema for a JSON Schema (draft 2020-12) document. */
23
+ export declare const unknown: WithOut<typeof unknownThunk, UnknownConst>;
24
+ /** A JSON Schema (draft 2020-12) document — the subset of keywords that `toJsonSchema` emits. */
25
+ export type Unknown = Ts<typeof unknown>;
26
+ declare const unknownConst: {
27
+ readonly type: import("../../types/rtti/module.f.ts").Or<["boolean", "number", "string", "integer", "array", "object", undefined]>;
28
+ readonly const: import("../../types/rtti/module.f.ts").Or<readonly [() => readonly ["or", import("../../types/rtti/module.f.ts").Or<[null, import("../../types/rtti/module.f.ts").Boolean, import("../../types/rtti/module.f.ts").Number, import("../../types/rtti/module.f.ts").String]>, import("../../types/rtti/module.f.ts").Type1<"record", /*elided*/ any>, import("../../types/rtti/module.f.ts").Type1<"array", /*elided*/ any>], undefined]>;
29
+ readonly not: import("../../types/rtti/module.f.ts").Or<readonly [WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>, undefined]>;
30
+ readonly anyOf: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"array", WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>>, undefined]>;
31
+ readonly items: import("../../types/rtti/module.f.ts").Or<[WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>, false, undefined]>;
32
+ readonly prefixItems: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"array", WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>>, undefined]>;
33
+ readonly properties: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"record", WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>>, undefined]>;
34
+ readonly required: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"array", import("../../types/rtti/module.f.ts").String>, undefined]>;
35
+ readonly additionalProperties: import("../../types/rtti/module.f.ts").Or<readonly [WithOut<() => readonly ["const", /*elided*/ any], UnknownConst>, undefined]>;
36
+ };
37
+ /**
38
+ * Hand-written base type used as the `$out` annotation on `unknown`.
39
+ *
40
+ * The `?` markers are required even though `Ts<>` already includes `undefined`
41
+ * in each field type. Without `?`, `Unknown = UnknownConst` would require all
42
+ * 9 fields to be present in every object literal returned by `toJsonSchema`,
43
+ * because TypeScript distinguishes "field absent" (`?`) from "field present but
44
+ * undefined" (`T | undefined`). JSON Schema objects only include the fields
45
+ * they need, so all fields must be optional.
46
+ */
47
+ type UnknownConst = {
48
+ readonly type?: Ts<typeof unknownConst.type>;
49
+ readonly const?: Ts<typeof unknownConst.const>;
50
+ readonly not?: Ts<typeof unknownConst.not>;
51
+ readonly anyOf?: Ts<typeof unknownConst.anyOf>;
52
+ readonly items?: Ts<typeof unknownConst.items>;
53
+ readonly prefixItems?: Ts<typeof unknownConst.prefixItems>;
54
+ readonly properties?: Ts<typeof unknownConst.properties>;
55
+ readonly required?: Ts<typeof unknownConst.required>;
56
+ readonly additionalProperties?: Ts<typeof unknownConst.additionalProperties>;
57
+ };
58
+ /**
59
+ * Converts an rtti `Type` to a JSON Schema (draft 2020-12) object.
60
+ *
61
+ * | rtti | JSON Schema |
62
+ * |-----------------------------------------------|-------------------------------------------------------------------------------------|
63
+ * | `boolean` / `number` / `string` | `{ "type": "..." }` |
64
+ * | `bigint` | `{ "type": "integer" }` (lossy; JSON integers are IEEE-754 doubles) |
65
+ * | `unknown` | `{}` (always-true schema) |
66
+ * | primitive const (`42`, `'x'`, `true`, `null`) | `{ "const": <value> }` |
67
+ * | `bigint` const | `{ "const": Number(value) }` (lossy for \|value\| > MAX_SAFE_INTEGER) |
68
+ * | `undefined` const | `{ "not": {} }` (no JSON value satisfies this) |
69
+ * | struct `{ a: T, … }` | `{ "type": "object", "properties": { "a": …T… }, "required": [non-optional keys] }` |
70
+ * | tuple `[A, B]` | `{ "type": "array", "prefixItems": […A…, …B…], "items": false }` |
71
+ * | `array(T)` | `{ "type": "array", "items": …T… }` |
72
+ * | `record(T)` | `{ "type": "object", "additionalProperties": …T… }` |
73
+ * | `or(...types)` | `{ "anyOf": […each…] }` |
74
+ */
75
+ export declare const toJsonSchema: (rtti: RttiType) => Unknown;
76
+ export {};
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Converts an rtti schema to a JSON Schema (draft 2020-12) object.
3
+ *
4
+ * Mirrors the visitor structure of `fs/types/rtti/ts/module.f.ts` (`printer` / `toTs`),
5
+ * but emits a JSON Schema object instead of a TypeScript type string.
6
+ *
7
+ * @module
8
+ */
9
+ import { array, option, or, record, string } from "../../types/rtti/module.f.js";
10
+ import { unknown as jsonUnknown } from "../module.f.js";
11
+ const unknownThunk = () => ['const', unknownConst];
12
+ /** rtti schema for a JSON Schema (draft 2020-12) document. */
13
+ export const unknown = unknownThunk;
14
+ const unknownConst = {
15
+ type: or('boolean', 'number', 'string', 'integer', 'array', 'object', undefined),
16
+ const: option(jsonUnknown),
17
+ not: option(unknown),
18
+ anyOf: option(array(unknown)),
19
+ items: or(unknown, false, undefined),
20
+ prefixItems: option(array(unknown)),
21
+ properties: option(record(unknown)),
22
+ required: option(array(string)),
23
+ additionalProperties: option(unknown),
24
+ };
25
+ /** Returns true if the rtti schema admits the value `undefined`. */
26
+ const admitsUndefined = (rtti) => {
27
+ if (rtti === undefined) {
28
+ return true;
29
+ }
30
+ if (typeof rtti !== 'function') {
31
+ return false;
32
+ }
33
+ const [t, ...r] = rtti();
34
+ return t === 'or' ? r.some(admitsUndefined) : false;
35
+ };
36
+ /** Returns the schema with `undefined` removed from any top-level `or`. */
37
+ const stripUndefined = (rtti) => {
38
+ if (typeof rtti !== 'function') {
39
+ return rtti;
40
+ }
41
+ const [t, ...r] = rtti();
42
+ if (t !== 'or') {
43
+ return rtti;
44
+ }
45
+ const rest = r.flatMap(t => t !== undefined ? [t] : []);
46
+ return rest.length === 1 ? rest[0] : or(...rest);
47
+ };
48
+ const constToJsonSchema = (rtti) => {
49
+ if (typeof rtti === 'undefined') {
50
+ return { not: {} };
51
+ }
52
+ if (typeof rtti !== 'object' || rtti === null) {
53
+ // bigint consts are represented as numbers (lossy for |value| > MAX_SAFE_INTEGER)
54
+ return { const: typeof rtti === 'bigint' ? Number(rtti) : rtti };
55
+ }
56
+ if (rtti instanceof Array) {
57
+ return {
58
+ type: 'array',
59
+ prefixItems: rtti.map(toJsonSchema),
60
+ items: false,
61
+ };
62
+ }
63
+ // Struct: keys not admitting undefined go into `required`; optional keys have
64
+ // undefined stripped from their property schema. additionalProperties is omitted
65
+ // (lenient), matching rtti's open-struct validation semantics.
66
+ const ents = Object.entries(rtti);
67
+ const properties = Object.fromEntries(ents.map(([k, v]) => [k, toJsonSchema(stripUndefined(v))]));
68
+ const required = ents
69
+ .filter(([, v]) => !admitsUndefined(v))
70
+ .map(([k]) => k);
71
+ return {
72
+ type: 'object',
73
+ properties,
74
+ ...(required.length > 0 ? { required } : {}),
75
+ };
76
+ };
77
+ /**
78
+ * Converts an rtti `Type` to a JSON Schema (draft 2020-12) object.
79
+ *
80
+ * | rtti | JSON Schema |
81
+ * |-----------------------------------------------|-------------------------------------------------------------------------------------|
82
+ * | `boolean` / `number` / `string` | `{ "type": "..." }` |
83
+ * | `bigint` | `{ "type": "integer" }` (lossy; JSON integers are IEEE-754 doubles) |
84
+ * | `unknown` | `{}` (always-true schema) |
85
+ * | primitive const (`42`, `'x'`, `true`, `null`) | `{ "const": <value> }` |
86
+ * | `bigint` const | `{ "const": Number(value) }` (lossy for \|value\| > MAX_SAFE_INTEGER) |
87
+ * | `undefined` const | `{ "not": {} }` (no JSON value satisfies this) |
88
+ * | struct `{ a: T, … }` | `{ "type": "object", "properties": { "a": …T… }, "required": [non-optional keys] }` |
89
+ * | tuple `[A, B]` | `{ "type": "array", "prefixItems": […A…, …B…], "items": false }` |
90
+ * | `array(T)` | `{ "type": "array", "items": …T… }` |
91
+ * | `record(T)` | `{ "type": "object", "additionalProperties": …T… }` |
92
+ * | `or(...types)` | `{ "anyOf": […each…] }` |
93
+ */
94
+ export const toJsonSchema = (rtti) => {
95
+ if (typeof rtti !== 'function') {
96
+ return constToJsonSchema(rtti);
97
+ }
98
+ const [tag, ...rest] = rtti();
99
+ switch (tag) {
100
+ case 'const': return constToJsonSchema(rest[0]);
101
+ case 'boolean': return { type: 'boolean' };
102
+ case 'number': return { type: 'number' };
103
+ case 'string': return { type: 'string' };
104
+ // bigint is not representable in JSON Schema; 'integer' is the closest approximation
105
+ case 'bigint': return { type: 'integer' };
106
+ case 'unknown': return {};
107
+ case 'array': return { type: 'array', items: toJsonSchema(rest[0]) };
108
+ case 'record': return { type: 'object', additionalProperties: toJsonSchema(rest[0]) };
109
+ case 'or': return { anyOf: rest.map(toJsonSchema) };
110
+ default: return {};
111
+ }
112
+ };
@@ -0,0 +1,33 @@
1
+ export declare const proof: {
2
+ tag0: {
3
+ boolean: () => void;
4
+ number: () => void;
5
+ string: () => void;
6
+ bigint: () => void;
7
+ unknown: () => void;
8
+ };
9
+ const: {
10
+ null: () => void;
11
+ true: () => void;
12
+ false: () => void;
13
+ number: () => void;
14
+ string: () => void;
15
+ undefined: () => void;
16
+ bigint: () => void;
17
+ };
18
+ array: () => void;
19
+ record: () => void;
20
+ or: () => void;
21
+ tuple: () => void;
22
+ struct: {
23
+ allRequired: () => void;
24
+ withOptional: () => void;
25
+ allOptional: () => void;
26
+ empty: () => void;
27
+ };
28
+ nested: {
29
+ arrayOfRecords: () => void;
30
+ orWithConst: () => void;
31
+ structWithOr: () => void;
32
+ };
33
+ };
@@ -0,0 +1,71 @@
1
+ import { boolean, number, string, bigint, unknown, array, record, or, option } from "../../types/rtti/module.f.js";
2
+ import { stringify } from "../module.f.js";
3
+ import { toJsonSchema } from "./module.f.js";
4
+ const serialize = (v) => stringify(e => e)(v);
5
+ const eq = (rtti, expected) => () => {
6
+ const result = serialize(toJsonSchema(rtti));
7
+ const exp = serialize(expected);
8
+ if (result !== exp) {
9
+ throw [result, exp];
10
+ }
11
+ };
12
+ export const proof = {
13
+ tag0: {
14
+ boolean: eq(boolean, { type: 'boolean' }),
15
+ number: eq(number, { type: 'number' }),
16
+ string: eq(string, { type: 'string' }),
17
+ bigint: eq(bigint, { type: 'integer' }),
18
+ unknown: eq(unknown, {}),
19
+ },
20
+ const: {
21
+ null: eq(null, { const: null }),
22
+ true: eq(true, { const: true }),
23
+ false: eq(false, { const: false }),
24
+ number: eq(42, { const: 42 }),
25
+ string: eq('hello', { const: 'hello' }),
26
+ undefined: eq(undefined, { not: {} }),
27
+ bigint: eq(7n, { const: 7 }),
28
+ },
29
+ array: eq(array(number), { type: 'array', items: { type: 'number' } }),
30
+ record: eq(record(string), { type: 'object', additionalProperties: { type: 'string' } }),
31
+ or: eq(or(string, number), { anyOf: [{ type: 'string' }, { type: 'number' }] }),
32
+ tuple: eq([number, string], {
33
+ type: 'array',
34
+ prefixItems: [{ type: 'number' }, { type: 'string' }],
35
+ items: false,
36
+ }),
37
+ struct: {
38
+ allRequired: eq({ x: number, y: string }, {
39
+ type: 'object',
40
+ properties: { x: { type: 'number' }, y: { type: 'string' } },
41
+ required: ['x', 'y'],
42
+ }),
43
+ withOptional: eq({ x: number, y: option(string) }, {
44
+ type: 'object',
45
+ properties: { x: { type: 'number' }, y: { type: 'string' } },
46
+ required: ['x'],
47
+ }),
48
+ allOptional: eq({ x: option(number) }, {
49
+ type: 'object',
50
+ properties: { x: { type: 'number' } },
51
+ }),
52
+ empty: eq({}, { type: 'object', properties: {} }),
53
+ },
54
+ nested: {
55
+ arrayOfRecords: eq(array(record(boolean)), {
56
+ type: 'array',
57
+ items: { type: 'object', additionalProperties: { type: 'boolean' } },
58
+ }),
59
+ orWithConst: eq(or(null, string, 42), {
60
+ anyOf: [{ const: null }, { type: 'string' }, { const: 42 }],
61
+ }),
62
+ structWithOr: eq({ id: or(string, number), name: option(string) }, {
63
+ type: 'object',
64
+ properties: {
65
+ id: { anyOf: [{ type: 'string' }, { type: 'number' }] },
66
+ name: { type: 'string' },
67
+ },
68
+ required: ['id'],
69
+ }),
70
+ },
71
+ };