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,17 +0,0 @@
|
|
|
1
|
-
import type { Memory, PlaceRead } from '../foundation.ts';
|
|
2
|
-
export declare class IndexedDbMemory implements Memory {
|
|
3
|
-
#private;
|
|
4
|
-
private constructor();
|
|
5
|
-
/** The memory named `name`, its database made on first use. `factory` is the engine's `indexedDB` where omitted. */
|
|
6
|
-
static open(name: string, factory?: IDBFactory): Promise<IndexedDbMemory>;
|
|
7
|
-
read({ place }: {
|
|
8
|
-
place: string;
|
|
9
|
-
}): Promise<PlaceRead>;
|
|
10
|
-
list(): Promise<readonly string[]>;
|
|
11
|
-
write({ writes, expect, }: {
|
|
12
|
-
writes: Readonly<Record<string, Readonly<Record<string, Uint8Array | null>>>>;
|
|
13
|
-
expect: Readonly<Record<string, string | null>>;
|
|
14
|
-
}): Promise<Readonly<Record<string, string | null>> | null>;
|
|
15
|
-
/** The database let go. */
|
|
16
|
-
close(): void;
|
|
17
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
const PLACES = 'places';
|
|
2
|
-
const COUNT = 'count';
|
|
3
|
-
const done = (request) => new Promise((resolve, reject) => {
|
|
4
|
-
request.onsuccess = () => resolve(request.result);
|
|
5
|
-
request.onerror = () => reject(request.error ?? new Error('IndexedDB refused the request'));
|
|
6
|
-
});
|
|
7
|
-
const copied = (entries) => Object.fromEntries(Object.entries(entries).map(([name, bytes]) => [name, bytes.slice()]));
|
|
8
|
-
export class IndexedDbMemory {
|
|
9
|
-
#db;
|
|
10
|
-
constructor(db) {
|
|
11
|
-
this.#db = db;
|
|
12
|
-
}
|
|
13
|
-
/** The memory named `name`, its database made on first use. `factory` is the engine's `indexedDB` where omitted. */
|
|
14
|
-
static async open(name, factory = indexedDB) {
|
|
15
|
-
const opening = factory.open(name, 1);
|
|
16
|
-
opening.onupgradeneeded = () => {
|
|
17
|
-
opening.result.createObjectStore(PLACES);
|
|
18
|
-
opening.result.createObjectStore(COUNT);
|
|
19
|
-
};
|
|
20
|
-
return new IndexedDbMemory(await done(opening));
|
|
21
|
-
}
|
|
22
|
-
async read({ place }) {
|
|
23
|
-
const held = (await done(this.#db.transaction(PLACES, 'readonly').objectStore(PLACES).get(place)));
|
|
24
|
-
if (held === undefined)
|
|
25
|
-
return { entries: {}, version: null };
|
|
26
|
-
return { entries: copied(held.entries), version: String(held.version) };
|
|
27
|
-
}
|
|
28
|
-
async list() {
|
|
29
|
-
const keys = await done(this.#db.transaction(PLACES, 'readonly').objectStore(PLACES).getAllKeys());
|
|
30
|
-
return keys.map(String);
|
|
31
|
-
}
|
|
32
|
-
write({ writes, expect, }) {
|
|
33
|
-
const places = [...new Set([...Object.keys(writes), ...Object.keys(expect)])];
|
|
34
|
-
return new Promise((resolve, reject) => {
|
|
35
|
-
const transaction = this.#db.transaction([PLACES, COUNT], 'readwrite');
|
|
36
|
-
const store = transaction.objectStore(PLACES);
|
|
37
|
-
const count = transaction.objectStore(COUNT);
|
|
38
|
-
const held = new Map();
|
|
39
|
-
let versions = null;
|
|
40
|
-
let refused = false;
|
|
41
|
-
transaction.oncomplete = () => resolve(versions);
|
|
42
|
-
transaction.onabort = () => (refused ? resolve(null) : reject(transaction.error ?? new Error('IndexedDB aborted the write')));
|
|
43
|
-
// Every place and the counter are read inside the transaction, and the writes follow in the same one.
|
|
44
|
-
const counted = count.get(COUNT);
|
|
45
|
-
const land = () => {
|
|
46
|
-
if (held.size < places.length || counted.readyState !== 'done')
|
|
47
|
-
return;
|
|
48
|
-
for (const name of places) {
|
|
49
|
-
const version = held.get(name) === undefined ? null : String(held.get(name).version);
|
|
50
|
-
if (!(name in expect) || expect[name] !== version) {
|
|
51
|
-
refused = true;
|
|
52
|
-
transaction.abort();
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
let next = counted.result ?? 0;
|
|
57
|
-
const landed = {};
|
|
58
|
-
for (const [name, entries] of Object.entries(writes)) {
|
|
59
|
-
const kept = { ...(held.get(name)?.entries ?? {}) };
|
|
60
|
-
for (const [entry, bytes] of Object.entries(entries)) {
|
|
61
|
-
if (bytes === null)
|
|
62
|
-
delete kept[entry];
|
|
63
|
-
else
|
|
64
|
-
kept[entry] = bytes.slice();
|
|
65
|
-
}
|
|
66
|
-
next += 1;
|
|
67
|
-
if (Object.keys(kept).length === 0) {
|
|
68
|
-
store.delete(name);
|
|
69
|
-
landed[name] = null;
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
store.put({ entries: kept, version: next }, name);
|
|
73
|
-
landed[name] = String(next);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
count.put(next, COUNT);
|
|
77
|
-
versions = landed;
|
|
78
|
-
};
|
|
79
|
-
counted.onsuccess = land;
|
|
80
|
-
for (const place of places) {
|
|
81
|
-
const request = store.get(place);
|
|
82
|
-
request.onsuccess = () => {
|
|
83
|
-
held.set(place, request.result);
|
|
84
|
-
land();
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
if (places.length === 0)
|
|
88
|
-
counted.onsuccess = () => {
|
|
89
|
-
versions = {};
|
|
90
|
-
};
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
/** The database let go. */
|
|
94
|
-
close() {
|
|
95
|
-
this.#db.close();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { Unlock } from '../ground/ground.ts';
|
|
2
|
-
/** Where the unlock keeps its AES key and the sealed key: values kept as they are. */
|
|
3
|
-
export interface Shelf {
|
|
4
|
-
get(key: string): Promise<unknown>;
|
|
5
|
-
set(key: string, value: unknown): Promise<void>;
|
|
6
|
-
}
|
|
7
|
-
/** A shelf in IndexedDB, which keeps a `CryptoKey` whole. */
|
|
8
|
-
export declare class IndexedDbShelf implements Shelf {
|
|
9
|
-
#private;
|
|
10
|
-
private constructor();
|
|
11
|
-
static open(name: string, factory?: IDBFactory): Promise<IndexedDbShelf>;
|
|
12
|
-
get(key: string): Promise<unknown>;
|
|
13
|
-
set(key: string, value: unknown): Promise<void>;
|
|
14
|
-
close(): void;
|
|
15
|
-
}
|
|
16
|
-
export declare class LockedUnlock implements Unlock {
|
|
17
|
-
#private;
|
|
18
|
-
constructor(shelf: Shelf, subtle?: SubtleCrypto);
|
|
19
|
-
/** The unlock of the origin, on a shelf in IndexedDB named `name`. */
|
|
20
|
-
static open(name?: string, factory?: IDBFactory): Promise<LockedUnlock>;
|
|
21
|
-
/** Its shelf let go, where the shelf holds a connection. */
|
|
22
|
-
close(): void;
|
|
23
|
-
key(): Promise<string>;
|
|
24
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
const LOCK = 'lock';
|
|
2
|
-
const SEALED = 'key';
|
|
3
|
-
const STORE = 'shelf';
|
|
4
|
-
// The words sealed in beside the key, so no other sealed value on the shelf opens as it.
|
|
5
|
-
const BOUND = new TextEncoder().encode('nervur-key');
|
|
6
|
-
const done = (request) => new Promise((resolve, reject) => {
|
|
7
|
-
request.onsuccess = () => resolve(request.result);
|
|
8
|
-
request.onerror = () => reject(request.error ?? new Error('IndexedDB refused the request'));
|
|
9
|
-
});
|
|
10
|
-
/** A shelf in IndexedDB, which keeps a `CryptoKey` whole. */
|
|
11
|
-
export class IndexedDbShelf {
|
|
12
|
-
#db;
|
|
13
|
-
constructor(db) {
|
|
14
|
-
this.#db = db;
|
|
15
|
-
}
|
|
16
|
-
static async open(name, factory = indexedDB) {
|
|
17
|
-
const opening = factory.open(name, 1);
|
|
18
|
-
opening.onupgradeneeded = () => {
|
|
19
|
-
opening.result.createObjectStore(STORE);
|
|
20
|
-
};
|
|
21
|
-
return new IndexedDbShelf(await done(opening));
|
|
22
|
-
}
|
|
23
|
-
get(key) {
|
|
24
|
-
return done(this.#db.transaction(STORE, 'readonly').objectStore(STORE).get(key));
|
|
25
|
-
}
|
|
26
|
-
async set(key, value) {
|
|
27
|
-
const transaction = this.#db.transaction(STORE, 'readwrite');
|
|
28
|
-
transaction.objectStore(STORE).put(value, key);
|
|
29
|
-
await new Promise((resolve, reject) => {
|
|
30
|
-
transaction.oncomplete = () => resolve();
|
|
31
|
-
transaction.onabort = () => reject(transaction.error ?? new Error('IndexedDB aborted the write'));
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
close() {
|
|
35
|
-
this.#db.close();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
const hex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
39
|
-
export class LockedUnlock {
|
|
40
|
-
#shelf;
|
|
41
|
-
#subtle;
|
|
42
|
-
#drawn;
|
|
43
|
-
constructor(shelf, subtle = crypto.subtle) {
|
|
44
|
-
this.#shelf = shelf;
|
|
45
|
-
this.#subtle = subtle;
|
|
46
|
-
}
|
|
47
|
-
/** The unlock of the origin, on a shelf in IndexedDB named `name`. */
|
|
48
|
-
static async open(name = 'nervur-unlock', factory = indexedDB) {
|
|
49
|
-
return new LockedUnlock(await IndexedDbShelf.open(name, factory));
|
|
50
|
-
}
|
|
51
|
-
/** Its shelf let go, where the shelf holds a connection. */
|
|
52
|
-
close() {
|
|
53
|
-
this.#shelf.close?.();
|
|
54
|
-
}
|
|
55
|
-
key() {
|
|
56
|
-
// One key is drawn at a time, so two first uses never draw two.
|
|
57
|
-
this.#drawn ??= this.#key();
|
|
58
|
-
this.#drawn.catch(() => (this.#drawn = undefined));
|
|
59
|
-
return this.#drawn;
|
|
60
|
-
}
|
|
61
|
-
// The key sealed on the shelf, or a fresh one sealed there first.
|
|
62
|
-
async #key() {
|
|
63
|
-
const lock = await this.#lock();
|
|
64
|
-
let sealed = (await this.#shelf.get(SEALED));
|
|
65
|
-
if (sealed === undefined) {
|
|
66
|
-
const key = crypto.getRandomValues(new Uint8Array(32));
|
|
67
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
68
|
-
sealed = { iv, data: new Uint8Array(await this.#subtle.encrypt({ name: 'AES-GCM', iv, additionalData: BOUND }, lock, key)) };
|
|
69
|
-
key.fill(0);
|
|
70
|
-
await this.#shelf.set(SEALED, sealed);
|
|
71
|
-
}
|
|
72
|
-
const key = new Uint8Array(await this.#subtle.decrypt({ name: 'AES-GCM', iv: sealed.iv, additionalData: BOUND }, lock, sealed.data));
|
|
73
|
-
const text = hex(key);
|
|
74
|
-
key.fill(0);
|
|
75
|
-
return text;
|
|
76
|
-
}
|
|
77
|
-
async #lock() {
|
|
78
|
-
const kept = (await this.#shelf.get(LOCK));
|
|
79
|
-
if (kept !== undefined)
|
|
80
|
-
return kept;
|
|
81
|
-
const made = await this.#subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
|
|
82
|
-
await this.#shelf.set(LOCK, made);
|
|
83
|
-
return made;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Classes } from '../foundation.ts';
|
|
2
|
-
/** What loads a module by its URL: the engine's `import()` where none is handed. */
|
|
3
|
-
export type Load = (href: string) => Promise<unknown>;
|
|
4
|
-
export declare const OriginClasses: Readonly<{
|
|
5
|
-
/** The classes of the module at `at`, resolved against `origin`, loaded once. */
|
|
6
|
-
open(at: string, origin: string, load?: Load): Promise<Classes>;
|
|
7
|
-
}>;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
-
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
10
|
-
// Classes from the ground's own origin. A module at a path under the
|
|
11
|
-
// origin exports `steward`, and `public` and `beings` where it has them, as
|
|
12
|
-
// `ClassList` takes them. A path that resolves off the origin is refused,
|
|
13
|
-
// as a folder of code on a NodeGround stays inside its folder. A service
|
|
14
|
-
// worker may not `import()`, so there the worker's own build hands a
|
|
15
|
-
// loader that answers the same paths from modules it imported itself.
|
|
16
|
-
import { ClassList } from '../bodies/class-list.js';
|
|
17
|
-
export const OriginClasses = Object.freeze({
|
|
18
|
-
/** The classes of the module at `at`, resolved against `origin`, loaded once. */
|
|
19
|
-
async open(at, origin, load = (href) => import(__rewriteRelativeImportExtension(href))) {
|
|
20
|
-
const base = new URL(origin.endsWith('/') ? origin : `${origin}/`);
|
|
21
|
-
const url = new URL(at, base);
|
|
22
|
-
if (url.origin !== base.origin || !url.href.startsWith(base.href))
|
|
23
|
-
throw new Error(`the code at ${at} stands off ${base.href}`);
|
|
24
|
-
const module = (await load(url.href));
|
|
25
|
-
if (module.steward === undefined)
|
|
26
|
-
throw new Error(`${url.href} exports no steward`);
|
|
27
|
-
if (module.beings !== undefined && !Array.isArray(module.beings))
|
|
28
|
-
throw new Error(`${url.href} exports beings that are not a list`);
|
|
29
|
-
return new ClassList({ steward: module.steward, ...(module.public === undefined ? {} : { public: module.public }), ...(module.beings === undefined ? {} : { beings: module.beings }) });
|
|
30
|
-
},
|
|
31
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { Clock } from '../foundation.ts';
|
|
2
|
-
import type { DurableStorage } from './durable.ts';
|
|
3
|
-
export declare class DurableClock implements Clock {
|
|
4
|
-
#private;
|
|
5
|
-
constructor(storage: DurableStorage);
|
|
6
|
-
now(): number;
|
|
7
|
-
wait({ id, ms }: {
|
|
8
|
-
id: string;
|
|
9
|
-
ms: number;
|
|
10
|
-
}): Promise<boolean>;
|
|
11
|
-
cancel({ id }: {
|
|
12
|
-
id: string;
|
|
13
|
-
}): void;
|
|
14
|
-
/**
|
|
15
|
-
* The object's alarm fired: every wait due ends, and the alarm is set for
|
|
16
|
-
* the next. The Durable Object's `alarm()` calls it.
|
|
17
|
-
*/
|
|
18
|
-
alarm(): Promise<void>;
|
|
19
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
export class DurableClock {
|
|
2
|
-
#storage;
|
|
3
|
-
#waits = new Map();
|
|
4
|
-
// The alarm as this clock last set or read it; `undefined` until it knows.
|
|
5
|
-
#armed;
|
|
6
|
-
#turn = Promise.resolve();
|
|
7
|
-
constructor(storage) {
|
|
8
|
-
this.#storage = storage;
|
|
9
|
-
}
|
|
10
|
-
now() {
|
|
11
|
-
return Date.now();
|
|
12
|
-
}
|
|
13
|
-
wait({ id, ms }) {
|
|
14
|
-
this.cancel({ id });
|
|
15
|
-
const at = Date.now() + Math.max(0, ms);
|
|
16
|
-
const waited = new Promise((resolve) => this.#waits.set(id, { at, resolve }));
|
|
17
|
-
// Storage that refuses the alarm leaves the wait to the next wake, as an eviction would.
|
|
18
|
-
this.#arm(at).catch(() => undefined);
|
|
19
|
-
return waited;
|
|
20
|
-
}
|
|
21
|
-
cancel({ id }) {
|
|
22
|
-
const held = this.#waits.get(id);
|
|
23
|
-
if (held === undefined)
|
|
24
|
-
return;
|
|
25
|
-
this.#waits.delete(id);
|
|
26
|
-
held.resolve(false);
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The object's alarm fired: every wait due ends, and the alarm is set for
|
|
30
|
-
* the next. The Durable Object's `alarm()` calls it.
|
|
31
|
-
*/
|
|
32
|
-
async alarm() {
|
|
33
|
-
this.#armed = null;
|
|
34
|
-
const now = Date.now();
|
|
35
|
-
let next;
|
|
36
|
-
for (const [id, { at, resolve }] of this.#waits) {
|
|
37
|
-
if (at > now) {
|
|
38
|
-
next = next === undefined ? at : Math.min(next, at);
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
this.#waits.delete(id);
|
|
42
|
-
resolve(true);
|
|
43
|
-
}
|
|
44
|
-
if (next !== undefined)
|
|
45
|
-
await this.#arm(next);
|
|
46
|
-
}
|
|
47
|
-
// The alarm moved earlier where `at` comes before it, and never later: a
|
|
48
|
-
// house of an earlier wake may still be owed the time it set.
|
|
49
|
-
#arm(at) {
|
|
50
|
-
const turn = this.#turn.then(async () => {
|
|
51
|
-
if (this.#armed === undefined)
|
|
52
|
-
this.#armed = await this.#storage.getAlarm();
|
|
53
|
-
if (this.#armed !== null && this.#armed <= at)
|
|
54
|
-
return;
|
|
55
|
-
this.#armed = at;
|
|
56
|
-
await this.#storage.setAlarm(at);
|
|
57
|
-
});
|
|
58
|
-
this.#turn = turn.catch(() => undefined);
|
|
59
|
-
return turn;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// An edge's memory: Durable Object storage, one memory for each name. The
|
|
3
|
-
// object runs one event at a time and a write is one transaction, so a
|
|
4
|
-
// write whose places moved lands nothing.
|
|
5
|
-
import { KeptMemory } from '../bodies/kept.js';
|
|
6
|
-
import { durableStore } from './durable.js';
|
|
7
|
-
export class DurableMemory extends KeptMemory {
|
|
8
|
-
constructor(storage, name) {
|
|
9
|
-
super(durableStore(storage), name);
|
|
10
|
-
}
|
|
11
|
-
}
|
package/dist/edge/durable.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { Store } from '../bodies/kept.ts';
|
|
2
|
-
/** A transaction on Durable Object storage: every write in it lands together, or none. */
|
|
3
|
-
export interface DurableTransaction {
|
|
4
|
-
get(key: string): Promise<unknown>;
|
|
5
|
-
put(entries: Record<string, unknown>): Promise<void>;
|
|
6
|
-
delete(keys: string[]): Promise<number>;
|
|
7
|
-
}
|
|
8
|
-
/** A Durable Object's storage: `ctx.storage` in a Worker. */
|
|
9
|
-
export interface DurableStorage extends DurableTransaction {
|
|
10
|
-
list(options: {
|
|
11
|
-
prefix: string;
|
|
12
|
-
}): Promise<Map<string, unknown>>;
|
|
13
|
-
transaction<T>(closure: (transaction: DurableTransaction) => Promise<T>): Promise<T>;
|
|
14
|
-
getAlarm(): Promise<number | null>;
|
|
15
|
-
setAlarm(at: number): Promise<void>;
|
|
16
|
-
}
|
|
17
|
-
/** A store of text over Durable Object storage, whose swap is one transaction. */
|
|
18
|
-
export declare const durableStore: (storage: DurableStorage) => Store;
|
package/dist/edge/durable.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const BATCH = 128;
|
|
2
|
-
/** A store of text over Durable Object storage, whose swap is one transaction. */
|
|
3
|
-
export const durableStore = (storage) => ({
|
|
4
|
-
get: async (key) => {
|
|
5
|
-
const value = await storage.get(key);
|
|
6
|
-
return typeof value === 'string' ? value : null;
|
|
7
|
-
},
|
|
8
|
-
keys: async (prefix) => [...(await storage.list({ prefix })).keys()],
|
|
9
|
-
swap: (writes, expect) => storage.transaction(async (transaction) => {
|
|
10
|
-
for (const [key, value] of Object.entries(expect)) {
|
|
11
|
-
const held = await transaction.get(key);
|
|
12
|
-
if ((typeof held === 'string' ? held : null) !== value)
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
const put = Object.entries(writes).filter((entry) => entry[1] !== null);
|
|
16
|
-
const gone = Object.entries(writes).flatMap(([key, value]) => (value === null ? [key] : []));
|
|
17
|
-
// The platform takes at most this many keys in one call; the transaction still lands them together.
|
|
18
|
-
for (let at = 0; at < put.length; at += BATCH)
|
|
19
|
-
await transaction.put(Object.fromEntries(put.slice(at, at + BATCH)));
|
|
20
|
-
for (let at = 0; at < gone.length; at += BATCH)
|
|
21
|
-
await transaction.delete(gone.slice(at, at + BATCH));
|
|
22
|
-
return true;
|
|
23
|
-
}),
|
|
24
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { BeingClass } from '../foundation.ts';
|
|
2
|
-
import { type Registry } from '../ground/ground.ts';
|
|
3
|
-
import type { DurableStorage } from './durable.ts';
|
|
4
|
-
import { type Connect } from './socket-carry.ts';
|
|
5
|
-
/** A house's code as the deploy bundles it: what a folder's entry exports. */
|
|
6
|
-
export interface Code {
|
|
7
|
-
readonly steward: BeingClass;
|
|
8
|
-
readonly public?: BeingClass;
|
|
9
|
-
readonly beings?: readonly BeingClass[];
|
|
10
|
-
}
|
|
11
|
-
export interface EdgeGroundOptions {
|
|
12
|
-
/** The deploy's own registry, which joins the terrain's as the ladder's first rung. */
|
|
13
|
-
readonly registry?: Registry;
|
|
14
|
-
/** Each house's code by the name its entry's `bundle` body gives in `at`. */
|
|
15
|
-
readonly code?: Readonly<Record<string, Code>>;
|
|
16
|
-
/** The platform's outbound sockets, `connect` from `cloudflare:sockets`; an edge without it dials the web alone. */
|
|
17
|
-
readonly connect?: Connect;
|
|
18
|
-
}
|
|
19
|
-
/** What the platform hands the object: its storage, and the hibernating WebSockets it accepts. */
|
|
20
|
-
export interface DurableState {
|
|
21
|
-
readonly storage: DurableStorage;
|
|
22
|
-
acceptWebSocket(socket: unknown): void;
|
|
23
|
-
}
|
|
24
|
-
/** The ground's Durable Object, as the platform calls it. */
|
|
25
|
-
export interface EdgeObject {
|
|
26
|
-
fetch(request: Request): Promise<Response>;
|
|
27
|
-
webSocketMessage(socket: unknown, message: string | ArrayBuffer): Promise<void>;
|
|
28
|
-
webSocketClose(socket: unknown): Promise<void>;
|
|
29
|
-
webSocketError(socket: unknown): Promise<void>;
|
|
30
|
-
alarm(): Promise<void>;
|
|
31
|
-
}
|
|
32
|
-
export declare const EdgeGround: Readonly<{
|
|
33
|
-
/**
|
|
34
|
-
* The ground's Durable Object class, which the Worker's module exports
|
|
35
|
-
* under the name its binding gives. The Worker's environment holds what
|
|
36
|
-
* opens its drawer and its hand, and nothing else: `NERVUR_SECRET`, the
|
|
37
|
-
* ground's key, sixty-four hex digits, and `NERVUR_HAND`, the key of its
|
|
38
|
-
* hand, both set as secrets. Every other setting is an entry in the
|
|
39
|
-
* drawer.
|
|
40
|
-
*/
|
|
41
|
-
object({ registry, code, connect }?: EdgeGroundOptions): new (state: DurableState, env: Readonly<Record<string, unknown>>) => EdgeObject;
|
|
42
|
-
/** The Worker: every request goes to the ground's one object, by the binding's name. */
|
|
43
|
-
worker({ binding, name }?: {
|
|
44
|
-
binding?: string;
|
|
45
|
-
name?: string;
|
|
46
|
-
}): {
|
|
47
|
-
fetch(request: Request, env: Readonly<Record<string, unknown>>): Promise<Response>;
|
|
48
|
-
};
|
|
49
|
-
}>;
|