nervur 0.21.4 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/README.md +215 -158
  2. package/dist/browser.js +4 -3
  3. package/dist/cli.js +16 -0
  4. package/dist/{being → core/being}/being.js +10 -0
  5. package/dist/{being → core/being}/faculty.js +5 -2
  6. package/dist/{being → core/being}/index.d.ts +1 -1
  7. package/dist/{being → core/being}/index.js +1 -1
  8. package/dist/{being → core/being}/kind.d.ts +2 -0
  9. package/dist/{being → core/being}/kind.js +6 -0
  10. package/dist/{being → core/being}/types.js +1 -1
  11. package/dist/{browser → core/browser}/index.d.ts +6 -5
  12. package/dist/{browser → core/browser}/index.js +10 -10
  13. package/dist/{browser → core/browser}/worker.d.ts +2 -3
  14. package/dist/{browser → core/browser}/worker.js +8 -11
  15. package/dist/{cli → core/cli}/command.d.ts +4 -4
  16. package/dist/{cli → core/cli}/command.js +116 -66
  17. package/dist/{cli → core/cli}/edge.js +4 -2
  18. package/dist/core/cli/harbor.d.ts +25 -0
  19. package/dist/{cli → core/cli}/harbor.js +45 -33
  20. package/dist/{contract → core/contract}/index.d.ts +6 -5
  21. package/dist/{contract → core/contract}/index.js +8 -9
  22. package/dist/core/contract/link.d.ts +4 -0
  23. package/dist/core/contract/link.js +45 -0
  24. package/dist/{crypto → core/crypto}/ed25519.js +22 -14
  25. package/dist/core/crypto/subtle.d.ts +3 -0
  26. package/dist/core/crypto/subtle.js +11 -0
  27. package/dist/core/crypto/x25519.js +24 -0
  28. package/dist/core/edge/index.d.ts +164 -0
  29. package/dist/core/edge/index.js +739 -0
  30. package/dist/core/edge/kit.d.ts +2 -0
  31. package/dist/core/edge/kit.js +2 -0
  32. package/dist/core/git/http.d.ts +6 -0
  33. package/dist/core/git/http.js +102 -0
  34. package/dist/core/git/index.d.ts +5 -0
  35. package/dist/core/git/index.js +10 -0
  36. package/dist/core/git/object.d.ts +40 -0
  37. package/dist/core/git/object.js +103 -0
  38. package/dist/core/git/pack.d.ts +9 -0
  39. package/dist/core/git/pack.js +171 -0
  40. package/dist/core/git/store.d.ts +23 -0
  41. package/dist/core/git/store.js +120 -0
  42. package/dist/core/git/zlib.d.ts +6 -0
  43. package/dist/core/git/zlib.js +210 -0
  44. package/dist/core/harbor/carrying.d.ts +51 -0
  45. package/dist/core/harbor/carrying.js +69 -0
  46. package/dist/core/harbor/catalogue.d.ts +33 -0
  47. package/dist/core/harbor/catalogue.js +365 -0
  48. package/dist/core/harbor/dna.d.ts +13 -0
  49. package/dist/core/harbor/dna.js +120 -0
  50. package/dist/{harbor → core/harbor}/dock.d.ts +22 -13
  51. package/dist/core/harbor/dock.js +381 -0
  52. package/dist/core/harbor/harbor.d.ts +16 -0
  53. package/dist/core/harbor/harbor.js +438 -0
  54. package/dist/core/harbor/index.d.ts +11 -0
  55. package/dist/{harbor → core/harbor}/index.js +5 -5
  56. package/dist/core/harbor/memory.d.ts +10 -0
  57. package/dist/core/harbor/memory.js +25 -0
  58. package/dist/{harbor → core/harbor}/package.d.ts +0 -1
  59. package/dist/{harbor → core/harbor}/package.js +9 -9
  60. package/dist/core/harbor/probe.d.ts +22 -0
  61. package/dist/core/harbor/probe.js +15 -0
  62. package/dist/core/harbor/registry.d.ts +15 -0
  63. package/dist/{harbor → core/harbor}/registry.js +26 -10
  64. package/dist/core/harbor/root-line.d.ts +7 -0
  65. package/dist/core/harbor/root-line.js +9 -0
  66. package/dist/{harbor → core/harbor}/terrain.d.ts +9 -6
  67. package/dist/{harbor → core/harbor}/terrain.js +5 -4
  68. package/dist/core/http/index.d.ts +2 -0
  69. package/dist/{http → core/http}/index.js +3 -5
  70. package/dist/{line → core/line}/answer.d.ts +2 -2
  71. package/dist/{line → core/line}/frame.d.ts +0 -5
  72. package/dist/{line → core/line}/frame.js +0 -4
  73. package/dist/core/line/ground.js +7 -0
  74. package/dist/core/line/index.d.ts +4 -0
  75. package/dist/core/line/index.js +8 -0
  76. package/dist/core/line/web.d.ts +12 -0
  77. package/dist/{line → core/line}/web.js +13 -77
  78. package/dist/{node → core/node}/index.d.ts +2 -15
  79. package/dist/core/node/index.js +124 -0
  80. package/dist/{pointer → core/pointer}/bodies.d.ts +7 -5
  81. package/dist/core/pointer/bodies.js +118 -0
  82. package/dist/core/pointer/index.d.ts +2 -0
  83. package/dist/{pointer → core/pointer}/index.js +1 -1
  84. package/dist/{pointer → core/pointer}/world.d.ts +13 -9
  85. package/dist/{pointer → core/pointer}/world.js +31 -16
  86. package/dist/{proof → core/proof}/contracts.d.ts +6 -2
  87. package/dist/{proof → core/proof}/contracts.js +11 -10
  88. package/dist/core/proof/index.d.ts +3 -0
  89. package/dist/{proof → core/proof}/index.js +2 -2
  90. package/dist/core/proof/law.d.ts +25 -0
  91. package/dist/core/proof/law.js +715 -0
  92. package/dist/{tcp → core/tcp}/index.d.ts +7 -4
  93. package/dist/{tcp → core/tcp}/index.js +11 -62
  94. package/dist/{ward/ward.d.ts → core/ward/house.d.ts} +24 -10
  95. package/dist/core/ward/house.js +473 -0
  96. package/dist/core/ward/index.d.ts +5 -0
  97. package/dist/core/ward/index.js +7 -0
  98. package/dist/{ward → core/ward}/partition.d.ts +1 -0
  99. package/dist/{ward → core/ward}/stance.d.ts +2 -2
  100. package/dist/{ward → core/ward}/stance.js +5 -2
  101. package/dist/{ward/ward-being.d.ts → core/ward/ward.d.ts} +14 -6
  102. package/dist/core/ward/ward.js +140 -0
  103. package/dist/core.d.ts +10 -0
  104. package/dist/core.js +19 -0
  105. package/dist/defaults/dock/index.d.ts +4 -0
  106. package/dist/defaults/dock/index.js +11 -0
  107. package/dist/defaults/index.d.ts +5 -0
  108. package/dist/defaults/index.js +10 -0
  109. package/dist/defaults/memory/index.d.ts +12 -0
  110. package/dist/defaults/memory/index.js +39 -0
  111. package/dist/defaults/tcp/index.d.ts +16 -0
  112. package/dist/defaults/tcp/index.js +82 -0
  113. package/dist/defaults/ward/index.d.ts +4 -0
  114. package/dist/defaults/ward/index.js +11 -0
  115. package/dist/defaults/web/index.d.ts +15 -0
  116. package/dist/defaults/web/index.js +90 -0
  117. package/dist/edge.js +4 -3
  118. package/dist/index.d.ts +1 -8
  119. package/dist/index.js +13 -18
  120. package/dist/kit.d.ts +4 -0
  121. package/dist/kit.js +12 -0
  122. package/dist/node.js +4 -3
  123. package/package.json +23 -18
  124. package/src/browser.ts +4 -3
  125. package/src/cli.ts +15 -0
  126. package/src/{being → core/being}/being.ts +4 -0
  127. package/src/{being → core/being}/faculty.ts +5 -2
  128. package/src/{being → core/being}/index.ts +1 -1
  129. package/src/{being → core/being}/kind.ts +7 -0
  130. package/src/{being → core/being}/types.ts +1 -1
  131. package/src/{browser → core/browser}/index.ts +16 -13
  132. package/src/{browser → core/browser}/worker.ts +11 -14
  133. package/src/{cli → core/cli}/command.ts +113 -66
  134. package/src/{cli → core/cli}/edge.ts +4 -2
  135. package/src/{cli → core/cli}/harbor.ts +48 -31
  136. package/src/{contract → core/contract}/index.ts +14 -14
  137. package/src/core/contract/link.ts +58 -0
  138. package/src/{crypto → core/crypto}/ed25519.ts +22 -14
  139. package/src/core/crypto/subtle.ts +13 -0
  140. package/src/core/crypto/x25519.ts +23 -0
  141. package/src/core/edge/index.ts +865 -0
  142. package/src/core/git/http.ts +103 -0
  143. package/src/core/git/index.ts +10 -0
  144. package/src/core/git/object.ts +116 -0
  145. package/src/core/git/pack.ts +147 -0
  146. package/src/core/git/store.ts +122 -0
  147. package/src/core/git/zlib.ts +197 -0
  148. package/src/core/harbor/carrying.ts +124 -0
  149. package/src/core/harbor/catalogue.ts +386 -0
  150. package/src/core/harbor/dna.ts +116 -0
  151. package/src/core/harbor/dock.ts +420 -0
  152. package/src/core/harbor/harbor.ts +433 -0
  153. package/src/core/harbor/index.ts +14 -0
  154. package/src/core/harbor/memory.ts +31 -0
  155. package/src/{harbor → core/harbor}/package.ts +15 -15
  156. package/src/core/harbor/probe.ts +39 -0
  157. package/src/{harbor → core/harbor}/registry.ts +28 -11
  158. package/src/core/harbor/root-line.ts +19 -0
  159. package/src/{harbor → core/harbor}/terrain.ts +14 -7
  160. package/src/{http → core/http}/index.ts +5 -8
  161. package/src/{line → core/line}/answer.ts +3 -2
  162. package/src/{line → core/line}/frame.ts +0 -5
  163. package/src/core/line/ground.ts +18 -0
  164. package/src/core/line/index.ts +8 -0
  165. package/src/{line → core/line}/web.ts +1 -105
  166. package/src/core/node/index.ts +139 -0
  167. package/src/core/pointer/bodies.ts +122 -0
  168. package/src/core/pointer/index.ts +4 -0
  169. package/src/{pointer → core/pointer}/world.ts +45 -21
  170. package/src/{proof → core/proof}/contracts.ts +18 -13
  171. package/src/{proof → core/proof}/index.ts +3 -3
  172. package/src/core/proof/law.ts +742 -0
  173. package/src/{stand → core/stand}/stand.ts +2 -2
  174. package/src/{tcp → core/tcp}/index.ts +17 -74
  175. package/src/core/ward/house.ts +524 -0
  176. package/src/core/ward/index.ts +7 -0
  177. package/src/{ward → core/ward}/partition.ts +5 -1
  178. package/src/{ward → core/ward}/stance.ts +5 -2
  179. package/src/core/ward/ward.ts +166 -0
  180. package/src/core.ts +55 -0
  181. package/src/defaults/dock/index.ts +12 -0
  182. package/src/defaults/index.ts +10 -0
  183. package/src/defaults/memory/index.ts +50 -0
  184. package/src/defaults/tcp/index.ts +89 -0
  185. package/src/defaults/ward/index.ts +12 -0
  186. package/src/defaults/web/index.ts +97 -0
  187. package/src/edge.ts +4 -3
  188. package/src/index.ts +13 -50
  189. package/src/kit.ts +15 -0
  190. package/src/node.ts +4 -3
  191. package/dist/cli/harbor.d.ts +0 -16
  192. package/dist/cli/main.js +0 -14
  193. package/dist/crypto/subtle.d.ts +0 -10
  194. package/dist/crypto/subtle.js +0 -29
  195. package/dist/crypto/x25519.js +0 -21
  196. package/dist/edge/index.d.ts +0 -81
  197. package/dist/edge/index.js +0 -400
  198. package/dist/harbor/carrying.d.ts +0 -17
  199. package/dist/harbor/carrying.js +0 -38
  200. package/dist/harbor/catalogue.d.ts +0 -28
  201. package/dist/harbor/catalogue.js +0 -109
  202. package/dist/harbor/dock.js +0 -236
  203. package/dist/harbor/harbor.d.ts +0 -45
  204. package/dist/harbor/harbor.js +0 -347
  205. package/dist/harbor/index.d.ts +0 -11
  206. package/dist/harbor/probe.d.ts +0 -18
  207. package/dist/harbor/probe.js +0 -15
  208. package/dist/harbor/registry.d.ts +0 -10
  209. package/dist/harbor/relay.d.ts +0 -15
  210. package/dist/harbor/relay.js +0 -406
  211. package/dist/harbor/root-line.d.ts +0 -9
  212. package/dist/harbor/root-line.js +0 -38
  213. package/dist/harbor/store.d.ts +0 -13
  214. package/dist/harbor/store.js +0 -53
  215. package/dist/http/index.d.ts +0 -4
  216. package/dist/line/ground.js +0 -9
  217. package/dist/line/index.d.ts +0 -5
  218. package/dist/line/index.js +0 -9
  219. package/dist/line/push.d.ts +0 -7
  220. package/dist/line/push.js +0 -92
  221. package/dist/line/web.d.ts +0 -30
  222. package/dist/node/index.js +0 -236
  223. package/dist/pointer/bodies.js +0 -80
  224. package/dist/pointer/index.d.ts +0 -2
  225. package/dist/proof/index.d.ts +0 -3
  226. package/dist/proof/law.d.ts +0 -90
  227. package/dist/proof/law.js +0 -591
  228. package/dist/tcp/frame.d.ts +0 -25
  229. package/dist/tcp/frame.js +0 -64
  230. package/dist/ward/index.d.ts +0 -6
  231. package/dist/ward/index.js +0 -8
  232. package/dist/ward/pilot.d.ts +0 -8
  233. package/dist/ward/pilot.js +0 -50
  234. package/dist/ward/ward-being.js +0 -82
  235. package/dist/ward/ward.js +0 -290
  236. package/src/cli/main.ts +0 -13
  237. package/src/crypto/subtle.ts +0 -33
  238. package/src/crypto/x25519.ts +0 -21
  239. package/src/edge/index.ts +0 -490
  240. package/src/harbor/carrying.ts +0 -56
  241. package/src/harbor/catalogue.ts +0 -134
  242. package/src/harbor/dock.ts +0 -265
  243. package/src/harbor/harbor.ts +0 -381
  244. package/src/harbor/index.ts +0 -14
  245. package/src/harbor/probe.ts +0 -34
  246. package/src/harbor/relay.ts +0 -418
  247. package/src/harbor/root-line.ts +0 -39
  248. package/src/harbor/store.ts +0 -69
  249. package/src/line/ground.ts +0 -19
  250. package/src/line/index.ts +0 -9
  251. package/src/line/push.ts +0 -112
  252. package/src/node/index.ts +0 -241
  253. package/src/pointer/bodies.ts +0 -82
  254. package/src/pointer/index.ts +0 -4
  255. package/src/proof/law.ts +0 -591
  256. package/src/ward/index.ts +0 -8
  257. package/src/ward/pilot.ts +0 -50
  258. package/src/ward/ward-being.ts +0 -103
  259. package/src/ward/ward.ts +0 -328
  260. /package/dist/{cli/main.d.ts → cli.d.ts} +0 -0
  261. /package/dist/{being → core/being}/being.d.ts +0 -0
  262. /package/dist/{being → core/being}/digest.d.ts +0 -0
  263. /package/dist/{being → core/being}/digest.js +0 -0
  264. /package/dist/{being → core/being}/faculty.d.ts +0 -0
  265. /package/dist/{being → core/being}/types.d.ts +0 -0
  266. /package/dist/{being → core/being}/words.d.ts +0 -0
  267. /package/dist/{being → core/being}/words.js +0 -0
  268. /package/dist/{cli → core/cli}/edge.d.ts +0 -0
  269. /package/dist/{crypto → core/crypto}/aes.d.ts +0 -0
  270. /package/dist/{crypto → core/crypto}/aes.js +0 -0
  271. /package/dist/{crypto → core/crypto}/bytes.d.ts +0 -0
  272. /package/dist/{crypto → core/crypto}/bytes.js +0 -0
  273. /package/dist/{crypto → core/crypto}/ed25519.d.ts +0 -0
  274. /package/dist/{crypto → core/crypto}/hash.d.ts +0 -0
  275. /package/dist/{crypto → core/crypto}/hash.js +0 -0
  276. /package/dist/{crypto → core/crypto}/index.d.ts +0 -0
  277. /package/dist/{crypto → core/crypto}/index.js +0 -0
  278. /package/dist/{crypto → core/crypto}/json.d.ts +0 -0
  279. /package/dist/{crypto → core/crypto}/json.js +0 -0
  280. /package/dist/{crypto → core/crypto}/mlkem.d.ts +0 -0
  281. /package/dist/{crypto → core/crypto}/mlkem.js +0 -0
  282. /package/dist/{crypto → core/crypto}/x25519.d.ts +0 -0
  283. /package/dist/{folder → core/folder}/index.d.ts +0 -0
  284. /package/dist/{folder → core/folder}/index.js +0 -0
  285. /package/dist/{http → core/http}/websocket.d.ts +0 -0
  286. /package/dist/{http → core/http}/websocket.js +0 -0
  287. /package/dist/{line → core/line}/answer.js +0 -0
  288. /package/dist/{line → core/line}/ground.d.ts +0 -0
  289. /package/dist/{proof → core/proof}/expect.d.ts +0 -0
  290. /package/dist/{proof → core/proof}/expect.js +0 -0
  291. /package/dist/{quo → core/quo}/address.d.ts +0 -0
  292. /package/dist/{quo → core/quo}/address.js +0 -0
  293. /package/dist/{quo → core/quo}/door.d.ts +0 -0
  294. /package/dist/{quo → core/quo}/door.js +0 -0
  295. /package/dist/{quo → core/quo}/index.d.ts +0 -0
  296. /package/dist/{quo → core/quo}/index.js +0 -0
  297. /package/dist/{quo → core/quo}/invitation.d.ts +0 -0
  298. /package/dist/{quo → core/quo}/invitation.js +0 -0
  299. /package/dist/{quo → core/quo}/keys.d.ts +0 -0
  300. /package/dist/{quo → core/quo}/keys.js +0 -0
  301. /package/dist/{quo → core/quo}/payload.d.ts +0 -0
  302. /package/dist/{quo → core/quo}/payload.js +0 -0
  303. /package/dist/{quo → core/quo}/relations.d.ts +0 -0
  304. /package/dist/{quo → core/quo}/relations.js +0 -0
  305. /package/dist/{quo → core/quo}/reply.d.ts +0 -0
  306. /package/dist/{quo → core/quo}/reply.js +0 -0
  307. /package/dist/{quo → core/quo}/seal.d.ts +0 -0
  308. /package/dist/{quo → core/quo}/seal.js +0 -0
  309. /package/dist/{quo → core/quo}/standing.d.ts +0 -0
  310. /package/dist/{quo → core/quo}/standing.js +0 -0
  311. /package/dist/{ward → core/ward}/allowance.d.ts +0 -0
  312. /package/dist/{ward → core/ward}/allowance.js +0 -0
  313. /package/dist/{ward → core/ward}/cells.d.ts +0 -0
  314. /package/dist/{ward → core/ward}/cells.js +0 -0
  315. /package/dist/{ward → core/ward}/partition.js +0 -0
  316. /package/src/{being → core/being}/digest.ts +0 -0
  317. /package/src/{being → core/being}/words.ts +0 -0
  318. /package/src/{crypto → core/crypto}/aes.ts +0 -0
  319. /package/src/{crypto → core/crypto}/bytes.ts +0 -0
  320. /package/src/{crypto → core/crypto}/hash.ts +0 -0
  321. /package/src/{crypto → core/crypto}/index.ts +0 -0
  322. /package/src/{crypto → core/crypto}/json.ts +0 -0
  323. /package/src/{crypto → core/crypto}/mlkem.ts +0 -0
  324. /package/src/{folder → core/folder}/index.ts +0 -0
  325. /package/src/{http → core/http}/websocket.ts +0 -0
  326. /package/src/{proof → core/proof}/expect.ts +0 -0
  327. /package/src/{quo → core/quo}/address.ts +0 -0
  328. /package/src/{quo → core/quo}/door.ts +0 -0
  329. /package/src/{quo → core/quo}/index.ts +0 -0
  330. /package/src/{quo → core/quo}/invitation.ts +0 -0
  331. /package/src/{quo → core/quo}/keys.ts +0 -0
  332. /package/src/{quo → core/quo}/payload.ts +0 -0
  333. /package/src/{quo → core/quo}/relations.ts +0 -0
  334. /package/src/{quo → core/quo}/reply.ts +0 -0
  335. /package/src/{quo → core/quo}/seal.ts +0 -0
  336. /package/src/{quo → core/quo}/standing.ts +0 -0
  337. /package/src/{stand → core/stand}/main.ts +0 -0
  338. /package/src/{ward → core/ward}/allowance.ts +0 -0
  339. /package/src/{ward → core/ward}/cells.ts +0 -0
@@ -0,0 +1,51 @@
1
+ import { Faculty, type BeingClass, type Stance } from '../being/index.ts';
2
+ import type { RootRequest } from './root-line.ts';
3
+ import type { Terrain } from './terrain.ts';
4
+ export interface Arrivals {
5
+ arrive(pk: string, bytes: Uint8Array): Promise<Uint8Array | null>;
6
+ }
7
+ export interface Ground extends Arrivals {
8
+ readonly terrain: Terrain;
9
+ }
10
+ export declare const groundOf: (stance: Stance) => Ground;
11
+ export declare const UNDIALED: unique symbol;
12
+ export type Dialed = Uint8Array | null | typeof UNDIALED;
13
+ export type WebListen = {
14
+ host: string;
15
+ port: number;
16
+ path: string;
17
+ origins: string[];
18
+ };
19
+ export interface WebListener {
20
+ readonly at: string[];
21
+ readonly port: number;
22
+ close(): Promise<void>;
23
+ }
24
+ export type WebServe = (ground: Arrivals, listen: WebListen) => Promise<WebListener>;
25
+ export interface TcpListening {
26
+ readonly at: string;
27
+ close(): Promise<void>;
28
+ }
29
+ export interface Sockets {
30
+ dial(address: string, pk: string, bytes: Uint8Array): Promise<Dialed>;
31
+ listen(ground: Arrivals, host: string, port: number): Promise<TcpListening>;
32
+ close(): Promise<void>;
33
+ }
34
+ export declare abstract class CarrierFaculty extends Faculty {
35
+ static readonly kind: string;
36
+ static readonly carries: string | null;
37
+ abstract accepts(address: string): boolean;
38
+ abstract dial(address: string, pk: string, bytes: Uint8Array): Promise<Dialed>;
39
+ listening(): string[];
40
+ relays(_pk: string): boolean;
41
+ relay(_pk: string, _bytes: Uint8Array): Promise<Uint8Array | null>;
42
+ heard(): Promise<void>;
43
+ awake(): Promise<void>;
44
+ sleep(): Promise<void>;
45
+ }
46
+ export declare const carrierIn: (beings: Readonly<Record<string, {
47
+ class: string;
48
+ }>>, classes: readonly BeingClass[], carries: string) => {
49
+ key: string;
50
+ open?: RootRequest;
51
+ } | null;
@@ -0,0 +1,69 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // A carrier is a faculty. It speaks the schemes of the addresses it dials,
3
+ // dials an address when the harbor carries a box there, and listens where
4
+ // its cells say, handing each box it hears to the harbor. The harbor keeps
5
+ // every route and walks it in order, handing each address to the carrier
6
+ // that speaks its scheme.
7
+ //
8
+ // A ground ships its carriers as plain classes the terrain names, each of
9
+ // a fixed kind and born of its stance alone. They are the kit's, of kinds
10
+ // under `org.nervur.`, and the dock opens them as faculties on the root's
11
+ // `open` alone.
12
+ import { Faculty, ships } from '../being/index.js';
13
+ // The ground a faculty of the box ward is born on.
14
+ export const groundOf = (stance) => {
15
+ const { ground } = stance;
16
+ if (!ground)
17
+ throw new Error('a kit faculty stands in the box ward alone');
18
+ return ground;
19
+ };
20
+ // Beside a reply and nothing, a dial answers `undialed` when the address
21
+ // could not be reached at all, so the harbor tries the next one. A box a
22
+ // line took may have been heard, so after it no other address is tried.
23
+ export const UNDIALED = Symbol('undialed');
24
+ export class CarrierFaculty extends Faculty {
25
+ static kind = 'org.nervur.carrier';
26
+ static {
27
+ ships(this);
28
+ }
29
+ // The carrier of Quo it speaks, as `quo/` names one by the scheme of its
30
+ // addresses, `tcp` or `web`, or null for one it does not name. A front
31
+ // finds the carrier it opens by this, and opens it under this name.
32
+ static carries = null;
33
+ // The addresses it listens at now.
34
+ listening() {
35
+ return [];
36
+ }
37
+ // Whether it answers for a ward pk no door of this harbor holds, as a
38
+ // relay does, and its answer.
39
+ relays(_pk) {
40
+ return false;
41
+ }
42
+ relay(_pk, _bytes) {
43
+ return Promise.resolve(null);
44
+ }
45
+ // A push the device heard, which says only that something waits: a
46
+ // carrier that collects for this harbor collects now.
47
+ heard() {
48
+ return Promise.resolve();
49
+ }
50
+ // Once the harbor has unpacked, and once before it closes.
51
+ awake() {
52
+ return Promise.resolve();
53
+ }
54
+ sleep() {
55
+ return Promise.resolve();
56
+ }
57
+ }
58
+ // What a front asks so a carrier of Quo stands in the box ward, read from
59
+ // the box's census and the classes its entry hands it: the key of a being
60
+ // whose class carries it, or else an `open` under the carrier's name of the
61
+ // class that carries it, or null where no class does.
62
+ export const carrierIn = (beings, classes, carries) => {
63
+ const carrying = classes.filter((C) => C.prototype instanceof CarrierFaculty && C.carries === carries);
64
+ const standing = Object.entries(beings).find(([, being]) => carrying.some((C) => C.kind === being.class));
65
+ if (standing)
66
+ return { key: standing[0] };
67
+ const C = carrying[0];
68
+ return C ? { key: carries, open: { method: 'open', args: { key: carries, class: C.kind } } } : null;
69
+ };
@@ -0,0 +1,33 @@
1
+ import { type AskSpec, type BeingClass, type JsonObject, type Stance } from '../being/index.ts';
2
+ import { type ObjectStore } from '../git/index.ts';
3
+ import { RegistryFaculty } from './registry.ts';
4
+ import type { Terrain } from './terrain.ts';
5
+ export declare const CATALOGUE = "catalogue";
6
+ export interface Cataloguing {
7
+ readonly terrain: Terrain;
8
+ readonly dna: ObjectStore & {
9
+ collect(keep: Iterable<string>): Promise<number>;
10
+ };
11
+ readonly kit: readonly BeingClass[];
12
+ ran(): Promise<void>;
13
+ stands(kinds: readonly string[]): boolean;
14
+ kept(): string[];
15
+ keep(): Promise<boolean>;
16
+ }
17
+ export declare class Catalogue extends RegistryFaculty {
18
+ #private;
19
+ static readonly kind: string;
20
+ static asks: Record<string, AskSpec>;
21
+ constructor(stance: Stance);
22
+ classOf(kind: string): BeingClass | undefined;
23
+ wake(): Promise<void>;
24
+ add(args: JsonObject): Promise<JsonObject>;
25
+ remove(args: JsonObject): Promise<JsonObject>;
26
+ live(args: JsonObject): Promise<JsonObject>;
27
+ log(args: JsonObject): Promise<JsonObject>;
28
+ origin(args: JsonObject): JsonObject;
29
+ fetch(): Promise<JsonObject>;
30
+ gc(): Promise<JsonObject>;
31
+ sources(): Promise<JsonObject>;
32
+ modules(): JsonObject;
33
+ }
@@ -0,0 +1,365 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // The catalogue: the core's registry, the harbor's DNA. She keeps its
3
+ // code as a git repository, from genesis, its objects in the root memory
4
+ // under her own places and never in a cell. Her cells keep `live`, the
5
+ // commit that stands, `origin`, where she fetches from, and `refs`, what
6
+ // she fetched last. A commit holds one file per module,
7
+ // `modules/<module id>.js`, and nothing else is read from it.
8
+ //
9
+ // add { source, name? } the module's source committed on `live`,
10
+ // and `live` moved to it
11
+ // remove { module } its file committed away, and `live` moved,
12
+ // refused while a being stands on its kinds
13
+ // live { commit? } `live` moved to a commit or a fetched ref,
14
+ // or, with none, the commit that stands
15
+ // log { commit? } the commits from `live` back, first
16
+ // parents alone
17
+ // origin { url } where `fetch` reads, or null
18
+ // fetch every ref of `origin` and all it reaches,
19
+ // kept, and the refs
20
+ // gc every object that neither `live` nor a
21
+ // fetched ref reaches, forgotten
22
+ // modules what `live` runs, and why a module does not
23
+ // sources `live`, and the source of each module
24
+ // file it holds, by its blob
25
+ //
26
+ // She resolves a kind to a class from the kit and the modules `live`
27
+ // runs, and nothing else does it for her: the harbor stands her before any
28
+ // registry, from the core alone. Moving `live` checks the commit's modules
29
+ // out, hands each to the terrain's loader and indexes them all: a set that
30
+ // does not run together is refused with its reason, and `live` stays. Once
31
+ // it runs, every being a new class resolves is born again on her stance,
32
+ // and what stands on what now resolves stands. A
33
+ // wake stands `live` again from the objects kept, with no network, and
34
+ // there a module that does not load, or splits a kind with one before it,
35
+ // stays out alone with its reason, and the beings of its kinds stand
36
+ // absent. At genesis `live` is an empty commit.
37
+ //
38
+ // What `add` and `remove` commit is staged aside and written to the DNA
39
+ // only once the commit checks, so a refused one leaves no object. `gc`
40
+ // takes its turn with every ask that writes the DNA, keeps her cells
41
+ // first, and forgets only what the kept `live` and refs do not reach, so
42
+ // a cut mid-collection loses nothing a restart stands.
43
+ //
44
+ // She is the core's, of kind `org.nervur.catalogue`, answers the root and an
45
+ // owner alone, and is born of her stance alone, whose `ground` is her
46
+ // harbor.
47
+ import { ships } from '../being/index.js';
48
+ import { blob, fetchPack, files, havesFrom, isId, listRefs, reachable, readCommit, StagedObjects, text, tree, writeCommit, writeTree } from '../git/index.js';
49
+ import { ownerAsk } from '../ward/index.js';
50
+ import { Registry, RegistryFaculty } from './registry.js';
51
+ export const CATALOGUE = 'catalogue';
52
+ const DIR = 'modules/';
53
+ const SUFFIX = '.js';
54
+ const pathOf = (module) => `${DIR}${module}${SUFFIX}`;
55
+ const WHO = { name: 'nervur', email: 'catalogue@nervur.org' };
56
+ const isText = (v) => typeof v === 'string';
57
+ const domainOf = (name) => `${name.split('.').slice(0, 2).join('.')}.`;
58
+ const failure = (e) => (e instanceof Error ? e.message : String(e));
59
+ export class Catalogue extends RegistryFaculty {
60
+ static kind = 'org.nervur.catalogue';
61
+ static {
62
+ ships(this);
63
+ }
64
+ static asks = {
65
+ add: ownerAsk('a module committed on live, and live moved to it', { required: ['source'] }),
66
+ remove: ownerAsk('a module committed away, and live moved', { required: ['module'] }),
67
+ live: ownerAsk('live moved to a commit, or the commit that stands'),
68
+ log: ownerAsk('the commits from live back'),
69
+ origin: ownerAsk('where fetch reads', { required: ['url'] }),
70
+ fetch: ownerAsk('every ref of origin and all it reaches, kept'),
71
+ modules: ownerAsk('the modules live runs'),
72
+ sources: ownerAsk('the source of each module file live holds, by its blob'),
73
+ gc: ownerAsk('every object neither live nor a fetched ref reaches, forgotten'),
74
+ };
75
+ #harbor;
76
+ // Every kind the kit and what `live` runs bring.
77
+ #registry;
78
+ // What `live` runs, by module, and why a module it holds does not.
79
+ #running = new Map();
80
+ #failed = new Map();
81
+ // Why `live` itself did not check out at wake, where it did not.
82
+ #fault = null;
83
+ // The asks that write the DNA, one after another.
84
+ #turn = Promise.resolve();
85
+ constructor(stance) {
86
+ super(stance);
87
+ const { ground } = stance;
88
+ if (!ground)
89
+ throw new Error('the catalogue stands in the box ward alone');
90
+ this.#harbor = ground;
91
+ this.#registry = new Registry(ground.kit, []);
92
+ }
93
+ classOf(kind) {
94
+ return this.#registry.classOf(kind);
95
+ }
96
+ // Why these modules cannot run together, or null.
97
+ #check(modules) {
98
+ return Registry.refusal(this.#harbor.kit, modules);
99
+ }
100
+ get #live() {
101
+ return isId(this.cells.live) ? this.cells.live : undefined;
102
+ }
103
+ get #refs() {
104
+ return (this.cells.refs ?? {});
105
+ }
106
+ #signature() {
107
+ return { ...WHO, seconds: Math.floor(this.#harbor.terrain.clock.now() / 1000), zone: '+0000' };
108
+ }
109
+ // One ask that writes the DNA at a time, in the order asked.
110
+ #serial(work) {
111
+ const done = this.#turn.then(work, work);
112
+ this.#turn = done.catch(() => undefined);
113
+ return done;
114
+ }
115
+ async #commit(dna, paths, message) {
116
+ const root = paths.size === 0 ? (await dna.put([tree([])]))[0] : await writeTree(dna, paths);
117
+ const who = this.#signature();
118
+ return writeCommit(dna, { tree: root, parents: this.#live ? [this.#live] : [], author: who, committer: who, message: `${message}\n` });
119
+ }
120
+ // Each module file of a commit, in the tree's order, its source loaded.
121
+ async #checkout(commit, dna = this.#harbor.dna) {
122
+ const out = [];
123
+ for (const [path, id] of await files(dna, commit)) {
124
+ if (!path.startsWith(DIR) || !path.endsWith(SUFFIX) || path.slice(DIR.length).includes('/'))
125
+ continue;
126
+ const name = path.slice(DIR.length, -SUFFIX.length);
127
+ let loaded;
128
+ try {
129
+ const o = await dna.get(id);
130
+ if (!o)
131
+ throw new Error(`blob ${id} is not kept`);
132
+ loaded = await this.#harbor.terrain.loader.load(text(o.body), id);
133
+ if (loaded.module !== name)
134
+ throw new Error(`${path} declares module ${loaded.module}`);
135
+ }
136
+ catch (e) {
137
+ loaded = e instanceof Error ? e : new Error(String(e));
138
+ }
139
+ out.push({ name, blob: id, loaded });
140
+ }
141
+ return out;
142
+ }
143
+ // `live` stood again, as a wake finds it: a genesis commit on empty
144
+ // memory, and every module that loads and splits no kind with those
145
+ // before it run; the rest stay out, each with its reason.
146
+ async wake() {
147
+ if (!this.#live) {
148
+ this.cells.live = await this.#commit(this.#harbor.dna, new Map(), 'genesis');
149
+ await this.#harbor.keep();
150
+ }
151
+ const running = new Map();
152
+ try {
153
+ for (const { name, blob: id, loaded } of await this.#checkout(this.#live)) {
154
+ const refused = loaded instanceof Error ? loaded.message : this.#check([...[...running.values()].map((r) => r.module), loaded]);
155
+ if (refused === null)
156
+ running.set(name, { module: loaded, blob: id });
157
+ else
158
+ this.#failed.set(name, { blob: id, reason: refused });
159
+ }
160
+ }
161
+ catch (e) {
162
+ this.#fault = failure(e);
163
+ }
164
+ this.#running = running;
165
+ this.#registry = new Registry(this.#harbor.kit, [...running.values()].map((r) => r.module));
166
+ }
167
+ // `live` moved to a commit whose every module loads and runs with the
168
+ // rest, or the reason it stays. A commit staged is written to the DNA
169
+ // once it checks, before it runs and before `live` names it.
170
+ async #move(commit, staged) {
171
+ let checked;
172
+ try {
173
+ checked = await this.#checkout(commit, staged);
174
+ }
175
+ catch (e) {
176
+ return failure(e);
177
+ }
178
+ const bad = checked.find((c) => c.loaded instanceof Error);
179
+ if (bad)
180
+ return `${bad.name}: ${bad.loaded.message}`;
181
+ const modules = checked.map((c) => c.loaded);
182
+ const unfit = this.#check(modules);
183
+ if (unfit !== null)
184
+ return unfit;
185
+ await staged?.flush();
186
+ this.#registry = new Registry(this.#harbor.kit, modules);
187
+ this.#running = new Map(checked.map((c) => [c.name, { module: c.loaded, blob: c.blob }]));
188
+ this.#failed = new Map();
189
+ this.#fault = null;
190
+ this.cells.live = commit;
191
+ await this.#harbor.ran();
192
+ return null;
193
+ }
194
+ async #paths() {
195
+ return this.#live ? files(this.#harbor.dna, this.#live) : new Map();
196
+ }
197
+ add(args) {
198
+ return this.#serial(() => this.#add(args));
199
+ }
200
+ async #add(args) {
201
+ if (!isText(args.source))
202
+ return { error: 'source is text' };
203
+ if (args.name !== undefined && !isText(args.name))
204
+ return { error: 'name is text' };
205
+ const staged = new StagedObjects(this.#harbor.dna);
206
+ const [id] = await staged.put([blob(args.source)]);
207
+ let loaded;
208
+ try {
209
+ loaded = await this.#harbor.terrain.loader.load(args.source, id);
210
+ }
211
+ catch (e) {
212
+ return { error: failure(e) };
213
+ }
214
+ if (args.name !== undefined && args.name !== loaded.module)
215
+ return { error: `the source is module ${loaded.module}, not ${args.name}` };
216
+ const paths = await this.#paths();
217
+ paths.set(pathOf(loaded.module), id);
218
+ const commit = await this.#commit(staged, paths, `add ${loaded.module} ${loaded.version}`);
219
+ const refused = await this.#move(commit, staged);
220
+ if (refused !== null)
221
+ return { error: refused };
222
+ return { added: loaded.module, version: loaded.version, live: commit };
223
+ }
224
+ remove(args) {
225
+ return this.#serial(() => this.#remove(args));
226
+ }
227
+ async #remove(args) {
228
+ const module = args.module;
229
+ const paths = await this.#paths();
230
+ if (!isText(module) || !paths.has(pathOf(module)))
231
+ return { error: 'no such module' };
232
+ if (this.#harbor.stands(this.#kindsOf(module)))
233
+ return { error: 'a being stands on this module' };
234
+ paths.delete(pathOf(module));
235
+ const staged = new StagedObjects(this.#harbor.dna);
236
+ const commit = await this.#commit(staged, paths, `remove ${module}`);
237
+ const refused = await this.#move(commit, staged);
238
+ if (refused !== null)
239
+ return { error: refused };
240
+ return { removed: module, live: commit };
241
+ }
242
+ // The kinds a module stands: the registry's, where it runs, and where it
243
+ // does not, every kind a row keeps in its domain that nothing resolves.
244
+ #kindsOf(module) {
245
+ if (this.#running.has(module))
246
+ return this.#registry.kindsOf(module);
247
+ const domain = domainOf(module);
248
+ return this.#harbor.kept().filter((kind) => kind.startsWith(domain) && this.#registry.classOf(kind) === undefined);
249
+ }
250
+ live(args) {
251
+ if (args.commit === undefined)
252
+ return Promise.resolve({ live: this.#live ?? null });
253
+ return this.#serial(() => this.#goLive(args));
254
+ }
255
+ async #goLive(args) {
256
+ const named = args.commit;
257
+ const commit = isText(named) && Object.hasOwn(this.#refs, named) ? this.#refs[named] : named;
258
+ if (!isId(commit))
259
+ return { error: 'commit is a commit id or a fetched ref' };
260
+ const refused = await this.#move(commit);
261
+ return refused === null ? { live: commit } : { error: refused };
262
+ }
263
+ async log(args) {
264
+ const from = args.commit ?? this.#live;
265
+ if (!isId(from))
266
+ return { error: 'commit is a commit id' };
267
+ const log = [];
268
+ try {
269
+ for (let at = from; at !== undefined;) {
270
+ const o = await this.#harbor.dna.get(at);
271
+ if (!o)
272
+ break;
273
+ const c = readCommit(o);
274
+ log.push({ commit: at, parents: [...c.parents], message: c.message.replace(/\n$/, ''), seconds: c.committer.seconds });
275
+ at = c.parents[0];
276
+ }
277
+ }
278
+ catch (e) {
279
+ return { error: failure(e) };
280
+ }
281
+ return { log };
282
+ }
283
+ origin(args) {
284
+ const url = args.url;
285
+ if (url === null) {
286
+ delete this.cells.origin;
287
+ return { origin: null };
288
+ }
289
+ if (!isText(url) || !/^https?:\/\/[^/\s]+/.test(url))
290
+ return { error: 'url is an http or https URL, or null' };
291
+ this.cells.origin = url;
292
+ return { origin: url };
293
+ }
294
+ // Read-only on the remote: its refs, and a pack of what they reach that
295
+ // is not kept here, each object kept. The commits `live` and the refs
296
+ // reach are its haves, so the pack carries only what is new.
297
+ fetch() {
298
+ return this.#serial(() => this.#fetch());
299
+ }
300
+ async #fetch() {
301
+ const url = this.cells.origin;
302
+ if (!isText(url))
303
+ return { error: 'no origin is set' };
304
+ try {
305
+ const refs = await listRefs(url);
306
+ const held = (id) => this.#harbor.dna.get(id);
307
+ const objects = await fetchPack(url, Object.values(refs), held, await havesFrom(this.#roots(), held));
308
+ await this.#harbor.dna.put([...objects.values()]);
309
+ this.cells.refs = refs;
310
+ return { refs, objects: objects.size };
311
+ }
312
+ catch (e) {
313
+ return { error: failure(e) };
314
+ }
315
+ }
316
+ // What the DNA keeps history from: `live` and every fetched ref.
317
+ #roots() {
318
+ return [...new Set([...(this.#live ? [this.#live] : []), ...Object.values(this.#refs)])];
319
+ }
320
+ // What `live` and every fetched ref reach stays, and the rest of the
321
+ // DNA is forgotten. Her cells are kept first, so she collects against
322
+ // what a restart stands; the whole reachable set is known before any
323
+ // object goes, and an object missing from it stops the collection.
324
+ gc() {
325
+ return this.#serial(async () => {
326
+ if (!(await this.#harbor.keep()))
327
+ throw new Error('her cells were not kept');
328
+ const dna = this.#harbor.dna;
329
+ const keep = new Set();
330
+ for (const root of this.#roots()) {
331
+ // An annotated tag is kept, and the commit it names walked; a
332
+ // missing object throws here, before anything is forgotten.
333
+ let at = root;
334
+ for (let o = await dna.get(at); o?.type === 'tag'; o = await dna.get(at)) {
335
+ keep.add(at);
336
+ at = /^object ([0-9a-f]{40})$/m.exec(text(o.body))?.[1] ?? '';
337
+ }
338
+ for (const id of await reachable(dna, at))
339
+ keep.add(id);
340
+ }
341
+ return { collected: await dna.collect(keep), kept: keep.size };
342
+ });
343
+ }
344
+ // Each module file `live` holds, by its blob's id, with its source: what
345
+ // an edge's next child carries.
346
+ async sources() {
347
+ const sources = [];
348
+ for (const [path, id] of await this.#paths()) {
349
+ if (!path.startsWith(DIR) || !path.endsWith(SUFFIX))
350
+ continue;
351
+ const o = await this.#harbor.dna.get(id);
352
+ if (o)
353
+ sources.push({ blob: id, source: text(o.body) });
354
+ }
355
+ return { live: this.#live ?? null, sources };
356
+ }
357
+ modules() {
358
+ const modules = {};
359
+ for (const [name, r] of this.#running)
360
+ modules[name] = { running: r.module.version, blob: r.blob };
361
+ for (const [name, f] of this.#failed)
362
+ modules[name] = { running: null, blob: f.blob, failed: f.reason };
363
+ return { live: this.#live ?? null, modules, ...(this.#fault === null ? {} : { failed: this.#fault }) };
364
+ }
365
+ }
@@ -0,0 +1,13 @@
1
+ import { type GitObject, type ObjectStore } from '../git/index.ts';
2
+ import type { Memory } from '../contract/index.ts';
3
+ import type { Package } from './package.ts';
4
+ export declare class Dna implements ObjectStore {
5
+ #private;
6
+ static readonly prefix: string;
7
+ constructor(memory: Memory, pack: Package);
8
+ static occupies(place: string): boolean;
9
+ get(id: string): Promise<GitObject | null>;
10
+ put(objects: readonly GitObject[]): Promise<string[]>;
11
+ count(): Promise<number>;
12
+ collect(keep: Iterable<string>): Promise<number>;
13
+ }
@@ -0,0 +1,120 @@
1
+ var _a;
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ // The DNA's objects, kept in the root memory under the catalogue's own
4
+ // places and sealed under the package as every row is, so the terrain
5
+ // reads no code and no object's id.
6
+ //
7
+ // place the hex of `catalogue`, then the first two digits of the
8
+ // entry's name, git's fan-out
9
+ // entry 16 bytes under ["dna", object id], as hex
10
+ // bytes nonce (12) || AES-256-GCM( the object deflated as git keeps
11
+ // it loose ), under 32 bytes of ["seal", "dna"], the entry's
12
+ // name as additional data
13
+ //
14
+ // An object read back is checked against its id, so a store that hands
15
+ // back other bytes hands back nothing.
16
+ import { concat, decrypt, encrypt, hex, utf8 } from '../crypto/index.js';
17
+ import { deflated, idOf, inflated } from '../git/index.js';
18
+ const NONCE = 12;
19
+ const FAN = 2;
20
+ export class Dna {
21
+ // The places under this prefix are the catalogue's, never a ward's: a
22
+ // ward's place is 32 digits, and a memory faculty's is its key's hex and
23
+ // 32 more, never fewer than 34.
24
+ static prefix = hex(utf8('catalogue'));
25
+ #memory;
26
+ #pack;
27
+ #held = new Map();
28
+ #seal;
29
+ constructor(memory, pack) {
30
+ this.#memory = memory;
31
+ this.#pack = pack;
32
+ }
33
+ static occupies(place) {
34
+ return place.length === _a.prefix.length + FAN && place.startsWith(_a.prefix);
35
+ }
36
+ #key() {
37
+ return (this.#seal ??= this.#pack.derive(['seal', 'dna'], 32));
38
+ }
39
+ async #entry(id) {
40
+ const name = hex(await this.#pack.derive(['dna', id], 16));
41
+ return { place: _a.prefix + name.slice(0, FAN), name };
42
+ }
43
+ async get(id) {
44
+ const held = this.#held.get(id);
45
+ if (held)
46
+ return held;
47
+ const { place, name } = await this.#entry(id);
48
+ const bytes = (await this.#memory.read(place)).get(name);
49
+ if (!bytes || bytes.length <= NONCE)
50
+ return null;
51
+ const open = await decrypt(await this.#key(), bytes.subarray(0, NONCE), utf8(name), bytes.subarray(NONCE));
52
+ if (open === null)
53
+ return null;
54
+ const o = await inflated(open);
55
+ if ((await idOf(o)) !== id)
56
+ return null;
57
+ this.#held.set(id, o);
58
+ return o;
59
+ }
60
+ // Every object sealed and written, one keep for each place.
61
+ async put(objects) {
62
+ const ids = [];
63
+ const places = new Map();
64
+ const kept = new Map();
65
+ for (const o of objects) {
66
+ const id = await idOf(o);
67
+ ids.push(id);
68
+ if (this.#held.has(id) || kept.has(id))
69
+ continue;
70
+ const { place, name } = await this.#entry(id);
71
+ const nonce = this.#pack.entropy.draw(NONCE);
72
+ const sealed = concat(nonce, await encrypt(await this.#key(), nonce, utf8(name), await deflated(o)));
73
+ if (!places.has(place))
74
+ places.set(place, new Map());
75
+ places.get(place).set(name, sealed);
76
+ kept.set(id, o);
77
+ }
78
+ for (const [place, entries] of places)
79
+ await this.#memory.write(place, entries);
80
+ for (const [id, o] of kept)
81
+ this.#held.set(id, o);
82
+ return ids;
83
+ }
84
+ async #places() {
85
+ return (await this.#memory.places()).filter((place) => _a.occupies(place));
86
+ }
87
+ // How many objects are kept.
88
+ async count() {
89
+ let n = 0;
90
+ for (const place of await this.#places())
91
+ n += (await this.#memory.read(place)).size;
92
+ return n;
93
+ }
94
+ // Every object but those `keep` names forgotten, and how many were. The
95
+ // names kept are drawn before any entry is touched, and a place is
96
+ // written with only its forgotten entries, so a cut anywhere loses
97
+ // nothing `keep` names.
98
+ async collect(keep) {
99
+ const ids = new Set(keep);
100
+ const names = new Set();
101
+ for (const id of ids)
102
+ names.add((await this.#entry(id)).name);
103
+ let forgotten = 0;
104
+ for (const place of await this.#places()) {
105
+ const gone = new Map();
106
+ for (const name of (await this.#memory.read(place)).keys())
107
+ if (!names.has(name))
108
+ gone.set(name, null);
109
+ if (gone.size === 0)
110
+ continue;
111
+ await this.#memory.write(place, gone);
112
+ forgotten += gone.size;
113
+ }
114
+ for (const id of [...this.#held.keys()])
115
+ if (!ids.has(id))
116
+ this.#held.delete(id);
117
+ return forgotten;
118
+ }
119
+ }
120
+ _a = Dna;