functionalscript 0.46.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/fjs/basen/cbase32/module.f.mjs +18 -10
  2. package/fjs/basen/cbase32/proof.f.d.mts +3 -0
  3. package/fjs/basen/cbase32/proof.f.mjs +24 -2
  4. package/fjs/basen/proof.f.mjs +8 -16
  5. package/fjs/bnf/ll1/proof.f.d.mts +2 -0
  6. package/fjs/bnf/ll1/proof.f.mjs +33 -18
  7. package/fjs/bnf/module.f.d.mts +2 -2
  8. package/fjs/bnf/module.f.mjs +2 -2
  9. package/fjs/cas/proof.f.mjs +4 -26
  10. package/fjs/ci/common/module.f.mjs +6 -0
  11. package/fjs/ci/config/module.f.d.mts +7 -7
  12. package/fjs/ci/config/module.f.mjs +7 -7
  13. package/fjs/cli/module.f.mjs +14 -10
  14. package/fjs/crypto/vdf/module.f.d.mts +0 -1
  15. package/fjs/crypto/vdf/module.f.mjs +3 -14
  16. package/fjs/dev/module.f.mjs +10 -20
  17. package/fjs/djs/parser/module.f.d.mts +101 -53
  18. package/fjs/djs/parser/module.f.mjs +732 -525
  19. package/fjs/djs/parser/proof.f.d.mts +6 -0
  20. package/fjs/djs/parser/proof.f.mjs +315 -11
  21. package/fjs/djs/parser/types.d.ts +29 -2
  22. package/fjs/djs/tokenizer/module.f.mjs +122 -105
  23. package/fjs/djs/tokenizer/proof.f.mjs +17 -0
  24. package/fjs/djs/transpiler/module.f.mjs +4 -5
  25. package/fjs/djs/transpiler/proof.f.d.mts +2 -0
  26. package/fjs/djs/transpiler/proof.f.mjs +25 -0
  27. package/fjs/edag/amnesia/module.f.d.mts +20 -0
  28. package/fjs/edag/amnesia/module.f.mjs +303 -0
  29. package/fjs/edag/amnesia/proof.f.d.mts +79 -0
  30. package/fjs/edag/amnesia/proof.f.mjs +573 -0
  31. package/fjs/edag/amnesia/types.d.ts +15 -0
  32. package/fjs/edag/module.f.d.mts +410 -0
  33. package/fjs/edag/module.f.mjs +559 -0
  34. package/fjs/edag/proof.f.d.mts +116 -0
  35. package/fjs/edag/proof.f.mjs +686 -0
  36. package/fjs/edag/types.d.ts +55 -0
  37. package/fjs/effects/node/memory/module.d.mts +37 -3
  38. package/fjs/effects/node/memory/module.mjs +38 -4
  39. package/fjs/effects/node/memory/proof.d.mts +5 -2
  40. package/fjs/effects/node/memory/proof.mjs +36 -6
  41. package/fjs/effects/node/module.d.mts +9 -1
  42. package/fjs/effects/node/module.f.d.mts +39 -0
  43. package/fjs/effects/node/module.f.mjs +46 -0
  44. package/fjs/effects/node/module.mjs +234 -32
  45. package/fjs/effects/node/types.d.ts +28 -2
  46. package/fjs/effects/node/virtual/module.f.d.mts +19 -10
  47. package/fjs/effects/node/virtual/module.f.mjs +246 -32
  48. package/fjs/effects/node/virtual/proof.f.d.mts +17 -2
  49. package/fjs/effects/node/virtual/proof.f.mjs +232 -8
  50. package/fjs/effects/node/virtual/types.d.ts +57 -1
  51. package/fjs/emergent_testing/browser/proof.d.mts +55 -0
  52. package/fjs/emergent_testing/browser/proof.mjs +352 -0
  53. package/fjs/emergent_testing/browser/species.proof.d.mts +4 -0
  54. package/fjs/emergent_testing/browser/species.proof.mjs +45 -0
  55. package/fjs/emergent_testing/browser.d.mts +79 -0
  56. package/fjs/emergent_testing/browser.mjs +432 -0
  57. package/fjs/emergent_testing/proof.f.d.mts +1 -1
  58. package/fjs/emergent_testing/proof.f.mjs +2 -2
  59. package/fjs/js/string_escape/module.f.d.mts +47 -0
  60. package/fjs/js/string_escape/module.f.mjs +95 -0
  61. package/fjs/js/string_escape/proof.f.d.mts +8 -0
  62. package/fjs/js/string_escape/proof.f.mjs +68 -0
  63. package/fjs/js/tokenizer/module.f.mjs +14 -12
  64. package/fjs/mcp/cas/module.f.d.mts +1 -7
  65. package/fjs/mcp/cas/module.f.mjs +24 -15
  66. package/fjs/mcp/evo/module.f.mjs +9 -24
  67. package/fjs/mcp/module.f.d.mts +3 -1
  68. package/fjs/mcp/module.f.mjs +4 -2
  69. package/fjs/media/json/rtti/module.f.d.mts +2 -2
  70. package/fjs/media/json/rtti/module.f.mjs +1 -1
  71. package/fjs/media/json/schema/module.f.d.mts +15 -5
  72. package/fjs/media/json/schema/module.f.mjs +42 -15
  73. package/fjs/media/json/schema/proof.f.d.mts +10 -1
  74. package/fjs/media/json/schema/proof.f.mjs +88 -16
  75. package/fjs/media/json/serializer/module.f.mjs +14 -26
  76. package/fjs/media/json/types.d.ts +1 -1
  77. package/fjs/media/lock/module.f.d.mts +8 -3
  78. package/fjs/media/lock/module.f.mjs +9 -3
  79. package/fjs/media/lock/proof.f.d.mts +1 -1
  80. package/fjs/media/lock/proof.f.mjs +2 -2
  81. package/fjs/media/lock/types.d.ts +1 -3
  82. package/fjs/media/module.f.d.mts +20 -13
  83. package/fjs/media/module.f.mjs +29 -16
  84. package/fjs/media/note/module.f.d.mts +14 -7
  85. package/fjs/media/note/module.f.mjs +14 -7
  86. package/fjs/media/note/proof.f.mjs +1 -1
  87. package/fjs/media/proof.f.mjs +5 -5
  88. package/fjs/media/revision/module.f.d.mts +8 -2
  89. package/fjs/media/revision/module.f.mjs +9 -3
  90. package/fjs/media/revision/proof.f.mjs +1 -1
  91. package/fjs/media/revision/types.d.ts +1 -1
  92. package/fjs/media/type/module.f.d.mts +20 -0
  93. package/fjs/media/type/module.f.mjs +74 -0
  94. package/fjs/media/type/proof.f.d.mts +5 -0
  95. package/fjs/media/type/proof.f.mjs +24 -1
  96. package/fjs/module.f.mjs +6 -0
  97. package/fjs/path/module.f.d.mts +60 -3
  98. package/fjs/path/module.f.mjs +138 -14
  99. package/fjs/path/proof.f.d.mts +3 -0
  100. package/fjs/path/proof.f.mjs +298 -2
  101. package/fjs/protocol/json_rpc/module.f.d.mts +65 -28
  102. package/fjs/protocol/json_rpc/module.f.mjs +49 -13
  103. package/fjs/protocol/json_rpc/proof.f.d.mts +4 -0
  104. package/fjs/protocol/json_rpc/proof.f.mjs +29 -0
  105. package/fjs/protocol/json_rpc/types.d.ts +12 -2
  106. package/fjs/protocol/mcp/module.f.d.mts +89 -54
  107. package/fjs/protocol/mcp/module.f.mjs +110 -56
  108. package/fjs/protocol/mcp/proof.f.d.mts +13 -0
  109. package/fjs/protocol/mcp/proof.f.mjs +107 -7
  110. package/fjs/protocol/mcp/stdio/module.f.mjs +3 -4
  111. package/fjs/protocol/mcp/stdio/proof.f.mjs +2 -2
  112. package/fjs/protocol/mcp/types.d.ts +14 -2
  113. package/fjs/text/utf8/module.f.mjs +27 -1
  114. package/fjs/text/utf8/proof.f.mjs +37 -0
  115. package/fjs/types/array/module.f.mjs +18 -11
  116. package/fjs/types/array/proof.f.mjs +27 -1
  117. package/fjs/types/bigfloat/module.f.d.mts +48 -9
  118. package/fjs/types/bigfloat/module.f.mjs +147 -59
  119. package/fjs/types/bigfloat/proof.f.d.mts +2 -0
  120. package/fjs/types/bigfloat/proof.f.mjs +136 -4
  121. package/fjs/types/bigfloat/types.d.ts +17 -0
  122. package/fjs/types/function/module.f.d.mts +11 -2
  123. package/fjs/types/function/module.f.mjs +20 -2
  124. package/fjs/types/function/proof.f.mjs +5 -1
  125. package/fjs/types/phantom/types.d.ts +21 -0
  126. package/fjs/types/result/module.f.d.mts +4 -4
  127. package/fjs/types/result/module.f.mjs +2 -2
  128. package/fjs/types/result/proof.f.d.mts +6 -2
  129. package/fjs/types/result/proof.f.mjs +17 -1
  130. package/fjs/types/rtti/common/module.f.d.mts +78 -5
  131. package/fjs/types/rtti/common/module.f.mjs +168 -3
  132. package/fjs/types/rtti/common/proof.f.d.mts +14 -0
  133. package/fjs/types/rtti/common/proof.f.mjs +61 -2
  134. package/fjs/types/rtti/common/types.d.ts +23 -1
  135. package/fjs/types/rtti/data/module.f.d.mts +57 -1
  136. package/fjs/types/rtti/data/module.f.mjs +241 -47
  137. package/fjs/types/rtti/data/proof.f.d.mts +10 -0
  138. package/fjs/types/rtti/data/proof.f.mjs +272 -39
  139. package/fjs/types/rtti/data/types.d.ts +14 -7
  140. package/fjs/types/rtti/host.proof.d.mts +33 -0
  141. package/fjs/types/rtti/host.proof.mjs +170 -0
  142. package/fjs/types/rtti/module.f.d.mts +51 -6
  143. package/fjs/types/rtti/module.f.mjs +50 -3
  144. package/fjs/types/rtti/parse/module.f.d.mts +31 -24
  145. package/fjs/types/rtti/parse/module.f.mjs +197 -59
  146. package/fjs/types/rtti/parse/proof.f.d.mts +37 -2
  147. package/fjs/types/rtti/parse/proof.f.mjs +171 -47
  148. package/fjs/types/rtti/proof.f.d.mts +6 -0
  149. package/fjs/types/rtti/proof.f.mjs +42 -1
  150. package/fjs/types/rtti/ts/module.f.d.mts +9 -3
  151. package/fjs/types/rtti/ts/module.f.mjs +68 -15
  152. package/fjs/types/rtti/ts/proof.f.d.mts +42 -2
  153. package/fjs/types/rtti/ts/proof.f.mjs +139 -6
  154. package/fjs/types/rtti/ts/types.d.ts +164 -16
  155. package/fjs/types/rtti/types.d.ts +31 -3
  156. package/fjs/types/rtti/validate/module.f.d.mts +120 -0
  157. package/fjs/types/rtti/validate/module.f.mjs +351 -0
  158. package/fjs/types/rtti/validate/proof.f.d.mts +154 -0
  159. package/fjs/types/rtti/validate/proof.f.mjs +781 -0
  160. package/fjs/types/ts/types.d.ts +4 -0
  161. package/fjs/web/module.f.d.mts +115 -0
  162. package/fjs/web/module.f.mjs +581 -0
  163. package/fjs/web/proof.f.d.mts +39 -0
  164. package/fjs/web/proof.f.mjs +413 -0
  165. package/fjs/web/types.d.ts +39 -0
  166. package/fjs/website/browser-prepare.d.mts +5 -0
  167. package/fjs/website/browser-prepare.mjs +115 -0
  168. package/fjs/website/browser-source.d.mts +37 -0
  169. package/fjs/website/browser-source.mjs +202 -0
  170. package/fjs/website/browser-source.proof.d.mts +31 -0
  171. package/fjs/website/browser-source.proof.mjs +162 -0
  172. package/fjs/website/browser.d.mts +5 -0
  173. package/fjs/website/browser.mjs +8 -0
  174. package/fjs/website/module.f.d.mts +3 -3
  175. package/fjs/website/module.f.mjs +62 -9
  176. package/fjs/website/proof.f.d.mts +4 -0
  177. package/fjs/website/proof.f.mjs +18 -3
  178. package/package.json +2 -3
@@ -1,4 +1,4 @@
1
- import { concat, isProperPrefix, join, normalize, relativize, toPosix } from "./module.f.mjs"
1
+ import { concat, escapes, isProperPrefix, join, normalize, parse, relativize, root, toPosix } from "./module.f.mjs"
2
2
  import { assertEq } from '../asserts/module.f.mjs'
3
3
 
4
4
  const normalizeTest = [
@@ -18,6 +18,193 @@ const normalizeTest = [
18
18
  const norm = normalize("../../dir/../file.json")
19
19
  assertEq(norm, "../../file.json")
20
20
  },
21
+ // A root survives normalization: it is not a segment, so it is not one of
22
+ // the empty ones the fold drops.
23
+ () => {
24
+ const norm = normalize("/a/b")
25
+ assertEq(norm, "/a/b")
26
+ },
27
+ () => {
28
+ const norm = normalize("//srv/share")
29
+ assertEq(norm, "//srv/share")
30
+ },
31
+ () => {
32
+ const norm = normalize("/")
33
+ assertEq(norm, "/")
34
+ },
35
+ // `..` has no parent of the root to name, so it is dropped — while the
36
+ // same input without a root keeps it.
37
+ () => {
38
+ const norm = normalize("/a/../..")
39
+ assertEq(norm, "/")
40
+ },
41
+ () => {
42
+ const norm = normalize("a/../..")
43
+ assertEq(norm, "..")
44
+ },
45
+ // A drive letter is an ordinary segment and needs no case of its own.
46
+ () => {
47
+ const norm = normalize("C:/a/b")
48
+ assertEq(norm, "C:/a/b")
49
+ },
50
+ () => {
51
+ const norm = normalize("C:\\a\\b")
52
+ assertEq(norm, "C:/a/b")
53
+ },
54
+ // An *interior* empty segment is still noise: only the leading one is a root.
55
+ () => {
56
+ const norm = normalize("a//b")
57
+ assertEq(norm, "a/b")
58
+ },
59
+ () => {
60
+ const norm = normalize("")
61
+ assertEq(norm, "")
62
+ },
63
+ ]
64
+
65
+ const escapesTest = [
66
+ // A `..` that cancels a real segment does not escape; one with nothing
67
+ // left to cancel does. `parse` cannot answer this — it folds with the root
68
+ // in place, which is exactly what removes the escaping `..`.
69
+ () => {
70
+ assertEq(escapes("/a/../b"), false)
71
+ },
72
+ () => {
73
+ assertEq(escapes("/../b"), true)
74
+ },
75
+ () => {
76
+ assertEq(escapes("a/../../b"), true)
77
+ },
78
+ () => {
79
+ assertEq(escapes("a/b/../c"), false)
80
+ },
81
+ // Taking the root off is not the same as dropping one leading `/`: these
82
+ // remainders read as rooted again, and a second fold would clamp the very
83
+ // `..` being looked for.
84
+ () => {
85
+ assertEq(escapes("///../secret"), true)
86
+ },
87
+ () => {
88
+ assertEq(escapes("/C:/../../secret"), true)
89
+ },
90
+ () => {
91
+ assertEq(escapes("//../secret"), true)
92
+ },
93
+ () => {
94
+ assertEq(escapes("C:/../secret"), true)
95
+ },
96
+ () => {
97
+ assertEq(escapes(""), false)
98
+ },
99
+ ]
100
+
101
+ const rootTest = [
102
+ () => {
103
+ const r = root("a/b")
104
+ assertEq(r, "")
105
+ },
106
+ () => {
107
+ const r = root("/a/b")
108
+ assertEq(r, "/")
109
+ },
110
+ () => {
111
+ const r = root("//srv/share")
112
+ assertEq(r, "//")
113
+ },
114
+ // Three or more leading slashes are an ordinary root followed by empty
115
+ // segments, which is what POSIX requires; exactly two are the UNC case.
116
+ () => {
117
+ const r = root("///a")
118
+ assertEq(r, "/")
119
+ },
120
+ () => {
121
+ const norm = normalize("///a")
122
+ assertEq(norm, "/a")
123
+ },
124
+ // Windows spells the UNC root with backslashes.
125
+ () => {
126
+ const r = root("\\\\srv\\share")
127
+ assertEq(r, "//")
128
+ },
129
+ // A Windows drive is a root, so `..` cannot climb off it — `C:\\..` is
130
+ // `C:\\` on Windows, not the parent of the drive.
131
+ () => {
132
+ const r = root("C:/a")
133
+ assertEq(r, "C:/")
134
+ },
135
+ () => {
136
+ const r = root("c:/a")
137
+ assertEq(r, "c:/")
138
+ },
139
+ () => {
140
+ const norm = normalize("C:/..")
141
+ assertEq(norm, "C:/")
142
+ },
143
+ () => {
144
+ const norm = normalize("C:/a/../../..")
145
+ assertEq(norm, "C:/")
146
+ },
147
+ () => {
148
+ const norm = normalize("C:/")
149
+ assertEq(norm, "C:/")
150
+ },
151
+ // Only the absolute spelling roots. A bare `C:` and the drive-relative
152
+ // `C:foo` — the current directory *on* drive C — stay ordinary segments,
153
+ // and a prefix that is not a single letter is not a drive at all.
154
+ () => {
155
+ const r = root("C:")
156
+ assertEq(r, "")
157
+ },
158
+ () => {
159
+ const r = root("C:a/b")
160
+ assertEq(r, "")
161
+ },
162
+ () => {
163
+ const r = root("1:/x")
164
+ assertEq(r, "")
165
+ },
166
+ () => {
167
+ const r = root("ab:/x")
168
+ assertEq(r, "")
169
+ },
170
+ // The UNC root stops at `//`: `server/share` are ordinary segments, so a
171
+ // `..` can still climb past a share. Parsing them into the root would let
172
+ // a `../` fold into it, which is what a root must never allow.
173
+ () => {
174
+ const c = concat("//srv/share")("../..")
175
+ assertEq(c, "//")
176
+ },
177
+ () => {
178
+ const p = parse("//a/b")
179
+ assertEq(join(...p), "a/b")
180
+ },
181
+ ]
182
+
183
+ const parseTest = [
184
+ // `parse` answers with segments only, so an absolute and a relative path
185
+ // that name the same segments are indistinguishable through it. `root`
186
+ // is what tells them apart.
187
+ () => {
188
+ const p = parse("/a/b")
189
+ assertEq(join(...p), "a/b")
190
+ },
191
+ () => {
192
+ const p = parse("a/b")
193
+ assertEq(join(...p), "a/b")
194
+ },
195
+ // The root still reaches the fold: a `..` that would escape it is dropped.
196
+ () => {
197
+ const p = parse("/..")
198
+ assertEq(p.length, 0)
199
+ },
200
+ () => {
201
+ const p = parse("..")
202
+ assertEq(join(...p), "..")
203
+ },
204
+ () => {
205
+ const p = parse("../..")
206
+ assertEq(join(...p), "../..")
207
+ },
21
208
  ]
22
209
 
23
210
  const concatTest = [
@@ -33,6 +220,115 @@ const concatTest = [
33
220
  const c = concat("a/../b/..")("c")
34
221
  assertEq(c, "c")
35
222
  },
223
+ // The root comes from `a`, so `concat` no longer turns an absolute path
224
+ // into a relative one. This is the shape `fjs compile` resolves imports
225
+ // with: `concat(concat(path)('..'))(importPath)`.
226
+ () => {
227
+ const c = concat("/a/b/m.f.js")("..")
228
+ assertEq(c, "/a/b")
229
+ },
230
+ () => {
231
+ const c = concat("//srv/share/m.f.js")("..")
232
+ assertEq(c, "//srv/share")
233
+ },
234
+ () => {
235
+ const c = concat("/a/b")("../c")
236
+ assertEq(c, "/a/c")
237
+ },
238
+ () => {
239
+ const c = concat("/")("y")
240
+ assertEq(c, "/y")
241
+ },
242
+ () => {
243
+ const c = concat("/a")("../..")
244
+ assertEq(c, "/")
245
+ },
246
+ () => {
247
+ const c = concat("C:/a/b/m.f.js")("..")
248
+ assertEq(c, "C:/a/b")
249
+ },
250
+ () => {
251
+ const c = concat(concat("C:/a/m.f.js")(".."))("../../lib.f.js")
252
+ assertEq(c, "C:/lib.f.js")
253
+ },
254
+ // A bare drive is not a root, and `concat` does not make it one: joined
255
+ // with a separator, `C:` and `dir` would be the drive root `C:/dir`
256
+ // instead of the drive-relative `C:dir` — a different place on the disk.
257
+ () => {
258
+ const c = concat("C:")("dir")
259
+ assertEq(c, "C:dir")
260
+ },
261
+ () => {
262
+ const r = root(concat("C:")("dir"))
263
+ assertEq(r, "")
264
+ },
265
+ // The drive-absolute spelling is still reachable, and still a root.
266
+ () => {
267
+ const c = concat("C:/")("dir")
268
+ assertEq(c, "C:/dir")
269
+ },
270
+ () => {
271
+ const r = root(concat("C:/")("dir"))
272
+ assertEq(r, "C:/")
273
+ },
274
+ // Only a real bare drive skips the separator: the letter's case does not
275
+ // matter, a non-letter is not a drive, and `C:foo` is longer than a bare
276
+ // drive — reading it as one would join it to `C:foox`.
277
+ () => {
278
+ const c = concat("ab:")("dir")
279
+ assertEq(c, "ab:/dir")
280
+ },
281
+ () => {
282
+ const c = concat("c:")("dir")
283
+ assertEq(c, "c:dir")
284
+ },
285
+ () => {
286
+ const c = concat("1:")("dir")
287
+ assertEq(c, "1:/dir")
288
+ },
289
+ () => {
290
+ const c = concat("C:foo")("x")
291
+ assertEq(c, "C:foo/x")
292
+ },
293
+ () => {
294
+ const c = concat("C:")("/abs")
295
+ assertEq(c, "/abs")
296
+ },
297
+ // The left side is folded before its root is read, so every spelling of a
298
+ // bare drive is one — testing the unfolded text caught only the literal.
299
+ () => {
300
+ const c = concat("./C:")("dir")
301
+ assertEq(c, "C:dir")
302
+ },
303
+ () => {
304
+ const c = concat("x/../C:")("dir")
305
+ assertEq(c, "C:dir")
306
+ },
307
+ // Whatever `a` spells, appending a relative `b` leaves its root alone.
308
+ () => {
309
+ ["C:", "./C:", "x/../C:", "C:/", "./a", "/a", "//a", "", "."].forEach(a =>
310
+ assertEq(root(concat(a)("dir")), root(normalize(a)), a))
311
+ },
312
+ // An absolute `b` names a path on its own, so it replaces `a` rather than
313
+ // being appended to it.
314
+ () => {
315
+ const c = concat("a")("/abs/x")
316
+ assertEq(c, "/abs/x")
317
+ },
318
+ () => {
319
+ const c = concat("/a/b")("//srv/share")
320
+ assertEq(c, "//srv/share")
321
+ },
322
+ // The separator `concat` inserts is not a root: with an empty `a`, a
323
+ // leading `..` in `b` still has nothing to escape.
324
+ () => {
325
+ const c = concat("")("../lib.f.js")
326
+ assertEq(c, "../lib.f.js")
327
+ },
328
+ () => {
329
+ const c = concat("")("./lib.f.js")
330
+ assertEq(c, "lib.f.js")
331
+ },
36
332
  ]
37
333
 
38
334
  const joinTest = [
@@ -122,4 +418,4 @@ const isProperPrefixTest = [
122
418
  },
123
419
  ]
124
420
 
125
- export const proof = { normalizeTest, concatTest, joinTest, relativizeTest, toPosixTest, isProperPrefixTest }
421
+ export const proof = { normalizeTest, escapesTest, rootTest, parseTest, concatTest, joinTest, relativizeTest, toPosixTest, isProperPrefixTest }
@@ -15,74 +15,79 @@
15
15
  * @module
16
16
  *
17
17
  * @import { Unknown } from '../../media/json/types.ts'
18
- * @import { Id, RpcError, Handlers, Response } from './types.ts'
18
+ * @import { Id, RpcError, Handlers, Response, SuccessResponse, ErrorResponse } from './types.ts'
19
19
  */
20
20
  import type { Unknown } from '../../media/json/types.ts';
21
- import type { RpcError, Handlers, Response } from './types.ts';
21
+ import type { Id, RpcError, Handlers, Response } from './types.ts';
22
22
  export declare const jsonrpc: '2.0';
23
23
  /** Request/response identifier: a string, a number, or `null`. */
24
- export declare const _id: import("../../types/rtti/types.ts").Or<[import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
24
+ export declare const _id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
25
25
  /**
26
26
  * A request or notification envelope. `id` present → request (a response is
27
27
  * expected); `id` absent → notification (no response). `params` is optional.
28
28
  *
29
+ * `open`, as every envelope in this module is: a bare struct is closed, and a
30
+ * peer implementing a later revision of the protocol may send members this one
31
+ * does not name — rejecting those outright is the opposite of what a wire
32
+ * format wants. Do not drop the wrapper.
33
+ *
29
34
  * https://www.jsonrpc.org/specification#request_object
30
35
  */
31
- export declare const request: {
36
+ export declare const request: import("../../types/rtti/types.ts").Rest<{
32
37
  readonly jsonrpc: "2.0";
33
38
  readonly method: import("../../types/rtti/types.ts")._Type0<"string">;
34
39
  readonly params: import("../../types/rtti/types.ts").Or<readonly [() => readonly ['or', typeof import("../../media/json/rtti/module.f.mjs").primitive, typeof import("../../media/json/rtti/module.f.mjs").object, typeof import("../../media/json/rtti/module.f.mjs").array], undefined]>;
35
- readonly id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts").Or<[import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>, undefined]>;
36
- };
37
- /** The JSON-RPC error object. */
38
- export declare const error: {
40
+ readonly id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>, undefined]>;
41
+ }, import("../../types/rtti/types.ts").Unknown>;
42
+ /** The JSON-RPC error object — `open`, for the reason {@link request} gives. */
43
+ export declare const error: import("../../types/rtti/types.ts").Rest<{
39
44
  readonly code: import("../../types/rtti/types.ts")._Type0<"number">;
40
45
  readonly message: import("../../types/rtti/types.ts")._Type0<"string">;
41
46
  readonly data: import("../../types/rtti/types.ts").Or<readonly [() => readonly ['or', typeof import("../../media/json/rtti/module.f.mjs").primitive, typeof import("../../media/json/rtti/module.f.mjs").object, typeof import("../../media/json/rtti/module.f.mjs").array], undefined]>;
42
- };
43
- export declare const successResponse: {
47
+ }, import("../../types/rtti/types.ts").Unknown>;
48
+ export declare const successResponse: import("../../types/rtti/types.ts").Rest<{
44
49
  readonly jsonrpc: "2.0";
45
50
  readonly result: () => readonly ['or', typeof import("../../media/json/rtti/module.f.mjs").primitive, typeof import("../../media/json/rtti/module.f.mjs").object, typeof import("../../media/json/rtti/module.f.mjs").array];
46
- readonly id: import("../../types/rtti/types.ts").Or<[import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
47
- };
48
- export declare const errorResponse: {
51
+ readonly id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
52
+ }, import("../../types/rtti/types.ts").Unknown>;
53
+ export declare const errorResponse: import("../../types/rtti/types.ts").Rest<{
49
54
  readonly jsonrpc: "2.0";
50
- readonly error: {
55
+ readonly error: import("../../types/rtti/types.ts").Rest<{
51
56
  readonly code: import("../../types/rtti/types.ts")._Type0<"number">;
52
57
  readonly message: import("../../types/rtti/types.ts")._Type0<"string">;
53
58
  readonly data: import("../../types/rtti/types.ts").Or<readonly [() => readonly ['or', typeof import("../../media/json/rtti/module.f.mjs").primitive, typeof import("../../media/json/rtti/module.f.mjs").object, typeof import("../../media/json/rtti/module.f.mjs").array], undefined]>;
54
- };
55
- readonly id: import("../../types/rtti/types.ts").Or<[import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
56
- };
59
+ }, import("../../types/rtti/types.ts").Unknown>;
60
+ readonly id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
61
+ }, import("../../types/rtti/types.ts").Unknown>;
57
62
  /**
58
63
  * A response envelope: either a success (`result`) or an error (`error`).
59
64
  * Derived from the rtti schema via `Ts<>` — the same declaration is the
60
- * runtime decoder and the static type, with no drift. rtti structs are open
65
+ * runtime decoder and the static type, with no drift. Both arms say `open`
61
66
  * (extra keys allowed), so "result XOR error" is not enforced at runtime; in
62
67
  * practice the dispatcher only ever constructs one or the other.
63
68
  *
64
69
  * https://www.jsonrpc.org/specification#response_object
65
70
  */
66
- export declare const response: import("../../types/rtti/types.ts").Or<[{
71
+ export declare const response: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts").Rest<{
67
72
  readonly jsonrpc: "2.0";
68
73
  readonly result: () => readonly ['or', typeof import("../../media/json/rtti/module.f.mjs").primitive, typeof import("../../media/json/rtti/module.f.mjs").object, typeof import("../../media/json/rtti/module.f.mjs").array];
69
- readonly id: import("../../types/rtti/types.ts").Or<[import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
70
- }, {
74
+ readonly id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
75
+ }, import("../../types/rtti/types.ts").Unknown>, import("../../types/rtti/types.ts").Rest<{
71
76
  readonly jsonrpc: "2.0";
72
- readonly error: {
77
+ readonly error: import("../../types/rtti/types.ts").Rest<{
73
78
  readonly code: import("../../types/rtti/types.ts")._Type0<"number">;
74
79
  readonly message: import("../../types/rtti/types.ts")._Type0<"string">;
75
80
  readonly data: import("../../types/rtti/types.ts").Or<readonly [() => readonly ['or', typeof import("../../media/json/rtti/module.f.mjs").primitive, typeof import("../../media/json/rtti/module.f.mjs").object, typeof import("../../media/json/rtti/module.f.mjs").array], undefined]>;
76
- };
77
- readonly id: import("../../types/rtti/types.ts").Or<[import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
78
- }]>;
81
+ }, import("../../types/rtti/types.ts").Unknown>;
82
+ readonly id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>;
83
+ }, import("../../types/rtti/types.ts").Unknown>]>;
79
84
  /** Decodes an untrusted value as a JSON-RPC request / notification. */
80
- export declare const decodeRequest: import("../../types/rtti/parse/types.ts").Parse<{
85
+ export declare const decodeRequest: import("../../types/rtti/parse/types.ts").Parse<import("../../types/rtti/types.ts").Rest<{
81
86
  readonly jsonrpc: "2.0";
82
87
  readonly method: import("../../types/rtti/types.ts")._Type0<"string">;
83
88
  readonly params: import("../../types/rtti/types.ts").Or<readonly [() => readonly ['or', typeof import("../../media/json/rtti/module.f.mjs").primitive, typeof import("../../media/json/rtti/module.f.mjs").object, typeof import("../../media/json/rtti/module.f.mjs").array], undefined]>;
84
- readonly id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts").Or<[import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>, undefined]>;
85
- }>;
89
+ readonly id: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts")._Type0<"string">, import("../../types/rtti/types.ts")._Type0<"number">, null]>, undefined]>;
90
+ }, import("../../types/rtti/types.ts").Unknown>>;
86
91
  /**
87
92
  * Builds an `RpcError` with the given `code` and `message` (no `data`).
88
93
  * @type {(code: number) => (message: string) => RpcError}
@@ -113,6 +118,38 @@ export declare const internalError: import("../../types/rtti/ts/types.ts").Struc
113
118
  readonly message: import("../../types/rtti/types.ts")._Type0<"string">;
114
119
  readonly data: import("../../types/rtti/types.ts").Or<readonly [() => readonly ['or', typeof import("../../media/json/rtti/module.f.mjs").primitive, typeof import("../../media/json/rtti/module.f.mjs").object, typeof import("../../media/json/rtti/module.f.mjs").array], undefined]>;
115
120
  }>;
121
+ /**
122
+ * The error half of the response envelope: `{ jsonrpc, error, id }`.
123
+ *
124
+ * Exported as one of a pair with {@link successResponseOf} — the `Response`
125
+ * schema, `jsonrpc`, `Id` and `RpcError` are all owned here, so the two shapes
126
+ * built from them are too. Every protocol layered on this module needs both
127
+ * (`fjs/protocol/mcp` and its stdio transport are the two consumers today),
128
+ * and a private constructor is what made each of them re-roll its own.
129
+ *
130
+ * It answers the `Response` union rather than {@link ErrorResponse}, the branch
131
+ * it always builds. That is deliberate: every consumer in the tree is a
132
+ * dispatcher answering either arm — `dispatch` here, `mcpStep` and the stdio
133
+ * transport in `fjs/protocol/mcp` — and `Handle` is defined in terms of the
134
+ * union, so the branch type would have to be widened again at each of them.
135
+ * {@link ErrorResponse} is exported for a caller that does want it.
136
+ *
137
+ * @type {(id: Id) => (error: RpcError) => Response}
138
+ */
139
+ export declare const errorResponseOf: (id: Id) => (error: RpcError) => Response;
140
+ /**
141
+ * The success half of the response envelope: `{ jsonrpc, result, id }`.
142
+ *
143
+ * The `…Of` suffix pairs with {@link errorResponseOf}, and both name the
144
+ * already-exported `successResponse` / `errorResponse` schemas they build a
145
+ * value of.
146
+ *
147
+ * It answers the union for the reason {@link errorResponseOf} does, and
148
+ * {@link SuccessResponse} names the branch for a caller that wants it.
149
+ *
150
+ * @type {(id: Id) => (result: Unknown) => Response}
151
+ */
152
+ export declare const successResponseOf: (id: Id) => (result: Unknown) => Response;
116
153
  /**
117
154
  * Dispatches an already-parsed JSON-RPC value against `handlers`.
118
155
  *
@@ -15,11 +15,11 @@
15
15
  * @module
16
16
  *
17
17
  * @import { Unknown } from '../../media/json/types.ts'
18
- * @import { Id, RpcError, Handlers, Response } from './types.ts'
18
+ * @import { Id, RpcError, Handlers, Response, SuccessResponse, ErrorResponse } from './types.ts'
19
19
  */
20
20
 
21
21
  import { at } from '../../types/object/module.f.mjs'
22
- import { number, string, or, option } from '../../types/rtti/module.f.mjs'
22
+ import { number, open, string, or, option } from '../../types/rtti/module.f.mjs'
23
23
  import { parse } from '../../types/rtti/parse/module.f.mjs'
24
24
  import { unknown } from '../../media/json/rtti/module.f.mjs'
25
25
 
@@ -32,29 +32,34 @@ export const _id = or(string, number, null)
32
32
  * A request or notification envelope. `id` present → request (a response is
33
33
  * expected); `id` absent → notification (no response). `params` is optional.
34
34
  *
35
+ * `open`, as every envelope in this module is: a bare struct is closed, and a
36
+ * peer implementing a later revision of the protocol may send members this one
37
+ * does not name — rejecting those outright is the opposite of what a wire
38
+ * format wants. Do not drop the wrapper.
39
+ *
35
40
  * https://www.jsonrpc.org/specification#request_object
36
41
  */
37
- export const request = /** @type {const} */ ({
42
+ export const request = open(/** @type {const} */ ({
38
43
  jsonrpc,
39
44
  method: string,
40
45
  params: option(unknown),
41
46
  id: option(_id),
42
- })
47
+ }))
43
48
 
44
- /** The JSON-RPC error object. */
45
- export const error = /** @type {const} */ ({
49
+ /** The JSON-RPC error object — `open`, for the reason {@link request} gives. */
50
+ export const error = open(/** @type {const} */ ({
46
51
  code: number,
47
52
  message: string,
48
53
  data: option(unknown),
49
- })
54
+ }))
50
55
 
51
- export const successResponse = /** @type {const} */ ({ jsonrpc, result: unknown, id: _id })
52
- export const errorResponse = /** @type {const} */ ({ jsonrpc, error, id: _id })
56
+ export const successResponse = open(/** @type {const} */ ({ jsonrpc, result: unknown, id: _id }))
57
+ export const errorResponse = open(/** @type {const} */ ({ jsonrpc, error, id: _id }))
53
58
 
54
59
  /**
55
60
  * A response envelope: either a success (`result`) or an error (`error`).
56
61
  * Derived from the rtti schema via `Ts<>` — the same declaration is the
57
- * runtime decoder and the static type, with no drift. rtti structs are open
62
+ * runtime decoder and the static type, with no drift. Both arms say `open`
58
63
  * (extra keys allowed), so "result XOR error" is not enforced at runtime; in
59
64
  * practice the dispatcher only ever constructs one or the other.
60
65
  *
@@ -78,8 +83,39 @@ export const methodNotFound = rpcError(-32601)('Method not found')
78
83
  export const invalidParams = rpcError(-32602)('Invalid params')
79
84
  export const internalError = rpcError(-32603)('Internal error')
80
85
 
81
- /** @type {(id: Id) => (error: RpcError) => Response} */
82
- const errorResponseOf = id => error => ({ jsonrpc, error, id })
86
+ /**
87
+ * The error half of the response envelope: `{ jsonrpc, error, id }`.
88
+ *
89
+ * Exported as one of a pair with {@link successResponseOf} — the `Response`
90
+ * schema, `jsonrpc`, `Id` and `RpcError` are all owned here, so the two shapes
91
+ * built from them are too. Every protocol layered on this module needs both
92
+ * (`fjs/protocol/mcp` and its stdio transport are the two consumers today),
93
+ * and a private constructor is what made each of them re-roll its own.
94
+ *
95
+ * It answers the `Response` union rather than {@link ErrorResponse}, the branch
96
+ * it always builds. That is deliberate: every consumer in the tree is a
97
+ * dispatcher answering either arm — `dispatch` here, `mcpStep` and the stdio
98
+ * transport in `fjs/protocol/mcp` — and `Handle` is defined in terms of the
99
+ * union, so the branch type would have to be widened again at each of them.
100
+ * {@link ErrorResponse} is exported for a caller that does want it.
101
+ *
102
+ * @type {(id: Id) => (error: RpcError) => Response}
103
+ */
104
+ export const errorResponseOf = id => error => ({ jsonrpc, error, id })
105
+
106
+ /**
107
+ * The success half of the response envelope: `{ jsonrpc, result, id }`.
108
+ *
109
+ * The `…Of` suffix pairs with {@link errorResponseOf}, and both name the
110
+ * already-exported `successResponse` / `errorResponse` schemas they build a
111
+ * value of.
112
+ *
113
+ * It answers the union for the reason {@link errorResponseOf} does, and
114
+ * {@link SuccessResponse} names the branch for a caller that wants it.
115
+ *
116
+ * @type {(id: Id) => (result: Unknown) => Response}
117
+ */
118
+ export const successResponseOf = id => result => ({ jsonrpc, result, id })
83
119
 
84
120
  /**
85
121
  * Dispatches an already-parsed JSON-RPC value against `handlers`.
@@ -110,6 +146,6 @@ export const dispatch = handlers => value => {
110
146
  }
111
147
  const [t2, result] = handler(params)
112
148
  return t2 === 'ok'
113
- ? { jsonrpc, result, id }
149
+ ? successResponseOf(id)(result)
114
150
  : errorResponseOf(id)(result)
115
151
  }
@@ -38,4 +38,8 @@ export declare const proof: {
38
38
  invalidRequest: () => void;
39
39
  notification: () => void;
40
40
  };
41
+ responseOf: {
42
+ success: () => void;
43
+ error: () => void;
44
+ };
41
45
  };
@@ -15,6 +15,9 @@ import {
15
15
  methodNotFound,
16
16
  invalidParams,
17
17
  internalError,
18
+ response,
19
+ errorResponseOf,
20
+ successResponseOf,
18
21
  } from './module.f.mjs'
19
22
 
20
23
  /** @type {(r: readonly [string, unknown]) => boolean} */
@@ -102,4 +105,30 @@ export const proof = {
102
105
  assertEq(r, null)
103
106
  },
104
107
  },
108
+
109
+ // The two envelope constructors are public API, so they are proven
110
+ // directly rather than only through `dispatch`. Each result is parsed back
111
+ // with this module's own `response` schema, which is the fact worth
112
+ // pinning: the constructor and the schema describing the shape it builds
113
+ // cannot drift apart.
114
+ responseOf: {
115
+ success: () => {
116
+ const r = successResponseOf(1)('pong')
117
+ assertEq(r.jsonrpc, '2.0')
118
+ assert('result' in r && r.result === 'pong', r)
119
+ assertEq(r.id, 1)
120
+ assert(isOk(parse(response)(r)), r)
121
+ },
122
+ // A non-`null` id on purpose: an `id` forced to `null` is a mutation
123
+ // this case would not see if it asked for `null` to begin with, and a
124
+ // string exercises the other arm of `Id` besides. `errorResponseOf`'s
125
+ // `null` id is covered by `dispatch.invalidRequest`.
126
+ error: () => {
127
+ const r = errorResponseOf('abc')(parseError)
128
+ assertEq(r.jsonrpc, '2.0')
129
+ assert('error' in r && r.error.code === -32700, r)
130
+ assertEq(r.id, 'abc')
131
+ assert(isOk(parse(response)(r)), r)
132
+ },
133
+ },
105
134
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import type { Unknown } from '../../media/json/types.ts';
9
9
  import type { Result } from '../../types/result/types.ts';
10
- import type { _id, request, error as errorSchema, response } from './module.f.mjs';
10
+ import type { _id, request, error as errorSchema, response, successResponse, errorResponse } from './module.f.mjs';
11
11
  import type { Ts } from '../../types/rtti/ts/types.ts';
12
12
  export type Id = Ts<typeof _id>;
13
13
  export type Request = Ts<typeof request>;
@@ -15,13 +15,23 @@ export type RpcError = Ts<typeof errorSchema>;
15
15
  /**
16
16
  * A response envelope: either a success (`result`) or an error (`error`).
17
17
  * Derived from the rtti schema via `Ts<>` — the same declaration is the
18
- * runtime decoder and the static type, with no drift. rtti structs are open
18
+ * runtime decoder and the static type, with no drift. Both arms say `open`
19
19
  * (extra keys allowed), so "result XOR error" is not enforced at runtime; in
20
20
  * practice the dispatcher only ever constructs one or the other.
21
21
  *
22
22
  * https://www.jsonrpc.org/specification#response_object
23
23
  */
24
24
  export type Response = Ts<typeof response>;
25
+ /**
26
+ * The success branch of {@link Response}: `result` is present, `error` is not.
27
+ *
28
+ * `successResponseOf` answers the `Response` union, not this — every consumer
29
+ * in the tree is a dispatcher that returns either arm. Name this where a
30
+ * caller genuinely holds one branch and wants `.result` without an `in` check.
31
+ */
32
+ export type SuccessResponse = Ts<typeof successResponse>;
33
+ /** The error branch of {@link Response}, the mirror of {@link SuccessResponse}. */
34
+ export type ErrorResponse = Ts<typeof errorResponse>;
25
35
  /** A method implementation: maps `params` to a result or an `RpcError`. */
26
36
  export type Handler = (params: Unknown | undefined) => Result<Unknown, RpcError>;
27
37
  /** A `method` name → `Handler` map. */