@bjornpagen/bumbledb 1.0.1 → 1.2.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 (172) hide show
  1. package/COOKBOOK.md +176 -36
  2. package/README.md +70 -26
  3. package/dist/capacity.d.ts +15 -55
  4. package/dist/capacity.d.ts.map +1 -1
  5. package/dist/capacity.js +63 -12
  6. package/dist/capacity.js.map +1 -1
  7. package/dist/changes.d.ts +28 -6
  8. package/dist/changes.d.ts.map +1 -1
  9. package/dist/changes.js +119 -71
  10. package/dist/changes.js.map +1 -1
  11. package/dist/closed.d.ts +23 -51
  12. package/dist/closed.d.ts.map +1 -1
  13. package/dist/closed.js +110 -168
  14. package/dist/closed.js.map +1 -1
  15. package/dist/codec.d.ts +8 -14
  16. package/dist/codec.d.ts.map +1 -1
  17. package/dist/codec.js +113 -188
  18. package/dist/codec.js.map +1 -1
  19. package/dist/compile.d.ts +17 -18
  20. package/dist/compile.d.ts.map +1 -1
  21. package/dist/compile.js +41 -20
  22. package/dist/compile.js.map +1 -1
  23. package/dist/db-native.d.ts +76 -41
  24. package/dist/db-native.d.ts.map +1 -1
  25. package/dist/db-native.js +8 -0
  26. package/dist/db-native.js.map +1 -1
  27. package/dist/db.d.ts +61 -26
  28. package/dist/db.d.ts.map +1 -1
  29. package/dist/db.js +152 -85
  30. package/dist/db.js.map +1 -1
  31. package/dist/errors.d.ts +11 -3
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +13 -1
  34. package/dist/errors.js.map +1 -1
  35. package/dist/face.d.ts +19 -25
  36. package/dist/face.d.ts.map +1 -1
  37. package/dist/face.js +30 -24
  38. package/dist/face.js.map +1 -1
  39. package/dist/fields.d.ts +13 -26
  40. package/dist/fields.d.ts.map +1 -1
  41. package/dist/fields.js +89 -116
  42. package/dist/fields.js.map +1 -1
  43. package/dist/immutable.d.ts +10 -0
  44. package/dist/immutable.d.ts.map +1 -0
  45. package/dist/immutable.js +75 -0
  46. package/dist/immutable.js.map +1 -0
  47. package/dist/index.d.ts +16 -12
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +6 -4
  50. package/dist/index.js.map +1 -1
  51. package/dist/internal/log.d.ts +6 -4
  52. package/dist/internal/log.d.ts.map +1 -1
  53. package/dist/internal/log.js +3 -1
  54. package/dist/internal/log.js.map +1 -1
  55. package/dist/law.d.ts +11 -11
  56. package/dist/law.d.ts.map +1 -1
  57. package/dist/law.js +1 -1
  58. package/dist/law.js.map +1 -1
  59. package/dist/lower.d.ts.map +1 -1
  60. package/dist/lower.js +11 -6
  61. package/dist/lower.js.map +1 -1
  62. package/dist/migration.d.ts +2 -3
  63. package/dist/migration.d.ts.map +1 -1
  64. package/dist/migration.js +5 -5
  65. package/dist/migration.js.map +1 -1
  66. package/dist/native.d.ts +2 -2
  67. package/dist/native.d.ts.map +1 -1
  68. package/dist/native.js.map +1 -1
  69. package/dist/query/atom.d.ts +1 -1
  70. package/dist/query/atom.d.ts.map +1 -1
  71. package/dist/query/atom.js.map +1 -1
  72. package/dist/query/description.d.ts +33 -0
  73. package/dist/query/description.d.ts.map +1 -0
  74. package/dist/query/description.js +302 -0
  75. package/dist/query/description.js.map +1 -0
  76. package/dist/query/lower.d.ts +60 -4
  77. package/dist/query/lower.d.ts.map +1 -1
  78. package/dist/query/lower.js +126 -161
  79. package/dist/query/lower.js.map +1 -1
  80. package/dist/query/parse-ir.d.ts +7 -2
  81. package/dist/query/parse-ir.d.ts.map +1 -1
  82. package/dist/query/parse-ir.js +240 -53
  83. package/dist/query/parse-ir.js.map +1 -1
  84. package/dist/query/run.d.ts.map +1 -1
  85. package/dist/query/run.js +8 -10
  86. package/dist/query/run.js.map +1 -1
  87. package/dist/query/scope.d.ts +3 -4
  88. package/dist/query/scope.d.ts.map +1 -1
  89. package/dist/query/scope.js +5 -10
  90. package/dist/query/scope.js.map +1 -1
  91. package/dist/relation.d.ts +22 -48
  92. package/dist/relation.d.ts.map +1 -1
  93. package/dist/relation.js +30 -82
  94. package/dist/relation.js.map +1 -1
  95. package/dist/result.d.ts +10 -25
  96. package/dist/result.d.ts.map +1 -1
  97. package/dist/result.js +7 -12
  98. package/dist/result.js.map +1 -1
  99. package/dist/rows.d.ts +11 -14
  100. package/dist/rows.d.ts.map +1 -1
  101. package/dist/rows.js +49 -237
  102. package/dist/rows.js.map +1 -1
  103. package/dist/runtime-codes.d.ts +1 -1
  104. package/dist/runtime-codes.d.ts.map +1 -1
  105. package/dist/runtime-codes.js +1 -2
  106. package/dist/runtime-codes.js.map +1 -1
  107. package/dist/runtime-errors.d.ts +27 -15
  108. package/dist/runtime-errors.d.ts.map +1 -1
  109. package/dist/runtime-errors.js +44 -8
  110. package/dist/runtime-errors.js.map +1 -1
  111. package/dist/runtime-native.d.ts +16 -34
  112. package/dist/runtime-native.d.ts.map +1 -1
  113. package/dist/runtime-native.js.map +1 -1
  114. package/dist/runtime.d.ts +11 -28
  115. package/dist/runtime.d.ts.map +1 -1
  116. package/dist/runtime.js +14 -54
  117. package/dist/runtime.js.map +1 -1
  118. package/dist/schema.d.ts +9 -1
  119. package/dist/schema.d.ts.map +1 -1
  120. package/dist/schema.js +85 -24
  121. package/dist/schema.js.map +1 -1
  122. package/dist/selection.d.ts +27 -0
  123. package/dist/selection.d.ts.map +1 -0
  124. package/dist/selection.js +104 -0
  125. package/dist/selection.js.map +1 -0
  126. package/dist/shape.d.ts +6 -9
  127. package/dist/shape.d.ts.map +1 -1
  128. package/dist/statements.d.ts +17 -52
  129. package/dist/statements.d.ts.map +1 -1
  130. package/dist/statements.js +92 -111
  131. package/dist/statements.js.map +1 -1
  132. package/dist/values.d.ts +25 -0
  133. package/dist/values.d.ts.map +1 -0
  134. package/dist/values.js +203 -0
  135. package/dist/values.js.map +1 -0
  136. package/pack-provenance.json +3 -3
  137. package/package.json +4 -4
  138. package/src/capacity.ts +86 -76
  139. package/src/changes.ts +225 -94
  140. package/src/closed.ts +161 -314
  141. package/src/codec.ts +123 -213
  142. package/src/compile.ts +61 -38
  143. package/src/db-native.ts +73 -68
  144. package/src/db.ts +268 -165
  145. package/src/errors.ts +16 -1
  146. package/src/face.ts +44 -74
  147. package/src/fields.ts +100 -146
  148. package/src/immutable.ts +76 -0
  149. package/src/index.ts +38 -31
  150. package/src/internal/log.ts +4 -5
  151. package/src/law.ts +14 -14
  152. package/src/lower.ts +13 -10
  153. package/src/migration.ts +5 -12
  154. package/src/native.ts +2 -1
  155. package/src/query/atom.ts +1 -0
  156. package/src/query/description.ts +394 -0
  157. package/src/query/lower.ts +139 -173
  158. package/src/query/parse-ir.ts +247 -54
  159. package/src/query/run.ts +15 -9
  160. package/src/query/scope.ts +10 -18
  161. package/src/relation.ts +58 -142
  162. package/src/result.ts +16 -45
  163. package/src/rows.ts +57 -250
  164. package/src/runtime-codes.ts +1 -2
  165. package/src/runtime-errors.ts +48 -8
  166. package/src/runtime-native.ts +13 -46
  167. package/src/runtime.ts +51 -119
  168. package/src/schema.ts +99 -29
  169. package/src/selection.ts +124 -0
  170. package/src/shape.ts +9 -9
  171. package/src/statements.ts +138 -189
  172. package/src/values.ts +223 -0
@@ -34,7 +34,7 @@ export interface RepositoryLockHandle {
34
34
  }
35
35
 
36
36
  /** Resource kinds installed in native worker tables. */
37
- export type NativeKind = "snapshot" | "result" | "cursor" | "draft" | "changes" | "repository-lock"
37
+ export type NativeKind = "snapshot" | "prepared" | "result" | "cursor" | "draft" | "changes" | "repository-lock"
38
38
 
39
39
  /**
40
40
  * Checked capability into the native registry. JS tokens validate
@@ -96,28 +96,13 @@ export type LogTakeWire =
96
96
  readonly digest: Uint8Array
97
97
  }
98
98
 
99
- export interface PolicyWire {
100
- readonly inputBytes: bigint
101
- readonly workingBytes: bigint
102
- readonly scratchBytes: bigint
103
- readonly resultBytes: bigint
104
- readonly rows: bigint
105
- readonly workUnits: bigint
106
- readonly timeoutMs: number
107
- }
108
-
109
99
  export interface OptionsWire {
110
- readonly workers: number
111
- readonly queueCapacity: number
112
- readonly cleanupCapacity: number
113
- readonly ownerCapacity: number
114
- readonly nativeHandleCapacity: number
115
- readonly inputBytes: bigint
116
- readonly workingBytes: bigint
117
- readonly scratchBytes: bigint
118
- readonly resultBytes: bigint
119
- readonly chunkBytes: bigint
120
- readonly cleanupTimeoutMs: number
100
+ readonly workers?: number | undefined
101
+ readonly queueCapacity?: number | undefined
102
+ readonly cleanupCapacity?: number | undefined
103
+ readonly ownerCapacity?: number | undefined
104
+ readonly nativeHandleCapacity?: number | undefined
105
+ readonly cleanupTimeoutMs?: number | undefined
121
106
  }
122
107
 
123
108
  export interface InspectionWire {
@@ -128,10 +113,6 @@ export interface InspectionWire {
128
113
  readonly owners: bigint
129
114
  readonly databases: bigint
130
115
  readonly natives: bigint
131
- readonly inputBytes: bigint
132
- readonly workingBytes: bigint
133
- readonly scratchBytes: bigint
134
- readonly resultBytes: bigint
135
116
  }
136
117
 
137
118
  export type CloseWire =
@@ -142,8 +123,8 @@ export type CloseWire =
142
123
  interface RuntimeNative {
143
124
  runtimeErrorCodes(): readonly string[]
144
125
  runtimeOpen(options: OptionsWire): RuntimeHandle
145
- runtimeReady(runtime: RuntimeHandle, policy: PolicyWire, callback: () => void): OperationHandle
146
- runtimeHash(runtime: RuntimeHandle, policy: PolicyWire, bytes: Uint8Array, callback: () => void): OperationHandle
126
+ runtimeReady(runtime: RuntimeHandle, callback: () => void): OperationHandle
127
+ runtimeHash(runtime: RuntimeHandle, bytes: Uint8Array, callback: () => void): OperationHandle
147
128
  runtimeTake(operation: OperationHandle): Uint8Array | null
148
129
  runtimeCancel(operation: OperationHandle, callback: (report: CloseWire) => void): void
149
130
  runtimeClose(runtime: RuntimeHandle, callback: (report: CloseWire) => void): void
@@ -156,20 +137,14 @@ interface RuntimeNative {
156
137
  * Predelivery: no JS take; cursor stays on row1 so retry does not skip.
157
138
  */
158
139
  runtimeArmPublicationCancel(runtime: RuntimeHandle): void
159
- runtimeDirectoryAcquire(
160
- runtime: RuntimeHandle,
161
- policy: PolicyWire,
162
- path: string,
163
- callback: () => void
164
- ): OperationHandle
140
+ runtimeDirectoryAcquire(runtime: RuntimeHandle, path: string, callback: () => void): OperationHandle
165
141
  runtimeDirectoryTake(operation: OperationHandle): DirectoryHandle
166
- runtimeDirectoryBegin(owner: DirectoryHandle, policy: PolicyWire): OperationHandle
142
+ runtimeDirectoryBegin(owner: DirectoryHandle): OperationHandle
167
143
  runtimeDirectoryCheck(operation: OperationHandle): void
168
144
  runtimeDirectoryEnd(operation: OperationHandle): void
169
145
  runtimeDirectoryClose(owner: DirectoryHandle, remove: boolean, callback: (report: CloseWire) => void): void
170
146
  runtimeDirectoryDbOpen(
171
147
  owner: DirectoryHandle,
172
- policy: PolicyWire,
173
148
  childName: string,
174
149
  spec: SchemaSpec,
175
150
  create: boolean,
@@ -178,12 +153,11 @@ interface RuntimeNative {
178
153
  runtimeDbTake(operation: OperationHandle): ManagedDbOutcome
179
154
  runtimeManagedDbClose(db: DbHandle, callback: (report: CloseWire) => void): void
180
155
 
181
- // --- successor log grammar on the executor (charged hashing over
156
+ // --- successor log grammar on the executor (hashing over
182
157
  // whole canonical change payloads; C06 through C09) ---
183
158
  runtimeLogCommandSeal(
184
159
  runtime: RuntimeHandle,
185
160
  schema: LogSchemaHandle,
186
- policy: PolicyWire,
187
161
  metadata: LogCommandMetadata,
188
162
  changes: Uint8Array,
189
163
  result: Uint8Array | null,
@@ -193,14 +167,12 @@ interface RuntimeNative {
193
167
  runtimeLogCommandParse(
194
168
  runtime: RuntimeHandle,
195
169
  schema: LogSchemaHandle,
196
- policy: PolicyWire,
197
170
  bytes: Uint8Array,
198
171
  limits: LogLimits,
199
172
  callback: () => void
200
173
  ): OperationHandle
201
174
  runtimeLogDecisionDecode(
202
175
  runtime: RuntimeHandle,
203
- policy: PolicyWire,
204
176
  bytes: Uint8Array,
205
177
  parent: LogDecisionStamp | null,
206
178
  limits: LogLimits,
@@ -208,12 +180,7 @@ interface RuntimeNative {
208
180
  ): OperationHandle
209
181
  runtimeLogTake(operation: OperationHandle): LogTakeWire
210
182
  /** L14 mint: `Runtime::mint_repository_lock` stamps `NativeKind::RepositoryLock` at take. */
211
- logRepositoryLockAcquire(
212
- runtime: RuntimeHandle,
213
- policy: PolicyWire,
214
- directory: string,
215
- callback: () => void
216
- ): OperationHandle
183
+ logRepositoryLockAcquire(runtime: RuntimeHandle, directory: string, callback: () => void): OperationHandle
217
184
  logRepositoryLockTake(operation: OperationHandle): RepositoryLockHandle
218
185
  logRepositoryLockRelease(owner: RepositoryLockHandle, callback: (report: CloseWire) => void): void
219
186
  }
package/src/runtime.ts CHANGED
@@ -1,43 +1,21 @@
1
1
  import { Context, Duration, Effect, Exit, Layer } from "effect"
2
2
  import type { CloseReport, OutstandingWork } from "#runtime-errors.ts"
3
3
  import { CloseFailure, DbError, dbError } from "#runtime-errors.ts"
4
- import type {
5
- CloseWire,
6
- OperationHandle,
7
- OptionsWire,
8
- PolicyWire,
9
- RepositoryLockHandle,
10
- RuntimeHandle
11
- } from "#runtime-native.ts"
4
+ import type { CloseWire, OperationHandle, OptionsWire, RepositoryLockHandle, RuntimeHandle } from "#runtime-native.ts"
12
5
  import { runtimeNative } from "#runtime-native.ts"
13
6
 
14
- export interface ExecutionPolicy {
15
- readonly inputBytes: bigint
16
- readonly workingBytes: bigint
17
- readonly scratchBytes: bigint
18
- readonly resultBytes: bigint
19
- readonly rows: bigint
20
- readonly workUnits: bigint
21
- readonly timeout: Duration.Input
22
- }
23
-
24
7
  export interface NativeRuntimeOptions {
25
- readonly workers: number
26
- readonly queueCapacity: number
27
- readonly cleanupCapacity: number
28
- readonly ownerCapacity: number
29
- readonly nativeHandleCapacity: number
30
- readonly inputBytes: bigint
31
- readonly workingBytes: bigint
32
- readonly scratchBytes: bigint
33
- readonly resultBytes: bigint
34
- readonly chunkBytes: bigint
35
- readonly cleanupTimeout: Duration.Input
8
+ readonly workers?: number
9
+ readonly queueCapacity?: number
10
+ readonly cleanupCapacity?: number
11
+ readonly ownerCapacity?: number
12
+ readonly nativeHandleCapacity?: number
13
+ readonly cleanupTimeout?: Duration.Input
36
14
  }
37
15
 
38
16
  interface RuntimeService {
39
17
  readonly close: () => Effect.Effect<CloseReport>
40
- readonly inspect: (work: ExecutionPolicy) => Effect.Effect<OutstandingWork, DbError>
18
+ readonly inspect: () => Effect.Effect<OutstandingWork, DbError>
41
19
  }
42
20
 
43
21
  const owners = new WeakMap<RuntimeService, RuntimeHandle>()
@@ -51,43 +29,23 @@ function count(value: number, operation: string): number {
51
29
  return value
52
30
  }
53
31
 
54
- function bytes(value: bigint, operation: string): bigint {
55
- if (typeof value !== "bigint" || value < 0n || value > 0xffffffffffffffffn) throw invalid(operation)
56
- return value
57
- }
58
-
59
32
  function millis(value: Duration.Input, operation: string): number {
60
33
  const duration = Duration.toMillis(value)
61
34
  if (!Number.isFinite(duration) || duration < 0 || duration > 0xffffffff) throw invalid(operation)
62
35
  return Math.ceil(duration)
63
36
  }
64
37
 
65
- export function policyWire(work: ExecutionPolicy, operation: string): PolicyWire {
66
- return {
67
- inputBytes: bytes(work.inputBytes, operation),
68
- workingBytes: bytes(work.workingBytes, operation),
69
- scratchBytes: bytes(work.scratchBytes, operation),
70
- resultBytes: bytes(work.resultBytes, operation),
71
- rows: bytes(work.rows, operation),
72
- workUnits: bytes(work.workUnits, operation),
73
- timeoutMs: millis(work.timeout, operation)
74
- }
75
- }
76
-
77
38
  function options(value: NativeRuntimeOptions): OptionsWire {
78
39
  const operation = "NativeRuntime.acquire"
79
40
  return {
80
- workers: count(value.workers, operation),
81
- queueCapacity: count(value.queueCapacity, operation),
82
- cleanupCapacity: count(value.cleanupCapacity, operation),
83
- ownerCapacity: count(value.ownerCapacity, operation),
84
- nativeHandleCapacity: count(value.nativeHandleCapacity, operation),
85
- inputBytes: bytes(value.inputBytes, operation),
86
- workingBytes: bytes(value.workingBytes, operation),
87
- scratchBytes: bytes(value.scratchBytes, operation),
88
- resultBytes: bytes(value.resultBytes, operation),
89
- chunkBytes: bytes(value.chunkBytes, operation),
90
- cleanupTimeoutMs: count(millis(value.cleanupTimeout, operation), operation)
41
+ workers: value.workers === undefined ? undefined : count(value.workers, operation),
42
+ queueCapacity: value.queueCapacity === undefined ? undefined : count(value.queueCapacity, operation),
43
+ cleanupCapacity: value.cleanupCapacity === undefined ? undefined : count(value.cleanupCapacity, operation),
44
+ ownerCapacity: value.ownerCapacity === undefined ? undefined : count(value.ownerCapacity, operation),
45
+ nativeHandleCapacity:
46
+ value.nativeHandleCapacity === undefined ? undefined : count(value.nativeHandleCapacity, operation),
47
+ cleanupTimeoutMs:
48
+ value.cleanupTimeout === undefined ? undefined : count(millis(value.cleanupTimeout, operation), operation)
91
49
  }
92
50
  }
93
51
 
@@ -169,14 +127,6 @@ function afterClose<A, E, R>(
169
127
  return effect.pipe(Effect.onExit(() => Effect.die(new CloseFailure({ operation, report }))))
170
128
  }
171
129
 
172
- /** Independent caps intersect: a delivery request cannot enlarge `work.resultBytes`. */
173
- export function deliveryResultBytes(requested: bigint, work: ExecutionPolicy): bigint {
174
- if (typeof requested !== "bigint" || requested < 0n) {
175
- throw invalid("deliveryResultBytes")
176
- }
177
- return requested < work.resultBytes ? requested : work.resultBytes
178
- }
179
-
180
130
  function close(handle: RuntimeHandle): Effect.Effect<CloseReport> {
181
131
  return drain("NativeRuntime.close", (callback) => runtimeNative.runtimeClose(handle, callback))
182
132
  }
@@ -257,54 +207,41 @@ const acquire = Effect.fn("NativeRuntime.acquire")(function* (configuration: Nat
257
207
  const wire = options(configuration)
258
208
  handle = runtimeNative.runtimeOpen(wire)
259
209
  const owner = handle
260
- const lease = runtimeNative.runtimeReady(
261
- owner,
262
- {
263
- inputBytes: 0n,
264
- workingBytes: 0n,
265
- scratchBytes: 0n,
266
- resultBytes: 0n,
267
- rows: 0n,
268
- workUnits: 1n,
269
- timeoutMs: wire.cleanupTimeoutMs
270
- },
271
- () => {
272
- if (signal.aborted) {
273
- try {
274
- runtimeNative.runtimeClose(owner, (report) => {
275
- stash.report = closeReport("NativeRuntime.acquire.cancel", report)
276
- })
277
- } catch (cause) {
278
- stash.report = { kind: "failed", error: failure("NativeRuntime.acquire.cancel", cause) }
279
- }
280
- return
281
- }
210
+ const lease = runtimeNative.runtimeReady(owner, () => {
211
+ if (signal.aborted) {
282
212
  try {
283
- runtimeNative.runtimeTake(lease)
284
- const service: RuntimeService = {
285
- close: () => close(owner),
286
- inspect: Effect.fn("NativeRuntime.inspect")(function* (work) {
287
- yield* nativeOperation(
288
- "NativeRuntime.inspect",
289
- (callback) =>
290
- runtimeNative.runtimeReady(owner, policyWire(work, "NativeRuntime.inspect"), callback),
291
- () => undefined
292
- )
293
- return runtimeNative.runtimeInspect(owner)
294
- })
295
- }
296
- owners.set(service, owner)
297
- resume(Effect.succeed(service))
213
+ runtimeNative.runtimeClose(owner, (report) => {
214
+ stash.report = closeReport("NativeRuntime.acquire.cancel", report)
215
+ })
298
216
  } catch (cause) {
299
- const error = failure("NativeRuntime.acquire", cause)
300
- resume(
301
- close(owner).pipe(
302
- Effect.flatMap((report) => afterClose("NativeRuntime.acquire", report, Effect.fail(error)))
217
+ stash.report = { kind: "failed", error: failure("NativeRuntime.acquire.cancel", cause) }
218
+ }
219
+ return
220
+ }
221
+ try {
222
+ runtimeNative.runtimeTake(lease)
223
+ const service: RuntimeService = {
224
+ close: () => close(owner),
225
+ inspect: Effect.fn("NativeRuntime.inspect")(function* () {
226
+ yield* nativeOperation(
227
+ "NativeRuntime.inspect",
228
+ (callback) => runtimeNative.runtimeReady(owner, callback),
229
+ () => undefined
303
230
  )
304
- )
231
+ return runtimeNative.runtimeInspect(owner)
232
+ })
305
233
  }
234
+ owners.set(service, owner)
235
+ resume(Effect.succeed(service))
236
+ } catch (cause) {
237
+ const error = failure("NativeRuntime.acquire", cause)
238
+ resume(
239
+ close(owner).pipe(
240
+ Effect.flatMap((report) => afterClose("NativeRuntime.acquire", report, Effect.fail(error)))
241
+ )
242
+ )
306
243
  }
307
- )
244
+ })
308
245
  } catch (cause) {
309
246
  const error = failure("NativeRuntime.acquire", cause)
310
247
  resume(
@@ -333,7 +270,7 @@ const acquire = Effect.fn("NativeRuntime.acquire")(function* (configuration: Nat
333
270
  export class NativeRuntime extends Context.Service<NativeRuntime, RuntimeService>()(
334
271
  "@bjornpagen/bumbledb/NativeRuntime"
335
272
  ) {
336
- static layer(options: NativeRuntimeOptions): Layer.Layer<NativeRuntime, DbError> {
273
+ static layer(options: NativeRuntimeOptions = {}): Layer.Layer<NativeRuntime, DbError> {
337
274
  return Layer.effect(NativeRuntime, acquire(options))
338
275
  }
339
276
  }
@@ -374,17 +311,12 @@ function joinLockRelease(operation: string, owner: RepositoryLockHandle): Effect
374
311
  */
375
312
  export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositoryLock")(function* (
376
313
  operation: string,
377
- directory: string,
378
- work: ExecutionPolicy
314
+ directory: string
379
315
  ) {
380
316
  if (directory.length === 0) {
381
317
  return yield* Effect.fail(invalid(operation))
382
318
  }
383
319
  const runtime = yield* runtimeHandle()
384
- const wire = yield* Effect.try({
385
- try: () => policyWire(work, operation),
386
- catch: () => invalid(operation)
387
- })
388
320
  const closeOp = `${operation}.repositoryLock`
389
321
  return yield* Effect.uninterruptibleMask((restore) =>
390
322
  Effect.gen(function* () {
@@ -406,7 +338,7 @@ export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositor
406
338
  const owner = yield* restore(
407
339
  nativeOperationWith(
408
340
  operation,
409
- (callback) => runtimeNative.logRepositoryLockAcquire(runtime, wire, directory, callback),
341
+ (callback) => runtimeNative.logRepositoryLockAcquire(runtime, directory, callback),
410
342
  runtimeNative.logRepositoryLockTake,
411
343
  (value) => value
412
344
  )
@@ -421,7 +353,7 @@ export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositor
421
353
  })
422
354
 
423
355
  /** Internal first executor consumer; not a replacement row codec or public hash API. */
424
- export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8Array, work: ExecutionPolicy) {
356
+ export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8Array) {
425
357
  const runtime = yield* NativeRuntime
426
358
  const handle = owners.get(runtime)
427
359
  if (handle === undefined) return yield* Effect.fail(invalid("hashChunk"))
@@ -429,7 +361,7 @@ export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8
429
361
  "hashChunk",
430
362
  (callback) => {
431
363
  if (!(input instanceof Uint8Array) || !(input.buffer instanceof ArrayBuffer)) throw invalid("hashChunk")
432
- return runtimeNative.runtimeHash(handle, policyWire(work, "hashChunk"), input, callback)
364
+ return runtimeNative.runtimeHash(handle, input, callback)
433
365
  },
434
366
  (value) => {
435
367
  if (value === null) throw dbError("hashChunk", { _tag: "Internal" })
package/src/schema.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { isDeepStrictEqual } from "node:util"
1
2
  import { AuthoringError, SdkInvariantError } from "#errors.ts"
2
3
  /**
3
4
  * `schema` — assembles relations and statements into a theory value (the
@@ -17,13 +18,15 @@ import { AuthoringError, SdkInvariantError } from "#errors.ts"
17
18
  */
18
19
 
19
20
  import type { AnyClosed } from "#closed.ts"
20
- import { isClosedMember, sealedFieldOf } from "#closed.ts"
21
- import type { FaceData } from "#face.ts"
21
+ import { isClosedMember, memberDescriptor, membersAgree, sealedFieldOf } from "#closed.ts"
22
+ import type { AnyFace } from "#face.ts"
22
23
  import { assertDeclarationOrderKey, assertDeclarationRecord, rosterOf } from "#fields.ts"
24
+ import { descriptorCache, isImmutable } from "#immutable.ts"
23
25
  import { type ClassesOf, classesComplete, computeClasses, type LawfulStatements, type SchemaClasses } from "#law.ts"
24
26
  import type { AnyRelation } from "#relation.ts"
25
27
  import type { LiteralSetSpec, LiteralSpec } from "#spec.ts"
26
- import { isStatement, renderStatement, type Statement } from "#statements.ts"
28
+ import { renderStatement, type Statement, statementDescriptor } from "#statements.ts"
29
+ import { arrayValue, recordValue } from "#values.ts"
27
30
 
28
31
  interface ImpliedKeys {
29
32
  readonly rendered: ReadonlySet<string>
@@ -54,7 +57,7 @@ function collectImplied(name: string, relations: SchemaRelations): ImpliedKeys {
54
57
  }
55
58
 
56
59
  function statementOwners(statement: Statement): readonly SchemaRelation[] {
57
- const data = statement.data
60
+ const data = statement
58
61
  if (data.kind === "key") {
59
62
  return [data.owner]
60
63
  }
@@ -69,9 +72,9 @@ function verifyMembership(name: string, relations: SchemaRelations, statement: S
69
72
  message: `schema ${name}: relation ${owner.name} is not declared in this schema — ${rendered}`
70
73
  })
71
74
  }
72
- if (member !== owner) {
75
+ if (!membersAgree(member, owner)) {
73
76
  throw new AuthoringError({
74
- message: `schema ${name}: statement references a different relation value named ${owner.name} than the one this schema declares — ${rendered}`
77
+ message: `schema ${name}: statement references a different relation declaration named ${owner.name} than the one this schema declares — ${rendered}`
75
78
  })
76
79
  }
77
80
  }
@@ -86,7 +89,7 @@ function bindingLiterals(set: LiteralSetSpec): readonly LiteralSpec[] {
86
89
 
87
90
  function verifyBindingHandles(
88
91
  name: string,
89
- face: FaceData,
92
+ face: AnyFace,
90
93
  binding: { readonly field: string; readonly set: LiteralSetSpec },
91
94
  rendered: string
92
95
  ): void {
@@ -109,7 +112,7 @@ function verifyBindingHandles(
109
112
  }
110
113
 
111
114
  function verifyHandles(name: string, statement: Statement, rendered: string): void {
112
- const data = statement.data
115
+ const data = statement
113
116
  if (data.kind === "key") {
114
117
  return
115
118
  }
@@ -122,11 +125,11 @@ function verifyHandles(name: string, statement: Statement, rendered: string): vo
122
125
 
123
126
  function closedTargetOf(statements: readonly Statement[], owner: string, field: string): string | undefined {
124
127
  for (const statement of statements) {
125
- const data = statement.data
128
+ const data = statement
126
129
  if (data.kind !== "containment" && data.kind !== "mirrors") {
127
130
  continue
128
131
  }
129
- const pairs: Array<readonly [FaceData, FaceData]> = [[data.source, data.target]]
132
+ const pairs: Array<readonly [AnyFace, AnyFace]> = [[data.source, data.target]]
130
133
  if (data.kind === "mirrors") {
131
134
  pairs.push([data.target, data.source])
132
135
  }
@@ -148,7 +151,7 @@ function closedTargetOf(statements: readonly Statement[], owner: string, field:
148
151
 
149
152
  function verifyClosedReferences(name: string, statements: readonly Statement[]): void {
150
153
  for (const statement of statements) {
151
- const data = statement.data
154
+ const data = statement
152
155
  if (data.kind === "key") {
153
156
  continue
154
157
  }
@@ -164,7 +167,7 @@ function verifyClosedReferences(name: string, statements: readonly Statement[]):
164
167
  function verifyClosedReferenceBinding(
165
168
  name: string,
166
169
  statements: readonly Statement[],
167
- face: FaceData,
170
+ face: AnyFace,
168
171
  binding: { readonly field: string; readonly set: LiteralSetSpec },
169
172
  rendered: string
170
173
  ): void {
@@ -213,7 +216,7 @@ function verifyTargetKeys(
213
216
  ): void {
214
217
  const declared = new Map<string, Array<readonly string[]>>()
215
218
  for (const statement of statements) {
216
- const data = statement.data
219
+ const data = statement
217
220
  if (data.kind !== "key") {
218
221
  continue
219
222
  }
@@ -225,7 +228,7 @@ function verifyTargetKeys(
225
228
  }
226
229
  }
227
230
  for (const statement of statements) {
228
- const data = statement.data
231
+ const data = statement
229
232
  if (data.kind === "key") {
230
233
  continue
231
234
  }
@@ -251,7 +254,7 @@ function verifyTargetKeys(
251
254
  */
252
255
  function verifyTargetKeyFace(
253
256
  name: string,
254
- face: FaceData,
257
+ face: AnyFace,
255
258
  implied: ReadonlyMap<string, ReadonlyArray<readonly string[]>>,
256
259
  declared: ReadonlyMap<string, ReadonlyArray<readonly string[]>>,
257
260
  rendered: string
@@ -310,25 +313,92 @@ interface Schema<Rels extends SchemaRelations, Classes extends SchemaClasses = S
310
313
 
311
314
  type AnySchema = Schema<SchemaRelations>
312
315
 
316
+ /**
317
+ * Logical schema equivalence, including the ordered native ordinals and
318
+ * host decoding domains. Schema display names do not change the theory.
319
+ * A database/snapshot still has its own resource identity and lifetime.
320
+ */
321
+ function schemasAgree(leftInput: AnySchema, rightInput: AnySchema): boolean {
322
+ const left = schemaDescriptor(leftInput)
323
+ const right = schemaDescriptor(rightInput)
324
+ if (left === right) return true
325
+ const cacheable = isImmutable(left) && isImmutable(right)
326
+ const cached = schemaComparisons.get(left)?.get(right)
327
+ if (cacheable && cached !== undefined) return cached
328
+ const names = Object.keys(left.relations)
329
+ const otherNames = Object.keys(right.relations)
330
+ const equal =
331
+ names.length === otherNames.length &&
332
+ names.every((name, index) => {
333
+ const member = right.relations[name]
334
+ return name === otherNames[index] && member !== undefined && membersAgree(left.relations[name], member)
335
+ }) &&
336
+ isDeepStrictEqual(left.statements, right.statements) &&
337
+ isDeepStrictEqual(left.classes, right.classes)
338
+ if (cacheable) {
339
+ let comparisons = schemaComparisons.get(left)
340
+ if (comparisons === undefined) {
341
+ comparisons = new WeakMap()
342
+ schemaComparisons.set(left, comparisons)
343
+ }
344
+ comparisons.set(right, equal)
345
+ }
346
+ return equal
347
+ }
348
+
349
+ const schemaComparisons = new WeakMap<AnySchema, WeakMap<AnySchema, boolean>>()
350
+
351
+ function schemaDescriptor<S extends AnySchema>(input: S): S
352
+ function schemaDescriptor(input: unknown): AnySchema
353
+ function schemaDescriptor(input: unknown): AnySchema {
354
+ return checkedSchema(input)
355
+ }
356
+
357
+ const checkedSchema = descriptorCache((input): AnySchema => {
358
+ const raw = recordValue("schema", input, ["name", "relations", "statements", "classes"])
359
+ if (typeof raw.name !== "string") throw new AuthoringError({ message: "schema: expected a name" })
360
+ const relations = ownRelations(raw.name, raw.relations)
361
+ const statements = arrayValue("schema statements", raw.statements, (_, statement) => statementDescriptor(statement))
362
+ const result = schema(raw.name, relations, statements)
363
+ const classes = recordValue("schema classes", raw.classes, Object.keys(result.classes))
364
+ for (const [name, fields] of Object.entries(result.classes)) {
365
+ const supplied = recordValue(`schema classes.${name}`, classes[name], Object.keys(fields))
366
+ for (const [field, value] of Object.entries(fields)) {
367
+ if (supplied[field] !== value)
368
+ throw new AuthoringError({ message: `schema classes.${name}.${field}: incorrect class` })
369
+ }
370
+ }
371
+ return result
372
+ })
373
+
313
374
  type EvaluatedClasses<C extends SchemaClasses> = C extends SchemaClasses
314
375
  ? { readonly [N in keyof C]: { readonly [F in keyof C[N]]: C[N][F] } }
315
376
  : never
316
377
 
378
+ function ownRelations<R extends SchemaRelations>(name: string, input: R): R
379
+ function ownRelations(name: string, input: unknown): SchemaRelations
380
+ function ownRelations(name: string, input: unknown): SchemaRelations {
381
+ if (typeof input !== "object" || input === null)
382
+ throw new AuthoringError({ message: `schema ${name}: expected relations` })
383
+ assertDeclarationRecord(`schema ${name} relations`, input)
384
+ return Object.freeze(
385
+ Object.fromEntries(Object.entries(input).map(([name, member]) => [name, memberDescriptor(member)]))
386
+ )
387
+ }
388
+
317
389
  function schema<const Rels extends SchemaRelations, const Stmts extends readonly Statement[]>(
318
390
  name: string,
319
391
  relations: Rels,
320
392
  statements: Stmts & LawfulStatements<Rels, Stmts>
321
393
  ): Schema<Rels, EvaluatedClasses<ClassesOf<Rels, Stmts>>> {
322
- const implied = collectImplied(name, relations)
394
+ assertDeclarationOrderKey("schema", name)
395
+ const ownedRelations = ownRelations(name, relations)
396
+ const ownedStatements = arrayValue("schema statements", statements, (_, statement) => statementDescriptor(statement))
397
+ const implied = collectImplied(name, ownedRelations)
323
398
  const seen = new Set<string>()
324
- for (const statement of statements) {
325
- if (!isStatement(statement)) {
326
- throw new AuthoringError({
327
- message: `schema ${name}: a statement is minted only by key/contained/mirrors/capacity — a structural literal skips the construction-time arity and roster walls`
328
- })
329
- }
399
+ for (const statement of ownedStatements) {
330
400
  const rendered = renderStatement(statement)
331
- verifyMembership(name, relations, statement, rendered)
401
+ verifyMembership(name, ownedRelations, statement, rendered)
332
402
  if (implied.rendered.has(rendered)) {
333
403
  throw new AuthoringError({
334
404
  message: `schema ${name}: ${rendered} is redundant here (closedness already implies it) — and rejected as a duplicate`
@@ -340,14 +410,14 @@ function schema<const Rels extends SchemaRelations, const Stmts extends readonly
340
410
  seen.add(rendered)
341
411
  verifyHandles(name, statement, rendered)
342
412
  }
343
- verifyClosedReferences(name, statements)
344
- verifyTargetKeys(name, statements, implied.roster)
345
- const classes = computeClasses(name, relations, statements)
346
- if (!classesComplete<EvaluatedClasses<ClassesOf<Rels, Stmts>>>(classes, relations)) {
413
+ verifyClosedReferences(name, ownedStatements)
414
+ verifyTargetKeys(name, ownedStatements, implied.roster)
415
+ const classes = computeClasses(name, ownedRelations, ownedStatements)
416
+ if (!classesComplete<EvaluatedClasses<ClassesOf<Rels, Stmts>>>(classes, ownedRelations)) {
347
417
  throw new SdkInvariantError({ message: `schema ${name}: class-map construction incomplete` })
348
418
  }
349
- return Object.freeze({ name, relations, statements: Object.freeze([...statements]), classes })
419
+ return Object.freeze({ name, relations: ownedRelations, statements: Object.freeze(ownedStatements), classes })
350
420
  }
351
421
 
352
422
  export type { AnySchema, Schema, SchemaRelation, SchemaRelations }
353
- export { schema }
423
+ export { schema, schemaDescriptor, schemasAgree }