nervur 0.23.0 → 0.24.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/CONTRIBUTING.md +13 -0
- package/LICENSE +55 -202
- package/NOTICE +3 -8
- package/README.md +14 -156
- package/RELEASING.md +15 -0
- package/SECURITY.md +9 -0
- package/bin/nervur.js +6 -0
- package/package.json +26 -81
- package/AUTHORING.md +0 -935
- package/COMMAND.md +0 -179
- package/FACES.md +0 -280
- package/FACULTIES.md +0 -578
- package/GROUNDS.md +0 -315
- package/KIT-SPEC.md +0 -304
- package/WORLD.md +0 -195
- package/dist/app/app-ground.d.ts +0 -18
- package/dist/app/app-ground.js +0 -23
- package/dist/app/index.d.ts +0 -2
- package/dist/app/index.js +0 -5
- package/dist/app/native.d.ts +0 -11
- package/dist/app/native.js +0 -13
- package/dist/being/being.d.ts +0 -306
- package/dist/being/being.js +0 -20
- package/dist/being/covers.d.ts +0 -9
- package/dist/being/covers.js +0 -21
- package/dist/being/index.d.ts +0 -4
- package/dist/being/index.js +0 -6
- package/dist/being/need.d.ts +0 -49
- package/dist/being/need.js +0 -33
- package/dist/being/schema.d.ts +0 -100
- package/dist/being/schema.js +0 -132
- package/dist/being/table.d.ts +0 -48
- package/dist/being/table.js +0 -344
- package/dist/bench/bench-ground.d.ts +0 -73
- package/dist/bench/bench-ground.js +0 -153
- package/dist/bench/bench.d.ts +0 -101
- package/dist/bench/bench.js +0 -368
- package/dist/bench/fake-clock.d.ts +0 -17
- package/dist/bench/fake-clock.js +0 -34
- package/dist/bench/fake-memory.d.ts +0 -16
- package/dist/bench/fake-memory.js +0 -46
- package/dist/bench/fake-network.d.ts +0 -66
- package/dist/bench/fake-network.js +0 -222
- package/dist/bench/fake-unlock.d.ts +0 -6
- package/dist/bench/fake-unlock.js +0 -10
- package/dist/bench/index.d.ts +0 -3
- package/dist/bench/index.js +0 -5
- package/dist/bench/seeded.d.ts +0 -11
- package/dist/bench/seeded.js +0 -37
- package/dist/bench/settle.d.ts +0 -2
- package/dist/bench/settle.js +0 -49
- package/dist/bench/stewards.d.ts +0 -169
- package/dist/bench/stewards.js +0 -128
- package/dist/bodies/class-list.d.ts +0 -14
- package/dist/bodies/class-list.js +0 -33
- package/dist/bodies/joined-carry.d.ts +0 -29
- package/dist/bodies/joined-carry.js +0 -48
- package/dist/bodies/kept.d.ts +0 -44
- package/dist/bodies/kept.js +0 -91
- package/dist/bodies/noble-crypto.d.ts +0 -22
- package/dist/bodies/noble-crypto.js +0 -88
- package/dist/bodies/seed-keys.d.ts +0 -10
- package/dist/bodies/seed-keys.js +0 -42
- package/dist/bodies/strict-tools.d.ts +0 -10
- package/dist/bodies/strict-tools.js +0 -306
- package/dist/bodies/vouch.d.ts +0 -20
- package/dist/bodies/vouch.js +0 -71
- package/dist/bodies/web-carry.d.ts +0 -72
- package/dist/bodies/web-carry.js +0 -245
- package/dist/bodies/web-clock.d.ts +0 -12
- package/dist/bodies/web-clock.js +0 -33
- package/dist/browser/browser-ground.d.ts +0 -76
- package/dist/browser/browser-ground.js +0 -294
- package/dist/browser/index.d.ts +0 -4
- package/dist/browser/index.js +0 -7
- package/dist/browser/indexeddb-memory.d.ts +0 -17
- package/dist/browser/indexeddb-memory.js +0 -97
- package/dist/browser/locked-unlock.d.ts +0 -24
- package/dist/browser/locked-unlock.js +0 -85
- package/dist/browser/origin-classes.d.ts +0 -7
- package/dist/browser/origin-classes.js +0 -31
- package/dist/edge/durable-clock.d.ts +0 -19
- package/dist/edge/durable-clock.js +0 -61
- package/dist/edge/durable-memory.d.ts +0 -5
- package/dist/edge/durable-memory.js +0 -11
- package/dist/edge/durable.d.ts +0 -18
- package/dist/edge/durable.js +0 -24
- package/dist/edge/edge-ground.d.ts +0 -49
- package/dist/edge/edge-ground.js +0 -223
- package/dist/edge/index.d.ts +0 -7
- package/dist/edge/index.js +0 -9
- package/dist/edge/native-crypto.d.ts +0 -8
- package/dist/edge/native-crypto.js +0 -102
- package/dist/edge/secret-unlock.d.ts +0 -7
- package/dist/edge/secret-unlock.js +0 -13
- package/dist/edge/socket-carry.d.ts +0 -39
- package/dist/edge/socket-carry.js +0 -111
- package/dist/foundation.d.ts +0 -162
- package/dist/foundation.js +0 -4
- package/dist/ground/dock.d.ts +0 -1460
- package/dist/ground/dock.js +0 -1159
- package/dist/ground/ground.d.ts +0 -239
- package/dist/ground/ground.js +0 -787
- package/dist/ground/views.d.ts +0 -76
- package/dist/ground/views.js +0 -208
- package/dist/house/crossing.d.ts +0 -50
- package/dist/house/crossing.js +0 -153
- package/dist/house/house.d.ts +0 -88
- package/dist/house/house.js +0 -2069
- package/dist/house/rows-shape.d.ts +0 -110
- package/dist/house/rows-shape.js +0 -12
- package/dist/house/rows.d.ts +0 -27
- package/dist/house/rows.js +0 -185
- package/dist/index.d.ts +0 -33
- package/dist/index.js +0 -29
- package/dist/node/bridge.d.ts +0 -20
- package/dist/node/bridge.js +0 -284
- package/dist/node/cli.d.ts +0 -2
- package/dist/node/cli.js +0 -225
- package/dist/node/file-memory.d.ts +0 -13
- package/dist/node/file-memory.js +0 -86
- package/dist/node/folder-classes.d.ts +0 -5
- package/dist/node/folder-classes.js +0 -40
- package/dist/node/gone.d.ts +0 -2
- package/dist/node/gone.js +0 -7
- package/dist/node/hand.d.ts +0 -39
- package/dist/node/hand.js +0 -107
- package/dist/node/http.d.ts +0 -11
- package/dist/node/http.js +0 -90
- package/dist/node/index.d.ts +0 -11
- package/dist/node/index.js +0 -13
- package/dist/node/ledger-memory.d.ts +0 -17
- package/dist/node/ledger-memory.js +0 -152
- package/dist/node/lock.d.ts +0 -10
- package/dist/node/lock.js +0 -78
- package/dist/node/node-ground.d.ts +0 -26
- package/dist/node/node-ground.js +0 -228
- package/dist/node/notify.d.ts +0 -1
- package/dist/node/notify.js +0 -18
- package/dist/node/shell.d.ts +0 -11
- package/dist/node/shell.js +0 -35
- package/dist/node/sync-folder.d.ts +0 -6
- package/dist/node/sync-folder.js +0 -18
- package/dist/node/tcp-carry.d.ts +0 -42
- package/dist/node/tcp-carry.js +0 -193
- package/dist/node/unlock.d.ts +0 -17
- package/dist/node/unlock.js +0 -86
- package/dist/node/websocket.d.ts +0 -12
- package/dist/node/websocket.js +0 -134
- package/dist/quo/frame.d.ts +0 -33
- package/dist/quo/frame.js +0 -92
- package/dist/quo/index.d.ts +0 -1
- package/dist/quo/index.js +0 -5
- package/dist/quo/room.d.ts +0 -154
- package/dist/quo/room.js +0 -362
- package/dist/serve/index.d.ts +0 -45
- package/dist/serve/index.js +0 -71
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// A network in the process. Grounds join it under a host, names point at
|
|
3
|
-
// hosts as DNS points them, and every box between hosts crosses it. A test
|
|
4
|
-
// makes it slow, drops, loses and repeats boxes on it, cuts it one way or
|
|
5
|
-
// both, turns a host off and points a name elsewhere, as the world does.
|
|
6
|
-
// It keeps the one clock every ground on it reads, which the test alone
|
|
7
|
-
// moves, and its chance is drawn from a seed, so a run goes the same way
|
|
8
|
-
// twice.
|
|
9
|
-
import { vouchesOf } from '../index.js';
|
|
10
|
-
import { FakeClock } from './fake-clock.js';
|
|
11
|
-
import { stream } from './seeded.js';
|
|
12
|
-
import { settle } from './settle.js';
|
|
13
|
-
const SCHEME = 'bench://';
|
|
14
|
-
const LATE = Symbol('late');
|
|
15
|
-
const pair = (a, b) => [a, b].sort().join('\n');
|
|
16
|
-
// Each network's clock, which the grounds on it and the bench read, and no entry exports.
|
|
17
|
-
const clocks = new WeakMap();
|
|
18
|
-
/** The clock of a network, as a BenchGround reads it. */
|
|
19
|
-
export const clockOf = (network) => clocks.get(network);
|
|
20
|
-
export class FakeNetwork {
|
|
21
|
-
#hosts = new Map();
|
|
22
|
-
#names = new Map();
|
|
23
|
-
// One-way cuts, `from` then `to`.
|
|
24
|
-
#cuts = new Set();
|
|
25
|
-
#links = new Map();
|
|
26
|
-
#clock;
|
|
27
|
-
#chance;
|
|
28
|
-
#drop = 0;
|
|
29
|
-
#lose = 0;
|
|
30
|
-
#twice = 0;
|
|
31
|
-
#waits = 0;
|
|
32
|
-
/** Every box that crossed between two hosts, in order. A box by pointer inside a host crosses nothing. */
|
|
33
|
-
crossings = [];
|
|
34
|
-
/** `seed` draws every chance; `start` is the time its clock starts at. */
|
|
35
|
-
constructor({ seed = 'network', start } = {}) {
|
|
36
|
-
this.#clock = new FakeClock(start);
|
|
37
|
-
clocks.set(this, this.#clock);
|
|
38
|
-
this.#chance = stream(`network:${seed}`);
|
|
39
|
-
}
|
|
40
|
-
/** The time on its clock, which every ground on it reads. */
|
|
41
|
-
now() {
|
|
42
|
-
return this.#clock.now();
|
|
43
|
-
}
|
|
44
|
-
/** Every ask, effect and reply the grounds started, run to where it waits on the clock or ends. */
|
|
45
|
-
settle() {
|
|
46
|
-
return settle();
|
|
47
|
-
}
|
|
48
|
-
/** The clock moved on by `ms` at once, every wait it passes fired, and what came due run. */
|
|
49
|
-
async advance(ms) {
|
|
50
|
-
this.#clock.advance(ms);
|
|
51
|
-
await settle();
|
|
52
|
-
}
|
|
53
|
-
/** A host's carry. One that listens is dialled at its names; one that does not only dials, as a device. */
|
|
54
|
-
join(host, { names = [], listens = true, serve } = {}) {
|
|
55
|
-
const held = this.#hosts.get(host) ?? { doors: new Map(), listens, up: true };
|
|
56
|
-
if (serve !== undefined)
|
|
57
|
-
held.serve = serve;
|
|
58
|
-
this.#hosts.set(host, held);
|
|
59
|
-
for (const name of names)
|
|
60
|
-
this.#names.set(name, host);
|
|
61
|
-
return {
|
|
62
|
-
listen: ({ ward, door }) => void held.doors.set(ward, door),
|
|
63
|
-
unlisten: ({ ward }) => void held.doors.delete(ward),
|
|
64
|
-
send: (options) => this.#send(host, options),
|
|
65
|
-
at: () => (held.listens ? [...this.#names].filter(([, to]) => to === host).map(([name]) => SCHEME + name) : []),
|
|
66
|
-
// Each name its addresses write is a domain, read at the listener of the host it points at.
|
|
67
|
-
vouched: ({ ward, at }) => vouchesOf({ fetcher: (url) => this.#get(host, url), ward, at, schemes: ['bench'] }),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
// A GET over the web from one host to the host a name points at, which a cut or a host off stops.
|
|
71
|
-
async #get(from, url) {
|
|
72
|
-
const to = this.#names.get(new URL(url).hostname);
|
|
73
|
-
const held = to === undefined ? undefined : this.#hosts.get(to);
|
|
74
|
-
if (to === undefined || held?.serve === undefined || !held.up || this.#cuts.has(`${from}\n${to}`))
|
|
75
|
-
throw new TypeError('fetch failed');
|
|
76
|
-
return held.serve(new Request(url));
|
|
77
|
-
}
|
|
78
|
-
/** A name pointed at a host, as a DNS record moved. */
|
|
79
|
-
point(name, host) {
|
|
80
|
-
this.#names.set(name, host);
|
|
81
|
-
}
|
|
82
|
-
/** How the link between two hosts behaves, both ways, until it is set again. */
|
|
83
|
-
link(a, b, link) {
|
|
84
|
-
for (const share of [link.drop, link.lose, link.duplicate])
|
|
85
|
-
if (share !== undefined && !(share >= 0 && share <= 1))
|
|
86
|
-
throw new TypeError('a share is between 0 and 1');
|
|
87
|
-
this.#links.set(pair(a, b), link);
|
|
88
|
-
}
|
|
89
|
-
/** No box crosses from `from` to `to` until `heal`; the other way stands. */
|
|
90
|
-
cut(from, to) {
|
|
91
|
-
this.#cuts.add(`${from}\n${to}`);
|
|
92
|
-
}
|
|
93
|
-
/** No box crosses between the two hosts, either way, until `heal`. */
|
|
94
|
-
partition(a, b) {
|
|
95
|
-
this.cut(a, b);
|
|
96
|
-
this.cut(b, a);
|
|
97
|
-
}
|
|
98
|
-
/** Every cut mended. Links keep how they behave. */
|
|
99
|
-
heal() {
|
|
100
|
-
this.#cuts.clear();
|
|
101
|
-
}
|
|
102
|
-
/** The host answers nothing, its doors forgotten, until `up`. */
|
|
103
|
-
down(host) {
|
|
104
|
-
const held = this.#hosts.get(host);
|
|
105
|
-
if (held === undefined)
|
|
106
|
-
return;
|
|
107
|
-
held.up = false;
|
|
108
|
-
held.doors.clear();
|
|
109
|
-
}
|
|
110
|
-
up(host) {
|
|
111
|
-
const held = this.#hosts.get(host);
|
|
112
|
-
if (held !== undefined)
|
|
113
|
-
held.up = true;
|
|
114
|
-
}
|
|
115
|
-
/** The next `count` boxes between hosts are dropped before their door. */
|
|
116
|
-
dropNext(count = 1) {
|
|
117
|
-
this.#drop += count;
|
|
118
|
-
}
|
|
119
|
-
/** The next `count` replies are lost after the far door answered. */
|
|
120
|
-
loseNext(count = 1) {
|
|
121
|
-
this.#lose += count;
|
|
122
|
-
}
|
|
123
|
-
/** The next `count` boxes are delivered twice, and the first reply returns. */
|
|
124
|
-
duplicateNext(count = 1) {
|
|
125
|
-
this.#twice += count;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* The clock moved on by `ms` in steps of `step`, the bench settling after
|
|
129
|
-
* each, so latencies end, retries leave and replies land as time passes.
|
|
130
|
-
*/
|
|
131
|
-
async elapse(ms, { step = 1_000 } = {}) {
|
|
132
|
-
await settle();
|
|
133
|
-
for (let left = ms; left > 0; left -= step) {
|
|
134
|
-
this.#clock.advance(Math.min(step, left));
|
|
135
|
-
await settle();
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
// Whether a chance of `share` comes up, drawn from the seed.
|
|
139
|
-
#roll(share) {
|
|
140
|
-
if (share === undefined || share <= 0)
|
|
141
|
-
return false;
|
|
142
|
-
const [a, b] = this.#chance(2);
|
|
143
|
-
return (a * 256 + b) / 65_536 < share;
|
|
144
|
-
}
|
|
145
|
-
async #delay(ms) {
|
|
146
|
-
if (ms === undefined || ms <= 0)
|
|
147
|
-
return;
|
|
148
|
-
await this.#clock.wait({ id: `network:${++this.#waits}`, ms });
|
|
149
|
-
}
|
|
150
|
-
// A door's answer, or LATE where the box's wait on the network's clock runs out first.
|
|
151
|
-
async #within(answer, wait) {
|
|
152
|
-
if (wait === undefined)
|
|
153
|
-
return answer;
|
|
154
|
-
const id = `network:late:${++this.#waits}`;
|
|
155
|
-
const late = this.#clock.wait({ id, ms: wait }).then((fired) => (fired ? LATE : new Promise(() => undefined)));
|
|
156
|
-
try {
|
|
157
|
-
return await Promise.race([answer, late]);
|
|
158
|
-
}
|
|
159
|
-
finally {
|
|
160
|
-
this.#clock.cancel({ id });
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
async #send(from, { ward, at, box, wait }) {
|
|
164
|
-
const own = this.#hosts.get(from);
|
|
165
|
-
if (own === undefined || !own.up)
|
|
166
|
-
return { reply: null, heard: false };
|
|
167
|
-
// A ward on this host takes the box by pointer, before any address, and crosses nothing.
|
|
168
|
-
const near = own.doors.get(ward);
|
|
169
|
-
if (near !== undefined) {
|
|
170
|
-
const reply = await near(box);
|
|
171
|
-
if (reply !== null)
|
|
172
|
-
return { reply };
|
|
173
|
-
}
|
|
174
|
-
for (const address of at) {
|
|
175
|
-
if (!address.startsWith(SCHEME))
|
|
176
|
-
continue;
|
|
177
|
-
const to = this.#names.get(address.slice(SCHEME.length));
|
|
178
|
-
const host = to === undefined ? undefined : this.#hosts.get(to);
|
|
179
|
-
if (to === undefined || host === undefined || !host.up || !host.listens || this.#cuts.has(`${from}\n${to}`))
|
|
180
|
-
continue;
|
|
181
|
-
const link = this.#links.get(pair(from, to)) ?? {};
|
|
182
|
-
const log = (outcome) => this.crossings.push({ from, to, ward, outcome });
|
|
183
|
-
await this.#delay(link.latency);
|
|
184
|
-
if (this.#drop > 0 || this.#roll(link.drop)) {
|
|
185
|
-
if (this.#drop > 0)
|
|
186
|
-
this.#drop--;
|
|
187
|
-
log('dropped');
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
// The door is read again after the latency: a host may have gone down, or a cut come, meanwhile.
|
|
191
|
-
const door = host.up && !this.#cuts.has(`${from}\n${to}`) ? host.doors.get(ward) : undefined;
|
|
192
|
-
if (door === undefined)
|
|
193
|
-
continue;
|
|
194
|
-
const twice = this.#twice > 0 || this.#roll(link.duplicate);
|
|
195
|
-
if (this.#twice > 0)
|
|
196
|
-
this.#twice--;
|
|
197
|
-
const reply = await this.#within(door(box), wait);
|
|
198
|
-
// A door that answered past the box's wait may have heard it.
|
|
199
|
-
if (reply === LATE) {
|
|
200
|
-
log('late');
|
|
201
|
-
return { reply: null, heard: true };
|
|
202
|
-
}
|
|
203
|
-
if (twice)
|
|
204
|
-
await door(box);
|
|
205
|
-
await this.#delay(link.latency);
|
|
206
|
-
// A reply lost after the door answered was heard, so no other address is tried.
|
|
207
|
-
if (this.#lose > 0 || this.#roll(link.lose) || this.#cuts.has(`${to}\n${from}`)) {
|
|
208
|
-
if (this.#lose > 0)
|
|
209
|
-
this.#lose--;
|
|
210
|
-
log('lost');
|
|
211
|
-
return { reply: null, heard: true };
|
|
212
|
-
}
|
|
213
|
-
if (reply === null) {
|
|
214
|
-
log('silent');
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
log(twice ? 'twice' : 'answered');
|
|
218
|
-
return { reply, via: address };
|
|
219
|
-
}
|
|
220
|
-
return { reply: null, heard: false };
|
|
221
|
-
}
|
|
222
|
-
}
|
package/dist/bench/index.d.ts
DELETED
package/dist/bench/index.js
DELETED
package/dist/bench/seeded.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { NobleCrypto } from '../index.ts';
|
|
2
|
-
/** A stream of bytes from a name: the same name, the same bytes. */
|
|
3
|
-
export declare const stream: (name: string) => (length: number) => Uint8Array;
|
|
4
|
-
/** Sixty-four hex digits from a name. */
|
|
5
|
-
export declare const seedOf: (name: string) => string;
|
|
6
|
-
/** The default crypto, with randomness drawn from a seed. */
|
|
7
|
-
export declare class SeededCrypto extends NobleCrypto {
|
|
8
|
-
#private;
|
|
9
|
-
constructor(name: string);
|
|
10
|
-
random(length: number): Uint8Array;
|
|
11
|
-
}
|
package/dist/bench/seeded.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// What makes a bench run the same twice: bytes drawn from a seed, never
|
|
3
|
-
// from the platform. They are for tests alone and guard nothing.
|
|
4
|
-
import { NobleCrypto } from '../index.js';
|
|
5
|
-
/** A stream of bytes from a name: the same name, the same bytes. */
|
|
6
|
-
export const stream = (name) => {
|
|
7
|
-
// FNV-1a over the name seeds xorshift32.
|
|
8
|
-
let state = 0x811c9dc5;
|
|
9
|
-
for (let at = 0; at < name.length; at++)
|
|
10
|
-
state = Math.imul(state ^ name.charCodeAt(at), 0x01000193) >>> 0;
|
|
11
|
-
if (state === 0)
|
|
12
|
-
state = 1;
|
|
13
|
-
return (length) => {
|
|
14
|
-
const out = new Uint8Array(length);
|
|
15
|
-
for (let at = 0; at < length; at++) {
|
|
16
|
-
state ^= state << 13;
|
|
17
|
-
state ^= state >>> 17;
|
|
18
|
-
state ^= state << 5;
|
|
19
|
-
state >>>= 0;
|
|
20
|
-
out[at] = state & 0xff;
|
|
21
|
-
}
|
|
22
|
-
return out;
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
/** Sixty-four hex digits from a name. */
|
|
26
|
-
export const seedOf = (name) => Array.from(stream(`seed:${name}`)(32), (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
27
|
-
/** The default crypto, with randomness drawn from a seed. */
|
|
28
|
-
export class SeededCrypto extends NobleCrypto {
|
|
29
|
-
#draw;
|
|
30
|
-
constructor(name) {
|
|
31
|
-
super();
|
|
32
|
-
this.#draw = stream(`random:${name}`);
|
|
33
|
-
}
|
|
34
|
-
random(length) {
|
|
35
|
-
return this.#draw(length);
|
|
36
|
-
}
|
|
37
|
-
}
|
package/dist/bench/settle.d.ts
DELETED
package/dist/bench/settle.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// Letting the bench go idle. The fakes do no I/O, so every ask, effect and
|
|
3
|
-
// reply they started runs on the event loop, and the one thing that leaves
|
|
4
|
-
// it is WebCrypto, which runs on the engine's own threads and outlasts any
|
|
5
|
-
// count of turns on a loaded machine. So the bench counts WebCrypto calls
|
|
6
|
-
// in flight, at `crypto.subtle` itself, the one door every body's crypto
|
|
7
|
-
// passes, and calls the loop idle only once none has been in flight for a
|
|
8
|
-
// run of turns. It counts in the process that imports the bench alone.
|
|
9
|
-
// Turns with nothing in flight before the bench calls the loop idle: the
|
|
10
|
-
// promise chains between two crypto calls resolve well within them.
|
|
11
|
-
const QUIET = 20;
|
|
12
|
-
// A loop that never goes idle is a fault the bench names, never a hang.
|
|
13
|
-
const BOUND = 200_000;
|
|
14
|
-
let inFlight = 0;
|
|
15
|
-
const METHODS = ['digest', 'importKey', 'exportKey', 'deriveBits', 'deriveKey', 'encrypt', 'decrypt', 'sign', 'verify', 'generateKey', 'wrapKey', 'unwrapKey'];
|
|
16
|
-
// Each method of the engine's `subtle`, counted while its promise is open. Wrapped once, however often the bench loads.
|
|
17
|
-
const subtle = globalThis.crypto?.subtle;
|
|
18
|
-
const COUNTED = Symbol.for('nervur.bench.counted');
|
|
19
|
-
if (subtle !== undefined && subtle[COUNTED] !== true) {
|
|
20
|
-
for (const method of METHODS) {
|
|
21
|
-
const original = subtle[method];
|
|
22
|
-
if (typeof original !== 'function')
|
|
23
|
-
continue;
|
|
24
|
-
subtle[method] = (...args) => {
|
|
25
|
-
inFlight++;
|
|
26
|
-
const done = () => {
|
|
27
|
-
inFlight--;
|
|
28
|
-
};
|
|
29
|
-
const called = original.apply(globalThis.crypto.subtle, args);
|
|
30
|
-
void called.then(done, done);
|
|
31
|
-
return called;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
subtle[COUNTED] = true;
|
|
35
|
-
}
|
|
36
|
-
// One turn of the event loop: an immediate where the engine has one, else the shortest timer.
|
|
37
|
-
const turnOnce = typeof globalThis.setImmediate === 'function'
|
|
38
|
-
? (next) => globalThis.setImmediate(next)
|
|
39
|
-
: (next) => setTimeout(next, 0);
|
|
40
|
-
/** Every ask, effect and reply the fakes started, run to where it waits on the clock or ends. */
|
|
41
|
-
export const settle = async () => {
|
|
42
|
-
let quiet = 0;
|
|
43
|
-
for (let turn = 0; quiet < QUIET; turn++) {
|
|
44
|
-
if (turn >= BOUND)
|
|
45
|
-
throw new Error('the bench never went idle: crypto stayed in flight');
|
|
46
|
-
await new Promise((next) => turnOnce(next));
|
|
47
|
-
quiet = inFlight === 0 ? quiet + 1 : 0;
|
|
48
|
-
}
|
|
49
|
-
};
|
package/dist/bench/stewards.d.ts
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { type Args, type Json } from '../being/index.ts';
|
|
2
|
-
/** One ask's outcome: its answer, or `queued` where it left as an effect whose reply lands later. */
|
|
3
|
-
type Outcome = {
|
|
4
|
-
answer: {
|
|
5
|
-
result: Json;
|
|
6
|
-
} | {
|
|
7
|
-
error: {
|
|
8
|
-
message: string;
|
|
9
|
-
};
|
|
10
|
-
};
|
|
11
|
-
} | {
|
|
12
|
-
queued: true;
|
|
13
|
-
};
|
|
14
|
-
declare const BenchSteward_base: import("../being/being.ts").BeingClass<{
|
|
15
|
-
heard: Json[];
|
|
16
|
-
}, Record<never, never>, {
|
|
17
|
-
readonly bear: {
|
|
18
|
-
readonly for: "root";
|
|
19
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
20
|
-
args?: undefined;
|
|
21
|
-
id: string;
|
|
22
|
-
kind: string;
|
|
23
|
-
}>;
|
|
24
|
-
};
|
|
25
|
-
readonly door: {
|
|
26
|
-
readonly for: "root";
|
|
27
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
28
|
-
id: string;
|
|
29
|
-
occupant: string;
|
|
30
|
-
role?: string | undefined;
|
|
31
|
-
}>;
|
|
32
|
-
readonly result: import("../being/schema.ts").Schema<{
|
|
33
|
-
door: import("../being/schema.ts").HandleMark;
|
|
34
|
-
}>;
|
|
35
|
-
};
|
|
36
|
-
readonly introduce: {
|
|
37
|
-
readonly for: "root";
|
|
38
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
39
|
-
from: string;
|
|
40
|
-
to: string;
|
|
41
|
-
}>;
|
|
42
|
-
};
|
|
43
|
-
readonly ask: {
|
|
44
|
-
readonly for: "root";
|
|
45
|
-
readonly hints: {
|
|
46
|
-
readonly idempotent: true;
|
|
47
|
-
};
|
|
48
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
49
|
-
args?: undefined;
|
|
50
|
-
id: string;
|
|
51
|
-
method: string;
|
|
52
|
-
}>;
|
|
53
|
-
readonly result: Readonly<{}>;
|
|
54
|
-
};
|
|
55
|
-
readonly shows: {
|
|
56
|
-
readonly for: "root";
|
|
57
|
-
readonly hints: {
|
|
58
|
-
readonly idempotent: true;
|
|
59
|
-
};
|
|
60
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
61
|
-
id: string;
|
|
62
|
-
}>;
|
|
63
|
-
readonly result: Readonly<{}>;
|
|
64
|
-
};
|
|
65
|
-
readonly heard: {
|
|
66
|
-
readonly for: "steward";
|
|
67
|
-
readonly args: Readonly<{
|
|
68
|
-
type: "object";
|
|
69
|
-
}>;
|
|
70
|
-
};
|
|
71
|
-
readonly replies: {
|
|
72
|
-
readonly for: "root";
|
|
73
|
-
readonly hints: {
|
|
74
|
-
readonly readOnly: true;
|
|
75
|
-
};
|
|
76
|
-
readonly result: import("../being/schema.ts").Schema<unknown[]>;
|
|
77
|
-
};
|
|
78
|
-
}>;
|
|
79
|
-
export declare class BenchSteward extends BenchSteward_base {
|
|
80
|
-
bear({ kind, id, args }: Args<BenchSteward, 'bear'>): void;
|
|
81
|
-
door({ id, occupant, role }: Args<BenchSteward, 'door'>): {
|
|
82
|
-
door: import("../being/schema.ts").Handle;
|
|
83
|
-
};
|
|
84
|
-
introduce({ from, to }: Args<BenchSteward, 'introduce'>): void;
|
|
85
|
-
ask({ id, method, args }: Args<BenchSteward, 'ask'>): Promise<Outcome>;
|
|
86
|
-
shows({ id }: Args<BenchSteward, 'shows'>): Promise<Json>;
|
|
87
|
-
heard(answer: Args<BenchSteward, 'heard'>): void;
|
|
88
|
-
replies(): Json[];
|
|
89
|
-
}
|
|
90
|
-
declare const BenchCaller_base: import("../being/being.ts").BeingClass<{
|
|
91
|
-
heard: Json[];
|
|
92
|
-
}, Record<never, never>, {
|
|
93
|
-
readonly adopt: {
|
|
94
|
-
readonly for: "root";
|
|
95
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
96
|
-
invitation: import("../being/schema.ts").HandleMark;
|
|
97
|
-
}>;
|
|
98
|
-
readonly result: import("../being/schema.ts").Schema<string>;
|
|
99
|
-
};
|
|
100
|
-
readonly ask: {
|
|
101
|
-
readonly for: "root";
|
|
102
|
-
readonly hints: {
|
|
103
|
-
readonly idempotent: true;
|
|
104
|
-
};
|
|
105
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
106
|
-
args?: undefined;
|
|
107
|
-
method: string;
|
|
108
|
-
standing: string;
|
|
109
|
-
}>;
|
|
110
|
-
readonly result: Readonly<{}>;
|
|
111
|
-
};
|
|
112
|
-
readonly describe: {
|
|
113
|
-
readonly for: "root";
|
|
114
|
-
readonly hints: {
|
|
115
|
-
readonly idempotent: true;
|
|
116
|
-
};
|
|
117
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
118
|
-
standing: string;
|
|
119
|
-
}>;
|
|
120
|
-
readonly result: Readonly<{}>;
|
|
121
|
-
};
|
|
122
|
-
readonly visit: {
|
|
123
|
-
readonly for: "root";
|
|
124
|
-
readonly hints: {
|
|
125
|
-
readonly idempotent: true;
|
|
126
|
-
};
|
|
127
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
128
|
-
args?: undefined;
|
|
129
|
-
at: string[];
|
|
130
|
-
method: string;
|
|
131
|
-
ward: string;
|
|
132
|
-
}>;
|
|
133
|
-
readonly result: Readonly<{}>;
|
|
134
|
-
};
|
|
135
|
-
readonly looks: {
|
|
136
|
-
readonly for: "root";
|
|
137
|
-
readonly hints: {
|
|
138
|
-
readonly idempotent: true;
|
|
139
|
-
};
|
|
140
|
-
readonly args: import("../being/schema.ts").Schema<{
|
|
141
|
-
at: string[];
|
|
142
|
-
ward: string;
|
|
143
|
-
}>;
|
|
144
|
-
readonly result: Readonly<{}>;
|
|
145
|
-
};
|
|
146
|
-
readonly heard: {
|
|
147
|
-
readonly for: "steward";
|
|
148
|
-
readonly args: Readonly<{
|
|
149
|
-
type: "object";
|
|
150
|
-
}>;
|
|
151
|
-
};
|
|
152
|
-
readonly replies: {
|
|
153
|
-
readonly for: "root";
|
|
154
|
-
readonly hints: {
|
|
155
|
-
readonly readOnly: true;
|
|
156
|
-
};
|
|
157
|
-
readonly result: import("../being/schema.ts").Schema<unknown[]>;
|
|
158
|
-
};
|
|
159
|
-
}>;
|
|
160
|
-
export declare class BenchCaller extends BenchCaller_base {
|
|
161
|
-
adopt({ invitation }: Args<BenchCaller, 'adopt'>): string;
|
|
162
|
-
ask({ standing, method, args }: Args<BenchCaller, 'ask'>): Promise<Outcome>;
|
|
163
|
-
describe({ standing }: Args<BenchCaller, 'describe'>): Promise<Json>;
|
|
164
|
-
visit({ ward, at, method, args }: Args<BenchCaller, 'visit'>): Promise<Outcome>;
|
|
165
|
-
looks({ ward, at }: Args<BenchCaller, 'looks'>): Promise<Json>;
|
|
166
|
-
heard(answer: Args<BenchCaller, 'heard'>): void;
|
|
167
|
-
replies(): Json[];
|
|
168
|
-
}
|
|
169
|
-
export {};
|
package/dist/bench/stewards.js
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// The bench's own beings, written as any author writes hers. The steward
|
|
3
|
-
// keeps the author's house where the test brings no steward of its own:
|
|
4
|
-
// it bears, invites and introduces through its powers, and asks as her
|
|
5
|
-
// steward. The caller asks through a standing, as a stranger, or as a
|
|
6
|
-
// being introduced to her. Each keeps the answers her effects' replies
|
|
7
|
-
// bring, so the bench reads what an effect answered once it lands.
|
|
8
|
-
import { Being, need, s } from '../being/index.js';
|
|
9
|
-
/** Any JSON value: a schema with no keyword. */
|
|
10
|
-
const ANY = Object.freeze({});
|
|
11
|
-
/** A reply's args: the answer an effect brought, a result or an error. */
|
|
12
|
-
const ANSWER = Object.freeze({ type: 'object' });
|
|
13
|
-
/** A card and one ask, as the caller asks a far public being. */
|
|
14
|
-
const CARD = { ward: s.string(), at: s.array(s.string()) };
|
|
15
|
-
const failed = (error) => ({ answer: { error: { message: error instanceof Error ? error.message : String(error) } } });
|
|
16
|
-
// A need for one ask a describe showed, whose result is read as the JSON it is.
|
|
17
|
-
const needFor = (shown) => {
|
|
18
|
-
const required = shown.args?.required;
|
|
19
|
-
const readOnly = shown.hints?.readOnly === true;
|
|
20
|
-
return need('asked', {
|
|
21
|
-
[shown.method]: {
|
|
22
|
-
args: { type: 'object', ...(Array.isArray(required) ? { required } : {}) },
|
|
23
|
-
result: ANY,
|
|
24
|
-
hints: { readOnly, idempotent: readOnly || shown.hints?.idempotent === true },
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
export class BenchSteward extends Being.of({
|
|
29
|
-
kind: 'org.nervur.bench.steward',
|
|
30
|
-
cells: { heard: [] },
|
|
31
|
-
asks: {
|
|
32
|
-
bear: { for: 'root', args: s.object({ kind: s.string(), id: s.string(), args: s.optional(ANY) }) },
|
|
33
|
-
door: { for: 'root', args: s.object({ id: s.string(), occupant: s.string(), role: s.optional(s.string()) }), result: s.object({ door: s.handle() }) },
|
|
34
|
-
introduce: { for: 'root', args: s.object({ from: s.string(), to: s.string() }) },
|
|
35
|
-
ask: { for: 'root', hints: { idempotent: true }, args: s.object({ id: s.string(), method: s.string(), args: s.optional(ANY) }), result: ANY },
|
|
36
|
-
shows: { for: 'root', hints: { idempotent: true }, args: s.object({ id: s.string() }), result: ANY },
|
|
37
|
-
heard: { for: 'steward', args: ANSWER },
|
|
38
|
-
replies: { for: 'root', hints: { readOnly: true }, result: s.array(ANY) },
|
|
39
|
-
},
|
|
40
|
-
}) {
|
|
41
|
-
bear({ kind, id, args }) {
|
|
42
|
-
this.powers.bear({ kind, id, ...(args === undefined ? {} : { args }) });
|
|
43
|
-
}
|
|
44
|
-
// A new occupant of hers, whose steward notes hold the role the bench plays.
|
|
45
|
-
door({ id, occupant, role }) {
|
|
46
|
-
return { door: this.powers.invite({ id, occupant, notes: role === undefined ? {} : { [role]: true } }) };
|
|
47
|
-
}
|
|
48
|
-
introduce({ from, to }) {
|
|
49
|
-
this.powers.introduce({ from, to });
|
|
50
|
-
}
|
|
51
|
-
// As her steward: awaited where her entry says so, an effect otherwise, whose answer lands as a reply.
|
|
52
|
-
async ask({ id, method, args }) {
|
|
53
|
-
try {
|
|
54
|
-
const answered = await this.powers.ask({ id, method, ...(args === undefined ? {} : { args }) }, { reply: 'heard' });
|
|
55
|
-
return answered === undefined ? { queued: true } : { answer: { result: (answered ?? null) } };
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
return failed(error);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
shows({ id }) {
|
|
62
|
-
return this.powers.ask({ id });
|
|
63
|
-
}
|
|
64
|
-
heard(answer) {
|
|
65
|
-
this.cells.heard = [...this.cells.heard, answer];
|
|
66
|
-
}
|
|
67
|
-
replies() {
|
|
68
|
-
return this.cells.heard;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
export class BenchCaller extends Being.of({
|
|
72
|
-
kind: 'org.nervur.bench.caller',
|
|
73
|
-
cells: { heard: [] },
|
|
74
|
-
asks: {
|
|
75
|
-
adopt: { for: 'root', args: s.object({ invitation: s.handle() }), result: s.string() },
|
|
76
|
-
ask: { for: 'root', hints: { idempotent: true }, args: s.object({ standing: s.string(), method: s.string(), args: s.optional(ANY) }), result: ANY },
|
|
77
|
-
describe: { for: 'root', hints: { idempotent: true }, args: s.object({ standing: s.string() }), result: ANY },
|
|
78
|
-
visit: { for: 'root', hints: { idempotent: true }, args: s.object({ ...CARD, method: s.string(), args: s.optional(ANY) }), result: ANY },
|
|
79
|
-
looks: { for: 'root', hints: { idempotent: true }, args: s.object(CARD), result: ANY },
|
|
80
|
-
heard: { for: 'steward', args: ANSWER },
|
|
81
|
-
replies: { for: 'root', hints: { readOnly: true }, result: s.array(ANY) },
|
|
82
|
-
},
|
|
83
|
-
}) {
|
|
84
|
-
adopt({ invitation }) {
|
|
85
|
-
return invitation;
|
|
86
|
-
}
|
|
87
|
-
// Through a standing she holds: what it shows her, then the ask by a need that describe covers.
|
|
88
|
-
async ask({ standing, method, args }) {
|
|
89
|
-
try {
|
|
90
|
-
const { asks } = await this.held(standing).describe();
|
|
91
|
-
const shown = asks.find((one) => one.method === method);
|
|
92
|
-
if (shown === undefined)
|
|
93
|
-
return failed('no such ask');
|
|
94
|
-
const face = this.held(standing, needFor(shown));
|
|
95
|
-
const answered = await face[method](args ?? {}, { reply: 'heard' });
|
|
96
|
-
return answered === undefined ? { queued: true } : { answer: { result: (answered ?? null) } };
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
return failed(error);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async describe({ standing }) {
|
|
103
|
-
return (await this.held(standing).describe());
|
|
104
|
-
}
|
|
105
|
-
// As a stranger to a far public being: what she shows a stranger, then the ask.
|
|
106
|
-
async visit({ ward, at, method, args }) {
|
|
107
|
-
try {
|
|
108
|
-
const { asks } = await this.stranger({ ward, at }).describe();
|
|
109
|
-
const shown = asks.find((one) => one.method === method);
|
|
110
|
-
if (shown === undefined)
|
|
111
|
-
return failed('no such ask');
|
|
112
|
-
const face = this.stranger({ ward, at }, needFor(shown));
|
|
113
|
-
return { answer: { result: ((await face[method](args ?? {})) ?? null) } };
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
return failed(error);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
async looks({ ward, at }) {
|
|
120
|
-
return (await this.stranger({ ward, at }).describe());
|
|
121
|
-
}
|
|
122
|
-
heard(answer) {
|
|
123
|
-
this.cells.heard = [...this.cells.heard, answer];
|
|
124
|
-
}
|
|
125
|
-
replies() {
|
|
126
|
-
return this.cells.heard;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { BeingClass, Classes } from '../foundation.ts';
|
|
2
|
-
export declare class ClassList implements Classes {
|
|
3
|
-
#private;
|
|
4
|
-
constructor({ steward, public: open, beings }: {
|
|
5
|
-
steward: BeingClass;
|
|
6
|
-
public?: BeingClass;
|
|
7
|
-
beings?: readonly BeingClass[];
|
|
8
|
-
});
|
|
9
|
-
resolve({ kind }: {
|
|
10
|
-
kind: string;
|
|
11
|
-
}): Promise<BeingClass | undefined>;
|
|
12
|
-
steward(): string;
|
|
13
|
-
public(): string | undefined;
|
|
14
|
-
}
|