@lix-js/sdk 0.11.0 → 0.12.1

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 (40) hide show
  1. package/README.md +48 -76
  2. package/dist/binding-types.d.ts +6 -15
  3. package/dist/binding.browser.js +18 -5
  4. package/dist/binding.node.js +5 -6
  5. package/dist/bundled-plugins/plugin_csv.lixplugin +0 -0
  6. package/dist/bundled-plugins/plugin_markdown.lixplugin +0 -0
  7. package/dist/errors.d.ts +0 -2
  8. package/dist/errors.js +0 -13
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js +1 -1
  11. package/dist/indexeddb-backend.d.ts +19 -0
  12. package/dist/indexeddb-backend.js +121 -0
  13. package/dist/lix.d.ts +1 -4
  14. package/dist/lix.js +21 -52
  15. package/dist/open-lix.d.ts +4 -9
  16. package/dist/open-lix.js +45 -135
  17. package/dist/remote/client.d.ts +2 -3
  18. package/dist/remote/client.js +13 -21
  19. package/dist/remote/{protocol.d.ts → server-protocol.d.ts} +35 -33
  20. package/dist/remote/{protocol.js → server-protocol.js} +24 -17
  21. package/dist/storage-adapter.d.ts +30 -0
  22. package/dist/storage-adapter.js +12 -0
  23. package/dist/types.d.ts +14 -24
  24. package/dist/value.d.ts +2 -1
  25. package/dist/value.js +23 -10
  26. package/dist/wasm/lix_js_sdk.d.ts +5 -10
  27. package/dist/wasm/lix_js_sdk.js +33 -45
  28. package/dist/wasm/lix_js_sdk_bg.wasm +0 -0
  29. package/dist/wasm/lix_js_sdk_bg.wasm.d.ts +3 -6
  30. package/dist/worker/client.d.ts +1 -12
  31. package/dist/worker/client.js +0 -161
  32. package/dist/worker/host.js +0 -23
  33. package/dist/worker/protocol.d.ts +1 -15
  34. package/package.json +8 -12
  35. package/dist/client-state.d.ts +0 -53
  36. package/dist/client-state.js +0 -315
  37. package/dist/local-storage-adapter.d.ts +0 -26
  38. package/dist/local-storage-adapter.js +0 -117
  39. package/dist/snapshot-persistence.d.ts +0 -7
  40. package/dist/snapshot-persistence.js +0 -26
@@ -1,5 +1,5 @@
1
1
  import type { BindingBatchStatement, BindingParam, LixStorageConfig } from "../binding-types.js";
2
- import type { CreateBranchOptions, ExecuteOptions, JsonValue, LixBatchOptions, MergeBranchOptions, SwitchBranchOptions, LixTelemetrySpan } from "../types.js";
2
+ import type { CreateBranchOptions, ExecuteOptions, LixBatchOptions, MergeBranchOptions, SwitchBranchOptions, LixTelemetrySpan } from "../types.js";
3
3
  export type WorkerRequest = {
4
4
  id: number;
5
5
  operation: WorkerOperation;
@@ -35,18 +35,6 @@ export type WorkerOperation = {
35
35
  kind: "activeBranchId";
36
36
  } | {
37
37
  kind: "activeAccountId";
38
- } | {
39
- kind: "clientState.entries";
40
- } | {
41
- kind: "clientState.get";
42
- key: string;
43
- } | {
44
- kind: "clientState.set";
45
- key: string;
46
- value: JsonValue;
47
- } | {
48
- kind: "clientState.delete";
49
- key: string;
50
38
  } | {
51
39
  kind: "createBranch";
52
40
  options: CreateBranchOptions;
@@ -70,8 +58,6 @@ export type WorkerOperation = {
70
58
  paths: string[];
71
59
  } | {
72
60
  kind: "syncDiskToLix";
73
- } | {
74
- kind: "exportSnapshot";
75
61
  } | {
76
62
  kind: "observe";
77
63
  sql: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lix-js/sdk",
3
3
  "type": "module",
4
- "version": "0.11.0",
4
+ "version": "0.12.1",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -17,13 +17,9 @@
17
17
  "workerd": "./dist/workerd.js",
18
18
  "default": "./dist/workerd.js"
19
19
  },
20
- "./remote-protocol": {
21
- "types": "./dist/remote/protocol.d.ts",
22
- "default": "./dist/remote/protocol.js"
23
- },
24
- "./local-storage-adapter": {
25
- "types": "./dist/local-storage-adapter.d.ts",
26
- "default": "./dist/local-storage-adapter.js"
20
+ "./server-protocol": {
21
+ "types": "./dist/remote/server-protocol.d.ts",
22
+ "default": "./dist/remote/server-protocol.js"
27
23
  }
28
24
  },
29
25
  "imports": {
@@ -57,10 +53,10 @@
57
53
  "typecheck": "tsc -p tsconfig.test.json --noEmit"
58
54
  },
59
55
  "optionalDependencies": {
60
- "@lix-js/sdk-darwin-arm64": "0.11.0",
61
- "@lix-js/sdk-linux-arm64": "0.11.0",
62
- "@lix-js/sdk-linux-x64": "0.11.0",
63
- "@lix-js/sdk-win32-x64": "0.11.0"
56
+ "@lix-js/sdk-darwin-arm64": "0.12.1",
57
+ "@lix-js/sdk-linux-arm64": "0.12.1",
58
+ "@lix-js/sdk-linux-x64": "0.12.1",
59
+ "@lix-js/sdk-win32-x64": "0.12.1"
64
60
  },
65
61
  "devDependencies": {
66
62
  "@vitest/browser-playwright": "4.1.10",
@@ -1,53 +0,0 @@
1
- import type { LixBinding } from "./binding-types.js";
2
- import type { JsonValue, LixSnapshotStorage } from "./types.js";
3
- export declare const ACTIVE_BRANCH_CLIENT_STATE_KEY = "lix_active_branch_id";
4
- export declare const ACTIVE_ACCOUNT_CLIENT_STATE_KEY = "lix_active_account_id";
5
- export type LixClientState = {
6
- /** Returns the hydrated client-local value without a network round trip. */
7
- get<T extends JsonValue = JsonValue>(key: string): T | undefined;
8
- /** Persists a client-local value in the configured client storage. */
9
- set(key: string, value: JsonValue): Promise<void>;
10
- /** Deletes a client-local value from the configured client storage. */
11
- delete(key: string): Promise<void>;
12
- /** Subscribes to successful mutations made through this client-state handle. */
13
- subscribe(listener: () => void): () => void;
14
- };
15
- export type ManagedClientState = LixClientState & {
16
- close(): Promise<void>;
17
- };
18
- export declare function unavailableClientState(): LixClientState;
19
- type ClientStateBinding = LixBinding & {
20
- exportSnapshot?: () => Promise<Uint8Array>;
21
- };
22
- export type OpenClientStateOptions = {
23
- readonly binding: ClientStateBinding;
24
- readonly saveSnapshot?: (snapshot: Uint8Array) => Promise<void>;
25
- readonly closeBinding?: boolean;
26
- };
27
- /**
28
- * Opens the typed client-state facade over a private local Rust Lix.
29
- *
30
- * Values are ordinary global, untracked `lix_key_value` rows. The physical
31
- * prefix is intentionally private so built-in Lix key/value rows never leak
32
- * through this small API.
33
- */
34
- export declare function openClientState(options: OpenClientStateOptions): Promise<ManagedLixClientState>;
35
- export declare class ManagedLixClientState implements LixClientState {
36
- #private;
37
- constructor(options: OpenClientStateOptions, initial: Map<string, JsonValue>);
38
- get<T extends JsonValue = JsonValue>(key: string): T | undefined;
39
- set(key: string, value: JsonValue): Promise<void>;
40
- delete(key: string): Promise<void>;
41
- subscribe(listener: () => void): () => void;
42
- close(): Promise<void>;
43
- }
44
- /**
45
- * Opens client state directly over snapshot storage without starting a local
46
- * Lix runtime. This is used by remote Lix connections, where the storage
47
- * option persists client-local state rather than the remote workspace.
48
- */
49
- export declare function openStoredClientState(options: {
50
- readonly storage: LixSnapshotStorage;
51
- readonly namespace: string;
52
- }): Promise<ManagedClientState>;
53
- export {};
@@ -1,315 +0,0 @@
1
- import { isSnapshotPersistenceAfterCommitError } from "./snapshot-persistence.js";
2
- import { Value } from "./value.js";
3
- export const ACTIVE_BRANCH_CLIENT_STATE_KEY = "lix_active_branch_id";
4
- export const ACTIVE_ACCOUNT_CLIENT_STATE_KEY = "lix_active_account_id";
5
- const STORED_CLIENT_STATE_HEADER = "lix-client-state-v1\n";
6
- export function unavailableClientState() {
7
- const unavailable = () => {
8
- const error = new Error("Lix client state requires client storage; pass storage to openLix()");
9
- error.name = "LixError";
10
- error.code = "LIX_CLIENT_STORAGE_REQUIRED";
11
- return error;
12
- };
13
- return {
14
- get: () => undefined,
15
- set: async () => {
16
- throw unavailable();
17
- },
18
- delete: async () => {
19
- throw unavailable();
20
- },
21
- subscribe: () => () => undefined,
22
- };
23
- }
24
- /**
25
- * Opens the typed client-state facade over a private local Rust Lix.
26
- *
27
- * Values are ordinary global, untracked `lix_key_value` rows. The physical
28
- * prefix is intentionally private so built-in Lix key/value rows never leak
29
- * through this small API.
30
- */
31
- export async function openClientState(options) {
32
- const entries = options.binding.clientStateEntries;
33
- if (!entries) {
34
- throw new Error("The selected Lix binding does not support typed client state");
35
- }
36
- const initial = new Map();
37
- for (const entry of await entries.call(options.binding)) {
38
- assertClientStateKey(entry.key);
39
- assertJsonValue(entry.value);
40
- initial.set(entry.key, cloneJsonValue(entry.value));
41
- }
42
- return new ManagedLixClientState(options, initial);
43
- }
44
- export class ManagedLixClientState {
45
- #binding;
46
- #saveSnapshot;
47
- #closeBinding;
48
- #values;
49
- #listeners = new Set();
50
- #operationQueue = Promise.resolve();
51
- #closePromise;
52
- #acceptingOperations = true;
53
- constructor(options, initial) {
54
- this.#binding = options.binding;
55
- this.#saveSnapshot = options.saveSnapshot;
56
- this.#closeBinding = options.closeBinding ?? false;
57
- this.#values = initial;
58
- }
59
- get(key) {
60
- assertClientStateKey(key);
61
- const value = this.#values.get(key);
62
- return value === undefined ? undefined : cloneJsonValue(value);
63
- }
64
- set(key, value) {
65
- assertClientStateKey(key);
66
- assertJsonValue(value);
67
- this.#assertOpen();
68
- const nextValue = cloneJsonValue(value);
69
- return this.#enqueue(async () => {
70
- const set = this.#binding.clientStateSet;
71
- if (!set)
72
- throw new Error("Typed Lix client state is unavailable");
73
- try {
74
- await set.call(this.#binding, key, nextValue);
75
- }
76
- catch (error) {
77
- if (!isSnapshotPersistenceAfterCommitError(error))
78
- throw error;
79
- this.#commitSet(key, nextValue);
80
- throw error;
81
- }
82
- this.#commitSet(key, nextValue);
83
- await this.#persist();
84
- });
85
- }
86
- delete(key) {
87
- assertClientStateKey(key);
88
- this.#assertOpen();
89
- return this.#enqueue(async () => {
90
- const deleteValue = this.#binding.clientStateDelete;
91
- if (!deleteValue)
92
- throw new Error("Typed Lix client state is unavailable");
93
- try {
94
- await deleteValue.call(this.#binding, key);
95
- }
96
- catch (error) {
97
- if (!isSnapshotPersistenceAfterCommitError(error))
98
- throw error;
99
- this.#commitDelete(key);
100
- throw error;
101
- }
102
- this.#commitDelete(key);
103
- await this.#persist();
104
- });
105
- }
106
- subscribe(listener) {
107
- if (typeof listener !== "function") {
108
- throw new TypeError("clientState.subscribe() requires a function");
109
- }
110
- this.#assertOpen();
111
- this.#listeners.add(listener);
112
- return () => this.#listeners.delete(listener);
113
- }
114
- async close() {
115
- if (this.#closePromise)
116
- return this.#closePromise;
117
- this.#acceptingOperations = false;
118
- this.#closePromise = (async () => {
119
- await this.#operationQueue;
120
- this.#listeners.clear();
121
- if (this.#closeBinding)
122
- await this.#binding.close();
123
- })();
124
- return this.#closePromise;
125
- }
126
- #enqueue(operation) {
127
- const result = this.#operationQueue.then(operation, operation);
128
- this.#operationQueue = result.then(() => undefined, () => undefined);
129
- return result;
130
- }
131
- async #persist() {
132
- if (!this.#saveSnapshot)
133
- return;
134
- if (!this.#binding.exportSnapshot) {
135
- throw new Error("The selected Lix binding cannot export storage snapshots");
136
- }
137
- await this.#saveSnapshot(await this.#binding.exportSnapshot());
138
- }
139
- #commitSet(key, value) {
140
- this.#values.set(key, value);
141
- this.#publish();
142
- }
143
- #commitDelete(key) {
144
- if (this.#values.delete(key))
145
- this.#publish();
146
- }
147
- #publish() {
148
- for (const listener of [...this.#listeners]) {
149
- try {
150
- listener();
151
- }
152
- catch {
153
- // Subscribers do not participate in the completed local transaction.
154
- }
155
- }
156
- }
157
- #assertOpen() {
158
- if (!this.#acceptingOperations) {
159
- throw new Error("Lix client state is closed");
160
- }
161
- }
162
- }
163
- /**
164
- * Opens client state directly over snapshot storage without starting a local
165
- * Lix runtime. This is used by remote Lix connections, where the storage
166
- * option persists client-local state rather than the remote workspace.
167
- */
168
- export async function openStoredClientState(options) {
169
- const snapshot = await options.storage.load(options.namespace);
170
- if (snapshot !== undefined && !(snapshot instanceof Uint8Array)) {
171
- throw new TypeError("Client-state storage load() must return a Uint8Array");
172
- }
173
- return new StoredClientState(options.storage, options.namespace, decodeStoredClientState(snapshot));
174
- }
175
- class StoredClientState {
176
- #storage;
177
- #namespace;
178
- #values;
179
- #listeners = new Set();
180
- #operationQueue = Promise.resolve();
181
- #closePromise;
182
- #acceptingOperations = true;
183
- #dirty = false;
184
- constructor(storage, namespace, values) {
185
- this.#storage = storage;
186
- this.#namespace = namespace;
187
- this.#values = values;
188
- }
189
- get(key) {
190
- assertClientStateKey(key);
191
- const value = this.#values.get(key);
192
- return value === undefined ? undefined : cloneJsonValue(value);
193
- }
194
- set(key, value) {
195
- assertClientStateKey(key);
196
- assertJsonValue(value);
197
- this.#assertOpen();
198
- const nextValue = cloneJsonValue(value);
199
- return this.#enqueue(async () => {
200
- this.#values.set(key, nextValue);
201
- this.#dirty = true;
202
- this.#publish();
203
- await this.#persist();
204
- });
205
- }
206
- delete(key) {
207
- assertClientStateKey(key);
208
- this.#assertOpen();
209
- return this.#enqueue(async () => {
210
- if (this.#values.delete(key))
211
- this.#publish();
212
- this.#dirty = true;
213
- await this.#persist();
214
- });
215
- }
216
- subscribe(listener) {
217
- if (typeof listener !== "function") {
218
- throw new TypeError("clientState.subscribe() requires a function");
219
- }
220
- this.#assertOpen();
221
- this.#listeners.add(listener);
222
- return () => this.#listeners.delete(listener);
223
- }
224
- async close() {
225
- if (this.#closePromise)
226
- return this.#closePromise;
227
- this.#acceptingOperations = false;
228
- this.#closePromise = (async () => {
229
- await this.#operationQueue;
230
- if (this.#dirty)
231
- await this.#persist();
232
- this.#listeners.clear();
233
- })();
234
- return this.#closePromise;
235
- }
236
- #enqueue(operation) {
237
- const result = this.#operationQueue.then(operation, operation);
238
- this.#operationQueue = result.then(() => undefined, () => undefined);
239
- return result;
240
- }
241
- async #persist() {
242
- await this.#storage.save(this.#namespace, encodeStoredClientState(this.#values));
243
- this.#dirty = false;
244
- }
245
- #publish() {
246
- for (const listener of [...this.#listeners]) {
247
- try {
248
- listener();
249
- }
250
- catch {
251
- // Subscribers do not participate in the completed mutation.
252
- }
253
- }
254
- }
255
- #assertOpen() {
256
- if (!this.#acceptingOperations) {
257
- throw new Error("Lix client state is closed");
258
- }
259
- }
260
- }
261
- function encodeStoredClientState(values) {
262
- const entries = [...values].map(([key, value]) => [
263
- key,
264
- cloneJsonValue(value),
265
- ]);
266
- return new TextEncoder().encode(`${STORED_CLIENT_STATE_HEADER}${JSON.stringify(entries)}`);
267
- }
268
- function decodeStoredClientState(snapshot) {
269
- if (snapshot === undefined)
270
- return new Map();
271
- const header = new TextEncoder().encode(STORED_CLIENT_STATE_HEADER);
272
- if (snapshot.length < header.length ||
273
- header.some((byte, index) => snapshot[index] !== byte)) {
274
- throw new Error("Stored Lix client state header is invalid");
275
- }
276
- let parsed;
277
- try {
278
- parsed = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(snapshot.subarray(header.length)));
279
- }
280
- catch (error) {
281
- throw new Error("Stored Lix client state is invalid", { cause: error });
282
- }
283
- if (!Array.isArray(parsed)) {
284
- throw new Error("Stored Lix client state entries must be an array");
285
- }
286
- const values = new Map();
287
- for (const entry of parsed) {
288
- if (!Array.isArray(entry) || entry.length !== 2) {
289
- throw new Error("Stored Lix client state entry is invalid");
290
- }
291
- const [key, value] = entry;
292
- assertClientStateKey(key);
293
- assertJsonValue(value);
294
- values.set(key, cloneJsonValue(value));
295
- }
296
- return values;
297
- }
298
- function assertClientStateKey(key) {
299
- if (typeof key !== "string" || key.length === 0) {
300
- throw new TypeError("clientState key must be a non-empty string");
301
- }
302
- }
303
- function assertJsonValue(value) {
304
- // Value.json owns the SDK's full JSON validation, including finite numbers,
305
- // well-formed strings, plain objects, and cycle detection.
306
- Value.json(value);
307
- }
308
- function cloneJsonValue(value) {
309
- if (Array.isArray(value))
310
- return value.map(cloneJsonValue);
311
- if (value && typeof value === "object") {
312
- return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, cloneJsonValue(entry)]));
313
- }
314
- return value;
315
- }
@@ -1,26 +0,0 @@
1
- import type { LixSnapshotStorage } from "./types.js";
2
- /** The subset of the browser Storage API used by {@link LocalStorage}. */
3
- export type WebStorageLike = Pick<Storage, "getItem" | "setItem">;
4
- export type LocalStorageOptions = {
5
- /**
6
- * Storage implementation to wrap. Defaults to `globalThis.localStorage`.
7
- * Supplying this is useful for non-browser hosts and tests.
8
- */
9
- storage?: WebStorageLike;
10
- /** Prefix used to isolate Lix snapshots from other application records. */
11
- prefix?: string;
12
- };
13
- /**
14
- * Persists opaque Lix snapshots in the browser's localStorage.
15
- *
16
- * Import this adapter from `@lix-js/sdk/local-storage-adapter`. Keeping it in
17
- * a separate entrypoint avoids referencing browser storage in applications
18
- * that do not use it.
19
- */
20
- export declare class LocalStorage implements LixSnapshotStorage {
21
- #private;
22
- constructor(options?: LocalStorageOptions);
23
- load(namespace: string): Promise<Uint8Array | undefined>;
24
- save(namespace: string, snapshot: Uint8Array): Promise<void>;
25
- }
26
- export type { LixSnapshotStorage } from "./types.js";
@@ -1,117 +0,0 @@
1
- const SNAPSHOT_FORMAT = "lix-snapshot";
2
- const SNAPSHOT_VERSION = 1;
3
- const DEFAULT_PREFIX = "@lix-js/sdk/snapshot/v1";
4
- /**
5
- * Persists opaque Lix snapshots in the browser's localStorage.
6
- *
7
- * Import this adapter from `@lix-js/sdk/local-storage-adapter`. Keeping it in
8
- * a separate entrypoint avoids referencing browser storage in applications
9
- * that do not use it.
10
- */
11
- export class LocalStorage {
12
- #storage;
13
- #prefix;
14
- constructor(options = {}) {
15
- if (!options || typeof options !== "object" || Array.isArray(options)) {
16
- throw new TypeError("LocalStorage options must be an object");
17
- }
18
- if (options.prefix !== undefined &&
19
- (typeof options.prefix !== "string" || options.prefix.length === 0)) {
20
- throw new TypeError("LocalStorage prefix must be a non-empty string");
21
- }
22
- const storage = options.storage ?? defaultLocalStorage();
23
- if (!storage ||
24
- typeof storage.getItem !== "function" ||
25
- typeof storage.setItem !== "function") {
26
- throw new TypeError("LocalStorage storage must implement getItem() and setItem()");
27
- }
28
- this.#storage = storage;
29
- this.#prefix = options.prefix ?? DEFAULT_PREFIX;
30
- }
31
- async load(namespace) {
32
- const key = this.#key(namespace);
33
- const stored = this.#storage.getItem(key);
34
- if (stored === null)
35
- return undefined;
36
- let value;
37
- try {
38
- value = JSON.parse(stored);
39
- }
40
- catch (error) {
41
- throw invalidSnapshot(key, "record is not valid JSON", error);
42
- }
43
- if (!isSnapshotEnvelope(value)) {
44
- throw invalidSnapshot(key, "record has an unsupported format or version");
45
- }
46
- try {
47
- return base64ToBytes(value.data);
48
- }
49
- catch (error) {
50
- throw invalidSnapshot(key, "record contains invalid base64", error);
51
- }
52
- }
53
- async save(namespace, snapshot) {
54
- const key = this.#key(namespace);
55
- if (!(snapshot instanceof Uint8Array)) {
56
- throw new TypeError("LocalStorage snapshot must be a Uint8Array");
57
- }
58
- const envelope = {
59
- format: SNAPSHOT_FORMAT,
60
- version: SNAPSHOT_VERSION,
61
- data: bytesToBase64(snapshot),
62
- };
63
- this.#storage.setItem(key, JSON.stringify(envelope));
64
- }
65
- #key(namespace) {
66
- if (typeof namespace !== "string" || namespace.length === 0) {
67
- throw new TypeError("LocalStorage snapshot namespace must be a non-empty string");
68
- }
69
- return `${this.#prefix}:${encodeURIComponent(namespace)}`;
70
- }
71
- }
72
- function defaultLocalStorage() {
73
- const storage = globalThis.localStorage;
74
- if (storage === undefined) {
75
- throw new Error("LocalStorage requires browser localStorage or an explicit storage option");
76
- }
77
- return storage;
78
- }
79
- function isSnapshotEnvelope(value) {
80
- if (!value || typeof value !== "object" || Array.isArray(value))
81
- return false;
82
- const envelope = value;
83
- return (envelope.format === SNAPSHOT_FORMAT &&
84
- envelope.version === SNAPSHOT_VERSION &&
85
- typeof envelope.data === "string");
86
- }
87
- function bytesToBase64(bytes) {
88
- let binary = "";
89
- const chunkSize = 0x8000;
90
- for (let offset = 0; offset < bytes.length; offset += chunkSize) {
91
- binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
92
- }
93
- return btoa(binary);
94
- }
95
- function base64ToBytes(base64) {
96
- if (!isCanonicalBase64(base64)) {
97
- throw new Error("invalid base64");
98
- }
99
- const binary = atob(base64);
100
- const bytes = new Uint8Array(binary.length);
101
- for (let index = 0; index < binary.length; index += 1) {
102
- bytes[index] = binary.charCodeAt(index);
103
- }
104
- return bytes;
105
- }
106
- function isCanonicalBase64(value) {
107
- if (value.length === 0)
108
- return true;
109
- if (value.length % 4 !== 0)
110
- return false;
111
- return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
112
- }
113
- function invalidSnapshot(key, reason, cause) {
114
- return new Error(`Invalid Lix snapshot in localStorage key '${key}': ${reason}`, {
115
- cause,
116
- });
117
- }
@@ -1,7 +0,0 @@
1
- export type SnapshotPersistenceAfterCommitError = Error & {
2
- readonly code: "LIX_SNAPSHOT_PERSISTENCE_FAILED";
3
- readonly lixSnapshotOperationCommitted: true;
4
- };
5
- /** Marks that the engine operation committed before durable snapshot saving failed. */
6
- export declare function snapshotPersistenceAfterCommitError(cause: unknown): SnapshotPersistenceAfterCommitError;
7
- export declare function isSnapshotPersistenceAfterCommitError(error: unknown): error is SnapshotPersistenceAfterCommitError;
@@ -1,26 +0,0 @@
1
- const OPERATION_COMMITTED = "lixSnapshotOperationCommitted";
2
- /** Marks that the engine operation committed before durable snapshot saving failed. */
3
- export function snapshotPersistenceAfterCommitError(cause) {
4
- const message = cause instanceof Error
5
- ? cause.message
6
- : "Saving the committed Lix snapshot failed";
7
- const error = new Error(message, { cause });
8
- error.name = "LixSnapshotPersistenceError";
9
- Object.defineProperties(error, {
10
- code: {
11
- value: "LIX_SNAPSHOT_PERSISTENCE_FAILED",
12
- enumerable: true,
13
- },
14
- [OPERATION_COMMITTED]: {
15
- value: true,
16
- enumerable: true,
17
- },
18
- });
19
- return error;
20
- }
21
- export function isSnapshotPersistenceAfterCommitError(error) {
22
- return (typeof error === "object" &&
23
- error !== null &&
24
- OPERATION_COMMITTED in error &&
25
- error[OPERATION_COMMITTED] === true);
26
- }