@bjornpagen/bumbledb 1.3.1 → 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 +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/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,31 +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
- }
97
-
98
- const changesInternals = new WeakMap<object, ChangesInternal>()
99
-
100
- /** Private cross-module accessor (db.apply retains the native ChangeSet). */
101
- function internalChanges(value: object): ChangesInternal | undefined {
102
- return changesInternals.get(value)
103
- }
104
-
105
76
  function refusal(operation: string, reason: "SpentHandle" | "ClosedHandle" | "InvalidArgument"): DbError {
106
77
  return new DbError({ operation, reason: { _tag: reason } })
107
78
  }
108
79
 
109
80
  interface Chunk {
110
81
  readonly rows: bigint
111
- readonly cells: readonly CellValue[]
82
+ readonly cells: CellValue[]
112
83
  readonly done: boolean
113
84
  readonly leftover: object | undefined
114
85
  }
@@ -167,16 +138,13 @@ function pullChunk(relation: AnyRelation, iterator: Iterator<object>, pending: o
167
138
  return { rows, cells, done: false, leftover }
168
139
  }
169
140
 
170
- function spendAndDrain(state: DraftState, operation: string): Effect.Effect<void> {
141
+ function spendAndDrain(state: DraftState): Effect.Effect<void> {
171
142
  return Effect.suspend(() => {
172
143
  if (state.spent) {
173
144
  return Effect.void
174
145
  }
175
146
  state.spent = true
176
- // Tracked drain: join the native close transition; the report is
177
- // diagnostic here (the ingestion failure itself is the caller's
178
- // error), but native Closing accounting is never dropped.
179
- return drainClose(operation, (callback) => dbNative.runtimeDraftClose(state.handle, callback)).pipe(Effect.asVoid)
147
+ return drain((done) => addon.runtimeDraftClose(state.handle, done)).pipe(Effect.asVoid)
180
148
  })
181
149
  }
182
150
 
@@ -186,7 +154,7 @@ function ingest(
186
154
  relation: AnyRelation,
187
155
  rows: Iterable<object>
188
156
  ): Effect.Effect<void, DbError> {
189
- const verb = operation === "ChangeDraft.insert" ? dbNative.runtimeDraftInsert : dbNative.runtimeDraftDelete
157
+ const verb = operation === "ChangeDraft.insert" ? addon.runtimeDraftInsert : addon.runtimeDraftDelete
190
158
  return Effect.gen(function* () {
191
159
  if (state.spent) {
192
160
  return yield* Effect.fail(refusal(operation, "SpentHandle"))
@@ -194,7 +162,7 @@ function ingest(
194
162
  if (state.inFlight) {
195
163
  // Reentrant construction refuses AND spends/drains — there is
196
164
  // no implicit queue.
197
- yield* spendAndDrain(state, operation)
165
+ yield* spendAndDrain(state)
198
166
  return yield* Effect.fail(refusal(operation, "SpentHandle"))
199
167
  }
200
168
  if (!membersAgree(state.theory.relations[relation.name], relation)) {
@@ -218,18 +186,19 @@ function ingest(
218
186
  const chunk = yield* Effect.try({
219
187
  try: () => pullChunk(relation, iterator, leftover),
220
188
  catch: (cause) => argumentError(operation, cause)
221
- }).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)))))
222
190
  leftover = chunk.leftover
223
191
  done = chunk.done && leftover === undefined
224
192
  if (chunk.rows === 0n) {
225
193
  continue
226
194
  }
227
195
  yield* eventLoopTurn()
228
- yield* nativeOperationWith(
196
+ yield* call(
229
197
  operation,
230
198
  (callback) => verb(state.handle, relationId, chunk.rows, chunk.cells, callback),
231
- dbNative.runtimeReportTake,
232
- () => undefined
199
+ (lease) => {
200
+ addon.runtimeStagedTake(lease)
201
+ }
233
202
  ).pipe(
234
203
  Effect.catch((error) =>
235
204
  Effect.sync(() => {
@@ -251,18 +220,18 @@ function ingest(
251
220
  if (state.spent || Exit.isSuccess(exit)) {
252
221
  return Effect.void
253
222
  }
254
- return spendAndDrain(state, operation)
223
+ return spendAndDrain(state)
255
224
  })
256
225
  })
257
226
  }
258
227
 
259
228
  function decodeChangeRecord<S extends AnySchema>(
260
229
  relations: readonly SchemaRelation[],
261
- record: ChangeRecordWire
230
+ record: ChangeRecordOut
262
231
  ): ChangeRecord<S> {
263
232
  const relation = relations[record.relation]
264
- if (relation === undefined || isClosedMember(relation) || (record.kind !== "add" && record.kind !== "remove")) {
265
- 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")
266
235
  }
267
236
  // Relation id resolves through this schema's own ordered roster. The
268
237
  // shared projector validates all field values before this union seam.
@@ -274,28 +243,25 @@ function decodeChangeRecord<S extends AnySchema>(
274
243
  }
275
244
 
276
245
  function changeRecords<S extends AnySchema>(
277
- handle: ChangesHandle,
246
+ handle: ChangesRef,
278
247
  relations: readonly SchemaRelation[]
279
248
  ): Stream.Stream<ChangeRecord<S>, DbError> {
280
249
  return Stream.unwrap(
281
250
  Effect.gen(function* () {
282
251
  const cursor = yield* Effect.acquireRelease(
283
- nativeOperationWith(
252
+ call(
284
253
  "ChangeSet.records",
285
- (callback) => dbNative.runtimeChangesCursor(handle, callback),
286
- dbNative.runtimeChangesCursorTake,
287
- (value) => value
254
+ (callback) => addon.runtimeChangesCursor(handle, callback),
255
+ addon.runtimeChangesCursorTake
288
256
  ),
289
- (value) =>
290
- releaseOwner("ChangeCursor.close", (callback) => dbNative.runtimeChangesCursorClose(value, callback)),
257
+ (value) => release("ChangeCursor.close", (callback) => addon.runtimeChangesCursorClose(value, callback)),
291
258
  { interruptible: true }
292
259
  )
293
260
  return Stream.paginate(undefined, () =>
294
- nativeOperationWith(
261
+ call(
295
262
  "ChangeSet.records",
296
- (callback) => dbNative.runtimeChangesCursorNext(cursor, callback),
297
- dbNative.runtimeChangePageTake,
298
- (page) => page
263
+ (callback) => addon.runtimeChangesCursorNext(cursor, callback),
264
+ addon.runtimeChangePageTake
299
265
  ).pipe(
300
266
  Effect.map((page) =>
301
267
  page === null
@@ -311,63 +277,59 @@ function changeRecords<S extends AnySchema>(
311
277
  function acquireChanges<S extends AnySchema>(
312
278
  theory: S,
313
279
  schemaId: SchemaId,
314
- acquire: Effect.Effect<ChangesWire, DbError>
280
+ acquire: Effect.Effect<ChangesOut, DbError>
315
281
  ): Effect.Effect<ChangeSet<S>, DbError, Scope.Scope> {
316
- return Effect.acquireRelease(
317
- acquire.pipe(Effect.map((wire) => makeChangeSet(theory, wire, schemaId))),
318
- (changes) =>
319
- Effect.suspend(() => {
320
- const internal = changesInternals.get(changes)
321
- if (internal === undefined) return Effect.void
322
- return releaseOwner("ChangeSet.close", (callback) => dbNative.runtimeChangesClose(internal.handle, callback))
323
- }),
324
- { 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)
325
285
  )
326
286
  }
327
287
 
328
- function makeChangeSet<S extends AnySchema>(theory: S, wire: ChangesWire, schemaId: SchemaId): ChangeSet<S> {
329
- const handle = wire.changes
330
- const relations = Object.values(theory.relations)
331
- const internal: ChangesInternal = { handle, schemaId }
332
- const value: ChangeSet<S> = {
333
- schemaId,
334
- counts: Object.freeze({ ...wire.counts }),
335
- byteLength: wire.byteLength,
336
- records() {
337
- return changeRecords<S>(handle, relations)
338
- },
339
- toBytes() {
340
- return nativeOperationWith(
341
- "ChangeSet.toBytes",
342
- (callback) => dbNative.runtimeChangesBytes(handle, callback),
343
- dbNative.runtimeBytesTake,
344
- (bytes) => bytes
345
- )
346
- },
347
- compose(other) {
348
- return Effect.suspend(() => {
349
- const right = internalChanges(other)
350
- if (right === undefined || right.schemaId !== schemaId)
351
- return Effect.fail(refusal("ChangeSet.compose", "InvalidArgument"))
352
- return acquireChanges(
353
- theory,
354
- schemaId,
355
- nativeOperationWith(
356
- "ChangeSet.compose",
357
- (callback) => dbNative.runtimeChangesCompose(handle, right.handle, callback),
358
- dbNative.runtimeChangesTake,
359
- (result) => result
360
- )
361
- )
362
- })
363
- },
364
- close() {
365
- return drainClose("ChangeSet.close", (callback) => dbNative.runtimeChangesClose(handle, callback))
366
- }
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
367
332
  }
368
- Object.freeze(value)
369
- changesInternals.set(value, internal)
370
- return value
371
333
  }
372
334
 
373
335
  function makeDraft<S extends AnySchema>(theory: S, state: DraftState, schemaId: SchemaId): ChangeDraft<S> {
@@ -387,25 +349,18 @@ function makeDraft<S extends AnySchema>(theory: S, state: DraftState, schemaId:
387
349
  return yield* Effect.fail(refusal("ChangeDraft.finish", "SpentHandle"))
388
350
  }
389
351
  if (state.inFlight) {
390
- yield* spendAndDrain(state, "ChangeDraft.finish")
352
+ yield* spendAndDrain(state)
391
353
  return yield* Effect.fail(refusal("ChangeDraft.finish", "SpentHandle"))
392
354
  }
393
355
  // Finish CONSUMES the draft, success or failure.
394
356
  state.spent = true
395
- return yield* nativeOperationWith(
357
+ return yield* call(
396
358
  "ChangeDraft.finish",
397
- (callback) => dbNative.runtimeDraftFinish(state.handle, callback),
398
- dbNative.runtimeChangesTake,
399
- (value) => value
359
+ (callback) => addon.runtimeDraftFinish(state.handle, callback),
360
+ addon.runtimeChangesTake
400
361
  )
401
362
  })
402
363
  )
403
- },
404
- close() {
405
- return Effect.suspend(() => {
406
- state.spent = true
407
- return drainClose("ChangeDraft.close", (callback) => dbNative.runtimeDraftClose(state.handle, callback))
408
- })
409
364
  }
410
365
  }
411
366
  return Object.freeze(draft)
@@ -414,73 +369,61 @@ function makeDraft<S extends AnySchema>(theory: S, state: DraftState, schemaId:
414
369
  /**
415
370
  * `ChangeSet.builder(schema)` — lazy scoped acquisition of a
416
371
  * database-free draft. Requires the acquired
417
- * `NativeRuntime`; the draft's native resources release with its scope, and
372
+ * `Bumble`; the draft's native resources release with its scope, and
418
373
  * the scope finalizer surfaces incomplete/failed teardown as a
419
374
  * `CloseFailure` defect.
420
375
  */
421
376
  const builder = Effect.fn("ChangeSet.builder")(function* <S extends AnySchema>(schema: S) {
422
- const handle = yield* runtimeHandle()
423
- const compiled = yield* CoreSchema.compile(schema)
424
- const spec = lower(compiled.schema)
425
- return yield* Effect.acquireRelease(
426
- Effect.gen(function* () {
427
- const draftHandle = yield* nativeOperationWith(
428
- "ChangeSet.builder",
429
- (callback) => dbNative.runtimeDraftOpen(handle, spec, callback),
430
- dbNative.runtimeDraftTake,
431
- (value) => value
432
- )
433
- const state: DraftState = {
434
- handle: draftHandle,
435
- 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,
436
389
  spent: false,
437
390
  inFlight: false
438
- }
439
- const draft = makeDraft(compiled.schema, state, compiled.schemaId)
440
- draftStates.set(draft, state)
441
- return draft
442
- }),
443
- (draft) =>
444
- Effect.suspend(() => {
445
- const state = draftStates.get(draft)
446
- if (state === undefined) {
447
- return Effect.void
448
- }
449
- // Idempotent: repeated close joins the same native
450
- // transition; the finalizer runs it unconditionally so an
451
- // abandoned draft is always drained.
452
- state.spent = true
453
- return releaseOwner("ChangeDraft.close", (callback) => dbNative.runtimeDraftClose(state.handle, callback))
454
391
  }),
455
- { interruptible: true }
392
+ catch: (cause) => dbError("ChangeSet.builder", cause)
393
+ }),
394
+ (owned) => (done) => {
395
+ owned.spent = true
396
+ addon.runtimeDraftClose(owned.handle, done)
397
+ }
456
398
  )
399
+ return makeDraft(theory as S, state, compiled.schemaId)
457
400
  })
458
401
 
459
- // The draft value → state registry lets the scope finalizer reach the
460
- // native handle without exposing it on the public capability.
461
- const draftStates = new WeakMap<object, DraftState>()
462
-
463
402
  /** Parse canonical native bytes, owning the input when the Effect starts. */
464
403
  const fromBytes = Effect.fn("ChangeSet.fromBytes")(function* <S extends AnySchema>(schema: S, bytes: Uint8Array) {
465
404
  const ownedBytes = yield* Effect.try({
466
405
  try: () => bytesValue("ChangeSet.fromBytes", bytes),
467
406
  catch: (cause) => argumentError("ChangeSet.fromBytes", cause)
468
407
  })
469
- const handle = yield* runtimeHandle()
470
- 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)
471
414
  return yield* acquireChanges(
472
- compiled.schema,
415
+ theory as S,
473
416
  compiled.schemaId,
474
- nativeOperationWith(
417
+ call(
475
418
  "ChangeSet.fromBytes",
476
- (callback) => dbNative.runtimeChangesParse(handle, lower(compiled.schema), ownedBytes, callback),
477
- dbNative.runtimeChangesTake,
478
- (wire) => wire
419
+ (done) => addon.runtimeChangesParse(handle, compiled.handle, ownedBytes, done),
420
+ addon.runtimeChangesTake
479
421
  )
480
422
  )
481
423
  })
482
424
 
483
425
  const ChangeSet = Object.freeze({ builder, fromBytes })
484
426
 
485
- export type { ChangeCounts, ChangeDraft, ChangeRecord }
486
- 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 }
package/src/codec.ts CHANGED
@@ -1,41 +1,26 @@
1
1
  import { Effect, Schema as EffectSchema, Result } from "effect"
2
- import { membersAgree } from "#closed.ts"
3
- import { schemaTables } from "#compile.ts"
4
- import { dbNative } from "#db-native.ts"
5
- import { AuthoringError } from "#errors.ts"
6
- import { type AnyField, fieldDescriptor, type Infer } from "#fields.ts"
7
- import { lower } from "#lower.ts"
8
- import { type AnyRelation, type Fact, relationDescriptor, relationFields } from "#relation.ts"
9
- import type { CellValue } from "#rows.ts"
10
- import { factOfCells, flatRowsOf } from "#rows.ts"
11
- import { nativeOperationWith, runtimeHandle } from "#runtime.ts"
12
- import { argumentError, DbError } from "#runtime-errors.ts"
13
- import type { AnySchema } from "#schema.ts"
14
- import { schemaDescriptor } from "#schema.ts"
15
- import type { Rel } from "#shape.ts"
16
- import { f64BitsHex } from "#spec.ts"
17
- import { fieldValue, recordValue } from "#values.ts"
2
+ import { membersAgree } from "./closed.ts"
3
+ import { compiledOf, schemaTables } from "./compile.ts"
4
+ import { AuthoringError, argumentError, DbError } from "./errors.ts"
5
+ import { type AnyField, fieldDescriptor, type Infer } from "./fields.ts"
6
+ import { addon } from "./native/addon.ts"
7
+ import { call } from "./native/op.ts"
8
+ import { type AnyRelation, type Fact, relationDescriptor, relationFields } from "./relation.ts"
9
+ import type { CellValue } from "./rows.ts"
10
+ import { factOfCells, flatRowsOf } from "./rows.ts"
11
+ import { runtimeHandle } from "./runtime.ts"
12
+ import type { AnySchema } from "./schema.ts"
13
+ import { schemaDescriptor } from "./schema.ts"
14
+ import type { Rel } from "./shape.ts"
15
+ import { f64BitsHex } from "./spec.ts"
16
+ import { fieldValue, recordValue } from "./values.ts"
18
17
 
19
18
  /**
20
- * Boundary row codecs, derived from the core relation descriptors — never a
21
- * second hand-maintained field roster.
22
- *
23
- * Two layers:
24
- *
25
- * 1. The CANONICAL native row codec: `encodeRows`/`decodeRows` run the
26
- * same native implementation the engine, log and migrations
27
- * share (owned bytes in, owned typed rows out; untrusted input cannot
28
- * inject native capabilities). Effect-only, `NativeRuntime` required.
29
- * 2. The schema-tagged JSON VALUE form for HTTP/export boundaries
30
- * (chapter 30): every `f64` — finite included — is
31
- * `{"$f64":"<16 lowercase hex digits>"}` of canonical binary64 bits;
32
- * integers are canonical decimal strings; `Uuid` is canonical UUID;
33
- * bytes use ONE strict lowercase-hex encoding; intervals are
34
- * `{start,end}` in their element encoding; closed references are handle
35
- * names. `JSON.stringify` of raw numbers is NOT the database value
36
- * wire codec — it loses infinities/NaN and cannot encode BigInt.
37
- * Decoders reject malformed widths, unknown tags and noncanonical
38
- * representations. These are pure bounded per-row functions.
19
+ * Row codecs derived from the relation descriptors: the canonical native row codec
20
+ * (`encodeRows`/`decodeRows`, the engine's own bytes) and the schema-tagged JSON value form for
21
+ * HTTP/export boundaries, where an `f64` is `{"$f64":"<16 hex>"}` of its canonical bits (raw JSON
22
+ * numbers lose infinities and NaN), integers are decimal strings, bytes are lowercase hex and
23
+ * closed references are handle names. Decoders reject anything noncanonical.
39
24
  */
40
25
 
41
26
  /** The typed row shape: a schema plus one of its relations. */
@@ -58,7 +43,7 @@ function checkedRowShape<R extends AnyRelation>(operation: string, input: RowSha
58
43
  if (relationId === undefined || !membersAgree(schema.relations[relation.name], relation)) {
59
44
  throw new AuthoringError({ message: `${operation}: relation is not declared in the schema` })
60
45
  }
61
- return { relation, relationId, spec: lower(schema) }
46
+ return { relation, relationId, schema: compiledOf(schema).handle }
62
47
  }
63
48
 
64
49
  function invalid(operation: string): DbError {
@@ -271,7 +256,7 @@ function rowSchema<R extends AnyRelation>(relation: R) {
271
256
  }
272
257
 
273
258
  /**
274
- * The canonical native row codec (chapter 35 roster): owned bytes
259
+ * The canonical native row codec: owned bytes
275
260
  * out, owned typed rows back in — the same implementation log sealing and
276
261
  * migrations use. Binding parameters is ingestion: input must stay stable
277
262
  * through execution, and no native work starts before the checked owned
@@ -281,8 +266,8 @@ const encodeRows = Effect.fn("encodeRows")(function* <R extends AnyRelation>(
281
266
  shape: RowShape<R>,
282
267
  rows: Iterable<Fact<R>>
283
268
  ) {
284
- const runtime = yield* runtimeHandle()
285
- const { relation, relationId, spec } = yield* Effect.try({
269
+ const runtime = yield* runtimeHandle
270
+ const { relation, relationId, schema } = yield* Effect.try({
286
271
  try: () => checkedRowShape("encodeRows", shape),
287
272
  catch: (cause) => argumentError("encodeRows", cause)
288
273
  })
@@ -290,28 +275,29 @@ const encodeRows = Effect.fn("encodeRows")(function* <R extends AnyRelation>(
290
275
  try: () => flatRowsOf(relation, rows as Iterable<object>),
291
276
  catch: (cause) => argumentError("encodeRows", cause)
292
277
  })
293
- return yield* nativeOperationWith(
278
+ return yield* call(
294
279
  "encodeRows",
295
- (callback) => dbNative.runtimeEncodeRows(runtime, spec, relationId, flat.rows, flat.cells, callback),
296
- dbNative.runtimeBytesTake,
297
- (bytes) => bytes
280
+ (callback) => addon.runtimeEncodeRows(runtime, schema, relationId, flat.rows, flat.cells, callback),
281
+ addon.runtimeBytesTake
298
282
  )
299
283
  })
300
284
 
301
285
  const decodeRows = Effect.fn("decodeRows")(function* <R extends AnyRelation>(shape: RowShape<R>, input: Uint8Array) {
302
- const runtime = yield* runtimeHandle()
303
- const { relation, relationId, spec } = yield* Effect.try({
286
+ const runtime = yield* runtimeHandle
287
+ const { relation, relationId, schema } = yield* Effect.try({
304
288
  try: () => checkedRowShape("decodeRows", shape),
305
289
  catch: (cause) => argumentError("decodeRows", cause)
306
290
  })
307
291
  if (!(input instanceof Uint8Array) || !(input.buffer instanceof ArrayBuffer)) {
308
292
  return yield* Effect.fail(invalid("decodeRows"))
309
293
  }
310
- return yield* nativeOperationWith(
294
+ return yield* call(
311
295
  "decodeRows",
312
- (callback) => dbNative.runtimeDecodeRows(runtime, spec, relationId, input, callback),
313
- dbNative.runtimeRowsTake,
314
- (rows) => Object.freeze(rows.map((row) => factOfCells(relation, row as readonly CellValue[])))
296
+ (callback) => addon.runtimeDecodeRows(runtime, schema, relationId, input, callback),
297
+ (lease) =>
298
+ ((rows) => Object.freeze(rows.map((row) => factOfCells(relation, row as readonly CellValue[]))))(
299
+ addon.runtimeRowsTake(lease)
300
+ )
315
301
  )
316
302
  })
317
303