functionalscript 0.46.1 → 0.47.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/fjs/basen/cbase32/module.f.mjs +18 -10
- package/fjs/basen/cbase32/proof.f.d.mts +3 -0
- package/fjs/basen/cbase32/proof.f.mjs +24 -2
- package/fjs/basen/proof.f.mjs +8 -16
- package/fjs/bnf/ll1/proof.f.d.mts +2 -0
- package/fjs/bnf/ll1/proof.f.mjs +33 -18
- package/fjs/bnf/module.f.d.mts +2 -2
- package/fjs/bnf/module.f.mjs +2 -2
- package/fjs/cas/proof.f.mjs +4 -26
- package/fjs/ci/common/module.f.mjs +6 -0
- package/fjs/ci/config/module.f.d.mts +7 -7
- package/fjs/ci/config/module.f.mjs +7 -7
- package/fjs/cli/module.f.mjs +14 -10
- package/fjs/crypto/vdf/module.f.d.mts +0 -1
- package/fjs/crypto/vdf/module.f.mjs +3 -14
- package/fjs/dev/module.f.mjs +10 -20
- package/fjs/djs/parser/module.f.d.mts +101 -53
- package/fjs/djs/parser/module.f.mjs +732 -525
- package/fjs/djs/parser/proof.f.d.mts +6 -0
- package/fjs/djs/parser/proof.f.mjs +315 -11
- package/fjs/djs/parser/types.d.ts +29 -2
- package/fjs/djs/tokenizer/module.f.mjs +122 -105
- package/fjs/djs/tokenizer/proof.f.mjs +17 -0
- package/fjs/djs/transpiler/module.f.mjs +4 -5
- package/fjs/djs/transpiler/proof.f.d.mts +2 -0
- package/fjs/djs/transpiler/proof.f.mjs +25 -0
- package/fjs/edag/amnesia/module.f.d.mts +20 -0
- package/fjs/edag/amnesia/module.f.mjs +303 -0
- package/fjs/edag/amnesia/proof.f.d.mts +79 -0
- package/fjs/edag/amnesia/proof.f.mjs +573 -0
- package/fjs/edag/amnesia/types.d.ts +15 -0
- package/fjs/edag/module.f.d.mts +410 -0
- package/fjs/edag/module.f.mjs +559 -0
- package/fjs/edag/proof.f.d.mts +116 -0
- package/fjs/edag/proof.f.mjs +686 -0
- package/fjs/edag/types.d.ts +55 -0
- package/fjs/effects/node/memory/module.d.mts +37 -3
- package/fjs/effects/node/memory/module.mjs +38 -4
- package/fjs/effects/node/memory/proof.d.mts +5 -2
- package/fjs/effects/node/memory/proof.mjs +36 -6
- package/fjs/effects/node/module.d.mts +9 -1
- package/fjs/effects/node/module.f.d.mts +39 -0
- package/fjs/effects/node/module.f.mjs +46 -0
- package/fjs/effects/node/module.mjs +234 -32
- package/fjs/effects/node/types.d.ts +28 -2
- package/fjs/effects/node/virtual/module.f.d.mts +19 -10
- package/fjs/effects/node/virtual/module.f.mjs +246 -32
- package/fjs/effects/node/virtual/proof.f.d.mts +17 -2
- package/fjs/effects/node/virtual/proof.f.mjs +232 -8
- package/fjs/effects/node/virtual/types.d.ts +57 -1
- package/fjs/emergent_testing/browser/proof.d.mts +55 -0
- package/fjs/emergent_testing/browser/proof.mjs +352 -0
- package/fjs/emergent_testing/browser/species.proof.d.mts +4 -0
- package/fjs/emergent_testing/browser/species.proof.mjs +45 -0
- package/fjs/emergent_testing/browser.d.mts +79 -0
- package/fjs/emergent_testing/browser.mjs +432 -0
- package/fjs/emergent_testing/proof.f.d.mts +1 -1
- package/fjs/emergent_testing/proof.f.mjs +2 -2
- package/fjs/js/string_escape/module.f.d.mts +47 -0
- package/fjs/js/string_escape/module.f.mjs +95 -0
- package/fjs/js/string_escape/proof.f.d.mts +8 -0
- package/fjs/js/string_escape/proof.f.mjs +68 -0
- package/fjs/js/tokenizer/module.f.mjs +14 -12
- package/fjs/mcp/cas/module.f.d.mts +1 -7
- package/fjs/mcp/cas/module.f.mjs +24 -15
- package/fjs/mcp/evo/module.f.mjs +9 -24
- package/fjs/mcp/module.f.d.mts +3 -1
- package/fjs/mcp/module.f.mjs +4 -2
- package/fjs/media/json/rtti/module.f.d.mts +2 -2
- package/fjs/media/json/rtti/module.f.mjs +1 -1
- package/fjs/media/json/schema/module.f.d.mts +15 -5
- package/fjs/media/json/schema/module.f.mjs +42 -15
- package/fjs/media/json/schema/proof.f.d.mts +10 -1
- package/fjs/media/json/schema/proof.f.mjs +88 -16
- package/fjs/media/json/serializer/module.f.mjs +14 -26
- package/fjs/media/json/types.d.ts +1 -1
- package/fjs/media/lock/module.f.d.mts +8 -3
- package/fjs/media/lock/module.f.mjs +9 -3
- package/fjs/media/lock/proof.f.d.mts +1 -1
- package/fjs/media/lock/proof.f.mjs +2 -2
- package/fjs/media/lock/types.d.ts +1 -3
- package/fjs/media/module.f.d.mts +20 -13
- package/fjs/media/module.f.mjs +29 -16
- package/fjs/media/note/module.f.d.mts +14 -7
- package/fjs/media/note/module.f.mjs +14 -7
- package/fjs/media/note/proof.f.mjs +1 -1
- package/fjs/media/proof.f.mjs +5 -5
- package/fjs/media/revision/module.f.d.mts +8 -2
- package/fjs/media/revision/module.f.mjs +9 -3
- package/fjs/media/revision/proof.f.mjs +1 -1
- package/fjs/media/revision/types.d.ts +1 -1
- package/fjs/media/type/module.f.d.mts +20 -0
- package/fjs/media/type/module.f.mjs +74 -0
- package/fjs/media/type/proof.f.d.mts +5 -0
- package/fjs/media/type/proof.f.mjs +24 -1
- package/fjs/module.f.mjs +6 -0
- package/fjs/path/module.f.d.mts +60 -3
- package/fjs/path/module.f.mjs +138 -14
- package/fjs/path/proof.f.d.mts +3 -0
- package/fjs/path/proof.f.mjs +298 -2
- package/fjs/protocol/json_rpc/module.f.d.mts +65 -28
- package/fjs/protocol/json_rpc/module.f.mjs +49 -13
- package/fjs/protocol/json_rpc/proof.f.d.mts +4 -0
- package/fjs/protocol/json_rpc/proof.f.mjs +29 -0
- package/fjs/protocol/json_rpc/types.d.ts +12 -2
- package/fjs/protocol/mcp/module.f.d.mts +89 -54
- package/fjs/protocol/mcp/module.f.mjs +110 -56
- package/fjs/protocol/mcp/proof.f.d.mts +13 -0
- package/fjs/protocol/mcp/proof.f.mjs +107 -7
- package/fjs/protocol/mcp/stdio/module.f.mjs +3 -4
- package/fjs/protocol/mcp/stdio/proof.f.mjs +2 -2
- package/fjs/protocol/mcp/types.d.ts +14 -2
- package/fjs/text/utf8/module.f.mjs +27 -1
- package/fjs/text/utf8/proof.f.mjs +37 -0
- package/fjs/types/array/module.f.mjs +18 -11
- package/fjs/types/array/proof.f.mjs +27 -1
- package/fjs/types/bigfloat/module.f.d.mts +48 -9
- package/fjs/types/bigfloat/module.f.mjs +147 -59
- package/fjs/types/bigfloat/proof.f.d.mts +2 -0
- package/fjs/types/bigfloat/proof.f.mjs +136 -4
- package/fjs/types/bigfloat/types.d.ts +17 -0
- package/fjs/types/function/module.f.d.mts +11 -2
- package/fjs/types/function/module.f.mjs +20 -2
- package/fjs/types/function/proof.f.mjs +5 -1
- package/fjs/types/phantom/types.d.ts +21 -0
- package/fjs/types/result/module.f.d.mts +4 -4
- package/fjs/types/result/module.f.mjs +2 -2
- package/fjs/types/result/proof.f.d.mts +6 -2
- package/fjs/types/result/proof.f.mjs +17 -1
- package/fjs/types/rtti/common/module.f.d.mts +78 -5
- package/fjs/types/rtti/common/module.f.mjs +168 -3
- package/fjs/types/rtti/common/proof.f.d.mts +14 -0
- package/fjs/types/rtti/common/proof.f.mjs +61 -2
- package/fjs/types/rtti/common/types.d.ts +23 -1
- package/fjs/types/rtti/data/module.f.d.mts +57 -1
- package/fjs/types/rtti/data/module.f.mjs +241 -47
- package/fjs/types/rtti/data/proof.f.d.mts +10 -0
- package/fjs/types/rtti/data/proof.f.mjs +272 -39
- package/fjs/types/rtti/data/types.d.ts +14 -7
- package/fjs/types/rtti/host.proof.d.mts +33 -0
- package/fjs/types/rtti/host.proof.mjs +170 -0
- package/fjs/types/rtti/module.f.d.mts +51 -6
- package/fjs/types/rtti/module.f.mjs +50 -3
- package/fjs/types/rtti/parse/module.f.d.mts +31 -24
- package/fjs/types/rtti/parse/module.f.mjs +197 -59
- package/fjs/types/rtti/parse/proof.f.d.mts +37 -2
- package/fjs/types/rtti/parse/proof.f.mjs +171 -47
- package/fjs/types/rtti/proof.f.d.mts +6 -0
- package/fjs/types/rtti/proof.f.mjs +42 -1
- package/fjs/types/rtti/ts/module.f.d.mts +9 -3
- package/fjs/types/rtti/ts/module.f.mjs +68 -15
- package/fjs/types/rtti/ts/proof.f.d.mts +42 -2
- package/fjs/types/rtti/ts/proof.f.mjs +139 -6
- package/fjs/types/rtti/ts/types.d.ts +164 -16
- package/fjs/types/rtti/types.d.ts +31 -3
- package/fjs/types/rtti/validate/module.f.d.mts +42 -25
- package/fjs/types/rtti/validate/module.f.mjs +187 -53
- package/fjs/types/rtti/validate/proof.f.d.mts +28 -2
- package/fjs/types/rtti/validate/proof.f.mjs +430 -75
- package/fjs/types/ts/types.d.ts +4 -0
- package/fjs/web/module.f.d.mts +115 -0
- package/fjs/web/module.f.mjs +581 -0
- package/fjs/web/proof.f.d.mts +39 -0
- package/fjs/web/proof.f.mjs +413 -0
- package/fjs/web/types.d.ts +39 -0
- package/fjs/website/browser-prepare.d.mts +5 -0
- package/fjs/website/browser-prepare.mjs +115 -0
- package/fjs/website/browser-source.d.mts +37 -0
- package/fjs/website/browser-source.mjs +202 -0
- package/fjs/website/browser-source.proof.d.mts +31 -0
- package/fjs/website/browser-source.proof.mjs +162 -0
- package/fjs/website/browser.d.mts +5 -0
- package/fjs/website/browser.mjs +8 -0
- package/fjs/website/module.f.d.mts +3 -3
- package/fjs/website/module.f.mjs +62 -9
- package/fjs/website/proof.f.d.mts +4 -0
- package/fjs/website/proof.f.mjs +18 -3
- package/package.json +2 -3
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { assertEq, assertStructurallySame } from '../../asserts/module.f.mjs'
|
|
2
|
+
import {
|
|
3
|
+
backspace,
|
|
4
|
+
cr,
|
|
5
|
+
ff,
|
|
6
|
+
ht,
|
|
7
|
+
latinSmallLetterB,
|
|
8
|
+
latinSmallLetterF,
|
|
9
|
+
latinSmallLetterN,
|
|
10
|
+
latinSmallLetterR,
|
|
11
|
+
latinSmallLetterT,
|
|
12
|
+
latinSmallLetterU,
|
|
13
|
+
lf,
|
|
14
|
+
quotationMark,
|
|
15
|
+
reverseSolidus,
|
|
16
|
+
solidus,
|
|
17
|
+
} from '../../text/ascii/module.f.mjs'
|
|
18
|
+
import {
|
|
19
|
+
codePointToEscape,
|
|
20
|
+
escapeToCodePoint,
|
|
21
|
+
optionalEscape,
|
|
22
|
+
simpleEscapes,
|
|
23
|
+
} from './module.f.mjs'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The escapes spelled out independently of the table, so the proof compares
|
|
27
|
+
* two statements of the set rather than the table against itself.
|
|
28
|
+
*
|
|
29
|
+
* @type {readonly (readonly [number, number])[]}
|
|
30
|
+
*/
|
|
31
|
+
const expected = [
|
|
32
|
+
[quotationMark, quotationMark],
|
|
33
|
+
[reverseSolidus, reverseSolidus],
|
|
34
|
+
[solidus, solidus],
|
|
35
|
+
[latinSmallLetterB, backspace],
|
|
36
|
+
[latinSmallLetterF, ff],
|
|
37
|
+
[latinSmallLetterN, lf],
|
|
38
|
+
[latinSmallLetterR, cr],
|
|
39
|
+
[latinSmallLetterT, ht],
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
export const proof = {
|
|
43
|
+
simpleEscapes: () => assertStructurallySame(simpleEscapes, expected),
|
|
44
|
+
// Every letter decodes to its code point, and — apart from the optional
|
|
45
|
+
// one — every code point encodes back to its letter. Round-tripping both
|
|
46
|
+
// ways is what the two derived views are for.
|
|
47
|
+
roundTrip: () => expected.forEach(([letter, codePoint]) => {
|
|
48
|
+
assertEq(escapeToCodePoint(letter), codePoint, letter)
|
|
49
|
+
assertEq(codePointToEscape(codePoint), codePoint === optionalEscape ? null : letter, codePoint)
|
|
50
|
+
}),
|
|
51
|
+
// `\/` is accepted and never produced: the one asymmetry between the views.
|
|
52
|
+
optionalEscape: [
|
|
53
|
+
() => assertEq(escapeToCodePoint(optionalEscape), solidus),
|
|
54
|
+
() => assertEq(codePointToEscape(optionalEscape), null),
|
|
55
|
+
],
|
|
56
|
+
// `u` is a letter the grammar accepts after a backslash, but its meaning
|
|
57
|
+
// comes from the digits that follow, so it is deliberately absent here.
|
|
58
|
+
unicodeEscapeIsNotSimple: () => assertEq(escapeToCodePoint(latinSmallLetterU), null),
|
|
59
|
+
// A code point with no simple escape, from either side.
|
|
60
|
+
noEscape: [
|
|
61
|
+
() => assertEq(escapeToCodePoint(0x61), null), // `a`
|
|
62
|
+
() => assertEq(codePointToEscape(0x61), null),
|
|
63
|
+
() => assertEq(codePointToEscape(0x00), null), // NUL: a control, but no short form
|
|
64
|
+
],
|
|
65
|
+
// The letters are distinct — a repeated letter would silently shadow an
|
|
66
|
+
// entry in the decode view, and the table is small enough to say so.
|
|
67
|
+
lettersAreDistinct: () => assertEq(new Set(simpleEscapes.map(([letter]) => letter)).size, simpleEscapes.length),
|
|
68
|
+
}
|
|
@@ -22,15 +22,14 @@ import { strictEqual } from '../../types/function/operator/module.f.mjs'
|
|
|
22
22
|
import { merge, fromRange, get } from '../../types/range_map/module.f.mjs'
|
|
23
23
|
import { empty, stateScan, flat, toArray, reduce as listReduce, scan, map as listMap } from '../../types/list/module.f.mjs'
|
|
24
24
|
import { keywords } from '../keywords/module.f.mjs'
|
|
25
|
+
import { simpleEscapes } from '../string_escape/module.f.mjs'
|
|
25
26
|
import { at, fromEntries } from '../../types/ordered_map/module.f.mjs'
|
|
26
27
|
import { one } from '../../types/range/module.f.mjs'
|
|
27
28
|
import {
|
|
28
29
|
range,
|
|
29
30
|
//
|
|
30
|
-
backspace,
|
|
31
31
|
ht,
|
|
32
32
|
lf,
|
|
33
|
-
ff,
|
|
34
33
|
cr,
|
|
35
34
|
//
|
|
36
35
|
exclamationMark,
|
|
@@ -70,12 +69,8 @@ import {
|
|
|
70
69
|
lowLine,
|
|
71
70
|
//
|
|
72
71
|
latinSmallLetterRange,
|
|
73
|
-
latinSmallLetterB,
|
|
74
72
|
latinSmallLetterE,
|
|
75
|
-
latinSmallLetterF,
|
|
76
73
|
latinSmallLetterN,
|
|
77
|
-
latinSmallLetterR,
|
|
78
|
-
latinSmallLetterT,
|
|
79
74
|
latinSmallLetterU,
|
|
80
75
|
//
|
|
81
76
|
leftCurlyBracket,
|
|
@@ -501,14 +496,21 @@ const parseEscapeDefault = state => input => {
|
|
|
501
496
|
return [{ first: { kind: 'error', message: 'unescaped character' }, tail: next[0] }, next[1]]
|
|
502
497
|
}
|
|
503
498
|
|
|
499
|
+
/**
|
|
500
|
+
* One dispatch entry per simple escape, appending the code point the letter
|
|
501
|
+
* denotes. The three self-denoting escapes are not special-cased: `\"` appends
|
|
502
|
+
* `"` because that is what the table pairs it with.
|
|
503
|
+
*
|
|
504
|
+
* @type {readonly _RangeFunc<_ParseEscapeCharState>[]}
|
|
505
|
+
*/
|
|
506
|
+
const simpleEscapeFuncs = simpleEscapes.map(([letter, codePoint]) =>
|
|
507
|
+
rangeFunc(one(letter))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(codePoint) }]))
|
|
508
|
+
|
|
504
509
|
/** @type {(state: _ParseEscapeCharState) => (input: number) => readonly [List<JsToken>, _TokenizerState]} */
|
|
505
510
|
const parseEscapeCharStateOp = create(parseEscapeDefault)([
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
rangeFunc(one(latinSmallLetterN))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(lf) }]),
|
|
510
|
-
rangeFunc(one(latinSmallLetterR))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(cr) }]),
|
|
511
|
-
rangeFunc(one(latinSmallLetterT))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(ht) }]),
|
|
511
|
+
...simpleEscapeFuncs,
|
|
512
|
+
// `\u` is the one escape whose meaning is not a lookup: the four hex
|
|
513
|
+
// digits that follow decide it, so it starts a state instead.
|
|
512
514
|
rangeFunc(one(latinSmallLetterU))(state => () => [empty, { kind: 'unicodeChar', value: state.value, unicode: 0, hexIndex: 0 }]),
|
|
513
515
|
])
|
|
514
516
|
|
|
@@ -116,7 +116,7 @@ import type { Key } from '../../effects/memory/types.ts';
|
|
|
116
116
|
/** Arguments for `cas_add`: content to store, with optional encoding type. */
|
|
117
117
|
export declare const casAddArgs: {
|
|
118
118
|
readonly content: import("../../types/rtti/types.ts")._Type0<"string">;
|
|
119
|
-
readonly type: import("../../types/rtti/types.ts").Or<["text", "base64", undefined]>;
|
|
119
|
+
readonly type: import("../../types/rtti/types.ts").Or<readonly ["text", "base64", undefined]>;
|
|
120
120
|
};
|
|
121
121
|
/** Arguments for `cas_get`: the cBase32 hash to look up; optionally request inline content. */
|
|
122
122
|
export declare const casGetArgs: {
|
|
@@ -131,12 +131,6 @@ export type _Meta = {
|
|
|
131
131
|
readonly type: 'text' | 'base64';
|
|
132
132
|
readonly uri: string;
|
|
133
133
|
};
|
|
134
|
-
/** @typedef {{
|
|
135
|
-
* readonly length: number
|
|
136
|
-
* readonly mimeType: string
|
|
137
|
-
* readonly type: 'text' | 'base64'
|
|
138
|
-
* readonly uri: string
|
|
139
|
-
* }} _Meta */
|
|
140
134
|
/**
|
|
141
135
|
* Registry of all CAS tools, bound to `home` and the Evo cache at
|
|
142
136
|
* `cacheKey`. `cas_add` is the only tool that writes, so it is the only one
|
package/fjs/mcp/cas/module.f.mjs
CHANGED
|
@@ -123,7 +123,7 @@ import { noteDialect } from '../../media/note/module.f.mjs'
|
|
|
123
123
|
import { maxLengthBytes } from '../../types/bit_vec/module.f.mjs'
|
|
124
124
|
import { ok } from '../../types/result/module.f.mjs'
|
|
125
125
|
import {
|
|
126
|
-
toolEntry, errorResult, okResult,
|
|
126
|
+
toolEntry, errorResult, okResult, toolResultStep,
|
|
127
127
|
} from '../../protocol/mcp/module.f.mjs'
|
|
128
128
|
import { collectRead, fileCas } from '../../cas/module.f.mjs'
|
|
129
129
|
import { errorSummary } from '../../effects/node/module.f.mjs'
|
|
@@ -135,11 +135,15 @@ import { syncRevision } from '../../cas/evo/module.f.mjs'
|
|
|
135
135
|
import { assertNotNullish } from '../../asserts/module.f.mjs'
|
|
136
136
|
|
|
137
137
|
// ── Argument schemas (declared once, used for both inputSchema and validate) ─────
|
|
138
|
+
//
|
|
139
|
+
// Closed, as the bare form says: a tool's argument list is a vocabulary this
|
|
140
|
+
// server defines rather than a format a client extends, and the JSON Schema
|
|
141
|
+
// derived from these now advertises exactly what the validator enforces.
|
|
138
142
|
|
|
139
143
|
/** Arguments for `cas_add`: content to store, with optional encoding type. */
|
|
140
144
|
export const casAddArgs = /** @type {const} */ ({
|
|
141
145
|
content: string,
|
|
142
|
-
type: or(
|
|
146
|
+
type: or('text', 'base64', undefined)
|
|
143
147
|
})
|
|
144
148
|
|
|
145
149
|
/** Arguments for `cas_get`: the cBase32 hash to look up; optionally request inline content. */
|
|
@@ -170,6 +174,14 @@ const detectDialect = detect([revisionDialect, lockDialect, noteDialect])
|
|
|
170
174
|
* readonly uri: string
|
|
171
175
|
* }} _Meta */
|
|
172
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Maps a media-type detector verdict to the `cas_get` wire metadata.
|
|
179
|
+
*
|
|
180
|
+
* @type {(uri: string) => (detected: { readonly length: bigint, readonly mime_type: string, readonly type: 'text' | 'base64' }) => _Meta}
|
|
181
|
+
*/
|
|
182
|
+
const toMeta = uri => ({ length, mime_type: mimeType, type }) =>
|
|
183
|
+
({ length: Number(length), mimeType, type, uri })
|
|
184
|
+
|
|
173
185
|
/**
|
|
174
186
|
* Registry of all CAS tools, bound to `home` and the Evo cache at
|
|
175
187
|
* `cacheKey`. `cas_add` is the only tool that writes, so it is the only one
|
|
@@ -220,21 +232,21 @@ export const casToolRegistry = home => cacheKey => {
|
|
|
220
232
|
return pureOk(errorResult(`invalid cBase32 hash: ${r.hash}`))
|
|
221
233
|
}
|
|
222
234
|
const uri = c.url(key)
|
|
235
|
+
const meta = toMeta(uri)
|
|
223
236
|
return resultStep(
|
|
224
237
|
detectStream(c.read(key)),
|
|
225
238
|
([tag, detected]) => {
|
|
226
239
|
if (tag === 'error') {
|
|
227
240
|
return pureOk(errorResult(`no such hash: ${r.hash}`))
|
|
228
241
|
}
|
|
229
|
-
const { length,
|
|
230
|
-
|
|
231
|
-
const meta = { length: Number(length), mimeType, type, uri }
|
|
242
|
+
const { length, type } = detected
|
|
243
|
+
const detectedMeta = meta(detected)
|
|
232
244
|
if (r.content !== true) {
|
|
233
245
|
// A dialect match can only be decided from the whole parsed blob;
|
|
234
246
|
// only attempt the extra bounded read when it stands a chance
|
|
235
247
|
// (whole-blob text within the same cap `content: true` allows).
|
|
236
248
|
if (type !== 'text' || length > maxLengthBytes) {
|
|
237
|
-
return pureOk(okResult(toJson(
|
|
249
|
+
return pureOk(okResult(toJson(detectedMeta)))
|
|
238
250
|
}
|
|
239
251
|
return resultStep(
|
|
240
252
|
collectRead(c.read(key)),
|
|
@@ -242,9 +254,9 @@ export const casToolRegistry = home => cacheKey => {
|
|
|
242
254
|
// Already known to fit from the streaming pass above, so an
|
|
243
255
|
// error here means the hash vanished between reads; fall back
|
|
244
256
|
// to the streaming verdict rather than fail the whole request.
|
|
245
|
-
if (collectTag === 'error') { return pureOk(okResult(toJson(
|
|
257
|
+
if (collectTag === 'error') { return pureOk(okResult(toJson(detectedMeta))) }
|
|
246
258
|
const refined = detectDialect(value)
|
|
247
|
-
return pureOk(okResult(toJson(
|
|
259
|
+
return pureOk(okResult(toJson(meta(refined))))
|
|
248
260
|
}
|
|
249
261
|
)
|
|
250
262
|
}
|
|
@@ -267,8 +279,7 @@ export const casToolRegistry = home => cacheKey => {
|
|
|
267
279
|
// hand — is reflected in the inline result too, not just the
|
|
268
280
|
// streaming guess.
|
|
269
281
|
const refined = detectDialect(value)
|
|
270
|
-
|
|
271
|
-
const refinedMeta = { length: Number(refined.length), mimeType: refined.mime_type, type: refined.type, uri }
|
|
282
|
+
const refinedMeta = meta(refined)
|
|
272
283
|
if (refined.type === 'text') {
|
|
273
284
|
// `type: 'text'` means the detector validated `value` as
|
|
274
285
|
// whole-blob UTF-8 with a byte-aligned length (see
|
|
@@ -295,12 +306,10 @@ export const casToolRegistry = home => cacheKey => {
|
|
|
295
306
|
'cas_list',
|
|
296
307
|
'List all stored content hashes (cBase32), one per line.',
|
|
297
308
|
casListArgs,
|
|
298
|
-
() =>
|
|
309
|
+
() => toolResultStep(
|
|
299
310
|
c.list(),
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
: okResult(r[1].map(vecToCBase32).join('\n')))
|
|
303
|
-
),
|
|
311
|
+
hs => hs.map(vecToCBase32).join('\n'),
|
|
312
|
+
errorSummary),
|
|
304
313
|
),
|
|
305
314
|
]
|
|
306
315
|
}
|
package/fjs/mcp/evo/module.f.mjs
CHANGED
|
@@ -51,15 +51,16 @@
|
|
|
51
51
|
|
|
52
52
|
import { string, option, array } from '../../types/rtti/module.f.mjs'
|
|
53
53
|
import { lockField } from '../../media/revision/module.f.mjs'
|
|
54
|
-
import { pureOk, resultStep } from '../../effects/module.f.mjs'
|
|
55
54
|
import { evoSummary } from '../../cas/evo/module.f.mjs'
|
|
56
|
-
import {
|
|
57
|
-
toolEntry, errorResult, okResult,
|
|
58
|
-
} from '../../protocol/mcp/module.f.mjs'
|
|
55
|
+
import { toolEntry, toolResultStep } from '../../protocol/mcp/module.f.mjs'
|
|
59
56
|
import { stringify } from '../../media/json/module.f.mjs'
|
|
60
57
|
import { identity } from '../../types/function/module.f.mjs'
|
|
61
58
|
|
|
62
59
|
// ── Argument schemas (declared once, used for both inputSchema and validate) ─────
|
|
60
|
+
//
|
|
61
|
+
// Closed, as the bare form says, for the reason `../cas/module.f.mjs` gives:
|
|
62
|
+
// a tool's argument list is a vocabulary this server defines rather than a
|
|
63
|
+
// format a client extends.
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* Arguments for `evo_list`: an optional status filter, forwarded unchanged to
|
|
@@ -125,23 +126,13 @@ export const evoToolRegistry = e => [
|
|
|
125
126
|
// A cache slot the runner cannot reach is a tool-level error, not a
|
|
126
127
|
// panic: the client asked a question the server could not answer, and
|
|
127
128
|
// saying so is an ordinary `isError` result.
|
|
128
|
-
(
|
|
129
|
-
e.list(archived),
|
|
130
|
-
r => pureOk(r[0] === 'error'
|
|
131
|
-
? errorResult(evoSummary(r[1]))
|
|
132
|
-
: okResult(toJson(r[1])))
|
|
133
|
-
)),
|
|
129
|
+
({ archived }) => toolResultStep(e.list(archived), toJson, evoSummary),
|
|
134
130
|
),
|
|
135
131
|
toolEntry(
|
|
136
132
|
'evo_head',
|
|
137
133
|
'List the current head hashes (cBase32) of a subject, one per line. Empty when the subject is unknown.',
|
|
138
134
|
evoHeadArgs,
|
|
139
|
-
({ subject }) =>
|
|
140
|
-
e.head(subject),
|
|
141
|
-
r => pureOk(r[0] === 'error'
|
|
142
|
-
? errorResult(evoSummary(r[1]))
|
|
143
|
-
: okResult(r[1].join('\n'))),
|
|
144
|
-
),
|
|
135
|
+
({ subject }) => toolResultStep(e.head(subject), hs => hs.join('\n'), evoSummary),
|
|
145
136
|
),
|
|
146
137
|
toolEntry(
|
|
147
138
|
'evo_revision',
|
|
@@ -152,10 +143,7 @@ export const evoToolRegistry = e => [
|
|
|
152
143
|
// the transport's `-32603`, not a tool-level error — see "Result size"
|
|
153
144
|
// in the module doc.
|
|
154
145
|
|
|
155
|
-
(
|
|
156
|
-
e.revision(hash),
|
|
157
|
-
r => pureOk(r[0] === 'error' ? errorResult(evoSummary(r[1])) : okResult(toJson(r[1])))
|
|
158
|
-
)),
|
|
146
|
+
({ hash }) => toolResultStep(e.revision(hash), toJson, evoSummary),
|
|
159
147
|
),
|
|
160
148
|
toolEntry(
|
|
161
149
|
'evo_add',
|
|
@@ -166,9 +154,6 @@ export const evoToolRegistry = e => [
|
|
|
166
154
|
// it can read. They stay distinguishable by tag — `evoSummary` is the
|
|
167
155
|
// renderer that switches on it — so telling them apart never needed
|
|
168
156
|
// them to arrive in separate layers.
|
|
169
|
-
input =>
|
|
170
|
-
e.add(input),
|
|
171
|
-
r => pureOk(r[0] === 'error' ? errorResult(evoSummary(r[1])) : okResult(r[1]))
|
|
172
|
-
),
|
|
157
|
+
input => toolResultStep(e.add(input), identity, evoSummary),
|
|
173
158
|
),
|
|
174
159
|
]
|
package/fjs/mcp/module.f.d.mts
CHANGED
|
@@ -50,7 +50,9 @@ import type { Key } from '../effects/memory/types.ts';
|
|
|
50
50
|
export declare const casMcpHandlers: (home: string) => (cacheKey: Key<Cache>) => McpHandlers<FileCasOperation | MemOp>;
|
|
51
51
|
/**
|
|
52
52
|
* Static MCP configuration for the CAS server: advertises the `tools`
|
|
53
|
-
* capability, identifies the server, and
|
|
53
|
+
* capability, identifies the server, and lists the protocol revisions it
|
|
54
|
+
* speaks — one, so every client is answered with `2024-11-05`, whether that is
|
|
55
|
+
* an echo or a counter-proposal.
|
|
54
56
|
* @type {McpConfig}
|
|
55
57
|
*/
|
|
56
58
|
export declare const casConfig: McpConfig;
|
package/fjs/mcp/module.f.mjs
CHANGED
|
@@ -62,13 +62,15 @@ export const casMcpHandlers = home => cacheKey =>
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Static MCP configuration for the CAS server: advertises the `tools`
|
|
65
|
-
* capability, identifies the server, and
|
|
65
|
+
* capability, identifies the server, and lists the protocol revisions it
|
|
66
|
+
* speaks — one, so every client is answered with `2024-11-05`, whether that is
|
|
67
|
+
* an echo or a counter-proposal.
|
|
66
68
|
* @type {McpConfig}
|
|
67
69
|
*/
|
|
68
70
|
export const casConfig = {
|
|
69
71
|
serverInfo: { name: 'functionalscript-cas', version: '0.30.0' },
|
|
70
72
|
capabilities: { tools: {} },
|
|
71
|
-
|
|
73
|
+
protocolVersions: ['2024-11-05'],
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
// ── Server ──────────────────────────────────────────────────────────────────────
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
*
|
|
13
13
|
* The TypeScript counterparts live in the sibling
|
|
14
14
|
* [`../types.ts`](../types.ts), which pins them against these schemas with
|
|
15
|
-
* `Assert<
|
|
15
|
+
* `Assert<Check<Unknown, typeof unknown>>`.
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
19
|
/** rtti schema matching any JSON primitive: `null`, `boolean`, `number`, or `string`. */
|
|
20
|
-
export declare const primitive: import("../../../types/rtti/types.ts").Or<[null, import("../../../types/rtti/types.ts")._Type0<"boolean">, import("../../../types/rtti/types.ts")._Type0<"number">, import("../../../types/rtti/types.ts")._Type0<"string">]>;
|
|
20
|
+
export declare const primitive: import("../../../types/rtti/types.ts").Or<readonly [null, import("../../../types/rtti/types.ts")._Type0<"boolean">, import("../../../types/rtti/types.ts")._Type0<"number">, import("../../../types/rtti/types.ts")._Type0<"string">]>;
|
|
21
21
|
/**
|
|
22
22
|
* rtti schema matching any JSON value: a primitive, an array of JSON values,
|
|
23
23
|
* or an object whose values are JSON values. Self-referential via a thunk;
|
|
@@ -19,13 +19,15 @@
|
|
|
19
19
|
*
|
|
20
20
|
* @import { Type as RttiType } from '../../../types/rtti/types.ts'
|
|
21
21
|
* @import { ArraySet, Data, KindSet, Node, ObjectSet, RuleSet, UnionSet } from '../../../types/rtti/data/types.ts'
|
|
22
|
-
* @import { Ts } from '../../../types/rtti/ts/types.ts'
|
|
22
|
+
* @import { Ts, Check } from '../../../types/rtti/ts/types.ts'
|
|
23
23
|
* @import { Phantom } from '../../../types/phantom/types.ts'
|
|
24
|
+
* @import { Assert } from '../../../asserts/types.ts'
|
|
24
25
|
*/
|
|
25
26
|
import type { Type as RttiType } from '../../../types/rtti/types.ts';
|
|
26
27
|
import type { Data } from '../../../types/rtti/data/types.ts';
|
|
27
|
-
import type { Ts } from '../../../types/rtti/ts/types.ts';
|
|
28
|
+
import type { Ts, Check } from '../../../types/rtti/ts/types.ts';
|
|
28
29
|
import type { Phantom } from '../../../types/phantom/types.ts';
|
|
30
|
+
import type { Assert } from '../../../asserts/types.ts';
|
|
29
31
|
/** @type {() => readonly ['const', typeof unknownConst]} */
|
|
30
32
|
declare const unknownThunk: () => readonly ['const', typeof unknownConst];
|
|
31
33
|
/**
|
|
@@ -33,18 +35,26 @@ declare const unknownThunk: () => readonly ['const', typeof unknownConst];
|
|
|
33
35
|
* @type {Phantom<typeof unknownThunk, _UnknownConst>}
|
|
34
36
|
*/
|
|
35
37
|
export declare const unknown: Phantom<typeof unknownThunk, _UnknownConst>;
|
|
38
|
+
export type _UnknownCheck0 = Assert<Check<_UnknownConst, typeof unknownThunk>>;
|
|
39
|
+
export type _UnknownCheck1 = Assert<Check<_UnknownConst, typeof unknown>>;
|
|
36
40
|
export type Unknown = Ts<typeof unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* Checked against the un-annotated thunk, so a wrong `_UnknownConst` above
|
|
43
|
+
* would be caught here instead of silently trusted via the `Phantom` lie.
|
|
44
|
+
* @typedef {Assert<Check<_UnknownConst, typeof unknownThunk>>} _UnknownCheck0
|
|
45
|
+
*/
|
|
46
|
+
/** @typedef {Assert<Check<_UnknownConst, typeof unknown>>} _UnknownCheck1 */
|
|
37
47
|
/** A JSON Schema (draft 2020-12) document — the subset of keywords that `toJsonSchema` emits. */
|
|
38
48
|
/** @typedef {Ts<typeof unknown>} Unknown */
|
|
39
49
|
declare const unknownConst: {
|
|
40
50
|
readonly $schema: import("../../../types/rtti/types.ts").Or<readonly [import("../../../types/rtti/types.ts")._Type0<"string">, undefined]>;
|
|
41
51
|
readonly $ref: import("../../../types/rtti/types.ts").Or<readonly [import("../../../types/rtti/types.ts")._Type0<"string">, undefined]>;
|
|
42
52
|
readonly $defs: import("../../../types/rtti/types.ts").Or<readonly [import("../../../types/rtti/types.ts").Type1<"record", Phantom<() => readonly ['const', typeof unknownConst], _UnknownConst>>, undefined]>;
|
|
43
|
-
readonly type: import("../../../types/rtti/types.ts").Or<["boolean", "number", "string", "integer", "array", "object", undefined]>;
|
|
53
|
+
readonly type: import("../../../types/rtti/types.ts").Or<readonly ["boolean", "number", "string", "integer", "array", "object", undefined]>;
|
|
44
54
|
readonly const: import("../../../types/rtti/types.ts").Or<readonly [() => readonly ['or', typeof import("../rtti/module.f.mjs").primitive, typeof import("../rtti/module.f.mjs").object, typeof import("../rtti/module.f.mjs").array], undefined]>;
|
|
45
55
|
readonly not: import("../../../types/rtti/types.ts").Or<readonly [Phantom<() => readonly ['const', typeof unknownConst], _UnknownConst>, undefined]>;
|
|
46
56
|
readonly anyOf: import("../../../types/rtti/types.ts").Or<readonly [import("../../../types/rtti/types.ts").Type1<"array", Phantom<() => readonly ['const', typeof unknownConst], _UnknownConst>>, undefined]>;
|
|
47
|
-
readonly items: import("../../../types/rtti/types.ts").Or<[Phantom<() => readonly ['const', typeof unknownConst], _UnknownConst>, false, undefined]>;
|
|
57
|
+
readonly items: import("../../../types/rtti/types.ts").Or<readonly [Phantom<() => readonly ['const', typeof unknownConst], _UnknownConst>, false, undefined]>;
|
|
48
58
|
readonly prefixItems: import("../../../types/rtti/types.ts").Or<readonly [import("../../../types/rtti/types.ts").Type1<"array", Phantom<() => readonly ['const', typeof unknownConst], _UnknownConst>>, undefined]>;
|
|
49
59
|
readonly minItems: import("../../../types/rtti/types.ts").Or<readonly [import("../../../types/rtti/types.ts")._Type0<"number">, undefined]>;
|
|
50
60
|
readonly properties: import("../../../types/rtti/types.ts").Or<readonly [import("../../../types/rtti/types.ts").Type1<"record", Phantom<() => readonly ['const', typeof unknownConst], _UnknownConst>>, undefined]>;
|
|
@@ -95,7 +105,7 @@ export declare const dataToJsonSchema: (data: Data) => Unknown;
|
|
|
95
105
|
* | `bigint` const | `{ "const": Number(value) }` (lossy for \|value\| > MAX_SAFE_INTEGER) |
|
|
96
106
|
* | `undefined` const | `{ "not": {} }` |
|
|
97
107
|
* | struct `{ a: T, … }` | `{ "type": "object", "properties": { "a": …T… }, "required": [non-optional keys] }` |
|
|
98
|
-
* | tuple `[A, B]` | `{ "type": "array", "prefixItems": […A…, …B…], "minItems": 2, "items":
|
|
108
|
+
* | tuple `[A, B]` | `{ "type": "array", "prefixItems": […A…, …B…], "minItems": 2, "items": {} }` |
|
|
99
109
|
* | `array(T)` | `{ "type": "array", "items": …T… }` |
|
|
100
110
|
* | `record(T)` | `{ "type": "object", "additionalProperties": …T… }` |
|
|
101
111
|
* | `or(...types)` | `{ "anyOf": […each…] }`, normalized and in canonical kind order |
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
*
|
|
20
20
|
* @import { Type as RttiType } from '../../../types/rtti/types.ts'
|
|
21
21
|
* @import { ArraySet, Data, KindSet, Node, ObjectSet, RuleSet, UnionSet } from '../../../types/rtti/data/types.ts'
|
|
22
|
-
* @import { Ts } from '../../../types/rtti/ts/types.ts'
|
|
22
|
+
* @import { Ts, Check } from '../../../types/rtti/ts/types.ts'
|
|
23
23
|
* @import { Phantom } from '../../../types/phantom/types.ts'
|
|
24
|
+
* @import { Assert } from '../../../asserts/types.ts'
|
|
24
25
|
*/
|
|
25
26
|
|
|
26
27
|
import { assert, assertNotNullish } from '../../../asserts/module.f.mjs'
|
|
@@ -38,6 +39,13 @@ const unknownThunk = () => ['const', unknownConst]
|
|
|
38
39
|
*/
|
|
39
40
|
export const unknown = unknownThunk
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Checked against the un-annotated thunk, so a wrong `_UnknownConst` above
|
|
44
|
+
* would be caught here instead of silently trusted via the `Phantom` lie.
|
|
45
|
+
* @typedef {Assert<Check<_UnknownConst, typeof unknownThunk>>} _UnknownCheck0
|
|
46
|
+
*/
|
|
47
|
+
/** @typedef {Assert<Check<_UnknownConst, typeof unknown>>} _UnknownCheck1 */
|
|
48
|
+
|
|
41
49
|
/** A JSON Schema (draft 2020-12) document — the subset of keywords that `toJsonSchema` emits. */
|
|
42
50
|
/** @typedef {Ts<typeof unknown>} Unknown */
|
|
43
51
|
|
|
@@ -165,22 +173,41 @@ const unitSchemas = bits => [
|
|
|
165
173
|
]
|
|
166
174
|
|
|
167
175
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
176
|
+
* The length below which the array would leave a declared position that
|
|
177
|
+
* excludes `undefined` unfilled: one past the last such position, and zero
|
|
178
|
+
* when every position admits absence. The array counterpart of the `required`
|
|
179
|
+
* key list — an absent element reads as `undefined` just as an absent key
|
|
180
|
+
* does — and, arrays being contiguous, one number says it for every position.
|
|
181
|
+
*
|
|
182
|
+
* @type {(rules: RuleSet) => (prefix: readonly Node[]) => number}
|
|
183
|
+
*/
|
|
184
|
+
const minLength = rules => prefix =>
|
|
185
|
+
prefix.findLastIndex(n => !admitsUndefined(rules)(n)) + 1
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* A set of arrays: `prefixItems` for the declared positions, `items` for what
|
|
189
|
+
* may follow — `false` when nothing may, which is what makes the exact-length
|
|
190
|
+
* pattern exact. `prefixItems` alone constrains only elements that exist
|
|
191
|
+
* (draft 2020-12 implies no minimum length), so the required length is
|
|
192
|
+
* `minItems`, and a position past it — one the array may simply end before —
|
|
193
|
+
* has `undefined` stripped from its schema, absence being expressed by
|
|
194
|
+
* `minItems` already. Both are the object side's `required` /
|
|
195
|
+
* {@link stripUndefined} pair, one kind over.
|
|
173
196
|
*
|
|
174
197
|
* @type {(rules: RuleSet) => (p: ArraySet) => Unknown}
|
|
175
198
|
*/
|
|
176
|
-
const arraySetSchema = rules => p =>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
})
|
|
199
|
+
const arraySetSchema = rules => p => {
|
|
200
|
+
const minItems = minLength(rules)(p.prefix)
|
|
201
|
+
return {
|
|
202
|
+
type: 'array',
|
|
203
|
+
...(p.prefix.length === 0 ? {} : {
|
|
204
|
+
prefixItems: p.prefix.map(
|
|
205
|
+
(n, i) => nodeSchema(rules)(i < minItems ? n : stripUndefined(n))),
|
|
206
|
+
}),
|
|
207
|
+
...(minItems === 0 ? {} : { minItems }),
|
|
208
|
+
items: p.rest === undefined ? false : nodeSchema(rules)(p.rest),
|
|
209
|
+
}
|
|
210
|
+
}
|
|
184
211
|
|
|
185
212
|
/** Whether the node's value set admits `undefined` — its unit bit, read
|
|
186
213
|
* through a reference if needed.
|
|
@@ -282,7 +309,7 @@ export const dataToJsonSchema = ([rules, entry]) => {
|
|
|
282
309
|
* | `bigint` const | `{ "const": Number(value) }` (lossy for \|value\| > MAX_SAFE_INTEGER) |
|
|
283
310
|
* | `undefined` const | `{ "not": {} }` |
|
|
284
311
|
* | struct `{ a: T, … }` | `{ "type": "object", "properties": { "a": …T… }, "required": [non-optional keys] }` |
|
|
285
|
-
* | tuple `[A, B]` | `{ "type": "array", "prefixItems": […A…, …B…], "minItems": 2, "items":
|
|
312
|
+
* | tuple `[A, B]` | `{ "type": "array", "prefixItems": […A…, …B…], "minItems": 2, "items": {} }` |
|
|
286
313
|
* | `array(T)` | `{ "type": "array", "items": …T… }` |
|
|
287
314
|
* | `record(T)` | `{ "type": "object", "additionalProperties": …T… }` |
|
|
288
315
|
* | `or(...types)` | `{ "anyOf": […each…] }`, normalized and in canonical kind order |
|
|
@@ -28,12 +28,19 @@ export declare const proof: {
|
|
|
28
28
|
array: () => void;
|
|
29
29
|
record: () => void;
|
|
30
30
|
or: () => void;
|
|
31
|
-
tuple:
|
|
31
|
+
tuple: {
|
|
32
|
+
allRequired: () => void;
|
|
33
|
+
withOptional: () => void;
|
|
34
|
+
allOptional: () => void;
|
|
35
|
+
open: () => void;
|
|
36
|
+
};
|
|
32
37
|
struct: {
|
|
33
38
|
allRequired: () => void;
|
|
34
39
|
withOptional: () => void;
|
|
35
40
|
allOptional: () => void;
|
|
36
41
|
empty: () => void;
|
|
42
|
+
openEmpty: () => void;
|
|
43
|
+
open: () => void;
|
|
37
44
|
orOptional: () => void;
|
|
38
45
|
withConst: () => void;
|
|
39
46
|
optionalOfEveryKind: () => void;
|
|
@@ -52,7 +59,9 @@ export declare const proof: {
|
|
|
52
59
|
duplicateLiteral: () => void;
|
|
53
60
|
never: () => void;
|
|
54
61
|
emptyTuple: () => void;
|
|
62
|
+
openEmptyTuple: () => void;
|
|
55
63
|
coverageCollapse: () => void;
|
|
64
|
+
noCollapseWhenClosed: () => void;
|
|
56
65
|
commutative: () => void;
|
|
57
66
|
};
|
|
58
67
|
recursion: {
|