nervur 0.22.0 → 0.22.2-3

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 (398) hide show
  1. package/AUTHORING.md +741 -0
  2. package/KIT-SPEC.md +286 -0
  3. package/LICENSE +1 -1
  4. package/NOTICE +6 -2
  5. package/README.md +97 -424
  6. package/dist/app/app-ground.d.ts +18 -0
  7. package/dist/app/app-ground.js +25 -0
  8. package/dist/app/index.d.ts +2 -0
  9. package/dist/app/index.js +5 -0
  10. package/dist/app/native.d.ts +46 -0
  11. package/dist/app/native.js +101 -0
  12. package/dist/being/being.d.ts +229 -0
  13. package/dist/being/being.js +20 -0
  14. package/dist/being/covers.d.ts +9 -0
  15. package/dist/being/covers.js +21 -0
  16. package/dist/being/index.d.ts +3 -0
  17. package/dist/being/index.js +5 -0
  18. package/dist/being/need.d.ts +49 -0
  19. package/dist/being/need.js +33 -0
  20. package/dist/being/schema.d.ts +100 -0
  21. package/dist/being/schema.js +132 -0
  22. package/dist/being/table.d.ts +42 -0
  23. package/dist/being/table.js +288 -0
  24. package/dist/bench/bench-ground.d.ts +55 -0
  25. package/dist/bench/bench-ground.js +101 -0
  26. package/dist/bench/bench.d.ts +87 -0
  27. package/dist/bench/bench.js +260 -0
  28. package/dist/bench/fake-carry.d.ts +32 -0
  29. package/dist/bench/fake-carry.js +56 -0
  30. package/dist/bench/fake-clock.d.ts +17 -0
  31. package/dist/bench/fake-clock.js +34 -0
  32. package/dist/bench/fake-custody.d.ts +9 -0
  33. package/dist/bench/fake-custody.js +10 -0
  34. package/dist/bench/fake-faculty.d.ts +25 -0
  35. package/dist/bench/fake-faculty.js +48 -0
  36. package/dist/bench/fake-keys.d.ts +5 -0
  37. package/dist/bench/fake-keys.js +9 -0
  38. package/dist/bench/fake-memory.d.ts +14 -0
  39. package/dist/bench/fake-memory.js +46 -0
  40. package/dist/bench/fake-network.d.ts +57 -0
  41. package/dist/bench/fake-network.js +169 -0
  42. package/dist/bench/index.d.ts +11 -0
  43. package/dist/bench/index.js +13 -0
  44. package/dist/bench/seeded.d.ts +11 -0
  45. package/dist/bench/seeded.js +37 -0
  46. package/dist/bench/settle.d.ts +2 -0
  47. package/dist/bench/settle.js +49 -0
  48. package/dist/bench/stewards.d.ts +17 -0
  49. package/dist/bench/stewards.js +20 -0
  50. package/dist/bodies/class-list.d.ts +14 -0
  51. package/dist/bodies/class-list.js +33 -0
  52. package/dist/bodies/joined-carry.d.ts +23 -0
  53. package/dist/bodies/joined-carry.js +42 -0
  54. package/dist/bodies/noble-crypto.d.ts +22 -0
  55. package/dist/bodies/noble-crypto.js +88 -0
  56. package/dist/bodies/seed-keys.d.ts +10 -0
  57. package/dist/bodies/seed-keys.js +42 -0
  58. package/dist/bodies/strict-tools.d.ts +10 -0
  59. package/dist/bodies/strict-tools.js +306 -0
  60. package/dist/bodies/web-carry.d.ts +65 -0
  61. package/dist/bodies/web-carry.js +224 -0
  62. package/dist/bodies/web-clock.d.ts +12 -0
  63. package/dist/bodies/web-clock.js +33 -0
  64. package/dist/browser/browser-ground.d.ts +69 -0
  65. package/dist/browser/browser-ground.js +219 -0
  66. package/dist/browser/index.d.ts +4 -0
  67. package/dist/browser/index.js +7 -0
  68. package/dist/browser/indexeddb-memory.d.ts +17 -0
  69. package/dist/browser/indexeddb-memory.js +97 -0
  70. package/dist/browser/locked-custody.d.ts +25 -0
  71. package/dist/browser/locked-custody.js +89 -0
  72. package/dist/browser/origin-classes.d.ts +7 -0
  73. package/dist/browser/origin-classes.js +31 -0
  74. package/dist/foundation.d.ts +150 -0
  75. package/dist/foundation.js +4 -0
  76. package/dist/ground/ground.d.ts +177 -0
  77. package/dist/ground/ground.js +363 -0
  78. package/dist/house/crossing.d.ts +37 -0
  79. package/dist/house/crossing.js +131 -0
  80. package/dist/house/house.d.ts +80 -0
  81. package/dist/house/house.js +1753 -0
  82. package/dist/house/rows-shape.d.ts +103 -0
  83. package/dist/house/rows-shape.js +12 -0
  84. package/dist/house/rows.d.ts +27 -0
  85. package/dist/house/rows.js +168 -0
  86. package/dist/index.d.ts +29 -1
  87. package/dist/index.js +25 -14
  88. package/dist/node/bridge.d.ts +17 -0
  89. package/dist/node/bridge.js +188 -0
  90. package/dist/node/cli.js +170 -0
  91. package/dist/node/custody.d.ts +20 -0
  92. package/dist/node/custody.js +34 -0
  93. package/dist/node/file-keys.d.ts +7 -0
  94. package/dist/node/file-keys.js +30 -0
  95. package/dist/node/file-memory.d.ts +13 -0
  96. package/dist/node/file-memory.js +91 -0
  97. package/dist/node/folder-classes.d.ts +5 -0
  98. package/dist/node/folder-classes.js +40 -0
  99. package/dist/node/gone.d.ts +2 -0
  100. package/dist/node/gone.js +7 -0
  101. package/dist/node/hand.d.ts +29 -0
  102. package/dist/node/hand.js +85 -0
  103. package/dist/node/held-keys.d.ts +14 -0
  104. package/dist/node/held-keys.js +36 -0
  105. package/dist/node/http.d.ts +11 -0
  106. package/dist/node/http.js +90 -0
  107. package/dist/node/index.d.ts +13 -0
  108. package/dist/node/index.js +15 -0
  109. package/dist/node/keychain-keys.d.ts +10 -0
  110. package/dist/node/keychain-keys.js +51 -0
  111. package/dist/node/ledger-memory.d.ts +17 -0
  112. package/dist/node/ledger-memory.js +151 -0
  113. package/dist/node/lock.d.ts +10 -0
  114. package/dist/node/lock.js +78 -0
  115. package/dist/node/node-ground.d.ts +33 -0
  116. package/dist/node/node-ground.js +147 -0
  117. package/dist/node/notify.d.ts +1 -0
  118. package/dist/node/notify.js +18 -0
  119. package/dist/node/tcp-carry.d.ts +39 -0
  120. package/dist/node/tcp-carry.js +216 -0
  121. package/dist/node/websocket.d.ts +6 -0
  122. package/dist/node/websocket.js +118 -0
  123. package/dist/quo/frame.d.ts +20 -0
  124. package/dist/quo/frame.js +48 -0
  125. package/dist/quo/room.d.ts +154 -0
  126. package/dist/quo/room.js +362 -0
  127. package/dist/serve/index.d.ts +27 -0
  128. package/dist/serve/index.js +55 -0
  129. package/package.json +49 -47
  130. package/dist/browser.d.ts +0 -1
  131. package/dist/browser.js +0 -8
  132. package/dist/cli.js +0 -16
  133. package/dist/core/being/being.d.ts +0 -17
  134. package/dist/core/being/being.js +0 -72
  135. package/dist/core/being/digest.d.ts +0 -3
  136. package/dist/core/being/digest.js +0 -17
  137. package/dist/core/being/faculty.d.ts +0 -10
  138. package/dist/core/being/faculty.js +0 -77
  139. package/dist/core/being/index.d.ts +0 -6
  140. package/dist/core/being/index.js +0 -8
  141. package/dist/core/being/kind.d.ts +0 -6
  142. package/dist/core/being/kind.js +0 -34
  143. package/dist/core/being/types.d.ts +0 -61
  144. package/dist/core/being/types.js +0 -4
  145. package/dist/core/being/words.d.ts +0 -13
  146. package/dist/core/being/words.js +0 -14
  147. package/dist/core/browser/index.d.ts +0 -37
  148. package/dist/core/browser/index.js +0 -196
  149. package/dist/core/browser/worker.d.ts +0 -11
  150. package/dist/core/browser/worker.js +0 -37
  151. package/dist/core/cli/command.d.ts +0 -13
  152. package/dist/core/cli/command.js +0 -330
  153. package/dist/core/cli/edge.d.ts +0 -7
  154. package/dist/core/cli/edge.js +0 -79
  155. package/dist/core/cli/harbor.d.ts +0 -25
  156. package/dist/core/cli/harbor.js +0 -115
  157. package/dist/core/contract/index.d.ts +0 -42
  158. package/dist/core/contract/index.js +0 -34
  159. package/dist/core/contract/link.d.ts +0 -4
  160. package/dist/core/contract/link.js +0 -45
  161. package/dist/core/crypto/aes.d.ts +0 -3
  162. package/dist/core/crypto/aes.js +0 -24
  163. package/dist/core/crypto/bytes.d.ts +0 -6
  164. package/dist/core/crypto/bytes.js +0 -33
  165. package/dist/core/crypto/ed25519.d.ts +0 -3
  166. package/dist/core/crypto/ed25519.js +0 -93
  167. package/dist/core/crypto/hash.d.ts +0 -2
  168. package/dist/core/crypto/hash.js +0 -11
  169. package/dist/core/crypto/index.d.ts +0 -7
  170. package/dist/core/crypto/index.js +0 -10
  171. package/dist/core/crypto/json.d.ts +0 -8
  172. package/dist/core/crypto/json.js +0 -250
  173. package/dist/core/crypto/mlkem.d.ts +0 -12
  174. package/dist/core/crypto/mlkem.js +0 -36
  175. package/dist/core/crypto/subtle.d.ts +0 -3
  176. package/dist/core/crypto/subtle.js +0 -11
  177. package/dist/core/crypto/x25519.d.ts +0 -2
  178. package/dist/core/crypto/x25519.js +0 -24
  179. package/dist/core/edge/index.d.ts +0 -164
  180. package/dist/core/edge/index.js +0 -739
  181. package/dist/core/edge/kit.d.ts +0 -2
  182. package/dist/core/edge/kit.js +0 -2
  183. package/dist/core/folder/index.d.ts +0 -18
  184. package/dist/core/folder/index.js +0 -130
  185. package/dist/core/git/http.d.ts +0 -6
  186. package/dist/core/git/http.js +0 -102
  187. package/dist/core/git/index.d.ts +0 -5
  188. package/dist/core/git/index.js +0 -10
  189. package/dist/core/git/object.d.ts +0 -40
  190. package/dist/core/git/object.js +0 -103
  191. package/dist/core/git/pack.d.ts +0 -9
  192. package/dist/core/git/pack.js +0 -171
  193. package/dist/core/git/store.d.ts +0 -23
  194. package/dist/core/git/store.js +0 -120
  195. package/dist/core/git/zlib.d.ts +0 -6
  196. package/dist/core/git/zlib.js +0 -210
  197. package/dist/core/harbor/carrying.d.ts +0 -51
  198. package/dist/core/harbor/carrying.js +0 -69
  199. package/dist/core/harbor/catalogue.d.ts +0 -33
  200. package/dist/core/harbor/catalogue.js +0 -365
  201. package/dist/core/harbor/dna.d.ts +0 -13
  202. package/dist/core/harbor/dna.js +0 -120
  203. package/dist/core/harbor/dock.d.ts +0 -62
  204. package/dist/core/harbor/dock.js +0 -381
  205. package/dist/core/harbor/harbor.d.ts +0 -16
  206. package/dist/core/harbor/harbor.js +0 -438
  207. package/dist/core/harbor/index.d.ts +0 -11
  208. package/dist/core/harbor/index.js +0 -14
  209. package/dist/core/harbor/memory.d.ts +0 -10
  210. package/dist/core/harbor/memory.js +0 -25
  211. package/dist/core/harbor/package.d.ts +0 -20
  212. package/dist/core/harbor/package.js +0 -172
  213. package/dist/core/harbor/probe.d.ts +0 -22
  214. package/dist/core/harbor/probe.js +0 -15
  215. package/dist/core/harbor/registry.d.ts +0 -15
  216. package/dist/core/harbor/registry.js +0 -110
  217. package/dist/core/harbor/root-line.d.ts +0 -7
  218. package/dist/core/harbor/root-line.js +0 -9
  219. package/dist/core/harbor/terrain.d.ts +0 -25
  220. package/dist/core/harbor/terrain.js +0 -28
  221. package/dist/core/http/index.d.ts +0 -2
  222. package/dist/core/http/index.js +0 -154
  223. package/dist/core/http/websocket.d.ts +0 -29
  224. package/dist/core/http/websocket.js +0 -133
  225. package/dist/core/line/answer.d.ts +0 -8
  226. package/dist/core/line/answer.js +0 -60
  227. package/dist/core/line/frame.d.ts +0 -29
  228. package/dist/core/line/frame.js +0 -77
  229. package/dist/core/line/ground.d.ts +0 -3
  230. package/dist/core/line/ground.js +0 -7
  231. package/dist/core/line/index.d.ts +0 -4
  232. package/dist/core/line/index.js +0 -8
  233. package/dist/core/line/web.d.ts +0 -12
  234. package/dist/core/line/web.js +0 -117
  235. package/dist/core/node/index.d.ts +0 -23
  236. package/dist/core/node/index.js +0 -124
  237. package/dist/core/pointer/bodies.d.ts +0 -31
  238. package/dist/core/pointer/bodies.js +0 -118
  239. package/dist/core/pointer/index.d.ts +0 -2
  240. package/dist/core/pointer/index.js +0 -4
  241. package/dist/core/pointer/world.d.ts +0 -45
  242. package/dist/core/pointer/world.js +0 -106
  243. package/dist/core/proof/contracts.d.ts +0 -19
  244. package/dist/core/proof/contracts.js +0 -116
  245. package/dist/core/proof/expect.d.ts +0 -6
  246. package/dist/core/proof/expect.js +0 -18
  247. package/dist/core/proof/index.d.ts +0 -3
  248. package/dist/core/proof/index.js +0 -14
  249. package/dist/core/proof/law.d.ts +0 -25
  250. package/dist/core/proof/law.js +0 -715
  251. package/dist/core/quo/address.d.ts +0 -19
  252. package/dist/core/quo/address.js +0 -84
  253. package/dist/core/quo/door.d.ts +0 -34
  254. package/dist/core/quo/door.js +0 -174
  255. package/dist/core/quo/index.d.ts +0 -9
  256. package/dist/core/quo/index.js +0 -12
  257. package/dist/core/quo/invitation.d.ts +0 -8
  258. package/dist/core/quo/invitation.js +0 -19
  259. package/dist/core/quo/keys.d.ts +0 -40
  260. package/dist/core/quo/keys.js +0 -79
  261. package/dist/core/quo/payload.d.ts +0 -15
  262. package/dist/core/quo/payload.js +0 -55
  263. package/dist/core/quo/relations.d.ts +0 -40
  264. package/dist/core/quo/relations.js +0 -33
  265. package/dist/core/quo/reply.d.ts +0 -13
  266. package/dist/core/quo/reply.js +0 -35
  267. package/dist/core/quo/seal.d.ts +0 -42
  268. package/dist/core/quo/seal.js +0 -78
  269. package/dist/core/quo/standing.d.ts +0 -39
  270. package/dist/core/quo/standing.js +0 -90
  271. package/dist/core/tcp/index.d.ts +0 -27
  272. package/dist/core/tcp/index.js +0 -209
  273. package/dist/core/ward/allowance.d.ts +0 -14
  274. package/dist/core/ward/allowance.js +0 -32
  275. package/dist/core/ward/cells.d.ts +0 -7
  276. package/dist/core/ward/cells.js +0 -74
  277. package/dist/core/ward/house.d.ts +0 -67
  278. package/dist/core/ward/house.js +0 -473
  279. package/dist/core/ward/index.d.ts +0 -5
  280. package/dist/core/ward/index.js +0 -7
  281. package/dist/core/ward/partition.d.ts +0 -50
  282. package/dist/core/ward/partition.js +0 -66
  283. package/dist/core/ward/stance.d.ts +0 -29
  284. package/dist/core/ward/stance.js +0 -186
  285. package/dist/core/ward/ward.d.ts +0 -44
  286. package/dist/core/ward/ward.js +0 -140
  287. package/dist/core.d.ts +0 -10
  288. package/dist/core.js +0 -19
  289. package/dist/defaults/dock/index.d.ts +0 -4
  290. package/dist/defaults/dock/index.js +0 -11
  291. package/dist/defaults/index.d.ts +0 -5
  292. package/dist/defaults/index.js +0 -10
  293. package/dist/defaults/memory/index.d.ts +0 -12
  294. package/dist/defaults/memory/index.js +0 -39
  295. package/dist/defaults/tcp/index.d.ts +0 -16
  296. package/dist/defaults/tcp/index.js +0 -82
  297. package/dist/defaults/ward/index.d.ts +0 -4
  298. package/dist/defaults/ward/index.js +0 -11
  299. package/dist/defaults/web/index.d.ts +0 -15
  300. package/dist/defaults/web/index.js +0 -90
  301. package/dist/edge.d.ts +0 -1
  302. package/dist/edge.js +0 -9
  303. package/dist/kit.d.ts +0 -4
  304. package/dist/kit.js +0 -12
  305. package/dist/node.d.ts +0 -1
  306. package/dist/node.js +0 -8
  307. package/src/browser.ts +0 -10
  308. package/src/cli.ts +0 -15
  309. package/src/core/being/being.ts +0 -84
  310. package/src/core/being/digest.ts +0 -18
  311. package/src/core/being/faculty.ts +0 -76
  312. package/src/core/being/index.ts +0 -8
  313. package/src/core/being/kind.ts +0 -37
  314. package/src/core/being/types.ts +0 -74
  315. package/src/core/being/words.ts +0 -31
  316. package/src/core/browser/index.ts +0 -219
  317. package/src/core/browser/worker.ts +0 -63
  318. package/src/core/cli/command.ts +0 -311
  319. package/src/core/cli/edge.ts +0 -86
  320. package/src/core/cli/harbor.ts +0 -120
  321. package/src/core/contract/index.ts +0 -82
  322. package/src/core/contract/link.ts +0 -58
  323. package/src/core/crypto/aes.ts +0 -26
  324. package/src/core/crypto/bytes.ts +0 -37
  325. package/src/core/crypto/ed25519.ts +0 -92
  326. package/src/core/crypto/hash.ts +0 -14
  327. package/src/core/crypto/index.ts +0 -10
  328. package/src/core/crypto/json.ts +0 -241
  329. package/src/core/crypto/mlkem.ts +0 -38
  330. package/src/core/crypto/subtle.ts +0 -13
  331. package/src/core/crypto/x25519.ts +0 -23
  332. package/src/core/edge/index.ts +0 -865
  333. package/src/core/folder/index.ts +0 -137
  334. package/src/core/git/http.ts +0 -103
  335. package/src/core/git/index.ts +0 -10
  336. package/src/core/git/object.ts +0 -116
  337. package/src/core/git/pack.ts +0 -147
  338. package/src/core/git/store.ts +0 -122
  339. package/src/core/git/zlib.ts +0 -197
  340. package/src/core/harbor/carrying.ts +0 -124
  341. package/src/core/harbor/catalogue.ts +0 -386
  342. package/src/core/harbor/dna.ts +0 -116
  343. package/src/core/harbor/dock.ts +0 -420
  344. package/src/core/harbor/harbor.ts +0 -433
  345. package/src/core/harbor/index.ts +0 -14
  346. package/src/core/harbor/memory.ts +0 -31
  347. package/src/core/harbor/package.ts +0 -195
  348. package/src/core/harbor/probe.ts +0 -39
  349. package/src/core/harbor/registry.ts +0 -107
  350. package/src/core/harbor/root-line.ts +0 -19
  351. package/src/core/harbor/terrain.ts +0 -54
  352. package/src/core/http/index.ts +0 -147
  353. package/src/core/http/websocket.ts +0 -124
  354. package/src/core/line/answer.ts +0 -60
  355. package/src/core/line/frame.ts +0 -83
  356. package/src/core/line/ground.ts +0 -18
  357. package/src/core/line/index.ts +0 -8
  358. package/src/core/line/web.ts +0 -123
  359. package/src/core/node/index.ts +0 -139
  360. package/src/core/pointer/bodies.ts +0 -122
  361. package/src/core/pointer/index.ts +0 -4
  362. package/src/core/pointer/world.ts +0 -134
  363. package/src/core/proof/contracts.ts +0 -134
  364. package/src/core/proof/expect.ts +0 -25
  365. package/src/core/proof/index.ts +0 -14
  366. package/src/core/proof/law.ts +0 -742
  367. package/src/core/quo/address.ts +0 -87
  368. package/src/core/quo/door.ts +0 -180
  369. package/src/core/quo/index.ts +0 -12
  370. package/src/core/quo/invitation.ts +0 -19
  371. package/src/core/quo/keys.ts +0 -91
  372. package/src/core/quo/payload.ts +0 -61
  373. package/src/core/quo/relations.ts +0 -62
  374. package/src/core/quo/reply.ts +0 -38
  375. package/src/core/quo/seal.ts +0 -101
  376. package/src/core/quo/standing.ts +0 -111
  377. package/src/core/stand/main.ts +0 -20
  378. package/src/core/stand/stand.ts +0 -259
  379. package/src/core/tcp/index.ts +0 -227
  380. package/src/core/ward/allowance.ts +0 -47
  381. package/src/core/ward/cells.ts +0 -77
  382. package/src/core/ward/house.ts +0 -524
  383. package/src/core/ward/index.ts +0 -7
  384. package/src/core/ward/partition.ts +0 -117
  385. package/src/core/ward/stance.ts +0 -207
  386. package/src/core/ward/ward.ts +0 -166
  387. package/src/core.ts +0 -55
  388. package/src/defaults/dock/index.ts +0 -12
  389. package/src/defaults/index.ts +0 -10
  390. package/src/defaults/memory/index.ts +0 -50
  391. package/src/defaults/tcp/index.ts +0 -89
  392. package/src/defaults/ward/index.ts +0 -12
  393. package/src/defaults/web/index.ts +0 -97
  394. package/src/edge.ts +0 -11
  395. package/src/index.ts +0 -18
  396. package/src/kit.ts +0 -15
  397. package/src/node.ts +0 -10
  398. /package/dist/{cli.d.ts → node/cli.d.ts} +0 -0
@@ -1,433 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The harbor: the onion, unpacked from a terrain alone, in three levels.
3
- // Each level stands the next and leaves it alone.
4
- //
5
- // L1, the boot, the harbor's own, the core alone
6
- // the seed and the package the harbor's one secret, from custody,
7
- // opens its sealed memory, the vault
8
- // the box ward unpacked as every ward is, under the
9
- // harbor's seed, no being standing yet
10
- // the catalogue the core's registry, stood from the core
11
- // on empty memory and from her row after;
12
- // every module the commit `live` names is
13
- // run through the terrain's loader
14
- // the registries every box faculty the catalogue resolves
15
- // to a registry, where her row names the
16
- // catalogue as hers
17
- // the dock the box ward's ward, of the class her row
18
- // names through its registry, the default
19
- // dock where it does not stand; the door is
20
- // hers from here, and `open` returns
21
- // L2, the dock's her `stand` ask: every other faculty, the
22
- // routes, and every hosted ward, each ward
23
- // standing on the class, the registry and
24
- // the memory the dock keeps for it
25
- // L3, each ward's beings born from her rows for the asks that
26
- // reach them, and dropped after; nothing
27
- // stands them
28
- //
29
- // L1 is one call and no ask can make it. After it the harbor takes asks
30
- // alone. Whoever holds a harbor has five things of it and no other:
31
- //
32
- // Harbor.open(terrain?) the boot
33
- // ask(request) the root line, a ward asked as its root
34
- // arrive(pk, box) a Quo box at one of its doors
35
- // holds(pk) whether one of its doors is that ward's
36
- // close() every carrier asleep, and the ground let go
37
- //
38
- // What stands inside it, the wards, the dock, the catalogue and the
39
- // carriers, is reached by an ask and never by a hand. The box ward's
40
- // stance carries the harbor's `ground`, the one thing the dock, the
41
- // catalogue and every faculty of the box are handed.
42
- //
43
- // The harbor fails only where the seed or the box ward's own place cannot
44
- // be read, or the catalogue does not stand. A module, a registry, a
45
- // faculty, a route, a hosted ward or a being that cannot stand fails
46
- // alone, and the census names it.
47
- //
48
- // Genesis and wake are one path: on empty memory the box ward holds no
49
- // catalogue, and the harbor writes her from the core; the root's asks
50
- // write the rest, and nothing enters unasked.
51
- import { isSilence, isWord, told, type BeingClass, type Invitation, type Json, type JsonObject } from '../being/index.ts';
52
- import type { Carrier } from '../contract/index.ts';
53
- import { hex, isHex, unhex } from '../crypto/index.ts';
54
- import { unspecified, WardKey } from '../quo/index.ts';
55
- import { DOCK, House, type Classes, type HouseParts, type Routes, type WardClass } from '../ward/index.ts';
56
- import { Catalogue, CATALOGUE, type Cataloguing } from './catalogue.ts';
57
- import { CarrierFaculty, UNDIALED, type Ground } from './carrying.ts';
58
- import { BOX, Dock, hostable, type Failures, type Hosted, type Hosting } from './dock.ts';
59
- import { Dna } from './dna.ts';
60
- import { Package, type Keeper } from './package.ts';
61
- import { MemoryFaculty } from './memory.ts';
62
- import { registers, RegistryFaculty } from './registry.ts';
63
- import { probeGround, type Probing } from './probe.ts';
64
- import { readRootRequest } from './root-line.ts';
65
- import { Terrain, type Defaults } from './terrain.ts';
66
-
67
- // The box ward's ward is a dock, the terrain's default at genesis and
68
- // while her row's kind does not resolve.
69
- const boxWard = ({ dock }: Defaults): WardClass => ({ genesis: dock.kind, contract: 'dock', fits: (C) => Dock.fulfils(C), fallback: dock });
70
-
71
- // A hosted ward's ward is a ward and no dock, of the kind `host` named or
72
- // the terrain's default at genesis.
73
- const hostedWard = ({ ward }: Defaults, kind: string = ward.kind): WardClass => ({ genesis: kind, contract: 'hosted ward', fits: hostable });
74
-
75
- // What the box ward's stance carries: the dock hosts through it, the
76
- // catalogue runs code through it, and every faculty of the box reaches its
77
- // terrain and the harbor's doors through it.
78
- export type HarborGround = Hosting & Cataloguing & Ground;
79
-
80
- export class Harbor {
81
- readonly #terrain: Terrain;
82
- readonly #package: Package;
83
- readonly #wards = new Map<string, House>();
84
- readonly #failed: Failures = { wards: {}, routes: {} };
85
- readonly #ground: HarborGround;
86
- // Every ward speaks through the harbor: a pk of one of its doors goes to
87
- // that door, any other along its route or to the terrain's carrier.
88
- readonly #carrier: Carrier = { carry: (pk, bytes) => this.#carry(pk, bytes) };
89
- // Every class a row names, through the registry faculty of the box the
90
- // row names, or the catalogue.
91
- readonly #classes: Classes = { classOf: (kind, registry) => this.#classOf(kind, registry) };
92
-
93
- private constructor(terrain: Terrain, pack: Package) {
94
- const { dock, ward, classes } = terrain.defaults;
95
- if (!Dock.fulfils(dock)) throw new Error('the default dock is no dock');
96
- if (!hostable(ward)) throw new Error('the default ward is no hosted ward');
97
- this.#terrain = terrain;
98
- this.#package = pack;
99
- const dna = new Dna(terrain.memory, pack);
100
- // The kit's classes the catalogue resolves: her own, the core's alone,
101
- // then the defaults the terrain hands in.
102
- const kit = [Catalogue, dock, ward, ...classes];
103
- this.#ground = {
104
- terrain,
105
- dna,
106
- kit,
107
- foreground: terrain.foreground,
108
- arrive: (pk, bytes) => this.arrive(pk, bytes),
109
- host: (name, invitation, seed, memory, kind, registry) => this.#host(name, invitation, seed, memory, kind, registry),
110
- stand: (hosted) => this.#stand(hosted),
111
- move: (name, seed, from, to) => this.#move(name, seed, from, to),
112
- release: async (name, memory) => this.#keeper(memory).forget(await this.#package.place(name)),
113
- route: (pk, at) => this.#route(pk, at),
114
- reached: (pk, at) => {
115
- if (!this.#dialable(at)) this.#failed.routes[pk] = 'no carrier dials it';
116
- },
117
- keep: () => this.#box.partition.kept(),
118
- failures: () => this.#failures(),
119
- sweep: () => this.#sweep(),
120
- awaken: async (key) => {
121
- const being = this.#box.being(key);
122
- if (being instanceof CarrierFaculty) await being.awake();
123
- },
124
- rouse: async (key) => {
125
- if (terrain.wakes) await this.#ground.awaken(key);
126
- },
127
- lull: async (key) => {
128
- const being = this.#box.being(key);
129
- if (being instanceof CarrierFaculty) await being.sleep();
130
- },
131
- listening: () => this.#carriers.flatMap((carrier) => carrier.listening()),
132
- heard: async () => {
133
- await Promise.all(this.#carriers.map((carrier) => carrier.heard()));
134
- },
135
- ran: () => {
136
- for (const house of this.#wards.values()) house.renew();
137
- return Promise.resolve();
138
- },
139
- stands: (kinds) => [...this.#wards.values()].some((house) => Object.values(house.partition.beings).some((row) => kinds.includes(row.class))),
140
- kept: () => [...new Set([...this.#wards.values()].flatMap((house) => Object.values(house.partition.beings).map((row) => row.class)))],
141
- };
142
- }
143
-
144
- // A harbor on the terrain handed in, or, with none, on the ground the
145
- // probe finds here. A ground another run holds refuses, and what the
146
- // ground serves beside the harbor stands once it is whole.
147
- static async open(from: Terrain | Probing = {}): Promise<Harbor> {
148
- const terrain = from instanceof Terrain ? from : probeGround(from).terrain;
149
- await terrain.claim();
150
- let harbor: Harbor;
151
- try {
152
- harbor = await Harbor.#boot(terrain);
153
- } catch (e) {
154
- await terrain.release();
155
- throw e;
156
- }
157
- try {
158
- await terrain.stand(harbor);
159
- } catch (e) {
160
- await harbor.close();
161
- throw e;
162
- }
163
- return harbor;
164
- }
165
-
166
- // L1, the one call no ask makes, ending with the door handed to the dock.
167
- // What stands beyond it stands because someone asked `stand`.
168
- static async #boot(terrain: Terrain): Promise<Harbor> {
169
- const seed = await terrain.custody.seed();
170
- const harbor = new Harbor(terrain, await Package.open(terrain.memory, terrain.entropy, seed));
171
- const box = await harbor.#unpack(BOX, seed, { ward: boxWard(terrain.defaults), carries: { ground: harbor.#ground }, classes: harbor.#classes, stands: true });
172
- if (box.partition.being(CATALOGUE)) box.stand(CATALOGUE);
173
- else if ((await box.make(CATALOGUE, Catalogue.kind)) !== 'booted' || !(await box.partition.kept())) throw new Error('the catalogue did not stand');
174
- const catalogue = box.being(CATALOGUE);
175
- if (!(catalogue instanceof Catalogue)) throw new Error('the catalogue did not stand');
176
- await catalogue.wake();
177
- for (const [key, row] of Object.entries(box.partition.beings)) {
178
- if (row.registry === undefined && registers(catalogue.classOf(row.class))) box.stand(key);
179
- }
180
- await box.standWard();
181
- return harbor;
182
- }
183
-
184
- // The root line: one request, `{ ward?, method?, args? }`, asked of the
185
- // box ward or the hosted ward named as its root, and one plain JSON
186
- // answer, whatever front carried it.
187
- //
188
- // { answer } what the ward said
189
- // { silence: true } she said nothing
190
- // { word } a kit word: unreached, late, ...
191
- // { error } the request was none, or names no ward that
192
- // stands, with the reason one did not
193
- async ask(value: unknown): Promise<JsonObject> {
194
- const request = readRootRequest(value);
195
- if (request === null) return { error: 'not a root request' };
196
- const ward = request.ward === undefined ? this.#box : this.#wards.get(request.ward);
197
- if (ward === undefined) {
198
- const failed = this.#failures().wards[request.ward!];
199
- return failed === undefined ? { error: 'no such ward' } : { error: 'the ward did not stand', reason: failed };
200
- }
201
- const out = await ward.root(request.method, request.args);
202
- if (isSilence(out)) return { silence: true };
203
- if (isWord(out)) return { word: told(out) as string };
204
- return { answer: out as Json };
205
- }
206
-
207
- // Bytes for one of this harbor's doors, or for a ward a relay of this
208
- // harbor answers for, or nothing.
209
- async arrive(pk: string, bytes: Uint8Array): Promise<Uint8Array | null> {
210
- const ward = this.#door(pk);
211
- if (ward) return (await ward.door.arrive(bytes)).bytes;
212
- return (await this.#relaying(pk)?.relay(pk, bytes)) ?? null;
213
- }
214
-
215
- // Whether one of this harbor's doors is that ward's.
216
- holds(pk: string): boolean {
217
- return this.#door(pk) !== undefined;
218
- }
219
-
220
- // Every carrier asleep, the last opened first: nothing listens and no
221
- // line stays open.
222
- async close(): Promise<void> {
223
- for (const carrier of this.#carriers.reverse()) await carrier.sleep();
224
- await this.#terrain.release();
225
- }
226
-
227
- get #box(): House {
228
- return this.#wards.get(BOX)!;
229
- }
230
-
231
- get #dock(): Dock {
232
- return this.#box.ward as Dock;
233
- }
234
-
235
- // A kind, through the registry faculty of the box named, or the
236
- // catalogue. The catalogue's own kind is the core's, and resolves before
237
- // any registry stands.
238
- #classOf(kind: string, registry: string = CATALOGUE): BeingClass | undefined {
239
- if (kind === Catalogue.kind) return Catalogue;
240
- const found = this.#wards.get(BOX)?.being(registry);
241
- return found instanceof RegistryFaculty ? found.classOf(kind) : undefined;
242
- }
243
-
244
- // The classes of a hosted ward, every one through the registry the dock
245
- // keeps for it.
246
- #through(registry: string | undefined): Classes {
247
- return { classOf: (kind) => this.#classOf(kind, registry) };
248
- }
249
-
250
- // A hosted ward standing, as the dock's cells name it.
251
- async #stand(hosted: Hosted): Promise<boolean> {
252
- const fresh = !this.#wards.has(hosted.ward);
253
- try {
254
- if (fresh) await this.#unpack(hosted.ward, unhex(hosted.seed), { ward: hostedWard(this.#terrain.defaults), classes: this.#through(hosted.registry) }, this.#keeper(hosted.memory));
255
- await this.#wards.get(hosted.ward)!.standWard();
256
- delete this.#failed.wards[hosted.ward];
257
- return fresh;
258
- } catch (e) {
259
- this.#wards.delete(hosted.ward);
260
- this.#failed.wards[hosted.ward] = e instanceof Error ? e.message : String(e);
261
- return false;
262
- }
263
- }
264
-
265
- // Every place of the root memory and of each memory faculty that the
266
- // dock does not name there, gone. A crash inside `host` or `move` leaves
267
- // such a place, sealed under a seed nothing kept or kept elsewhere, and
268
- // nothing else writes one. A place a memory faculty keeps in the root
269
- // memory is the memory faculty's.
270
- async #sweep(): Promise<string[]> {
271
- const named = async (memory: string | undefined): Promise<Set<string>> => {
272
- const wards = this.#dock.hosted.filter((h) => h.memory === memory).map((h) => h.ward);
273
- return new Set(await Promise.all((memory === undefined ? [BOX, ...wards] : wards).map((name) => this.#package.place(name))));
274
- };
275
- const memories = this.#memories();
276
- const swept: string[] = [];
277
- const root = await named(undefined);
278
- for (const place of await this.#terrain.memory.places()) {
279
- if (root.has(place) || Dna.occupies(place) || memories.some(([, s]) => s.occupies(place))) continue;
280
- await this.#terrain.memory.forget(place);
281
- swept.push(place);
282
- }
283
- for (const [key, memory] of memories) {
284
- const mine = await named(key);
285
- for (const place of await memory.places()) {
286
- if (mine.has(place)) continue;
287
- await memory.forget(place);
288
- swept.push(place);
289
- }
290
- }
291
- return swept;
292
- }
293
-
294
- // A standing ward, carried whole to another keeper. The ward leaves the
295
- // harbor's doors while it moves, so no arrival lands on the keeper it
296
- // leaves.
297
- async #move(name: string, seed: string, from: string | undefined, to: string | undefined): Promise<string | null> {
298
- const ward = this.#wards.get(name);
299
- if (!ward) return 'the ward does not stand';
300
- const source = this.#keeper(from);
301
- const target = this.#keeper(to);
302
- this.#wards.delete(name);
303
- try {
304
- if (!(await ward.partition.kept())) throw new Error('the ward was not kept');
305
- const place = await this.#package.place(name);
306
- const entries = new Map<string, Uint8Array | null>(await source.read(place));
307
- for (const entry of (await target.read(place)).keys()) if (!entries.has(entry)) entries.set(entry, null);
308
- await target.write(place, entries);
309
- const registry = this.#dock.hosted.find((h) => h.ward === name)?.registry;
310
- const moved = await this.#unpack(name, unhex(seed), { ward: hostedWard(this.#terrain.defaults), classes: this.#through(registry) }, target);
311
- await moved.standWard();
312
- return null;
313
- } catch (e) {
314
- this.#wards.set(name, ward);
315
- return e instanceof Error ? e.message : String(e);
316
- }
317
- }
318
-
319
- // The root memory, or the memory faculty standing under the key named.
320
- #keeper(memory: string | undefined): Keeper {
321
- if (memory === undefined) return this.#terrain.memory;
322
- const being = this.#box.being(memory);
323
- if (being instanceof MemoryFaculty) return being;
324
- throw new Error(`the memory faculty ${memory} does not stand`);
325
- }
326
-
327
- #memories(): [string, MemoryFaculty][] {
328
- return Object.keys(this.#box.partition.beings).flatMap((key): [string, MemoryFaculty][] => {
329
- const being = this.#box.being(key);
330
- return being instanceof MemoryFaculty ? [[key, being]] : [];
331
- });
332
- }
333
-
334
- // What did not stand when the harbor opened, each alone, and why the box
335
- // ward stands the default dock where her row names another.
336
- #failures(): Failures {
337
- const fault = this.#box.fault;
338
- return { wards: { ...this.#failed.wards, ...(fault === null ? {} : { [BOX]: fault }) }, routes: { ...this.#failed.routes } };
339
- }
340
-
341
- // This harbor's own doors and relays first, then a route's addresses in
342
- // order, each to the carrier that dials it, and with no route the
343
- // terrain's carrier.
344
- async #carry(pk: string, bytes: Uint8Array): Promise<Uint8Array | null> {
345
- if (this.holds(pk) || this.#relaying(pk)) return this.arrive(pk, bytes);
346
- const at = this.#dock.routes[pk];
347
- if (at === undefined) return this.#terrain.carrier.carry(pk, bytes);
348
- for (const address of at) {
349
- const carrier = this.#carriers.find((c) => c.accepts(address));
350
- const out = carrier ? await carrier.dial(address, pk, bytes) : UNDIALED;
351
- if (out !== UNDIALED) return out;
352
- }
353
- return null;
354
- }
355
-
356
- // A ward the dock hosts stands now, under the seed named or a drawn one,
357
- // and the seed it stands under comes back. A ward whose key another ward
358
- // of this harbor already has does not stand.
359
- async #host(name: string, invitation: Invitation, seed?: string, memory?: string, kind?: string, registry?: string): Promise<string | null> {
360
- if (this.#wards.has(name)) return null;
361
- const bytes = seed === undefined ? this.#terrain.entropy.draw(32) : isHex(seed, 32) ? unhex(seed) : null;
362
- if (bytes === null || this.holds((await WardKey.from(bytes)).pk)) return null;
363
- let house: House;
364
- try {
365
- house = await this.#unpack(name, bytes, { dock: invitation, ward: hostedWard(this.#terrain.defaults, kind), classes: this.#through(registry) }, this.#keeper(memory));
366
- await house.standWard();
367
- } catch {
368
- this.#wards.delete(name);
369
- return null;
370
- }
371
- if (!house.ward.stance.standings.get(DOCK)) {
372
- this.#wards.delete(name);
373
- return null;
374
- }
375
- return hex(bytes);
376
- }
377
-
378
- // A route is taken where a carrier of this harbor dials one of its
379
- // addresses; forgetting one always is.
380
- #route(pk: string, at: readonly string[] | null): boolean {
381
- const taken = isHex(pk, 64) && (at === null || this.#dialable(at));
382
- if (taken) delete this.#failed.routes[pk];
383
- return taken;
384
- }
385
-
386
- // The carrier faculties standing in the box ward.
387
- get #carriers(): CarrierFaculty[] {
388
- return Object.keys(this.#box.partition.beings)
389
- .map((key) => this.#box.being(key))
390
- .filter((being): being is CarrierFaculty => being instanceof CarrierFaculty);
391
- }
392
-
393
- #relaying(pk: string): CarrierFaculty | undefined {
394
- return this.#carriers.find((carrier) => carrier.relays(pk));
395
- }
396
-
397
- #dialable(at: readonly string[]): boolean {
398
- return at.some((address) => this.#carriers.some((carrier) => carrier.accepts(address)));
399
- }
400
-
401
- async #unpack(name: string, seed: Uint8Array, own: Pick<HouseParts, 'dock' | 'ward' | 'carries' | 'classes' | 'stands'>, keeper?: Keeper): Promise<House> {
402
- const { entropy, clock } = this.#terrain;
403
- const ward = await House.unpack({
404
- seed,
405
- memory: await this.#package.rows(name, keeper),
406
- entropy,
407
- clock,
408
- carrier: this.#carrier,
409
- routes: this.#routes,
410
- ...own,
411
- });
412
- this.#wards.set(name, ward);
413
- return ward;
414
- }
415
-
416
- // Every ward is reached where the harbor is: where the root's `reach`
417
- // says, and where it said nothing, where its carriers listen, save the
418
- // unspecified host. What a ward learns from an invitation's `at` is the
419
- // dock's to keep. A ward of this harbor needs no route.
420
- readonly #routes: Routes = {
421
- reach: () => {
422
- if (!this.#wards.has(BOX)) return [];
423
- const set = this.#dock.addresses;
424
- return set.length > 0 ? set : this.#carriers.flatMap((carrier) => carrier.listening()).filter((address) => !unspecified(address));
425
- },
426
- learn: (ward, at) => (this.holds(ward) ? Promise.resolve() : this.#dock.learn(ward, at)),
427
- };
428
-
429
- #door(pk: string): House | undefined {
430
- for (const ward of this.#wards.values()) if (ward.pk === pk) return ward;
431
- return undefined;
432
- }
433
- }
@@ -1,14 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The onion: a terrain, the package, the dock, the catalogue and its
3
- // registry, and the harbor.
4
- export { Catalogue, CATALOGUE, type Cataloguing } from './catalogue.ts';
5
- export { Dna } from './dna.ts';
6
- export { BOX, Dock, type DockStance, type Failures, type Hosted, type Hosting } from './dock.ts';
7
- export { CarrierFaculty, carrierIn, groundOf, UNDIALED, type Arrivals, type Dialed, type Ground, type Sockets, type TcpListening, type WebListen, type WebListener, type WebServe } from './carrying.ts';
8
- export { Harbor } from './harbor.ts';
9
- export { carryPlaces, copyPackage, landPlaces, Package, readCarried, type Carried } from './package.ts';
10
- export { readRootRequest, type RootRequest } from './root-line.ts';
11
- export { addGround, grounds, probeGround, type GroundProbe, type KnownGround, type Probing } from './probe.ts';
12
- export { registers, Registry, REGISTRY, RegistryFaculty } from './registry.ts';
13
- export { MEMORY, MemoryFaculty } from './memory.ts';
14
- export { Terrain, type Defaults } from './terrain.ts';
@@ -1,31 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The memory faculty: a faculty that keeps a hosted ward's place, where the
3
- // root memory does not. It makes the same promise the Memory body makes,
4
- // lent once the dock stands: sealed entries in places, each keep whole or
5
- // not at all. The box ward never moves off the root memory, since the
6
- // memory faculty itself stands from the box ward's cells.
7
- //
8
- // A ward is the unit a memory faculty holds: its head, its ward and every
9
- // being of it are one place and one keep, so an answer's cells and the
10
- // door's keys move together, whichever memory faculty keeps them. A memory
11
- // faculty of a disk, a cloud, a database or a ledger is a class fulfilling
12
- // `org.nervur.memory`.
13
- import { Faculty, ships } from '../being/index.ts';
14
-
15
- export const MEMORY = 'org.nervur.memory';
16
-
17
- export abstract class MemoryFaculty extends Faculty {
18
- static override readonly kind: string = MEMORY;
19
- static {
20
- ships(this);
21
- }
22
- abstract read(place: string): Promise<Map<string, Uint8Array>>;
23
- abstract write(place: string, entries: ReadonlyMap<string, Uint8Array | null>): Promise<void>;
24
- abstract places(): Promise<string[]>;
25
- abstract forget(place: string): Promise<void>;
26
- // Whether a place of the root memory is this memory faculty's own, so the root's
27
- // sweep leaves it.
28
- occupies(_place: string): boolean {
29
- return false;
30
- }
31
- }
@@ -1,195 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The package: a harbor's memory, sealed under the harbor's seed. The
3
- // terrain keeps its bytes and reads neither a ward's name, nor a being's
4
- // key, nor anything a row holds.
5
- //
6
- // key HKDF-SHA-256 of the seed under `nervur-package`
7
- // place 16 bytes under ["place", ward], as hex
8
- // entry 16 bytes under ["entry", ward, row], as hex
9
- // seal 32 bytes under ["seal", ward]
10
- // entry bytes nonce (12) || AES-256-GCM( JSON { row, value } ),
11
- // the entry's name as additional data
12
- //
13
- // A row is the head, under the empty name, or one being, under her key. An
14
- // entry that does not open refuses the whole ward, so nothing unpacks from
15
- // bytes the package did not seal.
16
- import { concat, decrypt, encrypt, hex, hkdf, isHex, unhex, utf8 } from '../crypto/index.ts';
17
- import type { Entropy, Memory, Rows } from '../contract/index.ts';
18
- import { HEAD_ROW } from '../ward/index.ts';
19
-
20
- // Every place of one memory, written into another, and the places
21
- // carried. The bytes are the package's own, sealed under a seed neither
22
- // memory holds, so a harbor opened on the second memory under that seed
23
- // stands what the first one stood. A place the second memory already
24
- // holds is written over.
25
- export const copyPackage = async (from: Memory, to: Memory): Promise<string[]> => {
26
- const places = await from.places();
27
- for (const place of places) await to.write(place, await from.read(place));
28
- return places;
29
- };
30
-
31
- // A harbor as one JSON value, for carrying it to another ground: every
32
- // place of its memory with its sealed entries as hex, its DNA among them,
33
- // and its seed only where the one who carries it says so.
34
- export type Carried = { seed?: string; places: Record<string, Record<string, string>> };
35
-
36
- // A carried harbor as JSON reads, or null for anything else.
37
- export const readCarried = (value: unknown): Carried | null => {
38
- if (typeof value !== 'object' || value === null) return null;
39
- const { seed, places } = value as { seed?: unknown; places?: unknown };
40
- if (seed !== undefined && !isHex(seed, 32)) return null;
41
- if (typeof places !== 'object' || places === null || Array.isArray(places)) return null;
42
- for (const entries of Object.values(places)) {
43
- if (typeof entries !== 'object' || entries === null || Array.isArray(entries)) return null;
44
- for (const bytes of Object.values(entries)) if (!isHex(bytes)) return null;
45
- }
46
- return { ...(seed === undefined ? {} : { seed }), places: places as Carried['places'] };
47
- };
48
-
49
- // Every place of a memory, as a carried harbor holds it.
50
- export const carryPlaces = async (memory: Memory): Promise<Carried['places']> => {
51
- const places: Carried['places'] = {};
52
- for (const place of await memory.places()) {
53
- places[place] = Object.fromEntries([...(await memory.read(place))].map(([name, bytes]) => [name, hex(bytes)]));
54
- }
55
- return places;
56
- };
57
-
58
- // A carried harbor's places, written into a memory, and their names.
59
- export const landPlaces = async (memory: Memory, carried: Carried): Promise<string[]> => {
60
- for (const [place, entries] of Object.entries(carried.places)) {
61
- await memory.write(place, new Map(Object.entries(entries).map(([name, bytes]) => [name, unhex(bytes)])));
62
- }
63
- return Object.keys(carried.places);
64
- };
65
-
66
- const NONCE = 12;
67
- const LABEL = utf8('nervur-package');
68
-
69
- type Sealed = { row: string; value: unknown };
70
-
71
- class PackageRows implements Rows {
72
- readonly rows: Record<string, unknown>;
73
- readonly #ward: WardPlace;
74
- readonly #told = new Set<string>();
75
- #line: Promise<boolean> = Promise.resolve(true);
76
-
77
- constructor(ward: WardPlace, rows: Record<string, unknown>) {
78
- this.#ward = ward;
79
- this.rows = rows;
80
- }
81
-
82
- told(row: string): void {
83
- this.#told.add(row);
84
- }
85
-
86
- // Every row told since the last keep, sealed and written as one keep, one
87
- // keep at a time. Where nothing was told, nothing is written, so an ask
88
- // that only reads keeps even where the keeper refuses every write.
89
- kept(): Promise<boolean> {
90
- const rows = [...this.#told];
91
- this.#told.clear();
92
- this.#line = this.#line.then(() => this.#write(rows)).catch(() => false);
93
- return this.#line;
94
- }
95
-
96
- async #write(told: string[]): Promise<boolean> {
97
- if (told.length === 0) return true;
98
- const beings = (this.rows.beings ?? {}) as Record<string, unknown>;
99
- const entries = new Map<string, Uint8Array | null>();
100
- for (const row of told) {
101
- const value = row === HEAD_ROW ? (this.rows.ward ?? null) : Object.hasOwn(beings, row) ? beings[row] : undefined;
102
- entries.set(await this.#ward.entry(row), value === undefined ? null : await this.#ward.seal(row, value));
103
- }
104
- await this.#ward.write(entries);
105
- return true;
106
- }
107
- }
108
-
109
- // What keeps a ward's place: the root memory, or a memory faculty the dock
110
- // lends.
111
- export type Keeper = Pick<Memory, 'read' | 'write' | 'places' | 'forget'>;
112
-
113
- class WardPlace {
114
- readonly #pack: Package;
115
- readonly #name: string;
116
- readonly #seal: Uint8Array;
117
- readonly #keeper: Keeper;
118
- readonly place: string;
119
-
120
- constructor(pack: Package, name: string, seal: Uint8Array, place: string, keeper: Keeper) {
121
- this.#pack = pack;
122
- this.#name = name;
123
- this.#seal = seal;
124
- this.place = place;
125
- this.#keeper = keeper;
126
- }
127
-
128
- read(): Promise<Map<string, Uint8Array>> {
129
- return this.#keeper.read(this.place);
130
- }
131
-
132
- entry(row: string): Promise<string> {
133
- return this.#pack.derive(['entry', this.#name, row], 16).then(hex);
134
- }
135
-
136
- async seal(row: string, value: unknown): Promise<Uint8Array> {
137
- const nonce = this.#pack.entropy.draw(NONCE);
138
- const text = utf8(JSON.stringify({ row, value } satisfies Sealed));
139
- return concat(nonce, await encrypt(this.#seal, nonce, utf8(await this.entry(row)), text));
140
- }
141
-
142
- // The name is the additional data, so an entry moved under another name
143
- // does not open either.
144
- async open(name: string, bytes: Uint8Array): Promise<Sealed> {
145
- const text = bytes.length > NONCE ? await decrypt(this.#seal, bytes.subarray(0, NONCE), utf8(name), bytes.subarray(NONCE)) : null;
146
- if (text === null) throw new Error('an entry the package did not seal');
147
- return JSON.parse(new TextDecoder().decode(text)) as Sealed;
148
- }
149
-
150
- write(entries: ReadonlyMap<string, Uint8Array | null>): Promise<void> {
151
- return this.#keeper.write(this.place, entries);
152
- }
153
- }
154
-
155
- export class Package {
156
- readonly memory: Memory;
157
- readonly entropy: Entropy;
158
- readonly #key: Uint8Array;
159
-
160
- private constructor(memory: Memory, entropy: Entropy, key: Uint8Array) {
161
- this.memory = memory;
162
- this.entropy = entropy;
163
- this.#key = key;
164
- }
165
-
166
- static async open(memory: Memory, entropy: Entropy, seed: Uint8Array): Promise<Package> {
167
- return new Package(memory, entropy, await hkdf(seed, LABEL, 32));
168
- }
169
-
170
- derive(info: string[], length: number): Promise<Uint8Array> {
171
- return hkdf(this.#key, utf8(JSON.stringify(info)), length);
172
- }
173
-
174
- // The place a ward's rows live in.
175
- place(name: string): Promise<string> {
176
- return this.derive(['place', name], 16).then(hex);
177
- }
178
-
179
- // One ward's rows, as its keeper kept them: the root memory unless a
180
- // memory faculty is named.
181
- async rows(name: string, keeper: Keeper = this.memory): Promise<Rows> {
182
- const ward = new WardPlace(this, name, await this.derive(['seal', name], 32), await this.place(name), keeper);
183
- const rows: Record<string, unknown> = {};
184
- const beings: Record<string, unknown> = {};
185
- let any = false;
186
- for (const [entry, bytes] of await ward.read()) {
187
- const { row, value } = await ward.open(entry, bytes);
188
- any = true;
189
- if (row === HEAD_ROW) rows.ward = value;
190
- else beings[row] = value;
191
- }
192
- if (any) rows.beings = beings;
193
- return new PackageRows(ward, rows);
194
- }
195
- }