functionalscript 0.45.0 → 0.46.1

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 (296) hide show
  1. package/README.md +5 -3
  2. package/fjs/asn.1/module.f.mjs +8 -1
  3. package/fjs/asn.1/proof.f.d.mts +10 -0
  4. package/fjs/asn.1/proof.f.mjs +16 -0
  5. package/fjs/basen/base128/module.f.mjs +22 -5
  6. package/fjs/bnf/data/module.f.d.mts +32 -6
  7. package/fjs/bnf/data/module.f.mjs +112 -7
  8. package/fjs/bnf/data/proof.f.d.mts +2 -0
  9. package/fjs/bnf/data/proof.f.mjs +74 -3
  10. package/fjs/bnf/data/types.d.ts +20 -2
  11. package/fjs/bnf/descent/module.f.d.mts +28 -14
  12. package/fjs/bnf/descent/module.f.mjs +122 -77
  13. package/fjs/bnf/descent/proof.f.d.mts +2 -0
  14. package/fjs/bnf/descent/proof.f.mjs +117 -31
  15. package/fjs/bnf/descent/types.d.ts +12 -14
  16. package/fjs/bnf/ll1/module.f.d.mts +64 -22
  17. package/fjs/bnf/ll1/module.f.mjs +214 -154
  18. package/fjs/bnf/ll1/proof.f.d.mts +15 -2
  19. package/fjs/bnf/ll1/proof.f.mjs +323 -149
  20. package/fjs/bnf/ll1/types.d.ts +16 -24
  21. package/fjs/bnf/matcher/module.f.d.mts +66 -0
  22. package/fjs/bnf/matcher/module.f.mjs +81 -0
  23. package/fjs/bnf/matcher/proof.f.d.mts +10 -0
  24. package/fjs/bnf/matcher/proof.f.mjs +79 -0
  25. package/fjs/bnf/matcher/types.d.ts +54 -0
  26. package/fjs/bnf/testlib.f.d.mts +31 -0
  27. package/fjs/bnf/testlib.f.mjs +80 -0
  28. package/fjs/cas/cli/module.f.d.mts +1 -1
  29. package/fjs/cas/cli/module.f.mjs +14 -20
  30. package/fjs/cas/cli/proof.f.d.mts +1 -3
  31. package/fjs/cas/cli/proof.f.mjs +44 -33
  32. package/fjs/cas/evo/module.f.d.mts +64 -18
  33. package/fjs/cas/evo/module.f.mjs +148 -70
  34. package/fjs/cas/evo/proof.f.d.mts +10 -1
  35. package/fjs/cas/evo/proof.f.mjs +305 -223
  36. package/fjs/cas/evo/types.d.ts +45 -9
  37. package/fjs/cas/module.f.d.mts +18 -32
  38. package/fjs/cas/module.f.mjs +129 -128
  39. package/fjs/cas/proof.f.d.mts +11 -8
  40. package/fjs/cas/proof.f.mjs +259 -147
  41. package/fjs/cas/types.d.ts +24 -10
  42. package/fjs/ci/config/module.f.d.mts +2 -2
  43. package/fjs/ci/config/module.f.mjs +2 -2
  44. package/fjs/ci/module.f.d.mts +5 -5
  45. package/fjs/ci/module.f.mjs +8 -7
  46. package/fjs/ci/nix/module.f.d.mts +7 -5
  47. package/fjs/ci/nix/module.f.mjs +13 -12
  48. package/fjs/ci/nix/proof.f.mjs +2 -2
  49. package/fjs/ci/proof.f.mjs +9 -5
  50. package/fjs/cli/module.f.d.mts +4 -6
  51. package/fjs/cli/module.f.mjs +4 -8
  52. package/fjs/cli/proof.f.mjs +17 -16
  53. package/fjs/cli/types.d.ts +2 -3
  54. package/fjs/common/monoid/types.d.ts +1 -1
  55. package/fjs/crypto/hmac/module.f.mjs +2 -2
  56. package/fjs/crypto/sha2/module.f.mjs +3 -1
  57. package/fjs/crypto/sha2/proof.f.d.mts +1 -0
  58. package/fjs/crypto/sha2/proof.f.mjs +24 -0
  59. package/fjs/crypto/sha2/types.d.ts +11 -0
  60. package/fjs/crypto/sign/module.f.mjs +2 -2
  61. package/fjs/dev/module.f.d.mts +13 -4
  62. package/fjs/dev/module.f.mjs +56 -27
  63. package/fjs/dev/update/module.f.d.mts +9 -4
  64. package/fjs/dev/update/module.f.mjs +14 -10
  65. package/fjs/dev/update/proof.f.d.mts +1 -3
  66. package/fjs/dev/update/proof.f.mjs +10 -5
  67. package/fjs/djs/module.f.d.mts +13 -5
  68. package/fjs/djs/module.f.mjs +31 -16
  69. package/fjs/djs/parser/module.f.d.mts +13 -3
  70. package/fjs/djs/parser/module.f.mjs +117 -16
  71. package/fjs/djs/parser/proof.f.d.mts +5 -1
  72. package/fjs/djs/parser/proof.f.mjs +274 -12
  73. package/fjs/djs/parser/types.d.ts +7 -1
  74. package/fjs/djs/proof.f.d.mts +18 -2
  75. package/fjs/djs/proof.f.mjs +187 -12
  76. package/fjs/djs/serializer/module.f.d.mts +25 -8
  77. package/fjs/djs/serializer/module.f.mjs +61 -15
  78. package/fjs/djs/serializer/proof.f.d.mts +5 -0
  79. package/fjs/djs/serializer/proof.f.mjs +23 -1
  80. package/fjs/djs/tokenizer/module.f.d.mts +17 -5
  81. package/fjs/djs/tokenizer/module.f.mjs +99 -60
  82. package/fjs/djs/tokenizer/proof.f.d.mts +1 -1
  83. package/fjs/djs/tokenizer/proof.f.mjs +74 -67
  84. package/fjs/djs/transpiler/module.f.d.mts +12 -7
  85. package/fjs/djs/transpiler/module.f.mjs +82 -56
  86. package/fjs/djs/transpiler/types.d.ts +7 -3
  87. package/fjs/djs/types.d.ts +7 -1
  88. package/fjs/effects/list/module.f.d.mts +14 -11
  89. package/fjs/effects/list/module.f.mjs +13 -11
  90. package/fjs/effects/list/types.d.ts +27 -7
  91. package/fjs/effects/memory/module.f.d.mts +2 -1
  92. package/fjs/effects/memory/module.f.mjs +3 -4
  93. package/fjs/effects/memory/proof.f.mjs +13 -7
  94. package/fjs/effects/memory/types.d.ts +4 -3
  95. package/fjs/effects/mock/module.f.d.mts +20 -4
  96. package/fjs/effects/mock/module.f.mjs +37 -5
  97. package/fjs/effects/mock/types.d.ts +10 -1
  98. package/fjs/effects/module.d.mts +4 -2
  99. package/fjs/effects/module.f.d.mts +466 -280
  100. package/fjs/effects/module.f.mjs +537 -299
  101. package/fjs/effects/module.mjs +2 -1
  102. package/fjs/effects/node/memory/module.d.mts +4 -2
  103. package/fjs/effects/node/memory/module.mjs +6 -3
  104. package/fjs/effects/node/memory/proof.mjs +10 -4
  105. package/fjs/effects/node/module.d.mts +4 -3
  106. package/fjs/effects/node/module.f.d.mts +168 -33
  107. package/fjs/effects/node/module.f.mjs +256 -52
  108. package/fjs/effects/node/module.mjs +57 -34
  109. package/fjs/effects/node/proof.f.d.mts +28 -2
  110. package/fjs/effects/node/proof.f.mjs +161 -42
  111. package/fjs/effects/node/types.d.ts +106 -18
  112. package/fjs/effects/node/virtual/module.f.d.mts +18 -4
  113. package/fjs/effects/node/virtual/module.f.mjs +110 -68
  114. package/fjs/effects/node/virtual/proof.f.d.mts +28 -2
  115. package/fjs/effects/node/virtual/proof.f.mjs +190 -9
  116. package/fjs/effects/proof.f.d.mts +69 -37
  117. package/fjs/effects/proof.f.mjs +410 -130
  118. package/fjs/effects/types.d.ts +161 -33
  119. package/fjs/emergent_testing/module.f.d.mts +19 -12
  120. package/fjs/emergent_testing/module.f.mjs +93 -33
  121. package/fjs/emergent_testing/proof.f.d.mts +21 -7
  122. package/fjs/emergent_testing/proof.f.mjs +166 -32
  123. package/fjs/emergent_testing/types.d.ts +22 -4
  124. package/fjs/fsm/module.f.d.mts +14 -4
  125. package/fjs/fsm/module.f.mjs +54 -37
  126. package/fjs/fsm/proof.f.d.mts +2 -0
  127. package/fjs/fsm/proof.f.mjs +83 -114
  128. package/fjs/js/keywords/module.f.d.mts +52 -0
  129. package/fjs/js/keywords/module.f.mjs +72 -0
  130. package/fjs/js/keywords/proof.f.d.mts +3 -0
  131. package/fjs/js/keywords/proof.f.mjs +13 -0
  132. package/fjs/js/tokenizer/module.f.d.mts +26 -6
  133. package/fjs/js/tokenizer/module.f.mjs +145 -159
  134. package/fjs/js/tokenizer/proof.f.d.mts +1 -0
  135. package/fjs/js/tokenizer/proof.f.mjs +54 -24
  136. package/fjs/js/tokenizer/types.d.ts +33 -24
  137. package/fjs/mcp/cas/module.f.d.mts +1 -6
  138. package/fjs/mcp/cas/module.f.mjs +55 -51
  139. package/fjs/mcp/cas/proof.f.d.mts +15 -0
  140. package/fjs/mcp/cas/proof.f.mjs +174 -0
  141. package/fjs/mcp/evo/module.f.d.mts +19 -10
  142. package/fjs/mcp/evo/module.f.mjs +48 -27
  143. package/fjs/mcp/evo/proof.f.d.mts +6 -1
  144. package/fjs/mcp/evo/proof.f.mjs +115 -31
  145. package/fjs/mcp/module.f.d.mts +4 -4
  146. package/fjs/mcp/module.f.mjs +6 -6
  147. package/fjs/mcp/proof.f.d.mts +5 -3
  148. package/fjs/mcp/proof.f.mjs +112 -46
  149. package/fjs/media/html/module.f.mjs +1 -1
  150. package/fjs/media/json/extended/module.f.d.mts +82 -0
  151. package/fjs/media/json/extended/module.f.mjs +153 -0
  152. package/fjs/media/json/extended/proof.f.d.mts +42 -0
  153. package/fjs/media/json/extended/proof.f.mjs +127 -0
  154. package/fjs/media/json/extended/types.d.ts +23 -0
  155. package/fjs/media/json/module.f.d.mts +8 -2
  156. package/fjs/media/json/module.f.mjs +43 -41
  157. package/fjs/media/json/number/module.f.d.mts +59 -0
  158. package/fjs/media/json/number/module.f.mjs +136 -0
  159. package/fjs/media/json/number/proof.f.d.mts +24 -0
  160. package/fjs/media/json/number/proof.f.mjs +86 -0
  161. package/fjs/media/json/number/types.d.ts +28 -0
  162. package/fjs/media/json/parser/module.f.d.mts +25 -13
  163. package/fjs/media/json/parser/module.f.mjs +114 -70
  164. package/fjs/media/json/parser/proof.f.d.mts +5 -0
  165. package/fjs/media/json/parser/proof.f.mjs +31 -1
  166. package/fjs/media/json/parser/types.d.ts +33 -14
  167. package/fjs/media/json/rtti/module.f.d.mts +1 -1
  168. package/fjs/media/json/rtti/module.f.mjs +1 -1
  169. package/fjs/media/json/rtti/proof.f.mjs +9 -9
  170. package/fjs/media/json/schema/module.f.mjs +3 -13
  171. package/fjs/media/json/schema/proof.f.d.mts +0 -1
  172. package/fjs/media/json/schema/proof.f.mjs +1 -2
  173. package/fjs/media/json/serializer/module.f.d.mts +32 -1
  174. package/fjs/media/json/serializer/module.f.mjs +64 -2
  175. package/fjs/media/json/tokenizer/module.f.mjs +7 -3
  176. package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
  177. package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
  178. package/fjs/media/json/types.d.ts +36 -10
  179. package/fjs/media/lock/module.f.d.mts +100 -0
  180. package/fjs/media/lock/module.f.mjs +125 -0
  181. package/fjs/media/lock/proof.f.d.mts +33 -0
  182. package/fjs/media/lock/proof.f.mjs +196 -0
  183. package/fjs/media/lock/types.d.ts +15 -0
  184. package/fjs/media/module.f.d.mts +6 -5
  185. package/fjs/media/module.f.mjs +8 -7
  186. package/fjs/media/note/module.f.d.mts +121 -0
  187. package/fjs/media/note/module.f.mjs +131 -0
  188. package/fjs/media/note/proof.f.d.mts +29 -0
  189. package/fjs/media/note/proof.f.mjs +150 -0
  190. package/fjs/media/note/types.d.ts +10 -0
  191. package/fjs/media/proof.f.d.mts +4 -1
  192. package/fjs/media/proof.f.mjs +40 -21
  193. package/fjs/media/revision/module.f.d.mts +78 -7
  194. package/fjs/media/revision/module.f.mjs +119 -12
  195. package/fjs/media/revision/proof.f.d.mts +10 -0
  196. package/fjs/media/revision/proof.f.mjs +88 -0
  197. package/fjs/media/revision/types.d.ts +34 -5
  198. package/fjs/media/type/module.f.d.mts +33 -15
  199. package/fjs/media/type/module.f.mjs +35 -29
  200. package/fjs/media/type/proof.f.d.mts +2 -1
  201. package/fjs/media/type/proof.f.mjs +30 -10
  202. package/fjs/module.f.mjs +29 -8
  203. package/fjs/nanvm/proof.f.mjs +3 -3
  204. package/fjs/nanvm/rust/module.f.mjs +1 -1
  205. package/fjs/nanvm/update/module.f.d.mts +4 -4
  206. package/fjs/nanvm/update/module.f.mjs +8 -9
  207. package/fjs/nanvm/update/proof.f.mjs +4 -3
  208. package/fjs/proof.f.d.mts +3 -3
  209. package/fjs/proof.f.mjs +36 -10
  210. package/fjs/protocol/json_rpc/module.f.d.mts +2 -2
  211. package/fjs/protocol/json_rpc/module.f.mjs +3 -3
  212. package/fjs/protocol/json_rpc/proof.f.mjs +4 -4
  213. package/fjs/protocol/mcp/module.f.d.mts +10 -13
  214. package/fjs/protocol/mcp/module.f.mjs +74 -58
  215. package/fjs/protocol/mcp/proof.f.d.mts +13 -4
  216. package/fjs/protocol/mcp/proof.f.mjs +193 -61
  217. package/fjs/protocol/mcp/stdio/module.f.d.mts +15 -7
  218. package/fjs/protocol/mcp/stdio/module.f.mjs +38 -25
  219. package/fjs/protocol/mcp/stdio/proof.f.d.mts +4 -2
  220. package/fjs/protocol/mcp/stdio/proof.f.mjs +45 -12
  221. package/fjs/protocol/mcp/stdio/types.d.ts +1 -1
  222. package/fjs/protocol/mcp/types.d.ts +17 -6
  223. package/fjs/sul/id/module.f.d.mts +0 -1
  224. package/fjs/sul/id/module.f.mjs +2 -3
  225. package/fjs/sul/level/hash/module.f.mjs +2 -1
  226. package/fjs/sul/level/hash/proof.f.mjs +2 -2
  227. package/fjs/sul/module.f.mjs +18 -13
  228. package/fjs/text/code_point/module.f.d.mts +8 -0
  229. package/fjs/text/code_point/module.f.mjs +8 -1
  230. package/fjs/text/code_point/proof.f.d.mts +1 -0
  231. package/fjs/text/code_point/proof.f.mjs +11 -0
  232. package/fjs/text/sgr/module.f.d.mts +4 -6
  233. package/fjs/text/sgr/module.f.mjs +6 -7
  234. package/fjs/text/utf16/module.f.mjs +7 -2
  235. package/fjs/text/utf16/proof.f.mjs +3 -3
  236. package/fjs/text/utf8/module.f.mjs +4 -2
  237. package/fjs/types/array/module.f.mjs +14 -2
  238. package/fjs/types/bit_vec/module.f.d.mts +0 -2
  239. package/fjs/types/bit_vec/module.f.mjs +46 -32
  240. package/fjs/types/bit_vec/proof.f.mjs +2 -2
  241. package/fjs/types/btree/remove/module.f.mjs +1 -1
  242. package/fjs/types/btree/set/module.f.mjs +9 -11
  243. package/fjs/types/btree/set/proof.f.mjs +12 -0
  244. package/fjs/types/byte_set/module.f.d.mts +11 -4
  245. package/fjs/types/byte_set/module.f.mjs +14 -6
  246. package/fjs/types/byte_set/proof.f.mjs +7 -7
  247. package/fjs/types/function/compare/module.f.mjs +10 -3
  248. package/fjs/types/list/module.f.d.mts +1 -1
  249. package/fjs/types/list/module.f.mjs +1 -1
  250. package/fjs/types/nullable/module.f.d.mts +18 -4
  251. package/fjs/types/nullable/module.f.mjs +21 -4
  252. package/fjs/types/nullable/proof.f.d.mts +4 -0
  253. package/fjs/types/nullable/proof.f.mjs +15 -0
  254. package/fjs/types/object/module.f.d.mts +12 -2
  255. package/fjs/types/object/module.f.mjs +11 -1
  256. package/fjs/types/patricia_trie/module.f.mjs +26 -11
  257. package/fjs/types/result/module.f.d.mts +3 -3
  258. package/fjs/types/result/module.f.mjs +3 -3
  259. package/fjs/types/rtti/common/module.f.d.mts +25 -28
  260. package/fjs/types/rtti/common/module.f.mjs +36 -32
  261. package/fjs/types/rtti/common/proof.f.mjs +5 -5
  262. package/fjs/types/rtti/data/module.f.d.mts +14 -0
  263. package/fjs/types/rtti/data/module.f.mjs +25 -2
  264. package/fjs/types/rtti/data/proof.f.d.mts +1 -0
  265. package/fjs/types/rtti/data/proof.f.mjs +25 -2
  266. package/fjs/types/rtti/parse/module.f.d.mts +24 -14
  267. package/fjs/types/rtti/parse/module.f.mjs +37 -28
  268. package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
  269. package/fjs/types/rtti/parse/proof.f.mjs +33 -14
  270. package/fjs/types/rtti/proof.f.mjs +3 -1
  271. package/fjs/types/rtti/ts/module.f.mjs +13 -17
  272. package/fjs/types/rtti/ts/types.d.ts +14 -1
  273. package/fjs/types/rtti/validate/module.f.d.mts +81 -19
  274. package/fjs/types/rtti/validate/module.f.mjs +107 -61
  275. package/fjs/types/rtti/validate/proof.f.d.mts +13 -10
  276. package/fjs/types/rtti/validate/proof.f.mjs +186 -197
  277. package/fjs/types/sorted_set/module.f.d.mts +18 -0
  278. package/fjs/types/sorted_set/module.f.mjs +22 -0
  279. package/fjs/types/sorted_set/proof.f.d.mts +1 -0
  280. package/fjs/types/sorted_set/proof.f.mjs +16 -1
  281. package/fjs/types/uint8array/module.f.d.mts +1 -1
  282. package/fjs/types/uint8array/module.f.mjs +1 -1
  283. package/fjs/website/module.f.d.mts +3 -3
  284. package/fjs/website/module.f.mjs +4 -7
  285. package/fjs/website/proof.f.mjs +2 -1
  286. package/package.json +2 -2
  287. package/fjs/dev/package_json/module.f.d.mts +0 -39
  288. package/fjs/dev/package_json/module.f.mjs +0 -40
  289. package/fjs/dev/package_json/proof.f.d.mts +0 -6
  290. package/fjs/dev/package_json/proof.f.mjs +0 -32
  291. package/fjs/effects/eff/module.f.d.mts +0 -20
  292. package/fjs/effects/eff/module.f.mjs +0 -70
  293. package/fjs/effects/eff/proof.f.d.mts +0 -15
  294. package/fjs/effects/eff/proof.f.mjs +0 -69
  295. package/fjs/effects/eff/types.d.ts +0 -71
  296. package/fjs/types/rtti/validate/types.d.ts +0 -6
@@ -0,0 +1,121 @@
1
+ /**
2
+ * `vnd.fjs.note` — a human-authored text item (a note, todo, issue, calendar
3
+ * event, …) as a BLOB of its own.
4
+ *
5
+ * The format is deliberately minimal: the dialect tag, the text, and
6
+ * optionally the subjects the item depends on and a priority. Everything
7
+ * else a richer item needs — a title, tags, dates, a status — is a future
8
+ * **optional** field:
9
+ * rtti structs are open, so additive extension keeps the tag (see the
10
+ * versioning rule in `fjs/media/revision/README.md`), and starting minimal is
11
+ * what keeps every extension additive.
12
+ *
13
+ * Like `vnd.fjs.lock`, a note is a **value**, not a step: no timestamps, no
14
+ * author, no history of its own. Edits over time are ordinary
15
+ * `vnd.fjs.revision` steps whose `subject` identifies the note and whose
16
+ * `snapshot` is one of these blobs, so no second history mechanism appears
17
+ * and `revision` stays the only one.
18
+ *
19
+ * This module is the pure format only: the rtti schema, the `dialect` tag,
20
+ * and decode/validate. Unlike its CAS siblings it has no hash fields, so
21
+ * there is no semantic refinement stage — structural validation is the whole
22
+ * check.
23
+ *
24
+ * See `README.md` for the full spec.
25
+ *
26
+ * @module
27
+ *
28
+ * @import { Unknown } from '../json/types.ts'
29
+ * @import { Result } from '../../types/result/types.ts'
30
+ * @import { ValidationError } from '../../types/rtti/common/types.ts'
31
+ * @import { DialectEntry } from '../types.ts'
32
+ * @import { Note, NoteError } from './types.ts'
33
+ */
34
+ import type { Unknown } from '../json/types.ts';
35
+ import type { Result } from '../../types/result/types.ts';
36
+ import type { ValidationError } from '../../types/rtti/common/types.ts';
37
+ import type { DialectEntry } from '../types.ts';
38
+ import type { Note, NoteError } from './types.ts';
39
+ /**
40
+ * Format tag: names the dialect of this BLOB. The media type it is served
41
+ * with is derived mechanically: `application/` + `dialect` + `+json`.
42
+ */
43
+ export declare const dialect: 'vnd.fjs.note';
44
+ /** The media type derived from {@link dialect}: `application/vnd.fjs.note+json`. */
45
+ export declare const mediaType: "application/vnd.fjs.note+json";
46
+ /**
47
+ * rtti schema for a `note` BLOB: the dialect tag, the text, and optionally
48
+ * the subjects the item depends on.
49
+ *
50
+ * `text` is **required** and may be `''`: an absent text and an empty one
51
+ * would otherwise be two spellings of one blob, and a blob whose only purpose
52
+ * is to hold text has nothing to say when it does not. Any string is valid —
53
+ * the format records the text and defines no markup for it; how a reader
54
+ * renders it (e.g. as Markdown) is the reader's decision.
55
+ *
56
+ * `dependencies` entries are **subject identity strings** — the vocabulary of
57
+ * `vnd.fjs.revision`'s `subject` and of lock-map keys — naming the mutable
58
+ * items this one depends on (a blocked-by todo, an issue's prerequisite).
59
+ * They are never content hashes: a dependency tracks the live item, and
60
+ * pinning it to immutable content is the revision layer's `lock`. Like
61
+ * `subject`, an identity string is unconstrained, so no semantic check
62
+ * applies. The field is optional because its absent value is the constant
63
+ * "depends on nothing"; an explicit `[]` says the same thing, and the format
64
+ * does not distinguish the two.
65
+ *
66
+ * `text` may reference an entry by its zero-based index in square brackets —
67
+ * `[0]` names `dependencies[0]` — so the entry order is **significant**:
68
+ * reordering or removing entries renumbers references. A bracketed integer
69
+ * that indexes no entry is ordinary text, not a broken reference, so the
70
+ * convention adds no validation stage (see the README).
71
+ *
72
+ * `priority` is the author's decision about the item's urgency, on the
73
+ * {@link priorities} scale — a closed literal union, so it too is enforced
74
+ * entirely structurally (a number would invite scale and range questions
75
+ * only a semantic check could answer). Absent means **unprioritized** — the
76
+ * author has not decided — which is the constant default that makes the
77
+ * field optional, and is deliberately distinct from any rank.
78
+ */
79
+ /**
80
+ * The priority scale, most urgent first: `P1` is "drop everything", `P5` is
81
+ * "someday". The vocabulary of `todo/README.md`'s issue format, reused
82
+ * rather than invented, so one scale ranks the repository's own issues and a
83
+ * note blob alike. Widening it later (e.g. a `P0`) follows the fail-closed
84
+ * path the versioning rule allows: an older reader rejects the new rank
85
+ * rather than misreading it.
86
+ */
87
+ export declare const priorities: readonly ['P1', 'P2', 'P3', 'P4', 'P5'];
88
+ export declare const noteSchema: {
89
+ readonly dialect: "vnd.fjs.note";
90
+ readonly text: import("../../types/rtti/types.ts")._Type0<"string">;
91
+ readonly dependencies: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts").Type1<"array", import("../../types/rtti/types.ts")._Type0<"string">>, undefined]>;
92
+ readonly priority: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts").Or<["P1", "P2", "P3", "P4", "P5"]>, undefined]>;
93
+ };
94
+ /** Serializes a note canonically, sorting every object's property names.
95
+ * @type {(note: Note) => string}
96
+ */
97
+ export declare const encodeText: (note: Note) => string;
98
+ /**
99
+ * Validates an already-parsed JSON value as a `note` BLOB. Structural (rtti)
100
+ * validation is the whole check: a note has no hash fields, so there is no
101
+ * semantic refinement stage and no `checkReferences` half.
102
+ *
103
+ * @type {(value: Unknown) => Result<Note, ValidationError>}
104
+ */
105
+ export declare const validate: (value: Unknown) => Result<Note, ValidationError>;
106
+ /**
107
+ * Decodes `text` as a `note` BLOB: JSON-parses it, then validates it per
108
+ * {@link validate}. Detection is semantic, not syntactic — any JSON that
109
+ * satisfies the schema is a note, regardless of key order or whitespace.
110
+ *
111
+ * @type {(text: string) => Result<Note, NoteError>}
112
+ */
113
+ export declare const decodeText: (text: string) => Result<Note, NoteError>;
114
+ /**
115
+ * This dialect as a registry entry for `fjs/media`'s `detect`. Registered
116
+ * with no refinement — structure alone decides the match — so a blob is
117
+ * detected as `vnd.fjs.note` exactly when {@link decodeText} would accept it.
118
+ *
119
+ * @type {DialectEntry}
120
+ */
121
+ export declare const noteDialect: DialectEntry;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * `vnd.fjs.note` — a human-authored text item (a note, todo, issue, calendar
3
+ * event, …) as a BLOB of its own.
4
+ *
5
+ * The format is deliberately minimal: the dialect tag, the text, and
6
+ * optionally the subjects the item depends on and a priority. Everything
7
+ * else a richer item needs — a title, tags, dates, a status — is a future
8
+ * **optional** field:
9
+ * rtti structs are open, so additive extension keeps the tag (see the
10
+ * versioning rule in `fjs/media/revision/README.md`), and starting minimal is
11
+ * what keeps every extension additive.
12
+ *
13
+ * Like `vnd.fjs.lock`, a note is a **value**, not a step: no timestamps, no
14
+ * author, no history of its own. Edits over time are ordinary
15
+ * `vnd.fjs.revision` steps whose `subject` identifies the note and whose
16
+ * `snapshot` is one of these blobs, so no second history mechanism appears
17
+ * and `revision` stays the only one.
18
+ *
19
+ * This module is the pure format only: the rtti schema, the `dialect` tag,
20
+ * and decode/validate. Unlike its CAS siblings it has no hash fields, so
21
+ * there is no semantic refinement stage — structural validation is the whole
22
+ * check.
23
+ *
24
+ * See `README.md` for the full spec.
25
+ *
26
+ * @module
27
+ *
28
+ * @import { Unknown } from '../json/types.ts'
29
+ * @import { Result } from '../../types/result/types.ts'
30
+ * @import { ValidationError } from '../../types/rtti/common/types.ts'
31
+ * @import { DialectEntry } from '../types.ts'
32
+ * @import { Note, NoteError } from './types.ts'
33
+ */
34
+
35
+ import { array, option, or, string } from '../../types/rtti/module.f.mjs'
36
+ import { parse as rttiParse } from '../../types/rtti/parse/module.f.mjs'
37
+ import { parse as parseJson, stringify } from '../json/module.f.mjs'
38
+ import { okThen } from '../../types/result/module.f.mjs'
39
+ import { dialectEntry } from '../module.f.mjs'
40
+ import { sort } from '../../types/object/module.f.mjs'
41
+
42
+ /**
43
+ * Format tag: names the dialect of this BLOB. The media type it is served
44
+ * with is derived mechanically: `application/` + `dialect` + `+json`.
45
+ */
46
+ export const dialect = /** @type {const} */ ('vnd.fjs.note')
47
+
48
+ /** The media type derived from {@link dialect}: `application/vnd.fjs.note+json`. */
49
+ export const mediaType = /** @type {const} */ (`application/${dialect}+json`)
50
+
51
+ /**
52
+ * rtti schema for a `note` BLOB: the dialect tag, the text, and optionally
53
+ * the subjects the item depends on.
54
+ *
55
+ * `text` is **required** and may be `''`: an absent text and an empty one
56
+ * would otherwise be two spellings of one blob, and a blob whose only purpose
57
+ * is to hold text has nothing to say when it does not. Any string is valid —
58
+ * the format records the text and defines no markup for it; how a reader
59
+ * renders it (e.g. as Markdown) is the reader's decision.
60
+ *
61
+ * `dependencies` entries are **subject identity strings** — the vocabulary of
62
+ * `vnd.fjs.revision`'s `subject` and of lock-map keys — naming the mutable
63
+ * items this one depends on (a blocked-by todo, an issue's prerequisite).
64
+ * They are never content hashes: a dependency tracks the live item, and
65
+ * pinning it to immutable content is the revision layer's `lock`. Like
66
+ * `subject`, an identity string is unconstrained, so no semantic check
67
+ * applies. The field is optional because its absent value is the constant
68
+ * "depends on nothing"; an explicit `[]` says the same thing, and the format
69
+ * does not distinguish the two.
70
+ *
71
+ * `text` may reference an entry by its zero-based index in square brackets —
72
+ * `[0]` names `dependencies[0]` — so the entry order is **significant**:
73
+ * reordering or removing entries renumbers references. A bracketed integer
74
+ * that indexes no entry is ordinary text, not a broken reference, so the
75
+ * convention adds no validation stage (see the README).
76
+ *
77
+ * `priority` is the author's decision about the item's urgency, on the
78
+ * {@link priorities} scale — a closed literal union, so it too is enforced
79
+ * entirely structurally (a number would invite scale and range questions
80
+ * only a semantic check could answer). Absent means **unprioritized** — the
81
+ * author has not decided — which is the constant default that makes the
82
+ * field optional, and is deliberately distinct from any rank.
83
+ */
84
+ /**
85
+ * The priority scale, most urgent first: `P1` is "drop everything", `P5` is
86
+ * "someday". The vocabulary of `todo/README.md`'s issue format, reused
87
+ * rather than invented, so one scale ranks the repository's own issues and a
88
+ * note blob alike. Widening it later (e.g. a `P0`) follows the fail-closed
89
+ * path the versioning rule allows: an older reader rejects the new rank
90
+ * rather than misreading it.
91
+ */
92
+ export const priorities = /** @type {const} */ (['P1', 'P2', 'P3', 'P4', 'P5'])
93
+
94
+ export const noteSchema = /** @type {const} */ ({
95
+ dialect,
96
+ text: string,
97
+ dependencies: option(array(string)),
98
+ priority: option(or(...priorities)),
99
+ })
100
+
101
+ /** Serializes a note canonically, sorting every object's property names.
102
+ * @type {(note: Note) => string}
103
+ */
104
+ export const encodeText = stringify(sort)
105
+
106
+ /**
107
+ * Validates an already-parsed JSON value as a `note` BLOB. Structural (rtti)
108
+ * validation is the whole check: a note has no hash fields, so there is no
109
+ * semantic refinement stage and no `checkReferences` half.
110
+ *
111
+ * @type {(value: Unknown) => Result<Note, ValidationError>}
112
+ */
113
+ export const validate = rttiParse(noteSchema)
114
+
115
+ /**
116
+ * Decodes `text` as a `note` BLOB: JSON-parses it, then validates it per
117
+ * {@link validate}. Detection is semantic, not syntactic — any JSON that
118
+ * satisfies the schema is a note, regardless of key order or whitespace.
119
+ *
120
+ * @type {(text: string) => Result<Note, NoteError>}
121
+ */
122
+ export const decodeText = text => okThen(validate)(parseJson(text))
123
+
124
+ /**
125
+ * This dialect as a registry entry for `fjs/media`'s `detect`. Registered
126
+ * with no refinement — structure alone decides the match — so a blob is
127
+ * detected as `vnd.fjs.note` exactly when {@link decodeText} would accept it.
128
+ *
129
+ * @type {DialectEntry}
130
+ */
131
+ export const noteDialect = dialectEntry(noteSchema)
@@ -0,0 +1,29 @@
1
+ export declare const proof: {
2
+ dialectAndMediaType: () => void;
3
+ priorityScale: () => void;
4
+ validate: {
5
+ minimalNoteAccepted: () => void;
6
+ emptyTextAccepted: () => void;
7
+ missingTextRejected: () => void;
8
+ nonStringTextRejected: () => void;
9
+ dependenciesAccepted: () => void;
10
+ emptyDependenciesAccepted: () => void;
11
+ nonArrayDependenciesRejected: () => void;
12
+ nonStringDependencyRejected: () => void;
13
+ priorityAccepted: () => void;
14
+ unknownPriorityRejected: () => void;
15
+ numericPriorityRejected: () => void;
16
+ unknownFieldsIgnored: () => void;
17
+ otherDialectRejected: () => void;
18
+ };
19
+ decodeText: {
20
+ validJson: () => void;
21
+ keyOrderIndependent: () => void;
22
+ malformedJsonRejected: () => void;
23
+ ordinaryJsonRejected: () => void;
24
+ };
25
+ encodeText: {
26
+ sortsPropertiesLexicographically: () => void;
27
+ };
28
+ noteDialectTag: () => void;
29
+ };
@@ -0,0 +1,150 @@
1
+ import { assert, assertEq } from '../../asserts/module.f.mjs'
2
+ import { dialect, mediaType, decodeText, encodeText, noteDialect, priorities, validate } from './module.f.mjs'
3
+ import { dialect as lockDialect } from '../lock/module.f.mjs'
4
+
5
+ export const proof = {
6
+ dialectAndMediaType: () => {
7
+ assertEq(dialect, 'vnd.fjs.note')
8
+ assertEq(mediaType, 'application/vnd.fjs.note+json')
9
+ },
10
+
11
+ // The published scale, most urgent first — `todo/README.md`'s vocabulary.
12
+ priorityScale: () => {
13
+ assertEq(priorities.join(','), 'P1,P2,P3,P4,P5')
14
+ },
15
+
16
+ validate: {
17
+ // The ordinary case: the tag and the text.
18
+ minimalNoteAccepted: () => {
19
+ const r = validate({ dialect, text: 'buy milk' })
20
+ assert(r[0] === 'ok', ['expected ok', r])
21
+ assertEq(r[1].text, 'buy milk')
22
+ },
23
+
24
+ // `text` may be empty — required rather than optional, so an absent
25
+ // text and an empty one are not two spellings of one blob.
26
+ emptyTextAccepted: () => {
27
+ const [t] = validate({ dialect, text: '' })
28
+ assertEq(t, 'ok')
29
+ },
30
+
31
+ // `text` is required: a blob carrying only the tag is not a note.
32
+ missingTextRejected: () => {
33
+ const [t] = validate({ dialect })
34
+ assertEq(t, 'error')
35
+ },
36
+
37
+ // The text is a string, nothing else.
38
+ nonStringTextRejected: () => {
39
+ const [t] = validate({ dialect, text: 42 })
40
+ assertEq(t, 'error')
41
+ },
42
+
43
+ // Dependencies are subject identity strings, unconstrained like
44
+ // `vnd.fjs.revision`'s `subject` — no semantic check applies. The
45
+ // text may reference them by index (`[0]`, `[1]`), which is a
46
+ // reading convention, not a validation rule: this blob validates the
47
+ // same with any text.
48
+ dependenciesAccepted: () => {
49
+ const r = validate({ dialect, text: 'ship it once [0] and [1] land', dependencies: ['write the spec', 'review'] })
50
+ assert(r[0] === 'ok', ['expected ok', r])
51
+ assertEq(r[1].dependencies?.length, 2)
52
+ },
53
+
54
+ // `[]` and an absent field both say "depends on nothing"; the format
55
+ // does not distinguish them, and both validate.
56
+ emptyDependenciesAccepted: () => {
57
+ const [t] = validate({ dialect, text: 'hi', dependencies: [] })
58
+ assertEq(t, 'ok')
59
+ },
60
+
61
+ // The field is a list of strings, nothing else.
62
+ nonArrayDependenciesRejected: () => {
63
+ const [t] = validate({ dialect, text: 'hi', dependencies: 'write the spec' })
64
+ assertEq(t, 'error')
65
+ },
66
+ nonStringDependencyRejected: () => {
67
+ const [t] = validate({ dialect, text: 'hi', dependencies: [42] })
68
+ assertEq(t, 'error')
69
+ },
70
+
71
+ // A priority is one of the closed `priorities` literals — the
72
+ // author's urgency decision, enforced entirely structurally.
73
+ priorityAccepted: () => {
74
+ const r = validate({ dialect, text: 'fix the build', priority: 'P1' })
75
+ assert(r[0] === 'ok', ['expected ok', r])
76
+ assertEq(r[1].priority, 'P1')
77
+ },
78
+
79
+ // The scale is closed: an unknown rank fails structurally rather than
80
+ // passing as a free-form string — the fail-closed widening path.
81
+ unknownPriorityRejected: () => {
82
+ const [t] = validate({ dialect, text: 'hi', priority: 'P0' })
83
+ assertEq(t, 'error')
84
+ },
85
+
86
+ // The rank is a literal, not a number: `1` is not `'P1'`.
87
+ numericPriorityRejected: () => {
88
+ const [t] = validate({ dialect, text: 'hi', priority: 1 })
89
+ assertEq(t, 'error')
90
+ },
91
+
92
+ // rtti structs are open, so unknown fields are ignored rather than
93
+ // rejected — the additive forward-compatibility path every future
94
+ // extension (title, tags, dates, …) relies on.
95
+ unknownFieldsIgnored: () => {
96
+ const [t] = validate({ dialect, text: 'call Bob', title: 'todo', done: true })
97
+ assertEq(t, 'ok')
98
+ },
99
+
100
+ // Another dialect's blob is not a note: the tag is matched as an
101
+ // exact literal.
102
+ otherDialectRejected: () => {
103
+ const [t] = validate({ dialect: lockDialect, text: 'hi' })
104
+ assertEq(t, 'error')
105
+ },
106
+ },
107
+
108
+ decodeText: {
109
+ validJson: () => {
110
+ const r = decodeText(`{"dialect":"${dialect}","text":"buy milk"}`)
111
+ assert(r[0] === 'ok', ['expected ok', r])
112
+ assertEq(r[1].text, 'buy milk')
113
+ },
114
+
115
+ // Key order carries no meaning: the JSON is parsed and the parsed
116
+ // value validated, so `dialect` need not come first.
117
+ keyOrderIndependent: () => {
118
+ const [t] = decodeText(`{"text":"hi","dialect":"${dialect}"}`)
119
+ assertEq(t, 'ok')
120
+ },
121
+
122
+ malformedJsonRejected: () => {
123
+ const [t] = decodeText('{not json')
124
+ assertEq(t, 'error')
125
+ },
126
+
127
+ ordinaryJsonRejected: () => {
128
+ const [t] = decodeText('{"hello":"world"}')
129
+ assertEq(t, 'error')
130
+ },
131
+ },
132
+
133
+ encodeText: {
134
+ // Two blobs differing only in property order converge on one byte
135
+ // sequence, so they address the same CAS blob. `dependencies` sorts
136
+ // ahead of `dialect`, and its array order is preserved — arrays retain
137
+ // their declared order under canonical serialization.
138
+ sortsPropertiesLexicographically: () => {
139
+ const decoded = decodeText(`{"text":"hi","dependencies":["b","a"],"dialect":"${dialect}"}`)
140
+ assert(decoded[0] === 'ok', ['expected ok', decoded])
141
+ assertEq(encodeText(decoded[1]), `{"dependencies":["b","a"],"dialect":"${dialect}","text":"hi"}`)
142
+ },
143
+ },
144
+
145
+ // The registry entry carries the schema's own tag; matching is exercised
146
+ // end to end through `detect` in `fjs/media/proof.f.mjs`.
147
+ noteDialectTag: () => {
148
+ assertEq(noteDialect.dialect, dialect)
149
+ },
150
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Type-level API for `fjs/media/note/module.f.mjs`: `Note` and `NoteError`.
3
+ */
4
+ import type { ValidationError } from '../../types/rtti/common/types.ts';
5
+ import type { Ts } from '../../types/rtti/ts/types.ts';
6
+ import type { noteSchema } from './module.f.mjs';
7
+ /** The TypeScript type derived from `noteSchema` — the single source of truth. */
8
+ export type Note = Ts<typeof noteSchema>;
9
+ /** Either a structural validation error or a JSON parse error message. */
10
+ export type NoteError = ValidationError | string;
@@ -4,7 +4,10 @@ export declare const proof: {
4
4
  keyOrderIndependent: () => void;
5
5
  invalidRevisionFallsThrough: () => void;
6
6
  nonHashSnapshotFallsThrough: () => void;
7
- secondDialect: () => void;
7
+ validLock: () => void;
8
+ lockAndRevisionDoNotOverlap: () => void;
9
+ nonHashLockValueFallsThrough: () => void;
10
+ validNote: () => void;
8
11
  nonVndDialectName: () => void;
9
12
  firstMatchWins: () => void;
10
13
  noDialects: () => void;
@@ -6,6 +6,8 @@ import { assertEq } from '../asserts/module.f.mjs'
6
6
  import { msb, u8ListToVec, repeat, vec8 } from '../types/bit_vec/module.f.mjs'
7
7
  import { detect, dialectEntry } from './module.f.mjs'
8
8
  import { dialect, revisionDialect } from './revision/module.f.mjs'
9
+ import { dialect as lockDialectName, lockDialect } from './lock/module.f.mjs'
10
+ import { dialect as noteDialectName, noteDialect } from './note/module.f.mjs'
9
11
  import { number, string } from '../types/rtti/module.f.mjs'
10
12
 
11
13
  // All test strings here are ASCII, so char code === UTF-8 byte value.
@@ -14,22 +16,12 @@ const utf8Bytes = s => u8ListToVec(msb)([...s].map(c => c.charCodeAt(0)))
14
16
 
15
17
  const revisionJson = `{"dialect":"${dialect}","subject":"8","parents":[],"snapshot":"8","generation":0}`
16
18
 
17
- /** @type {readonly DialectEntry[]} */
18
- const dialects = [revisionDialect]
19
-
20
- const detectRevision = detect(dialects)
21
-
22
- /**
23
- * A second dialect following the same `vnd.fjs.<name>` convention, registered
24
- * with no refinement: structure alone decides the match.
19
+ /** The three dialects `fjs/media` itself ships, in the order `fjs/mcp` registers them.
20
+ * @type {readonly DialectEntry[]}
25
21
  */
26
- const noteSchema = /** @type {const} */ ({
27
- dialect: 'vnd.fjs.note',
28
- text: string,
29
- })
22
+ const dialects = [revisionDialect, lockDialect, noteDialect]
30
23
 
31
- /** @type {DialectEntry} */
32
- const noteDialect = dialectEntry(noteSchema)
24
+ const detectRevision = detect(dialects)
33
25
 
34
26
  /** A dialect name outside `vnd.fjs.*` — registerable, and detected as itself. */
35
27
  const gadgetSchema = /** @type {const} */ ({
@@ -83,14 +75,41 @@ export const proof = {
83
75
  assertEq(m.mime_type, 'text/plain')
84
76
  },
85
77
 
86
- // A second dialect, registered by the caller, is recognized alongside the
87
- // first and an entry with no refinement matches on structure alone.
88
- secondDialect: () => {
89
- const d = detect([revisionDialect, noteDialect])
90
- assertEq(d(utf8Bytes('{"dialect":"vnd.fjs.note","text":"hi"}')).mime_type, 'application/vnd.fjs.note+json')
91
- assertEq(d(utf8Bytes(revisionJson)).mime_type, 'application/vnd.fjs.revision+json')
78
+ // A valid shared-lock blob is recognized as its own dialect, alongside the
79
+ // revision one, and reported under the derived media type.
80
+ validLock: () => {
81
+ const m = detectRevision(utf8Bytes(`{"dialect":"${lockDialectName}","lock":{"dependency":"8"}}`))
82
+ assertEq(m.type, 'text')
83
+ assertEq(m.mime_type, 'application/vnd.fjs.lock+json')
84
+ },
85
+
86
+ // The two dialects never claim each other's blobs: each schema matches its
87
+ // own `dialect` literal, so a revision carrying an inline `lock` is still a
88
+ // revision, and a lock blob is never a revision.
89
+ lockAndRevisionDoNotOverlap: () => {
90
+ const withLock = `{"dialect":"${dialect}","subject":"8","parents":[],"snapshot":"8","generation":0,"lock":{"d":"8"}}`
91
+ assertEq(detectRevision(utf8Bytes(withLock)).mime_type, 'application/vnd.fjs.revision+json')
92
+ assertEq(detectRevision(utf8Bytes(revisionJson)).mime_type, 'application/vnd.fjs.revision+json')
93
+ },
94
+
95
+ // `lockDialect` carries the semantic check too: a structurally valid lock
96
+ // blob whose binding is not a cbase32 hash is not detected as one, exactly
97
+ // as its `decodeText` would say.
98
+ nonHashLockValueFallsThrough: () => {
99
+ const text = `{"dialect":"${lockDialectName}","lock":{"dependency":"not a hash"}}`
100
+ const m = detectRevision(utf8Bytes(text))
101
+ assertEq(m.type, 'text')
102
+ assertEq(m.mime_type, 'text/plain')
103
+ },
104
+
105
+ // The note dialect is registered with no refinement, so structure alone
106
+ // decides the match — a valid note is recognized alongside its siblings.
107
+ validNote: () => {
108
+ const m = detectRevision(utf8Bytes(`{"dialect":"${noteDialectName}","text":"hi"}`))
109
+ assertEq(m.type, 'text')
110
+ assertEq(m.mime_type, 'application/vnd.fjs.note+json')
92
111
  // Same tag, wrong shape: no entry matches.
93
- assertEq(d(utf8Bytes('{"dialect":"vnd.fjs.note","text":42}')).mime_type, 'text/plain')
112
+ assertEq(detectRevision(utf8Bytes(`{"dialect":"${noteDialectName}","text":42}`)).mime_type, 'text/plain')
94
113
  },
95
114
 
96
115
  // The name is neither grammar-checked nor allowlisted: a dialect outside
@@ -17,12 +17,13 @@
17
17
  * @import { Unknown } from '../json/types.ts'
18
18
  * @import { Result } from '../../types/result/types.ts'
19
19
  * @import { DialectEntry } from '../types.ts'
20
- * @import { Revision, RevisionError } from './types.ts'
20
+ * @import { String as RttiString } from '../../types/rtti/types.ts'
21
+ * @import { LockField, LockFieldSchema, LockMap, LockSchema, Revision, RevisionError } from './types.ts'
21
22
  */
22
23
  import type { Unknown } from '../json/types.ts';
23
24
  import type { Result } from '../../types/result/types.ts';
24
25
  import type { DialectEntry } from '../types.ts';
25
- import type { Revision, RevisionError } from './types.ts';
26
+ import type { LockField, LockFieldSchema, LockMap, LockSchema, Revision, RevisionError } from './types.ts';
26
27
  /**
27
28
  * Format tag: names the dialect of this BLOB. The media type it is served
28
29
  * with is derived mechanically: `application/` + `dialect` + `+json`.
@@ -40,8 +41,52 @@ export declare const mediaType: "application/vnd.fjs.revision+json";
40
41
  * not by this schema on its own.
41
42
  */
42
43
  export declare const hash: import("../../types/rtti/types.ts")._Type0<"string">;
43
- /** Structural schema for a Stage 1 flat lock map. */
44
- export declare const _lock: import("../../types/rtti/types.ts").Type1<"record", import("../../types/rtti/types.ts")._Type0<"string">>;
44
+ /**
45
+ * rtti schema for a lock map: an open map whose every value is either a
46
+ * direct hash string or a nested lock map, to any depth.
47
+ *
48
+ * Self-referential through {@link lockValue}, which is a module-level
49
+ * constant rather than a union rebuilt inside the thunk: the rtti data form
50
+ * (`fjs/types/rtti/data`, which `toJsonSchema` routes through) closes
51
+ * reference cycles by *identity*, so a schema handing out a fresh union thunk
52
+ * on every call would present an infinite graph and never terminate.
53
+ *
54
+ * The named `@type` — rather than `@type {const}` — is what a self-referential
55
+ * schema needs twice over: a `const` cannot reference itself in its own
56
+ * initializer at all, and naming the recursive position is also what keeps
57
+ * declaration emit from inlining the structure and giving up at depth (see
58
+ * `fjs/AGENTS.md` §3.2 and `../json/rtti/module.f.mjs`).
59
+ *
60
+ * Like `hash`, this is `string` at the structural level; cbase32 decodability
61
+ * of every direct value, at every depth, is enforced by
62
+ * {@link checkReferences}.
63
+ *
64
+ * @type {LockSchema}
65
+ */
66
+ export declare const lock: LockSchema;
67
+ /**
68
+ * rtti schema for a revision's `lock` **field**: the bindings inline as a lock
69
+ * map, or a hash naming a `vnd.fjs.lock` blob (`fjs/media/lock`) that holds
70
+ * one to share — see [Shared lock references](./README.md#shared-lock-references).
71
+ *
72
+ * Structurally identical to {@link lockValue}, and deliberately a separate
73
+ * name: the two positions mean different things. A string *inside* a map is a
74
+ * dependency's content hash; a string in this position is a lock blob's hash,
75
+ * i.e. where the whole map lives. Only the top level is widened, so a nested
76
+ * string keeps meaning exactly what it always did and no position is
77
+ * ambiguous.
78
+ *
79
+ * Widening the field rather than adding a `lockRef` sibling is what keeps this
80
+ * dialect: an older reader validates `lock` as a map and rejects a string
81
+ * outright, whereas an unknown sibling field would validate and be read as
82
+ * "no bindings were recorded" — the fail-open misread the versioning rule
83
+ * exists to prevent. It also makes "inline map *and* reference" unstatable, so
84
+ * the format defines no precedence between them, consistent with its refusal
85
+ * to define overlay or inheritance for nested maps.
86
+ *
87
+ * @type {LockFieldSchema}
88
+ */
89
+ export declare const lockField: LockFieldSchema;
45
90
  /**
46
91
  * rtti schema for a `revision` BLOB. See the README for the full semantics of
47
92
  * each field; `dialect` is the type discriminant, matched here as an exact
@@ -54,7 +99,7 @@ export declare const revisionSchema: {
54
99
  readonly snapshot: import("../../types/rtti/types.ts")._Type0<"string">;
55
100
  readonly generation: import("../../types/rtti/types.ts")._Type0<"number">;
56
101
  readonly archived: import("../../types/rtti/types.ts").Or<readonly [true, undefined]>;
57
- readonly lock: import("../../types/rtti/types.ts").Or<readonly [import("../../types/rtti/types.ts").Type1<"record", import("../../types/rtti/types.ts")._Type0<"string">>, undefined]>;
102
+ readonly lock: import("../../types/rtti/types.ts").Or<readonly [LockFieldSchema, undefined]>;
58
103
  };
59
104
  /** Serializes a revision canonically, recursively sorting every object's property names.
60
105
  * @type {(revision: Revision) => string}
@@ -64,12 +109,38 @@ export declare const encodeText: (revision: Revision) => string;
64
109
  * @type {(s: string) => boolean}
65
110
  */
66
111
  export declare const isHash: (s: string) => boolean;
112
+ /**
113
+ * The first reason a structurally valid lock map is not a valid one, or `null`
114
+ * — {@link lockError} rooted at the empty scope, so a reported path is
115
+ * relative to the map itself.
116
+ *
117
+ * Exported because `fjs/media/lock` validates the very same map as a
118
+ * standalone blob: one recursive schema and one semantic check, so a map means
119
+ * the same thing inline and shared, and the two forms cannot drift.
120
+ *
121
+ * @type {(lock: LockMap) => string | null}
122
+ */
123
+ export declare const lockMapError: (lock: LockMap) => string | null;
124
+ /**
125
+ * The first reason a structurally valid `lock` field is not a valid one, or
126
+ * `null`. A map is checked entry by entry ({@link lockMapError}); a string is
127
+ * a reference to a `vnd.fjs.lock` blob and is checked as a cbase32 hash and
128
+ * nothing more — this module is pure format with no store access, so whether
129
+ * the blob exists, and what its bindings mean once fetched, stay a resolver's
130
+ * business exactly as they do for `snapshot`.
131
+ *
132
+ * @type {(value: LockField) => string | null}
133
+ */
134
+ export declare const lockFieldError: (value: LockField) => string | null;
67
135
  /**
68
136
  * Checks the semantic refinements the structural schema can't express on an
69
137
  * already shape-valid revision: every `parents` entry and the `snapshot` must
70
- * decode as a cbase32 hash ({@link isHash}), and `generation` must be a
138
+ * decode as a cbase32 hash ({@link isHash}), the `lock` field must too
139
+ * every direct value at every depth of an inline map, or the shared-lock
140
+ * reference itself ({@link lockFieldError}) — and `generation` must be a
71
141
  * non-negative *safe* integer. `subject` is not checked — it is an identity
72
- * string, never a snapshot reference, so any string is valid.
142
+ * string, never a snapshot reference, so any string is valid, and the same
143
+ * goes for a lock map's keys, which are subjects.
73
144
  *
74
145
  * `generation` uses `Number.isSafeInteger`, not `Number.isInteger`: a value at
75
146
  * or above `2 ** 53` passes `isInteger` but is no longer uniquely