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.
Files changed (95) hide show
  1. package/README.md +1 -1
  2. package/fjs/bnf/descent/module.f.d.ts +43 -2
  3. package/fjs/bnf/descent/module.f.js +37 -12
  4. package/fjs/bnf/descent/proof.f.d.ts +1 -0
  5. package/fjs/bnf/descent/proof.f.js +73 -32
  6. package/fjs/bnf/ll1/module.f.js +2 -2
  7. package/fjs/cas/evo/module.f.d.ts +28 -8
  8. package/fjs/cas/evo/module.f.js +43 -10
  9. package/fjs/cas/evo/proof.f.d.ts +4 -0
  10. package/fjs/cas/evo/proof.f.js +82 -1
  11. package/fjs/ci/config/module.f.d.ts +10 -7
  12. package/fjs/ci/config/module.f.js +23 -8
  13. package/fjs/ci/module.f.js +12 -5
  14. package/fjs/ci/nix/module.f.d.ts +61 -0
  15. package/fjs/ci/nix/module.f.js +92 -0
  16. package/fjs/ci/nix/proof.f.d.ts +23 -0
  17. package/fjs/ci/nix/proof.f.js +109 -0
  18. package/fjs/ci/node/module.f.d.ts +23 -1
  19. package/fjs/ci/node/module.f.js +47 -3
  20. package/fjs/ci/node/proof.f.d.ts +3 -0
  21. package/fjs/ci/node/proof.f.js +17 -0
  22. package/fjs/ci/proof.f.d.ts +2 -0
  23. package/fjs/ci/proof.f.js +47 -9
  24. package/fjs/dev/module.f.d.ts +1 -0
  25. package/fjs/dev/module.f.js +13 -2
  26. package/fjs/dev/update/module.f.d.ts +11 -0
  27. package/fjs/dev/update/module.f.js +20 -0
  28. package/fjs/dev/update/proof.f.d.ts +7 -0
  29. package/fjs/dev/update/proof.f.js +35 -0
  30. package/fjs/djs/ast/module.f.d.ts +47 -0
  31. package/fjs/djs/ast/module.f.js +9 -0
  32. package/fjs/djs/tokenizer/module.f.js +2 -2
  33. package/fjs/djs/tokenizer/proof.f.d.ts +1 -0
  34. package/fjs/djs/tokenizer/proof.f.js +70 -16
  35. package/fjs/effects/module.f.d.ts +20 -0
  36. package/fjs/effects/module.f.js +25 -1
  37. package/fjs/effects/node/module.d.ts +3 -3
  38. package/fjs/effects/node/module.f.d.ts +17 -7
  39. package/fjs/effects/node/module.f.js +22 -0
  40. package/fjs/effects/node/module.js +12 -12
  41. package/fjs/effects/node/proof.f.d.ts +1 -0
  42. package/fjs/effects/node/proof.f.js +14 -1
  43. package/fjs/effects/node/virtual/module.f.js +1 -1
  44. package/fjs/effects/proof.f.d.ts +5 -0
  45. package/fjs/effects/proof.f.js +22 -0
  46. package/fjs/emergent_testing/all.test.js +2 -1
  47. package/fjs/emergent_testing/module.f.d.ts +3 -3
  48. package/fjs/emergent_testing/module.f.js +8 -10
  49. package/fjs/emergent_testing/proof.f.js +2 -2
  50. package/fjs/emergent_testing/scenarios/thenable.pass.js +1 -1
  51. package/fjs/fsc/module.f.js +4 -4
  52. package/fjs/fsm/module.f.js +1 -1
  53. package/fjs/js/tokenizer/module.f.d.ts +1 -0
  54. package/fjs/js/tokenizer/module.f.js +13 -6
  55. package/fjs/{cas/mcp → mcp/cas}/module.f.d.ts +3 -25
  56. package/fjs/{cas/mcp → mcp/cas}/module.f.js +18 -58
  57. package/fjs/mcp/evo/module.f.d.ts +32 -0
  58. package/fjs/{cas/evo/mcp → mcp/evo}/module.f.js +19 -14
  59. package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.d.ts +1 -0
  60. package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.js +23 -7
  61. package/fjs/mcp/module.f.d.ts +54 -237
  62. package/fjs/mcp/module.f.js +55 -258
  63. package/fjs/mcp/proof.f.d.ts +43 -32
  64. package/fjs/mcp/proof.f.js +508 -200
  65. package/fjs/media/nix/module.f.d.ts +30 -0
  66. package/fjs/media/nix/module.f.js +166 -0
  67. package/fjs/media/nix/proof.f.d.ts +32 -0
  68. package/fjs/media/nix/proof.f.js +127 -0
  69. package/fjs/module.f.js +1 -1
  70. package/fjs/protocol/json_rpc/module.f.d.ts +114 -0
  71. package/fjs/{media/json/rpc → protocol/json_rpc}/module.f.js +3 -3
  72. package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.js +3 -3
  73. package/fjs/protocol/mcp/module.f.d.ts +239 -0
  74. package/fjs/protocol/mcp/module.f.js +272 -0
  75. package/fjs/protocol/mcp/proof.f.d.ts +34 -0
  76. package/fjs/protocol/mcp/proof.f.js +208 -0
  77. package/fjs/{mcp → protocol/mcp}/stdio/module.f.d.ts +5 -5
  78. package/fjs/{mcp → protocol/mcp}/stdio/module.f.js +11 -11
  79. package/fjs/{mcp → protocol/mcp}/stdio/proof.f.js +9 -9
  80. package/fjs/types/range_map/module.f.d.ts +14 -13
  81. package/fjs/types/range_map/module.f.js +18 -13
  82. package/fjs/types/range_map/proof.f.js +26 -39
  83. package/fjs/types/range_set/module.f.d.ts +5 -0
  84. package/fjs/types/range_set/module.f.js +16 -0
  85. package/fjs/types/range_set/proof.f.d.ts +1 -0
  86. package/fjs/types/range_set/proof.f.js +19 -0
  87. package/package.json +5 -5
  88. package/fjs/cas/evo/mcp/module.f.d.ts +0 -27
  89. package/fjs/cas/mcp/proof.f.d.ts +0 -45
  90. package/fjs/cas/mcp/proof.f.js +0 -545
  91. package/fjs/ci/playwright/module.f.d.ts +0 -2
  92. package/fjs/ci/playwright/module.f.js +0 -25
  93. package/fjs/media/json/rpc/module.f.d.ts +0 -114
  94. /package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.d.ts +0 -0
  95. /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 '../../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 '../../media/json/rpc/module.f.ts';
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 '../../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 '../../media/json/rpc/module.f.js';
41
- import { error, ok } from '../../types/result/module.f.js';
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 '../../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 '../../media/json/rpc/module.f.js';
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])(2)
17
- * const range2 = rmOps.fromRange([5, 15])(5)
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 (rmOps.get(-1)(merged) !== 0) { throw 'error' }
25
+ * if (get(-1) !== 0) { throw 'error' }
25
26
  * //
26
- * if (rmOps.get(0)(merged) !== 2) { throw 'error' }
27
- * if (rmOps.get(2)(merged) !== 2) { throw 'error' }
27
+ * if (get(0) !== 2) { throw 'error' }
28
+ * if (get(2) !== 2) { throw 'error' }
28
29
  * // overlapped: 2 | 5 = 7
29
- * if (rmOps.get(7)(merged) !== 7) { throw 'error' }
30
+ * if (get(7) !== 7) { throw 'error' }
30
31
  * //
31
- * if (rmOps.get(12)(merged) !== 5) { throw 'error' }
32
- * if (rmOps.get(15)(merged) !== 5) { throw 'error' }
32
+ * if (get(12) !== 5) { throw 'error' }
33
+ * if (get(15) !== 5) { throw 'error' }
33
34
  * //
34
- * if (rmOps.get(16)(merged) !== 0) { throw 'error' }
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) => (value: number) => (rm: RangeMapArray<T>) => T;
67
- export declare const fromRange: <T>(def: T) => (r: Range) => (value: T) => RangeMapArray<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: (value: number) => (rm: RangeMapArray<T>) => T;
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: (r: Range) => (value: T) => RangeMapArray<T>;
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])(2)
17
- * const range2 = rmOps.fromRange([5, 15])(5)
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 (rmOps.get(-1)(merged) !== 0) { throw 'error' }
25
+ * if (get(-1) !== 0) { throw 'error' }
25
26
  * //
26
- * if (rmOps.get(0)(merged) !== 2) { throw 'error' }
27
- * if (rmOps.get(2)(merged) !== 2) { throw 'error' }
27
+ * if (get(0) !== 2) { throw 'error' }
28
+ * if (get(2) !== 2) { throw 'error' }
28
29
  * // overlapped: 2 | 5 = 7
29
- * if (rmOps.get(7)(merged) !== 7) { throw 'error' }
30
+ * if (get(7) !== 7) { throw 'error' }
30
31
  * //
31
- * if (rmOps.get(12)(merged) !== 5) { throw 'error' }
32
- * if (rmOps.get(15)(merged) !== 5) { throw 'error' }
32
+ * if (get(12) !== 5) { throw 'error' }
33
+ * if (get(15) !== 5) { throw 'error' }
33
34
  * //
34
- * if (rmOps.get(16)(merged) !== 0) { throw 'error' }
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 => value => rm => {
63
- const pos = bsearch(rm.length)(mid => value <= rm[mid][1] ? -1 : 1);
64
- return pos < rm.length ? rm[pos][0] : def;
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]) => v => [[def, a - 1], [v, 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])(2);
24
- const range2 = rmOps.fromRange([5, 15])(5);
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
- assertEq(rmOps.get(-1)(merged), 0, 'error');
29
+ const get = rmOps.get(merged);
30
+ assertEq(get(-1), 0, 'error');
30
31
  //
31
- assertEq(rmOps.get(0)(merged), 2, 'error');
32
- assertEq(rmOps.get(2)(merged), 2, 'error');
32
+ assertEq(get(0), 2, 'error');
33
+ assertEq(get(2), 2, 'error');
33
34
  // 2 | 5 = 7
34
- assertEq(rmOps.get(7)(merged), 7, 'error');
35
+ assertEq(get(7), 7, 'error');
35
36
  //
36
- assertEq(rmOps.get(12)(merged), 5, 'error');
37
- assertEq(rmOps.get(15)(merged), 5, 'error');
37
+ assertEq(get(12), 5, 'error');
38
+ assertEq(get(15), 5, 'error');
38
39
  //
39
- assertEq(rmOps.get(16)(merged), 0, 'error');
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(5)(rm));
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(10)(rm));
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(15)(rm));
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(20)(rm));
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(25)(rm));
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(30)(rm));
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(35)(rm));
129
+ const result = str(at(rm)(35));
129
130
  assertEq(result, '[]');
130
131
  },
131
132
  () => {
132
133
  const rm = [];
133
- const result = str(at(10)(rm));
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])(42);
141
+ const rm = fromRange(def)(42)([1, 7]);
142
+ const g = get(def)(rm);
141
143
  return [
142
- () => {
143
- const result = get(def)(0)(rm);
144
- assertEq(result, -1);
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.39.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": "npx npm-check-updates -u && npm install && deno install && bun install && npm run ci-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
- "@playwright/test": "1.62.0",
47
- "@types/node": "26.1.2",
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>[];