@bjornpagen/bumbledb 0.20.3 → 1.0.1
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 +67 -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 +161 -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 +53 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +28 -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 +64 -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 +32 -0
- package/dist/migration.d.ts.map +1 -0
- package/dist/migration.js +47 -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 +325 -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 +159 -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 +292 -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 +27 -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 +148 -0
- package/src/db-native.ts +238 -0
- package/src/db.ts +461 -1632
- package/src/errors.ts +46 -0
- package/src/face.ts +2 -2
- package/src/fields.ts +181 -55
- package/src/index.ts +62 -126
- package/src/internal/log.ts +49 -0
- package/src/law.ts +13 -28
- package/src/lower.ts +9 -4
- package/src/migration.ts +68 -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 +401 -0
- package/src/runtime-codes.ts +18 -0
- package/src/runtime-errors.ts +69 -0
- package/src/runtime-native.ts +222 -0
- package/src/runtime.ts +439 -0
- package/src/scalar.ts +560 -0
- package/src/schema.ts +35 -37
- package/src/shape.ts +30 -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,401 @@
|
|
|
1
|
+
import { AuthoringError, SdkInvariantError } from "#errors.ts"
|
|
2
|
+
/**
|
|
3
|
+
* The 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 all value types: 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.
|
|
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, 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. Strings charge
|
|
68
|
+
* two bytes per UTF-16 code unit, not their UTF-8 wire size. Native admission
|
|
69
|
+
* measures and bounds UTF-8 separately; byte views charge their length.
|
|
70
|
+
*/
|
|
71
|
+
function hostCellCharge(value: unknown): bigint {
|
|
72
|
+
if (typeof value === "string") {
|
|
73
|
+
return BigInt(value.length) * 2n
|
|
74
|
+
}
|
|
75
|
+
if (value instanceof Uint8Array) {
|
|
76
|
+
return BigInt(value.byteLength)
|
|
77
|
+
}
|
|
78
|
+
if (typeof value === "boolean") {
|
|
79
|
+
return 1n
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === "bigint" || typeof value === "number") {
|
|
82
|
+
return 8n
|
|
83
|
+
}
|
|
84
|
+
if (value !== null && typeof value === "object" && "start" in value && "end" in value) {
|
|
85
|
+
return 16n
|
|
86
|
+
}
|
|
87
|
+
return 0n
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function assertHostCellFits(context: string, value: unknown, limit: bigint): bigint {
|
|
91
|
+
const charge = hostCellCharge(value)
|
|
92
|
+
if (charge > limit) {
|
|
93
|
+
throw new AuthoringError({
|
|
94
|
+
message: `${context}: host cell of ${charge} bytes exceeds the ${limit}-byte converter bound — refuse before copy or scan`
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
return charge
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The write half of the closed bijection: one handle NAME to its u64 row
|
|
102
|
+
* id (declaration order = row ids). An unknown name is a pointed refusal
|
|
103
|
+
* naming the vocabulary and its roster.
|
|
104
|
+
*/
|
|
105
|
+
function closedCellOf(context: string, closed: AnyClosedRoster, name: string): CellValue {
|
|
106
|
+
const id = closed.handles.indexOf(name)
|
|
107
|
+
if (id === -1) {
|
|
108
|
+
throw new AuthoringError({
|
|
109
|
+
message: `${context}: "${name}" is not a handle of ${closed.name} — the roster is ${closed.handles.join(", ")}`
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
return BigInt(id)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function handleOf(context: string, closed: AnyClosedRoster, cell: unknown): string {
|
|
116
|
+
if (typeof cell !== "bigint") {
|
|
117
|
+
throw literalShapeError(context, `a ${closed.name} handle id (bigint)`, cell)
|
|
118
|
+
}
|
|
119
|
+
const handle = closed.handles[Number(cell)]
|
|
120
|
+
if (handle === undefined) {
|
|
121
|
+
throw new AuthoringError({
|
|
122
|
+
message: `${context}: id ${cell} is outside the ${closed.name} roster (${closed.handles.join(", ")})`
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
return handle
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Conservative owned-byte charge of one cell (host accounting, not wire). */
|
|
129
|
+
function cellBytes(cell: CellValue): bigint {
|
|
130
|
+
if (typeof cell === "boolean") {
|
|
131
|
+
return 1n
|
|
132
|
+
}
|
|
133
|
+
if (typeof cell === "bigint" || typeof cell === "number") {
|
|
134
|
+
return 8n
|
|
135
|
+
}
|
|
136
|
+
if (typeof cell === "string") {
|
|
137
|
+
// Account for the host string here; native admission separately
|
|
138
|
+
// checks and charges its exact UTF-8 encoding.
|
|
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
|
+
// UUIDs cross as canonical hyphenated text, not raw bytes.
|
|
190
|
+
return value
|
|
191
|
+
}
|
|
192
|
+
case "bytes": {
|
|
193
|
+
if (!(value instanceof Uint8Array)) {
|
|
194
|
+
throw literalShapeError(context, "Uint8Array", value)
|
|
195
|
+
}
|
|
196
|
+
refuseShared(context, value)
|
|
197
|
+
assertHostCellFits(context, value, HOST_CELL_MAX)
|
|
198
|
+
if (value.byteLength !== field.width) {
|
|
199
|
+
throw new AuthoringError({
|
|
200
|
+
message: `${context}: bytes<${field.width}> takes exactly ${field.width} bytes (got ${value.byteLength})`
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
// Owned copy at the ownership boundary: later caller mutation
|
|
204
|
+
// cannot change an accepted cell.
|
|
205
|
+
return Uint8Array.from(value)
|
|
206
|
+
}
|
|
207
|
+
case "interval": {
|
|
208
|
+
if (field.element === "f64") {
|
|
209
|
+
if (!isFloatIntervalValue(value)) {
|
|
210
|
+
throw literalShapeError(context, "float interval ({ start, end } numbers)", value)
|
|
211
|
+
}
|
|
212
|
+
if (Number.isNaN(value.start) || Number.isNaN(value.end)) {
|
|
213
|
+
throw new AuthoringError({ message: `${context}: a float interval endpoint cannot be NaN` })
|
|
214
|
+
}
|
|
215
|
+
const start = Object.is(value.start, -0) ? 0 : value.start
|
|
216
|
+
const end = Object.is(value.end, -0) ? 0 : value.end
|
|
217
|
+
if (!(start < end)) {
|
|
218
|
+
throw new AuthoringError({
|
|
219
|
+
message: `${context}: a float interval is half-open and nonempty (start < end strictly)`
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
return { start, end }
|
|
223
|
+
}
|
|
224
|
+
if (!isIntervalValue(value)) {
|
|
225
|
+
throw literalShapeError(context, "interval ({ start, end } bigints)", value)
|
|
226
|
+
}
|
|
227
|
+
return { start: value.start, end: value.end }
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* The flat projector: every fact's cells land in ONE row-major cell array
|
|
234
|
+
* (length rows × arity) with the row count and charged byte size counted
|
|
235
|
+
* while projecting. Missing-field refusal and per-cell judgment are
|
|
236
|
+
* {@link cellOf}'s, byte for byte.
|
|
237
|
+
*/
|
|
238
|
+
function flatRowsOf(data: RelationData, facts: Iterable<object>): FlatRows {
|
|
239
|
+
const cells: CellValue[] = []
|
|
240
|
+
let rows = 0n
|
|
241
|
+
let bytes = 0n
|
|
242
|
+
for (const fact of facts) {
|
|
243
|
+
rows += 1n
|
|
244
|
+
const record = recordOf(fact)
|
|
245
|
+
for (const declared of data.fields) {
|
|
246
|
+
const value = record[declared.name]
|
|
247
|
+
if (value === undefined) {
|
|
248
|
+
throw new AuthoringError({ message: `relation ${data.name}: fact is missing field ${declared.name}` })
|
|
249
|
+
}
|
|
250
|
+
const cell = cellOf(`relation ${data.name} field ${declared.name}`, declared.field, value)
|
|
251
|
+
bytes += cellBytes(cell)
|
|
252
|
+
cells.push(cell)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return { rows, cells, bytes }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function keyCellsOf(
|
|
259
|
+
data: RelationData,
|
|
260
|
+
projection: readonly string[],
|
|
261
|
+
key: Readonly<Record<string, unknown>>
|
|
262
|
+
): CellValue[] {
|
|
263
|
+
for (const supplied of Object.keys(key)) {
|
|
264
|
+
if (key[supplied] !== undefined && !projection.includes(supplied)) {
|
|
265
|
+
throw new AuthoringError({
|
|
266
|
+
message: `relation ${data.name}: key object carries field ${supplied} outside the primary key projection (${projection.join(", ")})`
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return projection.map(function marshalKeyCell(fieldName) {
|
|
271
|
+
const declared = data.fields.find(function byName(candidate) {
|
|
272
|
+
return candidate.name === fieldName
|
|
273
|
+
})
|
|
274
|
+
if (declared === undefined) {
|
|
275
|
+
throw new AuthoringError({
|
|
276
|
+
message: `relation ${data.name}: key projection cites unknown field ${fieldName}`
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
const value = key[fieldName]
|
|
280
|
+
if (value === undefined) {
|
|
281
|
+
throw new AuthoringError({
|
|
282
|
+
message: `relation ${data.name}: key object is missing field ${fieldName} — get reads through the primary (first-declared) key, whose projection is (${projection.join(", ")})`
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
return cellOf(`relation ${data.name} key field ${fieldName}`, declared.field, value)
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function decodeCell(context: string, field: AnyField, cell: unknown): unknown {
|
|
290
|
+
const roster = rosterOf(field)
|
|
291
|
+
if (roster !== undefined) {
|
|
292
|
+
return handleOf(context, roster, cell)
|
|
293
|
+
}
|
|
294
|
+
switch (field.kind) {
|
|
295
|
+
case "bool": {
|
|
296
|
+
if (typeof cell !== "boolean") {
|
|
297
|
+
throw new SdkInvariantError({ message: `${context}: expected boolean cell` })
|
|
298
|
+
}
|
|
299
|
+
return cell
|
|
300
|
+
}
|
|
301
|
+
case "u64":
|
|
302
|
+
case "i64": {
|
|
303
|
+
if (typeof cell !== "bigint") {
|
|
304
|
+
throw new SdkInvariantError({ message: `${context}: expected bigint cell` })
|
|
305
|
+
}
|
|
306
|
+
return cell
|
|
307
|
+
}
|
|
308
|
+
case "f64": {
|
|
309
|
+
if (typeof cell !== "number") {
|
|
310
|
+
throw new SdkInvariantError({ message: `${context}: expected number cell` })
|
|
311
|
+
}
|
|
312
|
+
return cell
|
|
313
|
+
}
|
|
314
|
+
case "str": {
|
|
315
|
+
if (typeof cell !== "string") {
|
|
316
|
+
throw new SdkInvariantError({ message: `${context}: expected string cell` })
|
|
317
|
+
}
|
|
318
|
+
return cell
|
|
319
|
+
}
|
|
320
|
+
case "uuid": {
|
|
321
|
+
// The native renderer spells uuid as the canonical
|
|
322
|
+
// canonical hyphenated UUID string (marshal.rs `uuid_hex`); the read
|
|
323
|
+
// side revalidates the spelling and keeps the string value.
|
|
324
|
+
if (!Uuid.isUuid(cell)) {
|
|
325
|
+
throw new SdkInvariantError({
|
|
326
|
+
message: `${context}: expected an uuid cell (canonical UUID text)`
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
return cell
|
|
330
|
+
}
|
|
331
|
+
case "bytes": {
|
|
332
|
+
if (!(cell instanceof Uint8Array)) {
|
|
333
|
+
throw new SdkInvariantError({ message: `${context}: expected owned bytes` })
|
|
334
|
+
}
|
|
335
|
+
return cell
|
|
336
|
+
}
|
|
337
|
+
case "interval": {
|
|
338
|
+
if (field.element === "f64") {
|
|
339
|
+
if (!isFloatIntervalValue(cell)) {
|
|
340
|
+
throw new SdkInvariantError({ message: `${context}: expected a float interval cell` })
|
|
341
|
+
}
|
|
342
|
+
return Object.freeze({ start: cell.start, end: cell.end })
|
|
343
|
+
}
|
|
344
|
+
if (!isIntervalValue(cell)) {
|
|
345
|
+
throw new SdkInvariantError({ message: `${context}: expected an integer interval cell` })
|
|
346
|
+
}
|
|
347
|
+
return Object.freeze({ start: cell.start, end: cell.end })
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function isCompleteFact<R extends AnyRelation>(
|
|
353
|
+
relation: R,
|
|
354
|
+
decoded: Readonly<Record<string, unknown>>
|
|
355
|
+
): decoded is Readonly<Record<string, unknown>> & Fact<R> {
|
|
356
|
+
return relation.data.fields.every(function present(declared) {
|
|
357
|
+
return decoded[declared.name] !== undefined
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* The read half: one owned positional row into a plain frozen record in
|
|
363
|
+
* declared field order: stable field names and shapes, no Proxy or per-cell closures.
|
|
364
|
+
*/
|
|
365
|
+
function factOfCells<R extends AnyRelation>(relation: R, row: readonly unknown[]): Fact<R> {
|
|
366
|
+
const data = relation.data
|
|
367
|
+
if (row.length !== data.fields.length) {
|
|
368
|
+
throw new SdkInvariantError({
|
|
369
|
+
message: `relation ${data.name}: row arity ${row.length} does not match the ${data.fields.length} declared fields`
|
|
370
|
+
})
|
|
371
|
+
}
|
|
372
|
+
const decoded: Record<string, unknown> = {}
|
|
373
|
+
data.fields.forEach(function decodeOne(declared, ordinal) {
|
|
374
|
+
const cell = row[ordinal]
|
|
375
|
+
if (cell === undefined) {
|
|
376
|
+
throw new SdkInvariantError({
|
|
377
|
+
message: `relation ${data.name}: row cell ${ordinal} (${declared.name}) is absent`
|
|
378
|
+
})
|
|
379
|
+
}
|
|
380
|
+
decoded[declared.name] = decodeCell(`relation ${data.name} field ${declared.name}`, declared.field, cell)
|
|
381
|
+
})
|
|
382
|
+
Object.freeze(decoded)
|
|
383
|
+
if (!isCompleteFact(relation, decoded)) {
|
|
384
|
+
throw new SdkInvariantError({ message: `relation ${data.name}: decoded row is not a complete fact` })
|
|
385
|
+
}
|
|
386
|
+
return decoded
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export type { CellValue, FlatRows }
|
|
390
|
+
export {
|
|
391
|
+
assertHostCellFits,
|
|
392
|
+
cellBytes,
|
|
393
|
+
cellOf,
|
|
394
|
+
decodeCell,
|
|
395
|
+
factOfCells,
|
|
396
|
+
flatRowsOf,
|
|
397
|
+
handleOf,
|
|
398
|
+
hostCellCharge,
|
|
399
|
+
keyCellsOf,
|
|
400
|
+
recordOf
|
|
401
|
+
}
|
|
@@ -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
|
+
}) {}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/** Exact-version internal bridge, shared by core and log. Not a public API. */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
DbHandle,
|
|
5
|
+
LogChainKind,
|
|
6
|
+
LogCommandKind,
|
|
7
|
+
LogCommandMetadata,
|
|
8
|
+
LogCommandRef,
|
|
9
|
+
LogCondition,
|
|
10
|
+
LogDecisionStamp,
|
|
11
|
+
LogIdentity,
|
|
12
|
+
LogLimits,
|
|
13
|
+
LogOutcomeWire,
|
|
14
|
+
LogSchemaHandle,
|
|
15
|
+
LogStateStamp,
|
|
16
|
+
Violation
|
|
17
|
+
} from "#native.ts"
|
|
18
|
+
import { native } from "#native.ts"
|
|
19
|
+
import type { SchemaSpec } from "#spec.ts"
|
|
20
|
+
|
|
21
|
+
export interface RuntimeHandle {
|
|
22
|
+
readonly __runtime: unique symbol
|
|
23
|
+
}
|
|
24
|
+
export interface OperationHandle {
|
|
25
|
+
readonly __operation: unique symbol
|
|
26
|
+
}
|
|
27
|
+
export interface DirectoryHandle {
|
|
28
|
+
readonly __directory: unique symbol
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Stamped `NativeKind::RepositoryLock` mint — not a directory-owner twin. */
|
|
32
|
+
export interface RepositoryLockHandle {
|
|
33
|
+
readonly __repositoryLock: unique symbol
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Resource kinds installed in native worker tables. */
|
|
37
|
+
export type NativeKind = "snapshot" | "result" | "cursor" | "draft" | "changes" | "repository-lock"
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Checked capability into the native registry. JS tokens validate
|
|
41
|
+
* these five fields; native re-judges kind/generation/owner on every verb.
|
|
42
|
+
*/
|
|
43
|
+
export interface Capability {
|
|
44
|
+
readonly runtime: bigint
|
|
45
|
+
readonly worker: number
|
|
46
|
+
readonly kind: NativeKind
|
|
47
|
+
readonly id: bigint
|
|
48
|
+
readonly generation: bigint
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type ManagedDbOutcome =
|
|
52
|
+
| { readonly tag: "accepted"; readonly db: DbHandle }
|
|
53
|
+
| { readonly tag: "rejected"; readonly violations: readonly Violation[] }
|
|
54
|
+
| {
|
|
55
|
+
readonly tag: "refused"
|
|
56
|
+
readonly kind: "schemaError" | "newtypeMismatch" | "fingerprintMismatch" | "destinationExists"
|
|
57
|
+
readonly message: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type LogTakeWire =
|
|
61
|
+
| { readonly ok: false; readonly kind: LogCommandKind | LogChainKind; readonly message: string }
|
|
62
|
+
| {
|
|
63
|
+
/** Command sealed: envelope bytes + reference. */
|
|
64
|
+
readonly ok: true
|
|
65
|
+
readonly bytes: Uint8Array
|
|
66
|
+
readonly ref: LogCommandRef
|
|
67
|
+
}
|
|
68
|
+
| {
|
|
69
|
+
/** Command parsed. */
|
|
70
|
+
readonly ok: true
|
|
71
|
+
readonly identity: LogIdentity
|
|
72
|
+
readonly receiptEpoch: bigint
|
|
73
|
+
readonly requestId: string
|
|
74
|
+
readonly condition: LogCondition
|
|
75
|
+
readonly changes: Uint8Array
|
|
76
|
+
readonly result: Uint8Array
|
|
77
|
+
readonly ref: LogCommandRef
|
|
78
|
+
}
|
|
79
|
+
| {
|
|
80
|
+
/** Decision framed. */
|
|
81
|
+
readonly ok: true
|
|
82
|
+
readonly bytes: Uint8Array
|
|
83
|
+
readonly digest: Uint8Array
|
|
84
|
+
}
|
|
85
|
+
| {
|
|
86
|
+
/** Decision decoded (and chain-verified when a parent was given). */
|
|
87
|
+
readonly ok: true
|
|
88
|
+
readonly identity: LogIdentity
|
|
89
|
+
readonly seq: bigint
|
|
90
|
+
readonly parent: LogDecisionStamp
|
|
91
|
+
readonly beforeState: LogStateStamp
|
|
92
|
+
readonly afterState: LogStateStamp
|
|
93
|
+
readonly commandBytes: Uint8Array
|
|
94
|
+
readonly command: LogCommandRef
|
|
95
|
+
readonly outcome: LogOutcomeWire
|
|
96
|
+
readonly digest: Uint8Array
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface PolicyWire {
|
|
100
|
+
readonly inputBytes: bigint
|
|
101
|
+
readonly workingBytes: bigint
|
|
102
|
+
readonly scratchBytes: bigint
|
|
103
|
+
readonly resultBytes: bigint
|
|
104
|
+
readonly rows: bigint
|
|
105
|
+
readonly workUnits: bigint
|
|
106
|
+
readonly timeoutMs: number
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface OptionsWire {
|
|
110
|
+
readonly workers: number
|
|
111
|
+
readonly queueCapacity: number
|
|
112
|
+
readonly cleanupCapacity: number
|
|
113
|
+
readonly ownerCapacity: number
|
|
114
|
+
readonly nativeHandleCapacity: number
|
|
115
|
+
readonly inputBytes: bigint
|
|
116
|
+
readonly workingBytes: bigint
|
|
117
|
+
readonly scratchBytes: bigint
|
|
118
|
+
readonly resultBytes: bigint
|
|
119
|
+
readonly chunkBytes: bigint
|
|
120
|
+
readonly cleanupTimeoutMs: number
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface InspectionWire {
|
|
124
|
+
readonly phase: "open" | "closing" | "closed"
|
|
125
|
+
readonly queued: bigint
|
|
126
|
+
readonly active: bigint
|
|
127
|
+
readonly retained: bigint
|
|
128
|
+
readonly owners: bigint
|
|
129
|
+
readonly databases: bigint
|
|
130
|
+
readonly natives: bigint
|
|
131
|
+
readonly inputBytes: bigint
|
|
132
|
+
readonly workingBytes: bigint
|
|
133
|
+
readonly scratchBytes: bigint
|
|
134
|
+
readonly resultBytes: bigint
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type CloseWire =
|
|
138
|
+
| { readonly kind: "closed" }
|
|
139
|
+
| { readonly kind: "incomplete"; readonly outstanding: InspectionWire }
|
|
140
|
+
| { readonly kind: "failed" }
|
|
141
|
+
|
|
142
|
+
interface RuntimeNative {
|
|
143
|
+
runtimeErrorCodes(): readonly string[]
|
|
144
|
+
runtimeOpen(options: OptionsWire): RuntimeHandle
|
|
145
|
+
runtimeReady(runtime: RuntimeHandle, policy: PolicyWire, callback: () => void): OperationHandle
|
|
146
|
+
runtimeHash(runtime: RuntimeHandle, policy: PolicyWire, bytes: Uint8Array, callback: () => void): OperationHandle
|
|
147
|
+
runtimeTake(operation: OperationHandle): Uint8Array | null
|
|
148
|
+
runtimeCancel(operation: OperationHandle, callback: (report: CloseWire) => void): void
|
|
149
|
+
runtimeClose(runtime: RuntimeHandle, callback: (report: CloseWire) => void): void
|
|
150
|
+
runtimeInspect(runtime: RuntimeHandle): InspectionWire
|
|
151
|
+
/**
|
|
152
|
+
* L12 request (D12/D25): one-shot arm. Next `dispatch_payload_message`
|
|
153
|
+
* must cancel after `work()` returns and the post-work `checkpoint()`
|
|
154
|
+
* (`runtime.rs` ~860–862), before `complete_operation` writes
|
|
155
|
+
* `operation.output`. A local `QueuedOutput` is not registration.
|
|
156
|
+
* Predelivery: no JS take; cursor stays on row1 so retry does not skip.
|
|
157
|
+
*/
|
|
158
|
+
runtimeArmPublicationCancel(runtime: RuntimeHandle): void
|
|
159
|
+
runtimeDirectoryAcquire(
|
|
160
|
+
runtime: RuntimeHandle,
|
|
161
|
+
policy: PolicyWire,
|
|
162
|
+
path: string,
|
|
163
|
+
callback: () => void
|
|
164
|
+
): OperationHandle
|
|
165
|
+
runtimeDirectoryTake(operation: OperationHandle): DirectoryHandle
|
|
166
|
+
runtimeDirectoryBegin(owner: DirectoryHandle, policy: PolicyWire): OperationHandle
|
|
167
|
+
runtimeDirectoryCheck(operation: OperationHandle): void
|
|
168
|
+
runtimeDirectoryEnd(operation: OperationHandle): void
|
|
169
|
+
runtimeDirectoryClose(owner: DirectoryHandle, remove: boolean, callback: (report: CloseWire) => void): void
|
|
170
|
+
runtimeDirectoryDbOpen(
|
|
171
|
+
owner: DirectoryHandle,
|
|
172
|
+
policy: PolicyWire,
|
|
173
|
+
childName: string,
|
|
174
|
+
spec: SchemaSpec,
|
|
175
|
+
create: boolean,
|
|
176
|
+
callback: () => void
|
|
177
|
+
): OperationHandle
|
|
178
|
+
runtimeDbTake(operation: OperationHandle): ManagedDbOutcome
|
|
179
|
+
runtimeManagedDbClose(db: DbHandle, callback: (report: CloseWire) => void): void
|
|
180
|
+
|
|
181
|
+
// --- successor log grammar on the executor (charged hashing over
|
|
182
|
+
// whole canonical change payloads; C06 through C09) ---
|
|
183
|
+
runtimeLogCommandSeal(
|
|
184
|
+
runtime: RuntimeHandle,
|
|
185
|
+
schema: LogSchemaHandle,
|
|
186
|
+
policy: PolicyWire,
|
|
187
|
+
metadata: LogCommandMetadata,
|
|
188
|
+
changes: Uint8Array,
|
|
189
|
+
result: Uint8Array | null,
|
|
190
|
+
limits: LogLimits,
|
|
191
|
+
callback: () => void
|
|
192
|
+
): OperationHandle
|
|
193
|
+
runtimeLogCommandParse(
|
|
194
|
+
runtime: RuntimeHandle,
|
|
195
|
+
schema: LogSchemaHandle,
|
|
196
|
+
policy: PolicyWire,
|
|
197
|
+
bytes: Uint8Array,
|
|
198
|
+
limits: LogLimits,
|
|
199
|
+
callback: () => void
|
|
200
|
+
): OperationHandle
|
|
201
|
+
runtimeLogDecisionDecode(
|
|
202
|
+
runtime: RuntimeHandle,
|
|
203
|
+
policy: PolicyWire,
|
|
204
|
+
bytes: Uint8Array,
|
|
205
|
+
parent: LogDecisionStamp | null,
|
|
206
|
+
limits: LogLimits,
|
|
207
|
+
callback: () => void
|
|
208
|
+
): OperationHandle
|
|
209
|
+
runtimeLogTake(operation: OperationHandle): LogTakeWire
|
|
210
|
+
/** L14 mint: `Runtime::mint_repository_lock` stamps `NativeKind::RepositoryLock` at take. */
|
|
211
|
+
logRepositoryLockAcquire(
|
|
212
|
+
runtime: RuntimeHandle,
|
|
213
|
+
policy: PolicyWire,
|
|
214
|
+
directory: string,
|
|
215
|
+
callback: () => void
|
|
216
|
+
): OperationHandle
|
|
217
|
+
logRepositoryLockTake(operation: OperationHandle): RepositoryLockHandle
|
|
218
|
+
logRepositoryLockRelease(owner: RepositoryLockHandle, callback: (report: CloseWire) => void): void
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// The checked source/fresh-addon roster test pins this private declaration.
|
|
222
|
+
export const runtimeNative = native as typeof native & RuntimeNative
|