nervur 0.21.4 → 0.22.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 +215 -158
- package/dist/browser.js +4 -3
- package/dist/cli.js +16 -0
- package/dist/{being → core/being}/being.js +10 -0
- package/dist/{being → core/being}/faculty.js +5 -2
- package/dist/{being → core/being}/index.d.ts +1 -1
- package/dist/{being → core/being}/index.js +1 -1
- package/dist/{being → core/being}/kind.d.ts +2 -0
- package/dist/{being → core/being}/kind.js +6 -0
- package/dist/{being → core/being}/types.js +1 -1
- package/dist/{browser → core/browser}/index.d.ts +6 -5
- package/dist/{browser → core/browser}/index.js +10 -10
- package/dist/{browser → core/browser}/worker.d.ts +2 -3
- package/dist/{browser → core/browser}/worker.js +8 -11
- package/dist/{cli → core/cli}/command.d.ts +4 -4
- package/dist/{cli → core/cli}/command.js +116 -66
- package/dist/{cli → core/cli}/edge.js +4 -2
- package/dist/core/cli/harbor.d.ts +25 -0
- package/dist/{cli → core/cli}/harbor.js +45 -33
- package/dist/{contract → core/contract}/index.d.ts +6 -5
- package/dist/{contract → core/contract}/index.js +8 -9
- package/dist/core/contract/link.d.ts +4 -0
- package/dist/core/contract/link.js +45 -0
- package/dist/{crypto → core/crypto}/ed25519.js +22 -14
- package/dist/core/crypto/subtle.d.ts +3 -0
- package/dist/core/crypto/subtle.js +11 -0
- package/dist/core/crypto/x25519.js +24 -0
- package/dist/core/edge/index.d.ts +164 -0
- package/dist/core/edge/index.js +739 -0
- package/dist/core/edge/kit.d.ts +2 -0
- package/dist/core/edge/kit.js +2 -0
- package/dist/core/git/http.d.ts +6 -0
- package/dist/core/git/http.js +102 -0
- package/dist/core/git/index.d.ts +5 -0
- package/dist/core/git/index.js +10 -0
- package/dist/core/git/object.d.ts +40 -0
- package/dist/core/git/object.js +103 -0
- package/dist/core/git/pack.d.ts +9 -0
- package/dist/core/git/pack.js +171 -0
- package/dist/core/git/store.d.ts +23 -0
- package/dist/core/git/store.js +120 -0
- package/dist/core/git/zlib.d.ts +6 -0
- package/dist/core/git/zlib.js +210 -0
- package/dist/core/harbor/carrying.d.ts +51 -0
- package/dist/core/harbor/carrying.js +69 -0
- package/dist/core/harbor/catalogue.d.ts +33 -0
- package/dist/core/harbor/catalogue.js +365 -0
- package/dist/core/harbor/dna.d.ts +13 -0
- package/dist/core/harbor/dna.js +120 -0
- package/dist/{harbor → core/harbor}/dock.d.ts +22 -13
- package/dist/core/harbor/dock.js +381 -0
- package/dist/core/harbor/harbor.d.ts +16 -0
- package/dist/core/harbor/harbor.js +438 -0
- package/dist/core/harbor/index.d.ts +11 -0
- package/dist/{harbor → core/harbor}/index.js +5 -5
- package/dist/core/harbor/memory.d.ts +10 -0
- package/dist/core/harbor/memory.js +25 -0
- package/dist/{harbor → core/harbor}/package.d.ts +0 -1
- package/dist/{harbor → core/harbor}/package.js +9 -9
- package/dist/core/harbor/probe.d.ts +22 -0
- package/dist/core/harbor/probe.js +15 -0
- package/dist/core/harbor/registry.d.ts +15 -0
- package/dist/{harbor → core/harbor}/registry.js +26 -10
- package/dist/core/harbor/root-line.d.ts +7 -0
- package/dist/core/harbor/root-line.js +9 -0
- package/dist/{harbor → core/harbor}/terrain.d.ts +9 -6
- package/dist/{harbor → core/harbor}/terrain.js +5 -4
- package/dist/core/http/index.d.ts +2 -0
- package/dist/{http → core/http}/index.js +3 -5
- package/dist/{line → core/line}/answer.d.ts +2 -2
- package/dist/{line → core/line}/frame.d.ts +0 -5
- package/dist/{line → core/line}/frame.js +0 -4
- package/dist/core/line/ground.js +7 -0
- package/dist/core/line/index.d.ts +4 -0
- package/dist/core/line/index.js +8 -0
- package/dist/core/line/web.d.ts +12 -0
- package/dist/{line → core/line}/web.js +13 -77
- package/dist/{node → core/node}/index.d.ts +2 -15
- package/dist/core/node/index.js +124 -0
- package/dist/{pointer → core/pointer}/bodies.d.ts +7 -5
- package/dist/core/pointer/bodies.js +118 -0
- package/dist/core/pointer/index.d.ts +2 -0
- package/dist/{pointer → core/pointer}/index.js +1 -1
- package/dist/{pointer → core/pointer}/world.d.ts +13 -9
- package/dist/{pointer → core/pointer}/world.js +31 -16
- package/dist/{proof → core/proof}/contracts.d.ts +6 -2
- package/dist/{proof → core/proof}/contracts.js +11 -10
- package/dist/core/proof/index.d.ts +3 -0
- package/dist/{proof → core/proof}/index.js +2 -2
- package/dist/core/proof/law.d.ts +25 -0
- package/dist/core/proof/law.js +715 -0
- package/dist/{tcp → core/tcp}/index.d.ts +7 -4
- package/dist/{tcp → core/tcp}/index.js +11 -62
- package/dist/{ward/ward.d.ts → core/ward/house.d.ts} +24 -10
- package/dist/core/ward/house.js +473 -0
- package/dist/core/ward/index.d.ts +5 -0
- package/dist/core/ward/index.js +7 -0
- package/dist/{ward → core/ward}/partition.d.ts +1 -0
- package/dist/{ward → core/ward}/stance.d.ts +2 -2
- package/dist/{ward → core/ward}/stance.js +5 -2
- package/dist/{ward/ward-being.d.ts → core/ward/ward.d.ts} +14 -6
- package/dist/core/ward/ward.js +140 -0
- package/dist/core.d.ts +10 -0
- package/dist/core.js +19 -0
- package/dist/defaults/dock/index.d.ts +4 -0
- package/dist/defaults/dock/index.js +11 -0
- package/dist/defaults/index.d.ts +5 -0
- package/dist/defaults/index.js +10 -0
- package/dist/defaults/memory/index.d.ts +12 -0
- package/dist/defaults/memory/index.js +39 -0
- package/dist/defaults/tcp/index.d.ts +16 -0
- package/dist/defaults/tcp/index.js +82 -0
- package/dist/defaults/ward/index.d.ts +4 -0
- package/dist/defaults/ward/index.js +11 -0
- package/dist/defaults/web/index.d.ts +15 -0
- package/dist/defaults/web/index.js +90 -0
- package/dist/edge.js +4 -3
- package/dist/index.d.ts +1 -8
- package/dist/index.js +13 -18
- package/dist/kit.d.ts +4 -0
- package/dist/kit.js +12 -0
- package/dist/node.js +4 -3
- package/package.json +23 -18
- package/src/browser.ts +4 -3
- package/src/cli.ts +15 -0
- package/src/{being → core/being}/being.ts +4 -0
- package/src/{being → core/being}/faculty.ts +5 -2
- package/src/{being → core/being}/index.ts +1 -1
- package/src/{being → core/being}/kind.ts +7 -0
- package/src/{being → core/being}/types.ts +1 -1
- package/src/{browser → core/browser}/index.ts +16 -13
- package/src/{browser → core/browser}/worker.ts +11 -14
- package/src/{cli → core/cli}/command.ts +113 -66
- package/src/{cli → core/cli}/edge.ts +4 -2
- package/src/{cli → core/cli}/harbor.ts +48 -31
- package/src/{contract → core/contract}/index.ts +14 -14
- package/src/core/contract/link.ts +58 -0
- package/src/{crypto → core/crypto}/ed25519.ts +22 -14
- package/src/core/crypto/subtle.ts +13 -0
- package/src/core/crypto/x25519.ts +23 -0
- package/src/core/edge/index.ts +865 -0
- package/src/core/git/http.ts +103 -0
- package/src/core/git/index.ts +10 -0
- package/src/core/git/object.ts +116 -0
- package/src/core/git/pack.ts +147 -0
- package/src/core/git/store.ts +122 -0
- package/src/core/git/zlib.ts +197 -0
- package/src/core/harbor/carrying.ts +124 -0
- package/src/core/harbor/catalogue.ts +386 -0
- package/src/core/harbor/dna.ts +116 -0
- package/src/core/harbor/dock.ts +420 -0
- package/src/core/harbor/harbor.ts +433 -0
- package/src/core/harbor/index.ts +14 -0
- package/src/core/harbor/memory.ts +31 -0
- package/src/{harbor → core/harbor}/package.ts +15 -15
- package/src/core/harbor/probe.ts +39 -0
- package/src/{harbor → core/harbor}/registry.ts +28 -11
- package/src/core/harbor/root-line.ts +19 -0
- package/src/{harbor → core/harbor}/terrain.ts +14 -7
- package/src/{http → core/http}/index.ts +5 -8
- package/src/{line → core/line}/answer.ts +3 -2
- package/src/{line → core/line}/frame.ts +0 -5
- package/src/core/line/ground.ts +18 -0
- package/src/core/line/index.ts +8 -0
- package/src/{line → core/line}/web.ts +1 -105
- package/src/core/node/index.ts +139 -0
- package/src/core/pointer/bodies.ts +122 -0
- package/src/core/pointer/index.ts +4 -0
- package/src/{pointer → core/pointer}/world.ts +45 -21
- package/src/{proof → core/proof}/contracts.ts +18 -13
- package/src/{proof → core/proof}/index.ts +3 -3
- package/src/core/proof/law.ts +742 -0
- package/src/{stand → core/stand}/stand.ts +2 -2
- package/src/{tcp → core/tcp}/index.ts +17 -74
- package/src/core/ward/house.ts +524 -0
- package/src/core/ward/index.ts +7 -0
- package/src/{ward → core/ward}/partition.ts +5 -1
- package/src/{ward → core/ward}/stance.ts +5 -2
- package/src/core/ward/ward.ts +166 -0
- package/src/core.ts +55 -0
- package/src/defaults/dock/index.ts +12 -0
- package/src/defaults/index.ts +10 -0
- package/src/defaults/memory/index.ts +50 -0
- package/src/defaults/tcp/index.ts +89 -0
- package/src/defaults/ward/index.ts +12 -0
- package/src/defaults/web/index.ts +97 -0
- package/src/edge.ts +4 -3
- package/src/index.ts +13 -50
- package/src/kit.ts +15 -0
- package/src/node.ts +4 -3
- package/dist/cli/harbor.d.ts +0 -16
- package/dist/cli/main.js +0 -14
- package/dist/crypto/subtle.d.ts +0 -10
- package/dist/crypto/subtle.js +0 -29
- package/dist/crypto/x25519.js +0 -21
- package/dist/edge/index.d.ts +0 -81
- package/dist/edge/index.js +0 -400
- package/dist/harbor/carrying.d.ts +0 -17
- package/dist/harbor/carrying.js +0 -38
- package/dist/harbor/catalogue.d.ts +0 -28
- package/dist/harbor/catalogue.js +0 -109
- package/dist/harbor/dock.js +0 -236
- package/dist/harbor/harbor.d.ts +0 -45
- package/dist/harbor/harbor.js +0 -347
- package/dist/harbor/index.d.ts +0 -11
- package/dist/harbor/probe.d.ts +0 -18
- package/dist/harbor/probe.js +0 -15
- package/dist/harbor/registry.d.ts +0 -10
- package/dist/harbor/relay.d.ts +0 -15
- package/dist/harbor/relay.js +0 -406
- package/dist/harbor/root-line.d.ts +0 -9
- package/dist/harbor/root-line.js +0 -38
- package/dist/harbor/store.d.ts +0 -13
- package/dist/harbor/store.js +0 -53
- package/dist/http/index.d.ts +0 -4
- package/dist/line/ground.js +0 -9
- package/dist/line/index.d.ts +0 -5
- package/dist/line/index.js +0 -9
- package/dist/line/push.d.ts +0 -7
- package/dist/line/push.js +0 -92
- package/dist/line/web.d.ts +0 -30
- package/dist/node/index.js +0 -236
- package/dist/pointer/bodies.js +0 -80
- package/dist/pointer/index.d.ts +0 -2
- package/dist/proof/index.d.ts +0 -3
- package/dist/proof/law.d.ts +0 -90
- package/dist/proof/law.js +0 -591
- package/dist/tcp/frame.d.ts +0 -25
- package/dist/tcp/frame.js +0 -64
- package/dist/ward/index.d.ts +0 -6
- package/dist/ward/index.js +0 -8
- package/dist/ward/pilot.d.ts +0 -8
- package/dist/ward/pilot.js +0 -50
- package/dist/ward/ward-being.js +0 -82
- package/dist/ward/ward.js +0 -290
- package/src/cli/main.ts +0 -13
- package/src/crypto/subtle.ts +0 -33
- package/src/crypto/x25519.ts +0 -21
- package/src/edge/index.ts +0 -490
- package/src/harbor/carrying.ts +0 -56
- package/src/harbor/catalogue.ts +0 -134
- package/src/harbor/dock.ts +0 -265
- package/src/harbor/harbor.ts +0 -381
- package/src/harbor/index.ts +0 -14
- package/src/harbor/probe.ts +0 -34
- package/src/harbor/relay.ts +0 -418
- package/src/harbor/root-line.ts +0 -39
- package/src/harbor/store.ts +0 -69
- package/src/line/ground.ts +0 -19
- package/src/line/index.ts +0 -9
- package/src/line/push.ts +0 -112
- package/src/node/index.ts +0 -241
- package/src/pointer/bodies.ts +0 -82
- package/src/pointer/index.ts +0 -4
- package/src/proof/law.ts +0 -591
- package/src/ward/index.ts +0 -8
- package/src/ward/pilot.ts +0 -50
- package/src/ward/ward-being.ts +0 -103
- package/src/ward/ward.ts +0 -328
- /package/dist/{cli/main.d.ts → cli.d.ts} +0 -0
- /package/dist/{being → core/being}/being.d.ts +0 -0
- /package/dist/{being → core/being}/digest.d.ts +0 -0
- /package/dist/{being → core/being}/digest.js +0 -0
- /package/dist/{being → core/being}/faculty.d.ts +0 -0
- /package/dist/{being → core/being}/types.d.ts +0 -0
- /package/dist/{being → core/being}/words.d.ts +0 -0
- /package/dist/{being → core/being}/words.js +0 -0
- /package/dist/{cli → core/cli}/edge.d.ts +0 -0
- /package/dist/{crypto → core/crypto}/aes.d.ts +0 -0
- /package/dist/{crypto → core/crypto}/aes.js +0 -0
- /package/dist/{crypto → core/crypto}/bytes.d.ts +0 -0
- /package/dist/{crypto → core/crypto}/bytes.js +0 -0
- /package/dist/{crypto → core/crypto}/ed25519.d.ts +0 -0
- /package/dist/{crypto → core/crypto}/hash.d.ts +0 -0
- /package/dist/{crypto → core/crypto}/hash.js +0 -0
- /package/dist/{crypto → core/crypto}/index.d.ts +0 -0
- /package/dist/{crypto → core/crypto}/index.js +0 -0
- /package/dist/{crypto → core/crypto}/json.d.ts +0 -0
- /package/dist/{crypto → core/crypto}/json.js +0 -0
- /package/dist/{crypto → core/crypto}/mlkem.d.ts +0 -0
- /package/dist/{crypto → core/crypto}/mlkem.js +0 -0
- /package/dist/{crypto → core/crypto}/x25519.d.ts +0 -0
- /package/dist/{folder → core/folder}/index.d.ts +0 -0
- /package/dist/{folder → core/folder}/index.js +0 -0
- /package/dist/{http → core/http}/websocket.d.ts +0 -0
- /package/dist/{http → core/http}/websocket.js +0 -0
- /package/dist/{line → core/line}/answer.js +0 -0
- /package/dist/{line → core/line}/ground.d.ts +0 -0
- /package/dist/{proof → core/proof}/expect.d.ts +0 -0
- /package/dist/{proof → core/proof}/expect.js +0 -0
- /package/dist/{quo → core/quo}/address.d.ts +0 -0
- /package/dist/{quo → core/quo}/address.js +0 -0
- /package/dist/{quo → core/quo}/door.d.ts +0 -0
- /package/dist/{quo → core/quo}/door.js +0 -0
- /package/dist/{quo → core/quo}/index.d.ts +0 -0
- /package/dist/{quo → core/quo}/index.js +0 -0
- /package/dist/{quo → core/quo}/invitation.d.ts +0 -0
- /package/dist/{quo → core/quo}/invitation.js +0 -0
- /package/dist/{quo → core/quo}/keys.d.ts +0 -0
- /package/dist/{quo → core/quo}/keys.js +0 -0
- /package/dist/{quo → core/quo}/payload.d.ts +0 -0
- /package/dist/{quo → core/quo}/payload.js +0 -0
- /package/dist/{quo → core/quo}/relations.d.ts +0 -0
- /package/dist/{quo → core/quo}/relations.js +0 -0
- /package/dist/{quo → core/quo}/reply.d.ts +0 -0
- /package/dist/{quo → core/quo}/reply.js +0 -0
- /package/dist/{quo → core/quo}/seal.d.ts +0 -0
- /package/dist/{quo → core/quo}/seal.js +0 -0
- /package/dist/{quo → core/quo}/standing.d.ts +0 -0
- /package/dist/{quo → core/quo}/standing.js +0 -0
- /package/dist/{ward → core/ward}/allowance.d.ts +0 -0
- /package/dist/{ward → core/ward}/allowance.js +0 -0
- /package/dist/{ward → core/ward}/cells.d.ts +0 -0
- /package/dist/{ward → core/ward}/cells.js +0 -0
- /package/dist/{ward → core/ward}/partition.js +0 -0
- /package/src/{being → core/being}/digest.ts +0 -0
- /package/src/{being → core/being}/words.ts +0 -0
- /package/src/{crypto → core/crypto}/aes.ts +0 -0
- /package/src/{crypto → core/crypto}/bytes.ts +0 -0
- /package/src/{crypto → core/crypto}/hash.ts +0 -0
- /package/src/{crypto → core/crypto}/index.ts +0 -0
- /package/src/{crypto → core/crypto}/json.ts +0 -0
- /package/src/{crypto → core/crypto}/mlkem.ts +0 -0
- /package/src/{folder → core/folder}/index.ts +0 -0
- /package/src/{http → core/http}/websocket.ts +0 -0
- /package/src/{proof → core/proof}/expect.ts +0 -0
- /package/src/{quo → core/quo}/address.ts +0 -0
- /package/src/{quo → core/quo}/door.ts +0 -0
- /package/src/{quo → core/quo}/index.ts +0 -0
- /package/src/{quo → core/quo}/invitation.ts +0 -0
- /package/src/{quo → core/quo}/keys.ts +0 -0
- /package/src/{quo → core/quo}/payload.ts +0 -0
- /package/src/{quo → core/quo}/relations.ts +0 -0
- /package/src/{quo → core/quo}/reply.ts +0 -0
- /package/src/{quo → core/quo}/seal.ts +0 -0
- /package/src/{quo → core/quo}/standing.ts +0 -0
- /package/src/{stand → core/stand}/main.ts +0 -0
- /package/src/{ward → core/ward}/allowance.ts +0 -0
- /package/src/{ward → core/ward}/cells.ts +0 -0
package/src/proof/law.ts
DELETED
|
@@ -1,591 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The unpacking law, as scenes any terrain's bodies run: a harbor is one
|
|
3
|
-
// package. Only the seed and the terrain's bodies stand outside it, the
|
|
4
|
-
// terrain keeps sealed bytes it cannot read, and the seed opens the memory,
|
|
5
|
-
// the dock stands the catalogue, the catalogue loads the modules her cells
|
|
6
|
-
// name, the faculties and every ward stand from cells, and the dock lends.
|
|
7
|
-
// It names no runtime, so it bundles.
|
|
8
|
-
import { Being, Faculty, type Asker, type Invitation, type JsonObject } from '../being/index.ts';
|
|
9
|
-
import { Memory, type Custody, type Module } from '../contract/index.ts';
|
|
10
|
-
import { copyPackage, GROUND_STORE, rootLine, Store, type Harbor } from '../harbor/index.ts';
|
|
11
|
-
import { CryptoEntropy, HeldLoader, VolatileMemory, World } from '../pointer/index.ts';
|
|
12
|
-
import type { Expect } from './expect.ts';
|
|
13
|
-
|
|
14
|
-
// The bodies a harbor stands on, and the same keeping as a new process
|
|
15
|
-
// would build it.
|
|
16
|
-
export type Bodies = { custody: Custody; memory: Memory };
|
|
17
|
-
export type Terrain = { name: string; make(): Bodies; again(bodies: Bodies): Bodies };
|
|
18
|
-
|
|
19
|
-
abstract class Echoes extends Faculty {
|
|
20
|
-
static override kind = 'org.example.echoes';
|
|
21
|
-
}
|
|
22
|
-
class Echo extends Echoes {
|
|
23
|
-
static override kind = 'org.example.echo';
|
|
24
|
-
static override asks = { echo: {} };
|
|
25
|
-
echo(args: JsonObject, asker: Asker): JsonObject {
|
|
26
|
-
return { echoed: args.text ?? null, to: asker.id ?? null };
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
class Shop extends Being {
|
|
31
|
-
static override kind = 'org.example.shop';
|
|
32
|
-
static override cells = { sold: [] as string[] };
|
|
33
|
-
static override asks = { buy: {} };
|
|
34
|
-
buy(args: JsonObject, asker: Asker): JsonObject {
|
|
35
|
-
(this.cells.sold as string[]).push(`${String(args.item)} to ${asker.id ?? 'nobody'}`);
|
|
36
|
-
return { bought: args.item ?? null };
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
class Customer extends Being {
|
|
41
|
-
static override kind = 'org.example.customer';
|
|
42
|
-
static override asks = { join: {}, buy: {}, echo: {}, spoil: {} };
|
|
43
|
-
spoil(): JsonObject {
|
|
44
|
-
try {
|
|
45
|
-
this.cells.when = new Date() as unknown as string;
|
|
46
|
-
return { kept: true };
|
|
47
|
-
} catch {
|
|
48
|
-
return { refused: true };
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
async join(args: JsonObject): Promise<JsonObject> {
|
|
52
|
-
return { joined: await this.stance.standings.take('shop', args.invitation as Invitation) };
|
|
53
|
-
}
|
|
54
|
-
async buy(args: JsonObject): Promise<JsonObject> {
|
|
55
|
-
return { said: ((await this.stance.standings.get('shop')?.ask('buy', args)) ?? null) as JsonObject };
|
|
56
|
-
}
|
|
57
|
-
async echo(args: JsonObject): Promise<JsonObject> {
|
|
58
|
-
if (!this.stance.standings.get('echo')) await this.stance.lend(Echoes, 'echo');
|
|
59
|
-
return { said: ((await this.stance.standings.get('echo')?.ask('echo', args)) ?? null) as JsonObject };
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// A being whose fields are her cells, and nothing else.
|
|
64
|
-
class Profile extends Being {
|
|
65
|
-
static override kind = 'org.example.profile';
|
|
66
|
-
static override cells = { name: '', age: 0 };
|
|
67
|
-
static override asks = { set: {}, get: {} };
|
|
68
|
-
set(args: JsonObject): JsonObject {
|
|
69
|
-
if (typeof args.name === 'string') this.cells.name = args.name;
|
|
70
|
-
if (typeof args.age === 'number') this.cells.age = args.age;
|
|
71
|
-
return this.get();
|
|
72
|
-
}
|
|
73
|
-
get(): JsonObject {
|
|
74
|
-
return { name: this.cells.name ?? null, age: this.cells.age ?? null };
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// A store a module brings: places on a shelf of this process, by the
|
|
79
|
-
// faculty's key, which outlive a harbor as a database outlives its client.
|
|
80
|
-
// A scene empties every shelf before it stands.
|
|
81
|
-
class Shelf extends Store {
|
|
82
|
-
static override kind = 'org.example.shelf';
|
|
83
|
-
static #shelves = new Map<string, Watched>();
|
|
84
|
-
static shelf(key: string): Watched {
|
|
85
|
-
let shelf = Shelf.#shelves.get(key);
|
|
86
|
-
if (!shelf) Shelf.#shelves.set(key, (shelf = new Watched(new VolatileMemory())));
|
|
87
|
-
return shelf;
|
|
88
|
-
}
|
|
89
|
-
static reset(): void {
|
|
90
|
-
Shelf.#shelves.clear();
|
|
91
|
-
}
|
|
92
|
-
read(place: string): Promise<Map<string, Uint8Array>> {
|
|
93
|
-
return Shelf.shelf(this.stance.key).read(place);
|
|
94
|
-
}
|
|
95
|
-
write(place: string, entries: ReadonlyMap<string, Uint8Array | null>): Promise<void> {
|
|
96
|
-
return Shelf.shelf(this.stance.key).write(place, entries);
|
|
97
|
-
}
|
|
98
|
-
places(): Promise<string[]> {
|
|
99
|
-
return Shelf.shelf(this.stance.key).places();
|
|
100
|
-
}
|
|
101
|
-
forget(place: string): Promise<void> {
|
|
102
|
-
return Shelf.shelf(this.stance.key).forget(place);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// One set of classes for every scene in a bundle, since a bundler renames
|
|
107
|
-
// a second class of the same name and a row resolves a class by its name.
|
|
108
|
-
export const classes = [Echo, Shop, Customer, Profile, Shelf];
|
|
109
|
-
export const law: Module = { module: 'org.example.law', version: '1', classes };
|
|
110
|
-
|
|
111
|
-
// A harbor of the world whose catalogue runs the law's module.
|
|
112
|
-
export const standOne = async (world: World, name: string, parts: Partial<Bodies> = {}): Promise<Harbor> => {
|
|
113
|
-
const harbor = await world.harbor(name, [law], parts);
|
|
114
|
-
await harbor.root('ask', { being: 'catalogue', method: 'add', args: { from: law.module } });
|
|
115
|
-
return harbor;
|
|
116
|
-
};
|
|
117
|
-
const CATALOGUE = { catalogue: { class: 'org.nervur.catalogue', public: false, absent: false } };
|
|
118
|
-
const NONE = { wards: {}, routes: {} };
|
|
119
|
-
|
|
120
|
-
// Every byte a memory is handed, seen from outside it, and a keep it
|
|
121
|
-
// refuses where `refuse` says.
|
|
122
|
-
class Watched extends Memory {
|
|
123
|
-
readonly inner: Memory;
|
|
124
|
-
readonly seen: { place: string; name: string; bytes: Uint8Array | null }[] = [];
|
|
125
|
-
refuse: (place: string) => boolean = () => false;
|
|
126
|
-
constructor(inner: Memory) {
|
|
127
|
-
super();
|
|
128
|
-
this.inner = inner;
|
|
129
|
-
}
|
|
130
|
-
read(place: string): Promise<Map<string, Uint8Array>> {
|
|
131
|
-
return this.inner.read(place);
|
|
132
|
-
}
|
|
133
|
-
write(place: string, entries: ReadonlyMap<string, Uint8Array | null>): Promise<void> {
|
|
134
|
-
if (this.refuse(place)) return Promise.reject(new Error('refused'));
|
|
135
|
-
for (const [name, bytes] of entries) this.seen.push({ place, name, bytes: bytes && bytes.slice() });
|
|
136
|
-
return this.inner.write(place, entries);
|
|
137
|
-
}
|
|
138
|
-
places(): Promise<string[]> {
|
|
139
|
-
return this.inner.places();
|
|
140
|
-
}
|
|
141
|
-
forget(place: string): Promise<void> {
|
|
142
|
-
return this.inner.forget(place);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const utf8 = (text: string): Uint8Array => new TextEncoder().encode(text);
|
|
147
|
-
const holds = (bytes: Uint8Array, part: Uint8Array): boolean => {
|
|
148
|
-
outer: for (let at = 0; at + part.length <= bytes.length; at += 1) {
|
|
149
|
-
for (let k = 0; k < part.length; k += 1) if (bytes[at + k] !== part[k]) continue outer;
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
return false;
|
|
153
|
-
};
|
|
154
|
-
const hex = (bytes: Uint8Array): string => [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
155
|
-
|
|
156
|
-
const census = async (harbor: Harbor, ward?: string): Promise<unknown> => {
|
|
157
|
-
const out = (await (ward === undefined ? harbor.root() : harbor.ward(ward)!.root())) as { object?: unknown; notes?: unknown };
|
|
158
|
-
return (out as { notes?: unknown }).notes;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// Acme hosts a shop; home opens an echo, hosts alice under a drawn seed and
|
|
162
|
-
// bob under a named one, and alice takes the shop and lends the echo.
|
|
163
|
-
const stand = async (world: World, t: Terrain, bodies: { acme: Bodies; home: Bodies }, seed: string, expect: Expect): Promise<void> => {
|
|
164
|
-
const acme = await standOne(world, 'acme', bodies.acme);
|
|
165
|
-
const home = await standOne(world, 'home', bodies.home);
|
|
166
|
-
expect.same(home.names, [], `${t.name}: a new harbor hosts nobody`);
|
|
167
|
-
expect.same(await home.root('open', { key: 'echo', class: 'org.example.echo' }), { opened: 'echo' });
|
|
168
|
-
expect.same(await home.root('host', { ward: 'alice' }), { hosted: 'alice' });
|
|
169
|
-
expect.same(await home.root('host', { ward: 'bob', seed }), { hosted: 'bob' });
|
|
170
|
-
expect.same(await home.ward('alice')!.root('boot', { key: 'me', class: 'org.example.customer' }), { booted: 'me' });
|
|
171
|
-
expect.same(await acme.root('host', { ward: 'shop' }), { hosted: 'shop' });
|
|
172
|
-
expect.same(await acme.ward('shop')!.root('boot', { key: 'shop', class: 'org.example.shop' }), { booted: 'shop' });
|
|
173
|
-
const invitation = (await acme.ward('shop')!.root('invite', { being: 'shop', id: 'alice' })) as JsonObject;
|
|
174
|
-
const me = async (method: string, args: JsonObject = {}): Promise<unknown> => ((await world.get('home').ward('alice')!.root('ask', { being: 'me', method, args })) as { answer: unknown }).answer;
|
|
175
|
-
expect.same(await me('join', { invitation }), { joined: 'shop' });
|
|
176
|
-
expect.same(await me('buy', { item: 'tea' }), { said: { bought: 'tea' } });
|
|
177
|
-
expect.same(await me('echo', { text: 'hi' }), { said: { echoed: 'hi', to: 'lent:1' } });
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
export const lawScenes = (t: Terrain, expect: Expect): [string, () => Promise<void>][] => [
|
|
181
|
-
[
|
|
182
|
-
`law on ${t.name}: empty memory unpacks a box ward holding the dock and her catalogue, and it wakes the same`,
|
|
183
|
-
async () => {
|
|
184
|
-
const bodies = t.make();
|
|
185
|
-
const world = new World();
|
|
186
|
-
const harbor = await world.harbor('h', [], bodies);
|
|
187
|
-
const pk = harbor.box.pk;
|
|
188
|
-
expect.same(await census(harbor), { pk, beings: CATALOGUE, failed: NONE });
|
|
189
|
-
const again = await world.restart('h', t.again(bodies));
|
|
190
|
-
expect.equal(again.box.pk, pk, 'the seed is the box ward');
|
|
191
|
-
expect.same(await census(again), { pk, beings: CATALOGUE, failed: NONE });
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
[
|
|
195
|
-
`law on ${t.name}: a restart without a module the catalogue names stands, its beings absent, and wakes them when it returns`,
|
|
196
|
-
async () => {
|
|
197
|
-
const bodies = t.make();
|
|
198
|
-
const world = new World();
|
|
199
|
-
const harbor = await standOne(world, 'h', bodies);
|
|
200
|
-
expect.same(await harbor.root('open', { key: 'echo', class: 'org.example.echo' }), { opened: 'echo' });
|
|
201
|
-
expect.same(await harbor.root('host', { ward: 'w' }), { hosted: 'w' });
|
|
202
|
-
const lost = await world.restart('h', { ...t.again(bodies), loader: new HeldLoader([]) });
|
|
203
|
-
expect.same(lost.names, ['w'], 'the rest of the harbor stands');
|
|
204
|
-
expect.same(((await census(lost)) as { beings: Record<string, { absent: boolean }> }).beings.echo?.absent, true);
|
|
205
|
-
expect.same(await lost.root('ask', { being: 'catalogue', method: 'modules' }), {
|
|
206
|
-
answer: { [law.module]: { version: '1', running: null, from: law.module, failed: `${law.module} is no module this bundle carries, and it carries none` } },
|
|
207
|
-
});
|
|
208
|
-
const again = await world.restart('h', { ...t.again(bodies), loader: new HeldLoader([law]) });
|
|
209
|
-
expect.same(((await census(again)) as { beings: Record<string, { absent: boolean }> }).beings.echo?.absent, false);
|
|
210
|
-
expect.same(await again.root('ask', { being: 'catalogue', method: 'modules' }), { answer: { [law.module]: { version: '1', running: '1', from: law.module } } });
|
|
211
|
-
},
|
|
212
|
-
],
|
|
213
|
-
[
|
|
214
|
-
`law on ${t.name}: a module that does not load is removed only once no being stands on the kinds kept for it`,
|
|
215
|
-
async () => {
|
|
216
|
-
const bodies = t.make();
|
|
217
|
-
const world = new World();
|
|
218
|
-
const harbor = await standOne(world, 'h', bodies);
|
|
219
|
-
expect.same(await harbor.root('open', { key: 'echo', class: 'org.example.echo' }), { opened: 'echo' });
|
|
220
|
-
const lost = await world.restart('h', { ...t.again(bodies), loader: new HeldLoader([]) });
|
|
221
|
-
const remove = () => lost.root('ask', { being: 'catalogue', method: 'remove', args: { module: law.module } });
|
|
222
|
-
expect.same(await remove(), { answer: { error: 'a being stands on this module' } });
|
|
223
|
-
expect.same(await lost.root('unboot', { being: 'echo' }), { unbooted: 'echo' });
|
|
224
|
-
expect.same(await remove(), { answer: { removed: law.module } });
|
|
225
|
-
const after = await world.restart('h', t.again(bodies));
|
|
226
|
-
expect.same(await after.root('ask', { being: 'catalogue', method: 'modules' }), { answer: {} });
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
[
|
|
230
|
-
`law on ${t.name}: a restart where a module splits a kind runs the modules before it, and the one alone stays out`,
|
|
231
|
-
async () => {
|
|
232
|
-
const bodies = t.make();
|
|
233
|
-
const world = new World();
|
|
234
|
-
const twin: Module = { module: 'org.example.twin', version: '1', classes: [] };
|
|
235
|
-
const harbor = await world.harbor('h', [law, twin], bodies);
|
|
236
|
-
for (const from of [law.module, twin.module]) await harbor.root('ask', { being: 'catalogue', method: 'add', args: { from } });
|
|
237
|
-
expect.same(await harbor.root('open', { key: 'echo', class: 'org.example.echo' }), { opened: 'echo' });
|
|
238
|
-
const split = await world.restart('h', { ...t.again(bodies), loader: new HeldLoader([law, { ...twin, version: '2', classes }]) });
|
|
239
|
-
expect.same(((await census(split)) as { beings: Record<string, { absent: boolean }> }).beings.echo?.absent, false, 'the module before it runs');
|
|
240
|
-
const modules = ((await split.root('ask', { being: 'catalogue', method: 'modules' })) as { answer: Record<string, { running: unknown; failed?: string }> }).answer;
|
|
241
|
-
expect.same([modules[law.module]?.running, modules[twin.module]?.running], ['1', null]);
|
|
242
|
-
expect.equal(modules[twin.module]?.failed, `kind org.example.echo is in modules ${law.module} and ${twin.module}`);
|
|
243
|
-
},
|
|
244
|
-
],
|
|
245
|
-
[
|
|
246
|
-
`law on ${t.name}: a hosted ward whose place does not open fails alone, and a box ward whose place does not open fails the harbor`,
|
|
247
|
-
async () => {
|
|
248
|
-
const bodies = t.make();
|
|
249
|
-
const watched = new Watched(bodies.memory);
|
|
250
|
-
const world = new World();
|
|
251
|
-
const harbor = await standOne(world, 'h', { custody: bodies.custody, memory: watched });
|
|
252
|
-
expect.same(await harbor.root('host', { ward: 'good' }), { hosted: 'good' });
|
|
253
|
-
const from = watched.seen.length;
|
|
254
|
-
expect.same(await harbor.root('host', { ward: 'bad' }), { hosted: 'bad' });
|
|
255
|
-
const box = watched.seen[0]!.place;
|
|
256
|
-
const bad = watched.seen.slice(from).find((s) => s.place !== box)!.place;
|
|
257
|
-
const places = [bad, box];
|
|
258
|
-
const spoil = async (place: string): Promise<void> => {
|
|
259
|
-
const [[name, bytes]] = [...(await watched.inner.read(place))];
|
|
260
|
-
const changed = bytes!.slice();
|
|
261
|
-
changed[0] = changed[0]! ^ 1;
|
|
262
|
-
await watched.inner.write(place, new Map([[name!, changed]]));
|
|
263
|
-
};
|
|
264
|
-
await spoil(places[0]!);
|
|
265
|
-
const again = await world.restart('h', t.again(bodies));
|
|
266
|
-
expect.same(again.names, ['good']);
|
|
267
|
-
expect.same(((await census(again)) as { failed: unknown }).failed, { wards: { bad: 'an entry the package did not seal' }, routes: {} });
|
|
268
|
-
expect.same(await rootLine(again, { ward: 'bad' }), { error: 'the ward did not stand', reason: 'an entry the package did not seal' });
|
|
269
|
-
expect.same(await again.root('host', { ward: 'bad' }), { error: 'bad is hosted already' }, 'its name and seed stay kept');
|
|
270
|
-
await spoil(places[1]!);
|
|
271
|
-
let refused = '';
|
|
272
|
-
await world.restart('h', t.again(bodies)).catch((e: Error) => (refused = e.message));
|
|
273
|
-
expect.equal(refused, 'an entry the package did not seal');
|
|
274
|
-
},
|
|
275
|
-
],
|
|
276
|
-
[
|
|
277
|
-
`law on ${t.name}: a package carried to another memory stands the same harbor under the same seed`,
|
|
278
|
-
async () => {
|
|
279
|
-
const here = t.make();
|
|
280
|
-
const there = t.make();
|
|
281
|
-
const world = new World();
|
|
282
|
-
const harbor = await standOne(world, 'here', here);
|
|
283
|
-
expect.same(await harbor.root('open', { key: 'echo', class: 'org.example.echo' }), { opened: 'echo' });
|
|
284
|
-
expect.same(await harbor.root('host', { ward: 'alice' }), { hosted: 'alice' });
|
|
285
|
-
expect.same(await harbor.ward('alice')!.root('boot', { key: 'me', class: 'org.example.customer' }), { booted: 'me' });
|
|
286
|
-
const pks = { box: harbor.box.pk, alice: harbor.ward('alice')!.pk };
|
|
287
|
-
const carried = await copyPackage(here.memory, there.memory);
|
|
288
|
-
expect.equal(carried.length, 2, 'the box ward and alice');
|
|
289
|
-
const moved = await world.harbor('there', [law], { custody: here.custody, memory: there.memory, loader: new HeldLoader([law]) });
|
|
290
|
-
expect.same({ box: moved.box.pk, alice: moved.ward('alice')!.pk }, pks);
|
|
291
|
-
expect.same(((await census(moved, 'alice')) as { beings: JsonObject }).beings, { me: { class: 'org.example.customer', public: false, absent: false } });
|
|
292
|
-
expect.same(((await moved.ward('alice')!.root('ask', { being: 'me', method: 'echo', args: { text: 'moved' } })) as { answer: unknown }).answer, { said: { echoed: 'moved', to: 'lent:1' } });
|
|
293
|
-
const stranger = await world.harbor('stranger', [law], { custody: t.make().custody, memory: there.memory, loader: new HeldLoader([law]) });
|
|
294
|
-
expect.same(stranger.names, [], 'the bytes carried say nothing without the seed');
|
|
295
|
-
},
|
|
296
|
-
],
|
|
297
|
-
[
|
|
298
|
-
`law on ${t.name}: the root sweeps every place the dock does not name, and nothing it does`,
|
|
299
|
-
async () => {
|
|
300
|
-
const bodies = t.make();
|
|
301
|
-
const world = new World();
|
|
302
|
-
const harbor = await standOne(world, 'h', bodies);
|
|
303
|
-
expect.same(await harbor.root('host', { ward: 'w' }), { hosted: 'w' });
|
|
304
|
-
const mine = (await bodies.memory.places()).sort();
|
|
305
|
-
expect.equal(mine.length, 2, 'the box ward and the ward it hosts');
|
|
306
|
-
await bodies.memory.write('ff'.repeat(16), new Map([['ab', new Uint8Array([1, 2, 3])]]));
|
|
307
|
-
expect.same(await harbor.root('sweep'), { swept: ['ff'.repeat(16)] });
|
|
308
|
-
expect.same((await bodies.memory.places()).sort(), mine, 'what the dock names stays');
|
|
309
|
-
expect.same(await harbor.root('sweep'), { swept: [] });
|
|
310
|
-
const again = await world.restart('h', t.again(bodies));
|
|
311
|
-
expect.same(again.names, ['w']);
|
|
312
|
-
expect.same(await again.ward('w')!.root('boot', { key: 'me', class: 'org.example.customer' }), { booted: 'me' });
|
|
313
|
-
},
|
|
314
|
-
],
|
|
315
|
-
[
|
|
316
|
-
`law on ${t.name}: a restart stands the dock, its faculties, every hosted ward and its beings, and lends, with nobody asking`,
|
|
317
|
-
async () => {
|
|
318
|
-
const bodies = { acme: t.make(), home: t.make() };
|
|
319
|
-
const world = new World();
|
|
320
|
-
const seed = hex(new CryptoEntropy().draw(32));
|
|
321
|
-
await stand(world, t, bodies, seed, expect);
|
|
322
|
-
const before = world.get('home');
|
|
323
|
-
const pks = { box: before.box.pk, alice: before.ward('alice')!.pk, bob: before.ward('bob')!.pk, shop: world.get('acme').ward('shop')!.pk };
|
|
324
|
-
const beings = await census(before, 'alice');
|
|
325
|
-
const home = await world.restart('home', t.again(bodies.home));
|
|
326
|
-
const acme = await world.restart('acme', t.again(bodies.acme));
|
|
327
|
-
expect.same(home.names, ['alice', 'bob']);
|
|
328
|
-
expect.same(acme.names, ['shop']);
|
|
329
|
-
expect.same({ box: home.box.pk, alice: home.ward('alice')!.pk, bob: home.ward('bob')!.pk, shop: acme.ward('shop')!.pk }, pks);
|
|
330
|
-
expect.same(await census(home, 'alice'), beings);
|
|
331
|
-
expect.equal(home.box.partition.being('echo')?.class, 'org.example.echo');
|
|
332
|
-
const me = async (method: string, args: JsonObject = {}): Promise<unknown> => ((await home.ward('alice')!.root('ask', { being: 'me', method, args })) as { answer: unknown }).answer;
|
|
333
|
-
expect.same(await me('buy', { item: 'cake' }), { said: { bought: 'cake' } });
|
|
334
|
-
expect.same(await me('echo', { text: 'again' }), { said: { echoed: 'again', to: 'lent:1' } });
|
|
335
|
-
expect.same(acme.ward('shop')!.partition.being('shop')?.cells, { sold: ['tea to alice', 'cake to alice'] });
|
|
336
|
-
},
|
|
337
|
-
],
|
|
338
|
-
[
|
|
339
|
-
`law on ${t.name}: every root ask is a step a restart may follow, and each step wakes whole`,
|
|
340
|
-
async () => {
|
|
341
|
-
const bodies = t.make();
|
|
342
|
-
const world = new World();
|
|
343
|
-
await world.harbor('h', [law], bodies);
|
|
344
|
-
const step = async (ask: (h: Harbor) => Promise<unknown>, want: unknown): Promise<Harbor> => {
|
|
345
|
-
expect.same(await ask(world.get('h')), want);
|
|
346
|
-
return world.restart('h', t.again(bodies));
|
|
347
|
-
};
|
|
348
|
-
await step((h) => h.root('ask', { being: 'catalogue', method: 'add', args: { from: law.module } }), { answer: { added: law.module, version: law.version } });
|
|
349
|
-
await step((h) => h.root('open', { key: 'echo', class: 'org.example.echo' }), { opened: 'echo' });
|
|
350
|
-
await step((h) => h.root('host', { ward: 'alice' }), { hosted: 'alice' });
|
|
351
|
-
await step((h) => h.ward('alice')!.root('boot', { key: 'me', class: 'org.example.customer' }), { booted: 'me' });
|
|
352
|
-
const h = await step(async (x) => ((await x.ward('alice')!.root('ask', { being: 'me', method: 'echo', args: { text: 'one' } })) as { answer: unknown }).answer, { said: { echoed: 'one', to: 'lent:1' } });
|
|
353
|
-
expect.same(((await h.ward('alice')!.root('ask', { being: 'me', method: 'echo', args: { text: 'two' } })) as { answer: unknown }).answer, { said: { echoed: 'two', to: 'lent:1' } });
|
|
354
|
-
},
|
|
355
|
-
],
|
|
356
|
-
[
|
|
357
|
-
`law on ${t.name}: the terrain keeps the seed and sealed bytes, and reads no name, cell or hosted seed`,
|
|
358
|
-
async () => {
|
|
359
|
-
const home = t.make();
|
|
360
|
-
const watched = new Watched(home.memory);
|
|
361
|
-
const world = new World();
|
|
362
|
-
const seed = hex(new CryptoEntropy().draw(32));
|
|
363
|
-
await stand(world, t, { acme: t.make(), home: { custody: home.custody, memory: watched } }, seed, expect);
|
|
364
|
-
const harbor = world.get('home');
|
|
365
|
-
const secrets = [seed, ...harbor.dock.hosted.map((h) => h.seed)].map(utf8);
|
|
366
|
-
// Names of five bytes or more: a shorter one turns up in random bytes
|
|
367
|
-
// of this size often enough to fail a run by chance.
|
|
368
|
-
const words = ['alice', 'org.example', 'hosted', 'lent:1', 'customer'].map(utf8);
|
|
369
|
-
expect.ok(watched.seen.length > 0, 'the harbor kept something');
|
|
370
|
-
for (const { place, name, bytes } of watched.seen) {
|
|
371
|
-
expect.ok(/^[0-9a-f]+$/.test(place) && /^[0-9a-f]+$/.test(name), 'names are the kit hex');
|
|
372
|
-
if (bytes === null) continue;
|
|
373
|
-
for (const secret of secrets) expect.ok(!holds(bytes, secret), 'no hosted seed in the clear');
|
|
374
|
-
for (const word of words) expect.ok(!holds(bytes, word), 'no name or cell in the clear');
|
|
375
|
-
expect.ok(!holds(bytes, await home.custody.seed()), 'no harbor seed in memory');
|
|
376
|
-
}
|
|
377
|
-
},
|
|
378
|
-
],
|
|
379
|
-
[
|
|
380
|
-
`law on ${t.name}: another seed finds nothing of the package, and a byte changed refuses to unpack`,
|
|
381
|
-
async () => {
|
|
382
|
-
const bodies = t.make();
|
|
383
|
-
const watched = new Watched(bodies.memory);
|
|
384
|
-
const world = new World();
|
|
385
|
-
const harbor = await standOne(world, 'h', { custody: bodies.custody, memory: watched });
|
|
386
|
-
expect.same(await harbor.root('host', { ward: 'w' }), { hosted: 'w' });
|
|
387
|
-
const last = [...watched.seen].reverse().find((s) => s.bytes !== null && s.bytes.length > 0)!;
|
|
388
|
-
const stranger = await world.harbor('stranger', [law], { custody: t.make().custody, memory: watched });
|
|
389
|
-
expect.same(stranger.names, [], 'another seed opens another package');
|
|
390
|
-
const bytes = last.bytes!.slice();
|
|
391
|
-
bytes[0] = bytes[0]! ^ 1;
|
|
392
|
-
await watched.inner.write(last.place, new Map([[last.name, bytes]]));
|
|
393
|
-
let refused = false;
|
|
394
|
-
await world.restart('h', t.again(bodies)).catch(() => (refused = true));
|
|
395
|
-
expect.ok(refused, 'a changed byte does not unpack');
|
|
396
|
-
},
|
|
397
|
-
],
|
|
398
|
-
];
|
|
399
|
-
|
|
400
|
-
// The keepers a hosted ward's place may live with: the root memory, the
|
|
401
|
-
// ground store the kit ships, and a store a module brings.
|
|
402
|
-
type Keeping = { name: string; store?: string; kind?: string };
|
|
403
|
-
const KEEPERS: Keeping[] = [{ name: 'the root memory' }, { name: 'a ground store', store: 'vault', kind: GROUND_STORE }, { name: 'a store of a module', store: 'shelf', kind: Shelf.kind }];
|
|
404
|
-
const onKeeper = (k: Keeping): JsonObject => (k.store === undefined ? {} : { store: k.store });
|
|
405
|
-
|
|
406
|
-
// The keeper opened in the box ward, where it is a store.
|
|
407
|
-
const openKeeper = async (harbor: Harbor, k: Keeping, expect: Expect): Promise<void> => {
|
|
408
|
-
if (k.store !== undefined) expect.same(await harbor.root('open', { key: k.store, class: k.kind! }), { opened: k.store });
|
|
409
|
-
};
|
|
410
|
-
const profile = async (harbor: Harbor, method: string, args: JsonObject = {}): Promise<unknown> => ((await harbor.ward('ana')!.root('ask', { being: 'me', method, args })) as { answer: unknown }).answer;
|
|
411
|
-
|
|
412
|
-
// Acme's shop kept by `k`, home's alice on the root memory, joined, and
|
|
413
|
-
// every box that reaches the shop's door seen.
|
|
414
|
-
const shopOn = async (world: World, t: Terrain, k: Keeping, expect: Expect): Promise<{ acme: Bodies; boxes: Uint8Array[]; me: (method: string, args?: JsonObject) => Promise<unknown> }> => {
|
|
415
|
-
const bodies = { acme: t.make(), home: t.make() };
|
|
416
|
-
const acme = await standOne(world, 'acme', bodies.acme);
|
|
417
|
-
const home = await standOne(world, 'home', bodies.home);
|
|
418
|
-
await openKeeper(acme, k, expect);
|
|
419
|
-
expect.same(await acme.root('host', { ward: 'shop', ...onKeeper(k) }), { hosted: 'shop' });
|
|
420
|
-
expect.same(await acme.ward('shop')!.root('boot', { key: 'shop', class: 'org.example.shop' }), { booted: 'shop' });
|
|
421
|
-
expect.same(await home.root('host', { ward: 'alice' }), { hosted: 'alice' });
|
|
422
|
-
expect.same(await home.ward('alice')!.root('boot', { key: 'me', class: 'org.example.customer' }), { booted: 'me' });
|
|
423
|
-
const invitation = (await acme.ward('shop')!.root('invite', { being: 'shop', id: 'alice' })) as JsonObject;
|
|
424
|
-
const shop = acme.ward('shop')!.pk;
|
|
425
|
-
const boxes: Uint8Array[] = [];
|
|
426
|
-
const carry = world.carry.bind(world);
|
|
427
|
-
world.carry = (pk, bytes) => {
|
|
428
|
-
if (pk === shop) boxes.push(bytes.slice());
|
|
429
|
-
return carry(pk, bytes);
|
|
430
|
-
};
|
|
431
|
-
const me = async (method: string, args: JsonObject = {}): Promise<unknown> => ((await world.get('home').ward('alice')!.root('ask', { being: 'me', method, args })) as { answer: unknown }).answer;
|
|
432
|
-
expect.same(await me('join', { invitation }), { joined: 'shop' });
|
|
433
|
-
return { acme: bodies.acme, boxes, me };
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
const sold = (harbor: Harbor): unknown => harbor.ward('shop')!.partition.being('shop')?.cells.sold;
|
|
437
|
-
|
|
438
|
-
// A cell is a being's field made durable, whoever keeps it: the law's
|
|
439
|
-
// scenes of a being's fields, run over every keeper a hosted ward's place
|
|
440
|
-
// may live with.
|
|
441
|
-
export const keeperScenes =(t: Terrain, expect: Expect): [string, () => Promise<void>][] => [
|
|
442
|
-
...KEEPERS.flatMap((k): [string, () => Promise<void>][] => [
|
|
443
|
-
[
|
|
444
|
-
`law on ${t.name}: a being's fields kept by ${k.name} come back at wake, sealed, where that keeper alone holds them`,
|
|
445
|
-
async () => {
|
|
446
|
-
Shelf.reset();
|
|
447
|
-
const bodies = t.make();
|
|
448
|
-
const watched = new Watched(bodies.memory);
|
|
449
|
-
const world = new World();
|
|
450
|
-
const harbor = await standOne(world, 'h', { custody: bodies.custody, memory: watched });
|
|
451
|
-
await openKeeper(harbor, k, expect);
|
|
452
|
-
expect.same(await harbor.root('host', { ward: 'ana', ...onKeeper(k) }), { hosted: 'ana' });
|
|
453
|
-
expect.same(await harbor.ward('ana')!.root('boot', { key: 'me', class: Profile.kind }), { booted: 'me' });
|
|
454
|
-
expect.same(await profile(harbor, 'set', { name: 'Anastasia', age: 31 }), { name: 'Anastasia', age: 31 });
|
|
455
|
-
const again = await world.restart('h', t.again(bodies));
|
|
456
|
-
expect.same(await profile(again, 'get'), { name: 'Anastasia', age: 31 });
|
|
457
|
-
expect.same(((await census(again)) as { failed: unknown }).failed, NONE);
|
|
458
|
-
const root = await bodies.memory.places();
|
|
459
|
-
const shelf = await Shelf.shelf('shelf').places();
|
|
460
|
-
if (k.store === undefined) expect.same([root.length, shelf.length], [2, 0], 'the box ward and ana, on the root memory');
|
|
461
|
-
else if (k.kind === GROUND_STORE) expect.same([root.length, root.filter((p) => p.length > 32).length, shelf.length], [2, 1, 0], 'ana behind the ground store key');
|
|
462
|
-
else expect.same([root.length, shelf.length], [1, 1], 'the box ward on the root memory, ana on the shelf');
|
|
463
|
-
const seen = [...watched.seen, ...Shelf.shelf('shelf').seen];
|
|
464
|
-
for (const { bytes } of seen) {
|
|
465
|
-
if (bytes === null) continue;
|
|
466
|
-
for (const word of ['Anastasia', 'org.example', 'vault'].map(utf8)) expect.ok(!holds(bytes, word), 'no field, kind or key in the clear');
|
|
467
|
-
}
|
|
468
|
-
},
|
|
469
|
-
],
|
|
470
|
-
[
|
|
471
|
-
`law on ${t.name}: an answer whose keep ${k.name} refuses is silence, and the being keeps what she had`,
|
|
472
|
-
async () => {
|
|
473
|
-
Shelf.reset();
|
|
474
|
-
const bodies = t.make();
|
|
475
|
-
const watched = new Watched(bodies.memory);
|
|
476
|
-
const world = new World();
|
|
477
|
-
const harbor = await standOne(world, 'h', { custody: bodies.custody, memory: watched });
|
|
478
|
-
const box = watched.seen[0]!.place;
|
|
479
|
-
await openKeeper(harbor, k, expect);
|
|
480
|
-
expect.same(await harbor.root('host', { ward: 'ana', ...onKeeper(k) }), { hosted: 'ana' });
|
|
481
|
-
expect.same(await harbor.ward('ana')!.root('boot', { key: 'me', class: Profile.kind }), { booted: 'me' });
|
|
482
|
-
expect.same(await profile(harbor, 'set', { age: 31 }), { name: '', age: 31 });
|
|
483
|
-
// Only the keeper named refuses, so a keep that went elsewhere
|
|
484
|
-
// would answer.
|
|
485
|
-
if (k.store === undefined) watched.refuse = (place) => place !== box;
|
|
486
|
-
else if (k.kind === GROUND_STORE) watched.refuse = (place) => place.length > box.length;
|
|
487
|
-
else Shelf.shelf('shelf').refuse = () => true;
|
|
488
|
-
expect.ok(JSON.stringify(await profile(harbor, 'set', { age: 32 })) !== JSON.stringify({ name: '', age: 32 }), 'no answer leaves an unkept keep');
|
|
489
|
-
expect.equal(harbor.ward('ana')!.partition.being('me')?.cells.age, 31, 'what she wrote is put back');
|
|
490
|
-
watched.refuse = () => false;
|
|
491
|
-
Shelf.shelf('shelf').refuse = () => false;
|
|
492
|
-
const again = await world.restart('h', t.again(bodies));
|
|
493
|
-
expect.same(await profile(again, 'get'), { name: '', age: 31 });
|
|
494
|
-
expect.same(await profile(again, 'set', { age: 32 }), { name: '', age: 32 });
|
|
495
|
-
},
|
|
496
|
-
],
|
|
497
|
-
[
|
|
498
|
-
`law on ${t.name}: the door's keys and the being's cells are one keep with ${k.name}, so a box replayed after a wake is answered once`,
|
|
499
|
-
async () => {
|
|
500
|
-
Shelf.reset();
|
|
501
|
-
const world = new World();
|
|
502
|
-
const { acme, boxes, me } = await shopOn(world, t, k, expect);
|
|
503
|
-
const from = boxes.length;
|
|
504
|
-
expect.same(await me('buy', { item: 'tea' }), { said: { bought: 'tea' } });
|
|
505
|
-
const replayed = boxes.slice(from);
|
|
506
|
-
expect.ok(replayed.length > 0, 'the buy crossed the door');
|
|
507
|
-
const again = await world.restart('acme', t.again(acme));
|
|
508
|
-
expect.same(sold(again), ['tea to alice']);
|
|
509
|
-
for (const bytes of replayed) await again.arrive(again.ward('shop')!.pk, bytes);
|
|
510
|
-
expect.same(sold(again), ['tea to alice'], 'a replayed box does not buy again');
|
|
511
|
-
expect.same(await me('buy', { item: 'cake' }), { said: { bought: 'cake' } });
|
|
512
|
-
expect.same(sold(world.get('acme')), ['tea to alice', 'cake to alice']);
|
|
513
|
-
},
|
|
514
|
-
],
|
|
515
|
-
]),
|
|
516
|
-
[
|
|
517
|
-
`law on ${t.name}: a ward whose store does not stand at wake fails alone, and stands whole once it returns`,
|
|
518
|
-
async () => {
|
|
519
|
-
Shelf.reset();
|
|
520
|
-
const bodies = t.make();
|
|
521
|
-
const world = new World();
|
|
522
|
-
const harbor = await standOne(world, 'h', bodies);
|
|
523
|
-
await openKeeper(harbor, KEEPERS[2]!, expect);
|
|
524
|
-
expect.same(await harbor.root('host', { ward: 'ana', store: 'shelf' }), { hosted: 'ana' });
|
|
525
|
-
expect.same(await harbor.root('host', { ward: 'bea' }), { hosted: 'bea' });
|
|
526
|
-
expect.same(await harbor.ward('ana')!.root('boot', { key: 'me', class: Profile.kind }), { booted: 'me' });
|
|
527
|
-
expect.same(await profile(harbor, 'set', { age: 31 }), { name: '', age: 31 });
|
|
528
|
-
const lost = await world.restart('h', { ...t.again(bodies), loader: new HeldLoader([]) });
|
|
529
|
-
expect.same(lost.names, ['bea'], 'the root and its own ward stand');
|
|
530
|
-
expect.same(((await census(lost)) as { failed: unknown }).failed, { wards: { ana: 'the store shelf does not stand' }, routes: {} });
|
|
531
|
-
const back = await world.restart('h', { ...t.again(bodies), loader: new HeldLoader([law]) });
|
|
532
|
-
expect.same(back.names, ['ana', 'bea']);
|
|
533
|
-
expect.same(await profile(back, 'get'), { name: '', age: 31 });
|
|
534
|
-
},
|
|
535
|
-
],
|
|
536
|
-
[
|
|
537
|
-
`law on ${t.name}: a ward carried between keepers keeps its fields, its door and its key, and leaves nothing where it was`,
|
|
538
|
-
async () => {
|
|
539
|
-
Shelf.reset();
|
|
540
|
-
const world = new World();
|
|
541
|
-
const { acme, me } = await shopOn(world, t, KEEPERS[0]!, expect);
|
|
542
|
-
const harbor = world.get('acme');
|
|
543
|
-
for (const k of KEEPERS.slice(1)) await openKeeper(harbor, k, expect);
|
|
544
|
-
const pk = harbor.ward('shop')!.pk;
|
|
545
|
-
const items = ['tea', 'cake', 'bread', 'milk'];
|
|
546
|
-
const vault = harbor.box.being('vault') as Store;
|
|
547
|
-
const counts = async (): Promise<number[]> => [(await acme.memory.places()).filter((p) => p.length === 32).length, (await vault.places()).length, (await Shelf.shelf('shelf').places()).length];
|
|
548
|
-
expect.same(await me('buy', { item: items[0]! }), { said: { bought: items[0]! } });
|
|
549
|
-
const road: [Keeping, number[]][] = [
|
|
550
|
-
[KEEPERS[1]!, [1, 1, 0]],
|
|
551
|
-
[KEEPERS[2]!, [1, 0, 1]],
|
|
552
|
-
[KEEPERS[0]!, [2, 0, 0]],
|
|
553
|
-
];
|
|
554
|
-
for (const [i, [k, want]] of road.entries()) {
|
|
555
|
-
expect.same(await world.get('acme').root('move', { ward: 'shop', store: k.store ?? null }), { moved: 'shop', store: k.store ?? null });
|
|
556
|
-
expect.same(await counts(), want, `only ${k.name} holds the shop`);
|
|
557
|
-
const again = await world.restart('acme', t.again(acme));
|
|
558
|
-
expect.equal(again.ward('shop')!.pk, pk, 'the key moves with the ward');
|
|
559
|
-
expect.same(await me('buy', { item: items[i + 1]! }), { said: { bought: items[i + 1]! } });
|
|
560
|
-
expect.same(sold(again), items.slice(0, i + 2).map((item) => `${item} to alice`));
|
|
561
|
-
}
|
|
562
|
-
expect.same(await world.get('acme').root('move', { ward: 'shop', store: null }), { error: 'shop is kept there already' });
|
|
563
|
-
expect.same(await world.get('acme').root('move', { ward: 'shop', store: 'echo' }), { error: 'no such store' });
|
|
564
|
-
},
|
|
565
|
-
],
|
|
566
|
-
[
|
|
567
|
-
`law on ${t.name}: a store keeping a ward does not go, and the root sweeps what no keeper is named for`,
|
|
568
|
-
async () => {
|
|
569
|
-
Shelf.reset();
|
|
570
|
-
const bodies = t.make();
|
|
571
|
-
const world = new World();
|
|
572
|
-
const harbor = await standOne(world, 'h', bodies);
|
|
573
|
-
for (const k of KEEPERS.slice(1)) await openKeeper(harbor, k, expect);
|
|
574
|
-
expect.same(await harbor.root('host', { ward: 'ana', store: 'vault' }), { hosted: 'ana' });
|
|
575
|
-
expect.same(await harbor.root('host', { ward: 'bea', store: 'shelf' }), { hosted: 'bea' });
|
|
576
|
-
expect.same(await harbor.root('host', { ward: 'cy', store: 'nothing' }), { error: 'no such store' });
|
|
577
|
-
expect.same(await harbor.root('unboot', { being: 'vault' }), { error: 'a ward is kept by this store' });
|
|
578
|
-
const vault = harbor.box.being('vault') as Store;
|
|
579
|
-
const before = [(await bodies.memory.places()).sort(), await vault.places(), await Shelf.shelf('shelf').places()];
|
|
580
|
-
const stray = 'ee'.repeat(16);
|
|
581
|
-
await vault.write(stray, new Map([['ab', new Uint8Array([1])]]));
|
|
582
|
-
await Shelf.shelf('shelf').write(stray, new Map([['ab', new Uint8Array([2])]]));
|
|
583
|
-
expect.same(await harbor.root('sweep'), { swept: [stray, stray] });
|
|
584
|
-
expect.same([(await bodies.memory.places()).sort(), await vault.places(), await Shelf.shelf('shelf').places()], before, 'what the dock names stays');
|
|
585
|
-
expect.same(await harbor.root('move', { ward: 'bea', store: null }), { moved: 'bea', store: null });
|
|
586
|
-
expect.same(await harbor.root('unboot', { being: 'shelf' }), { unbooted: 'shelf' });
|
|
587
|
-
const again = await world.restart('h', t.again(bodies));
|
|
588
|
-
expect.same(again.names, ['ana', 'bea']);
|
|
589
|
-
},
|
|
590
|
-
],
|
|
591
|
-
];
|
package/src/ward/index.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The ward: its partition, its stances, its ward-being, and the ward.
|
|
3
|
-
export { allow, CEILING, DEFAULT } from './allowance.ts';
|
|
4
|
-
export { HEAD_ROW, Partition, type BeingRow, type Head, type StandingRow } from './partition.ts';
|
|
5
|
-
export { DEPTH } from './stance.ts';
|
|
6
|
-
export { DOCK, Ward, type Catalogue, type Lender, type Routes, type WardParts } from './ward.ts';
|
|
7
|
-
export { ownerAsk, PILOT, pilots, rootAsk, WardBeing, type Steward, type WardBeingStance } from './ward-being.ts';
|
|
8
|
-
export { Pilot } from './pilot.ts';
|
package/src/ward/pilot.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The pilot: a kit being that holds an owner's relation on another ward's
|
|
3
|
-
// ward-being, so whoever pilots her ward pilots that ward from afar. The
|
|
4
|
-
// relation is an ordinary standing, kept in her ward's partition, so it
|
|
5
|
-
// moves its keys and survives a restart like any other.
|
|
6
|
-
//
|
|
7
|
-
// hold { invitation } the owner invitation taken, under `far`
|
|
8
|
-
// pilot { method?, args? } the far ward-being asked, as its owner
|
|
9
|
-
// drop the relation let go
|
|
10
|
-
//
|
|
11
|
-
// She is the kit's, of kind `org.nervur.pilot`, and answers whoever pilots
|
|
12
|
-
// her own ward alone.
|
|
13
|
-
import { Being, isSilence, isWord, told, type AskSpec, type JsonObject } from '../being/index.ts';
|
|
14
|
-
import { readInvitation } from '../quo/index.ts';
|
|
15
|
-
import { ownerAsk, PILOT } from './ward-being.ts';
|
|
16
|
-
|
|
17
|
-
// The standing id the far ward-being is held under.
|
|
18
|
-
const FAR = 'far';
|
|
19
|
-
|
|
20
|
-
const object = (v: unknown): v is JsonObject => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
21
|
-
|
|
22
|
-
export class Pilot extends Being {
|
|
23
|
-
static override readonly kind: string = PILOT;
|
|
24
|
-
static override asks: Record<string, AskSpec> = {
|
|
25
|
-
hold: ownerAsk('an owner invitation on a far ward, held from now on', { required: ['invitation'] }),
|
|
26
|
-
pilot: ownerAsk('the far ward-being, asked as its owner'),
|
|
27
|
-
drop: ownerAsk('the far ward let go'),
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
async hold(args: JsonObject): Promise<JsonObject> {
|
|
31
|
-
const invitation = readInvitation(args.invitation);
|
|
32
|
-
if (invitation === null) return { error: 'no invitation' };
|
|
33
|
-
if (this.stance.standings.get(FAR)) return { error: 'a ward is held' };
|
|
34
|
-
return (await this.stance.standings.take(FAR, invitation)) === null ? { error: 'not taken' } : { held: invitation.ward };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async pilot(args: JsonObject): Promise<JsonObject> {
|
|
38
|
-
if ((args.method !== undefined && typeof args.method !== 'string') || (args.args !== undefined && !object(args.args))) return { error: 'method is text, args an object' };
|
|
39
|
-
const standing = this.stance.standings.get(FAR);
|
|
40
|
-
if (!standing) return { error: 'no ward is held' };
|
|
41
|
-
const out = await standing.ask(args.method, args.args ?? {});
|
|
42
|
-
if (isSilence(out)) return { error: 'silence' };
|
|
43
|
-
if (isWord(out)) return { error: told(out) as string };
|
|
44
|
-
return { answer: out };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
drop(): JsonObject {
|
|
48
|
-
return this.stance.standings.remove(FAR) ? { dropped: true } : { error: 'no ward is held' };
|
|
49
|
-
}
|
|
50
|
-
}
|