@lix-js/sdk 0.8.4 → 0.9.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 (46) hide show
  1. package/README.md +82 -16
  2. package/dist/binding-types.d.ts +18 -2
  3. package/dist/binding.browser.d.ts +2 -2
  4. package/dist/binding.browser.js +3 -3
  5. package/dist/binding.node.d.ts +2 -2
  6. package/dist/binding.node.js +10 -4
  7. package/dist/bundled-plugins/plugin_csv_v2.lixplugin +0 -0
  8. package/dist/bundled-plugins/plugin_markdown_incremental_v2.lixplugin +0 -0
  9. package/dist/bundled-plugins.js +4 -4
  10. package/dist/client-state.d.ts +40 -0
  11. package/dist/client-state.js +178 -0
  12. package/dist/index.d.ts +2 -1
  13. package/dist/lix.d.ts +44 -0
  14. package/dist/lix.js +369 -0
  15. package/dist/local-storage-adapter.d.ts +26 -0
  16. package/dist/local-storage-adapter.js +117 -0
  17. package/dist/open-lix.d.ts +3 -34
  18. package/dist/open-lix.js +103 -170
  19. package/dist/remote/client.d.ts +8 -0
  20. package/dist/remote/client.js +1036 -0
  21. package/dist/remote/protocol.d.ts +166 -0
  22. package/dist/remote/protocol.js +362 -0
  23. package/dist/remote/sse.d.ts +12 -0
  24. package/dist/remote/sse.js +87 -0
  25. package/dist/snapshot-persistence.d.ts +7 -0
  26. package/dist/snapshot-persistence.js +26 -0
  27. package/dist/types.d.ts +58 -1
  28. package/dist/wasm/lix_js_sdk.d.ts +16 -4
  29. package/dist/wasm/lix_js_sdk.js +75 -17
  30. package/dist/wasm/lix_js_sdk_bg.wasm +0 -0
  31. package/dist/wasm/lix_js_sdk_bg.wasm.d.ts +8 -2
  32. package/dist/worker/client.d.ts +23 -4
  33. package/dist/worker/client.js +287 -10
  34. package/dist/worker/host.js +30 -2
  35. package/dist/worker/protocol.d.ts +26 -2
  36. package/dist/workerd.js +1 -3
  37. package/package.json +19 -16
  38. package/dist/bundled-plugins/plugin_csv.lixplugin +0 -0
  39. package/dist/bundled-plugins/plugin_md_v2.lixplugin +0 -0
  40. package/dist/jco/js-component-bindgen-component.core.wasm +0 -0
  41. package/dist/jco/js-component-bindgen-component.core2.wasm +0 -0
  42. package/dist/jco/js-component-bindgen-component.js +0 -13662
  43. package/dist/jco-transpile.browser.d.ts +0 -14
  44. package/dist/jco-transpile.browser.js +0 -22
  45. package/dist/plugin-runtime.d.ts +0 -45
  46. package/dist/plugin-runtime.js +0 -124
package/dist/open-lix.js CHANGED
@@ -1,16 +1,7 @@
1
- import { localFilesystemAlreadyOpen, localFilesystemNotOpen, invalidArgument, } from "./errors.js";
2
- import { normalizeOptionals, wrapExecuteResult } from "./result.js";
3
- import { normalizeParam, toNativeValue } from "./value.js";
4
- import { openLixWorker } from "./worker/client.js";
5
- const transactionFinalizer = new FinalizationRegistry(({ client, transactionId }) => {
6
- client.notify({ kind: "transaction.abandon", transactionId });
7
- });
8
- const observeFinalizer = new FinalizationRegistry(({ client, observeId }) => {
9
- void observeId.then((id) => {
10
- if (id !== undefined)
11
- client.notify({ kind: "observe.close", observeId: id });
12
- });
13
- });
1
+ import { localFilesystemAlreadyOpen, localFilesystemNotOpen, } from "./errors.js";
2
+ import { ACTIVE_BRANCH_CLIENT_STATE_KEY, openClientState, } from "./client-state.js";
3
+ import { Lix } from "./lix.js";
4
+ export { Lix, LixTransaction, ObserveEvents } from "./lix.js";
14
5
  export class SQLite {
15
6
  path;
16
7
  constructor(options) {
@@ -53,16 +44,13 @@ export class LocalFilesystem {
53
44
  throw new TypeError("importPaths() paths must contain non-empty strings");
54
45
  }
55
46
  if (path.endsWith("/")) {
56
- throw new TypeError("importPaths() paths must contain file paths, not directory paths");
47
+ throw new TypeError("importPaths() paths must not end with a trailing slash");
57
48
  }
58
49
  }
59
- await this.client("importPaths").request({
60
- kind: "importFilesystemPaths",
61
- paths: [...paths],
62
- });
50
+ await this.client("importPaths").importFilesystemPaths([...paths]);
63
51
  }
64
52
  async syncDiskToLix() {
65
- return this.client("syncDiskToLix").request({ kind: "syncDiskToLix" });
53
+ return this.client("syncDiskToLix").syncDiskToLix();
66
54
  }
67
55
  client(operation) {
68
56
  const client = openLocalFilesystems.get(this);
@@ -79,11 +67,67 @@ export async function openLix(options = {}) {
79
67
  if ("backend" in options) {
80
68
  throw new TypeError("openLix() option 'backend' was removed; use 'storage' instead");
81
69
  }
70
+ if (options.telemetry !== undefined &&
71
+ (typeof options.telemetry !== "object" ||
72
+ typeof options.telemetry.onSpan !== "function")) {
73
+ throw new TypeError("openLix() telemetry requires an onSpan callback");
74
+ }
75
+ if (options.server !== undefined) {
76
+ const { openRemoteLixBinding } = await import("./remote/client.js");
77
+ if (options.storage === undefined) {
78
+ return new Lix(await openRemoteLixBinding(options.server));
79
+ }
80
+ assertSnapshotStorage(options.storage);
81
+ const { openPersistentLixWorkerBinding } = await import("./worker/client.js");
82
+ const clientBinding = await openPersistentLixWorkerBinding({
83
+ storage: options.storage,
84
+ namespace: remoteClientStateNamespace(options.server.url),
85
+ });
86
+ let clientState;
87
+ try {
88
+ clientState = await openClientState({
89
+ binding: clientBinding,
90
+ closeBinding: true,
91
+ });
92
+ }
93
+ catch (error) {
94
+ await clientBinding.close().catch(() => undefined);
95
+ throw error;
96
+ }
97
+ const restoredBranchId = clientState.get(ACTIVE_BRANCH_CLIENT_STATE_KEY);
98
+ let remoteBinding;
99
+ try {
100
+ try {
101
+ remoteBinding = await openRemoteLixBinding(options.server, {
102
+ initialActiveBranchId: restoredBranchId,
103
+ });
104
+ }
105
+ catch (error) {
106
+ if (!restoredBranchId || !isBranchNotFoundError(error))
107
+ throw error;
108
+ remoteBinding = await openRemoteLixBinding(options.server);
109
+ }
110
+ const activeBranchId = await remoteBinding.activeBranchId();
111
+ if (activeBranchId !== restoredBranchId) {
112
+ await clientState.set(ACTIVE_BRANCH_CLIENT_STATE_KEY, activeBranchId);
113
+ }
114
+ return new Lix(remoteBinding, clientState);
115
+ }
116
+ catch (error) {
117
+ await remoteBinding?.close().catch(() => undefined);
118
+ await clientState.close().catch(() => undefined);
119
+ throw error;
120
+ }
121
+ }
122
+ const { openLixWorkerBinding } = await import("./worker/client.js");
82
123
  if (options.storage === undefined) {
83
- return new Lix(await openLixWorker({ kind: "memory" }));
124
+ return new Lix(await openLixWorkerBinding({ kind: "memory" }, undefined, options.telemetry));
84
125
  }
85
126
  if (options.storage instanceof SQLite) {
86
- return new Lix(await openLixWorker({ kind: "sqlite", path: options.storage.path }));
127
+ return new Lix(await openLixWorkerBinding({
128
+ kind: "sqlite",
129
+ path: options.storage.path,
130
+ }, undefined, options.telemetry));
87
131
  }
88
132
  if (options.storage instanceof LocalFilesystem) {
89
133
  const storage = options.storage;
@@ -92,175 +136,64 @@ export async function openLix(options = {}) {
92
136
  }
93
137
  openLocalFilesystems.set(storage, null);
94
138
  try {
95
- const client = await openLixWorker({
139
+ const binding = await openLixWorkerBinding({
96
140
  kind: "localFilesystem",
97
141
  path: storage.path,
98
142
  lixDir: storage.lixDir,
99
143
  syncAllFiles: storage.syncAllFiles,
100
- }, () => openLocalFilesystems.delete(storage));
101
- openLocalFilesystems.set(storage, client);
102
- return new Lix(client);
144
+ }, () => openLocalFilesystems.delete(storage), options.telemetry);
145
+ openLocalFilesystems.set(storage, binding);
146
+ return new Lix(binding);
103
147
  }
104
148
  catch (error) {
105
149
  openLocalFilesystems.delete(storage);
106
150
  throw error;
107
151
  }
108
152
  }
109
- throw new TypeError("openLix() requires storage to be SQLite or LocalFilesystem");
110
- }
111
- export class Lix {
112
- client;
113
- closePromise;
114
- constructor(client) {
115
- this.client = client;
116
- }
117
- async execute(sql, params = [], options) {
118
- assertExecuteArgs("lix", sql, params, options);
119
- return wrapExecuteResult(await this.client.request({
120
- kind: "execute",
121
- sql,
122
- params: params.map((param, index) => toNativeValue(normalizeParam(param, index))),
123
- options,
124
- }));
125
- }
126
- observe(sql, params = []) {
127
- assertSqlArgs("observe", "lix", sql, params);
128
- return new ObserveEvents(this.client, this.client.request({
129
- kind: "observe",
130
- sql,
131
- params: params.map((param, index) => toNativeValue(normalizeParam(param, index))),
132
- }));
133
- }
134
- async beginTransaction() {
135
- const transactionId = await this.client.request({
136
- kind: "beginTransaction",
153
+ if (isSnapshotStorage(options.storage)) {
154
+ const { openPersistentLixWorkerBinding } = await import("./worker/client.js");
155
+ const binding = await openPersistentLixWorkerBinding({
156
+ storage: options.storage,
157
+ namespace: "local",
158
+ telemetry: options.telemetry,
137
159
  });
138
- return new LixTransaction(this.client, transactionId);
139
- }
140
- async activeBranchId() {
141
- return this.client.request({ kind: "activeBranchId" });
142
- }
143
- async createBranch(options) {
144
- return this.client.request({ kind: "createBranch", options });
145
- }
146
- async switchBranch(options) {
147
- return this.client.request({ kind: "switchBranch", options });
148
- }
149
- async mergeBranchPreview(options) {
150
- return normalizeOptionals(await this.client.request({ kind: "mergeBranchPreview", options }));
151
- }
152
- async mergeBranch(options) {
153
- const receipt = normalizeOptionals(await this.client.request({ kind: "mergeBranch", options }));
154
- receipt.createdMergeCommitId ??= null;
155
- return receipt;
156
- }
157
- async close() {
158
- this.closePromise ??= (async () => {
159
- await this.client.request({ kind: "close" });
160
- await this.client.terminate();
161
- })();
162
160
  try {
163
- await this.closePromise;
161
+ const clientState = await openClientState({ binding });
162
+ return new Lix(binding, clientState);
164
163
  }
165
164
  catch (error) {
166
- this.closePromise = undefined;
165
+ await binding.close().catch(() => undefined);
167
166
  throw error;
168
167
  }
169
168
  }
169
+ throw new TypeError("openLix() requires storage to be SQLite, LocalFilesystem, or a Lix snapshot storage adapter");
170
170
  }
171
- export class ObserveEvents {
172
- client;
173
- setup = {};
174
- closed = false;
175
- observeId;
176
- constructor(client, observeId) {
177
- this.client = client;
178
- const setup = this.setup;
179
- this.observeId = observeId.catch((error) => {
180
- setup.error = error;
181
- return undefined;
182
- });
183
- observeFinalizer.register(this, { client, observeId: this.observeId }, this);
184
- }
185
- async next() {
186
- if (this.closed)
187
- return undefined;
188
- const observeId = await this.observeId;
189
- if (observeId === undefined) {
190
- throw this.setup.error;
191
- }
192
- const event = await this.client.request({
193
- kind: "observe.next",
194
- observeId,
195
- });
196
- if (event == null) {
197
- return undefined;
198
- }
199
- return {
200
- sequence: event.sequence,
201
- mutationSequence: event.mutationSequence,
202
- result: wrapExecuteResult(event.rows),
203
- };
204
- }
205
- close() {
206
- if (this.closed)
207
- return;
208
- this.closed = true;
209
- observeFinalizer.unregister(this);
210
- void this.observeId.then((observeId) => {
211
- if (observeId !== undefined) {
212
- this.client.notify({ kind: "observe.close", observeId });
213
- }
214
- });
215
- }
171
+ function isSnapshotStorage(value) {
172
+ return (typeof value === "object" &&
173
+ value !== null &&
174
+ typeof value.load === "function" &&
175
+ typeof value.save === "function");
216
176
  }
217
- export class LixTransaction {
218
- client;
219
- transactionId;
220
- constructor(client, transactionId) {
221
- this.client = client;
222
- this.transactionId = transactionId;
223
- transactionFinalizer.register(this, { client, transactionId }, this);
224
- }
225
- async execute(sql, params = [], options) {
226
- assertExecuteArgs("lixTransaction", sql, params, options);
227
- return wrapExecuteResult(await this.client.request({
228
- kind: "transaction.execute",
229
- transactionId: this.transactionId,
230
- sql,
231
- params: params.map((param, index) => toNativeValue(normalizeParam(param, index))),
232
- options,
233
- }));
234
- }
235
- async commit() {
236
- return this.finish("transaction.commit");
237
- }
238
- async rollback() {
239
- return this.finish("transaction.rollback");
240
- }
241
- async finish(kind) {
242
- transactionFinalizer.unregister(this);
243
- return this.client.request({ kind, transactionId: this.transactionId });
177
+ function assertSnapshotStorage(value) {
178
+ if (!isSnapshotStorage(value)) {
179
+ throw new TypeError("openLix() remote storage must implement load() and save()");
244
180
  }
245
181
  }
246
- function assertExecuteArgs(receiver, sql, params, options) {
247
- assertSqlArgs("execute", receiver, sql, params);
248
- if (options === undefined) {
249
- return;
250
- }
251
- if (!options || typeof options !== "object" || Array.isArray(options)) {
252
- throw invalidArgument("execute", "options", "object", typeof options, receiver);
253
- }
254
- if (options.originKey !== undefined &&
255
- typeof options.originKey !== "string") {
256
- throw invalidArgument("execute", "options.originKey", "string", typeof options.originKey, receiver);
257
- }
182
+ function remoteClientStateNamespace(value) {
183
+ let url;
184
+ try {
185
+ url = new URL(value);
186
+ }
187
+ catch {
188
+ throw new TypeError("openLix() remote server url must be an absolute URL");
189
+ }
190
+ url.pathname = url.pathname.replace(/\/$/, "");
191
+ url.search = "";
192
+ url.hash = "";
193
+ return `remote:${url.href}`;
258
194
  }
259
- function assertSqlArgs(operation, receiver, sql, params) {
260
- if (typeof sql !== "string") {
261
- throw invalidArgument(operation, "sql", "string", typeof sql, receiver);
262
- }
263
- if (!Array.isArray(params)) {
264
- throw invalidArgument(operation, "params", "array", typeof params, receiver);
265
- }
195
+ function isBranchNotFoundError(error) {
196
+ return (error instanceof Error &&
197
+ "code" in error &&
198
+ error.code === "LIX_BRANCH_NOT_FOUND");
266
199
  }
@@ -0,0 +1,8 @@
1
+ import type { LixBinding } from "../binding-types.js";
2
+ import type { RemoteLixServerOptions } from "../types.js";
3
+ import { type RemoteHandshakeRequest } from "./protocol.js";
4
+ type RemoteLixClientOptions = {
5
+ initialActiveBranchId?: RemoteHandshakeRequest["activeBranchId"];
6
+ };
7
+ export declare function openRemoteLixBinding(options: RemoteLixServerOptions, clientOptions?: RemoteLixClientOptions): Promise<LixBinding>;
8
+ export {};