@lix-js/sdk 0.16.1 → 0.17.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 (71) hide show
  1. package/README.md +162 -44
  2. package/dist/binding-types.d.ts +15 -5
  3. package/dist/binding.browser.d.ts +2 -0
  4. package/dist/binding.browser.js +14 -4
  5. package/dist/binding.node-wasm.d.ts +1 -1
  6. package/dist/binding.node-wasm.js +5 -3
  7. package/dist/binding.node.d.ts +2 -0
  8. package/dist/binding.node.js +34 -8
  9. package/dist/bundled-plugins/plugin_csv.lixplugin +0 -0
  10. package/dist/bundled-plugins/plugin_markdown.lixplugin +0 -0
  11. package/dist/compatibility.d.ts +6 -0
  12. package/dist/compatibility.js +6 -0
  13. package/dist/component-host/dispatch.d.ts +12 -0
  14. package/dist/component-host/dispatch.js +364 -0
  15. package/dist/component-host/index.d.ts +15 -0
  16. package/dist/component-host/index.js +84 -0
  17. package/dist/component-host/instrument.d.ts +6 -0
  18. package/dist/component-host/instrument.js +260 -0
  19. package/dist/conversion-provider.d.ts +4 -0
  20. package/dist/conversion-provider.js +20 -0
  21. package/dist/hosted-lix.js +1 -1
  22. package/dist/http-transport.d.ts +25 -0
  23. package/dist/http-transport.js +162 -0
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.js +1 -0
  26. package/dist/lix.d.ts +14 -9
  27. package/dist/lix.js +44 -9
  28. package/dist/migration-binding.browser.d.ts +33 -0
  29. package/dist/migration-binding.browser.js +46 -0
  30. package/dist/migration-binding.node.d.ts +6 -0
  31. package/dist/migration-binding.node.js +5 -0
  32. package/dist/migration-wasm/lix_js_sdk.d.ts +271 -0
  33. package/dist/migration-wasm/lix_js_sdk.js +1852 -0
  34. package/dist/migration-wasm/lix_js_sdk_bg.wasm +4 -0
  35. package/dist/migration-wasm/lix_js_sdk_bg.wasm.d.ts +101 -0
  36. package/dist/migration.d.ts +23 -0
  37. package/dist/migration.js +55 -0
  38. package/dist/open-lix.js +29 -18
  39. package/dist/open-progress.d.ts +10 -0
  40. package/dist/open-progress.js +59 -0
  41. package/dist/remote/client.d.ts +2 -1
  42. package/dist/remote/client.js +11 -1
  43. package/dist/result.d.ts +4 -3
  44. package/dist/result.js +3 -4
  45. package/dist/storage-adapter.d.ts +7 -1
  46. package/dist/storage-ownership.d.ts +5 -0
  47. package/dist/storage-ownership.js +4 -0
  48. package/dist/types.d.ts +65 -31
  49. package/dist/wasm/lix_js_sdk.d.ts +45 -16
  50. package/dist/wasm/lix_js_sdk.js +214 -60
  51. package/dist/wasm/lix_js_sdk_bg.wasm +2 -2
  52. package/dist/wasm/lix_js_sdk_bg.wasm.d.ts +17 -9
  53. package/dist/worker/client.d.ts +12 -3
  54. package/dist/worker/client.js +95 -81
  55. package/dist/worker/durable-local-admission.d.ts +20 -0
  56. package/dist/worker/durable-local-admission.js +87 -0
  57. package/dist/worker/entry.shared.browser.d.ts +1 -0
  58. package/dist/worker/entry.shared.browser.js +211 -0
  59. package/dist/worker/factory.browser.d.ts +2 -0
  60. package/dist/worker/factory.browser.js +65 -1
  61. package/dist/worker/factory.node.d.ts +1 -0
  62. package/dist/worker/factory.node.js +3 -0
  63. package/dist/worker/host.d.ts +4 -2
  64. package/dist/worker/host.js +86 -32
  65. package/dist/worker/protocol.d.ts +25 -8
  66. package/dist/worker/protocol.js +25 -7
  67. package/dist/worker/shared-admission.d.ts +26 -0
  68. package/dist/worker/shared-admission.js +116 -0
  69. package/dist/worker/shared-engine.d.ts +34 -0
  70. package/dist/worker/shared-engine.js +194 -0
  71. package/package.json +21 -9
@@ -0,0 +1,194 @@
1
+ import { fetchTransport, HttpTransportError } from "../http-transport.js";
2
+ /** One physical owner; ports receive independent sessions, never the root. */
3
+ export class SharedEngineOwner {
4
+ open;
5
+ root;
6
+ principalId;
7
+ state = "closed";
8
+ get lifecycleState() { return this.state; }
9
+ clients = new Set();
10
+ queue = Promise.resolve();
11
+ constructor(open) {
12
+ this.open = open;
13
+ }
14
+ backgroundTelemetry = span => {
15
+ for (const client of this.clients) {
16
+ try {
17
+ client.telemetry?.(span);
18
+ }
19
+ catch { /* Host telemetry cannot interrupt engine work. */ }
20
+ }
21
+ };
22
+ attach(client) {
23
+ const operation = this.queue.then(async () => {
24
+ if (client.isDisconnected?.())
25
+ throw new Error("Shared engine client disconnected before admission");
26
+ // Admission precedes storage opening and captures initial credentials.
27
+ if (this.state === "closing")
28
+ throw new HttpTransportError("LIX_OWNER_CLOSE_FAILED", "Storage owner has not completed closing");
29
+ const identity = await client.verifyIdentity();
30
+ if (client.isDisconnected?.())
31
+ throw new HttpTransportError("LIX_TRANSPORT_UNAVAILABLE", "Client disconnected during admission");
32
+ if (identity.authorityUrl !== client.server.url)
33
+ throw new HttpTransportError("LIX_SHARED_ENGINE_IDENTITY_MISMATCH", "Admission authority does not match this client");
34
+ const opensRoot = this.root === undefined;
35
+ if (!this.root) {
36
+ this.state = "opening";
37
+ const originalServer = client.server;
38
+ const headers = identity.headers;
39
+ // Bind native admission to the exact credentials actually used during
40
+ // opening; a later dynamic-header read cannot authorize another principal.
41
+ client.server = { ...originalServer, headers, headerProvider: identity.online === false
42
+ ? async () => { throw new HttpTransportError("LIX_IDENTITY_UNVERIFIED_OFFLINE", "Cached local admission does not authorize remote requests"); }
43
+ : undefined };
44
+ this.clients.add(client);
45
+ try {
46
+ this.root = await this.open(this.transport(), this.backgroundTelemetry, client);
47
+ const principal = await this.root.activeAccountId();
48
+ if (principal !== identity.accountId)
49
+ throw new HttpTransportError("LIX_SHARED_ENGINE_IDENTITY_MISMATCH", "Stored replica account does not match admission");
50
+ this.principalId = principal;
51
+ this.state = "ready";
52
+ }
53
+ catch (error) {
54
+ this.clients.delete(client);
55
+ if (this.root) {
56
+ this.state = "closing";
57
+ await this.root.close();
58
+ this.root = undefined;
59
+ this.principalId = undefined;
60
+ }
61
+ this.state = "closed";
62
+ throw error;
63
+ }
64
+ finally {
65
+ client.server = originalServer;
66
+ }
67
+ }
68
+ const root = this.root;
69
+ try {
70
+ if (client.server.url !== identity.authorityUrl ||
71
+ this.principalId !== identity.accountId) {
72
+ throw Object.assign(new Error("Shared engine repository/account does not match this client"), { code: "LIX_SHARED_ENGINE_IDENTITY_MISMATCH" });
73
+ }
74
+ await client.commitIdentity?.();
75
+ this.clients.add(client);
76
+ const report = opensRoot ? root.openReport?.() : undefined;
77
+ const child = await root.openAnotherSession({}, client.telemetry ?? (() => { }));
78
+ if (report === undefined)
79
+ return child;
80
+ // Only the opening caller performed initialization/migration. Later
81
+ // attachments must not inherit that first caller's opening report.
82
+ return new Proxy(child, {
83
+ get(target, property) {
84
+ if (property === "openReport")
85
+ return () => report;
86
+ const value = Reflect.get(target, property, target);
87
+ return typeof value === "function" ? value.bind(target) : value;
88
+ },
89
+ });
90
+ }
91
+ catch (error) {
92
+ this.clients.delete(client);
93
+ if (this.clients.size === 0) {
94
+ this.state = "closing";
95
+ await root.close();
96
+ this.root = undefined;
97
+ this.principalId = undefined;
98
+ this.state = "closed";
99
+ }
100
+ throw error;
101
+ }
102
+ });
103
+ this.queue = operation.catch(() => undefined);
104
+ return operation;
105
+ }
106
+ /** Serialize closed-storage conversion with root admission across all ports. */
107
+ convert(client, conversion, branchId) {
108
+ const operation = this.queue.then(async () => {
109
+ if (client.isDisconnected?.())
110
+ throw new Error("Shared engine client disconnected before conversion");
111
+ if (this.root) {
112
+ const identity = await client.verifyIdentity();
113
+ if (client.server.url !== identity.authorityUrl ||
114
+ this.principalId !== identity.accountId) {
115
+ throw Object.assign(new Error("Shared engine repository/account does not match this client"), { code: "LIX_SHARED_ENGINE_IDENTITY_MISMATCH" });
116
+ }
117
+ if (branchId !== undefined && branchId !== await this.root.activeBranchId()) {
118
+ throw Object.assign(new Error("The converted replica selected a different branch"), { code: "LIX_PARTIAL_CONVERSION_BRANCH_MISMATCH" });
119
+ }
120
+ // A competing caller already admitted the converted partial store.
121
+ // Never close its live sessions just to repeat an explicit conversion.
122
+ return;
123
+ }
124
+ if (this.state !== "closed")
125
+ throw new HttpTransportError("LIX_OWNER_NOT_CLOSED", "Migration requires a closed storage owner");
126
+ this.state = "migration-exclusive";
127
+ this.clients.add(client);
128
+ try {
129
+ await conversion(this.transport());
130
+ }
131
+ finally {
132
+ this.clients.delete(client);
133
+ this.state = "closed";
134
+ }
135
+ });
136
+ this.queue = operation.catch(() => undefined);
137
+ return operation;
138
+ }
139
+ deactivate(client) {
140
+ this.clients.delete(client);
141
+ }
142
+ async detach(client) {
143
+ const operation = this.queue.then(async () => {
144
+ this.clients.delete(client);
145
+ if (this.clients.size === 0 && this.root) {
146
+ const root = this.root;
147
+ // Keep ownership on a failed close; never open a second engine over it.
148
+ this.state = "closing";
149
+ await root.close();
150
+ this.root = undefined;
151
+ this.principalId = undefined;
152
+ this.state = "closed";
153
+ }
154
+ });
155
+ this.queue = operation.catch(() => undefined);
156
+ await operation;
157
+ }
158
+ transport() {
159
+ const first = this.clients.values().next().value;
160
+ if (!first)
161
+ throw new HttpTransportError("LIX_TRANSPORT_UNAVAILABLE", "No live shared-engine transport");
162
+ return {
163
+ url: first.server.url,
164
+ headers: [],
165
+ transport: async (request) => {
166
+ let unavailable;
167
+ // Credentials and callback remain paired when a tab leaves or suspends.
168
+ for (const client of this.clients) {
169
+ if (client.isDisconnected?.())
170
+ continue;
171
+ const server = client.server;
172
+ let supplied;
173
+ try {
174
+ supplied = (server.headerProvider ? await server.headerProvider() : server.headers).map(([name, value]) => [name, value]);
175
+ }
176
+ catch (error) {
177
+ unavailable = error;
178
+ continue;
179
+ }
180
+ if (!this.clients.has(client) || client.isDisconnected?.())
181
+ continue;
182
+ const headers = new Headers(request.init.headers);
183
+ for (const [name, value] of supplied)
184
+ headers.set(name, value);
185
+ const response = await (server.transport ?? fetchTransport())({ ...request, init: { ...request.init, headers, credentials: "omit" } });
186
+ if (response.status === 401 || response.status === 403)
187
+ await client.rejectCredentials?.(supplied);
188
+ return response;
189
+ }
190
+ throw unavailable ?? new HttpTransportError("LIX_TRANSPORT_UNAVAILABLE", "No verified live shared-engine transport");
191
+ },
192
+ };
193
+ }
194
+ }
package/package.json CHANGED
@@ -1,13 +1,21 @@
1
1
  {
2
2
  "name": "@lix-js/sdk",
3
3
  "type": "module",
4
- "version": "0.16.1",
4
+ "version": "0.17.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/opral/lix"
9
9
  },
10
10
  "exports": {
11
+ "./compatibility": {
12
+ "types": "./dist/compatibility.d.ts",
13
+ "default": "./dist/compatibility.js"
14
+ },
15
+ "./migration": {
16
+ "types": "./dist/migration.d.ts",
17
+ "default": "./dist/migration.js"
18
+ },
11
19
  ".": {
12
20
  "types": "./dist/index.d.ts",
13
21
  "default": "./dist/index.js"
@@ -27,17 +35,19 @@
27
35
  "dist"
28
36
  ],
29
37
  "scripts": {
30
- "build": "npm run clean && npm run build:native && npm run build:wasm && npm run build:ts && npm run build:plugins",
31
- "build:browser": "npm run clean && npm run build:wasm && npm run build:ts && npm run build:plugins",
38
+ "build": "npm run clean && npm run build:native && npm run build:migration:native && npm run build:wasm && npm run build:migration:wasm && npm run build:ts && npm run build:plugins",
39
+ "build:browser": "npm run clean && npm run build:wasm && npm run build:migration:wasm && npm run build:ts && npm run build:plugins",
32
40
  "build:native": "node ./scripts/build-native.js",
33
41
  "build:wasm": "node ./scripts/build-wasm.js",
42
+ "build:migration:wasm": "LIX_OFFLINE_MIGRATION=1 node ./scripts/build-wasm.js",
43
+ "build:migration:native": "LIX_OFFLINE_MIGRATION=1 node ./scripts/build-native.js",
34
44
  "build:wasm:dev": "LIX_WASM_PROFILE=dev node ./scripts/build-wasm.js",
35
45
  "build:plugins": "node ./scripts/build-bundled-plugins.js",
36
46
  "benchmark:plugin-reopen": "node ./scripts/benchmark-plugin-reopen.mjs",
37
47
  "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",
38
48
  "clean": "node ./scripts/clean.js",
39
49
  "prepare:native-package": "node ./scripts/prepare-native-package.js",
40
- "build:ts": "tsc -p tsconfig.json",
50
+ "build:ts": "tsc -p tsconfig.json && node ./scripts/build-compatibility.js",
41
51
  "test": "npm run build && vitest run",
42
52
  "test:browser": "npm run build:browser && vitest run --config vitest.browser.config.ts",
43
53
  "test:browser:built": "vitest run --config vitest.browser.config.ts",
@@ -45,20 +55,22 @@
45
55
  "typecheck": "tsc -p tsconfig.test.json --noEmit"
46
56
  },
47
57
  "optionalDependencies": {
48
- "@lix-js/sdk-darwin-arm64": "0.16.1",
49
- "@lix-js/sdk-linux-arm64": "0.16.1",
50
- "@lix-js/sdk-linux-x64": "0.16.1",
51
- "@lix-js/sdk-win32-x64": "0.16.1"
58
+ "@lix-js/sdk-darwin-arm64": "0.17.0",
59
+ "@lix-js/sdk-linux-arm64": "0.17.0",
60
+ "@lix-js/sdk-linux-x64": "0.17.0",
61
+ "@lix-js/sdk-win32-x64": "0.17.0"
52
62
  },
53
63
  "devDependencies": {
54
- "@vitest/browser-playwright": "4.1.10",
55
64
  "@types/node": "^24.10.2",
65
+ "@vitest/browser-playwright": "4.1.10",
56
66
  "playwright": "1.57.0",
57
67
  "typescript": "^5.5.4",
58
68
  "vite": "8.1.4",
59
69
  "vitest": "4.1.10"
60
70
  },
61
71
  "dependencies": {
72
+ "@bytecodealliance/jco-transpile": "0.13.0",
73
+ "binaryen": "130.0.0",
62
74
  "fflate": "^0.8.3"
63
75
  }
64
76
  }