@lix-js/sdk 0.12.3 → 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 +132 -6
  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 -6
  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 +2 -2
  40. package/dist/worker/factory.node.js +3 -10
  41. package/dist/worker/host.d.ts +2 -1
  42. package/dist/worker/host.js +283 -37
  43. package/dist/worker/protocol.d.ts +80 -3
  44. package/package.json +6 -10
  45. package/dist/indexeddb-backend.d.ts +0 -19
  46. package/dist/indexeddb-backend.js +0 -121
  47. package/dist/remote/sse.d.ts +0 -12
  48. package/dist/remote/sse.js +0 -87
  49. package/dist/workerd.d.ts +0 -25
  50. package/dist/workerd.js +0 -35
@@ -1,30 +1,109 @@
1
1
  import { createWorkerConnection, openDirectLixBinding } from "#worker-factory";
2
- import { deserializeWorkerError, } from "./protocol.js";
2
+ import { ownedSnapshotRestoreChunks } from "../snapshot-restore.js";
3
+ import { deserializeWorkerError, serializeWorkerError, } from "./protocol.js";
3
4
  const MAX_IDLE_WORKERS = 1;
4
5
  // The common serial reopen path retains one worker so its prepared plugin cache
5
6
  // survives close(). Concurrent opens still receive isolated workers.
6
7
  const idleWorkers = [];
7
- export async function openLixWorker(storage, onDisposed, telemetry) {
8
+ export async function openLixWorker(storage, onDisposed, telemetry, server, onProgress, snapshot) {
8
9
  let client = idleWorkers.pop();
9
10
  while (client?.isDisposed)
10
11
  client = idleWorkers.pop();
11
12
  client ??= new LixWorkerClient();
12
- client.beginLease(onDisposed, telemetry);
13
+ client.beginLease(onDisposed, telemetry, server, onProgress);
14
+ let snapshotReader;
15
+ let snapshotPumpStarted = false;
13
16
  try {
14
- await client.request({
17
+ // Lock the source before telling the worker to start a restore. A locked or
18
+ // otherwise unusable stream therefore cannot leave a worker open pending input.
19
+ snapshotReader = snapshot?.getReader();
20
+ const snapshotId = snapshotReader
21
+ ? client.allocateSnapshotInputId()
22
+ : undefined;
23
+ const open = client.request({
15
24
  kind: "open",
16
25
  storage,
17
26
  telemetryEnabled: telemetry !== undefined,
18
- });
27
+ progressEnabled: onProgress !== undefined,
28
+ snapshotId,
29
+ server: serializeSyncServer(server),
30
+ }, 0);
31
+ if (snapshotReader && snapshotId !== undefined) {
32
+ snapshotPumpStarted = true;
33
+ await pumpSnapshotToWorker(client, snapshotReader, snapshotId, open);
34
+ }
35
+ client.openReport = await open;
19
36
  return client;
20
37
  }
21
38
  catch (error) {
39
+ if (snapshotReader && !snapshotPumpStarted) {
40
+ await snapshotReader.cancel(error).catch(() => undefined);
41
+ snapshotReader.releaseLock();
42
+ }
22
43
  await client.terminate();
23
44
  throw error;
24
45
  }
25
46
  }
47
+ export async function pumpSnapshotToWorker(client, reader, snapshotId, open) {
48
+ let producerError = false;
49
+ // Convert rejection into data immediately so an early worker-open failure is
50
+ // always observed, even while or after a source read is pending.
51
+ const openCompletion = open.then((value) => ({ kind: "open-complete", value }), (error) => ({ kind: "open-error", error }));
52
+ try {
53
+ while (true) {
54
+ const outcome = await Promise.race([
55
+ reader.read().then((result) => ({ kind: "source", result }), (error) => ({ kind: "source-error", error })),
56
+ openCompletion,
57
+ ]);
58
+ if (outcome.kind === "open-error")
59
+ throw outcome.error;
60
+ if (outcome.kind === "open-complete") {
61
+ // Success cannot normally precede EOF, but if a binding completes early,
62
+ // stop consuming the now-unneeded producer without reporting an error.
63
+ await reader.cancel().catch(() => undefined);
64
+ return;
65
+ }
66
+ if (outcome.kind === "source-error") {
67
+ producerError = true;
68
+ throw outcome.error;
69
+ }
70
+ const read = outcome.result;
71
+ if (read.done)
72
+ break;
73
+ if (!(read.value instanceof Uint8Array)) {
74
+ producerError = true;
75
+ throw new TypeError("snapshot stream chunks must be Uint8Array values");
76
+ }
77
+ for (const chunk of ownedSnapshotRestoreChunks(read.value)) {
78
+ await client.request({
79
+ kind: "openSnapshot.write",
80
+ snapshotId,
81
+ chunk,
82
+ });
83
+ }
84
+ }
85
+ await client.request({ kind: "openSnapshot.finish", snapshotId });
86
+ }
87
+ catch (error) {
88
+ await reader.cancel(error).catch(() => undefined);
89
+ client.notify({ kind: "openSnapshot.cancel", snapshotId });
90
+ try {
91
+ await open;
92
+ }
93
+ catch (openError) {
94
+ // Decoder errors are more useful than the restore-lane write/close
95
+ // rejection. Producer errors remain authoritative.
96
+ if (!producerError)
97
+ throw openError;
98
+ }
99
+ throw error;
100
+ }
101
+ finally {
102
+ reader.releaseLock();
103
+ }
104
+ }
26
105
  /** Opens the local worker transport behind the semantic Lix binding. */
27
- export async function openLixWorkerBinding(storage, onDisposed, telemetry) {
106
+ export async function openLixWorkerBinding(storage, onDisposed, telemetry, server, onProgress, snapshot) {
28
107
  if (openDirectLixBinding) {
29
108
  const telemetryDispatch = telemetry
30
109
  ? (span) => {
@@ -36,47 +115,149 @@ export async function openLixWorkerBinding(storage, onDisposed, telemetry) {
36
115
  }
37
116
  }
38
117
  : undefined;
39
- const binding = await openDirectLixBinding(storage, telemetryDispatch);
118
+ const binding = await openDirectLixBinding(storage, telemetryDispatch, telemetry?.parentContext?.(), await resolveDirectSyncServer(server), onProgress, snapshot);
40
119
  if (binding) {
120
+ const operationAwareBinding = telemetry?.parentContext
121
+ ? wrapTelemetryParentBinding(binding, telemetry.parentContext)
122
+ : binding;
41
123
  if (!onDisposed)
42
- return binding;
43
- let disposed = false;
44
- return new Proxy(binding, {
45
- get(target, property, receiver) {
46
- if (property === "close") {
47
- return async () => {
48
- try {
49
- await target.close();
50
- }
51
- finally {
52
- if (!disposed) {
53
- disposed = true;
54
- onDisposed();
55
- }
56
- }
57
- };
58
- }
59
- const value = Reflect.get(target, property, receiver);
60
- return typeof value === "function" ? value.bind(target) : value;
61
- }
62
- });
124
+ return operationAwareBinding;
125
+ return wrapDirectBinding(operationAwareBinding, new BindingLease(onDisposed));
63
126
  }
64
127
  }
65
- const client = await openLixWorker(storage, onDisposed, telemetry);
66
- return workerBinding(client);
128
+ const client = await openLixWorker(storage, onDisposed, telemetry, server, onProgress, snapshot);
129
+ return workerBinding(client, new BindingLease(() => releaseWorker(client)), 0);
130
+ }
131
+ function wrapTelemetryParentBinding(binding, parentContext) {
132
+ const prepareOperation = () => binding.setTelemetryParent(parentContext());
133
+ return new Proxy(binding, {
134
+ get(target, property, receiver) {
135
+ if (property === "setTelemetryParent") {
136
+ return target.setTelemetryParent.bind(target);
137
+ }
138
+ if (property === "openAnotherSession") {
139
+ return async (options) => {
140
+ prepareOperation();
141
+ return wrapTelemetryParentBinding(await target.openAnotherSession(options), parentContext);
142
+ };
143
+ }
144
+ if (property === "observe") {
145
+ return async (sql, params) => {
146
+ prepareOperation();
147
+ return wrapTelemetryParentObserve(await target.observe(sql, params), parentContext);
148
+ };
149
+ }
150
+ if (property === "beginTransaction") {
151
+ return async () => {
152
+ prepareOperation();
153
+ return wrapTelemetryParentTransaction(await target.beginTransaction(), target, parentContext);
154
+ };
155
+ }
156
+ const value = Reflect.get(target, property, receiver);
157
+ if (typeof value !== "function")
158
+ return value;
159
+ return (...args) => {
160
+ prepareOperation();
161
+ return Reflect.apply(value, target, args);
162
+ };
163
+ },
164
+ });
165
+ }
166
+ function wrapTelemetryParentObserve(events, parentContext) {
167
+ return {
168
+ setTelemetryParent: (parent) => events.setTelemetryParent(parent),
169
+ next: () => {
170
+ events.setTelemetryParent(parentContext());
171
+ return events.next();
172
+ },
173
+ close: () => events.close(),
174
+ };
175
+ }
176
+ function wrapTelemetryParentTransaction(transaction, binding, parentContext) {
177
+ const prepareOperation = () => binding.setTelemetryParent(parentContext());
178
+ return {
179
+ execute: (sql, params, options) => {
180
+ prepareOperation();
181
+ return transaction.execute(sql, params, options);
182
+ },
183
+ commit: () => {
184
+ prepareOperation();
185
+ return transaction.commit();
186
+ },
187
+ rollback: () => {
188
+ prepareOperation();
189
+ return transaction.rollback();
190
+ },
191
+ };
192
+ }
193
+ /** @internal Exported only for worker lifecycle tests. */
194
+ export class BindingLease {
195
+ releaseLast;
196
+ references = 1;
197
+ constructor(releaseLast) {
198
+ this.releaseLast = releaseLast;
199
+ }
200
+ retain() {
201
+ this.references += 1;
202
+ }
203
+ async release() {
204
+ this.references -= 1;
205
+ if (this.references === 0)
206
+ await this.releaseLast();
207
+ }
208
+ }
209
+ function wrapDirectBinding(binding, lease) {
210
+ let closed = false;
211
+ return new Proxy(binding, {
212
+ get(target, property, receiver) {
213
+ if (property === "openAnotherSession") {
214
+ return async (options) => {
215
+ const opened = await target.openAnotherSession(options);
216
+ lease.retain();
217
+ return wrapDirectBinding(opened, lease);
218
+ };
219
+ }
220
+ if (property === "close") {
221
+ return async () => {
222
+ if (closed)
223
+ return;
224
+ try {
225
+ await target.close();
226
+ }
227
+ finally {
228
+ closed = true;
229
+ await lease.release();
230
+ }
231
+ };
232
+ }
233
+ const value = Reflect.get(target, property, receiver);
234
+ return typeof value === "function" ? value.bind(target) : value;
235
+ },
236
+ });
67
237
  }
68
- function workerBinding(client) {
238
+ /** @internal Exported only for worker lifecycle tests. */
239
+ export function workerBinding(client, lease, sessionId) {
69
240
  let closed = false;
70
241
  const request = (operation) => {
71
242
  if (closed)
72
243
  return Promise.reject(workerClosedError());
73
- return client.request(operation);
244
+ return client.request(operation, sessionId);
74
245
  };
75
246
  const notify = (notification) => {
76
247
  if (!closed)
77
248
  client.notify(notification);
78
249
  };
79
250
  return {
251
+ openReport: () => client.openReport,
252
+ setTelemetryParent: () => { },
253
+ openAnotherSession: async (options) => {
254
+ const openedSessionId = await request({
255
+ kind: "openAnotherSession",
256
+ options,
257
+ });
258
+ lease.retain();
259
+ return workerBinding(client, lease, openedSessionId);
260
+ },
80
261
  execute: (sql, params, options) => request({ kind: "execute", sql, params, options }),
81
262
  executeBatch: (statements, options) => request({ kind: "executeBatch", statements, options }),
82
263
  observe: async (sql, params) => {
@@ -96,7 +277,6 @@ function workerBinding(client) {
96
277
  activeBranchId: () => request({ kind: "activeBranchId" }),
97
278
  activeAccountId: () => request({ kind: "activeAccountId" }),
98
279
  createBranch: (options) => request({ kind: "createBranch", options }),
99
- createCheckpoint: () => request({ kind: "createCheckpoint" }),
100
280
  undo: () => request({ kind: "undo" }),
101
281
  redo: () => request({ kind: "redo" }),
102
282
  switchBranch: (options) => request({ kind: "switchBranch", options }),
@@ -104,12 +284,46 @@ function workerBinding(client) {
104
284
  mergeBranchPreview: (options) => request({ kind: "mergeBranchPreview", options }),
105
285
  mergeBranch: (options) => request({ kind: "mergeBranch", options }),
106
286
  syncDiskToLix: () => request({ kind: "syncDiskToLix" }),
287
+ exportSnapshot: () => {
288
+ const exportId = request({ kind: "exportSnapshot" });
289
+ let canceled = false;
290
+ return {
291
+ next: async () => {
292
+ if (canceled)
293
+ return undefined;
294
+ return request({
295
+ kind: "exportSnapshot.next",
296
+ exportId: await exportId,
297
+ });
298
+ },
299
+ cancel: async () => {
300
+ if (canceled)
301
+ return;
302
+ canceled = true;
303
+ await request({
304
+ kind: "exportSnapshot.cancel",
305
+ exportId: await exportId,
306
+ });
307
+ },
308
+ };
309
+ },
107
310
  close: async () => {
108
311
  if (closed)
109
312
  return;
110
- await request({ kind: "close" });
111
- closed = true;
112
- await releaseWorker(client);
313
+ try {
314
+ await request({ kind: "close" });
315
+ }
316
+ catch (error) {
317
+ // A failed semantic close may leave the worker host retaining its OPFS
318
+ // session. Terminating the worker is the ownership backstop before an
319
+ // outer caller can safely release its cross-tab lock.
320
+ await client.terminate().catch(() => undefined);
321
+ throw error;
322
+ }
323
+ finally {
324
+ closed = true;
325
+ await lease.release();
326
+ }
113
327
  },
114
328
  };
115
329
  }
@@ -128,6 +342,7 @@ function workerTransactionBinding(request, transactionId) {
128
342
  }
129
343
  function workerObserveBinding(request, notify, observeId) {
130
344
  return {
345
+ setTelemetryParent: () => { },
131
346
  next: () => request({ kind: "observe.next", observeId }),
132
347
  close: () => notify({ kind: "observe.close", observeId }),
133
348
  };
@@ -143,11 +358,16 @@ async function releaseWorker(client) {
143
358
  export class LixWorkerClient {
144
359
  connection;
145
360
  nextRequestId = 1;
361
+ nextSnapshotInputId = 1;
146
362
  pending = new Map();
147
363
  disposed = false;
148
364
  leased = false;
149
365
  onDisposed;
150
366
  telemetry;
367
+ syncServer;
368
+ onProgress;
369
+ openReport;
370
+ syncFetchControllers = new Map();
151
371
  constructor(connection = createWorkerConnection()) {
152
372
  this.connection = connection;
153
373
  connection.onMessage((message) => this.handleMessage(message));
@@ -156,12 +376,17 @@ export class LixWorkerClient {
156
376
  get isDisposed() {
157
377
  return this.disposed;
158
378
  }
159
- beginLease(onDisposed, telemetry) {
379
+ allocateSnapshotInputId() {
380
+ return this.nextSnapshotInputId++;
381
+ }
382
+ beginLease(onDisposed, telemetry, syncServer, onProgress) {
160
383
  if (this.disposed || this.leased)
161
384
  throw workerClosedError();
162
385
  this.leased = true;
163
386
  this.onDisposed = onDisposed;
164
387
  this.telemetry = telemetry;
388
+ this.syncServer = syncServer;
389
+ this.onProgress = onProgress;
165
390
  }
166
391
  endLease() {
167
392
  if (!this.leased)
@@ -170,9 +395,15 @@ export class LixWorkerClient {
170
395
  const onDisposed = this.onDisposed;
171
396
  this.onDisposed = undefined;
172
397
  this.telemetry = undefined;
398
+ this.syncServer = undefined;
399
+ this.onProgress = undefined;
400
+ this.openReport = undefined;
401
+ for (const controller of this.syncFetchControllers.values())
402
+ controller.abort();
403
+ this.syncFetchControllers.clear();
173
404
  onDisposed?.();
174
405
  }
175
- request(operation) {
406
+ request(operation, sessionId = 0) {
176
407
  if (this.disposed || !this.leased) {
177
408
  return Promise.reject(workerClosedError());
178
409
  }
@@ -185,7 +416,12 @@ export class LixWorkerClient {
185
416
  reject,
186
417
  });
187
418
  try {
188
- this.connection.postMessage({ id, operation });
419
+ this.connection.postMessage({
420
+ id,
421
+ sessionId,
422
+ telemetryParent: this.telemetry?.parentContext?.(),
423
+ operation,
424
+ });
189
425
  }
190
426
  catch (error) {
191
427
  this.pending.delete(id);
@@ -219,12 +455,7 @@ export class LixWorkerClient {
219
455
  }
220
456
  handleMessage(message) {
221
457
  if ("kind" in message) {
222
- try {
223
- this.telemetry?.onSpan(message.span);
224
- }
225
- catch {
226
- // Telemetry callbacks are isolated from Lix operation results.
227
- }
458
+ this.handleWorkerEvent(message);
228
459
  return;
229
460
  }
230
461
  const pending = this.pending.get(message.id);
@@ -238,6 +469,107 @@ export class LixWorkerClient {
238
469
  else
239
470
  pending.reject(deserializeWorkerError(message.error));
240
471
  }
472
+ handleWorkerEvent(message) {
473
+ switch (message.kind) {
474
+ case "telemetry":
475
+ try {
476
+ this.telemetry?.onSpan(message.span);
477
+ }
478
+ catch {
479
+ // Telemetry callbacks are isolated from Lix operation results.
480
+ }
481
+ break;
482
+ case "open.progress":
483
+ try {
484
+ this.onProgress?.(message.progress);
485
+ }
486
+ catch {
487
+ // Open progress is observational and cannot fail repository opening.
488
+ }
489
+ break;
490
+ case "sync.headers":
491
+ void this.resolveSyncHeaders(message.requestId);
492
+ break;
493
+ case "sync.fetch":
494
+ void this.resolveSyncFetch(message.requestId, message.request);
495
+ break;
496
+ case "sync.fetch.cancel":
497
+ this.syncFetchControllers.get(message.requestId)?.abort();
498
+ this.syncFetchControllers.delete(message.requestId);
499
+ break;
500
+ }
501
+ }
502
+ async resolveSyncHeaders(requestId) {
503
+ try {
504
+ const source = this.syncServer?.headers;
505
+ const headers = typeof source === "function" ? await source() : source;
506
+ this.notify({
507
+ kind: "sync.headers.result",
508
+ requestId,
509
+ result: { ok: true, headers: headerEntries(headers) },
510
+ });
511
+ }
512
+ catch (error) {
513
+ this.notify({
514
+ kind: "sync.headers.result",
515
+ requestId,
516
+ result: { ok: false, error: serializeWorkerError(error) },
517
+ });
518
+ }
519
+ }
520
+ async resolveSyncFetch(requestId, request) {
521
+ const fetcher = this.syncServer?.fetch;
522
+ if (!fetcher) {
523
+ this.notify({
524
+ kind: "sync.fetch.result",
525
+ requestId,
526
+ result: {
527
+ ok: false,
528
+ error: serializeWorkerError(new Error("Sync fetch bridge is unavailable")),
529
+ },
530
+ });
531
+ return;
532
+ }
533
+ const controller = new AbortController();
534
+ this.syncFetchControllers.set(requestId, controller);
535
+ try {
536
+ const response = await fetcher(request.url, {
537
+ method: request.method,
538
+ headers: request.headers,
539
+ body: typeof request.body === "string"
540
+ ? request.body
541
+ : request.body?.slice().buffer,
542
+ credentials: request.credentials,
543
+ signal: controller.signal,
544
+ });
545
+ const body = await readSyncResponseBody(response, request.responseLimit, controller);
546
+ this.notify({
547
+ kind: "sync.fetch.result",
548
+ requestId,
549
+ result: {
550
+ ok: true,
551
+ response: {
552
+ status: response.status,
553
+ statusText: response.statusText,
554
+ headers: headerEntries(response.headers),
555
+ body,
556
+ },
557
+ },
558
+ });
559
+ }
560
+ catch (error) {
561
+ if (!controller.signal.aborted || isSyncResponseTooLarge(error)) {
562
+ this.notify({
563
+ kind: "sync.fetch.result",
564
+ requestId,
565
+ result: { ok: false, error: serializeWorkerError(error) },
566
+ });
567
+ }
568
+ }
569
+ finally {
570
+ this.syncFetchControllers.delete(requestId);
571
+ }
572
+ }
241
573
  handleFatal(error) {
242
574
  if (this.disposed)
243
575
  return;
@@ -255,9 +587,90 @@ export class LixWorkerClient {
255
587
  this.connection.unref();
256
588
  }
257
589
  }
590
+ async function readSyncResponseBody(response, limit, controller) {
591
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
592
+ throw new TypeError("Browser sync fetch has no valid response limit");
593
+ }
594
+ const declaredLength = Number(response.headers.get("content-length"));
595
+ if (Number.isFinite(declaredLength) && declaredLength > limit) {
596
+ const error = syncResponseTooLarge(limit);
597
+ controller.abort(error);
598
+ throw error;
599
+ }
600
+ const stream = response.body;
601
+ if (!stream)
602
+ return new Uint8Array();
603
+ const reader = stream.getReader();
604
+ const chunks = [];
605
+ let total = 0;
606
+ try {
607
+ while (true) {
608
+ const { done, value } = await reader.read();
609
+ if (done)
610
+ break;
611
+ total += value.byteLength;
612
+ if (total > limit) {
613
+ const error = syncResponseTooLarge(limit);
614
+ controller.abort(error);
615
+ await reader.cancel(error).catch(() => undefined);
616
+ throw error;
617
+ }
618
+ chunks.push(value);
619
+ }
620
+ }
621
+ finally {
622
+ reader.releaseLock();
623
+ }
624
+ const body = new Uint8Array(total);
625
+ let offset = 0;
626
+ for (const chunk of chunks) {
627
+ body.set(chunk, offset);
628
+ offset += chunk.byteLength;
629
+ }
630
+ return body;
631
+ }
632
+ function syncResponseTooLarge(limit) {
633
+ const error = new Error(`sync fetch response exceeds ${limit} bytes`);
634
+ error.name = "LixError";
635
+ error.code = "LIX_ERROR_SYNC_RESPONSE_TOO_LARGE";
636
+ return error;
637
+ }
638
+ function isSyncResponseTooLarge(error) {
639
+ return (error instanceof Error &&
640
+ error.code ===
641
+ "LIX_ERROR_SYNC_RESPONSE_TOO_LARGE");
642
+ }
258
643
  function workerClosedError() {
259
644
  const error = new Error("Lix worker is closed");
260
645
  error.name = "LixError";
261
646
  error.code = "LIX_ERROR_CLOSED";
262
647
  return error;
263
648
  }
649
+ function serializeSyncServer(server) {
650
+ if (!server)
651
+ return undefined;
652
+ return {
653
+ url: new URL(server.url).toString(),
654
+ headers: typeof server.headers === "function"
655
+ ? undefined
656
+ : headerEntries(server.headers),
657
+ dynamicHeaders: typeof server.headers === "function",
658
+ customFetch: server.fetch !== undefined,
659
+ };
660
+ }
661
+ function headerEntries(headers) {
662
+ const entries = [];
663
+ new Headers(headers).forEach((value, name) => entries.push([name, value]));
664
+ return entries;
665
+ }
666
+ async function resolveDirectSyncServer(server) {
667
+ if (!server)
668
+ return undefined;
669
+ const source = server.headers;
670
+ const headers = typeof source === "function" ? await source() : source;
671
+ return {
672
+ url: new URL(server.url).toString(),
673
+ headers: headerEntries(headers),
674
+ fetch: server.fetch,
675
+ };
676
+ }
@@ -1,4 +1,4 @@
1
- import type { LixBinding, LixStorageConfig, TelemetryDispatch } from "../binding-types.js";
1
+ import type { LixBinding, LixStorageConfig, TelemetryDispatch, TelemetryParentContext, OpenProgressDispatch, SyncServerBindingOptions } from "../binding-types.js";
2
2
  import type { WorkerConnection } from "./protocol.js";
3
- export declare const openDirectLixBinding: undefined | ((storage: LixStorageConfig, telemetry?: TelemetryDispatch) => Promise<LixBinding>);
3
+ export declare const openDirectLixBinding: undefined | ((storage: LixStorageConfig, telemetry?: TelemetryDispatch, telemetryParent?: TelemetryParentContext, server?: SyncServerBindingOptions, openProgress?: OpenProgressDispatch, snapshot?: ReadableStream<Uint8Array>) => Promise<LixBinding>);
4
4
  export declare function createWorkerConnection(): WorkerConnection;
@@ -1,5 +1,5 @@
1
- import type { LixBinding, LixStorageConfig, TelemetryDispatch } from "../binding-types.js";
1
+ import type { LixBinding, LixStorageConfig, SyncServerBindingOptions, TelemetryDispatch, TelemetryParentContext, OpenProgressDispatch } from "../binding-types.js";
2
2
  import type { WorkerConnection } from "./protocol.js";
3
3
  export declare function createWorkerConnection(): WorkerConnection;
4
4
  export declare function workerExecArgv(execArgv: readonly string[]): string[];
5
- export declare const openDirectLixBinding: (storage: LixStorageConfig, telemetry?: TelemetryDispatch) => Promise<LixBinding | undefined>;
5
+ export declare const openDirectLixBinding: (storage: LixStorageConfig, telemetry?: TelemetryDispatch, telemetryParent?: TelemetryParentContext, server?: SyncServerBindingOptions, openProgress?: OpenProgressDispatch, snapshot?: ReadableStream<Uint8Array>) => Promise<LixBinding | undefined>;
@@ -1,5 +1,5 @@
1
1
  import { Worker } from "node:worker_threads";
2
- import { openNativeLixBinding } from "../binding.node.js";
2
+ import { openLixBinding } from "../binding.node.js";
3
3
  export function createWorkerConnection() {
4
4
  const worker = new Worker(new URL("./entry.node.js", import.meta.url), {
5
5
  name: "lix",
@@ -52,13 +52,6 @@ export function workerExecArgv(execArgv) {
52
52
  /// Native Lix already owns a dedicated serialized engine actor. Routing it
53
53
  /// through a second JavaScript worker adds two message-port hops per query
54
54
  /// without adding isolation or concurrency.
55
- export const openDirectLixBinding = async (storage, telemetry) => {
56
- try {
57
- return await openNativeLixBinding(storage, telemetry);
58
- }
59
- catch (error) {
60
- if (storage.kind === "memory")
61
- return undefined;
62
- throw error;
63
- }
55
+ export const openDirectLixBinding = async (storage, telemetry, telemetryParent, server, openProgress, snapshot) => {
56
+ return openLixBinding(storage, telemetry, telemetryParent, server, openProgress, snapshot);
64
57
  };
@@ -1,2 +1,3 @@
1
- import { type WorkerHostEndpoint } from "./protocol.js";
1
+ import { type WorkerHostEndpoint, type WorkerSyncFetchResponse } from "./protocol.js";
2
2
  export declare function startWorkerHost(endpoint: WorkerHostEndpoint): void;
3
+ export declare function responseFromSyncFetch(resolved: WorkerSyncFetchResponse): Response;