functionalscript 0.39.0 → 0.41.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/cas/evo/module.f.d.ts +28 -8
- package/fjs/cas/evo/module.f.js +43 -10
- package/fjs/cas/evo/proof.f.d.ts +4 -0
- package/fjs/cas/evo/proof.f.js +82 -1
- package/fjs/ci/config/module.f.d.ts +10 -7
- package/fjs/ci/config/module.f.js +23 -8
- package/fjs/ci/module.f.js +12 -5
- 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 +47 -3
- package/fjs/ci/node/proof.f.d.ts +3 -0
- package/fjs/ci/node/proof.f.js +17 -0
- package/fjs/ci/proof.f.d.ts +2 -0
- package/fjs/ci/proof.f.js +47 -9
- package/fjs/dev/module.f.d.ts +1 -0
- package/fjs/dev/module.f.js +13 -2
- 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/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/effects/module.f.d.ts +20 -0
- package/fjs/effects/module.f.js +25 -1
- package/fjs/effects/node/module.d.ts +3 -3
- package/fjs/effects/node/module.f.d.ts +17 -7
- package/fjs/effects/node/module.f.js +22 -0
- 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 +14 -1
- package/fjs/effects/node/virtual/module.f.js +1 -1
- package/fjs/effects/proof.f.d.ts +5 -0
- package/fjs/effects/proof.f.js +22 -0
- 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 +8 -10
- package/fjs/emergent_testing/proof.f.js +2 -2
- 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 +18 -58
- package/fjs/mcp/evo/module.f.d.ts +32 -0
- package/fjs/{cas/evo/mcp → mcp/evo}/module.f.js +19 -14
- package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.d.ts +1 -0
- package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.js +23 -7
- 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 +508 -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/module.f.js +1 -1
- 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 +5 -5
- package/fjs/{mcp → protocol/mcp}/stdio/module.f.js +11 -11
- package/fjs/{mcp → protocol/mcp}/stdio/proof.f.js +9 -9
- 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/package.json +5 -5
- package/fjs/cas/evo/mcp/module.f.d.ts +0 -27
- 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,10 +29,10 @@
|
|
|
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)`.
|
|
@@ -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, step } 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 };
|
|
@@ -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).
|
|
@@ -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.
|
|
@@ -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 { genericMerge } from '../sorted_list/module.f.js';
|
|
@@ -59,11 +60,15 @@ const tailReduce = equal => state => tail => {
|
|
|
59
60
|
return { first: state, tail: tailResult };
|
|
60
61
|
};
|
|
61
62
|
export const merge = op => genericMerge({ reduceOp: reduceOp(op), tailReduce: tailReduce(op.equal) })(null);
|
|
62
|
-
export const get = def =>
|
|
63
|
-
const
|
|
64
|
-
|
|
63
|
+
export const get = def => rm => {
|
|
64
|
+
const length = rm.length;
|
|
65
|
+
const search = bsearch(length);
|
|
66
|
+
return value => {
|
|
67
|
+
const pos = search(mid => value <= rm[mid][1] ? -1 : 1);
|
|
68
|
+
return pos < length ? rm[pos][0] : def;
|
|
69
|
+
};
|
|
65
70
|
};
|
|
66
|
-
export const fromRange = def => ([a, b]) =>
|
|
71
|
+
export const fromRange = def => v => ([a, b]) => [[def, a - 1], [v, b]];
|
|
67
72
|
/**
|
|
68
73
|
* Creates a set of operations for managing range maps using the specified properties.
|
|
69
74
|
*
|
|
@@ -20,23 +20,24 @@ export const proof = {
|
|
|
20
20
|
def: 0,
|
|
21
21
|
});
|
|
22
22
|
// Create range maps
|
|
23
|
-
const range1 = rmOps.fromRange([0, 10])
|
|
24
|
-
const range2 = rmOps.fromRange([5, 15])
|
|
23
|
+
const range1 = rmOps.fromRange(2)([0, 10]);
|
|
24
|
+
const range2 = rmOps.fromRange(5)([5, 15]);
|
|
25
25
|
// Merge range maps
|
|
26
26
|
const merged = toArray(rmOps.merge(range1)(range2));
|
|
27
27
|
// Retrieve values from the merged range map
|
|
28
28
|
//
|
|
29
|
-
|
|
29
|
+
const get = rmOps.get(merged);
|
|
30
|
+
assertEq(get(-1), 0, 'error');
|
|
30
31
|
//
|
|
31
|
-
assertEq(
|
|
32
|
-
assertEq(
|
|
32
|
+
assertEq(get(0), 2, 'error');
|
|
33
|
+
assertEq(get(2), 2, 'error');
|
|
33
34
|
// 2 | 5 = 7
|
|
34
|
-
assertEq(
|
|
35
|
+
assertEq(get(7), 7, 'error');
|
|
35
36
|
//
|
|
36
|
-
assertEq(
|
|
37
|
-
assertEq(
|
|
37
|
+
assertEq(get(12), 5, 'error');
|
|
38
|
+
assertEq(get(15), 5, 'error');
|
|
38
39
|
//
|
|
39
|
-
assertEq(
|
|
40
|
+
assertEq(get(16), 0, 'error');
|
|
40
41
|
},
|
|
41
42
|
merge: [
|
|
42
43
|
() => {
|
|
@@ -95,70 +96,56 @@ export const proof = {
|
|
|
95
96
|
return [
|
|
96
97
|
() => {
|
|
97
98
|
const rm = [[['a'], 10], [['b'], 20], [['c'], 30]];
|
|
98
|
-
const result = str(at(
|
|
99
|
+
const result = str(at(rm)(5));
|
|
99
100
|
assertEq(result, '["a"]');
|
|
100
101
|
},
|
|
101
102
|
() => {
|
|
102
103
|
const rm = [[['a'], 10], [['b'], 20], [['c'], 30]];
|
|
103
|
-
const result = str(at(
|
|
104
|
+
const result = str(at(rm)(10));
|
|
104
105
|
assertEq(result, '["a"]');
|
|
105
106
|
},
|
|
106
107
|
() => {
|
|
107
108
|
const rm = [[['a'], 10], [['b'], 20], [['c'], 30]];
|
|
108
|
-
const result = str(at(
|
|
109
|
+
const result = str(at(rm)(15));
|
|
109
110
|
assertEq(result, '["b"]');
|
|
110
111
|
},
|
|
111
112
|
() => {
|
|
112
113
|
const rm = [[['a'], 10], [['b'], 20], [['c'], 30]];
|
|
113
|
-
const result = str(at(
|
|
114
|
+
const result = str(at(rm)(20));
|
|
114
115
|
assertEq(result, '["b"]');
|
|
115
116
|
},
|
|
116
117
|
() => {
|
|
117
118
|
const rm = [[['a'], 10], [['b'], 20], [['c'], 30]];
|
|
118
|
-
const result = str(at(
|
|
119
|
+
const result = str(at(rm)(25));
|
|
119
120
|
assertEq(result, '["c"]');
|
|
120
121
|
},
|
|
121
122
|
() => {
|
|
122
123
|
const rm = [[['a'], 10], [['b'], 20], [['c'], 30]];
|
|
123
|
-
const result = str(at(
|
|
124
|
+
const result = str(at(rm)(30));
|
|
124
125
|
assertEq(result, '["c"]');
|
|
125
126
|
},
|
|
126
127
|
() => {
|
|
127
128
|
const rm = [[['a'], 10], [['b'], 20], [['c'], 30]];
|
|
128
|
-
const result = str(at(
|
|
129
|
+
const result = str(at(rm)(35));
|
|
129
130
|
assertEq(result, '[]');
|
|
130
131
|
},
|
|
131
132
|
() => {
|
|
132
133
|
const rm = [];
|
|
133
|
-
const result = str(at(
|
|
134
|
+
const result = str(at(rm)(10));
|
|
134
135
|
assertEq(result, '[]');
|
|
135
136
|
}
|
|
136
137
|
];
|
|
137
138
|
},
|
|
138
139
|
fromRange: () => {
|
|
139
140
|
const def = -1;
|
|
140
|
-
const rm = fromRange(def)([1, 7])
|
|
141
|
+
const rm = fromRange(def)(42)([1, 7]);
|
|
142
|
+
const g = get(def)(rm);
|
|
141
143
|
return [
|
|
142
|
-
() =>
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
() =>
|
|
147
|
-
const result = get(def)(1)(rm);
|
|
148
|
-
assertEq(result, 42);
|
|
149
|
-
},
|
|
150
|
-
() => {
|
|
151
|
-
const result = get(def)(3)(rm);
|
|
152
|
-
assertEq(result, 42);
|
|
153
|
-
},
|
|
154
|
-
() => {
|
|
155
|
-
const result = get(def)(7)(rm);
|
|
156
|
-
assertEq(result, 42);
|
|
157
|
-
},
|
|
158
|
-
() => {
|
|
159
|
-
const result = get(def)(9)(rm);
|
|
160
|
-
assertEq(result, -1);
|
|
161
|
-
},
|
|
144
|
+
() => assertEq(g(0), -1),
|
|
145
|
+
() => assertEq(g(1), 42),
|
|
146
|
+
() => assertEq(g(3), 42),
|
|
147
|
+
() => assertEq(g(7), 42),
|
|
148
|
+
() => assertEq(g(9), -1),
|
|
162
149
|
];
|
|
163
|
-
}
|
|
150
|
+
},
|
|
164
151
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constructs a range set containing a single numeric range.
|
|
3
|
+
*/
|
|
4
|
+
export declare const fromRange: (r: import("../range/module.f.ts").Range) => import("../range_map/module.f.ts").RangeMapArray<boolean>;
|
|
5
|
+
export declare const merge: import("../range_map/module.f.ts").RangeMerge<boolean>, get: (rm: import("../range_map/module.f.ts").RangeMapArray<boolean>) => (value: number) => boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A set of numeric ranges, represented as a `range_map` of `boolean`.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { rangeMap } from '../range_map/module.f.js';
|
|
7
|
+
const map = rangeMap({
|
|
8
|
+
union: a => b => a || b,
|
|
9
|
+
equal: a => b => a === b,
|
|
10
|
+
def: false,
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Constructs a range set containing a single numeric range.
|
|
14
|
+
*/
|
|
15
|
+
export const fromRange = map.fromRange(true);
|
|
16
|
+
export const { merge, get } = map;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const proof: () => void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { assert } from "../../asserts/module.f.js";
|
|
2
|
+
import { toArray } from "../list/module.f.js";
|
|
3
|
+
import { fromRange, merge, get } from "./module.f.js";
|
|
4
|
+
import {} from '../range/module.f.js';
|
|
5
|
+
const c = (a) => a.charCodeAt(0);
|
|
6
|
+
const range = ([a, b]) => [c(a), c(b)];
|
|
7
|
+
export const proof = () => {
|
|
8
|
+
const digit = fromRange(range('09'));
|
|
9
|
+
const initial = toArray(merge(fromRange(range('AZ')))(fromRange(range('az'))));
|
|
10
|
+
const isInitial = get(initial);
|
|
11
|
+
assert(!isInitial(0));
|
|
12
|
+
assert(isInitial(c('a')));
|
|
13
|
+
assert(!isInitial(c('1')));
|
|
14
|
+
assert(isInitial(c('Z')));
|
|
15
|
+
const isNext = get(toArray(merge(initial)(digit)));
|
|
16
|
+
assert(isNext(c('0')));
|
|
17
|
+
assert(isNext(c('A')));
|
|
18
|
+
assert(!isNext(c('@')));
|
|
19
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functionalscript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"**/*.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"cov": "node --test --experimental-test-coverage --test-coverage-include=**/module.f.ts",
|
|
14
14
|
"start": "node ./fjs/module.ts",
|
|
15
15
|
"ci-update": "node ./fjs/module.ts ci",
|
|
16
|
-
"update": "
|
|
16
|
+
"dev-update": "node ./fjs/module.ts r ./fjs/dev/update/module.f.ts",
|
|
17
|
+
"update": "npm run ci-update && npm install && deno install && bun install",
|
|
17
18
|
"index-html": "node ./fjs/module.ts r ./fjs/website/module.f.ts",
|
|
18
19
|
"website": "npm run prepack && npm run index-html"
|
|
19
20
|
},
|
|
@@ -43,8 +44,7 @@
|
|
|
43
44
|
},
|
|
44
45
|
"homepage": "https://github.com/functionalscript/functionalscript#readme",
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@
|
|
47
|
-
"
|
|
48
|
-
"typescript": "7.0.2"
|
|
47
|
+
"@types/node": "=26.0.0",
|
|
48
|
+
"typescript": "=7.0.2"
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { type Operation } from '../../../effects/module.f.ts';
|
|
2
|
-
import { type MemOp } from '../../../effects/memory/module.f.ts';
|
|
3
|
-
import { type ToolEntry } from '../../../mcp/module.f.ts';
|
|
4
|
-
import { type Evo } from '../module.f.ts';
|
|
5
|
-
/** Arguments for `evo_list`: none. */
|
|
6
|
-
export declare const evoListArgs: {};
|
|
7
|
-
/** Arguments for `evo_head`: the subject whose current heads are requested. */
|
|
8
|
-
export declare const evoHeadArgs: {
|
|
9
|
-
readonly subject: import("../../../types/rtti/module.f.ts").String;
|
|
10
|
-
};
|
|
11
|
-
/** Arguments for `evo_revision`: the hash of the revision to read. */
|
|
12
|
-
export declare const evoRevisionArgs: {
|
|
13
|
-
readonly hash: import("../../../types/rtti/module.f.ts").String;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Arguments for `evo_add`: a new revision, per `fjs/cas/evo`'s
|
|
17
|
-
* `RevisionData` — every field of it the caller supplies, i.e. all but
|
|
18
|
-
* `generation`, which the server computes.
|
|
19
|
-
*/
|
|
20
|
-
export declare const evoAddArgs: {
|
|
21
|
-
readonly parents: import("../../../types/rtti/module.f.ts").Type1<"array", import("../../../types/rtti/module.f.ts").String>;
|
|
22
|
-
readonly snapshot: import("../../../types/rtti/module.f.ts").Or<readonly [import("../../../types/rtti/module.f.ts").String, undefined]>;
|
|
23
|
-
readonly subject: import("../../../types/rtti/module.f.ts").Or<readonly [import("../../../types/rtti/module.f.ts").String, undefined]>;
|
|
24
|
-
readonly archived: import("../../../types/rtti/module.f.ts").Or<readonly [true, undefined]>;
|
|
25
|
-
};
|
|
26
|
-
/** Registry of all Evo tools, bound to an `Evo<O>`. */
|
|
27
|
-
export declare const evoToolRegistry: <O extends Operation>(e: Evo<O>) => readonly ToolEntry<O | MemOp>[];
|