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,111 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The standing's side of a relation: it seals asks and reads replies, and
3
- // moves to its announced key and the following edge key when an object
4
- // comes back to an ask above every ask it has moved on, and on nothing else.
5
- //
6
- // Its state is a value, so a ward keeps it in its partition.
7
- import { encapsulate, hex, unhex } from '../crypto/index.ts';
8
- import type { Invitation } from './invitation.ts';
9
- import { Ephemeral, SigningKey, wardHalves, type Draw } from './keys.ts';
10
- import { writePayload } from './payload.ts';
11
- import { readReply, type Reply } from './reply.ts';
12
- import { checkBody, follow, knockEdge, openReply, sealAsk, signBody, splitBody } from './seal.ts';
13
-
14
- export type Read = Reply | { readonly nothing: true };
15
-
16
- // A knock that brought no object back: its bytes, sent again as they are,
17
- // and what the standing moves to if it bound.
18
- type Knock = { readonly bytes: string; readonly lid: string; readonly next: string; readonly edge: string; readonly seq: number };
19
-
20
- export type StandingState = {
21
- readonly key: string | null;
22
- readonly edge: string | null;
23
- readonly sent: number;
24
- readonly moved: number;
25
- readonly knock: Knock | null;
26
- // Whether the last ask after an unanswered knock asked under its own key,
27
- // so the next sends the knock again.
28
- readonly probed: boolean;
29
- };
30
-
31
- export const freshStanding = (): StandingState => ({ key: null, edge: null, sent: 0, moved: 0, knock: null, probed: false });
32
-
33
- // One ask out: what `read` needs to read its reply and to move.
34
- export type Sent = { readonly bytes: Uint8Array; readonly lid: Ephemeral; readonly next: string; readonly edge: string; readonly seq: number };
35
-
36
- export class Standing {
37
- readonly invitation: Invitation;
38
- state: StandingState;
39
- readonly draw: Draw;
40
-
41
- constructor(invitation: Invitation, state: StandingState, draw: Draw) {
42
- this.invitation = invitation;
43
- this.state = state;
44
- this.draw = draw;
45
- }
46
-
47
- async ask(method?: string, args?: string): Promise<Sent | null> {
48
- const s = this.state;
49
- if (s.key !== null) return this.#ask(unhex(s.key), unhex(s.edge!), method, args);
50
- if (s.knock === null) return this.#knock(method, args);
51
- if (!s.probed) {
52
- this.state = { ...s, probed: true };
53
- return this.#ask(unhex(s.knock.next), unhex(s.knock.edge), method, args);
54
- }
55
- this.state = { ...s, probed: false };
56
- // The same bytes again: a new lid would be a new box.
57
- return { bytes: unhex(s.knock.bytes), lid: await Ephemeral.from(unhex(s.knock.lid)), next: s.knock.next, edge: s.knock.edge, seq: s.knock.seq };
58
- }
59
-
60
- async read(sent: Sent, bytes: Uint8Array | null): Promise<Read> {
61
- if (bytes === null) return { nothing: true };
62
- const reply = await this.#open(sent, bytes);
63
- if (reply === null) return { silence: true };
64
- if ('object' in reply.text && sent.seq > this.state.moved) {
65
- const edge = await follow(unhex(sent.edge), reply.agreement);
66
- this.state = { ...this.state, key: sent.next, edge: hex(edge), moved: sent.seq, knock: null, probed: false };
67
- }
68
- return reply.text;
69
- }
70
-
71
- async #knock(method?: string, args?: string): Promise<Sent | null> {
72
- const heir = await SigningKey.from(unhex(this.invitation.secret));
73
- const next = await SigningKey.draw(this.draw);
74
- const lid = await Ephemeral.draw(this.draw);
75
- const sent = encapsulate(unhex(this.invitation.lock), this.draw(32));
76
- if (sent === null) return null;
77
- const edge = await knockEdge(sent.shared);
78
- const seq = this.state.sent + 1;
79
- const body = await this.#body(heir, next, seq, method, args);
80
- const bytes = await sealAsk(lid, unhex(wardHalves(this.invitation.ward).padlock), unhex(this.invitation.heir), edge, body, sent.ciphertext);
81
- if (bytes === null) return null;
82
- this.state = { ...this.state, sent: seq, knock: { bytes: hex(bytes), lid: hex(lid.secret), next: hex(next.secret), edge: hex(edge), seq } };
83
- return { bytes, lid, next: hex(next.secret), edge: hex(edge), seq };
84
- }
85
-
86
- async #ask(secret: Uint8Array, edge: Uint8Array, method?: string, args?: string): Promise<Sent | null> {
87
- const by = await SigningKey.from(secret);
88
- const next = await SigningKey.draw(this.draw);
89
- const lid = await Ephemeral.draw(this.draw);
90
- const seq = this.state.sent + 1;
91
- const body = await this.#body(by, next, seq, method, args);
92
- const bytes = await sealAsk(lid, unhex(wardHalves(this.invitation.ward).padlock), unhex(this.invitation.heir), edge, body);
93
- if (bytes === null) return null;
94
- this.state = { ...this.state, sent: seq };
95
- return { bytes, lid, next: hex(next.secret), edge: hex(edge), seq };
96
- }
97
-
98
- #body(by: SigningKey, next: SigningKey, seq: number, method?: string, args?: string): Promise<Uint8Array> {
99
- const text = writePayload({ to: this.invitation.heir, by: by.id, next: next.id, seq, ...(method === undefined ? {} : { method }), ...(args === undefined ? {} : { args }) });
100
- return signBody(new TextEncoder().encode(text), by);
101
- }
102
-
103
- async #open(sent: Sent, bytes: Uint8Array): Promise<{ text: Reply; agreement: Uint8Array } | null> {
104
- const opened = await openReply(bytes, sent.lid);
105
- const body = opened && splitBody(opened.bytes);
106
- if (!opened || !body) return null;
107
- if (!(await checkBody(unhex(wardHalves(this.invitation.ward).signing), body))) return null;
108
- const text = readReply(body.text);
109
- return text && { text, agreement: opened.agreement };
110
- }
111
- }
@@ -1,20 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The stand program: one JSON request a line on stdin, one answer a line on
3
- // stdout, until stdin closes.
4
- import { createInterface } from 'node:readline';
5
- import { Stand } from './stand.ts';
6
-
7
- const stand = new Stand((n) => globalThis.crypto.getRandomValues(new Uint8Array(n)));
8
- const pending: Promise<void>[] = [];
9
-
10
- for await (const line of createInterface({ input: process.stdin, crlfDelay: Infinity })) {
11
- if (line === '') continue;
12
- pending.push(
13
- stand.handle(line).then(
14
- (answer) => void process.stdout.write(`${JSON.stringify(answer)}\n`),
15
- (error: unknown) => void process.stderr.write(`${String(error)}\n`),
16
- ),
17
- );
18
- }
19
- await Promise.all(pending);
20
- await stand.close();
@@ -1,259 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The stand of `quo/vectors/HARNESS.md`, over `quo/`, `nervur/tcp` and
3
- // `nervur/http`: part one judged, and part two carried, its wards
4
- // answering on one tcp listener and one web listener, a post's and a held
5
- // line's, and asking through a dialer of each.
6
- import { hex, isHex, unhex } from '../crypto/index.ts';
7
- import { UNDIALED, type Dialed, type WebListener } from '../harbor/index.ts';
8
- import { webServe } from '../http/index.ts';
9
- import { WebDialer } from '../line/index.ts';
10
- import { Door, freshStanding, MemoryRelations, readInvitation, Standing, tcpAddress, WardKey, webAddress, type Behind, type Choice, type Draw, type Invitation, type Payload, type Read, type Sent } from '../quo/index.ts';
11
- import { TcpCarrier, TcpListener } from '../tcp/index.ts';
12
-
13
- type Answer = Record<string, unknown>;
14
- const ERRORS = ['bad request', 'no such op', 'no such ward', 'not reached', 'ward stood', 'name held'] as const;
15
- type Error = (typeof ERRORS)[number];
16
- class Refused {
17
- readonly error: Error;
18
- constructor(error: Error) {
19
- this.error = error;
20
- }
21
- }
22
- const refuse = (error: Error): never => {
23
- throw new Refused(error);
24
- };
25
-
26
- // What the verifier names behind a door.
27
- const REACHES: Record<string, (p: Payload) => Choice> = {
28
- echo: (p) => ({ object: p.method === undefined ? '{}' : (p.args ?? '{}'), seen: null }),
29
- marked: (p) => ({ object: p.method === undefined ? '{}' : (p.args ?? '{}'), seen: p.method === undefined ? null : '1' }),
30
- null: () => ({ object: 'null', seen: null }),
31
- silent: () => ({ silence: true }),
32
- };
33
-
34
- class StandWard implements Behind {
35
- readonly door: Door;
36
- readonly zero: boolean;
37
- readonly #zero: string | undefined;
38
- readonly names = new Map<string, string>();
39
- readonly reaches = new Map<string, string>();
40
- readonly standings = new Map<string, { standing: Standing; last?: Sent }>();
41
-
42
- constructor(key: WardKey, zero: string | undefined, draw: Draw) {
43
- this.#zero = zero;
44
- this.zero = zero !== undefined;
45
- this.door = new Door(key, new MemoryRelations(), this, draw);
46
- }
47
-
48
- answer(heir: string | null, p: Payload): Promise<Choice> {
49
- const reach = heir === null ? this.#zero! : this.reaches.get(heir);
50
- return Promise.resolve(reach === undefined ? { silence: true } : REACHES[reach]!(p));
51
- }
52
- }
53
-
54
- const OPS = new Set(['ward', 'invite', 'release', 'arrive', 'ask', 'read', 'listen', 'route', 'send']);
55
-
56
- export class Stand {
57
- readonly #wards = new Map<string, StandWard>();
58
- readonly #draw: Draw;
59
- readonly #tcp = new TcpCarrier();
60
- readonly #web = new WebDialer();
61
- readonly #routes = new Map<string, string>();
62
- #tcpListener: Promise<TcpListener> | undefined;
63
- #webListener: Promise<WebListener> | undefined;
64
-
65
- constructor(draw: Draw) {
66
- this.#draw = draw;
67
- }
68
-
69
- async close(): Promise<void> {
70
- await this.#tcp.close();
71
- await this.#web.close();
72
- await (await this.#tcpListener)?.close();
73
- await (await this.#webListener)?.close();
74
- }
75
-
76
- // Every ward this program stands, as a listener reaches them.
77
- readonly #doors = {
78
- arrive: async (pk: string, bytes: Uint8Array): Promise<Uint8Array | null> => {
79
- const ward = this.#wards.get(pk);
80
- return ward ? (await ward.door.arrive(bytes)).bytes : null;
81
- },
82
- };
83
-
84
- // Bytes to a ward at one address, over the carrier its scheme names.
85
- #dial(at: string, pk: string, bytes: Uint8Array): Promise<Dialed> {
86
- return tcpAddress(at) ? this.#tcp.dial(at, pk, bytes) : this.#web.dial(at, pk, bytes);
87
- }
88
-
89
- // The addresses this program listens at, for an invitation's `at`.
90
- async #listening(): Promise<string[]> {
91
- const tcp = await this.#tcpListener;
92
- const web = await this.#webListener;
93
- return [...(tcp ? [tcp.at] : []), ...(web ? web.at : [])];
94
- }
95
-
96
- async handle(line: string): Promise<Answer> {
97
- let request: unknown;
98
- try {
99
- request = JSON.parse(line);
100
- } catch {
101
- return { id: null, error: 'bad request' };
102
- }
103
- if (typeof request !== 'object' || request === null || Array.isArray(request)) return { id: null, error: 'bad request' };
104
- const r = request as Record<string, unknown>;
105
- if (typeof r.id !== 'string') return { id: null, error: 'bad request' };
106
- try {
107
- return { id: r.id, ...(await this.#op(r)) };
108
- } catch (e) {
109
- if (e instanceof Refused) return { id: r.id, error: e.error };
110
- throw e;
111
- }
112
- }
113
-
114
- async #op(r: Record<string, unknown>): Promise<Answer> {
115
- if (typeof r.op !== 'string') return refuse('bad request');
116
- if (!OPS.has(r.op)) return refuse('no such op');
117
- switch (r.op) {
118
- case 'ward':
119
- return this.#ward(text(r.seed), optional(r.reach));
120
- case 'invite':
121
- return this.#invite(r);
122
- case 'release':
123
- return this.#release(r);
124
- case 'arrive':
125
- return this.#arrive(r);
126
- case 'ask':
127
- return this.#ask(r);
128
- case 'read':
129
- return this.#read(r);
130
- case 'listen':
131
- return this.#listen(r.scheme);
132
- case 'route':
133
- return this.#route(r);
134
- default:
135
- return this.#send(r);
136
- }
137
- }
138
-
139
- // One listener of a scheme this program stands, answering for every ward
140
- // it stands.
141
- async #listen(scheme: unknown): Promise<Answer> {
142
- if (scheme === undefined || scheme === 'tcp') {
143
- this.#tcpListener ??= TcpListener.listen(this.#doors);
144
- return { at: (await this.#tcpListener).at };
145
- }
146
- if (scheme !== 'http' && scheme !== 'ws') return refuse('bad request');
147
- this.#webListener ??= webServe(this.#doors, { host: '127.0.0.1', port: 0, path: '/quo', origins: ['*'] });
148
- return { at: (await this.#webListener).at.find((at) => at.startsWith(`${scheme}:`))! };
149
- }
150
-
151
- #route(r: Record<string, unknown>): Answer {
152
- const far = r.far;
153
- if (!isHex(far, 64) || (tcpAddress(r.at) === null && webAddress(r.at) === null)) return refuse('bad request');
154
- this.#routes.set(far, r.at as string);
155
- return { routed: far };
156
- }
157
-
158
- async #send(r: Record<string, unknown>): Promise<Answer> {
159
- const invitation = inviteOf(r.invitation);
160
- const method = r.method === undefined ? undefined : text(r.method);
161
- const args = r.args === undefined ? undefined : objectText(r.args);
162
- const ward = this.#ward_(r.ward);
163
- const held = this.#standing(ward, invitation);
164
- const sent = await held.standing.ask(method, args);
165
- if (sent === null) return refuse('bad request');
166
- held.last = sent;
167
- const far = invitation.ward;
168
- const route = this.#routes.get(far);
169
- let reply: Uint8Array | null = null;
170
- for (const at of route === undefined ? (invitation.at ?? []) : [route]) {
171
- const out = await this.#dial(at, far, sent.bytes);
172
- if (out === UNDIALED) continue;
173
- reply = out;
174
- break;
175
- }
176
- return { read: readOf(await held.standing.read(sent, reply)) };
177
- }
178
-
179
- async #ward(seed: string, reach: string | undefined): Promise<Answer> {
180
- reached(reach);
181
- const key = await WardKey.from(seed);
182
- if (this.#wards.has(key.pk)) refuse('ward stood');
183
- this.#wards.set(key.pk, new StandWard(key, reach, this.#draw));
184
- return { ward: key.pk };
185
- }
186
-
187
- async #invite(r: Record<string, unknown>): Promise<Answer> {
188
- const name = text(r.heir);
189
- const reach = optional(r.reach) ?? 'echo';
190
- const ward = this.#ward_(r.ward);
191
- reached(reach);
192
- if (ward.names.has(name)) refuse('name held');
193
- const { heir, invitation } = await ward.door.invite(await this.#listening());
194
- ward.names.set(name, heir);
195
- ward.reaches.set(heir, reach);
196
- return { invitation };
197
- }
198
-
199
- #release(r: Record<string, unknown>): Answer {
200
- const name = text(r.heir);
201
- const ward = this.#ward_(r.ward);
202
- const heir = ward.names.get(name);
203
- if (heir === undefined) return { released: null };
204
- ward.names.delete(name);
205
- ward.door.remove(heir);
206
- return { released: name };
207
- }
208
-
209
- async #arrive(r: Record<string, unknown>): Promise<Answer> {
210
- const box = r.box;
211
- if (!isHex(box)) return refuse('bad request');
212
- const ward = this.#ward_(r.ward);
213
- const judged = await ward.door.arrive(unhex(box));
214
- return { reply: hex(judged.bytes) };
215
- }
216
-
217
- async #ask(r: Record<string, unknown>): Promise<Answer> {
218
- const invitation = inviteOf(r.invitation);
219
- const method = r.method === undefined ? undefined : text(r.method);
220
- const args = r.args === undefined ? undefined : objectText(r.args);
221
- const ward = this.#ward_(r.ward);
222
- const held = this.#standing(ward, invitation);
223
- const sent = await held.standing.ask(method, args);
224
- if (sent === null) return refuse('bad request');
225
- held.last = sent;
226
- return { box: hex(sent.bytes) };
227
- }
228
-
229
- async #read(r: Record<string, unknown>): Promise<Answer> {
230
- const invitation = inviteOf(r.invitation);
231
- if (r.reply !== null && !isHex(r.reply)) return refuse('bad request');
232
- const ward = this.#ward_(r.ward);
233
- const held = this.#standing(ward, invitation);
234
- if (held.last === undefined) return refuse('bad request');
235
- const read = await held.standing.read(held.last, r.reply === null ? null : unhex(r.reply as string));
236
- return { read: readOf(read) };
237
- }
238
-
239
- #standing(ward: StandWard, invitation: Invitation): { standing: Standing; last?: Sent } {
240
- const key = `${invitation.ward}.${invitation.heir}`;
241
- let held = ward.standings.get(key);
242
- if (!held) ward.standings.set(key, (held = { standing: new Standing(invitation, freshStanding(), this.#draw) }));
243
- return held;
244
- }
245
-
246
- #ward_(pk: unknown): StandWard {
247
- if (!isHex(pk, 64)) return refuse('bad request');
248
- return this.#wards.get(pk) ?? refuse('no such ward');
249
- }
250
- }
251
-
252
- const text = (value: unknown): string => (typeof value === 'string' ? value : refuse('bad request'));
253
- const optional = (value: unknown): string | undefined => (value === undefined ? undefined : text(value));
254
- const reached = (reach: string | undefined): void => {
255
- if (reach !== undefined && !Object.hasOwn(REACHES, reach)) refuse('not reached');
256
- };
257
- const inviteOf = (value: unknown): Invitation => readInvitation(value) ?? refuse('bad request');
258
- const objectText = (value: unknown): string => (typeof value === 'object' && value !== null && !Array.isArray(value) ? JSON.stringify(value) : refuse('bad request'));
259
- const readOf = (read: Read): unknown => ('object' in read ? { object: JSON.parse(read.object) as unknown, seen: read.seen } : read);
@@ -1,227 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // Quo over TCP: `nervur/tcp`, an entry that names a platform, Node's
3
- // sockets.
4
- //
5
- // TcpCarrier a Carrier that dials: bytes to a ward pk go in an ask
6
- // frame to the `tcp://host:port` addresses its book holds
7
- // for that pk, in their order
8
- // TcpListener answers ask frames for the doors it is handed, a harbor
9
- // among them, at a `tcp://` address
10
- // NodeSockets both, as the body a Node terrain hands the TCP carrier
11
- //
12
- // A connection is opened per address, carries many asks at once, and is
13
- // dropped when either side closes it; the next ask dials again. An ask
14
- // goes to the next address only when the one before could not be dialed,
15
- // since an ask a line took may have been heard.
16
- import { connect, createServer, type Server, type Socket } from 'node:net';
17
- import { hex, isHex, unhex } from '../crypto/index.ts';
18
- import { Carrier } from '../contract/index.ts';
19
- import { UNDIALED, type Arrivals, type Dialed, type Sockets } from '../harbor/index.ts';
20
- import { tcpAddress, tcpAt, type TcpAddress } from '../quo/index.ts';
21
- import { askFrame, FrameReader, nothingFrame, replyFrame } from '../line/index.ts';
22
-
23
- // What a listener hands an ask to: bytes for a ward pk, or nothing.
24
- export interface Doors {
25
- arrive(pk: string, bytes: Uint8Array): Promise<Uint8Array | null>;
26
- }
27
-
28
- const MAX_ID = 0xffff_ffff;
29
-
30
- // The `tcp` addresses among some, in their order.
31
- const tcpAddresses = (at: readonly string[]): TcpAddress[] => at.map(tcpAddress).filter((a): a is TcpAddress => a !== null);
32
-
33
- class Line {
34
- readonly #socket: Socket;
35
- readonly #reader = new FrameReader();
36
- readonly #waiting = new Map<number, (box: Uint8Array | null) => void>();
37
- #next = 1;
38
- closed = false;
39
-
40
- constructor(socket: Socket, onClose: () => void) {
41
- this.#socket = socket;
42
- socket.on('data', (chunk: Buffer) => {
43
- for (const f of this.#reader.push(new Uint8Array(chunk))) {
44
- if (f.kind === 'ask') continue;
45
- const resolve = this.#waiting.get(f.id);
46
- this.#waiting.delete(f.id);
47
- resolve?.(f.kind === 'reply' ? f.box : null);
48
- }
49
- if (this.#reader.bad) socket.destroy();
50
- });
51
- socket.on('error', () => socket.destroy());
52
- socket.on('close', () => {
53
- this.closed = true;
54
- onClose();
55
- for (const resolve of this.#waiting.values()) resolve(null);
56
- this.#waiting.clear();
57
- });
58
- }
59
-
60
- static open(host: string, port: number, onClose: () => void): Promise<Line | null> {
61
- return new Promise((resolve) => {
62
- const socket = connect({ host, port });
63
- socket.once('connect', () => resolve(new Line(socket, onClose)));
64
- socket.once('error', () => {
65
- socket.destroy();
66
- resolve(null);
67
- });
68
- });
69
- }
70
-
71
- ask(pk: Uint8Array, box: Uint8Array): Promise<Uint8Array | null> {
72
- if (this.closed) return Promise.resolve(null);
73
- const id = this.#next;
74
- this.#next = this.#next === MAX_ID ? 1 : this.#next + 1;
75
- return new Promise((resolve) => {
76
- this.#waiting.set(id, resolve);
77
- this.#socket.write(askFrame(id, pk, box));
78
- });
79
- }
80
-
81
- close(): void {
82
- this.#socket.destroy();
83
- }
84
- }
85
-
86
- export class TcpCarrier extends Carrier {
87
- readonly #routes = new Map<string, TcpAddress[]>();
88
- readonly #lines = new Map<string, Promise<Line | null>>();
89
-
90
- // Where a ward pk is reached, replacing any route before: its `tcp`
91
- // addresses in order, the others skipped. A list with none is not
92
- // taken; null forgets the route.
93
- route(pk: string, at: readonly string[] | null): boolean {
94
- if (!isHex(pk, 64)) return false;
95
- if (at === null) return this.#routes.delete(pk) || true;
96
- const mine = tcpAddresses(at);
97
- if (mine.length === 0) return false;
98
- this.#routes.set(pk, mine);
99
- return true;
100
- }
101
-
102
- // Whether a ward pk has a route.
103
- routed(pk: string): boolean {
104
- return this.#routes.has(pk);
105
- }
106
-
107
- // Nothing where the pk has no route, no address is reached, or the
108
- // connection closed before an answer.
109
- carry(pk: string, bytes: Uint8Array): Promise<Uint8Array | null> {
110
- return this.#carry(this.#routes.get(pk) ?? [], pk, bytes);
111
- }
112
-
113
- // Bytes to a ward pk at the addresses named, as a route would carry them.
114
- carryAt(at: readonly string[], pk: string, bytes: Uint8Array): Promise<Uint8Array | null> {
115
- return isHex(pk, 64) ? this.#carry(tcpAddresses(at), pk, bytes) : Promise.resolve(null);
116
- }
117
-
118
- async #carry(addresses: readonly TcpAddress[], pk: string, bytes: Uint8Array): Promise<Uint8Array | null> {
119
- for (const address of addresses) {
120
- const out = await this.#dial(address, pk, bytes);
121
- if (out !== UNDIALED) return out;
122
- }
123
- return null;
124
- }
125
-
126
- // Bytes to a ward pk at one address, or `UNDIALED` where no line opens.
127
- dial(at: string, pk: string, bytes: Uint8Array): Promise<Dialed> {
128
- const address = tcpAddress(at);
129
- return address && isHex(pk, 64) ? this.#dial(address, pk, bytes) : Promise.resolve(UNDIALED);
130
- }
131
-
132
- async #dial(address: TcpAddress, pk: string, bytes: Uint8Array): Promise<Dialed> {
133
- const line = await this.#line(address);
134
- return line ? line.ask(unhex(pk), bytes) : UNDIALED;
135
- }
136
-
137
- #line({ host, port }: TcpAddress): Promise<Line | null> {
138
- const at = tcpAt(host, port);
139
- let line = this.#lines.get(at);
140
- if (!line) {
141
- line = Line.open(host, port, () => this.#lines.delete(at)).then((dialed) => {
142
- if (!dialed) this.#lines.delete(at);
143
- return dialed;
144
- });
145
- this.#lines.set(at, line);
146
- }
147
- return line;
148
- }
149
-
150
- async close(): Promise<void> {
151
- const lines = [...this.#lines.values()];
152
- this.#lines.clear();
153
- for (const line of await Promise.all(lines)) line?.close();
154
- }
155
- }
156
-
157
- export class TcpListener {
158
- readonly #server: Server;
159
- readonly #doors: Doors;
160
- readonly #sockets = new Set<Socket>();
161
-
162
- private constructor(doors: Doors) {
163
- this.#doors = doors;
164
- this.#server = createServer((socket) => this.#serve(socket));
165
- }
166
-
167
- // A listener on `host` and `port`, 127.0.0.1 and a free port unless named.
168
- static listen(doors: Doors, host = '127.0.0.1', port = 0): Promise<TcpListener> {
169
- const listener = new TcpListener(doors);
170
- return new Promise((resolve, reject) => {
171
- listener.#server.once('error', reject);
172
- listener.#server.listen(port, host, () => resolve(listener));
173
- });
174
- }
175
-
176
- // The `tcp://host:port` address this listener answers at.
177
- get at(): string {
178
- const address = this.#server.address() as { address: string; port: number };
179
- return tcpAt(address.address, address.port);
180
- }
181
-
182
- #serve(socket: Socket): void {
183
- this.#sockets.add(socket);
184
- const reader = new FrameReader();
185
- socket.on('data', (chunk: Buffer) => {
186
- for (const f of reader.push(new Uint8Array(chunk))) {
187
- if (f.kind === 'ask') void this.#answer(socket, f.id, f.pk, f.box);
188
- }
189
- if (reader.bad) socket.destroy();
190
- });
191
- socket.on('error', () => socket.destroy());
192
- socket.on('close', () => this.#sockets.delete(socket));
193
- }
194
-
195
- async #answer(socket: Socket, id: number, pk: Uint8Array, box: Uint8Array): Promise<void> {
196
- let reply: Uint8Array | null = null;
197
- try {
198
- reply = await this.#doors.arrive(hex(pk), box);
199
- } catch {
200
- reply = null;
201
- }
202
- if (!socket.destroyed) socket.write(reply === null ? nothingFrame(id) : replyFrame(id, reply));
203
- }
204
-
205
- close(): Promise<void> {
206
- for (const socket of this.#sockets) socket.destroy();
207
- return new Promise((resolve) => this.#server.close(() => resolve()));
208
- }
209
- }
210
-
211
- // Node's sockets under the TCP carrier: a dialer of its own for each
212
- // carrier that asks, and a listener wherever it is told.
213
- export class NodeSockets implements Sockets {
214
- readonly #dialer = new TcpCarrier();
215
-
216
- dial(address: string, pk: string, bytes: Uint8Array): Promise<Dialed> {
217
- return this.#dialer.dial(address, pk, bytes);
218
- }
219
-
220
- listen(ground: Arrivals, host: string, port: number): Promise<TcpListener> {
221
- return TcpListener.listen(ground, host, port);
222
- }
223
-
224
- close(): Promise<void> {
225
- return this.#dialer.close();
226
- }
227
- }
@@ -1,47 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The allowance: how long a being waits on one ask. It is on no wire. A
3
- // being who says nothing gets the default, and nobody gets past the
4
- // ceiling.
5
- import type { Clock } from '../contract/index.ts';
6
- import type { Wanted } from '../being/index.ts';
7
-
8
- export const DEFAULT = 30_000;
9
- export const CEILING = 300_000;
10
-
11
- export const allow = (wanted: Wanted | undefined): number => {
12
- const whole = typeof wanted?.time === 'number' ? Math.floor(wanted.time) : NaN;
13
- return Math.min(Number.isFinite(whole) && whole > 0 ? whole : DEFAULT, CEILING);
14
- };
15
-
16
- export const LATE: unique symbol = Symbol('late');
17
- export type Late = typeof LATE;
18
-
19
- // One ask's bell. It bounds the wait of the being who asked and the send
20
- // that holds the relation's line, so a far side that never answers holds
21
- // neither for longer than the allowance.
22
- export class Bell {
23
- readonly #wait: { done: Promise<void>; cancel(): void };
24
- readonly #late: Promise<Late>;
25
- #rang = false;
26
-
27
- constructor(clock: Clock, ms: number) {
28
- this.#wait = clock.wait(ms);
29
- this.#late = this.#wait.done.then((): Late => {
30
- this.#rang = true;
31
- return LATE;
32
- });
33
- }
34
-
35
- get rang(): boolean {
36
- return this.#rang;
37
- }
38
-
39
- race<T>(work: Promise<T>): Promise<T | Late> {
40
- work.catch(() => {});
41
- return Promise.race([work, this.#late]);
42
- }
43
-
44
- cancel(): void {
45
- this.#wait.cancel();
46
- }
47
- }