@bjornpagen/bumbledb-log 0.17.0 → 0.19.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/README.md +26 -18
- package/package.json +5 -3
- package/src/braids.ts +19 -7
- package/src/bytes.ts +79 -11
- package/src/chain.ts +179 -63
- package/src/codec.ts +141 -62
- package/src/descriptor.ts +392 -348
- package/src/errors.ts +140 -10
- package/src/index.ts +23 -8
- package/src/keys.ts +212 -15
- package/src/manifest.ts +171 -75
- package/src/replica.ts +606 -295
- package/src/store-s3.ts +377 -0
- package/src/store.ts +524 -105
- package/src/tenants.ts +322 -26
- package/src/value.ts +80 -29
- package/src/vector.ts +173 -0
- package/src/writer.ts +484 -120
package/src/descriptor.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The parsed protocol descriptor: the
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* every pure function downstream reads ids, never names. Braid
|
|
7
|
-
* derivation and the schema fingerprint mirror live on the same parse:
|
|
8
|
-
* one boundary, parsed in full, cached per theory value.
|
|
2
|
+
* The parsed protocol descriptor: one thin parse of the engine's sealed
|
|
3
|
+
* truth — relation ids, sealed field order, resolved closed rows,
|
|
4
|
+
* materialized statements, and the real fingerprint — plus the braid
|
|
5
|
+
* map the protocol derives from that truth. Cached per theory value.
|
|
9
6
|
*/
|
|
10
7
|
|
|
11
8
|
import type {
|
|
@@ -13,20 +10,22 @@ import type {
|
|
|
13
10
|
LiteralSetSpec,
|
|
14
11
|
LiteralSpec,
|
|
15
12
|
SchemaSpec,
|
|
13
|
+
SealedDescriptor,
|
|
14
|
+
SealedSide,
|
|
15
|
+
SealedStatement,
|
|
16
16
|
SideSpec,
|
|
17
17
|
StatementSpec,
|
|
18
|
-
ValueSpec
|
|
19
|
-
ValueTypeSpec
|
|
18
|
+
ValueSpec
|
|
20
19
|
} from "@bjornpagen/bumbledb"
|
|
21
|
-
import {
|
|
20
|
+
import { internalDescriptor, lower } from "@bjornpagen/bumbledb"
|
|
22
21
|
import * as errors from "@superbuilders/errors"
|
|
23
|
-
import {
|
|
24
|
-
import
|
|
25
|
-
import {
|
|
22
|
+
import { regex } from "arkregex"
|
|
23
|
+
import { fromHex } from "#bytes.ts"
|
|
24
|
+
import type { Value } from "#value.ts"
|
|
26
25
|
|
|
27
26
|
interface FieldInfo {
|
|
28
27
|
readonly name: string
|
|
29
|
-
readonly type:
|
|
28
|
+
readonly type: SchemaSpec["relations"][number]["fields"][number]["valueType"]
|
|
30
29
|
readonly fresh: boolean
|
|
31
30
|
/** Set when the field's newtype names a closed relation's id class. */
|
|
32
31
|
readonly closedRef: string | undefined
|
|
@@ -40,13 +39,13 @@ interface RelationInfo {
|
|
|
40
39
|
/** Sealed order: a closed relation's synthetic u64 `id` at ordinal 0. */
|
|
41
40
|
readonly fields: readonly FieldInfo[]
|
|
42
41
|
/** Closed ground axioms in sealed order (id first), resolved values. */
|
|
43
|
-
readonly rows: ReadonlyArray<readonly
|
|
42
|
+
readonly rows: ReadonlyArray<readonly Value[]>
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
interface SideInfo {
|
|
47
46
|
readonly relation: number
|
|
48
47
|
readonly projection: readonly number[]
|
|
49
|
-
readonly selection: ReadonlyArray<{ readonly field: number; readonly values: readonly
|
|
48
|
+
readonly selection: ReadonlyArray<{ readonly field: number; readonly values: readonly Value[] }>
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
type WeightInfo =
|
|
@@ -78,38 +77,125 @@ type StatementInfo =
|
|
|
78
77
|
readonly source: SideInfo
|
|
79
78
|
}
|
|
80
79
|
|
|
80
|
+
declare const braidBrand: unique symbol
|
|
81
|
+
type Braid = string & { readonly [braidBrand]: typeof braidBrand }
|
|
82
|
+
|
|
83
|
+
const BRAID_ID = regex("^c[0-9a-f]{8}$")
|
|
84
|
+
const ID_CLASS = regex("^(.*)\\.id$")
|
|
85
|
+
|
|
86
|
+
function braid(raw: string): Braid {
|
|
87
|
+
if (!BRAID_ID.test(raw)) {
|
|
88
|
+
throw errors.new(`not a braid id: ${raw}`)
|
|
89
|
+
}
|
|
90
|
+
return raw as Braid
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function refuseShape(why: string): never {
|
|
94
|
+
throw errors.new(`theory: ${why}`)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** `{name}.id` — the closed relation's generator class. */
|
|
98
|
+
function idClassOwner(newtype: string): string | undefined {
|
|
99
|
+
const match = ID_CLASS.exec(newtype)
|
|
100
|
+
const owner = match?.[1]
|
|
101
|
+
if (owner === undefined || owner.length === 0) {
|
|
102
|
+
return undefined
|
|
103
|
+
}
|
|
104
|
+
return owner
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function indexRelations(relations: readonly RelationInfo[]): Map<number, RelationInfo> {
|
|
108
|
+
const byId = new Map<number, RelationInfo>()
|
|
109
|
+
for (const relation of relations) {
|
|
110
|
+
if (byId.has(relation.id)) {
|
|
111
|
+
refuseShape(`duplicate relation id ${relation.id}`)
|
|
112
|
+
}
|
|
113
|
+
byId.set(relation.id, relation)
|
|
114
|
+
}
|
|
115
|
+
return byId
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function relationOfId(byId: ReadonlyMap<number, RelationInfo>, id: number): RelationInfo {
|
|
119
|
+
const relation = byId.get(id)
|
|
120
|
+
if (relation === undefined) {
|
|
121
|
+
refuseShape(`unknown relation id ${id}`)
|
|
122
|
+
}
|
|
123
|
+
return relation
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function fieldNamed(relation: RelationInfo, name: string): { readonly ordinal: number; readonly field: FieldInfo } {
|
|
127
|
+
let ordinal = 0
|
|
128
|
+
for (const field of relation.fields) {
|
|
129
|
+
if (field.name === name) {
|
|
130
|
+
return { ordinal, field }
|
|
131
|
+
}
|
|
132
|
+
ordinal += 1
|
|
133
|
+
}
|
|
134
|
+
refuseShape(`relation ${relation.name} has no field ${name}`)
|
|
135
|
+
}
|
|
136
|
+
|
|
81
137
|
interface SerialStatement {
|
|
82
138
|
readonly statement: number
|
|
83
|
-
readonly braid:
|
|
139
|
+
readonly braid: Braid
|
|
84
140
|
}
|
|
85
141
|
|
|
86
|
-
|
|
142
|
+
function serialAtOf(
|
|
143
|
+
byId: ReadonlyMap<number, RelationInfo>,
|
|
144
|
+
statements: readonly StatementInfo[],
|
|
145
|
+
braidOfRelation: ReadonlyMap<number, Braid>
|
|
146
|
+
): SerialStatement[] {
|
|
147
|
+
const serialAt: SerialStatement[] = []
|
|
148
|
+
for (const statement of statements) {
|
|
149
|
+
if (statement.kind === "functionality" && statement.projection.length === 0) {
|
|
150
|
+
const relation = relationOfId(byId, statement.relation)
|
|
151
|
+
if (relation.fields.length > 0) {
|
|
152
|
+
const braid = braidOfRelation.get(statement.relation)
|
|
153
|
+
if (braid !== undefined) {
|
|
154
|
+
serialAt.push({ statement: statement.id, braid })
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (statement.kind === "capacity" && statement.target.projection.length === 0) {
|
|
159
|
+
const targetRelation = relationOfId(byId, statement.target.relation)
|
|
160
|
+
if (!targetRelation.closed) {
|
|
161
|
+
const braid = braidOfRelation.get(statement.target.relation)
|
|
162
|
+
if (braid === undefined) {
|
|
163
|
+
refuseShape(`capacity ${statement.id} target ${targetRelation.name} is not in the derived braid set`)
|
|
164
|
+
}
|
|
165
|
+
serialAt.push({ statement: statement.id, braid })
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return serialAt
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
interface Descriptor {
|
|
87
173
|
readonly relations: readonly RelationInfo[]
|
|
88
174
|
readonly relationByName: ReadonlyMap<string, RelationInfo>
|
|
89
175
|
readonly statements: readonly StatementInfo[]
|
|
90
|
-
/** Ordinary relation id → braid id
|
|
91
|
-
readonly braidOfRelation: ReadonlyMap<number,
|
|
92
|
-
/** Braid id
|
|
93
|
-
readonly braidMembers: ReadonlyMap<
|
|
176
|
+
/** Ordinary relation id → braid id (`c{smallest:08x}`). */
|
|
177
|
+
readonly braidOfRelation: ReadonlyMap<number, Braid>
|
|
178
|
+
/** Braid id → member relation ids, ascending. */
|
|
179
|
+
readonly braidMembers: ReadonlyMap<Braid, readonly number[]>
|
|
94
180
|
readonly serialAtStatements: readonly SerialStatement[]
|
|
95
181
|
readonly fingerprint: string
|
|
96
182
|
readonly fingerprintBytes: Uint8Array
|
|
97
183
|
}
|
|
98
184
|
|
|
99
185
|
/** The pure trio's input: the theory value, its lowered spec, or an already-parsed descriptor. */
|
|
100
|
-
type
|
|
186
|
+
type Theory = AnySchema | SchemaSpec | Descriptor
|
|
101
187
|
|
|
102
|
-
function isDescriptor(theory:
|
|
188
|
+
function isDescriptor(theory: Theory): theory is Descriptor {
|
|
103
189
|
return "braidMembers" in theory
|
|
104
190
|
}
|
|
105
191
|
|
|
106
|
-
function isSpec(theory:
|
|
192
|
+
function isSpec(theory: Theory): theory is SchemaSpec {
|
|
107
193
|
return Array.isArray((theory as SchemaSpec).relations)
|
|
108
194
|
}
|
|
109
195
|
|
|
110
|
-
const cache = new WeakMap<object,
|
|
196
|
+
const cache = new WeakMap<object, Descriptor>()
|
|
111
197
|
|
|
112
|
-
function descriptorOf(theory:
|
|
198
|
+
function descriptorOf(theory: Theory): Descriptor {
|
|
113
199
|
if (isDescriptor(theory)) {
|
|
114
200
|
return theory
|
|
115
201
|
}
|
|
@@ -118,17 +204,192 @@ function descriptorOf(theory: LogTheory): LogDescriptor {
|
|
|
118
204
|
return hit
|
|
119
205
|
}
|
|
120
206
|
const spec = isSpec(theory) ? theory : lower(theory)
|
|
121
|
-
const parsed = spec === theory ?
|
|
207
|
+
const parsed = spec === theory ? fromSealed(spec) : (cache.get(spec) ?? fromSealed(spec))
|
|
122
208
|
cache.set(theory, parsed)
|
|
123
209
|
cache.set(spec, parsed)
|
|
124
210
|
return parsed
|
|
125
211
|
}
|
|
126
212
|
|
|
127
|
-
function braidHex(relationId: number):
|
|
128
|
-
return `c${relationId.toString(16).padStart(8, "0")}`
|
|
213
|
+
function braidHex(relationId: number): Braid {
|
|
214
|
+
return braid(`c${relationId.toString(16).padStart(8, "0")}`)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function asValue(raw: unknown): Value {
|
|
218
|
+
if (typeof raw === "boolean" || typeof raw === "bigint" || typeof raw === "string" || raw instanceof Uint8Array) {
|
|
219
|
+
return raw
|
|
220
|
+
}
|
|
221
|
+
if (typeof raw === "object" && raw !== null && "start" in raw && "end" in raw) {
|
|
222
|
+
const interval = raw as { start: unknown; end: unknown }
|
|
223
|
+
if (typeof interval.start === "bigint" && typeof interval.end === "bigint") {
|
|
224
|
+
return { start: interval.start, end: interval.end }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
refuseShape("sealed value is not a raw fact value")
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function sideOf(side: SealedSide): SideInfo {
|
|
231
|
+
return {
|
|
232
|
+
relation: side.relation,
|
|
233
|
+
projection: side.projection,
|
|
234
|
+
selection: side.selection.map(function valuesOf(binding) {
|
|
235
|
+
return { field: binding.field, values: binding.values.map(asValue) }
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function statementOf(statement: SealedStatement): StatementInfo {
|
|
241
|
+
switch (statement.kind) {
|
|
242
|
+
case "functionality":
|
|
243
|
+
return statement
|
|
244
|
+
case "containment":
|
|
245
|
+
return {
|
|
246
|
+
id: statement.id,
|
|
247
|
+
kind: "containment",
|
|
248
|
+
source: sideOf(statement.source),
|
|
249
|
+
target: sideOf(statement.target)
|
|
250
|
+
}
|
|
251
|
+
case "capacity":
|
|
252
|
+
return {
|
|
253
|
+
id: statement.id,
|
|
254
|
+
kind: "capacity",
|
|
255
|
+
target: sideOf(statement.target),
|
|
256
|
+
weight: statement.weight,
|
|
257
|
+
lo: statement.lo,
|
|
258
|
+
hi: statement.hi,
|
|
259
|
+
source: sideOf(statement.source)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function fromSealed(spec: SchemaSpec): Descriptor {
|
|
265
|
+
const sealed: SealedDescriptor = internalDescriptor(spec)
|
|
266
|
+
const specByName = new Map<string, SchemaSpec["relations"][number]>()
|
|
267
|
+
for (const relation of spec.relations) {
|
|
268
|
+
if (specByName.has(relation.name)) {
|
|
269
|
+
refuseShape(`duplicate relation ${relation.name}`)
|
|
270
|
+
}
|
|
271
|
+
specByName.set(relation.name, relation)
|
|
272
|
+
}
|
|
273
|
+
const closedOwners = new Set(
|
|
274
|
+
spec.relations
|
|
275
|
+
.filter(function closedOf(relation) {
|
|
276
|
+
return relation.closed !== undefined
|
|
277
|
+
})
|
|
278
|
+
.map(function nameOf(relation) {
|
|
279
|
+
return relation.name
|
|
280
|
+
})
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
const relations: RelationInfo[] = sealed.relations.map(function relationOf(relation) {
|
|
284
|
+
const declared = specByName.get(relation.name)
|
|
285
|
+
if (declared === undefined) {
|
|
286
|
+
refuseShape(`sealed relation ${relation.name} is not in the spec`)
|
|
287
|
+
}
|
|
288
|
+
const closed = relation.extension !== undefined
|
|
289
|
+
const fields: FieldInfo[] = relation.fields.map(function fieldOf(field) {
|
|
290
|
+
if (field.name === "id" && closed) {
|
|
291
|
+
return { name: field.name, type: field.valueType, fresh: false, closedRef: relation.name }
|
|
292
|
+
}
|
|
293
|
+
const specField = declared.fields.find(function named(candidate) {
|
|
294
|
+
return candidate.name === field.name
|
|
295
|
+
})
|
|
296
|
+
let closedRef: string | undefined
|
|
297
|
+
const owner = specField?.newtype === undefined ? undefined : idClassOwner(specField.newtype)
|
|
298
|
+
if (owner !== undefined && closedOwners.has(owner)) {
|
|
299
|
+
closedRef = owner
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
name: field.name,
|
|
303
|
+
type: field.valueType,
|
|
304
|
+
fresh: specField?.fresh === true,
|
|
305
|
+
closedRef
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
const extension = relation.extension ?? []
|
|
309
|
+
const named = new Map(
|
|
310
|
+
relation.fields.map(function indexOf(field) {
|
|
311
|
+
return [field.name, field]
|
|
312
|
+
})
|
|
313
|
+
)
|
|
314
|
+
const rows = extension.map(function rowOf(row) {
|
|
315
|
+
const cells = new Map(
|
|
316
|
+
row.values.map(function cellOf(cell) {
|
|
317
|
+
return [cell.name, asValue(cell.value)]
|
|
318
|
+
})
|
|
319
|
+
)
|
|
320
|
+
return relation.fields.map(function cellValue(field) {
|
|
321
|
+
const hit = cells.get(field.name)
|
|
322
|
+
if (hit !== undefined) {
|
|
323
|
+
return hit
|
|
324
|
+
}
|
|
325
|
+
if (field.name === "id" && named.has("id")) {
|
|
326
|
+
return row.id
|
|
327
|
+
}
|
|
328
|
+
return refuseShape(`closed relation ${relation.name}: sealed row ${row.handle} missing field ${field.name}`)
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
return {
|
|
332
|
+
id: relation.id,
|
|
333
|
+
name: relation.name,
|
|
334
|
+
closed,
|
|
335
|
+
handles: extension.map(function handleOf(row) {
|
|
336
|
+
return row.handle
|
|
337
|
+
}),
|
|
338
|
+
fields,
|
|
339
|
+
rows
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
const byName = new Map<string, RelationInfo>()
|
|
344
|
+
for (const relation of relations) {
|
|
345
|
+
if (byName.has(relation.name)) {
|
|
346
|
+
refuseShape(`duplicate relation ${relation.name}`)
|
|
347
|
+
}
|
|
348
|
+
byName.set(relation.name, relation)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const statements = sealed.statements.map(statementOf)
|
|
352
|
+
const byId = indexRelations(relations)
|
|
353
|
+
const { braidOfRelation, braidMembers } = deriveBraids(byId, statements)
|
|
354
|
+
const serialAtStatements = serialAtOf(byId, statements, braidOfRelation)
|
|
355
|
+
|
|
356
|
+
const fingerprint = sealed.fingerprint
|
|
357
|
+
return {
|
|
358
|
+
relations,
|
|
359
|
+
relationByName: byName,
|
|
360
|
+
statements,
|
|
361
|
+
braidOfRelation,
|
|
362
|
+
braidMembers,
|
|
363
|
+
serialAtStatements,
|
|
364
|
+
fingerprint,
|
|
365
|
+
fingerprintBytes: fromHex(fingerprint)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* The same descriptor under a pinned fingerprint — for stores whose
|
|
371
|
+
* identity is carried (a manifest, a conformance sidecar) rather than
|
|
372
|
+
* recomputed.
|
|
373
|
+
*/
|
|
374
|
+
function withFingerprint(descriptor: Descriptor, fingerprint: string): Descriptor {
|
|
375
|
+
return {
|
|
376
|
+
relations: descriptor.relations,
|
|
377
|
+
relationByName: descriptor.relationByName,
|
|
378
|
+
statements: descriptor.statements,
|
|
379
|
+
braidOfRelation: descriptor.braidOfRelation,
|
|
380
|
+
braidMembers: descriptor.braidMembers,
|
|
381
|
+
serialAtStatements: descriptor.serialAtStatements,
|
|
382
|
+
fingerprint,
|
|
383
|
+
fingerprintBytes: fromHex(fingerprint)
|
|
384
|
+
}
|
|
129
385
|
}
|
|
130
386
|
|
|
131
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Assemble a descriptor from a SchemaSpec that is not a theory — the
|
|
389
|
+
* conformance corpus pins the codec and the braid map on shapes the
|
|
390
|
+
* engine seal refuses. Theories enter through descriptorOf.
|
|
391
|
+
*/
|
|
392
|
+
function rawValue(value: ValueSpec): Value {
|
|
132
393
|
switch (value.kind) {
|
|
133
394
|
case "bool":
|
|
134
395
|
return value.value
|
|
@@ -147,8 +408,39 @@ function logValueOf(value: ValueSpec): LogValue {
|
|
|
147
408
|
|
|
148
409
|
interface SpecTables {
|
|
149
410
|
readonly spec: SchemaSpec
|
|
150
|
-
readonly relations: RelationInfo[]
|
|
151
411
|
readonly byName: Map<string, RelationInfo>
|
|
412
|
+
readonly byId: Map<number, RelationInfo>
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function zipClosedPayload(
|
|
416
|
+
relation: RelationInfo,
|
|
417
|
+
handle: string,
|
|
418
|
+
literals: readonly LiteralSpec[]
|
|
419
|
+
): Array<readonly [FieldInfo, LiteralSpec]> {
|
|
420
|
+
const pairs: Array<readonly [FieldInfo, LiteralSpec]> = []
|
|
421
|
+
const pending = literals[Symbol.iterator]()
|
|
422
|
+
let seenId = false
|
|
423
|
+
for (const field of relation.fields) {
|
|
424
|
+
if (!seenId) {
|
|
425
|
+
if (field.name !== "id") {
|
|
426
|
+
refuseShape(`closed relation ${relation.name} has no sealed id field`)
|
|
427
|
+
}
|
|
428
|
+
seenId = true
|
|
429
|
+
continue
|
|
430
|
+
}
|
|
431
|
+
const next = pending.next()
|
|
432
|
+
if (next.done) {
|
|
433
|
+
refuseShape(`closed row ${handle} of ${relation.name}: fewer cells than payload fields`)
|
|
434
|
+
}
|
|
435
|
+
pairs.push([field, next.value])
|
|
436
|
+
}
|
|
437
|
+
if (!seenId) {
|
|
438
|
+
refuseShape(`closed relation ${relation.name} has no sealed id field`)
|
|
439
|
+
}
|
|
440
|
+
if (!pending.next().done) {
|
|
441
|
+
refuseShape(`closed row ${handle} of ${relation.name}: more cells than payload fields`)
|
|
442
|
+
}
|
|
443
|
+
return pairs
|
|
152
444
|
}
|
|
153
445
|
|
|
154
446
|
function fieldsOf(
|
|
@@ -166,8 +458,8 @@ function fieldsOf(
|
|
|
166
458
|
}
|
|
167
459
|
for (const field of relation.fields) {
|
|
168
460
|
let closedRef: string | undefined
|
|
169
|
-
|
|
170
|
-
|
|
461
|
+
const owner = field.newtype === undefined ? undefined : idClassOwner(field.newtype)
|
|
462
|
+
if (owner !== undefined) {
|
|
171
463
|
const target = tables.get(owner)
|
|
172
464
|
if (target?.closed === true) {
|
|
173
465
|
closedRef = owner
|
|
@@ -178,27 +470,25 @@ function fieldsOf(
|
|
|
178
470
|
return fields
|
|
179
471
|
}
|
|
180
472
|
|
|
181
|
-
function resolveLiteral(tables: SpecTables, relation: RelationInfo, field: FieldInfo, literal: LiteralSpec):
|
|
473
|
+
function resolveLiteral(tables: SpecTables, relation: RelationInfo, field: FieldInfo, literal: LiteralSpec): Value {
|
|
182
474
|
if (literal.kind === "value") {
|
|
183
|
-
return
|
|
475
|
+
return rawValue(literal.value)
|
|
184
476
|
}
|
|
185
477
|
if (field.closedRef === undefined) {
|
|
186
|
-
|
|
187
|
-
`handle literal ${literal.handle} on ${relation.name}.${field.name}, which references no closed roster`
|
|
188
|
-
)
|
|
478
|
+
refuseShape(`handle literal ${literal.handle} on ${relation.name}.${field.name}, which references no closed roster`)
|
|
189
479
|
}
|
|
190
480
|
const target = tables.byName.get(field.closedRef)
|
|
191
481
|
if (target === undefined) {
|
|
192
|
-
|
|
482
|
+
refuseShape(`handle literal ${literal.handle}: unknown closed relation ${field.closedRef}`)
|
|
193
483
|
}
|
|
194
484
|
const id = target.handles.indexOf(literal.handle)
|
|
195
485
|
if (id === -1) {
|
|
196
|
-
|
|
486
|
+
refuseShape(`handle literal ${literal.handle} is not in the ${target.name} roster`)
|
|
197
487
|
}
|
|
198
488
|
return BigInt(id)
|
|
199
489
|
}
|
|
200
490
|
|
|
201
|
-
function literalSetOf(tables: SpecTables, relation: RelationInfo, field: FieldInfo, set: LiteralSetSpec):
|
|
491
|
+
function literalSetOf(tables: SpecTables, relation: RelationInfo, field: FieldInfo, set: LiteralSetSpec): Value[] {
|
|
202
492
|
if (set.kind === "one") {
|
|
203
493
|
return [resolveLiteral(tables, relation, field, set.literal)]
|
|
204
494
|
}
|
|
@@ -208,29 +498,19 @@ function literalSetOf(tables: SpecTables, relation: RelationInfo, field: FieldIn
|
|
|
208
498
|
}
|
|
209
499
|
|
|
210
500
|
function fieldOrdinal(relation: RelationInfo, name: string): number {
|
|
211
|
-
|
|
212
|
-
return field.name === name
|
|
213
|
-
})
|
|
214
|
-
if (ordinal === -1) {
|
|
215
|
-
throw errors.new(`relation ${relation.name} has no field ${name}`)
|
|
216
|
-
}
|
|
217
|
-
return ordinal
|
|
501
|
+
return fieldNamed(relation, name).ordinal
|
|
218
502
|
}
|
|
219
503
|
|
|
220
|
-
function
|
|
504
|
+
function specSideOf(tables: SpecTables, side: SideSpec): SideInfo {
|
|
221
505
|
const relation = tables.byName.get(side.relation)
|
|
222
506
|
if (relation === undefined) {
|
|
223
|
-
|
|
507
|
+
refuseShape(`statement cites unknown relation ${side.relation}`)
|
|
224
508
|
}
|
|
225
509
|
const projection = side.projection.map(function ordinalOf(name) {
|
|
226
510
|
return fieldOrdinal(relation, name)
|
|
227
511
|
})
|
|
228
512
|
const selection = side.selection.map(function bindingOf(binding) {
|
|
229
|
-
const ordinal =
|
|
230
|
-
const field = relation.fields[ordinal]
|
|
231
|
-
if (field === undefined) {
|
|
232
|
-
throw errors.new(`relation ${relation.name} has no field ordinal ${ordinal}`)
|
|
233
|
-
}
|
|
513
|
+
const { ordinal, field } = fieldNamed(relation, binding[0])
|
|
234
514
|
return { field: ordinal, values: literalSetOf(tables, relation, field, binding[1]) }
|
|
235
515
|
})
|
|
236
516
|
return { relation: relation.id, projection, selection }
|
|
@@ -238,7 +518,7 @@ function sideOf(tables: SpecTables, side: SideSpec): SideInfo {
|
|
|
238
518
|
|
|
239
519
|
function boundValue(context: string, bound: { readonly kind: string }): bigint {
|
|
240
520
|
if (bound.kind !== "lit") {
|
|
241
|
-
|
|
521
|
+
refuseShape(`${context}: dependent floors are refused by the schema grammar`)
|
|
242
522
|
}
|
|
243
523
|
return (bound as { readonly kind: "lit"; readonly value: bigint }).value
|
|
244
524
|
}
|
|
@@ -248,12 +528,12 @@ function capacityOf(
|
|
|
248
528
|
id: number,
|
|
249
529
|
statement: Extract<StatementSpec, { kind: "capacity" }>
|
|
250
530
|
): StatementInfo {
|
|
251
|
-
const target =
|
|
252
|
-
const source =
|
|
531
|
+
const target = specSideOf(tables, statement.target)
|
|
532
|
+
const source = specSideOf(tables, statement.source)
|
|
253
533
|
const sourceRelation = tables.byName.get(statement.source.relation)
|
|
254
534
|
const targetRelation = tables.byName.get(statement.target.relation)
|
|
255
535
|
if (sourceRelation === undefined || targetRelation === undefined) {
|
|
256
|
-
|
|
536
|
+
refuseShape("capacity statement cites unknown relations")
|
|
257
537
|
}
|
|
258
538
|
let weight: WeightInfo
|
|
259
539
|
switch (statement.weight.kind) {
|
|
@@ -306,56 +586,62 @@ function capacityOf(
|
|
|
306
586
|
return { id, kind: "capacity", target, weight, lo, hi, source }
|
|
307
587
|
}
|
|
308
588
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
function parseSpec(spec: SchemaSpec): LogDescriptor {
|
|
589
|
+
function assembleFromSpec(spec: SchemaSpec): Descriptor {
|
|
312
590
|
const prepass = new Map<string, { closed: boolean; handles: readonly string[] }>()
|
|
313
591
|
for (const relation of spec.relations) {
|
|
592
|
+
if (prepass.has(relation.name)) {
|
|
593
|
+
refuseShape(`duplicate relation ${relation.name}`)
|
|
594
|
+
}
|
|
314
595
|
prepass.set(relation.name, {
|
|
315
596
|
closed: relation.closed !== undefined,
|
|
316
597
|
handles: relation.closed === undefined ? [] : relation.closed.rows.map((row) => row.handle)
|
|
317
598
|
})
|
|
318
599
|
}
|
|
319
600
|
|
|
320
|
-
const relations: RelationInfo[] = []
|
|
321
601
|
const byName = new Map<string, RelationInfo>()
|
|
322
|
-
|
|
323
|
-
|
|
602
|
+
const byId = new Map<number, RelationInfo>()
|
|
603
|
+
let nextId = 0
|
|
604
|
+
for (const relation of spec.relations) {
|
|
605
|
+
const table = prepass.get(relation.name)
|
|
606
|
+
if (table === undefined) {
|
|
607
|
+
refuseShape(`relation ${relation.name} missing from the closedness table`)
|
|
608
|
+
}
|
|
324
609
|
const info: RelationInfo = {
|
|
325
|
-
id,
|
|
610
|
+
id: nextId,
|
|
326
611
|
name: relation.name,
|
|
327
|
-
closed:
|
|
328
|
-
handles:
|
|
329
|
-
fields,
|
|
612
|
+
closed: table.closed,
|
|
613
|
+
handles: table.handles,
|
|
614
|
+
fields: fieldsOf(prepass, relation),
|
|
330
615
|
rows: []
|
|
331
616
|
}
|
|
332
|
-
|
|
617
|
+
nextId += 1
|
|
333
618
|
byName.set(relation.name, info)
|
|
334
|
-
|
|
335
|
-
|
|
619
|
+
byId.set(info.id, info)
|
|
620
|
+
}
|
|
336
621
|
|
|
337
|
-
spec
|
|
622
|
+
const tables: SpecTables = { spec, byName, byId }
|
|
623
|
+
|
|
624
|
+
for (const relation of spec.relations) {
|
|
338
625
|
if (relation.closed === undefined) {
|
|
339
|
-
|
|
626
|
+
continue
|
|
340
627
|
}
|
|
341
|
-
const info =
|
|
628
|
+
const info = byName.get(relation.name)
|
|
342
629
|
if (info === undefined) {
|
|
343
|
-
|
|
630
|
+
refuseShape(`closed relation ${relation.name} missing`)
|
|
344
631
|
}
|
|
345
632
|
const rows = relation.closed.rows.map(function resolveRow(row, rowId) {
|
|
346
|
-
const values:
|
|
347
|
-
|
|
348
|
-
const field = info.fields[column + 1]
|
|
349
|
-
if (field === undefined) {
|
|
350
|
-
throw errors.new(`closed row ${row.handle} of ${relation.name}: no field at column ${column}`)
|
|
351
|
-
}
|
|
633
|
+
const values: Value[] = [BigInt(rowId)]
|
|
634
|
+
for (const [field, literal] of zipClosedPayload(info, row.handle, row.values)) {
|
|
352
635
|
values.push(resolveLiteral(tables, info, field, literal))
|
|
353
|
-
}
|
|
636
|
+
}
|
|
354
637
|
return values
|
|
355
638
|
})
|
|
356
|
-
|
|
357
|
-
byName.set(relation.name,
|
|
358
|
-
|
|
639
|
+
const updated = { ...info, rows }
|
|
640
|
+
byName.set(relation.name, updated)
|
|
641
|
+
byId.set(info.id, updated)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const relations = [...byId.values()]
|
|
359
645
|
|
|
360
646
|
const statements: StatementInfo[] = []
|
|
361
647
|
relations.forEach(function freshKeys(relation) {
|
|
@@ -375,7 +661,7 @@ function parseSpec(spec: SchemaSpec): LogDescriptor {
|
|
|
375
661
|
case "fd": {
|
|
376
662
|
const relation = byName.get(statement.relation)
|
|
377
663
|
if (relation === undefined) {
|
|
378
|
-
|
|
664
|
+
refuseShape(`key statement cites unknown relation ${statement.relation}`)
|
|
379
665
|
}
|
|
380
666
|
statements.push({
|
|
381
667
|
id: statements.length,
|
|
@@ -388,8 +674,8 @@ function parseSpec(spec: SchemaSpec): LogDescriptor {
|
|
|
388
674
|
break
|
|
389
675
|
}
|
|
390
676
|
case "containment": {
|
|
391
|
-
const source =
|
|
392
|
-
const target =
|
|
677
|
+
const source = specSideOf(tables, statement.source)
|
|
678
|
+
const target = specSideOf(tables, statement.target)
|
|
393
679
|
statements.push({ id: statements.length, kind: "containment", source, target })
|
|
394
680
|
if (statement.bidirectional) {
|
|
395
681
|
statements.push({ id: statements.length, kind: "containment", source: target, target: source })
|
|
@@ -403,78 +689,28 @@ function parseSpec(spec: SchemaSpec): LogDescriptor {
|
|
|
403
689
|
}
|
|
404
690
|
}
|
|
405
691
|
|
|
406
|
-
const { braidOfRelation, braidMembers } = deriveBraids(
|
|
407
|
-
|
|
408
|
-
const serialAtStatements: SerialStatement[] = []
|
|
409
|
-
for (const statement of statements) {
|
|
410
|
-
if (statement.kind === "functionality" && statement.projection.length === 0) {
|
|
411
|
-
const braid = braidOfRelation.get(statement.relation)
|
|
412
|
-
if (braid !== undefined) {
|
|
413
|
-
serialAtStatements.push({ statement: statement.id, braid })
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
if (statement.kind === "capacity" && statement.target.projection.length === 0) {
|
|
417
|
-
const targetRelation = relations[statement.target.relation]
|
|
418
|
-
if (targetRelation !== undefined && !targetRelation.closed) {
|
|
419
|
-
const braid = braidOfRelation.get(statement.target.relation)
|
|
420
|
-
if (braid !== undefined) {
|
|
421
|
-
serialAtStatements.push({ statement: statement.id, braid })
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
let hashed: { readonly hex: string; readonly bytes: Uint8Array } | undefined
|
|
428
|
-
function fingerprintLazily(): { readonly hex: string; readonly bytes: Uint8Array } {
|
|
429
|
-
if (hashed === undefined) {
|
|
430
|
-
const bytes = fingerprintOf(relations, statements)
|
|
431
|
-
hashed = { hex: toHex(bytes), bytes }
|
|
432
|
-
}
|
|
433
|
-
return hashed
|
|
434
|
-
}
|
|
692
|
+
const { braidOfRelation, braidMembers } = deriveBraids(byId, statements)
|
|
693
|
+
const serialAtStatements = serialAtOf(byId, statements, braidOfRelation)
|
|
435
694
|
|
|
436
|
-
const
|
|
695
|
+
const fingerprint = "00".repeat(32)
|
|
696
|
+
return {
|
|
437
697
|
relations,
|
|
438
698
|
relationByName: byName,
|
|
439
699
|
statements,
|
|
440
700
|
braidOfRelation,
|
|
441
701
|
braidMembers,
|
|
442
702
|
serialAtStatements,
|
|
443
|
-
get fingerprint() {
|
|
444
|
-
return fingerprintLazily().hex
|
|
445
|
-
},
|
|
446
|
-
get fingerprintBytes() {
|
|
447
|
-
return fingerprintLazily().bytes
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
return descriptor
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* The same descriptor under a pinned fingerprint — for stores whose
|
|
455
|
-
* identity is carried (a manifest, a conformance sidecar) rather than
|
|
456
|
-
* recomputed, e.g. when the mirror cannot hash a closed relation's
|
|
457
|
-
* interned string axioms.
|
|
458
|
-
*/
|
|
459
|
-
function withFingerprint(descriptor: LogDescriptor, fingerprint: string): LogDescriptor {
|
|
460
|
-
return {
|
|
461
|
-
relations: descriptor.relations,
|
|
462
|
-
relationByName: descriptor.relationByName,
|
|
463
|
-
statements: descriptor.statements,
|
|
464
|
-
braidOfRelation: descriptor.braidOfRelation,
|
|
465
|
-
braidMembers: descriptor.braidMembers,
|
|
466
|
-
serialAtStatements: descriptor.serialAtStatements,
|
|
467
703
|
fingerprint,
|
|
468
704
|
fingerprintBytes: fromHex(fingerprint)
|
|
469
705
|
}
|
|
470
706
|
}
|
|
471
707
|
|
|
472
708
|
function deriveBraids(
|
|
473
|
-
|
|
709
|
+
byId: ReadonlyMap<number, RelationInfo>,
|
|
474
710
|
statements: readonly StatementInfo[]
|
|
475
|
-
): { braidOfRelation: Map<number,
|
|
711
|
+
): { braidOfRelation: Map<number, Braid>; braidMembers: Map<Braid, readonly number[]> } {
|
|
476
712
|
const parent = new Map<number, number>()
|
|
477
|
-
for (const relation of
|
|
713
|
+
for (const relation of byId.values()) {
|
|
478
714
|
if (!relation.closed) {
|
|
479
715
|
parent.set(relation.id, relation.id)
|
|
480
716
|
}
|
|
@@ -500,9 +736,9 @@ function deriveBraids(
|
|
|
500
736
|
if (statement.kind === "functionality") {
|
|
501
737
|
continue
|
|
502
738
|
}
|
|
503
|
-
const source =
|
|
504
|
-
const target =
|
|
505
|
-
if (source
|
|
739
|
+
const source = relationOfId(byId, statement.source.relation)
|
|
740
|
+
const target = relationOfId(byId, statement.target.relation)
|
|
741
|
+
if (source.closed || target.closed) {
|
|
506
742
|
continue
|
|
507
743
|
}
|
|
508
744
|
union(source.id, target.id)
|
|
@@ -517,8 +753,8 @@ function deriveBraids(
|
|
|
517
753
|
list.push(id)
|
|
518
754
|
}
|
|
519
755
|
}
|
|
520
|
-
const braidOfRelation = new Map<number,
|
|
521
|
-
const braidMembers = new Map<
|
|
756
|
+
const braidOfRelation = new Map<number, Braid>()
|
|
757
|
+
const braidMembers = new Map<Braid, readonly number[]>()
|
|
522
758
|
for (const [root, ids] of [...members.entries()].sort(function byRoot(a, b) {
|
|
523
759
|
return a[0] - b[0]
|
|
524
760
|
})) {
|
|
@@ -534,208 +770,16 @@ function deriveBraids(
|
|
|
534
770
|
return { braidOfRelation, braidMembers }
|
|
535
771
|
}
|
|
536
772
|
|
|
537
|
-
/** `bumbledb-schema-v5` canonical bytes, mirrored from the engine's own encoder. */
|
|
538
|
-
const FORMAT_VERSION_LABEL = "bumbledb-schema-v5"
|
|
539
|
-
|
|
540
|
-
const VALUE_TYPE_TAG = { bool: 0, u64: 2, i64: 3, string: 4, fixedBytes: 5, interval: 6, fixedInterval: 7 } as const
|
|
541
|
-
|
|
542
|
-
function putLen(out: ByteWriter, len: number): void {
|
|
543
|
-
out.u32le(len)
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function putBytes(out: ByteWriter, raw: Uint8Array): void {
|
|
547
|
-
putLen(out, raw.length)
|
|
548
|
-
out.bytes(raw)
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
function putString(out: ByteWriter, text: string): void {
|
|
552
|
-
putBytes(out, utf8Encoder.encode(text))
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function putValueType(out: ByteWriter, type: ValueTypeSpec): void {
|
|
556
|
-
switch (type.kind) {
|
|
557
|
-
case "bool": {
|
|
558
|
-
out.u8(VALUE_TYPE_TAG.bool)
|
|
559
|
-
return
|
|
560
|
-
}
|
|
561
|
-
case "u64": {
|
|
562
|
-
out.u8(VALUE_TYPE_TAG.u64)
|
|
563
|
-
return
|
|
564
|
-
}
|
|
565
|
-
case "i64": {
|
|
566
|
-
out.u8(VALUE_TYPE_TAG.i64)
|
|
567
|
-
return
|
|
568
|
-
}
|
|
569
|
-
case "string": {
|
|
570
|
-
out.u8(VALUE_TYPE_TAG.string)
|
|
571
|
-
return
|
|
572
|
-
}
|
|
573
|
-
case "fixedBytes": {
|
|
574
|
-
out.u8(VALUE_TYPE_TAG.fixedBytes)
|
|
575
|
-
out.u16le(type.len)
|
|
576
|
-
return
|
|
577
|
-
}
|
|
578
|
-
case "interval": {
|
|
579
|
-
if (type.width === undefined) {
|
|
580
|
-
out.u8(VALUE_TYPE_TAG.interval)
|
|
581
|
-
out.u8(type.element === "u64" ? 0 : 1)
|
|
582
|
-
return
|
|
583
|
-
}
|
|
584
|
-
out.u8(VALUE_TYPE_TAG.fixedInterval)
|
|
585
|
-
out.u8(type.element === "u64" ? 0 : 1)
|
|
586
|
-
out.u64le(type.width)
|
|
587
|
-
return
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function putSide(out: ByteWriter, relations: readonly RelationInfo[], side: SideInfo): void {
|
|
593
|
-
out.u32le(side.relation)
|
|
594
|
-
putLen(out, side.projection.length)
|
|
595
|
-
for (const field of side.projection) {
|
|
596
|
-
out.u16le(field)
|
|
597
|
-
}
|
|
598
|
-
putLen(out, side.selection.length)
|
|
599
|
-
const relation = relations[side.relation]
|
|
600
|
-
if (relation === undefined) {
|
|
601
|
-
throw errors.new(`side cites unknown relation id ${side.relation}`)
|
|
602
|
-
}
|
|
603
|
-
for (const binding of side.selection) {
|
|
604
|
-
out.u16le(binding.field)
|
|
605
|
-
const field = relation.fields[binding.field]
|
|
606
|
-
if (field === undefined) {
|
|
607
|
-
throw errors.new(`selection cites unknown field ordinal ${binding.field}`)
|
|
608
|
-
}
|
|
609
|
-
putLen(out, binding.values.length)
|
|
610
|
-
for (const value of binding.values) {
|
|
611
|
-
if (typeof value === "string") {
|
|
612
|
-
putString(out, value)
|
|
613
|
-
} else {
|
|
614
|
-
writeCanonicalLiteral(out, field.type, value)
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
function fingerprintOf(relations: readonly RelationInfo[], statements: readonly StatementInfo[]): Uint8Array {
|
|
621
|
-
const out = new ByteWriter(1024)
|
|
622
|
-
putString(out, FORMAT_VERSION_LABEL)
|
|
623
|
-
putLen(out, relations.length)
|
|
624
|
-
for (const relation of relations) {
|
|
625
|
-
putString(out, relation.name)
|
|
626
|
-
putLen(out, relation.fields.length)
|
|
627
|
-
for (const field of relation.fields) {
|
|
628
|
-
putString(out, field.name)
|
|
629
|
-
putValueType(out, field.type)
|
|
630
|
-
out.u8(field.fresh ? 1 : 0)
|
|
631
|
-
}
|
|
632
|
-
if (!relation.closed) {
|
|
633
|
-
out.u8(0)
|
|
634
|
-
} else {
|
|
635
|
-
out.u8(1)
|
|
636
|
-
putLen(out, relation.rows.length)
|
|
637
|
-
relation.rows.forEach(function putRow(row, rowId) {
|
|
638
|
-
const handle = relation.handles[rowId]
|
|
639
|
-
if (handle === undefined) {
|
|
640
|
-
throw errors.new(`closed relation ${relation.name}: no handle for row ${rowId}`)
|
|
641
|
-
}
|
|
642
|
-
putString(out, handle)
|
|
643
|
-
const fact = new ByteWriter(64)
|
|
644
|
-
row.forEach(function putCell(value, ordinal) {
|
|
645
|
-
const field = relation.fields[ordinal]
|
|
646
|
-
if (field === undefined) {
|
|
647
|
-
throw errors.new(`closed relation ${relation.name}: no field at ordinal ${ordinal}`)
|
|
648
|
-
}
|
|
649
|
-
if (field.type.kind === "string") {
|
|
650
|
-
throw errors.new(
|
|
651
|
-
`closed relation ${relation.name} has a string ground axiom — the fingerprint mirror does not carry interned axiom columns`
|
|
652
|
-
)
|
|
653
|
-
}
|
|
654
|
-
writeCanonicalLiteral(fact, field.type, value)
|
|
655
|
-
})
|
|
656
|
-
putBytes(out, fact.finish())
|
|
657
|
-
})
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
putLen(out, statements.length)
|
|
661
|
-
for (const statement of statements) {
|
|
662
|
-
switch (statement.kind) {
|
|
663
|
-
case "functionality": {
|
|
664
|
-
out.u8(0)
|
|
665
|
-
out.u32le(statement.relation)
|
|
666
|
-
putLen(out, statement.projection.length)
|
|
667
|
-
for (const field of statement.projection) {
|
|
668
|
-
out.u16le(field)
|
|
669
|
-
}
|
|
670
|
-
break
|
|
671
|
-
}
|
|
672
|
-
case "containment": {
|
|
673
|
-
out.u8(1)
|
|
674
|
-
putSide(out, relations, statement.source)
|
|
675
|
-
putSide(out, relations, statement.target)
|
|
676
|
-
break
|
|
677
|
-
}
|
|
678
|
-
case "capacity": {
|
|
679
|
-
out.u8(4)
|
|
680
|
-
putSide(out, relations, statement.target)
|
|
681
|
-
switch (statement.weight.kind) {
|
|
682
|
-
case "unit": {
|
|
683
|
-
out.u8(0)
|
|
684
|
-
break
|
|
685
|
-
}
|
|
686
|
-
case "field": {
|
|
687
|
-
out.u8(1)
|
|
688
|
-
out.u16le(statement.weight.field)
|
|
689
|
-
break
|
|
690
|
-
}
|
|
691
|
-
case "duration": {
|
|
692
|
-
out.u8(2)
|
|
693
|
-
out.u16le(statement.weight.field)
|
|
694
|
-
break
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
out.u64le(statement.lo)
|
|
698
|
-
switch (statement.hi.kind) {
|
|
699
|
-
case "unbounded": {
|
|
700
|
-
out.u8(0)
|
|
701
|
-
break
|
|
702
|
-
}
|
|
703
|
-
case "lit": {
|
|
704
|
-
out.u8(1)
|
|
705
|
-
out.u8(0)
|
|
706
|
-
out.u64le(statement.hi.value)
|
|
707
|
-
break
|
|
708
|
-
}
|
|
709
|
-
case "targetField": {
|
|
710
|
-
out.u8(1)
|
|
711
|
-
out.u8(1)
|
|
712
|
-
out.u16le(statement.hi.field)
|
|
713
|
-
break
|
|
714
|
-
}
|
|
715
|
-
case "targetDuration": {
|
|
716
|
-
out.u8(1)
|
|
717
|
-
out.u8(2)
|
|
718
|
-
out.u16le(statement.hi.field)
|
|
719
|
-
break
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
putSide(out, relations, statement.source)
|
|
723
|
-
break
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
return new Uint8Array(internalBlake3(out.finish()))
|
|
728
|
-
}
|
|
729
|
-
|
|
730
773
|
export type {
|
|
774
|
+
Braid,
|
|
775
|
+
Descriptor,
|
|
731
776
|
FieldInfo,
|
|
732
777
|
HiInfo,
|
|
733
|
-
LogDescriptor,
|
|
734
|
-
LogTheory,
|
|
735
778
|
RelationInfo,
|
|
736
779
|
SerialStatement,
|
|
737
780
|
SideInfo,
|
|
738
781
|
StatementInfo,
|
|
782
|
+
Theory,
|
|
739
783
|
WeightInfo
|
|
740
784
|
}
|
|
741
|
-
export { braidHex, descriptorOf, withFingerprint }
|
|
785
|
+
export { assembleFromSpec, braid, braidHex, descriptorOf, withFingerprint }
|