functionalscript 0.39.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.
Files changed (88) 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 +1 -1
  8. package/fjs/cas/evo/module.f.js +1 -1
  9. package/fjs/cas/evo/proof.f.js +1 -1
  10. package/fjs/ci/config/module.f.d.ts +7 -4
  11. package/fjs/ci/config/module.f.js +20 -5
  12. package/fjs/ci/module.f.js +12 -5
  13. package/fjs/ci/nix/module.f.d.ts +61 -0
  14. package/fjs/ci/nix/module.f.js +92 -0
  15. package/fjs/ci/nix/proof.f.d.ts +23 -0
  16. package/fjs/ci/nix/proof.f.js +109 -0
  17. package/fjs/ci/node/module.f.d.ts +23 -1
  18. package/fjs/ci/node/module.f.js +47 -3
  19. package/fjs/ci/proof.f.d.ts +2 -0
  20. package/fjs/ci/proof.f.js +47 -9
  21. package/fjs/dev/module.f.d.ts +1 -0
  22. package/fjs/dev/module.f.js +13 -2
  23. package/fjs/dev/update/module.f.d.ts +11 -0
  24. package/fjs/dev/update/module.f.js +20 -0
  25. package/fjs/dev/update/proof.f.d.ts +7 -0
  26. package/fjs/dev/update/proof.f.js +35 -0
  27. package/fjs/djs/ast/module.f.d.ts +47 -0
  28. package/fjs/djs/ast/module.f.js +9 -0
  29. package/fjs/djs/tokenizer/module.f.js +2 -2
  30. package/fjs/djs/tokenizer/proof.f.d.ts +1 -0
  31. package/fjs/djs/tokenizer/proof.f.js +70 -16
  32. package/fjs/effects/node/module.d.ts +3 -3
  33. package/fjs/effects/node/module.f.d.ts +17 -7
  34. package/fjs/effects/node/module.f.js +22 -0
  35. package/fjs/effects/node/module.js +12 -12
  36. package/fjs/effects/node/proof.f.d.ts +1 -0
  37. package/fjs/effects/node/proof.f.js +14 -1
  38. package/fjs/effects/node/virtual/module.f.js +1 -1
  39. package/fjs/emergent_testing/all.test.js +2 -1
  40. package/fjs/emergent_testing/module.f.d.ts +3 -3
  41. package/fjs/emergent_testing/module.f.js +8 -10
  42. package/fjs/emergent_testing/proof.f.js +2 -2
  43. package/fjs/emergent_testing/scenarios/thenable.pass.js +1 -1
  44. package/fjs/fsc/module.f.js +4 -4
  45. package/fjs/fsm/module.f.js +1 -1
  46. package/fjs/js/tokenizer/module.f.d.ts +1 -0
  47. package/fjs/js/tokenizer/module.f.js +13 -6
  48. package/fjs/{cas/mcp → mcp/cas}/module.f.d.ts +3 -25
  49. package/fjs/{cas/mcp → mcp/cas}/module.f.js +18 -58
  50. package/fjs/mcp/evo/module.f.d.ts +27 -0
  51. package/fjs/{cas/evo/mcp → mcp/evo}/module.f.js +9 -9
  52. package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.js +7 -7
  53. package/fjs/mcp/module.f.d.ts +54 -237
  54. package/fjs/mcp/module.f.js +55 -258
  55. package/fjs/mcp/proof.f.d.ts +43 -32
  56. package/fjs/mcp/proof.f.js +508 -200
  57. package/fjs/media/nix/module.f.d.ts +30 -0
  58. package/fjs/media/nix/module.f.js +166 -0
  59. package/fjs/media/nix/proof.f.d.ts +32 -0
  60. package/fjs/media/nix/proof.f.js +127 -0
  61. package/fjs/module.f.js +1 -1
  62. package/fjs/protocol/json_rpc/module.f.d.ts +114 -0
  63. package/fjs/{media/json/rpc → protocol/json_rpc}/module.f.js +3 -3
  64. package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.js +3 -3
  65. package/fjs/protocol/mcp/module.f.d.ts +239 -0
  66. package/fjs/protocol/mcp/module.f.js +272 -0
  67. package/fjs/protocol/mcp/proof.f.d.ts +34 -0
  68. package/fjs/protocol/mcp/proof.f.js +208 -0
  69. package/fjs/{mcp → protocol/mcp}/stdio/module.f.d.ts +5 -5
  70. package/fjs/{mcp → protocol/mcp}/stdio/module.f.js +11 -11
  71. package/fjs/{mcp → protocol/mcp}/stdio/proof.f.js +9 -9
  72. package/fjs/types/range_map/module.f.d.ts +14 -13
  73. package/fjs/types/range_map/module.f.js +18 -13
  74. package/fjs/types/range_map/proof.f.js +26 -39
  75. package/fjs/types/range_set/module.f.d.ts +5 -0
  76. package/fjs/types/range_set/module.f.js +16 -0
  77. package/fjs/types/range_set/proof.f.d.ts +1 -0
  78. package/fjs/types/range_set/proof.f.js +19 -0
  79. package/package.json +5 -5
  80. package/fjs/cas/evo/mcp/module.f.d.ts +0 -27
  81. package/fjs/cas/mcp/proof.f.d.ts +0 -45
  82. package/fjs/cas/mcp/proof.f.js +0 -545
  83. package/fjs/ci/playwright/module.f.d.ts +0 -2
  84. package/fjs/ci/playwright/module.f.js +0 -25
  85. package/fjs/media/json/rpc/module.f.d.ts +0 -114
  86. /package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.d.ts +0 -0
  87. /package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.d.ts +0 -0
  88. /package/fjs/{mcp → protocol/mcp}/stdio/proof.f.d.ts +0 -0
@@ -0,0 +1,30 @@
1
+ /**
2
+ * A minimal, checked eDSL for constructing and serializing Nix expressions.
3
+ *
4
+ * The tree deliberately models syntax rather than evaluated Nix values. String
5
+ * expressions are escaped double-quoted strings; the other supported forms are
6
+ * represented by tagged tuples.
7
+ *
8
+ * @module
9
+ */
10
+ import { type List as ChunkList } from '../../types/list/module.f.ts';
11
+ type Identifier = string;
12
+ type AttributeName = string;
13
+ type AttributePath = readonly [AttributeName, ...AttributeName[]];
14
+ type Binding = readonly ['=', AttributePath, Expression];
15
+ type Reference = readonly ['ref', Identifier, ...AttributeName[]];
16
+ type AttributeSet = readonly ['set', ...Binding[]];
17
+ type NixList = readonly ['list', ...Reference[]];
18
+ type ApplicationArgument = Reference | AttributeSet;
19
+ type Application = readonly ['apply', Reference, ...ApplicationArgument[]];
20
+ type OpenSetPattern = readonly ['open-set-pattern', ...Identifier[]];
21
+ type Lambda = readonly ['lambda', OpenSetPattern, Expression];
22
+ type Let = readonly ['let', readonly Binding[], Expression];
23
+ type IndentedString = readonly ['indented-string', string];
24
+ /** The Nix syntax supported by the serializer. */
25
+ export type Expression = string | Reference | AttributeSet | NixList | Application | Lambda | Let | IndentedString;
26
+ /** Serializes an expression into composable chunks, or rejects an invalid identifier. */
27
+ export declare const nix: (expression: Expression) => ChunkList<string> | undefined;
28
+ /** Serializes an expression with exactly one trailing newline on success. */
29
+ export declare const nixToString: (expression: Expression) => string | undefined;
30
+ export {};
@@ -0,0 +1,166 @@
1
+ /**
2
+ * A minimal, checked eDSL for constructing and serializing Nix expressions.
3
+ *
4
+ * The tree deliberately models syntax rather than evaluated Nix values. String
5
+ * expressions are escaped double-quoted strings; the other supported forms are
6
+ * represented by tagged tuples.
7
+ *
8
+ * @module
9
+ */
10
+ import { toArray } from '../../types/list/module.f.js';
11
+ import { concat } from '../../types/string/module.f.js';
12
+ import { includes } from '../../types/array/module.f.js';
13
+ import { digitRange, latinCapitalLetterRange, latinSmallLetterRange, range, } from '../../text/ascii/module.f.js';
14
+ import { fromRange, get, merge } from '../../types/range_set/module.f.js';
15
+ const reservedWords = [
16
+ 'assert',
17
+ 'else',
18
+ 'if',
19
+ 'in',
20
+ 'inherit',
21
+ 'let',
22
+ 'or',
23
+ 'rec',
24
+ 'then',
25
+ 'with',
26
+ ];
27
+ const isReservedWord = includes(reservedWords);
28
+ const letters = merge(fromRange(latinCapitalLetterRange))(fromRange(latinSmallLetterRange));
29
+ const identifierInitial = toArray(merge(letters)(fromRange(range('_'))));
30
+ const getIdentifierInitial = get(identifierInitial);
31
+ const isIdentifierInitial = (character) => getIdentifierInitial(character.charCodeAt(0));
32
+ const identifierTrailing = toArray(merge(merge(identifierInitial)(fromRange(digitRange)))(merge(fromRange(range("'")))(fromRange(range('-')))));
33
+ const getIdentifierTrailing = get(identifierTrailing);
34
+ const isIdentifierTrailing = (character) => getIdentifierTrailing(character.charCodeAt(0));
35
+ const isIdentifier = (value) => {
36
+ const [initial, ...trailing] = value;
37
+ return initial !== undefined
38
+ && isIdentifierInitial(initial)
39
+ && trailing.every(isIdentifierTrailing)
40
+ && !isReservedWord(value);
41
+ };
42
+ const indent = (level) => ' '.repeat(level);
43
+ const escapeQuoted = (value) => value
44
+ .replaceAll('\\', '\\\\')
45
+ .replaceAll('"', '\\"')
46
+ .replaceAll('${', '\\${')
47
+ .replaceAll('\n', '\\n')
48
+ .replaceAll('\r', '\\r')
49
+ .replaceAll('\t', '\\t');
50
+ const quoted = (value) => `"${escapeQuoted(value)}"`;
51
+ const attributeName = (value) => isIdentifier(value) ? value : quoted(value);
52
+ const attributePath = (path) => path.map(attributeName).join('.');
53
+ const escapeIndented = (value) => value
54
+ .replaceAll("''", "'''")
55
+ .replaceAll('${', "''${");
56
+ const protectLeadingWhitespace = (line) => {
57
+ const contentStart = [...line, 'x'].findIndex(character => character !== ' ' && character !== '\t');
58
+ const leading = line.slice(0, contentStart)
59
+ .replaceAll(' ', "''\\ ")
60
+ .replaceAll('\t', "''\\t");
61
+ return `${leading}${line.slice(contentStart)}`;
62
+ };
63
+ const serializeReference = ([, name, ...selection]) => isIdentifier(name)
64
+ ? [name, ...selection.map(attributeName)].join('.')
65
+ : undefined;
66
+ const serializeReferenceChunks = (reference) => {
67
+ const serialized = serializeReference(reference);
68
+ return serialized === undefined ? undefined : [serialized];
69
+ };
70
+ const serializePattern = ([, ...names]) => names.every((name, index) => isIdentifier(name) && names.indexOf(name) === index)
71
+ ? `{ ${[...names, '...'].join(', ')} }`
72
+ : undefined;
73
+ const joinChunks = (chunks, separator) => chunks.flatMap((chunk, index) => index === 0 ? chunk : [separator, ...chunk]);
74
+ const isPathPrefix = (prefix, path) => prefix.length <= path.length
75
+ && prefix.every((name, index) => name === path[index]);
76
+ const pathsConflict = (a, b) => isPathPrefix(a, b) || isPathPrefix(b, a);
77
+ const bindingsCompatible = (bindings) => bindings.every(([, path], index) => bindings.slice(0, index).every(([, previous]) => !pathsConflict(path, previous)));
78
+ const serializeBindings = (bindings, level) => {
79
+ if (!bindingsCompatible(bindings)) {
80
+ return undefined;
81
+ }
82
+ const serialized = bindings.map(([, path, value]) => {
83
+ const expression = serialize(value, level);
84
+ return expression === undefined
85
+ ? undefined
86
+ : [indent(level), attributePath(path), ' = ', ...expression, ';'];
87
+ });
88
+ const defined = serialized.flatMap(value => value === undefined ? [] : [value]);
89
+ return defined.length !== serialized.length
90
+ ? undefined
91
+ : joinChunks(defined, '\n');
92
+ };
93
+ const serializeSet = ([, ...bindings], level) => {
94
+ if (bindings.length === 0) {
95
+ return ['{}'];
96
+ }
97
+ const body = serializeBindings(bindings, level + 1);
98
+ return body === undefined ? undefined : ['{\n', ...body, '\n', indent(level), '}'];
99
+ };
100
+ const serializeList = ([, ...references]) => {
101
+ const items = references.map(serializeReference);
102
+ const definedItems = items.flatMap(item => item === undefined ? [] : [item]);
103
+ return items.includes(undefined)
104
+ ? undefined
105
+ : items.length === 0
106
+ ? ['[ ]']
107
+ : ['[ ', ...definedItems.flatMap((item, index) => index === 0 ? [item] : [' ', item]), ' ]'];
108
+ };
109
+ const serializeApplication = ([, fn, ...args], level) => {
110
+ const serializedFn = serializeReference(fn);
111
+ const serializedArgs = args.map(argument => argument[0] === 'ref'
112
+ ? serializeReferenceChunks(argument)
113
+ : serializeSet(argument, level));
114
+ const definedArgs = serializedArgs.flatMap(argument => argument === undefined ? [] : [argument]);
115
+ return serializedFn === undefined || definedArgs.length !== serializedArgs.length
116
+ ? undefined
117
+ : [serializedFn, ...definedArgs.flatMap(argument => [' ', ...argument])];
118
+ };
119
+ const serializeLambda = ([, pattern, body], level) => {
120
+ const serializedPattern = serializePattern(pattern);
121
+ const serializedBody = serialize(body, level);
122
+ return serializedPattern === undefined || serializedBody === undefined
123
+ ? undefined
124
+ : [serializedPattern, ': ', ...serializedBody];
125
+ };
126
+ const serializeLet = ([, bindings, body], level) => {
127
+ const serializedBindings = serializeBindings(bindings, level + 1);
128
+ const serializedBody = serialize(body, level);
129
+ return serializedBindings === undefined || serializedBody === undefined
130
+ ? undefined
131
+ : ['let\n', ...serializedBindings, '\n', indent(level), 'in\n', indent(level), ...serializedBody];
132
+ };
133
+ const serialize = (expression, level) => {
134
+ if (typeof expression === 'string') {
135
+ return [quoted(expression)];
136
+ }
137
+ switch (expression[0]) {
138
+ case 'ref': {
139
+ return serializeReferenceChunks(expression);
140
+ }
141
+ case 'set': return serializeSet(expression, level);
142
+ case 'list': return serializeList(expression);
143
+ case 'apply': return serializeApplication(expression, level);
144
+ case 'lambda': return serializeLambda(expression, level);
145
+ case 'let': return serializeLet(expression, level);
146
+ case 'indented-string': {
147
+ const [, value] = expression;
148
+ const contentIndent = indent(level + 1);
149
+ const content = escapeIndented(value)
150
+ .split('\n')
151
+ .map(protectLeadingWhitespace)
152
+ .map(line => `${contentIndent}${line}`)
153
+ .join('\n');
154
+ return ["''\n", content, '\n', indent(level), "''"];
155
+ }
156
+ }
157
+ };
158
+ /** Serializes an expression into composable chunks, or rejects an invalid identifier. */
159
+ export const nix = (expression) => {
160
+ return serialize(expression, 0);
161
+ };
162
+ /** Serializes an expression with exactly one trailing newline on success. */
163
+ export const nixToString = (expression) => {
164
+ const chunks = nix(expression);
165
+ return chunks === undefined ? undefined : `${concat(chunks)}\n`;
166
+ };
@@ -0,0 +1,32 @@
1
+ export declare const proof: {
2
+ strings: () => void;
3
+ reference: () => void;
4
+ emptySetAndList: () => void;
5
+ multiReferenceList: () => void;
6
+ compatiblePaths: () => void;
7
+ chunks: () => void;
8
+ indentedStringEscaping: () => void;
9
+ emptyPattern: () => void;
10
+ node24: () => void;
11
+ node22: () => void;
12
+ invalid: {
13
+ reference: () => void;
14
+ reservedReference: () => void;
15
+ emptyReference: () => void;
16
+ digitReference: () => void;
17
+ nonAsciiReference: () => void;
18
+ pattern: () => void;
19
+ duplicatePattern: () => void;
20
+ duplicateBinding: () => void;
21
+ parentThenChildBinding: () => void;
22
+ childThenParentBinding: () => void;
23
+ duplicateLetBinding: () => void;
24
+ bindingValue: () => void;
25
+ listItem: () => void;
26
+ applicationFunction: () => void;
27
+ applicationArgument: () => void;
28
+ lambdaBody: () => void;
29
+ letBinding: () => void;
30
+ letBody: () => void;
31
+ };
32
+ };
@@ -0,0 +1,127 @@
1
+ import { assert, assertEq } from '../../asserts/module.f.js';
2
+ import { toArray } from '../../types/list/module.f.js';
3
+ import { nix, nixToString } from './module.f.js';
4
+ const nodeFlake = (nodePackage, shellHook) => ['set',
5
+ ['=', ['inputs', 'nixpkgs', 'url'], 'github:NixOS/nixpkgs/<commit>'],
6
+ ['=', ['outputs'], ['lambda',
7
+ ['open-set-pattern', 'nixpkgs'],
8
+ ['set',
9
+ ['=', ['devShells', 'aarch64-linux', 'default'],
10
+ ['let', [
11
+ ['=', ['pkgs'], ['apply',
12
+ ['ref', 'import'],
13
+ ['ref', 'nixpkgs'],
14
+ ['set', ['=', ['system'], 'aarch64-linux']]
15
+ ]]
16
+ ], ['apply',
17
+ ['ref', 'pkgs', 'mkShell'],
18
+ ['set',
19
+ ['=', ['packages'], ['list', ['ref', 'pkgs', nodePackage]]],
20
+ ...(shellHook ? [[
21
+ '=',
22
+ ['shellHook'],
23
+ ['indented-string', `export NPM_CONFIG_PREFIX="$HOME/.npm-global"
24
+ export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
25
+ mkdir -p "$NPM_CONFIG_PREFIX"`]
26
+ ]] : [])
27
+ ]
28
+ ]]
29
+ ]
30
+ ]
31
+ ]]
32
+ ];
33
+ const node24 = `{
34
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/<commit>";
35
+ outputs = { nixpkgs, ... }: {
36
+ devShells.aarch64-linux.default = let
37
+ pkgs = import nixpkgs {
38
+ system = "aarch64-linux";
39
+ };
40
+ in
41
+ pkgs.mkShell {
42
+ packages = [ pkgs.nodejs_24 ];
43
+ };
44
+ };
45
+ }
46
+ `;
47
+ const node22 = `{
48
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/<commit>";
49
+ outputs = { nixpkgs, ... }: {
50
+ devShells.aarch64-linux.default = let
51
+ pkgs = import nixpkgs {
52
+ system = "aarch64-linux";
53
+ };
54
+ in
55
+ pkgs.mkShell {
56
+ packages = [ pkgs.nodejs_22 ];
57
+ shellHook = ''
58
+ export NPM_CONFIG_PREFIX="$HOME/.npm-global"
59
+ export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
60
+ mkdir -p "$NPM_CONFIG_PREFIX"
61
+ '';
62
+ };
63
+ };
64
+ }
65
+ `;
66
+ export const proof = {
67
+ strings: () => {
68
+ assertEq(nixToString('"\\${\n\r\t'), `"\\"\\\\\\\${\\n\\r\\t"\n`);
69
+ },
70
+ reference: () => {
71
+ assertEq(nixToString(['ref', 'pkgs', 'a.b', 'or']), 'pkgs."a.b"."or"\n');
72
+ assertEq(nixToString(['ref', "a-b'9"]), "a-b'9\n");
73
+ assertEq(nixToString(['ref', '_AZ']), '_AZ\n');
74
+ },
75
+ emptySetAndList: () => {
76
+ assertEq(nixToString(['set']), '{}\n');
77
+ assertEq(nixToString(['list']), '[ ]\n');
78
+ },
79
+ multiReferenceList: () => {
80
+ assertEq(nixToString(['list', ['ref', 'pkgs', 'nodejs_22'], ['ref', 'pkgs', 'nodejs_24']]), '[ pkgs.nodejs_22 pkgs.nodejs_24 ]\n');
81
+ },
82
+ compatiblePaths: () => {
83
+ assertEq(nixToString(['set', ['=', ['x', 'y'], 'a'], ['=', ['x', 'z'], 'b']]), '{\n x.y = "a";\n x.z = "b";\n}\n');
84
+ },
85
+ chunks: () => {
86
+ const chunks = nix(['list', ['ref', 'pkgs', 'nodejs_24']]);
87
+ assert(chunks !== undefined);
88
+ const [open, reference, close, extra] = toArray(chunks);
89
+ assertEq(open, '[ ');
90
+ assertEq(reference, 'pkgs.nodejs_24');
91
+ assertEq(close, ' ]');
92
+ assertEq(extra, undefined);
93
+ },
94
+ indentedStringEscaping: () => {
95
+ assertEq(nixToString(['indented-string', "a '' ${b}"]), "''\n a ''' ''${b}\n''\n");
96
+ assertEq(nixToString(['indented-string', ' a\n\t b\n ']), "''\n ''\\ ''\\ a\n ''\\t''\\ b\n ''\\ ''\\ \n''\n");
97
+ },
98
+ emptyPattern: () => {
99
+ assertEq(nixToString(['lambda', ['open-set-pattern'], ['set']]), '{ ... }: {}\n');
100
+ },
101
+ node24: () => {
102
+ assertEq(nixToString(nodeFlake('nodejs_24', false)), node24);
103
+ },
104
+ node22: () => {
105
+ assertEq(nixToString(nodeFlake('nodejs_22', true)), node22);
106
+ },
107
+ invalid: {
108
+ reference: () => assertEq(nixToString(['ref', 'not valid']), undefined),
109
+ reservedReference: () => assertEq(nixToString(['ref', 'let']), undefined),
110
+ emptyReference: () => assertEq(nixToString(['ref', '']), undefined),
111
+ digitReference: () => assertEq(nixToString(['ref', '1abc']), undefined),
112
+ nonAsciiReference: () => assertEq(nixToString(['ref', 'é']), undefined),
113
+ pattern: () => assertEq(nixToString(['lambda', ['open-set-pattern', 'if'], ['set']]), undefined),
114
+ duplicatePattern: () => assertEq(nixToString(['lambda', ['open-set-pattern', 'x', 'x'], ['set']]), undefined),
115
+ duplicateBinding: () => assertEq(nixToString(['set', ['=', ['x'], 'a'], ['=', ['x'], 'b']]), undefined),
116
+ parentThenChildBinding: () => assertEq(nixToString(['set', ['=', ['x'], 'a'], ['=', ['x', 'y'], 'b']]), undefined),
117
+ childThenParentBinding: () => assertEq(nixToString(['set', ['=', ['x', 'y'], 'a'], ['=', ['x'], 'b']]), undefined),
118
+ duplicateLetBinding: () => assertEq(nixToString(['let', [['=', ['x'], 'a'], ['=', ['x'], 'b']], ['ref', 'x']]), undefined),
119
+ bindingValue: () => assertEq(nixToString(['set', ['=', ['ok'], ['ref', 'bad name']]]), undefined),
120
+ listItem: () => assertEq(nixToString(['list', ['ref', 'valid'], ['ref', 'bad name']]), undefined),
121
+ applicationFunction: () => assertEq(nixToString(['apply', ['ref', 'with']]), undefined),
122
+ applicationArgument: () => assertEq(nixToString(['apply', ['ref', 'f'], ['ref', 'bad name']]), undefined),
123
+ lambdaBody: () => assertEq(nixToString(['lambda', ['open-set-pattern', 'good'], ['ref', 'bad name']]), undefined),
124
+ letBinding: () => assertEq(nixToString(['let', [['=', ['x'], ['ref', 'bad name']]], ['ref', 'x']]), undefined),
125
+ letBody: () => assertEq(nixToString(['let', [], ['ref', 'bad name']]), undefined),
126
+ }
127
+ };
package/fjs/module.f.js CHANGED
@@ -9,7 +9,7 @@ import { commands as casCommands } from './cas/cli/module.f.js';
9
9
  import { main as ciMain } from './ci/module.f.js';
10
10
  import { import_ } from './effects/node/module.f.js';
11
11
  import { dispatch } from './cli/module.f.js';
12
- import { casMcpServer } from './cas/mcp/module.f.js';
12
+ import { casMcpServer } from './mcp/module.f.js';
13
13
  import { pure, step } from './effects/module.f.js';
14
14
  import { unwrap } from './types/result/module.f.js';
15
15
  const commands = [
@@ -0,0 +1,114 @@
1
+ import type { Ts } from '../../types/rtti/ts/module.f.ts';
2
+ import type { Result } from '../../types/result/module.f.ts';
3
+ import { unknown, type Unknown } from '../../media/json/module.f.ts';
4
+ export declare const jsonrpc: '2.0';
5
+ /** Request/response identifier: a string, a number, or `null`. */
6
+ declare const id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
7
+ /**
8
+ * A request or notification envelope. `id` present → request (a response is
9
+ * expected); `id` absent → notification (no response). `params` is optional.
10
+ *
11
+ * https://www.jsonrpc.org/specification#request_object
12
+ */
13
+ export declare const request: {
14
+ readonly jsonrpc: "2.0";
15
+ readonly method: import("../../types/rtti/module.f.ts").String;
16
+ readonly params: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
17
+ readonly id: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>, undefined]>;
18
+ };
19
+ /** The JSON-RPC error object. */
20
+ export declare const error: {
21
+ readonly code: import("../../types/rtti/module.f.ts").Number;
22
+ readonly message: import("../../types/rtti/module.f.ts").String;
23
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
24
+ };
25
+ export type Id = Ts<typeof id>;
26
+ export type Request = Ts<typeof request>;
27
+ export type RpcError = Ts<typeof error>;
28
+ export declare const successResponse: {
29
+ readonly jsonrpc: "2.0";
30
+ readonly result: typeof unknown;
31
+ readonly id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
32
+ };
33
+ export declare const errorResponse: {
34
+ readonly jsonrpc: "2.0";
35
+ readonly error: {
36
+ readonly code: import("../../types/rtti/module.f.ts").Number;
37
+ readonly message: import("../../types/rtti/module.f.ts").String;
38
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
39
+ };
40
+ readonly id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
41
+ };
42
+ /**
43
+ * A response envelope: either a success (`result`) or an error (`error`).
44
+ * Derived from the rtti schema via `Ts<>` — the same declaration is the
45
+ * runtime decoder and the static type, with no drift. rtti structs are open
46
+ * (extra keys allowed), so "result XOR error" is not enforced at runtime; in
47
+ * practice the dispatcher only ever constructs one or the other.
48
+ *
49
+ * https://www.jsonrpc.org/specification#response_object
50
+ */
51
+ export declare const response: import("../../types/rtti/module.f.ts").Or<[{
52
+ readonly jsonrpc: "2.0";
53
+ readonly result: typeof unknown;
54
+ readonly id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
55
+ }, {
56
+ readonly jsonrpc: "2.0";
57
+ readonly error: {
58
+ readonly code: import("../../types/rtti/module.f.ts").Number;
59
+ readonly message: import("../../types/rtti/module.f.ts").String;
60
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
61
+ };
62
+ readonly id: import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>;
63
+ }]>;
64
+ export type Response = Ts<typeof response>;
65
+ /** Decodes an untrusted value as a JSON-RPC request / notification. */
66
+ export declare const decodeRequest: import("../../types/rtti/common/module.f.ts").Validate<{
67
+ readonly jsonrpc: "2.0";
68
+ readonly method: import("../../types/rtti/module.f.ts").String;
69
+ readonly params: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
70
+ readonly id: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").String, import("../../types/rtti/module.f.ts").Number, null]>, undefined]>;
71
+ }>;
72
+ /** A method implementation: maps `params` to a result or an `RpcError`. */
73
+ export type Handler = (params: Unknown | undefined) => Result<Unknown, RpcError>;
74
+ /** A `method` name → `Handler` map. */
75
+ export type Handlers = {
76
+ readonly [method: string]: Handler;
77
+ };
78
+ /** Builds an `RpcError` with the given `code` and `message` (no `data`). */
79
+ export declare const rpcError: (code: number) => (message: string) => RpcError;
80
+ export declare const parseError: import("../../types/rtti/ts/module.f.ts").StructTs<{
81
+ readonly code: import("../../types/rtti/module.f.ts").Number;
82
+ readonly message: import("../../types/rtti/module.f.ts").String;
83
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
84
+ }>;
85
+ export declare const invalidRequest: import("../../types/rtti/ts/module.f.ts").StructTs<{
86
+ readonly code: import("../../types/rtti/module.f.ts").Number;
87
+ readonly message: import("../../types/rtti/module.f.ts").String;
88
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
89
+ }>;
90
+ export declare const methodNotFound: import("../../types/rtti/ts/module.f.ts").StructTs<{
91
+ readonly code: import("../../types/rtti/module.f.ts").Number;
92
+ readonly message: import("../../types/rtti/module.f.ts").String;
93
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
94
+ }>;
95
+ export declare const invalidParams: import("../../types/rtti/ts/module.f.ts").StructTs<{
96
+ readonly code: import("../../types/rtti/module.f.ts").Number;
97
+ readonly message: import("../../types/rtti/module.f.ts").String;
98
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
99
+ }>;
100
+ export declare const internalError: import("../../types/rtti/ts/module.f.ts").StructTs<{
101
+ readonly code: import("../../types/rtti/module.f.ts").Number;
102
+ readonly message: import("../../types/rtti/module.f.ts").String;
103
+ readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
104
+ }>;
105
+ /**
106
+ * Dispatches an already-parsed JSON-RPC value against `handlers`.
107
+ *
108
+ * - invalid envelope → `Invalid Request` (`-32600`) with `id: null`
109
+ * - notification (no `id`) → `null` (never a response)
110
+ * - unknown method → `Method not found` (`-32601`)
111
+ * - otherwise the handler's `ok` / `error` result becomes a success / error response
112
+ */
113
+ export declare const dispatch: (handlers: Handlers) => (value: Unknown) => Response | null;
114
+ export {};
@@ -14,9 +14,9 @@
14
14
  *
15
15
  * @module
16
16
  */
17
- import { number, string, or, option } from '../../../types/rtti/module.f.js';
18
- import { validate } from '../../../types/rtti/validate/module.f.js';
19
- import { unknown } from '../module.f.js';
17
+ import { number, string, or, option } from '../../types/rtti/module.f.js';
18
+ import { validate } from '../../types/rtti/validate/module.f.js';
19
+ import { unknown } from '../../media/json/module.f.js';
20
20
  export const jsonrpc = '2.0';
21
21
  /** Request/response identifier: a string, a number, or `null`. */
22
22
  const id = or(string, number, null);
@@ -1,6 +1,6 @@
1
- import { assert, assertEq } from '../../../asserts/module.f.js';
2
- import { ok, error } from '../../../types/result/module.f.js';
3
- import { validate } from '../../../types/rtti/validate/module.f.js';
1
+ import { assert, assertEq } from '../../asserts/module.f.js';
2
+ import { ok, error } from '../../types/result/module.f.js';
3
+ import { validate } from '../../types/rtti/validate/module.f.js';
4
4
  import { error as errorSchema, decodeRequest, dispatch, rpcError, parseError, invalidRequest, methodNotFound, invalidParams, internalError, } from './module.f.js';
5
5
  const isOk = (r) => r[0] === 'ok';
6
6
  const handlers = {