@pixerate/editor 0.23.0 → 0.24.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 (155) hide show
  1. package/README.md +96 -1
  2. package/THIRD_PARTY_NOTICES.md +51 -0
  3. package/dist/agent/index.cjs +9 -8
  4. package/dist/agent/index.cjs.map +1 -1
  5. package/dist/agent/index.d.cts +4 -3
  6. package/dist/agent/index.d.ts +4 -3
  7. package/dist/agent/index.js +3 -2
  8. package/dist/agent/index.js.map +1 -1
  9. package/dist/canvas/index.cjs +1210 -17
  10. package/dist/canvas/index.cjs.map +1 -1
  11. package/dist/canvas/index.d.cts +318 -1
  12. package/dist/canvas/index.d.ts +318 -1
  13. package/dist/canvas/index.js +1199 -6
  14. package/dist/canvas/index.js.map +1 -1
  15. package/dist/{chunk-QBJ22YZR.js → chunk-3CN2WD3X.js} +1354 -206
  16. package/dist/chunk-3CN2WD3X.js.map +1 -0
  17. package/dist/{chunk-JJDLPEWL.js → chunk-3LV6RRPH.js} +2 -2
  18. package/dist/{chunk-MJZBNWG7.js → chunk-5S3Z4PPF.js} +78 -557
  19. package/dist/chunk-5S3Z4PPF.js.map +1 -0
  20. package/dist/chunk-664LQYFJ.js +115 -0
  21. package/dist/chunk-664LQYFJ.js.map +1 -0
  22. package/dist/{chunk-AA4ZEVCC.cjs → chunk-D27YFC46.cjs} +101 -580
  23. package/dist/chunk-D27YFC46.cjs.map +1 -0
  24. package/dist/chunk-D6FH2YAX.cjs +131 -0
  25. package/dist/chunk-D6FH2YAX.cjs.map +1 -0
  26. package/dist/{chunk-4SUNJZWB.cjs → chunk-FD4LPFAE.cjs} +48 -200
  27. package/dist/chunk-FD4LPFAE.cjs.map +1 -0
  28. package/dist/chunk-FODNLX5N.js +749 -0
  29. package/dist/chunk-FODNLX5N.js.map +1 -0
  30. package/dist/{chunk-OYXSKQFR.js → chunk-FPENXAEJ.js} +35 -187
  31. package/dist/chunk-FPENXAEJ.js.map +1 -0
  32. package/dist/chunk-HLIMTC6N.js +1 -0
  33. package/dist/chunk-HLIMTC6N.js.map +1 -0
  34. package/dist/{chunk-QGHGJ56O.js → chunk-ITBSKI26.js} +1483 -19
  35. package/dist/chunk-ITBSKI26.js.map +1 -0
  36. package/dist/{chunk-XDMXXC4Z.cjs → chunk-MA2F5KHF.cjs} +350 -227
  37. package/dist/chunk-MA2F5KHF.cjs.map +1 -0
  38. package/dist/{chunk-RIKBJLS2.cjs → chunk-MCSJFWAO.cjs} +1356 -208
  39. package/dist/chunk-MCSJFWAO.cjs.map +1 -0
  40. package/dist/chunk-MKA3AHLV.cjs +256 -0
  41. package/dist/chunk-MKA3AHLV.cjs.map +1 -0
  42. package/dist/chunk-OLZJXL4R.cjs +749 -0
  43. package/dist/chunk-OLZJXL4R.cjs.map +1 -0
  44. package/dist/chunk-OXSV64JX.js +689 -0
  45. package/dist/chunk-OXSV64JX.js.map +1 -0
  46. package/dist/chunk-P3RQ5UJE.cjs +689 -0
  47. package/dist/chunk-P3RQ5UJE.cjs.map +1 -0
  48. package/dist/chunk-P6ODPZ4W.js +256 -0
  49. package/dist/chunk-P6ODPZ4W.js.map +1 -0
  50. package/dist/chunk-PGNZKFQS.js +685 -0
  51. package/dist/chunk-PGNZKFQS.js.map +1 -0
  52. package/dist/chunk-PL4JB3BF.cjs +1 -0
  53. package/dist/chunk-PL4JB3BF.cjs.map +1 -0
  54. package/dist/{chunk-GVSMRPHB.cjs → chunk-SBCZ6RN5.cjs} +1568 -104
  55. package/dist/chunk-SBCZ6RN5.cjs.map +1 -0
  56. package/dist/{chunk-CL37JGFW.cjs → chunk-SML64VJG.cjs} +4 -4
  57. package/dist/{chunk-CL37JGFW.cjs.map → chunk-SML64VJG.cjs.map} +1 -1
  58. package/dist/chunk-TNNJXF46.cjs +115 -0
  59. package/dist/chunk-TNNJXF46.cjs.map +1 -0
  60. package/dist/chunk-U5HZSFCS.js +131 -0
  61. package/dist/chunk-U5HZSFCS.js.map +1 -0
  62. package/dist/{chunk-K3R5NG2D.js → chunk-U5KWZU4X.js} +251 -128
  63. package/dist/chunk-U5KWZU4X.js.map +1 -0
  64. package/dist/chunk-XNORY7M6.cjs +685 -0
  65. package/dist/chunk-XNORY7M6.cjs.map +1 -0
  66. package/dist/commands/index.d.cts +5 -353
  67. package/dist/commands/index.d.ts +5 -353
  68. package/dist/comment-store-BEMgVlAA.d.cts +135 -0
  69. package/dist/comment-store-BEMgVlAA.d.ts +135 -0
  70. package/dist/compare/index.cjs +15 -0
  71. package/dist/compare/index.cjs.map +1 -0
  72. package/dist/compare/index.d.cts +58 -0
  73. package/dist/compare/index.d.ts +58 -0
  74. package/dist/compare/index.js +15 -0
  75. package/dist/compare/index.js.map +1 -0
  76. package/dist/{controller-pePCJLT1.d.cts → controller-BhLLAoi8.d.cts} +45 -4
  77. package/dist/{controller-pePCJLT1.d.ts → controller-BhLLAoi8.d.ts} +45 -4
  78. package/dist/documents-BQjzJNe2.d.ts +76 -0
  79. package/dist/documents-KRTPhvst.d.cts +76 -0
  80. package/dist/extensions/index.cjs +53 -5
  81. package/dist/extensions/index.cjs.map +1 -1
  82. package/dist/extensions/index.d.cts +7 -3
  83. package/dist/extensions/index.d.ts +7 -3
  84. package/dist/extensions/index.js +52 -4
  85. package/dist/generation/index.cjs +1373 -0
  86. package/dist/generation/index.cjs.map +1 -0
  87. package/dist/generation/index.d.cts +554 -0
  88. package/dist/generation/index.d.ts +554 -0
  89. package/dist/generation/index.js +1373 -0
  90. package/dist/generation/index.js.map +1 -0
  91. package/dist/grammar/index.cjs +48 -3
  92. package/dist/grammar/index.cjs.map +1 -1
  93. package/dist/grammar/index.d.cts +322 -100
  94. package/dist/grammar/index.d.ts +322 -100
  95. package/dist/grammar/index.js +54 -9
  96. package/dist/image-editor/index.cjs +1831 -30
  97. package/dist/image-editor/index.cjs.map +1 -1
  98. package/dist/image-editor/index.d.cts +590 -3
  99. package/dist/image-editor/index.d.ts +590 -3
  100. package/dist/image-editor/index.js +1807 -6
  101. package/dist/image-editor/index.js.map +1 -1
  102. package/dist/{index-CCr_z9kF.d.cts → index-BhJBSm2d.d.cts} +370 -4
  103. package/dist/{index-Ck6ajwXh.d.ts → index-fJrlkJCd.d.ts} +370 -4
  104. package/dist/index.cjs +162 -25
  105. package/dist/index.cjs.map +1 -1
  106. package/dist/index.d.cts +14 -8
  107. package/dist/index.d.ts +14 -8
  108. package/dist/index.js +174 -37
  109. package/dist/index.js.map +1 -1
  110. package/dist/migrate-iTlm3a8h.d.cts +112 -0
  111. package/dist/migrate-iTlm3a8h.d.ts +112 -0
  112. package/dist/persistence/index.cjs +676 -0
  113. package/dist/persistence/index.cjs.map +1 -0
  114. package/dist/persistence/index.d.cts +347 -0
  115. package/dist/persistence/index.d.ts +347 -0
  116. package/dist/persistence/index.js +676 -0
  117. package/dist/persistence/index.js.map +1 -0
  118. package/dist/{registry-D5-R90p0.d.cts → registry-BDe-vlv3.d.ts} +2 -40
  119. package/dist/registry-D32LpIc4.d.cts +354 -0
  120. package/dist/{registry-D5-R90p0.d.ts → registry-DnzpgY27.d.cts} +2 -40
  121. package/dist/registry-evKTP4ls.d.ts +354 -0
  122. package/dist/schema-DYDGdYL9.d.cts +41 -0
  123. package/dist/schema-DYDGdYL9.d.ts +41 -0
  124. package/dist/serializers/index.cjs +4 -3
  125. package/dist/serializers/index.cjs.map +1 -1
  126. package/dist/serializers/index.d.cts +2 -1
  127. package/dist/serializers/index.d.ts +2 -1
  128. package/dist/serializers/index.js +3 -2
  129. package/dist/sourcemap-C8AH3MBQ.d.cts +91 -0
  130. package/dist/sourcemap-DA1Evhrv.d.ts +91 -0
  131. package/dist/spreadsheet/index.cjs +37 -3
  132. package/dist/spreadsheet/index.cjs.map +1 -1
  133. package/dist/spreadsheet/index.d.cts +188 -17
  134. package/dist/spreadsheet/index.d.ts +188 -17
  135. package/dist/spreadsheet/index.js +43 -9
  136. package/dist/{tokens-CyLZmIei.d.cts → tokens-D7yXvUU6.d.cts} +15 -2
  137. package/dist/{tokens-CyLZmIei.d.ts → tokens-D7yXvUU6.d.ts} +15 -2
  138. package/dist/{track-changes-BSF58RK0.d.ts → track-changes-BK5sVf4_.d.cts} +18 -136
  139. package/dist/{track-changes-BSF58RK0.d.cts → track-changes-CDjCrwKe.d.ts} +18 -136
  140. package/package.json +16 -1
  141. package/dist/chunk-4SUNJZWB.cjs.map +0 -1
  142. package/dist/chunk-AA4ZEVCC.cjs.map +0 -1
  143. package/dist/chunk-AXV2ZW34.cjs +0 -118
  144. package/dist/chunk-AXV2ZW34.cjs.map +0 -1
  145. package/dist/chunk-B2ZRLN4S.js +0 -118
  146. package/dist/chunk-B2ZRLN4S.js.map +0 -1
  147. package/dist/chunk-GVSMRPHB.cjs.map +0 -1
  148. package/dist/chunk-K3R5NG2D.js.map +0 -1
  149. package/dist/chunk-MJZBNWG7.js.map +0 -1
  150. package/dist/chunk-OYXSKQFR.js.map +0 -1
  151. package/dist/chunk-QBJ22YZR.js.map +0 -1
  152. package/dist/chunk-QGHGJ56O.js.map +0 -1
  153. package/dist/chunk-RIKBJLS2.cjs.map +0 -1
  154. package/dist/chunk-XDMXXC4Z.cjs.map +0 -1
  155. /package/dist/{chunk-JJDLPEWL.js.map → chunk-3LV6RRPH.js.map} +0 -0
@@ -0,0 +1,676 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+
5
+
6
+
7
+ var _chunkD6FH2YAXcjs = require('../chunk-D6FH2YAX.cjs');
8
+
9
+ // src/persistence/recoveryPolicy.ts
10
+ function exponentialBackoff(failures, base = 1e3, max = 3e4) {
11
+ const exp = Math.min(Math.max(failures - 1, 0), 16);
12
+ return Math.min(base * 2 ** exp, max);
13
+ }
14
+ function createRecoveryPolicy(options = {}) {
15
+ const interval = _nullishCoalesce(options.interval, () => ( 5e3));
16
+ const retryDelay = _nullishCoalesce(options.retryDelay, () => ( (() => interval)));
17
+ const minWake = interval * 0.01;
18
+ let lastRevision = null;
19
+ let lastTime = null;
20
+ let gap = interval;
21
+ let ownsDirtyMeta = false;
22
+ let cleaning = false;
23
+ let snapshots = 0;
24
+ let snapshotsAtClean = 0;
25
+ let failures = 0;
26
+ const fail = (now) => {
27
+ failures++;
28
+ lastRevision = null;
29
+ lastTime = now;
30
+ gap = retryDelay(failures);
31
+ };
32
+ return {
33
+ tick(now, revision, savedRevision, tickOptions) {
34
+ const force = _optionalChain([tickOptions, 'optionalAccess', _ => _.force]) === true;
35
+ const since = force || lastTime === null ? Infinity : now - lastTime;
36
+ if (revision !== savedRevision) {
37
+ if (lastRevision === revision) return { type: "nothing" };
38
+ if (since >= gap) {
39
+ lastRevision = revision;
40
+ lastTime = now;
41
+ ownsDirtyMeta = true;
42
+ snapshots++;
43
+ return { type: "writeSnapshot", revision };
44
+ }
45
+ return { type: "wakeAfter", delay: Math.max(gap - since, minWake) };
46
+ }
47
+ if (ownsDirtyMeta && !cleaning) {
48
+ if (lastRevision !== null || since >= gap) {
49
+ cleaning = true;
50
+ snapshotsAtClean = snapshots;
51
+ return { type: "writeClean" };
52
+ }
53
+ return { type: "wakeAfter", delay: Math.max(gap - since, minWake) };
54
+ }
55
+ return { type: "nothing" };
56
+ },
57
+ written(revision, ok, now) {
58
+ if (ok) {
59
+ if (lastRevision === revision) {
60
+ failures = 0;
61
+ gap = interval;
62
+ }
63
+ } else if (lastRevision === revision) {
64
+ fail(now);
65
+ }
66
+ },
67
+ cleaned(ok, now) {
68
+ cleaning = false;
69
+ if (ok) {
70
+ ownsDirtyMeta = snapshots !== snapshotsAtClean;
71
+ failures = 0;
72
+ gap = interval;
73
+ } else {
74
+ fail(now);
75
+ }
76
+ },
77
+ get ownsSnapshot() {
78
+ return ownsDirtyMeta;
79
+ },
80
+ get failures() {
81
+ return failures;
82
+ }
83
+ };
84
+ }
85
+
86
+ // src/persistence/storage.ts
87
+ var StorageUnavailableError = class extends Error {
88
+ constructor(kind) {
89
+ super(`${kind} storage is not available in this environment`);
90
+ this.name = "StorageUnavailableError";
91
+ }
92
+ };
93
+ var InjectedStorageError = class extends Error {
94
+ constructor(operation, key) {
95
+ super(`injected ${operation} failure for "${key}"`);
96
+ this.name = "InjectedStorageError";
97
+ }
98
+ };
99
+ function checkFailure(failure, operation, key) {
100
+ if (!failure) return;
101
+ const result = failure(operation, key);
102
+ if (result instanceof Error) throw result;
103
+ if (result) throw new InjectedStorageError(operation, key);
104
+ }
105
+ function withStorageFailures(inner, failure) {
106
+ return {
107
+ kind: inner.kind,
108
+ get available() {
109
+ return inner.available;
110
+ },
111
+ async get(key) {
112
+ checkFailure(failure, "get", key);
113
+ return inner.get(key);
114
+ },
115
+ async set(key, value) {
116
+ checkFailure(failure, "set", key);
117
+ return inner.set(key, value);
118
+ },
119
+ async remove(key) {
120
+ checkFailure(failure, "remove", key);
121
+ return inner.remove(key);
122
+ }
123
+ };
124
+ }
125
+ function guarded(kind, isAvailable, failure, impl) {
126
+ const check = (operation, key) => {
127
+ if (!isAvailable()) throw new StorageUnavailableError(kind);
128
+ checkFailure(failure, operation, key);
129
+ };
130
+ return {
131
+ kind,
132
+ get available() {
133
+ return isAvailable();
134
+ },
135
+ async get(key) {
136
+ check("get", key);
137
+ return impl.get(key);
138
+ },
139
+ async set(key, value) {
140
+ check("set", key);
141
+ return impl.set(key, value);
142
+ },
143
+ async remove(key) {
144
+ check("remove", key);
145
+ return impl.remove(key);
146
+ }
147
+ };
148
+ }
149
+ function createMemoryStorage(options = {}) {
150
+ const entries = new Map(Object.entries(_nullishCoalesce(options.initial, () => ( {}))));
151
+ const adapter = guarded("memory", () => true, options.failure, {
152
+ async get(key) {
153
+ return entries.has(key) ? entries.get(key) : null;
154
+ },
155
+ async set(key, value) {
156
+ entries.set(key, value);
157
+ },
158
+ async remove(key) {
159
+ entries.delete(key);
160
+ }
161
+ });
162
+ return Object.assign(adapter, { entries });
163
+ }
164
+ function detectLocalStorage() {
165
+ try {
166
+ const storage = globalThis.localStorage;
167
+ return storage && typeof storage.getItem === "function" ? storage : void 0;
168
+ } catch (e2) {
169
+ return void 0;
170
+ }
171
+ }
172
+ function isLocalStorageAvailable() {
173
+ return detectLocalStorage() !== void 0;
174
+ }
175
+ function createLocalStorageAdapter(prefix = "", options = {}) {
176
+ const storage = _nullishCoalesce(options.storage, () => ( detectLocalStorage()));
177
+ return guarded("localStorage", () => storage !== void 0, options.failure, {
178
+ async get(key) {
179
+ return storage.getItem(prefix + key);
180
+ },
181
+ async set(key, value) {
182
+ storage.setItem(prefix + key, value);
183
+ },
184
+ async remove(key) {
185
+ storage.removeItem(prefix + key);
186
+ }
187
+ });
188
+ }
189
+ function detectIndexedDb() {
190
+ try {
191
+ const factory = globalThis.indexedDB;
192
+ return factory && typeof factory.open === "function" ? factory : void 0;
193
+ } catch (e3) {
194
+ return void 0;
195
+ }
196
+ }
197
+ function isIndexedDbAvailable() {
198
+ return detectIndexedDb() !== void 0;
199
+ }
200
+ var requestToPromise = (request) => new Promise((resolve, reject) => {
201
+ request.onsuccess = () => resolve(request.result);
202
+ request.onerror = () => reject(_nullishCoalesce(request.error, () => ( new Error("IndexedDB request failed"))));
203
+ });
204
+ function createIndexedDbAdapter(name = "pixerate-editor", options = {}) {
205
+ const factory = _nullishCoalesce(options.indexedDB, () => ( detectIndexedDb()));
206
+ const storeName = _nullishCoalesce(options.store, () => ( "kv"));
207
+ let dbPromise = null;
208
+ const open = () => {
209
+ dbPromise ??= new Promise((resolve, reject) => {
210
+ const request = factory.open(name, 1);
211
+ request.onupgradeneeded = () => {
212
+ const db = request.result;
213
+ if (!db.objectStoreNames.contains(storeName)) db.createObjectStore(storeName);
214
+ };
215
+ request.onsuccess = () => resolve(request.result);
216
+ request.onerror = () => {
217
+ dbPromise = null;
218
+ reject(_nullishCoalesce(request.error, () => ( new Error("IndexedDB open failed"))));
219
+ };
220
+ });
221
+ return dbPromise;
222
+ };
223
+ const write = async (fn) => {
224
+ const db = await open();
225
+ await new Promise((resolve, reject) => {
226
+ const tx = db.transaction(storeName, "readwrite");
227
+ tx.oncomplete = () => resolve();
228
+ tx.onerror = () => reject(_nullishCoalesce(tx.error, () => ( new Error("IndexedDB transaction failed"))));
229
+ tx.onabort = () => reject(_nullishCoalesce(tx.error, () => ( new Error("IndexedDB transaction aborted"))));
230
+ fn(tx.objectStore(storeName));
231
+ });
232
+ };
233
+ return guarded("indexedDB", () => factory !== void 0, options.failure, {
234
+ async get(key) {
235
+ const db = await open();
236
+ const tx = db.transaction(storeName, "readonly");
237
+ const value = await requestToPromise(tx.objectStore(storeName).get(key));
238
+ return typeof value === "string" ? value : null;
239
+ },
240
+ set: (key, value) => write((store) => store.put(value, key)),
241
+ remove: (key) => write((store) => store.delete(key))
242
+ });
243
+ }
244
+ function detectOpfsRoot() {
245
+ try {
246
+ const storage = _optionalChain([globalThis, 'access', _2 => _2.navigator, 'optionalAccess', _3 => _3.storage]);
247
+ if (storage && typeof storage.getDirectory === "function") {
248
+ return () => storage.getDirectory();
249
+ }
250
+ } catch (e4) {
251
+ }
252
+ return void 0;
253
+ }
254
+ function isOpfsAvailable() {
255
+ return detectOpfsRoot() !== void 0;
256
+ }
257
+ var isNotFound = (error) => !!error && typeof error === "object" && error.name === "NotFoundError";
258
+ function createOpfsAdapter(dir = "pixerate-editor", options = {}) {
259
+ const getRoot = _nullishCoalesce(options.root, () => ( detectOpfsRoot()));
260
+ let dirPromise = null;
261
+ const directory = () => {
262
+ dirPromise ??= getRoot().then((root) => root.getDirectoryHandle(dir, { create: true }));
263
+ dirPromise.catch(() => {
264
+ dirPromise = null;
265
+ });
266
+ return dirPromise;
267
+ };
268
+ const fileName = (key) => encodeURIComponent(key) || "_";
269
+ const readFile = async (d, name) => {
270
+ try {
271
+ const handle = await d.getFileHandle(name);
272
+ return await (await handle.getFile()).text();
273
+ } catch (error) {
274
+ if (isNotFound(error)) return null;
275
+ throw error;
276
+ }
277
+ };
278
+ const writeFile = async (d, name, value) => {
279
+ const handle = await d.getFileHandle(name, { create: true });
280
+ if (typeof handle.createWritable !== "function") {
281
+ throw new StorageUnavailableError("opfs (createWritable)");
282
+ }
283
+ const writable = await handle.createWritable();
284
+ await writable.write(value);
285
+ await writable.close();
286
+ return handle;
287
+ };
288
+ const removeFile = async (d, name) => {
289
+ try {
290
+ await d.removeEntry(name);
291
+ } catch (error) {
292
+ if (!isNotFound(error)) throw error;
293
+ }
294
+ };
295
+ return guarded("opfs", () => getRoot !== void 0, options.failure, {
296
+ async get(key) {
297
+ const d = await directory();
298
+ const base = fileName(key);
299
+ const pointer = await readFile(d, `${base}.ptr`);
300
+ if (pointer === "a" || pointer === "b") return readFile(d, `${base}.${pointer}`);
301
+ return readFile(d, base);
302
+ },
303
+ async set(key, value) {
304
+ const d = await directory();
305
+ const base = fileName(key);
306
+ const pointer = await readFile(d, `${base}.ptr`);
307
+ if (!options.disableRename && pointer === null) {
308
+ const tmp = await writeFile(d, `${base}.tmp`, value);
309
+ if (typeof tmp.move === "function") {
310
+ await tmp.move(base);
311
+ return;
312
+ }
313
+ await removeFile(d, `${base}.tmp`);
314
+ }
315
+ const next = pointer === "a" ? "b" : "a";
316
+ await writeFile(d, `${base}.${next}`, value);
317
+ await writeFile(d, `${base}.ptr`, next);
318
+ },
319
+ async remove(key) {
320
+ const d = await directory();
321
+ const base = fileName(key);
322
+ for (const name of [`${base}.ptr`, base, `${base}.a`, `${base}.b`, `${base}.tmp`]) {
323
+ await removeFile(d, name);
324
+ }
325
+ }
326
+ });
327
+ }
328
+
329
+ // src/persistence/autosave.ts
330
+ var recoveryMetaKey = (key) => `${key}:meta`;
331
+ function formatRecoveryMessage(meta, formatTime = (time) => new Date(time).toLocaleString()) {
332
+ return typeof meta.time === "number" && Number.isFinite(meta.time) ? `Restore unsaved changes from ${formatTime(meta.time)}?` : "Restore unsaved changes?";
333
+ }
334
+ function createAutosaveController(options) {
335
+ const {
336
+ storage,
337
+ key,
338
+ getSnapshot,
339
+ revision,
340
+ savedRevision,
341
+ interval = 5e3,
342
+ debounceMs = 1e3,
343
+ retryBaseMs = 1e3,
344
+ retryMaxMs = 3e4
345
+ } = options;
346
+ const serialize = _nullishCoalesce(options.serialize, () => ( ((value) => JSON.stringify(value))));
347
+ const deserialize = _nullishCoalesce(options.deserialize, () => ( ((text) => JSON.parse(text))));
348
+ const now = _nullishCoalesce(options.now, () => ( (() => Date.now())));
349
+ const wallClock = _nullishCoalesce(options.wallClock, () => ( (() => Date.now())));
350
+ const setTimer = _nullishCoalesce(options.setTimeout, () => ( ((fn, ms) => globalThis.setTimeout(fn, ms))));
351
+ const clearTimer = _nullishCoalesce(options.clearTimeout, () => ( ((handle) => globalThis.clearTimeout(handle))));
352
+ const metaKey = recoveryMetaKey(key);
353
+ const policy = createRecoveryPolicy({
354
+ interval,
355
+ retryDelay: (failures) => exponentialBackoff(failures, retryBaseMs, retryMaxMs)
356
+ });
357
+ let hydrated = options.ready === true;
358
+ let destroyed = false;
359
+ let timer = null;
360
+ let timerDue = Infinity;
361
+ let inflight = null;
362
+ let observedRevision = revision();
363
+ let lastChangeAt = null;
364
+ let pendingSince = null;
365
+ let forceNext = false;
366
+ let info = {
367
+ status: "idle",
368
+ error: null,
369
+ failures: 0,
370
+ nextAttemptAt: null,
371
+ lastSavedAt: null,
372
+ ready: hydrated
373
+ };
374
+ const listeners = /* @__PURE__ */ new Set();
375
+ const setInfo = (patch) => {
376
+ const next = { ...info, ...patch, failures: policy.failures, ready: hydrated };
377
+ if (next.status === info.status && next.error === info.error && next.failures === info.failures && next.nextAttemptAt === info.nextAttemptAt && next.lastSavedAt === info.lastSavedAt && next.ready === info.ready) {
378
+ return;
379
+ }
380
+ info = next;
381
+ _optionalChain([options, 'access', _4 => _4.onStatus, 'optionalCall', _5 => _5(info)]);
382
+ listeners.forEach((listener) => {
383
+ try {
384
+ listener(info);
385
+ } catch (error) {
386
+ console.error("[autosave] Error in status listener:", error);
387
+ }
388
+ });
389
+ };
390
+ const cancelTimer = () => {
391
+ if (timer !== null) clearTimer(timer);
392
+ timer = null;
393
+ timerDue = Infinity;
394
+ };
395
+ const schedule = (delay) => {
396
+ if (destroyed) return;
397
+ const due = now() + Math.max(0, delay);
398
+ if (timer !== null && timerDue <= due) return;
399
+ cancelTimer();
400
+ timerDue = due;
401
+ timer = setTimer(() => {
402
+ timer = null;
403
+ timerDue = Infinity;
404
+ tick();
405
+ }, Math.max(0, delay));
406
+ };
407
+ const observe = (t) => {
408
+ const rev = revision();
409
+ if (rev !== observedRevision) {
410
+ observedRevision = rev;
411
+ lastChangeAt = t;
412
+ pendingSince ??= t;
413
+ }
414
+ return rev;
415
+ };
416
+ const docName = () => typeof options.name === "function" ? options.name() : options.name;
417
+ const run = (action, rev) => {
418
+ if (action.type === "writeSnapshot") {
419
+ setInfo({ status: "saving", nextAttemptAt: null });
420
+ inflight = (async () => {
421
+ let ok = false;
422
+ let error = null;
423
+ try {
424
+ const text = serialize(getSnapshot());
425
+ await storage.set(key, text);
426
+ const meta = {
427
+ dirty: true,
428
+ revision: action.revision,
429
+ time: wallClock(),
430
+ name: docName()
431
+ };
432
+ await storage.set(metaKey, JSON.stringify(meta));
433
+ ok = true;
434
+ } catch (e) {
435
+ error = e;
436
+ }
437
+ const t = now();
438
+ policy.written(action.revision, ok, t);
439
+ inflight = null;
440
+ if (ok) {
441
+ if (observedRevision === action.revision) pendingSince = null;
442
+ setInfo({ status: "saved", error: null, lastSavedAt: t });
443
+ } else {
444
+ setInfo({ status: "error", error });
445
+ }
446
+ tick();
447
+ })();
448
+ } else if (action.type === "writeClean") {
449
+ setInfo({ status: "saving", nextAttemptAt: null });
450
+ inflight = (async () => {
451
+ let ok = false;
452
+ let error = null;
453
+ try {
454
+ await storage.set(metaKey, JSON.stringify({ dirty: false }));
455
+ ok = true;
456
+ } catch (e) {
457
+ error = e;
458
+ }
459
+ policy.cleaned(ok, now());
460
+ inflight = null;
461
+ if (ok) {
462
+ if (!policy.ownsSnapshot) await storage.remove(key).catch(() => void 0);
463
+ setInfo({ status: "saved", error: null });
464
+ } else {
465
+ setInfo({ status: "error", error });
466
+ }
467
+ tick();
468
+ })();
469
+ } else if (action.type === "wakeAfter") {
470
+ schedule(action.delay);
471
+ setInfo(
472
+ info.status === "error" ? { nextAttemptAt: now() + action.delay } : { status: rev !== savedRevision() ? "dirty" : info.status, nextAttemptAt: now() + action.delay }
473
+ );
474
+ } else if (rev === savedRevision() && info.status === "dirty") {
475
+ setInfo({ status: policy.ownsSnapshot ? "dirty" : "idle", nextAttemptAt: null });
476
+ } else if (rev !== savedRevision() && info.status !== "saved" && info.status !== "error") {
477
+ setInfo({ status: "dirty" });
478
+ }
479
+ };
480
+ function tick() {
481
+ if (destroyed || inflight) return;
482
+ const t = now();
483
+ const rev = observe(t);
484
+ const saved = savedRevision();
485
+ if (!hydrated) {
486
+ if (rev !== saved) setInfo({ status: "dirty" });
487
+ return;
488
+ }
489
+ const force = forceNext;
490
+ forceNext = false;
491
+ if (rev !== saved && !force && info.status !== "error") {
492
+ const changed = _nullishCoalesce(lastChangeAt, () => ( t));
493
+ const due = Math.min(changed + debounceMs, (_nullishCoalesce(pendingSince, () => ( changed))) + interval);
494
+ if (t < due) {
495
+ setInfo({ status: "dirty", nextAttemptAt: due });
496
+ schedule(due - t);
497
+ return;
498
+ }
499
+ }
500
+ run(policy.tick(t, rev, saved, { force }), rev);
501
+ }
502
+ const whenIdle = async () => {
503
+ while (inflight) await inflight;
504
+ };
505
+ return {
506
+ getStatus: () => info,
507
+ subscribe(listener) {
508
+ listeners.add(listener);
509
+ return () => {
510
+ listeners.delete(listener);
511
+ };
512
+ },
513
+ notify() {
514
+ if (destroyed) return;
515
+ const t = now();
516
+ const before = observedRevision;
517
+ const rev = observe(t);
518
+ const dirty = rev !== savedRevision();
519
+ if (dirty && rev !== before && info.status !== "error" && !inflight) {
520
+ setInfo({ status: "dirty" });
521
+ }
522
+ if (!hydrated) return;
523
+ const changed = _nullishCoalesce(lastChangeAt, () => ( t));
524
+ const due = Math.min(changed + debounceMs, (_nullishCoalesce(pendingSince, () => ( changed))) + interval);
525
+ schedule(dirty ? due - t : 0);
526
+ },
527
+ ready() {
528
+ if (hydrated || destroyed) return;
529
+ hydrated = true;
530
+ setInfo({});
531
+ schedule(0);
532
+ },
533
+ get isReady() {
534
+ return hydrated;
535
+ },
536
+ async flushNow() {
537
+ if (destroyed || !hydrated) return;
538
+ await whenIdle();
539
+ cancelTimer();
540
+ forceNext = true;
541
+ tick();
542
+ await whenIdle();
543
+ },
544
+ async markClean() {
545
+ if (destroyed) return;
546
+ await whenIdle();
547
+ cancelTimer();
548
+ tick();
549
+ await whenIdle();
550
+ },
551
+ async checkRecovery() {
552
+ if (policy.ownsSnapshot) return null;
553
+ let meta;
554
+ try {
555
+ const metaText = await storage.get(metaKey);
556
+ if (!metaText) return null;
557
+ meta = JSON.parse(metaText);
558
+ } catch (e5) {
559
+ return null;
560
+ }
561
+ if (!meta || meta.dirty !== true) return null;
562
+ const text = await storage.get(key).catch(() => null);
563
+ if (text === null) return null;
564
+ try {
565
+ return { snapshot: deserialize(text), text, meta };
566
+ } catch (error) {
567
+ return { snapshot: void 0, text, meta, error };
568
+ }
569
+ },
570
+ async discardRecovery() {
571
+ if (policy.ownsSnapshot) return false;
572
+ await whenIdle();
573
+ await storage.remove(metaKey);
574
+ await storage.remove(key);
575
+ return true;
576
+ },
577
+ tick,
578
+ whenIdle,
579
+ destroy() {
580
+ destroyed = true;
581
+ cancelTimer();
582
+ listeners.clear();
583
+ }
584
+ };
585
+ }
586
+
587
+ // src/persistence/revisions.ts
588
+ function createRevisionTracker(options = {}) {
589
+ let revision = 0;
590
+ let saved = 0;
591
+ const listeners = /* @__PURE__ */ new Set();
592
+ const emit = () => listeners.forEach((listener) => {
593
+ try {
594
+ listener();
595
+ } catch (error) {
596
+ console.error("[autosave] Error in revision listener:", error);
597
+ }
598
+ });
599
+ const syncSaved = () => {
600
+ if (options.dirty && !options.dirty.dirty) saved = revision;
601
+ };
602
+ const unsubscribers = [];
603
+ if (options.history) {
604
+ unsubscribers.push(
605
+ options.history.subscribe(() => {
606
+ revision++;
607
+ emit();
608
+ })
609
+ );
610
+ }
611
+ if (options.dirty) {
612
+ let first = true;
613
+ unsubscribers.push(
614
+ options.dirty.subscribe((dirty) => {
615
+ if (first) {
616
+ first = false;
617
+ if (dirty && revision === saved) revision++;
618
+ return;
619
+ }
620
+ if (dirty && revision === saved) revision++;
621
+ if (!dirty) saved = revision;
622
+ emit();
623
+ })
624
+ );
625
+ }
626
+ return {
627
+ revision: () => revision,
628
+ savedRevision: () => {
629
+ syncSaved();
630
+ return saved;
631
+ },
632
+ bump() {
633
+ revision++;
634
+ emit();
635
+ },
636
+ markSaved() {
637
+ saved = revision;
638
+ emit();
639
+ },
640
+ subscribe(listener) {
641
+ listeners.add(listener);
642
+ return () => {
643
+ listeners.delete(listener);
644
+ };
645
+ },
646
+ destroy() {
647
+ unsubscribers.forEach((fn) => fn());
648
+ unsubscribers.length = 0;
649
+ listeners.clear();
650
+ }
651
+ };
652
+ }
653
+
654
+
655
+
656
+
657
+
658
+
659
+
660
+
661
+
662
+
663
+
664
+
665
+
666
+
667
+
668
+
669
+
670
+
671
+
672
+
673
+
674
+
675
+ exports.InjectedStorageError = InjectedStorageError; exports.StorageUnavailableError = StorageUnavailableError; exports.createAutosaveController = createAutosaveController; exports.createIndexedDbAdapter = createIndexedDbAdapter; exports.createLoadReport = _chunkD6FH2YAXcjs.createLoadReport; exports.createLocalStorageAdapter = createLocalStorageAdapter; exports.createMemoryStorage = createMemoryStorage; exports.createOpfsAdapter = createOpfsAdapter; exports.createRecoveryPolicy = createRecoveryPolicy; exports.createRevisionTracker = createRevisionTracker; exports.exponentialBackoff = exponentialBackoff; exports.formatRecoveryMessage = formatRecoveryMessage; exports.hasLoadProblems = _chunkD6FH2YAXcjs.hasLoadProblems; exports.isIndexedDbAvailable = isIndexedDbAvailable; exports.isLocalStorageAvailable = isLocalStorageAvailable; exports.isOpfsAvailable = isOpfsAvailable; exports.loadDocument = _chunkD6FH2YAXcjs.loadDocument; exports.loadSections = _chunkD6FH2YAXcjs.loadSections; exports.migrateDocument = _chunkD6FH2YAXcjs.migrateDocument; exports.recoveryMetaKey = recoveryMetaKey; exports.withStorageFailures = withStorageFailures;
676
+ //# sourceMappingURL=index.cjs.map