@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
package/README.md CHANGED
@@ -18,10 +18,36 @@ import { openLix } from "@lix-js/sdk";
18
18
 
19
19
  const lix = await openLix();
20
20
  const result = await lix.execute("SELECT $1 AS message", ["hello"]);
21
- console.log(result.rows[0]?.get("message"));
21
+ console.log(result.rows[0]?.message);
22
22
  await lix.close();
23
23
  ```
24
24
 
25
+ ## Synchronized local repositories
26
+
27
+ Use sync mode when reads and writes should execute locally while Lix exchanges
28
+ commits with a hosted repository in the background:
29
+
30
+ ```ts
31
+ import { openLix } from "@lix-js/sdk";
32
+ import { OpfsStorage } from "@lix-js/storage-opfs";
33
+
34
+ const lix = await openLix({
35
+ storage: new OpfsStorage({ name: "acme" }),
36
+ server: {
37
+ mode: "sync",
38
+ url: "https://example.com/lix/01936f4e-7b6c-7c3d-8f9a-123456789abc",
39
+ headers: async () => ({
40
+ Authorization: `Bearer ${await accessToken()}`,
41
+ }),
42
+ },
43
+ });
44
+ ```
45
+
46
+ `await lix.execute(...)` means that the local transaction committed. It does
47
+ not mean that the server has received the commit. Current data and new commits
48
+ synchronize automatically; older history and binary content load when needed.
49
+ See [Collaboration and Sync](https://lix.dev/docs/collaboration-and-sync).
50
+
25
51
  ## Remote repositories
26
52
 
27
53
  Use the same Lix client as a thin client against a hosted repository:
@@ -30,7 +56,7 @@ Use the same Lix client as a thin client against a hosted repository:
30
56
  const lix = await openLix({
31
57
  server: {
32
58
  mode: "remote",
33
- url: "https://example.com/repositories/acme",
59
+ url: "https://example.com/lix/01936f4e-7b6c-7c3d-8f9a-123456789abc",
34
60
  headers: async () => ({
35
61
  Authorization: `Bearer ${await accessToken()}`,
36
62
  }),
@@ -78,7 +104,7 @@ const result = await lix.execute(
78
104
  "SELECT content FROM lix_file WHERE path = $1",
79
105
  ["/hello.txt"],
80
106
  );
81
- const bytes = result.rows[0]?.value("content").asBytes();
107
+ const bytes = result.rows[0]?.content as Uint8Array | undefined;
82
108
 
83
109
  console.log(bytes && new TextDecoder().decode(bytes));
84
110
 
@@ -143,12 +169,16 @@ try {
143
169
  ## Notes
144
170
 
145
171
  - `openLix()` opens a fresh in-memory Lix. Install `@lix-js/storage-filesystem` and pass `new FilesystemStorage({ path })` for a filesystem repository directory backed by `<path>/.lix/.internal/rocksdb`.
146
- - In browsers, pass `new IndexedDbStorage({ name })` to persist a complete local Lix across reloads.
147
- - Only one Lix handle may open an IndexedDB storage name at a time, including across browser tabs.
172
+ - In browsers, install a storage provider such as `@lix-js/storage-opfs` and pass its
173
+ storage registration to `openLix()`.
174
+ - JavaScript storage packages register a worker-loadable module URL. That module exports
175
+ `createLixStorageProvider(options)` and returns the SDK's Rust-shaped `LixStorageProvider`:
176
+ `beginRead`, `beginWrite`, read/scan handles, write mutation methods, `commit`, and `rollback`.
177
+ The provider module is loaded beside the Lix Wasm engine in its dedicated worker; it does not
178
+ bundle or select a Lix engine version.
148
179
  - Pass `syncAllFiles: false` to start filesystem sync with no regular repository files, then call `storage.importPaths(["notes/today.md"])` on the `FilesystemStorage` instance to sync selected files. Imported paths are exact repository-relative file paths, not directories or globs.
149
- - In browsers, local mode and remote mode with IndexedDB storage load the Rust
150
- engine as WebAssembly. In remote mode, the local engine contains only client
151
- state.
180
+ - Browser-local storage loads the Rust engine as WebAssembly. Remote mode does
181
+ not open a local storage provider.
152
182
  - `FilesystemStorage` is Node.js-only. Constructing it is safe in
153
183
  shared code, but passing one to `openLix()` in a browser throws an error.
154
184
  - The package is ESM-only.
@@ -161,8 +191,8 @@ try {
161
191
  - Every browser `openLix()` owns one dedicated worker, so database work does
162
192
  not block the page's main thread. Node.js uses the native binding's actor.
163
193
  - Node.js executes installed Component API v1 plugins with the Rust SDK's
164
- Wasmtime runtime. The browser and Workerd bindings currently open without a
165
- component runtime: they can use ordinary Lix storage and SQL, but do not
194
+ Wasmtime runtime. The browser binding currently opens without a component
195
+ runtime: it can use ordinary Lix storage and SQL, but does not
166
196
  execute installed plugins. A browser Component host is a separate follow-up.
167
197
  - A page Content Security Policy only needs to permit the package's same-origin
168
198
  worker. WebAssembly compilation happens inside that worker, so the required
@@ -1,10 +1,16 @@
1
- import type { CreateBranchOptions, CreateBranchReceipt, CreateCheckpointReceipt, UndoReceipt, RedoReceipt, ExecuteOptions, LixBatchOptions, MergeBranchOptions, MergeBranchPreview, MergeBranchReceipt, SwitchBranchOptions, SwitchBranchReceipt, LixTelemetrySpan } from "./types.js";
1
+ import type { CreateBranchOptions, CreateBranchReceipt, UndoReceipt, RedoReceipt, ExecuteOptions, LixBatchOptions, MergeBranchOptions, MergeBranchPreview, MergeBranchReceipt, SwitchBranchOptions, SwitchBranchReceipt, LixTelemetrySpan, LixTelemetryParentContext, LixOpenProgress, LixOpenReport, OpenAnotherSessionOptions, ResultColumn } from "./types.js";
2
2
  import type { NativeLixValue } from "./value.js";
3
- import type { LixStorageAdapterConfig } from "./storage-adapter.js";
3
+ import type { LixStorageProvider } from "./storage-adapter.js";
4
+ export type SyncServerBindingOptions = {
5
+ url: string;
6
+ headers: [string, string][];
7
+ headerProvider?: () => Promise<[string, string][]>;
8
+ fetch?: typeof fetch;
9
+ };
4
10
  export type BindingExecuteResult = {
5
11
  statementIndex?: number;
6
12
  label?: string;
7
- columns: string[];
13
+ columns: ResultColumn[];
8
14
  rows: NativeLixValue[][];
9
15
  rowsAffected: number;
10
16
  notices: Array<{
@@ -19,12 +25,25 @@ export type BindingObserveEvent = {
19
25
  rows: BindingExecuteResult;
20
26
  };
21
27
  export type BindingParam = NativeLixValue;
28
+ export type SnapshotExportBinding = {
29
+ next(): Promise<Uint8Array | null | undefined>;
30
+ cancel(): void | Promise<void>;
31
+ };
32
+ export type SnapshotRestoreBinding<T> = {
33
+ write(chunk: Uint8Array): Promise<void>;
34
+ isComplete(): boolean;
35
+ finish(): Promise<T>;
36
+ cancel(): void | Promise<void>;
37
+ };
22
38
  export type BindingBatchStatement = {
23
39
  sql: string;
24
40
  params: BindingParam[];
25
41
  label?: string;
26
42
  };
27
43
  export type LixBinding = {
44
+ openReport?(): LixOpenReport | undefined;
45
+ setTelemetryParent(parent?: TelemetryParentContext): void;
46
+ openAnotherSession(options: OpenAnotherSessionOptions): Promise<LixBinding>;
28
47
  execute(sql: string, params: BindingParam[], options?: ExecuteOptions): Promise<BindingExecuteResult>;
29
48
  executeBatch(statements: BindingBatchStatement[], options?: LixBatchOptions): Promise<BindingExecuteResult[]>;
30
49
  observe(sql: string, params: BindingParam[]): Promise<ObserveEventsBinding>;
@@ -32,7 +51,6 @@ export type LixBinding = {
32
51
  activeBranchId(): Promise<string>;
33
52
  activeAccountId(): Promise<string>;
34
53
  createBranch(options: CreateBranchOptions): Promise<CreateBranchReceipt>;
35
- createCheckpoint(): Promise<CreateCheckpointReceipt>;
36
54
  undo(): Promise<UndoReceipt>;
37
55
  redo(): Promise<RedoReceipt>;
38
56
  switchBranch(options: SwitchBranchOptions): Promise<SwitchBranchReceipt>;
@@ -40,8 +58,7 @@ export type LixBinding = {
40
58
  mergeBranchPreview(options: MergeBranchOptions): Promise<MergeBranchPreview>;
41
59
  mergeBranch(options: MergeBranchOptions): Promise<MergeBranchReceipt>;
42
60
  syncDiskToLix(): Promise<void>;
43
- /** Explicit snapshot utility available on direct in-memory WASM bindings. */
44
- exportSnapshot?(): Promise<Uint8Array>;
61
+ exportSnapshot?(): SnapshotExportBinding;
45
62
  close(): Promise<void>;
46
63
  };
47
64
  export type LixTransactionBinding = {
@@ -50,13 +67,24 @@ export type LixTransactionBinding = {
50
67
  rollback(): Promise<void>;
51
68
  };
52
69
  export type ObserveEventsBinding = {
70
+ setTelemetryParent(parent?: TelemetryParentContext): void;
53
71
  next(): Promise<BindingObserveEvent | null | undefined>;
54
72
  close(): void;
55
73
  };
56
74
  export type TelemetryDispatch = (span: LixTelemetrySpan) => void;
75
+ export type OpenProgressDispatch = (progress: LixOpenProgress) => void;
76
+ export type TelemetryParentContext = LixTelemetryParentContext;
77
+ export type LixStorageProviderModule = {
78
+ createLixStorageProvider(options: unknown): Promise<LixStorageProvider>;
79
+ };
57
80
  export type LixStorageConfig = {
58
81
  kind: "memory";
59
82
  } | {
60
- kind: "indexedDb";
61
- name: string;
62
- } | LixStorageAdapterConfig;
83
+ kind: "jsStorage";
84
+ moduleUrl: string;
85
+ options: unknown;
86
+ } | {
87
+ kind: "filesystem";
88
+ path: string;
89
+ syncAllFiles: boolean;
90
+ };
@@ -1,2 +1,2 @@
1
- import type { LixStorageConfig, LixBinding, TelemetryDispatch } from "./binding-types.js";
2
- export declare function openLixBinding(storage: LixStorageConfig, telemetry?: TelemetryDispatch): Promise<LixBinding>;
1
+ import type { LixStorageConfig, LixBinding, SyncServerBindingOptions, TelemetryDispatch, TelemetryParentContext, OpenProgressDispatch } from "./binding-types.js";
2
+ export declare function openLixBinding(storage: LixStorageConfig, telemetry?: TelemetryDispatch, telemetryParent?: TelemetryParentContext, server?: SyncServerBindingOptions, openProgress?: OpenProgressDispatch, snapshot?: ReadableStream<Uint8Array>): Promise<LixBinding>;
@@ -1,23 +1,38 @@
1
- import { IndexedDbBackend } from "./indexeddb-backend.js";
1
+ import { initializeBrowserWasm } from "./browser-wasm-init.js";
2
+ import { restoreSnapshot } from "./snapshot-restore.js";
2
3
  // Generated before TypeScript compilation and emitted beside this module.
3
- // @ts-expect-error Generated by build:wasm.
4
- import initWasm, { openIndexedDb, openMemory } from "./wasm/lix_js_sdk.js";
4
+ // @ts-ignore Generated by build:wasm and absent in source-only checks.
5
+ import initWasm, { openJsStorage, openJsStorageFromSnapshot, openMemory, openMemoryFromSnapshot, } from "./wasm/lix_js_sdk.js";
5
6
  let wasmInitialized;
6
7
  function initializeWasm() {
7
- return (wasmInitialized ??= initWasm());
8
+ if (wasmInitialized !== undefined)
9
+ return wasmInitialized;
10
+ const initialization = initializeBrowserWasm(initWasm, new URL("./wasm/lix_js_sdk_bg.wasm", import.meta.url));
11
+ wasmInitialized = initialization;
12
+ return wasmInitialized;
8
13
  }
9
- export async function openLixBinding(storage, telemetry) {
14
+ export async function openLixBinding(storage, telemetry, telemetryParent, server, openProgress, snapshot) {
10
15
  await initializeWasm();
11
16
  switch (storage.kind) {
12
17
  case "memory":
13
- return openMemory(telemetry);
14
- case "indexedDb": {
15
- const backend = await IndexedDbBackend.open(storage.name);
18
+ return (snapshot
19
+ ? restoreSnapshot(snapshot, openMemoryFromSnapshot(telemetry, telemetryParent, openProgress))
20
+ : openMemory(telemetry, telemetryParent, server, openProgress));
21
+ case "jsStorage": {
22
+ const module = (await import(
23
+ /* @vite-ignore */ storage.moduleUrl));
24
+ if (typeof module.createLixStorageProvider !== "function") {
25
+ throw new TypeError(`Storage provider module '${storage.moduleUrl}' does not export createLixStorageProvider()`);
26
+ }
27
+ const provider = await module.createLixStorageProvider(storage.options);
16
28
  try {
17
- return (await openIndexedDb(backend, telemetry));
29
+ const binding = (await (snapshot
30
+ ? restoreSnapshot(snapshot, openJsStorageFromSnapshot(provider, telemetry, telemetryParent, openProgress))
31
+ : openJsStorage(provider, telemetry, telemetryParent, server, openProgress)));
32
+ return binding;
18
33
  }
19
34
  catch (error) {
20
- await backend.close().catch(() => undefined);
35
+ await provider.close().catch(() => undefined);
21
36
  throw error;
22
37
  }
23
38
  }
@@ -1,2 +1,2 @@
1
- import type { LixBinding, TelemetryDispatch } from "./binding-types.js";
2
- export declare function openMemoryWasmBinding(telemetry?: TelemetryDispatch): Promise<LixBinding>;
1
+ import type { LixBinding, TelemetryDispatch, TelemetryParentContext, OpenProgressDispatch } from "./binding-types.js";
2
+ export declare function openMemoryWasmBinding(telemetry?: TelemetryDispatch, telemetryParent?: TelemetryParentContext, openProgress?: OpenProgressDispatch, snapshot?: ReadableStream<Uint8Array>): Promise<LixBinding>;
@@ -1,14 +1,18 @@
1
1
  import { readFile } from "node:fs/promises";
2
+ import { restoreSnapshot } from "./snapshot-restore.js";
2
3
  // Generated before TypeScript compilation and emitted beside this module.
3
- // @ts-expect-error Generated by build:wasm.
4
- import initWasm, { openMemory } from "./wasm/lix_js_sdk.js";
4
+ // @ts-ignore Generated by build:wasm and absent in source-only checks.
5
+ import initWasm, { openMemory, openMemoryFromSnapshot } from "./wasm/lix_js_sdk.js";
5
6
  let wasmInitialized;
6
7
  function initializeWasm() {
7
8
  return (wasmInitialized ??= initWasm({
8
9
  module_or_path: readFile(new URL("./wasm/lix_js_sdk_bg.wasm", import.meta.url)),
9
10
  }));
10
11
  }
11
- export async function openMemoryWasmBinding(telemetry) {
12
+ export async function openMemoryWasmBinding(telemetry, telemetryParent, openProgress, snapshot) {
12
13
  await initializeWasm();
13
- return openMemory(telemetry);
14
+ if (snapshot) {
15
+ return restoreSnapshot(snapshot, openMemoryFromSnapshot(telemetry, telemetryParent, openProgress));
16
+ }
17
+ return openMemory(telemetry, telemetryParent, undefined, openProgress);
14
18
  }
@@ -1,3 +1,3 @@
1
- import type { LixStorageConfig, LixBinding, TelemetryDispatch } from "./binding-types.js";
2
- export declare function openLixBinding(storage: LixStorageConfig, telemetry?: TelemetryDispatch): Promise<LixBinding>;
3
- export declare function openNativeLixBinding(storage: LixStorageConfig, telemetry?: TelemetryDispatch): Promise<LixBinding>;
1
+ import type { LixStorageConfig, LixBinding, SyncServerBindingOptions, TelemetryDispatch, TelemetryParentContext, OpenProgressDispatch } from "./binding-types.js";
2
+ export declare function openLixBinding(storage: LixStorageConfig, telemetry?: TelemetryDispatch, telemetryParent?: TelemetryParentContext, server?: SyncServerBindingOptions, openProgress?: OpenProgressDispatch, snapshot?: ReadableStream<Uint8Array>): Promise<LixBinding>;
3
+ export declare function openNativeLixBinding(storage: LixStorageConfig, telemetry?: TelemetryDispatch, telemetryParent?: TelemetryParentContext, server?: SyncServerBindingOptions, openProgress?: OpenProgressDispatch, snapshot?: ReadableStream<Uint8Array>): Promise<LixBinding>;
@@ -1,6 +1,32 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { restoreSnapshot } from "./snapshot-restore.js";
5
+ function normalizeNativeObserveEvents(events) {
6
+ return new Proxy(events, {
7
+ get(target, property, receiver) {
8
+ if (property === "setTelemetryParent") {
9
+ return (parent) => target.setTelemetryParent(parent === undefined ? undefined : JSON.stringify(parent));
10
+ }
11
+ const value = Reflect.get(target, property, receiver);
12
+ return typeof value === "function" ? value.bind(target) : value;
13
+ },
14
+ });
15
+ }
16
+ function normalizeNativeBinding(binding) {
17
+ return new Proxy(binding, {
18
+ get(target, property, receiver) {
19
+ if (property === "setTelemetryParent") {
20
+ return (parent) => target.setTelemetryParent(parent === undefined ? undefined : JSON.stringify(parent));
21
+ }
22
+ if (property === "observe") {
23
+ return async (sql, params) => normalizeNativeObserveEvents(await target.observe(sql, params));
24
+ }
25
+ const value = Reflect.get(target, property, receiver);
26
+ return typeof value === "function" ? value.bind(target) : value;
27
+ },
28
+ });
29
+ }
4
30
  const require = createRequire(import.meta.url);
5
31
  const localNativePath = fileURLToPath(new URL("../lix_js_sdk.node", import.meta.url));
6
32
  const nativePackages = {
@@ -30,6 +56,12 @@ function resolveNativePath() {
30
56
  }
31
57
  let addon;
32
58
  let addonLoadError;
59
+ class NativeAddonUnavailableError extends Error {
60
+ constructor(message, options) {
61
+ super(message, options);
62
+ this.name = "NativeAddonUnavailableError";
63
+ }
64
+ }
33
65
  function loadNativeAddon() {
34
66
  if (addon)
35
67
  return addon;
@@ -40,51 +72,76 @@ function loadNativeAddon() {
40
72
  return addon;
41
73
  }
42
74
  catch (cause) {
43
- const error = new Error(`Failed to load @lix-js/sdk native addon for ${process.platform}-${process.arch}. ` +
75
+ const error = new NativeAddonUnavailableError(`Failed to load @lix-js/sdk native addon for ${process.platform}-${process.arch}. ` +
44
76
  "This package requires the matching optional native binary package. " +
45
77
  "Run `npm run build` from packages/js-sdk for local development, or install a release that includes your platform binary.", { cause });
46
78
  addonLoadError = error;
47
79
  throw error;
48
80
  }
49
81
  }
50
- export async function openLixBinding(storage, telemetry) {
82
+ export async function openLixBinding(storage, telemetry, telemetryParent, server, openProgress, snapshot) {
51
83
  try {
52
- return await openNativeLixBinding(storage, telemetry);
84
+ return await openNativeLixBinding(storage, telemetry, telemetryParent, server, openProgress, snapshot);
53
85
  }
54
86
  catch (nativeError) {
55
- if (storage.kind !== "memory")
87
+ if (!(nativeError instanceof NativeAddonUnavailableError) ||
88
+ storage.kind !== "memory" ||
89
+ server !== undefined) {
56
90
  throw nativeError;
91
+ }
57
92
  try {
58
93
  const { openMemoryWasmBinding } = await import("./binding.node-wasm.js");
59
- return await openMemoryWasmBinding(telemetry);
94
+ return await openMemoryWasmBinding(telemetry, telemetryParent, openProgress, snapshot);
60
95
  }
61
96
  catch (wasmError) {
62
97
  throw new AggregateError([nativeError, wasmError], "Failed to open in-memory Lix with either the native or WebAssembly binding.");
63
98
  }
64
99
  }
65
100
  }
66
- export async function openNativeLixBinding(storage, telemetry) {
101
+ export async function openNativeLixBinding(storage, telemetry, telemetryParent, server, openProgress, snapshot) {
102
+ if (server?.fetch) {
103
+ throw new TypeError("Custom sync fetch is only supported by the browser worker");
104
+ }
105
+ const nativeOpenProgress = openProgress
106
+ ? (progressJson) => {
107
+ try {
108
+ openProgress(JSON.parse(progressJson));
109
+ }
110
+ catch {
111
+ // Open progress is observational and cannot fail repository opening.
112
+ }
113
+ }
114
+ : undefined;
67
115
  switch (storage.kind) {
68
116
  case "memory": {
69
117
  const nativeAddon = loadNativeAddon();
70
118
  const nativeTelemetry = telemetry
71
119
  ? (spanJson) => telemetry(JSON.parse(spanJson))
72
120
  : undefined;
73
- if (nativeTelemetry)
74
- return nativeAddon.Lix.openMemory(nativeTelemetry);
75
- return nativeAddon.Lix.openMemory();
121
+ if (snapshot) {
122
+ const restore = nativeAddon.Lix.openMemoryFromSnapshot(nativeTelemetry, telemetryParent ? JSON.stringify(telemetryParent) : undefined, nativeOpenProgress);
123
+ return normalizeNativeBinding(await restoreSnapshot(snapshot, restore));
124
+ }
125
+ if (nativeTelemetry) {
126
+ return normalizeNativeBinding(await nativeAddon.Lix.openMemory(nativeTelemetry, telemetryParent ? JSON.stringify(telemetryParent) : undefined, server?.url, server?.headers, nativeOpenProgress));
127
+ }
128
+ return normalizeNativeBinding(await nativeAddon.Lix.openMemory(undefined, undefined, server?.url, server?.headers, nativeOpenProgress));
76
129
  }
77
- case "indexedDb":
78
- throw new Error("IndexedDbStorage is only available in browsers");
130
+ case "jsStorage":
131
+ throw new Error("JavaScript storage providers are only available in browsers");
79
132
  case "filesystem": {
80
133
  const nativeAddon = loadNativeAddon();
81
134
  const nativeTelemetry = telemetry
82
135
  ? (spanJson) => telemetry(JSON.parse(spanJson))
83
136
  : undefined;
137
+ if (snapshot) {
138
+ const restore = nativeAddon.Lix.openFilesystemStorageFromSnapshot(storage.path, storage.syncAllFiles, nativeTelemetry, telemetryParent ? JSON.stringify(telemetryParent) : undefined, nativeOpenProgress);
139
+ return normalizeNativeBinding(await restoreSnapshot(snapshot, restore));
140
+ }
84
141
  if (nativeTelemetry) {
85
- return nativeAddon.Lix.openFilesystemStorage(storage.path, storage.syncAllFiles, nativeTelemetry);
142
+ return normalizeNativeBinding(await nativeAddon.Lix.openFilesystemStorage(storage.path, storage.syncAllFiles, nativeTelemetry, telemetryParent ? JSON.stringify(telemetryParent) : undefined, server?.url, server?.headers, nativeOpenProgress));
86
143
  }
87
- return nativeAddon.Lix.openFilesystemStorage(storage.path, storage.syncAllFiles);
144
+ return normalizeNativeBinding(await nativeAddon.Lix.openFilesystemStorage(storage.path, storage.syncAllFiles, undefined, undefined, server?.url, server?.headers, nativeOpenProgress));
88
145
  }
89
146
  }
90
147
  }
@@ -0,0 +1,14 @@
1
+ type WasmInitializer = (options: {
2
+ module_or_path: URL;
3
+ }) => Promise<unknown>;
4
+ /**
5
+ * Initializes one fingerprinted Wasm asset without racing the browser's shared
6
+ * HTTP cache across tabs.
7
+ *
8
+ * Chromium can abort one consumer when separate workers cold-load the same
9
+ * large response concurrently. The lock lasts only for the initial streaming
10
+ * compilation. Once it releases, later workers consume the completed immutable
11
+ * cache entry and compile independently.
12
+ */
13
+ export declare function initializeBrowserWasm(initialize: WasmInitializer, moduleUrl: URL): Promise<unknown>;
14
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Initializes one fingerprinted Wasm asset without racing the browser's shared
3
+ * HTTP cache across tabs.
4
+ *
5
+ * Chromium can abort one consumer when separate workers cold-load the same
6
+ * large response concurrently. The lock lasts only for the initial streaming
7
+ * compilation. Once it releases, later workers consume the completed immutable
8
+ * cache entry and compile independently.
9
+ */
10
+ export function initializeBrowserWasm(initialize, moduleUrl) {
11
+ const lockManager = globalThis.navigator.locks;
12
+ const run = () => initialize({ module_or_path: moduleUrl });
13
+ if (!lockManager)
14
+ return run();
15
+ return lockManager.request(`lix:browser-wasm:${moduleUrl.href}`, { mode: "exclusive" }, run);
16
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { IndexedDbStorage, Lix, LixTransaction, ObserveEvents, openLix, } from "./open-lix.js";
2
- export type { LixStorage, LixStorageAdapterConfig, LixStorageConnection, } from "./storage-adapter.js";
1
+ export { Lix, LixTransaction, ObserveEvents, openLix } from "./open-lix.js";
2
+ export type { LixStorage, LixStorageBound, LixStorageChangeWatch, LixStorageCommitResult, LixStorageErrorCode, LixStorageGetManyRequest, LixStorageKeyRange, LixStoragePrecondition, LixStorageProjectedValue, LixStorageProvider, LixStorageProviderRegistration, LixStoragePutEntry, LixStorageRead, LixStorageReadEntry, LixStorageReadOptions, LixStorageScanOrder, LixStorageScanSource, LixStorageSpace, LixStorageWrite, LixStorageWriteOptions, LixStorageWriteStats, } from "./storage-adapter.js";
3
+ export { LixStorageError } from "./storage-adapter.js";
3
4
  export { bundledPluginArchives, type BundledPluginArchive, } from "./bundled-plugins.js";
4
- export { Row } from "./result.js";
5
5
  export { Value } from "./value.js";
6
- export type { CreateBranchOptions, CreateBranchReceipt, CreateCheckpointReceipt, RedoReceipt, ExecuteOptions, ExecuteResult, LixBatchOptions, LixBatchStatement, IndexedDbStorageOptions, JsonValue, LixValue, MergeBranchOptions, MergeBranchOutcome, MergeBranchPreview, MergeBranchReceipt, MergeChangeStats, MergeConflict, MergeConflictSide, ObserveEvent, OpenLixOptions, LixTelemetryOptions, LixTelemetrySpan, RemoteLixFetch, RemoteLixServerOptions, UndoReceipt, SqlParam, SwitchBranchOptions, SwitchBranchReceipt, } from "./types.js";
6
+ export type { CreateBranchOptions, CreateBranchReceipt, RedoReceipt, ExecuteOptions, ExecuteResult, ExecuteBatchResult, LixBatchOptions, LixBatchStatement, JsonValue, LixValue, ResultArrayRow, ResultColumn, ResultColumnType, ResultObjectRow, ResultRow, MergeBranchOptions, MergeBranchOutcome, MergeBranchPreview, MergeBranchReceipt, MergeChangeStats, MergeConflict, MergeConflictSide, ObserveEvent, OpenLixOptions, OpenAnotherSessionOptions, LixTelemetryOptions, LixTelemetryParentContext, LixTelemetrySpan, LixTelemetrySpanLink, LixOpenMigrationReport, LixOpenPhase, LixOpenProgress, LixOpenProgressOptions, LixOpenReport, RemoteLixFetch, RemoteLixServerOptions, SyncLixServerOptions, UndoReceipt, SqlParam, SwitchBranchOptions, SwitchBranchReceipt, } from "./types.js";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { IndexedDbStorage, Lix, LixTransaction, ObserveEvents, openLix, } from "./open-lix.js";
1
+ export { Lix, LixTransaction, ObserveEvents, openLix } from "./open-lix.js";
2
+ export { LixStorageError } from "./storage-adapter.js";
2
3
  export { bundledPluginArchives, } from "./bundled-plugins.js";
3
- export { Row } from "./result.js";
4
4
  export { Value } from "./value.js";
package/dist/lix.d.ts CHANGED
@@ -1,12 +1,27 @@
1
1
  import type { LixBinding, LixTransactionBinding, ObserveEventsBinding } from "./binding-types.js";
2
- import type { CreateBranchOptions, CreateBranchReceipt, CreateCheckpointReceipt, RedoReceipt, ExecuteOptions, ExecuteResult, ExecuteBatchResult, LixBatchOptions, LixBatchStatement, MergeBranchOptions, MergeBranchPreview, MergeBranchReceipt, ObserveEvent, SqlParam, SwitchBranchOptions, SwitchBranchReceipt, UndoReceipt } from "./types.js";
2
+ import type { CreateBranchOptions, CreateBranchReceipt, RedoReceipt, ExecuteOptions, ExecuteResult, ExecuteBatchResult, LixBatchOptions, LixBatchStatement, MergeBranchOptions, MergeBranchPreview, MergeBranchReceipt, ObserveEvent, OpenAnotherSessionOptions, LixOpenReport, SqlParam, ResultArrayRow, ResultObjectRow, ResultRow, SwitchBranchOptions, SwitchBranchReceipt, UndoReceipt } from "./types.js";
3
3
  export declare class Lix {
4
4
  #private;
5
5
  private readonly binding;
6
+ readonly openReport: LixOpenReport | undefined;
6
7
  private closePromise;
7
8
  constructor(binding: LixBinding);
8
- execute(sql: string, params?: SqlParam[], options?: ExecuteOptions): Promise<ExecuteResult>;
9
- executeBatch(statements: readonly LixBatchStatement[], options?: LixBatchOptions): Promise<readonly ExecuteBatchResult[]>;
9
+ /** Opens an independent session over the same repository storage. */
10
+ openAnotherSession(options?: OpenAnotherSessionOptions): Promise<Lix>;
11
+ execute(sql: string, params: SqlParam[] | undefined, options: ExecuteOptions & {
12
+ rowMode: "array";
13
+ }): Promise<ExecuteResult<ResultArrayRow>>;
14
+ execute<TRow extends object = ResultObjectRow>(sql: string, params?: SqlParam[], options?: ExecuteOptions & {
15
+ rowMode?: "object";
16
+ }): Promise<ExecuteResult<TRow>>;
17
+ execute(sql: string, params: SqlParam[] | undefined, options?: ExecuteOptions): Promise<ExecuteResult<ResultRow>>;
18
+ executeBatch(statements: readonly LixBatchStatement[], options: LixBatchOptions & {
19
+ rowMode: "array";
20
+ }): Promise<readonly ExecuteBatchResult<ResultArrayRow>[]>;
21
+ executeBatch(statements: readonly LixBatchStatement[], options?: LixBatchOptions & {
22
+ rowMode?: "object";
23
+ }): Promise<readonly ExecuteBatchResult<ResultObjectRow>[]>;
24
+ executeBatch(statements: readonly LixBatchStatement[], options?: LixBatchOptions): Promise<readonly ExecuteBatchResult<ResultRow>[]>;
10
25
  observe(sql: string, params?: SqlParam[]): ObserveEvents;
11
26
  beginTransaction(): Promise<LixTransaction>;
12
27
  activeBranchId(): Promise<string>;
@@ -14,7 +29,8 @@ export declare class Lix {
14
29
  /** Subscribes to successful branch switches made through this Lix handle. */
15
30
  subscribeActiveBranch(listener: () => void): () => void;
16
31
  createBranch(options: CreateBranchOptions): Promise<CreateBranchReceipt>;
17
- createCheckpoint(): Promise<CreateCheckpointReceipt>;
32
+ /** Streams a deterministic snapshot of the complete Lix. */
33
+ exportSnapshot(): ReadableStream<Uint8Array>;
18
34
  undo(): Promise<UndoReceipt>;
19
35
  redo(): Promise<RedoReceipt>;
20
36
  switchBranch(options: SwitchBranchOptions): Promise<SwitchBranchReceipt>;
@@ -37,7 +53,13 @@ export declare class LixTransaction {
37
53
  private finishPromise;
38
54
  private finished;
39
55
  constructor(binding: LixTransactionBinding, onFinish?: () => void);
40
- execute(sql: string, params?: SqlParam[], options?: ExecuteOptions): Promise<ExecuteResult>;
56
+ execute(sql: string, params: SqlParam[] | undefined, options: ExecuteOptions & {
57
+ rowMode: "array";
58
+ }): Promise<ExecuteResult<ResultArrayRow>>;
59
+ execute<TRow extends object = ResultObjectRow>(sql: string, params?: SqlParam[], options?: ExecuteOptions & {
60
+ rowMode?: "object";
61
+ }): Promise<ExecuteResult<TRow>>;
62
+ execute(sql: string, params: SqlParam[] | undefined, options?: ExecuteOptions): Promise<ExecuteResult<ResultRow>>;
41
63
  commit(): Promise<void>;
42
64
  rollback(): Promise<void>;
43
65
  private finish;