nervur 0.16.0 → 0.17.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 (111) hide show
  1. package/GETTING_STARTED.md +138 -0
  2. package/NOTICE +1 -1
  3. package/README.md +108 -20
  4. package/dist/being/being.d.ts +24 -0
  5. package/dist/being/being.js +109 -0
  6. package/dist/being/digest.d.ts +3 -0
  7. package/dist/being/digest.js +37 -0
  8. package/dist/being/index.d.ts +5 -0
  9. package/dist/being/index.js +6 -0
  10. package/dist/being/silence.d.ts +12 -0
  11. package/dist/being/silence.js +41 -0
  12. package/dist/being/types.d.ts +89 -0
  13. package/dist/being/types.js +58 -0
  14. package/dist/conformance/assert.d.ts +11 -0
  15. package/dist/conformance/assert.js +106 -0
  16. package/dist/conformance/beings.d.ts +198 -0
  17. package/dist/conformance/beings.js +183 -0
  18. package/dist/conformance/estate.d.ts +5 -0
  19. package/dist/conformance/estate.js +388 -0
  20. package/dist/conformance/index.d.ts +81 -0
  21. package/dist/conformance/index.js +816 -0
  22. package/dist/conformance/reach.d.ts +10 -0
  23. package/dist/conformance/reach.js +72 -0
  24. package/dist/conformance/store.d.ts +5 -0
  25. package/dist/conformance/store.js +123 -0
  26. package/dist/harbor/core.d.ts +53 -0
  27. package/dist/harbor/core.js +621 -0
  28. package/dist/harbor/dial.d.ts +9 -0
  29. package/dist/harbor/dial.js +81 -0
  30. package/dist/harbor/index.d.ts +8 -0
  31. package/dist/harbor/index.js +16 -0
  32. package/dist/harbor/memory.d.ts +25 -0
  33. package/dist/harbor/memory.js +72 -0
  34. package/dist/harbor/reach.d.ts +36 -0
  35. package/dist/harbor/reach.js +191 -0
  36. package/dist/harbor/store.d.ts +40 -0
  37. package/dist/harbor/store.js +70 -0
  38. package/dist/vector/cases.d.ts +41 -0
  39. package/dist/vector/cases.js +195 -0
  40. package/dist/vector/index.d.ts +6 -0
  41. package/dist/vector/index.js +8 -0
  42. package/dist/vector/stand.d.ts +9 -0
  43. package/dist/vector/stand.js +76 -0
  44. package/dist/vector/world.d.ts +143 -0
  45. package/dist/vector/world.js +198 -0
  46. package/dist/ward/allowance.d.ts +10 -0
  47. package/dist/ward/allowance.js +71 -0
  48. package/dist/ward/arithmetic.d.ts +31 -0
  49. package/dist/ward/arithmetic.js +245 -0
  50. package/dist/ward/cells.d.ts +7 -0
  51. package/dist/ward/cells.js +186 -0
  52. package/dist/ward/door.d.ts +18 -0
  53. package/dist/ward/door.js +186 -0
  54. package/dist/ward/ground.d.ts +23 -0
  55. package/dist/ward/ground.js +38 -0
  56. package/dist/ward/heirs.d.ts +13 -0
  57. package/dist/ward/heirs.js +115 -0
  58. package/dist/ward/index.d.ts +9 -0
  59. package/dist/ward/index.js +13 -0
  60. package/dist/ward/owner.d.ts +13 -0
  61. package/dist/ward/owner.js +220 -0
  62. package/dist/ward/partition.d.ts +65 -0
  63. package/dist/ward/partition.js +295 -0
  64. package/dist/ward/seal.d.ts +52 -0
  65. package/dist/ward/seal.js +145 -0
  66. package/dist/ward/stance.d.ts +25 -0
  67. package/dist/ward/stance.js +413 -0
  68. package/dist/ward/ward.d.ts +12 -0
  69. package/dist/ward/ward.js +361 -0
  70. package/package.json +37 -15
  71. package/protocol/SPEC.md +1843 -0
  72. package/protocol/vectors/arithmetic.json +117 -0
  73. package/protocol/vectors/door.json +345 -0
  74. package/protocol/vectors/framing.json +92 -0
  75. package/protocol/vectors/wire.json +48 -0
  76. package/quo-kit.md +652 -0
  77. package/src/being/being.ts +123 -0
  78. package/src/being/digest.ts +46 -0
  79. package/src/being/index.ts +7 -0
  80. package/src/being/silence.ts +46 -0
  81. package/src/being/types.ts +170 -0
  82. package/src/conformance/assert.ts +100 -0
  83. package/src/conformance/beings.ts +183 -0
  84. package/src/conformance/estate.ts +412 -0
  85. package/src/conformance/index.ts +962 -0
  86. package/src/conformance/reach.ts +83 -0
  87. package/src/conformance/store.ts +136 -0
  88. package/src/harbor/core.ts +660 -0
  89. package/src/harbor/dial.ts +112 -0
  90. package/src/harbor/index.ts +17 -0
  91. package/src/harbor/memory.ts +83 -0
  92. package/src/harbor/reach.ts +215 -0
  93. package/src/harbor/store.ts +101 -0
  94. package/src/vector/cases.ts +229 -0
  95. package/src/vector/index.ts +11 -0
  96. package/src/vector/stand.ts +75 -0
  97. package/src/vector/world.ts +220 -0
  98. package/src/ward/allowance.ts +85 -0
  99. package/src/ward/arithmetic.ts +247 -0
  100. package/src/ward/cells.ts +190 -0
  101. package/src/ward/door.ts +186 -0
  102. package/src/ward/ground.ts +160 -0
  103. package/src/ward/heirs.ts +114 -0
  104. package/src/ward/index.ts +17 -0
  105. package/src/ward/owner.ts +214 -0
  106. package/src/ward/partition.ts +353 -0
  107. package/src/ward/seal.ts +174 -0
  108. package/src/ward/stance.ts +433 -0
  109. package/src/ward/ward.ts +378 -0
  110. package/index.js +0 -17
  111. package/nervur.js +0 -5
@@ -0,0 +1,378 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // A ward that is its own ward. Booted by a harbor with one object, the
3
+ // ground, and hands back two pointers: the door and the ask. Boots itself as
4
+ // the first being in its own map, and every being it later instantiates gets
5
+ // a stance built by the same code. Mints every key, seals every ask that
6
+ // leaves, opens every one that arrives.
7
+ import { silence, isSilence, unreached, isWord, word } from '../being/silence.ts';
8
+ import { OWNER } from '../being/types.ts';
9
+ import type { Asker, BeingLike, Cells, JsonObject, Reply, Stance, Wanted, Word } from '../being/types.ts';
10
+ import type { Ground, WardPointers } from './ground.ts';
11
+ import { at, drop, open, put, emptyBind, emptyCells, HEAD, MINTED, type Bind, type Partition, type StandingKeys } from './partition.ts';
12
+ import { Heirs } from './heirs.ts';
13
+ import { makeDoor, type Door } from './door.ts';
14
+ import { buildStance } from './stance.ts';
15
+ import { ownerAnswer } from './owner.ts';
16
+ import { allow, within, LATE } from './allowance.ts';
17
+ import { beingKey, openReply, sealAsk, wardKey, wardPadlock, wardSignPk, type ReplyPayload, type WardKey } from './seal.ts';
18
+ import { unhex } from './arithmetic.ts';
19
+ import { guardCells, unguarded, cellFault } from './cells.ts';
20
+
21
+ // One being of the ward as the ward holds her: her door, her bind table, her
22
+ // stance and her cells. The owner's asks see this and nothing more. Not
23
+ // `Booted`, which is a harbor's word for a ward it started: this is a ward's
24
+ // word for a being living in it, and the two are one module apart.
25
+ export type Resident = Door & { bind: Bind; stance: Stance; cells: Cells & { class?: string } };
26
+
27
+ // The ward. One call in, two pointers out. Async because keys are derived.
28
+ export async function Ward(ground: Ground): Promise<WardPointers> {
29
+ const w = await Self.boot(ground);
30
+ return { door: w.door, ask: w.ask };
31
+ }
32
+
33
+ class Self implements BeingLike {
34
+ readonly key: WardKey;
35
+ readonly pk: string;
36
+ readonly g: Ground;
37
+ readonly p: Partition;
38
+ readonly heirs: Heirs;
39
+ readonly doors = new Map<string, Resident>();
40
+ readonly #birthing = new Set<string>(); // keys whose constructor is running: taken, though no door and no row say so yet
41
+ readonly door: WardPointers['door'];
42
+ readonly ask: WardPointers['ask'];
43
+
44
+ static async boot(ground: Ground): Promise<Self> {
45
+ return new Self(ground, await wardKey(ground.seed));
46
+ }
47
+
48
+ private constructor(ground: Ground, key: WardKey) {
49
+ this.g = ground;
50
+ this.key = key;
51
+ this.pk = key.pk;
52
+ this.p = open(ground.memory); // the partition: the ward's own state, and every being's row
53
+ // Opening is itself a write: a memory nobody has written comes back with
54
+ // a version stamped on it and the ward's own tables under it. A ward that
55
+ // said nothing here would have every being of it kept and the head that
56
+ // says which version they are written under kept by nobody, so the next
57
+ // boot would read a partition of no version and refuse it.
58
+ this.#wrote(HEAD);
59
+ this.heirs = new Heirs(this.p, () => this.#wrote(HEAD));
60
+ this.door = makeDoor(
61
+ key,
62
+ this.heirs,
63
+ this.doors,
64
+ () => this.p.public,
65
+ (n) => ground.random(n),
66
+ () => this.#keep(),
67
+ ); // pointer one
68
+ this.#boot(this.pk, () => this); // the ward's ward is itself
69
+ // a restart is silent: every being in the cells is constructed again, unasked.
70
+ // A throw on a boot the owner asked for is that boot's error, and nothing
71
+ // half-lives. This boot nobody asked for, and there is nobody to hand an
72
+ // error to. Any constructor may throw, a default that is not a value, an
73
+ // author's mistake, a shape she does not recognise after an upgrade, and
74
+ // let through here it is one being taking down the ward and every other
75
+ // being in it, on a restart. She is absent instead: no door, so silence
76
+ // at the door, and her cells sit untouched, waiting for the run that can
77
+ // read them. Her row stays hers: nobody boots another class over it, the
78
+ // owner sees her as absent, and the owner may unboot her.
79
+ for (const [k, c] of Object.entries(this.p.beings)) {
80
+ const className = c.class;
81
+ if (k === this.pk || !className) continue;
82
+ try {
83
+ this.#boot(k, (stance) => this.g.instantiate(className, stance));
84
+ } catch {
85
+ /* she is not here this run. the ward is, and so is everyone else. */
86
+ }
87
+ }
88
+ // pointer two. the owner. the same answer every named asker gets, asker filled in as owner.
89
+ this.ask = async (method, args = {}) => {
90
+ try {
91
+ const out = await this.answer({ id: OWNER }, method, args as JsonObject);
92
+ // The same question the door asks, in the same place: after she has
93
+ // answered and before the owner holds the answer. Silence is what a
94
+ // failed ask says here, so a store that refused is silence, and the
95
+ // owner reads it as it reads every other ask that did not take.
96
+ if (!(await this.#keep().catch(() => false))) return silence;
97
+ return isSilence(out) || isWord(out) ? silence : out;
98
+ } catch {
99
+ return silence;
100
+ }
101
+ };
102
+ }
103
+
104
+ // ---- the ward as a being. asked only by its owners: the root, through
105
+ // the ask pointer, and its occupants, through the door, whom only the root
106
+ // can invite. A stranger cannot be a named asker here, because the door
107
+ // names nobody the ward did not bind.
108
+ async answer(asker: Asker, method?: string, args: JsonObject = {}): Promise<Reply> {
109
+ // Nobody is not an owner. The door names nobody the ward did not bind, so
110
+ // the only way here unnamed is a partition that says the ward is its own
111
+ // public being, which no owner ask will write. Silence, not an error
112
+ // object: an unnamed asker is a stranger, and a stranger hears one thing.
113
+ if (asker.id === undefined) return silence;
114
+ return ownerAnswer(
115
+ {
116
+ pk: this.pk,
117
+ doors: this.doors,
118
+ absent: () => this.#absent(),
119
+ publicKey: () => this.p.public,
120
+ instantiate: (className, key) => this.#instantiate(className, key),
121
+ unboot: (key) => this.#unboot(key),
122
+ setPublic: (key) => {
123
+ this.p.public = key;
124
+ this.#wrote(HEAD);
125
+ },
126
+ },
127
+ asker,
128
+ method,
129
+ args,
130
+ );
131
+ }
132
+
133
+ // ---- ward functions. on the object. no stance reaches them.
134
+
135
+ // The partition was written, and which row of it: a being by her key, or
136
+ // the head. The harbor is told, and nothing more: what it does with the
137
+ // word is its own, and the ward never learns. Every write says its row,
138
+ // because a harbor told only that something moved must keep all of it, and
139
+ // a being's row is the one part of a partition that grows without limit.
140
+ #wrote(row: string): void {
141
+ this.g.wrote?.(row);
142
+ }
143
+
144
+ // Whether what was written is kept, asked once at the end of an arrival
145
+ // and never during one. A harbor that keeps nothing was never going to
146
+ // refuse anything, so its silence here is yes: a ward driven with no store
147
+ // under it answers exactly as it did before a harbor could say no.
148
+ async #keep(): Promise<boolean> {
149
+ return this.g.keep ? await this.g.keep() : true;
150
+ }
151
+
152
+ // The rows no door holds this run: beings whose class threw at birth or
153
+ // is not the harbor's to give. Key to class.
154
+ #absent(): Record<string, string | null> {
155
+ const out: Record<string, string | null> = {};
156
+ for (const [k, c] of Object.entries(this.p.beings)) if (k !== this.pk && !this.doors.has(k)) put(out, k, c.class ?? null);
157
+ return out;
158
+ }
159
+
160
+ // A new being under a key nobody holds. A key with a row in the partition
161
+ // is booted, whether a door holds it this run or not: an absent being's
162
+ // row is hers, with every relation in it, and no class is booted over it.
163
+ // A throw at birth is that boot's refusal and nothing half-lives; the
164
+ // ward says which, since the owner hears objects.
165
+ #instantiate(className: string, key: string): Resident | 'threw' | null {
166
+ // The three refusals are read before the constructor runs, and the
167
+ // constructor can reach the stance and boot: a class that boots its own
168
+ // key from inside itself would pass all three, since neither its door nor
169
+ // its row exists yet, and the two objects would then share one row. A key
170
+ // being born is a key taken, for as long as the birth lasts.
171
+ if (typeof key !== 'string' || typeof className !== 'string' || !key || this.doors.has(key) || at(this.p.beings, key) || this.#birthing.has(key)) return null;
172
+ let door: Resident | null;
173
+ this.#birthing.add(key);
174
+ try {
175
+ door = this.#boot(key, (stance) => this.g.instantiate(className, stance));
176
+ } catch {
177
+ return 'threw';
178
+ } finally {
179
+ this.#birthing.delete(key);
180
+ }
181
+ if (!door) return null;
182
+ unguarded(door.cells).class = className; // so a restart finds her. the ward's key, written behind her guard
183
+ this.#wrote(key);
184
+ return door;
185
+ }
186
+
187
+ // The inverse of boot, and the only way a being leaves a ward. Her
188
+ // relations go first and by the same calls she would have used herself,
189
+ // so every occupant hears `removed` at the door from the keys it kept,
190
+ // rather than meeting a being who is simply not there any more. What is
191
+ // left after that is her cells and her bind table, and they go with her:
192
+ // a row naming a being no door holds would be read on the next restart
193
+ // and boot her again. An absent being leaves the same way: she has no
194
+ // stance to speak for her, so the ward closes her heirs itself.
195
+ #unboot(key: string): string[] | null {
196
+ if (key === this.pk) return null;
197
+ const door = this.doors.get(key);
198
+ const cells = door?.cells ?? at(this.p.beings, key);
199
+ if (!cells) return null;
200
+ const ids = (r: unknown) => (r !== null && typeof r === 'object' ? Object.keys(r) : []);
201
+ const occupants = ids(cells.occupants),
202
+ standings = ids(cells.standings);
203
+ if (door) {
204
+ for (const id of occupants) door.stance.occupants.remove(id);
205
+ for (const id of standings) door.stance.standings.remove(id);
206
+ } else {
207
+ const bind = at(this.p.bind, key);
208
+ for (const heir of Object.values(bind?.occupants ?? {})) this.heirs.close(heir);
209
+ }
210
+ // A ward whose public being is gone answers arrivals for no heir with
211
+ // silence, as a ward that never had one does.
212
+ if (this.p.public === key) this.p.public = null;
213
+ drop(this.p.beings, key);
214
+ drop(this.p.bind, key);
215
+ this.doors.delete(key);
216
+ this.#wrote(key); // she is gone, and her row goes with her
217
+ this.#wrote(HEAD); // her heirs were closed, and the public being may have been her
218
+ return [...occupants, ...standings];
219
+ }
220
+
221
+ // Nothing is written until there is somebody to write it for: a class the
222
+ // harbor does not know makes no being, and a boot that made nobody must
223
+ // leave the partition as it found it.
224
+ //
225
+ // `make` is who to build with the stance this raises. Two callers and two
226
+ // makers: the ground's `instantiate` for a being of the ward, and the ward
227
+ // itself, which is the first being in its own map and is already made.
228
+ #boot(key: string, make: (stance: Stance) => BeingLike | null): Resident | null {
229
+ const cells = guardCells(at(this.p.beings, key) ?? emptyCells(), () => this.#wrote(key));
230
+ const bind = at(this.p.bind, key) ?? emptyBind();
231
+ const stance: Stance = buildStance(
232
+ {
233
+ pk: this.pk,
234
+ // This stance, not merely this key: unboot and boot again under the
235
+ // same key makes a new being, and the old stance is not hers.
236
+ live: () => this.doors.get(key)?.stance === stance,
237
+ mintKey: (b) => this.#mintKey(b, key),
238
+ openHeir: (heir, being, id) => this.heirs.open(heir, being, id),
239
+ closeHeir: (heir) => this.heirs.close(heir),
240
+ send: (b, keys, method, args, wanted) => this.#send(b, keys, method, args, wanted, key),
241
+ // A throw at birth is null to her, not a throw in her method: she asked
242
+ // for a being and got none, and her own answer is still hers to give.
243
+ instantiate: (className, k) => {
244
+ const made = this.#instantiate(className, k);
245
+ return made && made !== 'threw' ? made.key : null;
246
+ },
247
+ // The two halves of a maker's way back, each the ward's own call: the
248
+ // being made mints an occupant for her maker, and a relation that
249
+ // never came about takes her out again.
250
+ relate: async (k, id) => (await this.doors.get(k)?.stance.occupants.invite(id)) ?? null,
251
+ unmake: (k) => void this.#unboot(k),
252
+ // What this device lends this ward's beings. The ward knocks and takes
253
+ // inside the harbor's own call and reads nothing in the value: which
254
+ // names there are, which ward may ask for one, and what becomes of one
255
+ // she did not take, is the harbor's and no word of the ward.
256
+ lend: async (name, take) => (await this.g.lend?.(name, take)) ?? false,
257
+ wrote: () => this.#wrote(key),
258
+ },
259
+ key,
260
+ cells,
261
+ bind,
262
+ );
263
+ const being = make(stance);
264
+ if (!being || typeof being.answer !== 'function') return null; // a being is anything with an answer, and nothing without one
265
+ put(this.p.beings, key, cells);
266
+ put(this.p.bind, key, bind);
267
+ const door: Resident = { key, cells, bind, stance, being };
268
+ this.doors.set(key, door);
269
+ this.#wrote(key);
270
+ return door;
271
+ }
272
+
273
+ async #mintKey(bind: Bind, row: string): Promise<{ seed: string; pk: string }> {
274
+ const k = await beingKey(this.g.random(32));
275
+ // The last few she minted, and no more. A relation rotates on every ask,
276
+ // so a list of all of them is a partition that grows for as long as she
277
+ // keeps talking and can never be written down. Nothing is read from this
278
+ // but the keys of the moment.
279
+ bind.minted.push(k.pk);
280
+ if (bind.minted.length > MINTED) bind.minted.splice(0, bind.minted.length - MINTED);
281
+ this.#wrote(row);
282
+ return k;
283
+ }
284
+
285
+ // One send for every door, the ward's own included. Mine: never leaves.
286
+ // Not mine: carry. Signs as the standing's current key, announces its
287
+ // next, seals to the far ward, opens the reply with the ephemeral secret,
288
+ // and rotates to the announced key once the far door has answered under
289
+ // the current one.
290
+ async #send(bind: Bind, keys: StandingKeys, method: string | undefined, args: JsonObject, wanted: Wanted | undefined, row: string): Promise<ReplyPayload | typeof silence | Word> {
291
+ // The harbor is told a call is in flight for as long as this one lasts,
292
+ // and told again when it ends. Everything this ward writes between the
293
+ // two is half of a relation: the count taken here, the key rotated when
294
+ // the reply opens, and whatever the being writes on either side of her
295
+ // own ask. A harbor may write those rows down; what it may not do is put
296
+ // the ward back to them.
297
+ const lower = this.g.calling?.();
298
+ try {
299
+ return await this.#sent(bind, keys, method, args, wanted, row);
300
+ } finally {
301
+ lower?.();
302
+ }
303
+ }
304
+
305
+ async #sent(bind: Bind, keys: StandingKeys, method: string | undefined, args: JsonObject, wanted: Wanted | undefined, row: string): Promise<ReplyPayload | typeof silence | Word> {
306
+ // What she asked for, held to what this ward allows. Asking for nothing is
307
+ // the default, and asking for more than the ceiling is the ceiling: budget
308
+ // is granted by a ward, never minted by a being.
309
+ const budget = allow(wanted);
310
+ const by = (await beingKey(unhex(keys.current))).pk;
311
+ // A public being is reached without a heir, and the door keeps nothing for
312
+ // whoever asked: no key it vouched for, so nothing to announce and nothing
313
+ // to rotate to. A standing on her signs with one key for life. Every other
314
+ // ask announces its next: there is no send that does not.
315
+ if (keys.next === null && keys.heir !== null) keys.next = (await this.#mintKey(bind, row)).seed;
316
+ const next = keys.next === null ? null : (await beingKey(unhex(keys.next))).pk;
317
+ // Her count for this relation, one higher every call and never reused. The
318
+ // far door honours each number once. One relation sends one at a time, so
319
+ // the numbers leave in the order they were taken.
320
+ const seq = (keys.seq += 1);
321
+ // Named here, before the bytes go out, and not at the end with the keys.
322
+ // The far door honours a number once, and it honours it before this side
323
+ // hears anything back: a reply that is lost, a wait that ran out, a road
324
+ // that took the bytes and said nothing, all end this call before the end
325
+ // of it is reached. A count named only there is a count a restart forgets
326
+ // while the door remembers, and she then speaks a number that door has
327
+ // already spent, which it refuses as the replay it cannot tell it from.
328
+ // So the rule is the send and not the reply: what she spoke under is
329
+ // written down because she spoke it.
330
+ this.#wrote(row);
331
+ // Args that will not go through the seal are args that never left. A
332
+ // being holds three answers and a throw is not one of them, so an ask
333
+ // that cannot be sealed is told what is true of it: no door was reached.
334
+ // One object of values, by the rule her cells keep and the far door
335
+ // reads: a Date would arrive as a string, a function would not arrive at
336
+ // all, a list or a string would be refused as a stranger's bytes, and
337
+ // she would never know the far being was asked something other than
338
+ // what she said.
339
+ if (args === null || typeof args !== 'object' || Array.isArray(args) || cellFault(args, 'args') !== null) return unreached();
340
+ let sealed;
341
+ try {
342
+ sealed = await sealAsk(keys.heir, { by, next, seq, time: budget.time, method, args }, unhex(keys.current), wardPadlock(keys.ward), this.g.random(32));
343
+ } catch {
344
+ return unreached();
345
+ }
346
+ const { bytes, ephemeral } = sealed;
347
+ // The wait is bounded here, and again around the lane in the stance. Two
348
+ // bounds and not one, because they end two different things: the stance's
349
+ // ends the wait a being is held in, and this one ends the occupancy of the
350
+ // relation's lane. A being holds three answers and a wait that does not
351
+ // end is none of them, and a lane nobody ever leaves is a relation the
352
+ // next ask never reaches. Only a bound on the wire breaks the second, and
353
+ // taking it out would leave one quiet far side holding the lane for good.
354
+ // What comes back late is not read.
355
+ //
356
+ // A wait that ran out is `late`, never unreached. Unreached promises
357
+ // nothing was delivered and is safe to retry; a bound that expired knows
358
+ // no such thing, because the far door may have heard and be working still.
359
+ //
360
+ // A harbor answers bytes or nothing. One that throws instead has answered
361
+ // nothing in a louder voice, and is read as nothing: no door was reached.
362
+ const carried = (keys.ward === this.pk ? this.door(new Uint8Array(bytes)).then((r) => r.bytes) : this.g.carry(keys.ward, bytes)).then(
363
+ (b) => b,
364
+ () => undefined,
365
+ );
366
+ const out = await within(budget.time, carried);
367
+ if (out === LATE) return word('late');
368
+ if (out === undefined) return unreached(); // nothing came back. no door was reached.
369
+ const reply = await openReply(out, ephemeral, wardSignPk(keys.ward));
370
+ if (!reply) return silence; // did not open, or not from that ward: not Quo's bytes
371
+ if ('object' in reply && keys.next !== null) {
372
+ keys.current = keys.next; // the far door holds `next` as announced. move to it.
373
+ keys.next = null;
374
+ }
375
+ this.#wrote(row); // the count moved, and the keys may have
376
+ return reply;
377
+ }
378
+ }
package/index.js DELETED
@@ -1,17 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The name, and what stands under it. This package is the unscoped twin of
3
- // the `@nervur-org` scope: `import ... from 'nervur'` is the library, and
4
- // `npx nervur` is the dock's command. It pins the two exactly, because a
5
- // name that stands for the kit stands for one version of it.
6
-
7
- export * from '@nervur-org/nervur';
8
-
9
- /** The published packages, and what each one is. */
10
- export const PACKAGES = Object.freeze({
11
- '@nervur-org/nervur': 'the library: harbor, ward and being, the first kit of Quo',
12
- '@nervur-org/dock': 'what every estate on Quo needs and nobody writes twice',
13
- '@nervur-org/ui': 'the kit: one token contract, one baseline, the primitives',
14
- });
15
-
16
- /** Where to read what the kit is. */
17
- export const HOMEPAGE = 'https://nervur.org';
package/nervur.js DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- // SPDX-License-Identifier: Apache-2.0
3
- // `nervur`: the dock's command under the kit's name. Every subcommand is the
4
- // dock's; this file only finds it.
5
- import '@nervur-org/dock/cli';