@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.
Files changed (50) hide show
  1. package/README.md +40 -10
  2. package/dist/binding-types.d.ts +37 -9
  3. package/dist/binding.browser.d.ts +2 -2
  4. package/dist/binding.browser.js +25 -10
  5. package/dist/binding.node-wasm.d.ts +2 -2
  6. package/dist/binding.node-wasm.js +8 -4
  7. package/dist/binding.node.d.ts +3 -3
  8. package/dist/binding.node.js +70 -13
  9. package/dist/browser-wasm-init.d.ts +14 -0
  10. package/dist/browser-wasm-init.js +16 -0
  11. package/dist/bundled-plugins/plugin_csv.lixplugin +0 -0
  12. package/dist/bundled-plugins/plugin_markdown.lixplugin +0 -0
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +2 -2
  15. package/dist/lix.d.ts +27 -5
  16. package/dist/lix.js +135 -12
  17. package/dist/open-lix.d.ts +4 -5
  18. package/dist/open-lix.js +173 -33
  19. package/dist/remote/client.d.ts +1 -2
  20. package/dist/remote/client.js +58 -1151
  21. package/dist/remote/server-protocol.d.ts +5 -6
  22. package/dist/remote/server-protocol.js +40 -9
  23. package/dist/result.d.ts +6 -13
  24. package/dist/result.js +9 -35
  25. package/dist/snapshot-restore.d.ts +6 -0
  26. package/dist/snapshot-restore.js +101 -0
  27. package/dist/storage-adapter.d.ts +175 -19
  28. package/dist/storage-adapter.js +21 -0
  29. package/dist/types.d.ts +101 -31
  30. package/dist/value.d.ts +1 -0
  31. package/dist/value.js +8 -0
  32. package/dist/wasm/lix_js_sdk.d.ts +119 -24
  33. package/dist/wasm/lix_js_sdk.js +599 -78
  34. package/dist/wasm/lix_js_sdk_bg.wasm +0 -0
  35. package/dist/wasm/lix_js_sdk_bg.wasm.d.ts +47 -6
  36. package/dist/worker/client.d.ts +27 -5
  37. package/dist/worker/client.js +458 -45
  38. package/dist/worker/factory.browser.d.ts +2 -2
  39. package/dist/worker/factory.node.d.ts +3 -2
  40. package/dist/worker/factory.node.js +18 -12
  41. package/dist/worker/host.d.ts +2 -1
  42. package/dist/worker/host.js +283 -37
  43. package/dist/worker/protocol.d.ts +80 -3
  44. package/package.json +6 -10
  45. package/dist/indexeddb-backend.d.ts +0 -19
  46. package/dist/indexeddb-backend.js +0 -121
  47. package/dist/remote/sse.d.ts +0 -12
  48. package/dist/remote/sse.js +0 -87
  49. package/dist/workerd.d.ts +0 -25
  50. 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 = 2;
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: string[];
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;
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
- const columns = stringArray(result.columns, "execute result columns");
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) => decodeWireValue(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, Value } from "./value.js";
2
- import type { ExecuteBatchResult, ExecuteResult, LixValue } from "./types.js";
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 wrapExecuteBatchResult(result: NativeExecuteResult): ExecuteBatchResult;
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 class Row {
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) => Row.fromRaw(result.columns, row.map(fromNativeValue))),
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
- * Operations exposed to a storage adapter while its Lix is open.
3
- *
4
- * This is intentionally narrower than the engine binding. Storage packages
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 LixStorageConnection = {
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
- /** Engine configuration currently supported by external JavaScript storage packages. */
12
- export type LixStorageAdapterConfig = {
13
- kind: "filesystem";
14
- path: string;
15
- syncAllFiles: boolean;
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
- * Protocol implemented by JavaScript storage packages accepted by `openLix()`.
36
+ * JavaScript representation of `lix::storage::StorageSpace`.
19
37
  *
20
- * Applications normally use a concrete package such as
21
- * `@lix-js/storage-filesystem` rather than implementing this directly.
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 LixStorage = {
24
- readonly lixStorage: {
25
- readonly version: 1;
26
- readonly config: LixStorageAdapterConfig;
27
- connect(connection: LixStorageConnection | undefined): void;
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 declare function isLixStorage(value: unknown): value is LixStorage;
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 {};
@@ -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
+ }