@lix-js/sdk 0.12.2 → 0.14.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 +40 -10
- package/dist/binding-types.d.ts +37 -9
- package/dist/binding.browser.d.ts +2 -2
- package/dist/binding.browser.js +25 -10
- package/dist/binding.node-wasm.d.ts +2 -2
- package/dist/binding.node-wasm.js +8 -4
- package/dist/binding.node.d.ts +3 -3
- package/dist/binding.node.js +70 -13
- package/dist/browser-wasm-init.d.ts +14 -0
- package/dist/browser-wasm-init.js +16 -0
- package/dist/bundled-plugins/plugin_csv.lixplugin +0 -0
- package/dist/bundled-plugins/plugin_markdown.lixplugin +0 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/lix.d.ts +27 -5
- package/dist/lix.js +135 -12
- package/dist/open-lix.d.ts +4 -5
- package/dist/open-lix.js +173 -33
- package/dist/remote/client.d.ts +1 -2
- package/dist/remote/client.js +58 -1151
- package/dist/remote/server-protocol.d.ts +5 -6
- package/dist/remote/server-protocol.js +40 -9
- package/dist/result.d.ts +6 -13
- package/dist/result.js +9 -35
- package/dist/snapshot-restore.d.ts +6 -0
- package/dist/snapshot-restore.js +101 -0
- package/dist/storage-adapter.d.ts +175 -19
- package/dist/storage-adapter.js +21 -0
- package/dist/types.d.ts +101 -31
- package/dist/value.d.ts +1 -0
- package/dist/value.js +8 -0
- package/dist/wasm/lix_js_sdk.d.ts +119 -24
- package/dist/wasm/lix_js_sdk.js +599 -78
- package/dist/wasm/lix_js_sdk_bg.wasm +0 -0
- package/dist/wasm/lix_js_sdk_bg.wasm.d.ts +47 -6
- package/dist/worker/client.d.ts +27 -5
- package/dist/worker/client.js +458 -45
- package/dist/worker/factory.browser.d.ts +2 -2
- package/dist/worker/factory.node.d.ts +3 -2
- package/dist/worker/factory.node.js +18 -12
- package/dist/worker/host.d.ts +2 -1
- package/dist/worker/host.js +283 -37
- package/dist/worker/protocol.d.ts +80 -3
- package/package.json +6 -10
- package/dist/indexeddb-backend.d.ts +0 -19
- package/dist/indexeddb-backend.js +0 -121
- package/dist/remote/sse.d.ts +0 -12
- package/dist/remote/sse.js +0 -87
- package/dist/workerd.d.ts +0 -25
- package/dist/workerd.js +0 -35
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { BindingExecuteResult, BindingObserveEvent } from "../binding-types.js";
|
|
2
2
|
import type { NativeLixValue } from "../value.js";
|
|
3
|
-
export declare const SERVER_PROTOCOL_VERSION =
|
|
4
|
-
export declare const SERVER_PROTOCOL_PATH = "/lix/v1/";
|
|
3
|
+
export declare const SERVER_PROTOCOL_VERSION = 6;
|
|
5
4
|
export type WireValue = {
|
|
6
5
|
kind: "null";
|
|
7
6
|
value: null;
|
|
@@ -20,6 +19,9 @@ export type WireValue = {
|
|
|
20
19
|
} | {
|
|
21
20
|
kind: "jsonb";
|
|
22
21
|
value: unknown;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "row_ref";
|
|
24
|
+
value: string;
|
|
23
25
|
} | {
|
|
24
26
|
kind: "timestamptz";
|
|
25
27
|
value: string;
|
|
@@ -65,7 +67,7 @@ export type ServerProtocolExecuteBatchRequest = {
|
|
|
65
67
|
cacheBlobs?: true;
|
|
66
68
|
};
|
|
67
69
|
export type ServerProtocolExecuteResponse = {
|
|
68
|
-
columns:
|
|
70
|
+
columns: BindingExecuteResult["columns"];
|
|
69
71
|
rows: WireValue[][];
|
|
70
72
|
rowsAffected: number;
|
|
71
73
|
notices: Array<{
|
|
@@ -128,9 +130,6 @@ export type ServerProtocolCreateBranchResponse = {
|
|
|
128
130
|
hidden: boolean;
|
|
129
131
|
commitId: string;
|
|
130
132
|
};
|
|
131
|
-
export type ServerProtocolCreateCheckpointResponse = {
|
|
132
|
-
commitId: string;
|
|
133
|
-
};
|
|
134
133
|
export type ServerProtocolUndoResponse = {
|
|
135
134
|
branchId: string;
|
|
136
135
|
targetCommitId: string;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export const SERVER_PROTOCOL_VERSION =
|
|
2
|
-
export const SERVER_PROTOCOL_PATH = "/lix/v1/";
|
|
1
|
+
export const SERVER_PROTOCOL_VERSION = 6;
|
|
3
2
|
export function encodeWireValue(value) {
|
|
4
3
|
switch (value.kind) {
|
|
5
4
|
case "null":
|
|
@@ -14,6 +13,8 @@ export function encodeWireValue(value) {
|
|
|
14
13
|
return { kind: "text", value: value.value };
|
|
15
14
|
case "jsonb":
|
|
16
15
|
return { kind: "jsonb", value: value.value };
|
|
16
|
+
case "row_ref":
|
|
17
|
+
return { kind: "row_ref", value: value.value };
|
|
17
18
|
case "timestamptz":
|
|
18
19
|
return { kind: "timestamptz", value: value.value };
|
|
19
20
|
case "blob":
|
|
@@ -22,7 +23,16 @@ export function encodeWireValue(value) {
|
|
|
22
23
|
}
|
|
23
24
|
export function decodeExecuteResult(value) {
|
|
24
25
|
const result = record(value, "execute result");
|
|
25
|
-
|
|
26
|
+
if (!Array.isArray(result.columns)) {
|
|
27
|
+
throw protocolError("execute result columns must be an array");
|
|
28
|
+
}
|
|
29
|
+
const columns = result.columns.map((column, index) => {
|
|
30
|
+
const item = record(column, `execute result column ${index}`);
|
|
31
|
+
if (typeof item.name !== "string" || !isResultColumnType(item.type)) {
|
|
32
|
+
throw protocolError(`execute result column ${index} requires a string name and valid type`);
|
|
33
|
+
}
|
|
34
|
+
return { name: item.name, type: item.type };
|
|
35
|
+
});
|
|
26
36
|
if (!Array.isArray(result.rows)) {
|
|
27
37
|
throw protocolError("execute result rows must be an array");
|
|
28
38
|
}
|
|
@@ -33,7 +43,14 @@ export function decodeExecuteResult(value) {
|
|
|
33
43
|
if (row.length !== columns.length) {
|
|
34
44
|
throw protocolError(`execute result row ${rowIndex} has ${row.length} values for ${columns.length} columns`);
|
|
35
45
|
}
|
|
36
|
-
return row.map((entry) =>
|
|
46
|
+
return row.map((entry, columnIndex) => {
|
|
47
|
+
const decoded = decodeWireValue(entry);
|
|
48
|
+
const declaredType = columns[columnIndex]?.type;
|
|
49
|
+
if (decoded.kind !== "null" && decoded.kind !== declaredType) {
|
|
50
|
+
throw protocolError(`execute result row ${rowIndex} column ${columnIndex} declares ${String(declaredType)} but contains ${decoded.kind}`);
|
|
51
|
+
}
|
|
52
|
+
return decoded;
|
|
53
|
+
});
|
|
37
54
|
});
|
|
38
55
|
if (typeof result.rowsAffected !== "number" ||
|
|
39
56
|
!Number.isSafeInteger(result.rowsAffected) ||
|
|
@@ -59,6 +76,17 @@ export function decodeExecuteResult(value) {
|
|
|
59
76
|
});
|
|
60
77
|
return { columns, rows, rowsAffected: result.rowsAffected, notices };
|
|
61
78
|
}
|
|
79
|
+
function isResultColumnType(value) {
|
|
80
|
+
return (value === "null" ||
|
|
81
|
+
value === "boolean" ||
|
|
82
|
+
value === "integer" ||
|
|
83
|
+
value === "real" ||
|
|
84
|
+
value === "text" ||
|
|
85
|
+
value === "jsonb" ||
|
|
86
|
+
value === "row_ref" ||
|
|
87
|
+
value === "timestamptz" ||
|
|
88
|
+
value === "blob");
|
|
89
|
+
}
|
|
62
90
|
export function decodeExecuteBatchResult(value) {
|
|
63
91
|
const result = record(value, "execute batch result");
|
|
64
92
|
const statementIndex = nonNegativeSafeInteger(result.statementIndex, "execute batch result statementIndex");
|
|
@@ -146,7 +174,8 @@ function applyObserveBlobDelta(delta, sequence, base) {
|
|
|
146
174
|
}
|
|
147
175
|
const baseValue = base.rows.rows[0]?.[0];
|
|
148
176
|
if (base.rows.columns.length !== 1 ||
|
|
149
|
-
base.rows.columns[0] !== "content" ||
|
|
177
|
+
base.rows.columns[0]?.name !== "content" ||
|
|
178
|
+
base.rows.columns[0]?.type !== "blob" ||
|
|
150
179
|
base.rows.rows.length !== 1 ||
|
|
151
180
|
base.rows.rows[0]?.length !== 1 ||
|
|
152
181
|
base.rows.rowsAffected !== 0 ||
|
|
@@ -173,7 +202,7 @@ function applyObserveBlobDelta(delta, sequence, base) {
|
|
|
173
202
|
blob.set(insert, prefixBytes);
|
|
174
203
|
blob.set(baseValue.blob.subarray(baseValue.blob.byteLength - suffixBytes), prefixBytes + insert.byteLength);
|
|
175
204
|
return {
|
|
176
|
-
columns: ["content"],
|
|
205
|
+
columns: [{ name: "content", type: "blob" }],
|
|
177
206
|
rows: [[{ kind: "blob", value: null, blob }]],
|
|
178
207
|
rowsAffected: 0,
|
|
179
208
|
notices: [],
|
|
@@ -265,9 +294,6 @@ export function record(value, description) {
|
|
|
265
294
|
}
|
|
266
295
|
return value;
|
|
267
296
|
}
|
|
268
|
-
function isRecord(value) {
|
|
269
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
270
|
-
}
|
|
271
297
|
function decodeWireValue(value) {
|
|
272
298
|
const wire = record(value, "wire value");
|
|
273
299
|
switch (wire.kind) {
|
|
@@ -298,6 +324,11 @@ function decodeWireValue(value) {
|
|
|
298
324
|
case "jsonb":
|
|
299
325
|
assertJsonValue(wire.value, "jsonb wire value");
|
|
300
326
|
return { kind: "jsonb", value: wire.value };
|
|
327
|
+
case "row_ref":
|
|
328
|
+
if (typeof wire.value !== "string") {
|
|
329
|
+
throw protocolError("row_ref wire value is invalid");
|
|
330
|
+
}
|
|
331
|
+
return { kind: "row_ref", value: wire.value };
|
|
301
332
|
case "timestamptz":
|
|
302
333
|
if (typeof wire.value !== "string") {
|
|
303
334
|
throw protocolError("timestamptz wire value is invalid");
|
package/dist/result.d.ts
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
import { type NativeLixValue
|
|
2
|
-
import type { ExecuteBatchResult, ExecuteResult,
|
|
3
|
-
export declare class Row {
|
|
4
|
-
private readonly columns;
|
|
5
|
-
private readonly values;
|
|
6
|
-
constructor(columns: string[], values: Value[]);
|
|
7
|
-
static fromRaw(columns: string[], values: LixValue[]): Row;
|
|
8
|
-
get(column: string): unknown;
|
|
9
|
-
value(column: string): Value;
|
|
10
|
-
toObject(): Record<string, unknown>;
|
|
11
|
-
toValueMap(): Record<string, Value>;
|
|
12
|
-
}
|
|
1
|
+
import { type NativeLixValue } from "./value.js";
|
|
2
|
+
import type { ExecuteBatchResult, ExecuteResult, ResultArrayRow, ResultObjectRow, ResultRow } from "./types.js";
|
|
13
3
|
type NativeExecuteResult = Omit<ExecuteResult, "rows"> & {
|
|
14
4
|
rows: NativeLixValue[][];
|
|
15
5
|
};
|
|
16
6
|
export declare function wrapExecuteResult(result: NativeExecuteResult): ExecuteResult;
|
|
17
|
-
export declare function
|
|
7
|
+
export declare function wrapExecuteResult(result: NativeExecuteResult, rowMode: "object"): ExecuteResult<ResultObjectRow>;
|
|
8
|
+
export declare function wrapExecuteResult(result: NativeExecuteResult, rowMode: "array"): ExecuteResult<ResultArrayRow>;
|
|
9
|
+
export declare function wrapExecuteResult(result: NativeExecuteResult, rowMode: "object" | "array"): ExecuteResult<ResultRow>;
|
|
10
|
+
export declare function wrapExecuteBatchResult(result: NativeExecuteResult, rowMode?: "object" | "array"): ExecuteBatchResult<ResultRow>;
|
|
18
11
|
export declare function normalizeOptionals<T>(value: T): T;
|
|
19
12
|
export {};
|
package/dist/result.js
CHANGED
|
@@ -1,42 +1,16 @@
|
|
|
1
1
|
import { fromNativeValue, Value } from "./value.js";
|
|
2
|
-
export
|
|
3
|
-
columns;
|
|
4
|
-
values;
|
|
5
|
-
constructor(columns, values) {
|
|
6
|
-
this.columns = columns;
|
|
7
|
-
this.values = values;
|
|
8
|
-
}
|
|
9
|
-
static fromRaw(columns, values) {
|
|
10
|
-
return new Row(columns, values.map((value) => Value._fromNative(value)));
|
|
11
|
-
}
|
|
12
|
-
get(column) {
|
|
13
|
-
return this.value(column).toJS();
|
|
14
|
-
}
|
|
15
|
-
value(column) {
|
|
16
|
-
const index = this.columns.indexOf(column);
|
|
17
|
-
if (index === -1) {
|
|
18
|
-
throw new Error(`Unknown column "${column}". Available columns: ${this.columns.join(", ")}`);
|
|
19
|
-
}
|
|
20
|
-
const value = this.values[index];
|
|
21
|
-
if (!value) {
|
|
22
|
-
throw new Error(`Column "${column}" is missing a value`);
|
|
23
|
-
}
|
|
24
|
-
return value;
|
|
25
|
-
}
|
|
26
|
-
toObject() {
|
|
27
|
-
return Object.fromEntries(this.columns.map((column, index) => [column, this.values[index]?.toJS()]));
|
|
28
|
-
}
|
|
29
|
-
toValueMap() {
|
|
30
|
-
return Object.fromEntries(this.columns.map((column, index) => [column, this.values[index]]));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
export function wrapExecuteResult(result) {
|
|
2
|
+
export function wrapExecuteResult(result, rowMode = "object") {
|
|
34
3
|
return {
|
|
35
4
|
...result,
|
|
36
|
-
rows: result.rows.map((row) =>
|
|
5
|
+
rows: result.rows.map((row) => {
|
|
6
|
+
const values = row.map((value) => Value._fromNative(fromNativeValue(value)).toJS());
|
|
7
|
+
if (rowMode === "array")
|
|
8
|
+
return values;
|
|
9
|
+
return Object.fromEntries(result.columns.map((column, index) => [column.name, values[index]]));
|
|
10
|
+
}),
|
|
37
11
|
};
|
|
38
12
|
}
|
|
39
|
-
export function wrapExecuteBatchResult(result) {
|
|
13
|
+
export function wrapExecuteBatchResult(result, rowMode = "object") {
|
|
40
14
|
const statementIndex = result.statementIndex;
|
|
41
15
|
if (typeof statementIndex !== "number" ||
|
|
42
16
|
!Number.isSafeInteger(statementIndex) ||
|
|
@@ -44,7 +18,7 @@ export function wrapExecuteBatchResult(result) {
|
|
|
44
18
|
throw new Error("executeBatch result is missing a valid statementIndex");
|
|
45
19
|
}
|
|
46
20
|
return {
|
|
47
|
-
...wrapExecuteResult(result),
|
|
21
|
+
...wrapExecuteResult(result, rowMode),
|
|
48
22
|
statementIndex,
|
|
49
23
|
};
|
|
50
24
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SnapshotRestoreBinding } from "./binding-types.js";
|
|
2
|
+
export declare const SNAPSHOT_RESTORE_CHUNK_BYTES: number;
|
|
3
|
+
/** Copies arbitrary caller-owned input into bounded transport-owned chunks. */
|
|
4
|
+
export declare function ownedSnapshotRestoreChunks(chunk: Uint8Array): Generator<Uint8Array>;
|
|
5
|
+
/** Pumps a snapshot into a bounded native restore without buffering the artifact. */
|
|
6
|
+
export declare function restoreSnapshot<T>(source: ReadableStream<Uint8Array>, restore: SnapshotRestoreBinding<T>): Promise<T>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export const SNAPSHOT_RESTORE_CHUNK_BYTES = 64 * 1024;
|
|
2
|
+
/** Copies arbitrary caller-owned input into bounded transport-owned chunks. */
|
|
3
|
+
export function* ownedSnapshotRestoreChunks(chunk) {
|
|
4
|
+
for (let offset = 0; offset < chunk.byteLength;) {
|
|
5
|
+
const end = Math.min(offset + SNAPSHOT_RESTORE_CHUNK_BYTES, chunk.byteLength);
|
|
6
|
+
const owned = new Uint8Array(end - offset);
|
|
7
|
+
owned.set(chunk.subarray(offset, end));
|
|
8
|
+
yield owned;
|
|
9
|
+
offset = end;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function waitForSnapshotRestoreCompletion(restore) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const poll = () => {
|
|
15
|
+
try {
|
|
16
|
+
if (restore.isComplete()) {
|
|
17
|
+
resolve();
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
reject(error);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// Keep this lightweight for long-running imports while still surfacing a
|
|
26
|
+
// decoder failure promptly when the producer has stopped yielding data.
|
|
27
|
+
setTimeout(poll, 10);
|
|
28
|
+
};
|
|
29
|
+
poll();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** Pumps a snapshot into a bounded native restore without buffering the artifact. */
|
|
33
|
+
export async function restoreSnapshot(source, restore) {
|
|
34
|
+
let reader;
|
|
35
|
+
try {
|
|
36
|
+
reader = source.getReader();
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// Some internal callers create the restore before entering this helper.
|
|
40
|
+
// Completion-aware cancellation prevents a locked source from stranding it.
|
|
41
|
+
await Promise.resolve()
|
|
42
|
+
.then(() => restore.cancel())
|
|
43
|
+
.catch(() => undefined);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
const backendCompletion = waitForSnapshotRestoreCompletion(restore).then(() => ({ kind: "backend-complete" }), (error) => ({ kind: "completion-error", error }));
|
|
47
|
+
let inputOpen = true;
|
|
48
|
+
try {
|
|
49
|
+
while (true) {
|
|
50
|
+
const outcome = await Promise.race([
|
|
51
|
+
reader.read().then((result) => ({ kind: "source", result }), (error) => ({ kind: "source-error", error })),
|
|
52
|
+
backendCompletion,
|
|
53
|
+
]);
|
|
54
|
+
if (outcome.kind === "completion-error")
|
|
55
|
+
throw outcome.error;
|
|
56
|
+
if (outcome.kind === "backend-complete") {
|
|
57
|
+
// The decoder can reject after accepting a malformed chunk while the
|
|
58
|
+
// producer stalls forever. Stop that pending read and drain finish() so
|
|
59
|
+
// the backend's structured semantic error remains authoritative.
|
|
60
|
+
await reader.cancel().catch(() => undefined);
|
|
61
|
+
inputOpen = false;
|
|
62
|
+
return await restore.finish();
|
|
63
|
+
}
|
|
64
|
+
if (outcome.kind === "source-error")
|
|
65
|
+
throw outcome.error;
|
|
66
|
+
const { value, done } = outcome.result;
|
|
67
|
+
if (done)
|
|
68
|
+
break;
|
|
69
|
+
if (!(value instanceof Uint8Array)) {
|
|
70
|
+
throw new TypeError("snapshot stream chunks must be Uint8Array values");
|
|
71
|
+
}
|
|
72
|
+
for (const chunk of ownedSnapshotRestoreChunks(value)) {
|
|
73
|
+
try {
|
|
74
|
+
await restore.write(chunk);
|
|
75
|
+
}
|
|
76
|
+
catch (writeError) {
|
|
77
|
+
// The decoder may have rejected before accepting this chunk. Stop the
|
|
78
|
+
// producer, then drain finish() so its semantic Lix error wins over the
|
|
79
|
+
// transport-level write rejection.
|
|
80
|
+
await reader.cancel(writeError).catch(() => undefined);
|
|
81
|
+
inputOpen = false;
|
|
82
|
+
return await restore.finish();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
inputOpen = false;
|
|
87
|
+
return await restore.finish();
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (inputOpen) {
|
|
91
|
+
inputOpen = false;
|
|
92
|
+
await Promise.resolve()
|
|
93
|
+
.then(() => restore.cancel())
|
|
94
|
+
.catch(() => undefined);
|
|
95
|
+
}
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
reader.releaseLock();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -1,30 +1,186 @@
|
|
|
1
|
+
/** Opaque package-level storage selection accepted by `openLix()`. */
|
|
2
|
+
export type LixStorage = {
|
|
3
|
+
readonly lixStorage: object;
|
|
4
|
+
};
|
|
1
5
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* receive only the controls required to manage their own persistence layer.
|
|
6
|
+
* Worker-loadable registration emitted by JavaScript storage packages.
|
|
7
|
+
* `moduleUrl` must export `createLixStorageProvider(options)` and is loaded in
|
|
8
|
+
* the same dedicated worker as the Lix Wasm engine.
|
|
6
9
|
*/
|
|
7
|
-
export type
|
|
10
|
+
export type LixStorageProviderRegistration = {
|
|
11
|
+
readonly version: 3;
|
|
12
|
+
readonly moduleUrl: string;
|
|
13
|
+
readonly options: unknown;
|
|
14
|
+
};
|
|
15
|
+
type JsProviderLixStorage = LixStorage & {
|
|
16
|
+
readonly lixStorage: LixStorageProviderRegistration;
|
|
17
|
+
};
|
|
18
|
+
type FilesystemStorageConnection = {
|
|
8
19
|
importFilesystemPaths(paths: string[]): Promise<void>;
|
|
9
20
|
syncDiskToLix(): Promise<void>;
|
|
10
21
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
type FilesystemLixStorage = LixStorage & {
|
|
23
|
+
readonly lixStorage: {
|
|
24
|
+
readonly version: 1;
|
|
25
|
+
readonly config: {
|
|
26
|
+
kind: "filesystem";
|
|
27
|
+
path: string;
|
|
28
|
+
syncAllFiles: boolean;
|
|
29
|
+
};
|
|
30
|
+
connect(connection: FilesystemStorageConnection | undefined): void;
|
|
31
|
+
};
|
|
16
32
|
};
|
|
33
|
+
export declare function isLixStorage(value: unknown): value is FilesystemLixStorage;
|
|
34
|
+
export declare function isJsProviderLixStorage(value: unknown): value is JsProviderLixStorage;
|
|
17
35
|
/**
|
|
18
|
-
*
|
|
36
|
+
* JavaScript representation of `lix::storage::StorageSpace`.
|
|
19
37
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
38
|
+
* These provider types intentionally mirror the Rust storage traits. Changes
|
|
39
|
+
* to `Storage`, `StorageRead`, `StorageWrite`, or `StorageScanSource` must be
|
|
40
|
+
* reflected here and in the single Rust↔JS bridge.
|
|
22
41
|
*/
|
|
23
|
-
export type
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
42
|
+
export type LixStorageSpace = {
|
|
43
|
+
id: number;
|
|
44
|
+
name: string;
|
|
45
|
+
valueSemantics: "mutable" | "immutable";
|
|
46
|
+
valueIntegrity: "backendVerified" | "contentAddressed";
|
|
47
|
+
};
|
|
48
|
+
export type LixStorageKeyRange = {
|
|
49
|
+
lower: LixStorageBound;
|
|
50
|
+
upper: LixStorageBound;
|
|
51
|
+
};
|
|
52
|
+
export type LixStorageBound = {
|
|
53
|
+
kind: "unbounded";
|
|
54
|
+
} | {
|
|
55
|
+
kind: "included";
|
|
56
|
+
key: Uint8Array;
|
|
57
|
+
} | {
|
|
58
|
+
kind: "excluded";
|
|
59
|
+
key: Uint8Array;
|
|
60
|
+
};
|
|
61
|
+
export type LixStorageProjection = "keyOnly" | "fullValue";
|
|
62
|
+
export type LixStorageScanOrder = "ascending" | "descending";
|
|
63
|
+
export type LixStorageReadOptions = {
|
|
64
|
+
snapshot?: Uint8Array;
|
|
65
|
+
consistency: "snapshot" | "staleOk" | "latest";
|
|
66
|
+
durability: "visible" | "durable";
|
|
67
|
+
/** Canonical unsigned 64-bit base-10 token obtained from `acquireSession()`. */
|
|
68
|
+
sessionToken?: string;
|
|
69
|
+
};
|
|
70
|
+
export type LixStorageWriteOptions = {
|
|
71
|
+
baseSnapshot?: Uint8Array;
|
|
72
|
+
idempotencyKey?: Uint8Array;
|
|
73
|
+
awaitDurable: boolean;
|
|
74
|
+
preconditions: LixStoragePrecondition[];
|
|
75
|
+
batchCapacityHintBytes: number;
|
|
76
|
+
/** Canonical unsigned 64-bit base-10 token obtained from `acquireSession()`. */
|
|
77
|
+
sessionToken?: string;
|
|
78
|
+
};
|
|
79
|
+
export type LixStoragePrecondition = {
|
|
80
|
+
kind: "keyAbsent";
|
|
81
|
+
space: LixStorageSpace;
|
|
82
|
+
key: Uint8Array;
|
|
83
|
+
} | {
|
|
84
|
+
kind: "keyPresent";
|
|
85
|
+
space: LixStorageSpace;
|
|
86
|
+
key: Uint8Array;
|
|
87
|
+
} | {
|
|
88
|
+
kind: "keyValueHashEquals";
|
|
89
|
+
space: LixStorageSpace;
|
|
90
|
+
key: Uint8Array;
|
|
91
|
+
hash: Uint8Array;
|
|
92
|
+
} | {
|
|
93
|
+
kind: "keyValueEquals";
|
|
94
|
+
space: LixStorageSpace;
|
|
95
|
+
key: Uint8Array;
|
|
96
|
+
expected: Uint8Array;
|
|
97
|
+
} | {
|
|
98
|
+
kind: "rangeEmpty";
|
|
99
|
+
space: LixStorageSpace;
|
|
100
|
+
range: LixStorageKeyRange;
|
|
101
|
+
} | {
|
|
102
|
+
kind: "branchEquals";
|
|
103
|
+
refKey: Uint8Array;
|
|
104
|
+
expected: Uint8Array;
|
|
105
|
+
};
|
|
106
|
+
export type LixStorageGetManyRequest = {
|
|
107
|
+
space: LixStorageSpace;
|
|
108
|
+
keys: Uint8Array[];
|
|
109
|
+
options: {
|
|
110
|
+
projection: LixStorageProjection;
|
|
28
111
|
};
|
|
29
112
|
};
|
|
30
|
-
export
|
|
113
|
+
export type LixStorageProjectedValue = {
|
|
114
|
+
kind: "keyOnly";
|
|
115
|
+
} | {
|
|
116
|
+
kind: "fullValue";
|
|
117
|
+
value: Uint8Array;
|
|
118
|
+
};
|
|
119
|
+
export type LixStorageReadEntry = {
|
|
120
|
+
key: Uint8Array;
|
|
121
|
+
value: LixStorageProjectedValue;
|
|
122
|
+
};
|
|
123
|
+
export type LixStoragePutEntry = {
|
|
124
|
+
key: Uint8Array;
|
|
125
|
+
value: Uint8Array;
|
|
126
|
+
};
|
|
127
|
+
export type LixStorageWriteStats = {
|
|
128
|
+
putEntries: number;
|
|
129
|
+
deletedEntries: number;
|
|
130
|
+
deletedRanges: number;
|
|
131
|
+
writtenBytes: number;
|
|
132
|
+
storageCalls: number;
|
|
133
|
+
};
|
|
134
|
+
export type LixStorageCommitResult = {
|
|
135
|
+
commitId?: Uint8Array;
|
|
136
|
+
stats: LixStorageWriteStats;
|
|
137
|
+
};
|
|
138
|
+
/** Mirrors `lix::storage::Storage`. */
|
|
139
|
+
export interface LixStorageProvider {
|
|
140
|
+
/** Joins the active generation and returns its canonical unsigned 64-bit base-10 token. */
|
|
141
|
+
acquireSession(): Promise<string>;
|
|
142
|
+
beginRead(options: LixStorageReadOptions): Promise<LixStorageRead>;
|
|
143
|
+
beginWrite(options: LixStorageWriteOptions): Promise<LixStorageWrite>;
|
|
144
|
+
watchForChanges(): Promise<LixStorageChangeWatch>;
|
|
145
|
+
/** SDK lifecycle hook corresponding to releasing the Rust storage owner. */
|
|
146
|
+
close(): Promise<void>;
|
|
147
|
+
}
|
|
148
|
+
/** Mirrors `lix::storage::StorageChangeWatch`. */
|
|
149
|
+
export interface LixStorageChangeWatch {
|
|
150
|
+
changed(): Promise<void>;
|
|
151
|
+
close(): void;
|
|
152
|
+
}
|
|
153
|
+
/** Mirrors `lix::storage::StorageRead`. */
|
|
154
|
+
export interface LixStorageRead {
|
|
155
|
+
/** Decimal u128, or undefined to disable snapshot-derived caching. */
|
|
156
|
+
snapshotCacheKey(): string | undefined;
|
|
157
|
+
getMany(requests: LixStorageGetManyRequest[]): Promise<Array<LixStorageProjectedValue | null>>;
|
|
158
|
+
beginScan(space: LixStorageSpace, range: LixStorageKeyRange, options: {
|
|
159
|
+
projection: LixStorageProjection;
|
|
160
|
+
order: LixStorageScanOrder;
|
|
161
|
+
}): Promise<LixStorageScanSource>;
|
|
162
|
+
}
|
|
163
|
+
/** Mirrors `lix::storage::StorageScanSource`. */
|
|
164
|
+
export interface LixStorageScanSource {
|
|
165
|
+
nextPage(limitRows: number): Promise<{
|
|
166
|
+
entries: LixStorageReadEntry[];
|
|
167
|
+
hasMore: boolean;
|
|
168
|
+
}>;
|
|
169
|
+
}
|
|
170
|
+
/** Mirrors `lix::storage::StorageWrite`. */
|
|
171
|
+
export interface LixStorageWrite {
|
|
172
|
+
putMany(space: LixStorageSpace, entries: LixStoragePutEntry[]): Promise<void>;
|
|
173
|
+
replaceMany(space: LixStorageSpace, entries: LixStoragePutEntry[]): Promise<void>;
|
|
174
|
+
deleteMany(space: LixStorageSpace, keys: Uint8Array[]): Promise<void>;
|
|
175
|
+
deleteRange(space: LixStorageSpace, range: LixStorageKeyRange): Promise<void>;
|
|
176
|
+
commit(): Promise<LixStorageCommitResult>;
|
|
177
|
+
rollback(): Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
export type LixStorageErrorCode = "LIX_STORAGE_UNSUPPORTED" | "LIX_STORAGE_INVALID_KEY" | "LIX_STORAGE_INVALID_CURSOR" | "LIX_STORAGE_READ_EXPIRED" | "LIX_STORAGE_WRITE_CONFLICT" | "LIX_STORAGE_PRECONDITION_FAILED" | "LIX_STORAGE_DURABILITY" | "LIX_STORAGE_FENCED" | "LIX_STORAGE_CLOSED" | "LIX_STORAGE_COMMIT_OUTCOME_UNKNOWN" | "LIX_STORAGE_CORRUPTION" | "LIX_STORAGE_IO";
|
|
180
|
+
/** Error representation decoded back into `lix::storage::StorageError`. */
|
|
181
|
+
export declare class LixStorageError extends Error {
|
|
182
|
+
readonly code: LixStorageErrorCode;
|
|
183
|
+
readonly details?: unknown;
|
|
184
|
+
constructor(code: LixStorageErrorCode, message: string, details?: unknown);
|
|
185
|
+
}
|
|
186
|
+
export {};
|
package/dist/storage-adapter.js
CHANGED
|
@@ -10,3 +10,24 @@ export function isLixStorage(value) {
|
|
|
10
10
|
adapter.config?.kind ===
|
|
11
11
|
"filesystem");
|
|
12
12
|
}
|
|
13
|
+
export function isJsProviderLixStorage(value) {
|
|
14
|
+
if (!value || typeof value !== "object" || !("lixStorage" in value)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const registration = value.lixStorage;
|
|
18
|
+
return Boolean(registration &&
|
|
19
|
+
typeof registration === "object" &&
|
|
20
|
+
registration.version === 3 &&
|
|
21
|
+
typeof registration.moduleUrl === "string");
|
|
22
|
+
}
|
|
23
|
+
/** Error representation decoded back into `lix::storage::StorageError`. */
|
|
24
|
+
export class LixStorageError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
details;
|
|
27
|
+
constructor(code, message, details) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = "LixStorageError";
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.details = details;
|
|
32
|
+
}
|
|
33
|
+
}
|