nervur 0.19.2 → 0.20.1
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.
- package/README.md +156 -104
- package/dist/being/being.d.ts +8 -15
- package/dist/being/being.js +20 -67
- package/dist/being/digest.d.ts +1 -1
- package/dist/being/digest.js +7 -27
- package/dist/being/faculty.d.ts +10 -0
- package/dist/being/faculty.js +74 -0
- package/dist/being/index.d.ts +5 -5
- package/dist/being/index.js +6 -5
- package/dist/being/kind.d.ts +4 -0
- package/dist/being/kind.js +28 -0
- package/dist/being/types.d.ts +35 -63
- package/dist/being/types.js +3 -57
- package/dist/being/words.d.ts +13 -0
- package/dist/being/words.js +14 -0
- package/dist/cli/command.d.ts +13 -0
- package/dist/cli/command.js +194 -0
- package/dist/cli/harbor.d.ts +21 -0
- package/dist/cli/harbor.js +141 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +14 -0
- package/dist/contract/index.d.ts +39 -0
- package/dist/contract/index.js +33 -0
- package/dist/crypto/aes.d.ts +3 -0
- package/dist/crypto/aes.js +24 -0
- package/dist/crypto/bytes.d.ts +6 -0
- package/dist/crypto/bytes.js +33 -0
- package/dist/crypto/ed25519.d.ts +3 -0
- package/dist/crypto/ed25519.js +85 -0
- package/dist/crypto/hash.d.ts +2 -0
- package/dist/crypto/hash.js +11 -0
- package/dist/crypto/index.d.ts +7 -0
- package/dist/crypto/index.js +10 -0
- package/dist/crypto/json.d.ts +8 -0
- package/dist/crypto/json.js +250 -0
- package/dist/crypto/mlkem.d.ts +12 -0
- package/dist/crypto/mlkem.js +36 -0
- package/dist/crypto/subtle.d.ts +10 -0
- package/dist/crypto/subtle.js +29 -0
- package/dist/crypto/x25519.d.ts +2 -0
- package/dist/crypto/x25519.js +21 -0
- package/dist/folder/index.d.ts +14 -0
- package/dist/folder/index.js +87 -0
- package/dist/harbor/catalogue.d.ts +25 -0
- package/dist/harbor/catalogue.js +93 -0
- package/dist/harbor/dock.d.ts +29 -0
- package/dist/harbor/dock.js +122 -0
- package/dist/harbor/harbor.d.ts +31 -0
- package/dist/harbor/harbor.js +146 -0
- package/dist/harbor/index.d.ts +7 -10
- package/dist/harbor/index.js +9 -18
- package/dist/harbor/package.d.ts +10 -0
- package/dist/harbor/package.js +115 -0
- package/dist/harbor/registry.d.ts +9 -0
- package/dist/harbor/registry.js +78 -0
- package/dist/harbor/root-line.d.ts +9 -0
- package/dist/harbor/root-line.js +36 -0
- package/dist/harbor/terrain.d.ts +9 -0
- package/dist/harbor/terrain.js +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +14 -0
- package/dist/pointer/bodies.d.ts +26 -0
- package/dist/pointer/bodies.js +64 -0
- package/dist/pointer/index.d.ts +2 -0
- package/dist/pointer/index.js +4 -0
- package/dist/pointer/world.d.ts +35 -0
- package/dist/pointer/world.js +85 -0
- package/dist/quo/door.d.ts +34 -0
- package/dist/quo/door.js +172 -0
- package/dist/quo/index.d.ts +8 -0
- package/dist/quo/index.js +11 -0
- package/dist/quo/invitation.d.ts +7 -0
- package/dist/quo/invitation.js +15 -0
- package/dist/quo/keys.d.ts +40 -0
- package/dist/quo/keys.js +79 -0
- package/dist/quo/payload.d.ts +15 -0
- package/dist/quo/payload.js +55 -0
- package/dist/quo/relations.d.ts +40 -0
- package/dist/quo/relations.js +33 -0
- package/dist/quo/reply.d.ts +13 -0
- package/dist/quo/reply.js +35 -0
- package/dist/quo/seal.d.ts +42 -0
- package/dist/quo/seal.js +78 -0
- package/dist/quo/standing.d.ts +39 -0
- package/dist/quo/standing.js +90 -0
- package/dist/tcp/frame.d.ts +29 -0
- package/dist/tcp/frame.js +74 -0
- package/dist/tcp/index.d.ts +18 -0
- package/dist/tcp/index.js +164 -0
- package/dist/ward/allowance.d.ts +13 -9
- package/dist/ward/allowance.js +28 -67
- package/dist/ward/cells.d.ts +6 -6
- package/dist/ward/cells.js +67 -179
- package/dist/ward/index.d.ts +6 -10
- package/dist/ward/index.js +7 -13
- package/dist/ward/partition.d.ts +44 -60
- package/dist/ward/partition.js +56 -285
- package/dist/ward/pilot.d.ts +8 -0
- package/dist/ward/pilot.js +50 -0
- package/dist/ward/stance.d.ts +27 -20
- package/dist/ward/stance.js +174 -406
- package/dist/ward/ward-being.d.ts +35 -0
- package/dist/ward/ward-being.js +82 -0
- package/dist/ward/ward.d.ts +45 -11
- package/dist/ward/ward.js +239 -357
- package/package.json +24 -31
- package/src/being/being.ts +35 -78
- package/src/being/digest.ts +7 -35
- package/src/being/faculty.ts +73 -0
- package/src/being/index.ts +6 -6
- package/src/being/kind.ts +30 -0
- package/src/being/types.ts +50 -146
- package/src/being/words.ts +31 -0
- package/src/cli/command.ts +188 -0
- package/src/cli/harbor.ts +137 -0
- package/src/cli/main.ts +13 -0
- package/src/contract/index.ts +73 -0
- package/src/crypto/aes.ts +26 -0
- package/src/crypto/bytes.ts +37 -0
- package/src/crypto/ed25519.ts +84 -0
- package/src/crypto/hash.ts +14 -0
- package/src/crypto/index.ts +10 -0
- package/src/crypto/json.ts +241 -0
- package/src/crypto/mlkem.ts +38 -0
- package/src/crypto/subtle.ts +33 -0
- package/src/crypto/x25519.ts +21 -0
- package/src/folder/index.ts +92 -0
- package/src/harbor/catalogue.ts +110 -0
- package/src/harbor/dock.ts +137 -0
- package/src/harbor/harbor.ts +180 -0
- package/src/harbor/index.ts +9 -19
- package/src/harbor/package.ts +131 -0
- package/src/harbor/registry.ts +73 -0
- package/src/harbor/root-line.ts +36 -0
- package/src/harbor/terrain.ts +15 -0
- package/src/index.ts +20 -0
- package/src/pointer/bodies.ts +65 -0
- package/src/pointer/index.ts +4 -0
- package/src/pointer/world.ts +94 -0
- package/src/quo/door.ts +178 -0
- package/src/quo/index.ts +11 -0
- package/src/quo/invitation.ts +15 -0
- package/src/quo/keys.ts +91 -0
- package/src/quo/payload.ts +61 -0
- package/src/quo/relations.ts +62 -0
- package/src/quo/reply.ts +38 -0
- package/src/quo/seal.ts +101 -0
- package/src/quo/standing.ts +111 -0
- package/src/stand/main.ts +20 -0
- package/src/stand/stand.ts +220 -0
- package/src/tcp/frame.ts +78 -0
- package/src/tcp/index.ts +173 -0
- package/src/ward/allowance.ts +37 -75
- package/src/ward/cells.ts +63 -176
- package/src/ward/index.ts +7 -17
- package/src/ward/partition.ts +86 -326
- package/src/ward/pilot.ts +50 -0
- package/src/ward/stance.ts +186 -420
- package/src/ward/ward-being.ts +103 -0
- package/src/ward/ward.ts +256 -359
- package/dist/being/lent.d.ts +0 -32
- package/dist/being/lent.js +0 -72
- package/dist/being/silence.d.ts +0 -12
- package/dist/being/silence.js +0 -41
- package/dist/conformance/assert.d.ts +0 -11
- package/dist/conformance/assert.js +0 -106
- package/dist/conformance/beings.d.ts +0 -199
- package/dist/conformance/beings.js +0 -188
- package/dist/conformance/estate.d.ts +0 -5
- package/dist/conformance/estate.js +0 -388
- package/dist/conformance/index.d.ts +0 -80
- package/dist/conformance/index.js +0 -819
- package/dist/conformance/reach.d.ts +0 -10
- package/dist/conformance/reach.js +0 -72
- package/dist/conformance/store.d.ts +0 -5
- package/dist/conformance/store.js +0 -113
- package/dist/harbor/box.d.ts +0 -121
- package/dist/harbor/box.js +0 -121
- package/dist/harbor/core.d.ts +0 -55
- package/dist/harbor/core.js +0 -662
- package/dist/harbor/dial.d.ts +0 -9
- package/dist/harbor/dial.js +0 -81
- package/dist/harbor/memory.d.ts +0 -29
- package/dist/harbor/memory.js +0 -104
- package/dist/harbor/reach.d.ts +0 -36
- package/dist/harbor/reach.js +0 -199
- package/dist/harbor/store.d.ts +0 -35
- package/dist/harbor/store.js +0 -62
- package/dist/vector/cases.d.ts +0 -42
- package/dist/vector/cases.js +0 -223
- package/dist/vector/index.d.ts +0 -6
- package/dist/vector/index.js +0 -8
- package/dist/vector/stand.d.ts +0 -9
- package/dist/vector/stand.js +0 -77
- package/dist/vector/world.d.ts +0 -144
- package/dist/vector/world.js +0 -209
- package/dist/ward/arithmetic.d.ts +0 -31
- package/dist/ward/arithmetic.js +0 -249
- package/dist/ward/door.d.ts +0 -18
- package/dist/ward/door.js +0 -186
- package/dist/ward/ground.d.ts +0 -29
- package/dist/ward/ground.js +0 -56
- package/dist/ward/heirs.d.ts +0 -13
- package/dist/ward/heirs.js +0 -117
- package/dist/ward/json.d.ts +0 -2
- package/dist/ward/json.js +0 -163
- package/dist/ward/owner.d.ts +0 -13
- package/dist/ward/owner.js +0 -220
- package/dist/ward/seal.d.ts +0 -52
- package/dist/ward/seal.js +0 -150
- package/src/being/lent.ts +0 -72
- package/src/being/silence.ts +0 -46
- package/src/conformance/assert.ts +0 -100
- package/src/conformance/beings.ts +0 -188
- package/src/conformance/estate.ts +0 -412
- package/src/conformance/index.ts +0 -965
- package/src/conformance/reach.ts +0 -83
- package/src/conformance/store.ts +0 -125
- package/src/harbor/box.ts +0 -131
- package/src/harbor/core.ts +0 -699
- package/src/harbor/dial.ts +0 -112
- package/src/harbor/memory.ts +0 -123
- package/src/harbor/reach.ts +0 -221
- package/src/harbor/store.ts +0 -91
- package/src/vector/cases.ts +0 -257
- package/src/vector/index.ts +0 -11
- package/src/vector/stand.ts +0 -76
- package/src/vector/world.ts +0 -232
- package/src/ward/arithmetic.ts +0 -251
- package/src/ward/door.ts +0 -186
- package/src/ward/ground.ts +0 -142
- package/src/ward/heirs.ts +0 -116
- package/src/ward/json.ts +0 -144
- package/src/ward/owner.ts +0 -214
- package/src/ward/seal.ts +0 -178
package/src/ward/ward.ts
CHANGED
|
@@ -1,410 +1,307 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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';
|
|
2
|
+
// The ward: one door, one partition, and the beings behind the door. It
|
|
3
|
+
// unpacks from its seed, its memory and a catalogue of classes, the
|
|
4
|
+
// ward-being first and every other being from her own row, and it stands
|
|
5
|
+
// what is behind its door for `quo/`.
|
|
6
|
+
import { readValue, writeValue } from '../crypto/index.ts';
|
|
7
|
+
import { Door, readInvitation, WardKey, type Behind, type Choice, type Invitation, type Payload } from '../quo/index.ts';
|
|
8
|
+
import type { Carrier, Clock, Entropy, Rows } from '../contract/index.ts';
|
|
9
|
+
import { digest, isSilence, isWord, kindOf, OWNER, silence, WARD, word, type Answer, type Asker, type BeingClass, type BeingLike, type JsonObject, type Reply } from '../being/index.ts';
|
|
10
|
+
import { Partition, PartitionRelations, type BeingRow } from './partition.ts';
|
|
11
|
+
import { DEPTH, WardStance, type Inside } from './stance.ts';
|
|
12
|
+
import { WardBeing, type Steward, type WardBeingStance } from './ward-being.ts';
|
|
20
13
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
// Where a ward finds a class by the kind its row keeps.
|
|
15
|
+
export interface Catalogue {
|
|
16
|
+
classOf(kind: string): BeingClass | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type WardParts = {
|
|
20
|
+
readonly seed: Uint8Array | string;
|
|
21
|
+
readonly memory: Rows;
|
|
22
|
+
readonly catalogue: Catalogue;
|
|
23
|
+
readonly entropy: Entropy;
|
|
24
|
+
readonly clock: Clock;
|
|
25
|
+
readonly carrier: Carrier;
|
|
26
|
+
// The ward-being: `WardBeing`, or a being that extends it.
|
|
27
|
+
readonly root?: (stance: WardBeingStance) => WardBeing;
|
|
28
|
+
// An invitation on the dock, taken by the ward-being under `dock` when
|
|
29
|
+
// she holds no such standing yet.
|
|
30
|
+
readonly dock?: Invitation;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// The standing every hosted ward's ward-being holds on the dock.
|
|
34
|
+
export const DOCK = 'dock';
|
|
35
|
+
|
|
36
|
+
// A ward-being that lends to her own ward's beings, as the dock does.
|
|
37
|
+
export interface Lender {
|
|
38
|
+
offer(contract: string): Promise<Invitation | null>;
|
|
39
|
+
withdraw(contract: string, heir: string): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
const lends = (being: unknown): being is Lender => typeof (being as Partial<Lender>).offer === 'function';
|
|
26
42
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
type Birth = 'booted' | 'key taken' | 'no such class' | 'threw at birth';
|
|
44
|
+
type Resident = { being: BeingLike; row: BeingRow; stance: WardStance };
|
|
45
|
+
|
|
46
|
+
class RootStance extends WardStance implements WardBeingStance {
|
|
47
|
+
readonly steward: Steward;
|
|
48
|
+
constructor(inside: Inside, row: BeingRow, steward: Steward) {
|
|
49
|
+
super(inside, WARD, row);
|
|
50
|
+
this.steward = steward;
|
|
51
|
+
}
|
|
31
52
|
}
|
|
32
53
|
|
|
33
|
-
class
|
|
54
|
+
export class Ward implements Behind, Inside {
|
|
34
55
|
readonly key: WardKey;
|
|
35
|
-
readonly
|
|
36
|
-
readonly
|
|
37
|
-
readonly
|
|
38
|
-
readonly
|
|
39
|
-
readonly
|
|
40
|
-
readonly #birthing = new Set<string>();
|
|
41
|
-
readonly door: WardPointers['door'];
|
|
42
|
-
readonly ask: WardPointers['ask'];
|
|
56
|
+
readonly partition: Partition;
|
|
57
|
+
readonly door: Door;
|
|
58
|
+
readonly clock: Clock;
|
|
59
|
+
readonly #parts: WardParts;
|
|
60
|
+
readonly #residents = new Map<string, Resident>();
|
|
61
|
+
readonly #birthing = new Set<string>();
|
|
43
62
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const w = new Self(ground, await wardKey(ground.seed));
|
|
51
|
-
await w.#takeBox();
|
|
52
|
-
w.#reborn();
|
|
53
|
-
return w;
|
|
63
|
+
private constructor(parts: WardParts, key: WardKey) {
|
|
64
|
+
this.#parts = parts;
|
|
65
|
+
this.key = key;
|
|
66
|
+
this.clock = parts.clock;
|
|
67
|
+
this.partition = new Partition(parts.memory);
|
|
68
|
+
this.door = new Door(key, new PartitionRelations(this.partition), this, parts.entropy.drawer);
|
|
54
69
|
}
|
|
55
70
|
|
|
56
|
-
// The
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
async
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
// The ward-being first, then every being her row names. A class the
|
|
72
|
+
// catalogue lacks, or one that throws, leaves that being absent and her
|
|
73
|
+
// row as it was.
|
|
74
|
+
static async unpack(parts: WardParts): Promise<Ward> {
|
|
75
|
+
const ward = new Ward(parts, await WardKey.from(parts.seed));
|
|
76
|
+
if (!ward.partition.being(WARD)) {
|
|
77
|
+
ward.partition.beings[WARD] = { class: '', cells: {}, occupants: {}, standings: {} };
|
|
78
|
+
ward.partition.told(WARD);
|
|
79
|
+
ward.partition.toldHead();
|
|
80
|
+
}
|
|
81
|
+
const own = ward.partition.being(WARD)!;
|
|
82
|
+
const stance = new RootStance(ward, own, ward.#steward);
|
|
83
|
+
const being = parts.root ? parts.root(stance) : new WardBeing(stance);
|
|
84
|
+
own.class = kindOf(being.constructor);
|
|
85
|
+
ward.#residents.set(WARD, { being, row: own, stance });
|
|
86
|
+
// The standing on the dock is kept at once: its heir is spent, so a
|
|
87
|
+
// restart before the ward's first keep would find no way back.
|
|
88
|
+
if (parts.dock && !Object.hasOwn(own.standings, DOCK) && (await stance.standings.take(DOCK, parts.dock)) !== null && !(await ward.partition.kept())) {
|
|
89
|
+
throw new Error('the standing on the dock was not kept');
|
|
90
|
+
}
|
|
91
|
+
for (const [key, row] of Object.entries(ward.partition.beings)) {
|
|
92
|
+
const C = key === WARD ? undefined : parts.catalogue.classOf(row.class);
|
|
93
|
+
if (C) ward.#bear(key, row, C);
|
|
94
|
+
}
|
|
95
|
+
return ward;
|
|
66
96
|
}
|
|
67
97
|
|
|
68
|
-
|
|
69
|
-
|
|
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;
|
|
98
|
+
get pk(): string {
|
|
99
|
+
return this.key.pk;
|
|
79
100
|
}
|
|
80
101
|
|
|
81
|
-
|
|
82
|
-
this.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
this.#
|
|
92
|
-
this.heirs = new Heirs(this.p, () => this.#wrote(HEAD));
|
|
93
|
-
this.door = makeDoor(
|
|
94
|
-
key,
|
|
95
|
-
this.heirs,
|
|
96
|
-
this.doors,
|
|
97
|
-
() => this.p.public,
|
|
98
|
-
(n) => ground.random(n),
|
|
99
|
-
() => this.#keep(),
|
|
100
|
-
); // pointer one
|
|
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
|
-
};
|
|
102
|
+
get draw() {
|
|
103
|
+
return this.#parts.entropy.drawer;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get wardBeing(): WardBeing {
|
|
107
|
+
return this.#residents.get(WARD)!.being as WardBeing;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// The being standing under a key, for the harbor's own faculties.
|
|
111
|
+
being(key: string): BeingLike | undefined {
|
|
112
|
+
return this.#residents.get(key)?.being;
|
|
116
113
|
}
|
|
117
114
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
115
|
+
// Every being her row names, of the class the catalogue now resolves for
|
|
116
|
+
// her kind: an absent being is born, and one whose class a module
|
|
117
|
+
// replaced is born again of the new class on the stance she holds, so
|
|
118
|
+
// her relations and their lanes stay hers. A new class that throws at
|
|
119
|
+
// birth leaves her as she was.
|
|
120
|
+
bearAnew(): void {
|
|
121
|
+
for (const [key, row] of Object.entries(this.partition.beings)) {
|
|
122
|
+
const C = key === WARD ? undefined : this.#parts.catalogue.classOf(row.class);
|
|
123
|
+
const resident = this.#residents.get(key);
|
|
124
|
+
if (!C || resident?.being.constructor === C) continue;
|
|
125
|
+
if (!resident) {
|
|
126
|
+
this.#bear(key, row, C);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
132
129
|
try {
|
|
133
|
-
|
|
130
|
+
resident.being = new C(resident.stance);
|
|
134
131
|
} catch {
|
|
135
|
-
|
|
132
|
+
continue;
|
|
136
133
|
}
|
|
137
134
|
}
|
|
138
135
|
}
|
|
139
136
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// names nobody the ward did not bind.
|
|
144
|
-
async answer(asker: Asker, method?: string, args: JsonObject = {}): Promise<Reply> {
|
|
145
|
-
// Nobody is not an owner. The door names nobody the ward did not bind, so
|
|
146
|
-
// the only way here unnamed is a partition that says the ward is its own
|
|
147
|
-
// public being, which no owner ask will write. Silence, not an error
|
|
148
|
-
// object: an unnamed asker is a stranger, and a stranger hears one thing.
|
|
149
|
-
if (asker.id === undefined) return silence;
|
|
150
|
-
return ownerAnswer(
|
|
151
|
-
{
|
|
152
|
-
pk: this.pk,
|
|
153
|
-
doors: this.doors,
|
|
154
|
-
absent: () => this.#absent(),
|
|
155
|
-
publicKey: () => this.p.public,
|
|
156
|
-
instantiate: (className, key) => this.#instantiate(className, key),
|
|
157
|
-
unboot: (key) => this.#unboot(key),
|
|
158
|
-
setPublic: (key) => {
|
|
159
|
-
this.p.public = key;
|
|
160
|
-
this.#wrote(HEAD);
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
asker,
|
|
164
|
-
method,
|
|
165
|
-
args,
|
|
166
|
-
);
|
|
137
|
+
// The unsealed ask: the ward-being, asked by whoever holds this ward.
|
|
138
|
+
async root(method?: string, args: JsonObject = {}): Promise<Answer> {
|
|
139
|
+
return this.#kept(WARD, () => this.#answer(WARD, { id: OWNER }, method, args));
|
|
167
140
|
}
|
|
168
141
|
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
142
|
+
// A being's answer, kept. Where the memory refuses, what she wrote is
|
|
143
|
+
// put back and her answer is silence. What her relations stand on is not
|
|
144
|
+
// put back: a relation's keys move only as far as its standing follows.
|
|
145
|
+
async #kept(key: string, work: () => Promise<Answer>): Promise<Answer> {
|
|
146
|
+
const row = this.partition.being(key);
|
|
147
|
+
const before = row && JSON.stringify(row.cells);
|
|
148
|
+
const out = await work();
|
|
149
|
+
if (await this.partition.kept()) return out;
|
|
150
|
+
if (row && before !== undefined) {
|
|
151
|
+
for (const k of Object.keys(row.cells)) delete row.cells[k];
|
|
152
|
+
for (const [k, v] of Object.entries(JSON.parse(before) as JsonObject)) Object.defineProperty(row.cells, k, { value: v, enumerable: true, writable: true, configurable: true });
|
|
153
|
+
this.partition.told(key);
|
|
154
|
+
}
|
|
155
|
+
return silence;
|
|
178
156
|
}
|
|
179
157
|
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return this.g.memory.kept();
|
|
158
|
+
// Behind: the zero head reaches the public being, an heir the being that
|
|
159
|
+
// invited it.
|
|
160
|
+
get zero(): boolean {
|
|
161
|
+
const key = this.partition.head.public;
|
|
162
|
+
return key !== null && this.#residents.has(key);
|
|
186
163
|
}
|
|
187
164
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
return
|
|
165
|
+
async answer(heir: string | null, p: Payload): Promise<Choice> {
|
|
166
|
+
const bound = heir === null ? undefined : this.partition.head.bind[heir];
|
|
167
|
+
const key = heir === null ? this.partition.head.public : bound?.being;
|
|
168
|
+
const asker: Asker = bound ? { id: bound.id } : {};
|
|
169
|
+
const args = p.args === undefined ? {} : readValue(p.args, DEPTH + 1);
|
|
170
|
+
if (key === null || key === undefined || typeof args !== 'object' || args === null || Array.isArray(args)) return { silence: true };
|
|
171
|
+
const out = await this.#kept(key, () => this.#answer(key, asker, p.method, args));
|
|
172
|
+
const object = isSilence(out) || isWord(out) ? undefined : writeValue(out, DEPTH);
|
|
173
|
+
if (object === undefined) return { silence: true };
|
|
174
|
+
const being = this.#residents.get(key)?.being;
|
|
175
|
+
const seen = p.method === undefined || !being?.describe ? null : await digest(being.describe(asker));
|
|
176
|
+
return { object, seen };
|
|
194
177
|
}
|
|
195
178
|
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
// The three refusals are read before the constructor runs, and the
|
|
203
|
-
// constructor can reach the stance and boot: a class that boots its own
|
|
204
|
-
// key from inside itself would pass all three, since neither its door nor
|
|
205
|
-
// its row exists yet, and the two objects would then share one row. A key
|
|
206
|
-
// being born is a key taken, for as long as the birth lasts.
|
|
207
|
-
if (typeof key !== 'string' || typeof className !== 'string' || !key || this.doors.has(key) || at(this.p.beings, key) || this.#birthing.has(key)) return null;
|
|
208
|
-
let door: Resident | null;
|
|
209
|
-
this.#birthing.add(key);
|
|
179
|
+
// Every path that reaches a being's answer. A throw, a word out of her,
|
|
180
|
+
// or nothing at all is silence.
|
|
181
|
+
async #answer(key: string, asker: Asker, method: string | undefined, args: JsonObject): Promise<Answer> {
|
|
182
|
+
const resident = this.#residents.get(key);
|
|
183
|
+
if (!resident) return silence;
|
|
184
|
+
let out: Reply;
|
|
210
185
|
try {
|
|
211
|
-
|
|
186
|
+
out = await resident.being.answer(asker, method, args);
|
|
212
187
|
} catch {
|
|
213
|
-
return
|
|
214
|
-
} finally {
|
|
215
|
-
this.#birthing.delete(key);
|
|
188
|
+
return silence;
|
|
216
189
|
}
|
|
217
|
-
|
|
218
|
-
unguarded(door.cells).class = className; // so a restart finds her. the ward's key, written behind her guard
|
|
219
|
-
this.#wrote(key);
|
|
220
|
-
return door;
|
|
190
|
+
return out === undefined || isWord(out) ? silence : out;
|
|
221
191
|
}
|
|
222
192
|
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
if (
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
standings = ids(cells.standings);
|
|
239
|
-
if (door) {
|
|
240
|
-
for (const id of occupants) door.stance.occupants.remove(id);
|
|
241
|
-
for (const id of standings) door.stance.standings.remove(id);
|
|
242
|
-
} else {
|
|
243
|
-
const bind = at(this.p.bind, key);
|
|
244
|
-
for (const heir of Object.values(bind?.occupants ?? {})) this.heirs.close(heir);
|
|
245
|
-
}
|
|
246
|
-
// A ward whose public being is gone answers arrivals for no heir with
|
|
247
|
-
// silence, as a ward that never had one does.
|
|
248
|
-
if (this.p.public === key) this.p.public = null;
|
|
249
|
-
drop(this.p.beings, key);
|
|
250
|
-
drop(this.p.bind, key);
|
|
251
|
-
this.doors.delete(key);
|
|
252
|
-
this.#wrote(key); // she is gone, and her row goes with her
|
|
253
|
-
this.#wrote(HEAD); // her heirs were closed, and the public being may have been her
|
|
254
|
-
return [...occupants, ...standings];
|
|
193
|
+
// Inside: what every stance of this ward stands on.
|
|
194
|
+
|
|
195
|
+
// One path through a door for every ask, this ward's own included.
|
|
196
|
+
async send(pk: string, bytes: Uint8Array): Promise<Uint8Array | null> {
|
|
197
|
+
if (pk === this.pk) return (await this.door.arrive(bytes)).bytes;
|
|
198
|
+
return this.#parts.carrier.carry(pk, bytes);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// A lend: the ward-being lends herself, as the dock does, or asks the dock
|
|
202
|
+
// through her standing.
|
|
203
|
+
async offer(contract: string): Promise<Invitation | null> {
|
|
204
|
+
const own = this.#residents.get(WARD)!;
|
|
205
|
+
if (lends(own.being)) return own.being.offer(contract);
|
|
206
|
+
const out = await own.stance.standings.get(DOCK)?.ask('lend', { contract });
|
|
207
|
+
return readInvitation((out as { invitation?: unknown } | undefined)?.invitation);
|
|
255
208
|
}
|
|
256
209
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// `make` is who to build with the stance this raises. Two callers and two
|
|
262
|
-
// makers: the ground's `instantiate` for a being of the ward, and the ward
|
|
263
|
-
// itself, which is the first being in its own map and is already made.
|
|
264
|
-
#boot(key: string, make: (stance: Stance) => BeingLike | null): Resident | null {
|
|
265
|
-
const cells = guardCells(at(this.p.beings, key) ?? emptyCells(), () => this.#wrote(key));
|
|
266
|
-
const bind = at(this.p.bind, key) ?? emptyBind();
|
|
267
|
-
const stance: Stance = buildStance(
|
|
268
|
-
{
|
|
269
|
-
pk: this.pk,
|
|
270
|
-
// This stance, not merely this key: unboot and boot again under the
|
|
271
|
-
// same key makes a new being, and the old stance is not hers.
|
|
272
|
-
live: () => this.doors.get(key)?.stance === stance,
|
|
273
|
-
mintKey: (b) => this.#mintKey(b, key),
|
|
274
|
-
openHeir: (heir, being, id) => this.heirs.open(heir, being, id),
|
|
275
|
-
closeHeir: (heir) => this.heirs.close(heir),
|
|
276
|
-
send: (b, keys, method, args, wanted) => this.#send(b, keys, method, args, wanted, key),
|
|
277
|
-
// A throw at birth is null to her, not a throw in her method: she asked
|
|
278
|
-
// for a being and got none, and her own answer is still hers to give.
|
|
279
|
-
instantiate: (className, k) => {
|
|
280
|
-
const made = this.#instantiate(className, k);
|
|
281
|
-
return made && made !== 'threw' ? made.key : null;
|
|
282
|
-
},
|
|
283
|
-
// The two halves of a maker's way back, each the ward's own call: the
|
|
284
|
-
// being made mints an occupant for her maker, and a relation that
|
|
285
|
-
// never came about takes her out again.
|
|
286
|
-
relate: async (k, id) => (await this.doors.get(k)?.stance.occupants.invite(id)) ?? null,
|
|
287
|
-
unmake: (k) => void this.#unboot(k),
|
|
288
|
-
lend: (name, take) => this.#lend(name, take),
|
|
289
|
-
wrote: () => this.#wrote(key),
|
|
290
|
-
},
|
|
291
|
-
key,
|
|
292
|
-
cells,
|
|
293
|
-
bind,
|
|
294
|
-
);
|
|
295
|
-
const being = make(stance);
|
|
296
|
-
if (!being || typeof being.answer !== 'function') return null; // a being is anything with an answer, and nothing without one
|
|
297
|
-
put(this.p.beings, key, cells);
|
|
298
|
-
put(this.p.bind, key, bind);
|
|
299
|
-
const door: Resident = { key, cells, bind, stance, being };
|
|
300
|
-
this.doors.set(key, door);
|
|
301
|
-
this.#wrote(key);
|
|
302
|
-
return door;
|
|
210
|
+
async retract(contract: string, heir: string): Promise<void> {
|
|
211
|
+
const own = this.#residents.get(WARD)!;
|
|
212
|
+
if (lends(own.being)) return own.being.withdraw(contract, heir);
|
|
213
|
+
await own.stance.standings.get(DOCK)?.ask('retract', { contract, heir });
|
|
303
214
|
}
|
|
304
215
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
// The last few she minted, and no more. A relation rotates on every ask,
|
|
308
|
-
// so a list of all of them is a partition that grows for as long as she
|
|
309
|
-
// keeps talking and can never be written down. Nothing is read from this
|
|
310
|
-
// but the keys of the moment.
|
|
311
|
-
bind.minted.push(k.pk);
|
|
312
|
-
if (bind.minted.length > MINTED) bind.minted.splice(0, bind.minted.length - MINTED);
|
|
313
|
-
this.#wrote(row);
|
|
314
|
-
return k;
|
|
216
|
+
live(key: string, row: BeingRow): boolean {
|
|
217
|
+
return this.#residents.get(key)?.row === row;
|
|
315
218
|
}
|
|
316
219
|
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
220
|
+
// A being boots another of this ward. With an id, the new being invites
|
|
221
|
+
// her maker under the maker's key, and the maker takes it under id; a
|
|
222
|
+
// relation that could not be made unmakes the new being.
|
|
223
|
+
async boot(maker: string, className: string, key: string, id?: string): Promise<string | null> {
|
|
224
|
+
if ((await this.#make(key, className)) !== 'booted') return null;
|
|
225
|
+
if (id === undefined) return key;
|
|
226
|
+
const invitation = await this.#residents.get(key)!.stance.occupants.invite(maker);
|
|
227
|
+
const taken = invitation ? await this.#residents.get(maker)?.stance.standings.take(id, invitation) : null;
|
|
228
|
+
if (taken) return key;
|
|
229
|
+
this.#unboot(key);
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async #make(key: string, className: string): Promise<Birth> {
|
|
234
|
+
if (key === '' || key === WARD || this.partition.being(key) || this.#birthing.has(key)) return 'key taken';
|
|
235
|
+
const C = this.#parts.catalogue.classOf(className);
|
|
236
|
+
if (!C) return 'no such class';
|
|
237
|
+
this.#birthing.add(key);
|
|
330
238
|
try {
|
|
331
|
-
|
|
239
|
+
const row: BeingRow = { class: className, cells: {}, occupants: {}, standings: {} };
|
|
240
|
+
this.partition.beings[key] = row;
|
|
241
|
+
if (!this.#bear(key, row, C)) {
|
|
242
|
+
delete this.partition.beings[key];
|
|
243
|
+
return 'threw at birth';
|
|
244
|
+
}
|
|
245
|
+
this.partition.told(key);
|
|
246
|
+
return 'booted';
|
|
332
247
|
} finally {
|
|
333
|
-
|
|
248
|
+
this.#birthing.delete(key);
|
|
334
249
|
}
|
|
335
250
|
}
|
|
336
251
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
// the default, and asking for more than the ceiling is the ceiling: budget
|
|
340
|
-
// is granted by a ward, never minted by a being.
|
|
341
|
-
const budget = allow(wanted);
|
|
342
|
-
const by = (await beingKey(unhex(keys.current))).pk;
|
|
343
|
-
// A public being is reached without a heir, and the door keeps nothing for
|
|
344
|
-
// whoever asked: no key it vouched for, so nothing to announce and nothing
|
|
345
|
-
// to rotate to. A standing on her signs with one key for life. Every other
|
|
346
|
-
// ask announces its next: there is no send that does not.
|
|
347
|
-
if (keys.next === null && keys.heir !== null) keys.next = (await this.#mintKey(bind, row)).seed;
|
|
348
|
-
const next = keys.next === null ? null : (await beingKey(unhex(keys.next))).pk;
|
|
349
|
-
// Her count for this relation, one higher every call and never reused. The
|
|
350
|
-
// far door honours each number once. One relation sends one at a time, so
|
|
351
|
-
// the numbers leave in the order they were taken.
|
|
352
|
-
const seq = (keys.seq += 1);
|
|
353
|
-
// Named here, before the bytes go out, and not at the end with the keys.
|
|
354
|
-
// The far door honours a number once, and it honours it before this side
|
|
355
|
-
// hears anything back: a reply that is lost, a wait that ran out, a road
|
|
356
|
-
// that took the bytes and said nothing, all end this call before the end
|
|
357
|
-
// of it is reached. A count named only there is a count a restart forgets
|
|
358
|
-
// while the door remembers, and she then speaks a number that door has
|
|
359
|
-
// already spent, which it refuses as the replay it cannot tell it from.
|
|
360
|
-
// So the rule is the send and not the reply: what she spoke under is
|
|
361
|
-
// written down because she spoke it.
|
|
362
|
-
this.#wrote(row);
|
|
363
|
-
// Args that will not go through the seal are args that never left. A
|
|
364
|
-
// being holds three answers and a throw is not one of them, so an ask
|
|
365
|
-
// that cannot be sealed is told what is true of it: no door was reached.
|
|
366
|
-
// One object of values, by the rule her cells keep and the far door
|
|
367
|
-
// reads: a Date would arrive as a string, a function would not arrive at
|
|
368
|
-
// all, a list or a string would be refused as a stranger's bytes, and
|
|
369
|
-
// she would never know the far being was asked something other than
|
|
370
|
-
// what she said.
|
|
371
|
-
if (args === null || typeof args !== 'object' || Array.isArray(args) || cellFault(args, 'args') !== null) return unreached();
|
|
372
|
-
let sealed;
|
|
252
|
+
#bear(key: string, row: BeingRow, C: BeingClass): boolean {
|
|
253
|
+
const stance = new WardStance(this, key, row);
|
|
373
254
|
try {
|
|
374
|
-
|
|
255
|
+
this.#residents.set(key, { being: new C(stance), row, stance });
|
|
256
|
+
return true;
|
|
375
257
|
} catch {
|
|
376
|
-
|
|
258
|
+
this.#residents.delete(key);
|
|
259
|
+
return false;
|
|
377
260
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// What comes back late is not read.
|
|
387
|
-
//
|
|
388
|
-
// A wait that ran out is `late`, never unreached. Unreached promises
|
|
389
|
-
// nothing was delivered and is safe to retry; a bound that expired knows
|
|
390
|
-
// no such thing, because the far door may have heard and be working still.
|
|
391
|
-
//
|
|
392
|
-
// A harbor answers bytes or nothing. One that throws instead has answered
|
|
393
|
-
// nothing in a louder voice, and is read as nothing: no door was reached.
|
|
394
|
-
const carried = (keys.ward === this.pk ? this.door(new Uint8Array(bytes)).then((r) => r.bytes) : this.g.carry(keys.ward, bytes)).then(
|
|
395
|
-
(b) => b,
|
|
396
|
-
() => undefined,
|
|
397
|
-
);
|
|
398
|
-
const out = await within(budget.time, carried);
|
|
399
|
-
if (out === LATE) return word('late');
|
|
400
|
-
if (out === undefined) return unreached(); // nothing came back. no door was reached.
|
|
401
|
-
const reply = await openReply(out, ephemeral, wardSignPk(keys.ward));
|
|
402
|
-
if (!reply) return silence; // did not open, or not from that ward: not Quo's bytes
|
|
403
|
-
if ('object' in reply && keys.next !== null) {
|
|
404
|
-
keys.current = keys.next; // the far door holds `next` as announced. move to it.
|
|
405
|
-
keys.next = null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#unboot(key: string): boolean {
|
|
264
|
+
const row = key === WARD ? undefined : this.partition.being(key);
|
|
265
|
+
if (!row) return false;
|
|
266
|
+
for (const { heir } of Object.values(row.occupants)) {
|
|
267
|
+
this.door.remove(heir);
|
|
268
|
+
delete this.partition.head.bind[heir];
|
|
406
269
|
}
|
|
407
|
-
this
|
|
408
|
-
|
|
270
|
+
if (this.partition.head.public === key) this.partition.head.public = null;
|
|
271
|
+
delete this.partition.beings[key];
|
|
272
|
+
this.#residents.delete(key);
|
|
273
|
+
this.partition.toldHead();
|
|
274
|
+
this.partition.told(key);
|
|
275
|
+
return true;
|
|
409
276
|
}
|
|
277
|
+
|
|
278
|
+
// What the ward-being asks of her ward.
|
|
279
|
+
readonly #steward: Steward = {
|
|
280
|
+
pk: () => this.pk,
|
|
281
|
+
classOf: (name) => this.#parts.catalogue.classOf(name),
|
|
282
|
+
beings: () => {
|
|
283
|
+
const out: ReturnType<Steward['beings']> = {};
|
|
284
|
+
for (const [key, row] of Object.entries(this.partition.beings)) {
|
|
285
|
+
if (key !== WARD) out[key] = { class: row.class, public: this.partition.head.public === key, absent: !this.#residents.has(key) };
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
288
|
+
},
|
|
289
|
+
boot: (key, className) => this.#make(key, className),
|
|
290
|
+
unboot: (key) => this.#unboot(key),
|
|
291
|
+
publish: (key) => {
|
|
292
|
+
if (key !== null && (key === WARD || !this.partition.being(key))) return false;
|
|
293
|
+
this.partition.head.public = key;
|
|
294
|
+
this.partition.toldHead();
|
|
295
|
+
return true;
|
|
296
|
+
},
|
|
297
|
+
invite: (being, id, notes): Promise<Invitation | null> => {
|
|
298
|
+
const resident = being === WARD ? undefined : this.#residents.get(being);
|
|
299
|
+
return resident ? resident.stance.occupants.invite(id, notes) : Promise.resolve(null);
|
|
300
|
+
},
|
|
301
|
+
ask: (being, method, args) => {
|
|
302
|
+
const key = being ?? this.partition.head.public;
|
|
303
|
+
if (key === null || key === WARD) return Promise.resolve(word('unreached'));
|
|
304
|
+
return this.#answer(key, being === null ? {} : { id: OWNER }, method, args);
|
|
305
|
+
},
|
|
306
|
+
};
|
|
410
307
|
}
|