@bjornpagen/bumbledb 0.20.2 → 1.0.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/COOKBOOK.md +283 -1611
- package/README.md +156 -118
- package/dist/capacity.d.ts +16 -12
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +16 -48
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +54 -0
- package/dist/changes.d.ts.map +1 -0
- package/dist/changes.js +245 -0
- package/dist/changes.js.map +1 -0
- package/dist/close.d.ts +24 -0
- package/dist/close.d.ts.map +1 -0
- package/dist/close.js +34 -0
- package/dist/close.js.map +1 -0
- package/dist/closed.d.ts +1 -3
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +24 -12
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +56 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +289 -0
- package/dist/codec.js.map +1 -0
- package/dist/compile.d.ts +68 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +75 -0
- package/dist/compile.js.map +1 -0
- package/dist/db-native.d.ts +170 -0
- package/dist/db-native.d.ts.map +1 -0
- package/dist/db-native.js +5 -0
- package/dist/db-native.js.map +1 -0
- package/dist/db.d.ts +101 -233
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +232 -1084
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +57 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +32 -0
- package/dist/errors.js.map +1 -0
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +2 -2
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +65 -24
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +94 -24
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +37 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -21
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +24 -0
- package/dist/internal/log.d.ts.map +1 -0
- package/dist/internal/log.js +10 -0
- package/dist/internal/log.js.map +1 -0
- package/dist/law.d.ts +8 -13
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +6 -59
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts +0 -13
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +9 -17
- package/dist/lower.js.map +1 -1
- package/dist/migration.d.ts +33 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +48 -0
- package/dist/migration.js.map +1 -0
- package/dist/native.d.ts +256 -301
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +65 -108
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +24 -13
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +7 -3
- package/dist/query/atom.js.map +1 -1
- package/dist/query/compute.d.ts +68 -0
- package/dist/query/compute.d.ts.map +1 -0
- package/dist/query/compute.js +141 -0
- package/dist/query/compute.js.map +1 -0
- package/dist/query/find.d.ts +15 -8
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +6 -2
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +23 -2
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +433 -80
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +17 -8
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts +12 -2
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +26 -9
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +47 -3
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +83 -19
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +3 -8
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +16 -10
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +47 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +50 -0
- package/dist/result.js.map +1 -0
- package/dist/rows.d.ts +69 -0
- package/dist/rows.d.ts.map +1 -0
- package/dist/rows.js +328 -0
- package/dist/rows.js.map +1 -0
- package/dist/runtime-codes.d.ts +2 -0
- package/dist/runtime-codes.d.ts.map +1 -0
- package/dist/runtime-codes.js +19 -0
- package/dist/runtime-codes.js.map +1 -0
- package/dist/runtime-errors.d.ts +106 -0
- package/dist/runtime-errors.d.ts.map +1 -0
- package/dist/runtime-errors.js +62 -0
- package/dist/runtime-errors.js.map +1 -0
- package/dist/runtime-native.d.ts +171 -0
- package/dist/runtime-native.d.ts.map +1 -0
- package/dist/runtime-native.js +5 -0
- package/dist/runtime-native.js.map +1 -0
- package/dist/runtime.d.ts +73 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +293 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scalar.d.ts +165 -0
- package/dist/scalar.d.ts.map +1 -0
- package/dist/scalar.js +344 -0
- package/dist/scalar.js.map +1 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +34 -37
- package/dist/schema.js.map +1 -1
- package/dist/shape.d.ts +28 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +2 -0
- package/dist/shape.js.map +1 -0
- package/dist/spec.d.ts +27 -3
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js +22 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +16 -3
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +54 -28
- package/dist/statements.js.map +1 -1
- package/dist/uuid.d.ts +23 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +54 -0
- package/dist/uuid.js.map +1 -0
- package/pack-provenance.json +7 -0
- package/package.json +12 -16
- package/src/capacity.ts +34 -54
- package/src/changes.ts +361 -0
- package/src/close.ts +53 -0
- package/src/closed.ts +25 -19
- package/src/codec.ts +379 -0
- package/src/compile.ts +149 -0
- package/src/db-native.ts +247 -0
- package/src/db.ts +461 -1632
- package/src/errors.ts +50 -0
- package/src/face.ts +2 -2
- package/src/fields.ts +182 -55
- package/src/index.ts +62 -126
- package/src/internal/log.ts +51 -0
- package/src/law.ts +13 -28
- package/src/lower.ts +9 -4
- package/src/migration.ts +69 -0
- package/src/native.ts +326 -487
- package/src/query/atom.ts +61 -25
- package/src/query/compute.ts +237 -0
- package/src/query/find.ts +31 -12
- package/src/query/lower.ts +531 -143
- package/src/query/parse-ir.ts +18 -13
- package/src/query/run.ts +32 -16
- package/src/query/scope.ts +140 -8
- package/src/relation.ts +17 -21
- package/src/result.ts +130 -0
- package/src/rows.ts +404 -0
- package/src/runtime-codes.ts +18 -0
- package/src/runtime-errors.ts +69 -0
- package/src/runtime-native.ts +242 -0
- package/src/runtime.ts +440 -0
- package/src/scalar.ts +560 -0
- package/src/schema.ts +35 -37
- package/src/shape.ts +31 -0
- package/src/spec.ts +33 -3
- package/src/statements.ts +75 -68
- package/src/uuid.ts +69 -0
- package/dist/marshal.d.ts +0 -44
- package/dist/marshal.d.ts.map +0 -1
- package/dist/marshal.js +0 -165
- package/dist/marshal.js.map +0 -1
- package/src/marshal.ts +0 -200
package/dist/db.js
CHANGED
|
@@ -1,1101 +1,249 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* all typed by the schema's relations record.
|
|
7
|
-
*
|
|
8
|
-
* A store read is one callback: `db.read((instance, witness) => …)`. The
|
|
9
|
-
* instance is invalid the moment the callback returns; the witness is a
|
|
10
|
-
* cloneable token and may escape. There is no handle-shaped read and no
|
|
11
|
-
* `using snap = db.read`. Builder, owned instance, and witness
|
|
12
|
-
* implement `Symbol.dispose`. Prepared plans are plain values whose
|
|
13
|
-
* engine-side half is reclaimed by a GC finalizer — reclamation only,
|
|
14
|
-
* never correctness.
|
|
15
|
-
*
|
|
16
|
-
* PROCESS MODEL: one process, one exclusive-lock handle per store. The
|
|
17
|
-
* `Db` value owns the LMDB environment's exclusive lock until process
|
|
18
|
-
* exit (or until GC reclaims the native handle); a second engine-level
|
|
19
|
-
* open of the same store is refused by the engine (`EnvironmentLocked`),
|
|
20
|
-
* matching Rust. Resume = reopen in a fresh process, or hold the one `Db`
|
|
21
|
-
* this process already opened. The host owns composition; retry is host
|
|
22
|
-
* policy.
|
|
23
|
-
*
|
|
24
|
-
* REJECTION IS DATA: a rejected commit is a domain outcome (it becomes the
|
|
25
|
-
* LLM repair prompt downstream), returned as a {@link WriteOutcome}
|
|
26
|
-
* carrying {@link Violation} values. A moved generation on
|
|
27
|
-
* {@link Db.writeFrom} is the `{ tag: "moved" }` arm, not an exception.
|
|
28
|
-
* Genuine failures — I/O, used-after-scope, spent handle, marshal shape —
|
|
29
|
-
* throw `@superbuilders/errors` wrapped errors instead.
|
|
30
|
-
*/
|
|
31
|
-
import * as path from "node:path";
|
|
32
|
-
import * as errors from "@superbuilders/errors";
|
|
33
|
-
import { regex } from "arkregex";
|
|
34
|
-
import { isClosedMember, sealedFieldsOf } from "#closed.ts";
|
|
35
|
-
import { rosterOf } from "#fields.ts";
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { internalChanges } from "#changes.ts";
|
|
3
|
+
import { drainClose, releaseOwner } from "#close.ts";
|
|
4
|
+
import { Schema as CoreSchema, schemaTables } from "#compile.ts";
|
|
5
|
+
import { dbNative } from "#db-native.ts";
|
|
36
6
|
import { lower } from "#lower.ts";
|
|
37
|
-
import { cellOf, factOf, handleOf, isFreshField, keyRowOf, recordOf, rowOf } from "#marshal.ts";
|
|
38
|
-
import { bridged, bridgedAsync, errorFromThrow, native } from "#native.ts";
|
|
39
7
|
import { lowerQuery } from "#query/lower.ts";
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
for (const declared of data.fields) {
|
|
63
|
-
const value = record[declared.name];
|
|
64
|
-
if (value === undefined) {
|
|
65
|
-
throw errors.new(`relation ${data.name}: fact is missing field ${declared.name}`);
|
|
66
|
-
}
|
|
67
|
-
cells.push(cellOf(`relation ${data.name} field ${declared.name}`, declared.field, value));
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return { rows, cells };
|
|
71
|
-
}
|
|
72
|
-
function mutateCollection(relation, facts, apply) {
|
|
73
|
-
const flat = rowsOf(relation, facts);
|
|
74
|
-
const report = apply(flat.rows, flat.cells);
|
|
75
|
-
return Object.freeze({ submitted: report.submitted, changed: report.changed });
|
|
76
|
-
}
|
|
77
|
-
function freshRangeOf(wire) {
|
|
78
|
-
if (wire.empty) {
|
|
79
|
-
return Object.freeze({
|
|
80
|
-
empty: true,
|
|
81
|
-
count: 0n,
|
|
82
|
-
at(_index) {
|
|
83
|
-
return undefined;
|
|
84
|
-
},
|
|
85
|
-
*[Symbol.iterator]() { }
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
const start = wire.start;
|
|
89
|
-
const endExclusive = wire.endExclusive;
|
|
90
|
-
const count = endExclusive - start;
|
|
91
|
-
return Object.freeze({
|
|
92
|
-
empty: false,
|
|
93
|
-
start,
|
|
94
|
-
endExclusive,
|
|
95
|
-
get count() {
|
|
96
|
-
return count;
|
|
97
|
-
},
|
|
98
|
-
at(index) {
|
|
99
|
-
if (index < 0n || index >= count) {
|
|
100
|
-
return undefined;
|
|
101
|
-
}
|
|
102
|
-
return start + index;
|
|
103
|
-
},
|
|
104
|
-
*[Symbol.iterator]() {
|
|
105
|
-
for (let id = start; id < endExclusive; id++) {
|
|
106
|
-
yield id;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
const abandonMark = Symbol("bumbledb.abandon");
|
|
112
|
-
function abandon(payload) {
|
|
113
|
-
return Object.freeze({ [abandonMark]: true, payload });
|
|
114
|
-
}
|
|
115
|
-
function isAbandon(value) {
|
|
116
|
-
return typeof value === "object" && value !== null && abandonMark in value;
|
|
117
|
-
}
|
|
118
|
-
function isAbandonedOutcome(outcome, sentinel) {
|
|
119
|
-
return isAbandon(sentinel) && outcome.abandoned === sentinel.payload;
|
|
120
|
-
}
|
|
121
|
-
function abandonedOutcome(sentinel) {
|
|
122
|
-
const outcome = Object.freeze({ tag: "abandoned", abandoned: sentinel.payload });
|
|
123
|
-
if (!isAbandonedOutcome(outcome, sentinel)) {
|
|
124
|
-
throw errors.new("bumbledb abandon outcome construction incomplete");
|
|
125
|
-
}
|
|
126
|
-
return outcome;
|
|
127
|
-
}
|
|
128
|
-
const witnessTypes = Symbol("bumbledb.witness.types");
|
|
129
|
-
const preparedTypes = Symbol("bumbledb.prepared.types");
|
|
130
|
-
function decodeOffendingFact(member, relation, fact) {
|
|
131
|
-
const declared = sealedFieldsOf(member);
|
|
132
|
-
const decoded = {};
|
|
133
|
-
for (const cell of fact.fields) {
|
|
134
|
-
const cited = declared.find(function byName(candidate) {
|
|
135
|
-
return candidate.name === cell.name;
|
|
136
|
-
});
|
|
137
|
-
const roster = rosterOf(cited?.field);
|
|
138
|
-
decoded[cell.name] =
|
|
139
|
-
roster !== undefined
|
|
140
|
-
? handleOf(`violation fact ${fact.relation} field ${cell.name}`, roster, cell.value)
|
|
141
|
-
: cell.value;
|
|
142
|
-
}
|
|
143
|
-
return Object.freeze({ relation, fact: Object.freeze(decoded) });
|
|
144
|
-
}
|
|
145
|
-
function violationFromEntry(entry, wire, facts) {
|
|
146
|
-
const canonical = wire.canonical;
|
|
147
|
-
if (entry.kind === "functionality") {
|
|
148
|
-
if (!("statement" in entry)) {
|
|
149
|
-
return Object.freeze({ kind: "functionality", statement: undefined, canonical, facts });
|
|
150
|
-
}
|
|
151
|
-
return Object.freeze({ kind: "functionality", statement: entry.statement, canonical, facts });
|
|
152
|
-
}
|
|
153
|
-
if (entry.kind === "capacity") {
|
|
154
|
-
if (wire.kind !== "capacity") {
|
|
155
|
-
throw errors.new(`bumbledb violation ${wire.statementId} is a capacity slot without a measure`);
|
|
156
|
-
}
|
|
157
|
-
return Object.freeze({
|
|
158
|
-
kind: "capacity",
|
|
159
|
-
statement: entry.statement,
|
|
160
|
-
canonical,
|
|
161
|
-
measure: wire.measure,
|
|
162
|
-
facts
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
if (wire.kind !== "containment") {
|
|
166
|
-
throw errors.new(`bumbledb violation ${wire.statementId} is a containment slot without a direction`);
|
|
167
|
-
}
|
|
168
|
-
if (entry.kind === "mirrors") {
|
|
169
|
-
return Object.freeze({
|
|
170
|
-
kind: "containment",
|
|
171
|
-
statement: entry.statement,
|
|
172
|
-
canonical,
|
|
173
|
-
direction: wire.direction,
|
|
174
|
-
orientation: entry.orientation,
|
|
175
|
-
facts
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
return Object.freeze({
|
|
179
|
-
kind: "containment",
|
|
180
|
-
statement: entry.statement,
|
|
181
|
-
canonical,
|
|
182
|
-
direction: wire.direction,
|
|
183
|
-
facts
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
function materializedEntries(theory) {
|
|
187
|
-
const entries = impliedKeyEntries(theory);
|
|
188
|
-
for (const statement of theory.statements) {
|
|
189
|
-
entries.push(...declaredEntries(statement));
|
|
190
|
-
}
|
|
191
|
-
return entries;
|
|
192
|
-
}
|
|
193
|
-
function impliedKeyEntries(theory) {
|
|
194
|
-
const entries = [];
|
|
195
|
-
for (const member of Object.values(theory.relations)) {
|
|
196
|
-
if (isClosedMember(member)) {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
for (const declared of member.data.fields) {
|
|
200
|
-
if (isFreshField(declared.field)) {
|
|
201
|
-
entries.push({
|
|
202
|
-
kind: "functionality",
|
|
203
|
-
owner: member.name,
|
|
204
|
-
projection: [declared.name]
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
for (const member of Object.values(theory.relations)) {
|
|
210
|
-
if (isClosedMember(member)) {
|
|
211
|
-
entries.push({
|
|
212
|
-
kind: "functionality",
|
|
213
|
-
owner: member.name,
|
|
214
|
-
projection: ["id"]
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return entries;
|
|
219
|
-
}
|
|
220
|
-
function declaredEntries(statement) {
|
|
221
|
-
const data = statement.data;
|
|
222
|
-
switch (data.kind) {
|
|
223
|
-
case "key": {
|
|
224
|
-
return [
|
|
225
|
-
{
|
|
226
|
-
kind: "functionality",
|
|
227
|
-
statement,
|
|
228
|
-
owner: data.owner.name,
|
|
229
|
-
projection: data.projection
|
|
230
|
-
}
|
|
231
|
-
];
|
|
232
|
-
}
|
|
233
|
-
case "containment": {
|
|
234
|
-
return [{ kind: "containment", statement }];
|
|
235
|
-
}
|
|
236
|
-
case "mirrors": {
|
|
237
|
-
return [
|
|
238
|
-
{ kind: "mirrors", statement, orientation: "written" },
|
|
239
|
-
{ kind: "mirrors", statement, orientation: "mirrored" }
|
|
240
|
-
];
|
|
241
|
-
}
|
|
242
|
-
case "capacity": {
|
|
243
|
-
return [{ kind: "capacity", statement }];
|
|
244
|
-
}
|
|
8
|
+
import { wireParams } from "#query/run.ts";
|
|
9
|
+
import { internalResult, makeCompleteResult } from "#result.ts";
|
|
10
|
+
import { factOfCells, keyCellsOf } from "#rows.ts";
|
|
11
|
+
import { nativeOperationWith, policyWire, runtimeHandle } from "#runtime.ts";
|
|
12
|
+
import { DbError } from "#runtime-errors.ts";
|
|
13
|
+
import { runtimeNative } from "#runtime-native.ts";
|
|
14
|
+
function refusal(operation, reason) {
|
|
15
|
+
return new DbError({ operation, reason: { _tag: reason } });
|
|
16
|
+
}
|
|
17
|
+
function witnessOf(wire) {
|
|
18
|
+
return Object.freeze({ store: wire.store, generation: wire.generation });
|
|
19
|
+
}
|
|
20
|
+
function outcomeOf(wire) {
|
|
21
|
+
switch (wire.tag) {
|
|
22
|
+
case "accepted":
|
|
23
|
+
return Object.freeze({ kind: "accepted", witness: witnessOf(wire.witness) });
|
|
24
|
+
case "no-change":
|
|
25
|
+
return Object.freeze({ kind: "no-change", witness: witnessOf(wire.witness) });
|
|
26
|
+
case "invariant-rejected":
|
|
27
|
+
return Object.freeze({ kind: "invariant-rejected", violations: wire.violations });
|
|
28
|
+
case "moved":
|
|
29
|
+
return Object.freeze({ kind: "moved", witnessed: witnessOf(wire.witnessed), current: witnessOf(wire.current) });
|
|
245
30
|
}
|
|
246
31
|
}
|
|
247
32
|
/**
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* be a runtime probe on the returned value.
|
|
33
|
+
* Validates the query template's schema binding (identity, the membership
|
|
34
|
+
* rule) and lowers it to IR plus wire params. Pure host preparation; the
|
|
35
|
+
* engine's IR validation under budget remains the authority.
|
|
252
36
|
*/
|
|
253
|
-
function
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
let primaryKey;
|
|
302
|
-
entries.forEach(function firstOwnedKey(entry, index) {
|
|
303
|
-
if (primaryKey === undefined && entry.kind === "functionality" && entry.owner === relation.name) {
|
|
304
|
-
primaryKey = Object.freeze({ statementId: index, projection: entry.projection });
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
relations.set(relation.name, Object.freeze({ id: relation.id, member, fieldIds, primaryKey }));
|
|
308
|
-
}
|
|
309
|
-
Object.keys(theory.relations).forEach(function verifyRelation(name, ordinal) {
|
|
310
|
-
const entry = relations.get(name);
|
|
311
|
-
if (entry === undefined) {
|
|
312
|
-
throw errors.new(`bumbledb manifest drift: schema relation ${name} is not in the manifest`);
|
|
313
|
-
}
|
|
314
|
-
if (entry.id !== ordinal) {
|
|
315
|
-
throw errors.new(`bumbledb manifest drift: relation ${name} has engine id ${entry.id}, its declaration ordinal is ${ordinal} — query lowering depends on declaration order = ids`);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
return Object.freeze({ relations, statements: Object.freeze(entries) });
|
|
319
|
-
}
|
|
320
|
-
function tablesFromTheory(theory) {
|
|
321
|
-
const entries = materializedEntries(theory);
|
|
322
|
-
const relations = new Map();
|
|
323
|
-
Object.keys(theory.relations).forEach(function byOrdinal(name, ordinal) {
|
|
324
|
-
const member = theory.relations[name];
|
|
325
|
-
if (member === undefined) {
|
|
326
|
-
throw errors.new(`bumbledb theory has no relation ${name}`);
|
|
327
|
-
}
|
|
328
|
-
const fieldIds = new Map();
|
|
329
|
-
sealedFieldsOf(member).forEach(function byField(declared, fieldOrdinal) {
|
|
330
|
-
fieldIds.set(declared.name, fieldOrdinal);
|
|
331
|
-
});
|
|
332
|
-
let primaryKey;
|
|
333
|
-
entries.forEach(function firstOwnedKey(entry, index) {
|
|
334
|
-
if (primaryKey === undefined && entry.kind === "functionality" && entry.owner === name) {
|
|
335
|
-
primaryKey = Object.freeze({ statementId: index, projection: entry.projection });
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
relations.set(name, Object.freeze({ id: ordinal, member, fieldIds, primaryKey }));
|
|
339
|
-
});
|
|
340
|
-
return Object.freeze({ relations, statements: Object.freeze(entries) });
|
|
341
|
-
}
|
|
342
|
-
const instanceStates = new WeakMap();
|
|
343
|
-
const witnessStates = new WeakMap();
|
|
344
|
-
const witnessReclaimer = new FinalizationRegistry(function reclaimWitness(handle) {
|
|
345
|
-
const closed = errors.trySync(function closeWitness() {
|
|
346
|
-
native.witnessClose(handle);
|
|
347
|
-
});
|
|
348
|
-
if (closed.error) {
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
const preparedPlans = new WeakMap();
|
|
353
|
-
const planReclaimer = new FinalizationRegistry(function reclaimPlan(handle) {
|
|
354
|
-
const closed = errors.trySync(function closePlan() {
|
|
355
|
-
native.preparedClose(handle);
|
|
356
|
-
});
|
|
357
|
-
if (closed.error) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
const ErrAsyncCallback = errors.new("bumbledb asyncCallback: a read or write callback returned a thenable — the callback is synchronous");
|
|
362
|
-
const ErrSpentHandle = errors.new("bumbledb spentHandle: a consumed builder, instance, or witness was used");
|
|
363
|
-
const ErrUseAfterScope = errors.new("bumbledb useAfterScope: a stashed read instance or write transaction was used after its callback returned");
|
|
364
|
-
const ErrForeignPrepared = errors.new("bumbledb foreignPrepared: a prepared query met a foreign instance");
|
|
365
|
-
const ErrForeignWitness = errors.new("bumbledb foreignWitness: a witness met a foreign store");
|
|
366
|
-
function catalogMethods(theory, tables, owner, assertLive, ops) {
|
|
367
|
-
function planOf(prepared) {
|
|
368
|
-
const plan = preparedPlans.get(prepared);
|
|
369
|
-
if (plan === undefined) {
|
|
370
|
-
throw errors.wrap(ErrForeignPrepared, "bumbledb execute target is not a prepared value of this SDK");
|
|
371
|
-
}
|
|
372
|
-
if (plan.owner !== owner) {
|
|
373
|
-
throw errors.wrap(ErrForeignPrepared, `bumbledb prepared value was prepared by a different store than this one (schema ${theory.name})`);
|
|
374
|
-
}
|
|
375
|
-
return plan;
|
|
376
|
-
}
|
|
377
|
-
function contains(relation, fact) {
|
|
378
|
-
assertLive();
|
|
379
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
380
|
-
return bridged("bumbledb instance contains", function readContains() {
|
|
381
|
-
return ops.contains(entry.id, rowOf(relation.data, recordOf(fact)));
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
function get(relation, keyOrStatement, declaredKey) {
|
|
385
|
-
assertLive();
|
|
386
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
387
|
-
return selectKeyRead(keyOrStatement, declaredKey, function byStatement(statement, key) {
|
|
388
|
-
const selected = declaredKeyOf(tables, theory, relation, statement);
|
|
389
|
-
const row = bridged("bumbledb instance get", function readGet() {
|
|
390
|
-
return ops.get(entry.id, selected.statementId, keyRowOf(relation.data, selected.projection, recordOf(key)));
|
|
391
|
-
});
|
|
392
|
-
return row === null ? undefined : factOf(relation, row);
|
|
393
|
-
}, function byPrimary(key) {
|
|
394
|
-
const primaryKey = entry.primaryKey;
|
|
395
|
-
if (primaryKey === undefined) {
|
|
396
|
-
throw errors.new(`relation ${relation.name} has no candidate key — keyed get requires a fresh field or a declared key statement`);
|
|
397
|
-
}
|
|
398
|
-
const row = bridged("bumbledb instance get", function readGet() {
|
|
399
|
-
return ops.get(entry.id, primaryKey.statementId, keyRowOf(relation.data, primaryKey.projection, recordOf(key)));
|
|
400
|
-
});
|
|
401
|
-
return row === null ? undefined : factOf(relation, row);
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
function scan(relation) {
|
|
405
|
-
assertLive();
|
|
406
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
407
|
-
const rows = bridged("bumbledb instance scan", function readScan() {
|
|
408
|
-
return ops.scan(entry.id);
|
|
409
|
-
});
|
|
410
|
-
return rows.map(function decodeRow(row) {
|
|
411
|
-
return factOf(relation, row);
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
function count(relation) {
|
|
415
|
-
assertLive();
|
|
416
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
417
|
-
return bridged("bumbledb instance count", function readCount() {
|
|
418
|
-
return ops.count(entry.id);
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
function execute(prepared, params) {
|
|
422
|
-
assertLive();
|
|
423
|
-
const plan = planOf(prepared);
|
|
424
|
-
const wire = wireParams(plan.params, recordOf(params));
|
|
425
|
-
const rows = bridged("execute bumbledb prepared query", function callExecute() {
|
|
426
|
-
return ops.execute(plan.handle, wire);
|
|
427
|
-
});
|
|
428
|
-
return decodeAnswers(plan.finds, rows);
|
|
429
|
-
}
|
|
430
|
-
function prepare(q) {
|
|
431
|
-
assertLive();
|
|
432
|
-
if (q.schema !== theory) {
|
|
433
|
-
throw errors.new(`query was built against schema ${q.schema.name}, not the identical schema value this store opened with — schema identity is the membership rule`);
|
|
434
|
-
}
|
|
435
|
-
const queryIr = lowerQuery(q);
|
|
436
|
-
const outcome = bridged("prepare bumbledb query", function callPrepare() {
|
|
437
|
-
return ops.prepare(queryIr);
|
|
438
|
-
});
|
|
439
|
-
if (!outcome.ok) {
|
|
440
|
-
throwPrepareRefusal(outcome.message);
|
|
441
|
-
}
|
|
442
|
-
const prepared = Object.freeze({});
|
|
443
|
-
preparedPlans.set(prepared, Object.freeze({
|
|
444
|
-
handle: outcome.prepared,
|
|
445
|
-
owner,
|
|
446
|
-
params: q.data.params,
|
|
447
|
-
finds: q.data.finds
|
|
448
|
-
}));
|
|
449
|
-
planReclaimer.register(prepared, outcome.prepared);
|
|
450
|
-
return prepared;
|
|
451
|
-
}
|
|
452
|
-
return { scan, count, get, contains, execute, prepare };
|
|
453
|
-
}
|
|
454
|
-
function ordinaryEntry(tables, theory, relation) {
|
|
455
|
-
const entry = tables.relations.get(relation.name);
|
|
456
|
-
if (entry === undefined || entry.member !== relation) {
|
|
457
|
-
throw errors.new(`relation ${relation.name} is not a member of schema ${theory.name}`);
|
|
458
|
-
}
|
|
459
|
-
if (isClosedMember(relation)) {
|
|
460
|
-
throw errors.new(`relation ${relation.name} is closed — its extension is schema data (axioms), never scanned or written`);
|
|
461
|
-
}
|
|
462
|
-
return entry;
|
|
463
|
-
}
|
|
464
|
-
function declaredKeyOf(tables, theory, relation, statement) {
|
|
465
|
-
const statementId = tables.statements.findIndex(function byIdentity(candidate) {
|
|
466
|
-
return "statement" in candidate && candidate.statement === statement;
|
|
467
|
-
});
|
|
468
|
-
const entry = tables.statements[statementId];
|
|
469
|
-
if (entry === undefined) {
|
|
470
|
-
throw errors.new(`keyed get statement is not a declared statement of schema ${theory.name} — statement identity is the membership rule`);
|
|
471
|
-
}
|
|
472
|
-
if (entry.kind !== "functionality") {
|
|
473
|
-
throw errors.new("keyed get takes a key() statement — containments and capacity statements key nothing");
|
|
474
|
-
}
|
|
475
|
-
if (entry.owner !== relation.name) {
|
|
476
|
-
throw errors.new(`keyed get statement keys ${entry.owner}, not ${relation.name} — the statement must be a declared key of the relation it reads`);
|
|
477
|
-
}
|
|
478
|
-
return Object.freeze({ statementId, projection: entry.projection });
|
|
479
|
-
}
|
|
480
|
-
function overlayMethods(theory, tables, assertLive, reads) {
|
|
481
|
-
function contains(relation, fact) {
|
|
482
|
-
assertLive();
|
|
483
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
484
|
-
return reads.contains(entry.id, rowOf(relation.data, recordOf(fact)));
|
|
485
|
-
}
|
|
486
|
-
function readThroughKey(relation, entry, selected, key) {
|
|
487
|
-
const row = reads.get(entry.id, selected.statementId, keyRowOf(relation.data, selected.projection, key));
|
|
37
|
+
function preparedOf(theory, query, params) {
|
|
38
|
+
if (query.schema !== theory) {
|
|
39
|
+
throw refusal("QueryReader.execute", "InvalidArgument");
|
|
40
|
+
}
|
|
41
|
+
const ir = lowerQuery(query);
|
|
42
|
+
const wire = wireParams(query.data.params, params);
|
|
43
|
+
return { ir, wire, finds: query.data.finds };
|
|
44
|
+
}
|
|
45
|
+
function executeOn(state, session, query, params, work) {
|
|
46
|
+
return Effect.acquireRelease(Effect.gen(function* () {
|
|
47
|
+
const prepared = yield* Effect.try({
|
|
48
|
+
try: () => preparedOf(state.theory, query, params),
|
|
49
|
+
catch: (cause) => (cause instanceof DbError ? cause : refusal("QueryReader.execute", "InvalidArgument"))
|
|
50
|
+
});
|
|
51
|
+
const wire = policyWire(work, "QueryReader.execute");
|
|
52
|
+
const handle = yield* nativeOperationWith("QueryReader.execute", (callback) => session === undefined
|
|
53
|
+
? dbNative.runtimeSnapshotExecute(state.handle, wire, prepared.ir, prepared.wire, callback)
|
|
54
|
+
: dbNative.runtimeSessionExecute(session, wire, prepared.ir, prepared.wire, callback), dbNative.runtimeResultTake, (value) => value);
|
|
55
|
+
return makeCompleteResult(handle, prepared.finds);
|
|
56
|
+
}), (result) => Effect.suspend(() => {
|
|
57
|
+
const internal = internalResultHandle(result);
|
|
58
|
+
if (internal === undefined) {
|
|
59
|
+
return Effect.void;
|
|
60
|
+
}
|
|
61
|
+
return releaseOwner("CompleteResult.close", (callback) => dbNative.runtimeResultClose(internal, callback));
|
|
62
|
+
}), { interruptible: true });
|
|
63
|
+
}
|
|
64
|
+
function internalResultHandle(result) {
|
|
65
|
+
return internalResult(result)?.handle;
|
|
66
|
+
}
|
|
67
|
+
function getOn(state, relation, key, work) {
|
|
68
|
+
return Effect.gen(function* () {
|
|
69
|
+
if (state.theory.relations[relation.name] !== relation) {
|
|
70
|
+
return yield* Effect.fail(refusal("QueryReader.get", "InvalidArgument"));
|
|
71
|
+
}
|
|
72
|
+
const tables = schemaTables(state.theory);
|
|
73
|
+
const relationId = tables.relationIds.get(relation.name);
|
|
74
|
+
const primary = tables.primaryKeys.get(relation.name);
|
|
75
|
+
if (relationId === undefined || primary === undefined) {
|
|
76
|
+
return yield* Effect.fail(refusal("QueryReader.get", "InvalidArgument"));
|
|
77
|
+
}
|
|
78
|
+
const cells = yield* Effect.try({
|
|
79
|
+
try: () => keyCellsOf(relation.data, primary.projection, key),
|
|
80
|
+
catch: (cause) => (cause instanceof DbError ? cause : refusal("QueryReader.get", "InvalidArgument"))
|
|
81
|
+
});
|
|
82
|
+
const row = yield* nativeOperationWith("QueryReader.get", (callback) => dbNative.runtimeSnapshotGet(state.handle, policyWire(work, "QueryReader.get"), relationId, primary.statementId, cells, callback), dbNative.runtimeRowTake, (value) => value);
|
|
488
83
|
if (row === null) {
|
|
489
|
-
return
|
|
490
|
-
}
|
|
491
|
-
return
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
return
|
|
498
|
-
},
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
return instance;
|
|
546
|
-
}
|
|
547
|
-
function openDb(handle, theory, manifest) {
|
|
548
|
-
const tables = tablesOf(theory, manifest);
|
|
549
|
-
/** This store's identity token: read scopes and prepared values carry it, so cross-store use is a typed refusal. */
|
|
550
|
-
const owner = Object.freeze({});
|
|
551
|
-
function isMemberName(name) {
|
|
552
|
-
return tables.relations.has(name);
|
|
553
|
-
}
|
|
554
|
-
function violationOf(wire) {
|
|
555
|
-
const entry = tables.statements[wire.statementId];
|
|
556
|
-
if (entry === undefined) {
|
|
557
|
-
throw errors.new(`bumbledb violation cites unknown statement id ${wire.statementId}`);
|
|
558
|
-
}
|
|
559
|
-
const facts = Object.freeze(wire.facts.map(function offending(fact) {
|
|
560
|
-
const rel = tables.relations.get(fact.relation);
|
|
561
|
-
if (rel === undefined || !isMemberName(fact.relation)) {
|
|
562
|
-
throw errors.new(`bumbledb violation cites unknown relation ${fact.relation}`);
|
|
563
|
-
}
|
|
564
|
-
return decodeOffendingFact(rel.member, fact.relation, fact);
|
|
565
|
-
}));
|
|
566
|
-
return violationFromEntry(entry, wire, facts);
|
|
567
|
-
}
|
|
568
|
-
function pointReadsOf(assertLive, reads) {
|
|
569
|
-
function contains(relation, fact) {
|
|
570
|
-
assertLive();
|
|
571
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
572
|
-
return reads.contains(entry.id, rowOf(relation.data, recordOf(fact)));
|
|
573
|
-
}
|
|
574
|
-
function readThroughKey(relation, entry, selected, key) {
|
|
575
|
-
const row = reads.get(entry.id, selected.statementId, keyRowOf(relation.data, selected.projection, key));
|
|
576
|
-
if (row === null) {
|
|
577
|
-
return undefined;
|
|
578
|
-
}
|
|
579
|
-
return factOf(relation, row);
|
|
580
|
-
}
|
|
581
|
-
function get(relation, keyOrStatement, declaredKey) {
|
|
582
|
-
assertLive();
|
|
583
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
584
|
-
return selectKeyRead(keyOrStatement, declaredKey, function byStatement(statement, key) {
|
|
585
|
-
return readThroughKey(relation, entry, declaredKeyOf(tables, theory, relation, statement), recordOf(key));
|
|
586
|
-
}, function byPrimary(key) {
|
|
587
|
-
const primaryKey = entry.primaryKey;
|
|
588
|
-
if (primaryKey === undefined) {
|
|
589
|
-
throw errors.new(`relation ${relation.name} has no candidate key — keyed get requires a fresh field or a declared key statement`);
|
|
590
|
-
}
|
|
591
|
-
return readThroughKey(relation, entry, primaryKey, recordOf(key));
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
return { contains, get };
|
|
595
|
-
}
|
|
596
|
-
function pinPrepared(preparedHandle, q) {
|
|
597
|
-
const prepared = Object.freeze({});
|
|
598
|
-
preparedPlans.set(prepared, Object.freeze({
|
|
599
|
-
handle: preparedHandle,
|
|
600
|
-
owner,
|
|
601
|
-
params: q.data.params,
|
|
602
|
-
finds: q.data.finds
|
|
603
|
-
}));
|
|
604
|
-
planReclaimer.register(prepared, preparedHandle);
|
|
605
|
-
return prepared;
|
|
606
|
-
}
|
|
607
|
-
function makeWitness(nativeHandle) {
|
|
608
|
-
const state = { handle: nativeHandle, spent: false, owner };
|
|
609
|
-
const witness = Object.freeze({
|
|
610
|
-
[Symbol.dispose]() {
|
|
611
|
-
if (state.spent) {
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
|
-
state.spent = true;
|
|
615
|
-
bridged("close bumbledb witness", function closeWitness() {
|
|
616
|
-
native.witnessClose(nativeHandle);
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
witnessStates.set(witness, state);
|
|
621
|
-
witnessReclaimer.register(witness, nativeHandle);
|
|
622
|
-
return witness;
|
|
623
|
-
}
|
|
624
|
-
function makeInstance(nativeHandle) {
|
|
625
|
-
return createReadInstance(nativeHandle, theory, tables, owner);
|
|
626
|
-
}
|
|
627
|
-
function read(body) {
|
|
628
|
-
let captured;
|
|
629
|
-
const result = bridged("bumbledb read", function runRead() {
|
|
630
|
-
return native.dbRead(handle, function onRead(nativeInstance, nativeWitness) {
|
|
631
|
-
const instance = makeInstance(nativeInstance);
|
|
632
|
-
const witness = makeWitness(nativeWitness);
|
|
633
|
-
const value = body(instance, witness);
|
|
634
|
-
const state = instanceStates.get(instance);
|
|
635
|
-
if (state !== undefined) {
|
|
636
|
-
state.live = false;
|
|
637
|
-
}
|
|
638
|
-
if (isThenable(value)) {
|
|
639
|
-
throw errors.wrap(ErrAsyncCallback, "bumbledb read callback returned a thenable");
|
|
640
|
-
}
|
|
641
|
-
captured = value;
|
|
642
|
-
return value;
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
return (captured ?? result);
|
|
646
|
-
}
|
|
647
|
-
function makeTx(resolveTx) {
|
|
648
|
-
const txState = { spent: false };
|
|
649
|
-
function assertLive() {
|
|
650
|
-
if (txState.spent) {
|
|
651
|
-
throw errors.wrap(ErrUseAfterScope, "bumbledb write transaction is spent");
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
const reads = pointReadsOf(assertLive, {
|
|
655
|
-
contains(relationId, row) {
|
|
656
|
-
const txHandle = resolveTx();
|
|
657
|
-
return bridged("bumbledb tx contains", function readContains() {
|
|
658
|
-
return native.txContains(txHandle, relationId, row);
|
|
659
|
-
});
|
|
660
|
-
},
|
|
661
|
-
get(relationId, statementId, key) {
|
|
662
|
-
const txHandle = resolveTx();
|
|
663
|
-
return bridged("bumbledb tx get", function readGet() {
|
|
664
|
-
return native.txGet(txHandle, relationId, statementId, key);
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
});
|
|
668
|
-
function insert(relation, facts) {
|
|
669
|
-
assertLive();
|
|
670
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
671
|
-
const txHandle = resolveTx();
|
|
672
|
-
return mutateCollection(relation, facts, function applyCells(rows, cells) {
|
|
673
|
-
return bridged("bumbledb tx insert", function record() {
|
|
674
|
-
return native.txInsert(txHandle, entry.id, rows, cells);
|
|
675
|
-
});
|
|
676
|
-
});
|
|
677
|
-
}
|
|
678
|
-
function remove(relation, facts) {
|
|
679
|
-
assertLive();
|
|
680
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
681
|
-
const txHandle = resolveTx();
|
|
682
|
-
const flat = rowsOf(relation, facts);
|
|
683
|
-
const report = bridged("bumbledb tx delete", function record() {
|
|
684
|
-
return native.txDelete(txHandle, entry.id, flat.rows, flat.cells);
|
|
685
|
-
});
|
|
686
|
-
return Object.freeze({ submitted: report.submitted, changed: report.changed });
|
|
687
|
-
}
|
|
688
|
-
function reserve(relation, field, count) {
|
|
689
|
-
assertLive();
|
|
690
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
691
|
-
const declared = relation.data.fields.find(function byName(candidate) {
|
|
692
|
-
return candidate.name === field;
|
|
693
|
-
});
|
|
694
|
-
if (declared === undefined || !isFreshField(declared.field)) {
|
|
695
|
-
throw errors.new(`relation ${relation.name}: field ${field} is not a fresh cell`);
|
|
696
|
-
}
|
|
697
|
-
const fieldId = entry.fieldIds.get(field);
|
|
698
|
-
if (fieldId === undefined) {
|
|
699
|
-
throw errors.new(`bumbledb manifest drift: relation ${relation.name} has no field id for ${field}`);
|
|
700
|
-
}
|
|
701
|
-
const txHandle = resolveTx();
|
|
702
|
-
const range = bridged("bumbledb tx reserve", function mint() {
|
|
703
|
-
return native.txReserve(txHandle, entry.id, fieldId, count);
|
|
704
|
-
});
|
|
705
|
-
return freshRangeOf(range);
|
|
706
|
-
}
|
|
707
|
-
const tx = Object.freeze({
|
|
708
|
-
insert,
|
|
709
|
-
delete: remove,
|
|
710
|
-
reserve,
|
|
711
|
-
contains: reads.contains,
|
|
712
|
-
get: reads.get
|
|
713
|
-
});
|
|
714
|
-
function spend() {
|
|
715
|
-
txState.spent = true;
|
|
716
|
-
}
|
|
717
|
-
return { tx, spend };
|
|
718
|
-
}
|
|
719
|
-
function mapNativeWrite(nativeOutcome, built) {
|
|
720
|
-
if (nativeOutcome.tag === "moved") {
|
|
721
|
-
return Object.freeze({
|
|
722
|
-
tag: "moved",
|
|
723
|
-
witnessed: nativeOutcome.witnessed,
|
|
724
|
-
current: nativeOutcome.current
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
if (nativeOutcome.tag === "rejected") {
|
|
728
|
-
return Object.freeze({
|
|
729
|
-
tag: "rejected",
|
|
730
|
-
violations: Object.freeze(nativeOutcome.violations.map(violationOf))
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
if (nativeOutcome.tag === "abandoned") {
|
|
734
|
-
if (built === undefined || !isAbandon(built)) {
|
|
735
|
-
throw errors.new("bumbledb write abandoned without an abandon sentinel");
|
|
736
|
-
}
|
|
737
|
-
return abandonedOutcome(built);
|
|
738
|
-
}
|
|
739
|
-
return Object.freeze({
|
|
740
|
-
tag: "accepted",
|
|
741
|
-
value: Object.freeze({
|
|
742
|
-
value: built,
|
|
743
|
-
generation: nativeOutcome.generation
|
|
744
|
-
})
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
function runWrite(invoke, fn) {
|
|
748
|
-
let built;
|
|
749
|
-
const nativeOutcome = bridged("bumbledb write", function callWrite() {
|
|
750
|
-
return invoke(function onWrite(txHandle) {
|
|
751
|
-
const made = makeTx(function resolveTx() {
|
|
752
|
-
return txHandle;
|
|
753
|
-
});
|
|
754
|
-
const result = errors.trySync(function buildDelta() {
|
|
755
|
-
return fn(made.tx);
|
|
756
|
-
});
|
|
757
|
-
made.spend();
|
|
758
|
-
if (result.error) {
|
|
759
|
-
throw errors.wrap(result.error, "build write delta");
|
|
84
|
+
return Option.none();
|
|
85
|
+
}
|
|
86
|
+
return Option.some(factOfCells(relation, row));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function makeSession(state, handle) {
|
|
90
|
+
const session = {
|
|
91
|
+
execute(query, params, work) {
|
|
92
|
+
return executeOn(state, handle, query, params, work);
|
|
93
|
+
},
|
|
94
|
+
close() {
|
|
95
|
+
return drainClose("ExecutionSession.close", (callback) => dbNative.runtimeSessionClose(handle, callback));
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
Object.freeze(session);
|
|
99
|
+
sessionHandles.set(session, handle);
|
|
100
|
+
return session;
|
|
101
|
+
}
|
|
102
|
+
function makeSnapshot(theory, handle, witness) {
|
|
103
|
+
const state = { theory, handle };
|
|
104
|
+
const snapshot = {
|
|
105
|
+
witness,
|
|
106
|
+
get(relation, key, work) {
|
|
107
|
+
return getOn(state, relation, key, work);
|
|
108
|
+
},
|
|
109
|
+
execute(query, params, work) {
|
|
110
|
+
return executeOn(state, undefined, query, params, work);
|
|
111
|
+
},
|
|
112
|
+
session(work) {
|
|
113
|
+
return Effect.acquireRelease(nativeOperationWith("Snapshot.session", (callback) => dbNative.runtimeSnapshotSession(handle, policyWire(work, "Snapshot.session"), callback), dbNative.runtimeSessionTake, (value) => makeSession(state, value)), (session) => Effect.suspend(() => releaseOwner("ExecutionSession.close", (callback) => dbNative.runtimeSessionClose(sessionHandles.get(session) ?? missingSession(), callback))), { interruptible: true });
|
|
114
|
+
},
|
|
115
|
+
close() {
|
|
116
|
+
return drainClose("Snapshot.close", (callback) => dbNative.runtimeSnapshotClose(handle, callback));
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
Object.freeze(snapshot);
|
|
120
|
+
snapshotHandles.set(snapshot, handle);
|
|
121
|
+
return snapshot;
|
|
122
|
+
}
|
|
123
|
+
const sessionHandles = new WeakMap();
|
|
124
|
+
function missingSession() {
|
|
125
|
+
throw new DbError({ operation: "ExecutionSession.close", reason: { _tag: "Internal" } });
|
|
126
|
+
}
|
|
127
|
+
function makeDb(theory, state) {
|
|
128
|
+
const value = {
|
|
129
|
+
schemaId: state.schemaId,
|
|
130
|
+
snapshot(work) {
|
|
131
|
+
return Effect.acquireRelease(nativeOperationWith("Db.snapshot", (callback) => dbNative.runtimeDbSnapshot(state.db, policyWire(work, "Db.snapshot"), callback), dbNative.runtimeSnapshotTake, (wire) => makeSnapshot(theory, wire.snapshot, witnessOf(wire.witness))), (snapshot) => Effect.suspend(() => releaseOwner("Snapshot.close", (callback) => dbNative.runtimeSnapshotClose(snapshotHandles.get(snapshot) ?? missingSnapshot(), callback))), { interruptible: true });
|
|
132
|
+
},
|
|
133
|
+
apply(changes, options) {
|
|
134
|
+
return Effect.gen(function* () {
|
|
135
|
+
const internal = internalChanges(changes);
|
|
136
|
+
if (internal === undefined) {
|
|
137
|
+
// A foreign object supplied dynamically refuses before
|
|
138
|
+
// any native dispatch.
|
|
139
|
+
return yield* Effect.fail(refusal("Db.apply", "InvalidArgument"));
|
|
760
140
|
}
|
|
761
|
-
if (
|
|
762
|
-
|
|
141
|
+
if (internal.schemaId !== state.schemaId) {
|
|
142
|
+
return yield* Effect.fail(refusal("Db.apply", "InvalidArgument"));
|
|
763
143
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
return
|
|
841
|
-
});
|
|
842
|
-
return openDb(dbHandle, theory, manifest);
|
|
843
|
-
}
|
|
844
|
-
async function createStore(storePath, theory) {
|
|
845
|
-
const canonical = path.resolve(storePath);
|
|
846
|
-
const spec = lower(theory);
|
|
847
|
-
const created = await bridgedAsync(`create bumbledb store at ${canonical}`, function callBridge() {
|
|
848
|
-
return native.dbCreate(canonical, spec);
|
|
849
|
-
});
|
|
850
|
-
if (created.tag === "schemaError" || created.tag === "newtypeMismatch") {
|
|
851
|
-
throwOpenRefusal("create", canonical, created.tag, created.message);
|
|
852
|
-
}
|
|
853
|
-
if (created.tag === "rejected") {
|
|
854
|
-
return Object.freeze({
|
|
855
|
-
tag: "rejected",
|
|
856
|
-
violations: Object.freeze(created.violations.map(function mapWire(wire) {
|
|
857
|
-
return mapViolationWithoutStore(theory, wire);
|
|
858
|
-
}))
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
return Object.freeze({ tag: "accepted", value: openFromHandle(created.db, theory) });
|
|
862
|
-
}
|
|
863
|
-
function mapViolationWithoutStore(theory, wire) {
|
|
864
|
-
const entries = materializedEntries(theory);
|
|
865
|
-
const entry = entries[wire.statementId];
|
|
866
|
-
if (entry === undefined) {
|
|
867
|
-
throw errors.new(`bumbledb violation cites unknown statement id ${wire.statementId}`);
|
|
868
|
-
}
|
|
869
|
-
const facts = Object.freeze(wire.facts.map(function offending(fact) {
|
|
870
|
-
const member = theory.relations[fact.relation];
|
|
871
|
-
if (member === undefined || !(fact.relation in theory.relations)) {
|
|
872
|
-
throw errors.new(`bumbledb violation cites unknown relation ${fact.relation}`);
|
|
873
|
-
}
|
|
874
|
-
return decodeOffendingFact(member, fact.relation, fact);
|
|
875
|
-
}));
|
|
876
|
-
return violationFromEntry(entry, wire, facts);
|
|
877
|
-
}
|
|
878
|
-
async function openStore(storePath, theory) {
|
|
879
|
-
const canonical = path.resolve(storePath);
|
|
880
|
-
const spec = lower(theory);
|
|
881
|
-
const opened = await bridgedAsync(`open bumbledb store at ${canonical}`, function callBridge() {
|
|
882
|
-
return native.dbOpen(canonical, spec);
|
|
883
|
-
});
|
|
884
|
-
if (!opened.ok) {
|
|
885
|
-
throwOpenRefusal("open", canonical, opened.kind, opened.message);
|
|
886
|
-
}
|
|
887
|
-
return openFromHandle(opened.db, theory);
|
|
888
|
-
}
|
|
889
|
-
const ownedRecords = new WeakMap();
|
|
890
|
-
const builderRecords = new WeakMap();
|
|
891
|
-
const ownedReclaimer = new FinalizationRegistry(function reclaimOwned(handle) {
|
|
892
|
-
const closed = errors.trySync(function closeOwned() {
|
|
893
|
-
native.ownedInstanceClose(handle);
|
|
894
|
-
});
|
|
895
|
-
if (closed.error) {
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
});
|
|
899
|
-
const builderReclaimer = new FinalizationRegistry(function reclaimBuilder(handle) {
|
|
900
|
-
const closed = errors.trySync(function closeBuilder() {
|
|
901
|
-
native.instanceBuilderClose(handle);
|
|
902
|
-
});
|
|
903
|
-
if (closed.error) {
|
|
904
|
-
return;
|
|
144
|
+
const expected = options.expected.kind === "any"
|
|
145
|
+
? { kind: "any" }
|
|
146
|
+
: { kind: "exact", store: options.expected.at.store, generation: options.expected.at.generation };
|
|
147
|
+
return yield* nativeOperationWith("Db.apply", (callback) => dbNative.runtimeDbApply(state.db, policyWire(options, "Db.apply"), internal.handle, expected, callback), dbNative.runtimeApplyTake, outcomeOf);
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
inspect(work) {
|
|
151
|
+
return nativeOperationWith("Db.inspect", (callback) => dbNative.runtimeDbInspect(state.db, policyWire(work, "Db.inspect"), callback), dbNative.runtimeDbInspectTake, (wire) => Object.freeze({
|
|
152
|
+
schemaId: state.schemaId,
|
|
153
|
+
generation: wire.generation,
|
|
154
|
+
mapBytes: wire.mapBytes,
|
|
155
|
+
populatedBytes: wire.populatedBytes,
|
|
156
|
+
diskBytes: wire.diskBytes,
|
|
157
|
+
residentEstimateBytes: wire.residentEstimateBytes,
|
|
158
|
+
retainedOperations: wire.retainedOperations
|
|
159
|
+
}));
|
|
160
|
+
},
|
|
161
|
+
close() {
|
|
162
|
+
// The one close authority: database child first, then the
|
|
163
|
+
// directory owner releases its kernel lock LAST (C09). Both
|
|
164
|
+
// joins are idempotent natively.
|
|
165
|
+
return drainClose("Db.close", (callback) => runtimeNative.runtimeManagedDbClose(state.db, callback)).pipe(Effect.flatMap((report) => drainClose("Db.directoryClose", (callback) => runtimeNative.runtimeDirectoryClose(state.directory, false, callback)).pipe(Effect.map((directoryReport) => (report.kind === "closed" ? directoryReport : report)))));
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
Object.freeze(value);
|
|
169
|
+
return value;
|
|
170
|
+
}
|
|
171
|
+
const snapshotHandles = new WeakMap();
|
|
172
|
+
function missingSnapshot() {
|
|
173
|
+
throw new DbError({ operation: "Snapshot.close", reason: { _tag: "Internal" } });
|
|
174
|
+
}
|
|
175
|
+
/** The managed child name under the database directory owner. */
|
|
176
|
+
const CHILD = "store";
|
|
177
|
+
function openDatabase(operation, path, schema, work, create) {
|
|
178
|
+
return Effect.gen(function* () {
|
|
179
|
+
const runtime = yield* runtimeHandle();
|
|
180
|
+
const compiled = yield* CoreSchema.compile(schema, work);
|
|
181
|
+
const spec = lower(schema);
|
|
182
|
+
const wire = yield* Effect.try({
|
|
183
|
+
try: () => policyWire(work, operation),
|
|
184
|
+
catch: () => refusal(operation, "InvalidArgument")
|
|
185
|
+
});
|
|
186
|
+
// Compound acquisition (TS-003): register the directory owner and
|
|
187
|
+
// its finalizer BEFORE any interruptible child-open step.
|
|
188
|
+
const directory = yield* Effect.acquireRelease(nativeOperationWith(operation, (callback) => runtimeNative.runtimeDirectoryAcquire(runtime, wire, path, callback), runtimeNative.runtimeDirectoryTake, (value) => value), (dir) => releaseOwner(`${operation}.directoryClose`, (callback) => runtimeNative.runtimeDirectoryClose(dir, false, callback)), { interruptible: true });
|
|
189
|
+
return yield* Effect.acquireRelease(Effect.gen(function* () {
|
|
190
|
+
const outcome = yield* nativeOperationWith(operation, (callback) => runtimeNative.runtimeDirectoryDbOpen(directory, wire, CHILD, spec, create, callback), runtimeNative.runtimeDbTake, (value) => value).pipe(Effect.catch((error) => drainClose(`${operation}.directoryClose`, (callback) => runtimeNative.runtimeDirectoryClose(directory, false, callback)).pipe(Effect.andThen(Effect.fail(error)))));
|
|
191
|
+
if (outcome.tag !== "accepted") {
|
|
192
|
+
yield* drainClose(`${operation}.directoryClose`, (callback) => runtimeNative.runtimeDirectoryClose(directory, false, callback));
|
|
193
|
+
return yield* Effect.fail(refusal(operation, "InvalidArgument"));
|
|
194
|
+
}
|
|
195
|
+
const state = { theory: schema, db: outcome.db, directory, schemaId: compiled.schemaId };
|
|
196
|
+
const db = makeDb(schema, state);
|
|
197
|
+
dbStates.set(db, state);
|
|
198
|
+
return db;
|
|
199
|
+
}), (db) => Effect.suspend(() => {
|
|
200
|
+
const state = dbStates.get(db);
|
|
201
|
+
if (state === undefined) {
|
|
202
|
+
return Effect.void;
|
|
203
|
+
}
|
|
204
|
+
return releaseOwner("Db.close", (callback) => runtimeNative.runtimeManagedDbClose(state.db, callback)).pipe(Effect.ensuring(releaseOwner("Db.directoryClose", (callback) => runtimeNative.runtimeDirectoryClose(state.directory, false, callback))));
|
|
205
|
+
}), { interruptible: true });
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
const dbStates = new WeakMap();
|
|
209
|
+
/**
|
|
210
|
+
* `Db.create` is the explicit constructor and refuses existing authority;
|
|
211
|
+
* `Db.open` of a missing or unreadable database never creates an empty
|
|
212
|
+
* replacement (chapter 30). Both compile the schema through the same
|
|
213
|
+
* implementation as `Schema.compile` — prior compilation is optional.
|
|
214
|
+
*/
|
|
215
|
+
const Db = Object.freeze({
|
|
216
|
+
create(path, schema, work) {
|
|
217
|
+
return openDatabase("Db.create", path, schema, work, true);
|
|
218
|
+
},
|
|
219
|
+
open(path, schema, work) {
|
|
220
|
+
return openDatabase("Db.open", path, schema, work, false);
|
|
905
221
|
}
|
|
906
222
|
});
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
contains(relationId, values) {
|
|
924
|
-
return native.ownedContains(nativeHandle, relationId, values);
|
|
925
|
-
},
|
|
926
|
-
get(relationId, statementId, keyValues) {
|
|
927
|
-
return native.ownedGet(nativeHandle, relationId, statementId, keyValues);
|
|
928
|
-
},
|
|
929
|
-
prepare(query) {
|
|
930
|
-
return native.ownedPrepare(nativeHandle, query);
|
|
931
|
-
},
|
|
932
|
-
execute(prepared, params) {
|
|
933
|
-
return native.ownedExecute(prepared, nativeHandle, params);
|
|
934
|
-
}
|
|
935
|
-
});
|
|
936
|
-
const instance = Object.freeze({
|
|
937
|
-
...methods,
|
|
938
|
-
[Symbol.dispose]() {
|
|
939
|
-
if (rec.spent) {
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
try {
|
|
943
|
-
native.ownedInstanceClose(nativeHandle);
|
|
944
|
-
}
|
|
945
|
-
catch (caught) {
|
|
946
|
-
const error = errorFromThrow(caught);
|
|
947
|
-
if (LEASED_FOR_PUBLISH.test(error.message)) {
|
|
948
|
-
throw errors.wrap(ErrSpentHandle, "bumbledb owned instance is leased for publish");
|
|
949
|
-
}
|
|
950
|
-
throw errors.wrap(error, "close bumbledb owned instance");
|
|
951
|
-
}
|
|
952
|
-
rec.spent = true;
|
|
953
|
-
ownedReclaimer.unregister(instance);
|
|
954
|
-
}
|
|
955
|
-
});
|
|
956
|
-
ownedRecords.set(instance, rec);
|
|
957
|
-
ownedReclaimer.register(instance, nativeHandle, instance);
|
|
958
|
-
return instance;
|
|
959
|
-
}
|
|
960
|
-
function wrapBuilder(nativeHandle, theory) {
|
|
961
|
-
const rec = { handle: nativeHandle, theory, spent: false };
|
|
962
|
-
const tables = tablesFromTheory(theory);
|
|
963
|
-
function assertLive() {
|
|
964
|
-
if (rec.spent) {
|
|
965
|
-
throw errors.wrap(ErrSpentHandle, "bumbledb instance builder has been spent");
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
const overlay = overlayMethods(theory, tables, assertLive, {
|
|
969
|
-
contains(relationId, row) {
|
|
970
|
-
return bridged("bumbledb builder contains", function readContains() {
|
|
971
|
-
return native.instanceBuilderContains(nativeHandle, relationId, row);
|
|
972
|
-
});
|
|
973
|
-
},
|
|
974
|
-
get(relationId, statementId, key) {
|
|
975
|
-
return bridged("bumbledb builder get", function readGet() {
|
|
976
|
-
return native.instanceBuilderGet(nativeHandle, relationId, statementId, key);
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
});
|
|
980
|
-
const builder = Object.freeze({
|
|
981
|
-
load(relation, facts) {
|
|
982
|
-
assertLive();
|
|
983
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
984
|
-
return mutateCollection(relation, facts, function applyCells(rows, cells) {
|
|
985
|
-
return bridged("bumbledb builder load", function loadCells() {
|
|
986
|
-
return native.instanceBuilderLoad(nativeHandle, entry.id, rows, cells);
|
|
987
|
-
});
|
|
988
|
-
});
|
|
989
|
-
},
|
|
990
|
-
delete(relation, facts) {
|
|
991
|
-
assertLive();
|
|
992
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
993
|
-
const flat = rowsOf(relation, facts);
|
|
994
|
-
const report = bridged("bumbledb builder delete", function remove() {
|
|
995
|
-
return native.instanceBuilderDelete(nativeHandle, entry.id, flat.rows, flat.cells);
|
|
996
|
-
});
|
|
997
|
-
return Object.freeze({ submitted: report.submitted, changed: report.changed });
|
|
998
|
-
},
|
|
999
|
-
reserve(relation, field, count) {
|
|
1000
|
-
assertLive();
|
|
1001
|
-
const entry = ordinaryEntry(tables, theory, relation);
|
|
1002
|
-
const declared = relation.data.fields.find(function byName(candidate) {
|
|
1003
|
-
return candidate.name === field;
|
|
1004
|
-
});
|
|
1005
|
-
if (declared === undefined || !isFreshField(declared.field)) {
|
|
1006
|
-
throw errors.new(`relation ${relation.name}: field ${field} is not a fresh cell`);
|
|
1007
|
-
}
|
|
1008
|
-
const fieldId = entry.fieldIds.get(field);
|
|
1009
|
-
if (fieldId === undefined) {
|
|
1010
|
-
throw errors.new(`bumbledb manifest drift: relation ${relation.name} has no field id for ${field}`);
|
|
1011
|
-
}
|
|
1012
|
-
const range = bridged("bumbledb builder reserve", function mint() {
|
|
1013
|
-
return native.instanceBuilderReserve(nativeHandle, entry.id, fieldId, count);
|
|
1014
|
-
});
|
|
1015
|
-
return freshRangeOf(range);
|
|
223
|
+
/**
|
|
224
|
+
* Private log-integration seam (C10): wraps a PUBLISHED core snapshot
|
|
225
|
+
* handle — minted by the internal log machine's native open/snapshot verbs
|
|
226
|
+
* — in the exact core `QueryReader` plus the scoped session acquisition
|
|
227
|
+
* (`PublishedSnapshot extends QueryReader` in chapter 35; the log adds
|
|
228
|
+
* identity/stamps/freshness AROUND this capability, never a second reader).
|
|
229
|
+
* The argument is the log package's branded handle for the same native
|
|
230
|
+
* registry entry, so the one cast below is a cross-package respelling of
|
|
231
|
+
* one native capability — the native side re-judges kind/generation/owner
|
|
232
|
+
* on every verb, so a forged object refuses there, typed.
|
|
233
|
+
*/
|
|
234
|
+
function internalPublishedReader(core, theory) {
|
|
235
|
+
const state = { theory, handle: core };
|
|
236
|
+
return Object.freeze({
|
|
237
|
+
get(relation, key, work) {
|
|
238
|
+
return getOn(state, relation, key, work);
|
|
1016
239
|
},
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
async admit() {
|
|
1020
|
-
if (rec.spent) {
|
|
1021
|
-
throw errors.wrap(ErrSpentHandle, "bumbledb instance builder has been spent");
|
|
1022
|
-
}
|
|
1023
|
-
rec.spent = true;
|
|
1024
|
-
builderReclaimer.unregister(builder);
|
|
1025
|
-
let outcome;
|
|
1026
|
-
try {
|
|
1027
|
-
outcome = await native.instanceBuilderAdmit(nativeHandle);
|
|
1028
|
-
}
|
|
1029
|
-
catch (caught) {
|
|
1030
|
-
throw errors.wrap(errorFromThrow(caught), "admit bumbledb instance");
|
|
1031
|
-
}
|
|
1032
|
-
if (outcome.tag === "rejected") {
|
|
1033
|
-
return Object.freeze({
|
|
1034
|
-
tag: "rejected",
|
|
1035
|
-
violations: Object.freeze(outcome.violations.map(function mapWire(wire) {
|
|
1036
|
-
return mapViolationWithoutStore(theory, wire);
|
|
1037
|
-
}))
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
return Object.freeze({
|
|
1041
|
-
tag: "accepted",
|
|
1042
|
-
value: wrapOwned(outcome.value, theory)
|
|
1043
|
-
});
|
|
240
|
+
execute(queryValue, params, work) {
|
|
241
|
+
return executeOn(state, undefined, queryValue, params, work);
|
|
1044
242
|
},
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
return;
|
|
1048
|
-
}
|
|
1049
|
-
rec.spent = true;
|
|
1050
|
-
builderReclaimer.unregister(builder);
|
|
1051
|
-
bridged("close bumbledb instance builder", function closeBuilder() {
|
|
1052
|
-
native.instanceBuilderClose(nativeHandle);
|
|
1053
|
-
});
|
|
243
|
+
session(work) {
|
|
244
|
+
return Effect.acquireRelease(nativeOperationWith("Snapshot.session", (callback) => dbNative.runtimeSnapshotSession(state.handle, policyWire(work, "Snapshot.session"), callback), dbNative.runtimeSessionTake, (value) => makeSession(state, value)), (session) => Effect.suspend(() => releaseOwner("ExecutionSession.close", (callback) => dbNative.runtimeSessionClose(sessionHandles.get(session) ?? missingSession(), callback))), { interruptible: true });
|
|
1054
245
|
}
|
|
1055
246
|
});
|
|
1056
|
-
builderRecords.set(builder, rec);
|
|
1057
|
-
builderReclaimer.register(builder, nativeHandle, builder);
|
|
1058
|
-
return builder;
|
|
1059
247
|
}
|
|
1060
|
-
|
|
1061
|
-
create(theory) {
|
|
1062
|
-
const spec = lower(theory);
|
|
1063
|
-
const handle = bridged("create bumbledb instance builder", function make() {
|
|
1064
|
-
return native.instanceBuilderNew(spec);
|
|
1065
|
-
});
|
|
1066
|
-
return wrapBuilder(handle, theory);
|
|
1067
|
-
}
|
|
1068
|
-
});
|
|
1069
|
-
/**
|
|
1070
|
-
* The store lifecycle — `Db.create(path, schema)` / `Db.open(path, schema)`.
|
|
1071
|
-
* Create refuses an already-initialized directory; open verifies format
|
|
1072
|
-
* version and the schema fingerprint. A second live handle on the same
|
|
1073
|
-
* path is the engine's `EnvironmentLocked`. There is no close anywhere:
|
|
1074
|
-
* the process owns the environment until GC/exit (durability is the
|
|
1075
|
-
* engine's per-commit fsync). Resume = reopen in a fresh process, or
|
|
1076
|
-
* hold the `Db` this process opened.
|
|
1077
|
-
*/
|
|
1078
|
-
const Db = Object.freeze({
|
|
1079
|
-
async create(storePath, theory) {
|
|
1080
|
-
return createStore(storePath, theory);
|
|
1081
|
-
},
|
|
1082
|
-
async open(storePath, theory) {
|
|
1083
|
-
return openStore(storePath, theory);
|
|
1084
|
-
},
|
|
1085
|
-
async fromInstance(storePath, instance) {
|
|
1086
|
-
const rec = ownedRecords.get(instance);
|
|
1087
|
-
if (rec === undefined) {
|
|
1088
|
-
throw errors.wrap(ErrSpentHandle, "bumbledb fromInstance target is not an owned instance of this SDK");
|
|
1089
|
-
}
|
|
1090
|
-
if (rec.spent) {
|
|
1091
|
-
throw errors.wrap(ErrSpentHandle, "bumbledb fromInstance target has been disposed");
|
|
1092
|
-
}
|
|
1093
|
-
const canonical = path.resolve(storePath);
|
|
1094
|
-
const dbHandle = await bridgedAsync(`publish bumbledb instance at ${canonical}`, function publish() {
|
|
1095
|
-
return native.dbFromInstance(canonical, rec.handle);
|
|
1096
|
-
});
|
|
1097
|
-
return openFromHandle(dbHandle, rec.theory);
|
|
1098
|
-
}
|
|
1099
|
-
});
|
|
1100
|
-
export { abandon, Db, ErrAsyncCallback, ErrFingerprintMismatch, ErrForeignPrepared, ErrForeignWitness, ErrIrError, ErrNewtypeMismatch, ErrSchemaError, ErrSpentHandle, ErrUseAfterScope, InstanceBuilder };
|
|
248
|
+
export { Db, internalPublishedReader };
|
|
1101
249
|
//# sourceMappingURL=db.js.map
|