@bjornpagen/bumbledb 1.0.1 → 1.2.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 +176 -36
- package/README.md +70 -26
- package/dist/capacity.d.ts +15 -55
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +63 -12
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +28 -6
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +119 -71
- package/dist/changes.js.map +1 -1
- package/dist/closed.d.ts +23 -51
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +110 -168
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +8 -14
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +113 -188
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +17 -18
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +41 -20
- package/dist/compile.js.map +1 -1
- package/dist/db-native.d.ts +76 -41
- package/dist/db-native.d.ts.map +1 -1
- package/dist/db-native.js +8 -0
- package/dist/db-native.js.map +1 -1
- package/dist/db.d.ts +61 -26
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +152 -85
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +11 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -1
- package/dist/errors.js.map +1 -1
- package/dist/face.d.ts +19 -25
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +30 -24
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +13 -26
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +89 -116
- package/dist/fields.js.map +1 -1
- package/dist/immutable.d.ts +10 -0
- package/dist/immutable.d.ts.map +1 -0
- package/dist/immutable.js +75 -0
- package/dist/immutable.js.map +1 -0
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +6 -4
- package/dist/internal/log.d.ts.map +1 -1
- package/dist/internal/log.js +3 -1
- package/dist/internal/log.js.map +1 -1
- package/dist/law.d.ts +11 -11
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +1 -1
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +11 -6
- package/dist/lower.js.map +1 -1
- package/dist/migration.d.ts +2 -3
- package/dist/migration.d.ts.map +1 -1
- package/dist/migration.js +5 -5
- package/dist/migration.js.map +1 -1
- package/dist/native.d.ts +2 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +1 -1
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/description.d.ts +33 -0
- package/dist/query/description.d.ts.map +1 -0
- package/dist/query/description.js +302 -0
- package/dist/query/description.js.map +1 -0
- package/dist/query/lower.d.ts +60 -4
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +126 -161
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +7 -2
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +240 -53
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +8 -10
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +3 -4
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +5 -10
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +22 -48
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +30 -82
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +10 -25
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +7 -12
- package/dist/result.js.map +1 -1
- package/dist/rows.d.ts +11 -14
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -237
- package/dist/rows.js.map +1 -1
- package/dist/runtime-codes.d.ts +1 -1
- package/dist/runtime-codes.d.ts.map +1 -1
- package/dist/runtime-codes.js +1 -2
- package/dist/runtime-codes.js.map +1 -1
- package/dist/runtime-errors.d.ts +27 -15
- package/dist/runtime-errors.d.ts.map +1 -1
- package/dist/runtime-errors.js +44 -8
- package/dist/runtime-errors.js.map +1 -1
- package/dist/runtime-native.d.ts +16 -34
- package/dist/runtime-native.d.ts.map +1 -1
- package/dist/runtime-native.js.map +1 -1
- package/dist/runtime.d.ts +11 -28
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +14 -54
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +9 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +85 -24
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +27 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +104 -0
- package/dist/selection.js.map +1 -0
- package/dist/shape.d.ts +6 -9
- package/dist/shape.d.ts.map +1 -1
- package/dist/statements.d.ts +17 -52
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +92 -111
- package/dist/statements.js.map +1 -1
- package/dist/values.d.ts +25 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +203 -0
- package/dist/values.js.map +1 -0
- package/pack-provenance.json +3 -3
- package/package.json +4 -4
- package/src/capacity.ts +86 -76
- package/src/changes.ts +225 -94
- package/src/closed.ts +161 -314
- package/src/codec.ts +123 -213
- package/src/compile.ts +61 -38
- package/src/db-native.ts +73 -68
- package/src/db.ts +268 -165
- package/src/errors.ts +16 -1
- package/src/face.ts +44 -74
- package/src/fields.ts +100 -146
- package/src/immutable.ts +76 -0
- package/src/index.ts +38 -31
- package/src/internal/log.ts +4 -5
- package/src/law.ts +14 -14
- package/src/lower.ts +13 -10
- package/src/migration.ts +5 -12
- package/src/native.ts +2 -1
- package/src/query/atom.ts +1 -0
- package/src/query/description.ts +394 -0
- package/src/query/lower.ts +139 -173
- package/src/query/parse-ir.ts +247 -54
- package/src/query/run.ts +15 -9
- package/src/query/scope.ts +10 -18
- package/src/relation.ts +58 -142
- package/src/result.ts +16 -45
- package/src/rows.ts +57 -250
- package/src/runtime-codes.ts +1 -2
- package/src/runtime-errors.ts +48 -8
- package/src/runtime-native.ts +13 -46
- package/src/runtime.ts +51 -119
- package/src/schema.ts +99 -29
- package/src/selection.ts +124 -0
- package/src/shape.ts +9 -9
- package/src/statements.ts +138 -189
- package/src/values.ts +223 -0
package/src/changes.ts
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import type { Scope } from "effect"
|
|
2
|
-
import { Effect, Exit } from "effect"
|
|
2
|
+
import { Effect, Exit, Option, Stream } from "effect"
|
|
3
3
|
import { drainClose, releaseOwner } from "#close.ts"
|
|
4
|
+
import { isClosedMember, membersAgree } from "#closed.ts"
|
|
4
5
|
import type { SchemaId } from "#compile.ts"
|
|
5
6
|
import { Schema as CoreSchema, schemaTables } from "#compile.ts"
|
|
6
|
-
import type { ChangesHandle, DraftHandle } from "#db-native.ts"
|
|
7
|
+
import type { ChangeRecordWire, ChangesHandle, ChangesWire, DraftHandle } from "#db-native.ts"
|
|
7
8
|
import { dbNative } from "#db-native.ts"
|
|
9
|
+
import { SdkInvariantError } from "#errors.ts"
|
|
8
10
|
import { lower } from "#lower.ts"
|
|
9
|
-
import type
|
|
11
|
+
import { type AnyRelation, type Fact, relationFields } from "#relation.ts"
|
|
10
12
|
import type { CellValue } from "#rows.ts"
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import { nativeOperationWith, policyWire, runtimeHandle } from "#runtime.ts"
|
|
13
|
+
import { factCellsOf, factOfCells, hostCellCharge } from "#rows.ts"
|
|
14
|
+
import { nativeOperationWith, runtimeHandle } from "#runtime.ts"
|
|
14
15
|
import type { CloseReport } from "#runtime-errors.ts"
|
|
15
|
-
import { DbError } from "#runtime-errors.ts"
|
|
16
|
-
import type { AnySchema } from "#schema.ts"
|
|
16
|
+
import { argumentError, DbError } from "#runtime-errors.ts"
|
|
17
|
+
import type { AnySchema, SchemaRelation } from "#schema.ts"
|
|
17
18
|
import type { Rel } from "#shape.ts"
|
|
19
|
+
import { bytesValue, recordValue } from "#values.ts"
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* `ChangeSet` — the engine's checked immutable delta:
|
|
@@ -23,17 +25,39 @@ import type { Rel } from "#shape.ts"
|
|
|
23
25
|
* add-wins. Immutable and reusable while open; sealing/submitting it later
|
|
24
26
|
* retains the SAME native value — no second JS row walk ever happens.
|
|
25
27
|
*/
|
|
28
|
+
/** Counts of distinct fact additions and removals, never input events. */
|
|
29
|
+
interface ChangeCounts {
|
|
30
|
+
readonly added: bigint
|
|
31
|
+
readonly removed: bigint
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A relation-name-discriminated union of plain, fully typed facts. */
|
|
35
|
+
type ChangeRecord<S extends AnySchema> = {
|
|
36
|
+
[N in keyof S["relations"]]: S["relations"][N] extends AnyRelation
|
|
37
|
+
? { readonly relation: N; readonly kind: "add" | "remove"; readonly fact: Fact<S["relations"][N]> }
|
|
38
|
+
: never
|
|
39
|
+
}[keyof S["relations"]]
|
|
40
|
+
|
|
26
41
|
interface ChangeSet<S extends AnySchema> {
|
|
27
42
|
readonly schemaId: SchemaId
|
|
43
|
+
/** Distinct requested actions; use judge for effective counts against a store. */
|
|
44
|
+
readonly counts: ChangeCounts
|
|
45
|
+
readonly byteLength: bigint
|
|
28
46
|
/** Phantom schema brand: a ChangeSet is only ever applied to its own S. */
|
|
29
47
|
readonly schema?: S
|
|
48
|
+
/** Independent scoped traversal, delivered in bounded native batches. Reusable. */
|
|
49
|
+
records(): Stream.Stream<ChangeRecord<S>, DbError>
|
|
50
|
+
/** Explicit full byte materialization. Mutating returned bytes cannot alter this value. */
|
|
51
|
+
toBytes(): Effect.Effect<Uint8Array, DbError>
|
|
52
|
+
/** Commutative add-wins composition into one command, not sequential replay. */
|
|
53
|
+
compose(other: ChangeSet<S>): Effect.Effect<ChangeSet<S>, DbError, Scope.Scope>
|
|
30
54
|
close(): Effect.Effect<CloseReport>
|
|
31
55
|
}
|
|
32
56
|
|
|
33
57
|
/**
|
|
34
58
|
* `ChangeDraft` — the scoped, database-free construction capability.
|
|
35
59
|
* Every method constructs a LAZY effect; execution reads the then-current
|
|
36
|
-
* iterable
|
|
60
|
+
* iterable again on every sequential rerun (no hidden
|
|
37
61
|
* memoization, no automatic retry, no iterator replay). Input must stay
|
|
38
62
|
* stable from an ingestion effect's execution start through its Exit;
|
|
39
63
|
* after successful ingestion the accepted native bytes are independent.
|
|
@@ -53,16 +77,14 @@ interface ChangeDraft<S extends AnySchema> {
|
|
|
53
77
|
* Host-copy granularity: one bounded host-to-native message per chunk, so
|
|
54
78
|
* real event-loop turns happen between chunks (each chunk completes through
|
|
55
79
|
* a native callback, not a microtask chain). These are converter
|
|
56
|
-
* granularity
|
|
57
|
-
*
|
|
58
|
-
* calls and chunks (chunks never reset it).
|
|
80
|
+
* granularity targets, not database-size limits. One larger row travels
|
|
81
|
+
* alone; otherwise native callbacks provide backpressure between batches.
|
|
59
82
|
*/
|
|
60
83
|
const CHUNK_BYTES = 65536n
|
|
61
84
|
const CHUNK_ROWS = 4096
|
|
62
85
|
|
|
63
86
|
interface DraftState {
|
|
64
87
|
readonly handle: DraftHandle
|
|
65
|
-
readonly policy: ExecutionPolicy
|
|
66
88
|
readonly theory: AnySchema
|
|
67
89
|
spent: boolean
|
|
68
90
|
inFlight: boolean
|
|
@@ -100,31 +122,15 @@ function eventLoopTurn(): Effect.Effect<void> {
|
|
|
100
122
|
}
|
|
101
123
|
|
|
102
124
|
function hostFactCharge(relation: AnyRelation, record: Readonly<Record<string, unknown>>): bigint {
|
|
103
|
-
const
|
|
125
|
+
const fields = relationFields(relation)
|
|
104
126
|
let bytes = 0n
|
|
105
|
-
for (const declared of
|
|
127
|
+
for (const declared of fields) {
|
|
106
128
|
const value = record[declared.name]
|
|
107
|
-
|
|
108
|
-
throw refusal("ChangeDraft.ingest", "InvalidArgument")
|
|
109
|
-
}
|
|
110
|
-
bytes += assertHostCellFits(`relation ${data.name} field ${declared.name}`, value, CHUNK_BYTES)
|
|
129
|
+
bytes += hostCellCharge(value)
|
|
111
130
|
}
|
|
112
131
|
return bytes
|
|
113
132
|
}
|
|
114
133
|
|
|
115
|
-
function projectFact(relation: AnyRelation, record: Readonly<Record<string, unknown>>): readonly CellValue[] {
|
|
116
|
-
const data = relation.data
|
|
117
|
-
const cells: CellValue[] = []
|
|
118
|
-
for (const declared of data.fields) {
|
|
119
|
-
const value = record[declared.name]
|
|
120
|
-
if (value === undefined) {
|
|
121
|
-
throw refusal("ChangeDraft.ingest", "InvalidArgument")
|
|
122
|
-
}
|
|
123
|
-
cells.push(cellOf(`relation ${data.name} field ${declared.name}`, declared.field, value))
|
|
124
|
-
}
|
|
125
|
-
return cells
|
|
126
|
-
}
|
|
127
|
-
|
|
128
134
|
/**
|
|
129
135
|
* Pulls one bounded chunk off the caller's iterator. Host length is judged
|
|
130
136
|
* before any string scan or byte copy. A leftover fact that does not fit
|
|
@@ -144,13 +150,17 @@ function pullChunk(relation: AnyRelation, iterator: Iterator<object>, pending: o
|
|
|
144
150
|
}
|
|
145
151
|
current = next.value
|
|
146
152
|
}
|
|
147
|
-
const record =
|
|
153
|
+
const record = recordValue(
|
|
154
|
+
`relation ${relation.name}`,
|
|
155
|
+
current,
|
|
156
|
+
relationFields(relation).map((field) => field.name)
|
|
157
|
+
)
|
|
148
158
|
const charge = hostFactCharge(relation, record)
|
|
149
159
|
if (rows > 0n && bytes + charge > CHUNK_BYTES) {
|
|
150
160
|
leftover = current
|
|
151
161
|
break
|
|
152
162
|
}
|
|
153
|
-
|
|
163
|
+
for (const cell of factCellsOf(relation, record)) cells.push(cell)
|
|
154
164
|
bytes += charge
|
|
155
165
|
rows += 1n
|
|
156
166
|
current = undefined
|
|
@@ -188,7 +198,7 @@ function ingest(
|
|
|
188
198
|
yield* spendAndDrain(state, operation)
|
|
189
199
|
return yield* Effect.fail(refusal(operation, "SpentHandle"))
|
|
190
200
|
}
|
|
191
|
-
if (state.theory.relations[relation.name]
|
|
201
|
+
if (!membersAgree(state.theory.relations[relation.name], relation)) {
|
|
192
202
|
return yield* Effect.fail(refusal(operation, "InvalidArgument"))
|
|
193
203
|
}
|
|
194
204
|
const tables = schemaTables(state.theory)
|
|
@@ -197,38 +207,45 @@ function ingest(
|
|
|
197
207
|
return yield* Effect.fail(refusal(operation, "InvalidArgument"))
|
|
198
208
|
}
|
|
199
209
|
state.inFlight = true
|
|
200
|
-
const wire = yield* Effect.try({
|
|
201
|
-
try: () => policyWire(state.policy, operation),
|
|
202
|
-
catch: () => refusal(operation, "InvalidArgument")
|
|
203
|
-
})
|
|
204
210
|
const body = Effect.gen(function* () {
|
|
205
|
-
const iterator =
|
|
211
|
+
const iterator = yield* Effect.try({
|
|
212
|
+
try: () => rows[Symbol.iterator](),
|
|
213
|
+
catch: (cause) => argumentError(operation, cause)
|
|
214
|
+
})
|
|
206
215
|
let leftover: object | undefined
|
|
207
216
|
let done = false
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
Effect.
|
|
227
|
-
|
|
228
|
-
|
|
217
|
+
return yield* Effect.gen(function* () {
|
|
218
|
+
while (!done) {
|
|
219
|
+
const chunk = yield* Effect.try({
|
|
220
|
+
try: () => pullChunk(relation, iterator, leftover),
|
|
221
|
+
catch: (cause) => argumentError(operation, cause)
|
|
222
|
+
}).pipe(Effect.catch((error) => spendAndDrain(state, operation).pipe(Effect.andThen(Effect.fail(error)))))
|
|
223
|
+
leftover = chunk.leftover
|
|
224
|
+
done = chunk.done && leftover === undefined
|
|
225
|
+
if (chunk.rows === 0n) {
|
|
226
|
+
continue
|
|
227
|
+
}
|
|
228
|
+
yield* eventLoopTurn()
|
|
229
|
+
yield* nativeOperationWith(
|
|
230
|
+
operation,
|
|
231
|
+
(callback) => verb(state.handle, relationId, chunk.rows, chunk.cells, callback),
|
|
232
|
+
dbNative.runtimeReportTake,
|
|
233
|
+
() => undefined
|
|
234
|
+
).pipe(
|
|
235
|
+
Effect.catch((error) =>
|
|
236
|
+
Effect.sync(() => {
|
|
237
|
+
state.spent = true
|
|
238
|
+
}).pipe(Effect.andThen(Effect.fail(error)))
|
|
239
|
+
)
|
|
229
240
|
)
|
|
241
|
+
}
|
|
242
|
+
}).pipe(
|
|
243
|
+
Effect.ensuring(
|
|
244
|
+
Effect.sync(() => {
|
|
245
|
+
if (!done) iterator.return?.()
|
|
246
|
+
})
|
|
230
247
|
)
|
|
231
|
-
|
|
248
|
+
)
|
|
232
249
|
})
|
|
233
250
|
return yield* Effect.onExit(body, (exit) => {
|
|
234
251
|
state.inFlight = false
|
|
@@ -240,19 +257,126 @@ function ingest(
|
|
|
240
257
|
})
|
|
241
258
|
}
|
|
242
259
|
|
|
243
|
-
function
|
|
260
|
+
function decodeChangeRecord<S extends AnySchema>(
|
|
261
|
+
relations: readonly SchemaRelation[],
|
|
262
|
+
record: ChangeRecordWire
|
|
263
|
+
): ChangeRecord<S> {
|
|
264
|
+
const relation = relations[record.relation]
|
|
265
|
+
if (relation === undefined || isClosedMember(relation) || (record.kind !== "add" && record.kind !== "remove")) {
|
|
266
|
+
throw new SdkInvariantError({ message: "ChangeSet.records: invalid native record descriptor" })
|
|
267
|
+
}
|
|
268
|
+
// Relation id resolves through this schema's own ordered roster. The
|
|
269
|
+
// shared projector validates all field values before this union seam.
|
|
270
|
+
return Object.freeze({
|
|
271
|
+
relation: relation.name,
|
|
272
|
+
kind: record.kind,
|
|
273
|
+
fact: factOfCells(relation, record.values)
|
|
274
|
+
}) as ChangeRecord<S>
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function changeRecords<S extends AnySchema>(
|
|
278
|
+
handle: ChangesHandle,
|
|
279
|
+
relations: readonly SchemaRelation[]
|
|
280
|
+
): Stream.Stream<ChangeRecord<S>, DbError> {
|
|
281
|
+
return Stream.unwrap(
|
|
282
|
+
Effect.gen(function* () {
|
|
283
|
+
const cursor = yield* Effect.acquireRelease(
|
|
284
|
+
nativeOperationWith(
|
|
285
|
+
"ChangeSet.records",
|
|
286
|
+
(callback) => dbNative.runtimeChangesCursor(handle, callback),
|
|
287
|
+
dbNative.runtimeChangesCursorTake,
|
|
288
|
+
(value) => value
|
|
289
|
+
),
|
|
290
|
+
(value) =>
|
|
291
|
+
releaseOwner("ChangeCursor.close", (callback) => dbNative.runtimeChangesCursorClose(value, callback)),
|
|
292
|
+
{ interruptible: true }
|
|
293
|
+
)
|
|
294
|
+
return Stream.paginate(undefined, () =>
|
|
295
|
+
nativeOperationWith(
|
|
296
|
+
"ChangeSet.records",
|
|
297
|
+
(callback) => dbNative.runtimeChangesCursorNext(cursor, callback),
|
|
298
|
+
dbNative.runtimeChangePageTake,
|
|
299
|
+
(page) => page
|
|
300
|
+
).pipe(
|
|
301
|
+
Effect.map((page) =>
|
|
302
|
+
page === null
|
|
303
|
+
? ([[], Option.none<undefined>()] as const)
|
|
304
|
+
: ([page.map((record) => decodeChangeRecord<S>(relations, record)), Option.some(undefined)] as const)
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
})
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function acquireChanges<S extends AnySchema>(
|
|
313
|
+
theory: S,
|
|
314
|
+
schemaId: SchemaId,
|
|
315
|
+
acquire: Effect.Effect<ChangesWire, DbError>
|
|
316
|
+
): Effect.Effect<ChangeSet<S>, DbError, Scope.Scope> {
|
|
317
|
+
return Effect.acquireRelease(
|
|
318
|
+
acquire.pipe(Effect.map((wire) => makeChangeSet(theory, wire, schemaId))),
|
|
319
|
+
(changes) =>
|
|
320
|
+
Effect.suspend(() => {
|
|
321
|
+
const internal = changesInternals.get(changes)
|
|
322
|
+
if (internal === undefined) return Effect.void
|
|
323
|
+
internal.closed = true
|
|
324
|
+
return releaseOwner("ChangeSet.close", (callback) => dbNative.runtimeChangesClose(internal.handle, callback))
|
|
325
|
+
}),
|
|
326
|
+
{ interruptible: true }
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function makeChangeSet<S extends AnySchema>(theory: S, wire: ChangesWire, schemaId: SchemaId): ChangeSet<S> {
|
|
331
|
+
const handle = wire.changes
|
|
332
|
+
const relations = Object.values(theory.relations)
|
|
333
|
+
const internal: ChangesInternal = { handle, schemaId, closed: false }
|
|
244
334
|
const value: ChangeSet<S> = {
|
|
245
335
|
schemaId,
|
|
336
|
+
counts: Object.freeze({ ...wire.counts }),
|
|
337
|
+
byteLength: wire.byteLength,
|
|
338
|
+
records() {
|
|
339
|
+
return changeRecords<S>(handle, relations)
|
|
340
|
+
},
|
|
341
|
+
toBytes() {
|
|
342
|
+
return nativeOperationWith(
|
|
343
|
+
"ChangeSet.toBytes",
|
|
344
|
+
(callback) => dbNative.runtimeChangesBytes(handle, callback),
|
|
345
|
+
dbNative.runtimeBytesTake,
|
|
346
|
+
(bytes) => bytes
|
|
347
|
+
)
|
|
348
|
+
},
|
|
349
|
+
compose(other) {
|
|
350
|
+
return Effect.suspend(() => {
|
|
351
|
+
const right = internalChanges(other)
|
|
352
|
+
if (right === undefined || right.schemaId !== schemaId)
|
|
353
|
+
return Effect.fail(refusal("ChangeSet.compose", "InvalidArgument"))
|
|
354
|
+
if (internal.closed || right.closed) return Effect.fail(refusal("ChangeSet.compose", "ClosedHandle"))
|
|
355
|
+
return acquireChanges(
|
|
356
|
+
theory,
|
|
357
|
+
schemaId,
|
|
358
|
+
nativeOperationWith(
|
|
359
|
+
"ChangeSet.compose",
|
|
360
|
+
(callback) => dbNative.runtimeChangesCompose(handle, right.handle, callback),
|
|
361
|
+
dbNative.runtimeChangesTake,
|
|
362
|
+
(result) => result
|
|
363
|
+
)
|
|
364
|
+
)
|
|
365
|
+
})
|
|
366
|
+
},
|
|
246
367
|
close() {
|
|
247
|
-
return
|
|
368
|
+
return Effect.suspend(() => {
|
|
369
|
+
internal.closed = true
|
|
370
|
+
return drainClose("ChangeSet.close", (callback) => dbNative.runtimeChangesClose(handle, callback))
|
|
371
|
+
})
|
|
248
372
|
}
|
|
249
373
|
}
|
|
250
374
|
Object.freeze(value)
|
|
251
|
-
changesInternals.set(value,
|
|
375
|
+
changesInternals.set(value, internal)
|
|
252
376
|
return value
|
|
253
377
|
}
|
|
254
378
|
|
|
255
|
-
function makeDraft<S extends AnySchema>(state: DraftState, schemaId: SchemaId): ChangeDraft<S> {
|
|
379
|
+
function makeDraft<S extends AnySchema>(theory: S, state: DraftState, schemaId: SchemaId): ChangeDraft<S> {
|
|
256
380
|
const draft: ChangeDraft<S> = {
|
|
257
381
|
insert(relation, rows) {
|
|
258
382
|
return ingest(state, "ChangeDraft.insert", relation, rows)
|
|
@@ -261,7 +385,9 @@ function makeDraft<S extends AnySchema>(state: DraftState, schemaId: SchemaId):
|
|
|
261
385
|
return ingest(state, "ChangeDraft.delete", relation, rows)
|
|
262
386
|
},
|
|
263
387
|
finish() {
|
|
264
|
-
return
|
|
388
|
+
return acquireChanges(
|
|
389
|
+
theory,
|
|
390
|
+
schemaId,
|
|
265
391
|
Effect.gen(function* () {
|
|
266
392
|
if (state.spent) {
|
|
267
393
|
return yield* Effect.fail(refusal("ChangeDraft.finish", "SpentHandle"))
|
|
@@ -272,27 +398,13 @@ function makeDraft<S extends AnySchema>(state: DraftState, schemaId: SchemaId):
|
|
|
272
398
|
}
|
|
273
399
|
// Finish CONSUMES the draft, success or failure.
|
|
274
400
|
state.spent = true
|
|
275
|
-
|
|
401
|
+
return yield* nativeOperationWith(
|
|
276
402
|
"ChangeDraft.finish",
|
|
277
|
-
(callback) =>
|
|
278
|
-
dbNative.runtimeDraftFinish(state.handle, policyWire(state.policy, "ChangeDraft.finish"), callback),
|
|
403
|
+
(callback) => dbNative.runtimeDraftFinish(state.handle, callback),
|
|
279
404
|
dbNative.runtimeChangesTake,
|
|
280
405
|
(value) => value
|
|
281
406
|
)
|
|
282
|
-
|
|
283
|
-
}),
|
|
284
|
-
(changes) =>
|
|
285
|
-
Effect.suspend(() => {
|
|
286
|
-
const internal = changesInternals.get(changes)
|
|
287
|
-
if (internal === undefined || internal.closed) {
|
|
288
|
-
return Effect.void
|
|
289
|
-
}
|
|
290
|
-
internal.closed = true
|
|
291
|
-
return releaseOwner("ChangeSet.close", (callback) =>
|
|
292
|
-
dbNative.runtimeChangesClose(internal.handle, callback)
|
|
293
|
-
)
|
|
294
|
-
}),
|
|
295
|
-
{ interruptible: true }
|
|
407
|
+
})
|
|
296
408
|
)
|
|
297
409
|
},
|
|
298
410
|
close() {
|
|
@@ -306,32 +418,31 @@ function makeDraft<S extends AnySchema>(state: DraftState, schemaId: SchemaId):
|
|
|
306
418
|
}
|
|
307
419
|
|
|
308
420
|
/**
|
|
309
|
-
* `ChangeSet.builder(schema
|
|
421
|
+
* `ChangeSet.builder(schema)` — lazy scoped acquisition of a
|
|
310
422
|
* database-free draft. Requires the acquired
|
|
311
423
|
* `NativeRuntime`; the draft's native resources release with its scope, and
|
|
312
424
|
* the scope finalizer surfaces incomplete/failed teardown as a
|
|
313
425
|
* `CloseFailure` defect.
|
|
314
426
|
*/
|
|
315
|
-
const builder = Effect.fn("ChangeSet.builder")(function* <S extends AnySchema>(schema: S
|
|
427
|
+
const builder = Effect.fn("ChangeSet.builder")(function* <S extends AnySchema>(schema: S) {
|
|
316
428
|
const handle = yield* runtimeHandle()
|
|
317
|
-
const compiled = yield* CoreSchema.compile(schema
|
|
318
|
-
const spec = lower(schema)
|
|
429
|
+
const compiled = yield* CoreSchema.compile(schema)
|
|
430
|
+
const spec = lower(compiled.schema)
|
|
319
431
|
return yield* Effect.acquireRelease(
|
|
320
432
|
Effect.gen(function* () {
|
|
321
433
|
const draftHandle = yield* nativeOperationWith(
|
|
322
434
|
"ChangeSet.builder",
|
|
323
|
-
(callback) => dbNative.runtimeDraftOpen(handle,
|
|
435
|
+
(callback) => dbNative.runtimeDraftOpen(handle, spec, callback),
|
|
324
436
|
dbNative.runtimeDraftTake,
|
|
325
437
|
(value) => value
|
|
326
438
|
)
|
|
327
439
|
const state: DraftState = {
|
|
328
440
|
handle: draftHandle,
|
|
329
|
-
|
|
330
|
-
theory: schema,
|
|
441
|
+
theory: compiled.schema,
|
|
331
442
|
spent: false,
|
|
332
443
|
inFlight: false
|
|
333
444
|
}
|
|
334
|
-
const draft = makeDraft
|
|
445
|
+
const draft = makeDraft(compiled.schema, state, compiled.schemaId)
|
|
335
446
|
draftStates.set(draft, state)
|
|
336
447
|
return draft
|
|
337
448
|
}),
|
|
@@ -355,7 +466,27 @@ const builder = Effect.fn("ChangeSet.builder")(function* <S extends AnySchema>(s
|
|
|
355
466
|
// native handle without exposing it on the public capability.
|
|
356
467
|
const draftStates = new WeakMap<object, DraftState>()
|
|
357
468
|
|
|
358
|
-
|
|
469
|
+
/** Parse canonical native bytes, owning the input when the Effect starts. */
|
|
470
|
+
const fromBytes = Effect.fn("ChangeSet.fromBytes")(function* <S extends AnySchema>(schema: S, bytes: Uint8Array) {
|
|
471
|
+
const ownedBytes = yield* Effect.try({
|
|
472
|
+
try: () => bytesValue("ChangeSet.fromBytes", bytes),
|
|
473
|
+
catch: (cause) => argumentError("ChangeSet.fromBytes", cause)
|
|
474
|
+
})
|
|
475
|
+
const handle = yield* runtimeHandle()
|
|
476
|
+
const compiled = yield* CoreSchema.compile(schema)
|
|
477
|
+
return yield* acquireChanges(
|
|
478
|
+
compiled.schema,
|
|
479
|
+
compiled.schemaId,
|
|
480
|
+
nativeOperationWith(
|
|
481
|
+
"ChangeSet.fromBytes",
|
|
482
|
+
(callback) => dbNative.runtimeChangesParse(handle, lower(compiled.schema), ownedBytes, callback),
|
|
483
|
+
dbNative.runtimeChangesTake,
|
|
484
|
+
(wire) => wire
|
|
485
|
+
)
|
|
486
|
+
)
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
const ChangeSet = Object.freeze({ builder, fromBytes })
|
|
359
490
|
|
|
360
|
-
export type { ChangeDraft }
|
|
491
|
+
export type { ChangeCounts, ChangeDraft, ChangeRecord }
|
|
361
492
|
export { ChangeSet, internalChanges }
|