@bjornpagen/bumbledb 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COOKBOOK.md +176 -36
- package/README.md +70 -26
- package/dist/capacity.d.ts +15 -55
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +63 -12
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +28 -6
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +119 -71
- package/dist/changes.js.map +1 -1
- package/dist/closed.d.ts +23 -51
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +110 -168
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +8 -14
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +113 -188
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +17 -18
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +41 -20
- package/dist/compile.js.map +1 -1
- package/dist/db-native.d.ts +76 -41
- package/dist/db-native.d.ts.map +1 -1
- package/dist/db-native.js +8 -0
- package/dist/db-native.js.map +1 -1
- package/dist/db.d.ts +61 -26
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +152 -85
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +11 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -1
- package/dist/errors.js.map +1 -1
- package/dist/face.d.ts +19 -25
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +30 -24
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +13 -26
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +89 -116
- package/dist/fields.js.map +1 -1
- package/dist/immutable.d.ts +10 -0
- package/dist/immutable.d.ts.map +1 -0
- package/dist/immutable.js +75 -0
- package/dist/immutable.js.map +1 -0
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +6 -4
- package/dist/internal/log.d.ts.map +1 -1
- package/dist/internal/log.js +3 -1
- package/dist/internal/log.js.map +1 -1
- package/dist/law.d.ts +11 -11
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +1 -1
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +11 -6
- package/dist/lower.js.map +1 -1
- package/dist/migration.d.ts +2 -3
- package/dist/migration.d.ts.map +1 -1
- package/dist/migration.js +5 -5
- package/dist/migration.js.map +1 -1
- package/dist/native.d.ts +2 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +1 -1
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js.map +1 -1
- package/dist/query/description.d.ts +33 -0
- package/dist/query/description.d.ts.map +1 -0
- package/dist/query/description.js +302 -0
- package/dist/query/description.js.map +1 -0
- package/dist/query/lower.d.ts +60 -4
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +126 -161
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts +7 -2
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +240 -53
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +8 -10
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +3 -4
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +5 -10
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +22 -48
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +30 -82
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +10 -25
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +7 -12
- package/dist/result.js.map +1 -1
- package/dist/rows.d.ts +11 -14
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -237
- package/dist/rows.js.map +1 -1
- package/dist/runtime-codes.d.ts +1 -1
- package/dist/runtime-codes.d.ts.map +1 -1
- package/dist/runtime-codes.js +1 -2
- package/dist/runtime-codes.js.map +1 -1
- package/dist/runtime-errors.d.ts +27 -15
- package/dist/runtime-errors.d.ts.map +1 -1
- package/dist/runtime-errors.js +44 -8
- package/dist/runtime-errors.js.map +1 -1
- package/dist/runtime-native.d.ts +16 -34
- package/dist/runtime-native.d.ts.map +1 -1
- package/dist/runtime-native.js.map +1 -1
- package/dist/runtime.d.ts +11 -28
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +14 -54
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +9 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +85 -24
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +27 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +104 -0
- package/dist/selection.js.map +1 -0
- package/dist/shape.d.ts +6 -9
- package/dist/shape.d.ts.map +1 -1
- package/dist/statements.d.ts +17 -52
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +92 -111
- package/dist/statements.js.map +1 -1
- package/dist/values.d.ts +25 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +203 -0
- package/dist/values.js.map +1 -0
- package/pack-provenance.json +3 -3
- package/package.json +4 -4
- package/src/capacity.ts +86 -76
- package/src/changes.ts +225 -94
- package/src/closed.ts +161 -314
- package/src/codec.ts +123 -213
- package/src/compile.ts +61 -38
- package/src/db-native.ts +73 -68
- package/src/db.ts +268 -165
- package/src/errors.ts +16 -1
- package/src/face.ts +44 -74
- package/src/fields.ts +100 -146
- package/src/immutable.ts +76 -0
- package/src/index.ts +38 -31
- package/src/internal/log.ts +4 -5
- package/src/law.ts +14 -14
- package/src/lower.ts +13 -10
- package/src/migration.ts +5 -12
- package/src/native.ts +2 -1
- package/src/query/atom.ts +1 -0
- package/src/query/description.ts +394 -0
- package/src/query/lower.ts +139 -173
- package/src/query/parse-ir.ts +247 -54
- package/src/query/run.ts +15 -9
- package/src/query/scope.ts +10 -18
- package/src/relation.ts +58 -142
- package/src/result.ts +16 -45
- package/src/rows.ts +57 -250
- package/src/runtime-codes.ts +1 -2
- package/src/runtime-errors.ts +48 -8
- package/src/runtime-native.ts +13 -46
- package/src/runtime.ts +51 -119
- package/src/schema.ts +99 -29
- package/src/selection.ts +124 -0
- package/src/shape.ts +9 -9
- package/src/statements.ts +138 -189
- package/src/values.ts +223 -0
package/src/closed.ts
CHANGED
|
@@ -1,364 +1,211 @@
|
|
|
1
1
|
import { AuthoringError } from "#errors.ts"
|
|
2
2
|
import {
|
|
3
|
-
type AnyClosedIdField,
|
|
4
3
|
type AnyField,
|
|
5
4
|
assertDeclarationOrderKey,
|
|
6
5
|
assertDeclarationRecord,
|
|
7
6
|
type ClosedHandleTuple,
|
|
8
7
|
type ClosedIdField,
|
|
9
|
-
type ClosedRoster,
|
|
10
8
|
type Infer,
|
|
11
|
-
|
|
9
|
+
ownHandles,
|
|
10
|
+
signaturesAgree
|
|
12
11
|
} from "#fields.ts"
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
|
|
12
|
+
import { descriptorCache } from "#immutable.ts"
|
|
13
|
+
import {
|
|
14
|
+
type AnyRelation,
|
|
15
|
+
type FieldsShape,
|
|
16
|
+
ownFields,
|
|
17
|
+
type RelationField,
|
|
18
|
+
relationDescriptor,
|
|
19
|
+
relationFields
|
|
20
|
+
} from "#relation.ts"
|
|
21
|
+
import { fieldValue, recordValue } from "#values.ts"
|
|
16
22
|
|
|
17
23
|
type PayloadField = AnyField
|
|
18
|
-
|
|
19
|
-
type
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
readonly handle: string
|
|
28
|
-
readonly values: readonly LiteralSpec[]
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface ClosedData {
|
|
32
|
-
readonly name: string
|
|
33
|
-
readonly handles: readonly string[]
|
|
34
|
-
readonly columns: readonly ClosedColumn[]
|
|
35
|
-
readonly rows: readonly ClosedRow[]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
type AxiomRow<Cols extends Record<string, PayloadField>> = { readonly [C in keyof Cols]: Infer<Cols[C]> }
|
|
39
|
-
|
|
40
|
-
type Axioms<Handles extends string, Cols extends Record<string, PayloadField>> = {
|
|
41
|
-
readonly [H in Handles]: AxiomRow<Cols>
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
interface ClosedCore<
|
|
45
|
-
Name extends string,
|
|
46
|
-
Handles extends ClosedHandleTuple,
|
|
47
|
-
Cols extends Record<string, PayloadField>
|
|
24
|
+
type PayloadColumns = FieldsShape & { readonly id?: never }
|
|
25
|
+
type AxiomRow<Cols extends FieldsShape> = { readonly [C in keyof Cols]: Infer<Cols[C]> }
|
|
26
|
+
type Axioms<Handles extends string, Cols extends FieldsShape> = { readonly [H in Handles]: AxiomRow<Cols> }
|
|
27
|
+
|
|
28
|
+
/** Closed relations declare their finite roster and its complete, typed ground facts. */
|
|
29
|
+
interface Closed<
|
|
30
|
+
Name extends string = string,
|
|
31
|
+
Handles extends ClosedHandleTuple = ClosedHandleTuple,
|
|
32
|
+
Cols extends FieldsShape = FieldsShape
|
|
48
33
|
> {
|
|
34
|
+
readonly kind: "closed"
|
|
49
35
|
readonly name: Name
|
|
50
|
-
|
|
51
|
-
readonly id: ClosedIdField<Name, Handles>
|
|
52
|
-
readonly data: ClosedData
|
|
53
|
-
|
|
54
|
-
readonly axioms: Axioms<Handles[number], Cols>
|
|
55
|
-
|
|
36
|
+
readonly handles: Handles
|
|
56
37
|
readonly columns: Cols
|
|
38
|
+
readonly axioms: Axioms<Handles[number], Cols>
|
|
57
39
|
}
|
|
58
|
-
|
|
59
|
-
type ClosedSelectionInput<Cols extends Record<string, PayloadField>> = SelectionInput<Cols>
|
|
60
|
-
|
|
61
|
-
interface SelectedClosed<
|
|
62
|
-
Name extends string,
|
|
63
|
-
Handles extends ClosedHandleTuple,
|
|
64
|
-
Cols extends Record<string, PayloadField>
|
|
65
|
-
> {
|
|
66
|
-
readonly relation: Closed<Name, Handles, Cols>
|
|
67
|
-
readonly selection: readonly SelectionBinding[]
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
interface AnySelectedClosed {
|
|
71
|
-
readonly relation: AnyClosed
|
|
72
|
-
readonly selection: readonly SelectionBinding[]
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
interface ClosedSelectable<
|
|
76
|
-
Name extends string,
|
|
77
|
-
Handles extends ClosedHandleTuple,
|
|
78
|
-
Cols extends Record<string, PayloadField>
|
|
79
|
-
> {
|
|
80
|
-
where(selection: ClosedSelectionInput<Cols>): SelectedClosed<Name, Handles, Cols>
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
type Closed<Name extends string, Handles extends ClosedHandleTuple, Cols extends Record<string, PayloadField>> = [
|
|
84
|
-
keyof Cols
|
|
85
|
-
] extends [never]
|
|
86
|
-
? ClosedCore<Name, Handles, Cols>
|
|
87
|
-
: ClosedCore<Name, Handles, Cols> & ClosedSelectable<Name, Handles, Cols>
|
|
88
|
-
|
|
89
|
-
interface AnyClosed {
|
|
90
|
-
readonly name: string
|
|
91
|
-
readonly id: AnyClosedIdField
|
|
92
|
-
readonly data: ClosedData
|
|
93
|
-
readonly axioms: Readonly<Record<string, object>>
|
|
94
|
-
readonly columns: Readonly<Record<string, PayloadField>>
|
|
95
|
-
}
|
|
40
|
+
type AnyClosed = Closed
|
|
96
41
|
|
|
97
42
|
function isClosedMember(member: AnyRelation | AnyClosed): member is AnyClosed {
|
|
98
|
-
return "
|
|
43
|
+
return member.kind === "closed"
|
|
99
44
|
}
|
|
100
45
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
function sealedFieldOf(member: AnyRelation | AnyClosed, fieldName: string): AnyField | undefined {
|
|
109
|
-
const declared = sealedFieldsOf(member).find(function byName(candidate) {
|
|
110
|
-
return candidate.name === fieldName
|
|
46
|
+
/** The synthetic id is an ordinary field descriptor, derived from the declared roster. */
|
|
47
|
+
function closedId<C extends AnyClosed>(member: C): ClosedIdField<C["name"], C["handles"]> {
|
|
48
|
+
recordValue("closed relation", member, ["kind", "name", "handles", "columns", "axioms"])
|
|
49
|
+
assertDeclarationOrderKey("closed relation", member.name)
|
|
50
|
+
return Object.freeze({
|
|
51
|
+
kind: "u64",
|
|
52
|
+
closed: Object.freeze({ name: member.name, handles: ownHandles(`closed relation ${member.name}`, member.handles) })
|
|
111
53
|
})
|
|
112
|
-
return declared?.field
|
|
113
54
|
}
|
|
114
55
|
|
|
115
|
-
function
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
shape.every(function stringHandle(handle) {
|
|
120
|
-
return typeof handle === "string"
|
|
121
|
-
})
|
|
122
|
-
)
|
|
56
|
+
function closedDescriptor<C extends AnyClosed>(input: C): C
|
|
57
|
+
function closedDescriptor(input: unknown): AnyClosed
|
|
58
|
+
function closedDescriptor(input: unknown): AnyClosed {
|
|
59
|
+
return checkedClosed(input)
|
|
123
60
|
}
|
|
124
61
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
62
|
+
const checkedClosed = descriptorCache((input): AnyClosed => {
|
|
63
|
+
const raw = recordValue("closed relation", input, ["kind", "name", "handles", "columns", "axioms"])
|
|
64
|
+
if (raw.kind !== "closed" || typeof raw.name !== "string")
|
|
65
|
+
throw new AuthoringError({ message: "closed relation: expected a closed declaration" })
|
|
66
|
+
assertDeclarationOrderKey("closed relation", raw.name)
|
|
67
|
+
const handles = ownHandles(`closed relation ${raw.name}`, raw.handles)
|
|
68
|
+
const columns = ownFields(`closed relation ${raw.name} columns`, raw.columns)
|
|
69
|
+
if (Object.hasOwn(columns, "id"))
|
|
70
|
+
throw new AuthoringError({
|
|
71
|
+
message: `closed relation ${raw.name}: payload column id collides with the synthetic id`
|
|
72
|
+
})
|
|
73
|
+
const axioms = recordValue(`closed relation ${raw.name} axioms`, raw.axioms, handles)
|
|
74
|
+
const names = Object.keys(columns)
|
|
75
|
+
const owned = Object.fromEntries(
|
|
76
|
+
handles.map((handle) => {
|
|
77
|
+
const row = recordValue(`closed relation ${raw.name}.${handle}`, axioms[handle], names)
|
|
78
|
+
return [
|
|
79
|
+
handle,
|
|
80
|
+
Object.freeze(
|
|
81
|
+
Object.fromEntries(
|
|
82
|
+
Object.entries(columns).map(([name, field]) => [
|
|
83
|
+
name,
|
|
84
|
+
fieldValue(`closed relation ${raw.name}.${handle}.${name}`, field, row[name])
|
|
85
|
+
])
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
]
|
|
141
89
|
})
|
|
142
90
|
)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* The trusted seam of the axiom-readback mint: every handle carries an own
|
|
147
|
-
* frozen row and every row carries every declared column as an own
|
|
148
|
-
* property — verified before the record is admitted as the typed
|
|
149
|
-
* {@link Axioms} (the trusted-admission-seam pattern — its home is
|
|
150
|
-
* `isTypedScope` in query/lower.ts).
|
|
151
|
-
*/
|
|
152
|
-
function axiomsMinted<Handles extends string, Cols extends Record<string, PayloadField>>(
|
|
153
|
-
record: Readonly<Record<string, object>>,
|
|
154
|
-
handles: readonly Handles[],
|
|
155
|
-
cols: readonly ClosedColumn[]
|
|
156
|
-
): record is Axioms<Handles, Cols> & Readonly<Record<string, object>> {
|
|
157
|
-
return handles.every(function rowMinted(handle) {
|
|
158
|
-
const row = record[handle]
|
|
159
|
-
return (
|
|
160
|
-
row !== undefined &&
|
|
161
|
-
cols.every(function columnMinted(column) {
|
|
162
|
-
return Object.hasOwn(row, column.name)
|
|
163
|
-
})
|
|
164
|
-
)
|
|
165
|
-
})
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function mintAxioms<Handles extends string, Cols extends Record<string, PayloadField>>(
|
|
169
|
-
name: string,
|
|
170
|
-
handles: readonly Handles[],
|
|
171
|
-
cols: readonly ClosedColumn[],
|
|
172
|
-
axioms: Axioms<Handles, Cols>
|
|
173
|
-
): Axioms<Handles, Cols> {
|
|
174
|
-
const out: Record<string, object> = {}
|
|
175
|
-
for (const handle of handles) {
|
|
176
|
-
const row = Object.freeze({ ...axioms[handle] })
|
|
177
|
-
Object.defineProperty(out, handle, { value: row, enumerable: true })
|
|
178
|
-
}
|
|
179
|
-
Object.freeze(out)
|
|
180
|
-
if (!axiomsMinted<Handles, Cols>(out, handles, cols)) {
|
|
181
|
-
throw new AuthoringError({ message: `closed relation ${name}: axiom-row minting incomplete` })
|
|
182
|
-
}
|
|
183
|
-
return out
|
|
184
|
-
}
|
|
91
|
+
return Object.freeze({ kind: "closed", name: raw.name, handles, columns, axioms: Object.freeze(owned) })
|
|
92
|
+
})
|
|
185
93
|
|
|
186
94
|
function closed<const Name extends string, const Handles extends ClosedHandleTuple>(
|
|
187
95
|
name: Name,
|
|
188
96
|
handles: Handles
|
|
189
97
|
): Closed<Name, Handles, Record<never, never>>
|
|
190
|
-
|
|
191
98
|
function closed<const Name extends string, const Handles extends ClosedHandleTuple, const Cols extends PayloadColumns>(
|
|
192
99
|
name: Name,
|
|
193
100
|
handles: Handles,
|
|
194
101
|
columns: Cols,
|
|
195
102
|
axioms: Axioms<Handles[number], Cols>
|
|
196
103
|
): Closed<Name, Handles, Cols>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
104
|
+
function closed(
|
|
105
|
+
name: string,
|
|
106
|
+
handles: ClosedHandleTuple,
|
|
107
|
+
columns?: PayloadColumns,
|
|
108
|
+
axioms?: Axioms<string, FieldsShape>
|
|
109
|
+
): AnyClosed {
|
|
110
|
+
if (columns === undefined && axioms === undefined) {
|
|
111
|
+
const roster = ownHandles(`closed relation ${name}`, handles)
|
|
112
|
+
return closedDescriptor({
|
|
113
|
+
kind: "closed",
|
|
114
|
+
name,
|
|
115
|
+
handles: roster,
|
|
116
|
+
columns: {},
|
|
117
|
+
axioms: Object.fromEntries(roster.map((handle) => [handle, {}]))
|
|
207
118
|
})
|
|
208
119
|
}
|
|
209
|
-
if (
|
|
120
|
+
if (columns === undefined || axioms === undefined)
|
|
210
121
|
throw new AuthoringError({
|
|
211
|
-
message: `closed relation ${name}:
|
|
122
|
+
message: `closed relation ${name}: payload columns and ground axioms must be supplied together`
|
|
212
123
|
})
|
|
213
|
-
}
|
|
214
|
-
if (columns === undefined) {
|
|
215
|
-
if (axioms !== undefined) {
|
|
216
|
-
throw new AuthoringError({
|
|
217
|
-
message: `closed relation ${name}: the bare tier declares no columns, so ground axioms are inadmissible`
|
|
218
|
-
})
|
|
219
|
-
}
|
|
220
|
-
return closedBare(name, handles)
|
|
221
|
-
}
|
|
222
|
-
if (axioms === undefined) {
|
|
223
|
-
throw new AuthoringError({
|
|
224
|
-
message: `closed relation ${name}: payload columns declared without ground axioms — the payload tier is spelled closed(name, handles, columns, axioms)`
|
|
225
|
-
})
|
|
226
|
-
}
|
|
227
|
-
return closedPayload(name, handles, columns, axioms)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function closedBare<Name extends string, const Hs extends ClosedHandleTuple>(
|
|
231
|
-
name: Name,
|
|
232
|
-
handles: Hs
|
|
233
|
-
): Closed<Name, Hs, Record<never, never>> {
|
|
234
|
-
const empty: Record<string, object> = {}
|
|
235
|
-
for (const handle of handles) {
|
|
236
|
-
/** A duplicated name mints one row; the roster's own duplicate refusal in {@link mintClosed} stays the judge. */
|
|
237
|
-
if (!Object.hasOwn(empty, handle)) {
|
|
238
|
-
Object.defineProperty(empty, handle, { value: Object.freeze({}), enumerable: true })
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
Object.freeze(empty)
|
|
242
|
-
if (!axiomsMinted<Hs[number], Record<never, never>>(empty, handles, [])) {
|
|
243
|
-
throw new AuthoringError({ message: `closed relation ${name}: bare-tier axiom-row minting incomplete` })
|
|
244
|
-
}
|
|
245
|
-
return mintClosed(name, handles, {}, empty)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function closedPayload<Name extends string, Handles extends ClosedHandleTuple, Cols extends PayloadColumns>(
|
|
249
|
-
name: Name,
|
|
250
|
-
handles: Handles,
|
|
251
|
-
columns: Cols,
|
|
252
|
-
axioms: Axioms<Handles[number], Cols>
|
|
253
|
-
): Closed<Name, Handles, Cols> {
|
|
254
|
-
assertDeclarationRecord(`closed relation ${name} columns`, columns)
|
|
255
|
-
for (const columnName of Object.keys(columns)) {
|
|
256
|
-
assertDeclarationOrderKey(`closed relation ${name} column`, columnName)
|
|
257
|
-
}
|
|
258
|
-
assertDeclarationRecord(`closed relation ${name} axioms`, axioms)
|
|
259
|
-
for (const handle of handles) {
|
|
260
|
-
assertDeclarationOrderKey(`closed relation ${name} handle`, handle)
|
|
261
|
-
}
|
|
262
|
-
if (!handleKeysOwn(axioms, handles)) {
|
|
263
|
-
throw new AuthoringError({ message: `closed relation ${name}: handle enumeration incomplete` })
|
|
264
|
-
}
|
|
265
|
-
return mintClosed(name, handles, columns, axioms)
|
|
124
|
+
return closedDescriptor({ kind: "closed", name, handles, columns, axioms })
|
|
266
125
|
}
|
|
267
126
|
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
)
|
|
276
|
-
const selectable = "where" in value && typeof value.where === "function"
|
|
277
|
-
return cols.length > 0 ? selectable : !selectable
|
|
127
|
+
function memberDescriptor<M extends AnyRelation | AnyClosed>(input: M): M
|
|
128
|
+
function memberDescriptor(input: unknown): AnyRelation | AnyClosed
|
|
129
|
+
function memberDescriptor(input: unknown): AnyRelation | AnyClosed {
|
|
130
|
+
if (typeof input !== "object" || input === null)
|
|
131
|
+
throw new AuthoringError({ message: "relation: expected a declaration record" })
|
|
132
|
+
assertDeclarationRecord("relation", input)
|
|
133
|
+
if ("kind" in input && input.kind === "closed") return closedDescriptor(input)
|
|
134
|
+
return relationDescriptor(input)
|
|
278
135
|
}
|
|
279
136
|
|
|
280
|
-
function
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const owner = holder.value
|
|
329
|
-
if (owner === undefined) {
|
|
330
|
-
throw new AuthoringError({
|
|
331
|
-
message: `closed relation ${name}: self-reference read before construction completed`
|
|
332
|
-
})
|
|
333
|
-
}
|
|
334
|
-
return Object.freeze({
|
|
335
|
-
relation: owner,
|
|
336
|
-
selection: resolveSelection(name, cols, Object.entries(selection))
|
|
137
|
+
function sealedFieldsOf(member: AnyRelation | AnyClosed): readonly RelationField[] {
|
|
138
|
+
return isClosedMember(member)
|
|
139
|
+
? [
|
|
140
|
+
{ name: "id", field: closedId(member) },
|
|
141
|
+
...Object.entries(member.columns).map(([name, field]) => ({ name, field }))
|
|
142
|
+
]
|
|
143
|
+
: relationFields(member)
|
|
144
|
+
}
|
|
145
|
+
function sealedFieldOf(member: AnyRelation | AnyClosed, name: string): AnyField | undefined {
|
|
146
|
+
if (isClosedMember(member)) {
|
|
147
|
+
if (name === "id") return closedId(member)
|
|
148
|
+
return Object.hasOwn(member.columns, name) ? member.columns[name] : undefined
|
|
149
|
+
}
|
|
150
|
+
return Object.hasOwn(member.fields, name) ? member.fields[name] : undefined
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function sameValue(a: unknown, b: unknown): boolean {
|
|
154
|
+
if (Object.is(a, b)) return true
|
|
155
|
+
if (a instanceof Uint8Array && b instanceof Uint8Array)
|
|
156
|
+
return a.length === b.length && a.every((byte, i) => byte === b[i])
|
|
157
|
+
if (
|
|
158
|
+
typeof a === "object" &&
|
|
159
|
+
a !== null &&
|
|
160
|
+
typeof b === "object" &&
|
|
161
|
+
b !== null &&
|
|
162
|
+
"start" in a &&
|
|
163
|
+
"end" in a &&
|
|
164
|
+
"start" in b &&
|
|
165
|
+
"end" in b
|
|
166
|
+
)
|
|
167
|
+
return Object.is(a.start, b.start) && Object.is(a.end, b.end)
|
|
168
|
+
return false
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Equal names must denote equal ordered declarations, never constructor identities. */
|
|
172
|
+
function membersAgree(left: AnyRelation | AnyClosed | undefined, right: AnyRelation | AnyClosed): boolean {
|
|
173
|
+
if (left === undefined) return false
|
|
174
|
+
const a = memberDescriptor(left)
|
|
175
|
+
const b = memberDescriptor(right)
|
|
176
|
+
if (a === b) return true
|
|
177
|
+
if (a.kind !== b.kind || a.name !== b.name) return false
|
|
178
|
+
const af = sealedFieldsOf(a)
|
|
179
|
+
const bf = sealedFieldsOf(b)
|
|
180
|
+
if (
|
|
181
|
+
af.length !== bf.length ||
|
|
182
|
+
!af.every((field, i) => {
|
|
183
|
+
const other = bf[i]
|
|
184
|
+
return other !== undefined && field.name === other.name && signaturesAgree(field.field, other.field)
|
|
337
185
|
})
|
|
186
|
+
)
|
|
187
|
+
return false
|
|
188
|
+
if (isClosedMember(a) && isClosedMember(b)) {
|
|
189
|
+
return (
|
|
190
|
+
a.handles.length === b.handles.length &&
|
|
191
|
+
a.handles.every(
|
|
192
|
+
(handle, i) =>
|
|
193
|
+
handle === b.handles[i] &&
|
|
194
|
+
Object.keys(a.columns).every((name) => sameValue(a.axioms[handle]?.[name], b.axioms[handle]?.[name]))
|
|
195
|
+
)
|
|
196
|
+
)
|
|
338
197
|
}
|
|
339
|
-
|
|
340
|
-
const value: ClosedCore<Name, Handles, Cols> =
|
|
341
|
-
cols.length > 0 ? Object.freeze({ ...core, where }) : Object.freeze(core)
|
|
342
|
-
if (!surfaceMinted(value, cols)) {
|
|
343
|
-
throw new AuthoringError({ message: `closed relation ${name}: ergonomic-surface minting incomplete` })
|
|
344
|
-
}
|
|
345
|
-
holder.value = value
|
|
346
|
-
return value
|
|
198
|
+
return true
|
|
347
199
|
}
|
|
348
200
|
|
|
349
|
-
export type {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
ClosedSelectable,
|
|
360
|
-
ClosedSelectionInput,
|
|
361
|
-
PayloadField,
|
|
362
|
-
SelectedClosed
|
|
201
|
+
export type { AnyClosed, AxiomRow, Axioms, Closed, PayloadField }
|
|
202
|
+
export {
|
|
203
|
+
closed,
|
|
204
|
+
closedDescriptor,
|
|
205
|
+
closedId,
|
|
206
|
+
isClosedMember,
|
|
207
|
+
memberDescriptor,
|
|
208
|
+
membersAgree,
|
|
209
|
+
sealedFieldOf,
|
|
210
|
+
sealedFieldsOf
|
|
363
211
|
}
|
|
364
|
-
export { closed, isClosedMember, sealedFieldOf, sealedFieldsOf }
|