@bjornpagen/bumbledb 1.0.0 → 1.1.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 (102) hide show
  1. package/COOKBOOK.md +64 -27
  2. package/README.md +21 -20
  3. package/dist/capacity.d.ts +1 -1
  4. package/dist/changes.d.ts +4 -5
  5. package/dist/changes.d.ts.map +1 -1
  6. package/dist/changes.js +37 -34
  7. package/dist/changes.js.map +1 -1
  8. package/dist/close.d.ts +2 -2
  9. package/dist/codec.d.ts +5 -6
  10. package/dist/codec.d.ts.map +1 -1
  11. package/dist/codec.js +6 -6
  12. package/dist/codec.js.map +1 -1
  13. package/dist/compile.d.ts +4 -6
  14. package/dist/compile.d.ts.map +1 -1
  15. package/dist/compile.js +6 -6
  16. package/dist/compile.js.map +1 -1
  17. package/dist/db-native.d.ts +43 -56
  18. package/dist/db-native.d.ts.map +1 -1
  19. package/dist/db-native.js.map +1 -1
  20. package/dist/db.d.ts +43 -28
  21. package/dist/db.d.ts.map +1 -1
  22. package/dist/db.js +77 -63
  23. package/dist/db.js.map +1 -1
  24. package/dist/errors.d.ts +4 -8
  25. package/dist/errors.d.ts.map +1 -1
  26. package/dist/errors.js +4 -8
  27. package/dist/errors.js.map +1 -1
  28. package/dist/fields.d.ts +4 -5
  29. package/dist/fields.d.ts.map +1 -1
  30. package/dist/fields.js.map +1 -1
  31. package/dist/index.d.ts +4 -4
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +2 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/internal/log.d.ts +4 -4
  36. package/dist/internal/log.d.ts.map +1 -1
  37. package/dist/internal/log.js +1 -1
  38. package/dist/internal/log.js.map +1 -1
  39. package/dist/law.d.ts +1 -1
  40. package/dist/migration.d.ts +5 -7
  41. package/dist/migration.d.ts.map +1 -1
  42. package/dist/migration.js +8 -9
  43. package/dist/migration.js.map +1 -1
  44. package/dist/native.d.ts +2 -2
  45. package/dist/native.d.ts.map +1 -1
  46. package/dist/native.js.map +1 -1
  47. package/dist/query/lower.d.ts +1 -1
  48. package/dist/result.d.ts +10 -25
  49. package/dist/result.d.ts.map +1 -1
  50. package/dist/result.js +7 -12
  51. package/dist/result.js.map +1 -1
  52. package/dist/rows.d.ts +10 -16
  53. package/dist/rows.d.ts.map +1 -1
  54. package/dist/rows.js +8 -45
  55. package/dist/rows.js.map +1 -1
  56. package/dist/runtime-codes.d.ts +1 -1
  57. package/dist/runtime-codes.d.ts.map +1 -1
  58. package/dist/runtime-codes.js +1 -2
  59. package/dist/runtime-codes.js.map +1 -1
  60. package/dist/runtime-errors.d.ts +3 -15
  61. package/dist/runtime-errors.d.ts.map +1 -1
  62. package/dist/runtime-errors.js +1 -5
  63. package/dist/runtime-errors.js.map +1 -1
  64. package/dist/runtime-native.d.ts +18 -48
  65. package/dist/runtime-native.d.ts.map +1 -1
  66. package/dist/runtime-native.js.map +1 -1
  67. package/dist/runtime.d.ts +12 -29
  68. package/dist/runtime.d.ts.map +1 -1
  69. package/dist/runtime.js +16 -57
  70. package/dist/runtime.js.map +1 -1
  71. package/dist/shape.d.ts +3 -4
  72. package/dist/shape.d.ts.map +1 -1
  73. package/dist/spec.d.ts +2 -2
  74. package/dist/spec.js +1 -1
  75. package/dist/statements.js +2 -2
  76. package/dist/statements.js.map +1 -1
  77. package/pack-provenance.json +3 -3
  78. package/package.json +4 -4
  79. package/src/capacity.ts +1 -1
  80. package/src/changes.ts +50 -48
  81. package/src/close.ts +2 -2
  82. package/src/codec.ts +8 -24
  83. package/src/compile.ts +8 -10
  84. package/src/db-native.ts +37 -83
  85. package/src/db.ts +156 -135
  86. package/src/errors.ts +4 -8
  87. package/src/fields.ts +4 -5
  88. package/src/index.ts +6 -5
  89. package/src/internal/log.ts +2 -7
  90. package/src/law.ts +1 -1
  91. package/src/migration.ts +8 -16
  92. package/src/native.ts +2 -3
  93. package/src/query/lower.ts +1 -1
  94. package/src/result.ts +16 -45
  95. package/src/rows.ts +12 -65
  96. package/src/runtime-codes.ts +1 -2
  97. package/src/runtime-errors.ts +1 -5
  98. package/src/runtime-native.ts +15 -68
  99. package/src/runtime.ts +53 -122
  100. package/src/shape.ts +3 -4
  101. package/src/spec.ts +2 -2
  102. package/src/statements.ts +2 -2
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
 
@@ -97,8 +55,7 @@ function failure(operation: string, cause: unknown): DbError {
97
55
 
98
56
  function closeReport(operation: string, report: CloseWire): CloseReport {
99
57
  // A payload-less native "failed" drain (cleanup capacity exhausted, or
100
- // owner teardown failed) is bounded diagnostic data per chapter 35's
101
- // CloseReport policy: it decodes as the core `Internal` reason — the
58
+ // owner teardown failed) decodes as the core `Internal` reason — the
102
59
  // same mapping the log bridge applies, so one wire arm has one meaning.
103
60
  // It is deliberately NOT `QueueFull`: a failed drain is never retryable
104
61
  // submit backpressure.
@@ -170,14 +127,6 @@ function afterClose<A, E, R>(
170
127
  return effect.pipe(Effect.onExit(() => Effect.die(new CloseFailure({ operation, report }))))
171
128
  }
172
129
 
173
- /** Independent caps intersect: a delivery request cannot enlarge `work.resultBytes`. */
174
- export function deliveryResultBytes(requested: bigint, work: ExecutionPolicy): bigint {
175
- if (typeof requested !== "bigint" || requested < 0n) {
176
- throw invalid("deliveryResultBytes")
177
- }
178
- return requested < work.resultBytes ? requested : work.resultBytes
179
- }
180
-
181
130
  function close(handle: RuntimeHandle): Effect.Effect<CloseReport> {
182
131
  return drain("NativeRuntime.close", (callback) => runtimeNative.runtimeClose(handle, callback))
183
132
  }
@@ -258,54 +207,41 @@ const acquire = Effect.fn("NativeRuntime.acquire")(function* (configuration: Nat
258
207
  const wire = options(configuration)
259
208
  handle = runtimeNative.runtimeOpen(wire)
260
209
  const owner = handle
261
- const lease = runtimeNative.runtimeReady(
262
- owner,
263
- {
264
- inputBytes: 0n,
265
- workingBytes: 0n,
266
- scratchBytes: 0n,
267
- resultBytes: 0n,
268
- rows: 0n,
269
- workUnits: 1n,
270
- timeoutMs: wire.cleanupTimeoutMs
271
- },
272
- () => {
273
- if (signal.aborted) {
274
- try {
275
- runtimeNative.runtimeClose(owner, (report) => {
276
- stash.report = closeReport("NativeRuntime.acquire.cancel", report)
277
- })
278
- } catch (cause) {
279
- stash.report = { kind: "failed", error: failure("NativeRuntime.acquire.cancel", cause) }
280
- }
281
- return
282
- }
210
+ const lease = runtimeNative.runtimeReady(owner, () => {
211
+ if (signal.aborted) {
283
212
  try {
284
- runtimeNative.runtimeTake(lease)
285
- const service: RuntimeService = {
286
- close: () => close(owner),
287
- inspect: Effect.fn("NativeRuntime.inspect")(function* (work) {
288
- yield* nativeOperation(
289
- "NativeRuntime.inspect",
290
- (callback) =>
291
- runtimeNative.runtimeReady(owner, policyWire(work, "NativeRuntime.inspect"), callback),
292
- () => undefined
293
- )
294
- return runtimeNative.runtimeInspect(owner)
295
- })
296
- }
297
- owners.set(service, owner)
298
- resume(Effect.succeed(service))
213
+ runtimeNative.runtimeClose(owner, (report) => {
214
+ stash.report = closeReport("NativeRuntime.acquire.cancel", report)
215
+ })
299
216
  } catch (cause) {
300
- const error = failure("NativeRuntime.acquire", cause)
301
- resume(
302
- close(owner).pipe(
303
- 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
304
230
  )
305
- )
231
+ return runtimeNative.runtimeInspect(owner)
232
+ })
306
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
+ )
307
243
  }
308
- )
244
+ })
309
245
  } catch (cause) {
310
246
  const error = failure("NativeRuntime.acquire", cause)
311
247
  resume(
@@ -334,7 +270,7 @@ const acquire = Effect.fn("NativeRuntime.acquire")(function* (configuration: Nat
334
270
  export class NativeRuntime extends Context.Service<NativeRuntime, RuntimeService>()(
335
271
  "@bjornpagen/bumbledb/NativeRuntime"
336
272
  ) {
337
- static layer(options: NativeRuntimeOptions): Layer.Layer<NativeRuntime, DbError> {
273
+ static layer(options: NativeRuntimeOptions = {}): Layer.Layer<NativeRuntime, DbError> {
338
274
  return Layer.effect(NativeRuntime, acquire(options))
339
275
  }
340
276
  }
@@ -368,24 +304,19 @@ function joinLockRelease(operation: string, owner: RepositoryLockHandle): Effect
368
304
 
369
305
  /**
370
306
  * Internal log seam: stamped mint only. Cleanup is registered before
371
- * the interruptible acquire (TS-003): `acquireRelease({ interruptible })`
307
+ * the interruptible acquire: `acquireRelease({ interruptible })`
372
308
  * installs the finalizer only after acquire succeeds. Interrupt, defect,
373
309
  * and acquire failure all run the same slot finalizer — empty slot is
374
310
  * a no-op. No `runtimeDirectoryAcquire`, no core `Db`, no JS bookkeeping.
375
311
  */
376
312
  export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositoryLock")(function* (
377
313
  operation: string,
378
- directory: string,
379
- work: ExecutionPolicy
314
+ directory: string
380
315
  ) {
381
316
  if (directory.length === 0) {
382
317
  return yield* Effect.fail(invalid(operation))
383
318
  }
384
319
  const runtime = yield* runtimeHandle()
385
- const wire = yield* Effect.try({
386
- try: () => policyWire(work, operation),
387
- catch: () => invalid(operation)
388
- })
389
320
  const closeOp = `${operation}.repositoryLock`
390
321
  return yield* Effect.uninterruptibleMask((restore) =>
391
322
  Effect.gen(function* () {
@@ -407,7 +338,7 @@ export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositor
407
338
  const owner = yield* restore(
408
339
  nativeOperationWith(
409
340
  operation,
410
- (callback) => runtimeNative.logRepositoryLockAcquire(runtime, wire, directory, callback),
341
+ (callback) => runtimeNative.logRepositoryLockAcquire(runtime, directory, callback),
411
342
  runtimeNative.logRepositoryLockTake,
412
343
  (value) => value
413
344
  )
@@ -422,7 +353,7 @@ export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositor
422
353
  })
423
354
 
424
355
  /** Internal first executor consumer; not a replacement row codec or public hash API. */
425
- export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8Array, work: ExecutionPolicy) {
356
+ export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8Array) {
426
357
  const runtime = yield* NativeRuntime
427
358
  const handle = owners.get(runtime)
428
359
  if (handle === undefined) return yield* Effect.fail(invalid("hashChunk"))
@@ -430,7 +361,7 @@ export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8
430
361
  "hashChunk",
431
362
  (callback) => {
432
363
  if (!(input instanceof Uint8Array) || !(input.buffer instanceof ArrayBuffer)) throw invalid("hashChunk")
433
- return runtimeNative.runtimeHash(handle, policyWire(work, "hashChunk"), input, callback)
364
+ return runtimeNative.runtimeHash(handle, input, callback)
434
365
  },
435
366
  (value) => {
436
367
  if (value === null) throw dbError("hashChunk", { _tag: "Internal" })
package/src/shape.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Query } from "#query/lower.ts"
2
2
  import type { ParamsRecord } from "#query/scope.ts"
3
3
  /**
4
- * Shared derived-type vocabulary of the chapter 35 core roster: `S` is a
4
+ * Shared derived types: `S` is a
5
5
  * declared core schema value's type, `Rel<S>` its ordinary (writable)
6
6
  * relations, `Fact<R>` the inferred row object, `Key<R>` the primary-key
7
7
  * object accepted by `QueryReader.get`, and `QueryTemplate<S, P, A>` the
@@ -17,15 +17,14 @@ type Rel<S extends AnySchema> = Extract<S["relations"][keyof S["relations"]], An
17
17
  /**
18
18
  * The primary-key object of a relation: the fields of its FIRST
19
19
  * materialized key statement (a closed relation's synthetic `(id)`,
20
- * otherwise the first declared `key`). Keys are declared statements now —
21
- * there is no fresh-implied key — so the exact projection is a schema
20
+ * otherwise the first declared `key`). The exact projection is a schema
22
21
  * fact, verified at execution against the compiled tables; the type admits
23
22
  * any subset of the fact's own fields and the runtime refuses a mismatch
24
23
  * with a typed `DbError` before any native work.
25
24
  */
26
25
  type Key<R extends AnyRelation> = Partial<Fact<R>>
27
26
 
28
- /** Chapter 35's spelling of the immutable typed query template. */
27
+ /** Immutable typed query template. */
29
28
  type QueryTemplate<S extends AnySchema, P extends ParamsRecord, A> = Query<S["relations"], A, P>
30
29
 
31
30
  export type { Key, QueryTemplate, Rel }
package/src/spec.ts CHANGED
@@ -58,7 +58,7 @@ type CapacityWindowSpec =
58
58
  /**
59
59
  * One field: name, structural type, and host newtype label. There is no
60
60
  * `fresh` mark: the database issues no identity, and key laws are declared
61
- * statements (C01, chapter 30).
61
+ * statements.
62
62
  */
63
63
  interface FieldSpec {
64
64
  readonly name: string
@@ -207,7 +207,7 @@ function renderLiteral(literal: LiteralSpec): string {
207
207
 
208
208
  /**
209
209
  * The canonical binary64 bit image as sixteen lowercase hex digits — the
210
- * one f64 rendering (chapter 11): every NaN is the quiet canonical NaN and
210
+ * one f64 rendering: every NaN is the quiet canonical NaN and
211
211
  * `-0` renders as `+0`, mirroring the engine's `f64:0x{bits:016x}`.
212
212
  */
213
213
  function f64BitsHex(value: number): string {
package/src/statements.ts CHANGED
@@ -275,9 +275,9 @@ function capacity(
275
275
  weight = options.weight.weight
276
276
  }
277
277
  const window = windowValue.window
278
- // The unit-floor and vacuous-window ban tables are deleted (C01):
278
+ // The unit-floor and vacuous-window ban tables are deleted:
279
279
  // `{1..*}`, `{N..*}` and `{0..*}` are accepted canonical laws now.
280
- // CapacityDimensionMixing (C18) remains — genuinely different semantics.
280
+ // CapacityDimensionMixing remains — genuinely different semantics.
281
281
  if (weight.kind === "unit" && window.kind === "range" && window.hi.kind === "durationField") {
282
282
  throw new AuthoringError({
283
283
  message: `a unit (count) window against the duration() bound on ${window.hi.field} mixes dimensions (C18) — weigh the source with weigh(duration(field)), or bound by a u64 field or literal`