@bjornpagen/bumbledb 1.1.0 → 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 +114 -11
- package/README.md +52 -9
- 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 +26 -3
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +89 -44
- 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 +4 -9
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +108 -183
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +16 -16
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +36 -15
- package/dist/compile.js.map +1 -1
- package/dist/db-native.d.ts +39 -0
- 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 +28 -8
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +91 -38
- 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 +14 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +2 -0
- package/dist/internal/log.d.ts.map +1 -1
- package/dist/internal/log.js +2 -0
- package/dist/internal/log.js.map +1 -1
- package/dist/law.d.ts +10 -10
- 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/native.d.ts +1 -1
- 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/rows.d.ts +7 -4
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +44 -198
- package/dist/rows.js.map +1 -1
- package/dist/runtime-errors.d.ts +26 -2
- package/dist/runtime-errors.d.ts.map +1 -1
- package/dist/runtime-errors.js +43 -3
- package/dist/runtime-errors.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 +183 -54
- package/src/closed.ts +161 -314
- package/src/codec.ts +116 -190
- package/src/compile.ts +56 -32
- package/src/db-native.ts +42 -0
- package/src/db.ts +134 -52
- 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 +34 -28
- package/src/internal/log.ts +2 -0
- package/src/law.ts +13 -13
- package/src/lower.ts +13 -10
- package/src/native.ts +2 -0
- 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/rows.ts +62 -205
- package/src/runtime-errors.ts +47 -3
- 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/fields.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { regex } from "arkregex"
|
|
2
|
-
import { Result } from "effect"
|
|
3
2
|
import { AuthoringError } from "#errors.ts"
|
|
4
|
-
import { DbError } from "#runtime-errors.ts"
|
|
5
3
|
import type { LiteralSpec } from "#spec.ts"
|
|
6
|
-
import { Uuid } from "#uuid.ts"
|
|
4
|
+
import type { Uuid } from "#uuid.ts"
|
|
5
|
+
import { arrayValue, fieldValue, recordValue, taggedValueOf, U64_MAX } from "#values.ts"
|
|
7
6
|
|
|
8
7
|
const INTEGER_INDEX_NAME = regex("^(?:0|[1-9][0-9]*)$")
|
|
9
8
|
|
|
@@ -23,54 +22,15 @@ interface IntervalValue {
|
|
|
23
22
|
/**
|
|
24
23
|
* A half-open dense float interval `[start, end)` as a plain value object:
|
|
25
24
|
* two canonical binary64 bounds on the dense numeric line.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* denote a missing bound, not a member point.
|
|
25
|
+
* Field-directed validation rejects NaN endpoints and empty intervals and
|
|
26
|
+
* normalizes signed zero. Infinite bounds denote a missing bound, not a
|
|
27
|
+
* member point; a plain object alone is not evidence of validation.
|
|
30
28
|
*/
|
|
31
29
|
interface FloatIntervalValue {
|
|
32
30
|
readonly start: number
|
|
33
31
|
readonly end: number
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
/**
|
|
37
|
-
* Constructs a checked interval literal — the `start..end` spelling.
|
|
38
|
-
* Half-open and nonempty by construction. Fallible pure parsing
|
|
39
|
-
* returns `Result` (use `Effect.fromResult(span(...))` inside a generator),
|
|
40
|
-
* never hidden I/O and never a thrown domain outcome.
|
|
41
|
-
*
|
|
42
|
-
* Two element domains, selected by argument type: `span(0n, 60n)` is a
|
|
43
|
-
* discrete integer interval; `span(0.5, 1.5)` is a dense float interval
|
|
44
|
-
* with canonical endpoints (NaN refused, `-0` normalized to `+0`,
|
|
45
|
-
* `-Infinity` legal only as the lower bound and `+Infinity` only as the
|
|
46
|
-
* upper bound — both enforced by strict numeric `start < end`).
|
|
47
|
-
*/
|
|
48
|
-
function span(start: bigint, end: bigint): Result.Result<IntervalValue, DbError>
|
|
49
|
-
function span(start: number, end: number): Result.Result<FloatIntervalValue, DbError>
|
|
50
|
-
function span(
|
|
51
|
-
start: bigint | number,
|
|
52
|
-
end: bigint | number
|
|
53
|
-
): Result.Result<IntervalValue, DbError> | Result.Result<FloatIntervalValue, DbError> {
|
|
54
|
-
if (typeof start === "bigint" && typeof end === "bigint") {
|
|
55
|
-
if (start >= end) {
|
|
56
|
-
return Result.fail(new DbError({ operation: "span", reason: { _tag: "InvalidArgument" } }))
|
|
57
|
-
}
|
|
58
|
-
return Result.succeed(Object.freeze({ start, end }))
|
|
59
|
-
}
|
|
60
|
-
if (typeof start === "number" && typeof end === "number") {
|
|
61
|
-
if (Number.isNaN(start) || Number.isNaN(end)) {
|
|
62
|
-
return Result.fail(new DbError({ operation: "span", reason: { _tag: "InvalidArgument" } }))
|
|
63
|
-
}
|
|
64
|
-
const lo = Object.is(start, -0) ? 0 : start
|
|
65
|
-
const hi = Object.is(end, -0) ? 0 : end
|
|
66
|
-
if (!(lo < hi)) {
|
|
67
|
-
return Result.fail(new DbError({ operation: "span", reason: { _tag: "InvalidArgument" } }))
|
|
68
|
-
}
|
|
69
|
-
return Result.succeed(Object.freeze({ start: lo, end: hi }))
|
|
70
|
-
}
|
|
71
|
-
return Result.fail(new DbError({ operation: "span", reason: { _tag: "InvalidArgument" } }))
|
|
72
|
-
}
|
|
73
|
-
|
|
74
34
|
/**
|
|
75
35
|
* Nonempty declaration-order handle vector — the ONE roster carrier. The
|
|
76
36
|
* handle union is `Handles[number]`, the ordinal of a handle is its tuple
|
|
@@ -187,11 +147,11 @@ type Infer<F extends AnyField> = F extends { readonly kind: "bool" }
|
|
|
187
147
|
? Uuid
|
|
188
148
|
: F extends { readonly kind: "bytes" }
|
|
189
149
|
? Uint8Array
|
|
190
|
-
: F extends { readonly kind: "interval"; readonly element:
|
|
191
|
-
?
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
150
|
+
: F extends { readonly kind: "interval"; readonly element: infer Element }
|
|
151
|
+
? Element extends "f64"
|
|
152
|
+
? FloatIntervalValue
|
|
153
|
+
: IntervalValue
|
|
154
|
+
: never
|
|
195
155
|
|
|
196
156
|
/**
|
|
197
157
|
* The typed shape refusal shared by every literal machine — the selection
|
|
@@ -263,47 +223,10 @@ function isFloatIntervalValue(value: unknown): value is FloatIntervalValue {
|
|
|
263
223
|
)
|
|
264
224
|
}
|
|
265
225
|
|
|
266
|
-
function handleLiteral(closed: AnyClosedRoster, value: unknown): LiteralSpec {
|
|
267
|
-
if (typeof value !== "string") {
|
|
268
|
-
throw literalShapeError("selection literal", `a ${closed.name} handle name (string)`, value)
|
|
269
|
-
}
|
|
270
|
-
if (!closed.handles.includes(value)) {
|
|
271
|
-
throw new AuthoringError({
|
|
272
|
-
message: `"${value}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`
|
|
273
|
-
})
|
|
274
|
-
}
|
|
275
|
-
return { kind: "handle", handle: value }
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function intervalLiteral(element: IntervalElementKind, value: unknown): LiteralSpec {
|
|
279
|
-
if (element === "f64") {
|
|
280
|
-
if (!isFloatIntervalValue(value)) {
|
|
281
|
-
throw literalShapeError("selection literal", "float interval ({ start, end } numbers)", value)
|
|
282
|
-
}
|
|
283
|
-
if (Number.isNaN(value.start) || Number.isNaN(value.end) || !(value.start < value.end)) {
|
|
284
|
-
throw new AuthoringError({
|
|
285
|
-
message: "selection literal: a float interval is half-open and nonempty with non-NaN canonical endpoints"
|
|
286
|
-
})
|
|
287
|
-
}
|
|
288
|
-
return {
|
|
289
|
-
kind: "value",
|
|
290
|
-
value: {
|
|
291
|
-
kind: "intervalF64",
|
|
292
|
-
start: Object.is(value.start, -0) ? 0 : value.start,
|
|
293
|
-
end: Object.is(value.end, -0) ? 0 : value.end
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
if (!isIntervalValue(value)) {
|
|
298
|
-
throw literalShapeError("selection literal", "interval ({ start, end } bigints)", value)
|
|
299
|
-
}
|
|
300
|
-
if (element === "u64") {
|
|
301
|
-
return { kind: "value", value: { kind: "intervalU64", start: value.start, end: value.end } }
|
|
302
|
-
}
|
|
303
|
-
return { kind: "value", value: { kind: "intervalI64", start: value.start, end: value.end } }
|
|
304
|
-
}
|
|
305
|
-
|
|
306
226
|
function assertDeclarationOrderKey(where: string, name: string): void {
|
|
227
|
+
if (typeof name !== "string" || !name.isWellFormed()) {
|
|
228
|
+
throw new AuthoringError({ message: `${where}: expected a well-formed Unicode name` })
|
|
229
|
+
}
|
|
307
230
|
if (INTEGER_INDEX_NAME.test(name)) {
|
|
308
231
|
throw new AuthoringError({
|
|
309
232
|
message: `${where}: name ${name} is an integer index — JavaScript object keys re-order integer indices, breaking the declaration-order law; use a non-numeric name`
|
|
@@ -317,12 +240,92 @@ function assertDeclarationOrderKey(where: string, name: string): void {
|
|
|
317
240
|
}
|
|
318
241
|
|
|
319
242
|
function assertDeclarationRecord(where: string, record: object): void {
|
|
243
|
+
if (typeof record !== "object" || record === null) {
|
|
244
|
+
throw new AuthoringError({ message: `${where}: expected a plain declaration record` })
|
|
245
|
+
}
|
|
320
246
|
const proto = Object.getPrototypeOf(record)
|
|
321
247
|
if (proto !== Object.prototype && proto !== null) {
|
|
322
248
|
throw new AuthoringError({
|
|
323
249
|
message: `${where}: the declaration record's prototype was replaced — a plain \`__proto__: {...}\` entry is the prototype setter, so its key silently vanishes from the declaration; spell it computed (["__proto__"]: {...}) to declare it as data`
|
|
324
250
|
})
|
|
325
251
|
}
|
|
252
|
+
for (const key of Reflect.ownKeys(record)) {
|
|
253
|
+
const property = Object.getOwnPropertyDescriptor(record, key)
|
|
254
|
+
if (typeof key !== "string" || property?.enumerable !== true || !("value" in property)) {
|
|
255
|
+
throw new AuthoringError({ message: `${where}: declarations require enumerable own data fields` })
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Own a checked roster; never freeze the caller's array. */
|
|
261
|
+
function ownHandles<const H extends ClosedHandleTuple>(context: string, handles: H): H
|
|
262
|
+
function ownHandles(context: string, handles: unknown): ClosedHandleTuple
|
|
263
|
+
function ownHandles(context: string, handles: unknown): ClosedHandleTuple {
|
|
264
|
+
if (!Array.isArray(handles) || handles.length === 0) {
|
|
265
|
+
throw new AuthoringError({ message: `${context}: expected a nonempty handle tuple` })
|
|
266
|
+
}
|
|
267
|
+
const seen = new Set<string>()
|
|
268
|
+
const values = arrayValue(context, handles, (_, handle) => {
|
|
269
|
+
if (typeof handle !== "string") throw new AuthoringError({ message: `${context}: expected string handles` })
|
|
270
|
+
if (!handle.isWellFormed()) throw new AuthoringError({ message: `${context}: expected well-formed string handles` })
|
|
271
|
+
if (seen.has(handle)) throw new AuthoringError({ message: `${context}: duplicate handle ${handle}` })
|
|
272
|
+
seen.add(handle)
|
|
273
|
+
return handle
|
|
274
|
+
})
|
|
275
|
+
const first = values[0]
|
|
276
|
+
if (first === undefined) throw new AuthoringError({ message: `${context}: expected a nonempty handle tuple` })
|
|
277
|
+
return Object.freeze([first, ...values.slice(1)])
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Checked structural field descriptors, shared by declaration boundaries. */
|
|
281
|
+
function fieldDescriptor<F extends AnyField>(context: string, input: F): F
|
|
282
|
+
function fieldDescriptor(context: string, input: unknown): AnyField
|
|
283
|
+
function fieldDescriptor(context: string, input: unknown): AnyField {
|
|
284
|
+
if (typeof input !== "object" || input === null || !("kind" in input)) {
|
|
285
|
+
throw new AuthoringError({ message: `${context}: expected a field descriptor` })
|
|
286
|
+
}
|
|
287
|
+
assertDeclarationRecord(context, input)
|
|
288
|
+
switch (input.kind) {
|
|
289
|
+
case "bool":
|
|
290
|
+
case "str":
|
|
291
|
+
case "i64":
|
|
292
|
+
case "f64":
|
|
293
|
+
case "uuid":
|
|
294
|
+
recordValue(context, input, ["kind"])
|
|
295
|
+
return Object.freeze({ kind: input.kind })
|
|
296
|
+
case "u64": {
|
|
297
|
+
if (!("closed" in input)) {
|
|
298
|
+
recordValue(context, input, ["kind"])
|
|
299
|
+
return u64
|
|
300
|
+
}
|
|
301
|
+
recordValue(context, input, ["kind", "closed"])
|
|
302
|
+
const roster = recordValue(context, input.closed, ["name", "handles"])
|
|
303
|
+
if (typeof roster.name !== "string") throw new AuthoringError({ message: `${context}: expected a roster name` })
|
|
304
|
+
assertDeclarationOrderKey(context, roster.name)
|
|
305
|
+
return Object.freeze({
|
|
306
|
+
kind: "u64",
|
|
307
|
+
closed: Object.freeze({ name: roster.name, handles: ownHandles(context, roster.handles) })
|
|
308
|
+
})
|
|
309
|
+
}
|
|
310
|
+
case "bytes": {
|
|
311
|
+
const value = recordValue(context, input, ["kind", "width"])
|
|
312
|
+
if (typeof value.width !== "number") throw new AuthoringError({ message: `${context}: expected a bytes width` })
|
|
313
|
+
return bytes(value.width)
|
|
314
|
+
}
|
|
315
|
+
case "interval": {
|
|
316
|
+
const value = recordValue(context, input, ["kind", "element", "width"])
|
|
317
|
+
if (value.element !== "u64" && value.element !== "i64" && value.element !== "f64") {
|
|
318
|
+
throw new AuthoringError({ message: `${context}: expected an interval element kind` })
|
|
319
|
+
}
|
|
320
|
+
if (value.width === undefined) return interval({ kind: value.element })
|
|
321
|
+
if (value.element === "f64" || typeof value.width !== "bigint") {
|
|
322
|
+
throw new AuthoringError({ message: `${context}: only discrete intervals accept a bigint width` })
|
|
323
|
+
}
|
|
324
|
+
return interval({ kind: value.element }, value.width)
|
|
325
|
+
}
|
|
326
|
+
default:
|
|
327
|
+
throw new AuthoringError({ message: `${context}: unknown field kind` })
|
|
328
|
+
}
|
|
326
329
|
}
|
|
327
330
|
|
|
328
331
|
const u64: U64Field = Object.freeze({ kind: "u64" })
|
|
@@ -371,69 +374,19 @@ function interval(
|
|
|
371
374
|
"interval(f64) takes no width — a fixed-width float interval is unrepresentable (rounded start + width is not an exact fixed length on the dense line); applications supply two checked bounds"
|
|
372
375
|
})
|
|
373
376
|
}
|
|
374
|
-
if (width !== undefined && width < 1n) {
|
|
377
|
+
if (width !== undefined && (typeof width !== "bigint" || width < 1n || width > U64_MAX)) {
|
|
375
378
|
throw new AuthoringError({
|
|
376
|
-
message:
|
|
379
|
+
message: "interval width must be a bigint in 1..=u64::MAX"
|
|
377
380
|
})
|
|
378
381
|
}
|
|
379
382
|
return Object.freeze({ kind: "interval", element: elementKind, width })
|
|
380
383
|
}
|
|
381
384
|
|
|
382
385
|
function literalOf(field: AnyField, value: unknown): LiteralSpec {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
return handleLiteral(roster, value)
|
|
386
|
-
}
|
|
387
|
-
switch (field.kind) {
|
|
388
|
-
case "bool": {
|
|
389
|
-
if (typeof value !== "boolean") {
|
|
390
|
-
throw literalShapeError("selection literal", "boolean", value)
|
|
391
|
-
}
|
|
392
|
-
return { kind: "value", value: { kind: "bool", value } }
|
|
393
|
-
}
|
|
394
|
-
case "u64": {
|
|
395
|
-
if (typeof value !== "bigint") {
|
|
396
|
-
throw literalShapeError("selection literal", "bigint", value)
|
|
397
|
-
}
|
|
398
|
-
return { kind: "value", value: { kind: "u64", value } }
|
|
399
|
-
}
|
|
400
|
-
case "i64": {
|
|
401
|
-
if (typeof value !== "bigint") {
|
|
402
|
-
throw literalShapeError("selection literal", "bigint", value)
|
|
403
|
-
}
|
|
404
|
-
return { kind: "value", value: { kind: "i64", value } }
|
|
405
|
-
}
|
|
406
|
-
case "str": {
|
|
407
|
-
if (typeof value !== "string") {
|
|
408
|
-
throw literalShapeError("selection literal", "string", value)
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if (!value.isWellFormed()) {
|
|
412
|
-
throw literalShapeError("selection literal", "well-formed string", value)
|
|
413
|
-
}
|
|
414
|
-
return { kind: "value", value: { kind: "string", value } }
|
|
415
|
-
}
|
|
416
|
-
case "f64": {
|
|
417
|
-
if (typeof value !== "number") {
|
|
418
|
-
throw literalShapeError("selection literal", "number", value)
|
|
419
|
-
}
|
|
420
|
-
return { kind: "value", value: { kind: "f64", value } }
|
|
421
|
-
}
|
|
422
|
-
case "uuid": {
|
|
423
|
-
if (!Uuid.isUuid(value)) {
|
|
424
|
-
throw literalShapeError("selection literal", "a UUID (canonical UUID text)", value)
|
|
425
|
-
}
|
|
426
|
-
return { kind: "value", value: { kind: "uuid", value } }
|
|
427
|
-
}
|
|
428
|
-
case "bytes": {
|
|
429
|
-
if (!(value instanceof Uint8Array)) {
|
|
430
|
-
throw literalShapeError("selection literal", "Uint8Array", value)
|
|
431
|
-
}
|
|
432
|
-
return { kind: "value", value: { kind: "fixedBytes", value } }
|
|
433
|
-
}
|
|
434
|
-
case "interval":
|
|
435
|
-
return intervalLiteral(field.element, value)
|
|
386
|
+
if ("closed" in field) {
|
|
387
|
+
return { kind: "handle", handle: fieldValue("selection literal", field, value) }
|
|
436
388
|
}
|
|
389
|
+
return { kind: "value", value: taggedValueOf("selection literal", field, value) }
|
|
437
390
|
}
|
|
438
391
|
|
|
439
392
|
export type {
|
|
@@ -463,16 +416,17 @@ export {
|
|
|
463
416
|
bool,
|
|
464
417
|
bytes,
|
|
465
418
|
f64,
|
|
419
|
+
fieldDescriptor,
|
|
466
420
|
i64,
|
|
467
421
|
interval,
|
|
468
422
|
isFloatIntervalValue,
|
|
469
423
|
isIntervalValue,
|
|
470
424
|
literalOf,
|
|
471
425
|
literalShapeError,
|
|
426
|
+
ownHandles,
|
|
472
427
|
rosterOf,
|
|
473
428
|
rostersAgree,
|
|
474
429
|
signaturesAgree,
|
|
475
|
-
span,
|
|
476
430
|
str,
|
|
477
431
|
u64,
|
|
478
432
|
uuid
|
package/src/immutable.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Only frozen plain data graphs are stable cache keys. Typed-array contents
|
|
2
|
+
* remain mutable even behind a frozen parent, so they never qualify. */
|
|
3
|
+
const immutable = new WeakSet<object>()
|
|
4
|
+
|
|
5
|
+
function isImmutable(input: unknown, visiting = new WeakSet<object>()): boolean {
|
|
6
|
+
if (typeof input !== "object" || input === null) return typeof input !== "function"
|
|
7
|
+
if (immutable.has(input)) return true
|
|
8
|
+
const prototype = Object.getPrototypeOf(input)
|
|
9
|
+
if (
|
|
10
|
+
(prototype !== Object.prototype && prototype !== null && prototype !== Array.prototype) ||
|
|
11
|
+
!Object.isFrozen(input) ||
|
|
12
|
+
visiting.has(input)
|
|
13
|
+
)
|
|
14
|
+
return false
|
|
15
|
+
visiting.add(input)
|
|
16
|
+
for (const name of Reflect.ownKeys(input)) {
|
|
17
|
+
const property = Object.getOwnPropertyDescriptor(input, name)
|
|
18
|
+
if (property === undefined || !("value" in property) || !isImmutable(property.value, visiting)) {
|
|
19
|
+
visiting.delete(input)
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
visiting.delete(input)
|
|
24
|
+
immutable.add(input)
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Validation is never skipped for a mutable structural input. Validated
|
|
29
|
+
* immutable outputs can be reused without requiring constructor identity. */
|
|
30
|
+
function descriptorCache<A extends object>(parse: (input: unknown) => A): (input: unknown) => A {
|
|
31
|
+
const cache = new WeakMap<object, A>()
|
|
32
|
+
return (input) => {
|
|
33
|
+
if (typeof input === "object" && input !== null) {
|
|
34
|
+
const found = cache.get(input)
|
|
35
|
+
if (found !== undefined) return found
|
|
36
|
+
}
|
|
37
|
+
const result = parse(input)
|
|
38
|
+
if (isImmutable(result)) {
|
|
39
|
+
cache.set(result, result)
|
|
40
|
+
if (typeof input === "object" && input !== null && isImmutable(input)) cache.set(input, result)
|
|
41
|
+
}
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Detached inspection data: share proven immutable branches, copy byte
|
|
47
|
+
* buffers and their parents, and preserve references within one snapshot.
|
|
48
|
+
* This is for already checked internal data, not an input validator. */
|
|
49
|
+
function snapshotData<A>(input: A, onCopy?: (source: object, snapshot: object) => void): A
|
|
50
|
+
function snapshotData(input: unknown, onCopy?: (source: object, snapshot: object) => void): unknown {
|
|
51
|
+
const copied = new WeakMap<object, object>()
|
|
52
|
+
function visit(value: unknown): unknown {
|
|
53
|
+
if (typeof value !== "object" || value === null || isImmutable(value)) return value
|
|
54
|
+
const existing = copied.get(value)
|
|
55
|
+
if (existing !== undefined) return existing
|
|
56
|
+
if (value instanceof Uint8Array) {
|
|
57
|
+
const bytes = Uint8Array.from(value)
|
|
58
|
+
copied.set(value, bytes)
|
|
59
|
+
return bytes
|
|
60
|
+
}
|
|
61
|
+
const result = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value))
|
|
62
|
+
copied.set(value, result)
|
|
63
|
+
for (const name of Reflect.ownKeys(value)) {
|
|
64
|
+
if (Array.isArray(value) && name === "length") continue
|
|
65
|
+
const property = Object.getOwnPropertyDescriptor(value, name)
|
|
66
|
+
if (property === undefined || !("value" in property)) throw new TypeError("snapshotData requires checked data")
|
|
67
|
+
Object.defineProperty(result, name, { value: visit(property.value), enumerable: property.enumerable === true })
|
|
68
|
+
}
|
|
69
|
+
Object.freeze(result)
|
|
70
|
+
onCopy?.(value, result)
|
|
71
|
+
return result
|
|
72
|
+
}
|
|
73
|
+
return visit(input)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { descriptorCache, isImmutable, snapshotData }
|
package/src/index.ts
CHANGED
|
@@ -16,40 +16,35 @@ export type {
|
|
|
16
16
|
WeightOnSource
|
|
17
17
|
} from "#capacity.ts"
|
|
18
18
|
export { duration, ref, weigh, within } from "#capacity.ts"
|
|
19
|
-
export type { ChangeDraft } from "#changes.ts"
|
|
19
|
+
export type { ChangeCounts, ChangeDraft, ChangeRecord } from "#changes.ts"
|
|
20
20
|
export { ChangeSet } from "#changes.ts"
|
|
21
21
|
export type {
|
|
22
22
|
AnyClosed,
|
|
23
|
-
AnySelectedClosed,
|
|
24
23
|
AxiomRow,
|
|
25
24
|
Axioms,
|
|
26
25
|
Closed,
|
|
27
|
-
|
|
28
|
-
ClosedCore,
|
|
29
|
-
ClosedData,
|
|
30
|
-
ClosedRow,
|
|
31
|
-
ClosedSelectionInput,
|
|
32
|
-
PayloadField,
|
|
33
|
-
SelectedClosed
|
|
26
|
+
PayloadField
|
|
34
27
|
} from "#closed.ts"
|
|
35
|
-
export { closed } from "#closed.ts"
|
|
28
|
+
export { closed, closedId } from "#closed.ts"
|
|
36
29
|
export type { RowShape } from "#codec.ts"
|
|
37
30
|
export { decodeBoundaryRows, decodeRows, encodeBoundaryRows, encodeRows, rowSchema, rowShape } from "#codec.ts"
|
|
38
31
|
export type { CompiledSchema, SchemaId } from "#compile.ts"
|
|
39
32
|
export { Schema } from "#compile.ts"
|
|
40
33
|
export type {
|
|
41
|
-
ApplyExpected,
|
|
42
|
-
ApplyOptions,
|
|
43
34
|
ApplyOutcome,
|
|
44
35
|
CoreWitness,
|
|
45
36
|
DbInspection,
|
|
37
|
+
JudgeOutcome,
|
|
46
38
|
PreparedQuery,
|
|
47
39
|
QueryReader,
|
|
48
40
|
Snapshot,
|
|
49
|
-
StorageInspection
|
|
41
|
+
StorageInspection,
|
|
42
|
+
WriteExpected,
|
|
43
|
+
WriteOptions
|
|
50
44
|
} from "#db.ts"
|
|
51
45
|
export { Db } from "#db.ts"
|
|
52
46
|
export {
|
|
47
|
+
AuthoringDiagnostic,
|
|
53
48
|
AuthoringError,
|
|
54
49
|
NativeLoadError,
|
|
55
50
|
NativeOperationError,
|
|
@@ -61,7 +56,6 @@ export type {
|
|
|
61
56
|
Arity,
|
|
62
57
|
Face,
|
|
63
58
|
FaceArityMismatch,
|
|
64
|
-
FaceData,
|
|
65
59
|
FaceFields,
|
|
66
60
|
FaceOwner,
|
|
67
61
|
FaceShapeMismatch,
|
|
@@ -94,14 +88,32 @@ export type {
|
|
|
94
88
|
U64Field,
|
|
95
89
|
UuidField
|
|
96
90
|
} from "#fields.ts"
|
|
97
|
-
export { bool, bytes, f64, i64, interval,
|
|
91
|
+
export { bool, bytes, f64, i64, interval, str, u64, uuid } from "#fields.ts"
|
|
98
92
|
export type { Same, SameLen } from "#judgment.ts"
|
|
99
93
|
export type { ClassesOf, ClassWall, LawfulStatements, RelationClasses, SchemaClasses } from "#law.ts"
|
|
100
|
-
export type {
|
|
94
|
+
export type {
|
|
95
|
+
AtomIr,
|
|
96
|
+
AtomSourceIr,
|
|
97
|
+
CmpOpIr,
|
|
98
|
+
ComparisonIr,
|
|
99
|
+
ConditionTreeIr,
|
|
100
|
+
FindTermIr,
|
|
101
|
+
HeadTermIr,
|
|
102
|
+
InteriorIr,
|
|
103
|
+
QueryIr,
|
|
104
|
+
RecIr,
|
|
105
|
+
RuleIr,
|
|
106
|
+
ScalarExprIr,
|
|
107
|
+
TermIr,
|
|
108
|
+
Violation,
|
|
109
|
+
ViolationFact
|
|
110
|
+
} from "#native.ts"
|
|
101
111
|
export type { FindColumn } from "#query/atom.ts"
|
|
102
112
|
export { ALLEN } from "#query/atom.ts"
|
|
103
113
|
export type { AnyComputeExpr, ComputeExpr, ComputeValue, QueryNode } from "#query/compute.ts"
|
|
104
114
|
export { Compute } from "#query/compute.ts"
|
|
115
|
+
export type { DescriptionParameter, DescriptionRow, DescriptionTable, QueryDescription } from "#query/description.ts"
|
|
116
|
+
export { describeQuery, queryFromDescription } from "#query/description.ts"
|
|
105
117
|
export type { Agg, RowOfFind } from "#query/find.ts"
|
|
106
118
|
export type {
|
|
107
119
|
AnyQuery,
|
|
@@ -136,16 +148,11 @@ export type {
|
|
|
136
148
|
export { v } from "#query/scope.ts"
|
|
137
149
|
export type {
|
|
138
150
|
AnyRelation,
|
|
139
|
-
AnySelected,
|
|
140
151
|
Fact,
|
|
141
152
|
FieldsShape,
|
|
142
153
|
Relation,
|
|
143
|
-
RelationData,
|
|
144
154
|
RelationField,
|
|
145
|
-
RelationFields
|
|
146
|
-
Selected,
|
|
147
|
-
SelectionBinding,
|
|
148
|
-
SelectionInput
|
|
155
|
+
RelationFields
|
|
149
156
|
} from "#relation.ts"
|
|
150
157
|
export { relation } from "#relation.ts"
|
|
151
158
|
export type { CompleteResult } from "#result.ts"
|
|
@@ -169,6 +176,8 @@ export type {
|
|
|
169
176
|
export { Scalar } from "#scalar.ts"
|
|
170
177
|
export type { AnySchema, Schema as SchemaDeclaration, SchemaRelation, SchemaRelations } from "#schema.ts"
|
|
171
178
|
export { schema } from "#schema.ts"
|
|
179
|
+
export type { AnySelected, FieldsOf, Selected, SelectionBinding, SelectionInput } from "#selection.ts"
|
|
180
|
+
export { select } from "#selection.ts"
|
|
172
181
|
export type { Key, QueryTemplate, Rel } from "#shape.ts"
|
|
173
182
|
export type {
|
|
174
183
|
CapacityBoundSpec,
|
|
@@ -185,14 +194,11 @@ export type {
|
|
|
185
194
|
ValueTypeSpec
|
|
186
195
|
} from "#spec.ts"
|
|
187
196
|
export type {
|
|
188
|
-
CapacityData,
|
|
189
197
|
CapacityStatement,
|
|
190
|
-
|
|
191
|
-
ContainmentData,
|
|
192
|
-
KeyData,
|
|
198
|
+
ContainmentStatement,
|
|
193
199
|
KeyStatement,
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
MirrorsStatement,
|
|
201
|
+
Statement
|
|
196
202
|
} from "#statements.ts"
|
|
197
203
|
export { capacity, contained, key, mirrors, renderStatement } from "#statements.ts"
|
|
198
204
|
export { Uuid } from "#uuid.ts"
|
package/src/internal/log.ts
CHANGED
|
@@ -21,6 +21,7 @@ export {
|
|
|
21
21
|
nativeBindingIsLoaded
|
|
22
22
|
} from "#native.ts"
|
|
23
23
|
export type { CompleteResult } from "#result.ts"
|
|
24
|
+
export { factCellsOf } from "#rows.ts"
|
|
24
25
|
export type { NativeRuntimeOptions, RepositoryLock } from "#runtime.ts"
|
|
25
26
|
export {
|
|
26
27
|
finalizeClose,
|
|
@@ -44,3 +45,4 @@ export type {
|
|
|
44
45
|
RuntimeHandle
|
|
45
46
|
} from "#runtime-native.ts"
|
|
46
47
|
export { runtimeNative } from "#runtime-native.ts"
|
|
48
|
+
export { schemasAgree } from "#schema.ts"
|
package/src/law.ts
CHANGED
|
@@ -58,7 +58,7 @@ import { AuthoringError } from "#errors.ts"
|
|
|
58
58
|
|
|
59
59
|
import type { AnyClosed } from "#closed.ts"
|
|
60
60
|
import { isClosedMember, sealedFieldsOf } from "#closed.ts"
|
|
61
|
-
import type {
|
|
61
|
+
import type { AnyFace } from "#face.ts"
|
|
62
62
|
import type { Same } from "#judgment.ts"
|
|
63
63
|
import type { AnyRelation, RelationFields } from "#relation.ts"
|
|
64
64
|
import type { SchemaRelation, SchemaRelations } from "#schema.ts"
|
|
@@ -104,9 +104,9 @@ type ZipCoords<
|
|
|
104
104
|
: Acc
|
|
105
105
|
: Acc
|
|
106
106
|
|
|
107
|
-
type StatementPairs<St extends Statement> = St
|
|
108
|
-
readonly source: infer S extends
|
|
109
|
-
readonly target: infer T extends
|
|
107
|
+
type StatementPairs<St extends Statement> = St extends {
|
|
108
|
+
readonly source: infer S extends AnyFace
|
|
109
|
+
readonly target: infer T extends AnyFace
|
|
110
110
|
}
|
|
111
111
|
? string extends S["owner"]["name"]
|
|
112
112
|
? []
|
|
@@ -208,7 +208,7 @@ type DeclaredKeysOf<Stmts extends readonly Statement[], Acc extends readonly Key
|
|
|
208
208
|
infer H extends Statement,
|
|
209
209
|
...infer T extends readonly Statement[]
|
|
210
210
|
]
|
|
211
|
-
? H
|
|
211
|
+
? H extends {
|
|
212
212
|
readonly kind: "key"
|
|
213
213
|
readonly owner: infer O extends AnyRelation
|
|
214
214
|
readonly projection: infer P extends readonly string[]
|
|
@@ -295,8 +295,8 @@ type DecidableRoster<Stmts extends readonly Statement[]> = [true] extends [IsMul
|
|
|
295
295
|
: [Stmts] extends [readonly [infer H extends Statement, ...infer T extends readonly Statement[]]]
|
|
296
296
|
? [true] extends [IsMulti<H>]
|
|
297
297
|
? false
|
|
298
|
-
: "key" extends H["
|
|
299
|
-
? [DecidableKeyData<H
|
|
298
|
+
: "key" extends H["kind"]
|
|
299
|
+
? [DecidableKeyData<H>] extends [true]
|
|
300
300
|
? DecidableRoster<T>
|
|
301
301
|
: false
|
|
302
302
|
: DecidableRoster<T>
|
|
@@ -322,7 +322,7 @@ type DeclaredKeyMatch<N extends string, PU extends string, Keys extends readonly
|
|
|
322
322
|
: DeclaredKeyMatch<N, PU, T>
|
|
323
323
|
: TargetKeyWall<N, PU>
|
|
324
324
|
|
|
325
|
-
type JudgeTargetFace<F extends
|
|
325
|
+
type JudgeTargetFace<F extends AnyFace, Keys extends readonly KeyEntry[]> = string extends F["owner"]["name"]
|
|
326
326
|
? true
|
|
327
327
|
: string extends F["projection"][number]
|
|
328
328
|
? true
|
|
@@ -334,9 +334,9 @@ type JudgeTargetFace<F extends FaceData, Keys extends readonly KeyEntry[]> = str
|
|
|
334
334
|
? DeclaredKeyMatch<F["owner"]["name"], F["projection"][number], Keys>
|
|
335
335
|
: true
|
|
336
336
|
|
|
337
|
-
type JudgeStatement<St extends Statement, Keys extends readonly KeyEntry[]> = St
|
|
338
|
-
readonly source:
|
|
339
|
-
readonly target: infer Tg extends
|
|
337
|
+
type JudgeStatement<St extends Statement, Keys extends readonly KeyEntry[]> = St extends {
|
|
338
|
+
readonly source: AnyFace
|
|
339
|
+
readonly target: infer Tg extends AnyFace
|
|
340
340
|
}
|
|
341
341
|
? JudgeTargetFace<Tg, Keys>
|
|
342
342
|
: true
|
|
@@ -452,8 +452,8 @@ function makeUnionFind(): UnionFind {
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
-
function statementFaces(statement: Statement): readonly [
|
|
456
|
-
const data = statement
|
|
455
|
+
function statementFaces(statement: Statement): readonly [AnyFace, AnyFace] | undefined {
|
|
456
|
+
const data = statement
|
|
457
457
|
if (data.kind === "key") {
|
|
458
458
|
return undefined
|
|
459
459
|
}
|
package/src/lower.ts
CHANGED
|
@@ -15,10 +15,10 @@ import { AuthoringError } from "#errors.ts"
|
|
|
15
15
|
|
|
16
16
|
import type { AnyClosed } from "#closed.ts"
|
|
17
17
|
import { isClosedMember } from "#closed.ts"
|
|
18
|
-
import type {
|
|
19
|
-
import type
|
|
18
|
+
import type { AnyFace } from "#face.ts"
|
|
19
|
+
import { type AnyField, literalOf } from "#fields.ts"
|
|
20
20
|
import type { RelationClasses } from "#law.ts"
|
|
21
|
-
import type
|
|
21
|
+
import { type AnyRelation, relationFields } from "#relation.ts"
|
|
22
22
|
import type { AnySchema } from "#schema.ts"
|
|
23
23
|
import type {
|
|
24
24
|
FieldSpec,
|
|
@@ -60,7 +60,7 @@ function lowerField(name: string, field: AnyField, newtype: string | undefined):
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
function lowerFace(face:
|
|
63
|
+
function lowerFace(face: AnyFace): SideSpec {
|
|
64
64
|
return {
|
|
65
65
|
relation: face.owner.name,
|
|
66
66
|
projection: [...face.projection],
|
|
@@ -71,7 +71,7 @@ function lowerFace(face: FaceData): SideSpec {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function lowerStatement(statement: Statement): StatementSpec {
|
|
74
|
-
const data = statement
|
|
74
|
+
const data = statement
|
|
75
75
|
switch (data.kind) {
|
|
76
76
|
case "key":
|
|
77
77
|
return { kind: "fd", relation: data.owner.name, projection: [...data.projection] }
|
|
@@ -101,18 +101,21 @@ function lowerStatement(statement: Statement): StatementSpec {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
function lowerRelation(relation: AnyRelation, classes: RelationClasses): RelationSpec {
|
|
104
|
-
const fields: FieldSpec[] = relation.
|
|
104
|
+
const fields: FieldSpec[] = relationFields(relation).map(function lowerDeclared(declared) {
|
|
105
105
|
return lowerField(declared.name, declared.field, classes[declared.name])
|
|
106
106
|
})
|
|
107
107
|
return { name: relation.name, fields, closed: undefined }
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function lowerClosed(member: AnyClosed, classes: RelationClasses): RelationSpec {
|
|
111
|
-
const fields: FieldSpec[] = member.
|
|
112
|
-
return lowerField(
|
|
111
|
+
const fields: FieldSpec[] = Object.entries(member.columns).map(function lowerColumn([name, field]) {
|
|
112
|
+
return lowerField(name, field, classes[name])
|
|
113
113
|
})
|
|
114
|
-
const rows = member.
|
|
115
|
-
return {
|
|
114
|
+
const rows = member.handles.map(function lowerRow(handle) {
|
|
115
|
+
return {
|
|
116
|
+
handle,
|
|
117
|
+
values: Object.entries(member.columns).map(([name, field]) => literalOf(field, member.axioms[handle]?.[name]))
|
|
118
|
+
}
|
|
116
119
|
})
|
|
117
120
|
const newtype = classes.id
|
|
118
121
|
if (newtype === undefined) {
|