@bjornpagen/bumbledb 1.3.1 → 2.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 +153 -77
- package/README.md +107 -41
- 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 -40
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +86 -104
- 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 +13 -27
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +20 -21
- 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 -57
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -28
- 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 +4 -4
- package/dist/query/compute.d.ts.map +1 -1
- package/dist/query/compute.js +4 -4
- 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 +22 -17
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +230 -343
- 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 +7 -7
- 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 -20
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +41 -205
- package/dist/runtime.js.map +1 -1
- package/dist/scalar.d.ts +4 -4
- package/dist/scalar.d.ts.map +1 -1
- package/dist/scalar.js +24 -8
- 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 -191
- package/src/closed.ts +5 -5
- package/src/codec.ts +35 -49
- 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 +92 -105
- 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 +7 -7
- package/src/query/find.ts +8 -8
- package/src/query/lower.ts +270 -392
- package/src/query/run.ts +42 -21
- package/src/query/scope.ts +29 -37
- 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 -277
- package/src/scalar.ts +32 -12
- 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 -189
- 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/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 -19
- package/dist/runtime-codes.js.map +0 -1
- package/dist/runtime-errors.d.ts +0 -138
- package/dist/runtime-errors.d.ts.map +0 -1
- package/dist/runtime-errors.js +0 -111
- package/dist/runtime-errors.js.map +0 -1
- package/dist/runtime-native.d.ts +0 -136
- 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/dist/schema-file.d.ts +0 -7
- package/dist/schema-file.d.ts.map +0 -1
- package/dist/schema-file.js +0 -24
- package/dist/schema-file.js.map +0 -1
- package/pack-provenance.json +0 -7
- package/src/close.ts +0 -53
- package/src/db-native.ts +0 -235
- package/src/internal/log.ts +0 -46
- package/src/native.ts +0 -736
- package/src/query/description.ts +0 -424
- package/src/query/parse-ir.ts +0 -293
- package/src/runtime-codes.ts +0 -18
- package/src/runtime-errors.ts +0 -122
- package/src/runtime-native.ts +0 -181
- package/src/schema-file.ts +0 -38
package/COOKBOOK.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
Worked recipes for the Effect-native surface: typed schema values,
|
|
4
4
|
declared keys and laws, application-owned `Uuid` identity, one bounded
|
|
5
5
|
native runtime, scoped resources, immutable final-state changes, `Option`
|
|
6
|
-
reads, sealed complete results,
|
|
6
|
+
reads, sealed complete results, one-shot page streams, and durable
|
|
7
|
+
`Database`s over an object store. Recipes 1 to 18 use the embedded `Db` from
|
|
8
|
+
`@bjornpagen/bumbledb/engine`; recipes 19 and 20 use `Database`.
|
|
7
9
|
|
|
8
10
|
Every `ts` fence below is extracted and type-checked against the real
|
|
9
11
|
package surface by `test/cookbook-doc.test.ts`; the imports fence here is
|
|
@@ -11,7 +13,7 @@ prepended to every recipe. The examples are lazy Effect programs — nothing
|
|
|
11
13
|
below runs a database at import time.
|
|
12
14
|
|
|
13
15
|
```ts
|
|
14
|
-
import { Effect, Option, Result, Schema as EffectSchema, Stream } from "effect"
|
|
16
|
+
import { Effect, Layer, Option, Result, Schema as EffectSchema, Stream } from "effect"
|
|
15
17
|
import {
|
|
16
18
|
alternatives,
|
|
17
19
|
bool,
|
|
@@ -21,25 +23,25 @@ import {
|
|
|
21
23
|
closedId,
|
|
22
24
|
Compute,
|
|
23
25
|
contained,
|
|
24
|
-
|
|
26
|
+
Database,
|
|
25
27
|
decodeBoundaryField,
|
|
26
|
-
describeQuery,
|
|
27
28
|
duration,
|
|
28
29
|
encodeBoundaryField,
|
|
29
30
|
f64,
|
|
30
31
|
fieldSchema,
|
|
32
|
+
FsStore,
|
|
31
33
|
i64,
|
|
32
34
|
uuid,
|
|
33
35
|
Uuid,
|
|
34
36
|
interval,
|
|
35
37
|
key,
|
|
36
38
|
mirrors,
|
|
37
|
-
|
|
39
|
+
Bumble,
|
|
38
40
|
on,
|
|
39
41
|
query,
|
|
40
|
-
queryFromDescription,
|
|
41
42
|
ref,
|
|
42
43
|
relation,
|
|
44
|
+
S3Store,
|
|
43
45
|
schema,
|
|
44
46
|
select,
|
|
45
47
|
str,
|
|
@@ -48,14 +50,16 @@ import {
|
|
|
48
50
|
weigh,
|
|
49
51
|
within
|
|
50
52
|
} from "@bjornpagen/bumbledb"
|
|
53
|
+
import { Db } from "@bjornpagen/bumbledb/engine"
|
|
51
54
|
import type {
|
|
52
|
-
ApplyOutcome,
|
|
53
55
|
CompleteResult,
|
|
54
56
|
Fact,
|
|
55
57
|
FloatIntervalValue,
|
|
56
58
|
IntervalValue,
|
|
57
|
-
|
|
59
|
+
Migrations,
|
|
60
|
+
S3Sender
|
|
58
61
|
} from "@bjornpagen/bumbledb"
|
|
62
|
+
import type { ApplyOutcome, QueryReader } from "@bjornpagen/bumbledb/engine"
|
|
59
63
|
|
|
60
64
|
declare const localPath: string
|
|
61
65
|
```
|
|
@@ -99,7 +103,7 @@ void [Learning, scoreIsNumber, unitsAreExact]
|
|
|
99
103
|
|
|
100
104
|
## 2. One runtime layer; explicit create and open
|
|
101
105
|
|
|
102
|
-
`
|
|
106
|
+
`Bumble.layer()` acquires the single native runtime
|
|
103
107
|
with scope; reuse ONE layer value so Effect's memoization shares it.
|
|
104
108
|
Optional configuration controls workers, outstanding jobs/handles, and cleanup
|
|
105
109
|
reporting. Defaults are up to four workers, 128 queued jobs, 128 cleanup reports,
|
|
@@ -107,10 +111,8 @@ reporting. Defaults are up to four workers, 128 queued jobs, 128 cleanup reports
|
|
|
107
111
|
Operations use ordinary allocation without byte/row/work quotas or execution
|
|
108
112
|
deadlines. Effect interruption requests cooperative cancellation. Runtime
|
|
109
113
|
inspection reports outstanding work, not memory usage.
|
|
110
|
-
Database `inspect()
|
|
111
|
-
|
|
112
|
-
None of these measures process heap usage or resident RAM. Log history
|
|
113
|
-
inspection uses the same `StorageInspection` fields.
|
|
114
|
+
Database `inspect()` reports the generation, the database's bytes on disk and
|
|
115
|
+
the native operations it retains; none of these measures heap or resident RAM.
|
|
114
116
|
`Db.open` never creates a missing database; `Db.create` refuses existing
|
|
115
117
|
authority. Both are scoped acquisitions.
|
|
116
118
|
|
|
@@ -133,7 +135,7 @@ const createOnce = Effect.scoped(
|
|
|
133
135
|
)
|
|
134
136
|
|
|
135
137
|
// One boundary; an Effect app provides the layer in its own graph instead.
|
|
136
|
-
const layer =
|
|
138
|
+
const layer = Bumble.layer()
|
|
137
139
|
void [openExisting.pipe(Effect.provide(layer)), createOnce.pipe(Effect.provide(layer))]
|
|
138
140
|
```
|
|
139
141
|
|
|
@@ -157,15 +159,14 @@ const applyOnce = Effect.scoped(
|
|
|
157
159
|
const draft = yield* ChangeSet.builder(Tasks)
|
|
158
160
|
yield* draft.insert(Task, [{ id: taskId, title: "write the cookbook", done: 0n }])
|
|
159
161
|
const changes = yield* draft.finish()
|
|
160
|
-
const outcome: ApplyOutcome = yield* db.apply(changes
|
|
161
|
-
switch (outcome.
|
|
162
|
-
case "
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
case "invariant-rejected":
|
|
162
|
+
const outcome: ApplyOutcome = yield* db.apply(changes)
|
|
163
|
+
switch (outcome._tag) {
|
|
164
|
+
case "Committed":
|
|
165
|
+
return outcome.generation
|
|
166
|
+
case "Rejected":
|
|
166
167
|
// Complete statement diagnostics, typed data — never a throw.
|
|
167
168
|
return yield* Effect.fail(outcome.violations)
|
|
168
|
-
case "
|
|
169
|
+
case "Moved":
|
|
169
170
|
return outcome.current
|
|
170
171
|
}
|
|
171
172
|
})
|
|
@@ -175,9 +176,9 @@ void applyOnce
|
|
|
175
176
|
|
|
176
177
|
For a noncommitting check, `db.judge` runs the same admission procedure and
|
|
177
178
|
aborts its private candidate on the native worker. It briefly takes the
|
|
178
|
-
writer; it does not hold a writer session open in JavaScript.
|
|
179
|
-
|
|
180
|
-
Operational errors stay in the Effect error channel.
|
|
179
|
+
writer; it does not hold a writer session open in JavaScript. A judgment
|
|
180
|
+
carries the generation it judged against, and an admitted one also carries the
|
|
181
|
+
net proposed fact counts. Operational errors stay in the Effect error channel.
|
|
181
182
|
|
|
182
183
|
```ts
|
|
183
184
|
const judgeOnce = Effect.scoped(
|
|
@@ -186,15 +187,15 @@ const judgeOnce = Effect.scoped(
|
|
|
186
187
|
const draft = yield* ChangeSet.builder(Tasks)
|
|
187
188
|
const id = yield* Effect.sync(() => crypto.randomUUID())
|
|
188
189
|
yield* draft.insert(Task, [{ id, title: "inspect a candidate", done: 0n }])
|
|
189
|
-
return yield* db.judge(yield* draft.finish()
|
|
190
|
+
return yield* db.judge(yield* draft.finish())
|
|
190
191
|
})
|
|
191
192
|
)
|
|
192
193
|
void judgeOnce
|
|
193
194
|
```
|
|
194
195
|
|
|
195
196
|
Judgment is optional: `apply` always judges for itself. To act on a judgment,
|
|
196
|
-
|
|
197
|
-
another writer could not change the database between these operations.
|
|
197
|
+
judge and apply with the same snapshot's witness and handle `Moved`; do not
|
|
198
|
+
assume another writer could not change the database between these operations.
|
|
198
199
|
|
|
199
200
|
Change sets also work as inspectable values without a database. Their
|
|
200
201
|
`counts` report distinct requested additions/removals, not the net change
|
|
@@ -320,39 +321,6 @@ const readTwoAuthors = (reader: QueryReader<typeof Library>) =>
|
|
|
320
321
|
void readTwoAuthors
|
|
321
322
|
```
|
|
322
323
|
|
|
323
|
-
Generated queries use the same checked rule, scalar, and execution machinery.
|
|
324
|
-
`describeQuery` exposes the logical IR and its names as owned data. Its relation
|
|
325
|
-
ordinals refer to the supplied schema's declaration order; variables are local
|
|
326
|
-
to each rule. Intermediate names are local labels. The result-field record
|
|
327
|
-
passed to `queryFromDescription` must match the actual derived head, including
|
|
328
|
-
closed vocabularies, and determines the returned row type. Parameters supplied
|
|
329
|
-
to a generated query are checked against their uses during execution.
|
|
330
|
-
|
|
331
|
-
```ts
|
|
332
|
-
const Event = relation("Event", { id: uuid, amount: u64 })
|
|
333
|
-
const Events = schema("Events", { Event }, [key(Event, ["id"])])
|
|
334
|
-
const countEvents = query(Events).rule((r) =>
|
|
335
|
-
r.match(Event, {}).find({ count: r.count() })
|
|
336
|
-
)
|
|
337
|
-
const description = describeQuery(countEvents)
|
|
338
|
-
const totalEvents = queryFromDescription(
|
|
339
|
-
Events,
|
|
340
|
-
{ ...description, columns: ["total"] },
|
|
341
|
-
{ total: u64 }
|
|
342
|
-
)
|
|
343
|
-
const readTotal = (reader: QueryReader<typeof Events>) =>
|
|
344
|
-
Effect.scoped(Effect.gen(function* () {
|
|
345
|
-
const result = yield* reader.execute(totalEvents, {})
|
|
346
|
-
const rows: readonly { readonly total: bigint }[] = yield* result.collect()
|
|
347
|
-
return rows
|
|
348
|
-
}))
|
|
349
|
-
void readTotal
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
Descriptions contain bigint and byte values, so they are not a JSON wire format.
|
|
353
|
-
An accepted description is an authored query; native preparation still checks
|
|
354
|
-
engine semantics. It carries no snapshot, prepared handle, or result rows.
|
|
355
|
-
|
|
356
324
|
## 6. Grouped exact aggregates
|
|
357
325
|
|
|
358
326
|
Aggregates fold the group's distinct full bindings: keep the identity-bearing
|
|
@@ -460,9 +428,9 @@ void [okOrRefused, mintOnce]
|
|
|
460
428
|
|
|
461
429
|
## 10. Witnessed correction: exact expected state
|
|
462
430
|
|
|
463
|
-
Read under a short scope, keep the
|
|
464
|
-
|
|
465
|
-
|
|
431
|
+
Read under a short scope, keep the snapshot's witness (an opaque value that
|
|
432
|
+
outlives the snapshot), and pass it to `apply` as the expected state. An intervening net change moves the apply instead of
|
|
433
|
+
silently overwriting.
|
|
466
434
|
|
|
467
435
|
```ts
|
|
468
436
|
const Account = relation("Account", { id: uuid, balance: i64 })
|
|
@@ -487,7 +455,7 @@ const correct = (accountId: Uuid) =>
|
|
|
487
455
|
yield* draft.delete(Account, [observed.previous])
|
|
488
456
|
yield* draft.insert(Account, [{ ...observed.previous, balance: observed.previous.balance + 1n }])
|
|
489
457
|
const changes = yield* draft.finish()
|
|
490
|
-
return yield* db.apply(changes,
|
|
458
|
+
return yield* db.apply(changes, observed.at)
|
|
491
459
|
})
|
|
492
460
|
)
|
|
493
461
|
void correct
|
|
@@ -511,28 +479,25 @@ const dense: FloatIntervalValue = { start: 0.25, end: 1.5 }
|
|
|
511
479
|
void [Windows, discrete, dense]
|
|
512
480
|
```
|
|
513
481
|
|
|
514
|
-
## 12. Scoped ownership
|
|
482
|
+
## 12. Scoped ownership
|
|
515
483
|
|
|
516
|
-
Every native resource
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
484
|
+
Every native resource belongs to the scope that acquired it: the database, snapshots, prepared
|
|
485
|
+
queries, results and change sets are released when their scope closes, innermost first. There is
|
|
486
|
+
no `close()`; to release something early, acquire it in a smaller scope. A release that cannot
|
|
487
|
+
drain cleanly is a `CloseFailure` defect in the Cause, never a swallowed failure.
|
|
520
488
|
|
|
521
489
|
```ts
|
|
522
490
|
const Item = relation("Item", { id: uuid, label: str })
|
|
523
491
|
const Items = schema("Items", { Item }, [key(Item, ["id"])])
|
|
524
492
|
|
|
525
|
-
const
|
|
493
|
+
const shortLived = Effect.scoped(
|
|
526
494
|
Effect.gen(function* () {
|
|
527
495
|
const db = yield* Db.open(localPath, Items)
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
// `failed` retain native Closing accounting — they are never
|
|
531
|
-
// counted as reclaimed.
|
|
532
|
-
return report.kind
|
|
496
|
+
const generation = yield* Effect.scoped(Effect.map(db.snapshot(), (snapshot) => snapshot.generation))
|
|
497
|
+
return generation
|
|
533
498
|
})
|
|
534
499
|
)
|
|
535
|
-
void
|
|
500
|
+
void shortLived
|
|
536
501
|
```
|
|
537
502
|
|
|
538
503
|
## 13. Derive slices, measure them, and round the total
|
|
@@ -575,7 +540,7 @@ const calculate = Effect.scoped(Effect.gen(function* () {
|
|
|
575
540
|
{ id: 1n, schedule: 1n, span: { start: 0n, end: 100n }, numerator: 1n },
|
|
576
541
|
{ id: 2n, schedule: 1n, span: { start: 100n, end: 200n }, numerator: 2n }
|
|
577
542
|
])
|
|
578
|
-
yield* db.apply(yield* change.finish()
|
|
543
|
+
yield* db.apply(yield* change.finish())
|
|
579
544
|
const snapshot = yield* db.snapshot()
|
|
580
545
|
return yield* (yield* snapshot.execute(amounts, {})).collect()
|
|
581
546
|
}))
|
|
@@ -770,3 +735,114 @@ agree across arms and cannot share a column with a projection.
|
|
|
770
735
|
Each expression retains its original failure boundary. Moving filters across
|
|
771
736
|
an imported stage can change which arithmetic executes; a later filter cannot
|
|
772
737
|
erase an upstream overflow.
|
|
738
|
+
|
|
739
|
+
## 19. A durable database: idempotent submits and fresh reads
|
|
740
|
+
|
|
741
|
+
A `Database` is a log in an object store read through a disposable local
|
|
742
|
+
cache. Provide it once with `Database.layer` under a service key; in a Lambda,
|
|
743
|
+
build the layer during init. Production opens with `onOpen: "verify"` after
|
|
744
|
+
`bumbledb migrate` ran in the deploy pipeline.
|
|
745
|
+
|
|
746
|
+
Mint one request id per intent and reuse it for every retry of that intent: a
|
|
747
|
+
command is decided at most once. `Decided` carries the receipt (`Committed`,
|
|
748
|
+
`NoChange`, `PreconditionFailed` or `InvariantRejected`). Every refusal except
|
|
749
|
+
`Unknown` proves the command is not in the log; after `Unknown`, `resolve`
|
|
750
|
+
answers from the log. `precondition: reader.revision` decides the command only
|
|
751
|
+
if nothing committed since that read, which makes read-modify-write exact.
|
|
752
|
+
|
|
753
|
+
```ts
|
|
754
|
+
const Task = relation("Task", { id: uuid, title: str, done: bool })
|
|
755
|
+
const Tasks = schema("Tasks", { Task }, [key(Task, ["id"])])
|
|
756
|
+
const openTasks = query(Tasks).rule((r) => {
|
|
757
|
+
const { id, title } = v(Task)
|
|
758
|
+
return r.match(Task, { id, title, done: false }).find({ id, title })
|
|
759
|
+
})
|
|
760
|
+
|
|
761
|
+
declare const migrations: Migrations
|
|
762
|
+
declare const s3: S3Sender
|
|
763
|
+
|
|
764
|
+
const TasksDb = Database.tag<typeof Tasks>("app/TasksDb")
|
|
765
|
+
const TasksLive = Database.layer(TasksDb, {
|
|
766
|
+
schema: Tasks,
|
|
767
|
+
migrations,
|
|
768
|
+
store: S3Store.make({
|
|
769
|
+
client: s3,
|
|
770
|
+
log: { bucket: "tasks--use1-az4--x-s3" },
|
|
771
|
+
checkpoints: { bucket: "tasks-checkpoints" },
|
|
772
|
+
prefix: "prod/"
|
|
773
|
+
}),
|
|
774
|
+
cache: { directory: "/tmp/tasks" },
|
|
775
|
+
onOpen: "verify"
|
|
776
|
+
}).pipe(Layer.provide(Bumble.layer()))
|
|
777
|
+
|
|
778
|
+
const completeFirst = Effect.scoped(
|
|
779
|
+
Effect.gen(function* () {
|
|
780
|
+
const db = yield* TasksDb
|
|
781
|
+
const reader = yield* db.read("latest")
|
|
782
|
+
const [first] = yield* (yield* reader.execute(openTasks, {})).collect()
|
|
783
|
+
if (first === undefined) {
|
|
784
|
+
return "nothing open"
|
|
785
|
+
}
|
|
786
|
+
const draft = yield* ChangeSet.builder(Tasks)
|
|
787
|
+
yield* draft.delete(Task, [{ ...first, done: false }])
|
|
788
|
+
yield* draft.insert(Task, [{ ...first, done: true }])
|
|
789
|
+
const requestId = Database.requestId()
|
|
790
|
+
const outcome = yield* db.submit(yield* draft.finish(), { requestId, precondition: reader.revision })
|
|
791
|
+
if (outcome._tag === "Decided") {
|
|
792
|
+
return outcome.receipt.outcome._tag
|
|
793
|
+
}
|
|
794
|
+
if (outcome.refusal._tag === "Unknown") {
|
|
795
|
+
const receipt = yield* db.resolve(requestId)
|
|
796
|
+
return Option.isSome(receipt) ? receipt.value.outcome._tag : "not decided"
|
|
797
|
+
}
|
|
798
|
+
return outcome.refusal._tag
|
|
799
|
+
})
|
|
800
|
+
)
|
|
801
|
+
|
|
802
|
+
void completeFirst.pipe(Effect.provide(TasksLive))
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
`read` takes `"cached"` (the cache as it is), `"latest"` (one request to find
|
|
806
|
+
the log's tip) or `{ atLeast: seq }` (read-your-writes after a receipt's `seq`).
|
|
807
|
+
|
|
808
|
+
## 20. One database per tenant
|
|
809
|
+
|
|
810
|
+
`Database.pool` opens a database per tenant on first use and closes it after
|
|
811
|
+
it has been idle for `idleTimeToLive`. Each tenant has its own log and cache;
|
|
812
|
+
`FsStore` runs the same protocol on a local directory, and `MemStore` is the
|
|
813
|
+
in-memory store for tests.
|
|
814
|
+
|
|
815
|
+
```ts
|
|
816
|
+
const Note = relation("Note", { id: uuid, text: str })
|
|
817
|
+
const Notes = schema("Notes", { Note }, [key(Note, ["id"])])
|
|
818
|
+
const allNotes = query(Notes).rule((r) => {
|
|
819
|
+
const { id, text } = v(Note)
|
|
820
|
+
return r.match(Note, { id, text }).find({ id, text })
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
declare const noteMigrations: Migrations
|
|
824
|
+
|
|
825
|
+
const perTenant = Effect.scoped(
|
|
826
|
+
Effect.gen(function* () {
|
|
827
|
+
const pool = yield* Database.pool({
|
|
828
|
+
schema: Notes,
|
|
829
|
+
migrations: noteMigrations,
|
|
830
|
+
store: (tenant) => FsStore.make(`data/${tenant}/log`),
|
|
831
|
+
cache: (tenant) => ({ directory: `data/${tenant}/cache` }),
|
|
832
|
+
onOpen: "migrate",
|
|
833
|
+
idleTimeToLive: "10 minutes"
|
|
834
|
+
})
|
|
835
|
+
const count = (tenant: string) =>
|
|
836
|
+
Effect.scoped(
|
|
837
|
+
Effect.gen(function* () {
|
|
838
|
+
const db = yield* pool.get(tenant)
|
|
839
|
+
const reader = yield* db.read("cached")
|
|
840
|
+
return (yield* (yield* reader.execute(allNotes, {})).collect()).length
|
|
841
|
+
})
|
|
842
|
+
)
|
|
843
|
+
return yield* Effect.all([count("acme"), count("globex")])
|
|
844
|
+
})
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
void perTenant.pipe(Effect.provide(Bumble.layer()))
|
|
848
|
+
```
|
package/README.md
CHANGED
|
@@ -8,10 +8,10 @@ the native engine.
|
|
|
8
8
|
|
|
9
9
|
The API is **Effect-native**: every database operation constructs a lazy
|
|
10
10
|
[`Effect`](https://effect.website) and every native resource is scoped.
|
|
11
|
-
There is no Promise, synchronous, or disposal twin.
|
|
12
|
-
construction
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
There is no Promise, synchronous, or disposal twin. Schema, query and scalar
|
|
12
|
+
construction stay ordinary synchronous expressions; a schema or query is
|
|
13
|
+
validated by the engine when it is defined. The package requires Effect
|
|
14
|
+
`^4.0.2` as a peer dependency.
|
|
15
15
|
|
|
16
16
|
Relation declarations describe their fields, and the statements passed to
|
|
17
17
|
`schema()` connect those fields into typed keys and references. Values remain
|
|
@@ -36,23 +36,29 @@ ordinary Effect Schema records for application inputs.
|
|
|
36
36
|
The TypeScript package ships native binaries for **darwin-arm64**
|
|
37
37
|
(macOS on Apple Silicon), **linux-arm64**, and **linux-x64**.
|
|
38
38
|
The matching `@bjornpagen/bumbledb-<platform>` package is selected
|
|
39
|
-
automatically during installation.
|
|
40
|
-
the
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
automatically during installation. Defining a schema or a query loads the
|
|
40
|
+
addon and validates the definition in the engine, so an invalid definition
|
|
41
|
+
throws `AuthoringError` where it is written. On an unsupported platform the
|
|
42
|
+
first definition fails with a diagnostic naming the running platform and the
|
|
43
|
+
available binaries. Node 26 or newer is required; Edge and browser runtimes
|
|
44
|
+
are unsupported.
|
|
43
45
|
Linux artifacts target Amazon Linux 2023 / glibc 2.34 or newer. Their
|
|
44
46
|
correctness CI is distinct from the Apple Silicon performance measurements.
|
|
45
47
|
|
|
46
48
|
## Install
|
|
47
49
|
|
|
48
|
-
This guide follows the 1.3.1 source API. Use the guide from the Git tag matching
|
|
49
|
-
your installed package. GitHub release tarballs and npm publication are separate;
|
|
50
|
-
the npm command below applies once that version is published.
|
|
51
|
-
|
|
52
50
|
```sh
|
|
53
|
-
pnpm add @bjornpagen/bumbledb
|
|
51
|
+
pnpm add @bjornpagen/bumbledb effect
|
|
54
52
|
```
|
|
55
53
|
|
|
54
|
+
`@aws-sdk/client-s3` is an optional peer dependency, needed only for `S3Store`.
|
|
55
|
+
|
|
56
|
+
The package has two entry points and one binary. `@bjornpagen/bumbledb`
|
|
57
|
+
holds authoring (relations, schemas, queries, change sets), the `Bumble`
|
|
58
|
+
runtime layer and the durable `Database`. `@bjornpagen/bumbledb/engine` is
|
|
59
|
+
the embedded `Db` for single-process use, tests and benchmarks. The
|
|
60
|
+
`bumbledb` binary generates, checks and applies migrations.
|
|
61
|
+
|
|
56
62
|
## Quick start
|
|
57
63
|
|
|
58
64
|
Declare relations, connect their fields with keys and references, build a
|
|
@@ -66,13 +72,12 @@ import {
|
|
|
66
72
|
capacity,
|
|
67
73
|
ChangeSet,
|
|
68
74
|
contained,
|
|
69
|
-
Db,
|
|
70
75
|
f64,
|
|
71
76
|
i64,
|
|
72
77
|
uuid,
|
|
73
78
|
interval,
|
|
74
79
|
key,
|
|
75
|
-
|
|
80
|
+
Bumble,
|
|
76
81
|
on,
|
|
77
82
|
query,
|
|
78
83
|
ref,
|
|
@@ -84,6 +89,7 @@ import {
|
|
|
84
89
|
weigh,
|
|
85
90
|
within
|
|
86
91
|
} from "@bjornpagen/bumbledb"
|
|
92
|
+
import { Db } from "@bjornpagen/bumbledb/engine"
|
|
87
93
|
|
|
88
94
|
// Relations describe stored records. Identity fields are ordinary
|
|
89
95
|
// application-owned Uuid values — the database issues no identity.
|
|
@@ -135,8 +141,8 @@ const program = Effect.scoped(
|
|
|
135
141
|
])
|
|
136
142
|
const changes = yield* draft.finish()
|
|
137
143
|
|
|
138
|
-
const outcome = yield* db.apply(changes
|
|
139
|
-
if (outcome.
|
|
144
|
+
const outcome = yield* db.apply(changes)
|
|
145
|
+
if (outcome._tag !== "Committed") {
|
|
140
146
|
return outcome
|
|
141
147
|
}
|
|
142
148
|
const snapshot = yield* db.snapshot()
|
|
@@ -152,12 +158,86 @@ const program = Effect.scoped(
|
|
|
152
158
|
|
|
153
159
|
// One boundary for this script; an Effect app supplies the layer in its
|
|
154
160
|
// own application graph instead.
|
|
155
|
-
void Effect.runPromise(program.pipe(Effect.provide(
|
|
161
|
+
void Effect.runPromise(program.pipe(Effect.provide(Bumble.layer())))
|
|
156
162
|
```
|
|
157
163
|
|
|
158
164
|
Every `ts` fence in this README is extracted and type-checked against the
|
|
159
165
|
real surface by `test/readme.test.ts` — the examples cannot drift.
|
|
160
166
|
|
|
167
|
+
## Durable databases
|
|
168
|
+
|
|
169
|
+
`Database` keeps a database as an immutable log in an object store and reads
|
|
170
|
+
it through a local cache that is only a cache. The store is `S3Store` (the log
|
|
171
|
+
in an S3 Express directory bucket, checkpoints in a Standard bucket, through the
|
|
172
|
+
app's `S3Client`), `FsStore` (a durable local directory) or `MemStore` (tests).
|
|
173
|
+
A cold open fetches up to 32 log objects at once, and every new connection
|
|
174
|
+
resolves DNS on one of libuv's 4 threads. Where lookups are slow, start Node
|
|
175
|
+
with `UV_THREADPOOL_SIZE=64` or give the `S3Client` an agent that caches
|
|
176
|
+
lookups.
|
|
177
|
+
|
|
178
|
+
Migrations are bundled with the code. `bumbledb generate --schema
|
|
179
|
+
src/schema.ts#App --name <name>` writes `migrations/NNNN_<name>/` and
|
|
180
|
+
`migrations/index.ts`; add a `populate` step to a generated `migration.ts`
|
|
181
|
+
when rows must be computed. Relations that keep their name and fields are
|
|
182
|
+
copied unchanged. `bumbledb check --schema src/schema.ts#App` fails when the
|
|
183
|
+
schema has an ungenerated change or a migration was edited.
|
|
184
|
+
`onOpen: "migrate"` creates the database and runs pending migrations;
|
|
185
|
+
`onOpen: "verify"` refuses to open while any is pending, so production runs
|
|
186
|
+
`bumbledb migrate --config <file>` from the deploy pipeline first.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { Effect, Option } from "effect"
|
|
190
|
+
import { Bumble, ChangeSet, Database, FsStore, key, type Migrations, query, relation, schema, str, uuid, v } from "@bjornpagen/bumbledb"
|
|
191
|
+
|
|
192
|
+
const Note = relation("Note", { id: uuid, text: str })
|
|
193
|
+
const App = schema("App", { Note }, [key(Note, ["id"])])
|
|
194
|
+
const notes = query(App).rule((r) => {
|
|
195
|
+
const { id, text } = v(Note)
|
|
196
|
+
return r.match(Note, { id, text }).find({ id, text })
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
// Generated by `bumbledb generate`: `import { migrations } from "./migrations/index.ts"`.
|
|
200
|
+
declare const migrations: Migrations
|
|
201
|
+
|
|
202
|
+
const program = Effect.scoped(
|
|
203
|
+
Effect.gen(function* () {
|
|
204
|
+
const db = yield* Database.make({
|
|
205
|
+
schema: App,
|
|
206
|
+
migrations,
|
|
207
|
+
store: FsStore.make("data/log"),
|
|
208
|
+
cache: { directory: "data/cache" },
|
|
209
|
+
onOpen: "migrate"
|
|
210
|
+
})
|
|
211
|
+
// One request id per intent: a retry submits the same command and is decided once.
|
|
212
|
+
const requestId = Database.requestId()
|
|
213
|
+
const draft = yield* ChangeSet.builder(App)
|
|
214
|
+
yield* draft.insert(Note, [{ id: yield* Effect.sync(() => crypto.randomUUID()), text: "hello" }])
|
|
215
|
+
const outcome = yield* db.submit(yield* draft.finish(), { requestId })
|
|
216
|
+
if (outcome._tag === "Refused" && outcome.refusal._tag === "Unknown") {
|
|
217
|
+
// The submit may or may not have been decided; its receipt says which.
|
|
218
|
+
const receipt = yield* db.resolve(requestId)
|
|
219
|
+
return Option.isSome(receipt) ? receipt.value.outcome._tag : "NotDecided"
|
|
220
|
+
}
|
|
221
|
+
const reader = yield* db.read("latest")
|
|
222
|
+
const rows = yield* (yield* reader.execute(notes, {})).collect()
|
|
223
|
+
return `${rows.length} notes at revision ${reader.revision}`
|
|
224
|
+
})
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
void Effect.runPromise(program.pipe(Effect.provide(Bumble.layer())))
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`submit` returns `Decided` with a receipt (`Committed`, `NoChange`,
|
|
231
|
+
`PreconditionFailed` or `InvariantRejected`) or `Refused` with the reason;
|
|
232
|
+
every refusal but `Unknown` proves the log does not decide the command. A
|
|
233
|
+
submit with `precondition: reader.revision` is decided only if nothing
|
|
234
|
+
committed after that read. `read` takes `"cached"`, `"latest"` or
|
|
235
|
+
`{ atLeast: seq }`.
|
|
236
|
+
`Database.layer(Database.tag<typeof App>("App/Database"), options)` provides
|
|
237
|
+
one database to an app graph, and `Database.pool({ ...options, store:
|
|
238
|
+
(tenant) => ..., cache: (tenant) => ... })` opens one per tenant on demand and
|
|
239
|
+
closes it when idle.
|
|
240
|
+
|
|
161
241
|
## Surface
|
|
162
242
|
|
|
163
243
|
The SDK translates TypeScript values directly into the engine's shared schema
|
|
@@ -181,16 +261,17 @@ and query representations.
|
|
|
181
261
|
measurement range, and `weigh` chooses a numeric field or interval
|
|
182
262
|
duration. Harmless equivalent window spellings lower to one canonical
|
|
183
263
|
law; genuinely different meanings still refuse.
|
|
184
|
-
- `
|
|
264
|
+
- `Bumble.layer()` owns the shared native runtime with sensible defaults;
|
|
185
265
|
provide it once in the app graph. `Db.create` and `Db.open` are scoped
|
|
186
266
|
Effects over that runtime; `open` never creates and `create` refuses
|
|
187
|
-
existing authority. `db.apply(changes,
|
|
188
|
-
|
|
189
|
-
`
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
judging.
|
|
267
|
+
existing authority. `db.apply(changes, expected?)` judges one immutable
|
|
268
|
+
final-state change: `Committed` (with the generation, and `changed: false`
|
|
269
|
+
when the state already held it), `Rejected` (complete statement
|
|
270
|
+
diagnostics), or `Moved` when `expected`, a snapshot's opaque `witness`, is
|
|
271
|
+
no longer current. `db.judge(changes, expected?)` uses the same admission
|
|
272
|
+
path and stores nothing: `Admitted` with net additions and removals,
|
|
273
|
+
`Rejected`, or `Moved` without judging. A judgment never guarantees a later
|
|
274
|
+
apply.
|
|
194
275
|
- `ChangeSet.builder(schema)` acquires a scoped database-free draft;
|
|
195
276
|
`insert`/`delete` are lazy bounded ingestion effects, `finish()` seals the
|
|
196
277
|
immutable `ChangeSet`. It exposes `schemaId`, `counts`, and `byteLength`.
|
|
@@ -217,21 +298,6 @@ and query representations.
|
|
|
217
298
|
`f64` are deterministic with one final rounding), named intermediate
|
|
218
299
|
results, nonrecursive composition of query templates, and linear
|
|
219
300
|
recursive reachability.
|
|
220
|
-
- `describeQuery(q)` returns owned logical IR, result names, intermediate
|
|
221
|
-
table names, and parameter names. `queryFromDescription(S, description,
|
|
222
|
-
resultFields)` checks a generated description through the same rule builder,
|
|
223
|
-
scalar grammar, lowering, and execution paths. The result-field record is
|
|
224
|
-
checked against the derived head and infers the returned row type; it is
|
|
225
|
-
not an unchecked cast. Generated parameters are validated by their uses at
|
|
226
|
-
execution. Ordinals refer to the supplied schema's ordered declarations;
|
|
227
|
-
variable ordinals are local to each rule. Native preparation still checks
|
|
228
|
-
engine semantics. The description contains plain values, including bigint
|
|
229
|
-
and byte arrays, rather than native resources or a JSON encoding.
|
|
230
|
-
Query `data` and `schema`, and compiled-schema inspection properties,
|
|
231
|
-
return detached snapshots: editing their byte buffers cannot change the
|
|
232
|
-
query or compilation. Checked immutable branches may be shared. Structural
|
|
233
|
-
declarations with byte payloads are copied when consumed; JavaScript byte
|
|
234
|
-
buffers themselves are mutable and are never treated as immutable cache keys.
|
|
235
301
|
- Operational failure is the one `DbError` tagged-reason class in the
|
|
236
302
|
Effect error channel; interruption and finalizer problems stay in
|
|
237
303
|
`Cause`. Resource owners are scoped and report honest `CloseReport`s;
|
package/dist/alternatives.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type AnyClosed } from "./closed.
|
|
2
|
-
import type { Face, SameArity, SameShapes } from "./face.
|
|
3
|
-
import { type ClosedIdField } from "./fields.
|
|
4
|
-
import type { RelationFields } from "./relation.
|
|
5
|
-
import { type ContainmentStatement, type KeyStatement, type MirrorsStatement } from "./statements.
|
|
1
|
+
import { type AnyClosed } from "./closed.ts";
|
|
2
|
+
import type { Face, SameArity, SameShapes } from "./face.ts";
|
|
3
|
+
import { type ClosedIdField } from "./fields.ts";
|
|
4
|
+
import type { RelationFields } from "./relation.ts";
|
|
5
|
+
import { type ContainmentStatement, type KeyStatement, type MirrorsStatement } from "./statements.ts";
|
|
6
6
|
type KeyFace<K extends KeyStatement> = Face<K["owner"], K["projection"]>;
|
|
7
7
|
type ScalarKey<K extends KeyStatement> = Extract<RelationFields<K["owner"]>[K["projection"][number]], {
|
|
8
8
|
readonly kind: "interval";
|
|
@@ -16,7 +16,6 @@ type Discriminator<P extends KeyStatement, C extends AnyClosed> = {
|
|
|
16
16
|
* Exhaustive closed alternatives, expanded into ordinary laws. Declare the
|
|
17
17
|
* supplied keys in the schema once; this helper emits only the discriminator
|
|
18
18
|
* containment followed by one mirrors statement per handle in roster order.
|
|
19
|
-
* No new statement kind or assembled payload representation is introduced.
|
|
20
19
|
*/
|
|
21
20
|
declare function alternatives<P extends KeyStatement, C extends AnyClosed, const Arms extends Record<C["handles"][number], KeyStatement>>(parent: P & ScalarKey<P>, discriminator: Discriminator<NoInfer<P>, NoInfer<C>>, roster: C, arms: Arms & Record<Exclude<keyof Arms, C["handles"][number]>, never> & {
|
|
22
21
|
readonly [H in C["handles"][number]]: ScalarKey<Arms[H]> & SameArity<KeyFace<P>, KeyFace<Arms[H]>> & SameShapes<KeyFace<P>, KeyFace<Arms[H]>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alternatives.d.ts","sourceRoot":"","sources":["../src/alternatives.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA6C,MAAM,
|
|
1
|
+
{"version":3,"file":"alternatives.d.ts","sourceRoot":"","sources":["../src/alternatives.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA6C,MAAM,aAAa,CAAA;AAEvF,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC5D,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,aAAa,CAAA;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAErB,MAAM,iBAAiB,CAAA;AAGxB,KAAK,OAAO,CAAC,CAAC,SAAS,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAA;AACxE,KAAK,SAAS,CAAC,CAAC,SAAS,YAAY,IACpC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,SAAS,KAAK,GACtG,OAAO,GACP;IAAE,QAAQ,CAAC,0CAA0C,EAAE,KAAK,CAAA;CAAE,CAAA;AAClE,KAAK,aAAa,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,SAAS,IAAI;KAChE,CAAC,IAAI,MAAM,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,GAClH,CAAC,GACD,KAAK;CACR,CAAC,MAAM,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAClC,MAAM,CAAA;AAcP;;;;GAIG;AACH,iBAAS,YAAY,CACpB,CAAC,SAAS,YAAY,EACtB,CAAC,SAAS,SAAS,EACnB,KAAK,CAAC,IAAI,SAAS,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,EAE7D,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,EACxB,aAAa,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EACpD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,IAAI,GACT,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG;IAC1D,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GACvD,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GACvC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;CACzC,GACA,SAAS,CAAC,oBAAoB,GAAG,gBAAgB,CAAC,EAAE,CAmCtD;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
package/dist/alternatives.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { closedDescriptor, closedId, sealedFieldOf } from "
|
|
2
|
-
import { AuthoringError } from "
|
|
3
|
-
import { signaturesAgree } from "
|
|
4
|
-
import { statementDescriptor } from "
|
|
5
|
-
import { recordValue } from "
|
|
1
|
+
import { closedDescriptor, closedId, sealedFieldOf } from "./closed.js";
|
|
2
|
+
import { AuthoringError } from "./errors.js";
|
|
3
|
+
import { signaturesAgree } from "./fields.js";
|
|
4
|
+
import { statementDescriptor } from "./statements.js";
|
|
5
|
+
import { recordValue } from "./values.js";
|
|
6
6
|
function scalarKey(input) {
|
|
7
7
|
const checked = statementDescriptor(input);
|
|
8
8
|
if (checked.kind !== "key")
|
|
@@ -19,7 +19,6 @@ function scalarKey(input) {
|
|
|
19
19
|
* Exhaustive closed alternatives, expanded into ordinary laws. Declare the
|
|
20
20
|
* supplied keys in the schema once; this helper emits only the discriminator
|
|
21
21
|
* containment followed by one mirrors statement per handle in roster order.
|
|
22
|
-
* No new statement kind or assembled payload representation is introduced.
|
|
23
22
|
*/
|
|
24
23
|
function alternatives(parent, discriminator, roster, arms) {
|
|
25
24
|
const primary = scalarKey(parent);
|