@bjornpagen/bumbledb 0.19.0 → 0.19.2
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 +2 -0
- package/README.md +1 -0
- package/dist/closed.d.ts +9 -9
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +27 -18
- package/dist/closed.js.map +1 -1
- package/dist/face.d.ts +18 -23
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +54 -8
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +28 -1
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/judgment.d.ts +13 -0
- package/dist/judgment.d.ts.map +1 -0
- package/dist/judgment.js +11 -0
- package/dist/judgment.js.map +1 -0
- package/dist/law.d.ts +2 -1
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js.map +1 -1
- package/dist/marshal.d.ts +2 -2
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js.map +1 -1
- package/dist/query/atom.d.ts +10 -6
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +4 -4
- package/dist/query/lower.js.map +1 -1
- package/dist/query/scope.d.ts +38 -31
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +17 -13
- package/dist/query/scope.js.map +1 -1
- package/dist/statements.js +2 -2
- package/dist/statements.js.map +1 -1
- package/package.json +3 -3
- package/src/closed.ts +85 -51
- package/src/face.ts +18 -24
- package/src/fields.ts +85 -7
- package/src/index.ts +5 -0
- package/src/judgment.ts +25 -0
- package/src/law.ts +2 -1
- package/src/marshal.ts +3 -3
- package/src/query/atom.ts +6 -5
- package/src/query/lower.ts +17 -7
- package/src/query/scope.ts +79 -47
- package/src/statements.ts +3 -3
package/src/closed.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as errors from "@superbuilders/errors"
|
|
2
2
|
import {
|
|
3
|
+
type AnyClosedIdField,
|
|
3
4
|
type AnyField,
|
|
4
5
|
assertDeclarationOrderKey,
|
|
5
6
|
assertDeclarationRecord,
|
|
7
|
+
type ClosedHandleTuple,
|
|
6
8
|
type ClosedIdField,
|
|
7
9
|
type ClosedRoster,
|
|
8
10
|
type Infer,
|
|
@@ -39,20 +41,28 @@ type Axioms<Handles extends string, Cols extends Record<string, PayloadField>> =
|
|
|
39
41
|
readonly [H in Handles]: AxiomRow<Cols>
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
interface ClosedCore<
|
|
44
|
+
interface ClosedCore<
|
|
45
|
+
Name extends string,
|
|
46
|
+
Handles extends ClosedHandleTuple,
|
|
47
|
+
Cols extends Record<string, PayloadField>
|
|
48
|
+
> {
|
|
43
49
|
readonly name: Name
|
|
44
50
|
|
|
45
51
|
readonly id: ClosedIdField<Name, Handles>
|
|
46
52
|
readonly data: ClosedData
|
|
47
53
|
|
|
48
|
-
readonly axioms: Axioms<Handles, Cols>
|
|
54
|
+
readonly axioms: Axioms<Handles[number], Cols>
|
|
49
55
|
|
|
50
56
|
readonly columns: Cols
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
type ClosedSelectionInput<Cols extends Record<string, PayloadField>> = SelectionInput<Cols>
|
|
54
60
|
|
|
55
|
-
interface SelectedClosed<
|
|
61
|
+
interface SelectedClosed<
|
|
62
|
+
Name extends string,
|
|
63
|
+
Handles extends ClosedHandleTuple,
|
|
64
|
+
Cols extends Record<string, PayloadField>
|
|
65
|
+
> {
|
|
56
66
|
readonly relation: Closed<Name, Handles, Cols>
|
|
57
67
|
readonly selection: readonly SelectionBinding[]
|
|
58
68
|
}
|
|
@@ -62,11 +72,15 @@ interface AnySelectedClosed {
|
|
|
62
72
|
readonly selection: readonly SelectionBinding[]
|
|
63
73
|
}
|
|
64
74
|
|
|
65
|
-
interface ClosedSelectable<
|
|
75
|
+
interface ClosedSelectable<
|
|
76
|
+
Name extends string,
|
|
77
|
+
Handles extends ClosedHandleTuple,
|
|
78
|
+
Cols extends Record<string, PayloadField>
|
|
79
|
+
> {
|
|
66
80
|
where(selection: ClosedSelectionInput<Cols>): SelectedClosed<Name, Handles, Cols>
|
|
67
81
|
}
|
|
68
82
|
|
|
69
|
-
type Closed<Name extends string, Handles extends
|
|
83
|
+
type Closed<Name extends string, Handles extends ClosedHandleTuple, Cols extends Record<string, PayloadField>> = [
|
|
70
84
|
keyof Cols
|
|
71
85
|
] extends [never]
|
|
72
86
|
? ClosedCore<Name, Handles, Cols>
|
|
@@ -74,7 +88,7 @@ type Closed<Name extends string, Handles extends string, Cols extends Record<str
|
|
|
74
88
|
|
|
75
89
|
interface AnyClosed {
|
|
76
90
|
readonly name: string
|
|
77
|
-
readonly id:
|
|
91
|
+
readonly id: AnyClosedIdField
|
|
78
92
|
readonly data: ClosedData
|
|
79
93
|
readonly axioms: Readonly<Record<string, object>>
|
|
80
94
|
readonly columns: Readonly<Record<string, PayloadField>>
|
|
@@ -98,25 +112,34 @@ function sealedFieldOf(member: AnyRelation | AnyClosed, fieldName: string): AnyF
|
|
|
98
112
|
return declared?.field
|
|
99
113
|
}
|
|
100
114
|
|
|
101
|
-
function isHandleTuple(
|
|
102
|
-
|
|
103
|
-
)
|
|
104
|
-
|
|
115
|
+
function isHandleTuple(shape: unknown): shape is ClosedHandleTuple {
|
|
116
|
+
return (
|
|
117
|
+
Array.isArray(shape) &&
|
|
118
|
+
shape.length > 0 &&
|
|
119
|
+
shape.every(function stringHandle(handle) {
|
|
120
|
+
return typeof handle === "string"
|
|
121
|
+
})
|
|
122
|
+
)
|
|
105
123
|
}
|
|
106
124
|
|
|
107
125
|
/**
|
|
108
|
-
* The trusted seam of the payload tier
|
|
109
|
-
* record
|
|
110
|
-
*
|
|
111
|
-
* that checkable fact before the key list is admitted at the handle type.
|
|
126
|
+
* The trusted seam of the payload tier: the handle tuple and the axioms
|
|
127
|
+
* record are the same finite set — every tuple name is an own key, and
|
|
128
|
+
* every own key is in the tuple.
|
|
112
129
|
*/
|
|
113
130
|
function handleKeysOwn<Handles extends string>(
|
|
114
131
|
axioms: { readonly [H in Handles]: object },
|
|
115
|
-
names: readonly
|
|
116
|
-
):
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
names: readonly Handles[]
|
|
133
|
+
): boolean {
|
|
134
|
+
const listed = new Set<string>(names)
|
|
135
|
+
return (
|
|
136
|
+
names.every(function ownHandle(name) {
|
|
137
|
+
return Object.hasOwn(axioms, name)
|
|
138
|
+
}) &&
|
|
139
|
+
Object.keys(axioms).every(function listedHandle(name) {
|
|
140
|
+
return listed.has(name)
|
|
141
|
+
})
|
|
142
|
+
)
|
|
120
143
|
}
|
|
121
144
|
|
|
122
145
|
/**
|
|
@@ -160,40 +183,50 @@ function mintAxioms<Handles extends string, Cols extends Record<string, PayloadF
|
|
|
160
183
|
return out
|
|
161
184
|
}
|
|
162
185
|
|
|
163
|
-
function closed<const Name extends string, const Handles extends
|
|
186
|
+
function closed<const Name extends string, const Handles extends ClosedHandleTuple>(
|
|
164
187
|
name: Name,
|
|
165
188
|
handles: Handles
|
|
166
|
-
): Closed<Name, Handles
|
|
189
|
+
): Closed<Name, Handles, Record<never, never>>
|
|
167
190
|
|
|
168
|
-
function closed<const Name extends string, const
|
|
191
|
+
function closed<const Name extends string, const Handles extends ClosedHandleTuple, const Cols extends PayloadColumns>(
|
|
169
192
|
name: Name,
|
|
193
|
+
handles: Handles,
|
|
170
194
|
columns: Cols,
|
|
171
|
-
axioms: Axioms<Handles, Cols>
|
|
195
|
+
axioms: Axioms<Handles[number], Cols>
|
|
172
196
|
): Closed<Name, Handles, Cols>
|
|
173
197
|
|
|
174
|
-
function closed<
|
|
198
|
+
function closed<Name extends string, Handles extends ClosedHandleTuple, Cols extends PayloadColumns>(
|
|
175
199
|
name: Name,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
200
|
+
handles: Handles,
|
|
201
|
+
columns?: Cols,
|
|
202
|
+
axioms?: Axioms<Handles[number], Cols>
|
|
203
|
+
): Closed<Name, Handles, Record<never, never>> | Closed<Name, Handles, Cols> {
|
|
204
|
+
if (!Array.isArray(handles)) {
|
|
205
|
+
throw errors.new(
|
|
206
|
+
`closed relation ${name}: payload columns declared without ground axioms — the payload tier is spelled closed(name, handles, columns, axioms)`
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
if (!isHandleTuple(handles)) {
|
|
210
|
+
throw errors.new(`closed relation ${name}: at least one handle is required (an empty vocabulary declares nothing)`)
|
|
211
|
+
}
|
|
212
|
+
if (columns === undefined) {
|
|
180
213
|
if (axioms !== undefined) {
|
|
181
214
|
throw errors.new(`closed relation ${name}: the bare tier declares no columns, so ground axioms are inadmissible`)
|
|
182
215
|
}
|
|
183
|
-
return closedBare(name,
|
|
216
|
+
return closedBare(name, handles)
|
|
184
217
|
}
|
|
185
218
|
if (axioms === undefined) {
|
|
186
219
|
throw errors.new(
|
|
187
|
-
`closed relation ${name}: payload columns declared without ground axioms — the payload tier is spelled closed(name, columns, axioms)
|
|
220
|
+
`closed relation ${name}: payload columns declared without ground axioms — the payload tier is spelled closed(name, handles, columns, axioms)`
|
|
188
221
|
)
|
|
189
222
|
}
|
|
190
|
-
return closedPayload(name,
|
|
223
|
+
return closedPayload(name, handles, columns, axioms)
|
|
191
224
|
}
|
|
192
225
|
|
|
193
|
-
function closedBare<Name extends string,
|
|
226
|
+
function closedBare<Name extends string, const Hs extends ClosedHandleTuple>(
|
|
194
227
|
name: Name,
|
|
195
|
-
handles:
|
|
196
|
-
): Closed<Name,
|
|
228
|
+
handles: Hs
|
|
229
|
+
): Closed<Name, Hs, Record<never, never>> {
|
|
197
230
|
const empty: Record<string, object> = {}
|
|
198
231
|
for (const handle of handles) {
|
|
199
232
|
/** A duplicated name mints one row; the roster's own duplicate refusal in {@link mintClosed} stays the judge. */
|
|
@@ -202,33 +235,37 @@ function closedBare<Name extends string, Handles extends string>(
|
|
|
202
235
|
}
|
|
203
236
|
}
|
|
204
237
|
Object.freeze(empty)
|
|
205
|
-
if (!axiomsMinted<
|
|
238
|
+
if (!axiomsMinted<Hs[number], Record<never, never>>(empty, handles, [])) {
|
|
206
239
|
throw errors.new(`closed relation ${name}: bare-tier axiom-row minting incomplete`)
|
|
207
240
|
}
|
|
208
|
-
return mintClosed
|
|
241
|
+
return mintClosed(name, handles, {}, empty)
|
|
209
242
|
}
|
|
210
243
|
|
|
211
|
-
function closedPayload<Name extends string, Handles extends
|
|
244
|
+
function closedPayload<Name extends string, Handles extends ClosedHandleTuple, Cols extends PayloadColumns>(
|
|
212
245
|
name: Name,
|
|
246
|
+
handles: Handles,
|
|
213
247
|
columns: Cols,
|
|
214
|
-
axioms: Axioms<Handles, Cols>
|
|
248
|
+
axioms: Axioms<Handles[number], Cols>
|
|
215
249
|
): Closed<Name, Handles, Cols> {
|
|
216
250
|
assertDeclarationRecord(`closed relation ${name} columns`, columns)
|
|
217
251
|
for (const columnName of Object.keys(columns)) {
|
|
218
252
|
assertDeclarationOrderKey(`closed relation ${name} column`, columnName)
|
|
219
253
|
}
|
|
220
254
|
assertDeclarationRecord(`closed relation ${name} axioms`, axioms)
|
|
221
|
-
const handles = Object.keys(axioms)
|
|
222
255
|
for (const handle of handles) {
|
|
223
256
|
assertDeclarationOrderKey(`closed relation ${name} handle`, handle)
|
|
224
257
|
}
|
|
225
258
|
if (!handleKeysOwn(axioms, handles)) {
|
|
226
259
|
throw errors.new(`closed relation ${name}: handle enumeration incomplete`)
|
|
227
260
|
}
|
|
228
|
-
return mintClosed
|
|
261
|
+
return mintClosed(name, handles, columns, axioms)
|
|
229
262
|
}
|
|
230
263
|
|
|
231
|
-
function surfaceMinted<
|
|
264
|
+
function surfaceMinted<
|
|
265
|
+
Name extends string,
|
|
266
|
+
Handles extends ClosedHandleTuple,
|
|
267
|
+
Cols extends Record<string, PayloadField>
|
|
268
|
+
>(
|
|
232
269
|
value: ClosedCore<Name, Handles, Cols>,
|
|
233
270
|
cols: readonly ClosedColumn[]
|
|
234
271
|
): value is ClosedCore<Name, Handles, Cols> & Closed<Name, Handles, Cols> {
|
|
@@ -236,16 +273,13 @@ function surfaceMinted<Name extends string, Handles extends string, Cols extends
|
|
|
236
273
|
return cols.length > 0 ? selectable : !selectable
|
|
237
274
|
}
|
|
238
275
|
|
|
239
|
-
function mintClosed<Name extends string, Handles extends
|
|
276
|
+
function mintClosed<Name extends string, Handles extends ClosedHandleTuple, Cols extends Record<string, PayloadField>>(
|
|
240
277
|
name: Name,
|
|
241
|
-
handles:
|
|
278
|
+
handles: Handles,
|
|
242
279
|
columns: Cols,
|
|
243
|
-
axioms: Axioms<Handles, Cols>
|
|
280
|
+
axioms: Axioms<Handles[number], Cols>
|
|
244
281
|
): Closed<Name, Handles, Cols> {
|
|
245
282
|
assertDeclarationOrderKey("closed relation", name)
|
|
246
|
-
if (handles.length === 0) {
|
|
247
|
-
throw errors.new(`closed relation ${name}: at least one handle is required (an empty vocabulary declares nothing)`)
|
|
248
|
-
}
|
|
249
283
|
const seen = new Set<string>()
|
|
250
284
|
for (const handle of handles) {
|
|
251
285
|
if (seen.has(handle)) {
|
|
@@ -253,7 +287,7 @@ function mintClosed<Name extends string, Handles extends string, Cols extends Re
|
|
|
253
287
|
}
|
|
254
288
|
seen.add(handle)
|
|
255
289
|
}
|
|
256
|
-
const handleList
|
|
290
|
+
const handleList = Object.freeze(handles)
|
|
257
291
|
const roster: ClosedRoster<Name, Handles> = Object.freeze({ name, handles: handleList })
|
|
258
292
|
const cols: ClosedColumn[] = []
|
|
259
293
|
for (const [columnName, field] of Object.entries(columns)) {
|
|
@@ -266,7 +300,7 @@ function mintClosed<Name extends string, Handles extends string, Cols extends Re
|
|
|
266
300
|
cols.push(Object.freeze({ name: columnName, field }))
|
|
267
301
|
}
|
|
268
302
|
Object.freeze(cols)
|
|
269
|
-
const rows: ClosedRow[] = handleList.map(function lowerRow(handle) {
|
|
303
|
+
const rows: ClosedRow[] = handleList.map(function lowerRow(handle: Handles[number]) {
|
|
270
304
|
const row: Readonly<Record<string, unknown>> = axioms[handle]
|
|
271
305
|
const values = cols.map(function lowerAxiomLiteral(column) {
|
|
272
306
|
return Object.freeze(literalOf(column.field, row[column.name]))
|
|
@@ -281,7 +315,7 @@ function mintClosed<Name extends string, Handles extends string, Cols extends Re
|
|
|
281
315
|
})
|
|
282
316
|
const id: ClosedIdField<Name, Handles> = Object.freeze({ kind: "u64", closed: roster })
|
|
283
317
|
|
|
284
|
-
const axiomsOut = mintAxioms<Handles, Cols>(name, handleList, cols, axioms)
|
|
318
|
+
const axiomsOut = mintAxioms<Handles[number], Cols>(name, handleList, cols, axioms)
|
|
285
319
|
const columnsOut: Cols = { ...columns }
|
|
286
320
|
Object.freeze(columnsOut)
|
|
287
321
|
const holder: { value: Closed<Name, Handles, Cols> | undefined } = { value: undefined }
|
|
@@ -299,7 +333,7 @@ function mintClosed<Name extends string, Handles extends string, Cols extends Re
|
|
|
299
333
|
const core = { name, id, data, axioms: axiomsOut, columns: columnsOut }
|
|
300
334
|
const value: ClosedCore<Name, Handles, Cols> =
|
|
301
335
|
cols.length > 0 ? Object.freeze({ ...core, where }) : Object.freeze(core)
|
|
302
|
-
if (!surfaceMinted
|
|
336
|
+
if (!surfaceMinted(value, cols)) {
|
|
303
337
|
throw errors.new(`closed relation ${name}: ergonomic-surface minting incomplete`)
|
|
304
338
|
}
|
|
305
339
|
holder.value = value
|
package/src/face.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as errors from "@superbuilders/errors"
|
|
2
2
|
import type { AnyClosed, AnySelectedClosed, PayloadField } from "#closed.ts"
|
|
3
|
-
import type { AnyField } from "#fields.ts"
|
|
3
|
+
import type { AnyField, SignatureOf } from "#fields.ts"
|
|
4
|
+
import type { Same } from "#judgment.ts"
|
|
4
5
|
import type { AnyRelation, AnySelected, FieldsShape, RelationFields, SelectionBinding } from "#relation.ts"
|
|
5
6
|
import { renderLiteralSet } from "#spec.ts"
|
|
6
7
|
|
|
@@ -46,24 +47,25 @@ type FaceFields<S extends FaceSource> = S extends AnySelected
|
|
|
46
47
|
? "id" | (keyof Row & string)
|
|
47
48
|
: never
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
]
|
|
50
|
+
/**
|
|
51
|
+
* The wire shape a face projects: the field's {@link SignatureOf} with an
|
|
52
|
+
* interval's width erased. A fixed-width interval pairs with a plain one —
|
|
53
|
+
* width is a measure label, not wire structure. Bytes width IS wire
|
|
54
|
+
* structure and stays. The roster stays the full handle vector.
|
|
55
|
+
*/
|
|
56
|
+
type ProjectedSignature<F extends AnyField> = F extends { readonly kind: "interval" }
|
|
57
|
+
? readonly [F["kind"], undefined, F extends { readonly element: infer E } ? E : undefined, undefined]
|
|
58
|
+
: SignatureOf<F>
|
|
59
59
|
|
|
60
|
-
type ShapeIn<Fields extends FieldsShape, K extends string> = K extends keyof Fields
|
|
60
|
+
type ShapeIn<Fields extends FieldsShape, K extends string> = K extends keyof Fields
|
|
61
|
+
? ProjectedSignature<Fields[K]>
|
|
62
|
+
: undefined
|
|
61
63
|
|
|
62
64
|
type ProjectedShape<S extends FaceSource, K extends string> = S extends AnySelected
|
|
63
65
|
? ShapeIn<RelationFields<S["relation"]>, K>
|
|
64
66
|
: S extends AnySelectedClosed
|
|
65
67
|
? K extends "id"
|
|
66
|
-
?
|
|
68
|
+
? ProjectedSignature<S["relation"]["id"]>
|
|
67
69
|
: ShapeIn<S["relation"]["columns"], K>
|
|
68
70
|
: S extends AnyRelation
|
|
69
71
|
? ShapeIn<RelationFields<S>, K>
|
|
@@ -72,7 +74,7 @@ type ProjectedShape<S extends FaceSource, K extends string> = S extends AnySelec
|
|
|
72
74
|
readonly columns: infer Cols extends Record<string, PayloadField>
|
|
73
75
|
}
|
|
74
76
|
? K extends "id"
|
|
75
|
-
?
|
|
77
|
+
? ProjectedSignature<Id>
|
|
76
78
|
: ShapeIn<Cols, K>
|
|
77
79
|
: undefined
|
|
78
80
|
|
|
@@ -92,11 +94,7 @@ interface FaceArityMismatch<Left, Right> {
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
type SameArity<A extends AnyFace, B extends AnyFace> =
|
|
95
|
-
Arity<A
|
|
96
|
-
? Arity<B> extends Arity<A>
|
|
97
|
-
? unknown
|
|
98
|
-
: FaceArityMismatch<Arity<A>, Arity<B>>
|
|
99
|
-
: FaceArityMismatch<Arity<A>, Arity<B>>
|
|
97
|
+
Same<Arity<A>, Arity<B>> extends true ? unknown : FaceArityMismatch<Arity<A>, Arity<B>>
|
|
100
98
|
|
|
101
99
|
interface FaceShapeMismatch<Left, Right> {
|
|
102
100
|
readonly "face shape mismatch — positionwise kind, width, element, and closed roster must be equal on both sides": readonly [
|
|
@@ -106,11 +104,7 @@ interface FaceShapeMismatch<Left, Right> {
|
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
type SameShapes<A extends AnyFace, B extends AnyFace> =
|
|
109
|
-
FaceShapes<A
|
|
110
|
-
? FaceShapes<B> extends FaceShapes<A>
|
|
111
|
-
? unknown
|
|
112
|
-
: FaceShapeMismatch<FaceShapes<A>, FaceShapes<B>>
|
|
113
|
-
: FaceShapeMismatch<FaceShapes<A>, FaceShapes<B>>
|
|
107
|
+
Same<FaceShapes<A>, FaceShapes<B>> extends true ? unknown : FaceShapeMismatch<FaceShapes<A>, FaceShapes<B>>
|
|
114
108
|
|
|
115
109
|
function on<S extends FaceSource, const F extends FaceFields<S>>(source: S, field: F): Face<S, readonly [F]>
|
|
116
110
|
function on<S extends FaceSource, const P extends readonly [FaceFields<S>, ...FaceFields<S>[]]>(
|
package/src/fields.ts
CHANGED
|
@@ -32,11 +32,22 @@ function span(start: bigint, end: bigint): IntervalValue {
|
|
|
32
32
|
return Object.freeze({ start, end })
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Nonempty declaration-order handle vector — the ONE roster carrier. The
|
|
37
|
+
* handle union is `Handles[number]`, the ordinal of a handle is its tuple
|
|
38
|
+
* position, and the roster size is the tuple length. An empty vocabulary
|
|
39
|
+
* is unspellable.
|
|
40
|
+
*/
|
|
41
|
+
type ClosedHandleTuple = readonly [string, ...string[]]
|
|
42
|
+
|
|
43
|
+
interface ClosedRoster<Name extends string, Handles extends ClosedHandleTuple> {
|
|
36
44
|
readonly name: Name
|
|
37
|
-
readonly handles:
|
|
45
|
+
readonly handles: Handles
|
|
38
46
|
}
|
|
39
47
|
|
|
48
|
+
/** The roster top type — what an erased carrier knows about any roster. */
|
|
49
|
+
type AnyClosedRoster = ClosedRoster<string, ClosedHandleTuple>
|
|
50
|
+
|
|
40
51
|
interface BoolField {
|
|
41
52
|
readonly kind: "bool"
|
|
42
53
|
}
|
|
@@ -73,12 +84,42 @@ interface IntervalField<
|
|
|
73
84
|
readonly width: Width
|
|
74
85
|
}
|
|
75
86
|
|
|
76
|
-
interface ClosedIdField<Name extends string
|
|
87
|
+
interface ClosedIdField<Name extends string, Handles extends ClosedHandleTuple> {
|
|
77
88
|
readonly kind: "u64"
|
|
78
|
-
readonly closed: ClosedRoster<Name,
|
|
89
|
+
readonly closed: ClosedRoster<Name, Handles>
|
|
79
90
|
}
|
|
80
91
|
|
|
81
|
-
|
|
92
|
+
/** The closed-id top type — the erased carrier's view of any closed id. */
|
|
93
|
+
type AnyClosedIdField = ClosedIdField<string, ClosedHandleTuple>
|
|
94
|
+
|
|
95
|
+
type AnyField =
|
|
96
|
+
| BoolField
|
|
97
|
+
| StrField
|
|
98
|
+
| U64Field
|
|
99
|
+
| FreshU64Field
|
|
100
|
+
| I64Field
|
|
101
|
+
| BytesField
|
|
102
|
+
| IntervalField
|
|
103
|
+
| AnyClosedIdField
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The ONE structural interpreter of a field descriptor — the positional
|
|
107
|
+
* signature every equality judgment reads (the positive join wall in
|
|
108
|
+
* `#query/scope.ts` and the face pairing wall in `#face.ts`). Two fields
|
|
109
|
+
* are one shape exactly when their signatures are the same tuple: kind,
|
|
110
|
+
* width, interval element, and the roster as name plus the handle VECTOR
|
|
111
|
+
* (order and length carry meaning; a set would forget both).
|
|
112
|
+
*/
|
|
113
|
+
type SignatureOf<F extends AnyField> = readonly [
|
|
114
|
+
F["kind"],
|
|
115
|
+
F extends { readonly width: infer W } ? W : undefined,
|
|
116
|
+
F extends { readonly element: infer E } ? E : undefined,
|
|
117
|
+
F extends {
|
|
118
|
+
readonly closed: { readonly name: infer N extends string; readonly handles: infer H extends ClosedHandleTuple }
|
|
119
|
+
}
|
|
120
|
+
? readonly [N, H]
|
|
121
|
+
: undefined
|
|
122
|
+
]
|
|
82
123
|
|
|
83
124
|
type Infer<F extends AnyField> = F extends { readonly kind: "bool" }
|
|
84
125
|
? boolean
|
|
@@ -106,13 +147,44 @@ function literalShapeError(context: string, expected: string, value: unknown): E
|
|
|
106
147
|
return errors.new(`${context}: expected ${expected}, got ${typeof value}`)
|
|
107
148
|
}
|
|
108
149
|
|
|
109
|
-
function rosterOf(field: AnyField | undefined):
|
|
150
|
+
function rosterOf(field: AnyField | undefined): AnyClosedRoster | undefined {
|
|
110
151
|
if (field !== undefined && "closed" in field) {
|
|
111
152
|
return field.closed
|
|
112
153
|
}
|
|
113
154
|
return undefined
|
|
114
155
|
}
|
|
115
156
|
|
|
157
|
+
/**
|
|
158
|
+
* The runtime twin of roster equality inside {@link SignatureOf}: same
|
|
159
|
+
* vocabulary name, same handle vector (order and length). Two absent
|
|
160
|
+
* rosters agree; a roster never agrees with a bare field.
|
|
161
|
+
*/
|
|
162
|
+
function rostersAgree(a: AnyClosedRoster | undefined, b: AnyClosedRoster | undefined): boolean {
|
|
163
|
+
if (a === undefined || b === undefined) {
|
|
164
|
+
return a === b
|
|
165
|
+
}
|
|
166
|
+
return (
|
|
167
|
+
a.name === b.name &&
|
|
168
|
+
a.handles.length === b.handles.length &&
|
|
169
|
+
a.handles.every(function sameHandle(handle, index) {
|
|
170
|
+
return handle === b.handles[index]
|
|
171
|
+
})
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The runtime twin of {@link SignatureOf} equality — the ONE spelling of
|
|
177
|
+
* "these two descriptors are one shape". Kind, width, interval element,
|
|
178
|
+
* and the roster vector must all agree.
|
|
179
|
+
*/
|
|
180
|
+
function signaturesAgree(a: AnyField, b: AnyField): boolean {
|
|
181
|
+
const widthA = "width" in a ? a.width : undefined
|
|
182
|
+
const widthB = "width" in b ? b.width : undefined
|
|
183
|
+
const elementA = "element" in a ? a.element : undefined
|
|
184
|
+
const elementB = "element" in b ? b.element : undefined
|
|
185
|
+
return a.kind === b.kind && widthA === widthB && elementA === elementB && rostersAgree(rosterOf(a), rosterOf(b))
|
|
186
|
+
}
|
|
187
|
+
|
|
116
188
|
function isIntervalValue(value: unknown): value is IntervalValue {
|
|
117
189
|
return (
|
|
118
190
|
typeof value === "object" &&
|
|
@@ -124,7 +196,7 @@ function isIntervalValue(value: unknown): value is IntervalValue {
|
|
|
124
196
|
)
|
|
125
197
|
}
|
|
126
198
|
|
|
127
|
-
function handleLiteral(closed:
|
|
199
|
+
function handleLiteral(closed: AnyClosedRoster, value: unknown): LiteralSpec {
|
|
128
200
|
if (typeof value !== "string") {
|
|
129
201
|
throw literalShapeError("selection literal", `a ${closed.name} handle name (string)`, value)
|
|
130
202
|
}
|
|
@@ -245,9 +317,12 @@ function literalOf(field: AnyField, value: unknown): LiteralSpec {
|
|
|
245
317
|
}
|
|
246
318
|
|
|
247
319
|
export type {
|
|
320
|
+
AnyClosedIdField,
|
|
321
|
+
AnyClosedRoster,
|
|
248
322
|
AnyField,
|
|
249
323
|
BoolField,
|
|
250
324
|
BytesField,
|
|
325
|
+
ClosedHandleTuple,
|
|
251
326
|
ClosedIdField,
|
|
252
327
|
ClosedRoster,
|
|
253
328
|
FreshU64Field,
|
|
@@ -255,6 +330,7 @@ export type {
|
|
|
255
330
|
Infer,
|
|
256
331
|
IntervalField,
|
|
257
332
|
IntervalValue,
|
|
333
|
+
SignatureOf,
|
|
258
334
|
StrField,
|
|
259
335
|
U64Field
|
|
260
336
|
}
|
|
@@ -269,6 +345,8 @@ export {
|
|
|
269
345
|
literalOf,
|
|
270
346
|
literalShapeError,
|
|
271
347
|
rosterOf,
|
|
348
|
+
rostersAgree,
|
|
349
|
+
signaturesAgree,
|
|
272
350
|
span,
|
|
273
351
|
str,
|
|
274
352
|
u64
|
package/src/index.ts
CHANGED
|
@@ -100,9 +100,12 @@ export type {
|
|
|
100
100
|
} from "#face.ts"
|
|
101
101
|
export { on } from "#face.ts"
|
|
102
102
|
export type {
|
|
103
|
+
AnyClosedIdField,
|
|
104
|
+
AnyClosedRoster,
|
|
103
105
|
AnyField,
|
|
104
106
|
BoolField,
|
|
105
107
|
BytesField,
|
|
108
|
+
ClosedHandleTuple,
|
|
106
109
|
ClosedIdField,
|
|
107
110
|
ClosedRoster,
|
|
108
111
|
FreshU64Field,
|
|
@@ -110,10 +113,12 @@ export type {
|
|
|
110
113
|
Infer,
|
|
111
114
|
IntervalField,
|
|
112
115
|
IntervalValue,
|
|
116
|
+
SignatureOf,
|
|
113
117
|
StrField,
|
|
114
118
|
U64Field
|
|
115
119
|
} from "#fields.ts"
|
|
116
120
|
export { bool, bytes, i64, interval, span, str, u64 } from "#fields.ts"
|
|
121
|
+
export type { Same, SameLen } from "#judgment.ts"
|
|
117
122
|
export type { ClassesOf, ClassWall, LawfulStatements, RelationClasses, SchemaClasses } from "#law.ts"
|
|
118
123
|
export { lower } from "#lower.ts"
|
|
119
124
|
export type { KeyFact } from "#marshal.ts"
|
package/src/judgment.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The type-level judgment kernel. Two primitives, no imports, no runtime.
|
|
3
|
+
*
|
|
4
|
+
* {@link Same} is definitional equality by mutual extension — the ONE
|
|
5
|
+
* spelling of `A = B` at the type tier. {@link SameLen} is Peano equality
|
|
6
|
+
* on tuple lengths — zero equals zero, successor recurses on successor,
|
|
7
|
+
* everything else is refused. An open array carries no Nat (its length is
|
|
8
|
+
* `number`), so it proves nothing.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
type Same<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false
|
|
12
|
+
|
|
13
|
+
type SameLen<A extends readonly unknown[], B extends readonly unknown[]> = number extends A["length"]
|
|
14
|
+
? false
|
|
15
|
+
: number extends B["length"]
|
|
16
|
+
? false
|
|
17
|
+
: A extends readonly [unknown, ...infer ARest]
|
|
18
|
+
? B extends readonly [unknown, ...infer BRest]
|
|
19
|
+
? SameLen<ARest, BRest>
|
|
20
|
+
: false
|
|
21
|
+
: B extends readonly [unknown, ...unknown[]]
|
|
22
|
+
? false
|
|
23
|
+
: true
|
|
24
|
+
|
|
25
|
+
export type { Same, SameLen }
|
package/src/law.ts
CHANGED
|
@@ -58,6 +58,7 @@ import * as errors from "@superbuilders/errors"
|
|
|
58
58
|
import type { AnyClosed } from "#closed.ts"
|
|
59
59
|
import { isClosedMember, sealedFieldsOf } from "#closed.ts"
|
|
60
60
|
import type { FaceData } from "#face.ts"
|
|
61
|
+
import type { Same } from "#judgment.ts"
|
|
61
62
|
import type { AnyRelation, RelationFields } from "#relation.ts"
|
|
62
63
|
import type { SchemaRelation, SchemaRelations } from "#schema.ts"
|
|
63
64
|
import { renderStatement, type Statement } from "#statements.ts"
|
|
@@ -196,7 +197,7 @@ type WallScan<Comps extends readonly string[], Gens extends string, Pairs extend
|
|
|
196
197
|
: WallScan<T, Gens, Pairs>
|
|
197
198
|
: unknown
|
|
198
199
|
|
|
199
|
-
type SetEq<A extends string, B extends string> =
|
|
200
|
+
type SetEq<A extends string, B extends string> = Same<A, B>
|
|
200
201
|
|
|
201
202
|
type KeyEntry = readonly [string, string]
|
|
202
203
|
|
package/src/marshal.ts
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import * as errors from "@superbuilders/errors"
|
|
31
|
-
import type {
|
|
31
|
+
import type { AnyClosedRoster, AnyField } from "#fields.ts"
|
|
32
32
|
import { isIntervalValue, literalShapeError, rosterOf } from "#fields.ts"
|
|
33
33
|
import type { FactValue } from "#native.ts"
|
|
34
34
|
import type { AnyRelation, Fact, FreshKeys, RelationData } from "#relation.ts"
|
|
@@ -61,7 +61,7 @@ function isStringIndexed(value: object): value is Readonly<Record<string, unknow
|
|
|
61
61
|
* violation. `indexOf` is the whole machine (the roster is ≤ 256 rows,
|
|
62
62
|
* engine law — no map is warranted).
|
|
63
63
|
*/
|
|
64
|
-
function closedCellOf(context: string, closed:
|
|
64
|
+
function closedCellOf(context: string, closed: AnyClosedRoster, name: string): FactValue {
|
|
65
65
|
const id = closed.handles.indexOf(name)
|
|
66
66
|
if (id === -1) {
|
|
67
67
|
throw errors.new(
|
|
@@ -71,7 +71,7 @@ function closedCellOf(context: string, closed: ClosedRoster, name: string): Fact
|
|
|
71
71
|
return BigInt(id)
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
function handleOf(context: string, closed:
|
|
74
|
+
function handleOf(context: string, closed: AnyClosedRoster, cell: FactValue): string {
|
|
75
75
|
if (typeof cell !== "bigint") {
|
|
76
76
|
throw literalShapeError(context, `a ${closed.name} handle id (bigint)`, cell)
|
|
77
77
|
}
|
package/src/query/atom.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as errors from "@superbuilders/errors"
|
|
2
|
-
import type {
|
|
2
|
+
import type { AnyClosedRoster, AnyField, Infer, IntervalValue } from "#fields.ts"
|
|
3
3
|
import type { ClassLookup, ClassRecordOf, SchemaClasses } from "#law.ts"
|
|
4
4
|
import type {
|
|
5
|
+
AntiJoinOk,
|
|
5
6
|
AnyVar,
|
|
6
7
|
ClassedField,
|
|
7
8
|
JoinOk,
|
|
@@ -76,7 +77,7 @@ type FindEntryData =
|
|
|
76
77
|
interface FindColumn {
|
|
77
78
|
readonly name: string
|
|
78
79
|
readonly entry: FindEntryData
|
|
79
|
-
readonly closed:
|
|
80
|
+
readonly closed: AnyClosedRoster | undefined
|
|
80
81
|
readonly slot: ClassedField | undefined
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -139,7 +140,7 @@ type DerivedTable = InteriorData | RecHead
|
|
|
139
140
|
|
|
140
141
|
type BindingInput<F extends AnyField> =
|
|
141
142
|
| Infer<F>
|
|
142
|
-
| (F extends
|
|
143
|
+
| (F extends { readonly closed: { readonly handles: readonly string[] } } ? readonly Infer<F>[] : never)
|
|
143
144
|
| (F extends { readonly kind: "interval" } ? bigint : never)
|
|
144
145
|
| AnyVar
|
|
145
146
|
| Param<string>
|
|
@@ -363,7 +364,7 @@ function not(
|
|
|
363
364
|
* closed-bound terms — the construction-time validations in
|
|
364
365
|
* `#query/lower.ts` are that ban's runtime twin.
|
|
365
366
|
*/
|
|
366
|
-
type NumericVarOk<V extends AnyVar> = V["field"] extends { readonly closed:
|
|
367
|
+
type NumericVarOk<V extends AnyVar> = V["field"] extends { readonly closed: AnyClosedRoster }
|
|
367
368
|
? false
|
|
368
369
|
: V["field"]["kind"] extends "u64" | "i64"
|
|
369
370
|
? true
|
|
@@ -450,7 +451,7 @@ type EqOk<Classes extends SchemaClasses, L, R> = L extends AnyVar
|
|
|
450
451
|
: false
|
|
451
452
|
|
|
452
453
|
type NotBindingOk<Classes extends SchemaClasses, S extends ClassedField, T> = T extends AnyVar
|
|
453
|
-
?
|
|
454
|
+
? AntiJoinOk<MintSlotOf<Classes, T>, S> extends true
|
|
454
455
|
? true
|
|
455
456
|
: false
|
|
456
457
|
: true
|