@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
package/src/values.ts ADDED
@@ -0,0 +1,223 @@
1
+ import { type AuthoringDiagnostic, AuthoringError } from "#errors.ts"
2
+ import type { AnyField, Infer } from "#fields.ts"
3
+ import type { ValueSpec } from "#spec.ts"
4
+ import { Uuid } from "#uuid.ts"
5
+
6
+ const U64_MAX = 0xffffffffffffffffn
7
+ const I64_MIN = -0x8000000000000000n
8
+ const I64_MAX = 0x7fffffffffffffffn
9
+
10
+ function invalid(context: string, expected: string, code: AuthoringDiagnostic["code"] = "InvalidValue"): never {
11
+ throw new AuthoringError({ message: `${context}: expected ${expected}`, diagnostic: { code, context, expected } })
12
+ }
13
+
14
+ /** Validate the complete record shape without reading any field getter. */
15
+ function recordValue(context: string, input: unknown, fields: readonly string[]): Readonly<Record<string, unknown>> {
16
+ if (typeof input !== "object" || input === null) {
17
+ return invalid(context, "a plain record", "InvalidRecord")
18
+ }
19
+ const prototype = Object.getPrototypeOf(input)
20
+ if (prototype !== Object.prototype && prototype !== null) {
21
+ return invalid(context, "a plain record", "InvalidRecord")
22
+ }
23
+ for (const name of Reflect.ownKeys(input)) {
24
+ if (typeof name !== "string" || !fields.includes(name)) {
25
+ return invalid(`${context}.${String(name)}`, "only the declared fields", "UnknownField")
26
+ }
27
+ const property = Object.getOwnPropertyDescriptor(input, name)
28
+ if (property?.enumerable !== true || !("value" in property)) {
29
+ return invalid(`${context}.${name}`, "enumerable own data fields", "InvalidRecord")
30
+ }
31
+ }
32
+ for (const name of fields) {
33
+ if (!Object.hasOwn(input, name)) {
34
+ return invalid(`${context}.${name}`, "a required own field", "MissingField")
35
+ }
36
+ }
37
+ return input as Readonly<Record<string, unknown>>
38
+ }
39
+
40
+ /** Own a dense data array without invoking caller element accessors or methods. */
41
+ function arrayValue<A>(context: string, input: unknown, parse: (context: string, input: unknown) => A): readonly A[] {
42
+ if (!Array.isArray(input)) return invalid(context, "an array", "InvalidRecord")
43
+ for (const name of Reflect.ownKeys(input)) {
44
+ if (name === "length") continue
45
+ if (typeof name !== "string" || !/^(0|[1-9][0-9]*)$/.test(name) || Number(name) >= input.length)
46
+ return invalid(context, "only array elements", "InvalidRecord")
47
+ const property = Object.getOwnPropertyDescriptor(input, name)
48
+ if (property?.enumerable !== true || !("value" in property))
49
+ return invalid(context, "own data elements", "InvalidRecord")
50
+ }
51
+ const result: A[] = []
52
+ for (let index = 0; index < input.length; index++) {
53
+ if (!Object.hasOwn(input, index)) return invalid(`${context}[${index}]`, "a present element", "MissingField")
54
+ result.push(parse(`${context}[${index}]`, input[index]))
55
+ }
56
+ return Object.freeze(result)
57
+ }
58
+
59
+ function integerValue(context: string, kind: "u64" | "i64", input: unknown): bigint {
60
+ if (typeof input !== "bigint") return invalid(context, `${kind} bigint`)
61
+ const minimum = kind === "u64" ? 0n : I64_MIN
62
+ const maximum = kind === "u64" ? U64_MAX : I64_MAX
63
+ if (input < minimum || input > maximum) return invalid(context, `${kind} bigint in range`)
64
+ return input
65
+ }
66
+
67
+ function floatValue(context: string, input: unknown): number {
68
+ if (typeof input !== "number") return invalid(context, "number")
69
+ if (Number.isNaN(input)) return Number.NaN
70
+ return Object.is(input, -0) ? 0 : input
71
+ }
72
+
73
+ /** Read typed-array internal slots through the intrinsic getters, never a
74
+ * caller's shadowed buffer/length properties or custom iterator. */
75
+ function bytesValue(context: string, input: unknown): Uint8Array {
76
+ if (!(input instanceof Uint8Array)) return invalid(context, "Uint8Array")
77
+ const prototype = Object.getPrototypeOf(Uint8Array.prototype)
78
+ const buffer = Reflect.get(prototype, "buffer", input)
79
+ if (!(buffer instanceof ArrayBuffer))
80
+ throw new AuthoringError({
81
+ message: `${context}: SharedArrayBuffer-backed views are refused`,
82
+ diagnostic: { code: "InvalidValue", context, expected: "ArrayBuffer-backed bytes" }
83
+ })
84
+ const offset = Reflect.get(prototype, "byteOffset", input)
85
+ const length = Reflect.get(prototype, "byteLength", input)
86
+ try {
87
+ return new Uint8Array(new Uint8Array(buffer, offset, length))
88
+ } catch {
89
+ return invalid(context, "non-detached bytes")
90
+ }
91
+ }
92
+
93
+ /**
94
+ * The host interpreter of a field descriptor. All ingress paths use the
95
+ * same range, width, ownership and canonicalization rules. Native admission
96
+ * remains authoritative; this interpreter mirrors its value domain.
97
+ */
98
+ function fieldValue<F extends AnyField>(context: string, field: F, input: unknown): Infer<F>
99
+ function fieldValue(context: string, field: AnyField, input: unknown): Infer<AnyField> {
100
+ if ("closed" in field) {
101
+ if (typeof input !== "string" || !field.closed.handles.includes(input)) {
102
+ return invalid(context, `a ${field.closed.name} handle`)
103
+ }
104
+ return input
105
+ }
106
+ switch (field.kind) {
107
+ case "bool":
108
+ return typeof input === "boolean" ? input : invalid(context, "boolean")
109
+ case "str":
110
+ return typeof input === "string" && input.isWellFormed() ? input : invalid(context, "well-formed string")
111
+ case "u64":
112
+ case "i64":
113
+ return integerValue(context, field.kind, input)
114
+ case "f64":
115
+ return floatValue(context, input)
116
+ case "uuid":
117
+ return Uuid.isUuid(input) ? input : invalid(context, "canonical UUID text")
118
+ case "bytes": {
119
+ const owned = bytesValue(context, input)
120
+ if (owned.byteLength !== field.width) return invalid(context, `bytes<${field.width}>`)
121
+ return owned
122
+ }
123
+ case "interval": {
124
+ const record = recordValue(context, input, ["start", "end"])
125
+ if (field.element === "f64") {
126
+ const start = floatValue(`${context}.start`, record.start)
127
+ const end = floatValue(`${context}.end`, record.end)
128
+ if (!(start < end)) return invalid(context, "a nonempty float interval with non-NaN endpoints")
129
+ return Object.freeze({ start, end })
130
+ }
131
+ const start = integerValue(`${context}.start`, field.element, record.start)
132
+ const end = integerValue(`${context}.end`, field.element, record.end)
133
+ if (start >= end) return invalid(context, "a nonempty interval (start < end)")
134
+ const maximumEnd = field.element === "u64" ? U64_MAX : I64_MAX
135
+ if (field.width !== undefined && (end === maximumEnd || end - start !== field.width)) {
136
+ return invalid(context, `a finite interval of width ${field.width}`)
137
+ }
138
+ return Object.freeze({ start, end })
139
+ }
140
+ }
141
+ }
142
+
143
+ /** One checked host value to the native logical scalar description. */
144
+ function taggedValueOf(context: string, field: AnyField, input: unknown): ValueSpec {
145
+ if ("closed" in field) {
146
+ const handle = fieldValue(context, field, input)
147
+ return { kind: "u64", value: BigInt(field.closed.handles.indexOf(handle)) }
148
+ }
149
+ switch (field.kind) {
150
+ case "bool":
151
+ return { kind: "bool", value: fieldValue(context, field, input) }
152
+ case "str":
153
+ return { kind: "string", value: fieldValue(context, field, input) }
154
+ case "u64":
155
+ return { kind: "u64", value: fieldValue(context, field, input) }
156
+ case "i64":
157
+ return { kind: "i64", value: fieldValue(context, field, input) }
158
+ case "f64":
159
+ return { kind: "f64", value: fieldValue(context, field, input) }
160
+ case "uuid":
161
+ return { kind: "uuid", value: fieldValue(context, field, input) }
162
+ case "bytes":
163
+ return { kind: "fixedBytes", value: fieldValue(context, field, input) }
164
+ case "interval": {
165
+ if (field.element === "f64") {
166
+ const value = fieldValue(context, { ...field, element: "f64" }, input)
167
+ return { kind: "intervalF64", ...value }
168
+ }
169
+ const value = fieldValue(context, { ...field, element: field.element }, input)
170
+ return { kind: field.element === "u64" ? "intervalU64" : "intervalI64", ...value }
171
+ }
172
+ }
173
+ }
174
+
175
+ /** Check a generated logical value through the same field interpreter. */
176
+ function valueDescriptor(context: string, input: unknown): ValueSpec {
177
+ if (typeof input !== "object" || input === null) return invalid(context, "a tagged value", "InvalidRecord")
178
+ const tag = recordValue(context, input, Object.keys(input)).kind
179
+ const intervalElement = { intervalU64: "u64", intervalI64: "i64", intervalF64: "f64" } as const
180
+ if (tag === "intervalU64" || tag === "intervalI64" || tag === "intervalF64") {
181
+ const raw = recordValue(context, input, ["kind", "start", "end"])
182
+ return Object.freeze(
183
+ taggedValueOf(
184
+ context,
185
+ { kind: "interval", element: intervalElement[tag], width: undefined },
186
+ { start: raw.start, end: raw.end }
187
+ )
188
+ )
189
+ }
190
+ const raw = recordValue(context, input, ["kind", "value"])
191
+ if (tag === "fixedBytes") {
192
+ const bytes = bytesValue(context, raw.value)
193
+ if (bytes.byteLength < 1 || bytes.byteLength > 64) return invalid(context, "bytes with width 1 through 64")
194
+ return Object.freeze({ kind: "fixedBytes", value: bytes })
195
+ }
196
+ if (tag === "string") return Object.freeze(taggedValueOf(context, { kind: "str" }, raw.value))
197
+ switch (tag) {
198
+ case "bool":
199
+ return Object.freeze(taggedValueOf(context, { kind: tag }, raw.value))
200
+ case "u64":
201
+ return Object.freeze(taggedValueOf(context, { kind: tag }, raw.value))
202
+ case "i64":
203
+ return Object.freeze(taggedValueOf(context, { kind: tag }, raw.value))
204
+ case "f64":
205
+ return Object.freeze(taggedValueOf(context, { kind: tag }, raw.value))
206
+ case "uuid":
207
+ return Object.freeze(taggedValueOf(context, { kind: tag }, raw.value))
208
+ }
209
+ return invalid(context, "a supported value tag", "InvalidDeclaration")
210
+ }
211
+
212
+ export {
213
+ arrayValue,
214
+ bytesValue,
215
+ fieldValue,
216
+ I64_MAX,
217
+ I64_MIN,
218
+ integerValue,
219
+ recordValue,
220
+ taggedValueOf,
221
+ U64_MAX,
222
+ valueDescriptor
223
+ }