nervur 0.20.1 → 0.20.2

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 (131) hide show
  1. package/README.md +130 -8
  2. package/dist/browser/index.d.ts +35 -0
  3. package/dist/browser/index.js +188 -0
  4. package/dist/browser/worker.d.ts +12 -0
  5. package/dist/browser/worker.js +40 -0
  6. package/dist/browser.d.ts +1 -0
  7. package/dist/browser.js +7 -0
  8. package/dist/cli/command.d.ts +1 -1
  9. package/dist/cli/command.js +59 -16
  10. package/dist/cli/harbor.d.ts +9 -11
  11. package/dist/cli/harbor.js +81 -123
  12. package/dist/contract/index.d.ts +2 -1
  13. package/dist/contract/index.js +3 -7
  14. package/dist/edge/index.d.ts +57 -0
  15. package/dist/edge/index.js +306 -0
  16. package/dist/edge.d.ts +1 -0
  17. package/dist/edge.js +8 -0
  18. package/dist/folder/index.d.ts +4 -0
  19. package/dist/folder/index.js +57 -14
  20. package/dist/harbor/carrying.d.ts +17 -0
  21. package/dist/harbor/carrying.js +38 -0
  22. package/dist/harbor/catalogue.d.ts +2 -0
  23. package/dist/harbor/catalogue.js +28 -20
  24. package/dist/harbor/dock.d.ts +22 -3
  25. package/dist/harbor/dock.js +69 -13
  26. package/dist/harbor/harbor.d.ts +14 -4
  27. package/dist/harbor/harbor.js +144 -15
  28. package/dist/harbor/index.d.ts +6 -3
  29. package/dist/harbor/index.js +4 -1
  30. package/dist/harbor/package.d.ts +2 -0
  31. package/dist/harbor/package.js +15 -0
  32. package/dist/harbor/probe.d.ts +18 -0
  33. package/dist/harbor/probe.js +15 -0
  34. package/dist/harbor/registry.d.ts +1 -0
  35. package/dist/harbor/registry.js +11 -0
  36. package/dist/harbor/relay.d.ts +15 -0
  37. package/dist/harbor/relay.js +387 -0
  38. package/dist/harbor/root-line.js +4 -2
  39. package/dist/harbor/terrain.d.ts +12 -0
  40. package/dist/harbor/terrain.js +19 -0
  41. package/dist/http/index.d.ts +4 -0
  42. package/dist/http/index.js +156 -0
  43. package/dist/http/websocket.d.ts +29 -0
  44. package/dist/http/websocket.js +133 -0
  45. package/dist/index.d.ts +2 -1
  46. package/dist/index.js +8 -2
  47. package/dist/line/answer.d.ts +8 -0
  48. package/dist/line/answer.js +60 -0
  49. package/dist/line/frame.d.ts +34 -0
  50. package/dist/line/frame.js +81 -0
  51. package/dist/line/ground.d.ts +3 -0
  52. package/dist/line/ground.js +9 -0
  53. package/dist/line/index.d.ts +5 -0
  54. package/dist/line/index.js +9 -0
  55. package/dist/line/push.d.ts +7 -0
  56. package/dist/line/push.js +92 -0
  57. package/dist/line/web.d.ts +30 -0
  58. package/dist/line/web.js +181 -0
  59. package/dist/node/index.d.ts +32 -0
  60. package/dist/node/index.js +164 -0
  61. package/dist/node.d.ts +1 -0
  62. package/dist/node.js +7 -0
  63. package/dist/pointer/bodies.d.ts +2 -0
  64. package/dist/pointer/bodies.js +10 -0
  65. package/dist/pointer/world.d.ts +7 -1
  66. package/dist/pointer/world.js +6 -0
  67. package/dist/quo/address.d.ts +19 -0
  68. package/dist/quo/address.js +84 -0
  69. package/dist/quo/door.d.ts +1 -1
  70. package/dist/quo/door.js +4 -2
  71. package/dist/quo/index.d.ts +1 -0
  72. package/dist/quo/index.js +1 -0
  73. package/dist/quo/invitation.d.ts +1 -0
  74. package/dist/quo/invitation.js +9 -5
  75. package/dist/tcp/frame.d.ts +0 -4
  76. package/dist/tcp/frame.js +0 -10
  77. package/dist/tcp/index.d.ts +8 -2
  78. package/dist/tcp/index.js +123 -27
  79. package/dist/ward/index.d.ts +2 -2
  80. package/dist/ward/index.js +1 -1
  81. package/dist/ward/stance.d.ts +2 -0
  82. package/dist/ward/stance.js +3 -1
  83. package/dist/ward/ward-being.d.ts +2 -1
  84. package/dist/ward/ward-being.js +1 -1
  85. package/dist/ward/ward.d.ts +7 -0
  86. package/dist/ward/ward.js +6 -0
  87. package/package.json +21 -2
  88. package/src/browser/index.ts +207 -0
  89. package/src/browser/worker.ts +66 -0
  90. package/src/browser.ts +9 -0
  91. package/src/cli/command.ts +53 -17
  92. package/src/cli/harbor.ts +82 -116
  93. package/src/contract/index.ts +11 -8
  94. package/src/edge/index.ts +359 -0
  95. package/src/edge.ts +10 -0
  96. package/src/folder/index.ts +57 -12
  97. package/src/harbor/carrying.ts +56 -0
  98. package/src/harbor/catalogue.ts +32 -17
  99. package/src/harbor/dock.ts +88 -17
  100. package/src/harbor/harbor.ts +146 -17
  101. package/src/harbor/index.ts +6 -3
  102. package/src/harbor/package.ts +16 -0
  103. package/src/harbor/probe.ts +34 -0
  104. package/src/harbor/registry.ts +11 -0
  105. package/src/harbor/relay.ts +399 -0
  106. package/src/harbor/root-line.ts +4 -1
  107. package/src/harbor/terrain.ts +28 -2
  108. package/src/http/index.ts +150 -0
  109. package/src/http/websocket.ts +124 -0
  110. package/src/index.ts +35 -2
  111. package/src/line/answer.ts +59 -0
  112. package/src/line/frame.ts +88 -0
  113. package/src/line/ground.ts +19 -0
  114. package/src/line/index.ts +9 -0
  115. package/src/line/push.ts +112 -0
  116. package/src/line/web.ts +227 -0
  117. package/src/node/index.ts +170 -0
  118. package/src/node.ts +9 -0
  119. package/src/pointer/bodies.ts +11 -0
  120. package/src/pointer/world.ts +18 -2
  121. package/src/quo/address.ts +87 -0
  122. package/src/quo/door.ts +4 -2
  123. package/src/quo/index.ts +1 -0
  124. package/src/quo/invitation.ts +10 -6
  125. package/src/stand/stand.ts +60 -21
  126. package/src/tcp/index.ts +142 -31
  127. package/src/ward/index.ts +2 -2
  128. package/src/ward/stance.ts +6 -1
  129. package/src/ward/ward-being.ts +2 -2
  130. package/src/ward/ward.ts +16 -0
  131. package/src/tcp/frame.ts +0 -78
@@ -1,37 +1,96 @@
1
1
  import { Carrier } from '../contract/index.js';
2
2
  import { hex, isHex, unhex } from '../crypto/index.js';
3
- import { WardKey } from '../quo/index.js';
3
+ import { unspecified, WardKey } from '../quo/index.js';
4
4
  import { DOCK, Pilot, Ward } from '../ward/index.js';
5
5
  import { Catalogue, CATALOGUE, catalogueFor } from './catalogue.js';
6
+ import { CarrierFaculty, UNDIALED } from './carrying.js';
6
7
  import { BOX, Dock } from './dock.js';
7
8
  import { Package } from './package.js';
8
9
  import { Registry } from './registry.js';
10
+ import { relayClient, relayFaculty } from './relay.js';
11
+ import { probeGround } from './probe.js';
12
+ import { Terrain } from './terrain.js';
9
13
  export class Harbor extends Carrier {
10
14
  terrain;
11
15
  #package;
12
16
  #wards = new Map();
13
17
  #kit;
18
+ #grounds;
14
19
  #registry;
20
+ #failed = { wards: {}, routes: {} };
15
21
  constructor(terrain, pack) {
16
22
  super();
17
23
  this.terrain = terrain;
18
24
  this.#package = pack;
19
- this.#kit = [catalogueFor(this), Pilot];
25
+ const faculties = [...terrain.faculties.map((make) => make(this)), relayFaculty(terrain.clock), relayClient(this, terrain.clock)];
26
+ this.#grounds = new Set(faculties.map((C) => C.kind));
27
+ this.#kit = [catalogueFor(this), Pilot, ...faculties];
20
28
  this.#registry = new Registry(this.#kit, []);
21
29
  }
22
- static async open(terrain) {
30
+ // A harbor on the terrain handed in, or, with none, on the ground the
31
+ // probe finds here. A ground another run holds refuses, and what the
32
+ // ground serves beside the harbor stands once it is whole.
33
+ static async open(from = {}) {
34
+ const terrain = from instanceof Terrain ? from : probeGround(from).terrain;
35
+ await terrain.claim();
36
+ let harbor;
37
+ try {
38
+ harbor = await Harbor.#stand(terrain);
39
+ }
40
+ catch (e) {
41
+ await terrain.release();
42
+ throw e;
43
+ }
44
+ try {
45
+ await terrain.stand(harbor);
46
+ }
47
+ catch (e) {
48
+ await harbor.close();
49
+ throw e;
50
+ }
51
+ return harbor;
52
+ }
53
+ static async #stand(terrain) {
23
54
  const seed = await terrain.custody.seed();
24
55
  const harbor = new Harbor(terrain, await Package.open(terrain.memory, terrain.entropy, seed));
25
56
  await harbor.#unpack(BOX, seed, undefined, (stance) => new Dock(stance, harbor));
26
- if (!harbor.box.partition.being(CATALOGUE))
57
+ const genesis = !harbor.box.partition.being(CATALOGUE);
58
+ if (genesis)
27
59
  await harbor.dock.standCatalogue();
28
60
  await harbor.catalogue.wake();
29
- for (const [pk, at] of Object.entries(harbor.dock.routes))
30
- terrain.carrier.route(pk, at);
31
- for (const hosted of harbor.dock.hosted)
32
- await harbor.#unpack(hosted.ward, unhex(hosted.seed));
61
+ if (genesis)
62
+ await harbor.dock.standCarriers(terrain.opens);
63
+ else if (terrain.wakes)
64
+ for (const carrier of harbor.carriers)
65
+ await carrier.awake();
66
+ for (const [pk, at] of Object.entries(harbor.dock.routes)) {
67
+ if (!harbor.#dialable(at))
68
+ harbor.#failed.routes[pk] = 'no carrier dials it';
69
+ }
70
+ for (const hosted of harbor.dock.hosted) {
71
+ try {
72
+ await harbor.#unpack(hosted.ward, unhex(hosted.seed));
73
+ }
74
+ catch (e) {
75
+ harbor.#failed.wards[hosted.ward] = e instanceof Error ? e.message : String(e);
76
+ }
77
+ }
33
78
  return harbor;
34
79
  }
80
+ // Hosting: every place of the terrain's memory the dock does not name,
81
+ // gone. A crash inside `host` leaves such a place, sealed under a seed
82
+ // nothing kept, and nothing else writes one.
83
+ async sweep() {
84
+ const mine = new Set(await Promise.all([BOX, ...this.dock.hosted.map((h) => h.ward)].map((name) => this.#package.place(name))));
85
+ const swept = (await this.terrain.memory.places()).filter((place) => !mine.has(place));
86
+ for (const place of swept)
87
+ await this.terrain.memory.forget(place);
88
+ return swept;
89
+ }
90
+ // Hosting: what did not stand when the harbor opened, each alone.
91
+ failures() {
92
+ return { wards: { ...this.#failed.wards }, routes: { ...this.#failed.routes } };
93
+ }
35
94
  get box() {
36
95
  return this.#wards.get(BOX);
37
96
  }
@@ -57,14 +116,30 @@ export class Harbor extends Carrier {
57
116
  holds(pk) {
58
117
  return this.#door(pk) !== undefined;
59
118
  }
60
- // Bytes for one of this harbor's doors, or nothing.
119
+ // Bytes for one of this harbor's doors, or for a ward a relay of this
120
+ // harbor answers for, or nothing.
61
121
  async arrive(pk, bytes) {
62
122
  const ward = this.#door(pk);
63
- return ward ? (await ward.door.arrive(bytes)).bytes : null;
123
+ if (ward)
124
+ return (await ward.door.arrive(bytes)).bytes;
125
+ return (await this.#relaying(pk)?.relay(pk, bytes)) ?? null;
64
126
  }
65
- // Carrier: this harbor's own doors first, then the terrain's.
66
- carry(pk, bytes) {
67
- return this.holds(pk) ? this.arrive(pk, bytes) : this.terrain.carrier.carry(pk, bytes);
127
+ // Carrier: this harbor's own doors and relays first, then a route's
128
+ // addresses in order, each to the carrier that dials it, and with no
129
+ // route the terrain's carrier.
130
+ async carry(pk, bytes) {
131
+ if (this.holds(pk) || this.#relaying(pk))
132
+ return this.arrive(pk, bytes);
133
+ const at = this.dock.routes[pk];
134
+ if (at === undefined)
135
+ return this.terrain.carrier.carry(pk, bytes);
136
+ for (const address of at) {
137
+ const carrier = this.carriers.find((c) => c.accepts(address));
138
+ const out = carrier ? await carrier.dial(address, pk, bytes) : UNDIALED;
139
+ if (out !== UNDIALED)
140
+ return out;
141
+ }
142
+ return null;
68
143
  }
69
144
  // Resolving: a kind, by the registry the catalogue runs.
70
145
  classOf(kind) {
@@ -74,6 +149,9 @@ export class Harbor extends Carrier {
74
149
  load(from) {
75
150
  return this.terrain.loader.load(from);
76
151
  }
152
+ check(modules) {
153
+ return Registry.refusal(this.#kit, modules);
154
+ }
77
155
  run(modules) {
78
156
  try {
79
157
  this.#registry = new Registry(this.#kit, modules);
@@ -119,9 +197,46 @@ export class Harbor extends Carrier {
119
197
  keep() {
120
198
  return this.box.partition.kept();
121
199
  }
122
- // Hosting: the terrain's carrier takes a route, or does not dial.
200
+ // Hosting: a route is taken where a carrier of this harbor dials one of
201
+ // its addresses; forgetting one always is.
123
202
  route(pk, at) {
124
- return this.terrain.carrier.route(pk, at);
203
+ const taken = isHex(pk, 64) && (at === null || this.#dialable(at));
204
+ if (taken)
205
+ delete this.#failed.routes[pk];
206
+ return taken;
207
+ }
208
+ // Hosting: the carriers this ground ships, and their sleep and waking.
209
+ grounds(kind) {
210
+ return this.#grounds.has(kind);
211
+ }
212
+ async awaken(key) {
213
+ const being = this.box.being(key);
214
+ if (being instanceof CarrierFaculty)
215
+ await being.awake();
216
+ }
217
+ async lull(key) {
218
+ const being = this.box.being(key);
219
+ if (being instanceof CarrierFaculty)
220
+ await being.sleep();
221
+ }
222
+ // The carrier faculties standing in the box ward.
223
+ get carriers() {
224
+ return Object.keys(this.box.partition.beings)
225
+ .map((key) => this.box.being(key))
226
+ .filter((being) => being instanceof CarrierFaculty);
227
+ }
228
+ // Every carrier asleep, the last opened first: nothing listens and no
229
+ // line stays open.
230
+ async close() {
231
+ for (const carrier of this.carriers.reverse())
232
+ await carrier.sleep();
233
+ await this.terrain.release();
234
+ }
235
+ #relaying(pk) {
236
+ return this.carriers.find((carrier) => carrier.relays(pk));
237
+ }
238
+ #dialable(at) {
239
+ return at.some((address) => this.carriers.some((carrier) => carrier.accepts(address)));
125
240
  }
126
241
  async #unpack(name, seed, dock, root) {
127
242
  const { entropy, clock } = this.terrain;
@@ -132,11 +247,25 @@ export class Harbor extends Carrier {
132
247
  entropy,
133
248
  clock,
134
249
  carrier: this,
250
+ routes: this.#routes,
135
251
  ...(dock ? { dock } : {}),
136
252
  ...(root ? { root } : {}),
137
253
  });
138
254
  this.#wards.set(name, ward);
139
255
  }
256
+ // Every ward is reached where the harbor is: where the root's `reach`
257
+ // says, and where it said nothing, where its carriers listen, save the
258
+ // unspecified host. What a ward learns from an invitation's `at` is the
259
+ // dock's to keep. A ward of this harbor needs no route.
260
+ #routes = {
261
+ reach: () => {
262
+ if (!this.#wards.has(BOX))
263
+ return [];
264
+ const set = this.dock.addresses;
265
+ return set.length > 0 ? set : this.carriers.flatMap((carrier) => carrier.listening()).filter((address) => !unspecified(address));
266
+ },
267
+ learn: (ward, at) => (this.holds(ward) ? Promise.resolve() : this.dock.learn(ward, at)),
268
+ };
140
269
  #door(pk) {
141
270
  for (const ward of this.#wards.values())
142
271
  if (ward.pk === pk)
@@ -1,7 +1,10 @@
1
1
  export { Catalogue, CATALOGUE, type Cataloguing, type ModuleRow } from './catalogue.ts';
2
- export { BOX, Dock, type Hosted, type Hosting } from './dock.ts';
2
+ export { BOX, Dock, type Failures, type Hosted, type Hosting } from './dock.ts';
3
+ export { CarrierFaculty, UNDIALED, type Dialed, type Ground, type GroundFaculty } from './carrying.ts';
3
4
  export { Harbor } from './harbor.ts';
4
- export { Package } from './package.ts';
5
+ export { copyPackage, Package } from './package.ts';
5
6
  export { readRootRequest, rootLine, type RootRequest } from './root-line.ts';
7
+ export { addGround, grounds, probeGround, type GroundProbe, type Probing } from './probe.ts';
6
8
  export { Registry } from './registry.ts';
7
- export { Terrain } from './terrain.ts';
9
+ export { Push, PUSH, RELAY, RELAY_CLIENT, RELAY_HOLD, RELAY_REST, RELAY_WAIT, RELAYED } from './relay.ts';
10
+ export { Terrain, type Opening } from './terrain.ts';
@@ -3,8 +3,11 @@
3
3
  // registry, and the harbor.
4
4
  export { Catalogue, CATALOGUE } from './catalogue.js';
5
5
  export { BOX, Dock } from './dock.js';
6
+ export { CarrierFaculty, UNDIALED } from './carrying.js';
6
7
  export { Harbor } from './harbor.js';
7
- export { Package } from './package.js';
8
+ export { copyPackage, Package } from './package.js';
8
9
  export { readRootRequest, rootLine } from './root-line.js';
10
+ export { addGround, grounds, probeGround } from './probe.js';
9
11
  export { Registry } from './registry.js';
12
+ export { Push, PUSH, RELAY, RELAY_CLIENT, RELAY_HOLD, RELAY_REST, RELAY_WAIT, RELAYED } from './relay.js';
10
13
  export { Terrain } from './terrain.js';
@@ -1,4 +1,5 @@
1
1
  import type { Entropy, Memory, Rows } from '../contract/index.ts';
2
+ export declare const copyPackage: (from: Memory, to: Memory) => Promise<string[]>;
2
3
  export declare class Package {
3
4
  #private;
4
5
  readonly memory: Memory;
@@ -6,5 +7,6 @@ export declare class Package {
6
7
  private constructor();
7
8
  static open(memory: Memory, entropy: Entropy, seed: Uint8Array): Promise<Package>;
8
9
  derive(info: string[], length: number): Promise<Uint8Array>;
10
+ place(name: string): Promise<string>;
9
11
  rows(name: string): Promise<Rows>;
10
12
  }
@@ -15,6 +15,17 @@
15
15
  // bytes the package did not seal.
16
16
  import { concat, decrypt, encrypt, hex, hkdf, utf8 } from '../crypto/index.js';
17
17
  import { HEAD_ROW } from '../ward/index.js';
18
+ // Every place of one memory, written into another, and the places
19
+ // carried. The bytes are the package's own, sealed under a seed neither
20
+ // memory holds, so a harbor opened on the second memory under that seed
21
+ // stands what the first one stood. A place the second memory already
22
+ // holds is written over.
23
+ export const copyPackage = async (from, to) => {
24
+ const places = await from.places();
25
+ for (const place of places)
26
+ await to.write(place, await from.read(place));
27
+ return places;
28
+ };
18
29
  const NONCE = 12;
19
30
  const LABEL = utf8('nervur-package');
20
31
  class PackageRows {
@@ -94,6 +105,10 @@ export class Package {
94
105
  derive(info, length) {
95
106
  return hkdf(this.#key, utf8(JSON.stringify(info)), length);
96
107
  }
108
+ // The place a ward's rows live in.
109
+ place(name) {
110
+ return this.derive(['place', name], 16).then(hex);
111
+ }
97
112
  // One ward's rows, as the package kept them.
98
113
  async rows(name) {
99
114
  const ward = new Ward(this, name, await this.derive(['seal', name], 32), hex(await this.derive(['place', name], 16)));
@@ -0,0 +1,18 @@
1
+ import type { Module } from '../contract/index.ts';
2
+ import type { Terrain } from './terrain.ts';
3
+ export type Probing = {
4
+ readonly modules?: readonly Module[];
5
+ readonly where?: string;
6
+ readonly given?: unknown;
7
+ };
8
+ export type GroundProbe = {
9
+ readonly name: string;
10
+ fits(probing: Probing): boolean;
11
+ terrain(probing: Probing): Terrain;
12
+ };
13
+ export declare const addGround: (probe: GroundProbe) => void;
14
+ export declare const grounds: () => string[];
15
+ export declare const probeGround: (probing: Probing, probes?: readonly GroundProbe[]) => {
16
+ ground: string;
17
+ terrain: Terrain;
18
+ };
@@ -0,0 +1,15 @@
1
+ const known = [];
2
+ // A ground this process can stand on, known once by its name.
3
+ export const addGround = (probe) => {
4
+ if (!known.some((k) => k.name === probe.name))
5
+ known.push(probe);
6
+ };
7
+ // The grounds known, in the order they are tried.
8
+ export const grounds = () => known.map((k) => k.name).reverse();
9
+ // The terrain of the first ground that fits, and its name.
10
+ export const probeGround = (probing, probes = known) => {
11
+ for (const probe of [...probes].reverse())
12
+ if (probe.fits(probing))
13
+ return { ground: probe.name, terrain: probe.terrain(probing) };
14
+ throw new Error('no ground fits here');
15
+ };
@@ -4,6 +4,7 @@ import type { Catalogue } from '../ward/index.ts';
4
4
  export declare class Registry implements Catalogue {
5
5
  #private;
6
6
  constructor(kit: readonly BeingClass[], modules: readonly Module[]);
7
+ static refusal(kit: readonly BeingClass[], modules: readonly Module[]): string | null;
7
8
  classOf(kind: string): BeingClass | undefined;
8
9
  kindsOf(module: string): string[];
9
10
  }
@@ -68,6 +68,17 @@ export class Registry {
68
68
  throw new Error(`kind ${kind} is a class and a contract`);
69
69
  }
70
70
  }
71
+ // Why a registry of these modules would be refused, or null.
72
+ static refusal(kit, modules) {
73
+ try {
74
+ const registry = new Registry(kit, modules);
75
+ void registry;
76
+ return null;
77
+ }
78
+ catch (e) {
79
+ return e.message;
80
+ }
81
+ }
71
82
  classOf(kind) {
72
83
  return this.#classes.get(kind);
73
84
  }
@@ -0,0 +1,15 @@
1
+ import { Faculty, type BeingClass } from '../being/index.ts';
2
+ import type { Clock } from '../contract/index.ts';
3
+ import { type Ground } from './carrying.ts';
4
+ export declare const RELAY = "org.nervur.relay";
5
+ export declare const RELAY_CLIENT = "org.nervur.relay-client";
6
+ export declare const PUSH = "org.nervur.push";
7
+ export declare const RELAY_WAIT = 30000;
8
+ export declare const RELAY_HOLD = 20000;
9
+ export declare const RELAY_REST = 1000;
10
+ export declare const RELAYED = 262144;
11
+ export declare abstract class Push extends Faculty {
12
+ static readonly kind: string;
13
+ }
14
+ export declare const relayFaculty: (clock: Clock) => BeingClass;
15
+ export declare const relayClient: (ground: Ground, clock: Clock) => BeingClass;