@rindle/room 0.5.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/build.sh +53 -0
- package/dist/authority.d.ts +57 -0
- package/dist/authority.d.ts.map +1 -0
- package/dist/authority.js +73 -0
- package/dist/authority.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/journal.d.ts +64 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +53 -0
- package/dist/journal.js.map +1 -0
- package/dist/mutation-tx.d.ts +79 -0
- package/dist/mutation-tx.d.ts.map +1 -0
- package/dist/mutation-tx.js +207 -0
- package/dist/mutation-tx.js.map +1 -0
- package/dist/shell.d.ts +134 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +1589 -0
- package/dist/shell.js.map +1 -0
- package/dist/token.d.ts +89 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +158 -0
- package/dist/token.js.map +1 -0
- package/dist/wasm.d.ts +7 -0
- package/dist/wasm.d.ts.map +1 -0
- package/dist/wasm.js +29 -0
- package/dist/wasm.js.map +1 -0
- package/package.json +71 -0
- package/pkg/rindle_room.d.ts +345 -0
- package/pkg/rindle_room.js +924 -0
- package/pkg/rindle_room_bg.wasm +0 -0
- package/pkg/rindle_room_bg.wasm.d.ts +45 -0
- package/src/authority.ts +127 -0
- package/src/index.ts +49 -0
- package/src/journal.ts +110 -0
- package/src/mutation-tx.ts +275 -0
- package/src/shell.ts +1859 -0
- package/src/token.ts +219 -0
- package/src/wasm.ts +32 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// The room-side MutationTx (RINDLE-REALTIME-DESIGN.md §5.1; OPTIMISTIC-WRITES-DESIGN.md
|
|
2
|
+
// §4.2): the write handle a room mutator runs against, structurally identical to
|
|
3
|
+
// `@rindle/optimistic`'s client MutationTx — the whole point of §4.2's "two registries,
|
|
4
|
+
// one interface" is that an app can register its client mutators in the room VERBATIM
|
|
5
|
+
// (`mutators` from a shared app-def typechecks against `RoomMutator` as-is). Declared
|
|
6
|
+
// locally rather than imported so @rindle/room does not depend on the browser client
|
|
7
|
+
// stack; TS structural typing keeps the two in lockstep at the app's call site.
|
|
8
|
+
//
|
|
9
|
+
// Backing: the wasm room's staged transaction (txGet/txAdd/txEdit/txRemove). Only
|
|
10
|
+
// concrete cells cross the JSON boundary — `undefined` ("leave unchanged") in the
|
|
11
|
+
// positional `edit` and the partial keyed `update` is resolved HERE against the
|
|
12
|
+
// effective row (`txGet`: live head under this tx's own staged writes), the same merge
|
|
13
|
+
// the browser WriteTxn does at its staging boundary. Reads are read-your-writes by
|
|
14
|
+
// construction. Every refusal below (unknown column, width, presence) throws — the
|
|
15
|
+
// shell catches a mutator throw and turns the whole mutation into a reject.
|
|
16
|
+
/**
|
|
17
|
+
* Tag an error as an ENVIRONMENT shortfall (H-iv-b): the room lacks a capability the
|
|
18
|
+
* mutation needs (today: `tx.query`), which is a verdict about the ROOM, not the
|
|
19
|
+
* mutation — the shell classifies it as a DEOPT (the client re-routes the mutation to
|
|
20
|
+
* the daemon stream, where the capability exists) instead of a FINAL rejection (which
|
|
21
|
+
* would drop the mutation). Contrast a validation/authz throw: re-routing can't help
|
|
22
|
+
* those, so they stay `rejected`.
|
|
23
|
+
*/
|
|
24
|
+
export function environmentShortfall(message) {
|
|
25
|
+
const e = new Error(message);
|
|
26
|
+
e.roomEnvironmentShortfall = true;
|
|
27
|
+
return e;
|
|
28
|
+
}
|
|
29
|
+
/** Whether `e` was tagged by {@link environmentShortfall}. */
|
|
30
|
+
export function isEnvironmentShortfall(e) {
|
|
31
|
+
return (e?.roomEnvironmentShortfall === true);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Guard against the two mutator shapes that would corrupt silently instead of failing loudly.
|
|
35
|
+
* A `shared(...)` GENERATOR registered verbatim returns an un-iterated generator — zero writes,
|
|
36
|
+
* acked as applied (data loss); an ASYNC mutator runs synchronously only to its first `await`,
|
|
37
|
+
* so later writes land OUTSIDE the committed transaction. Both shells call this on the
|
|
38
|
+
* mutator's return value inside their try/reject path, so either shape becomes an explicit
|
|
39
|
+
* rejection with a pointed message. (An adapter that DRIVES a shared generator registry against
|
|
40
|
+
* the room tx is future work — managed-writes design §8.)
|
|
41
|
+
*/
|
|
42
|
+
export function assertSyncMutatorReturn(returned, name) {
|
|
43
|
+
if (returned === undefined || returned === null)
|
|
44
|
+
return;
|
|
45
|
+
const r = returned;
|
|
46
|
+
if (typeof r.then === "function") {
|
|
47
|
+
throw new Error(`mutator \`${name}\` returned a promise — room mutators must be synchronous ` +
|
|
48
|
+
`(writes after an \`await\` would land outside the transaction)`);
|
|
49
|
+
}
|
|
50
|
+
if (typeof r.next === "function") {
|
|
51
|
+
throw new Error(`mutator \`${name}\` returned a generator — a shared(...) registry cannot register ` +
|
|
52
|
+
`verbatim as room mutators (nothing would drive it; zero writes would be acked). ` +
|
|
53
|
+
`Write plain synchronous (tx, args, ctx) mutators for the room bundle.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function shapeOf(shapes, table) {
|
|
57
|
+
const s = shapes.get(table);
|
|
58
|
+
if (!s)
|
|
59
|
+
throw new Error(`unknown table \`${table}\``);
|
|
60
|
+
return s;
|
|
61
|
+
}
|
|
62
|
+
/** pk cells (primaryKey order) from a keyed probe — every pk column must be named. */
|
|
63
|
+
function keyedPk(shape, table, pk) {
|
|
64
|
+
return shape.primaryKey.map((c) => {
|
|
65
|
+
const name = shape.columns[c];
|
|
66
|
+
const v = pk[name];
|
|
67
|
+
if (v === undefined) {
|
|
68
|
+
throw new Error(`missing primary-key column \`${name}\` for \`${table}\``);
|
|
69
|
+
}
|
|
70
|
+
return v;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function assertKnownColumns(shape, table, row) {
|
|
74
|
+
for (const name of Object.keys(row)) {
|
|
75
|
+
if (!shape.columns.includes(name)) {
|
|
76
|
+
throw new Error(`unknown column \`${name}\` for \`${table}\``);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Build the MutationTx for one open wasm transaction. Valid only while that
|
|
81
|
+
* transaction is open — the shell creates one per mutation and never retains it. */
|
|
82
|
+
export function mutationTx(room, shapes) {
|
|
83
|
+
const getRow = (table, pkCells) => {
|
|
84
|
+
const text = room.txGet(table, JSON.stringify(pkCells));
|
|
85
|
+
return text === undefined ? undefined : JSON.parse(text);
|
|
86
|
+
};
|
|
87
|
+
/** Resolve `undefined` cells ("leave unchanged") against the effective row; the
|
|
88
|
+
* row must exist. Only concrete cells may cross the JSON boundary — a stringified
|
|
89
|
+
* `undefined` would silently become `null`. */
|
|
90
|
+
const resolveCells = (table, shape, cells) => {
|
|
91
|
+
if (cells.length !== shape.columns.length) {
|
|
92
|
+
throw new Error(`row width does not match the schema of \`${table}\``);
|
|
93
|
+
}
|
|
94
|
+
const pkCells = shape.primaryKey.map((c) => {
|
|
95
|
+
const v = cells[c];
|
|
96
|
+
if (v === undefined) {
|
|
97
|
+
throw new Error(`primary-key cells must be concrete (\`${table}\`)`);
|
|
98
|
+
}
|
|
99
|
+
return v;
|
|
100
|
+
});
|
|
101
|
+
const current = getRow(table, pkCells);
|
|
102
|
+
if (cells.every((v) => v !== undefined))
|
|
103
|
+
return cells;
|
|
104
|
+
if (current === undefined)
|
|
105
|
+
return undefined;
|
|
106
|
+
return cells.map((v, i) => (v === undefined ? current[i] : v));
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
row(table, pk) {
|
|
110
|
+
const shape = shapeOf(shapes, table);
|
|
111
|
+
const cells = getRow(table, keyedPk(shape, table, pk));
|
|
112
|
+
if (cells === undefined)
|
|
113
|
+
return undefined;
|
|
114
|
+
const out = {};
|
|
115
|
+
shape.columns.forEach((name, i) => (out[name] = cells[i]));
|
|
116
|
+
return out;
|
|
117
|
+
},
|
|
118
|
+
insert(table, row) {
|
|
119
|
+
const shape = shapeOf(shapes, table);
|
|
120
|
+
assertKnownColumns(shape, table, row);
|
|
121
|
+
const cells = shape.columns.map((name) => {
|
|
122
|
+
const v = row[name];
|
|
123
|
+
if (v === undefined) {
|
|
124
|
+
throw new Error(`insert of \`${table}\` is missing column \`${name}\``);
|
|
125
|
+
}
|
|
126
|
+
return v;
|
|
127
|
+
});
|
|
128
|
+
room.txAdd(table, JSON.stringify(cells));
|
|
129
|
+
},
|
|
130
|
+
update(table, row) {
|
|
131
|
+
const shape = shapeOf(shapes, table);
|
|
132
|
+
assertKnownColumns(shape, table, row);
|
|
133
|
+
const pkCells = keyedPk(shape, table, row);
|
|
134
|
+
const current = getRow(table, pkCells);
|
|
135
|
+
if (current === undefined)
|
|
136
|
+
return; // rebase-friendly no-op
|
|
137
|
+
const cells = shape.columns.map((name, i) => {
|
|
138
|
+
const v = row[name];
|
|
139
|
+
return v === undefined ? current[i] : v;
|
|
140
|
+
});
|
|
141
|
+
room.txEdit(table, JSON.stringify(cells));
|
|
142
|
+
},
|
|
143
|
+
upsert(table, row) {
|
|
144
|
+
const shape = shapeOf(shapes, table);
|
|
145
|
+
assertKnownColumns(shape, table, row);
|
|
146
|
+
const cells = shape.columns.map((name) => {
|
|
147
|
+
const v = row[name];
|
|
148
|
+
if (v === undefined) {
|
|
149
|
+
throw new Error(`upsert of \`${table}\` is missing column \`${name}\``);
|
|
150
|
+
}
|
|
151
|
+
return v;
|
|
152
|
+
});
|
|
153
|
+
const pkCells = keyedPk(shape, table, row);
|
|
154
|
+
if (getRow(table, pkCells) === undefined) {
|
|
155
|
+
room.txAdd(table, JSON.stringify(cells));
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
room.txEdit(table, JSON.stringify(cells));
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
delete(table, pk) {
|
|
162
|
+
const shape = shapeOf(shapes, table);
|
|
163
|
+
const pkCells = keyedPk(shape, table, pk);
|
|
164
|
+
if (getRow(table, pkCells) === undefined)
|
|
165
|
+
return; // rebase-friendly no-op
|
|
166
|
+
room.txRemove(table, JSON.stringify(pkCells));
|
|
167
|
+
},
|
|
168
|
+
query() {
|
|
169
|
+
// An environment shortfall, not a mutation verdict: the shell classifies this
|
|
170
|
+
// throw as a DEOPT so the client re-routes to the daemon stream (H-iv-b).
|
|
171
|
+
throw environmentShortfall("tx.query is not supported in room mutators yet");
|
|
172
|
+
},
|
|
173
|
+
get(table, pk) {
|
|
174
|
+
shapeOf(shapes, table);
|
|
175
|
+
return getRow(table, pk);
|
|
176
|
+
},
|
|
177
|
+
add(table, row) {
|
|
178
|
+
const shape = shapeOf(shapes, table);
|
|
179
|
+
if (row.some((v) => v === undefined)) {
|
|
180
|
+
throw new Error(`add of \`${table}\`: cells must be concrete`);
|
|
181
|
+
}
|
|
182
|
+
if (row.length !== shape.columns.length) {
|
|
183
|
+
throw new Error(`row width does not match the schema of \`${table}\``);
|
|
184
|
+
}
|
|
185
|
+
room.txAdd(table, JSON.stringify(row));
|
|
186
|
+
},
|
|
187
|
+
remove(table, row) {
|
|
188
|
+
const shape = shapeOf(shapes, table);
|
|
189
|
+
if (row.length !== shape.columns.length) {
|
|
190
|
+
throw new Error(`row width does not match the schema of \`${table}\``);
|
|
191
|
+
}
|
|
192
|
+
room.txRemove(table, JSON.stringify(shape.primaryKey.map((c) => row[c])));
|
|
193
|
+
},
|
|
194
|
+
edit(table, _oldRow, newRow) {
|
|
195
|
+
// The authority composes `old` from its own effective read (the wasm side);
|
|
196
|
+
// the caller's oldRow is its *prediction* of old — unused here, kept in the
|
|
197
|
+
// signature for client-registry compatibility.
|
|
198
|
+
const shape = shapeOf(shapes, table);
|
|
199
|
+
const resolved = resolveCells(table, shape, newRow);
|
|
200
|
+
if (resolved === undefined) {
|
|
201
|
+
throw new Error(`no row with that primary key in \`${table}\``);
|
|
202
|
+
}
|
|
203
|
+
room.txEdit(table, JSON.stringify(resolved));
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=mutation-tx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutation-tx.js","sourceRoot":"","sources":["../src/mutation-tx.ts"],"names":[],"mappings":"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"}
|
package/dist/shell.d.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { type RoomMutator } from "./mutation-tx.ts";
|
|
2
|
+
import { type RoomJournal } from "./journal.ts";
|
|
3
|
+
import type { RoomAuthority } from "./authority.ts";
|
|
4
|
+
/** The upstream half of the shell's config: where rindled lives and what to follow. */
|
|
5
|
+
export interface UpstreamOptions {
|
|
6
|
+
/** rindled's public subscription plane, e.g. `ws://127.0.0.1:7601`. */
|
|
7
|
+
wsUrl: string;
|
|
8
|
+
/** rindled's private control plane, e.g. `http://127.0.0.1:7600` (lease minting). */
|
|
9
|
+
controlUrl: string;
|
|
10
|
+
/** Bearer token for the control plane (required unless rindled runs unauthenticated). */
|
|
11
|
+
authToken?: string;
|
|
12
|
+
/** The document footprint — a wire `Ast` (§3.1). What this room follows and serves from. */
|
|
13
|
+
footprintAst: unknown;
|
|
14
|
+
/** Lease TTL passed to `/materialize` (rindled's default when omitted). */
|
|
15
|
+
leaseTtlMs?: number;
|
|
16
|
+
/** The `init` clientID on the upstream socket (diagnostic identity). */
|
|
17
|
+
clientId?: string;
|
|
18
|
+
}
|
|
19
|
+
/** The downstream half: how clients are authorized and served (§4/§10.1). */
|
|
20
|
+
export interface DownstreamOptions {
|
|
21
|
+
/** This room's document id — lease tokens for any other doc are refused. */
|
|
22
|
+
docId: string;
|
|
23
|
+
/** Token key ring: `kid` → shared secret (the API server signs with the same ring). */
|
|
24
|
+
tokenKeys: Record<string, string>;
|
|
25
|
+
/** Idle grace before an unsubscribed query's pipeline is reclaimed (default 30s). */
|
|
26
|
+
idleTtlMs?: number;
|
|
27
|
+
/** Lease-expiry / idle-sweep cadence (default 1s). */
|
|
28
|
+
sweepIntervalMs?: number;
|
|
29
|
+
/** How long a revocation keeps refusing pre-revocation tokens (default 30min — set
|
|
30
|
+
* it ≥ the longest token TTL the API server mints). */
|
|
31
|
+
revocationWindowMs?: number;
|
|
32
|
+
/** The room's private control plane (`POST /revoke`, `GET /stats`). Omit to run
|
|
33
|
+
* without one (no revocation surface). */
|
|
34
|
+
control?: {
|
|
35
|
+
authToken: string;
|
|
36
|
+
port?: number;
|
|
37
|
+
};
|
|
38
|
+
/** The write plane (§5.1). Omit to run read-only (writes are refused). */
|
|
39
|
+
writes?: WritesOptions;
|
|
40
|
+
/** Per-socket downstream send budget in bytes (§9; default 4 MiB). A socket whose
|
|
41
|
+
* queued bytes would exceed it is terminated with a gap — re-subscribing is the
|
|
42
|
+
* repair. */
|
|
43
|
+
sendBudgetBytes?: number;
|
|
44
|
+
}
|
|
45
|
+
/** One table's §3.3 scope spec (H-iv-b): an element of the api-server's
|
|
46
|
+
* `RoomBootResponse.scopes`, passed VERBATIM to the wasm room's `enableWritesV2`.
|
|
47
|
+
* Structurally identical to `@rindle/api-server`'s `RoomScopeSpec` (declared locally —
|
|
48
|
+
* the shell must not depend on the api-server package; TS structural typing keeps the
|
|
49
|
+
* two in lockstep at the host's threading site). */
|
|
50
|
+
export interface RoomScopeSpec {
|
|
51
|
+
table: string;
|
|
52
|
+
/** The footprint's row-local predicate for this table (a wire `Condition`) — drives
|
|
53
|
+
* the commit gate's absent-read proof. Absent ⇒ absent reads on this table always
|
|
54
|
+
* deopt (fail closed). */
|
|
55
|
+
footprintWhere?: unknown;
|
|
56
|
+
writable: {
|
|
57
|
+
kind: "none";
|
|
58
|
+
} | {
|
|
59
|
+
kind: "predicate";
|
|
60
|
+
where?: unknown;
|
|
61
|
+
joinKeyCols: string[];
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** The §5.1 write plane: the room's own mutator registry over its owned tables. */
|
|
65
|
+
export interface WritesOptions {
|
|
66
|
+
/** The server registry (§4.2): named mutators run against the shared head. PLAIN
|
|
67
|
+
* synchronous `(tx, args, ctx)` functions only — a `shared(...)` GENERATOR registry
|
|
68
|
+
* does NOT register verbatim (nothing drives it here; the shell rejects a mutator
|
|
69
|
+
* that returns a generator/promise — see `assertSyncMutatorReturn`). */
|
|
70
|
+
mutators: Record<string, RoomMutator>;
|
|
71
|
+
/** §3.2's owned set — the only tables mutators may write. Must all be in the
|
|
72
|
+
* upstream footprint; followed tables and the ledger are never writable. */
|
|
73
|
+
ownedTables: string[];
|
|
74
|
+
/** The §3.3 per-table scope specs from the boot wire (`RoomBootResponse.scopes` —
|
|
75
|
+
* H-iv-b). Present ⇒ the write plane enables in v2 GATED mode (`enableWritesV2`):
|
|
76
|
+
* staged writes validate against the writable predicates, join-key edits refuse,
|
|
77
|
+
* absent reads must prove against `footprintWhere`, context (`kind:"none"`) tables
|
|
78
|
+
* become txGet-READABLE, and a violating commit returns a structured DEOPT instead
|
|
79
|
+
* of applying. The scopes' writable tables must be a SUBSET of {@link ownedTables}
|
|
80
|
+
* (the host's own declaration) — a wider server scope throws at construction, so a
|
|
81
|
+
* self-hoster's owned set can never be extended from the wire. Absent ⇒ the v1
|
|
82
|
+
* table-granular write plane, byte-identical to before. */
|
|
83
|
+
scopes?: RoomScopeSpec[];
|
|
84
|
+
/** The durable sidecar an ack means (§8.1). Defaults to `memoryJournal()` — the
|
|
85
|
+
* "survives nothing beyond the process" class; hosts bring their own. */
|
|
86
|
+
journal?: RoomJournal;
|
|
87
|
+
/** The journal group-commit window (default 5ms): mutations arriving within it
|
|
88
|
+
* share one append, and their acks ride one ledger commit. */
|
|
89
|
+
groupCommitMs?: number;
|
|
90
|
+
/** The write authority (§5.3.1) — the API server's `/apply-row-change-txn` host
|
|
91
|
+
* (or the P3 gate's mock). Omit to run journal-only (P2 semantics: nothing is
|
|
92
|
+
* ever durable upstream). With an authority, the shell claims a placement epoch
|
|
93
|
+
* at boot, probes durable lmids before replay, and write-behinds on the flush
|
|
94
|
+
* cadence (§5.3). */
|
|
95
|
+
authority?: RoomAuthority;
|
|
96
|
+
/** The flush debounce (§5.3; default 250ms, within the design's ≤1s budget). */
|
|
97
|
+
flushDebounceMs?: number;
|
|
98
|
+
/** Flush immediately once this many keys are dirty (default 512). */
|
|
99
|
+
flushDirtyMax?: number;
|
|
100
|
+
}
|
|
101
|
+
export interface RoomShellOptions {
|
|
102
|
+
upstream: UpstreamOptions;
|
|
103
|
+
downstream: DownstreamOptions;
|
|
104
|
+
/** Downstream ws port (default 0 = ephemeral; bound on 127.0.0.1). */
|
|
105
|
+
port?: number;
|
|
106
|
+
/** Diagnostic sink (default: silent). */
|
|
107
|
+
log?: (line: string) => void;
|
|
108
|
+
}
|
|
109
|
+
export interface RoomShell {
|
|
110
|
+
/** The bound downstream port. */
|
|
111
|
+
readonly port: number;
|
|
112
|
+
/** The bound control-plane port (0 when no control plane was configured). */
|
|
113
|
+
readonly controlPort: number;
|
|
114
|
+
/** Resolves when the CURRENT incarnation is live (seq-0 snapshot applied);
|
|
115
|
+
* immediately if it already is. */
|
|
116
|
+
awaitLive(): Promise<void>;
|
|
117
|
+
/** The last-applied upstream commit version, if live. */
|
|
118
|
+
cv(): number | undefined;
|
|
119
|
+
/** The upstream subscription epoch of the current incarnation, if any. */
|
|
120
|
+
upstreamEpoch(): number | undefined;
|
|
121
|
+
/** This incarnation's downstream bootId (rotates on every re-subscribe). */
|
|
122
|
+
bootId(): string;
|
|
123
|
+
/** Fire the write-behind flush now (instead of the debounce) and await its
|
|
124
|
+
* settlement — deterministic flushing for tests and drain-before-close. No-op
|
|
125
|
+
* without an authority. */
|
|
126
|
+
flushNow(): Promise<void>;
|
|
127
|
+
close(): Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
/** Boot a room shell: init the wasm, mint the upstream lease, connect the upstream leg,
|
|
130
|
+
* and serve the downstream ws (+ the private control plane, if configured). Returns
|
|
131
|
+
* once the ports are bound and the upstream connection is underway — await
|
|
132
|
+
* `shell.awaitLive()` for the seed. */
|
|
133
|
+
export declare function createRoomShell(opts: RoomShellOptions): Promise<RoomShell>;
|
|
134
|
+
//# sourceMappingURL=shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"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"}
|