@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.
Files changed (290) hide show
  1. package/COOKBOOK.md +153 -77
  2. package/README.md +107 -41
  3. package/dist/alternatives.d.ts +5 -6
  4. package/dist/alternatives.d.ts.map +1 -1
  5. package/dist/alternatives.js +5 -6
  6. package/dist/alternatives.js.map +1 -1
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +85 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/capacity.d.ts +14 -17
  12. package/dist/capacity.d.ts.map +1 -1
  13. package/dist/capacity.js +7 -7
  14. package/dist/capacity.js.map +1 -1
  15. package/dist/changes.d.ts +32 -40
  16. package/dist/changes.d.ts.map +1 -1
  17. package/dist/changes.js +86 -104
  18. package/dist/changes.js.map +1 -1
  19. package/dist/closed.d.ts +2 -2
  20. package/dist/closed.d.ts.map +1 -1
  21. package/dist/closed.js +5 -5
  22. package/dist/closed.js.map +1 -1
  23. package/dist/codec.d.ts +13 -27
  24. package/dist/codec.d.ts.map +1 -1
  25. package/dist/codec.js +20 -21
  26. package/dist/codec.js.map +1 -1
  27. package/dist/compile.d.ts +26 -50
  28. package/dist/compile.d.ts.map +1 -1
  29. package/dist/compile.js +75 -64
  30. package/dist/compile.js.map +1 -1
  31. package/dist/database/database.d.ts +98 -0
  32. package/dist/database/database.d.ts.map +1 -0
  33. package/dist/database/database.js +249 -0
  34. package/dist/database/database.js.map +1 -0
  35. package/dist/database/driver.d.ts +40 -0
  36. package/dist/database/driver.d.ts.map +1 -0
  37. package/dist/database/driver.js +68 -0
  38. package/dist/database/driver.js.map +1 -0
  39. package/dist/database/fs.d.ts +12 -0
  40. package/dist/database/fs.d.ts.map +1 -0
  41. package/dist/database/fs.js +120 -0
  42. package/dist/database/fs.js.map +1 -0
  43. package/dist/database/generate.d.ts +26 -0
  44. package/dist/database/generate.d.ts.map +1 -0
  45. package/dist/database/generate.js +102 -0
  46. package/dist/database/generate.js.map +1 -0
  47. package/dist/database/hosted.d.ts +32 -0
  48. package/dist/database/hosted.d.ts.map +1 -0
  49. package/dist/database/hosted.js +44 -0
  50. package/dist/database/hosted.js.map +1 -0
  51. package/dist/database/io.d.ts +94 -0
  52. package/dist/database/io.d.ts.map +1 -0
  53. package/dist/database/io.js +35 -0
  54. package/dist/database/io.js.map +1 -0
  55. package/dist/database/mem.d.ts +26 -0
  56. package/dist/database/mem.d.ts.map +1 -0
  57. package/dist/database/mem.js +67 -0
  58. package/dist/database/mem.js.map +1 -0
  59. package/dist/database/migration.d.ts +55 -0
  60. package/dist/database/migration.d.ts.map +1 -0
  61. package/dist/database/migration.js +46 -0
  62. package/dist/database/migration.js.map +1 -0
  63. package/dist/database/s3.d.ts +36 -0
  64. package/dist/database/s3.d.ts.map +1 -0
  65. package/dist/database/s3.js +141 -0
  66. package/dist/database/s3.js.map +1 -0
  67. package/dist/db.d.ts +44 -128
  68. package/dist/db.d.ts.map +1 -1
  69. package/dist/db.js +163 -287
  70. package/dist/db.js.map +1 -1
  71. package/dist/engine.d.ts +7 -0
  72. package/dist/engine.d.ts.map +1 -0
  73. package/dist/engine.js +2 -0
  74. package/dist/engine.js.map +1 -0
  75. package/dist/errors.d.ts +152 -46
  76. package/dist/errors.d.ts.map +1 -1
  77. package/dist/errors.js +107 -19
  78. package/dist/errors.js.map +1 -1
  79. package/dist/face.d.ts +5 -5
  80. package/dist/face.d.ts.map +1 -1
  81. package/dist/face.js +6 -6
  82. package/dist/face.js.map +1 -1
  83. package/dist/fields.d.ts +4 -5
  84. package/dist/fields.d.ts.map +1 -1
  85. package/dist/fields.js +3 -3
  86. package/dist/fields.js.map +1 -1
  87. package/dist/index.d.ts +60 -57
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +27 -28
  90. package/dist/index.js.map +1 -1
  91. package/dist/judgment.d.ts +1 -1
  92. package/dist/judgment.js +1 -1
  93. package/dist/law.d.ts +16 -68
  94. package/dist/law.d.ts.map +1 -1
  95. package/dist/law.js +3 -3
  96. package/dist/law.js.map +1 -1
  97. package/dist/lower.d.ts +3 -3
  98. package/dist/lower.d.ts.map +1 -1
  99. package/dist/lower.js +35 -39
  100. package/dist/lower.js.map +1 -1
  101. package/dist/native/addon.d.ts +23 -0
  102. package/dist/native/addon.d.ts.map +1 -0
  103. package/dist/native/addon.js +15 -0
  104. package/dist/native/addon.js.map +1 -0
  105. package/dist/native/binding.d.ts +996 -0
  106. package/dist/native/json.d.ts +14 -0
  107. package/dist/native/json.d.ts.map +1 -0
  108. package/dist/native/json.js +73 -0
  109. package/dist/native/json.js.map +1 -0
  110. package/dist/native/load.d.ts +9 -0
  111. package/dist/native/load.d.ts.map +1 -0
  112. package/dist/native/load.js +30 -0
  113. package/dist/native/load.js.map +1 -0
  114. package/dist/native/op.d.ts +29 -0
  115. package/dist/native/op.d.ts.map +1 -0
  116. package/dist/native/op.js +50 -0
  117. package/dist/native/op.js.map +1 -0
  118. package/dist/query/atom.d.ts +13 -14
  119. package/dist/query/atom.d.ts.map +1 -1
  120. package/dist/query/atom.js +2 -2
  121. package/dist/query/atom.js.map +1 -1
  122. package/dist/query/compute.d.ts +4 -4
  123. package/dist/query/compute.d.ts.map +1 -1
  124. package/dist/query/compute.js +4 -4
  125. package/dist/query/compute.js.map +1 -1
  126. package/dist/query/find.d.ts +8 -8
  127. package/dist/query/find.d.ts.map +1 -1
  128. package/dist/query/find.js +1 -1
  129. package/dist/query/lower.d.ts +22 -17
  130. package/dist/query/lower.d.ts.map +1 -1
  131. package/dist/query/lower.js +230 -343
  132. package/dist/query/lower.js.map +1 -1
  133. package/dist/query/run.d.ts +5 -5
  134. package/dist/query/run.d.ts.map +1 -1
  135. package/dist/query/run.js +32 -15
  136. package/dist/query/run.js.map +1 -1
  137. package/dist/query/scope.d.ts +20 -28
  138. package/dist/query/scope.d.ts.map +1 -1
  139. package/dist/query/scope.js +7 -7
  140. package/dist/query/scope.js.map +1 -1
  141. package/dist/query/segments.d.ts +2 -2
  142. package/dist/query/segments.d.ts.map +1 -1
  143. package/dist/query/segments.js +3 -3
  144. package/dist/query/segments.js.map +1 -1
  145. package/dist/relation.d.ts +1 -1
  146. package/dist/relation.d.ts.map +1 -1
  147. package/dist/relation.js +4 -4
  148. package/dist/relation.js.map +1 -1
  149. package/dist/result.d.ts +15 -22
  150. package/dist/result.d.ts.map +1 -1
  151. package/dist/result.js +30 -39
  152. package/dist/result.js.map +1 -1
  153. package/dist/rows.d.ts +9 -33
  154. package/dist/rows.d.ts.map +1 -1
  155. package/dist/rows.js +8 -12
  156. package/dist/rows.js.map +1 -1
  157. package/dist/runtime.d.ts +17 -20
  158. package/dist/runtime.d.ts.map +1 -1
  159. package/dist/runtime.js +41 -205
  160. package/dist/runtime.js.map +1 -1
  161. package/dist/scalar.d.ts +4 -4
  162. package/dist/scalar.d.ts.map +1 -1
  163. package/dist/scalar.js +24 -8
  164. package/dist/scalar.js.map +1 -1
  165. package/dist/schema.d.ts +7 -18
  166. package/dist/schema.d.ts.map +1 -1
  167. package/dist/schema.js +18 -228
  168. package/dist/schema.js.map +1 -1
  169. package/dist/selection.d.ts +4 -4
  170. package/dist/selection.d.ts.map +1 -1
  171. package/dist/selection.js +5 -5
  172. package/dist/selection.js.map +1 -1
  173. package/dist/shape.d.ts +5 -5
  174. package/dist/shape.d.ts.map +1 -1
  175. package/dist/spec.d.ts +7 -79
  176. package/dist/spec.d.ts.map +1 -1
  177. package/dist/spec.js.map +1 -1
  178. package/dist/statements.d.ts +3 -3
  179. package/dist/statements.d.ts.map +1 -1
  180. package/dist/statements.js +10 -10
  181. package/dist/statements.js.map +1 -1
  182. package/dist/uuid.d.ts +1 -1
  183. package/dist/uuid.d.ts.map +1 -1
  184. package/dist/uuid.js +1 -1
  185. package/dist/uuid.js.map +1 -1
  186. package/dist/values.d.ts +2 -2
  187. package/dist/values.d.ts.map +1 -1
  188. package/dist/values.js +2 -2
  189. package/dist/values.js.map +1 -1
  190. package/package.json +18 -18
  191. package/src/alternatives.ts +7 -8
  192. package/src/bin.ts +82 -0
  193. package/src/capacity.ts +20 -23
  194. package/src/changes.ts +134 -191
  195. package/src/closed.ts +5 -5
  196. package/src/codec.ts +35 -49
  197. package/src/compile.ts +91 -113
  198. package/src/database/database.ts +413 -0
  199. package/src/database/driver.ts +134 -0
  200. package/src/database/fs.ts +119 -0
  201. package/src/database/generate.ts +116 -0
  202. package/src/database/hosted.ts +102 -0
  203. package/src/database/io.ts +134 -0
  204. package/src/database/mem.ts +113 -0
  205. package/src/database/migration.ts +107 -0
  206. package/src/database/s3.ts +231 -0
  207. package/src/db.ts +290 -547
  208. package/src/engine.ts +14 -0
  209. package/src/errors.ts +118 -39
  210. package/src/face.ts +9 -9
  211. package/src/fields.ts +6 -7
  212. package/src/index.ts +92 -105
  213. package/src/judgment.ts +1 -1
  214. package/src/law.ts +18 -70
  215. package/src/lower.ts +61 -72
  216. package/src/native/addon.ts +54 -0
  217. package/src/native/binding.d.ts +996 -0
  218. package/src/native/json.ts +100 -0
  219. package/src/native/load.ts +31 -0
  220. package/src/native/op.ts +73 -0
  221. package/src/query/atom.ts +15 -16
  222. package/src/query/compute.ts +7 -7
  223. package/src/query/find.ts +8 -8
  224. package/src/query/lower.ts +270 -392
  225. package/src/query/run.ts +42 -21
  226. package/src/query/scope.ts +29 -37
  227. package/src/query/segments.ts +4 -4
  228. package/src/relation.ts +4 -4
  229. package/src/result.ts +53 -80
  230. package/src/rows.ts +18 -46
  231. package/src/runtime.ts +50 -277
  232. package/src/scalar.ts +32 -12
  233. package/src/schema.ts +23 -284
  234. package/src/selection.ts +6 -6
  235. package/src/shape.ts +5 -5
  236. package/src/spec.ts +7 -85
  237. package/src/statements.ts +11 -11
  238. package/src/uuid.ts +1 -1
  239. package/src/values.ts +4 -4
  240. package/dist/close.d.ts +0 -24
  241. package/dist/close.d.ts.map +0 -1
  242. package/dist/close.js +0 -34
  243. package/dist/close.js.map +0 -1
  244. package/dist/db-native.d.ts +0 -189
  245. package/dist/db-native.d.ts.map +0 -1
  246. package/dist/db-native.js +0 -13
  247. package/dist/db-native.js.map +0 -1
  248. package/dist/internal/log.d.ts +0 -26
  249. package/dist/internal/log.d.ts.map +0 -1
  250. package/dist/internal/log.js +0 -12
  251. package/dist/internal/log.js.map +0 -1
  252. package/dist/native.d.ts +0 -562
  253. package/dist/native.d.ts.map +0 -1
  254. package/dist/native.js +0 -123
  255. package/dist/native.js.map +0 -1
  256. package/dist/query/description.d.ts +0 -41
  257. package/dist/query/description.d.ts.map +0 -1
  258. package/dist/query/description.js +0 -311
  259. package/dist/query/description.js.map +0 -1
  260. package/dist/query/parse-ir.d.ts +0 -10
  261. package/dist/query/parse-ir.d.ts.map +0 -1
  262. package/dist/query/parse-ir.js +0 -280
  263. package/dist/query/parse-ir.js.map +0 -1
  264. package/dist/runtime-codes.d.ts +0 -2
  265. package/dist/runtime-codes.d.ts.map +0 -1
  266. package/dist/runtime-codes.js +0 -19
  267. package/dist/runtime-codes.js.map +0 -1
  268. package/dist/runtime-errors.d.ts +0 -138
  269. package/dist/runtime-errors.d.ts.map +0 -1
  270. package/dist/runtime-errors.js +0 -111
  271. package/dist/runtime-errors.js.map +0 -1
  272. package/dist/runtime-native.d.ts +0 -136
  273. package/dist/runtime-native.d.ts.map +0 -1
  274. package/dist/runtime-native.js +0 -5
  275. package/dist/runtime-native.js.map +0 -1
  276. package/dist/schema-file.d.ts +0 -7
  277. package/dist/schema-file.d.ts.map +0 -1
  278. package/dist/schema-file.js +0 -24
  279. package/dist/schema-file.js.map +0 -1
  280. package/pack-provenance.json +0 -7
  281. package/src/close.ts +0 -53
  282. package/src/db-native.ts +0 -235
  283. package/src/internal/log.ts +0 -46
  284. package/src/native.ts +0 -736
  285. package/src/query/description.ts +0 -424
  286. package/src/query/parse-ir.ts +0 -293
  287. package/src/runtime-codes.ts +0 -18
  288. package/src/runtime-errors.ts +0 -122
  289. package/src/runtime-native.ts +0 -181
  290. 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, and one-shot page streams.
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
- Db,
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
- NativeRuntime,
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
- QueryReader
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
- `NativeRuntime.layer()` acquires the single native runtime
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().storage` reports virtual map extent, populated file length,
111
- non-free LMDB pages, and allocated disk blocks (null when unavailable).
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 = NativeRuntime.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, { expected: { kind: "any" } })
161
- switch (outcome.kind) {
162
- case "accepted":
163
- case "no-change":
164
- return outcome.witness
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 "moved":
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. Admitted and
179
- rejected judgments include the actual base and net proposed fact counts.
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(), { expected: { kind: "any" } })
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
- use its base as an exact expected witness and handle `moved`; do not assume
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 copied witness, and apply with
464
- `expected: { kind: "exact", at: witness }`. An intervening net change moves
465
- the apply instead of silently overwriting.
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, { expected: { kind: "exact", at: observed.at } })
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 and honest close
482
+ ## 12. Scoped ownership
515
483
 
516
- Every native resource is scoped; early `close()` is itself an Effect
517
- returning the honest `CloseReport`. A scope finalizer that cannot complete
518
- teardown surfaces a structured `CloseFailure` DEFECT in the Cause — never a
519
- silently swallowed failure, never falsely reclaimed resources.
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 explicitClose = Effect.scoped(
493
+ const shortLived = Effect.scoped(
526
494
  Effect.gen(function* () {
527
495
  const db = yield* Db.open(localPath, Items)
528
- const report = yield* db.close()
529
- // `closed` releases this capability's obligations; `incomplete` and
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 explicitClose
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(), { expected: { kind: "any" } })
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. Pure schema/query/scalar
12
- construction and reads of already-owned metadata stay ordinary synchronous
13
- expressions with no hidden native work. The package requires Effect
14
- `4.0.0-rc.112` exactly, as a peer dependency.
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. Schema and query authoring do not load
40
- the addon. Attempting native database work on an unsupported platform fails
41
- with a diagnostic identifying the running platform and available binaries.
42
- Node 24 or newer is required; Edge and browser runtimes are unsupported.
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@1.3.1 effect@4.0.0-rc.112
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
- NativeRuntime,
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, { expected: { kind: "any" } })
139
- if (outcome.kind !== "accepted" && outcome.kind !== "no-change") {
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(NativeRuntime.layer())))
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
- - `NativeRuntime.layer()` owns the shared native runtime with sensible defaults;
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, { expected })` judges one
188
- immutable final-state change: `accepted`, `no-change`,
189
- `invariant-rejected` (complete statement diagnostics), or `moved`.
190
- `db.judge(changes, { expected })` uses the same admission path but aborts
191
- the private candidate. It returns `admitted` or `invariant-rejected` with
192
- the actual base witness and net additions/removals, or `moved` without
193
- judging. Both use `WriteOptions`; judgment never guarantees a later apply.
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;
@@ -1,8 +1,8 @@
1
- import { type AnyClosed } from "./closed.js";
2
- import type { Face, SameArity, SameShapes } from "./face.js";
3
- import { type ClosedIdField } from "./fields.js";
4
- import type { RelationFields } from "./relation.js";
5
- import { type ContainmentStatement, type KeyStatement, type MirrorsStatement } from "./statements.js";
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,YAAY,CAAA;AAEtF,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC3D,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,YAAY,CAAA;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EAErB,MAAM,gBAAgB,CAAA;AAGvB,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;;;;;GAKG;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"}
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"}
@@ -1,8 +1,8 @@
1
- import { closedDescriptor, closedId, sealedFieldOf } from "#closed.ts";
2
- import { AuthoringError } from "#errors.ts";
3
- import { signaturesAgree } from "#fields.ts";
4
- import { statementDescriptor } from "#statements.ts";
5
- import { recordValue } from "#values.ts";
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);