functionalscript 0.38.0 → 0.40.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/README.md +1 -1
- package/fjs/bnf/descent/module.f.d.ts +43 -2
- package/fjs/bnf/descent/module.f.js +37 -12
- package/fjs/bnf/descent/proof.f.d.ts +1 -0
- package/fjs/bnf/descent/proof.f.js +73 -32
- package/fjs/bnf/ll1/module.f.js +2 -2
- package/fjs/bnf/token_symbol/module.f.d.ts +38 -0
- package/fjs/bnf/token_symbol/module.f.js +48 -0
- package/fjs/bnf/token_symbol/proof.f.d.ts +10 -0
- package/fjs/bnf/token_symbol/proof.f.js +37 -0
- package/fjs/cas/cli/module.f.js +6 -9
- package/fjs/cas/evo/module.f.d.ts +77 -13
- package/fjs/cas/evo/module.f.js +130 -18
- package/fjs/cas/evo/proof.f.d.ts +7 -0
- package/fjs/cas/evo/proof.f.js +120 -1
- package/fjs/cas/module.f.js +64 -37
- package/fjs/cas/proof.f.d.ts +3 -9
- package/fjs/cas/proof.f.js +94 -74
- package/fjs/ci/bun/module.f.js +3 -3
- package/fjs/ci/common/module.f.d.ts +1 -2
- package/fjs/ci/common/module.f.js +1 -5
- package/fjs/ci/config/module.f.d.ts +11 -8
- package/fjs/ci/config/module.f.js +24 -9
- package/fjs/ci/deno/module.f.js +3 -3
- package/fjs/ci/module.f.js +13 -7
- package/fjs/ci/nix/module.f.d.ts +61 -0
- package/fjs/ci/nix/module.f.js +92 -0
- package/fjs/ci/nix/proof.f.d.ts +23 -0
- package/fjs/ci/nix/proof.f.js +109 -0
- package/fjs/ci/node/module.f.d.ts +23 -1
- package/fjs/ci/node/module.f.js +58 -12
- package/fjs/ci/proof.f.d.ts +2 -0
- package/fjs/ci/proof.f.js +49 -9
- package/fjs/cli/module.f.js +2 -2
- package/fjs/common/monoid/module.f.d.ts +46 -2
- package/fjs/common/monoid/module.f.js +44 -0
- package/fjs/common/monoid/proof.f.d.ts +5 -0
- package/fjs/common/monoid/proof.f.js +27 -1
- package/fjs/dev/module.f.d.ts +1 -0
- package/fjs/dev/module.f.js +38 -27
- package/fjs/dev/update/module.f.d.ts +11 -0
- package/fjs/dev/update/module.f.js +20 -0
- package/fjs/dev/update/proof.f.d.ts +7 -0
- package/fjs/dev/update/proof.f.js +35 -0
- package/fjs/djs/ast/module.f.d.ts +47 -0
- package/fjs/djs/ast/module.f.js +9 -0
- package/fjs/djs/module.f.js +5 -8
- package/fjs/djs/parser/module.f.d.ts +3 -0
- package/fjs/djs/parser/module.f.js +17 -0
- package/fjs/djs/tokenizer/module.f.js +2 -2
- package/fjs/djs/tokenizer/proof.f.d.ts +1 -0
- package/fjs/djs/tokenizer/proof.f.js +70 -16
- package/fjs/djs/transpiler/module.f.js +6 -6
- package/fjs/effects/eff/module.f.d.ts +72 -0
- package/fjs/effects/eff/module.f.js +46 -0
- package/fjs/effects/eff/proof.f.d.ts +11 -0
- package/fjs/effects/eff/proof.f.js +58 -0
- package/fjs/effects/list/module.f.d.ts +11 -5
- package/fjs/effects/list/module.f.js +2 -1
- package/fjs/effects/memory/proof.f.js +7 -8
- package/fjs/effects/module.f.d.ts +334 -52
- package/fjs/effects/module.f.js +253 -45
- package/fjs/effects/node/memory/proof.js +3 -2
- package/fjs/effects/node/module.d.ts +3 -3
- package/fjs/effects/node/module.f.d.ts +18 -8
- package/fjs/effects/node/module.f.js +31 -11
- package/fjs/effects/node/module.js +12 -12
- package/fjs/effects/node/proof.f.d.ts +1 -0
- package/fjs/effects/node/proof.f.js +32 -11
- package/fjs/effects/node/virtual/module.f.js +1 -1
- package/fjs/effects/proof.f.d.ts +32 -6
- package/fjs/effects/proof.f.js +131 -39
- package/fjs/emergent_testing/all.test.js +2 -1
- package/fjs/emergent_testing/module.f.d.ts +3 -3
- package/fjs/emergent_testing/module.f.js +63 -49
- package/fjs/emergent_testing/proof.f.d.ts +4 -0
- package/fjs/emergent_testing/proof.f.js +54 -21
- package/fjs/emergent_testing/scenarios/thenable.pass.js +1 -1
- package/fjs/fsc/module.f.js +4 -4
- package/fjs/fsm/module.f.js +1 -1
- package/fjs/js/tokenizer/module.f.d.ts +1 -0
- package/fjs/js/tokenizer/module.f.js +13 -6
- package/fjs/{cas/mcp → mcp/cas}/module.f.d.ts +3 -25
- package/fjs/{cas/mcp → mcp/cas}/module.f.js +26 -64
- package/fjs/mcp/evo/module.f.d.ts +27 -0
- package/fjs/mcp/evo/module.f.js +94 -0
- package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.d.ts +2 -0
- package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.js +40 -9
- package/fjs/mcp/module.f.d.ts +54 -237
- package/fjs/mcp/module.f.js +55 -258
- package/fjs/mcp/proof.f.d.ts +43 -32
- package/fjs/mcp/proof.f.js +509 -200
- package/fjs/media/nix/module.f.d.ts +30 -0
- package/fjs/media/nix/module.f.js +166 -0
- package/fjs/media/nix/proof.f.d.ts +32 -0
- package/fjs/media/nix/proof.f.js +127 -0
- package/fjs/media/type/module.f.js +2 -2
- package/fjs/media/type/proof.f.js +7 -10
- package/fjs/module.f.js +5 -9
- package/fjs/protocol/json_rpc/module.f.d.ts +114 -0
- package/fjs/{media/json/rpc → protocol/json_rpc}/module.f.js +3 -3
- package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.js +3 -3
- package/fjs/protocol/mcp/module.f.d.ts +239 -0
- package/fjs/protocol/mcp/module.f.js +272 -0
- package/fjs/protocol/mcp/proof.f.d.ts +34 -0
- package/fjs/protocol/mcp/proof.f.js +208 -0
- package/fjs/{mcp → protocol/mcp}/stdio/module.f.d.ts +7 -7
- package/fjs/{mcp → protocol/mcp}/stdio/module.f.js +22 -22
- package/fjs/{mcp → protocol/mcp}/stdio/proof.f.js +9 -9
- package/fjs/types/bigint/module.f.js +4 -3
- package/fjs/types/bit_vec/module.f.js +5 -15
- package/fjs/types/btree/remove/module.f.d.ts +5 -0
- package/fjs/types/btree/remove/module.f.js +12 -0
- package/fjs/types/list/module.f.d.ts +21 -0
- package/fjs/types/list/module.f.js +17 -0
- package/fjs/types/list/proof.f.d.ts +1 -0
- package/fjs/types/list/proof.f.js +24 -1
- package/fjs/types/number/module.f.js +2 -1
- package/fjs/types/range_map/module.f.d.ts +14 -13
- package/fjs/types/range_map/module.f.js +18 -13
- package/fjs/types/range_map/proof.f.js +26 -39
- package/fjs/types/range_set/module.f.d.ts +5 -0
- package/fjs/types/range_set/module.f.js +16 -0
- package/fjs/types/range_set/proof.f.d.ts +1 -0
- package/fjs/types/range_set/proof.f.js +19 -0
- package/fjs/types/string/module.f.js +10 -2
- package/fjs/website/module.f.js +2 -3
- package/package.json +5 -5
- package/fjs/cas/evo/mcp/module.f.d.ts +0 -19
- package/fjs/cas/evo/mcp/module.f.js +0 -56
- package/fjs/cas/mcp/proof.f.d.ts +0 -45
- package/fjs/cas/mcp/proof.f.js +0 -545
- package/fjs/ci/playwright/module.f.d.ts +0 -2
- package/fjs/ci/playwright/module.f.js +0 -25
- package/fjs/media/json/rpc/module.f.d.ts +0 -114
- /package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.d.ts +0 -0
- /package/fjs/{mcp → protocol/mcp}/stdio/proof.f.d.ts +0 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { assert, assertEq } from '../../asserts/module.f.js';
|
|
2
|
+
import { pure, step } from '../../effects/module.f.js';
|
|
3
|
+
import { eff } from '../../effects/eff/module.f.js';
|
|
4
|
+
import { run } from '../../effects/mock/module.f.js';
|
|
5
|
+
import { asBase, asNominal, create, read } from '../../effects/memory/module.f.js';
|
|
6
|
+
import { uninitializedState, mcpStep, notInitialized, } from './module.f.js';
|
|
7
|
+
const initial = { next: 0, values: {} };
|
|
8
|
+
const mock = {
|
|
9
|
+
memCreate: value => state => {
|
|
10
|
+
const id = `k${state.next}`;
|
|
11
|
+
const key = asNominal(id);
|
|
12
|
+
return [{ next: state.next + 1, values: { ...state.values, [id]: value } }, key];
|
|
13
|
+
},
|
|
14
|
+
memRead: key => state => [state, state.values[asBase(key)]],
|
|
15
|
+
memWrite: (key, value) => state => {
|
|
16
|
+
const id = asBase(key);
|
|
17
|
+
return [{ ...state, values: { ...state.values, [id]: value } }, undefined];
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
// ── Fixtures ──────────────────────────────────────────────────────────────────
|
|
21
|
+
const config = {
|
|
22
|
+
serverInfo: { name: 'test-server', version: '0.1.0' },
|
|
23
|
+
capabilities: { tools: {} },
|
|
24
|
+
protocolVersion: '2024-11-05',
|
|
25
|
+
};
|
|
26
|
+
const configNoTools = { ...config, capabilities: {} };
|
|
27
|
+
const handlers = {
|
|
28
|
+
// Echoes a received cursor as `nextCursor` so tests can observe pagination params.
|
|
29
|
+
toolsList: (p) => pure(p.cursor === undefined
|
|
30
|
+
? { tools: [{ name: 'greet', inputSchema: {} }] }
|
|
31
|
+
: { tools: [], nextCursor: p.cursor }),
|
|
32
|
+
toolsCall: (_p) => pure({ content: [{ type: 'text', text: 'hello' }] }),
|
|
33
|
+
};
|
|
34
|
+
// Run a memory effect against the mock, return the result.
|
|
35
|
+
const runMem = (effect) => run(mock)(initial)(effect)[1];
|
|
36
|
+
// TypeScript infers O = Operation (the upper bound) rather than O = never when
|
|
37
|
+
// O flows through McpHandlers<never>, so we cast the widened type down to MemOp.
|
|
38
|
+
const asMemEffect = (e) => e;
|
|
39
|
+
// Run one step from uninitializedState, return [response, newState].
|
|
40
|
+
const step1 = (cfg) => (msg) => runMem(asMemEffect(step(create(uninitializedState), key => step(mcpStep(cfg)(handlers)(key)(msg), resp => step(read(key), state => pure([resp, state]))))));
|
|
41
|
+
// Run initialize then a second step, return [response, newState] of the second.
|
|
42
|
+
const step2 = (cfg) => (msg1) => (msg2) => runMem(asMemEffect(eff(create(uninitializedState)).step(key => eff(mcpStep(cfg)(handlers)(key)(msg1)).step(() => eff(mcpStep(cfg)(handlers)(key)(msg2)).step(resp => step(read(key), state => pure([resp, state]))).value).value).value));
|
|
43
|
+
// Run initialize, notifications/initialized, then a third step; return [response, newState] of the third.
|
|
44
|
+
const step3 = (cfg) => (msg1) => (msg2) => (msg3) => runMem(asMemEffect(eff(create(uninitializedState)).step(key => eff(mcpStep(cfg)(handlers)(key)(msg1)).step(() => eff(mcpStep(cfg)(handlers)(key)(msg2)).step(() => eff(mcpStep(cfg)(handlers)(key)(msg3)).step(resp => eff(read(key)).step(state => pure([resp, state])).value).value).value).value).value));
|
|
45
|
+
// ── Test messages ─────────────────────────────────────────────────────────────
|
|
46
|
+
const initMsg = { jsonrpc: '2.0', method: 'initialize', id: 1,
|
|
47
|
+
params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'client', version: '0.0.1' } } };
|
|
48
|
+
const initNotif = { jsonrpc: '2.0', method: 'notifications/initialized' };
|
|
49
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
50
|
+
export const proof = {
|
|
51
|
+
lifecycle: {
|
|
52
|
+
initialStateIsUninitialized: () => {
|
|
53
|
+
assert(uninitializedState[0] === 'uninitialized');
|
|
54
|
+
},
|
|
55
|
+
initializeTransitionsToInitializing: () => {
|
|
56
|
+
const [, newState] = step1(config)(initMsg);
|
|
57
|
+
assert(newState[0] === 'initializing');
|
|
58
|
+
},
|
|
59
|
+
initializedNotificationTransitionsToInitialized: () => {
|
|
60
|
+
const [resp, newState] = step2(config)(initMsg)(initNotif);
|
|
61
|
+
assertEq(resp, null);
|
|
62
|
+
assert(newState[0] === 'initialized');
|
|
63
|
+
},
|
|
64
|
+
initializeReturnsResult: () => {
|
|
65
|
+
const [resp] = step1(config)(initMsg);
|
|
66
|
+
assert(resp !== null && typeof resp === 'object' && 'result' in resp);
|
|
67
|
+
const r = resp.result;
|
|
68
|
+
assertEq(r.protocolVersion, '2024-11-05');
|
|
69
|
+
},
|
|
70
|
+
initializeWithBadParamsReturnsInvalidParams: () => {
|
|
71
|
+
const bad = { jsonrpc: '2.0', method: 'initialize', id: 2, params: { wrong: true } };
|
|
72
|
+
const [resp, newState] = step1(config)(bad);
|
|
73
|
+
assert(newState[0] === 'uninitialized');
|
|
74
|
+
assertEq(resp.error.code, -32602);
|
|
75
|
+
},
|
|
76
|
+
notificationBeforeInitReturnNull: () => {
|
|
77
|
+
const [resp, newState] = step1(config)(initNotif);
|
|
78
|
+
assert(newState[0] === 'uninitialized');
|
|
79
|
+
assertEq(resp, null);
|
|
80
|
+
},
|
|
81
|
+
unknownNotificationReturnNull: () => {
|
|
82
|
+
const notif = { jsonrpc: '2.0', method: 'notifications/unknown' };
|
|
83
|
+
const [resp] = step2(config)(initMsg)(notif);
|
|
84
|
+
assertEq(resp, null);
|
|
85
|
+
},
|
|
86
|
+
doubleInitializeReturnsInvalidRequest: () => {
|
|
87
|
+
const [resp, newState] = step2(config)(initMsg)(initMsg);
|
|
88
|
+
assert(newState[0] === 'initializing');
|
|
89
|
+
assertEq(resp.error.code, -32600);
|
|
90
|
+
},
|
|
91
|
+
pingBeforeInitSucceeds: () => {
|
|
92
|
+
const msg = { jsonrpc: '2.0', method: 'ping', id: 11 };
|
|
93
|
+
const [resp, newState] = step1(config)(msg);
|
|
94
|
+
assert(newState[0] === 'uninitialized');
|
|
95
|
+
assert(!('error' in resp));
|
|
96
|
+
},
|
|
97
|
+
pingDuringInitializingSucceeds: () => {
|
|
98
|
+
const msg = { jsonrpc: '2.0', method: 'ping', id: 12 };
|
|
99
|
+
const [resp, newState] = step2(config)(initMsg)(msg);
|
|
100
|
+
assert(newState[0] === 'initializing');
|
|
101
|
+
assert(!('error' in resp));
|
|
102
|
+
},
|
|
103
|
+
pingAfterInitSucceeds: () => {
|
|
104
|
+
const msg = { jsonrpc: '2.0', method: 'ping', id: 15 };
|
|
105
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
106
|
+
assert(!('error' in resp));
|
|
107
|
+
},
|
|
108
|
+
pingWithObjectParamsSucceeds: () => {
|
|
109
|
+
const msg = { jsonrpc: '2.0', method: 'ping', id: 19, params: {} };
|
|
110
|
+
const [resp] = step1(config)(msg);
|
|
111
|
+
assert(!('error' in resp));
|
|
112
|
+
},
|
|
113
|
+
pingInvalidParamsReturnsInvalidParams: () => {
|
|
114
|
+
const msg = { jsonrpc: '2.0', method: 'ping', id: 20, params: 1 };
|
|
115
|
+
const [resp] = step1(config)(msg);
|
|
116
|
+
assertEq(resp.error.code, -32602);
|
|
117
|
+
},
|
|
118
|
+
initializedNotificationObjectParamsTransitions: () => {
|
|
119
|
+
const notif = { jsonrpc: '2.0', method: 'notifications/initialized', params: {} };
|
|
120
|
+
const [resp, newState] = step2(config)(initMsg)(notif);
|
|
121
|
+
assertEq(resp, null);
|
|
122
|
+
assert(newState[0] === 'initialized');
|
|
123
|
+
},
|
|
124
|
+
initializedNotificationBadParamsIgnored: () => {
|
|
125
|
+
const notif = { jsonrpc: '2.0', method: 'notifications/initialized', params: 1 };
|
|
126
|
+
const [resp, newState] = step2(config)(initMsg)(notif);
|
|
127
|
+
assertEq(resp, null);
|
|
128
|
+
assert(newState[0] === 'initializing');
|
|
129
|
+
},
|
|
130
|
+
methodBeforeInitReturnsNotInitialized: () => {
|
|
131
|
+
const msg = { jsonrpc: '2.0', method: 'tools/list', id: 3 };
|
|
132
|
+
const [resp, newState] = step1(config)(msg);
|
|
133
|
+
assert(newState[0] === 'uninitialized');
|
|
134
|
+
assertEq(resp.error.code, notInitialized.code);
|
|
135
|
+
},
|
|
136
|
+
methodDuringInitializingReturnsNotInitialized: () => {
|
|
137
|
+
const msg = { jsonrpc: '2.0', method: 'tools/list', id: 16 };
|
|
138
|
+
const [resp, newState] = step2(config)(initMsg)(msg);
|
|
139
|
+
assert(newState[0] === 'initializing');
|
|
140
|
+
assertEq(resp.error.code, notInitialized.code);
|
|
141
|
+
},
|
|
142
|
+
invalidEnvelopeReturnsInvalidRequest: () => {
|
|
143
|
+
const bad = { jsonrpc: '1.0', method: 'ping', id: 4 };
|
|
144
|
+
const [resp] = step1(config)(bad);
|
|
145
|
+
assertEq(resp.error.code, -32600);
|
|
146
|
+
assertEq(resp.id, null);
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
tools: {
|
|
150
|
+
toolsListSucceeds: () => {
|
|
151
|
+
const msg = { jsonrpc: '2.0', method: 'tools/list', id: 5 };
|
|
152
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
153
|
+
assertEq(resp.result.tools.length, 1);
|
|
154
|
+
assertEq(resp.result.tools[0].name, 'greet');
|
|
155
|
+
},
|
|
156
|
+
toolsListPassesCursorToHandler: () => {
|
|
157
|
+
const msg = { jsonrpc: '2.0', method: 'tools/list', id: 17,
|
|
158
|
+
params: { cursor: 'page-2' } };
|
|
159
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
160
|
+
assertEq(resp.result.nextCursor, 'page-2');
|
|
161
|
+
},
|
|
162
|
+
toolsListInvalidCursorReturnsInvalidParams: () => {
|
|
163
|
+
const msg = { jsonrpc: '2.0', method: 'tools/list', id: 18,
|
|
164
|
+
params: { cursor: 42 } };
|
|
165
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
166
|
+
assertEq(resp.error.code, -32602);
|
|
167
|
+
},
|
|
168
|
+
toolsCallSucceeds: () => {
|
|
169
|
+
const msg = { jsonrpc: '2.0', method: 'tools/call', id: 6,
|
|
170
|
+
params: { name: 'greet', arguments: {} } };
|
|
171
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
172
|
+
assertEq(resp.result.content[0].text, 'hello');
|
|
173
|
+
},
|
|
174
|
+
toolsCallBadParamsReturnsInvalidParams: () => {
|
|
175
|
+
const msg = { jsonrpc: '2.0', method: 'tools/call', id: 7, params: { missing: true } };
|
|
176
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
177
|
+
assertEq(resp.error.code, -32602);
|
|
178
|
+
},
|
|
179
|
+
toolsCallAbsentArgumentsSucceeds: () => {
|
|
180
|
+
const msg = { jsonrpc: '2.0', method: 'tools/call', id: 13,
|
|
181
|
+
params: { name: 'greet' } };
|
|
182
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
183
|
+
assertEq(resp.result.content[0].text, 'hello');
|
|
184
|
+
},
|
|
185
|
+
toolsCallNullArgumentsReturnsInvalidParams: () => {
|
|
186
|
+
const msg = { jsonrpc: '2.0', method: 'tools/call', id: 14,
|
|
187
|
+
params: { name: 'greet', arguments: null } };
|
|
188
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
189
|
+
assertEq(resp.error.code, -32602);
|
|
190
|
+
},
|
|
191
|
+
toolsListWithoutCapabilityReturnsMethodNotFound: () => {
|
|
192
|
+
const msg = { jsonrpc: '2.0', method: 'tools/list', id: 8 };
|
|
193
|
+
const [resp] = step3(configNoTools)(initMsg)(initNotif)(msg);
|
|
194
|
+
assertEq(resp.error.code, -32601);
|
|
195
|
+
},
|
|
196
|
+
toolsCallWithoutCapabilityReturnsMethodNotFound: () => {
|
|
197
|
+
const msg = { jsonrpc: '2.0', method: 'tools/call', id: 9,
|
|
198
|
+
params: { name: 'greet', arguments: {} } };
|
|
199
|
+
const [resp] = step3(configNoTools)(initMsg)(initNotif)(msg);
|
|
200
|
+
assertEq(resp.error.code, -32601);
|
|
201
|
+
},
|
|
202
|
+
unknownMethodReturnsMethodNotFound: () => {
|
|
203
|
+
const msg = { jsonrpc: '2.0', method: 'resources/list', id: 10 };
|
|
204
|
+
const [resp] = step3(config)(initMsg)(initNotif)(msg);
|
|
205
|
+
assertEq(resp.error.code, -32601);
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* stdio transport for JSON-RPC / MCP servers.
|
|
3
3
|
*
|
|
4
4
|
* `stdioTransport` wraps a step function — the `mcpStep`-shaped
|
|
5
|
-
* `(value) => Effect<O, Response | null>` from `fjs/mcp/module.f.ts` — in the
|
|
5
|
+
* `(value) => Effect<O, Response | null>` from `fjs/protocol/mcp/module.f.ts` — in the
|
|
6
6
|
* canonical read → parse → dispatch → write loop, expressed as a recursive
|
|
7
7
|
* effect so it stays in the pure effect model and is fully testable against a
|
|
8
8
|
* mock stdin / stdout (see `fjs/effects/node/virtual`) with no real process.
|
|
@@ -29,19 +29,19 @@
|
|
|
29
29
|
*
|
|
30
30
|
* @module
|
|
31
31
|
*/
|
|
32
|
-
import { type Effect, type Operation } from '
|
|
33
|
-
import { type Read, type Write } from '
|
|
34
|
-
import { type Unknown } from '
|
|
35
|
-
import { type Response } from '../../
|
|
32
|
+
import { type Effect, type Operation } from '../../../effects/module.f.ts';
|
|
33
|
+
import { type Read, type Write } from '../../../effects/node/module.f.ts';
|
|
34
|
+
import { type Unknown } from '../../../media/json/module.f.ts';
|
|
35
|
+
import { type Response } from '../../json_rpc/module.f.ts';
|
|
36
36
|
/**
|
|
37
37
|
* A transport step: maps one parsed JSON-RPC message to a response, or `null`
|
|
38
38
|
* for a notification that needs no reply. The shape of `mcpStep(config)(handlers)(key)`.
|
|
39
39
|
*/
|
|
40
40
|
export type Step<O extends Operation> = (value: Unknown) => Effect<O, Response | null>;
|
|
41
41
|
/**
|
|
42
|
-
* Drives the read-parse-dispatch-write loop for `
|
|
42
|
+
* Drives the read-parse-dispatch-write loop for `handler` over stdin/stdout.
|
|
43
43
|
*
|
|
44
44
|
* Recurses after each handled line; terminates (resolving to `void`) when
|
|
45
45
|
* `readLine` reports EOF.
|
|
46
46
|
*/
|
|
47
|
-
export declare const stdioTransport: <O extends Operation>(
|
|
47
|
+
export declare const stdioTransport: <O extends Operation>(handler: Step<O>) => Effect<Read | Write | O, void>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* stdio transport for JSON-RPC / MCP servers.
|
|
3
3
|
*
|
|
4
4
|
* `stdioTransport` wraps a step function — the `mcpStep`-shaped
|
|
5
|
-
* `(value) => Effect<O, Response | null>` from `fjs/mcp/module.f.ts` — in the
|
|
5
|
+
* `(value) => Effect<O, Response | null>` from `fjs/protocol/mcp/module.f.ts` — in the
|
|
6
6
|
* canonical read → parse → dispatch → write loop, expressed as a recursive
|
|
7
7
|
* effect so it stays in the pure effect model and is fully testable against a
|
|
8
8
|
* mock stdin / stdout (see `fjs/effects/node/virtual`) with no real process.
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
*
|
|
30
30
|
* @module
|
|
31
31
|
*/
|
|
32
|
-
import { pure } from '
|
|
33
|
-
import { readLine, write } from '
|
|
34
|
-
import { tryUtf8 } from '
|
|
35
|
-
import { stringToList } from '
|
|
36
|
-
import { stringify } from '
|
|
37
|
-
import { tokenize } from '
|
|
38
|
-
import { parse } from '
|
|
39
|
-
import { sort } from '
|
|
40
|
-
import { internalError, jsonrpc, parseError } from '../../
|
|
41
|
-
import { error, ok } from '
|
|
32
|
+
import { pure, step } from '../../../effects/module.f.js';
|
|
33
|
+
import { readLine, write } from '../../../effects/node/module.f.js';
|
|
34
|
+
import { tryUtf8 } from '../../../text/module.f.js';
|
|
35
|
+
import { stringToList } from '../../../text/utf16/module.f.js';
|
|
36
|
+
import { stringify } from '../../../media/json/module.f.js';
|
|
37
|
+
import { tokenize } from '../../../media/json/tokenizer/module.f.js';
|
|
38
|
+
import { parse } from '../../../media/json/parser/module.f.js';
|
|
39
|
+
import { sort } from '../../../types/object/module.f.js';
|
|
40
|
+
import { internalError, jsonrpc, parseError } from '../../json_rpc/module.f.js';
|
|
41
|
+
import { error, ok } from '../../../types/result/module.f.js';
|
|
42
42
|
const stringifyJson = stringify(sort);
|
|
43
43
|
/** The parse-error response (`-32700`, `id: null`) for a malformed input line. */
|
|
44
44
|
const parseErrorResponse = { jsonrpc, error: parseError, id: null };
|
|
@@ -49,24 +49,24 @@ const writeResponse = (resp) => {
|
|
|
49
49
|
const v = tryUtf8(stringifyJson(resp) + '\n');
|
|
50
50
|
return v === null
|
|
51
51
|
? pure(error(undefined))
|
|
52
|
-
: write('stdout', v)
|
|
52
|
+
: step(write('stdout', v), () => pure(ok(undefined)));
|
|
53
53
|
};
|
|
54
54
|
/**
|
|
55
|
-
* Drives the read-parse-dispatch-write loop for `
|
|
55
|
+
* Drives the read-parse-dispatch-write loop for `handler` over stdin/stdout.
|
|
56
56
|
*
|
|
57
57
|
* Recurses after each handled line; terminates (resolving to `void`) when
|
|
58
58
|
* `readLine` reports EOF.
|
|
59
59
|
*/
|
|
60
|
-
export const stdioTransport = (
|
|
60
|
+
export const stdioTransport = (handler) => step(readLine('stdin'), line => line === null
|
|
61
61
|
? pure(undefined)
|
|
62
|
-
: handleLine(
|
|
63
|
-
const handleLine = (
|
|
62
|
+
: handleLine(handler)(line));
|
|
63
|
+
const handleLine = (handler) => (line) => {
|
|
64
64
|
const [t, value] = parse(tokenize(stringToList(line)));
|
|
65
|
-
return (t === 'error'
|
|
65
|
+
return step(t === 'error'
|
|
66
66
|
? writeResponse(parseErrorResponse)
|
|
67
|
-
: step(value)
|
|
67
|
+
: step(handler(value), resp => resp === null
|
|
68
68
|
? pure(undefined)
|
|
69
|
-
: writeResponse(resp)
|
|
69
|
+
: step(writeResponse(resp), ([t2]) => t2 === 'error'
|
|
70
70
|
// The real response didn't fit. Retry with a fixed, small
|
|
71
71
|
// internal-error body carrying `resp.id` — but a
|
|
72
72
|
// caller-controlled `id` (e.g. a very large string) can
|
|
@@ -74,8 +74,8 @@ const handleLine = (step) => (line) => {
|
|
|
74
74
|
// that retry is bounded by one more: an `id: null`
|
|
75
75
|
// internal-error, whose fully-constant shape is the only
|
|
76
76
|
// line in this transport guaranteed to always encode.
|
|
77
|
-
? writeResponse(internalErrorResponse(resp.id))
|
|
78
|
-
? writeResponse(internalErrorResponse(null))
|
|
77
|
+
? step(writeResponse(internalErrorResponse(resp.id)), ([t3]) => t3 === 'error'
|
|
78
|
+
? step(writeResponse(internalErrorResponse(null)), () => pure(undefined))
|
|
79
79
|
: pure(undefined))
|
|
80
|
-
: pure(undefined)))
|
|
80
|
+
: pure(undefined))), () => stdioTransport(handler));
|
|
81
81
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { assertEq } from '
|
|
2
|
-
import { pure } from '
|
|
3
|
-
import { emptyState, virtual } from '
|
|
4
|
-
import { stringify } from '
|
|
5
|
-
import { utf8 } from '
|
|
6
|
-
import { fromVec } from '
|
|
7
|
-
import { maxLengthBytes } from '
|
|
8
|
-
import { sort } from '
|
|
9
|
-
import { internalError, jsonrpc, parseError } from '../../
|
|
1
|
+
import { assertEq } from '../../../asserts/module.f.js';
|
|
2
|
+
import { pure } from '../../../effects/module.f.js';
|
|
3
|
+
import { emptyState, virtual } from '../../../effects/node/virtual/module.f.js';
|
|
4
|
+
import { stringify } from '../../../media/json/module.f.js';
|
|
5
|
+
import { utf8 } from '../../../text/module.f.js';
|
|
6
|
+
import { fromVec } from '../../../types/uint8array/module.f.js';
|
|
7
|
+
import { maxLengthBytes } from '../../../types/bit_vec/module.f.js';
|
|
8
|
+
import { sort } from '../../../types/object/module.f.js';
|
|
9
|
+
import { internalError, jsonrpc, parseError } from '../../json_rpc/module.f.js';
|
|
10
10
|
import { stdioTransport } from './module.f.js';
|
|
11
11
|
const stringifyJson = stringify(sort);
|
|
12
12
|
// Extracts the request `id` (a request has one; a notification does not).
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
import { cmp } from '../function/compare/module.f.js';
|
|
20
|
-
import {
|
|
20
|
+
import {} from '../list/module.f.js';
|
|
21
|
+
import { fold } from '../../common/monoid/module.f.js';
|
|
21
22
|
/**
|
|
22
23
|
* Adds two `bigint` values.
|
|
23
24
|
*
|
|
@@ -31,7 +32,7 @@ export const addition = a => b => a + b;
|
|
|
31
32
|
* @param input - A list of bigint values.
|
|
32
33
|
* @returns The sum of all values in the list.
|
|
33
34
|
*/
|
|
34
|
-
export const sum =
|
|
35
|
+
export const sum = fold({ identity: 0n, operation: addition });
|
|
35
36
|
/**
|
|
36
37
|
* Multiplies two `bigint` values.
|
|
37
38
|
*
|
|
@@ -45,7 +46,7 @@ export const multiple = a => b => a * b;
|
|
|
45
46
|
* @param input - A list of bigint values.
|
|
46
47
|
* @returns The product of all values in the list.
|
|
47
48
|
*/
|
|
48
|
-
export const product =
|
|
49
|
+
export const product = fold({ identity: 1n, operation: multiple });
|
|
49
50
|
/**
|
|
50
51
|
* Calculates the absolute value of a `bigint`.
|
|
51
52
|
*
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { bitLength, divUp, mask, maxLength, xor } from '../bigint/module.f.js';
|
|
25
25
|
import { flip, identity } from '../function/module.f.js';
|
|
26
|
-
import {
|
|
26
|
+
import { map, tryFold } from '../list/module.f.js';
|
|
27
27
|
import { asBase, asNominal } from '../nominal/module.f.js';
|
|
28
28
|
import { repeat as mRepeat } from '../../common/monoid/module.f.js';
|
|
29
29
|
import { cmp, max, min } from '../function/compare/module.f.js';
|
|
@@ -174,24 +174,14 @@ const listToVecOp = (unpackConcat) => ({
|
|
|
174
174
|
* the final reduce prepends higher (earlier) slots in front of accumulated
|
|
175
175
|
* later runs. An empty list yields `unpackEmpty`.
|
|
176
176
|
*
|
|
177
|
+
* Returns `null` as soon as the accumulated length exceeds `maxLength`, which
|
|
178
|
+
* `tryFold` propagates by abandoning the rest of the list.
|
|
179
|
+
*
|
|
177
180
|
* This is the bit-vector analogue of a builder that accumulates appended pieces
|
|
178
181
|
* and materializes the combined result on demand, such as `StringBuilder`
|
|
179
182
|
* (Java, C#) or `strings.Builder` (Go).
|
|
180
183
|
*/
|
|
181
|
-
const unpackListToVec = (unpackConcat) =>
|
|
182
|
-
const { init, update, end } = listToVecOp(unpackConcat);
|
|
183
|
-
return (list) => {
|
|
184
|
-
let result = init;
|
|
185
|
-
for (const e of iterable(list)) {
|
|
186
|
-
const candidate = update(e, result);
|
|
187
|
-
if (candidate === null) {
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
result = candidate;
|
|
191
|
-
}
|
|
192
|
-
return end(result);
|
|
193
|
-
};
|
|
194
|
-
};
|
|
184
|
+
const unpackListToVec = (unpackConcat) => tryFold(listToVecOp(unpackConcat));
|
|
195
185
|
const bo = ({ front, removeFront, norm, uintCmp, unpackSplit, unpackConcatUint }) => {
|
|
196
186
|
const unpackPopFront = (len) => {
|
|
197
187
|
const m = mask(len);
|
|
@@ -7,3 +7,8 @@ import { type TNode, type Tree } from '../types/module.f.ts';
|
|
|
7
7
|
import type { Compare } from '../../function/compare/module.f.ts';
|
|
8
8
|
export declare const nodeRemove: <T>(c: Compare<T>) => (node: TNode<T>) => Tree<T>;
|
|
9
9
|
export declare const remove: <T>(c: Compare<T>) => (tree: Tree<T>) => Tree<T>;
|
|
10
|
+
export declare const proof: {
|
|
11
|
+
throw: {
|
|
12
|
+
reduceValue0DefaultBranch: () => void;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -172,3 +172,15 @@ export const nodeRemove = (c) => (node) => {
|
|
|
172
172
|
return collapseRoot(reduce(initReduce(tf)(first))(tt));
|
|
173
173
|
};
|
|
174
174
|
export const remove = c => map(nodeRemove(c));
|
|
175
|
+
// `reduceValue0`/`reduceValue2`/`initValue0`/`initValue1` merge a Branch1's lone
|
|
176
|
+
// child into its Branch3 sibling. The sibling is always a Branch (length 3 or 5)
|
|
177
|
+
// in every reachable tree shape, so the `default` arm guarding a mismatched
|
|
178
|
+
// (leaf) sibling length can't be hit through the public `remove` API. Exercise
|
|
179
|
+
// it directly here so the invariant guard itself stays covered.
|
|
180
|
+
export const proof = {
|
|
181
|
+
throw: {
|
|
182
|
+
reduceValue0DefaultBranch: () => {
|
|
183
|
+
reduceValue0([['leaf']])([['x'], 's', ['y']]);
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Nullable } from '../nullable/module.f.ts';
|
|
1
2
|
import { type Scan, type StateScan, type Fold, type Reduce, type Equal } from '../function/operator/module.f.ts';
|
|
2
3
|
export type List<T> = NotLazy<T> | Thunk<T>;
|
|
3
4
|
type NotLazy<T> = Result<T> | Concat<T> | readonly T[];
|
|
@@ -44,6 +45,26 @@ export declare const stateScan: <I, S, O>(op: StateScan<I, S, O>) => (init: S) =
|
|
|
44
45
|
export declare const foldScan: <I, O>(op: Fold<I, O>) => (init: O) => (input: List<I>) => Thunk<O>;
|
|
45
46
|
export declare const fold: <I, O>(op: Fold<I, O>) => (init: O) => (input: List<I>) => O;
|
|
46
47
|
export declare const reduce: <T>(op: Reduce<T>) => <D>(def: D) => (input: List<T>) => D | T;
|
|
48
|
+
/**
|
|
49
|
+
* A fold that can bail out early, packaged as plain data.
|
|
50
|
+
*
|
|
51
|
+
* `init` is the starting state, `update` advances the state by one item and
|
|
52
|
+
* returns `null` to abort the whole fold, and `end` finalizes the surviving
|
|
53
|
+
* state into a result. Keeping the three parts together lets `tryFold` drive
|
|
54
|
+
* any short-circuiting accumulation without knowing its domain.
|
|
55
|
+
*/
|
|
56
|
+
export type Accumulator<I, T, R> = {
|
|
57
|
+
readonly init: T;
|
|
58
|
+
readonly update: (i: I, state: T) => Nullable<T>;
|
|
59
|
+
readonly end: (state: T) => R;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Folds `input` with an {@link Accumulator}, short-circuiting to `null` the
|
|
63
|
+
* moment `update` rejects an item; otherwise finalizes with `end`. This is the
|
|
64
|
+
* list-level `try*` sibling of {@link fold} for accumulations that can fail
|
|
65
|
+
* partway through (see the `try*`/`Nullable` convention in `AGENTS.md`).
|
|
66
|
+
*/
|
|
67
|
+
export declare const tryFold: <I, T, R>({ init, update, end }: Accumulator<I, T, R>) => (input: List<I>) => Nullable<R>;
|
|
47
68
|
export declare const length: <T>(input: List<T>) => number;
|
|
48
69
|
export type Entry<T> = readonly [number, T];
|
|
49
70
|
export declare const entries: <T>(input: List<T>) => Thunk<Entry<T>>;
|
|
@@ -127,6 +127,23 @@ export const stateScan = op => compose(stateScanToScan(op))(scan);
|
|
|
127
127
|
export const foldScan = op => compose(foldToScan(op))(scan);
|
|
128
128
|
export const fold = op => init => compose(foldScan(op)(init))(last(init));
|
|
129
129
|
export const reduce = op => def => compose(scan(reduceToScan(op)))(last(def));
|
|
130
|
+
/**
|
|
131
|
+
* Folds `input` with an {@link Accumulator}, short-circuiting to `null` the
|
|
132
|
+
* moment `update` rejects an item; otherwise finalizes with `end`. This is the
|
|
133
|
+
* list-level `try*` sibling of {@link fold} for accumulations that can fail
|
|
134
|
+
* partway through (see the `try*`/`Nullable` convention in `AGENTS.md`).
|
|
135
|
+
*/
|
|
136
|
+
export const tryFold = ({ init, update, end }) => (input) => {
|
|
137
|
+
let state = init;
|
|
138
|
+
for (const i of iterable(input)) {
|
|
139
|
+
const candidate = update(i, state);
|
|
140
|
+
if (candidate === null) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
state = candidate;
|
|
144
|
+
}
|
|
145
|
+
return end(state);
|
|
146
|
+
};
|
|
130
147
|
const lengthList = list => () => {
|
|
131
148
|
const notLazy = trampoline(list);
|
|
132
149
|
if (notLazy === null) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { length, concat, countdown, cycle, drop, dropWhile, entries, every, filter, find, flat, flatMap, map, next, reduce, reverse, scan, some, take, takeWhile, toArray, zip, first, filterMap, isEmpty, equal } from './module.f.js';
|
|
1
|
+
import { length, concat, countdown, cycle, drop, dropWhile, entries, every, filter, find, flat, flatMap, map, next, reduce, reverse, scan, some, take, takeWhile, toArray, zip, first, filterMap, isEmpty, equal, tryFold } from './module.f.js';
|
|
2
2
|
import { stringify } from '../../media/json/module.f.js';
|
|
3
3
|
import { sort } from '../object/module.f.js';
|
|
4
4
|
import { addition, strictEqual, reduceToScan } from '../function/operator/module.f.js';
|
|
@@ -112,6 +112,28 @@ const entriesTest = [
|
|
|
112
112
|
assertEq(result, '[[0,"hello"],[1,"world"]]');
|
|
113
113
|
}
|
|
114
114
|
];
|
|
115
|
+
const sumUpTo = (limit) => ({
|
|
116
|
+
init: 0,
|
|
117
|
+
update: (i, state) => {
|
|
118
|
+
const next = state + i;
|
|
119
|
+
return next > limit ? null : next;
|
|
120
|
+
},
|
|
121
|
+
end: (state) => state * 10,
|
|
122
|
+
});
|
|
123
|
+
const tryFoldTest = [
|
|
124
|
+
() => {
|
|
125
|
+
const result = tryFold(sumUpTo(100))([1, 2, 3]);
|
|
126
|
+
assertEq(result, 60);
|
|
127
|
+
},
|
|
128
|
+
() => {
|
|
129
|
+
const result = tryFold(sumUpTo(4))([1, 2, 3]);
|
|
130
|
+
assertEq(result, null);
|
|
131
|
+
},
|
|
132
|
+
() => {
|
|
133
|
+
const result = tryFold(sumUpTo(100))([]);
|
|
134
|
+
assertEq(result, 0);
|
|
135
|
+
},
|
|
136
|
+
];
|
|
115
137
|
const reverseTest = [
|
|
116
138
|
() => {
|
|
117
139
|
const result = str(reverse([]));
|
|
@@ -257,6 +279,7 @@ export const proof = {
|
|
|
257
279
|
drop: dropTest,
|
|
258
280
|
additionTests,
|
|
259
281
|
entries: entriesTest,
|
|
282
|
+
tryFold: tryFoldTest,
|
|
260
283
|
reverse: reverseTest,
|
|
261
284
|
zip: zipTest,
|
|
262
285
|
logic,
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
import { reduce } from '../list/module.f.js';
|
|
8
8
|
import { addition } from '../function/operator/module.f.js';
|
|
9
9
|
import { cmp as uCmp, min as uMin, max as uMax } from '../function/compare/module.f.js';
|
|
10
|
-
|
|
10
|
+
import { fold } from '../../common/monoid/module.f.js';
|
|
11
|
+
export const sum = fold({ identity: 0, operation: addition });
|
|
11
12
|
export const min = reduce(uMin)(null);
|
|
12
13
|
export const max = reduce(uMax)(null);
|
|
13
14
|
export const cmp = uCmp;
|
|
@@ -13,25 +13,26 @@
|
|
|
13
13
|
* })
|
|
14
14
|
*
|
|
15
15
|
* // Create range maps
|
|
16
|
-
* const range1 = rmOps.fromRange([0, 10])
|
|
17
|
-
* const range2 = rmOps.fromRange([5, 15])
|
|
16
|
+
* const range1 = rmOps.fromRange(2)([0, 10])
|
|
17
|
+
* const range2 = rmOps.fromRange(5)([5, 15])
|
|
18
18
|
*
|
|
19
19
|
* // Merge range maps
|
|
20
20
|
* const merged = toArray(rmOps.merge(range1)(range2))
|
|
21
21
|
*
|
|
22
22
|
* // Retrieve values from the merged range map
|
|
23
|
+
* const get = rmOps.get(merged)
|
|
23
24
|
* //
|
|
24
|
-
* if (
|
|
25
|
+
* if (get(-1) !== 0) { throw 'error' }
|
|
25
26
|
* //
|
|
26
|
-
* if (
|
|
27
|
-
* if (
|
|
27
|
+
* if (get(0) !== 2) { throw 'error' }
|
|
28
|
+
* if (get(2) !== 2) { throw 'error' }
|
|
28
29
|
* // overlapped: 2 | 5 = 7
|
|
29
|
-
* if (
|
|
30
|
+
* if (get(7) !== 7) { throw 'error' }
|
|
30
31
|
* //
|
|
31
|
-
* if (
|
|
32
|
-
* if (
|
|
32
|
+
* if (get(12) !== 5) { throw 'error' }
|
|
33
|
+
* if (get(15) !== 5) { throw 'error' }
|
|
33
34
|
* //
|
|
34
|
-
* if (
|
|
35
|
+
* if (get(16) !== 0) { throw 'error' }
|
|
35
36
|
* ```
|
|
36
37
|
*/
|
|
37
38
|
import { type SortedList } from '../sorted_list/module.f.ts';
|
|
@@ -63,8 +64,8 @@ export type Properties<T> = {
|
|
|
63
64
|
};
|
|
64
65
|
export type RangeMerge<T> = Reduce<RangeMap<T>>;
|
|
65
66
|
export declare const merge: <T>(op: Properties<T>) => RangeMerge<T>;
|
|
66
|
-
export declare const get: <T>(def: T) => (
|
|
67
|
-
export declare const fromRange: <T>(def: T) => (
|
|
67
|
+
export declare const get: <T>(def: T) => (rm: RangeMapArray<T>) => (value: number) => T;
|
|
68
|
+
export declare const fromRange: <T>(def: T) => (value: T) => (r: Range) => RangeMapArray<T>;
|
|
68
69
|
/**
|
|
69
70
|
* Represents a set of operations for managing range maps.
|
|
70
71
|
*/
|
|
@@ -76,11 +77,11 @@ export type RangeMapOp<T> = {
|
|
|
76
77
|
/**
|
|
77
78
|
* Retrieves the value associated with a given numeric range.
|
|
78
79
|
*/
|
|
79
|
-
readonly get: (
|
|
80
|
+
readonly get: (rm: RangeMapArray<T>) => (value: number) => T;
|
|
80
81
|
/**
|
|
81
82
|
* Constructs a range map for a single numeric range and value.
|
|
82
83
|
*/
|
|
83
|
-
readonly fromRange: (
|
|
84
|
+
readonly fromRange: (value: T) => (r: Range) => RangeMapArray<T>;
|
|
84
85
|
};
|
|
85
86
|
/**
|
|
86
87
|
* Creates a set of operations for managing range maps using the specified properties.
|