@lix-js/sdk 0.12.3 → 0.15.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 +54 -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 +20 -13
  5. package/dist/binding.node-wasm.d.ts +2 -2
  6. package/dist/binding.node-wasm.js +9 -11
  7. package/dist/binding.node.d.ts +3 -3
  8. package/dist/binding.node.js +70 -13
  9. package/dist/bundled-plugins/plugin_csv.lixplugin +0 -0
  10. package/dist/bundled-plugins/plugin_markdown.lixplugin +0 -0
  11. package/dist/index.d.ts +4 -4
  12. package/dist/index.js +2 -2
  13. package/dist/lix.d.ts +27 -5
  14. package/dist/lix.js +125 -6
  15. package/dist/open-lix.d.ts +4 -5
  16. package/dist/open-lix.js +174 -33
  17. package/dist/remote/client.d.ts +1 -2
  18. package/dist/remote/client.js +40 -1151
  19. package/dist/remote/server-protocol.d.ts +5 -6
  20. package/dist/remote/server-protocol.js +40 -6
  21. package/dist/result.d.ts +6 -13
  22. package/dist/result.js +9 -35
  23. package/dist/snapshot-restore.d.ts +6 -0
  24. package/dist/snapshot-restore.js +101 -0
  25. package/dist/storage-adapter.d.ts +175 -19
  26. package/dist/storage-adapter.js +21 -0
  27. package/dist/types.d.ts +104 -34
  28. package/dist/value.d.ts +1 -0
  29. package/dist/value.js +8 -0
  30. package/dist/wasm/lix_js_sdk.d.ts +121 -24
  31. package/dist/wasm/lix_js_sdk.js +606 -78
  32. package/dist/wasm/lix_js_sdk_bg.wasm +0 -0
  33. package/dist/wasm/lix_js_sdk_bg.wasm.d.ts +48 -6
  34. package/dist/wasm-init.d.ts +2 -0
  35. package/dist/wasm-init.js +23 -0
  36. package/dist/worker/client.d.ts +31 -5
  37. package/dist/worker/client.js +541 -45
  38. package/dist/worker/factory.browser.d.ts +2 -2
  39. package/dist/worker/factory.node.d.ts +2 -2
  40. package/dist/worker/factory.node.js +3 -10
  41. package/dist/worker/host.d.ts +4 -2
  42. package/dist/worker/host.js +401 -43
  43. package/dist/worker/protocol.d.ts +107 -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,13 +1,57 @@
1
1
  import type { BindingBatchStatement, BindingParam, LixStorageConfig } from "../binding-types.js";
2
- import type { CreateBranchOptions, ExecuteOptions, LixBatchOptions, MergeBranchOptions, SwitchBranchOptions, LixTelemetrySpan } from "../types.js";
2
+ import type { CreateBranchOptions, ExecuteOptions, LixBatchOptions, MergeBranchOptions, SwitchBranchOptions, LixTelemetrySpan, LixTelemetryParentContext, LixOpenProgress, OpenAnotherSessionOptions } from "../types.js";
3
+ export type WorkerSyncServerOptions = {
4
+ url: string;
5
+ headers?: [string, string][];
6
+ dynamicHeaders: boolean;
7
+ customFetch: boolean;
8
+ };
9
+ export type WorkerSyncFetchRequest = {
10
+ url: string;
11
+ method: string;
12
+ headers: [string, string][];
13
+ body?: string | Uint8Array;
14
+ credentials?: RequestCredentials;
15
+ } & ({
16
+ responseMode: "buffered";
17
+ responseLimit: number;
18
+ } | {
19
+ responseMode: "stream";
20
+ });
21
+ type WorkerSyncFetchResponseHead = {
22
+ status: number;
23
+ statusText: string;
24
+ headers: [string, string][];
25
+ };
26
+ export type WorkerSyncFetchResponse = WorkerSyncFetchResponseHead & ({
27
+ streaming: true;
28
+ } | {
29
+ streaming?: false;
30
+ body: Uint8Array;
31
+ });
3
32
  export type WorkerRequest = {
4
33
  id: number;
34
+ sessionId: number;
35
+ telemetryParent?: LixTelemetryParentContext;
5
36
  operation: WorkerOperation;
6
37
  };
7
38
  export type WorkerOperation = {
8
39
  kind: "open";
9
40
  storage: LixStorageConfig;
10
41
  telemetryEnabled: boolean;
42
+ progressEnabled: boolean;
43
+ snapshotId?: number;
44
+ server?: WorkerSyncServerOptions;
45
+ } | {
46
+ kind: "openSnapshot.write";
47
+ snapshotId: number;
48
+ chunk: Uint8Array;
49
+ } | {
50
+ kind: "openSnapshot.finish";
51
+ snapshotId: number;
52
+ } | {
53
+ kind: "openAnotherSession";
54
+ options: OpenAnotherSessionOptions;
11
55
  } | {
12
56
  kind: "execute";
13
57
  sql: string;
@@ -38,8 +82,6 @@ export type WorkerOperation = {
38
82
  } | {
39
83
  kind: "createBranch";
40
84
  options: CreateBranchOptions;
41
- } | {
42
- kind: "createCheckpoint";
43
85
  } | {
44
86
  kind: "undo";
45
87
  } | {
@@ -58,6 +100,14 @@ export type WorkerOperation = {
58
100
  paths: string[];
59
101
  } | {
60
102
  kind: "syncDiskToLix";
103
+ } | {
104
+ kind: "exportSnapshot";
105
+ } | {
106
+ kind: "exportSnapshot.next";
107
+ exportId: number;
108
+ } | {
109
+ kind: "exportSnapshot.cancel";
110
+ exportId: number;
61
111
  } | {
62
112
  kind: "observe";
63
113
  sql: string;
@@ -74,6 +124,43 @@ export type WorkerNotification = {
74
124
  } | {
75
125
  kind: "observe.close";
76
126
  observeId: number;
127
+ } | {
128
+ kind: "openSnapshot.cancel";
129
+ snapshotId: number;
130
+ } | {
131
+ kind: "sync.headers.result";
132
+ requestId: number;
133
+ result: {
134
+ ok: true;
135
+ headers: [string, string][];
136
+ } | {
137
+ ok: false;
138
+ error: SerializedWorkerError;
139
+ };
140
+ } | {
141
+ kind: "sync.fetch.result";
142
+ requestId: number;
143
+ result: {
144
+ ok: true;
145
+ response: WorkerSyncFetchResponse;
146
+ } | {
147
+ ok: false;
148
+ error: SerializedWorkerError;
149
+ };
150
+ } | {
151
+ kind: "sync.fetch.stream.result";
152
+ requestId: number;
153
+ result: {
154
+ ok: true;
155
+ done: true;
156
+ } | {
157
+ ok: true;
158
+ done: false;
159
+ chunk: Uint8Array;
160
+ } | {
161
+ ok: false;
162
+ error: SerializedWorkerError;
163
+ };
77
164
  };
78
165
  export type WorkerInput = WorkerRequest | WorkerNotification;
79
166
  export type WorkerConnection = {
@@ -107,6 +194,23 @@ export type WorkerResponse = {
107
194
  } | {
108
195
  kind: "telemetry";
109
196
  span: LixTelemetrySpan;
197
+ } | {
198
+ kind: "open.progress";
199
+ progress: LixOpenProgress;
200
+ } | {
201
+ kind: "sync.headers";
202
+ requestId: number;
203
+ } | {
204
+ kind: "sync.fetch";
205
+ requestId: number;
206
+ request: WorkerSyncFetchRequest;
207
+ } | {
208
+ kind: "sync.fetch.stream.pull";
209
+ requestId: number;
210
+ } | {
211
+ kind: "sync.fetch.cancel";
212
+ requestId: number;
110
213
  };
111
214
  export declare function serializeWorkerError(error: unknown): SerializedWorkerError;
112
215
  export declare function deserializeWorkerError(error: SerializedWorkerError): Error;
216
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lix-js/sdk",
3
3
  "type": "module",
4
- "version": "0.12.3",
4
+ "version": "0.15.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -12,11 +12,6 @@
12
12
  "types": "./dist/index.d.ts",
13
13
  "default": "./dist/index.js"
14
14
  },
15
- "./workerd": {
16
- "types": "./dist/workerd.d.ts",
17
- "workerd": "./dist/workerd.js",
18
- "default": "./dist/workerd.js"
19
- },
20
15
  "./server-protocol": {
21
16
  "types": "./dist/remote/server-protocol.d.ts",
22
17
  "default": "./dist/remote/server-protocol.js"
@@ -43,6 +38,7 @@
43
38
  "build:wasm:dev": "LIX_WASM_PROFILE=dev node ./scripts/build-wasm.js",
44
39
  "build:plugins": "node ./scripts/build-bundled-plugins.js",
45
40
  "benchmark:plugin-reopen": "node ./scripts/benchmark-plugin-reopen.mjs",
41
+ "benchmark:storage-boundary": "LIX_WASM_STORAGE_BENCH=1 npm run build:browser && LIX_WASM_STORAGE_BENCH=1 vitest run --config vitest.browser.config.ts src/storage-bridge.bench.browser.test.ts",
46
42
  "clean": "node ./scripts/clean.js",
47
43
  "prepare:native-package": "node ./scripts/prepare-native-package.js",
48
44
  "build:ts": "tsc -p tsconfig.json",
@@ -53,10 +49,10 @@
53
49
  "typecheck": "tsc -p tsconfig.test.json --noEmit"
54
50
  },
55
51
  "optionalDependencies": {
56
- "@lix-js/sdk-darwin-arm64": "0.12.3",
57
- "@lix-js/sdk-linux-arm64": "0.12.3",
58
- "@lix-js/sdk-linux-x64": "0.12.3",
59
- "@lix-js/sdk-win32-x64": "0.12.3"
52
+ "@lix-js/sdk-darwin-arm64": "0.15.0",
53
+ "@lix-js/sdk-linux-arm64": "0.15.0",
54
+ "@lix-js/sdk-linux-x64": "0.15.0",
55
+ "@lix-js/sdk-win32-x64": "0.15.0"
60
56
  },
61
57
  "devDependencies": {
62
58
  "@vitest/browser-playwright": "4.1.10",
@@ -1,19 +0,0 @@
1
- type IndexedDbEntry = {
2
- key: Uint8Array;
3
- value: Uint8Array;
4
- };
5
- type IndexedDbChanges = {
6
- deletes: Uint8Array[];
7
- puts: IndexedDbEntry[];
8
- strictDurability: boolean;
9
- };
10
- /** Internal worker-local bridge used by the WASM IndexedDB storage adapter. */
11
- export declare class IndexedDbBackend {
12
- #private;
13
- private constructor();
14
- static open(name: string): Promise<IndexedDbBackend>;
15
- loadEntries(): Promise<IndexedDbEntry[]>;
16
- applyChanges(changes: IndexedDbChanges): Promise<void>;
17
- close(): Promise<void>;
18
- }
19
- export {};
@@ -1,121 +0,0 @@
1
- const DATABASE_VERSION = 1;
2
- const ENTRY_STORE = "entries";
3
- /** Internal worker-local bridge used by the WASM IndexedDB storage adapter. */
4
- export class IndexedDbBackend {
5
- #database;
6
- #releaseLock;
7
- #closed = false;
8
- constructor(database, releaseLock) {
9
- this.#database = database;
10
- this.#releaseLock = releaseLock;
11
- }
12
- static async open(name) {
13
- const releaseLock = await acquireDatabaseLock(name);
14
- try {
15
- const request = indexedDB.open(name, DATABASE_VERSION);
16
- request.onupgradeneeded = () => {
17
- if (!request.result.objectStoreNames.contains(ENTRY_STORE)) {
18
- request.result.createObjectStore(ENTRY_STORE);
19
- }
20
- };
21
- return new IndexedDbBackend(await openDatabase(request), releaseLock);
22
- }
23
- catch (error) {
24
- releaseLock();
25
- throw error;
26
- }
27
- }
28
- async loadEntries() {
29
- const transaction = this.#database.transaction(ENTRY_STORE, "readonly");
30
- const store = transaction.objectStore(ENTRY_STORE);
31
- const entries = [];
32
- await new Promise((resolve, reject) => {
33
- const request = store.openCursor();
34
- request.onerror = () => reject(request.error ?? transaction.error);
35
- request.onsuccess = () => {
36
- const cursor = request.result;
37
- if (!cursor) {
38
- resolve();
39
- return;
40
- }
41
- try {
42
- entries.push({
43
- key: copyBytes(cursor.key, "IndexedDB entry key"),
44
- value: copyBytes(cursor.value, "IndexedDB entry value"),
45
- });
46
- }
47
- catch (error) {
48
- transaction.abort();
49
- reject(error);
50
- return;
51
- }
52
- cursor.continue();
53
- };
54
- });
55
- await transactionDone(transaction);
56
- return entries;
57
- }
58
- async applyChanges(changes) {
59
- const transaction = this.#database.transaction(ENTRY_STORE, "readwrite", {
60
- durability: changes.strictDurability ? "strict" : "default",
61
- });
62
- const store = transaction.objectStore(ENTRY_STORE);
63
- for (const key of changes.deletes)
64
- store.delete(binaryKey(key));
65
- for (const entry of changes.puts) {
66
- store.put(entry.value, binaryKey(entry.key));
67
- }
68
- await transactionDone(transaction);
69
- }
70
- async close() {
71
- if (this.#closed)
72
- return;
73
- this.#closed = true;
74
- this.#database.close();
75
- this.#releaseLock();
76
- }
77
- }
78
- function acquireDatabaseLock(name) {
79
- let release;
80
- const released = new Promise((resolve) => {
81
- release = resolve;
82
- });
83
- return new Promise((resolve, reject) => {
84
- void navigator.locks
85
- .request(`lix:indexeddb:${name}`, { mode: "exclusive", ifAvailable: true }, async (lock) => {
86
- if (!lock) {
87
- reject(new Error(`IndexedDB storage '${name}' is already open`));
88
- return;
89
- }
90
- resolve(release);
91
- await released;
92
- })
93
- .catch(reject);
94
- });
95
- }
96
- function openDatabase(request) {
97
- return new Promise((resolve, reject) => {
98
- request.onsuccess = () => resolve(request.result);
99
- request.onerror = () => reject(request.error);
100
- request.onblocked = () => reject(new Error("IndexedDB database open was blocked"));
101
- });
102
- }
103
- function binaryKey(value) {
104
- return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
105
- }
106
- function transactionDone(transaction) {
107
- return new Promise((resolve, reject) => {
108
- transaction.oncomplete = () => resolve();
109
- transaction.onabort = () => reject(transaction.error);
110
- transaction.onerror = () => reject(transaction.error);
111
- });
112
- }
113
- function copyBytes(value, label) {
114
- if (value instanceof ArrayBuffer) {
115
- return new Uint8Array(value.slice(0));
116
- }
117
- if (ArrayBuffer.isView(value)) {
118
- return new Uint8Array(value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength));
119
- }
120
- throw new Error(`${label} is not binary data`);
121
- }
@@ -1,12 +0,0 @@
1
- export type SseEvent = {
2
- event: string;
3
- data: string;
4
- retry?: number;
5
- };
6
- /**
7
- * Parses a fetch response body as a stream of server-sent events.
8
- *
9
- * The caller remains responsible for validating the response status and
10
- * content type before passing its body here.
11
- */
12
- export declare function readSseEvents(stream: ReadableStream<Uint8Array>): AsyncGenerator<SseEvent, void, void>;
@@ -1,87 +0,0 @@
1
- /**
2
- * Parses a fetch response body as a stream of server-sent events.
3
- *
4
- * The caller remains responsible for validating the response status and
5
- * content type before passing its body here.
6
- */
7
- export async function* readSseEvents(stream) {
8
- const decoder = new TextDecoder();
9
- const reader = stream.getReader();
10
- let bufferedText = "";
11
- let eventName = "";
12
- let retry;
13
- let dataLines = [];
14
- function processLine(line) {
15
- if (line.length === 0) {
16
- return dispatchEvent();
17
- }
18
- if (line.startsWith(":")) {
19
- return undefined;
20
- }
21
- const colonIndex = line.indexOf(":");
22
- const field = colonIndex === -1 ? line : line.slice(0, colonIndex);
23
- let value = colonIndex === -1 ? "" : line.slice(colonIndex + 1);
24
- if (value.startsWith(" "))
25
- value = value.slice(1);
26
- switch (field) {
27
- case "event":
28
- eventName = value;
29
- break;
30
- case "data":
31
- dataLines.push(value);
32
- break;
33
- case "retry": {
34
- if (/^\d+$/.test(value)) {
35
- const parsed = Number(value);
36
- if (Number.isSafeInteger(parsed))
37
- retry = parsed;
38
- }
39
- break;
40
- }
41
- // Event IDs and extension fields are intentionally ignored. Remote Lix
42
- // observations use their payload sequence for resumption instead.
43
- default:
44
- break;
45
- }
46
- return undefined;
47
- }
48
- function dispatchEvent() {
49
- const hasData = dataLines.length !== 0;
50
- const event = hasData
51
- ? {
52
- event: eventName.length === 0 ? "message" : eventName,
53
- data: dataLines.join("\n"),
54
- ...(retry === undefined ? {} : { retry }),
55
- }
56
- : undefined;
57
- eventName = "";
58
- retry = undefined;
59
- dataLines = [];
60
- return event;
61
- }
62
- try {
63
- while (true) {
64
- const { done, value } = await reader.read();
65
- if (done)
66
- break;
67
- bufferedText += decoder.decode(value, { stream: true });
68
- let newlineIndex = bufferedText.indexOf("\n");
69
- while (newlineIndex !== -1) {
70
- let line = bufferedText.slice(0, newlineIndex);
71
- bufferedText = bufferedText.slice(newlineIndex + 1);
72
- if (line.endsWith("\r"))
73
- line = line.slice(0, -1);
74
- const event = processLine(line);
75
- if (event !== undefined)
76
- yield event;
77
- newlineIndex = bufferedText.indexOf("\n");
78
- }
79
- }
80
- decoder.decode();
81
- // SSE dispatches only at a blank line. An incomplete final frame indicates
82
- // a truncated connection and is deliberately left for the caller to retry.
83
- }
84
- finally {
85
- reader.releaseLock();
86
- }
87
- }
package/dist/workerd.d.ts DELETED
@@ -1,25 +0,0 @@
1
- import type { LixBinding } from "./binding-types.js";
2
- export type SqlScriptPlan = {
3
- statements: SqlScriptStatement[];
4
- };
5
- export type SqlScriptStatement = {
6
- sql: string;
7
- paramStart: number;
8
- paramEnd: number;
9
- };
10
- export interface OpenMemoryLixOptions {
11
- snapshot?: Uint8Array;
12
- }
13
- export interface WorkerdLixBinding extends LixBinding {
14
- exportSnapshot(): Promise<Uint8Array>;
15
- }
16
- /** Parses SQL using the same DataFusion dialect and transaction policy as Lix. */
17
- export declare function parseSqlScript(sql: string, providedParamCount: number): SqlScriptPlan;
18
- /**
19
- * Opens an in-memory Lix binding directly in a Cloudflare Worker isolate.
20
- *
21
- * The browser SDK intentionally uses Web Workers. Workerd does not implement
22
- * that API, so this entry point initializes the precompiled module directly.
23
- * Plugin execution is deliberately unavailable in this environment.
24
- */
25
- export declare function openMemoryLix(options?: OpenMemoryLixOptions): Promise<WorkerdLixBinding>;
package/dist/workerd.js DELETED
@@ -1,35 +0,0 @@
1
- // Generated before TypeScript compilation and emitted beside this module.
2
- // @ts-expect-error Generated by build:wasm.
3
- import wasmModule from "./wasm/lix_js_sdk_bg.wasm";
4
- // @ts-expect-error Generated by build:wasm.
5
- import { initSync, openMemoryFromSnapshot, parseSqlScript as parseSqlScriptBinding } from "./wasm/lix_js_sdk.js";
6
- let initialized = false;
7
- function initializeWasm() {
8
- if (initialized)
9
- return;
10
- initSync({ module: wasmModule });
11
- initialized = true;
12
- }
13
- /** Parses SQL using the same DataFusion dialect and transaction policy as Lix. */
14
- export function parseSqlScript(sql, providedParamCount) {
15
- initializeWasm();
16
- return parseSqlScriptBinding(sql, providedParamCount);
17
- }
18
- /**
19
- * Opens an in-memory Lix binding directly in a Cloudflare Worker isolate.
20
- *
21
- * The browser SDK intentionally uses Web Workers. Workerd does not implement
22
- * that API, so this entry point initializes the precompiled module directly.
23
- * Plugin execution is deliberately unavailable in this environment.
24
- */
25
- export async function openMemoryLix(options = {}) {
26
- if (!options || typeof options !== "object" || Array.isArray(options)) {
27
- throw new TypeError("openMemoryLix() options must be an object");
28
- }
29
- if (options.snapshot !== undefined &&
30
- !(options.snapshot instanceof Uint8Array)) {
31
- throw new TypeError("openMemoryLix() snapshot must be a Uint8Array");
32
- }
33
- initializeWasm();
34
- return openMemoryFromSnapshot(undefined, options.snapshot);
35
- }