@lix-js/sdk 0.12.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -10
- package/dist/binding-types.d.ts +37 -9
- package/dist/binding.browser.d.ts +2 -2
- package/dist/binding.browser.js +25 -10
- package/dist/binding.node-wasm.d.ts +2 -2
- package/dist/binding.node-wasm.js +8 -4
- package/dist/binding.node.d.ts +3 -3
- package/dist/binding.node.js +70 -13
- package/dist/browser-wasm-init.d.ts +14 -0
- package/dist/browser-wasm-init.js +16 -0
- package/dist/bundled-plugins/plugin_csv.lixplugin +0 -0
- package/dist/bundled-plugins/plugin_markdown.lixplugin +0 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/lix.d.ts +27 -5
- package/dist/lix.js +135 -12
- package/dist/open-lix.d.ts +4 -5
- package/dist/open-lix.js +173 -33
- package/dist/remote/client.d.ts +1 -2
- package/dist/remote/client.js +58 -1151
- package/dist/remote/server-protocol.d.ts +5 -6
- package/dist/remote/server-protocol.js +40 -9
- package/dist/result.d.ts +6 -13
- package/dist/result.js +9 -35
- package/dist/snapshot-restore.d.ts +6 -0
- package/dist/snapshot-restore.js +101 -0
- package/dist/storage-adapter.d.ts +175 -19
- package/dist/storage-adapter.js +21 -0
- package/dist/types.d.ts +101 -31
- package/dist/value.d.ts +1 -0
- package/dist/value.js +8 -0
- package/dist/wasm/lix_js_sdk.d.ts +119 -24
- package/dist/wasm/lix_js_sdk.js +599 -78
- package/dist/wasm/lix_js_sdk_bg.wasm +0 -0
- package/dist/wasm/lix_js_sdk_bg.wasm.d.ts +47 -6
- package/dist/worker/client.d.ts +27 -5
- package/dist/worker/client.js +458 -45
- package/dist/worker/factory.browser.d.ts +2 -2
- package/dist/worker/factory.node.d.ts +3 -2
- package/dist/worker/factory.node.js +18 -12
- package/dist/worker/host.d.ts +2 -1
- package/dist/worker/host.js +283 -37
- package/dist/worker/protocol.d.ts +80 -3
- package/package.json +6 -10
- package/dist/indexeddb-backend.d.ts +0 -19
- package/dist/indexeddb-backend.js +0 -121
- package/dist/remote/sse.d.ts +0 -12
- package/dist/remote/sse.js +0 -87
- package/dist/workerd.d.ts +0 -25
- package/dist/workerd.js +0 -35
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,90 @@ 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
|
+
const exportSnapshot = this.binding.exportSnapshot;
|
|
122
|
+
if (!exportSnapshot) {
|
|
123
|
+
const error = new Error("snapshot export is not available for remote Lix handles");
|
|
124
|
+
error.name = "LixError";
|
|
125
|
+
error.code = "LIX_UNSUPPORTED_STORAGE";
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
resolveBinding(exportSnapshot.call(this.binding));
|
|
129
|
+
await completed;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
rejectBinding(error);
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// Pull observes setup errors through `binding`; this catch only prevents
|
|
137
|
+
// the lifecycle-tracking promise from becoming an unhandled rejection.
|
|
138
|
+
void operation.catch((error) => rejectBinding(error));
|
|
139
|
+
let finished = false;
|
|
140
|
+
let cancelPromise;
|
|
141
|
+
const active = {
|
|
142
|
+
binding,
|
|
143
|
+
finish: () => {
|
|
144
|
+
if (finished)
|
|
145
|
+
return;
|
|
146
|
+
finished = true;
|
|
147
|
+
this.#snapshotExports.delete(active);
|
|
148
|
+
finish();
|
|
149
|
+
},
|
|
150
|
+
cancel: () => (cancelPromise ??= (async () => {
|
|
151
|
+
try {
|
|
152
|
+
await (await binding).cancel();
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
active.finish();
|
|
156
|
+
}
|
|
157
|
+
})()),
|
|
158
|
+
};
|
|
159
|
+
snapshot = active;
|
|
160
|
+
this.#snapshotExports.add(active);
|
|
161
|
+
return snapshot;
|
|
162
|
+
};
|
|
163
|
+
return new ReadableStream({
|
|
164
|
+
pull: async (controller) => {
|
|
165
|
+
const active = start();
|
|
166
|
+
const binding = await active.binding;
|
|
167
|
+
try {
|
|
168
|
+
const chunk = await binding.next();
|
|
169
|
+
if (chunk == null) {
|
|
170
|
+
active.finish();
|
|
171
|
+
controller.close();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
controller.enqueue(chunk);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
await active.cancel().catch(() => undefined);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
cancel: async () => {
|
|
182
|
+
if (!snapshot)
|
|
183
|
+
return;
|
|
184
|
+
await snapshot.cancel();
|
|
185
|
+
},
|
|
186
|
+
}, { highWaterMark: 0 });
|
|
87
187
|
}
|
|
88
188
|
async undo() {
|
|
89
189
|
return this.#runOperation(() => this.binding.undo());
|
|
@@ -118,7 +218,10 @@ export class Lix {
|
|
|
118
218
|
async close() {
|
|
119
219
|
if (!this.closePromise) {
|
|
120
220
|
if (this.#transactionsOpening > 0 || this.#activeTransactions > 0) {
|
|
121
|
-
|
|
221
|
+
const error = new Error("cannot close Lix while an explicit transaction is active");
|
|
222
|
+
error.name = "LixError";
|
|
223
|
+
error.code = "LIX_INVALID_TRANSACTION_STATE";
|
|
224
|
+
throw error;
|
|
122
225
|
}
|
|
123
226
|
// Flip the public lifecycle gate before the first await. Operations that
|
|
124
227
|
// already entered the gate are allowed to finish; later calls fail closed.
|
|
@@ -128,6 +231,7 @@ export class Lix {
|
|
|
128
231
|
}
|
|
129
232
|
this.#observations.clear();
|
|
130
233
|
this.closePromise = (async () => {
|
|
234
|
+
await Promise.allSettled([...this.#snapshotExports].map((snapshot) => snapshot.cancel()));
|
|
131
235
|
await Promise.allSettled([...this.#inFlightOperations]);
|
|
132
236
|
const results = await Promise.allSettled([
|
|
133
237
|
Promise.resolve().then(() => this.binding.close()),
|
|
@@ -161,11 +265,19 @@ export class Lix {
|
|
|
161
265
|
throw error;
|
|
162
266
|
}
|
|
163
267
|
}
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
268
|
+
function assertOpenAnotherSessionOptions(options) {
|
|
269
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
270
|
+
throw new TypeError("openAnotherSession() options must be an object");
|
|
271
|
+
}
|
|
272
|
+
for (const [name, value] of [
|
|
273
|
+
["branchId", options.branchId],
|
|
274
|
+
["accountId", options.accountId],
|
|
275
|
+
]) {
|
|
276
|
+
if (value !== undefined &&
|
|
277
|
+
(typeof value !== "string" || value.length === 0)) {
|
|
278
|
+
throw new TypeError(`openAnotherSession() ${name} must be a non-empty string`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
169
281
|
}
|
|
170
282
|
export class ObserveEvents {
|
|
171
283
|
onClose;
|
|
@@ -221,7 +333,8 @@ export class LixTransaction {
|
|
|
221
333
|
}
|
|
222
334
|
async execute(sql, params = [], options) {
|
|
223
335
|
assertExecuteArgs("lixTransaction", sql, params, options);
|
|
224
|
-
|
|
336
|
+
const { rowMode = "object", ...bindingOptions } = options ?? {};
|
|
337
|
+
return wrapExecuteResult(await this.binding.execute(sql, params.map((param, index) => toNativeValue(normalizeParam(param, index))), bindingOptions), rowMode);
|
|
225
338
|
}
|
|
226
339
|
async commit() {
|
|
227
340
|
return this.finish("transaction.commit");
|
|
@@ -274,6 +387,11 @@ function assertExecuteArgs(receiver, sql, params, options) {
|
|
|
274
387
|
typeof options.idempotencyKey !== "string") {
|
|
275
388
|
throw invalidArgument("execute", "options.idempotencyKey", "string", typeof options.idempotencyKey, receiver);
|
|
276
389
|
}
|
|
390
|
+
if (options.rowMode !== undefined &&
|
|
391
|
+
options.rowMode !== "object" &&
|
|
392
|
+
options.rowMode !== "array") {
|
|
393
|
+
throw invalidArgument("execute", "options.rowMode", '"object" | "array"', typeof options.rowMode, receiver);
|
|
394
|
+
}
|
|
277
395
|
}
|
|
278
396
|
function assertSqlArgs(operation, receiver, sql, params) {
|
|
279
397
|
if (typeof sql !== "string") {
|
|
@@ -334,6 +452,11 @@ function assertBatchOptions(options) {
|
|
|
334
452
|
typeof options.idempotencyKey !== "string") {
|
|
335
453
|
throw invalidArgument("executeBatch", "options.idempotencyKey", "string", typeof options.idempotencyKey);
|
|
336
454
|
}
|
|
455
|
+
if (options.rowMode !== undefined &&
|
|
456
|
+
options.rowMode !== "object" &&
|
|
457
|
+
options.rowMode !== "array") {
|
|
458
|
+
throw invalidArgument("lix.executeBatch", "options.rowMode", '"object" | "array"', typeof options.rowMode);
|
|
459
|
+
}
|
|
337
460
|
}
|
|
338
461
|
function withBatchStatementIndex(error, statementIndex) {
|
|
339
462
|
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,52 @@ 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
|
-
return new Lix(await openLixWorkerBinding({ kind: "memory" }, undefined, options.telemetry));
|
|
62
|
+
return new Lix(await openLixWorkerBinding({ kind: "memory" }, undefined, options.telemetry, syncServer, options.onProgress, snapshot));
|
|
63
|
+
}
|
|
64
|
+
if (isJsProviderLixStorage(options.storage)) {
|
|
65
|
+
return openJsProviderStorage(options.storage, options.telemetry, syncServer, options.onProgress, snapshot);
|
|
37
66
|
}
|
|
38
67
|
if (isLixStorage(options.storage)) {
|
|
39
68
|
const storage = options.storage;
|
|
@@ -47,12 +76,13 @@ export async function openLix(options = {}) {
|
|
|
47
76
|
openStorages.delete(storage);
|
|
48
77
|
};
|
|
49
78
|
try {
|
|
50
|
-
binding = await openLixWorkerBinding(storage.lixStorage.config, disconnect, options.telemetry);
|
|
79
|
+
binding = await openLixWorkerBinding(storage.lixStorage.config, disconnect, options.telemetry, syncServer, options.onProgress, snapshot);
|
|
80
|
+
const routed = routeStorageBinding(binding);
|
|
51
81
|
storage.lixStorage.connect({
|
|
52
|
-
importFilesystemPaths: (paths) =>
|
|
53
|
-
syncDiskToLix: () =>
|
|
82
|
+
importFilesystemPaths: (paths) => routed.current().importFilesystemPaths(paths),
|
|
83
|
+
syncDiskToLix: () => routed.current().syncDiskToLix(),
|
|
54
84
|
});
|
|
55
|
-
return new Lix(binding);
|
|
85
|
+
return new Lix(routed.binding);
|
|
56
86
|
}
|
|
57
87
|
catch (error) {
|
|
58
88
|
disconnect();
|
|
@@ -60,25 +90,135 @@ export async function openLix(options = {}) {
|
|
|
60
90
|
throw error;
|
|
61
91
|
}
|
|
62
92
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
93
|
+
throw new TypeError("openLix() requires a Lix storage adapter");
|
|
94
|
+
}
|
|
95
|
+
function routeStorageBinding(root) {
|
|
96
|
+
const live = new Set();
|
|
97
|
+
const wrap = (binding) => {
|
|
98
|
+
live.add(binding);
|
|
99
|
+
return new Proxy(binding, {
|
|
100
|
+
get(target, property, receiver) {
|
|
101
|
+
if (property === "openAnotherSession") {
|
|
102
|
+
return async (options) => wrap(await target.openAnotherSession(options));
|
|
103
|
+
}
|
|
104
|
+
if (property === "close") {
|
|
105
|
+
return async () => {
|
|
106
|
+
try {
|
|
107
|
+
await target.close();
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
live.delete(target);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const value = Reflect.get(target, property, receiver);
|
|
115
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
binding: wrap(root),
|
|
121
|
+
current: () => {
|
|
122
|
+
const bindings = [...live];
|
|
123
|
+
return bindings[bindings.length - 1] ?? root;
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function openJsProviderStorage(storage, telemetry, syncServer, onProgress, snapshot) {
|
|
128
|
+
const { openLixWorkerBinding } = await import("./worker/client.js");
|
|
129
|
+
if (openStorages.has(storage))
|
|
130
|
+
throw storageAlreadyOpen();
|
|
131
|
+
openStorages.add(storage);
|
|
132
|
+
let binding;
|
|
133
|
+
try {
|
|
134
|
+
const opened = await openLixWorkerBinding({
|
|
135
|
+
kind: "jsStorage",
|
|
136
|
+
moduleUrl: storage.lixStorage.moduleUrl,
|
|
137
|
+
options: storage.lixStorage.options,
|
|
138
|
+
}, () => openStorages.delete(storage), telemetry, syncServer, onProgress, snapshot);
|
|
139
|
+
binding = opened;
|
|
140
|
+
return new Lix(opened);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
openStorages.delete(storage);
|
|
144
|
+
await binding?.close().catch(() => undefined);
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
(function (openLix) {
|
|
149
|
+
async function fromSnapshot(source, options = {}) {
|
|
150
|
+
const prepared = prepareSnapshotSource(source);
|
|
71
151
|
try {
|
|
72
|
-
|
|
73
|
-
return new Lix(binding);
|
|
152
|
+
return await openLixInternal(options, prepared.stream);
|
|
74
153
|
}
|
|
75
154
|
catch (error) {
|
|
76
|
-
|
|
77
|
-
await binding?.close().catch(() => undefined);
|
|
155
|
+
await prepared.cancel(error);
|
|
78
156
|
throw error;
|
|
79
157
|
}
|
|
80
158
|
}
|
|
81
|
-
|
|
159
|
+
openLix.fromSnapshot = fromSnapshot;
|
|
160
|
+
})(openLix || (openLix = {}));
|
|
161
|
+
function prepareSnapshotSource(source) {
|
|
162
|
+
if (source instanceof Uint8Array) {
|
|
163
|
+
let sent = false;
|
|
164
|
+
const stream = new ReadableStream({
|
|
165
|
+
pull(controller) {
|
|
166
|
+
if (!sent) {
|
|
167
|
+
sent = true;
|
|
168
|
+
controller.enqueue(source);
|
|
169
|
+
}
|
|
170
|
+
controller.close();
|
|
171
|
+
},
|
|
172
|
+
}, { highWaterMark: 0 });
|
|
173
|
+
return {
|
|
174
|
+
stream,
|
|
175
|
+
cancel: async (reason) => {
|
|
176
|
+
await stream.cancel(reason).catch(() => undefined);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (!source || typeof source.getReader !== "function") {
|
|
181
|
+
throw new TypeError("openLix.fromSnapshot() requires a ReadableStream<Uint8Array> or Uint8Array");
|
|
182
|
+
}
|
|
183
|
+
// Acquire the caller's stream before openLixInternal can start a native or
|
|
184
|
+
// worker restore. A locked source therefore fails without creating backend work.
|
|
185
|
+
const reader = source.getReader();
|
|
186
|
+
let released = false;
|
|
187
|
+
const release = () => {
|
|
188
|
+
if (released)
|
|
189
|
+
return;
|
|
190
|
+
released = true;
|
|
191
|
+
reader.releaseLock();
|
|
192
|
+
};
|
|
193
|
+
const cancel = async (reason) => {
|
|
194
|
+
if (released)
|
|
195
|
+
return;
|
|
196
|
+
try {
|
|
197
|
+
await reader.cancel(reason);
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
release();
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
const stream = new ReadableStream({
|
|
204
|
+
async pull(controller) {
|
|
205
|
+
try {
|
|
206
|
+
const result = await reader.read();
|
|
207
|
+
if (result.done) {
|
|
208
|
+
release();
|
|
209
|
+
controller.close();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
controller.enqueue(result.value);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
release();
|
|
216
|
+
controller.error(error);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
cancel,
|
|
220
|
+
}, { highWaterMark: 0 });
|
|
221
|
+
return { stream, cancel };
|
|
82
222
|
}
|
|
83
223
|
function storageAlreadyOpen() {
|
|
84
224
|
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 {};
|