@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.
- package/README.md +54 -10
- package/dist/binding-types.d.ts +37 -9
- package/dist/binding.browser.d.ts +2 -2
- package/dist/binding.browser.js +20 -13
- package/dist/binding.node-wasm.d.ts +2 -2
- package/dist/binding.node-wasm.js +9 -11
- package/dist/binding.node.d.ts +3 -3
- package/dist/binding.node.js +70 -13
- 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 +125 -6
- package/dist/open-lix.d.ts +4 -5
- package/dist/open-lix.js +174 -33
- package/dist/remote/client.d.ts +1 -2
- package/dist/remote/client.js +40 -1151
- package/dist/remote/server-protocol.d.ts +5 -6
- package/dist/remote/server-protocol.js +40 -6
- 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 +104 -34
- package/dist/value.d.ts +1 -0
- package/dist/value.js +8 -0
- package/dist/wasm/lix_js_sdk.d.ts +121 -24
- package/dist/wasm/lix_js_sdk.js +606 -78
- package/dist/wasm/lix_js_sdk_bg.wasm +0 -0
- package/dist/wasm/lix_js_sdk_bg.wasm.d.ts +48 -6
- package/dist/wasm-init.d.ts +2 -0
- package/dist/wasm-init.js +23 -0
- package/dist/worker/client.d.ts +31 -5
- package/dist/worker/client.js +541 -45
- package/dist/worker/factory.browser.d.ts +2 -2
- package/dist/worker/factory.node.d.ts +2 -2
- package/dist/worker/factory.node.js +3 -10
- package/dist/worker/host.d.ts +4 -2
- package/dist/worker/host.js +401 -43
- package/dist/worker/protocol.d.ts +107 -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
package/dist/lix.js
CHANGED
|
@@ -15,26 +15,44 @@ const observeFinalizer = new FinalizationRegistry(({ observe, onClose }) => {
|
|
|
15
15
|
});
|
|
16
16
|
export class Lix {
|
|
17
17
|
binding;
|
|
18
|
+
openReport;
|
|
18
19
|
closePromise;
|
|
19
20
|
#activeBranchListeners = new Set();
|
|
20
21
|
#inFlightOperations = new Set();
|
|
21
22
|
#observations = new Map();
|
|
23
|
+
#snapshotExports = new Set();
|
|
22
24
|
#nextObservationId = 0;
|
|
23
25
|
#transactionsOpening = 0;
|
|
24
26
|
#activeTransactions = 0;
|
|
25
27
|
#acceptingOperations = true;
|
|
26
28
|
constructor(binding) {
|
|
27
29
|
this.binding = binding;
|
|
30
|
+
const report = binding.openReport?.();
|
|
31
|
+
this.openReport = report
|
|
32
|
+
? Object.freeze({
|
|
33
|
+
...report,
|
|
34
|
+
...(report.migration
|
|
35
|
+
? { migration: Object.freeze({ ...report.migration }) }
|
|
36
|
+
: {}),
|
|
37
|
+
})
|
|
38
|
+
: undefined;
|
|
39
|
+
}
|
|
40
|
+
/** Opens an independent session over the same repository storage. */
|
|
41
|
+
async openAnotherSession(options = {}) {
|
|
42
|
+
assertOpenAnotherSessionOptions(options);
|
|
43
|
+
return this.#runOperation(async () => new Lix(await this.binding.openAnotherSession(options)));
|
|
28
44
|
}
|
|
29
45
|
async execute(sql, params = [], options) {
|
|
30
46
|
assertExecuteArgs("lix", sql, params, options);
|
|
31
|
-
|
|
47
|
+
const { rowMode = "object", ...bindingOptions } = options ?? {};
|
|
48
|
+
return this.#runOperation(async () => wrapExecuteResult(await this.binding.execute(sql, params.map((param, index) => toNativeValue(normalizeParam(param, index))), bindingOptions), rowMode));
|
|
32
49
|
}
|
|
33
50
|
async executeBatch(statements, options) {
|
|
34
51
|
const normalizedStatements = normalizeBatchStatements(statements, options);
|
|
52
|
+
const { rowMode = "object", ...bindingOptions } = options ?? {};
|
|
35
53
|
return this.#runOperation(async () => {
|
|
36
|
-
const results = await this.binding.executeBatch(normalizedStatements,
|
|
37
|
-
return results.map(wrapExecuteBatchResult);
|
|
54
|
+
const results = await this.binding.executeBatch(normalizedStatements, bindingOptions);
|
|
55
|
+
return results.map((result) => wrapExecuteBatchResult(result, rowMode));
|
|
38
56
|
});
|
|
39
57
|
}
|
|
40
58
|
observe(sql, params = []) {
|
|
@@ -82,8 +100,83 @@ export class Lix {
|
|
|
82
100
|
async createBranch(options) {
|
|
83
101
|
return this.#runOperation(() => this.binding.createBranch(options));
|
|
84
102
|
}
|
|
85
|
-
|
|
86
|
-
|
|
103
|
+
/** Streams a deterministic snapshot of the complete Lix. */
|
|
104
|
+
exportSnapshot() {
|
|
105
|
+
let snapshot;
|
|
106
|
+
const start = () => {
|
|
107
|
+
if (snapshot)
|
|
108
|
+
return snapshot;
|
|
109
|
+
let finish;
|
|
110
|
+
const completed = new Promise((resolve) => {
|
|
111
|
+
finish = resolve;
|
|
112
|
+
});
|
|
113
|
+
let resolveBinding;
|
|
114
|
+
let rejectBinding;
|
|
115
|
+
const binding = new Promise((resolve, reject) => {
|
|
116
|
+
resolveBinding = resolve;
|
|
117
|
+
rejectBinding = reject;
|
|
118
|
+
});
|
|
119
|
+
const operation = this.#runOperation(async () => {
|
|
120
|
+
try {
|
|
121
|
+
resolveBinding(this.binding.exportSnapshot());
|
|
122
|
+
await completed;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
rejectBinding(error);
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
// Pull observes setup errors through `binding`; this catch only prevents
|
|
130
|
+
// the lifecycle-tracking promise from becoming an unhandled rejection.
|
|
131
|
+
void operation.catch((error) => rejectBinding(error));
|
|
132
|
+
let finished = false;
|
|
133
|
+
let cancelPromise;
|
|
134
|
+
const active = {
|
|
135
|
+
binding,
|
|
136
|
+
finish: () => {
|
|
137
|
+
if (finished)
|
|
138
|
+
return;
|
|
139
|
+
finished = true;
|
|
140
|
+
this.#snapshotExports.delete(active);
|
|
141
|
+
finish();
|
|
142
|
+
},
|
|
143
|
+
cancel: () => (cancelPromise ??= (async () => {
|
|
144
|
+
try {
|
|
145
|
+
await (await binding).cancel();
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
active.finish();
|
|
149
|
+
}
|
|
150
|
+
})()),
|
|
151
|
+
};
|
|
152
|
+
snapshot = active;
|
|
153
|
+
this.#snapshotExports.add(active);
|
|
154
|
+
return snapshot;
|
|
155
|
+
};
|
|
156
|
+
return new ReadableStream({
|
|
157
|
+
pull: async (controller) => {
|
|
158
|
+
const active = start();
|
|
159
|
+
const binding = await active.binding;
|
|
160
|
+
try {
|
|
161
|
+
const chunk = await binding.next();
|
|
162
|
+
if (chunk == null) {
|
|
163
|
+
active.finish();
|
|
164
|
+
controller.close();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
controller.enqueue(chunk);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
await active.cancel().catch(() => undefined);
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
cancel: async () => {
|
|
175
|
+
if (!snapshot)
|
|
176
|
+
return;
|
|
177
|
+
await snapshot.cancel();
|
|
178
|
+
},
|
|
179
|
+
}, { highWaterMark: 0 });
|
|
87
180
|
}
|
|
88
181
|
async undo() {
|
|
89
182
|
return this.#runOperation(() => this.binding.undo());
|
|
@@ -131,6 +224,7 @@ export class Lix {
|
|
|
131
224
|
}
|
|
132
225
|
this.#observations.clear();
|
|
133
226
|
this.closePromise = (async () => {
|
|
227
|
+
await Promise.allSettled([...this.#snapshotExports].map((snapshot) => snapshot.cancel()));
|
|
134
228
|
await Promise.allSettled([...this.#inFlightOperations]);
|
|
135
229
|
const results = await Promise.allSettled([
|
|
136
230
|
Promise.resolve().then(() => this.binding.close()),
|
|
@@ -164,6 +258,20 @@ export class Lix {
|
|
|
164
258
|
throw error;
|
|
165
259
|
}
|
|
166
260
|
}
|
|
261
|
+
function assertOpenAnotherSessionOptions(options) {
|
|
262
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
263
|
+
throw new TypeError("openAnotherSession() options must be an object");
|
|
264
|
+
}
|
|
265
|
+
for (const [name, value] of [
|
|
266
|
+
["branchId", options.branchId],
|
|
267
|
+
["accountId", options.accountId],
|
|
268
|
+
]) {
|
|
269
|
+
if (value !== undefined &&
|
|
270
|
+
(typeof value !== "string" || value.length === 0)) {
|
|
271
|
+
throw new TypeError(`openAnotherSession() ${name} must be a non-empty string`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
167
275
|
export class ObserveEvents {
|
|
168
276
|
onClose;
|
|
169
277
|
setup = {};
|
|
@@ -218,7 +326,8 @@ export class LixTransaction {
|
|
|
218
326
|
}
|
|
219
327
|
async execute(sql, params = [], options) {
|
|
220
328
|
assertExecuteArgs("lixTransaction", sql, params, options);
|
|
221
|
-
|
|
329
|
+
const { rowMode = "object", ...bindingOptions } = options ?? {};
|
|
330
|
+
return wrapExecuteResult(await this.binding.execute(sql, params.map((param, index) => toNativeValue(normalizeParam(param, index))), bindingOptions), rowMode);
|
|
222
331
|
}
|
|
223
332
|
async commit() {
|
|
224
333
|
return this.finish("transaction.commit");
|
|
@@ -271,6 +380,11 @@ function assertExecuteArgs(receiver, sql, params, options) {
|
|
|
271
380
|
typeof options.idempotencyKey !== "string") {
|
|
272
381
|
throw invalidArgument("execute", "options.idempotencyKey", "string", typeof options.idempotencyKey, receiver);
|
|
273
382
|
}
|
|
383
|
+
if (options.rowMode !== undefined &&
|
|
384
|
+
options.rowMode !== "object" &&
|
|
385
|
+
options.rowMode !== "array") {
|
|
386
|
+
throw invalidArgument("execute", "options.rowMode", '"object" | "array"', typeof options.rowMode, receiver);
|
|
387
|
+
}
|
|
274
388
|
}
|
|
275
389
|
function assertSqlArgs(operation, receiver, sql, params) {
|
|
276
390
|
if (typeof sql !== "string") {
|
|
@@ -331,6 +445,11 @@ function assertBatchOptions(options) {
|
|
|
331
445
|
typeof options.idempotencyKey !== "string") {
|
|
332
446
|
throw invalidArgument("executeBatch", "options.idempotencyKey", "string", typeof options.idempotencyKey);
|
|
333
447
|
}
|
|
448
|
+
if (options.rowMode !== undefined &&
|
|
449
|
+
options.rowMode !== "object" &&
|
|
450
|
+
options.rowMode !== "array") {
|
|
451
|
+
throw invalidArgument("lix.executeBatch", "options.rowMode", '"object" | "array"', typeof options.rowMode);
|
|
452
|
+
}
|
|
334
453
|
}
|
|
335
454
|
function withBatchStatementIndex(error, statementIndex) {
|
|
336
455
|
if (!error || typeof error !== "object")
|
package/dist/open-lix.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Lix } from "./lix.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { OpenLixOptions } from "./types.js";
|
|
3
3
|
export { Lix, LixTransaction, ObserveEvents } from "./lix.js";
|
|
4
|
-
export declare class IndexedDbStorage {
|
|
5
|
-
readonly name: string;
|
|
6
|
-
constructor(options: IndexedDbStorageOptions);
|
|
7
|
-
}
|
|
8
4
|
export declare function openLix(options?: OpenLixOptions): Promise<Lix>;
|
|
5
|
+
export declare namespace openLix {
|
|
6
|
+
function fromSnapshot(source: ReadableStream<Uint8Array> | Uint8Array, options?: OpenLixOptions): Promise<Lix>;
|
|
7
|
+
}
|
package/dist/open-lix.js
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { Lix } from "./lix.js";
|
|
2
|
-
import { isLixStorage } from "./storage-adapter.js";
|
|
2
|
+
import { isJsProviderLixStorage, isLixStorage, } from "./storage-adapter.js";
|
|
3
3
|
export { Lix, LixTransaction, ObserveEvents } from "./lix.js";
|
|
4
4
|
const openStorages = new WeakSet();
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
name;
|
|
8
|
-
constructor(options) {
|
|
9
|
-
if (!options || typeof options.name !== "string" || options.name.length === 0) {
|
|
10
|
-
throw new TypeError("IndexedDbStorage requires a non-empty name");
|
|
11
|
-
}
|
|
12
|
-
this.name = options.name;
|
|
13
|
-
}
|
|
5
|
+
export function openLix(options = {}) {
|
|
6
|
+
return openLixInternal(options);
|
|
14
7
|
}
|
|
15
|
-
|
|
8
|
+
async function openLixInternal(options, snapshot) {
|
|
16
9
|
if (!options || typeof options !== "object") {
|
|
17
10
|
throw new TypeError("openLix() options must be an object");
|
|
18
11
|
}
|
|
@@ -24,16 +17,53 @@ export async function openLix(options = {}) {
|
|
|
24
17
|
typeof options.telemetry.onSpan !== "function")) {
|
|
25
18
|
throw new TypeError("openLix() telemetry requires an onSpan callback");
|
|
26
19
|
}
|
|
20
|
+
if (options.telemetry?.parentContext !== undefined &&
|
|
21
|
+
typeof options.telemetry.parentContext !== "function") {
|
|
22
|
+
throw new TypeError("openLix() telemetry parentContext must be a context provider function");
|
|
23
|
+
}
|
|
24
|
+
if (options.onProgress !== undefined &&
|
|
25
|
+
typeof options.onProgress !== "function") {
|
|
26
|
+
throw new TypeError("openLix() onProgress must be a function");
|
|
27
|
+
}
|
|
27
28
|
if (options.server !== undefined) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
if (snapshot) {
|
|
30
|
+
throw new TypeError("openLix.fromSnapshot() does not accept server mode");
|
|
31
|
+
}
|
|
32
|
+
if (options.server.mode === "remote") {
|
|
33
|
+
const { openRemoteLixBinding } = await import("./remote/client.js");
|
|
34
|
+
if ("storage" in options && options.storage !== undefined) {
|
|
35
|
+
throw new TypeError("openLix() remote mode does not accept storage");
|
|
36
|
+
}
|
|
37
|
+
return new Lix(await openRemoteLixBinding(options.server));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const syncServer = options.server?.mode === "sync"
|
|
41
|
+
? {
|
|
42
|
+
url: new URL(options.server.url).toString(),
|
|
43
|
+
headers: options.server.headers,
|
|
44
|
+
fetch: options.server.fetch,
|
|
31
45
|
}
|
|
32
|
-
|
|
46
|
+
: undefined;
|
|
47
|
+
if (syncServer?.fetch !== undefined && typeof syncServer.fetch !== "function") {
|
|
48
|
+
throw new TypeError("openLix() sync server fetch must be a function");
|
|
49
|
+
}
|
|
50
|
+
if (syncServer?.headers !== undefined && typeof syncServer.headers !== "function") {
|
|
51
|
+
// Validate static headers before opening a worker/native runtime.
|
|
52
|
+
new Headers(syncServer.headers);
|
|
53
|
+
}
|
|
54
|
+
if (options.server !== undefined && syncServer === undefined) {
|
|
55
|
+
throw new TypeError("openLix() server mode must be 'remote' or 'sync'");
|
|
56
|
+
}
|
|
57
|
+
if (syncServer !== undefined && options.storage === undefined) {
|
|
58
|
+
throw new TypeError("openLix() sync mode requires a durability-capable storage adapter");
|
|
33
59
|
}
|
|
34
60
|
const { openLixWorkerBinding } = await import("./worker/client.js");
|
|
35
61
|
if (options.storage === undefined) {
|
|
36
|
-
|
|
62
|
+
const binding = await openLixWorkerBinding({ kind: "memory" }, undefined, options.telemetry, syncServer, options.onProgress, snapshot);
|
|
63
|
+
return new Lix(binding);
|
|
64
|
+
}
|
|
65
|
+
if (isJsProviderLixStorage(options.storage)) {
|
|
66
|
+
return openJsProviderStorage(options.storage, options.telemetry, syncServer, options.onProgress, snapshot);
|
|
37
67
|
}
|
|
38
68
|
if (isLixStorage(options.storage)) {
|
|
39
69
|
const storage = options.storage;
|
|
@@ -47,12 +77,13 @@ export async function openLix(options = {}) {
|
|
|
47
77
|
openStorages.delete(storage);
|
|
48
78
|
};
|
|
49
79
|
try {
|
|
50
|
-
binding = await openLixWorkerBinding(storage.lixStorage.config, disconnect, options.telemetry);
|
|
80
|
+
binding = await openLixWorkerBinding(storage.lixStorage.config, disconnect, options.telemetry, syncServer, options.onProgress, snapshot);
|
|
81
|
+
const routed = routeStorageBinding(binding);
|
|
51
82
|
storage.lixStorage.connect({
|
|
52
|
-
importFilesystemPaths: (paths) =>
|
|
53
|
-
syncDiskToLix: () =>
|
|
83
|
+
importFilesystemPaths: (paths) => routed.current().importFilesystemPaths(paths),
|
|
84
|
+
syncDiskToLix: () => routed.current().syncDiskToLix(),
|
|
54
85
|
});
|
|
55
|
-
return new Lix(binding);
|
|
86
|
+
return new Lix(routed.binding);
|
|
56
87
|
}
|
|
57
88
|
catch (error) {
|
|
58
89
|
disconnect();
|
|
@@ -60,25 +91,135 @@ export async function openLix(options = {}) {
|
|
|
60
91
|
throw error;
|
|
61
92
|
}
|
|
62
93
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
94
|
+
throw new TypeError("openLix() requires a Lix storage adapter");
|
|
95
|
+
}
|
|
96
|
+
function routeStorageBinding(root) {
|
|
97
|
+
const live = new Set();
|
|
98
|
+
const wrap = (binding) => {
|
|
99
|
+
live.add(binding);
|
|
100
|
+
return new Proxy(binding, {
|
|
101
|
+
get(target, property, receiver) {
|
|
102
|
+
if (property === "openAnotherSession") {
|
|
103
|
+
return async (options) => wrap(await target.openAnotherSession(options));
|
|
104
|
+
}
|
|
105
|
+
if (property === "close") {
|
|
106
|
+
return async () => {
|
|
107
|
+
try {
|
|
108
|
+
await target.close();
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
live.delete(target);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const value = Reflect.get(target, property, receiver);
|
|
116
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
binding: wrap(root),
|
|
122
|
+
current: () => {
|
|
123
|
+
const bindings = [...live];
|
|
124
|
+
return bindings[bindings.length - 1] ?? root;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function openJsProviderStorage(storage, telemetry, syncServer, onProgress, snapshot) {
|
|
129
|
+
const { openLixWorkerBinding } = await import("./worker/client.js");
|
|
130
|
+
if (openStorages.has(storage))
|
|
131
|
+
throw storageAlreadyOpen();
|
|
132
|
+
openStorages.add(storage);
|
|
133
|
+
let binding;
|
|
134
|
+
try {
|
|
135
|
+
const opened = await openLixWorkerBinding({
|
|
136
|
+
kind: "jsStorage",
|
|
137
|
+
moduleUrl: storage.lixStorage.moduleUrl,
|
|
138
|
+
options: storage.lixStorage.options,
|
|
139
|
+
}, () => openStorages.delete(storage), telemetry, syncServer, onProgress, snapshot);
|
|
140
|
+
binding = opened;
|
|
141
|
+
return new Lix(opened);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
openStorages.delete(storage);
|
|
145
|
+
await binding?.close().catch(() => undefined);
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
(function (openLix) {
|
|
150
|
+
async function fromSnapshot(source, options = {}) {
|
|
151
|
+
const prepared = prepareSnapshotSource(source);
|
|
71
152
|
try {
|
|
72
|
-
|
|
73
|
-
return new Lix(binding);
|
|
153
|
+
return await openLixInternal(options, prepared.stream);
|
|
74
154
|
}
|
|
75
155
|
catch (error) {
|
|
76
|
-
|
|
77
|
-
await binding?.close().catch(() => undefined);
|
|
156
|
+
await prepared.cancel(error);
|
|
78
157
|
throw error;
|
|
79
158
|
}
|
|
80
159
|
}
|
|
81
|
-
|
|
160
|
+
openLix.fromSnapshot = fromSnapshot;
|
|
161
|
+
})(openLix || (openLix = {}));
|
|
162
|
+
function prepareSnapshotSource(source) {
|
|
163
|
+
if (source instanceof Uint8Array) {
|
|
164
|
+
let sent = false;
|
|
165
|
+
const stream = new ReadableStream({
|
|
166
|
+
pull(controller) {
|
|
167
|
+
if (!sent) {
|
|
168
|
+
sent = true;
|
|
169
|
+
controller.enqueue(source);
|
|
170
|
+
}
|
|
171
|
+
controller.close();
|
|
172
|
+
},
|
|
173
|
+
}, { highWaterMark: 0 });
|
|
174
|
+
return {
|
|
175
|
+
stream,
|
|
176
|
+
cancel: async (reason) => {
|
|
177
|
+
await stream.cancel(reason).catch(() => undefined);
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (!source || typeof source.getReader !== "function") {
|
|
182
|
+
throw new TypeError("openLix.fromSnapshot() requires a ReadableStream<Uint8Array> or Uint8Array");
|
|
183
|
+
}
|
|
184
|
+
// Acquire the caller's stream before openLixInternal can start a native or
|
|
185
|
+
// worker restore. A locked source therefore fails without creating backend work.
|
|
186
|
+
const reader = source.getReader();
|
|
187
|
+
let released = false;
|
|
188
|
+
const release = () => {
|
|
189
|
+
if (released)
|
|
190
|
+
return;
|
|
191
|
+
released = true;
|
|
192
|
+
reader.releaseLock();
|
|
193
|
+
};
|
|
194
|
+
const cancel = async (reason) => {
|
|
195
|
+
if (released)
|
|
196
|
+
return;
|
|
197
|
+
try {
|
|
198
|
+
await reader.cancel(reason);
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
release();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const stream = new ReadableStream({
|
|
205
|
+
async pull(controller) {
|
|
206
|
+
try {
|
|
207
|
+
const result = await reader.read();
|
|
208
|
+
if (result.done) {
|
|
209
|
+
release();
|
|
210
|
+
controller.close();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
controller.enqueue(result.value);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
release();
|
|
217
|
+
controller.error(error);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
cancel,
|
|
221
|
+
}, { highWaterMark: 0 });
|
|
222
|
+
return { stream, cancel };
|
|
82
223
|
}
|
|
83
224
|
function storageAlreadyOpen() {
|
|
84
225
|
const error = new Error("openLix() storage is already open; close the existing Lix or create a new storage adapter");
|
package/dist/remote/client.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { LixBinding } from "../binding-types.js";
|
|
2
2
|
import type { RemoteLixServerOptions } from "../types.js";
|
|
3
|
-
import { type ServerProtocolHandshakeRequest } from "./server-protocol.js";
|
|
4
3
|
type RemoteLixClientOptions = {
|
|
5
|
-
initialActiveBranchId?:
|
|
4
|
+
initialActiveBranchId?: string;
|
|
6
5
|
};
|
|
7
6
|
export declare function openRemoteLixBinding(options: RemoteLixServerOptions, clientOptions?: RemoteLixClientOptions): Promise<LixBinding>;
|
|
8
7
|
export {};
|