@bjornpagen/bumbledb 0.19.1 → 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 +8 -4
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js.map +1 -1
- package/dist/query/scope.d.ts +32 -36
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +8 -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 +4 -4
- package/src/query/lower.ts +3 -3
- package/src/query/scope.ts +55 -60
- package/src/statements.ts +3 -3
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,5 +1,5 @@
|
|
|
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
5
|
AntiJoinOk,
|
|
@@ -77,7 +77,7 @@ type FindEntryData =
|
|
|
77
77
|
interface FindColumn {
|
|
78
78
|
readonly name: string
|
|
79
79
|
readonly entry: FindEntryData
|
|
80
|
-
readonly closed:
|
|
80
|
+
readonly closed: AnyClosedRoster | undefined
|
|
81
81
|
readonly slot: ClassedField | undefined
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -140,7 +140,7 @@ type DerivedTable = InteriorData | RecHead
|
|
|
140
140
|
|
|
141
141
|
type BindingInput<F extends AnyField> =
|
|
142
142
|
| Infer<F>
|
|
143
|
-
| (F extends
|
|
143
|
+
| (F extends { readonly closed: { readonly handles: readonly string[] } } ? readonly Infer<F>[] : never)
|
|
144
144
|
| (F extends { readonly kind: "interval" } ? bigint : never)
|
|
145
145
|
| AnyVar
|
|
146
146
|
| Param<string>
|
|
@@ -364,7 +364,7 @@ function not(
|
|
|
364
364
|
* closed-bound terms — the construction-time validations in
|
|
365
365
|
* `#query/lower.ts` are that ban's runtime twin.
|
|
366
366
|
*/
|
|
367
|
-
type NumericVarOk<V extends AnyVar> = V["field"] extends { readonly closed:
|
|
367
|
+
type NumericVarOk<V extends AnyVar> = V["field"] extends { readonly closed: AnyClosedRoster }
|
|
368
368
|
? false
|
|
369
369
|
: V["field"]["kind"] extends "u64" | "i64"
|
|
370
370
|
? true
|
package/src/query/lower.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as errors from "@superbuilders/errors"
|
|
2
2
|
import { sealedFieldsOf } from "#closed.ts"
|
|
3
|
-
import type {
|
|
3
|
+
import type { AnyClosedRoster, AnyField } from "#fields.ts"
|
|
4
4
|
import { assertDeclarationOrderKey, isIntervalValue, literalShapeError, rosterOf } from "#fields.ts"
|
|
5
5
|
import type { ClassRecordOf, SchemaClasses } from "#law.ts"
|
|
6
6
|
import type {
|
|
@@ -1171,7 +1171,7 @@ function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaCl
|
|
|
1171
1171
|
return raw
|
|
1172
1172
|
}
|
|
1173
1173
|
|
|
1174
|
-
function renderClosedSlice(closed:
|
|
1174
|
+
function renderClosedSlice(closed: AnyClosedRoster | undefined): string {
|
|
1175
1175
|
return closed === undefined ? "a bare value" : `a ${closed.name} reference`
|
|
1176
1176
|
}
|
|
1177
1177
|
|
|
@@ -1187,7 +1187,7 @@ function headSignature(column: FindColumn): string {
|
|
|
1187
1187
|
return `${column.name}:${agg.op}`
|
|
1188
1188
|
}
|
|
1189
1189
|
|
|
1190
|
-
function renderParamAnchor(roster:
|
|
1190
|
+
function renderParamAnchor(roster: AnyClosedRoster | undefined): string {
|
|
1191
1191
|
return roster === undefined ? "a non-closed position" : `a ${roster.name} reference`
|
|
1192
1192
|
}
|
|
1193
1193
|
|
package/src/query/scope.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as errors from "@superbuilders/errors"
|
|
2
2
|
import type { AnyClosed } from "#closed.ts"
|
|
3
3
|
import { sealedFieldsOf } from "#closed.ts"
|
|
4
|
-
import type { AnyField, Infer } from "#fields.ts"
|
|
5
|
-
import { rosterOf } from "#fields.ts"
|
|
4
|
+
import type { AnyField, Infer, SignatureOf } from "#fields.ts"
|
|
5
|
+
import { rosterOf, signaturesAgree } from "#fields.ts"
|
|
6
|
+
import type { Same, SameLen } from "#judgment.ts"
|
|
6
7
|
import type { ClassLookup, ClassRecordOf, SchemaClasses } from "#law.ts"
|
|
7
8
|
import type { QueryParam } from "#native.ts"
|
|
8
9
|
import type { AnyRelation, RelationFields } from "#relation.ts"
|
|
@@ -19,7 +20,7 @@ type MatchFields<R extends MatchOwner> = R extends AnyClosed
|
|
|
19
20
|
? RelationFields<R>
|
|
20
21
|
: never
|
|
21
22
|
|
|
22
|
-
interface Var<F extends AnyField
|
|
23
|
+
interface Var<F extends AnyField, RN extends string, K extends string> {
|
|
23
24
|
readonly [term]: "var"
|
|
24
25
|
readonly owner: MatchOwner & { readonly name: RN }
|
|
25
26
|
readonly column: K
|
|
@@ -27,19 +28,19 @@ interface Var<F extends AnyField = AnyField, RN extends string = string, K exten
|
|
|
27
28
|
readonly label: string
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
type AnyVar = Var
|
|
31
|
+
type AnyVar = Var<AnyField, string, string>
|
|
31
32
|
|
|
32
|
-
interface Param<Name extends string
|
|
33
|
+
interface Param<Name extends string> {
|
|
33
34
|
readonly [term]: "param"
|
|
34
35
|
readonly name: Name
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
interface SetParam<Name extends string
|
|
38
|
+
interface SetParam<Name extends string> {
|
|
38
39
|
readonly [term]: "setParam"
|
|
39
40
|
readonly name: Name
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
type AnyTerm =
|
|
43
|
+
type AnyTerm = AnyVar | Param<string> | SetParam<string>
|
|
43
44
|
|
|
44
45
|
function isTerm(value: unknown): value is AnyTerm {
|
|
45
46
|
return typeof value === "object" && value !== null && term in value
|
|
@@ -147,81 +148,75 @@ type UnionToIntersection<U> = (U extends unknown ? (member: U) => void : never)
|
|
|
147
148
|
|
|
148
149
|
type ShapeOf<U> = [U] extends [never] ? Record<never, never> : Flatten<UnionToIntersection<U>>
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
/**
|
|
152
|
+
* A slot's identity for the positive-join judgment: the field's structural
|
|
153
|
+
* signature ({@link SignatureOf} — the ONE interpreter, shared with the
|
|
154
|
+
* face pairing wall) plus the slot's law class.
|
|
155
|
+
*/
|
|
156
|
+
type SlotSignature<S extends ClassedField> = readonly [SignatureOf<S["field"]>, S["class"]]
|
|
153
157
|
|
|
154
|
-
type
|
|
155
|
-
readonly closed: { readonly name: infer N extends string; readonly handles: readonly (infer H extends string)[] }
|
|
156
|
-
}
|
|
157
|
-
? readonly [N, H]
|
|
158
|
-
: undefined
|
|
159
|
-
|
|
160
|
-
type JoinOk<A extends ClassedField, B extends ClassedField> = [
|
|
161
|
-
A["field"]["kind"],
|
|
162
|
-
A["class"],
|
|
163
|
-
WidthOf<A["field"]>,
|
|
164
|
-
ElementOf<A["field"]>,
|
|
165
|
-
RosterOf<A["field"]>
|
|
166
|
-
] extends [B["field"]["kind"], B["class"], WidthOf<B["field"]>, ElementOf<B["field"]>, RosterOf<B["field"]>]
|
|
167
|
-
? [B["field"]["kind"], B["class"], WidthOf<B["field"]>, ElementOf<B["field"]>, RosterOf<B["field"]>] extends [
|
|
168
|
-
A["field"]["kind"],
|
|
169
|
-
A["class"],
|
|
170
|
-
WidthOf<A["field"]>,
|
|
171
|
-
ElementOf<A["field"]>,
|
|
172
|
-
RosterOf<A["field"]>
|
|
173
|
-
]
|
|
174
|
-
? true
|
|
175
|
-
: false
|
|
176
|
-
: false
|
|
158
|
+
type JoinOk<A extends ClassedField, B extends ClassedField> = Same<SlotSignature<A>, SlotSignature<B>>
|
|
177
159
|
|
|
178
160
|
type U64Wire<F extends AnyField> = F extends { readonly kind: "u64" }
|
|
179
|
-
?
|
|
180
|
-
?
|
|
181
|
-
:
|
|
161
|
+
? F extends { readonly closed: unknown }
|
|
162
|
+
? false
|
|
163
|
+
: true
|
|
182
164
|
: false
|
|
183
165
|
|
|
166
|
+
type ClosedHandles<F extends AnyField> = F extends {
|
|
167
|
+
readonly closed: { readonly handles: infer H extends readonly string[] }
|
|
168
|
+
}
|
|
169
|
+
? H
|
|
170
|
+
: never
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Two closed ids anti-join when their handle vectors carry the same Peano
|
|
174
|
+
* length ({@link SameLen}: zero equals zero, successor recurses on
|
|
175
|
+
* successor). A bare field has no vector and proves nothing.
|
|
176
|
+
*/
|
|
177
|
+
type ClosedIdOk<A extends AnyField, B extends AnyField> = [ClosedHandles<A>] extends [never]
|
|
178
|
+
? false
|
|
179
|
+
: [ClosedHandles<B>] extends [never]
|
|
180
|
+
? false
|
|
181
|
+
: SameLen<ClosedHandles<A>, ClosedHandles<B>>
|
|
182
|
+
|
|
184
183
|
/**
|
|
185
184
|
* Anti-join class safety: class-equal slots, plus same-identity u64
|
|
186
|
-
* (fresh mint, foreign-key copy, or bare u64) when one side is bare
|
|
187
|
-
*
|
|
188
|
-
*
|
|
185
|
+
* (fresh mint, foreign-key copy, or bare u64) when one side is bare,
|
|
186
|
+
* plus two closed-id fields whose handle tuples have the same length.
|
|
187
|
+
* Two distinct generators stay refused.
|
|
189
188
|
*/
|
|
190
189
|
type AntiJoinOk<A extends ClassedField, B extends ClassedField> =
|
|
191
190
|
JoinOk<A, B> extends true
|
|
192
191
|
? true
|
|
193
|
-
:
|
|
194
|
-
?
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
192
|
+
: ClosedIdOk<A["field"], B["field"]> extends true
|
|
193
|
+
? true
|
|
194
|
+
: U64Wire<A["field"]> extends true
|
|
195
|
+
? U64Wire<B["field"]> extends true
|
|
196
|
+
? [A["class"]] extends [undefined]
|
|
198
197
|
? true
|
|
199
|
-
:
|
|
198
|
+
: [B["class"]] extends [undefined]
|
|
199
|
+
? true
|
|
200
|
+
: false
|
|
201
|
+
: false
|
|
200
202
|
: false
|
|
201
|
-
: false
|
|
202
203
|
|
|
203
204
|
function fieldJoins(a: ClassedField, b: ClassedField): boolean {
|
|
204
|
-
|
|
205
|
-
const widthB = "width" in b.field ? b.field.width : undefined
|
|
206
|
-
const elementA = "element" in a.field ? a.field.element : undefined
|
|
207
|
-
const elementB = "element" in b.field ? b.field.element : undefined
|
|
208
|
-
const rosterA = rosterOf(a.field)
|
|
209
|
-
const rosterB = rosterOf(b.field)
|
|
210
|
-
return (
|
|
211
|
-
a.field.kind === b.field.kind &&
|
|
212
|
-
a.class === b.class &&
|
|
213
|
-
widthA === widthB &&
|
|
214
|
-
elementA === elementB &&
|
|
215
|
-
rosterA === rosterB
|
|
216
|
-
)
|
|
205
|
+
return a.class === b.class && signaturesAgree(a.field, b.field)
|
|
217
206
|
}
|
|
218
207
|
|
|
219
208
|
function u64Wire(field: AnyField): boolean {
|
|
220
209
|
return field.kind === "u64" && rosterOf(field) === undefined
|
|
221
210
|
}
|
|
222
211
|
|
|
212
|
+
function closedIdAntiJoins(a: AnyField, b: AnyField): boolean {
|
|
213
|
+
const rosterA = rosterOf(a)
|
|
214
|
+
const rosterB = rosterOf(b)
|
|
215
|
+
return rosterA !== undefined && rosterB !== undefined && rosterA.handles.length === rosterB.handles.length
|
|
216
|
+
}
|
|
217
|
+
|
|
223
218
|
function fieldAntiJoins(a: ClassedField, b: ClassedField): boolean {
|
|
224
|
-
if (fieldJoins(a, b)) {
|
|
219
|
+
if (fieldJoins(a, b) || closedIdAntiJoins(a.field, b.field)) {
|
|
225
220
|
return true
|
|
226
221
|
}
|
|
227
222
|
return u64Wire(a.field) && u64Wire(b.field) && (a.class === undefined || b.class === undefined)
|
package/src/statements.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "#capacity.ts"
|
|
13
13
|
import { isClosedMember, sealedFieldOf } from "#closed.ts"
|
|
14
14
|
import { type AnyFace, type FaceData, renderFace, type SameArity, type SameShapes } from "#face.ts"
|
|
15
|
-
import { type
|
|
15
|
+
import { type AnyClosedRoster, rosterOf, rostersAgree } from "#fields.ts"
|
|
16
16
|
import type { AnyRelation, RelationFields } from "#relation.ts"
|
|
17
17
|
import { type CapacityWindowSpec, renderCapacityWindow, renderWeight, type WeightSpec } from "#spec.ts"
|
|
18
18
|
|
|
@@ -67,7 +67,7 @@ interface KeyStatement<R extends AnyRelation, Projection extends readonly string
|
|
|
67
67
|
readonly data: KeyData<R, Projection>
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
function renderRosterSide(roster:
|
|
70
|
+
function renderRosterSide(roster: AnyClosedRoster | undefined): string {
|
|
71
71
|
return roster === undefined ? "a bare column" : `a ${roster.name} reference`
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -96,7 +96,7 @@ function assertRosterAgreement(source: FaceData, target: FaceData, statement: St
|
|
|
96
96
|
}
|
|
97
97
|
const sourceRoster = rosterOf(sealedFieldOf(source.owner, fieldName))
|
|
98
98
|
const targetRoster = rosterOf(sealedFieldOf(target.owner, targetField))
|
|
99
|
-
if (sourceRoster
|
|
99
|
+
if (!rostersAgree(sourceRoster, targetRoster)) {
|
|
100
100
|
throw errors.new(
|
|
101
101
|
`${source.owner.name}.${fieldName} is ${renderRosterSide(sourceRoster)} but ${target.owner.name}.${targetField} is ${renderRosterSide(targetRoster)} — closedness rides the descriptor: a closed reference is spelled with the vocabulary's own id descriptor (one meaning, one spelling), so faces pair closed-with-closed through one roster or bare-with-bare, never across — ${renderStatement(statement)}`
|
|
102
102
|
)
|