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,101 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// The two native bodies of a phone's ground, over what the shell hands in.
|
|
3
|
+
// The shell's secret store is the iOS Keychain or the Android Keystore, and
|
|
4
|
+
// its store is a native file or database the operating system never
|
|
5
|
+
// evicts. The library holds the contracts' logic here; a shell holds only
|
|
6
|
+
// the two small interfaces below, in its own native code.
|
|
7
|
+
import { SeedKeys } from '../bodies/seed-keys.js';
|
|
8
|
+
const NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
9
|
+
const SEED = /^[0-9a-f]{64}$/;
|
|
10
|
+
const hex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
11
|
+
const bytes = (text) => new Uint8Array(text.match(/../g)?.map((pair) => parseInt(pair, 16)) ?? []);
|
|
12
|
+
/** One seed for each house in the platform's secret store, drawn on first use. */
|
|
13
|
+
export class NativeCustody {
|
|
14
|
+
#secrets;
|
|
15
|
+
#turn = Promise.resolve();
|
|
16
|
+
constructor(secrets) {
|
|
17
|
+
this.#secrets = secrets;
|
|
18
|
+
}
|
|
19
|
+
keys({ house }) {
|
|
20
|
+
if (!NAME.test(house))
|
|
21
|
+
return Promise.reject(new TypeError(`no house is named ${house}`));
|
|
22
|
+
// One seed is drawn at a time, so two first uses of a house never draw two.
|
|
23
|
+
const turn = this.#turn.then(async () => {
|
|
24
|
+
const name = `nervur.seed.${house}`;
|
|
25
|
+
let seed = await this.#secrets.get(name);
|
|
26
|
+
if (seed === null) {
|
|
27
|
+
seed = hex(crypto.getRandomValues(new Uint8Array(32)));
|
|
28
|
+
await this.#secrets.set(name, seed);
|
|
29
|
+
}
|
|
30
|
+
if (!SEED.test(seed))
|
|
31
|
+
throw new Error(`the seed of ${house} is not sixty-four hex digits`);
|
|
32
|
+
return new SeedKeys(seed);
|
|
33
|
+
});
|
|
34
|
+
this.#turn = turn.catch(() => undefined);
|
|
35
|
+
return turn;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Memory over a native store, one memory for each name. Each place is one
|
|
40
|
+
* key, its entries as hex beside its version. Versions come from one
|
|
41
|
+
* counter for the memory, so a place removed and written again never
|
|
42
|
+
* shows a version it showed before. A write reads every place it names
|
|
43
|
+
* and swaps them all against what it read.
|
|
44
|
+
*/
|
|
45
|
+
export class NativeMemory {
|
|
46
|
+
#store;
|
|
47
|
+
#prefix;
|
|
48
|
+
constructor(store, name) {
|
|
49
|
+
if (!NAME.test(name))
|
|
50
|
+
throw new TypeError(`no memory is named ${name}`);
|
|
51
|
+
this.#store = store;
|
|
52
|
+
this.#prefix = `nervur/${name}/`;
|
|
53
|
+
}
|
|
54
|
+
#key(place) {
|
|
55
|
+
return `${this.#prefix}p/${place}`;
|
|
56
|
+
}
|
|
57
|
+
async read({ place }) {
|
|
58
|
+
const text = await this.#store.get(this.#key(place));
|
|
59
|
+
if (text === null)
|
|
60
|
+
return { entries: {}, version: null };
|
|
61
|
+
const held = JSON.parse(text);
|
|
62
|
+
return { entries: Object.fromEntries(Object.entries(held.e).map(([name, value]) => [name, bytes(value)])), version: String(held.v) };
|
|
63
|
+
}
|
|
64
|
+
async list() {
|
|
65
|
+
const places = `${this.#prefix}p/`;
|
|
66
|
+
return (await this.#store.keys(places)).map((key) => key.slice(places.length));
|
|
67
|
+
}
|
|
68
|
+
async write({ writes, expect, }) {
|
|
69
|
+
const places = [...new Set([...Object.keys(writes), ...Object.keys(expect)])];
|
|
70
|
+
const count = `${this.#prefix}count`;
|
|
71
|
+
const held = { [count]: await this.#store.get(count) };
|
|
72
|
+
for (const place of places)
|
|
73
|
+
held[this.#key(place)] = await this.#store.get(this.#key(place));
|
|
74
|
+
for (const place of places) {
|
|
75
|
+
const text = held[this.#key(place)];
|
|
76
|
+
const version = text === null ? null : String(JSON.parse(text).v);
|
|
77
|
+
if (!(place in expect) || expect[place] !== version)
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
let next = held[count] === null ? 0 : Number(held[count]);
|
|
81
|
+
const landing = {};
|
|
82
|
+
const versions = {};
|
|
83
|
+
for (const [place, entries] of Object.entries(writes)) {
|
|
84
|
+
const text = held[this.#key(place)];
|
|
85
|
+
const kept = { ...(text === null ? {} : JSON.parse(text).e) };
|
|
86
|
+
for (const [name, value] of Object.entries(entries)) {
|
|
87
|
+
if (value === null)
|
|
88
|
+
delete kept[name];
|
|
89
|
+
else
|
|
90
|
+
kept[name] = hex(value);
|
|
91
|
+
}
|
|
92
|
+
next += 1;
|
|
93
|
+
const empty = Object.keys(kept).length === 0;
|
|
94
|
+
landing[this.#key(place)] = empty ? null : JSON.stringify({ v: next, e: kept });
|
|
95
|
+
versions[place] = empty ? null : String(next);
|
|
96
|
+
}
|
|
97
|
+
landing[count] = String(next);
|
|
98
|
+
// What another writer moved since the read refuses the swap, as it would the versions.
|
|
99
|
+
return (await this.#store.swap(landing, held)) ? versions : null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type { MethodSpec, Need, BLUEPRINT } from './need.ts';
|
|
2
|
+
import type { Handle, Inbound, Infer, Invitation, Outbound } from './schema.ts';
|
|
3
|
+
/** A JSON value, as cells and notes hold. */
|
|
4
|
+
export type Json = null | boolean | number | string | readonly Json[] | {
|
|
5
|
+
readonly [key: string]: Json;
|
|
6
|
+
};
|
|
7
|
+
/** What a call outward carries: JSON, bytes, and the handles and invitations the callee's schema places. */
|
|
8
|
+
export type Passed = Json | Uint8Array | Handle | Invitation | readonly Passed[] | {
|
|
9
|
+
readonly [key: string]: Passed;
|
|
10
|
+
};
|
|
11
|
+
/** Notes on a relation. */
|
|
12
|
+
export type Notes = Readonly<Record<string, Json>>;
|
|
13
|
+
/** Where the house placed her. */
|
|
14
|
+
export type Position = 'steward' | 'public' | 'normal';
|
|
15
|
+
/** Who asks now. */
|
|
16
|
+
export interface Asker {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly notes: Notes;
|
|
19
|
+
readonly steward: Notes;
|
|
20
|
+
readonly signer?: Uint8Array;
|
|
21
|
+
}
|
|
22
|
+
/** What a role and her state read of her. */
|
|
23
|
+
export interface Me<C> {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly position: Position;
|
|
26
|
+
readonly cells: Readonly<C>;
|
|
27
|
+
}
|
|
28
|
+
type Names = string | readonly string[];
|
|
29
|
+
/** One example of an ask, run by the bench and nowhere else. */
|
|
30
|
+
export interface Example {
|
|
31
|
+
readonly description?: string;
|
|
32
|
+
readonly cells?: Json;
|
|
33
|
+
readonly role?: string;
|
|
34
|
+
readonly args?: Json;
|
|
35
|
+
readonly fakes?: Json;
|
|
36
|
+
readonly gives?: Json;
|
|
37
|
+
}
|
|
38
|
+
/** An ask's entry: the method's gate. */
|
|
39
|
+
export interface Entry extends MethodSpec {
|
|
40
|
+
readonly in?: Names;
|
|
41
|
+
readonly for?: Names;
|
|
42
|
+
readonly to?: Names;
|
|
43
|
+
readonly examples?: readonly Example[];
|
|
44
|
+
}
|
|
45
|
+
/** What a class declares. */
|
|
46
|
+
export interface Declaration<C = Record<string, Json>, N = Record<string, Need>, A = Record<string, Entry>> {
|
|
47
|
+
readonly kind: string;
|
|
48
|
+
readonly description?: string;
|
|
49
|
+
readonly cells?: C;
|
|
50
|
+
readonly needs?: N;
|
|
51
|
+
readonly roles?: Readonly<Record<string, (asker: Asker, me: Me<C>) => boolean>>;
|
|
52
|
+
readonly state?: (me: Me<C>) => string;
|
|
53
|
+
readonly asks: A;
|
|
54
|
+
/** Markup as text over her asks, which a screen renders. */
|
|
55
|
+
readonly view?: string;
|
|
56
|
+
}
|
|
57
|
+
/** A relation, with her notes and her steward's. */
|
|
58
|
+
export interface Relation {
|
|
59
|
+
readonly id: string;
|
|
60
|
+
readonly notes: Notes;
|
|
61
|
+
readonly steward: Notes;
|
|
62
|
+
}
|
|
63
|
+
type Resolved<S> = S extends {
|
|
64
|
+
readonly result: infer R;
|
|
65
|
+
} ? Inbound<Infer<R>> : null;
|
|
66
|
+
type Sent<S> = S extends {
|
|
67
|
+
readonly args: infer A;
|
|
68
|
+
} ? Outbound<Infer<A>> : Record<string, never>;
|
|
69
|
+
type IsAwaited<S> = S extends {
|
|
70
|
+
readonly hints: {
|
|
71
|
+
readonly idempotent: true;
|
|
72
|
+
};
|
|
73
|
+
} ? true : S extends {
|
|
74
|
+
readonly hints: {
|
|
75
|
+
readonly readOnly: true;
|
|
76
|
+
};
|
|
77
|
+
} ? true : false;
|
|
78
|
+
/** How she calls one method: awaited, or an effect answered through `reply`. */
|
|
79
|
+
type Given<S, O extends unknown[]> = {} extends Sent<S> ? [args?: Sent<S>, ...O] : [args: Sent<S>, ...O];
|
|
80
|
+
type IsReadOnly<S> = S extends {
|
|
81
|
+
readonly hints: {
|
|
82
|
+
readonly readOnly: true;
|
|
83
|
+
};
|
|
84
|
+
} ? true : false;
|
|
85
|
+
export type Call<S, R extends string> = IsReadOnly<S> extends true ? (...given: Given<S, [options?: {
|
|
86
|
+
after?: Resolved<S>;
|
|
87
|
+
}]>) => Promise<Resolved<S>> : IsAwaited<S> extends true ? (...given: Given<S, []>) => Promise<Resolved<S>> : (...given: Given<S, [options?: {
|
|
88
|
+
reply?: R;
|
|
89
|
+
}]>) => void;
|
|
90
|
+
/** The frozen face of a need: its methods and nothing else. */
|
|
91
|
+
export type Face<X, R extends string = string> = {
|
|
92
|
+
readonly [K in Exclude<keyof X, typeof BLUEPRINT>]: Call<X[K], R>;
|
|
93
|
+
};
|
|
94
|
+
/** A standing asked with no need matched, such as her steward. */
|
|
95
|
+
export type Untyped = Readonly<Record<string, (args?: Passed, options?: {
|
|
96
|
+
reply?: string;
|
|
97
|
+
}) => Promise<unknown> | void>>;
|
|
98
|
+
/** What `this.house` gives every being. */
|
|
99
|
+
export interface HouseReach<R extends string = string> {
|
|
100
|
+
now(): number;
|
|
101
|
+
random(options: {
|
|
102
|
+
length: number;
|
|
103
|
+
}): Uint8Array;
|
|
104
|
+
alarm(options: {
|
|
105
|
+
at: number;
|
|
106
|
+
ask: R;
|
|
107
|
+
args?: Json;
|
|
108
|
+
key: string;
|
|
109
|
+
}): void;
|
|
110
|
+
cancelAlarm(options: {
|
|
111
|
+
key: string;
|
|
112
|
+
}): void;
|
|
113
|
+
}
|
|
114
|
+
/** Her standings. */
|
|
115
|
+
export interface Standings {
|
|
116
|
+
list(): readonly Relation[];
|
|
117
|
+
note(id: string, notes: Notes): void;
|
|
118
|
+
drop(id: string): void;
|
|
119
|
+
}
|
|
120
|
+
/** Her occupants and handles. */
|
|
121
|
+
export interface Occupants {
|
|
122
|
+
list(): readonly Relation[];
|
|
123
|
+
note(id: string, notes: Notes): void;
|
|
124
|
+
dismiss(id: string): void;
|
|
125
|
+
}
|
|
126
|
+
/** The steward's six powers. */
|
|
127
|
+
export interface Powers {
|
|
128
|
+
/** A new occupant of another being, as a handle she may pass on. */
|
|
129
|
+
invite(options: {
|
|
130
|
+
id: string;
|
|
131
|
+
occupant: string;
|
|
132
|
+
notes?: Notes;
|
|
133
|
+
expires?: number;
|
|
134
|
+
}): Handle;
|
|
135
|
+
bear(options: {
|
|
136
|
+
kind: string;
|
|
137
|
+
id: string;
|
|
138
|
+
args?: Json;
|
|
139
|
+
}): void;
|
|
140
|
+
remove(options: {
|
|
141
|
+
id: string;
|
|
142
|
+
}): void;
|
|
143
|
+
/** Every being, her kind, whether she is absent, and the replies her table refused. */
|
|
144
|
+
list(): Promise<readonly {
|
|
145
|
+
id: string;
|
|
146
|
+
kind: string;
|
|
147
|
+
absent: boolean;
|
|
148
|
+
dead: readonly {
|
|
149
|
+
ask: string;
|
|
150
|
+
args: Json;
|
|
151
|
+
at: number;
|
|
152
|
+
why: string;
|
|
153
|
+
}[];
|
|
154
|
+
}[]>;
|
|
155
|
+
/** Her args may carry a handle she holds or an invitation she carries, as any call outward. */
|
|
156
|
+
ask(call: {
|
|
157
|
+
id: string;
|
|
158
|
+
method: string;
|
|
159
|
+
args?: Passed;
|
|
160
|
+
}, options?: {
|
|
161
|
+
reply?: string;
|
|
162
|
+
}): Promise<unknown> | void;
|
|
163
|
+
introduce(options: {
|
|
164
|
+
from: string;
|
|
165
|
+
to: string;
|
|
166
|
+
notes?: Notes;
|
|
167
|
+
}): void;
|
|
168
|
+
}
|
|
169
|
+
export declare const DECLARATION: unique symbol;
|
|
170
|
+
declare const ASKS: unique symbol;
|
|
171
|
+
/** The asks of a class, carried in its type alone. */
|
|
172
|
+
export interface Typed<A> {
|
|
173
|
+
readonly [ASKS]?: A;
|
|
174
|
+
}
|
|
175
|
+
type AskOf<T, K> = T extends Typed<infer A> ? (K extends keyof A ? A[K] : never) : never;
|
|
176
|
+
/** The args her method `K` receives. */
|
|
177
|
+
export type Args<T, K extends string> = AskOf<T, K> extends {
|
|
178
|
+
readonly args: infer S;
|
|
179
|
+
} ? Inbound<Infer<S>> : Record<string, never>;
|
|
180
|
+
/** What her method `K` answers. */
|
|
181
|
+
export type Result<T, K extends string> = AskOf<T, K> extends {
|
|
182
|
+
readonly result: infer S;
|
|
183
|
+
} ? Outbound<Infer<S>> : void;
|
|
184
|
+
/** What every being reaches. */
|
|
185
|
+
export declare abstract class Being<C = Record<string, Json>, R extends string = string> {
|
|
186
|
+
readonly id: string;
|
|
187
|
+
readonly position: Position;
|
|
188
|
+
readonly asker: Asker;
|
|
189
|
+
cells: C;
|
|
190
|
+
readonly house: HouseReach<R>;
|
|
191
|
+
readonly standings: Standings;
|
|
192
|
+
readonly occupants: Occupants;
|
|
193
|
+
/** Her steward, asked as a standing, where she is not one. */
|
|
194
|
+
readonly steward?: Untyped;
|
|
195
|
+
/** The house's powers, where she is the steward. */
|
|
196
|
+
readonly powers?: Powers;
|
|
197
|
+
/** A standing matched to a need. */
|
|
198
|
+
held: <X extends Need>(id: string, need: X) => Face<X, R>;
|
|
199
|
+
/**
|
|
200
|
+
* A handle to one of her asks. Each invitation it leaves as is spent within
|
|
201
|
+
* `expires` milliseconds of leaving, or never binds.
|
|
202
|
+
*/
|
|
203
|
+
handle: (ask: R, options?: {
|
|
204
|
+
bind?: Json;
|
|
205
|
+
notes?: Notes;
|
|
206
|
+
once?: boolean;
|
|
207
|
+
expires?: number;
|
|
208
|
+
}) => Handle;
|
|
209
|
+
/** A new occupant, as a handle, with `expires` as `handle` has it. */
|
|
210
|
+
invite: (id: string, options?: {
|
|
211
|
+
notes?: Notes;
|
|
212
|
+
expires?: number;
|
|
213
|
+
}) => Handle;
|
|
214
|
+
/** Throws an error the asker can act on. */
|
|
215
|
+
fail: (message: string) => never;
|
|
216
|
+
/** A class, declared by one object. */
|
|
217
|
+
static of<C extends Record<string, Json> = Record<string, never>, const N extends Record<string, Need> = Record<never, never>, const A extends Record<string, Entry> = Record<string, Entry>>(declaration: Declaration<C, N, A>): BeingClass<C, N, A>;
|
|
218
|
+
}
|
|
219
|
+
/** What `Being.of` gives: a class to extend. */
|
|
220
|
+
export type BeingClass<C, N, A> = (abstract new () => Being<C, keyof A & string> & {
|
|
221
|
+
readonly [K in keyof N]: Face<N[K], keyof A & string>;
|
|
222
|
+
} & Typed<A>) & {
|
|
223
|
+
readonly [DECLARATION]: Declaration<C, N, A>;
|
|
224
|
+
};
|
|
225
|
+
/** The members of `Being` a class may not name. */
|
|
226
|
+
export declare const MEMBERS: ReadonlySet<string>;
|
|
227
|
+
/** The declaration a class carries, or nothing where it is no class of `Being.of`. */
|
|
228
|
+
export declare const declarationOf: (value: unknown) => Declaration | undefined;
|
|
229
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const DECLARATION = Symbol('declaration');
|
|
2
|
+
/** What every being reaches. */
|
|
3
|
+
export class Being {
|
|
4
|
+
/** A class, declared by one object. */
|
|
5
|
+
static of(declaration) {
|
|
6
|
+
// A class of her own, so the house reads her declaration from it.
|
|
7
|
+
class Declared extends Being {
|
|
8
|
+
static [DECLARATION] = declaration;
|
|
9
|
+
}
|
|
10
|
+
return Declared;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** The members of `Being` a class may not name. */
|
|
14
|
+
export const MEMBERS = new Set(['id', 'position', 'asker', 'cells', 'house', 'standings', 'occupants', 'steward', 'powers', 'held', 'handle', 'invite', 'fail']);
|
|
15
|
+
/** The declaration a class carries, or nothing where it is no class of `Being.of`. */
|
|
16
|
+
export const declarationOf = (value) => {
|
|
17
|
+
if (typeof value !== 'function' || !(value.prototype instanceof Being))
|
|
18
|
+
return undefined;
|
|
19
|
+
return value[DECLARATION];
|
|
20
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Blueprint } from './need.ts';
|
|
2
|
+
/** Covered, or the first rule that fails, as a sentence. */
|
|
3
|
+
export type Cover = {
|
|
4
|
+
readonly covered: true;
|
|
5
|
+
} | {
|
|
6
|
+
readonly covered: false;
|
|
7
|
+
readonly why: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const covers: (offer: Blueprint, want: Blueprint) => Cover;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const requiredOf = (schema) => {
|
|
2
|
+
const required = schema.required;
|
|
3
|
+
return Array.isArray(required) ? required : [];
|
|
4
|
+
};
|
|
5
|
+
export const covers = (offer, want) => {
|
|
6
|
+
if (offer.name !== want.name)
|
|
7
|
+
return { covered: false, why: `the offer is ${offer.name}, and the need is ${want.name}` };
|
|
8
|
+
for (const [name, wanted] of Object.entries(want.methods)) {
|
|
9
|
+
const offered = offer.methods[name];
|
|
10
|
+
if (offered === undefined)
|
|
11
|
+
return { covered: false, why: `${want.name} offers no ${name}` };
|
|
12
|
+
const sent = new Set(requiredOf(wanted.args));
|
|
13
|
+
const extra = requiredOf(offered.args).find((key) => !sent.has(key));
|
|
14
|
+
if (extra !== undefined)
|
|
15
|
+
return { covered: false, why: `${want.name}.${name} requires ${extra}, which the need does not send` };
|
|
16
|
+
if (offered.hints.idempotent !== wanted.hints.idempotent) {
|
|
17
|
+
return { covered: false, why: `${want.name}.${name} is idempotent in one and not the other` };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return { covered: true };
|
|
21
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Schema } from './schema.ts';
|
|
2
|
+
/** The three hints an ask or a method may carry. */
|
|
3
|
+
export interface Hints {
|
|
4
|
+
readonly readOnly?: boolean;
|
|
5
|
+
readonly idempotent?: boolean;
|
|
6
|
+
readonly destructive?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** A method as its author writes it, in a need or in an ask's entry. */
|
|
9
|
+
export interface MethodSpec {
|
|
10
|
+
readonly args?: Schema;
|
|
11
|
+
readonly result?: Schema;
|
|
12
|
+
readonly hints?: Hints;
|
|
13
|
+
readonly description?: string;
|
|
14
|
+
/** How long an awaited caller waits for it, in milliseconds, within the bound. */
|
|
15
|
+
readonly wait?: number;
|
|
16
|
+
}
|
|
17
|
+
/** A method of a blueprint, every field settled. */
|
|
18
|
+
export interface BlueprintMethod {
|
|
19
|
+
readonly args: Schema;
|
|
20
|
+
readonly result?: Schema;
|
|
21
|
+
readonly hints: Required<Hints>;
|
|
22
|
+
readonly description?: string;
|
|
23
|
+
readonly wait: number;
|
|
24
|
+
}
|
|
25
|
+
/** An awaited call's wait where none is named, and the bound no wait passes. */
|
|
26
|
+
export declare const WAIT = 30000;
|
|
27
|
+
export declare const WAIT_BOUND = 300000;
|
|
28
|
+
/** The shape of what can be called: a name and its methods. */
|
|
29
|
+
export interface Blueprint {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly methods: Readonly<Record<string, BlueprintMethod>>;
|
|
32
|
+
}
|
|
33
|
+
export declare const BLUEPRINT: unique symbol;
|
|
34
|
+
/** A need: her methods by name, and the blueprint they make. */
|
|
35
|
+
export type Need<M extends Record<string, MethodSpec> = Record<string, MethodSpec>> = {
|
|
36
|
+
readonly [K in keyof M]: M[K];
|
|
37
|
+
} & {
|
|
38
|
+
readonly [BLUEPRINT]: Blueprint;
|
|
39
|
+
};
|
|
40
|
+
/** Whether a method is awaited: `readOnly` implies `idempotent`. */
|
|
41
|
+
export declare const awaited: (hints: Hints | undefined) => boolean;
|
|
42
|
+
/** Hints settled, `readOnly` carrying `idempotent` with it. */
|
|
43
|
+
export declare const settled: (hints: Hints | undefined) => Required<Hints>;
|
|
44
|
+
/** One method settled: the empty object where args are omitted. */
|
|
45
|
+
export declare const method: (spec: MethodSpec) => BlueprintMethod;
|
|
46
|
+
/** A blueprint at its minimum, under the name matching uses. */
|
|
47
|
+
export declare const need: <const M extends Record<string, MethodSpec>>(name: string, methods: M) => Need<M>;
|
|
48
|
+
/** The blueprint a need makes, or nothing where the value is no need. */
|
|
49
|
+
export declare const blueprintOf: (value: unknown) => Blueprint | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// A need: a blueprint at its minimum, the shape of what she calls.
|
|
3
|
+
import { s } from './schema.js';
|
|
4
|
+
/** An awaited call's wait where none is named, and the bound no wait passes. */
|
|
5
|
+
export const WAIT = 30_000;
|
|
6
|
+
export const WAIT_BOUND = 300_000;
|
|
7
|
+
export const BLUEPRINT = Symbol('blueprint');
|
|
8
|
+
/** Whether a method is awaited: `readOnly` implies `idempotent`. */
|
|
9
|
+
export const awaited = (hints) => hints?.idempotent === true || hints?.readOnly === true;
|
|
10
|
+
/** Hints settled, `readOnly` carrying `idempotent` with it. */
|
|
11
|
+
export const settled = (hints) => ({
|
|
12
|
+
readOnly: hints?.readOnly === true,
|
|
13
|
+
idempotent: awaited(hints),
|
|
14
|
+
destructive: hints?.destructive === true,
|
|
15
|
+
});
|
|
16
|
+
/** One method settled: the empty object where args are omitted. */
|
|
17
|
+
export const method = (spec) => Object.freeze({
|
|
18
|
+
args: spec.args ?? s.object({}),
|
|
19
|
+
...(spec.result === undefined ? {} : { result: spec.result }),
|
|
20
|
+
hints: Object.freeze(settled(spec.hints)),
|
|
21
|
+
...(spec.description === undefined ? {} : { description: spec.description }),
|
|
22
|
+
wait: typeof spec.wait === 'number' ? Math.min(spec.wait, WAIT_BOUND) : WAIT,
|
|
23
|
+
});
|
|
24
|
+
/** A blueprint at its minimum, under the name matching uses. */
|
|
25
|
+
export const need = (name, methods) => {
|
|
26
|
+
const blueprint = Object.freeze({
|
|
27
|
+
name,
|
|
28
|
+
methods: Object.freeze(Object.fromEntries(Object.entries(methods).map(([key, spec]) => [key, method(spec)]))),
|
|
29
|
+
});
|
|
30
|
+
return Object.freeze({ ...methods, [BLUEPRINT]: blueprint });
|
|
31
|
+
};
|
|
32
|
+
/** The blueprint a need makes, or nothing where the value is no need. */
|
|
33
|
+
export const blueprintOf = (value) => typeof value === 'object' && value !== null && BLUEPRINT in value ? value[BLUEPRINT] : undefined;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
declare const TYPE: unique symbol;
|
|
2
|
+
/** A schema in the closed subset, typed by what it admits. */
|
|
3
|
+
export type Schema<T = unknown> = {
|
|
4
|
+
readonly [key: string]: unknown;
|
|
5
|
+
} & {
|
|
6
|
+
readonly [TYPE]?: T;
|
|
7
|
+
};
|
|
8
|
+
/** The type a schema admits. */
|
|
9
|
+
export type Infer<S> = S extends {
|
|
10
|
+
readonly [TYPE]?: infer T;
|
|
11
|
+
} ? T : never;
|
|
12
|
+
/** A relation crossing: a handle on the way out, a standing id on the way in. */
|
|
13
|
+
export interface HandleMark {
|
|
14
|
+
readonly handleMark: true;
|
|
15
|
+
}
|
|
16
|
+
/** An invitation carried unopened. It is an object and never a value. */
|
|
17
|
+
export interface Invitation {
|
|
18
|
+
readonly invitation: true;
|
|
19
|
+
}
|
|
20
|
+
/** A reference to one of her asks, or to an occupant she minted. */
|
|
21
|
+
export interface Handle {
|
|
22
|
+
readonly id: string;
|
|
23
|
+
}
|
|
24
|
+
type Mapped<T, H> = T extends HandleMark ? H : T extends Invitation ? // A handle under s.invitation leaves as an invitation too.
|
|
25
|
+
Invitation | (H extends Handle ? Handle : never) : T extends Uint8Array ? T : T extends readonly (infer E)[] ? Mapped<E, H>[] : T extends object ? {
|
|
26
|
+
[K in keyof T]: Mapped<T[K], H>;
|
|
27
|
+
} : T;
|
|
28
|
+
/** What a method receives: a handle has arrived as a standing id. */
|
|
29
|
+
export type Inbound<T> = Mapped<T, string>;
|
|
30
|
+
/** What a call sends: a handle leaves as a handle, or as an invitation she carries, for its receiver to take. */
|
|
31
|
+
export type Outbound<T> = Mapped<T, Handle | Invitation>;
|
|
32
|
+
export declare const HANDLE_MEDIA = "application/vnd.nervur.handle";
|
|
33
|
+
export declare const INVITATION_MEDIA = "application/vnd.nervur.invitation";
|
|
34
|
+
/** The keywords the house checks, and no other. */
|
|
35
|
+
export declare const KEYWORDS: ReadonlySet<string>;
|
|
36
|
+
declare const OPTIONAL: unique symbol;
|
|
37
|
+
type Optional<S extends Schema> = S & {
|
|
38
|
+
readonly [OPTIONAL]: true;
|
|
39
|
+
};
|
|
40
|
+
type Flat<T> = {
|
|
41
|
+
[K in keyof T]: T[K];
|
|
42
|
+
} & {};
|
|
43
|
+
type Props = Record<string, Schema>;
|
|
44
|
+
type OptionalKeys<P extends Props> = {
|
|
45
|
+
[K in keyof P]: P[K] extends {
|
|
46
|
+
readonly [OPTIONAL]: true;
|
|
47
|
+
} ? K : never;
|
|
48
|
+
}[keyof P];
|
|
49
|
+
type ObjectOf<P extends Props> = Flat<{
|
|
50
|
+
-readonly [K in Exclude<keyof P, OptionalKeys<P>>]: Infer<P[K]>;
|
|
51
|
+
} & {
|
|
52
|
+
-readonly [K in OptionalKeys<P>]?: Infer<P[K]>;
|
|
53
|
+
}>;
|
|
54
|
+
/** A method of a need, as `s.reply` reads it. */
|
|
55
|
+
export interface MethodRef<R = unknown> {
|
|
56
|
+
readonly args?: Schema;
|
|
57
|
+
readonly result?: Schema<R>;
|
|
58
|
+
}
|
|
59
|
+
/** The args of a reply: the method's result, or its error. */
|
|
60
|
+
export type Reply<R> = {
|
|
61
|
+
result: R;
|
|
62
|
+
error?: undefined;
|
|
63
|
+
} | {
|
|
64
|
+
error: {
|
|
65
|
+
message: string;
|
|
66
|
+
};
|
|
67
|
+
result?: undefined;
|
|
68
|
+
};
|
|
69
|
+
type Bounds = {
|
|
70
|
+
minimum?: number;
|
|
71
|
+
maximum?: number;
|
|
72
|
+
};
|
|
73
|
+
type Lengths = {
|
|
74
|
+
minLength?: number;
|
|
75
|
+
maxLength?: number;
|
|
76
|
+
};
|
|
77
|
+
type Scalar = string | number | boolean | null;
|
|
78
|
+
export declare const s: Readonly<{
|
|
79
|
+
object: <P extends Props>(properties: P) => Schema<ObjectOf<P>>;
|
|
80
|
+
string: (lengths?: Lengths & {
|
|
81
|
+
pattern?: string;
|
|
82
|
+
}) => Schema<string>;
|
|
83
|
+
number: (bounds?: Bounds) => Schema<number>;
|
|
84
|
+
integer: (bounds?: Bounds) => Schema<number>;
|
|
85
|
+
boolean: () => Schema<boolean>;
|
|
86
|
+
array: <S extends Schema>(items: S) => Schema<Infer<S>[]>;
|
|
87
|
+
enum: <const V extends readonly Scalar[]>(values: V) => Schema<V[number]>;
|
|
88
|
+
const: <const V extends Scalar>(value: V) => Schema<V>;
|
|
89
|
+
bytes: (lengths?: Lengths) => Schema<Uint8Array>;
|
|
90
|
+
optional: <S extends Schema>(schema: S) => Optional<S>;
|
|
91
|
+
handle: () => Schema<HandleMark>;
|
|
92
|
+
invitation: () => Schema<Invitation>;
|
|
93
|
+
reply: <R>(method: MethodRef<R>) => Schema<Reply<unknown extends R ? null : R>>;
|
|
94
|
+
}>;
|
|
95
|
+
/**
|
|
96
|
+
* Every way a schema leaves the subset, each as a sentence naming where.
|
|
97
|
+
* The empty list is a schema the house checks.
|
|
98
|
+
*/
|
|
99
|
+
export declare const outside: (schema: unknown, at?: string) => string[];
|
|
100
|
+
export {};
|