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/conformance/reach.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The reach conformance suite: what every reach must show, written against
|
|
3
|
-
// the interface alone. A reach is handed in as a maker of a fresh line to a
|
|
4
|
-
// far side the suite controls: `hold` puts a door behind a pk over there,
|
|
5
|
-
// `drop` takes the far side away, the line down or the listener gone. The
|
|
6
|
-
// suite asks for bytes and expects the door's bytes back, nothing for a pk
|
|
7
|
-
// nobody holds, and nothing once the far side is gone. It cannot assert
|
|
8
|
-
// that a reach which sent and then lost the line answers nothing at all,
|
|
9
|
-
// since a suite cannot wait forever; that promise is read in the code.
|
|
10
|
-
import { assert } from './assert.ts';
|
|
11
|
-
import type { Reach } from '../harbor/reach.ts';
|
|
12
|
-
import type { Runner } from './index.ts';
|
|
13
|
-
|
|
14
|
-
export type FarSide = {
|
|
15
|
-
reach: Reach;
|
|
16
|
-
hold(pk: string, door: (bytes: Uint8Array) => Promise<Uint8Array>): Promise<void>;
|
|
17
|
-
drop(): Promise<void>;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const pk = (b: string) => b.repeat(64);
|
|
21
|
-
const bytes = (...xs: number[]) => Uint8Array.from(xs);
|
|
22
|
-
const wait = (ms: number) => new Promise<void>((ok) => setTimeout(ok, ms));
|
|
23
|
-
|
|
24
|
-
export function conformReach(label: string, make: () => Promise<FarSide>, { test }: { test: Runner }) {
|
|
25
|
-
const t = (name: string, fn: () => Promise<void>) => test(`[${label}] ${name}`, {}, fn);
|
|
26
|
-
|
|
27
|
-
t('bytes to a pk the far side holds come back as the door answered', async () => {
|
|
28
|
-
const far = await make();
|
|
29
|
-
try {
|
|
30
|
-
await far.hold(pk('aa'), async (b) => bytes(...b, 9));
|
|
31
|
-
assert.deepEqual(await far.reach.carry(pk('aa'), bytes(1, 2, 3)), bytes(1, 2, 3, 9));
|
|
32
|
-
} finally {
|
|
33
|
-
await far.drop();
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
t('bytes to a pk nobody holds are nothing', async () => {
|
|
38
|
-
const far = await make();
|
|
39
|
-
try {
|
|
40
|
-
await far.hold(pk('aa'), async (b) => b);
|
|
41
|
-
assert.equal(await far.reach.carry(pk('bb'), bytes(1)), undefined);
|
|
42
|
-
} finally {
|
|
43
|
-
await far.drop();
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
t('asks in flight at once each get their own answer', async () => {
|
|
48
|
-
const far = await make();
|
|
49
|
-
try {
|
|
50
|
-
await far.hold(pk('aa'), async (b) => (await wait(b[0] * 10), bytes(b[0])));
|
|
51
|
-
const out = await Promise.all([far.reach.carry(pk('aa'), bytes(3)), far.reach.carry(pk('aa'), bytes(1)), far.reach.carry(pk('aa'), bytes(2))]);
|
|
52
|
-
assert.deepEqual(out, [bytes(3), bytes(1), bytes(2)]);
|
|
53
|
-
} finally {
|
|
54
|
-
await far.drop();
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
t('what comes back is a copy, and what was sent was one', async () => {
|
|
59
|
-
const far = await make();
|
|
60
|
-
try {
|
|
61
|
-
let seen: Uint8Array | undefined;
|
|
62
|
-
const reply = bytes(7, 7);
|
|
63
|
-
await far.hold(pk('aa'), async (b) => ((seen = b), reply));
|
|
64
|
-
const sent = bytes(4, 5);
|
|
65
|
-
const back = (await far.reach.carry(pk('aa'), sent))!;
|
|
66
|
-
assert.deepEqual(back, bytes(7, 7));
|
|
67
|
-
back[0] = 0;
|
|
68
|
-
assert.deepEqual(reply, bytes(7, 7), 'the caller wrote through to the door');
|
|
69
|
-
seen![0] = 0;
|
|
70
|
-
assert.deepEqual(sent, bytes(4, 5), 'the door wrote through to the caller');
|
|
71
|
-
} finally {
|
|
72
|
-
await far.drop();
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
t('once the far side is gone, bytes are nothing', async () => {
|
|
77
|
-
const far = await make();
|
|
78
|
-
await far.hold(pk('aa'), async (b) => b);
|
|
79
|
-
assert.deepEqual(await far.reach.carry(pk('aa'), bytes(1)), bytes(1));
|
|
80
|
-
await far.drop();
|
|
81
|
-
assert.equal(await far.reach.carry(pk('aa'), bytes(1)), undefined);
|
|
82
|
-
});
|
|
83
|
-
}
|
package/src/conformance/store.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The store conformance suite: what every store must show, written against
|
|
3
|
-
// the interface alone. A store is handed in as a maker of fresh ones: an
|
|
4
|
-
// empty folder, a database nobody opened, a view of storage under a prefix
|
|
5
|
-
// nobody used. Nothing here knows how a store keeps anything; it asks for
|
|
6
|
-
// what it put and expects it back, as the harbor will.
|
|
7
|
-
import { assert } from './assert.ts';
|
|
8
|
-
import type { Kept, Store } from '../harbor/store.ts';
|
|
9
|
-
import type { Runner } from './index.ts';
|
|
10
|
-
|
|
11
|
-
const seed = (b: number) => new Uint8Array(32).fill(b);
|
|
12
|
-
const kept = (b: number, extra: Record<string, unknown> = {}): Kept => ({ seed: seed(b), partition: { beings: {}, bind: {}, ...extra }, record: { pk: '', code: 'classes/index.ts', user: 'me' } });
|
|
13
|
-
// A partition as a ward writes one: beings under their keys, a bind table
|
|
14
|
-
// beside each, and whatever else the head carries.
|
|
15
|
-
const ward = (beings: Record<string, unknown>, extra: Record<string, unknown> = {}): Record<string, unknown> => ({
|
|
16
|
-
beings,
|
|
17
|
-
bind: Object.fromEntries(Object.keys(beings).map((k) => [k, { standings: {} }])),
|
|
18
|
-
...extra,
|
|
19
|
-
});
|
|
20
|
-
const ALL = (p: Record<string, unknown>): string[] => ['', ...Object.keys(p.beings as object)];
|
|
21
|
-
|
|
22
|
-
export function conformStore(label: string, make: () => Promise<Store>, { test }: { test: Runner }) {
|
|
23
|
-
const t = (name: string, fn: () => Promise<void>) => test(`[${label}] ${name}`, {}, fn);
|
|
24
|
-
|
|
25
|
-
t('a fresh store keeps nothing', async () => {
|
|
26
|
-
const s = await make();
|
|
27
|
-
assert.deepEqual(await s.list(), []);
|
|
28
|
-
assert.equal(await s.load('main'), undefined);
|
|
29
|
-
assert.equal(await s.take('main'), undefined);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
t('put, then load: seed, partition and record come back as they went in', async () => {
|
|
33
|
-
const s = await make();
|
|
34
|
-
await s.put('main', kept(7, { n: 1, deep: { list: [1, 'two', null], flag: true } }));
|
|
35
|
-
assert.deepEqual(await s.list(), ['main']);
|
|
36
|
-
const back = await s.load('main');
|
|
37
|
-
assert.ok(back);
|
|
38
|
-
assert.deepEqual(back.seed, seed(7));
|
|
39
|
-
assert.deepEqual(back.partition, { beings: {}, bind: {}, n: 1, deep: { list: [1, 'two', null], flag: true } });
|
|
40
|
-
assert.deepEqual(back.record, { pk: '', code: 'classes/index.ts', user: 'me' });
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
t('a name already kept is refused, and what was kept stands', async () => {
|
|
44
|
-
const s = await make();
|
|
45
|
-
await s.put('main', kept(1));
|
|
46
|
-
let refused = false;
|
|
47
|
-
try {
|
|
48
|
-
await s.put('main', kept(2));
|
|
49
|
-
} catch {
|
|
50
|
-
refused = true;
|
|
51
|
-
}
|
|
52
|
-
assert.ok(refused, 'a second put under one name must throw');
|
|
53
|
-
assert.deepEqual((await s.load('main'))!.seed, seed(1));
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
t('save writes every row it is given, and touches nothing else', async () => {
|
|
57
|
-
const s = await make();
|
|
58
|
-
await s.put('main', kept(3));
|
|
59
|
-
const p = ward({ a: { x: 1 } });
|
|
60
|
-
await s.save('main', p, ALL(p));
|
|
61
|
-
const back = (await s.load('main'))!;
|
|
62
|
-
assert.deepEqual(back.partition, p);
|
|
63
|
-
assert.deepEqual(back.seed, seed(3));
|
|
64
|
-
assert.deepEqual(back.record, kept(3).record);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// What a store may do with the rows it is told about is its own: writing
|
|
68
|
-
// the whole partition every time is correct. What it may never do is write
|
|
69
|
-
// less than it was told, or keep a being the rows say is gone.
|
|
70
|
-
t('save writes the rows it names, and a being the rows drop is gone', async () => {
|
|
71
|
-
const s = await make();
|
|
72
|
-
const p = ward({ a: { x: 1 }, b: { y: 1 } });
|
|
73
|
-
await s.put('main', { ...kept(3), partition: p });
|
|
74
|
-
// one being moves, and only her row is named
|
|
75
|
-
(p.beings as Record<string, { x: number }>).a.x = 2;
|
|
76
|
-
await s.save('main', p, ['a']);
|
|
77
|
-
assert.deepEqual((await s.load('main'))!.partition, p, 'the row that was named is written');
|
|
78
|
-
// she leaves: her row is named and her key is gone from the partition
|
|
79
|
-
delete (p.beings as Record<string, unknown>).a;
|
|
80
|
-
delete (p.bind as Record<string, unknown>).a;
|
|
81
|
-
await s.save('main', p, ['a']);
|
|
82
|
-
assert.deepEqual((await s.load('main'))!.partition, p, 'a row whose being is gone is dropped');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
t('record replaces the record and touches nothing else', async () => {
|
|
86
|
-
const s = await make();
|
|
87
|
-
await s.put('main', kept(4, { n: 9 }));
|
|
88
|
-
await s.record('main', { pk: 'ab'.repeat(64), code: 'elsewhere.ts', user: 'her' });
|
|
89
|
-
const back = (await s.load('main'))!;
|
|
90
|
-
assert.deepEqual(back.record, { pk: 'ab'.repeat(64), code: 'elsewhere.ts', user: 'her' });
|
|
91
|
-
assert.deepEqual(back.partition, { beings: {}, bind: {}, n: 9 });
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
t('save and record on a name not kept are nothing', async () => {
|
|
95
|
-
const s = await make();
|
|
96
|
-
await s.save('ghost', { beings: {}, bind: {} }, ['']);
|
|
97
|
-
await s.record('ghost', { pk: '', code: '', user: '' });
|
|
98
|
-
assert.deepEqual(await s.list(), []);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
t('the partition handed back is values, not the object that was put', async () => {
|
|
102
|
-
const s = await make();
|
|
103
|
-
const k = kept(5, { n: 1 });
|
|
104
|
-
await s.put('main', k);
|
|
105
|
-
(k.partition as { n: number }).n = 2;
|
|
106
|
-
assert.deepEqual((await s.load('main'))!.partition, { beings: {}, bind: {}, n: 1 });
|
|
107
|
-
const a = (await s.load('main'))!;
|
|
108
|
-
(a.partition as { n: number }).n = 3;
|
|
109
|
-
assert.deepEqual((await s.load('main'))!.partition, { beings: {}, bind: {}, n: 1 });
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
t('take hands the ward out and forgets it; the name is free again', async () => {
|
|
113
|
-
const s = await make();
|
|
114
|
-
await s.put('main', kept(6, { n: 6 }));
|
|
115
|
-
await s.put('other', kept(8));
|
|
116
|
-
const out = await s.take('main');
|
|
117
|
-
assert.ok(out);
|
|
118
|
-
assert.deepEqual(out.seed, seed(6));
|
|
119
|
-
assert.deepEqual(out.partition, { beings: {}, bind: {}, n: 6 });
|
|
120
|
-
assert.equal(await s.load('main'), undefined);
|
|
121
|
-
assert.deepEqual(await s.list(), ['other']);
|
|
122
|
-
await s.put('main', kept(9));
|
|
123
|
-
assert.deepEqual((await s.load('main'))!.seed, seed(9));
|
|
124
|
-
});
|
|
125
|
-
}
|
package/src/harbor/box.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The box as one being. A harbor boots a ward of its own and one being in
|
|
3
|
-
// it, the box's own, who holds a standing at every lent being the box
|
|
4
|
-
// offers, under the name each is lent as, and keeps the roll of the wards
|
|
5
|
-
// the box hosts. Every hosted ward is handed one invitation on her at boot
|
|
6
|
-
// and takes it as its own standing; from then on the device is one
|
|
7
|
-
// standing, what it offers is her describe, and a lend is the ask `offer`
|
|
8
|
-
// on her.
|
|
9
|
-
//
|
|
10
|
-
// The harbor works in acts and holds no memory of its own. Act one boots the
|
|
11
|
-
// box's ward on the plain ground, no box, because it is the box; her lent
|
|
12
|
-
// beings come up, and the root places her standings and nothing else. Act
|
|
13
|
-
// two asks her who is hosted and boots each with one invitation. Everything
|
|
14
|
-
// the device is lives in her ward as a lent being's cells, placed through
|
|
15
|
-
// asks and read from no file.
|
|
16
|
-
//
|
|
17
|
-
// A runtime that needs her to be more, a resolver of names or a carrier of
|
|
18
|
-
// her lent beings' asks for a screen, extends her and overrides the asks.
|
|
19
|
-
import { Being, isInvitation, OWNER } from '../being/index.ts';
|
|
20
|
-
import { BOX } from '../being/lent.ts';
|
|
21
|
-
import type { Asker, BeingClass, Invitation, JsonObject, OccupantRecord } from '../being/index.ts';
|
|
22
|
-
import { OFFER, RETRACT, type WardPointers } from '../ward/ground.ts';
|
|
23
|
-
|
|
24
|
-
// A ward as its root holds it: the ask pointer is all the acts need.
|
|
25
|
-
export type Root = Pick<WardPointers, 'ask'>;
|
|
26
|
-
|
|
27
|
-
export { BOX } from '../being/lent.ts';
|
|
28
|
-
|
|
29
|
-
// The name of the box's own ward, one per harbor: the seed a memory harbor
|
|
30
|
-
// boots it under, and the name a store keeps it by.
|
|
31
|
-
export const BOX_SEED = 'box';
|
|
32
|
-
|
|
33
|
-
// A ward the box vouches for: invited by the root at act two, with the one
|
|
34
|
-
// note that says so. A screen or a model the root lets in has no such note
|
|
35
|
-
// and is shown nothing of the lend seam.
|
|
36
|
-
const ward = (occ: OccupantRecord | undefined) => occ?.notes.ward === true;
|
|
37
|
-
const root = (_occ: OccupantRecord | undefined, asker: Asker) => asker.id === OWNER;
|
|
38
|
-
|
|
39
|
-
export const BOX_ASKS = {
|
|
40
|
-
[OFFER]: { description: 'an invitation on the being lent under this name', input: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] }, for: ward },
|
|
41
|
-
[RETRACT]: { description: 'an offer under this name that was not taken, by its heir', input: { type: 'object', properties: { name: { type: 'string' }, heir: { type: 'string' } }, required: ['name', 'heir'] }, for: ward },
|
|
42
|
-
host: { description: 'a ward this box hosts from now', input: { type: 'object', properties: { seed: { type: 'string' } }, required: ['seed'] }, for: root },
|
|
43
|
-
roll: { description: 'the wards this box hosts', input: { type: 'object' }, for: root },
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export class Box extends Being {
|
|
47
|
-
static override cells: JsonObject = { hosted: [], offers: {} };
|
|
48
|
-
static override asks = BOX_ASKS;
|
|
49
|
-
|
|
50
|
-
// Offer: the being under the name, asked to offer, answers her own
|
|
51
|
-
// invitation. A subclass that lends under more names than it holds
|
|
52
|
-
// standings, a contract's digest for one, resolves the name first and
|
|
53
|
-
// calls this with the standing's own.
|
|
54
|
-
async offer(args: JsonObject): Promise<JsonObject> {
|
|
55
|
-
const at = this.standings[args.name as string];
|
|
56
|
-
if (!at) return { error: 'lends none' };
|
|
57
|
-
const out = await at.ask(OFFER);
|
|
58
|
-
return isInvitation(out) ? out : { error: 'not offered' };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async retract(args: JsonObject): Promise<JsonObject> {
|
|
62
|
-
const at = this.standings[args.name as string];
|
|
63
|
-
if (!at) return { error: 'lends none' };
|
|
64
|
-
const out = await at.ask(RETRACT, { heir: args.heir as string });
|
|
65
|
-
return out !== null && typeof out === 'object' && !Array.isArray(out) ? out : { retracted: null };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// A ward on the roll, and its one invitation on her. She mints it the first
|
|
69
|
-
// time and keeps it, and hands the same one at every later boot until it
|
|
70
|
-
// is spent: a ward that could not take the box at birth, the box down or
|
|
71
|
-
// the knock lost, takes it at the next, as the spec promises. A ward that
|
|
72
|
-
// holds the standing already ignores it, and a spent heir opens no door,
|
|
73
|
-
// so handing it again costs nothing. The value never leaves her ward: it
|
|
74
|
-
// is read by the root alone and rides in the ground and nowhere else.
|
|
75
|
-
async host(args: JsonObject): Promise<JsonObject> {
|
|
76
|
-
const hosted = ((this.cells.hosted as string[] | undefined) ??= []);
|
|
77
|
-
const offers = ((this.cells.offers as Record<string, Invitation> | undefined) ??= {});
|
|
78
|
-
if (typeof args.seed !== 'string') return { error: 'a seed is a word' };
|
|
79
|
-
if (!hosted.includes(args.seed)) hosted.push(args.seed);
|
|
80
|
-
if (!Object.hasOwn(offers, args.seed)) {
|
|
81
|
-
const inv = await this.invite(args.seed, { ward: true });
|
|
82
|
-
if (inv !== null) offers[args.seed] = inv;
|
|
83
|
-
}
|
|
84
|
-
return { hosted: [...hosted], invitation: offers[args.seed] ?? null };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
roll(): JsonObject {
|
|
88
|
-
return { hosted: [...((this.cells.hosted as string[] | undefined) ?? [])] };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Act one's second half, on a box ward already booted: the box's being and
|
|
93
|
-
// each lent being booted by key, and her standing placed at each under its
|
|
94
|
-
// key by the root. A ward woken from its memory already holds all of it,
|
|
95
|
-
// and a key already booted is refused and costs nothing, so this is run at
|
|
96
|
-
// every boot and does the right thing at each.
|
|
97
|
-
export async function stand(box: Root, lent: Record<string, BeingClass>, own = 'Box'): Promise<void> {
|
|
98
|
-
const up = async (key: string, cls: string) => {
|
|
99
|
-
const out = (await box.ask('boot', { key, class: cls })) as { booted?: string; error?: string };
|
|
100
|
-
return out.booted === key;
|
|
101
|
-
};
|
|
102
|
-
await up(BOX, own);
|
|
103
|
-
for (const name of Object.keys(lent)) {
|
|
104
|
-
const fresh = await up(name, lent[name].name);
|
|
105
|
-
if (!fresh) continue;
|
|
106
|
-
await hold(box, name);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// The box's standing at one lent being of her ward, under that being's key,
|
|
111
|
-
// placed by the root: the lent being invites the box's being as `box`, and
|
|
112
|
-
// she knocks. Refused where either already holds the relation.
|
|
113
|
-
export async function hold(box: Root, key: string): Promise<boolean> {
|
|
114
|
-
const inv = await box.ask('invite', { being: key, id: BOX });
|
|
115
|
-
if (!isInvitation(inv)) return false;
|
|
116
|
-
const out = (await box.ask('knock', { being: BOX, id: key, invitation: inv as unknown as JsonObject })) as { taken?: unknown };
|
|
117
|
-
return out?.taken === key;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// One invitation on the box's being for the ward named, and the ward on the
|
|
121
|
-
// roll: the same one at every boot, minted once by her and kept by her.
|
|
122
|
-
export async function offerTo(box: Root, seed: string): Promise<Invitation | undefined> {
|
|
123
|
-
const out = (await box.ask('ask', { being: BOX, method: 'host', args: { seed } })) as { invitation?: unknown };
|
|
124
|
-
return isInvitation(out?.invitation) ? out.invitation : undefined;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Who the box hosts, asked of the box's being.
|
|
128
|
-
export async function roll(box: Root): Promise<string[]> {
|
|
129
|
-
const out = (await box.ask('ask', { being: BOX, method: 'roll' })) as { hosted?: string[] };
|
|
130
|
-
return out.hosted ?? [];
|
|
131
|
-
}
|