@bjornpagen/bumbledb 1.3.0 → 2.0.0-rc.1
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 +224 -95
- package/README.md +114 -45
- package/dist/alternatives.d.ts +5 -6
- package/dist/alternatives.d.ts.map +1 -1
- package/dist/alternatives.js +5 -6
- package/dist/alternatives.js.map +1 -1
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +85 -0
- package/dist/bin.js.map +1 -0
- package/dist/capacity.d.ts +14 -17
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +7 -7
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +32 -41
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +86 -110
- package/dist/changes.js.map +1 -1
- package/dist/closed.d.ts +2 -2
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +5 -5
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +20 -27
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +48 -22
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +26 -50
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +75 -64
- package/dist/compile.js.map +1 -1
- package/dist/database/database.d.ts +98 -0
- package/dist/database/database.d.ts.map +1 -0
- package/dist/database/database.js +249 -0
- package/dist/database/database.js.map +1 -0
- package/dist/database/driver.d.ts +40 -0
- package/dist/database/driver.d.ts.map +1 -0
- package/dist/database/driver.js +68 -0
- package/dist/database/driver.js.map +1 -0
- package/dist/database/fs.d.ts +12 -0
- package/dist/database/fs.d.ts.map +1 -0
- package/dist/database/fs.js +120 -0
- package/dist/database/fs.js.map +1 -0
- package/dist/database/generate.d.ts +26 -0
- package/dist/database/generate.d.ts.map +1 -0
- package/dist/database/generate.js +102 -0
- package/dist/database/generate.js.map +1 -0
- package/dist/database/hosted.d.ts +32 -0
- package/dist/database/hosted.d.ts.map +1 -0
- package/dist/database/hosted.js +44 -0
- package/dist/database/hosted.js.map +1 -0
- package/dist/database/io.d.ts +94 -0
- package/dist/database/io.d.ts.map +1 -0
- package/dist/database/io.js +35 -0
- package/dist/database/io.js.map +1 -0
- package/dist/database/mem.d.ts +26 -0
- package/dist/database/mem.d.ts.map +1 -0
- package/dist/database/mem.js +67 -0
- package/dist/database/mem.js.map +1 -0
- package/dist/database/migration.d.ts +55 -0
- package/dist/database/migration.d.ts.map +1 -0
- package/dist/database/migration.js +46 -0
- package/dist/database/migration.js.map +1 -0
- package/dist/database/s3.d.ts +36 -0
- package/dist/database/s3.d.ts.map +1 -0
- package/dist/database/s3.js +141 -0
- package/dist/database/s3.js.map +1 -0
- package/dist/db.d.ts +44 -128
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +163 -287
- package/dist/db.js.map +1 -1
- package/dist/engine.d.ts +7 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +2 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +152 -46
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +107 -19
- package/dist/errors.js.map +1 -1
- package/dist/face.d.ts +5 -5
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +6 -6
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +4 -5
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +3 -3
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +60 -58
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -29
- package/dist/index.js.map +1 -1
- package/dist/judgment.d.ts +1 -1
- package/dist/judgment.js +1 -1
- package/dist/law.d.ts +16 -68
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +3 -3
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts +3 -3
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +35 -39
- package/dist/lower.js.map +1 -1
- package/dist/native/addon.d.ts +23 -0
- package/dist/native/addon.d.ts.map +1 -0
- package/dist/native/addon.js +15 -0
- package/dist/native/addon.js.map +1 -0
- package/dist/native/binding.d.ts +996 -0
- package/dist/native/json.d.ts +14 -0
- package/dist/native/json.d.ts.map +1 -0
- package/dist/native/json.js +73 -0
- package/dist/native/json.js.map +1 -0
- package/dist/native/load.d.ts +9 -0
- package/dist/native/load.d.ts.map +1 -0
- package/dist/native/load.js +30 -0
- package/dist/native/load.js.map +1 -0
- package/dist/native/op.d.ts +29 -0
- package/dist/native/op.d.ts.map +1 -0
- package/dist/native/op.js +50 -0
- package/dist/native/op.js.map +1 -0
- package/dist/query/atom.d.ts +13 -14
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +2 -2
- package/dist/query/atom.js.map +1 -1
- package/dist/query/compute.d.ts +7 -7
- package/dist/query/compute.d.ts.map +1 -1
- package/dist/query/compute.js +7 -16
- package/dist/query/compute.js.map +1 -1
- package/dist/query/find.d.ts +8 -8
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +1 -1
- package/dist/query/lower.d.ts +31 -17
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +260 -368
- package/dist/query/lower.js.map +1 -1
- package/dist/query/run.d.ts +5 -5
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +32 -15
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +20 -28
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +13 -8
- package/dist/query/scope.js.map +1 -1
- package/dist/query/segments.d.ts +2 -2
- package/dist/query/segments.d.ts.map +1 -1
- package/dist/query/segments.js +3 -3
- package/dist/query/segments.js.map +1 -1
- package/dist/relation.d.ts +1 -1
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +4 -4
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +15 -22
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +30 -39
- package/dist/result.js.map +1 -1
- package/dist/rows.d.ts +9 -33
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +8 -12
- package/dist/rows.js.map +1 -1
- package/dist/runtime.d.ts +17 -43
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +41 -241
- package/dist/runtime.js.map +1 -1
- package/dist/scalar.d.ts +36 -106
- package/dist/scalar.d.ts.map +1 -1
- package/dist/scalar.js +36 -185
- package/dist/scalar.js.map +1 -1
- package/dist/schema.d.ts +7 -18
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +18 -228
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +4 -4
- package/dist/selection.d.ts.map +1 -1
- package/dist/selection.js +5 -5
- package/dist/selection.js.map +1 -1
- package/dist/shape.d.ts +5 -5
- package/dist/shape.d.ts.map +1 -1
- package/dist/spec.d.ts +7 -79
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +3 -3
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +10 -10
- package/dist/statements.js.map +1 -1
- package/dist/uuid.d.ts +1 -1
- package/dist/uuid.d.ts.map +1 -1
- package/dist/uuid.js +1 -1
- package/dist/uuid.js.map +1 -1
- package/dist/values.d.ts +2 -2
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +2 -2
- package/dist/values.js.map +1 -1
- package/package.json +18 -18
- package/src/alternatives.ts +7 -8
- package/src/bin.ts +82 -0
- package/src/capacity.ts +20 -23
- package/src/changes.ts +134 -197
- package/src/closed.ts +5 -5
- package/src/codec.ts +77 -51
- package/src/compile.ts +91 -113
- package/src/database/database.ts +413 -0
- package/src/database/driver.ts +134 -0
- package/src/database/fs.ts +119 -0
- package/src/database/generate.ts +116 -0
- package/src/database/hosted.ts +102 -0
- package/src/database/io.ts +134 -0
- package/src/database/mem.ts +113 -0
- package/src/database/migration.ts +107 -0
- package/src/database/s3.ts +231 -0
- package/src/db.ts +290 -547
- package/src/engine.ts +14 -0
- package/src/errors.ts +118 -39
- package/src/face.ts +9 -9
- package/src/fields.ts +6 -7
- package/src/index.ts +102 -109
- package/src/judgment.ts +1 -1
- package/src/law.ts +18 -70
- package/src/lower.ts +61 -72
- package/src/native/addon.ts +54 -0
- package/src/native/binding.d.ts +996 -0
- package/src/native/json.ts +100 -0
- package/src/native/load.ts +31 -0
- package/src/native/op.ts +73 -0
- package/src/query/atom.ts +15 -16
- package/src/query/compute.ts +13 -22
- package/src/query/find.ts +8 -8
- package/src/query/lower.ts +323 -414
- package/src/query/run.ts +42 -21
- package/src/query/scope.ts +37 -38
- package/src/query/segments.ts +4 -4
- package/src/relation.ts +4 -4
- package/src/result.ts +53 -80
- package/src/rows.ts +18 -46
- package/src/runtime.ts +50 -346
- package/src/scalar.ts +76 -336
- package/src/schema.ts +23 -284
- package/src/selection.ts +6 -6
- package/src/shape.ts +5 -5
- package/src/spec.ts +7 -85
- package/src/statements.ts +11 -11
- package/src/uuid.ts +1 -1
- package/src/values.ts +4 -4
- package/dist/close.d.ts +0 -24
- package/dist/close.d.ts.map +0 -1
- package/dist/close.js +0 -34
- package/dist/close.js.map +0 -1
- package/dist/db-native.d.ts +0 -196
- package/dist/db-native.d.ts.map +0 -1
- package/dist/db-native.js +0 -13
- package/dist/db-native.js.map +0 -1
- package/dist/internal/log.d.ts +0 -26
- package/dist/internal/log.d.ts.map +0 -1
- package/dist/internal/log.js +0 -12
- package/dist/internal/log.js.map +0 -1
- package/dist/migration.d.ts +0 -31
- package/dist/migration.d.ts.map +0 -1
- package/dist/migration.js +0 -47
- package/dist/migration.js.map +0 -1
- package/dist/native.d.ts +0 -562
- package/dist/native.d.ts.map +0 -1
- package/dist/native.js +0 -123
- package/dist/native.js.map +0 -1
- package/dist/query/description.d.ts +0 -41
- package/dist/query/description.d.ts.map +0 -1
- package/dist/query/description.js +0 -311
- package/dist/query/description.js.map +0 -1
- package/dist/query/parse-ir.d.ts +0 -10
- package/dist/query/parse-ir.d.ts.map +0 -1
- package/dist/query/parse-ir.js +0 -280
- package/dist/query/parse-ir.js.map +0 -1
- package/dist/runtime-codes.d.ts +0 -2
- package/dist/runtime-codes.d.ts.map +0 -1
- package/dist/runtime-codes.js +0 -18
- package/dist/runtime-codes.js.map +0 -1
- package/dist/runtime-errors.d.ts +0 -118
- package/dist/runtime-errors.d.ts.map +0 -1
- package/dist/runtime-errors.js +0 -98
- package/dist/runtime-errors.js.map +0 -1
- package/dist/runtime-native.d.ts +0 -141
- package/dist/runtime-native.d.ts.map +0 -1
- package/dist/runtime-native.js +0 -5
- package/dist/runtime-native.js.map +0 -1
- package/pack-provenance.json +0 -7
- package/src/close.ts +0 -53
- package/src/db-native.ts +0 -243
- package/src/internal/log.ts +0 -48
- package/src/migration.ts +0 -61
- package/src/native.ts +0 -736
- package/src/query/description.ts +0 -424
- package/src/query/parse-ir.ts +0 -291
- package/src/runtime-codes.ts +0 -17
- package/src/runtime-errors.ts +0 -109
- package/src/runtime-native.ts +0 -189
package/src/db.ts
CHANGED
|
@@ -1,117 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The embedded engine surface. `Db.create`/`Db.open` own a database directory for the current
|
|
3
|
+
* scope; snapshots, prepared queries and results are scoped the same way, so every native
|
|
4
|
+
* resource is released by its scope and a release that does not drain cleanly is a
|
|
5
|
+
* `CloseFailure` defect. Every verb is a lazy Effect that runs on the shared native executor.
|
|
6
|
+
*/
|
|
1
7
|
import type { Scope } from "effect"
|
|
2
8
|
import { Effect, Option } from "effect"
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from "
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import
|
|
21
|
-
import type {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import
|
|
25
|
-
import type {
|
|
26
|
-
import
|
|
27
|
-
import {
|
|
28
|
-
import type {
|
|
29
|
-
import { factOfCells, keyCellsOf } from "#rows.ts"
|
|
30
|
-
import type { NativeRuntime } from "#runtime.ts"
|
|
31
|
-
import { nativeOperationWith, runtimeHandle } from "#runtime.ts"
|
|
32
|
-
import type { CloseReport } from "#runtime-errors.ts"
|
|
33
|
-
import { argumentError, DbError } from "#runtime-errors.ts"
|
|
34
|
-
import type { DirectoryHandle } from "#runtime-native.ts"
|
|
35
|
-
import { runtimeNative } from "#runtime-native.ts"
|
|
36
|
-
import type { AnySchema } from "#schema.ts"
|
|
37
|
-
import { schemaDescriptor, schemasAgree } from "#schema.ts"
|
|
38
|
-
import type { Key, QueryTemplate, Rel } from "#shape.ts"
|
|
39
|
-
import type { KeyStatement } from "#statements.ts"
|
|
40
|
-
import { integerValue, recordValue } from "#values.ts"
|
|
9
|
+
import type { ChangeSet } from "./changes.ts"
|
|
10
|
+
import { ChangeSetLive } from "./changes.ts"
|
|
11
|
+
import { membersAgree } from "./closed.ts"
|
|
12
|
+
import type { SchemaId } from "./compile.ts"
|
|
13
|
+
import { compiledOf, declaredKey, schemaTables } from "./compile.ts"
|
|
14
|
+
import { argumentError, DbError } from "./errors.ts"
|
|
15
|
+
import type { DbRef, DirectoryRef, PreparedRef, SnapshotRef, WitnessRef } from "./native/addon.ts"
|
|
16
|
+
import { addon } from "./native/addon.ts"
|
|
17
|
+
import type { ApplyOutcome, DbInspection as DbInspectionOut, DbOpened, JudgeOutcome } from "./native/binding.d.ts"
|
|
18
|
+
import type { Start } from "./native/op.ts"
|
|
19
|
+
import { call, drain, scoped } from "./native/op.ts"
|
|
20
|
+
import type { AnyQuery } from "./query/lower.ts"
|
|
21
|
+
import { queryHandleOf } from "./query/lower.ts"
|
|
22
|
+
import { wireParams } from "./query/run.ts"
|
|
23
|
+
import type { ParamsRecord } from "./query/scope.ts"
|
|
24
|
+
import type { Fact } from "./relation.ts"
|
|
25
|
+
import type { CompleteResult } from "./result.ts"
|
|
26
|
+
import { CompleteResultLive } from "./result.ts"
|
|
27
|
+
import type { CellValue } from "./rows.ts"
|
|
28
|
+
import { factOfCells, keyCellsOf } from "./rows.ts"
|
|
29
|
+
import type { Bumble } from "./runtime.ts"
|
|
30
|
+
import { runtimeHandle } from "./runtime.ts"
|
|
31
|
+
import type { AnySchema } from "./schema.ts"
|
|
32
|
+
import { schemaDescriptor, schemasAgree } from "./schema.ts"
|
|
33
|
+
import type { Key, QueryTemplate, Rel } from "./shape.ts"
|
|
34
|
+
import type { KeyStatement } from "./statements.ts"
|
|
41
35
|
|
|
42
36
|
/**
|
|
43
|
-
* The
|
|
44
|
-
*
|
|
45
|
-
* capability, one immutable final-state `apply`, bounded `inspect` and
|
|
46
|
-
* honest `close`. Effect-only: every method constructs a lazy effect; all
|
|
47
|
-
* native work runs on the shared executor with cooperative cancellation.
|
|
48
|
-
* Resources are scoped with `CloseFailure`-defect
|
|
49
|
-
* finalizers. There is no Promise/sync/disposal twin, no transaction
|
|
50
|
-
* callback, no per-row fiber and no Proxy row anywhere.
|
|
51
|
-
*
|
|
52
|
-
* Database open is the managed two-step handshake: acquire the kernel-held
|
|
53
|
-
* directory owner and register its finalizer before the interruptible
|
|
54
|
-
* child-open step. `Db.open` NEVER creates on missing/error; `Db.create`
|
|
55
|
-
* refuses existing authority. Published log snapshots use this same
|
|
56
|
-
* `QueryReader` literally — no second reader or adapter.
|
|
57
|
-
*/
|
|
58
|
-
|
|
59
|
-
/** The core-local witness: catalog/store identity plus generation — never a log StateStamp. */
|
|
60
|
-
interface CoreWitness {
|
|
61
|
-
readonly store: string
|
|
62
|
-
readonly generation: bigint
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
type WriteExpected = { readonly kind: "any" } | { readonly kind: "exact"; readonly at: CoreWitness }
|
|
66
|
-
|
|
67
|
-
/** Expected-state intent shared by judgment and application. */
|
|
68
|
-
type WriteOptions = { readonly expected: WriteExpected }
|
|
69
|
-
|
|
70
|
-
type ApplyOutcome =
|
|
71
|
-
| { readonly kind: "accepted"; readonly witness: CoreWitness }
|
|
72
|
-
| { readonly kind: "no-change"; readonly witness: CoreWitness }
|
|
73
|
-
| { readonly kind: "invariant-rejected"; readonly violations: readonly Violation[] }
|
|
74
|
-
| { readonly kind: "moved"; readonly witnessed: CoreWitness; readonly current: CoreWitness }
|
|
75
|
-
|
|
76
|
-
/** A judgment against the actual current base, not a promise about a later apply.
|
|
77
|
-
* A moved witness is unjudged and therefore carries no made-up change counts.
|
|
37
|
+
* The state one snapshot observed, as an opaque proof: a write given it applies only while that
|
|
38
|
+
* state is current. Only a snapshot mints one, and it is valid only for the database that did.
|
|
78
39
|
*/
|
|
79
|
-
type
|
|
80
|
-
| { readonly kind: "admitted"; readonly base: CoreWitness; readonly changes: ChangeCounts }
|
|
81
|
-
| {
|
|
82
|
-
readonly kind: "invariant-rejected"
|
|
83
|
-
readonly base: CoreWitness
|
|
84
|
-
readonly changes: ChangeCounts
|
|
85
|
-
readonly violations: readonly Violation[]
|
|
86
|
-
}
|
|
87
|
-
| { readonly kind: "moved"; readonly witnessed: CoreWitness; readonly current: CoreWitness }
|
|
88
|
-
|
|
89
|
-
/** Storage measurements, not heap usage or mapped-page residency. */
|
|
90
|
-
interface StorageInspection {
|
|
91
|
-
/** Reserved virtual address range for the LMDB mapping. */
|
|
92
|
-
readonly virtualMapBytes: bigint
|
|
93
|
-
/** File length; may include sparse regions and free pages. */
|
|
94
|
-
readonly populatedFileBytes: bigint
|
|
95
|
-
/** LMDB's non-free branch, leaf and overflow pages. Not resident RAM. */
|
|
96
|
-
readonly nonFreePageBytes: bigint
|
|
97
|
-
/** Allocated filesystem blocks, or null when the OS cannot report them. */
|
|
98
|
-
readonly allocatedDiskBytes: bigint | null
|
|
99
|
-
}
|
|
40
|
+
type Witness = WitnessRef
|
|
100
41
|
|
|
101
42
|
/** Database diagnostics: measurements, never retained rows. */
|
|
102
|
-
interface DbInspection {
|
|
43
|
+
interface DbInspection extends DbInspectionOut {
|
|
103
44
|
readonly schemaId: SchemaId
|
|
104
|
-
readonly generation: bigint
|
|
105
|
-
readonly storage: StorageInspection
|
|
106
|
-
readonly retainedOperations: bigint
|
|
107
45
|
}
|
|
108
46
|
|
|
109
47
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* cross-package read helper takes this interface with no adapter. Missing
|
|
113
|
-
* key is `Option.none`, never a fake I/O error or nullable row. It carries
|
|
114
|
-
* no writable authority — never a `Db`, an `apply`, or a raw transaction.
|
|
48
|
+
* Typed reads over one coherent snapshot. A missing key is `Option.none`. It carries no write
|
|
49
|
+
* authority.
|
|
115
50
|
*/
|
|
116
51
|
interface QueryReader<S extends AnySchema> {
|
|
117
52
|
get<K extends KeyStatement<Rel<S>, readonly string[]>>(
|
|
@@ -128,472 +63,323 @@ interface QueryReader<S extends AnySchema> {
|
|
|
128
63
|
}
|
|
129
64
|
|
|
130
65
|
interface Snapshot<S extends AnySchema> extends QueryReader<S> {
|
|
131
|
-
readonly witness:
|
|
132
|
-
|
|
66
|
+
readonly witness: Witness
|
|
67
|
+
/** The committed generation the snapshot observed. */
|
|
68
|
+
readonly generation: bigint
|
|
133
69
|
}
|
|
134
70
|
|
|
135
|
-
/** One compiled plan and its reusable buffers, pinned to the
|
|
136
|
-
* Closing it releases its own state, not the snapshot or completed results.
|
|
137
|
-
*/
|
|
71
|
+
/** One compiled plan and its reusable buffers, pinned to the snapshot that prepared it. */
|
|
138
72
|
interface PreparedQuery<P extends ParamsRecord, A> {
|
|
139
73
|
execute(params: P): Effect.Effect<CompleteResult<A>, DbError, Scope.Scope>
|
|
140
|
-
/**
|
|
74
|
+
/** Drops the reusable execution buffers, keeping the compiled plan. */
|
|
141
75
|
releaseMemory(): Effect.Effect<void, DbError>
|
|
142
|
-
close(): Effect.Effect<CloseReport>
|
|
143
76
|
}
|
|
144
77
|
|
|
145
78
|
interface Db<S extends AnySchema> {
|
|
146
79
|
readonly schemaId: SchemaId
|
|
147
80
|
snapshot(): Effect.Effect<Snapshot<S>, DbError, Scope.Scope>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
81
|
+
/** Applies `changes` atomically; with `expected`, only if the database is still at that state. */
|
|
82
|
+
apply(changes: ChangeSet<S>, expected?: Witness): Effect.Effect<ApplyOutcome, DbError>
|
|
83
|
+
/** Judges `changes` against the current state (or `expected`) and stores nothing. */
|
|
84
|
+
judge(changes: ChangeSet<S>, expected?: Witness): Effect.Effect<JudgeOutcome, DbError>
|
|
151
85
|
inspect(): Effect.Effect<DbInspection, DbError>
|
|
152
|
-
/**
|
|
86
|
+
/** Clears shared query caches without invalidating live snapshots or results. */
|
|
153
87
|
clearCache(): Effect.Effect<void, DbError>
|
|
154
|
-
close(): Effect.Effect<CloseReport>
|
|
155
88
|
}
|
|
156
89
|
|
|
157
|
-
function
|
|
158
|
-
return new DbError({ operation, reason: { _tag:
|
|
90
|
+
function invalid(operation: string): DbError {
|
|
91
|
+
return new DbError({ operation, reason: { _tag: "InvalidArgument" } })
|
|
159
92
|
}
|
|
160
93
|
|
|
161
|
-
|
|
162
|
-
|
|
94
|
+
/** A database the engine would not open: its refusal, as an engine error. */
|
|
95
|
+
function refused(operation: string, opened: Exclude<DbOpened, { _tag: "Opened" }>): DbError {
|
|
96
|
+
const message =
|
|
97
|
+
opened._tag === "Rejected" ? opened.violations.map((violation) => violation.spelling).join("; ") : opened.message
|
|
98
|
+
return new DbError({ operation, reason: { _tag: "Engine", kind: opened._tag, message } })
|
|
163
99
|
}
|
|
164
100
|
|
|
165
|
-
function
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return Object.freeze({ kind: "no-change", witness: witnessOf(wire.witness) })
|
|
171
|
-
case "invariant-rejected":
|
|
172
|
-
return Object.freeze({ kind: "invariant-rejected", violations: wire.violations })
|
|
173
|
-
case "moved":
|
|
174
|
-
return Object.freeze({ kind: "moved", witnessed: witnessOf(wire.witnessed), current: witnessOf(wire.current) })
|
|
175
|
-
}
|
|
101
|
+
function changesOf(schemaId: SchemaId, changes: object, operation: string) {
|
|
102
|
+
return Effect.suspend(() => {
|
|
103
|
+
const handle = ChangeSetLive.handle(changes, schemaId)
|
|
104
|
+
return handle === undefined ? Effect.fail(invalid(operation)) : Effect.succeed(handle)
|
|
105
|
+
})
|
|
176
106
|
}
|
|
177
107
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
108
|
+
/** A completed result owned by the current scope. */
|
|
109
|
+
function scopedResult<A>(
|
|
110
|
+
operation: string,
|
|
111
|
+
start: Start,
|
|
112
|
+
finds: AnyQuery["data"]["finds"]
|
|
113
|
+
): Effect.Effect<CompleteResult<A>, DbError, Scope.Scope> {
|
|
114
|
+
return Effect.map(
|
|
115
|
+
scoped(
|
|
116
|
+
"CompleteResult.release",
|
|
117
|
+
call(operation, start, addon.runtimeResultTake),
|
|
118
|
+
(handle) => (done) => addon.runtimeResultClose(handle, done)
|
|
119
|
+
),
|
|
120
|
+
(handle) => new CompleteResultLive<A>(handle, finds)
|
|
121
|
+
)
|
|
192
122
|
}
|
|
193
123
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const record = recordValue("write options", options, ["expected"])
|
|
202
|
-
const intent = recordValue(
|
|
203
|
-
"expected state",
|
|
204
|
-
record.expected,
|
|
205
|
-
options.expected.kind === "any" ? ["kind"] : ["kind", "at"]
|
|
206
|
-
)
|
|
207
|
-
let expected: ExpectedWire
|
|
208
|
-
if (intent.kind === "any") {
|
|
209
|
-
expected = { kind: "any" }
|
|
210
|
-
} else if (intent.kind === "exact") {
|
|
211
|
-
const at = recordValue("expected witness", intent.at, ["store", "generation"])
|
|
212
|
-
if (typeof at.store !== "string" || !/^[0-9a-f]{32}$/.test(at.store)) {
|
|
213
|
-
throw refusal(operation, "InvalidArgument")
|
|
214
|
-
}
|
|
215
|
-
expected = {
|
|
216
|
-
kind: "exact",
|
|
217
|
-
store: at.store,
|
|
218
|
-
generation: integerValue("expected generation", "u64", at.generation)
|
|
219
|
-
}
|
|
220
|
-
} else {
|
|
221
|
-
throw refusal(operation, "InvalidArgument")
|
|
222
|
-
}
|
|
223
|
-
return { handle: internal.handle, expected }
|
|
224
|
-
},
|
|
225
|
-
catch: (cause) => argumentError(operation, cause)
|
|
124
|
+
const getOn = Effect.fn("QueryReader.get")(function* <
|
|
125
|
+
S extends AnySchema,
|
|
126
|
+
K extends KeyStatement<Rel<S>, readonly string[]>
|
|
127
|
+
>(theory: S, handle: SnapshotRef, key: K, value: Key<K>) {
|
|
128
|
+
const resolved = yield* Effect.try({
|
|
129
|
+
try: () => declaredKey(theory, key),
|
|
130
|
+
catch: (cause) => argumentError("QueryReader.get", cause)
|
|
226
131
|
})
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
*
|
|
232
|
-
* engine's IR validation remains the authority.
|
|
233
|
-
*/
|
|
234
|
-
function preparedOf<S extends AnySchema>(
|
|
235
|
-
theory: S,
|
|
236
|
-
query: AnyQuery,
|
|
237
|
-
params: Readonly<Record<string, unknown>>
|
|
238
|
-
): {
|
|
239
|
-
readonly ir: ReturnType<typeof lowerQuery>
|
|
240
|
-
readonly wire: ReturnType<typeof wireParams>
|
|
241
|
-
readonly finds: AnyQuery["data"]["finds"]
|
|
242
|
-
} {
|
|
243
|
-
if (!schemasAgree(query.schema, theory)) {
|
|
244
|
-
throw refusal("QueryReader.execute", "InvalidArgument")
|
|
132
|
+
if (resolved === undefined || key.kind !== "key") return yield* Effect.fail(invalid("QueryReader.get"))
|
|
133
|
+
const relation = resolved.key.owner
|
|
134
|
+
const relationId = schemaTables(theory).relationIds.get(relation.name)
|
|
135
|
+
if (!membersAgree(theory.relations[relation.name], relation) || relationId === undefined) {
|
|
136
|
+
return yield* Effect.fail(invalid("QueryReader.get"))
|
|
245
137
|
}
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
138
|
+
const cells = yield* Effect.try({
|
|
139
|
+
try: () => keyCellsOf(relation, resolved.key.projection, value),
|
|
140
|
+
catch: (cause) => argumentError("QueryReader.get", cause)
|
|
141
|
+
})
|
|
142
|
+
const row = yield* call(
|
|
143
|
+
"QueryReader.get",
|
|
144
|
+
(done) => addon.runtimeSnapshotGet(handle, relationId, resolved.statementId, cells, done),
|
|
145
|
+
addon.runtimeRowTake
|
|
146
|
+
)
|
|
147
|
+
return row === null
|
|
148
|
+
? Option.none<Fact<K["owner"]>>()
|
|
149
|
+
: Option.some(factOfCells(relation, row as readonly CellValue[]) as Fact<K["owner"]>)
|
|
150
|
+
})
|
|
255
151
|
|
|
256
152
|
function executeOn<S extends AnySchema, A>(
|
|
257
|
-
|
|
153
|
+
theory: S,
|
|
154
|
+
handle: SnapshotRef,
|
|
258
155
|
query: AnyQuery,
|
|
259
156
|
params: Readonly<Record<string, unknown>>
|
|
260
157
|
): Effect.Effect<CompleteResult<A>, DbError, Scope.Scope> {
|
|
261
|
-
return scopedResult(
|
|
262
|
-
Effect.gen(function* () {
|
|
263
|
-
const prepared = yield* Effect.try({
|
|
264
|
-
try: () => preparedOf(state.theory, query, params),
|
|
265
|
-
catch: (cause) => argumentError("QueryReader.execute", cause)
|
|
266
|
-
})
|
|
267
|
-
const handle = yield* nativeOperationWith(
|
|
268
|
-
"QueryReader.execute",
|
|
269
|
-
(callback) => dbNative.runtimeSnapshotExecute(state.handle, prepared.ir, prepared.wire, callback),
|
|
270
|
-
dbNative.runtimeResultTake,
|
|
271
|
-
(value) => value
|
|
272
|
-
)
|
|
273
|
-
return makeCompleteResult<A>(handle, prepared.finds)
|
|
274
|
-
})
|
|
275
|
-
)
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function scopedResult<A>(acquire: Effect.Effect<CompleteResult<A>, DbError>) {
|
|
279
|
-
return Effect.acquireRelease(
|
|
280
|
-
acquire,
|
|
281
|
-
(result) =>
|
|
282
|
-
Effect.suspend(() => {
|
|
283
|
-
const internal = internalResultHandle(result)
|
|
284
|
-
if (internal === undefined) {
|
|
285
|
-
return Effect.void
|
|
286
|
-
}
|
|
287
|
-
return releaseOwner("CompleteResult.close", (callback) => dbNative.runtimeResultClose(internal, callback))
|
|
288
|
-
}),
|
|
289
|
-
{ interruptible: true }
|
|
290
|
-
)
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function internalResultHandle(result: object) {
|
|
294
|
-
return internalResult(result)?.handle
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function getOn<S extends AnySchema, K extends KeyStatement<Rel<S>, readonly string[]>>(
|
|
298
|
-
state: SnapshotState<S>,
|
|
299
|
-
key: K,
|
|
300
|
-
value: Key<K>
|
|
301
|
-
): Effect.Effect<Option.Option<Fact<K["owner"]>>, DbError> {
|
|
302
158
|
return Effect.gen(function* () {
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
return yield* Effect.fail(refusal("QueryReader.get", "InvalidArgument"))
|
|
310
|
-
}
|
|
311
|
-
const { statementId } = resolved
|
|
312
|
-
const relation = resolved.key.owner
|
|
313
|
-
if (!membersAgree(state.theory.relations[relation.name], relation)) {
|
|
314
|
-
return yield* Effect.fail(refusal("QueryReader.get", "InvalidArgument"))
|
|
315
|
-
}
|
|
316
|
-
const relationId = tables.relationIds.get(relation.name)
|
|
317
|
-
if (relationId === undefined) {
|
|
318
|
-
return yield* Effect.fail(refusal("QueryReader.get", "InvalidArgument"))
|
|
319
|
-
}
|
|
320
|
-
const cells = yield* Effect.try({
|
|
321
|
-
try: () => keyCellsOf(relation, resolved.key.projection, value),
|
|
322
|
-
catch: (cause) => argumentError("QueryReader.get", cause)
|
|
159
|
+
const prepared = yield* Effect.try({
|
|
160
|
+
try: () => {
|
|
161
|
+
if (!schemasAgree(query.schema, theory)) throw invalid("QueryReader.execute")
|
|
162
|
+
return { query: queryHandleOf(query), wire: wireParams(query.data.params, params) }
|
|
163
|
+
},
|
|
164
|
+
catch: (cause) => argumentError("QueryReader.execute", cause)
|
|
323
165
|
})
|
|
324
|
-
|
|
325
|
-
"QueryReader.
|
|
326
|
-
(
|
|
327
|
-
|
|
328
|
-
(value) => value
|
|
166
|
+
return yield* scopedResult<A>(
|
|
167
|
+
"QueryReader.execute",
|
|
168
|
+
(done) => addon.runtimeSnapshotExecute(handle, prepared.query, prepared.wire, done),
|
|
169
|
+
query.data.finds
|
|
329
170
|
)
|
|
330
|
-
|
|
331
|
-
return Option.none<Fact<K["owner"]>>()
|
|
332
|
-
}
|
|
333
|
-
return Option.some(factOfCells(relation, row as readonly CellValue[]))
|
|
334
|
-
})
|
|
171
|
+
}).pipe(Effect.withSpan("QueryReader.execute"))
|
|
335
172
|
}
|
|
336
173
|
|
|
337
|
-
|
|
338
|
-
handle:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const result = yield* nativeOperationWith(
|
|
359
|
-
"PreparedQuery.execute",
|
|
360
|
-
(callback) => dbNative.runtimePreparedExecute(handle, args, callback),
|
|
361
|
-
dbNative.runtimeResultTake,
|
|
362
|
-
(value) => makeCompleteResult<A>(value, finds)
|
|
363
|
-
)
|
|
364
|
-
return result
|
|
365
|
-
})
|
|
174
|
+
class PreparedQueryLive<P extends ParamsRecord, A> implements PreparedQuery<P, A> {
|
|
175
|
+
readonly #handle: PreparedRef
|
|
176
|
+
readonly #query: AnyQuery
|
|
177
|
+
|
|
178
|
+
constructor(handle: PreparedRef, query: AnyQuery) {
|
|
179
|
+
this.#handle = handle
|
|
180
|
+
this.#query = query
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
execute(params: P): Effect.Effect<CompleteResult<A>, DbError, Scope.Scope> {
|
|
184
|
+
const handle = this.#handle
|
|
185
|
+
const query = this.#query
|
|
186
|
+
return Effect.gen(function* () {
|
|
187
|
+
const args = yield* Effect.try({
|
|
188
|
+
try: () => wireParams(query.data.params, params),
|
|
189
|
+
catch: (cause) => argumentError("PreparedQuery.execute", cause)
|
|
190
|
+
})
|
|
191
|
+
return yield* scopedResult<A>(
|
|
192
|
+
"PreparedQuery.execute",
|
|
193
|
+
(done) => addon.runtimePreparedExecute(handle, args, done),
|
|
194
|
+
query.data.finds
|
|
366
195
|
)
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
196
|
+
}).pipe(Effect.withSpan("PreparedQuery.execute"))
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
releaseMemory(): Effect.Effect<void, DbError> {
|
|
200
|
+
const handle = this.#handle
|
|
201
|
+
return call(
|
|
202
|
+
"PreparedQuery.releaseMemory",
|
|
203
|
+
(done) => addon.runtimePreparedReleaseMemory(handle, done),
|
|
204
|
+
addon.runtimeTake
|
|
205
|
+
).pipe(Effect.withSpan("PreparedQuery.releaseMemory"))
|
|
206
|
+
}
|
|
372
207
|
}
|
|
373
208
|
|
|
374
209
|
function prepareOn<S extends AnySchema, P extends ParamsRecord, A>(
|
|
375
|
-
|
|
210
|
+
theory: S,
|
|
211
|
+
handle: SnapshotRef,
|
|
376
212
|
query: QueryTemplate<S, P, A>
|
|
377
213
|
): Effect.Effect<PreparedQuery<P, A>, DbError, Scope.Scope> {
|
|
378
214
|
return Effect.gen(function* () {
|
|
379
215
|
const ir = yield* Effect.try({
|
|
380
216
|
try: () => {
|
|
381
|
-
if (!schemasAgree(query.schema,
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
return lowerQuery(query)
|
|
217
|
+
if (!schemasAgree(query.schema, theory)) throw invalid("QueryReader.prepare")
|
|
218
|
+
return queryHandleOf(query as AnyQuery)
|
|
385
219
|
},
|
|
386
220
|
catch: (cause) => argumentError("QueryReader.prepare", cause)
|
|
387
221
|
})
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
dbNative.runtimePreparedTake,
|
|
393
|
-
(value) => value
|
|
394
|
-
),
|
|
395
|
-
(value) => releaseOwner("PreparedQuery.close", (callback) => dbNative.runtimePreparedClose(value, callback)),
|
|
396
|
-
{ interruptible: true }
|
|
222
|
+
const prepared = yield* scoped(
|
|
223
|
+
"PreparedQuery.release",
|
|
224
|
+
call("QueryReader.prepare", (done) => addon.runtimeSnapshotPrepare(handle, ir, done), addon.runtimePreparedTake),
|
|
225
|
+
(owned) => (done) => addon.runtimePreparedClose(owned, done)
|
|
397
226
|
)
|
|
398
|
-
return
|
|
399
|
-
})
|
|
227
|
+
return new PreparedQueryLive<P, A>(prepared, query)
|
|
228
|
+
}).pipe(Effect.withSpan("QueryReader.prepare"))
|
|
400
229
|
}
|
|
401
230
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
231
|
+
class SnapshotLive<S extends AnySchema> implements Snapshot<S> {
|
|
232
|
+
readonly #theory: S
|
|
233
|
+
readonly #handle: SnapshotRef
|
|
234
|
+
readonly witness: Witness
|
|
235
|
+
readonly generation: bigint
|
|
236
|
+
|
|
237
|
+
constructor(theory: S, handle: SnapshotRef, witness: Witness, generation: bigint) {
|
|
238
|
+
this.#theory = theory
|
|
239
|
+
this.#handle = handle
|
|
240
|
+
this.witness = witness
|
|
241
|
+
this.generation = generation
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
get<K extends KeyStatement<Rel<S>, readonly string[]>>(
|
|
245
|
+
key: K,
|
|
246
|
+
value: NoInfer<Key<K>>
|
|
247
|
+
): Effect.Effect<Option.Option<Fact<K["owner"]>>, DbError> {
|
|
248
|
+
return getOn(this.#theory, this.#handle, key, value)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
execute<P extends ParamsRecord, A>(
|
|
252
|
+
query: QueryTemplate<S, P, A>,
|
|
253
|
+
params: P
|
|
254
|
+
): Effect.Effect<CompleteResult<A>, DbError, Scope.Scope> {
|
|
255
|
+
return executeOn<S, A>(this.#theory, this.#handle, query as AnyQuery, params)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
prepare<P extends ParamsRecord, A>(
|
|
259
|
+
query: QueryTemplate<S, P, A>
|
|
260
|
+
): Effect.Effect<PreparedQuery<P, A>, DbError, Scope.Scope> {
|
|
261
|
+
return prepareOn(this.#theory, this.#handle, query)
|
|
418
262
|
}
|
|
419
|
-
Object.freeze(snapshot)
|
|
420
|
-
snapshotHandles.set(snapshot, handle)
|
|
421
|
-
return snapshot
|
|
422
263
|
}
|
|
423
264
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
265
|
+
/** A snapshot `start` pins, read through `theory` and released by the current scope. */
|
|
266
|
+
function scopedSnapshot<S extends AnySchema>(
|
|
267
|
+
operation: string,
|
|
268
|
+
theory: S,
|
|
269
|
+
start: Start
|
|
270
|
+
): Effect.Effect<Snapshot<S>, DbError, Scope.Scope> {
|
|
271
|
+
return Effect.map(
|
|
272
|
+
scoped(
|
|
273
|
+
"Snapshot.release",
|
|
274
|
+
call(operation, start, addon.runtimeSnapshotTake),
|
|
275
|
+
(opened) => (done) => addon.runtimeSnapshotClose(opened.snapshot, done)
|
|
276
|
+
),
|
|
277
|
+
(opened): Snapshot<S> => new SnapshotLive(theory, opened.snapshot, opened.witness, opened.generation)
|
|
278
|
+
)
|
|
429
279
|
}
|
|
430
280
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
)
|
|
441
|
-
},
|
|
442
|
-
snapshot() {
|
|
443
|
-
return Effect.acquireRelease(
|
|
444
|
-
nativeOperationWith(
|
|
445
|
-
"Db.snapshot",
|
|
446
|
-
(callback) => dbNative.runtimeDbSnapshot(state.db, callback),
|
|
447
|
-
dbNative.runtimeSnapshotTake,
|
|
448
|
-
(wire) => makeSnapshot(theory, wire.snapshot, witnessOf(wire.witness))
|
|
449
|
-
),
|
|
450
|
-
(snapshot) =>
|
|
451
|
-
Effect.suspend(() =>
|
|
452
|
-
releaseOwner("Snapshot.close", (callback) =>
|
|
453
|
-
dbNative.runtimeSnapshotClose(snapshotHandles.get(snapshot) ?? missingSnapshot(), callback)
|
|
454
|
-
)
|
|
455
|
-
),
|
|
456
|
-
{ interruptible: true }
|
|
457
|
-
)
|
|
458
|
-
},
|
|
459
|
-
apply(changes, options) {
|
|
460
|
-
return Effect.gen(function* () {
|
|
461
|
-
const input = yield* writeInputs(state, changes, options, "Db.apply")
|
|
462
|
-
return yield* nativeOperationWith(
|
|
463
|
-
"Db.apply",
|
|
464
|
-
(callback) => dbNative.runtimeDbApply(state.db, input.handle, input.expected, callback),
|
|
465
|
-
dbNative.runtimeApplyTake,
|
|
466
|
-
outcomeOf
|
|
467
|
-
)
|
|
468
|
-
})
|
|
469
|
-
},
|
|
470
|
-
judge(changes, options) {
|
|
471
|
-
return Effect.gen(function* () {
|
|
472
|
-
const input = yield* writeInputs(state, changes, options, "Db.judge")
|
|
473
|
-
return yield* nativeOperationWith(
|
|
474
|
-
"Db.judge",
|
|
475
|
-
(callback) => dbNative.runtimeDbJudge(state.db, input.handle, input.expected, callback),
|
|
476
|
-
dbNative.runtimeJudgeTake,
|
|
477
|
-
judgmentOf
|
|
478
|
-
)
|
|
479
|
-
})
|
|
480
|
-
},
|
|
481
|
-
inspect() {
|
|
482
|
-
return nativeOperationWith(
|
|
483
|
-
"Db.inspect",
|
|
484
|
-
(callback) => dbNative.runtimeDbInspect(state.db, callback),
|
|
485
|
-
dbNative.runtimeDbInspectTake,
|
|
486
|
-
(wire: DbInspectionWire): DbInspection =>
|
|
487
|
-
Object.freeze({
|
|
488
|
-
schemaId: state.schemaId,
|
|
489
|
-
generation: wire.generation,
|
|
490
|
-
storage: Object.freeze(wire.storage),
|
|
491
|
-
retainedOperations: wire.retainedOperations
|
|
492
|
-
})
|
|
493
|
-
)
|
|
494
|
-
},
|
|
495
|
-
close() {
|
|
496
|
-
// The one close authority: database child first, then the
|
|
497
|
-
// directory owner releases its kernel lock LAST. Both
|
|
498
|
-
// joins are idempotent natively.
|
|
499
|
-
return drainClose("Db.close", (callback) => runtimeNative.runtimeManagedDbClose(state.db, callback)).pipe(
|
|
500
|
-
Effect.flatMap((report) =>
|
|
501
|
-
drainClose("Db.directoryClose", (callback) =>
|
|
502
|
-
runtimeNative.runtimeDirectoryClose(state.directory, false, callback)
|
|
503
|
-
).pipe(Effect.map((directoryReport) => (report.kind === "closed" ? directoryReport : report)))
|
|
504
|
-
)
|
|
505
|
-
)
|
|
506
|
-
}
|
|
281
|
+
class DbLive<S extends AnySchema> implements Db<S> {
|
|
282
|
+
readonly #theory: S
|
|
283
|
+
readonly #handle: DbRef
|
|
284
|
+
readonly schemaId: SchemaId
|
|
285
|
+
|
|
286
|
+
constructor(theory: S, handle: DbRef, schemaId: SchemaId) {
|
|
287
|
+
this.#theory = theory
|
|
288
|
+
this.#handle = handle
|
|
289
|
+
this.schemaId = schemaId
|
|
507
290
|
}
|
|
508
|
-
Object.freeze(value)
|
|
509
|
-
return value
|
|
510
|
-
}
|
|
511
291
|
|
|
512
|
-
|
|
292
|
+
snapshot(): Effect.Effect<Snapshot<S>, DbError, Scope.Scope> {
|
|
293
|
+
const handle = this.#handle
|
|
294
|
+
return scopedSnapshot("Db.snapshot", this.#theory, (done) => addon.runtimeDbSnapshot(handle, done)).pipe(
|
|
295
|
+
Effect.withSpan("Db.snapshot")
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
apply(changes: ChangeSet<S>, expected?: Witness): Effect.Effect<ApplyOutcome, DbError> {
|
|
300
|
+
const handle = this.#handle
|
|
301
|
+
return changesOf(this.schemaId, changes, "Db.apply").pipe(
|
|
302
|
+
Effect.flatMap((owned) =>
|
|
303
|
+
call("Db.apply", (done) => addon.runtimeDbApply(handle, owned, expected ?? null, done), addon.runtimeApplyTake)
|
|
304
|
+
),
|
|
305
|
+
Effect.withSpan("Db.apply")
|
|
306
|
+
)
|
|
307
|
+
}
|
|
513
308
|
|
|
514
|
-
|
|
515
|
-
|
|
309
|
+
judge(changes: ChangeSet<S>, expected?: Witness): Effect.Effect<JudgeOutcome, DbError> {
|
|
310
|
+
const handle = this.#handle
|
|
311
|
+
return changesOf(this.schemaId, changes, "Db.judge").pipe(
|
|
312
|
+
Effect.flatMap((owned) =>
|
|
313
|
+
call("Db.judge", (done) => addon.runtimeDbJudge(handle, owned, expected ?? null, done), addon.runtimeJudgeTake)
|
|
314
|
+
),
|
|
315
|
+
Effect.withSpan("Db.judge")
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
inspect(): Effect.Effect<DbInspection, DbError> {
|
|
320
|
+
const handle = this.#handle
|
|
321
|
+
const schemaId = this.schemaId
|
|
322
|
+
return call(
|
|
323
|
+
"Db.inspect",
|
|
324
|
+
(done) => addon.runtimeDbInspect(handle, done),
|
|
325
|
+
(lease): DbInspection => Object.freeze({ ...addon.runtimeDbInspectTake(lease), schemaId })
|
|
326
|
+
).pipe(Effect.withSpan("Db.inspect"))
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
clearCache(): Effect.Effect<void, DbError> {
|
|
330
|
+
const handle = this.#handle
|
|
331
|
+
return call("Db.clearCache", (done) => addon.runtimeDbClearCache(handle, done), addon.runtimeTake).pipe(
|
|
332
|
+
Effect.withSpan("Db.clearCache")
|
|
333
|
+
)
|
|
334
|
+
}
|
|
516
335
|
}
|
|
517
336
|
|
|
518
|
-
/** The
|
|
519
|
-
const CHILD = "store"
|
|
337
|
+
/** The database's child directory inside the owned directory. */
|
|
338
|
+
const CHILD = "store.bdb"
|
|
339
|
+
|
|
340
|
+
const closeDirectory = (directory: DirectoryRef) => (done: Parameters<typeof addon.runtimeDirectoryClose>[2]) =>
|
|
341
|
+
addon.runtimeDirectoryClose(directory, false, done)
|
|
520
342
|
|
|
521
|
-
|
|
343
|
+
/**
|
|
344
|
+
* Takes the directory lock, then opens the database inside it. Both are owned by the current
|
|
345
|
+
* scope, which releases the database before the directory. A failed open releases the
|
|
346
|
+
* directory at once rather than when the scope ends.
|
|
347
|
+
*/
|
|
348
|
+
const openDatabase = <S extends AnySchema>(
|
|
522
349
|
operation: "Db.create" | "Db.open",
|
|
523
350
|
path: string,
|
|
524
351
|
schema: S,
|
|
525
352
|
create: boolean
|
|
526
|
-
): Effect.Effect<Db<S>, DbError,
|
|
527
|
-
|
|
528
|
-
const runtime = yield* runtimeHandle
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
(value) => value
|
|
539
|
-
),
|
|
540
|
-
(dir) =>
|
|
541
|
-
releaseOwner(`${operation}.directoryClose`, (callback) =>
|
|
542
|
-
runtimeNative.runtimeDirectoryClose(dir, false, callback)
|
|
543
|
-
),
|
|
544
|
-
{ interruptible: true }
|
|
353
|
+
): Effect.Effect<Db<S>, DbError, Bumble | Scope.Scope> =>
|
|
354
|
+
Effect.gen(function* () {
|
|
355
|
+
const runtime = yield* runtimeHandle
|
|
356
|
+
const theory = yield* Effect.try({
|
|
357
|
+
try: () => schemaDescriptor(schema),
|
|
358
|
+
catch: (cause) => argumentError(operation, cause)
|
|
359
|
+
})
|
|
360
|
+
const compiled = compiledOf(theory)
|
|
361
|
+
const directory = yield* scoped(
|
|
362
|
+
"Db.directoryRelease",
|
|
363
|
+
call(operation, (done) => addon.runtimeDirectoryAcquire(runtime, path, done), addon.runtimeDirectoryTake),
|
|
364
|
+
closeDirectory
|
|
545
365
|
)
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
drainClose(`${operation}.directoryClose`, (callback) =>
|
|
556
|
-
runtimeNative.runtimeDirectoryClose(directory, false, callback)
|
|
557
|
-
).pipe(Effect.andThen(Effect.fail(error)))
|
|
558
|
-
)
|
|
559
|
-
)
|
|
560
|
-
if (outcome.tag !== "accepted") {
|
|
561
|
-
yield* drainClose(`${operation}.directoryClose`, (callback) =>
|
|
562
|
-
runtimeNative.runtimeDirectoryClose(directory, false, callback)
|
|
563
|
-
)
|
|
564
|
-
return yield* Effect.fail(refusal(operation, "InvalidArgument"))
|
|
565
|
-
}
|
|
566
|
-
const state: DbState = { theory: compiled.schema, db: outcome.db, directory, schemaId: compiled.schemaId }
|
|
567
|
-
const db = makeDb(compiled.schema, state)
|
|
568
|
-
dbStates.set(db, state)
|
|
569
|
-
return db
|
|
570
|
-
}),
|
|
571
|
-
(db) =>
|
|
572
|
-
Effect.suspend(() => {
|
|
573
|
-
const state = dbStates.get(db)
|
|
574
|
-
if (state === undefined) {
|
|
575
|
-
return Effect.void
|
|
576
|
-
}
|
|
577
|
-
return releaseOwner("Db.close", (callback) => runtimeNative.runtimeManagedDbClose(state.db, callback)).pipe(
|
|
578
|
-
Effect.ensuring(
|
|
579
|
-
releaseOwner("Db.directoryClose", (callback) =>
|
|
580
|
-
runtimeNative.runtimeDirectoryClose(state.directory, false, callback)
|
|
581
|
-
)
|
|
582
|
-
)
|
|
583
|
-
)
|
|
584
|
-
}),
|
|
585
|
-
{ interruptible: true }
|
|
366
|
+
const opened = call(
|
|
367
|
+
operation,
|
|
368
|
+
(done) => addon.runtimeDirectoryDbOpen(directory, CHILD, compiled.handle, create, done),
|
|
369
|
+
addon.runtimeDbTake
|
|
370
|
+
).pipe(
|
|
371
|
+
Effect.flatMap((opened) =>
|
|
372
|
+
opened._tag === "Opened" ? Effect.succeed(opened.db) : Effect.fail(refused(operation, opened))
|
|
373
|
+
),
|
|
374
|
+
Effect.tapError(() => drain(closeDirectory(directory)))
|
|
586
375
|
)
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const dbStates = new WeakMap<object, DbState>()
|
|
376
|
+
const handle = yield* scoped("Db.release", opened, (db) => (done) => addon.runtimeManagedDbClose(db, done))
|
|
377
|
+
return new DbLive(theory as S, handle, compiled.schemaId) as Db<S>
|
|
378
|
+
}).pipe(Effect.withSpan(operation))
|
|
591
379
|
|
|
592
380
|
/**
|
|
593
|
-
* `Db.create`
|
|
594
|
-
*
|
|
595
|
-
* replacement. Both compile the schema through the same
|
|
596
|
-
* implementation as `Schema.compile` — prior compilation is optional.
|
|
381
|
+
* `Db.create` makes a new database and refuses an existing one; `Db.open` opens an existing one
|
|
382
|
+
* and never creates. Both compile the schema first.
|
|
597
383
|
*/
|
|
598
384
|
const Db = Object.freeze({
|
|
599
385
|
create<S extends AnySchema>(path: string, schema: S) {
|
|
@@ -604,49 +390,6 @@ const Db = Object.freeze({
|
|
|
604
390
|
}
|
|
605
391
|
})
|
|
606
392
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
* — in the exact core `QueryReader` plus the scoped session acquisition
|
|
611
|
-
* (the log adds identity, stamps and freshness around this capability,
|
|
612
|
-
* never a second reader).
|
|
613
|
-
* The argument is the log package's branded handle for the same native
|
|
614
|
-
* registry entry, so the one cast below is a cross-package respelling of
|
|
615
|
-
* one native capability — the native side re-judges kind/generation/owner
|
|
616
|
-
* on every verb, so a forged object refuses there, typed.
|
|
617
|
-
*/
|
|
618
|
-
function internalPublishedReader<S extends AnySchema>(
|
|
619
|
-
core: object,
|
|
620
|
-
theory: S
|
|
621
|
-
): {
|
|
622
|
-
readonly get: QueryReader<S>["get"]
|
|
623
|
-
readonly execute: QueryReader<S>["execute"]
|
|
624
|
-
readonly prepare: QueryReader<S>["prepare"]
|
|
625
|
-
} {
|
|
626
|
-
const state: SnapshotState<S> = { theory: schemaDescriptor(theory), handle: core as SnapshotHandle }
|
|
627
|
-
return Object.freeze({
|
|
628
|
-
get<K extends KeyStatement<Rel<S>, readonly string[]>>(key: K, value: NoInfer<Key<K>>) {
|
|
629
|
-
return getOn(state, key, value)
|
|
630
|
-
},
|
|
631
|
-
execute<P extends ParamsRecord, A>(queryValue: QueryTemplate<S, P, A>, params: P) {
|
|
632
|
-
return executeOn<S, A>(state, queryValue as AnyQuery, params)
|
|
633
|
-
},
|
|
634
|
-
prepare<P extends ParamsRecord, A>(queryValue: QueryTemplate<S, P, A>) {
|
|
635
|
-
return prepareOn(state, queryValue)
|
|
636
|
-
}
|
|
637
|
-
})
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
export type {
|
|
641
|
-
ApplyOutcome,
|
|
642
|
-
CoreWitness,
|
|
643
|
-
DbInspection,
|
|
644
|
-
JudgeOutcome,
|
|
645
|
-
PreparedQuery,
|
|
646
|
-
QueryReader,
|
|
647
|
-
Snapshot,
|
|
648
|
-
StorageInspection,
|
|
649
|
-
WriteExpected,
|
|
650
|
-
WriteOptions
|
|
651
|
-
}
|
|
652
|
-
export { Db, internalPublishedReader }
|
|
393
|
+
export type { ApplyOutcome, JudgeOutcome } from "./native/binding.d.ts"
|
|
394
|
+
export type { DbInspection, PreparedQuery, QueryReader, Snapshot, Witness }
|
|
395
|
+
export { Db, scopedSnapshot }
|