nervur 0.19.2 → 0.20.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.
- package/README.md +66 -101
- package/dist/being/being.d.ts +7 -15
- package/dist/being/being.js +16 -67
- package/dist/being/digest.d.ts +1 -1
- package/dist/being/digest.js +7 -27
- package/dist/being/faculty.d.ts +9 -0
- package/dist/being/faculty.js +68 -0
- package/dist/being/index.d.ts +4 -5
- package/dist/being/index.js +5 -5
- package/dist/being/types.d.ts +35 -62
- package/dist/being/types.js +3 -57
- package/dist/being/words.d.ts +13 -0
- package/dist/being/words.js +14 -0
- package/dist/contract/index.d.ts +26 -0
- package/dist/contract/index.js +19 -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 +13 -0
- package/dist/folder/index.js +125 -0
- package/dist/harbor/dock.d.ts +19 -0
- package/dist/harbor/dock.js +92 -0
- package/dist/harbor/harbor.d.ts +22 -0
- package/dist/harbor/harbor.js +84 -0
- package/dist/harbor/index.d.ts +4 -10
- package/dist/harbor/index.js +5 -18
- package/dist/harbor/registry.d.ts +7 -0
- package/dist/harbor/registry.js +13 -0
- package/dist/harbor/terrain.d.ts +8 -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 +20 -0
- package/dist/pointer/bodies.js +45 -0
- package/dist/pointer/index.d.ts +2 -0
- package/dist/pointer/index.js +4 -0
- package/dist/pointer/world.d.ts +34 -0
- package/dist/pointer/world.js +76 -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/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 +5 -10
- package/dist/ward/index.js +6 -13
- package/dist/ward/partition.d.ts +44 -60
- package/dist/ward/partition.js +56 -285
- package/dist/ward/stance.d.ts +29 -20
- package/dist/ward/stance.js +173 -406
- package/dist/ward/ward-being.d.ts +33 -0
- package/dist/ward/ward-being.js +76 -0
- package/dist/ward/ward.d.ts +43 -11
- package/dist/ward/ward.js +215 -363
- package/package.json +19 -33
- package/src/being/being.ts +31 -78
- package/src/being/digest.ts +7 -35
- package/src/being/faculty.ts +66 -0
- package/src/being/index.ts +5 -6
- package/src/being/types.ts +47 -145
- package/src/being/words.ts +31 -0
- package/src/contract/index.ts +44 -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 +134 -0
- package/src/harbor/dock.ts +101 -0
- package/src/harbor/harbor.ts +105 -0
- package/src/harbor/index.ts +5 -19
- package/src/harbor/registry.ts +20 -0
- package/src/harbor/terrain.ts +13 -0
- package/src/index.ts +20 -0
- package/src/pointer/bodies.ts +47 -0
- package/src/pointer/index.ts +4 -0
- package/src/pointer/world.ts +91 -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 +19 -0
- package/src/stand/stand.ts +178 -0
- package/src/ward/allowance.ts +37 -75
- package/src/ward/cells.ts +63 -176
- package/src/ward/index.ts +6 -17
- package/src/ward/partition.ts +86 -326
- package/src/ward/stance.ts +185 -420
- package/src/ward/ward-being.ts +97 -0
- package/src/ward/ward.ts +229 -363
- 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/partition.ts
CHANGED
|
@@ -1,353 +1,113 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The partition
|
|
3
|
-
//
|
|
4
|
-
// every secret included. Nothing here is ever in a being's cells.
|
|
5
|
-
import { unguarded } from './cells.ts';
|
|
6
|
-
import type { Cells } from '../being/types.ts';
|
|
7
|
-
|
|
8
|
-
// The shape of everything below. A ward stamps what it writes and refuses
|
|
9
|
-
// what it cannot read: the partition outlives the ward that wrote it, and a
|
|
10
|
-
// ward that guessed at a shape it did not know would not fail, it would answer
|
|
11
|
-
// wrongly on relations it had silently misread.
|
|
2
|
+
// The partition: everything one ward keeps, as rows of values in the memory
|
|
3
|
+
// its terrain opened for it.
|
|
12
4
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// step added here is from one member of READS to the next.
|
|
34
|
-
function upgrade(p: Partition): Partition {
|
|
35
|
-
return p;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// One occupant, as the door knows her: by the heir pk her invitation carried.
|
|
39
|
-
// `current` is the pk that may speak now. `announced` is the pk `current`
|
|
40
|
-
// vouched for on its last call, and may speak next. Both may speak until one
|
|
41
|
-
// does, so a lost reply after a rotation strands nobody. `fresh` is true
|
|
42
|
-
// until the knock: the heir is a key the inviter handed out, and it dies the
|
|
43
|
-
// moment it is used.
|
|
44
|
-
export type Heir = {
|
|
45
|
-
being: string; // the key of the being who minted the id
|
|
46
|
-
id: string; // her id for the occupant
|
|
47
|
-
current: string; // pk hex
|
|
48
|
-
announced: string | null; // pk hex
|
|
49
|
-
fresh: boolean;
|
|
50
|
-
// Once-only delivery. The highest number honoured, and which numbers below
|
|
51
|
-
// it are already spent. Durable, because a door that forgets what it
|
|
52
|
-
// honoured honours it again after a restart.
|
|
53
|
-
mark: number;
|
|
54
|
-
spent: number[];
|
|
55
|
-
// Reserved, and nothing writes it. A door refuses a number at or below the
|
|
56
|
-
// span, which is right for an accident and for an interception. A kit that
|
|
57
|
-
// grows retry may want the other answer, giving back what it already said
|
|
58
|
-
// rather than one silence, and that answer has to live somewhere durable.
|
|
59
|
-
// The slot is cut now because a Heir that grows a field after 1.0.0 is a
|
|
60
|
-
// partition shape change, and this is the cheapest thing in this file.
|
|
61
|
-
last?: { seq: number; reply: string };
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// One standing, as the caller's ward keeps it: where to send, how to seal,
|
|
65
|
-
// which key to sign with, and the key it has announced for next time.
|
|
66
|
-
export type StandingKeys = {
|
|
67
|
-
ward: string; // the far ward's pk, 128 hex: signing pk then padlock
|
|
68
|
-
heir: string | null; // the heir pk this standing was born on. null: the far ward's public being
|
|
69
|
-
current: string; // seed hex of the signing key that speaks now
|
|
70
|
-
next: string | null; // seed hex of the key announced on the last call, if any
|
|
71
|
-
seq: number; // the last number she spoke under. the next call is this plus one
|
|
5
|
+
// rows.ward the door's lock, heirs and gone, which being and id
|
|
6
|
+
// each heir belongs to, and the public being
|
|
7
|
+
// rows.beings[key] one being: her class, cells, occupants and standings
|
|
8
|
+
//
|
|
9
|
+
// The ward writes into the rows directly and tells the memory which row it
|
|
10
|
+
// wrote: the head as the empty name, which no being's key is, or the
|
|
11
|
+
// being's key.
|
|
12
|
+
import type { Lock } from '../crypto/index.ts';
|
|
13
|
+
import { GONE, keptLock, lockOf, type HeirRecord, type Invitation, type KeptLock, type Relations, type StandingState } from '../quo/index.ts';
|
|
14
|
+
import type { Rows } from '../contract/index.ts';
|
|
15
|
+
import type { JsonObject } from '../being/index.ts';
|
|
16
|
+
|
|
17
|
+
export const HEAD_ROW = '';
|
|
18
|
+
|
|
19
|
+
export type Head = {
|
|
20
|
+
lock?: KeptLock;
|
|
21
|
+
heirs: Record<string, HeirRecord>;
|
|
22
|
+
gone: string[];
|
|
23
|
+
bind: Record<string, { being: string; id: string }>;
|
|
24
|
+
public: string | null;
|
|
72
25
|
};
|
|
73
26
|
|
|
74
|
-
export type
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
27
|
+
export type StandingRow = { invitation: Invitation; state: StandingState; seen: string | null };
|
|
28
|
+
export type BeingRow = {
|
|
29
|
+
class: string;
|
|
30
|
+
cells: JsonObject;
|
|
31
|
+
occupants: Record<string, { heir: string; notes: JsonObject }>;
|
|
32
|
+
standings: Record<string, StandingRow>;
|
|
80
33
|
};
|
|
81
34
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// silence. Nothing else survives the removal. The list is bounded, oldest
|
|
85
|
-
// out, because a door that remembered every relation ever ended would grow
|
|
86
|
-
// for as long as she keeps inviting.
|
|
87
|
-
export type Gone = { current: string; announced: string | null };
|
|
88
|
-
export const GONE = 256;
|
|
89
|
-
|
|
90
|
-
// The last few pks a relation minted, and no more. A relation rotates on
|
|
91
|
-
// every ask, so a list of all of them is a partition that grows for as long
|
|
92
|
-
// as she keeps talking and can never be written down. It lives here, beside
|
|
93
|
-
// the other bound on the durable shape, because it is a fact about what a
|
|
94
|
-
// partition may hold and not about how a ward mints.
|
|
95
|
-
export const MINTED = 8;
|
|
35
|
+
export class Partition {
|
|
36
|
+
readonly memory: Rows;
|
|
96
37
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// she may still take.
|
|
103
|
-
export const KNOCKS = 64;
|
|
104
|
-
export const prune = (knocks: Record<string, unknown>, answered: Record<string, true>): void => {
|
|
105
|
-
const names = Object.keys(knocks);
|
|
106
|
-
if (names.length <= KNOCKS) return;
|
|
107
|
-
const order = [...names.filter((n) => !answered[n]), ...names.filter((n) => answered[n])];
|
|
108
|
-
for (const old of order.slice(0, names.length - KNOCKS)) {
|
|
109
|
-
delete knocks[old];
|
|
110
|
-
delete answered[old];
|
|
38
|
+
constructor(memory: Rows) {
|
|
39
|
+
this.memory = memory;
|
|
40
|
+
const rows = memory.rows as { ward?: Head; beings?: Record<string, BeingRow> };
|
|
41
|
+
rows.ward ??= { heirs: {}, gone: [], bind: {}, public: null };
|
|
42
|
+
rows.beings ??= {};
|
|
111
43
|
}
|
|
112
|
-
};
|
|
113
44
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
bind: Record<string, Bind>; // key -> her bind table
|
|
118
|
-
heirs: Record<string, Heir>; // heir pk -> occupant, across every being of the ward
|
|
119
|
-
gone: Record<string, Gone>; // heir pk -> the last keys of a relation she removed
|
|
120
|
-
public: string | null; // the one public being's key
|
|
121
|
-
};
|
|
45
|
+
get head(): Head {
|
|
46
|
+
return this.memory.rows.ward as Head;
|
|
47
|
+
}
|
|
122
48
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// the door or the heirs then act on without looking again. A `spent` that is
|
|
127
|
-
// not an array rejects the door's promise on the first bound ask, a `mark`
|
|
128
|
-
// that is not a number honours every number ever sent and grows without
|
|
129
|
-
// bound, a being row that is null refuses the whole ward at birth by
|
|
130
|
-
// accident and says nothing about why. So the shape is read once, here, and
|
|
131
|
-
// what is not it throws with the path that failed, which names the trouble
|
|
132
|
-
// to whoever is holding the file.
|
|
133
|
-
//
|
|
134
|
-
// The rule, so the reader does not grow into a schema: read every field the
|
|
135
|
-
// ward acts on, and refuse only what would make it act wrongly. So `minted`
|
|
136
|
-
// must be a list, because she pushes to it, and the truth in `answered` is
|
|
137
|
-
// not read, because anything there says answered and nothing else follows.
|
|
138
|
-
// `last` is reserved and nobody reads it, so nobody reads its shape either;
|
|
139
|
-
// the run that gives it a reader gives it a line here. A being's own cells
|
|
140
|
-
// are hers, of any shape JSON carries, and only the two records the ward
|
|
141
|
-
// keeps inside them are read.
|
|
142
|
-
const fault = (path: string, want: string): never => {
|
|
143
|
-
throw new Error(`partition ${path} is not ${want}`);
|
|
144
|
-
};
|
|
145
|
-
const table = (v: unknown, path: string): Record<string, unknown> => (typeof v === 'object' && v !== null && !Array.isArray(v) ? (v as Record<string, unknown>) : fault(path, 'an object'));
|
|
146
|
-
const list = (v: unknown, path: string): unknown[] => (Array.isArray(v) ? (v as unknown[]) : fault(path, 'an array'));
|
|
147
|
-
const text = (v: unknown, path: string): void => void (typeof v === 'string' || fault(path, 'a string'));
|
|
148
|
-
const orNull = (v: unknown, path: string): void => void (v === null || typeof v === 'string' || fault(path, 'a string or null'));
|
|
149
|
-
const whole = (v: unknown, path: string): void => void (Number.isSafeInteger(v) || fault(path, 'a whole number'));
|
|
150
|
-
const flag = (v: unknown, path: string): void => void (typeof v === 'boolean' || fault(path, 'true or false'));
|
|
49
|
+
get beings(): Record<string, BeingRow> {
|
|
50
|
+
return this.memory.rows.beings as Record<string, BeingRow>;
|
|
51
|
+
}
|
|
151
52
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const c = table(cells, `beings.${key}`);
|
|
155
|
-
table(c.standings, `beings.${key}.standings`);
|
|
156
|
-
table(c.occupants, `beings.${key}.occupants`);
|
|
157
|
-
if (c.class !== undefined) text(c.class, `beings.${key}.class`);
|
|
53
|
+
being(key: string): BeingRow | undefined {
|
|
54
|
+
return Object.hasOwn(this.beings, key) ? this.beings[key] : undefined;
|
|
158
55
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const w = `bind.${key}.standings.${id}`;
|
|
163
|
-
const s = table(keys, w);
|
|
164
|
-
text(s.ward, `${w}.ward`);
|
|
165
|
-
orNull(s.heir, `${w}.heir`);
|
|
166
|
-
text(s.current, `${w}.current`);
|
|
167
|
-
orNull(s.next, `${w}.next`);
|
|
168
|
-
whole(s.seq, `${w}.seq`);
|
|
169
|
-
}
|
|
170
|
-
for (const [id, pk] of Object.entries(table(b.occupants, `bind.${key}.occupants`))) text(pk, `bind.${key}.occupants.${id}`);
|
|
171
|
-
for (const [name, keys] of Object.entries(table(b.knocks, `bind.${key}.knocks`))) {
|
|
172
|
-
const w = `bind.${key}.knocks.${name}`;
|
|
173
|
-
const k = table(keys, w);
|
|
174
|
-
text(k.current, `${w}.current`);
|
|
175
|
-
orNull(k.next, `${w}.next`);
|
|
176
|
-
flag(k.spoke, `${w}.spoke`);
|
|
177
|
-
flag(k.sent, `${w}.sent`);
|
|
178
|
-
whole(k.seq, `${w}.seq`);
|
|
179
|
-
}
|
|
180
|
-
table(b.answered, `bind.${key}.answered`);
|
|
181
|
-
for (const [i, pk] of list(b.minted, `bind.${key}.minted`).entries()) text(pk, `bind.${key}.minted[${i}]`);
|
|
56
|
+
|
|
57
|
+
told(key: string): void {
|
|
58
|
+
this.memory.told(key);
|
|
182
59
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
text(h.id, `heirs.${pk}.id`);
|
|
187
|
-
text(h.current, `heirs.${pk}.current`);
|
|
188
|
-
orNull(h.announced, `heirs.${pk}.announced`);
|
|
189
|
-
flag(h.fresh, `heirs.${pk}.fresh`);
|
|
190
|
-
whole(h.mark, `heirs.${pk}.mark`);
|
|
191
|
-
for (const [i, n] of list(h.spent, `heirs.${pk}.spent`).entries()) whole(n, `heirs.${pk}.spent[${i}]`);
|
|
60
|
+
|
|
61
|
+
toldHead(): void {
|
|
62
|
+
this.memory.told(HEAD_ROW);
|
|
192
63
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
orNull(g.announced, `gone.${pk}.announced`);
|
|
64
|
+
|
|
65
|
+
kept(): Promise<boolean> {
|
|
66
|
+
return this.memory.kept().catch(() => false);
|
|
197
67
|
}
|
|
198
|
-
orNull(p.public, 'public');
|
|
199
|
-
return p;
|
|
200
68
|
}
|
|
201
69
|
|
|
202
|
-
// The
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
export function open(memory: Record<string, unknown>): Partition {
|
|
209
|
-
const p = memory as Partition;
|
|
210
|
-
if (Object.keys(memory).length > 0 && p.version !== VERSION) {
|
|
211
|
-
if (!READS.includes(p.version)) throw new Error(`partition is version ${p.version}, this ward reads ${READS.join(', ')}`);
|
|
212
|
-
upgrade(p).version = VERSION;
|
|
70
|
+
// The door's memory, written into the head.
|
|
71
|
+
export class PartitionRelations implements Relations {
|
|
72
|
+
readonly #partition: Partition;
|
|
73
|
+
|
|
74
|
+
constructor(partition: Partition) {
|
|
75
|
+
this.#partition = partition;
|
|
213
76
|
}
|
|
214
|
-
p.version ??= VERSION;
|
|
215
|
-
p.beings ??= {};
|
|
216
|
-
p.bind ??= {};
|
|
217
|
-
p.heirs ??= {};
|
|
218
|
-
p.gone ??= {};
|
|
219
|
-
p.public ??= null;
|
|
220
|
-
return read(p);
|
|
221
|
-
}
|
|
222
77
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// object alive in the runtime. Own keys only, both ways, and a write that
|
|
227
|
-
// names one of those lands on the record where it belongs.
|
|
228
|
-
export const at = <T>(rec: Record<string, T>, key: string): T | undefined => (Object.hasOwn(rec, key) ? rec[key] : undefined);
|
|
229
|
-
export const put = <T>(rec: Record<string, T>, key: string, value: T): T => {
|
|
230
|
-
Object.defineProperty(rec, key, { value, writable: true, enumerable: true, configurable: true });
|
|
231
|
-
return value;
|
|
232
|
-
};
|
|
233
|
-
export const drop = (rec: Record<string, unknown>, key: string): void => {
|
|
234
|
-
if (Object.hasOwn(rec, key)) delete rec[key];
|
|
235
|
-
};
|
|
78
|
+
get #head(): Head {
|
|
79
|
+
return this.#partition.head;
|
|
80
|
+
}
|
|
236
81
|
|
|
237
|
-
|
|
238
|
-
|
|
82
|
+
lock(): Lock | undefined {
|
|
83
|
+
return lockOf(this.#head.lock);
|
|
84
|
+
}
|
|
239
85
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
// one thing here that grows without limit, so she is a row: her cells and her
|
|
245
|
-
// bind table, under her key. Everything else is one head row, and every part
|
|
246
|
-
// of it is bounded already, one record per occupant and two lists that drop
|
|
247
|
-
// their oldest.
|
|
248
|
-
//
|
|
249
|
-
// The split is here, where the shape is, and not in the harbor, which keeps
|
|
250
|
-
// what it is given and reads none of it. A store is handed the rows that
|
|
251
|
-
// moved; what it does with that is its own, and one that writes every row is
|
|
252
|
-
// as correct as one that writes the few.
|
|
253
|
-
export const HEAD = ''; // the row that is not a being: no being's key is empty
|
|
254
|
-
// What a put back never moves: the two sides of a relation. `beings` and
|
|
255
|
-
// `bind` are the tables the rows hang under and are never written whole.
|
|
256
|
-
// `heirs` and `gone` are the door's side of every relation this ward holds,
|
|
257
|
-
// and a being's own `bind` row is her side of hers.
|
|
258
|
-
//
|
|
259
|
-
// A relation cannot be put back, only broken. Her keys rotate when a reply
|
|
260
|
-
// opens and the door's rotate when it honours, so the two move at different
|
|
261
|
-
// moments and neither side holds the seed of a key it has moved past. Stand
|
|
262
|
-
// either one at an earlier moment and it speaks under a key the other will
|
|
263
|
-
// not admit, with no way back: the seed is gone. The count needs no help
|
|
264
|
-
// here, because a door honours any unseen number inside its span and a
|
|
265
|
-
// caller's count climbs at every attempt, so numbers find each other again
|
|
266
|
-
// on their own. Keys do not.
|
|
267
|
-
//
|
|
268
|
-
// Ahead of the device is survivable and behind the far side is not, so what
|
|
269
|
-
// a refusal puts back is what a being wrote and never what her relations are
|
|
270
|
-
// standing on. What it costs is that her keys are then ahead of what the
|
|
271
|
-
// store kept, which a restart can undo: a smaller hole, and the same one a
|
|
272
|
-
// restart has anyway.
|
|
273
|
-
const STANDING = new Set(['beings', 'bind', 'heirs', 'gone']);
|
|
86
|
+
setLock(lock: Lock): void {
|
|
87
|
+
this.#head.lock = keptLock(lock);
|
|
88
|
+
this.#partition.toldHead();
|
|
89
|
+
}
|
|
274
90
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
// hands the same thing back, and a field this file grows later needs no line
|
|
279
|
-
// in a store to survive a round trip.
|
|
280
|
-
//
|
|
281
|
-
// Undefined where the key names nobody, which is a row the store drops.
|
|
282
|
-
export const rowOf = (p: Partition, row: string): Record<string, unknown> | undefined => {
|
|
283
|
-
const all = p as unknown as Record<string, unknown>;
|
|
284
|
-
if (row === HEAD) return Object.fromEntries(Object.entries(all).filter(([k]) => k !== 'beings' && k !== 'bind'));
|
|
285
|
-
const cells = at(p.beings ?? {}, row);
|
|
286
|
-
return cells === undefined ? undefined : { cells, bind: at(p.bind ?? {}, row) ?? emptyBind() };
|
|
287
|
-
};
|
|
288
|
-
export const rowsIn = (p: Record<string, unknown>): string[] => [HEAD, ...Object.keys((p.beings as Record<string, unknown>) ?? {})];
|
|
91
|
+
get(heir: string): HeirRecord | undefined {
|
|
92
|
+
return Object.hasOwn(this.#head.heirs, heir) ? this.#head.heirs[heir] : undefined;
|
|
93
|
+
}
|
|
289
94
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
// partition already has, so a row replaced by a fresh object would strand her
|
|
299
|
-
// on cells nothing keeps. Only the head may be written whole, because
|
|
300
|
-
// everything under it is read through the partition each time.
|
|
301
|
-
//
|
|
302
|
-
// A row the store does not hold is a being it never kept, and what is kept
|
|
303
|
-
// for her is nothing: her row goes, the way the store's own is already gone.
|
|
304
|
-
export const restoreRow = (p: Partition, row: string, value: Record<string, unknown> | undefined): void => {
|
|
305
|
-
const all = p as unknown as Record<string, unknown>;
|
|
306
|
-
if (row === HEAD) {
|
|
307
|
-
for (const k of Object.keys(all)) if (!STANDING.has(k)) drop(all, k);
|
|
308
|
-
for (const [k, v] of Object.entries(value ?? {})) if (!STANDING.has(k)) put(all, k, v);
|
|
309
|
-
return;
|
|
95
|
+
set(heir: string, record: HeirRecord): void {
|
|
96
|
+
const head = this.#head;
|
|
97
|
+
head.heirs[heir] = record;
|
|
98
|
+
if (record.state === 'kept') {
|
|
99
|
+
head.gone.push(heir);
|
|
100
|
+
while (head.gone.length > GONE) delete head.heirs[head.gone.shift()!];
|
|
101
|
+
}
|
|
102
|
+
this.#partition.toldHead();
|
|
310
103
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
drop(p.bind ?? {}, row);
|
|
316
|
-
return;
|
|
104
|
+
|
|
105
|
+
delete(heir: string): void {
|
|
106
|
+
delete this.#head.heirs[heir];
|
|
107
|
+
this.#partition.toldHead();
|
|
317
108
|
}
|
|
318
|
-
// Behind the guard and never through it: a being's cells in a partition
|
|
319
|
-
// are the guarded object her stance writes through, and the guard is hers.
|
|
320
|
-
// What the harbor puts back is not a write of hers, so it says nothing to
|
|
321
|
-
// the ward and marks nothing dirty.
|
|
322
|
-
const into = (rows: Record<string, unknown>, kept: Record<string, unknown>): void => {
|
|
323
|
-
const guarded = at(rows, row) as Cells | undefined;
|
|
324
|
-
if (guarded === undefined) return void put(rows, row, kept);
|
|
325
|
-
const held = unguarded(guarded) as unknown as Record<string, unknown>;
|
|
326
|
-
for (const k of Object.keys(held)) drop(held, k);
|
|
327
|
-
for (const [k, v] of Object.entries(kept)) put(held, k, v);
|
|
328
|
-
};
|
|
329
|
-
into(p.beings ?? {}, (value.cells ?? emptyCells()) as unknown as Record<string, unknown>);
|
|
330
|
-
};
|
|
331
109
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
export const fromRows = (rows: Record<string, Record<string, unknown>>): Record<string, unknown> => {
|
|
335
|
-
const out: Record<string, unknown> = { ...(rows[HEAD] ?? {}) };
|
|
336
|
-
const beings: Record<string, unknown> = {};
|
|
337
|
-
const bind: Record<string, unknown> = {};
|
|
338
|
-
let any = Object.keys(out).length > 0;
|
|
339
|
-
for (const [row, value] of Object.entries(rows)) {
|
|
340
|
-
if (row === HEAD) continue;
|
|
341
|
-
put(beings, row, value.cells);
|
|
342
|
-
put(bind, row, value.bind);
|
|
343
|
-
any = true;
|
|
110
|
+
kept(): Promise<boolean> {
|
|
111
|
+
return this.#partition.kept();
|
|
344
112
|
}
|
|
345
|
-
|
|
346
|
-
// rows must come back as the empty memory they are, because `open` reads an
|
|
347
|
-
// empty one as a ward's first breath and anything else as a partition
|
|
348
|
-
// somebody wrote, which must then say which version wrote it.
|
|
349
|
-
if (!any) return {};
|
|
350
|
-
out.beings = beings;
|
|
351
|
-
out.bind = bind;
|
|
352
|
-
return out;
|
|
353
|
-
};
|
|
113
|
+
}
|