@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.
- package/COOKBOOK.md +176 -36
- package/README.md +70 -26
- package/dist/capacity.d.ts +15 -55
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +63 -12
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +28 -6
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +119 -71
- package/dist/changes.js.map +1 -1
- package/dist/closed.d.ts +23 -51
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +110 -168
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +8 -14
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +113 -188
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +17 -18
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +41 -20
- package/dist/compile.js.map +1 -1
- package/dist/db-native.d.ts +76 -41
- package/dist/db-native.d.ts.map +1 -1
- package/dist/db-native.js +8 -0
- package/dist/db-native.js.map +1 -1
- package/dist/db.d.ts +61 -26
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +152 -85
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +11 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -1
- package/dist/errors.js.map +1 -1
- package/dist/face.d.ts +19 -25
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +30 -24
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +13 -26
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +89 -116
- package/dist/fields.js.map +1 -1
- package/dist/immutable.d.ts +10 -0
- package/dist/immutable.d.ts.map +1 -0
- package/dist/immutable.js +75 -0
- package/dist/immutable.js.map +1 -0
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +6 -4
- package/dist/internal/log.d.ts.map +1 -1
- package/dist/internal/log.js +3 -1
- package/dist/internal/log.js.map +1 -1
- package/dist/law.d.ts +11 -11
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +1 -1
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +11 -6
- package/dist/lower.js.map +1 -1
- package/dist/migration.d.ts +2 -3
- package/dist/migration.d.ts.map +1 -1
- package/dist/migration.js +5 -5
- package/dist/migration.js.map +1 -1
- package/dist/native.d.ts +2 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +1 -1
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/description.d.ts +33 -0
- package/dist/query/description.d.ts.map +1 -0
- package/dist/query/description.js +302 -0
- package/dist/query/description.js.map +1 -0
- package/dist/query/lower.d.ts +60 -4
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +126 -161
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +7 -2
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +240 -53
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +8 -10
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +3 -4
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +5 -10
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +22 -48
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +30 -82
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +10 -25
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +7 -12
- package/dist/result.js.map +1 -1
- package/dist/rows.d.ts +11 -14
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -237
- package/dist/rows.js.map +1 -1
- package/dist/runtime-codes.d.ts +1 -1
- package/dist/runtime-codes.d.ts.map +1 -1
- package/dist/runtime-codes.js +1 -2
- package/dist/runtime-codes.js.map +1 -1
- package/dist/runtime-errors.d.ts +27 -15
- package/dist/runtime-errors.d.ts.map +1 -1
- package/dist/runtime-errors.js +44 -8
- package/dist/runtime-errors.js.map +1 -1
- package/dist/runtime-native.d.ts +16 -34
- package/dist/runtime-native.d.ts.map +1 -1
- package/dist/runtime-native.js.map +1 -1
- package/dist/runtime.d.ts +11 -28
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +14 -54
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +9 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +85 -24
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +27 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +104 -0
- package/dist/selection.js.map +1 -0
- package/dist/shape.d.ts +6 -9
- package/dist/shape.d.ts.map +1 -1
- package/dist/statements.d.ts +17 -52
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +92 -111
- package/dist/statements.js.map +1 -1
- package/dist/values.d.ts +25 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +203 -0
- package/dist/values.js.map +1 -0
- package/pack-provenance.json +3 -3
- package/package.json +4 -4
- package/src/capacity.ts +86 -76
- package/src/changes.ts +225 -94
- package/src/closed.ts +161 -314
- package/src/codec.ts +123 -213
- package/src/compile.ts +61 -38
- package/src/db-native.ts +73 -68
- package/src/db.ts +268 -165
- package/src/errors.ts +16 -1
- package/src/face.ts +44 -74
- package/src/fields.ts +100 -146
- package/src/immutable.ts +76 -0
- package/src/index.ts +38 -31
- package/src/internal/log.ts +4 -5
- package/src/law.ts +14 -14
- package/src/lower.ts +13 -10
- package/src/migration.ts +5 -12
- package/src/native.ts +2 -1
- package/src/query/atom.ts +1 -0
- package/src/query/description.ts +394 -0
- package/src/query/lower.ts +139 -173
- package/src/query/parse-ir.ts +247 -54
- package/src/query/run.ts +15 -9
- package/src/query/scope.ts +10 -18
- package/src/relation.ts +58 -142
- package/src/result.ts +16 -45
- package/src/rows.ts +57 -250
- package/src/runtime-codes.ts +1 -2
- package/src/runtime-errors.ts +48 -8
- package/src/runtime-native.ts +13 -46
- package/src/runtime.ts +51 -119
- package/src/schema.ts +99 -29
- package/src/selection.ts +124 -0
- package/src/shape.ts +9 -9
- package/src/statements.ts +138 -189
- 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
|
+
}
|