@bjornpagen/bumbledb 1.3.0 → 2.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/COOKBOOK.md +224 -95
  2. package/README.md +114 -45
  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 -41
  16. package/dist/changes.d.ts.map +1 -1
  17. package/dist/changes.js +86 -110
  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 +20 -27
  24. package/dist/codec.d.ts.map +1 -1
  25. package/dist/codec.js +48 -22
  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 -58
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +27 -29
  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 +7 -7
  123. package/dist/query/compute.d.ts.map +1 -1
  124. package/dist/query/compute.js +7 -16
  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 +31 -17
  130. package/dist/query/lower.d.ts.map +1 -1
  131. package/dist/query/lower.js +260 -368
  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 +13 -8
  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 -43
  158. package/dist/runtime.d.ts.map +1 -1
  159. package/dist/runtime.js +41 -241
  160. package/dist/runtime.js.map +1 -1
  161. package/dist/scalar.d.ts +36 -106
  162. package/dist/scalar.d.ts.map +1 -1
  163. package/dist/scalar.js +36 -185
  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 -197
  195. package/src/closed.ts +5 -5
  196. package/src/codec.ts +77 -51
  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 +102 -109
  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 +13 -22
  223. package/src/query/find.ts +8 -8
  224. package/src/query/lower.ts +323 -414
  225. package/src/query/run.ts +42 -21
  226. package/src/query/scope.ts +37 -38
  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 -346
  232. package/src/scalar.ts +76 -336
  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 -196
  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/migration.d.ts +0 -31
  253. package/dist/migration.d.ts.map +0 -1
  254. package/dist/migration.js +0 -47
  255. package/dist/migration.js.map +0 -1
  256. package/dist/native.d.ts +0 -562
  257. package/dist/native.d.ts.map +0 -1
  258. package/dist/native.js +0 -123
  259. package/dist/native.js.map +0 -1
  260. package/dist/query/description.d.ts +0 -41
  261. package/dist/query/description.d.ts.map +0 -1
  262. package/dist/query/description.js +0 -311
  263. package/dist/query/description.js.map +0 -1
  264. package/dist/query/parse-ir.d.ts +0 -10
  265. package/dist/query/parse-ir.d.ts.map +0 -1
  266. package/dist/query/parse-ir.js +0 -280
  267. package/dist/query/parse-ir.js.map +0 -1
  268. package/dist/runtime-codes.d.ts +0 -2
  269. package/dist/runtime-codes.d.ts.map +0 -1
  270. package/dist/runtime-codes.js +0 -18
  271. package/dist/runtime-codes.js.map +0 -1
  272. package/dist/runtime-errors.d.ts +0 -118
  273. package/dist/runtime-errors.d.ts.map +0 -1
  274. package/dist/runtime-errors.js +0 -98
  275. package/dist/runtime-errors.js.map +0 -1
  276. package/dist/runtime-native.d.ts +0 -141
  277. package/dist/runtime-native.d.ts.map +0 -1
  278. package/dist/runtime-native.js +0 -5
  279. package/dist/runtime-native.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 -243
  283. package/src/internal/log.ts +0 -48
  284. package/src/migration.ts +0 -61
  285. package/src/native.ts +0 -736
  286. package/src/query/description.ts +0 -424
  287. package/src/query/parse-ir.ts +0 -291
  288. package/src/runtime-codes.ts +0 -17
  289. package/src/runtime-errors.ts +0 -109
  290. package/src/runtime-native.ts +0 -189
package/src/changes.ts CHANGED
@@ -1,43 +1,34 @@
1
1
  import type { Scope } from "effect"
2
2
  import { Effect, Exit, Option, Stream } from "effect"
3
- import { drainClose, releaseOwner } from "#close.ts"
4
- import { isClosedMember, membersAgree } from "#closed.ts"
5
- import type { SchemaId } from "#compile.ts"
6
- import { Schema as CoreSchema, schemaTables } from "#compile.ts"
7
- import type { ChangeRecordWire, ChangesHandle, ChangesWire, DraftHandle } from "#db-native.ts"
8
- import { dbNative } from "#db-native.ts"
9
- import { SdkInvariantError } from "#errors.ts"
10
- import { lower } from "#lower.ts"
11
- import { type AnyRelation, type Fact, relationFields } from "#relation.ts"
12
- import type { CellValue } from "#rows.ts"
13
- import { factCellsOf, factOfCells, hostCellCharge } from "#rows.ts"
14
- import { nativeOperationWith, runtimeHandle } from "#runtime.ts"
15
- import type { CloseReport } from "#runtime-errors.ts"
16
- import { argumentError, DbError } from "#runtime-errors.ts"
17
- import type { AnySchema, SchemaRelation } from "#schema.ts"
18
- import type { Rel } from "#shape.ts"
19
- import { bytesValue, recordValue } from "#values.ts"
20
-
21
- /**
22
- * `ChangeSet` — the engine's checked immutable delta:
23
- * schema-fingerprint-bound canonical native bytes with
24
- * one-command `(add, remove ∖ add)` normalization and exact same-fact
25
- * add-wins. Immutable and reusable while open; sealing/submitting it later
26
- * retains the SAME native value — no second JS row walk ever happens.
27
- */
28
- /** Counts of distinct fact additions and removals, never input events. */
29
- interface ChangeCounts {
30
- readonly added: bigint
31
- readonly removed: bigint
32
- }
3
+ import { isClosedMember, membersAgree } from "./closed.ts"
4
+ import type { SchemaId } from "./compile.ts"
5
+ import { compiledOf, schemaTables } from "./compile.ts"
6
+ import { argumentError, DbError, dbError, internalError } from "./errors.ts"
7
+ import type { ChangesRef, DraftRef } from "./native/addon.ts"
8
+ import { addon } from "./native/addon.ts"
9
+ import type { ChangeCounts, ChangeKindOut, ChangeRecordOut, ChangesOut } from "./native/binding.d.ts"
10
+ import { call, drain, release, scoped } from "./native/op.ts"
11
+ import { type AnyRelation, type Fact, relationFields } from "./relation.ts"
12
+ import type { CellValue } from "./rows.ts"
13
+ import { factCellsOf, factOfCells, hostCellCharge } from "./rows.ts"
14
+ import { runtimeHandle } from "./runtime.ts"
15
+ import type { AnySchema, SchemaRelation } from "./schema.ts"
16
+ import { schemaDescriptor } from "./schema.ts"
17
+ import type { Rel } from "./shape.ts"
18
+ import { bytesValue, recordValue } from "./values.ts"
33
19
 
34
20
  /** A relation-name-discriminated union of plain, fully typed facts. */
35
21
  type ChangeRecord<S extends AnySchema> = {
36
22
  [N in keyof S["relations"]]: S["relations"][N] extends AnyRelation
37
- ? { readonly relation: N; readonly kind: "add" | "remove"; readonly fact: Fact<S["relations"][N]> }
23
+ ? { readonly relation: N; readonly kind: ChangeKindOut; readonly fact: Fact<S["relations"][N]> }
38
24
  : never
39
25
  }[keyof S["relations"]]
40
26
 
27
+ /**
28
+ * The engine's checked immutable delta: schema-fingerprint-bound canonical native bytes with
29
+ * one-command `(add, remove ∖ add)` normalization and exact same-fact add-wins. Reusable while
30
+ * open; submitting it retains the same native value, so no second row walk happens.
31
+ */
41
32
  interface ChangeSet<S extends AnySchema> {
42
33
  readonly schemaId: SchemaId
43
34
  /** Distinct requested actions; use judge for effective counts against a store. */
@@ -51,26 +42,18 @@ interface ChangeSet<S extends AnySchema> {
51
42
  toBytes(): Effect.Effect<Uint8Array, DbError>
52
43
  /** Commutative add-wins composition into one command, not sequential replay. */
53
44
  compose(other: ChangeSet<S>): Effect.Effect<ChangeSet<S>, DbError, Scope.Scope>
54
- close(): Effect.Effect<CloseReport>
55
45
  }
56
46
 
57
47
  /**
58
- * `ChangeDraft` — the scoped, database-free construction capability.
59
- * Every method constructs a LAZY effect; execution reads the then-current
60
- * iterable again on every sequential rerun (no hidden
61
- * memoization, no automatic retry, no iterator replay). Input must stay
62
- * stable from an ingestion effect's execution start through its Exit;
63
- * after successful ingestion the accepted native bytes are independent.
64
- * Failure/interruption SPENDS the draft and initiates tracked drain.
65
- * Concurrent/reentrant construction refuses and spends/drains the draft.
66
- * `finish` consumes the draft; later ingestion or a second finish refuses
67
- * through the spent capability state.
48
+ * A scoped, database-free change draft. Each method returns a lazy effect that reads its iterable
49
+ * when it runs (every rerun reads it again); accepted rows are copied, so later mutation of the
50
+ * input changes nothing. A failure, an interruption or concurrent use spends the draft; `finish`
51
+ * consumes it.
68
52
  */
69
53
  interface ChangeDraft<S extends AnySchema> {
70
54
  insert<R extends Rel<S>>(relation: R, rows: Iterable<Fact<R>>): Effect.Effect<void, DbError>
71
55
  delete<R extends Rel<S>>(relation: R, rows: Iterable<Fact<R>>): Effect.Effect<void, DbError>
72
56
  finish(): Effect.Effect<ChangeSet<S>, DbError, Scope.Scope>
73
- close(): Effect.Effect<CloseReport>
74
57
  }
75
58
 
76
59
  /**
@@ -84,32 +67,19 @@ const CHUNK_BYTES = 65536n
84
67
  const CHUNK_ROWS = 4096
85
68
 
86
69
  interface DraftState {
87
- readonly handle: DraftHandle
70
+ readonly handle: DraftRef
88
71
  readonly theory: AnySchema
89
72
  spent: boolean
90
73
  inFlight: boolean
91
74
  }
92
75
 
93
- interface ChangesInternal {
94
- readonly handle: ChangesHandle
95
- readonly schemaId: SchemaId
96
- closed: boolean
97
- }
98
-
99
- const changesInternals = new WeakMap<object, ChangesInternal>()
100
-
101
- /** Private cross-module accessor (db.apply retains the native ChangeSet). */
102
- function internalChanges(value: object): ChangesInternal | undefined {
103
- return changesInternals.get(value)
104
- }
105
-
106
76
  function refusal(operation: string, reason: "SpentHandle" | "ClosedHandle" | "InvalidArgument"): DbError {
107
77
  return new DbError({ operation, reason: { _tag: reason } })
108
78
  }
109
79
 
110
80
  interface Chunk {
111
81
  readonly rows: bigint
112
- readonly cells: readonly CellValue[]
82
+ readonly cells: CellValue[]
113
83
  readonly done: boolean
114
84
  readonly leftover: object | undefined
115
85
  }
@@ -168,16 +138,13 @@ function pullChunk(relation: AnyRelation, iterator: Iterator<object>, pending: o
168
138
  return { rows, cells, done: false, leftover }
169
139
  }
170
140
 
171
- function spendAndDrain(state: DraftState, operation: string): Effect.Effect<void> {
141
+ function spendAndDrain(state: DraftState): Effect.Effect<void> {
172
142
  return Effect.suspend(() => {
173
143
  if (state.spent) {
174
144
  return Effect.void
175
145
  }
176
146
  state.spent = true
177
- // Tracked drain: join the native close transition; the report is
178
- // diagnostic here (the ingestion failure itself is the caller's
179
- // error), but native Closing accounting is never dropped.
180
- return drainClose(operation, (callback) => dbNative.runtimeDraftClose(state.handle, callback)).pipe(Effect.asVoid)
147
+ return drain((done) => addon.runtimeDraftClose(state.handle, done)).pipe(Effect.asVoid)
181
148
  })
182
149
  }
183
150
 
@@ -187,7 +154,7 @@ function ingest(
187
154
  relation: AnyRelation,
188
155
  rows: Iterable<object>
189
156
  ): Effect.Effect<void, DbError> {
190
- const verb = operation === "ChangeDraft.insert" ? dbNative.runtimeDraftInsert : dbNative.runtimeDraftDelete
157
+ const verb = operation === "ChangeDraft.insert" ? addon.runtimeDraftInsert : addon.runtimeDraftDelete
191
158
  return Effect.gen(function* () {
192
159
  if (state.spent) {
193
160
  return yield* Effect.fail(refusal(operation, "SpentHandle"))
@@ -195,7 +162,7 @@ function ingest(
195
162
  if (state.inFlight) {
196
163
  // Reentrant construction refuses AND spends/drains — there is
197
164
  // no implicit queue.
198
- yield* spendAndDrain(state, operation)
165
+ yield* spendAndDrain(state)
199
166
  return yield* Effect.fail(refusal(operation, "SpentHandle"))
200
167
  }
201
168
  if (!membersAgree(state.theory.relations[relation.name], relation)) {
@@ -219,18 +186,19 @@ function ingest(
219
186
  const chunk = yield* Effect.try({
220
187
  try: () => pullChunk(relation, iterator, leftover),
221
188
  catch: (cause) => argumentError(operation, cause)
222
- }).pipe(Effect.catch((error) => spendAndDrain(state, operation).pipe(Effect.andThen(Effect.fail(error)))))
189
+ }).pipe(Effect.catch((error) => spendAndDrain(state).pipe(Effect.andThen(Effect.fail(error)))))
223
190
  leftover = chunk.leftover
224
191
  done = chunk.done && leftover === undefined
225
192
  if (chunk.rows === 0n) {
226
193
  continue
227
194
  }
228
195
  yield* eventLoopTurn()
229
- yield* nativeOperationWith(
196
+ yield* call(
230
197
  operation,
231
198
  (callback) => verb(state.handle, relationId, chunk.rows, chunk.cells, callback),
232
- dbNative.runtimeReportTake,
233
- () => undefined
199
+ (lease) => {
200
+ addon.runtimeStagedTake(lease)
201
+ }
234
202
  ).pipe(
235
203
  Effect.catch((error) =>
236
204
  Effect.sync(() => {
@@ -252,18 +220,18 @@ function ingest(
252
220
  if (state.spent || Exit.isSuccess(exit)) {
253
221
  return Effect.void
254
222
  }
255
- return spendAndDrain(state, operation)
223
+ return spendAndDrain(state)
256
224
  })
257
225
  })
258
226
  }
259
227
 
260
228
  function decodeChangeRecord<S extends AnySchema>(
261
229
  relations: readonly SchemaRelation[],
262
- record: ChangeRecordWire
230
+ record: ChangeRecordOut
263
231
  ): ChangeRecord<S> {
264
232
  const relation = relations[record.relation]
265
- if (relation === undefined || isClosedMember(relation) || (record.kind !== "add" && record.kind !== "remove")) {
266
- throw new SdkInvariantError({ message: "ChangeSet.records: invalid native record descriptor" })
233
+ if (relation === undefined || isClosedMember(relation)) {
234
+ throw internalError("ChangeSet.records: invalid native record descriptor")
267
235
  }
268
236
  // Relation id resolves through this schema's own ordered roster. The
269
237
  // shared projector validates all field values before this union seam.
@@ -275,28 +243,25 @@ function decodeChangeRecord<S extends AnySchema>(
275
243
  }
276
244
 
277
245
  function changeRecords<S extends AnySchema>(
278
- handle: ChangesHandle,
246
+ handle: ChangesRef,
279
247
  relations: readonly SchemaRelation[]
280
248
  ): Stream.Stream<ChangeRecord<S>, DbError> {
281
249
  return Stream.unwrap(
282
250
  Effect.gen(function* () {
283
251
  const cursor = yield* Effect.acquireRelease(
284
- nativeOperationWith(
252
+ call(
285
253
  "ChangeSet.records",
286
- (callback) => dbNative.runtimeChangesCursor(handle, callback),
287
- dbNative.runtimeChangesCursorTake,
288
- (value) => value
254
+ (callback) => addon.runtimeChangesCursor(handle, callback),
255
+ addon.runtimeChangesCursorTake
289
256
  ),
290
- (value) =>
291
- releaseOwner("ChangeCursor.close", (callback) => dbNative.runtimeChangesCursorClose(value, callback)),
257
+ (value) => release("ChangeCursor.close", (callback) => addon.runtimeChangesCursorClose(value, callback)),
292
258
  { interruptible: true }
293
259
  )
294
260
  return Stream.paginate(undefined, () =>
295
- nativeOperationWith(
261
+ call(
296
262
  "ChangeSet.records",
297
- (callback) => dbNative.runtimeChangesCursorNext(cursor, callback),
298
- dbNative.runtimeChangePageTake,
299
- (page) => page
263
+ (callback) => addon.runtimeChangesCursorNext(cursor, callback),
264
+ addon.runtimeChangePageTake
300
265
  ).pipe(
301
266
  Effect.map((page) =>
302
267
  page === null
@@ -312,68 +277,59 @@ function changeRecords<S extends AnySchema>(
312
277
  function acquireChanges<S extends AnySchema>(
313
278
  theory: S,
314
279
  schemaId: SchemaId,
315
- acquire: Effect.Effect<ChangesWire, DbError>
280
+ acquire: Effect.Effect<ChangesOut, DbError>
316
281
  ): Effect.Effect<ChangeSet<S>, DbError, Scope.Scope> {
317
- return Effect.acquireRelease(
318
- acquire.pipe(Effect.map((wire) => makeChangeSet(theory, wire, schemaId))),
319
- (changes) =>
320
- Effect.suspend(() => {
321
- const internal = changesInternals.get(changes)
322
- if (internal === undefined) return Effect.void
323
- internal.closed = true
324
- return releaseOwner("ChangeSet.close", (callback) => dbNative.runtimeChangesClose(internal.handle, callback))
325
- }),
326
- { interruptible: true }
282
+ return Effect.map(
283
+ scoped("ChangeSet.release", acquire, (wire) => (done) => addon.runtimeChangesClose(wire.changes, done)),
284
+ (wire): ChangeSet<S> => new ChangeSetLive(theory, wire, schemaId)
327
285
  )
328
286
  }
329
287
 
330
- function makeChangeSet<S extends AnySchema>(theory: S, wire: ChangesWire, schemaId: SchemaId): ChangeSet<S> {
331
- const handle = wire.changes
332
- const relations = Object.values(theory.relations)
333
- const internal: ChangesInternal = { handle, schemaId, closed: false }
334
- const value: ChangeSet<S> = {
335
- schemaId,
336
- counts: Object.freeze({ ...wire.counts }),
337
- byteLength: wire.byteLength,
338
- records() {
339
- return changeRecords<S>(handle, relations)
340
- },
341
- toBytes() {
342
- return nativeOperationWith(
343
- "ChangeSet.toBytes",
344
- (callback) => dbNative.runtimeChangesBytes(handle, callback),
345
- dbNative.runtimeBytesTake,
346
- (bytes) => bytes
347
- )
348
- },
349
- compose(other) {
350
- return Effect.suspend(() => {
351
- const right = internalChanges(other)
352
- if (right === undefined || right.schemaId !== schemaId)
353
- return Effect.fail(refusal("ChangeSet.compose", "InvalidArgument"))
354
- if (internal.closed || right.closed) return Effect.fail(refusal("ChangeSet.compose", "ClosedHandle"))
355
- return acquireChanges(
356
- theory,
357
- schemaId,
358
- nativeOperationWith(
359
- "ChangeSet.compose",
360
- (callback) => dbNative.runtimeChangesCompose(handle, right.handle, callback),
361
- dbNative.runtimeChangesTake,
362
- (result) => result
363
- )
364
- )
365
- })
366
- },
367
- close() {
368
- return Effect.suspend(() => {
369
- internal.closed = true
370
- return drainClose("ChangeSet.close", (callback) => dbNative.runtimeChangesClose(handle, callback))
371
- })
372
- }
288
+ class ChangeSetLive<S extends AnySchema> implements ChangeSet<S> {
289
+ readonly #theory: S
290
+ readonly #handle: ChangesRef
291
+ readonly schemaId: SchemaId
292
+ readonly counts: ChangeCounts
293
+ readonly byteLength: bigint
294
+
295
+ constructor(theory: S, wire: ChangesOut, schemaId: SchemaId) {
296
+ this.#theory = theory
297
+ this.#handle = wire.changes
298
+ this.schemaId = schemaId
299
+ this.counts = Object.freeze({ ...wire.counts })
300
+ this.byteLength = wire.byteLength
301
+ }
302
+
303
+ records(): Stream.Stream<ChangeRecord<S>, DbError> {
304
+ return changeRecords<S>(this.#handle, Object.values(this.#theory.relations)).pipe(
305
+ Stream.withSpan("ChangeSet.records")
306
+ )
307
+ }
308
+
309
+ toBytes(): Effect.Effect<Uint8Array, DbError> {
310
+ const handle = this.#handle
311
+ return call("ChangeSet.toBytes", (done) => addon.runtimeChangesBytes(handle, done), addon.runtimeBytesTake).pipe(
312
+ Effect.withSpan("ChangeSet.toBytes")
313
+ )
314
+ }
315
+
316
+ compose(other: ChangeSet<S>): Effect.Effect<ChangeSet<S>, DbError, Scope.Scope> {
317
+ const left = this.#handle
318
+ const right = ChangeSetLive.handle(other, this.schemaId)
319
+ if (right === undefined) return Effect.fail(refusal("ChangeSet.compose", "InvalidArgument"))
320
+ return acquireChanges(
321
+ this.#theory,
322
+ this.schemaId,
323
+ call("ChangeSet.compose", (done) => addon.runtimeChangesCompose(left, right, done), addon.runtimeChangesTake)
324
+ ).pipe(Effect.withSpan("ChangeSet.compose"))
325
+ }
326
+
327
+ /** The native change set behind `value`, when `value` is one this SDK made for `schemaId`. */
328
+ static handle(value: object, schemaId: SchemaId): ChangesRef | undefined {
329
+ if (!(#handle in value)) return undefined
330
+ const changes = value as unknown as ChangeSetLive<AnySchema>
331
+ return changes.schemaId === schemaId ? changes.#handle : undefined
373
332
  }
374
- Object.freeze(value)
375
- changesInternals.set(value, internal)
376
- return value
377
333
  }
378
334
 
379
335
  function makeDraft<S extends AnySchema>(theory: S, state: DraftState, schemaId: SchemaId): ChangeDraft<S> {
@@ -393,25 +349,18 @@ function makeDraft<S extends AnySchema>(theory: S, state: DraftState, schemaId:
393
349
  return yield* Effect.fail(refusal("ChangeDraft.finish", "SpentHandle"))
394
350
  }
395
351
  if (state.inFlight) {
396
- yield* spendAndDrain(state, "ChangeDraft.finish")
352
+ yield* spendAndDrain(state)
397
353
  return yield* Effect.fail(refusal("ChangeDraft.finish", "SpentHandle"))
398
354
  }
399
355
  // Finish CONSUMES the draft, success or failure.
400
356
  state.spent = true
401
- return yield* nativeOperationWith(
357
+ return yield* call(
402
358
  "ChangeDraft.finish",
403
- (callback) => dbNative.runtimeDraftFinish(state.handle, callback),
404
- dbNative.runtimeChangesTake,
405
- (value) => value
359
+ (callback) => addon.runtimeDraftFinish(state.handle, callback),
360
+ addon.runtimeChangesTake
406
361
  )
407
362
  })
408
363
  )
409
- },
410
- close() {
411
- return Effect.suspend(() => {
412
- state.spent = true
413
- return drainClose("ChangeDraft.close", (callback) => dbNative.runtimeDraftClose(state.handle, callback))
414
- })
415
364
  }
416
365
  }
417
366
  return Object.freeze(draft)
@@ -420,73 +369,61 @@ function makeDraft<S extends AnySchema>(theory: S, state: DraftState, schemaId:
420
369
  /**
421
370
  * `ChangeSet.builder(schema)` — lazy scoped acquisition of a
422
371
  * database-free draft. Requires the acquired
423
- * `NativeRuntime`; the draft's native resources release with its scope, and
372
+ * `Bumble`; the draft's native resources release with its scope, and
424
373
  * the scope finalizer surfaces incomplete/failed teardown as a
425
374
  * `CloseFailure` defect.
426
375
  */
427
376
  const builder = Effect.fn("ChangeSet.builder")(function* <S extends AnySchema>(schema: S) {
428
- const handle = yield* runtimeHandle()
429
- const compiled = yield* CoreSchema.compile(schema)
430
- const spec = lower(compiled.schema)
431
- return yield* Effect.acquireRelease(
432
- Effect.gen(function* () {
433
- const draftHandle = yield* nativeOperationWith(
434
- "ChangeSet.builder",
435
- (callback) => dbNative.runtimeDraftOpen(handle, spec, callback),
436
- dbNative.runtimeDraftTake,
437
- (value) => value
438
- )
439
- const state: DraftState = {
440
- handle: draftHandle,
441
- theory: compiled.schema,
377
+ const handle = yield* runtimeHandle
378
+ const theory = yield* Effect.try({
379
+ try: () => schemaDescriptor(schema),
380
+ catch: (cause) => argumentError("ChangeSet.builder", cause)
381
+ })
382
+ const compiled = compiledOf(theory)
383
+ const state = yield* scoped(
384
+ "ChangeDraft.release",
385
+ Effect.try({
386
+ try: (): DraftState => ({
387
+ handle: addon.runtimeDraftOpen(handle, compiled.handle),
388
+ theory,
442
389
  spent: false,
443
390
  inFlight: false
444
- }
445
- const draft = makeDraft(compiled.schema, state, compiled.schemaId)
446
- draftStates.set(draft, state)
447
- return draft
448
- }),
449
- (draft) =>
450
- Effect.suspend(() => {
451
- const state = draftStates.get(draft)
452
- if (state === undefined) {
453
- return Effect.void
454
- }
455
- // Idempotent: repeated close joins the same native
456
- // transition; the finalizer runs it unconditionally so an
457
- // abandoned draft is always drained.
458
- state.spent = true
459
- return releaseOwner("ChangeDraft.close", (callback) => dbNative.runtimeDraftClose(state.handle, callback))
460
391
  }),
461
- { interruptible: true }
392
+ catch: (cause) => dbError("ChangeSet.builder", cause)
393
+ }),
394
+ (owned) => (done) => {
395
+ owned.spent = true
396
+ addon.runtimeDraftClose(owned.handle, done)
397
+ }
462
398
  )
399
+ return makeDraft(theory as S, state, compiled.schemaId)
463
400
  })
464
401
 
465
- // The draft value → state registry lets the scope finalizer reach the
466
- // native handle without exposing it on the public capability.
467
- const draftStates = new WeakMap<object, DraftState>()
468
-
469
402
  /** Parse canonical native bytes, owning the input when the Effect starts. */
470
403
  const fromBytes = Effect.fn("ChangeSet.fromBytes")(function* <S extends AnySchema>(schema: S, bytes: Uint8Array) {
471
404
  const ownedBytes = yield* Effect.try({
472
405
  try: () => bytesValue("ChangeSet.fromBytes", bytes),
473
406
  catch: (cause) => argumentError("ChangeSet.fromBytes", cause)
474
407
  })
475
- const handle = yield* runtimeHandle()
476
- const compiled = yield* CoreSchema.compile(schema)
408
+ const handle = yield* runtimeHandle
409
+ const theory = yield* Effect.try({
410
+ try: () => schemaDescriptor(schema),
411
+ catch: (cause) => argumentError("ChangeSet.fromBytes", cause)
412
+ })
413
+ const compiled = compiledOf(theory)
477
414
  return yield* acquireChanges(
478
- compiled.schema,
415
+ theory as S,
479
416
  compiled.schemaId,
480
- nativeOperationWith(
417
+ call(
481
418
  "ChangeSet.fromBytes",
482
- (callback) => dbNative.runtimeChangesParse(handle, lower(compiled.schema), ownedBytes, callback),
483
- dbNative.runtimeChangesTake,
484
- (wire) => wire
419
+ (done) => addon.runtimeChangesParse(handle, compiled.handle, ownedBytes, done),
420
+ addon.runtimeChangesTake
485
421
  )
486
422
  )
487
423
  })
488
424
 
489
425
  const ChangeSet = Object.freeze({ builder, fromBytes })
490
426
 
491
- export type { ChangeCounts, ChangeDraft, ChangeRecord }
492
- export { ChangeSet, internalChanges }
427
+ export type { ChangeCounts } from "./native/binding.d.ts"
428
+ export type { ChangeDraft, ChangeRecord }
429
+ export { ChangeSet, ChangeSetLive }
package/src/closed.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AuthoringError } from "#errors.ts"
1
+ import { AuthoringError } from "./errors.ts"
2
2
  import {
3
3
  type AnyField,
4
4
  assertDeclarationOrderKey,
@@ -8,8 +8,8 @@ import {
8
8
  type Infer,
9
9
  ownHandles,
10
10
  signaturesAgree
11
- } from "#fields.ts"
12
- import { descriptorCache } from "#immutable.ts"
11
+ } from "./fields.ts"
12
+ import { descriptorCache } from "./immutable.ts"
13
13
  import {
14
14
  type AnyRelation,
15
15
  type FieldsShape,
@@ -17,8 +17,8 @@ import {
17
17
  type RelationField,
18
18
  relationDescriptor,
19
19
  relationFields
20
- } from "#relation.ts"
21
- import { fieldValue, recordValue } from "#values.ts"
20
+ } from "./relation.ts"
21
+ import { fieldValue, recordValue } from "./values.ts"
22
22
 
23
23
  type PayloadField = AnyField
24
24
  type PayloadColumns = FieldsShape & { readonly id?: never }