nervur 0.18.0 → 0.19.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 (53) hide show
  1. package/README.md +7 -7
  2. package/dist/being/index.d.ts +1 -0
  3. package/dist/being/index.js +1 -0
  4. package/dist/being/lent.d.ts +32 -0
  5. package/dist/being/lent.js +63 -0
  6. package/dist/being/types.js +1 -1
  7. package/dist/conformance/beings.d.ts +2 -1
  8. package/dist/conformance/beings.js +7 -2
  9. package/dist/conformance/index.d.ts +0 -1
  10. package/dist/conformance/index.js +8 -5
  11. package/dist/harbor/box.d.ts +121 -0
  12. package/dist/harbor/box.js +121 -0
  13. package/dist/harbor/core.d.ts +5 -5
  14. package/dist/harbor/core.js +73 -72
  15. package/dist/harbor/index.d.ts +3 -1
  16. package/dist/harbor/index.js +3 -0
  17. package/dist/harbor/memory.d.ts +8 -4
  18. package/dist/harbor/memory.js +43 -11
  19. package/dist/harbor/store.js +1 -1
  20. package/dist/vector/cases.d.ts +4 -3
  21. package/dist/vector/cases.js +33 -5
  22. package/dist/vector/world.d.ts +1 -0
  23. package/dist/vector/world.js +17 -6
  24. package/dist/ward/ground.d.ts +13 -7
  25. package/dist/ward/ground.js +29 -11
  26. package/dist/ward/heirs.js +4 -2
  27. package/dist/ward/index.d.ts +2 -1
  28. package/dist/ward/index.js +1 -0
  29. package/dist/ward/partition.js +2 -2
  30. package/dist/ward/seal.js +1 -1
  31. package/dist/ward/stance.d.ts +0 -5
  32. package/dist/ward/ward.d.ts +1 -1
  33. package/dist/ward/ward.js +62 -27
  34. package/package.json +7 -5
  35. package/src/being/index.ts +1 -0
  36. package/src/being/lent.ts +66 -0
  37. package/src/being/types.ts +1 -1
  38. package/src/conformance/beings.ts +8 -3
  39. package/src/conformance/index.ts +18 -15
  40. package/src/harbor/box.ts +131 -0
  41. package/src/harbor/core.ts +23 -22
  42. package/src/harbor/index.ts +4 -1
  43. package/src/harbor/memory.ts +52 -12
  44. package/src/harbor/store.ts +1 -1
  45. package/src/vector/cases.ts +37 -9
  46. package/src/vector/world.ts +18 -6
  47. package/src/ward/ground.ts +85 -103
  48. package/src/ward/heirs.ts +4 -2
  49. package/src/ward/index.ts +2 -1
  50. package/src/ward/partition.ts +2 -2
  51. package/src/ward/seal.ts +1 -1
  52. package/src/ward/stance.ts +1 -1
  53. package/src/ward/ward.ts +59 -27
package/src/ward/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // nervur/ward — one function. Every ward is the same ward.
3
3
  export { Ward } from './ward.ts';
4
- export type { Ground, WardPointers, Lend } from './ground.ts';
4
+ export type { Ground, WardPointers, Memory } from './ground.ts';
5
+ export { BOX, OFFER, RETRACT, volatile } from './ground.ts';
5
6
  export type { Partition, Heir, Bind, StandingKeys } from './partition.ts';
6
7
  // The three bounds on what a partition may hold, for a kit that must keep
7
8
  // the same ones and for a test that pins them.
@@ -312,7 +312,7 @@ export const restoreRow = (p: Partition, row: string, value: Record<string, unkn
312
312
  drop(p.beings ?? {}, row);
313
313
  // Her bind goes with her: a being the store never kept has no relation
314
314
  // anybody is standing on, so there is nothing to leave forward.
315
- drop((p.bind ?? {}) as unknown as Record<string, unknown>, row);
315
+ drop(p.bind ?? {}, row);
316
316
  return;
317
317
  }
318
318
  // Behind the guard and never through it: a being's cells in a partition
@@ -326,7 +326,7 @@ export const restoreRow = (p: Partition, row: string, value: Record<string, unkn
326
326
  for (const k of Object.keys(held)) drop(held, k);
327
327
  for (const [k, v] of Object.entries(kept)) put(held, k, v);
328
328
  };
329
- into((p.beings ?? {}) as unknown as Record<string, unknown>, (value.cells ?? emptyCells()) as unknown as Record<string, unknown>);
329
+ into(p.beings ?? {}, (value.cells ?? emptyCells()) as unknown as Record<string, unknown>);
330
330
  };
331
331
 
332
332
  // The partition back from its rows. A head that is absent is a partition
package/src/ward/seal.ts CHANGED
@@ -54,7 +54,7 @@ export const SIZE = 1024 * 1024;
54
54
  // designs and no separation at all: one secret would be doing two jobs with
55
55
  // nothing said about it, and a second kit would have to reproduce a
56
56
  // construction nobody named. HKDF-SHA-256 under a label is the separation
57
- // said out loud, and it is what `protocol/vectors/framing.json` pins.
57
+ // said out loud, and it is what `quo/vectors/framing.json` pins.
58
58
  //
59
59
  // Bytes are key material and text is not. A seed handed in as bytes of the
60
60
  // key length is taken as it stands, which is what a harbor mints; anything
@@ -41,7 +41,7 @@ export type Inside = {
41
41
  };
42
42
 
43
43
  // An invitation as it travels: the far ward's pk, the heir pk, the heir's secret seed. Or just the ward, for its public being.
44
- export type SealedInvitation = { ward: string; heir?: string; secret?: string };
44
+ type SealedInvitation ={ ward: string; heir?: string; secret?: string };
45
45
 
46
46
  // The three calls on `standings` share the object with the ids she takes, so
47
47
  // a standing named after one of them would be unreachable: `standings.knock`
package/src/ward/ward.ts CHANGED
@@ -5,9 +5,9 @@
5
5
  // a stance built by the same code. Mints every key, seals every ask that
6
6
  // leaves, opens every one that arrives.
7
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';
8
+ import { OWNER, isInvitation } from '../being/types.ts';
9
+ import type { Asker, BeingLike, Cells, Invitation, JsonObject, Reply, Stance, Wanted, Word } from '../being/types.ts';
10
+ import { BOX, OFFER, RETRACT, type Ground, type WardPointers } from './ground.ts';
11
11
  import { at, drop, open, put, emptyBind, emptyCells, HEAD, MINTED, type Bind, type Partition, type StandingKeys } from './partition.ts';
12
12
  import { Heirs } from './heirs.ts';
13
13
  import { makeDoor, type Door } from './door.ts';
@@ -41,15 +41,48 @@ class Self implements BeingLike {
41
41
  readonly door: WardPointers['door'];
42
42
  readonly ask: WardPointers['ask'];
43
43
 
44
+ // Birth, in order: the ward as its own first being, the box taken, and
45
+ // only then every being the partition names constructed again. A being
46
+ // who takes what her box lends does it at her birth, so the box must be
47
+ // hers to ask before she is born, or a restart would be the one birth at
48
+ // which the device lends nothing.
44
49
  static async boot(ground: Ground): Promise<Self> {
45
- return new Self(ground, await wardKey(ground.seed));
50
+ const w = new Self(ground, await wardKey(ground.seed));
51
+ await w.#takeBox();
52
+ w.#reborn();
53
+ return w;
54
+ }
55
+
56
+ // The device, taken as one standing under `BOX` by the ward's own being,
57
+ // once: a ward woken from its partition already holds it. A knock that is
58
+ // not answered leaves the ward up and lending nothing this run.
59
+ async #takeBox(): Promise<void> {
60
+ const inv = this.g.box;
61
+ const mine = this.doors.get(this.pk)?.stance;
62
+ if (!inv || !mine || mine.standings[BOX]) return;
63
+ const out = await mine.standings.knock(inv);
64
+ if (isSilence(out) || isWord(out)) return;
65
+ await mine.standings.take(BOX, inv);
66
+ }
67
+
68
+ // What this device lends this ward's beings: the box's being, asked to
69
+ // offer a name, answers the lent being's own invitation, and the taker knocks
70
+ // and takes it. The ward reads nothing in the value but that it is one.
71
+ async #lend(name: string, take: (inv: Invitation) => Promise<boolean>): Promise<boolean> {
72
+ const box = this.doors.get(this.pk)?.stance.standings[BOX];
73
+ if (!box) return false;
74
+ const inv = await box.ask(OFFER, { name });
75
+ if (!isInvitation(inv)) return false;
76
+ const took = await take(inv);
77
+ if (!took) await box.ask(RETRACT, { name, heir: inv.heir ?? null });
78
+ return took;
46
79
  }
47
80
 
48
81
  private constructor(ground: Ground, key: WardKey) {
49
82
  this.g = ground;
50
83
  this.key = key;
51
84
  this.pk = key.pk;
52
- this.p = open(ground.memory); // the partition: the ward's own state, and every being's row
85
+ this.p = open(ground.memory.rows); // the partition: the ward's own state, and every being's row
53
86
  // Opening is itself a write: a memory nobody has written comes back with
54
87
  // a version stamped on it and the ward's own tables under it. A ward that
55
88
  // said nothing here would have every being of it kept and the head that
@@ -66,6 +99,23 @@ class Self implements BeingLike {
66
99
  () => this.#keep(),
67
100
  ); // pointer one
68
101
  this.#boot(this.pk, () => this); // the ward's ward is itself
102
+ // pointer two. the owner. the same answer every named asker gets, asker filled in as owner.
103
+ this.ask = async (method, args = {}) => {
104
+ try {
105
+ const out = await this.answer({ id: OWNER }, method, args as JsonObject);
106
+ // The same question the door asks, in the same place: after she has
107
+ // answered and before the owner holds the answer. Silence is what a
108
+ // failed ask says here, so a store that refused is silence, and the
109
+ // owner reads it as it reads every other ask that did not take.
110
+ if (!(await this.#keep().catch(() => false))) return silence;
111
+ return isSilence(out) || isWord(out) ? silence : out;
112
+ } catch {
113
+ return silence;
114
+ }
115
+ };
116
+ }
117
+
118
+ #reborn(): void {
69
119
  // a restart is silent: every being in the cells is constructed again, unasked.
70
120
  // A throw on a boot the owner asked for is that boot's error, and nothing
71
121
  // half-lives. This boot nobody asked for, and there is nobody to hand an
@@ -85,20 +135,6 @@ class Self implements BeingLike {
85
135
  /* she is not here this run. the ward is, and so is everyone else. */
86
136
  }
87
137
  }
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
138
  }
103
139
 
104
140
  // ---- the ward as a being. asked only by its owners: the root, through
@@ -138,7 +174,7 @@ class Self implements BeingLike {
138
174
  // because a harbor told only that something moved must keep all of it, and
139
175
  // a being's row is the one part of a partition that grows without limit.
140
176
  #wrote(row: string): void {
141
- this.g.wrote?.(row);
177
+ this.g.memory.told(row);
142
178
  }
143
179
 
144
180
  // Whether what was written is kept, asked once at the end of an arrival
@@ -146,7 +182,7 @@ class Self implements BeingLike {
146
182
  // refuse anything, so its silence here is yes: a ward driven with no store
147
183
  // under it answers exactly as it did before a harbor could say no.
148
184
  async #keep(): Promise<boolean> {
149
- return this.g.keep ? await this.g.keep() : true;
185
+ return this.g.memory.kept();
150
186
  }
151
187
 
152
188
  // The rows no door holds this run: beings whose class threw at birth or
@@ -249,11 +285,7 @@ class Self implements BeingLike {
249
285
  // never came about takes her out again.
250
286
  relate: async (k, id) => (await this.doors.get(k)?.stance.occupants.invite(id)) ?? null,
251
287
  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,
288
+ lend: (name, take) => this.#lend(name, take),
257
289
  wrote: () => this.#wrote(key),
258
290
  },
259
291
  key,
@@ -294,7 +326,7 @@ class Self implements BeingLike {
294
326
  // the reply opens, and whatever the being writes on either side of her
295
327
  // own ask. A harbor may write those rows down; what it may not do is put
296
328
  // the ward back to them.
297
- const lower = this.g.calling?.();
329
+ const lower = this.g.memory.during();
298
330
  try {
299
331
  return await this.#sent(bind, keys, method, args, wanted, row);
300
332
  } finally {