nervur 0.22.0 → 0.22.2-3
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/AUTHORING.md +741 -0
- package/KIT-SPEC.md +286 -0
- package/LICENSE +1 -1
- package/NOTICE +6 -2
- package/README.md +97 -424
- package/dist/app/app-ground.d.ts +18 -0
- package/dist/app/app-ground.js +25 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/app/index.js +5 -0
- package/dist/app/native.d.ts +46 -0
- package/dist/app/native.js +101 -0
- package/dist/being/being.d.ts +229 -0
- package/dist/being/being.js +20 -0
- package/dist/being/covers.d.ts +9 -0
- package/dist/being/covers.js +21 -0
- package/dist/being/index.d.ts +3 -0
- package/dist/being/index.js +5 -0
- package/dist/being/need.d.ts +49 -0
- package/dist/being/need.js +33 -0
- package/dist/being/schema.d.ts +100 -0
- package/dist/being/schema.js +132 -0
- package/dist/being/table.d.ts +42 -0
- package/dist/being/table.js +288 -0
- package/dist/bench/bench-ground.d.ts +55 -0
- package/dist/bench/bench-ground.js +101 -0
- package/dist/bench/bench.d.ts +87 -0
- package/dist/bench/bench.js +260 -0
- package/dist/bench/fake-carry.d.ts +32 -0
- package/dist/bench/fake-carry.js +56 -0
- package/dist/bench/fake-clock.d.ts +17 -0
- package/dist/bench/fake-clock.js +34 -0
- package/dist/bench/fake-custody.d.ts +9 -0
- package/dist/bench/fake-custody.js +10 -0
- package/dist/bench/fake-faculty.d.ts +25 -0
- package/dist/bench/fake-faculty.js +48 -0
- package/dist/bench/fake-keys.d.ts +5 -0
- package/dist/bench/fake-keys.js +9 -0
- package/dist/bench/fake-memory.d.ts +14 -0
- package/dist/bench/fake-memory.js +46 -0
- package/dist/bench/fake-network.d.ts +57 -0
- package/dist/bench/fake-network.js +169 -0
- package/dist/bench/index.d.ts +11 -0
- package/dist/bench/index.js +13 -0
- package/dist/bench/seeded.d.ts +11 -0
- package/dist/bench/seeded.js +37 -0
- package/dist/bench/settle.d.ts +2 -0
- package/dist/bench/settle.js +49 -0
- package/dist/bench/stewards.d.ts +17 -0
- package/dist/bench/stewards.js +20 -0
- package/dist/bodies/class-list.d.ts +14 -0
- package/dist/bodies/class-list.js +33 -0
- package/dist/bodies/joined-carry.d.ts +23 -0
- package/dist/bodies/joined-carry.js +42 -0
- package/dist/bodies/noble-crypto.d.ts +22 -0
- package/dist/bodies/noble-crypto.js +88 -0
- package/dist/bodies/seed-keys.d.ts +10 -0
- package/dist/bodies/seed-keys.js +42 -0
- package/dist/bodies/strict-tools.d.ts +10 -0
- package/dist/bodies/strict-tools.js +306 -0
- package/dist/bodies/web-carry.d.ts +65 -0
- package/dist/bodies/web-carry.js +224 -0
- package/dist/bodies/web-clock.d.ts +12 -0
- package/dist/bodies/web-clock.js +33 -0
- package/dist/browser/browser-ground.d.ts +69 -0
- package/dist/browser/browser-ground.js +219 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.js +7 -0
- package/dist/browser/indexeddb-memory.d.ts +17 -0
- package/dist/browser/indexeddb-memory.js +97 -0
- package/dist/browser/locked-custody.d.ts +25 -0
- package/dist/browser/locked-custody.js +89 -0
- package/dist/browser/origin-classes.d.ts +7 -0
- package/dist/browser/origin-classes.js +31 -0
- package/dist/foundation.d.ts +150 -0
- package/dist/foundation.js +4 -0
- package/dist/ground/ground.d.ts +177 -0
- package/dist/ground/ground.js +363 -0
- package/dist/house/crossing.d.ts +37 -0
- package/dist/house/crossing.js +131 -0
- package/dist/house/house.d.ts +80 -0
- package/dist/house/house.js +1753 -0
- package/dist/house/rows-shape.d.ts +103 -0
- package/dist/house/rows-shape.js +12 -0
- package/dist/house/rows.d.ts +27 -0
- package/dist/house/rows.js +168 -0
- package/dist/index.d.ts +29 -1
- package/dist/index.js +25 -14
- package/dist/node/bridge.d.ts +17 -0
- package/dist/node/bridge.js +188 -0
- package/dist/node/cli.js +170 -0
- package/dist/node/custody.d.ts +20 -0
- package/dist/node/custody.js +34 -0
- package/dist/node/file-keys.d.ts +7 -0
- package/dist/node/file-keys.js +30 -0
- package/dist/node/file-memory.d.ts +13 -0
- package/dist/node/file-memory.js +91 -0
- package/dist/node/folder-classes.d.ts +5 -0
- package/dist/node/folder-classes.js +40 -0
- package/dist/node/gone.d.ts +2 -0
- package/dist/node/gone.js +7 -0
- package/dist/node/hand.d.ts +29 -0
- package/dist/node/hand.js +85 -0
- package/dist/node/held-keys.d.ts +14 -0
- package/dist/node/held-keys.js +36 -0
- package/dist/node/http.d.ts +11 -0
- package/dist/node/http.js +90 -0
- package/dist/node/index.d.ts +13 -0
- package/dist/node/index.js +15 -0
- package/dist/node/keychain-keys.d.ts +10 -0
- package/dist/node/keychain-keys.js +51 -0
- package/dist/node/ledger-memory.d.ts +17 -0
- package/dist/node/ledger-memory.js +151 -0
- package/dist/node/lock.d.ts +10 -0
- package/dist/node/lock.js +78 -0
- package/dist/node/node-ground.d.ts +33 -0
- package/dist/node/node-ground.js +147 -0
- package/dist/node/notify.d.ts +1 -0
- package/dist/node/notify.js +18 -0
- package/dist/node/tcp-carry.d.ts +39 -0
- package/dist/node/tcp-carry.js +216 -0
- package/dist/node/websocket.d.ts +6 -0
- package/dist/node/websocket.js +118 -0
- package/dist/quo/frame.d.ts +20 -0
- package/dist/quo/frame.js +48 -0
- package/dist/quo/room.d.ts +154 -0
- package/dist/quo/room.js +362 -0
- package/dist/serve/index.d.ts +27 -0
- package/dist/serve/index.js +55 -0
- package/package.json +49 -47
- package/dist/browser.d.ts +0 -1
- package/dist/browser.js +0 -8
- package/dist/cli.js +0 -16
- package/dist/core/being/being.d.ts +0 -17
- package/dist/core/being/being.js +0 -72
- package/dist/core/being/digest.d.ts +0 -3
- package/dist/core/being/digest.js +0 -17
- package/dist/core/being/faculty.d.ts +0 -10
- package/dist/core/being/faculty.js +0 -77
- package/dist/core/being/index.d.ts +0 -6
- package/dist/core/being/index.js +0 -8
- package/dist/core/being/kind.d.ts +0 -6
- package/dist/core/being/kind.js +0 -34
- package/dist/core/being/types.d.ts +0 -61
- package/dist/core/being/types.js +0 -4
- package/dist/core/being/words.d.ts +0 -13
- package/dist/core/being/words.js +0 -14
- package/dist/core/browser/index.d.ts +0 -37
- package/dist/core/browser/index.js +0 -196
- package/dist/core/browser/worker.d.ts +0 -11
- package/dist/core/browser/worker.js +0 -37
- package/dist/core/cli/command.d.ts +0 -13
- package/dist/core/cli/command.js +0 -330
- package/dist/core/cli/edge.d.ts +0 -7
- package/dist/core/cli/edge.js +0 -79
- package/dist/core/cli/harbor.d.ts +0 -25
- package/dist/core/cli/harbor.js +0 -115
- package/dist/core/contract/index.d.ts +0 -42
- package/dist/core/contract/index.js +0 -34
- package/dist/core/contract/link.d.ts +0 -4
- package/dist/core/contract/link.js +0 -45
- package/dist/core/crypto/aes.d.ts +0 -3
- package/dist/core/crypto/aes.js +0 -24
- package/dist/core/crypto/bytes.d.ts +0 -6
- package/dist/core/crypto/bytes.js +0 -33
- package/dist/core/crypto/ed25519.d.ts +0 -3
- package/dist/core/crypto/ed25519.js +0 -93
- package/dist/core/crypto/hash.d.ts +0 -2
- package/dist/core/crypto/hash.js +0 -11
- package/dist/core/crypto/index.d.ts +0 -7
- package/dist/core/crypto/index.js +0 -10
- package/dist/core/crypto/json.d.ts +0 -8
- package/dist/core/crypto/json.js +0 -250
- package/dist/core/crypto/mlkem.d.ts +0 -12
- package/dist/core/crypto/mlkem.js +0 -36
- package/dist/core/crypto/subtle.d.ts +0 -3
- package/dist/core/crypto/subtle.js +0 -11
- package/dist/core/crypto/x25519.d.ts +0 -2
- package/dist/core/crypto/x25519.js +0 -24
- package/dist/core/edge/index.d.ts +0 -164
- package/dist/core/edge/index.js +0 -739
- package/dist/core/edge/kit.d.ts +0 -2
- package/dist/core/edge/kit.js +0 -2
- package/dist/core/folder/index.d.ts +0 -18
- package/dist/core/folder/index.js +0 -130
- package/dist/core/git/http.d.ts +0 -6
- package/dist/core/git/http.js +0 -102
- package/dist/core/git/index.d.ts +0 -5
- package/dist/core/git/index.js +0 -10
- package/dist/core/git/object.d.ts +0 -40
- package/dist/core/git/object.js +0 -103
- package/dist/core/git/pack.d.ts +0 -9
- package/dist/core/git/pack.js +0 -171
- package/dist/core/git/store.d.ts +0 -23
- package/dist/core/git/store.js +0 -120
- package/dist/core/git/zlib.d.ts +0 -6
- package/dist/core/git/zlib.js +0 -210
- package/dist/core/harbor/carrying.d.ts +0 -51
- package/dist/core/harbor/carrying.js +0 -69
- package/dist/core/harbor/catalogue.d.ts +0 -33
- package/dist/core/harbor/catalogue.js +0 -365
- package/dist/core/harbor/dna.d.ts +0 -13
- package/dist/core/harbor/dna.js +0 -120
- package/dist/core/harbor/dock.d.ts +0 -62
- package/dist/core/harbor/dock.js +0 -381
- package/dist/core/harbor/harbor.d.ts +0 -16
- package/dist/core/harbor/harbor.js +0 -438
- package/dist/core/harbor/index.d.ts +0 -11
- package/dist/core/harbor/index.js +0 -14
- package/dist/core/harbor/memory.d.ts +0 -10
- package/dist/core/harbor/memory.js +0 -25
- package/dist/core/harbor/package.d.ts +0 -20
- package/dist/core/harbor/package.js +0 -172
- package/dist/core/harbor/probe.d.ts +0 -22
- package/dist/core/harbor/probe.js +0 -15
- package/dist/core/harbor/registry.d.ts +0 -15
- package/dist/core/harbor/registry.js +0 -110
- package/dist/core/harbor/root-line.d.ts +0 -7
- package/dist/core/harbor/root-line.js +0 -9
- package/dist/core/harbor/terrain.d.ts +0 -25
- package/dist/core/harbor/terrain.js +0 -28
- package/dist/core/http/index.d.ts +0 -2
- package/dist/core/http/index.js +0 -154
- package/dist/core/http/websocket.d.ts +0 -29
- package/dist/core/http/websocket.js +0 -133
- package/dist/core/line/answer.d.ts +0 -8
- package/dist/core/line/answer.js +0 -60
- package/dist/core/line/frame.d.ts +0 -29
- package/dist/core/line/frame.js +0 -77
- package/dist/core/line/ground.d.ts +0 -3
- package/dist/core/line/ground.js +0 -7
- package/dist/core/line/index.d.ts +0 -4
- package/dist/core/line/index.js +0 -8
- package/dist/core/line/web.d.ts +0 -12
- package/dist/core/line/web.js +0 -117
- package/dist/core/node/index.d.ts +0 -23
- package/dist/core/node/index.js +0 -124
- package/dist/core/pointer/bodies.d.ts +0 -31
- package/dist/core/pointer/bodies.js +0 -118
- package/dist/core/pointer/index.d.ts +0 -2
- package/dist/core/pointer/index.js +0 -4
- package/dist/core/pointer/world.d.ts +0 -45
- package/dist/core/pointer/world.js +0 -106
- package/dist/core/proof/contracts.d.ts +0 -19
- package/dist/core/proof/contracts.js +0 -116
- package/dist/core/proof/expect.d.ts +0 -6
- package/dist/core/proof/expect.js +0 -18
- package/dist/core/proof/index.d.ts +0 -3
- package/dist/core/proof/index.js +0 -14
- package/dist/core/proof/law.d.ts +0 -25
- package/dist/core/proof/law.js +0 -715
- package/dist/core/quo/address.d.ts +0 -19
- package/dist/core/quo/address.js +0 -84
- package/dist/core/quo/door.d.ts +0 -34
- package/dist/core/quo/door.js +0 -174
- package/dist/core/quo/index.d.ts +0 -9
- package/dist/core/quo/index.js +0 -12
- package/dist/core/quo/invitation.d.ts +0 -8
- package/dist/core/quo/invitation.js +0 -19
- package/dist/core/quo/keys.d.ts +0 -40
- package/dist/core/quo/keys.js +0 -79
- package/dist/core/quo/payload.d.ts +0 -15
- package/dist/core/quo/payload.js +0 -55
- package/dist/core/quo/relations.d.ts +0 -40
- package/dist/core/quo/relations.js +0 -33
- package/dist/core/quo/reply.d.ts +0 -13
- package/dist/core/quo/reply.js +0 -35
- package/dist/core/quo/seal.d.ts +0 -42
- package/dist/core/quo/seal.js +0 -78
- package/dist/core/quo/standing.d.ts +0 -39
- package/dist/core/quo/standing.js +0 -90
- package/dist/core/tcp/index.d.ts +0 -27
- package/dist/core/tcp/index.js +0 -209
- package/dist/core/ward/allowance.d.ts +0 -14
- package/dist/core/ward/allowance.js +0 -32
- package/dist/core/ward/cells.d.ts +0 -7
- package/dist/core/ward/cells.js +0 -74
- package/dist/core/ward/house.d.ts +0 -67
- package/dist/core/ward/house.js +0 -473
- package/dist/core/ward/index.d.ts +0 -5
- package/dist/core/ward/index.js +0 -7
- package/dist/core/ward/partition.d.ts +0 -50
- package/dist/core/ward/partition.js +0 -66
- package/dist/core/ward/stance.d.ts +0 -29
- package/dist/core/ward/stance.js +0 -186
- package/dist/core/ward/ward.d.ts +0 -44
- package/dist/core/ward/ward.js +0 -140
- package/dist/core.d.ts +0 -10
- package/dist/core.js +0 -19
- package/dist/defaults/dock/index.d.ts +0 -4
- package/dist/defaults/dock/index.js +0 -11
- package/dist/defaults/index.d.ts +0 -5
- package/dist/defaults/index.js +0 -10
- package/dist/defaults/memory/index.d.ts +0 -12
- package/dist/defaults/memory/index.js +0 -39
- package/dist/defaults/tcp/index.d.ts +0 -16
- package/dist/defaults/tcp/index.js +0 -82
- package/dist/defaults/ward/index.d.ts +0 -4
- package/dist/defaults/ward/index.js +0 -11
- package/dist/defaults/web/index.d.ts +0 -15
- package/dist/defaults/web/index.js +0 -90
- package/dist/edge.d.ts +0 -1
- package/dist/edge.js +0 -9
- package/dist/kit.d.ts +0 -4
- package/dist/kit.js +0 -12
- package/dist/node.d.ts +0 -1
- package/dist/node.js +0 -8
- package/src/browser.ts +0 -10
- package/src/cli.ts +0 -15
- package/src/core/being/being.ts +0 -84
- package/src/core/being/digest.ts +0 -18
- package/src/core/being/faculty.ts +0 -76
- package/src/core/being/index.ts +0 -8
- package/src/core/being/kind.ts +0 -37
- package/src/core/being/types.ts +0 -74
- package/src/core/being/words.ts +0 -31
- package/src/core/browser/index.ts +0 -219
- package/src/core/browser/worker.ts +0 -63
- package/src/core/cli/command.ts +0 -311
- package/src/core/cli/edge.ts +0 -86
- package/src/core/cli/harbor.ts +0 -120
- package/src/core/contract/index.ts +0 -82
- package/src/core/contract/link.ts +0 -58
- package/src/core/crypto/aes.ts +0 -26
- package/src/core/crypto/bytes.ts +0 -37
- package/src/core/crypto/ed25519.ts +0 -92
- package/src/core/crypto/hash.ts +0 -14
- package/src/core/crypto/index.ts +0 -10
- package/src/core/crypto/json.ts +0 -241
- package/src/core/crypto/mlkem.ts +0 -38
- package/src/core/crypto/subtle.ts +0 -13
- package/src/core/crypto/x25519.ts +0 -23
- package/src/core/edge/index.ts +0 -865
- package/src/core/folder/index.ts +0 -137
- package/src/core/git/http.ts +0 -103
- package/src/core/git/index.ts +0 -10
- package/src/core/git/object.ts +0 -116
- package/src/core/git/pack.ts +0 -147
- package/src/core/git/store.ts +0 -122
- package/src/core/git/zlib.ts +0 -197
- package/src/core/harbor/carrying.ts +0 -124
- package/src/core/harbor/catalogue.ts +0 -386
- package/src/core/harbor/dna.ts +0 -116
- package/src/core/harbor/dock.ts +0 -420
- package/src/core/harbor/harbor.ts +0 -433
- package/src/core/harbor/index.ts +0 -14
- package/src/core/harbor/memory.ts +0 -31
- package/src/core/harbor/package.ts +0 -195
- package/src/core/harbor/probe.ts +0 -39
- package/src/core/harbor/registry.ts +0 -107
- package/src/core/harbor/root-line.ts +0 -19
- package/src/core/harbor/terrain.ts +0 -54
- package/src/core/http/index.ts +0 -147
- package/src/core/http/websocket.ts +0 -124
- package/src/core/line/answer.ts +0 -60
- package/src/core/line/frame.ts +0 -83
- package/src/core/line/ground.ts +0 -18
- package/src/core/line/index.ts +0 -8
- package/src/core/line/web.ts +0 -123
- package/src/core/node/index.ts +0 -139
- package/src/core/pointer/bodies.ts +0 -122
- package/src/core/pointer/index.ts +0 -4
- package/src/core/pointer/world.ts +0 -134
- package/src/core/proof/contracts.ts +0 -134
- package/src/core/proof/expect.ts +0 -25
- package/src/core/proof/index.ts +0 -14
- package/src/core/proof/law.ts +0 -742
- package/src/core/quo/address.ts +0 -87
- package/src/core/quo/door.ts +0 -180
- package/src/core/quo/index.ts +0 -12
- package/src/core/quo/invitation.ts +0 -19
- package/src/core/quo/keys.ts +0 -91
- package/src/core/quo/payload.ts +0 -61
- package/src/core/quo/relations.ts +0 -62
- package/src/core/quo/reply.ts +0 -38
- package/src/core/quo/seal.ts +0 -101
- package/src/core/quo/standing.ts +0 -111
- package/src/core/stand/main.ts +0 -20
- package/src/core/stand/stand.ts +0 -259
- package/src/core/tcp/index.ts +0 -227
- package/src/core/ward/allowance.ts +0 -47
- package/src/core/ward/cells.ts +0 -77
- package/src/core/ward/house.ts +0 -524
- package/src/core/ward/index.ts +0 -7
- package/src/core/ward/partition.ts +0 -117
- package/src/core/ward/stance.ts +0 -207
- package/src/core/ward/ward.ts +0 -166
- package/src/core.ts +0 -55
- package/src/defaults/dock/index.ts +0 -12
- package/src/defaults/index.ts +0 -10
- package/src/defaults/memory/index.ts +0 -50
- package/src/defaults/tcp/index.ts +0 -89
- package/src/defaults/ward/index.ts +0 -12
- package/src/defaults/web/index.ts +0 -97
- package/src/edge.ts +0 -11
- package/src/index.ts +0 -18
- package/src/kit.ts +0 -15
- package/src/node.ts +0 -10
- /package/dist/{cli.d.ts → node/cli.d.ts} +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { Json, Position } from '../being/being.ts';
|
|
2
|
+
import { type Table } from '../being/table.ts';
|
|
3
|
+
import type { BeingClass } from '../foundation.ts';
|
|
4
|
+
import { type Answer, type Offer } from '../house/house.ts';
|
|
5
|
+
import { FakeClock } from './fake-clock.ts';
|
|
6
|
+
export interface BenchOptions {
|
|
7
|
+
/** Her classes, and any other class her beings meet. */
|
|
8
|
+
readonly classes: readonly BeingClass[];
|
|
9
|
+
/** Fakes of the faculties her needs name. */
|
|
10
|
+
readonly offers?: readonly Offer[];
|
|
11
|
+
/** One seed, so a bench runs the same each time. */
|
|
12
|
+
readonly seed?: string;
|
|
13
|
+
/** Her steward, placed as the author's house's steward; the bench's own where none is given. */
|
|
14
|
+
readonly steward?: BeingClass;
|
|
15
|
+
/** Her public being, placed as the author's house's public being. */
|
|
16
|
+
readonly public?: BeingClass;
|
|
17
|
+
}
|
|
18
|
+
/** Where a check places her: a normal being, the steward, or the public being. */
|
|
19
|
+
export interface CheckOptions {
|
|
20
|
+
readonly classes?: readonly BeingClass[];
|
|
21
|
+
readonly seed?: string;
|
|
22
|
+
readonly position?: Position;
|
|
23
|
+
/** The steward beside her, where she is not it. */
|
|
24
|
+
readonly steward?: BeingClass;
|
|
25
|
+
}
|
|
26
|
+
/** One thing the bench checked: what, and why it failed where it did. */
|
|
27
|
+
export interface Finding {
|
|
28
|
+
readonly ok: boolean;
|
|
29
|
+
readonly what: string;
|
|
30
|
+
readonly why?: string;
|
|
31
|
+
}
|
|
32
|
+
/** A being the bench placed, asked through a handle as any role. */
|
|
33
|
+
export declare class Placed {
|
|
34
|
+
#private;
|
|
35
|
+
readonly id: string;
|
|
36
|
+
constructor(bench: Bench, id: string, table: Table);
|
|
37
|
+
/** The role an ask's entry names first; `steward` where it names every occupant. */
|
|
38
|
+
roleFor(method: string): string;
|
|
39
|
+
/** One ask, crossing as a box, as the occupant who plays `role`. `null` is silence or nothing. */
|
|
40
|
+
ask(method: string, args?: Json, { role }?: {
|
|
41
|
+
role?: string;
|
|
42
|
+
}): Promise<Answer | null>;
|
|
43
|
+
/** Her describe as `role` sees it: `root` where she is the steward, her steward otherwise. */
|
|
44
|
+
describe({ role }?: {
|
|
45
|
+
role?: string;
|
|
46
|
+
}): Promise<{
|
|
47
|
+
state: string;
|
|
48
|
+
asks: {
|
|
49
|
+
method: string;
|
|
50
|
+
}[];
|
|
51
|
+
} | null>;
|
|
52
|
+
/** Her cells as they stand. */
|
|
53
|
+
cells(): Promise<Record<string, Json> | null>;
|
|
54
|
+
}
|
|
55
|
+
export declare class Bench {
|
|
56
|
+
#private;
|
|
57
|
+
readonly clock: FakeClock;
|
|
58
|
+
private constructor();
|
|
59
|
+
static open({ classes, offers, seed, steward, public: open }: BenchOptions): Promise<Bench>;
|
|
60
|
+
/**
|
|
61
|
+
* Her class here: the steward or the public being where the bench opened
|
|
62
|
+
* with her there, and otherwise borne, from her `born` or from the cells
|
|
63
|
+
* an example starts from.
|
|
64
|
+
*/
|
|
65
|
+
place(Class: BeingClass, { id, cells, born }?: {
|
|
66
|
+
id?: string;
|
|
67
|
+
cells?: Record<string, Json>;
|
|
68
|
+
born?: Record<string, Json>;
|
|
69
|
+
}): Promise<Placed>;
|
|
70
|
+
/** Lets every ask, effect and reply the houses started run to its end. */
|
|
71
|
+
settle(): Promise<void>;
|
|
72
|
+
/** The clock moved on, and what came due run. */
|
|
73
|
+
advance(ms: number): Promise<void>;
|
|
74
|
+
cellsOf(being: string): Promise<Record<string, Json> | null>;
|
|
75
|
+
through(being: string, role: string, ask: string | undefined, method: string | undefined, args: Json): Promise<Answer | {
|
|
76
|
+
describe: Json;
|
|
77
|
+
} | null>;
|
|
78
|
+
/** Every example of her class run, twice, on two fresh benches. */
|
|
79
|
+
static examples(Class: BeingClass, options?: CheckOptions): Promise<Finding[]>;
|
|
80
|
+
/**
|
|
81
|
+
* Every state her default cells and her examples reach, described to
|
|
82
|
+
* every role she names: what each is shown is what her table says.
|
|
83
|
+
*/
|
|
84
|
+
static walk(Class: BeingClass, options?: CheckOptions): Promise<Finding[]>;
|
|
85
|
+
/** Her examples and her walk, and an error naming every finding that failed. */
|
|
86
|
+
static check(Class: BeingClass, options?: CheckOptions): Promise<Finding[]>;
|
|
87
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { resolve } from '../being/table.js';
|
|
2
|
+
import { blueprintOf } from '../being/need.js';
|
|
3
|
+
import { StrictTools } from '../bodies/strict-tools.js';
|
|
4
|
+
import { ClassList } from '../bodies/class-list.js';
|
|
5
|
+
import { openForBench } from '../house/house.js';
|
|
6
|
+
import { Room } from '../quo/room.js';
|
|
7
|
+
import { FakeCarry } from './fake-carry.js';
|
|
8
|
+
import { FakeClock } from './fake-clock.js';
|
|
9
|
+
import { FakeKeys } from './fake-keys.js';
|
|
10
|
+
import { FakeMemory } from './fake-memory.js';
|
|
11
|
+
import { SeededCrypto } from './seeded.js';
|
|
12
|
+
import { settle } from './settle.js';
|
|
13
|
+
import { BenchSteward, BenchTester } from './stewards.js';
|
|
14
|
+
const STEWARD = 'steward';
|
|
15
|
+
const PUBLIC = 'public';
|
|
16
|
+
const tools = new StrictTools();
|
|
17
|
+
/** A being the bench placed, asked through a handle as any role. */
|
|
18
|
+
export class Placed {
|
|
19
|
+
id;
|
|
20
|
+
#bench;
|
|
21
|
+
#table;
|
|
22
|
+
constructor(bench, id, table) {
|
|
23
|
+
this.#bench = bench;
|
|
24
|
+
this.id = id;
|
|
25
|
+
this.#table = table;
|
|
26
|
+
}
|
|
27
|
+
/** The role an ask's entry names first; `steward` where it names every occupant. */
|
|
28
|
+
roleFor(method) {
|
|
29
|
+
const entry = this.#table.asks[method];
|
|
30
|
+
return entry?.for?.[0] ?? STEWARD;
|
|
31
|
+
}
|
|
32
|
+
/** One ask, crossing as a box, as the occupant who plays `role`. `null` is silence or nothing. */
|
|
33
|
+
async ask(method, args = {}, { role = this.roleFor(method) } = {}) {
|
|
34
|
+
const answered = await this.#bench.through(this.id, role, method, method, args);
|
|
35
|
+
return answered === null || 'describe' in answered ? null : answered;
|
|
36
|
+
}
|
|
37
|
+
/** Her describe as `role` sees it: `root` where she is the steward, her steward otherwise. */
|
|
38
|
+
async describe({ role = this.id === STEWARD ? 'root' : STEWARD } = {}) {
|
|
39
|
+
const answered = await this.#bench.through(this.id, role, undefined, undefined, {});
|
|
40
|
+
return answered !== null && 'describe' in answered ? answered.describe : null;
|
|
41
|
+
}
|
|
42
|
+
/** Her cells as they stand. */
|
|
43
|
+
cells() {
|
|
44
|
+
return this.#bench.cellsOf(this.id);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class Bench {
|
|
48
|
+
clock;
|
|
49
|
+
#author;
|
|
50
|
+
#tester;
|
|
51
|
+
#standings = new Map();
|
|
52
|
+
#steward;
|
|
53
|
+
#public;
|
|
54
|
+
#stranger;
|
|
55
|
+
#count = 0;
|
|
56
|
+
constructor(clock, author, tester, placed, stranger) {
|
|
57
|
+
this.clock = clock;
|
|
58
|
+
this.#author = author;
|
|
59
|
+
this.#tester = tester;
|
|
60
|
+
this.#steward = placed.steward;
|
|
61
|
+
this.#public = placed.public;
|
|
62
|
+
this.#stranger = stranger;
|
|
63
|
+
}
|
|
64
|
+
static async open({ classes, offers = [], seed = 'bench', steward, public: open }) {
|
|
65
|
+
const clock = new FakeClock();
|
|
66
|
+
const network = new Map();
|
|
67
|
+
const house = async (name, list, handed) => {
|
|
68
|
+
const crypto = new SeededCrypto(`${seed}:${name}`);
|
|
69
|
+
const carry = new FakeCarry({ network, address: `bench://${name}` });
|
|
70
|
+
const opened = await openForBench({ keys: new FakeKeys(`${seed}:${name}`, crypto), memory: new FakeMemory(), classes: list, carry, clock, crypto, tools }, handed);
|
|
71
|
+
carry.listen(opened.opened);
|
|
72
|
+
return opened;
|
|
73
|
+
};
|
|
74
|
+
const author = await house('author', new ClassList({ steward: steward ?? BenchSteward, ...(open === undefined ? {} : { public: open }), beings: classes }), offers);
|
|
75
|
+
const tester = await house('bench', new ClassList({ steward: BenchTester }), []);
|
|
76
|
+
// The stranger's key, drawn from the seed, so a re-run signs as the first run did.
|
|
77
|
+
const crypto = new SeededCrypto(`${seed}:stranger`);
|
|
78
|
+
const stranger = { room: new Room(new FakeKeys(`${seed}:stranger`, crypto), crypto, tools), secret: tools.hex(crypto.random(32)) };
|
|
79
|
+
return new Bench(clock, author, tester, { steward, public: open }, stranger);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Her class here: the steward or the public being where the bench opened
|
|
83
|
+
* with her there, and otherwise borne, from her `born` or from the cells
|
|
84
|
+
* an example starts from.
|
|
85
|
+
*/
|
|
86
|
+
async place(Class, { id, cells, born } = {}) {
|
|
87
|
+
const table = resolve(Class);
|
|
88
|
+
const position = Class === this.#steward ? STEWARD : Class === this.#public ? PUBLIC : undefined;
|
|
89
|
+
const name = position ?? id ?? `placed-${++this.#count}`;
|
|
90
|
+
if (position === undefined)
|
|
91
|
+
await this.#author.access.bear(table.kind, name, born);
|
|
92
|
+
await this.settle();
|
|
93
|
+
if (cells !== undefined) {
|
|
94
|
+
const patched = await this.#author.access.patch(name, (row) => {
|
|
95
|
+
row.cells = { ...row.cells, ...cells };
|
|
96
|
+
});
|
|
97
|
+
if (!patched)
|
|
98
|
+
throw new Error('memory refused the example’s cells');
|
|
99
|
+
}
|
|
100
|
+
return new Placed(this, name, table);
|
|
101
|
+
}
|
|
102
|
+
/** Lets every ask, effect and reply the houses started run to its end. */
|
|
103
|
+
async settle() {
|
|
104
|
+
await settle();
|
|
105
|
+
}
|
|
106
|
+
/** The clock moved on, and what came due run. */
|
|
107
|
+
async advance(ms) {
|
|
108
|
+
this.clock.advance(ms);
|
|
109
|
+
await this.settle();
|
|
110
|
+
}
|
|
111
|
+
cellsOf(being) {
|
|
112
|
+
return this.#author.access.cells(being);
|
|
113
|
+
}
|
|
114
|
+
// Who plays a role. On the public being, a role `stranger` holds is played
|
|
115
|
+
// by a box with the zero head. A role `root` holds is played through the
|
|
116
|
+
// hand, on any being. Otherwise an occupant named for it plays it, the
|
|
117
|
+
// role true in both sets of notes. A handle is admitted to one ask;
|
|
118
|
+
// `being` is introduced; `steward` is hers.
|
|
119
|
+
async through(being, role, ask, method, args) {
|
|
120
|
+
const holds = async (asker) => role === asker || (await this.#author.access.roles(being, asker)).includes(role);
|
|
121
|
+
const played = being === PUBLIC && (await holds('stranger')) ? this.#zero(method, args) : (await holds('root')) ? this.#hand(being, method, args) : undefined;
|
|
122
|
+
if (played !== undefined) {
|
|
123
|
+
const answered = await played;
|
|
124
|
+
await this.settle();
|
|
125
|
+
return answered;
|
|
126
|
+
}
|
|
127
|
+
const occupant = role === 'handle' ? `handle:bench-${ask}` : role === 'being' ? 'being:bench' : role;
|
|
128
|
+
const key = `${being}\n${occupant}`;
|
|
129
|
+
let standing = this.#standings.get(key);
|
|
130
|
+
if (standing === undefined) {
|
|
131
|
+
const notes = role === STEWARD || role === 'being' || role === 'handle' ? {} : { [role]: true };
|
|
132
|
+
const invitation = await this.#author.access.occupant(being, occupant, { notes, steward: notes, ...(role === 'handle' ? { ask: ask } : {}) });
|
|
133
|
+
const adopted = await this.#tester.opened.ask({ method: 'adopt', args: { invitation } });
|
|
134
|
+
if (!('result' in adopted))
|
|
135
|
+
throw new Error(`the bench could not hold ${role}: ${JSON.stringify(adopted)}`);
|
|
136
|
+
standing = adopted.result;
|
|
137
|
+
this.#standings.set(key, standing);
|
|
138
|
+
}
|
|
139
|
+
const answered = await this.#tester.access.far(STEWARD, standing, method, args);
|
|
140
|
+
await this.settle();
|
|
141
|
+
return answered;
|
|
142
|
+
}
|
|
143
|
+
// A being asked by the holder of the house's hand, which no box carries.
|
|
144
|
+
#hand(being, method, args) {
|
|
145
|
+
return this.#author.opened.ask({ id: being, ...(method === undefined ? {} : { method, args }) });
|
|
146
|
+
}
|
|
147
|
+
// The public being asked by a stranger: a box with the zero head, signed with the bench's stranger key.
|
|
148
|
+
async #zero(method, args) {
|
|
149
|
+
const { room, secret } = this.#stranger;
|
|
150
|
+
const ward = this.#author.opened.ward;
|
|
151
|
+
const sealed = await room.stranger(ward, secret, { ...(method === undefined ? {} : { method }), args: tools.canonical(args) });
|
|
152
|
+
const read = await room.strangerRead(ward, sealed.lid, await this.#author.opened.door(sealed.box));
|
|
153
|
+
if (!('object' in read))
|
|
154
|
+
return null;
|
|
155
|
+
return method === undefined ? { describe: read.object } : read.object;
|
|
156
|
+
}
|
|
157
|
+
/** Every example of her class run, twice, on two fresh benches. */
|
|
158
|
+
static async examples(Class, options = {}) {
|
|
159
|
+
const table = resolve(Class);
|
|
160
|
+
const where = { ...options, seed: options.seed ?? 'examples' };
|
|
161
|
+
const findings = [];
|
|
162
|
+
for (const entry of Object.values(table.asks)) {
|
|
163
|
+
for (const [index, raw] of entry.examples.entries()) {
|
|
164
|
+
const example = raw;
|
|
165
|
+
const what = `${entry.method}, example ${index + 1}${example.description === undefined ? '' : `: ${example.description}`}`;
|
|
166
|
+
try {
|
|
167
|
+
const first = await Bench.#run(Class, table, entry, example, where);
|
|
168
|
+
const second = await Bench.#run(Class, table, entry, example, where);
|
|
169
|
+
if (example.gives !== undefined && tools.canonical(first.answer) !== tools.canonical(example.gives)) {
|
|
170
|
+
findings.push({ ok: false, what, why: `it gives ${JSON.stringify(first.answer)}, and the example says ${JSON.stringify(example.gives)}` });
|
|
171
|
+
}
|
|
172
|
+
else if (tools.canonical(first) !== tools.canonical(second)) {
|
|
173
|
+
findings.push({ ok: false, what, why: 'a re-run on the same cells answered differently' });
|
|
174
|
+
}
|
|
175
|
+
else
|
|
176
|
+
findings.push({ ok: true, what });
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
findings.push({ ok: false, what, why: error instanceof Error ? error.message : String(error) });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return findings;
|
|
184
|
+
}
|
|
185
|
+
// A fresh bench with her in the position the check names, and the steward beside her where one is given.
|
|
186
|
+
static #opening(Class, table, example, { classes = [], seed, position = 'normal', steward }) {
|
|
187
|
+
return Bench.open({
|
|
188
|
+
classes: position === 'normal' ? [Class, ...classes] : classes,
|
|
189
|
+
offers: fakes(table, example),
|
|
190
|
+
...(seed === undefined ? {} : { seed }),
|
|
191
|
+
...(position === 'steward' ? { steward: Class } : steward === undefined ? {} : { steward }),
|
|
192
|
+
...(position === 'public' ? { public: Class } : {}),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
static async #run(Class, table, entry, example, options) {
|
|
196
|
+
const bench = await Bench.#opening(Class, table, example, options);
|
|
197
|
+
const placed = await bench.place(Class, example.cells === undefined ? {} : { cells: example.cells });
|
|
198
|
+
const answer = await placed.ask(entry.method, example.args ?? {}, example.role === undefined ? {} : { role: example.role });
|
|
199
|
+
return { answer: answer ?? { nothing: true }, cells: await placed.cells() };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Every state her default cells and her examples reach, described to
|
|
203
|
+
* every role she names: what each is shown is what her table says.
|
|
204
|
+
*/
|
|
205
|
+
static async walk(Class, options = {}) {
|
|
206
|
+
const table = resolve(Class);
|
|
207
|
+
const where = { ...options, seed: options.seed ?? 'walk' };
|
|
208
|
+
const position = options.position ?? 'normal';
|
|
209
|
+
const findings = [];
|
|
210
|
+
const starts = [undefined, ...Object.values(table.asks).flatMap((entry) => entry.examples.map((example) => example.cells))];
|
|
211
|
+
const seen = new Set();
|
|
212
|
+
// Each role her position lets someone hold: the steward has no steward, and only the public being has strangers.
|
|
213
|
+
const roles = [...new Set([STEWARD, ...Object.keys(table.roles), ...Object.values(table.asks).flatMap((entry) => entry.for ?? [])])].filter((role) => role !== 'handle' && (role !== 'stranger' || position === 'public') && (role !== STEWARD || position !== 'steward'));
|
|
214
|
+
for (const cells of starts) {
|
|
215
|
+
// One visit a state, from the first cells that reach it.
|
|
216
|
+
const state = table.state({ id: '', position, cells: { ...table.cells, ...cells } });
|
|
217
|
+
if (seen.has(state))
|
|
218
|
+
continue;
|
|
219
|
+
seen.add(state);
|
|
220
|
+
const bench = await Bench.#opening(Class, table, {}, where);
|
|
221
|
+
const placed = await bench.place(Class, cells === undefined ? {} : { cells });
|
|
222
|
+
// The roles a stranger holds, to whom a public being never shows an effect.
|
|
223
|
+
const strange = position === 'public' ? await bench.#author.access.roles(PUBLIC, 'stranger') : [];
|
|
224
|
+
for (const role of roles) {
|
|
225
|
+
const what = `${state}, as ${role}`;
|
|
226
|
+
const described = await placed.describe({ role });
|
|
227
|
+
const shown = (described?.asks ?? []).map((entry) => entry.method).sort();
|
|
228
|
+
const owed = Object.values(table.asks)
|
|
229
|
+
.filter((entry) => !(entry.effect && (role === 'stranger' || strange.includes(role))))
|
|
230
|
+
.filter((entry) => (entry.for === null ? true : entry.for.includes(role)))
|
|
231
|
+
.map((entry) => entry.method)
|
|
232
|
+
.sort();
|
|
233
|
+
if (described === null)
|
|
234
|
+
findings.push({ ok: false, what, why: 'she answered nothing' });
|
|
235
|
+
else if (described.state !== state)
|
|
236
|
+
findings.push({ ok: false, what, why: `she says ${described.state}` });
|
|
237
|
+
else if (shown.join() !== owed.join())
|
|
238
|
+
findings.push({ ok: false, what, why: `she shows ${shown.join(', ') || 'nothing'}, and her table owes ${owed.join(', ') || 'nothing'}` });
|
|
239
|
+
else
|
|
240
|
+
findings.push({ ok: true, what });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return findings;
|
|
244
|
+
}
|
|
245
|
+
/** Her examples and her walk, and an error naming every finding that failed. */
|
|
246
|
+
static async check(Class, options = {}) {
|
|
247
|
+
const findings = [...(await Bench.examples(Class, options)), ...(await Bench.walk(Class, options))];
|
|
248
|
+
const failed = findings.filter((finding) => !finding.ok);
|
|
249
|
+
if (failed.length > 0)
|
|
250
|
+
throw new Error(`the bench found ${failed.length}:\n${failed.map((finding) => `- ${finding.what}: ${finding.why}`).join('\n')}`);
|
|
251
|
+
return findings;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// A fake of each faculty her needs name, answering what an example says,
|
|
255
|
+
// and `{ result: null }` where it says nothing.
|
|
256
|
+
const fakes = (table, example) => Object.entries(table.needs).map(([member, blueprint]) => {
|
|
257
|
+
const said = example.fakes?.[member] ?? {};
|
|
258
|
+
const object = Object.fromEntries(Object.keys(blueprint.methods).map((method) => [method, async () => said[method] ?? { result: null }]));
|
|
259
|
+
return { blueprint: blueprintOf(blueprint) ?? blueprint, object, kinds: [table.kind] };
|
|
260
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Carry, Sent } from '../foundation.ts';
|
|
2
|
+
export type Door = (box: Uint8Array) => Promise<Uint8Array | null>;
|
|
3
|
+
export declare class FakeCarry implements Carry {
|
|
4
|
+
#private;
|
|
5
|
+
/** The next `lose` replies are lost after the far door answered. */
|
|
6
|
+
lose: number;
|
|
7
|
+
/** Every address a box was carried to, in order. */
|
|
8
|
+
readonly tried: string[];
|
|
9
|
+
/** `at` names the addresses written into invitations, as a ground names its own; omitted, the one it listens at. */
|
|
10
|
+
constructor({ network, address, at }: {
|
|
11
|
+
network: Map<string, Door>;
|
|
12
|
+
address: string;
|
|
13
|
+
at?: readonly string[];
|
|
14
|
+
});
|
|
15
|
+
/** A house's door answers at this carry's address, and by pointer to whoever sends through this carry. */
|
|
16
|
+
listen({ ward, door }: {
|
|
17
|
+
ward: string;
|
|
18
|
+
door: Door;
|
|
19
|
+
}): void;
|
|
20
|
+
/** A ward this carry answers no more, by pointer or at its address. */
|
|
21
|
+
unlisten({ ward }: {
|
|
22
|
+
ward: string;
|
|
23
|
+
}): void;
|
|
24
|
+
send({ ward, at, box }: {
|
|
25
|
+
ward: string;
|
|
26
|
+
at: readonly string[];
|
|
27
|
+
box: Uint8Array;
|
|
28
|
+
}): Promise<Sent>;
|
|
29
|
+
at(_options?: {
|
|
30
|
+
toward?: string;
|
|
31
|
+
}): readonly string[];
|
|
32
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export class FakeCarry {
|
|
2
|
+
#network;
|
|
3
|
+
#address;
|
|
4
|
+
#at;
|
|
5
|
+
/** The next `lose` replies are lost after the far door answered. */
|
|
6
|
+
lose = 0;
|
|
7
|
+
/** Every address a box was carried to, in order. */
|
|
8
|
+
tried = [];
|
|
9
|
+
/** `at` names the addresses written into invitations, as a ground names its own; omitted, the one it listens at. */
|
|
10
|
+
constructor({ network, address, at }) {
|
|
11
|
+
this.#network = network;
|
|
12
|
+
this.#address = address;
|
|
13
|
+
this.#at = at;
|
|
14
|
+
}
|
|
15
|
+
#doors = new Map();
|
|
16
|
+
/** A house's door answers at this carry's address, and by pointer to whoever sends through this carry. */
|
|
17
|
+
listen({ ward, door }) {
|
|
18
|
+
this.#doors.set(ward, door);
|
|
19
|
+
this.#network.set(this.#address, door);
|
|
20
|
+
}
|
|
21
|
+
/** A ward this carry answers no more, by pointer or at its address. */
|
|
22
|
+
unlisten({ ward }) {
|
|
23
|
+
const door = this.#doors.get(ward);
|
|
24
|
+
this.#doors.delete(ward);
|
|
25
|
+
if (door !== undefined && this.#network.get(this.#address) === door)
|
|
26
|
+
this.#network.delete(this.#address);
|
|
27
|
+
}
|
|
28
|
+
async send({ ward, at, box }) {
|
|
29
|
+
const held = this.#doors.get(ward);
|
|
30
|
+
if (held !== undefined) {
|
|
31
|
+
const reply = await held(box);
|
|
32
|
+
if (reply !== null)
|
|
33
|
+
return { reply };
|
|
34
|
+
}
|
|
35
|
+
for (const address of at) {
|
|
36
|
+
const door = this.#network.get(address);
|
|
37
|
+
if (door === undefined)
|
|
38
|
+
continue;
|
|
39
|
+
this.tried.push(address);
|
|
40
|
+
const reply = await door(box);
|
|
41
|
+
// A reply lost after the door answered was heard, so no other address is tried.
|
|
42
|
+
if (this.lose > 0) {
|
|
43
|
+
this.lose--;
|
|
44
|
+
return { reply: null, heard: true };
|
|
45
|
+
}
|
|
46
|
+
// A door that gives nothing was not delivered to, so the next address may be.
|
|
47
|
+
if (reply === null)
|
|
48
|
+
continue;
|
|
49
|
+
return { reply, via: address };
|
|
50
|
+
}
|
|
51
|
+
return { reply: null, heard: false };
|
|
52
|
+
}
|
|
53
|
+
at(_options = {}) {
|
|
54
|
+
return this.#at ?? [this.#address];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Clock } from '../foundation.ts';
|
|
2
|
+
export declare class FakeClock implements Clock {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(start?: number);
|
|
5
|
+
now(): number;
|
|
6
|
+
wait({ id, ms }: {
|
|
7
|
+
id: string;
|
|
8
|
+
ms: number;
|
|
9
|
+
}): Promise<boolean>;
|
|
10
|
+
cancel({ id }: {
|
|
11
|
+
id: string;
|
|
12
|
+
}): void;
|
|
13
|
+
/** The time moved on, and every wait it passes fired, earliest first. */
|
|
14
|
+
advance(ms: number): void;
|
|
15
|
+
/** The time of the earliest wait, or `null`. */
|
|
16
|
+
next(): number | null;
|
|
17
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class FakeClock {
|
|
2
|
+
#now;
|
|
3
|
+
#waits = new Map();
|
|
4
|
+
constructor(start = Date.UTC(2026, 0, 1)) {
|
|
5
|
+
this.#now = start;
|
|
6
|
+
}
|
|
7
|
+
now() {
|
|
8
|
+
return this.#now;
|
|
9
|
+
}
|
|
10
|
+
wait({ id, ms }) {
|
|
11
|
+
this.#waits.get(id)?.resolve(false);
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
this.#waits.set(id, { at: this.#now + Math.max(0, ms), resolve });
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
cancel({ id }) {
|
|
17
|
+
this.#waits.get(id)?.resolve(false);
|
|
18
|
+
this.#waits.delete(id);
|
|
19
|
+
}
|
|
20
|
+
/** The time moved on, and every wait it passes fired, earliest first. */
|
|
21
|
+
advance(ms) {
|
|
22
|
+
this.#now += ms;
|
|
23
|
+
const due = [...this.#waits].filter(([, waiting]) => waiting.at <= this.#now).sort(([, a], [, b]) => a.at - b.at);
|
|
24
|
+
for (const [id, waiting] of due) {
|
|
25
|
+
this.#waits.delete(id);
|
|
26
|
+
waiting.resolve(true);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** The time of the earliest wait, or `null`. */
|
|
30
|
+
next() {
|
|
31
|
+
const times = [...this.#waits.values()].map((waiting) => waiting.at);
|
|
32
|
+
return times.length === 0 ? null : Math.min(...times);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Json } from '../being/being.ts';
|
|
2
|
+
import type { Faculty } from '../ground/ground.ts';
|
|
3
|
+
type Method = (args: Json) => Json | Promise<Json>;
|
|
4
|
+
/** One call the faculty received. */
|
|
5
|
+
export interface Called {
|
|
6
|
+
readonly method: string;
|
|
7
|
+
readonly args: Json;
|
|
8
|
+
readonly id: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class FakeFaculty {
|
|
11
|
+
#private;
|
|
12
|
+
/** Every call, in the order it came, repeats of one call id among them. */
|
|
13
|
+
readonly calls: Called[];
|
|
14
|
+
/** `blueprint` is a need or a blueprint; `methods` answer each method's args with its result. */
|
|
15
|
+
constructor(blueprint: unknown, methods: Readonly<Record<string, Method>>);
|
|
16
|
+
/** The next `count` calls throw, as a crash does. */
|
|
17
|
+
throwNext(count?: number): void;
|
|
18
|
+
/** The next call answers `{ error: { message } }`, which is final. */
|
|
19
|
+
refuseNext(message: string): void;
|
|
20
|
+
/** The next `count` calls never answer. */
|
|
21
|
+
hangNext(count?: number): void;
|
|
22
|
+
/** The offer a ground hands: the blueprint, and an object whose methods take args and a context. */
|
|
23
|
+
get offer(): Faculty;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export class FakeFaculty {
|
|
2
|
+
/** Every call, in the order it came, repeats of one call id among them. */
|
|
3
|
+
calls = [];
|
|
4
|
+
#blueprint;
|
|
5
|
+
#methods;
|
|
6
|
+
#throw = 0;
|
|
7
|
+
#refuse = [];
|
|
8
|
+
#hang = 0;
|
|
9
|
+
/** `blueprint` is a need or a blueprint; `methods` answer each method's args with its result. */
|
|
10
|
+
constructor(blueprint, methods) {
|
|
11
|
+
this.#blueprint = blueprint;
|
|
12
|
+
this.#methods = methods;
|
|
13
|
+
}
|
|
14
|
+
/** The next `count` calls throw, as a crash does. */
|
|
15
|
+
throwNext(count = 1) {
|
|
16
|
+
this.#throw += count;
|
|
17
|
+
}
|
|
18
|
+
/** The next call answers `{ error: { message } }`, which is final. */
|
|
19
|
+
refuseNext(message) {
|
|
20
|
+
this.#refuse.push(message);
|
|
21
|
+
}
|
|
22
|
+
/** The next `count` calls never answer. */
|
|
23
|
+
hangNext(count = 1) {
|
|
24
|
+
this.#hang += count;
|
|
25
|
+
}
|
|
26
|
+
/** The offer a ground hands: the blueprint, and an object whose methods take args and a context. */
|
|
27
|
+
get offer() {
|
|
28
|
+
const object = Object.fromEntries(Object.entries(this.#methods).map(([method, answer]) => [
|
|
29
|
+
method,
|
|
30
|
+
async (args, context) => {
|
|
31
|
+
this.calls.push({ method, args, id: context.id });
|
|
32
|
+
if (this.#hang > 0) {
|
|
33
|
+
this.#hang--;
|
|
34
|
+
return new Promise(() => undefined);
|
|
35
|
+
}
|
|
36
|
+
if (this.#throw > 0) {
|
|
37
|
+
this.#throw--;
|
|
38
|
+
throw new Error('the fake faculty crashed');
|
|
39
|
+
}
|
|
40
|
+
const refused = this.#refuse.shift();
|
|
41
|
+
if (refused !== undefined)
|
|
42
|
+
return { error: { message: refused } };
|
|
43
|
+
return { result: await answer(args) };
|
|
44
|
+
},
|
|
45
|
+
]));
|
|
46
|
+
return { blueprint: this.#blueprint, object };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Keys from a name: every ward of a test named, and the same each run.
|
|
3
|
+
import { SeedKeys } from '../bodies/seed-keys.js';
|
|
4
|
+
import { seedOf } from './seeded.js';
|
|
5
|
+
export class FakeKeys extends SeedKeys {
|
|
6
|
+
constructor(name = 'fake', crypto) {
|
|
7
|
+
super(seedOf(name), crypto);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Memory, PlaceRead } from '../foundation.ts';
|
|
2
|
+
export declare class FakeMemory implements Memory {
|
|
3
|
+
#private;
|
|
4
|
+
/** The next `count` writes are refused, as a memory another writer moved. */
|
|
5
|
+
refuseNext(count?: number): void;
|
|
6
|
+
read({ place }: {
|
|
7
|
+
place: string;
|
|
8
|
+
}): Promise<PlaceRead>;
|
|
9
|
+
list(): Promise<readonly string[]>;
|
|
10
|
+
write({ writes, expect, }: {
|
|
11
|
+
writes: Readonly<Record<string, Readonly<Record<string, Uint8Array | null>>>>;
|
|
12
|
+
expect: Readonly<Record<string, string | null>>;
|
|
13
|
+
}): Promise<Readonly<Record<string, string | null>> | null>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export class FakeMemory {
|
|
2
|
+
#places = new Map();
|
|
3
|
+
#refuse = 0;
|
|
4
|
+
/** The next `count` writes are refused, as a memory another writer moved. */
|
|
5
|
+
refuseNext(count = 1) {
|
|
6
|
+
this.#refuse += count;
|
|
7
|
+
}
|
|
8
|
+
async read({ place }) {
|
|
9
|
+
const held = this.#places.get(place);
|
|
10
|
+
if (held === undefined)
|
|
11
|
+
return { entries: {}, version: null };
|
|
12
|
+
return { entries: Object.fromEntries([...held.entries].map(([name, bytes]) => [name, bytes.slice()])), version: String(held.version) };
|
|
13
|
+
}
|
|
14
|
+
async list() {
|
|
15
|
+
return [...this.#places.keys()];
|
|
16
|
+
}
|
|
17
|
+
async write({ writes, expect, }) {
|
|
18
|
+
if (this.#refuse > 0) {
|
|
19
|
+
this.#refuse--;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
for (const place of new Set([...Object.keys(writes), ...Object.keys(expect)])) {
|
|
23
|
+
const held = this.#places.get(place);
|
|
24
|
+
const version = held === undefined ? null : String(held.version);
|
|
25
|
+
if (!(place in expect) || expect[place] !== version)
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const versions = {};
|
|
29
|
+
for (const [place, entries] of Object.entries(writes)) {
|
|
30
|
+
const held = this.#places.get(place) ?? { entries: new Map(), version: 0 };
|
|
31
|
+
for (const [name, bytes] of Object.entries(entries)) {
|
|
32
|
+
if (bytes === null)
|
|
33
|
+
held.entries.delete(name);
|
|
34
|
+
else
|
|
35
|
+
held.entries.set(name, bytes.slice());
|
|
36
|
+
}
|
|
37
|
+
held.version++;
|
|
38
|
+
if (held.entries.size === 0)
|
|
39
|
+
this.#places.delete(place);
|
|
40
|
+
else
|
|
41
|
+
this.#places.set(place, held);
|
|
42
|
+
versions[place] = held.entries.size === 0 ? null : String(held.version);
|
|
43
|
+
}
|
|
44
|
+
return versions;
|
|
45
|
+
}
|
|
46
|
+
}
|