@bjornpagen/bumbledb 0.19.1 → 0.20.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 +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/db.d.ts +8 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +6 -0
- package/dist/db.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 +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -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/native.d.ts +168 -3
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +86 -2
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +10 -7
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/find.d.ts +1 -2
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +3 -39
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +48 -19
- package/dist/query/lower.js.map +1 -1
- package/dist/query/scope.d.ts +43 -38
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +24 -15
- package/dist/query/scope.js.map +1 -1
- package/dist/spec.d.ts +1 -1
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +1 -1
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +2 -2
- package/dist/statements.js.map +1 -1
- package/package.json +5 -4
- package/src/closed.ts +85 -51
- package/src/db.ts +15 -0
- package/src/face.ts +18 -24
- package/src/fields.ts +85 -7
- package/src/index.ts +43 -1
- package/src/judgment.ts +25 -0
- package/src/law.ts +2 -1
- package/src/marshal.ts +3 -3
- package/src/native.ts +320 -12
- package/src/query/atom.ts +6 -19
- package/src/query/find.ts +1 -16
- package/src/query/lower.ts +51 -32
- package/src/query/scope.ts +84 -65
- package/src/spec.ts +0 -1
- package/src/statements.ts +3 -4
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 {
|
|
@@ -61,6 +61,7 @@ import type {
|
|
|
61
61
|
import {
|
|
62
62
|
fieldAntiJoins,
|
|
63
63
|
fieldJoins,
|
|
64
|
+
headFieldJoins,
|
|
64
65
|
inferred,
|
|
65
66
|
isTerm,
|
|
66
67
|
makeParam,
|
|
@@ -85,9 +86,6 @@ interface RuleValue<Row, P extends ParamsRecord, Head extends HeadShape = undefi
|
|
|
85
86
|
|
|
86
87
|
type AnyRuleValue = RuleValue<unknown, ParamsRecord, HeadShape>
|
|
87
88
|
|
|
88
|
-
type HeadOf<T> =
|
|
89
|
-
InferredOf<T> extends { readonly head: infer H extends Readonly<Record<string, ClassedField>> } ? H : undefined
|
|
90
|
-
|
|
91
89
|
type InteriorBindingOk<V> = V extends AnyVar ? true : false
|
|
92
90
|
|
|
93
91
|
type CheckInteriorBindings<B> = {
|
|
@@ -1171,7 +1169,7 @@ function makeRecRuleScope<Rels extends SchemaRelations, Classes extends SchemaCl
|
|
|
1171
1169
|
return raw
|
|
1172
1170
|
}
|
|
1173
1171
|
|
|
1174
|
-
function renderClosedSlice(closed:
|
|
1172
|
+
function renderClosedSlice(closed: AnyClosedRoster | undefined): string {
|
|
1175
1173
|
return closed === undefined ? "a bare value" : `a ${closed.name} reference`
|
|
1176
1174
|
}
|
|
1177
1175
|
|
|
@@ -1187,7 +1185,7 @@ function headSignature(column: FindColumn): string {
|
|
|
1187
1185
|
return `${column.name}:${agg.op}`
|
|
1188
1186
|
}
|
|
1189
1187
|
|
|
1190
|
-
function renderParamAnchor(roster:
|
|
1188
|
+
function renderParamAnchor(roster: AnyClosedRoster | undefined): string {
|
|
1191
1189
|
return roster === undefined ? "a non-closed position" : `a ${roster.name} reference`
|
|
1192
1190
|
}
|
|
1193
1191
|
|
|
@@ -1307,7 +1305,15 @@ interface RawQuery {
|
|
|
1307
1305
|
reach(name: string, arms: never): never
|
|
1308
1306
|
}
|
|
1309
1307
|
|
|
1310
|
-
|
|
1308
|
+
/**
|
|
1309
|
+
* Verifies every rule derives the same head and returns the MERGED head:
|
|
1310
|
+
* the meet of the rules' column slots. A head column joins class-equal
|
|
1311
|
+
* slots, and — the anti-join law's head twin — same-identity u64 wire
|
|
1312
|
+
* with at least one side bare; when the bare arm admits the pair, the
|
|
1313
|
+
* merged column's class claim demotes to bare, so a consumer joining
|
|
1314
|
+
* the head cannot inherit provenance a rule never proved.
|
|
1315
|
+
*/
|
|
1316
|
+
function alignedHeadOf(label: string, rules: readonly RuleData[]): readonly FindColumn[] {
|
|
1311
1317
|
const first = rules[0]
|
|
1312
1318
|
if (first === undefined) {
|
|
1313
1319
|
throw errors.new(`${label} needs at least one rule`)
|
|
@@ -1330,13 +1336,33 @@ function assertAlignedHeads(label: string, rules: readonly RuleData[]): void {
|
|
|
1330
1336
|
if (lead === undefined) {
|
|
1331
1337
|
return
|
|
1332
1338
|
}
|
|
1333
|
-
if (lead.slot !== undefined && column.slot !== undefined && !
|
|
1339
|
+
if (lead.slot !== undefined && column.slot !== undefined && !headFieldJoins(lead.slot, column.slot)) {
|
|
1334
1340
|
throw errors.new(
|
|
1335
|
-
`every rule of ${label} derives the same head — the head column ${lead.name} is bound at ${renderFieldKind(lead.slot)} in rule 0 but at ${renderFieldKind(column.slot)} in rule ${index} (a head column joins
|
|
1341
|
+
`every rule of ${label} derives the same head — the head column ${lead.name} is bound at ${renderFieldKind(lead.slot)} in rule 0 but at ${renderFieldKind(column.slot)} in rule ${index} (a head column joins class-equal slots; same-wire u64 admits a bare side, and the merged head is bare)`
|
|
1336
1342
|
)
|
|
1337
1343
|
}
|
|
1338
1344
|
})
|
|
1339
1345
|
})
|
|
1346
|
+
const merged = first.finds.map(function meetColumn(lead, position) {
|
|
1347
|
+
const slot = lead.slot
|
|
1348
|
+
if (slot === undefined || slot.class === undefined) {
|
|
1349
|
+
return lead
|
|
1350
|
+
}
|
|
1351
|
+
const demoted = rules.some(function bareElsewhere(rule) {
|
|
1352
|
+
const column = rule.finds[position]
|
|
1353
|
+
return column !== undefined && column.slot !== undefined && column.slot.class !== slot.class
|
|
1354
|
+
})
|
|
1355
|
+
if (!demoted) {
|
|
1356
|
+
return lead
|
|
1357
|
+
}
|
|
1358
|
+
return Object.freeze({
|
|
1359
|
+
name: lead.name,
|
|
1360
|
+
entry: lead.entry,
|
|
1361
|
+
closed: lead.closed,
|
|
1362
|
+
slot: Object.freeze({ field: slot.field, class: undefined })
|
|
1363
|
+
})
|
|
1364
|
+
})
|
|
1365
|
+
return Object.freeze(merged)
|
|
1340
1366
|
}
|
|
1341
1367
|
|
|
1342
1368
|
function afterMainError(what: string): Error {
|
|
@@ -1351,7 +1377,7 @@ function makeRawQuery(
|
|
|
1351
1377
|
rec: RecData | undefined,
|
|
1352
1378
|
rules: readonly RuleData[]
|
|
1353
1379
|
): RawQuery {
|
|
1354
|
-
|
|
1380
|
+
const mergedFinds = alignedHeadOf("a query", rules)
|
|
1355
1381
|
const first = rules[0]
|
|
1356
1382
|
if (first === undefined) {
|
|
1357
1383
|
throw errors.new("a query needs at least one rule")
|
|
@@ -1366,7 +1392,7 @@ function makeRawQuery(
|
|
|
1366
1392
|
kind: "cq" as const,
|
|
1367
1393
|
interiors: frozenInteriors,
|
|
1368
1394
|
rules: frozenRules,
|
|
1369
|
-
finds:
|
|
1395
|
+
finds: mergedFinds,
|
|
1370
1396
|
params
|
|
1371
1397
|
})
|
|
1372
1398
|
: Object.freeze({
|
|
@@ -1374,7 +1400,7 @@ function makeRawQuery(
|
|
|
1374
1400
|
interiors: frozenInteriors,
|
|
1375
1401
|
rec,
|
|
1376
1402
|
rules: frozenRules,
|
|
1377
|
-
finds:
|
|
1403
|
+
finds: mergedFinds,
|
|
1378
1404
|
params
|
|
1379
1405
|
})
|
|
1380
1406
|
const value: RawQuery = {
|
|
@@ -1416,12 +1442,8 @@ function collectInterior<Rels extends SchemaRelations, Classes extends SchemaCla
|
|
|
1416
1442
|
const rules = builds.map(function buildRule(buildOne) {
|
|
1417
1443
|
return buildOne(makeInteriorRuleScope<Rels, Classes>(theory, env, name)).rule
|
|
1418
1444
|
})
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
if (first === undefined) {
|
|
1422
|
-
throw errors.new(`query: interior ${name} needs at least one rule`)
|
|
1423
|
-
}
|
|
1424
|
-
return Object.freeze({ name, finds: first.finds, rules: Object.freeze(rules) })
|
|
1445
|
+
const mergedFinds = alignedHeadOf(`interior ${name}`, rules)
|
|
1446
|
+
return Object.freeze({ name, finds: mergedFinds, rules: Object.freeze(rules) })
|
|
1425
1447
|
}
|
|
1426
1448
|
|
|
1427
1449
|
function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>(
|
|
@@ -1442,22 +1464,27 @@ function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>
|
|
|
1442
1464
|
const base = baseBuilds.map(function buildBase(buildOne) {
|
|
1443
1465
|
return buildOne(makeRecRuleScope<Rels, Classes>(theory, baseEnv, handle, "rec-base")).rule
|
|
1444
1466
|
})
|
|
1445
|
-
|
|
1467
|
+
const baseFinds = alignedHeadOf(`rec ${name}`, base)
|
|
1446
1468
|
const first = base[0]
|
|
1447
1469
|
if (first === undefined) {
|
|
1448
1470
|
throw errors.new(`query: rec ${name} has no base arms`)
|
|
1449
1471
|
}
|
|
1450
|
-
const firstFind =
|
|
1472
|
+
const firstFind = baseFinds[0]
|
|
1451
1473
|
if (firstFind === undefined) {
|
|
1452
1474
|
throw errors.new(`query: rec ${name} has no head`)
|
|
1453
1475
|
}
|
|
1454
|
-
const finds: RecHead["finds"] = [firstFind, ...
|
|
1476
|
+
const finds: RecHead["finds"] = [firstFind, ...baseFinds.slice(1)]
|
|
1455
1477
|
const head: RecHead = Object.freeze({ name, finds })
|
|
1456
1478
|
const recEnv: DerivedEnv = { interiors, rec: head }
|
|
1457
1479
|
const rec = recBuilds.map(function buildRec(buildOne) {
|
|
1458
1480
|
return buildOne(makeRecRuleScope<Rels, Classes>(theory, recEnv, head, "rec-arm")).rule
|
|
1459
1481
|
})
|
|
1460
|
-
|
|
1482
|
+
const mergedAll = alignedHeadOf(`rec ${name}`, [...base, ...rec])
|
|
1483
|
+
const mergedFirst = mergedAll[0]
|
|
1484
|
+
if (mergedFirst === undefined) {
|
|
1485
|
+
throw errors.new(`query: rec ${name} has no head`)
|
|
1486
|
+
}
|
|
1487
|
+
const sealedFinds: RecHead["finds"] = [mergedFirst, ...mergedAll.slice(1)]
|
|
1461
1488
|
const firstRec = rec[0]
|
|
1462
1489
|
if (firstRec === undefined) {
|
|
1463
1490
|
throw errors.new(`query: rec ${name} has no rec arms`)
|
|
@@ -1466,7 +1493,7 @@ function collectRec<Rels extends SchemaRelations, Classes extends SchemaClasses>
|
|
|
1466
1493
|
const sealedRec: RecData["rec"] = [firstRec, ...rec.slice(1)]
|
|
1467
1494
|
const recData: RecData = Object.freeze({
|
|
1468
1495
|
name,
|
|
1469
|
-
finds,
|
|
1496
|
+
finds: sealedFinds,
|
|
1470
1497
|
base: sealedBase,
|
|
1471
1498
|
rec: sealedRec
|
|
1472
1499
|
})
|
|
@@ -1949,11 +1976,6 @@ function lowerQuery(q: AnyQuery): ParsedQuery {
|
|
|
1949
1976
|
export type {
|
|
1950
1977
|
AnyQuery,
|
|
1951
1978
|
AnyRuleValue,
|
|
1952
|
-
HeadOf,
|
|
1953
|
-
HeadShape,
|
|
1954
|
-
InteriorRuleChain,
|
|
1955
|
-
InteriorRuleScope,
|
|
1956
|
-
ParamsOf,
|
|
1957
1979
|
Query,
|
|
1958
1980
|
QueryData,
|
|
1959
1981
|
QueryParams,
|
|
@@ -1963,12 +1985,9 @@ export type {
|
|
|
1963
1985
|
QueryRuleChain,
|
|
1964
1986
|
QueryRuleScope,
|
|
1965
1987
|
QueryStart,
|
|
1966
|
-
RawChain,
|
|
1967
|
-
RawScope,
|
|
1968
1988
|
RecRuleChain,
|
|
1969
1989
|
RecRuleScope,
|
|
1970
|
-
RowOf,
|
|
1971
1990
|
RuleValue,
|
|
1972
1991
|
TermOps
|
|
1973
1992
|
}
|
|
1974
|
-
export { lowerQuery,
|
|
1993
|
+
export { lowerQuery, query, taggedCmpLiteral }
|
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,84 +148,94 @@ 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
|
+
|
|
218
|
+
function u64BareWirePair(a: ClassedField, b: ClassedField): boolean {
|
|
219
|
+
return u64Wire(a.field) && u64Wire(b.field) && (a.class === undefined || b.class === undefined)
|
|
220
|
+
}
|
|
221
|
+
|
|
223
222
|
function fieldAntiJoins(a: ClassedField, b: ClassedField): boolean {
|
|
224
|
-
if (fieldJoins(a, b)) {
|
|
223
|
+
if (fieldJoins(a, b) || closedIdAntiJoins(a.field, b.field)) {
|
|
225
224
|
return true
|
|
226
225
|
}
|
|
227
|
-
return
|
|
226
|
+
return u64BareWirePair(a, b)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Union-head class safety, the anti-join law's head twin: class-equal
|
|
231
|
+
* slots, plus same-identity u64 wire when one side is bare. A head cell
|
|
232
|
+
* is a value that flows to the caller either way — the class is
|
|
233
|
+
* provenance, not wire shape — and the merged head carries the meet:
|
|
234
|
+
* a bare rule demotes the column's class claim to bare, so downstream
|
|
235
|
+
* joins cannot inherit provenance a rule never proved.
|
|
236
|
+
*/
|
|
237
|
+
function headFieldJoins(a: ClassedField, b: ClassedField): boolean {
|
|
238
|
+
return fieldJoins(a, b) || u64BareWirePair(a, b)
|
|
228
239
|
}
|
|
229
240
|
|
|
230
241
|
function renderFieldKind(slot: ClassedField): string {
|
|
@@ -257,7 +268,6 @@ interface ParamEntry {
|
|
|
257
268
|
|
|
258
269
|
export type {
|
|
259
270
|
AntiJoinOk,
|
|
260
|
-
AnyTerm,
|
|
261
271
|
AnyVar,
|
|
262
272
|
ClassedField,
|
|
263
273
|
ExactVars,
|
|
@@ -266,7 +276,6 @@ export type {
|
|
|
266
276
|
JoinOk,
|
|
267
277
|
MatchFields,
|
|
268
278
|
MatchOwner,
|
|
269
|
-
MintClassOf,
|
|
270
279
|
MintSlotOf,
|
|
271
280
|
Param,
|
|
272
281
|
ParamEntry,
|
|
@@ -274,8 +283,18 @@ export type {
|
|
|
274
283
|
ParamValueAt,
|
|
275
284
|
SetParam,
|
|
276
285
|
ShapeOf,
|
|
277
|
-
UnionToIntersection,
|
|
278
286
|
Var,
|
|
279
287
|
VarsOf
|
|
280
288
|
}
|
|
281
|
-
export {
|
|
289
|
+
export {
|
|
290
|
+
fieldAntiJoins,
|
|
291
|
+
fieldJoins,
|
|
292
|
+
headFieldJoins,
|
|
293
|
+
inferred,
|
|
294
|
+
isTerm,
|
|
295
|
+
makeParam,
|
|
296
|
+
makeSetParam,
|
|
297
|
+
renderFieldKind,
|
|
298
|
+
term,
|
|
299
|
+
v
|
|
300
|
+
}
|
package/src/spec.ts
CHANGED
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
|
)
|
|
@@ -312,7 +312,6 @@ export type {
|
|
|
312
312
|
ContainmentData,
|
|
313
313
|
KeyData,
|
|
314
314
|
KeyStatement,
|
|
315
|
-
MirrorsData,
|
|
316
315
|
Statement,
|
|
317
316
|
StatementData
|
|
318
317
|
}
|