@bjornpagen/bumbledb 0.3.0 → 0.4.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 +66 -46
- package/README.md +28 -15
- package/dist/closed.d.ts +50 -73
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +38 -109
- package/dist/closed.js.map +1 -1
- package/dist/db.d.ts +4 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +26 -3
- package/dist/db.js.map +1 -1
- package/dist/face.d.ts +39 -39
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +7 -16
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +28 -14
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +15 -14
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/marshal.d.ts +33 -6
- package/dist/marshal.d.ts.map +1 -1
- package/dist/marshal.js +67 -6
- package/dist/marshal.js.map +1 -1
- package/dist/query/atom.d.ts +59 -14
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +3 -0
- package/dist/query/atom.js.map +1 -1
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +251 -26
- package/dist/query/lower.js.map +1 -1
- package/dist/query/run.d.ts +15 -5
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +26 -6
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +35 -13
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +16 -4
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +8 -7
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +32 -10
- package/dist/relation.js.map +1 -1
- package/dist/spec.d.ts +3 -2
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +10 -4
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +79 -7
- package/dist/statements.js.map +1 -1
- package/package.json +2 -2
- package/src/closed.ts +72 -179
- package/src/db.ts +42 -5
- package/src/face.ts +34 -45
- package/src/fields.ts +46 -34
- package/src/index.ts +1 -2
- package/src/marshal.ts +74 -7
- package/src/query/atom.ts +58 -15
- package/src/query/lower.ts +301 -28
- package/src/query/run.ts +26 -6
- package/src/query/scope.ts +49 -15
- package/src/relation.ts +45 -17
- package/src/spec.ts +3 -2
- package/src/statements.ts +86 -7
package/src/query/scope.ts
CHANGED
|
@@ -228,25 +228,38 @@ type WidthOf<F extends AnyField> = F extends { readonly width: infer W } ? W : u
|
|
|
228
228
|
/** Reads an interval descriptor's element kind; `undefined` on scalar kinds. */
|
|
229
229
|
type ElementOf<F extends AnyField> = F extends { readonly element: infer E } ? E : undefined
|
|
230
230
|
|
|
231
|
+
/** Reads a closed reference's handle union; `undefined` on every non-closed kind (the roster IS descriptor structure). */
|
|
232
|
+
type RosterOf<F extends AnyField> = F extends {
|
|
233
|
+
readonly closed: { readonly handles: readonly (infer H extends string)[] }
|
|
234
|
+
}
|
|
235
|
+
? H
|
|
236
|
+
: undefined
|
|
237
|
+
|
|
231
238
|
/**
|
|
232
239
|
* The join judgment: two bound slots join iff their descriptors' structure
|
|
233
|
-
* agrees (kind, width label, interval element
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
240
|
+
* agrees (kind, width label, interval element, and the closed ROSTER — a
|
|
241
|
+
* closed reference pairs only with the same vocabulary, never with a bare
|
|
242
|
+
* u64: the roster keys every closed judgment downstream, so a join across
|
|
243
|
+
* it would decode/order/translate incoherently by binding order) AND their
|
|
244
|
+
* law-computed classes agree — same class name joins, and bare
|
|
245
|
+
* (`undefined`) pairs only with bare (ruling 3: a field in no law has no
|
|
246
|
+
* class; a bare↔classed pairing refuses). The class names come off the
|
|
247
|
+
* SCHEMA type's class map — the statements are the typing; no descriptor
|
|
248
|
+
* label beyond the roster exists to compare.
|
|
238
249
|
*/
|
|
239
250
|
type JoinOk<A extends ClassedField, B extends ClassedField> = [
|
|
240
251
|
A["field"]["kind"],
|
|
241
252
|
A["class"],
|
|
242
253
|
WidthOf<A["field"]>,
|
|
243
|
-
ElementOf<A["field"]
|
|
244
|
-
|
|
245
|
-
|
|
254
|
+
ElementOf<A["field"]>,
|
|
255
|
+
RosterOf<A["field"]>
|
|
256
|
+
] extends [B["field"]["kind"], B["class"], WidthOf<B["field"]>, ElementOf<B["field"]>, RosterOf<B["field"]>]
|
|
257
|
+
? [B["field"]["kind"], B["class"], WidthOf<B["field"]>, ElementOf<B["field"]>, RosterOf<B["field"]>] extends [
|
|
246
258
|
A["field"]["kind"],
|
|
247
259
|
A["class"],
|
|
248
260
|
WidthOf<A["field"]>,
|
|
249
|
-
ElementOf<A["field"]
|
|
261
|
+
ElementOf<A["field"]>,
|
|
262
|
+
RosterOf<A["field"]>
|
|
250
263
|
]
|
|
251
264
|
? true
|
|
252
265
|
: false
|
|
@@ -255,7 +268,8 @@ type JoinOk<A extends ClassedField, B extends ClassedField> = [
|
|
|
255
268
|
/**
|
|
256
269
|
* The runtime twin of {@link JoinOk}: two bound slots join iff descriptor
|
|
257
270
|
* structure and class agree — the same comparison the type tier makes,
|
|
258
|
-
* judged on the honest runtime values (the descriptor
|
|
271
|
+
* judged on the honest runtime values (the descriptor, the roster by VALUE
|
|
272
|
+
* IDENTITY — vocabulary identity is value identity — and the schema
|
|
259
273
|
* value's frozen class map). The rule builders throw through this on a
|
|
260
274
|
* class-unequal variable reuse, so the wall holds for untyped callers too,
|
|
261
275
|
* not only where the compiler can see.
|
|
@@ -265,17 +279,30 @@ function fieldJoins(a: ClassedField, b: ClassedField): boolean {
|
|
|
265
279
|
const widthB = "width" in b.field ? b.field.width : undefined
|
|
266
280
|
const elementA = "element" in a.field ? a.field.element : undefined
|
|
267
281
|
const elementB = "element" in b.field ? b.field.element : undefined
|
|
268
|
-
|
|
282
|
+
const rosterA = "closed" in a.field ? a.field.closed : undefined
|
|
283
|
+
const rosterB = "closed" in b.field ? b.field.closed : undefined
|
|
284
|
+
return (
|
|
285
|
+
a.field.kind === b.field.kind &&
|
|
286
|
+
a.class === b.class &&
|
|
287
|
+
widthA === widthB &&
|
|
288
|
+
elementA === elementB &&
|
|
289
|
+
rosterA === rosterB
|
|
290
|
+
)
|
|
269
291
|
}
|
|
270
292
|
|
|
271
293
|
/**
|
|
272
294
|
* Renders one bound slot for join-mismatch diagnostics — the structural
|
|
273
|
-
* kind in the schema grammar's spelling
|
|
274
|
-
*
|
|
295
|
+
* kind in the schema grammar's spelling (a closed reference names its
|
|
296
|
+
* vocabulary: the roster is part of the structure being compared) plus the
|
|
297
|
+
* slot's law-computed class (`u64 in class Holder.id`; a lawless slot
|
|
298
|
+
* renders `bare`).
|
|
275
299
|
*/
|
|
276
300
|
function renderFieldKind(slot: ClassedField): string {
|
|
277
301
|
const field = slot.field
|
|
278
302
|
let base: string = field.kind
|
|
303
|
+
if ("closed" in field) {
|
|
304
|
+
base = `u64 referencing ${field.closed.name}`
|
|
305
|
+
}
|
|
279
306
|
if (field.kind === "bytes") {
|
|
280
307
|
base = `bytes<${field.width}>`
|
|
281
308
|
}
|
|
@@ -287,7 +314,10 @@ function renderFieldKind(slot: ClassedField): string {
|
|
|
287
314
|
|
|
288
315
|
/**
|
|
289
316
|
* What a PARAM anchored at field `F` accepts at execution: the field's
|
|
290
|
-
* bare value type, exactly
|
|
317
|
+
* bare value type, exactly — at a CLOSED-reference field that is the
|
|
318
|
+
* handle-name union (`"DirectPass" | "Failed"`), translated name → row id
|
|
319
|
+
* at execute through the one roster-verification point
|
|
320
|
+
* (`taggedHandleId`). At an interval field the engine resolves the
|
|
291
321
|
* bivalent anchor to the INTERVAL reading (value equality) — the point
|
|
292
322
|
* reading of a param is spelled `pointIn(r.param(...), w)`, whose sibling
|
|
293
323
|
* anchors it element-typed.
|
|
@@ -304,13 +334,17 @@ type InferredOf<T> = T extends { readonly [inferred]?: infer S } ? Exclude<S, un
|
|
|
304
334
|
* positions) — the op keeps literal tagging op-aware at `pointIn`
|
|
305
335
|
* (the bug-hunt fix, preserved). `anchor` is `undefined` only on a query
|
|
306
336
|
* built but not yet anchored by any rule; lowering and the wire both refuse
|
|
307
|
-
* that state typed.
|
|
337
|
+
* that state typed. `members` is present exactly on a MEMBERSHIP-ARRAY
|
|
338
|
+
* entry (a literal set at a closed field, folded into the program): the
|
|
339
|
+
* SDK itself translates and supplies the set at every execute — the entry
|
|
340
|
+
* is never read from, and never demanded of, the host's params object.
|
|
308
341
|
*/
|
|
309
342
|
interface ParamEntry {
|
|
310
343
|
readonly name: string
|
|
311
344
|
readonly shape: "value" | "set" | "mask"
|
|
312
345
|
readonly anchor: AnyField | "measure" | undefined
|
|
313
346
|
readonly op: "binding" | "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "pointIn" | "allen"
|
|
347
|
+
readonly members: readonly string[] | undefined
|
|
314
348
|
}
|
|
315
349
|
|
|
316
350
|
export type {
|
package/src/relation.ts
CHANGED
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import * as errors from "@superbuilders/errors"
|
|
15
|
-
import type { OneOf } from "#face.ts"
|
|
16
15
|
import { type AnyField, assertDeclarationOrderKey, type Infer, literalOf } from "#fields.ts"
|
|
17
|
-
import type
|
|
16
|
+
import { type LiteralSetSpec, type LiteralSpec, renderLiteral } from "#spec.ts"
|
|
18
17
|
|
|
19
18
|
/** Flattens an intersection into one displayed object type (hover legibility). */
|
|
20
19
|
type Flatten<T> = { [K in keyof T]: T[K] }
|
|
@@ -37,16 +36,41 @@ function refsComplete<RName extends string, Fields extends FieldsShape>(
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
|
-
* Resolves one selection entry to its lowered literal set:
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* the
|
|
39
|
+
* Resolves one selection entry to its lowered literal set: a plain ARRAY
|
|
40
|
+
* (detected by `Array.isArray` — no field's value type is an array;
|
|
41
|
+
* `Uint8Array` is not one) becomes a disjunctive set, anything else the
|
|
42
|
+
* bare literal. The degenerate sets are construction errors, each
|
|
43
|
+
* self-locating (`context` names the relation and field) — the empty set
|
|
44
|
+
* selects nothing, the one-element set is the bare literal respelled, and
|
|
45
|
+
* a DUPLICATE literal (judged on the canonical rendering — the engine's
|
|
46
|
+
* own duplicate test, reached here first so its index-speak twin at
|
|
47
|
+
* `Db.create` stays unreachable from this surface) is the same respelling
|
|
48
|
+
* in disguise (the canonical-utterance law; the old set combinator's
|
|
49
|
+
* signature made the length degenerates unwritable, and the refusals here
|
|
50
|
+
* are that law's runtime seat). The lowered set — `{ kind: "many",
|
|
51
|
+
* literals }` — is byte-identical to what the combinator produced, so no
|
|
52
|
+
* fingerprint moves.
|
|
45
53
|
*/
|
|
46
|
-
function resolveEntry(field: AnyField, entry: unknown): LiteralSetSpec {
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
function resolveEntry(context: string, field: AnyField, entry: unknown): LiteralSetSpec {
|
|
55
|
+
if (Array.isArray(entry)) {
|
|
56
|
+
if (entry.length < 2) {
|
|
57
|
+
throw errors.new(
|
|
58
|
+
entry.length === 0
|
|
59
|
+
? `${context}: an empty literal set selects nothing — write the selection you mean`
|
|
60
|
+
: `${context}: a one-element literal set is the bare literal respelled — write the literal (the canonical-utterance law: one meaning, one spelling)`
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
const seen = new Set<string>()
|
|
64
|
+
const literals: LiteralSpec[] = entry.map(function lowerSetLiteral(literal: unknown) {
|
|
65
|
+
const lowered = Object.freeze(literalOf(field, literal))
|
|
66
|
+
const rendered = renderLiteral(lowered)
|
|
67
|
+
if (seen.has(rendered)) {
|
|
68
|
+
throw errors.new(
|
|
69
|
+
`${context}: the literal set spells ${rendered} twice — write it once (the canonical-utterance law: one meaning, one spelling)`
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
seen.add(rendered)
|
|
73
|
+
return lowered
|
|
50
74
|
})
|
|
51
75
|
return Object.freeze({ kind: "many", literals: Object.freeze(literals) })
|
|
52
76
|
}
|
|
@@ -78,7 +102,9 @@ function resolveSelection(
|
|
|
78
102
|
if (declared === undefined) {
|
|
79
103
|
throw errors.new(`relation ${name} has no field ${fieldName}`)
|
|
80
104
|
}
|
|
81
|
-
bindings.push(
|
|
105
|
+
bindings.push(
|
|
106
|
+
Object.freeze({ field: fieldName, set: resolveEntry(`relation ${name}.${fieldName}`, declared.field, entry) })
|
|
107
|
+
)
|
|
82
108
|
}
|
|
83
109
|
if (bindings.length === 0) {
|
|
84
110
|
throw errors.new(
|
|
@@ -131,13 +157,15 @@ interface SelectionBinding {
|
|
|
131
157
|
|
|
132
158
|
/**
|
|
133
159
|
* The `where()` argument: per field, a bare structural literal of that
|
|
134
|
-
* field's value type (a closed
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
160
|
+
* field's value type (a closed reference's literal IS its handle name —
|
|
161
|
+
* `"Savings"`, verified against the roster at construction), a plain
|
|
162
|
+
* ARRAY of such literals read disjunctively — `kind: ["Checking",
|
|
163
|
+
* "Savings"]` — or a `span(start, end)` interval literal. Membership is
|
|
164
|
+
* an array, never an operator (the drizzle law); equality-only by
|
|
165
|
+
* construction: no operator parameter exists anywhere.
|
|
138
166
|
*/
|
|
139
167
|
type SelectionInput<Fields extends FieldsShape> = {
|
|
140
|
-
readonly [K in keyof Fields]?: Infer<Fields[K]> |
|
|
168
|
+
readonly [K in keyof Fields]?: Infer<Fields[K]> | readonly Infer<Fields[K]>[]
|
|
141
169
|
}
|
|
142
170
|
|
|
143
171
|
/** A relation with a selection applied — what `on()` consumes as a σ-carrying source. */
|
package/src/spec.ts
CHANGED
|
@@ -62,8 +62,9 @@ type LiteralSpec =
|
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* One σ binding's right side: a single literal or a literal set (read
|
|
65
|
-
* disjunctively). The SDK's selection
|
|
66
|
-
*
|
|
65
|
+
* disjunctively). The SDK's selection resolver refuses the degenerate sets
|
|
66
|
+
* (a membership array needs two members — the empty set selects nothing,
|
|
67
|
+
* the one-element set is the bare literal respelled), so a lowered `many`
|
|
67
68
|
* always carries ≥ 2 literals.
|
|
68
69
|
*/
|
|
69
70
|
type LiteralSetSpec =
|
package/src/statements.ts
CHANGED
|
@@ -8,10 +8,16 @@
|
|
|
8
8
|
* Every field reference is checked against the relation it names in the
|
|
9
9
|
* TYPE — existence through {@link FaceFields} (`on(R, "nope")` does not
|
|
10
10
|
* compile) and STRUCTURAL compatibility through {@link SameShapes}: the two
|
|
11
|
-
* faces' projected kind/width/element
|
|
12
|
-
* (the minimal kernel — descriptors are pure structure) and
|
|
13
|
-
* positionwise equal, so a u64 face against a str face, a
|
|
14
|
-
* mismatch,
|
|
11
|
+
* faces' projected kind/width/element/roster quadruples are read off the
|
|
12
|
+
* schema type (the minimal kernel — descriptors are pure structure) and
|
|
13
|
+
* constrained positionwise equal, so a u64 face against a str face, a
|
|
14
|
+
* bytes width mismatch, an interval element mismatch, or a bare column
|
|
15
|
+
* against a closed reference is a compile error. The ROSTER slot carries a
|
|
16
|
+
* construction-time runtime twin here ({@link assertRosterAgreement} —
|
|
17
|
+
* roster IDENTITY, positionwise: a closed vocabulary's referencing column
|
|
18
|
+
* is spelled with the vocabulary's own id descriptor, the ONE spelling, so
|
|
19
|
+
* a plain u64 column can never alias a vocabulary through a declared law
|
|
20
|
+
* and the SDK's descriptor-keyed closed judgments stay sound). Domains are
|
|
15
21
|
* NOT compared here — there is no domain to compare at construction: the
|
|
16
22
|
* statements themselves are what define the equivalence classes, and the
|
|
17
23
|
* domain wall lives where they aggregate — `schema()` (the
|
|
@@ -28,6 +34,7 @@
|
|
|
28
34
|
import * as errors from "@superbuilders/errors"
|
|
29
35
|
import type { Count } from "#count.ts"
|
|
30
36
|
import { type AnyFace, type FaceData, renderFace, type SameArity, type SameShapes } from "#face.ts"
|
|
37
|
+
import type { AnyField, ClosedRoster } from "#fields.ts"
|
|
31
38
|
import type { AnyRelation, RelationFields } from "#relation.ts"
|
|
32
39
|
import { renderWindow, type WindowSpec } from "#spec.ts"
|
|
33
40
|
|
|
@@ -98,6 +105,72 @@ interface KeyStatement<R extends AnyRelation, Projection extends readonly string
|
|
|
98
105
|
readonly data: KeyData<R, Projection>
|
|
99
106
|
}
|
|
100
107
|
|
|
108
|
+
/**
|
|
109
|
+
* The field descriptor one face position projects: an ordinary relation's
|
|
110
|
+
* declared field; a closed relation's SEALED shape — the synthetic `id`
|
|
111
|
+
* (the value's own roster-carrying descriptor, by identity) or a declared
|
|
112
|
+
* payload column. `undefined` when the name is foreign (the type tier makes
|
|
113
|
+
* that unwritable; the engine re-judges projections at `Db.create`).
|
|
114
|
+
*/
|
|
115
|
+
function projectedFieldOf(face: FaceData, fieldName: string): AnyField | undefined {
|
|
116
|
+
const owner = face.owner
|
|
117
|
+
if ("axioms" in owner) {
|
|
118
|
+
if (fieldName === "id") {
|
|
119
|
+
return owner.id
|
|
120
|
+
}
|
|
121
|
+
const column = owner.data.columns.find(function byName(candidate) {
|
|
122
|
+
return candidate.name === fieldName
|
|
123
|
+
})
|
|
124
|
+
return column?.field
|
|
125
|
+
}
|
|
126
|
+
const declared = owner.data.fields.find(function byName(candidate) {
|
|
127
|
+
return candidate.name === fieldName
|
|
128
|
+
})
|
|
129
|
+
return declared?.field
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The roster a descriptor carries: present exactly on a closed reference, absent on every other kind. */
|
|
133
|
+
function rosterOfField(field: AnyField | undefined): ClosedRoster | undefined {
|
|
134
|
+
if (field !== undefined && "closed" in field) {
|
|
135
|
+
return field.closed
|
|
136
|
+
}
|
|
137
|
+
return undefined
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Renders one face position's closedness for the roster-agreement diagnostics. */
|
|
141
|
+
function renderRosterSide(roster: ClosedRoster | undefined): string {
|
|
142
|
+
return roster === undefined ? "a bare column" : `a ${roster.name} reference`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The runtime twin of {@link SameShapes}'s roster slot: the two faces'
|
|
147
|
+
* projected descriptors must agree POSITIONWISE on closedness — the same
|
|
148
|
+
* roster (value identity — vocabulary identity is value identity, the
|
|
149
|
+
* SDK's membership rule everywhere) or none. Without this wall a plain u64
|
|
150
|
+
* column could alias a closed vocabulary through a declared containment
|
|
151
|
+
* (`docs/architecture/10-data-model.md` spells the ENGINE encoding that
|
|
152
|
+
* way), and every descriptor-keyed closed judgment — the orderable ban,
|
|
153
|
+
* the name↔id marshal, answer decode — would silently miss it. The
|
|
154
|
+
* vocabulary's own descriptor (`Kind.id`) is the ONE spelling of a closed
|
|
155
|
+
* reference at this surface (the canonical-utterance law); the engine
|
|
156
|
+
* cannot backstop this one — the wire carries plain u64s, no rosters.
|
|
157
|
+
*/
|
|
158
|
+
function assertRosterAgreement(source: FaceData, target: FaceData, statement: Statement): void {
|
|
159
|
+
source.projection.forEach(function agreeAt(fieldName, position) {
|
|
160
|
+
const targetField = target.projection[position]
|
|
161
|
+
if (targetField === undefined) {
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
const sourceRoster = rosterOfField(projectedFieldOf(source, fieldName))
|
|
165
|
+
const targetRoster = rosterOfField(projectedFieldOf(target, targetField))
|
|
166
|
+
if (sourceRoster !== targetRoster) {
|
|
167
|
+
throw errors.new(
|
|
168
|
+
`${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)}`
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
101
174
|
/**
|
|
102
175
|
* `R(X) -> R` — the FD key form, composite keys as tuples. No selection
|
|
103
176
|
* parameter exists (the FD-with-selection shape is unrepresentable, as in
|
|
@@ -145,7 +218,9 @@ function contained<A extends AnyFace, B extends AnyFace>(
|
|
|
145
218
|
target: target.data,
|
|
146
219
|
bidirectional: false
|
|
147
220
|
})
|
|
148
|
-
|
|
221
|
+
const statement = Object.freeze({ data })
|
|
222
|
+
assertRosterAgreement(data.source, data.target, statement)
|
|
223
|
+
return statement
|
|
149
224
|
}
|
|
150
225
|
|
|
151
226
|
/**
|
|
@@ -167,7 +242,9 @@ function mirrors<A extends AnyFace, B extends AnyFace>(
|
|
|
167
242
|
target: target.data,
|
|
168
243
|
bidirectional: true
|
|
169
244
|
})
|
|
170
|
-
|
|
245
|
+
const statement = Object.freeze({ data })
|
|
246
|
+
assertRosterAgreement(data.source, data.target, statement)
|
|
247
|
+
return statement
|
|
171
248
|
}
|
|
172
249
|
|
|
173
250
|
/**
|
|
@@ -191,7 +268,9 @@ function window<B extends AnyFace, A extends AnyFace>(
|
|
|
191
268
|
window: count.window,
|
|
192
269
|
source: source.data
|
|
193
270
|
})
|
|
194
|
-
|
|
271
|
+
const statement = Object.freeze({ data })
|
|
272
|
+
assertRosterAgreement(data.source, data.target, statement)
|
|
273
|
+
return statement
|
|
195
274
|
}
|
|
196
275
|
|
|
197
276
|
/**
|