@bjornpagen/bumbledb 0.20.2 → 1.0.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 +283 -1611
- package/README.md +156 -118
- package/dist/capacity.d.ts +16 -12
- package/dist/capacity.d.ts.map +1 -1
- package/dist/capacity.js +16 -48
- package/dist/capacity.js.map +1 -1
- package/dist/changes.d.ts +54 -0
- package/dist/changes.d.ts.map +1 -0
- package/dist/changes.js +245 -0
- package/dist/changes.js.map +1 -0
- package/dist/close.d.ts +24 -0
- package/dist/close.d.ts.map +1 -0
- package/dist/close.js +34 -0
- package/dist/close.js.map +1 -0
- package/dist/closed.d.ts +1 -3
- package/dist/closed.d.ts.map +1 -1
- package/dist/closed.js +24 -12
- package/dist/closed.js.map +1 -1
- package/dist/codec.d.ts +56 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +289 -0
- package/dist/codec.js.map +1 -0
- package/dist/compile.d.ts +68 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +75 -0
- package/dist/compile.js.map +1 -0
- package/dist/db-native.d.ts +170 -0
- package/dist/db-native.d.ts.map +1 -0
- package/dist/db-native.js +5 -0
- package/dist/db-native.js.map +1 -0
- package/dist/db.d.ts +101 -233
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +232 -1084
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +57 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +32 -0
- package/dist/errors.js.map +1 -0
- package/dist/face.d.ts.map +1 -1
- package/dist/face.js +2 -2
- package/dist/face.js.map +1 -1
- package/dist/fields.d.ts +65 -24
- package/dist/fields.d.ts.map +1 -1
- package/dist/fields.js +94 -24
- package/dist/fields.js.map +1 -1
- package/dist/index.d.ts +37 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -21
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +24 -0
- package/dist/internal/log.d.ts.map +1 -0
- package/dist/internal/log.js +10 -0
- package/dist/internal/log.js.map +1 -0
- package/dist/law.d.ts +8 -13
- package/dist/law.d.ts.map +1 -1
- package/dist/law.js +6 -59
- package/dist/law.js.map +1 -1
- package/dist/lower.d.ts +0 -13
- package/dist/lower.d.ts.map +1 -1
- package/dist/lower.js +9 -17
- package/dist/lower.js.map +1 -1
- package/dist/migration.d.ts +33 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +48 -0
- package/dist/migration.js.map +1 -0
- package/dist/native.d.ts +256 -301
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +65 -108
- package/dist/native.js.map +1 -1
- package/dist/query/atom.d.ts +24 -13
- package/dist/query/atom.d.ts.map +1 -1
- package/dist/query/atom.js +7 -3
- package/dist/query/atom.js.map +1 -1
- package/dist/query/compute.d.ts +68 -0
- package/dist/query/compute.d.ts.map +1 -0
- package/dist/query/compute.js +141 -0
- package/dist/query/compute.js.map +1 -0
- package/dist/query/find.d.ts +15 -8
- package/dist/query/find.d.ts.map +1 -1
- package/dist/query/find.js +6 -2
- package/dist/query/find.js.map +1 -1
- package/dist/query/lower.d.ts +23 -2
- package/dist/query/lower.d.ts.map +1 -1
- package/dist/query/lower.js +433 -80
- package/dist/query/lower.js.map +1 -1
- package/dist/query/parse-ir.d.ts.map +1 -1
- package/dist/query/parse-ir.js +17 -8
- package/dist/query/parse-ir.js.map +1 -1
- package/dist/query/run.d.ts +12 -2
- package/dist/query/run.d.ts.map +1 -1
- package/dist/query/run.js +26 -9
- package/dist/query/run.js.map +1 -1
- package/dist/query/scope.d.ts +47 -3
- package/dist/query/scope.d.ts.map +1 -1
- package/dist/query/scope.js +83 -19
- package/dist/query/scope.js.map +1 -1
- package/dist/relation.d.ts +3 -8
- package/dist/relation.d.ts.map +1 -1
- package/dist/relation.js +16 -10
- package/dist/relation.js.map +1 -1
- package/dist/result.d.ts +47 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +50 -0
- package/dist/result.js.map +1 -0
- package/dist/rows.d.ts +69 -0
- package/dist/rows.d.ts.map +1 -0
- package/dist/rows.js +328 -0
- package/dist/rows.js.map +1 -0
- package/dist/runtime-codes.d.ts +2 -0
- package/dist/runtime-codes.d.ts.map +1 -0
- package/dist/runtime-codes.js +19 -0
- package/dist/runtime-codes.js.map +1 -0
- package/dist/runtime-errors.d.ts +106 -0
- package/dist/runtime-errors.d.ts.map +1 -0
- package/dist/runtime-errors.js +62 -0
- package/dist/runtime-errors.js.map +1 -0
- package/dist/runtime-native.d.ts +171 -0
- package/dist/runtime-native.d.ts.map +1 -0
- package/dist/runtime-native.js +5 -0
- package/dist/runtime-native.js.map +1 -0
- package/dist/runtime.d.ts +73 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +293 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scalar.d.ts +165 -0
- package/dist/scalar.d.ts.map +1 -0
- package/dist/scalar.js +344 -0
- package/dist/scalar.js.map +1 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +34 -37
- package/dist/schema.js.map +1 -1
- package/dist/shape.d.ts +28 -0
- package/dist/shape.d.ts.map +1 -0
- package/dist/shape.js +2 -0
- package/dist/shape.js.map +1 -0
- package/dist/spec.d.ts +27 -3
- package/dist/spec.d.ts.map +1 -1
- package/dist/spec.js +22 -1
- package/dist/spec.js.map +1 -1
- package/dist/statements.d.ts +16 -3
- package/dist/statements.d.ts.map +1 -1
- package/dist/statements.js +54 -28
- package/dist/statements.js.map +1 -1
- package/dist/uuid.d.ts +23 -0
- package/dist/uuid.d.ts.map +1 -0
- package/dist/uuid.js +54 -0
- package/dist/uuid.js.map +1 -0
- package/pack-provenance.json +7 -0
- package/package.json +12 -16
- package/src/capacity.ts +34 -54
- package/src/changes.ts +361 -0
- package/src/close.ts +53 -0
- package/src/closed.ts +25 -19
- package/src/codec.ts +379 -0
- package/src/compile.ts +149 -0
- package/src/db-native.ts +247 -0
- package/src/db.ts +461 -1632
- package/src/errors.ts +50 -0
- package/src/face.ts +2 -2
- package/src/fields.ts +182 -55
- package/src/index.ts +62 -126
- package/src/internal/log.ts +51 -0
- package/src/law.ts +13 -28
- package/src/lower.ts +9 -4
- package/src/migration.ts +69 -0
- package/src/native.ts +326 -487
- package/src/query/atom.ts +61 -25
- package/src/query/compute.ts +237 -0
- package/src/query/find.ts +31 -12
- package/src/query/lower.ts +531 -143
- package/src/query/parse-ir.ts +18 -13
- package/src/query/run.ts +32 -16
- package/src/query/scope.ts +140 -8
- package/src/relation.ts +17 -21
- package/src/result.ts +130 -0
- package/src/rows.ts +404 -0
- package/src/runtime-codes.ts +18 -0
- package/src/runtime-errors.ts +69 -0
- package/src/runtime-native.ts +242 -0
- package/src/runtime.ts +440 -0
- package/src/scalar.ts +560 -0
- package/src/schema.ts +35 -37
- package/src/shape.ts +31 -0
- package/src/spec.ts +33 -3
- package/src/statements.ts +75 -68
- package/src/uuid.ts +69 -0
- package/dist/marshal.d.ts +0 -44
- package/dist/marshal.d.ts.map +0 -1
- package/dist/marshal.js +0 -165
- package/dist/marshal.js.map +0 -1
- package/src/marshal.ts +0 -200
package/src/rows.ts
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { AuthoringError, SdkInvariantError } from "#errors.ts"
|
|
2
|
+
/**
|
|
3
|
+
* The successor row codec: fact object ⇄ positional cell array by field
|
|
4
|
+
* ordinal, schema-directed, in ONE place. The write side lowers named host
|
|
5
|
+
* objects to rows in the relation's field-declaration order (declaration
|
|
6
|
+
* order = ordinal ids); the read side decodes owned rows back to named
|
|
7
|
+
* objects of BARE structural values. This module is the one fact⇄row
|
|
8
|
+
* projector. It covers the COMPLETE successor value roster: bool, u64, i64,
|
|
9
|
+
* f64, uuid, str, bytes<N>, discrete intervals and dense float intervals,
|
|
10
|
+
* plus the closed-handle bijection (name ⇄ declaration-order row id).
|
|
11
|
+
*
|
|
12
|
+
* Cells here are OWNED plain host values; the native boundary re-judges
|
|
13
|
+
* every crossing against its resident sealed roster. Shape misuse throws
|
|
14
|
+
* the pure {@link AuthoringError}; the Effect ingestion boundary catches
|
|
15
|
+
* and types it (never an untracked partial draft). SharedArrayBuffer-backed
|
|
16
|
+
* views are refused before any copy. Host length is judged before string
|
|
17
|
+
* scans and byte copies (TS-004).
|
|
18
|
+
*/
|
|
19
|
+
import type { AnyClosedRoster, AnyField } from "#fields.ts"
|
|
20
|
+
import { isFloatIntervalValue, isIntervalValue, literalShapeError, rosterOf } from "#fields.ts"
|
|
21
|
+
import type { AnyRelation, Fact, RelationData } from "#relation.ts"
|
|
22
|
+
import { Uuid } from "#uuid.ts"
|
|
23
|
+
|
|
24
|
+
/** Converter-granularity host cell wall (64 KiB). One oversize cell refuses. */
|
|
25
|
+
const HOST_CELL_MAX = 65536n
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* One owned cell at the private bridge boundary. The declared sealed field
|
|
29
|
+
* type disambiguates the union: `string` is text, an `uuid` in its
|
|
30
|
+
* canonical hyphenated UUID spelling (chapter 35), or a closed handle
|
|
31
|
+
* before lowering (closed handles cross as `bigint` row ids),
|
|
32
|
+
* `Uint8Array` is `bytes<N>`, `{ start, end }` bigints are a discrete
|
|
33
|
+
* interval and numbers a dense float interval.
|
|
34
|
+
*/
|
|
35
|
+
type CellValue =
|
|
36
|
+
| boolean
|
|
37
|
+
| bigint
|
|
38
|
+
| number
|
|
39
|
+
| string
|
|
40
|
+
| Uint8Array
|
|
41
|
+
| { readonly start: bigint; readonly end: bigint }
|
|
42
|
+
| { readonly start: number; readonly end: number }
|
|
43
|
+
|
|
44
|
+
interface FlatRows {
|
|
45
|
+
readonly rows: bigint
|
|
46
|
+
readonly cells: readonly CellValue[]
|
|
47
|
+
/** Conservative charged byte size of the owned cells (input accounting). */
|
|
48
|
+
readonly bytes: bigint
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function recordOf(fact: object): Readonly<Record<string, unknown>> {
|
|
52
|
+
if (typeof fact !== "object" && typeof fact !== "function") {
|
|
53
|
+
throw new AuthoringError({ message: "fact object is not string-indexable" })
|
|
54
|
+
}
|
|
55
|
+
return fact as Readonly<Record<string, unknown>>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function refuseShared(context: string, value: Uint8Array): void {
|
|
59
|
+
if (!(value.buffer instanceof ArrayBuffer)) {
|
|
60
|
+
throw new AuthoringError({
|
|
61
|
+
message: `${context}: SharedArrayBuffer-backed views are refused — make a synchronized stable copy into ordinary unshared input first`
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Cheap host charge before any string scan or byte copy (TS-004). UTF-16
|
|
68
|
+
* code units × 2 upper-bounds UTF-8; byte views charge their length.
|
|
69
|
+
*/
|
|
70
|
+
function hostCellCharge(value: unknown): bigint {
|
|
71
|
+
if (typeof value === "string") {
|
|
72
|
+
return BigInt(value.length) * 2n
|
|
73
|
+
}
|
|
74
|
+
if (value instanceof Uint8Array) {
|
|
75
|
+
return BigInt(value.byteLength)
|
|
76
|
+
}
|
|
77
|
+
if (typeof value === "boolean") {
|
|
78
|
+
return 1n
|
|
79
|
+
}
|
|
80
|
+
if (typeof value === "bigint" || typeof value === "number") {
|
|
81
|
+
return 8n
|
|
82
|
+
}
|
|
83
|
+
if (value !== null && typeof value === "object" && "start" in value && "end" in value) {
|
|
84
|
+
return 16n
|
|
85
|
+
}
|
|
86
|
+
return 0n
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function assertHostCellFits(context: string, value: unknown, limit: bigint): bigint {
|
|
90
|
+
const charge = hostCellCharge(value)
|
|
91
|
+
if (charge > limit) {
|
|
92
|
+
throw new AuthoringError({
|
|
93
|
+
message: `${context}: host cell of ${charge} bytes exceeds the ${limit}-byte converter bound — refuse before copy or scan`
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
return charge
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The write half of the closed bijection: one handle NAME to its u64 row
|
|
101
|
+
* id (declaration order = row ids). An unknown name is a pointed refusal
|
|
102
|
+
* naming the vocabulary and its roster.
|
|
103
|
+
*/
|
|
104
|
+
function closedCellOf(context: string, closed: AnyClosedRoster, name: string): CellValue {
|
|
105
|
+
const id = closed.handles.indexOf(name)
|
|
106
|
+
if (id === -1) {
|
|
107
|
+
throw new AuthoringError({
|
|
108
|
+
message: `${context}: "${name}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
return BigInt(id)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function handleOf(context: string, closed: AnyClosedRoster, cell: unknown): string {
|
|
115
|
+
if (typeof cell !== "bigint") {
|
|
116
|
+
throw literalShapeError(context, `a ${closed.name} handle id (bigint)`, cell)
|
|
117
|
+
}
|
|
118
|
+
const handle = closed.handles[Number(cell)]
|
|
119
|
+
if (handle === undefined) {
|
|
120
|
+
throw new AuthoringError({
|
|
121
|
+
message: `${context}: id ${cell} is outside the ${closed.name} roster (${closed.handles.join(", ")})`
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
return handle
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Conservative owned-byte charge of one cell (host accounting, not wire). */
|
|
128
|
+
function cellBytes(cell: CellValue): bigint {
|
|
129
|
+
if (typeof cell === "boolean") {
|
|
130
|
+
return 1n
|
|
131
|
+
}
|
|
132
|
+
if (typeof cell === "bigint" || typeof cell === "number") {
|
|
133
|
+
return 8n
|
|
134
|
+
}
|
|
135
|
+
if (typeof cell === "string") {
|
|
136
|
+
// UTF-16 code units × 2 is a cheap safe upper bound before exact
|
|
137
|
+
// UTF-8 conversion is charged natively (chapter 35: reject oversize
|
|
138
|
+
// cells with cheap length bounds before costly conversion).
|
|
139
|
+
return BigInt(cell.length) * 2n
|
|
140
|
+
}
|
|
141
|
+
if (cell instanceof Uint8Array) {
|
|
142
|
+
return BigInt(cell.byteLength)
|
|
143
|
+
}
|
|
144
|
+
return 16n
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function cellOf(context: string, field: AnyField, value: unknown): CellValue {
|
|
148
|
+
const roster = rosterOf(field)
|
|
149
|
+
if (roster !== undefined) {
|
|
150
|
+
if (typeof value !== "string") {
|
|
151
|
+
throw literalShapeError(context, `a ${roster.name} handle name (string)`, value)
|
|
152
|
+
}
|
|
153
|
+
return closedCellOf(context, roster, value)
|
|
154
|
+
}
|
|
155
|
+
switch (field.kind) {
|
|
156
|
+
case "bool": {
|
|
157
|
+
if (typeof value !== "boolean") {
|
|
158
|
+
throw literalShapeError(context, "boolean", value)
|
|
159
|
+
}
|
|
160
|
+
return value
|
|
161
|
+
}
|
|
162
|
+
case "u64":
|
|
163
|
+
case "i64": {
|
|
164
|
+
if (typeof value !== "bigint") {
|
|
165
|
+
throw literalShapeError(context, "bigint", value)
|
|
166
|
+
}
|
|
167
|
+
return value
|
|
168
|
+
}
|
|
169
|
+
case "str": {
|
|
170
|
+
if (typeof value !== "string") {
|
|
171
|
+
throw literalShapeError(context, "string", value)
|
|
172
|
+
}
|
|
173
|
+
assertHostCellFits(context, value, HOST_CELL_MAX)
|
|
174
|
+
if (!value.isWellFormed()) {
|
|
175
|
+
throw literalShapeError(context, "well-formed string", value)
|
|
176
|
+
}
|
|
177
|
+
return value
|
|
178
|
+
}
|
|
179
|
+
case "f64": {
|
|
180
|
+
if (typeof value !== "number") {
|
|
181
|
+
throw literalShapeError(context, "number", value)
|
|
182
|
+
}
|
|
183
|
+
return value
|
|
184
|
+
}
|
|
185
|
+
case "uuid": {
|
|
186
|
+
if (!Uuid.isUuid(value)) {
|
|
187
|
+
throw literalShapeError(context, "a UUID (canonical UUID text)", value)
|
|
188
|
+
}
|
|
189
|
+
// Chapter 35: uuid crosses the bridge as the canonical
|
|
190
|
+
// canonical hyphenated UUID STRING (the native marshal's spelling),
|
|
191
|
+
// never as sixteen raw bytes.
|
|
192
|
+
return value
|
|
193
|
+
}
|
|
194
|
+
case "bytes": {
|
|
195
|
+
if (!(value instanceof Uint8Array)) {
|
|
196
|
+
throw literalShapeError(context, "Uint8Array", value)
|
|
197
|
+
}
|
|
198
|
+
refuseShared(context, value)
|
|
199
|
+
assertHostCellFits(context, value, HOST_CELL_MAX)
|
|
200
|
+
if (value.byteLength !== field.width) {
|
|
201
|
+
throw new AuthoringError({
|
|
202
|
+
message: `${context}: bytes<${field.width}> takes exactly ${field.width} bytes (got ${value.byteLength})`
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
// Owned copy at the ownership boundary: later caller mutation
|
|
206
|
+
// cannot change an accepted cell.
|
|
207
|
+
return Uint8Array.from(value)
|
|
208
|
+
}
|
|
209
|
+
case "interval": {
|
|
210
|
+
if (field.element === "f64") {
|
|
211
|
+
if (!isFloatIntervalValue(value)) {
|
|
212
|
+
throw literalShapeError(context, "float interval ({ start, end } numbers)", value)
|
|
213
|
+
}
|
|
214
|
+
if (Number.isNaN(value.start) || Number.isNaN(value.end)) {
|
|
215
|
+
throw new AuthoringError({ message: `${context}: a float interval endpoint cannot be NaN` })
|
|
216
|
+
}
|
|
217
|
+
const start = Object.is(value.start, -0) ? 0 : value.start
|
|
218
|
+
const end = Object.is(value.end, -0) ? 0 : value.end
|
|
219
|
+
if (!(start < end)) {
|
|
220
|
+
throw new AuthoringError({
|
|
221
|
+
message: `${context}: a float interval is half-open and nonempty (start < end strictly)`
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
return { start, end }
|
|
225
|
+
}
|
|
226
|
+
if (!isIntervalValue(value)) {
|
|
227
|
+
throw literalShapeError(context, "interval ({ start, end } bigints)", value)
|
|
228
|
+
}
|
|
229
|
+
return { start: value.start, end: value.end }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The flat projector: every fact's cells land in ONE row-major cell array
|
|
236
|
+
* (length rows × arity) with the row count and charged byte size counted
|
|
237
|
+
* while projecting. Missing-field refusal and per-cell judgment are
|
|
238
|
+
* {@link cellOf}'s, byte for byte.
|
|
239
|
+
*/
|
|
240
|
+
function flatRowsOf(data: RelationData, facts: Iterable<object>): FlatRows {
|
|
241
|
+
const cells: CellValue[] = []
|
|
242
|
+
let rows = 0n
|
|
243
|
+
let bytes = 0n
|
|
244
|
+
for (const fact of facts) {
|
|
245
|
+
rows += 1n
|
|
246
|
+
const record = recordOf(fact)
|
|
247
|
+
for (const declared of data.fields) {
|
|
248
|
+
const value = record[declared.name]
|
|
249
|
+
if (value === undefined) {
|
|
250
|
+
throw new AuthoringError({ message: `relation ${data.name}: fact is missing field ${declared.name}` })
|
|
251
|
+
}
|
|
252
|
+
const cell = cellOf(`relation ${data.name} field ${declared.name}`, declared.field, value)
|
|
253
|
+
bytes += cellBytes(cell)
|
|
254
|
+
cells.push(cell)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return { rows, cells, bytes }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function keyCellsOf(
|
|
261
|
+
data: RelationData,
|
|
262
|
+
projection: readonly string[],
|
|
263
|
+
key: Readonly<Record<string, unknown>>
|
|
264
|
+
): CellValue[] {
|
|
265
|
+
for (const supplied of Object.keys(key)) {
|
|
266
|
+
if (key[supplied] !== undefined && !projection.includes(supplied)) {
|
|
267
|
+
throw new AuthoringError({
|
|
268
|
+
message: `relation ${data.name}: key object carries field ${supplied} outside the primary key projection (${projection.join(", ")})`
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return projection.map(function marshalKeyCell(fieldName) {
|
|
273
|
+
const declared = data.fields.find(function byName(candidate) {
|
|
274
|
+
return candidate.name === fieldName
|
|
275
|
+
})
|
|
276
|
+
if (declared === undefined) {
|
|
277
|
+
throw new AuthoringError({
|
|
278
|
+
message: `relation ${data.name}: key projection cites unknown field ${fieldName}`
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
const value = key[fieldName]
|
|
282
|
+
if (value === undefined) {
|
|
283
|
+
throw new AuthoringError({
|
|
284
|
+
message: `relation ${data.name}: key object is missing field ${fieldName} — get reads through the primary (first-declared) key, whose projection is (${projection.join(", ")})`
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
return cellOf(`relation ${data.name} key field ${fieldName}`, declared.field, value)
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function decodeCell(context: string, field: AnyField, cell: unknown): unknown {
|
|
292
|
+
const roster = rosterOf(field)
|
|
293
|
+
if (roster !== undefined) {
|
|
294
|
+
return handleOf(context, roster, cell)
|
|
295
|
+
}
|
|
296
|
+
switch (field.kind) {
|
|
297
|
+
case "bool": {
|
|
298
|
+
if (typeof cell !== "boolean") {
|
|
299
|
+
throw new SdkInvariantError({ message: `${context}: expected boolean cell` })
|
|
300
|
+
}
|
|
301
|
+
return cell
|
|
302
|
+
}
|
|
303
|
+
case "u64":
|
|
304
|
+
case "i64": {
|
|
305
|
+
if (typeof cell !== "bigint") {
|
|
306
|
+
throw new SdkInvariantError({ message: `${context}: expected bigint cell` })
|
|
307
|
+
}
|
|
308
|
+
return cell
|
|
309
|
+
}
|
|
310
|
+
case "f64": {
|
|
311
|
+
if (typeof cell !== "number") {
|
|
312
|
+
throw new SdkInvariantError({ message: `${context}: expected number cell` })
|
|
313
|
+
}
|
|
314
|
+
return cell
|
|
315
|
+
}
|
|
316
|
+
case "str": {
|
|
317
|
+
if (typeof cell !== "string") {
|
|
318
|
+
throw new SdkInvariantError({ message: `${context}: expected string cell` })
|
|
319
|
+
}
|
|
320
|
+
return cell
|
|
321
|
+
}
|
|
322
|
+
case "uuid": {
|
|
323
|
+
// The native renderer spells uuid as the canonical
|
|
324
|
+
// canonical hyphenated UUID string (marshal.rs `uuid_hex`); the read
|
|
325
|
+
// side revalidates the spelling and keeps the string value.
|
|
326
|
+
if (!Uuid.isUuid(cell)) {
|
|
327
|
+
throw new SdkInvariantError({
|
|
328
|
+
message: `${context}: expected an uuid cell (canonical UUID text)`
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
return cell
|
|
332
|
+
}
|
|
333
|
+
case "bytes": {
|
|
334
|
+
if (!(cell instanceof Uint8Array)) {
|
|
335
|
+
throw new SdkInvariantError({ message: `${context}: expected owned bytes` })
|
|
336
|
+
}
|
|
337
|
+
return cell
|
|
338
|
+
}
|
|
339
|
+
case "interval": {
|
|
340
|
+
if (field.element === "f64") {
|
|
341
|
+
if (!isFloatIntervalValue(cell)) {
|
|
342
|
+
throw new SdkInvariantError({ message: `${context}: expected a float interval cell` })
|
|
343
|
+
}
|
|
344
|
+
return Object.freeze({ start: cell.start, end: cell.end })
|
|
345
|
+
}
|
|
346
|
+
if (!isIntervalValue(cell)) {
|
|
347
|
+
throw new SdkInvariantError({ message: `${context}: expected an integer interval cell` })
|
|
348
|
+
}
|
|
349
|
+
return Object.freeze({ start: cell.start, end: cell.end })
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function isCompleteFact<R extends AnyRelation>(
|
|
355
|
+
relation: R,
|
|
356
|
+
decoded: Readonly<Record<string, unknown>>
|
|
357
|
+
): decoded is Readonly<Record<string, unknown>> & Fact<R> {
|
|
358
|
+
return relation.data.fields.every(function present(declared) {
|
|
359
|
+
return decoded[declared.name] !== undefined
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* The read half: one owned positional row into a plain frozen record in
|
|
365
|
+
* declared field order — the SAME fields and shapes on every row (chapter
|
|
366
|
+
* 35's stable row shape rule; no Proxy, no per-cell closures).
|
|
367
|
+
*/
|
|
368
|
+
function factOfCells<R extends AnyRelation>(relation: R, row: readonly unknown[]): Fact<R> {
|
|
369
|
+
const data = relation.data
|
|
370
|
+
if (row.length !== data.fields.length) {
|
|
371
|
+
throw new SdkInvariantError({
|
|
372
|
+
message: `relation ${data.name}: row arity ${row.length} does not match the ${data.fields.length} declared fields`
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
const decoded: Record<string, unknown> = {}
|
|
376
|
+
data.fields.forEach(function decodeOne(declared, ordinal) {
|
|
377
|
+
const cell = row[ordinal]
|
|
378
|
+
if (cell === undefined) {
|
|
379
|
+
throw new SdkInvariantError({
|
|
380
|
+
message: `relation ${data.name}: row cell ${ordinal} (${declared.name}) is absent`
|
|
381
|
+
})
|
|
382
|
+
}
|
|
383
|
+
decoded[declared.name] = decodeCell(`relation ${data.name} field ${declared.name}`, declared.field, cell)
|
|
384
|
+
})
|
|
385
|
+
Object.freeze(decoded)
|
|
386
|
+
if (!isCompleteFact(relation, decoded)) {
|
|
387
|
+
throw new SdkInvariantError({ message: `relation ${data.name}: decoded row is not a complete fact` })
|
|
388
|
+
}
|
|
389
|
+
return decoded
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export type { CellValue, FlatRows }
|
|
393
|
+
export {
|
|
394
|
+
assertHostCellFits,
|
|
395
|
+
cellBytes,
|
|
396
|
+
cellOf,
|
|
397
|
+
decodeCell,
|
|
398
|
+
factOfCells,
|
|
399
|
+
flatRowsOf,
|
|
400
|
+
handleOf,
|
|
401
|
+
hostCellCharge,
|
|
402
|
+
keyCellsOf,
|
|
403
|
+
recordOf
|
|
404
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated from the exact native runtimeErrorCodes export; do not edit.
|
|
2
|
+
export const runtimeErrorCodes = [
|
|
3
|
+
"RuntimeAlreadyLive",
|
|
4
|
+
"ForeignRuntime",
|
|
5
|
+
"ClosedHandle",
|
|
6
|
+
"SpentHandle",
|
|
7
|
+
"QueueFull",
|
|
8
|
+
"InvalidArgument",
|
|
9
|
+
"Internal",
|
|
10
|
+
"DirectoryBusy",
|
|
11
|
+
"WriterBusy",
|
|
12
|
+
"InvalidPath",
|
|
13
|
+
"Io",
|
|
14
|
+
"ResourceLimit",
|
|
15
|
+
"Engine",
|
|
16
|
+
"Cancelled",
|
|
17
|
+
"DeadlineExceeded"
|
|
18
|
+
] as const
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Schema } from "effect"
|
|
2
|
+
import { runtimeErrorCodes } from "#runtime-codes.ts"
|
|
3
|
+
|
|
4
|
+
export { runtimeErrorCodes } from "#runtime-codes.ts"
|
|
5
|
+
|
|
6
|
+
const ResourceLimit = Schema.Struct({
|
|
7
|
+
_tag: Schema.Literal("ResourceLimit"),
|
|
8
|
+
dimension: Schema.String,
|
|
9
|
+
used: Schema.BigInt,
|
|
10
|
+
requested: Schema.BigInt,
|
|
11
|
+
limit: Schema.BigInt
|
|
12
|
+
})
|
|
13
|
+
const PlainReason = Schema.Struct({
|
|
14
|
+
_tag: Schema.Literals(
|
|
15
|
+
runtimeErrorCodes.filter((code) => code !== "ResourceLimit" && code !== "Io" && code !== "Engine")
|
|
16
|
+
)
|
|
17
|
+
})
|
|
18
|
+
const Io = Schema.Struct({
|
|
19
|
+
_tag: Schema.Literal("Io"),
|
|
20
|
+
kind: Schema.String,
|
|
21
|
+
osCode: Schema.optional(Schema.Number)
|
|
22
|
+
})
|
|
23
|
+
/** A typed engine refusal crossing the executor: core family tag + message. */
|
|
24
|
+
const Engine = Schema.Struct({
|
|
25
|
+
_tag: Schema.Literal("Engine"),
|
|
26
|
+
kind: Schema.String,
|
|
27
|
+
message: Schema.String
|
|
28
|
+
})
|
|
29
|
+
export const DbReason = Schema.Union([ResourceLimit, Io, Engine, PlainReason])
|
|
30
|
+
export class DbError extends Schema.TaggedError<DbError>()("DbError", {
|
|
31
|
+
operation: Schema.String,
|
|
32
|
+
reason: DbReason
|
|
33
|
+
}) {
|
|
34
|
+
get code() {
|
|
35
|
+
return this.reason._tag
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const decodeReason = Schema.decodeUnknownOption(DbReason)
|
|
40
|
+
export function dbError(operation: string, cause: unknown): DbError {
|
|
41
|
+
const decoded = decodeReason(cause)
|
|
42
|
+
return new DbError({ operation, reason: decoded._tag === "Some" ? decoded.value : { _tag: "Internal" } })
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const Outstanding = Schema.Struct({
|
|
46
|
+
phase: Schema.Literals(["open", "closing", "closed"]),
|
|
47
|
+
queued: Schema.BigInt,
|
|
48
|
+
active: Schema.BigInt,
|
|
49
|
+
retained: Schema.BigInt,
|
|
50
|
+
owners: Schema.BigInt,
|
|
51
|
+
databases: Schema.BigInt,
|
|
52
|
+
natives: Schema.BigInt,
|
|
53
|
+
inputBytes: Schema.BigInt,
|
|
54
|
+
workingBytes: Schema.BigInt,
|
|
55
|
+
scratchBytes: Schema.BigInt,
|
|
56
|
+
resultBytes: Schema.BigInt
|
|
57
|
+
})
|
|
58
|
+
export type OutstandingWork = typeof Outstanding.Type
|
|
59
|
+
const Close = Schema.Union([
|
|
60
|
+
Schema.Struct({ kind: Schema.Literal("closed") }),
|
|
61
|
+
Schema.Struct({ kind: Schema.Literal("incomplete"), outstanding: Outstanding }),
|
|
62
|
+
Schema.Struct({ kind: Schema.Literal("failed"), error: DbError })
|
|
63
|
+
])
|
|
64
|
+
export type CloseReport = typeof Close.Type
|
|
65
|
+
|
|
66
|
+
export class CloseFailure extends Schema.TaggedError<CloseFailure>()("CloseFailure", {
|
|
67
|
+
operation: Schema.String,
|
|
68
|
+
report: Close
|
|
69
|
+
}) {}
|