@pixerate/editor 0.164.0 → 0.166.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.
- package/dist/agent/index.cjs +54 -39
- package/dist/agent/index.js +24 -9
- package/dist/animation/index.cjs +6 -5
- package/dist/animation/index.js +5 -4
- package/dist/animation-editor/index.cjs +65 -64
- package/dist/animation-editor/index.js +8 -7
- package/dist/bridge/cli.cjs +3 -3
- package/dist/bridge/cli.js +2 -2
- package/dist/bridge/index.cjs +3 -3
- package/dist/bridge/index.js +2 -2
- package/dist/canvas/index.cjs +89 -87
- package/dist/canvas/index.js +11 -9
- package/dist/chart/controller.d.cts +60 -2
- package/dist/chart/controller.d.ts +60 -2
- package/dist/chart/index.cjs +657 -163
- package/dist/chart/index.d.cts +3 -0
- package/dist/chart/index.d.ts +3 -0
- package/dist/chart/index.js +512 -18
- package/dist/chart/layout-families.d.cts +14 -0
- package/dist/chart/layout-families.d.ts +14 -0
- package/dist/chart/layout-shared.d.cts +5 -0
- package/dist/chart/layout-shared.d.ts +5 -0
- package/dist/chart/links.d.cts +34 -0
- package/dist/chart/links.d.ts +34 -0
- package/dist/chart/mermaid.d.cts +9 -6
- package/dist/chart/mermaid.d.ts +9 -6
- package/dist/chart/race.d.cts +26 -0
- package/dist/chart/race.d.ts +26 -0
- package/dist/chart/schemas.d.cts +5 -0
- package/dist/chart/schemas.d.ts +5 -0
- package/dist/chart/spec.d.cts +3 -2
- package/dist/chart/spec.d.ts +3 -2
- package/dist/chart/style.d.cts +33 -0
- package/dist/chart/style.d.ts +33 -0
- package/dist/chart/types.d.cts +28 -2
- package/dist/chart/types.d.ts +28 -2
- package/dist/{chunk-ZVNAMTFO.cjs → chunk-3FSUJIXN.cjs} +18 -174
- package/dist/{chunk-RQCMSEM5.cjs → chunk-4QUUHTGJ.cjs} +2 -2
- package/dist/{chunk-CIPIESX4.cjs → chunk-4ZC5QJE2.cjs} +3 -3
- package/dist/{chunk-S6X2QRVU.cjs → chunk-5UEFBM2M.cjs} +1 -138
- package/dist/{chunk-5FNGLV2D.js → chunk-5UNQDC3Q.js} +18 -3
- package/dist/{chunk-YSAGBIRW.cjs → chunk-6FYIFGBW.cjs} +16 -16
- package/dist/{chunk-QYZGBVNC.js → chunk-7272D3K2.js} +10 -8
- package/dist/{chunk-462WGGXX.cjs → chunk-AMX23CSN.cjs} +27 -16
- package/dist/{chunk-3VLOOO3G.js → chunk-BDIM6NVW.js} +1 -1
- package/dist/{chunk-R3P2M7HU.cjs → chunk-BPX4Z3N3.cjs} +355 -207
- package/dist/{chunk-D54JHQK2.cjs → chunk-CHLV7UFP.cjs} +51 -51
- package/dist/{chunk-OXERWBKE.js → chunk-DFCAR7AW.js} +3 -3
- package/dist/{chunk-GZ2FM3ZB.js → chunk-DKNEQX7T.js} +6 -162
- package/dist/{chunk-JCXMJUQO.cjs → chunk-EGZM52EO.cjs} +3 -3
- package/dist/{chunk-ES3UEVUT.cjs → chunk-EUIDTZNQ.cjs} +112 -110
- package/dist/{chunk-H3OEM3Q2.js → chunk-F6R67I3A.js} +1 -1
- package/dist/{chunk-3NEZJCBY.js → chunk-FR432ROA.js} +349 -201
- package/dist/chunk-HIDFL5LQ.js +484 -0
- package/dist/{chunk-22KLZZGH.cjs → chunk-HXOBOHH2.cjs} +8 -8
- package/dist/{chunk-MKTDELCD.js → chunk-I5MRJW7B.js} +1 -1
- package/dist/{chunk-SDHZG5TF.js → chunk-ILP77UD6.js} +160 -2
- package/dist/{chunk-Z4P2RBMJ.js → chunk-ITANXMZD.js} +177 -3
- package/dist/chunk-KGRZMHZB.js +204 -0
- package/dist/{chunk-SET6KJOP.js → chunk-LPIKXPBM.js} +1 -1
- package/dist/{chunk-BQWGN5WH.js → chunk-M2TPMYJE.js} +1 -1
- package/dist/{chunk-GTELFJRE.cjs → chunk-M5GLQQWG.cjs} +2 -2
- package/dist/{chunk-4VU4CP7R.js → chunk-MHLU6GRJ.js} +1 -138
- package/dist/{chunk-NZD2MZJC.cjs → chunk-MZFZSVUJ.cjs} +38 -23
- package/dist/{chunk-DAPQGA55.js → chunk-NQQDGGXY.js} +2 -2
- package/dist/chunk-O6OGNDND.js +164 -0
- package/dist/{chunk-PA3IMPPS.cjs → chunk-OVYQAYGH.cjs} +81 -81
- package/dist/{chunk-YXOW456B.js → chunk-PPEDZ5WR.js} +1 -1
- package/dist/{chunk-24M7VD6M.cjs → chunk-S4EIAIEW.cjs} +7 -7
- package/dist/{chunk-M4MIZPQI.cjs → chunk-SJ2CPTQC.cjs} +189 -15
- package/dist/{chunk-YGG7WDWT.cjs → chunk-SZWVMB3B.cjs} +24 -18
- package/dist/chunk-TFF2GTGN.cjs +153 -0
- package/dist/chunk-TTCJPX4U.cjs +204 -0
- package/dist/{chunk-SBQVEBSU.cjs → chunk-UUUTEVX6.cjs} +242 -68
- package/dist/{chunk-WI7QJA7A.cjs → chunk-UXETV2NC.cjs} +161 -3
- package/dist/{chunk-GKNEJ6EN.cjs → chunk-V5L2G22R.cjs} +321 -107
- package/dist/{chunk-TALQONZ5.js → chunk-VB2CY6EG.js} +3 -3
- package/dist/{chunk-QY4KUDMY.js → chunk-VRXXAXUT.js} +2 -2
- package/dist/chunk-VU6WQSMB.cjs +164 -0
- package/dist/{chunk-NHLYK3AX.js → chunk-XBQX76CT.js} +182 -8
- package/dist/{chunk-5AARIS3R.cjs → chunk-XCDGM7TD.cjs} +37 -37
- package/dist/{chunk-POR54B55.js → chunk-XNU45TYA.js} +7 -1
- package/dist/{chunk-EVXQNQND.js → chunk-XZB3TJ6K.js} +16 -5
- package/dist/chunk-YBAPT7NL.js +153 -0
- package/dist/{chunk-SZA42R4N.js → chunk-YUFTVNLX.js} +230 -16
- package/dist/chunk-Z2UQYMPS.cjs +484 -0
- package/dist/color/index.cjs +5 -3
- package/dist/color/index.js +12 -10
- package/dist/diagram/index.cjs +55 -243
- package/dist/diagram/index.js +12 -200
- package/dist/export/index.cjs +7 -6
- package/dist/export/index.js +6 -5
- package/dist/extensions/index.cjs +12 -9
- package/dist/extensions/index.js +11 -8
- package/dist/gradient/index.cjs +29 -27
- package/dist/gradient/index.js +6 -4
- package/dist/image-editor/index.cjs +644 -643
- package/dist/image-editor/index.js +7 -6
- package/dist/index.cjs +21 -18
- package/dist/index.js +14 -11
- package/dist/paint-editor/index.cjs +19 -18
- package/dist/paint-editor/index.js +7 -6
- package/dist/parameters/index.cjs +2 -2
- package/dist/parameters/index.js +1 -1
- package/dist/persistence/assetWrites.d.cts +22 -0
- package/dist/persistence/assetWrites.d.ts +22 -0
- package/dist/persistence/assets.d.cts +71 -0
- package/dist/persistence/assets.d.ts +71 -0
- package/dist/persistence/documentStore.d.cts +17 -1
- package/dist/persistence/documentStore.d.ts +17 -1
- package/dist/persistence/documentTools.d.cts +5 -1
- package/dist/persistence/documentTools.d.ts +5 -1
- package/dist/persistence/editorAutosave.d.cts +7 -0
- package/dist/persistence/editorAutosave.d.ts +7 -0
- package/dist/persistence/index.cjs +141 -21
- package/dist/persistence/index.js +130 -10
- package/dist/persistence/storage.d.cts +13 -1
- package/dist/persistence/storage.d.ts +13 -1
- package/dist/spreadsheet/index.cjs +3 -3
- package/dist/spreadsheet/index.js +2 -2
- package/dist/tokens/index.cjs +11 -3
- package/dist/tokens/index.d.cts +1 -0
- package/dist/tokens/index.d.ts +1 -0
- package/dist/tokens/index.js +10 -2
- package/dist/tokens/style.d.cts +36 -0
- package/dist/tokens/style.d.ts +36 -0
- package/dist/tools/index.cjs +15 -3
- package/dist/tools/index.d.cts +2 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +14 -2
- package/dist/tools/style-tools.d.cts +19 -0
- package/dist/tools/style-tools.d.ts +19 -0
- package/dist/tools/style.d.cts +212 -0
- package/dist/tools/style.d.ts +212 -0
- package/dist/tools/view.d.cts +6 -0
- package/dist/tools/view.d.ts +6 -0
- package/dist/typography/index.cjs +8 -6
- package/dist/typography/index.js +5 -3
- package/dist/vector/index.cjs +953 -952
- package/dist/vector/index.js +7 -6
- package/dist/video-editor/index.cjs +8 -7
- package/dist/video-editor/index.js +6 -5
- package/package.json +1 -1
- package/skills/pixerate-chart/SKILL.md +20 -2
- package/dist/chunk-ERAGQL2N.cjs +0 -68
- package/dist/chunk-KMU6QJB7.js +0 -68
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
var _chunkSJ2CPTQCcjs = require('../chunk-SJ2CPTQC.cjs');
|
|
24
27
|
require('../chunk-2BSBFBOA.cjs');
|
|
25
28
|
|
|
26
29
|
// src/persistence/recoveryPolicy.ts
|
|
@@ -136,7 +139,13 @@ function withStorageFailures(inner, failure) {
|
|
|
136
139
|
async remove(key) {
|
|
137
140
|
checkFailure(failure, "remove", key);
|
|
138
141
|
return inner.remove(key);
|
|
139
|
-
}
|
|
142
|
+
},
|
|
143
|
+
...inner.keys ? {
|
|
144
|
+
async keys(prefix = "") {
|
|
145
|
+
checkFailure(failure, "keys", prefix);
|
|
146
|
+
return inner.keys(prefix);
|
|
147
|
+
}
|
|
148
|
+
} : {}
|
|
140
149
|
};
|
|
141
150
|
}
|
|
142
151
|
function guarded(kind, isAvailable, failure, impl) {
|
|
@@ -160,7 +169,13 @@ function guarded(kind, isAvailable, failure, impl) {
|
|
|
160
169
|
async remove(key) {
|
|
161
170
|
check("remove", key);
|
|
162
171
|
return impl.remove(key);
|
|
163
|
-
}
|
|
172
|
+
},
|
|
173
|
+
...impl.keys ? {
|
|
174
|
+
async keys(prefix = "") {
|
|
175
|
+
check("keys", prefix);
|
|
176
|
+
return impl.keys(prefix);
|
|
177
|
+
}
|
|
178
|
+
} : {}
|
|
164
179
|
};
|
|
165
180
|
}
|
|
166
181
|
function createMemoryStorage(options = {}) {
|
|
@@ -174,6 +189,9 @@ function createMemoryStorage(options = {}) {
|
|
|
174
189
|
},
|
|
175
190
|
async remove(key) {
|
|
176
191
|
entries.delete(key);
|
|
192
|
+
},
|
|
193
|
+
async keys(prefix) {
|
|
194
|
+
return [...entries.keys()].filter((key) => key.startsWith(prefix));
|
|
177
195
|
}
|
|
178
196
|
});
|
|
179
197
|
return Object.assign(adapter, { entries });
|
|
@@ -200,7 +218,18 @@ function createLocalStorageAdapter(prefix = "", options = {}) {
|
|
|
200
218
|
},
|
|
201
219
|
async remove(key) {
|
|
202
220
|
storage.removeItem(prefix + key);
|
|
203
|
-
}
|
|
221
|
+
},
|
|
222
|
+
// Listing needs `length` and `key()` (real Web Storage has both).
|
|
223
|
+
...!storage || typeof storage.key === "function" && typeof storage.length === "number" ? {
|
|
224
|
+
async keys(start) {
|
|
225
|
+
const out = [];
|
|
226
|
+
for (let i = 0; i < storage.length; i++) {
|
|
227
|
+
const key = storage.key(i);
|
|
228
|
+
if (key !== null && key.startsWith(prefix + start)) out.push(key.slice(prefix.length));
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
} : {}
|
|
204
233
|
});
|
|
205
234
|
}
|
|
206
235
|
function detectIndexedDb() {
|
|
@@ -255,7 +284,13 @@ function createIndexedDbAdapter(name = "pixerate-editor", options = {}) {
|
|
|
255
284
|
return typeof value === "string" ? value : null;
|
|
256
285
|
},
|
|
257
286
|
set: (key, value) => write((store) => store.put(value, key)),
|
|
258
|
-
remove: (key) => write((store) => store.delete(key))
|
|
287
|
+
remove: (key) => write((store) => store.delete(key)),
|
|
288
|
+
async keys(prefix) {
|
|
289
|
+
const db = await open();
|
|
290
|
+
const tx = db.transaction(storeName, "readonly");
|
|
291
|
+
const all = await requestToPromise(tx.objectStore(storeName).getAllKeys());
|
|
292
|
+
return all.filter((key) => typeof key === "string" && key.startsWith(prefix));
|
|
293
|
+
}
|
|
259
294
|
});
|
|
260
295
|
}
|
|
261
296
|
function detectOpfsRoot() {
|
|
@@ -339,6 +374,30 @@ function createOpfsAdapter(dir = "pixerate-editor", options = {}) {
|
|
|
339
374
|
for (const name of [`${base}.ptr`, base, `${base}.a`, `${base}.b`, `${base}.tmp`]) {
|
|
340
375
|
await removeFile(d, name);
|
|
341
376
|
}
|
|
377
|
+
},
|
|
378
|
+
async keys(prefix) {
|
|
379
|
+
const d = await directory();
|
|
380
|
+
if (typeof d.keys !== "function") throw new StorageUnavailableError("opfs (directory listing)");
|
|
381
|
+
const names = /* @__PURE__ */ new Set();
|
|
382
|
+
for await (const name of d.keys()) names.add(name);
|
|
383
|
+
const keys = /* @__PURE__ */ new Set();
|
|
384
|
+
for (const name of names) {
|
|
385
|
+
if (name.endsWith(".tmp")) continue;
|
|
386
|
+
if (name.endsWith(".ptr")) keys.add(name.slice(0, -4));
|
|
387
|
+
else if (/\.[ab]$/.test(name) && names.has(`${name.slice(0, -2)}.ptr`)) continue;
|
|
388
|
+
else keys.add(name);
|
|
389
|
+
}
|
|
390
|
+
const out = [];
|
|
391
|
+
for (const name of keys) {
|
|
392
|
+
let key;
|
|
393
|
+
try {
|
|
394
|
+
key = decodeURIComponent(name);
|
|
395
|
+
} catch (e5) {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (key.startsWith(prefix)) out.push(key);
|
|
399
|
+
}
|
|
400
|
+
return out;
|
|
342
401
|
}
|
|
343
402
|
});
|
|
344
403
|
}
|
|
@@ -572,7 +631,7 @@ function createAutosaveController(options) {
|
|
|
572
631
|
const metaText = await storage.get(metaKey);
|
|
573
632
|
if (!metaText) return null;
|
|
574
633
|
meta = JSON.parse(metaText);
|
|
575
|
-
} catch (
|
|
634
|
+
} catch (e6) {
|
|
576
635
|
return null;
|
|
577
636
|
}
|
|
578
637
|
if (!meta || meta.dirty !== true) return null;
|
|
@@ -670,15 +729,16 @@ function createRevisionTracker(options = {}) {
|
|
|
670
729
|
|
|
671
730
|
// src/persistence/editorAutosave.ts
|
|
672
731
|
function createEditorAutosave(doc, options) {
|
|
673
|
-
const { dirty, key, ...rest } = options;
|
|
732
|
+
const { dirty, key, collectAssetGarbage, ...rest } = options;
|
|
733
|
+
const collect = () => _chunkSJ2CPTQCcjs.collectRetiredEditorAssets.call(void 0, rest.storage, doc, collectAssetGarbage);
|
|
674
734
|
const revisions = createRevisionTracker({ history: doc, dirty });
|
|
675
735
|
const controller = createAutosaveController({
|
|
676
736
|
...rest,
|
|
677
737
|
// Image and paint documents hold asset references: their bytes are saved and read with them.
|
|
678
|
-
storage: doc.assets ?
|
|
738
|
+
storage: doc.assets ? _chunkSJ2CPTQCcjs.withEditorAssets.call(void 0, rest.storage, doc.assets) : rest.storage,
|
|
679
739
|
key: _nullishCoalesce(key, () => ( doc.kind)),
|
|
680
|
-
getSnapshot: () =>
|
|
681
|
-
deserialize: (text) =>
|
|
740
|
+
getSnapshot: () => _chunkSJ2CPTQCcjs.toEditorDocumentFile.call(void 0, doc),
|
|
741
|
+
deserialize: (text) => _chunkSJ2CPTQCcjs.parseEditorDocument.call(void 0, text, { kind: doc.kind }),
|
|
682
742
|
revision: revisions.revision,
|
|
683
743
|
savedRevision: revisions.savedRevision
|
|
684
744
|
});
|
|
@@ -696,10 +756,12 @@ function createEditorAutosave(doc, options) {
|
|
|
696
756
|
async discard() {
|
|
697
757
|
await controller.discardRecovery();
|
|
698
758
|
controller.ready();
|
|
759
|
+
await collect();
|
|
699
760
|
},
|
|
700
761
|
async markClean() {
|
|
701
762
|
revisions.markSaved();
|
|
702
763
|
await controller.markClean();
|
|
764
|
+
await collect();
|
|
703
765
|
},
|
|
704
766
|
destroy() {
|
|
705
767
|
unsubscribe();
|
|
@@ -720,6 +782,7 @@ function createEditorDocumentStore(options) {
|
|
|
720
782
|
const now = _nullishCoalesce(options.now, () => ( Date.now));
|
|
721
783
|
const indexKey = `${ns}:index`;
|
|
722
784
|
const fileKey = (key) => `${ns}:file:${key}`;
|
|
785
|
+
const gc = _nullishCoalesce(options.collectAssetGarbage, () => ( true));
|
|
723
786
|
let index = null;
|
|
724
787
|
let queue = Promise.resolve();
|
|
725
788
|
const run = (task) => {
|
|
@@ -737,7 +800,7 @@ function createEditorDocumentStore(options) {
|
|
|
737
800
|
if (data && Array.isArray(data.items)) {
|
|
738
801
|
items = data.items.filter((item) => item && typeof item.key === "string" && typeof item.kind === "string");
|
|
739
802
|
}
|
|
740
|
-
} catch (
|
|
803
|
+
} catch (e7) {
|
|
741
804
|
items = [];
|
|
742
805
|
}
|
|
743
806
|
}
|
|
@@ -751,7 +814,7 @@ function createEditorDocumentStore(options) {
|
|
|
751
814
|
const newestFirst = (a, b) => a.savedAt < b.savedAt ? 1 : a.savedAt > b.savedAt ? -1 : 0;
|
|
752
815
|
const readFile = async (key) => {
|
|
753
816
|
const raw = await storage.get(fileKey(key));
|
|
754
|
-
return raw === null ? null :
|
|
817
|
+
return raw === null ? null : _chunkSJ2CPTQCcjs.parseEditorDocument.call(void 0, raw);
|
|
755
818
|
};
|
|
756
819
|
return {
|
|
757
820
|
save: (doc, name) => run(async () => {
|
|
@@ -763,9 +826,8 @@ function createEditorDocumentStore(options) {
|
|
|
763
826
|
formatVersion: doc.formatVersion,
|
|
764
827
|
savedAt: new Date(now()).toISOString()
|
|
765
828
|
};
|
|
766
|
-
const text = JSON.stringify(
|
|
767
|
-
|
|
768
|
-
await storage.set(fileKey(entry.key), text);
|
|
829
|
+
const text = JSON.stringify(_chunkSJ2CPTQCcjs.toEditorDocumentFile.call(void 0, doc, trimmed));
|
|
830
|
+
await (doc.assets ? _chunkSJ2CPTQCcjs.withEditorAssets.call(void 0, storage, doc.assets) : storage).set(fileKey(entry.key), text);
|
|
769
831
|
await saveIndex([...(await loadIndex()).filter((item) => item.key !== entry.key), entry]);
|
|
770
832
|
return entry;
|
|
771
833
|
}),
|
|
@@ -776,13 +838,13 @@ function createEditorDocumentStore(options) {
|
|
|
776
838
|
const file = entry ? await readFile(key) : null;
|
|
777
839
|
if (!entry || !file) {
|
|
778
840
|
const keys = (await loadIndex()).filter((item) => item.kind === doc.kind).sort(newestFirst).map((item) => item.key);
|
|
779
|
-
throw new (0,
|
|
841
|
+
throw new (0, _chunkSJ2CPTQCcjs.EditorDocumentLoadError)(
|
|
780
842
|
`There is no saved document "${key}". ${keys.length ? `Saved ${doc.kind} documents: ${keys.join(", ")}.` : `No ${doc.kind} documents are saved yet.`}`
|
|
781
843
|
);
|
|
782
844
|
}
|
|
783
845
|
if (doc.assets) {
|
|
784
|
-
const missing = await
|
|
785
|
-
if (missing.length) throw new (0,
|
|
846
|
+
const missing = await _chunkSJ2CPTQCcjs.preloadEditorAssets.call(void 0, storage, doc.assets, file);
|
|
847
|
+
if (missing.length) throw new (0, _chunkSJ2CPTQCcjs.EditorDocumentLoadError)(`The saved document "${key}" refers to ${missing.length} asset${missing.length === 1 ? "" : "s"} that storage no longer has, so it cannot be opened.`);
|
|
786
848
|
}
|
|
787
849
|
const report = doc.loadDocument(file, { history: _nullishCoalesce(openOptions.history, () => ( "reset")) });
|
|
788
850
|
return { ...entry, report };
|
|
@@ -792,8 +854,10 @@ function createEditorDocumentStore(options) {
|
|
|
792
854
|
if (!items.some((item) => item.key === key)) return false;
|
|
793
855
|
await saveIndex(items.filter((item) => item.key !== key));
|
|
794
856
|
await storage.remove(fileKey(key));
|
|
857
|
+
if (gc) await _chunkSJ2CPTQCcjs.tryCollectEditorAssetGarbage.call(void 0, storage, gc === true ? {} : gc);
|
|
795
858
|
return true;
|
|
796
|
-
})
|
|
859
|
+
}),
|
|
860
|
+
...storage.keys ? { collectAssetGarbage: (gcOptions) => run(() => _chunkSJ2CPTQCcjs.collectEditorAssetGarbage.call(void 0, storage, gcOptions)) } : {}
|
|
797
861
|
};
|
|
798
862
|
}
|
|
799
863
|
|
|
@@ -939,13 +1003,66 @@ function createDocumentToolProvider(doc, options = {}) {
|
|
|
939
1003
|
}
|
|
940
1004
|
}
|
|
941
1005
|
];
|
|
1006
|
+
const LIST_LIMIT = 100;
|
|
1007
|
+
const assetTools = (s) => [
|
|
1008
|
+
{
|
|
1009
|
+
name: `find_unreferenced_${kind}_assets`,
|
|
1010
|
+
title: `Find unreferenced ${noun} assets`,
|
|
1011
|
+
description: `Finds the stored image data (layer pixels, masks and other assets of every image and paint document in the host's storage) that no saved document or autosave snapshot refers to any more, without deleting anything. The open ${noun}'s own assets always count as used. Returns { assets, referenced, kept, unreferenced, unreferencedChars, refs, more }: unreferenced is how many could be deleted, unreferencedChars the characters of data they hold, refs up to ${LIST_LIMIT} of their references for delete_unreferenced_${kind}_assets, and more how many were left out of refs.`,
|
|
1012
|
+
inputSchema: object({}),
|
|
1013
|
+
annotations: { title: `Find unreferenced ${noun} assets`, ...READ },
|
|
1014
|
+
handler: async () => {
|
|
1015
|
+
const report = await s.collectAssetGarbage({ dryRun: true, keep: [doc.getDocument()] });
|
|
1016
|
+
return {
|
|
1017
|
+
assets: report.assets,
|
|
1018
|
+
referenced: report.referenced,
|
|
1019
|
+
kept: report.kept,
|
|
1020
|
+
unreferenced: report.unreferenced.length,
|
|
1021
|
+
unreferencedChars: report.unreferencedChars,
|
|
1022
|
+
refs: report.unreferenced.slice(0, LIST_LIMIT),
|
|
1023
|
+
more: Math.max(0, report.unreferenced.length - LIST_LIMIT)
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
name: `delete_unreferenced_${kind}_assets`,
|
|
1029
|
+
title: `Delete unreferenced ${noun} assets`,
|
|
1030
|
+
description: `Deletes stored image data that no saved document or autosave snapshot refers to, to free browser storage. Saved documents and the open ${noun} are never affected, and a write in progress is waited for. Call find_unreferenced_${kind}_assets first to see what would go, and do it when the user asks to free space or clean up storage. It cannot be undone, but nothing refers to the deleted data. Returns { removed, freedChars, kept, remaining }: how many assets were deleted, the characters of data freed, how many unreferenced ones were kept because a write was using them, and how many assets are left.`,
|
|
1031
|
+
inputSchema: object({
|
|
1032
|
+
refs: {
|
|
1033
|
+
type: "array",
|
|
1034
|
+
items: { type: "string", pattern: "^pixerate-asset:" },
|
|
1035
|
+
minItems: 1,
|
|
1036
|
+
uniqueItems: true,
|
|
1037
|
+
description: `Delete only these references from find_unreferenced_${kind}_assets (refs). Default: every unreferenced asset. If any is no longer unreferenced, nothing is deleted and the error names it.`
|
|
1038
|
+
}
|
|
1039
|
+
}),
|
|
1040
|
+
annotations: { title: `Delete unreferenced ${noun} assets`, ...OVERWRITE },
|
|
1041
|
+
handler: async ({ refs }) => {
|
|
1042
|
+
writable();
|
|
1043
|
+
const report = await s.collectAssetGarbage({ keep: [doc.getDocument()], ...refs ? { only: refs } : {} });
|
|
1044
|
+
return {
|
|
1045
|
+
removed: report.removed.length,
|
|
1046
|
+
freedChars: report.freedChars,
|
|
1047
|
+
kept: report.kept,
|
|
1048
|
+
remaining: report.assets - report.removed.length
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
];
|
|
942
1053
|
return {
|
|
943
1054
|
id: _nullishCoalesce(options.id, () => ( `${kind}-document`)),
|
|
944
1055
|
...options.namespace !== void 0 ? { namespace: options.namespace } : {},
|
|
945
1056
|
title: _nullishCoalesce(options.title, () => ( `${noun.charAt(0).toUpperCase()}${noun.slice(1)} document`)),
|
|
946
1057
|
description: `Get, load${options.history === false ? "" : ", undo, redo"}${store ? ", save and open" : ""} the whole ${noun}.`,
|
|
947
1058
|
...options.skill ? { skill: options.skill } : {},
|
|
948
|
-
tools: [
|
|
1059
|
+
tools: [
|
|
1060
|
+
get,
|
|
1061
|
+
load,
|
|
1062
|
+
...options.history === false ? [] : [undo, redo],
|
|
1063
|
+
...store ? storeTools(store) : [],
|
|
1064
|
+
..._optionalChain([store, 'optionalAccess', _11 => _11.collectAssetGarbage]) && doc.assets ? assetTools(store) : []
|
|
1065
|
+
]
|
|
949
1066
|
};
|
|
950
1067
|
}
|
|
951
1068
|
|
|
@@ -990,4 +1107,7 @@ function createDocumentToolProvider(doc, options = {}) {
|
|
|
990
1107
|
|
|
991
1108
|
|
|
992
1109
|
|
|
993
|
-
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
exports.EDITOR_ASSET_PREFIX = _chunkSJ2CPTQCcjs.EDITOR_ASSET_PREFIX; exports.EDITOR_DOCUMENT_FORMAT = _chunkSJ2CPTQCcjs.EDITOR_DOCUMENT_FORMAT; exports.EditorDocumentLoadError = _chunkSJ2CPTQCcjs.EditorDocumentLoadError; exports.InjectedStorageError = InjectedStorageError; exports.StorageUnavailableError = StorageUnavailableError; exports.collectAssetRefs = _chunkSJ2CPTQCcjs.collectAssetRefs; exports.collectEditorAssetGarbage = _chunkSJ2CPTQCcjs.collectEditorAssetGarbage; exports.collectRetiredEditorAssets = _chunkSJ2CPTQCcjs.collectRetiredEditorAssets; exports.createAutosaveController = createAutosaveController; exports.createDocumentToolProvider = createDocumentToolProvider; exports.createEditorAssetRegistry = _chunkSJ2CPTQCcjs.createEditorAssetRegistry; exports.createEditorAutosave = createEditorAutosave; exports.createEditorDocumentStore = createEditorDocumentStore; exports.createIndexedDbAdapter = createIndexedDbAdapter; exports.createLoadReport = _chunkSJ2CPTQCcjs.createLoadReport; exports.createLocalStorageAdapter = createLocalStorageAdapter; exports.createMemoryStorage = createMemoryStorage; exports.createOpfsAdapter = createOpfsAdapter; exports.createRecoveryPolicy = createRecoveryPolicy; exports.createRevisionTracker = createRevisionTracker; exports.defineEditorDocument = _chunkSJ2CPTQCcjs.defineEditorDocument; exports.exponentialBackoff = exponentialBackoff; exports.externalizeAssets = _chunkSJ2CPTQCcjs.externalizeAssets; exports.formatRecoveryMessage = formatRecoveryMessage; exports.hasLoadProblems = _chunkSJ2CPTQCcjs.hasLoadProblems; exports.internalizeAssets = _chunkSJ2CPTQCcjs.internalizeAssets; exports.isEditorDocumentFile = _chunkSJ2CPTQCcjs.isEditorDocumentFile; exports.isIndexedDbAvailable = isIndexedDbAvailable; exports.isInlineAsset = _chunkSJ2CPTQCcjs.isInlineAsset; exports.isLocalStorageAvailable = isLocalStorageAvailable; exports.isOpfsAvailable = isOpfsAvailable; exports.loadDocument = _chunkSJ2CPTQCcjs.loadDocument; exports.loadSections = _chunkSJ2CPTQCcjs.loadSections; exports.migrateDocument = _chunkSJ2CPTQCcjs.migrateDocument; exports.parseEditorDocument = _chunkSJ2CPTQCcjs.parseEditorDocument; exports.persistEditorAssets = _chunkSJ2CPTQCcjs.persistEditorAssets; exports.preloadEditorAssets = _chunkSJ2CPTQCcjs.preloadEditorAssets; exports.recoveryMetaKey = recoveryMetaKey; exports.serializeEditorDocument = _chunkSJ2CPTQCcjs.serializeEditorDocument; exports.slugifyDocumentName = slugifyDocumentName; exports.toEditorDocumentFile = _chunkSJ2CPTQCcjs.toEditorDocumentFile; exports.tryCollectEditorAssetGarbage = _chunkSJ2CPTQCcjs.tryCollectEditorAssetGarbage; exports.withEditorAssets = _chunkSJ2CPTQCcjs.withEditorAssets; exports.withStorageFailures = withStorageFailures;
|
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
EDITOR_DOCUMENT_FORMAT,
|
|
4
4
|
EditorDocumentLoadError,
|
|
5
5
|
collectAssetRefs,
|
|
6
|
+
collectEditorAssetGarbage,
|
|
7
|
+
collectRetiredEditorAssets,
|
|
6
8
|
createEditorAssetRegistry,
|
|
7
9
|
createLoadReport,
|
|
8
10
|
defineEditorDocument,
|
|
@@ -19,8 +21,9 @@ import {
|
|
|
19
21
|
preloadEditorAssets,
|
|
20
22
|
serializeEditorDocument,
|
|
21
23
|
toEditorDocumentFile,
|
|
24
|
+
tryCollectEditorAssetGarbage,
|
|
22
25
|
withEditorAssets
|
|
23
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-XBQX76CT.js";
|
|
24
27
|
import "../chunk-HF5QZS7Z.js";
|
|
25
28
|
|
|
26
29
|
// src/persistence/recoveryPolicy.ts
|
|
@@ -136,7 +139,13 @@ function withStorageFailures(inner, failure) {
|
|
|
136
139
|
async remove(key) {
|
|
137
140
|
checkFailure(failure, "remove", key);
|
|
138
141
|
return inner.remove(key);
|
|
139
|
-
}
|
|
142
|
+
},
|
|
143
|
+
...inner.keys ? {
|
|
144
|
+
async keys(prefix = "") {
|
|
145
|
+
checkFailure(failure, "keys", prefix);
|
|
146
|
+
return inner.keys(prefix);
|
|
147
|
+
}
|
|
148
|
+
} : {}
|
|
140
149
|
};
|
|
141
150
|
}
|
|
142
151
|
function guarded(kind, isAvailable, failure, impl) {
|
|
@@ -160,7 +169,13 @@ function guarded(kind, isAvailable, failure, impl) {
|
|
|
160
169
|
async remove(key) {
|
|
161
170
|
check("remove", key);
|
|
162
171
|
return impl.remove(key);
|
|
163
|
-
}
|
|
172
|
+
},
|
|
173
|
+
...impl.keys ? {
|
|
174
|
+
async keys(prefix = "") {
|
|
175
|
+
check("keys", prefix);
|
|
176
|
+
return impl.keys(prefix);
|
|
177
|
+
}
|
|
178
|
+
} : {}
|
|
164
179
|
};
|
|
165
180
|
}
|
|
166
181
|
function createMemoryStorage(options = {}) {
|
|
@@ -174,6 +189,9 @@ function createMemoryStorage(options = {}) {
|
|
|
174
189
|
},
|
|
175
190
|
async remove(key) {
|
|
176
191
|
entries.delete(key);
|
|
192
|
+
},
|
|
193
|
+
async keys(prefix) {
|
|
194
|
+
return [...entries.keys()].filter((key) => key.startsWith(prefix));
|
|
177
195
|
}
|
|
178
196
|
});
|
|
179
197
|
return Object.assign(adapter, { entries });
|
|
@@ -200,7 +218,18 @@ function createLocalStorageAdapter(prefix = "", options = {}) {
|
|
|
200
218
|
},
|
|
201
219
|
async remove(key) {
|
|
202
220
|
storage.removeItem(prefix + key);
|
|
203
|
-
}
|
|
221
|
+
},
|
|
222
|
+
// Listing needs `length` and `key()` (real Web Storage has both).
|
|
223
|
+
...!storage || typeof storage.key === "function" && typeof storage.length === "number" ? {
|
|
224
|
+
async keys(start) {
|
|
225
|
+
const out = [];
|
|
226
|
+
for (let i = 0; i < storage.length; i++) {
|
|
227
|
+
const key = storage.key(i);
|
|
228
|
+
if (key !== null && key.startsWith(prefix + start)) out.push(key.slice(prefix.length));
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
232
|
+
} : {}
|
|
204
233
|
});
|
|
205
234
|
}
|
|
206
235
|
function detectIndexedDb() {
|
|
@@ -255,7 +284,13 @@ function createIndexedDbAdapter(name = "pixerate-editor", options = {}) {
|
|
|
255
284
|
return typeof value === "string" ? value : null;
|
|
256
285
|
},
|
|
257
286
|
set: (key, value) => write((store) => store.put(value, key)),
|
|
258
|
-
remove: (key) => write((store) => store.delete(key))
|
|
287
|
+
remove: (key) => write((store) => store.delete(key)),
|
|
288
|
+
async keys(prefix) {
|
|
289
|
+
const db = await open();
|
|
290
|
+
const tx = db.transaction(storeName, "readonly");
|
|
291
|
+
const all = await requestToPromise(tx.objectStore(storeName).getAllKeys());
|
|
292
|
+
return all.filter((key) => typeof key === "string" && key.startsWith(prefix));
|
|
293
|
+
}
|
|
259
294
|
});
|
|
260
295
|
}
|
|
261
296
|
function detectOpfsRoot() {
|
|
@@ -339,6 +374,30 @@ function createOpfsAdapter(dir = "pixerate-editor", options = {}) {
|
|
|
339
374
|
for (const name of [`${base}.ptr`, base, `${base}.a`, `${base}.b`, `${base}.tmp`]) {
|
|
340
375
|
await removeFile(d, name);
|
|
341
376
|
}
|
|
377
|
+
},
|
|
378
|
+
async keys(prefix) {
|
|
379
|
+
const d = await directory();
|
|
380
|
+
if (typeof d.keys !== "function") throw new StorageUnavailableError("opfs (directory listing)");
|
|
381
|
+
const names = /* @__PURE__ */ new Set();
|
|
382
|
+
for await (const name of d.keys()) names.add(name);
|
|
383
|
+
const keys = /* @__PURE__ */ new Set();
|
|
384
|
+
for (const name of names) {
|
|
385
|
+
if (name.endsWith(".tmp")) continue;
|
|
386
|
+
if (name.endsWith(".ptr")) keys.add(name.slice(0, -4));
|
|
387
|
+
else if (/\.[ab]$/.test(name) && names.has(`${name.slice(0, -2)}.ptr`)) continue;
|
|
388
|
+
else keys.add(name);
|
|
389
|
+
}
|
|
390
|
+
const out = [];
|
|
391
|
+
for (const name of keys) {
|
|
392
|
+
let key;
|
|
393
|
+
try {
|
|
394
|
+
key = decodeURIComponent(name);
|
|
395
|
+
} catch {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (key.startsWith(prefix)) out.push(key);
|
|
399
|
+
}
|
|
400
|
+
return out;
|
|
342
401
|
}
|
|
343
402
|
});
|
|
344
403
|
}
|
|
@@ -670,7 +729,8 @@ function createRevisionTracker(options = {}) {
|
|
|
670
729
|
|
|
671
730
|
// src/persistence/editorAutosave.ts
|
|
672
731
|
function createEditorAutosave(doc, options) {
|
|
673
|
-
const { dirty, key, ...rest } = options;
|
|
732
|
+
const { dirty, key, collectAssetGarbage, ...rest } = options;
|
|
733
|
+
const collect = () => collectRetiredEditorAssets(rest.storage, doc, collectAssetGarbage);
|
|
674
734
|
const revisions = createRevisionTracker({ history: doc, dirty });
|
|
675
735
|
const controller = createAutosaveController({
|
|
676
736
|
...rest,
|
|
@@ -696,10 +756,12 @@ function createEditorAutosave(doc, options) {
|
|
|
696
756
|
async discard() {
|
|
697
757
|
await controller.discardRecovery();
|
|
698
758
|
controller.ready();
|
|
759
|
+
await collect();
|
|
699
760
|
},
|
|
700
761
|
async markClean() {
|
|
701
762
|
revisions.markSaved();
|
|
702
763
|
await controller.markClean();
|
|
764
|
+
await collect();
|
|
703
765
|
},
|
|
704
766
|
destroy() {
|
|
705
767
|
unsubscribe();
|
|
@@ -720,6 +782,7 @@ function createEditorDocumentStore(options) {
|
|
|
720
782
|
const now = options.now ?? Date.now;
|
|
721
783
|
const indexKey = `${ns}:index`;
|
|
722
784
|
const fileKey = (key) => `${ns}:file:${key}`;
|
|
785
|
+
const gc = options.collectAssetGarbage ?? true;
|
|
723
786
|
let index = null;
|
|
724
787
|
let queue = Promise.resolve();
|
|
725
788
|
const run = (task) => {
|
|
@@ -764,8 +827,7 @@ function createEditorDocumentStore(options) {
|
|
|
764
827
|
savedAt: new Date(now()).toISOString()
|
|
765
828
|
};
|
|
766
829
|
const text = JSON.stringify(toEditorDocumentFile(doc, trimmed));
|
|
767
|
-
|
|
768
|
-
await storage.set(fileKey(entry.key), text);
|
|
830
|
+
await (doc.assets ? withEditorAssets(storage, doc.assets) : storage).set(fileKey(entry.key), text);
|
|
769
831
|
await saveIndex([...(await loadIndex()).filter((item) => item.key !== entry.key), entry]);
|
|
770
832
|
return entry;
|
|
771
833
|
}),
|
|
@@ -792,8 +854,10 @@ function createEditorDocumentStore(options) {
|
|
|
792
854
|
if (!items.some((item) => item.key === key)) return false;
|
|
793
855
|
await saveIndex(items.filter((item) => item.key !== key));
|
|
794
856
|
await storage.remove(fileKey(key));
|
|
857
|
+
if (gc) await tryCollectEditorAssetGarbage(storage, gc === true ? {} : gc);
|
|
795
858
|
return true;
|
|
796
|
-
})
|
|
859
|
+
}),
|
|
860
|
+
...storage.keys ? { collectAssetGarbage: (gcOptions) => run(() => collectEditorAssetGarbage(storage, gcOptions)) } : {}
|
|
797
861
|
};
|
|
798
862
|
}
|
|
799
863
|
|
|
@@ -939,13 +1003,66 @@ function createDocumentToolProvider(doc, options = {}) {
|
|
|
939
1003
|
}
|
|
940
1004
|
}
|
|
941
1005
|
];
|
|
1006
|
+
const LIST_LIMIT = 100;
|
|
1007
|
+
const assetTools = (s) => [
|
|
1008
|
+
{
|
|
1009
|
+
name: `find_unreferenced_${kind}_assets`,
|
|
1010
|
+
title: `Find unreferenced ${noun} assets`,
|
|
1011
|
+
description: `Finds the stored image data (layer pixels, masks and other assets of every image and paint document in the host's storage) that no saved document or autosave snapshot refers to any more, without deleting anything. The open ${noun}'s own assets always count as used. Returns { assets, referenced, kept, unreferenced, unreferencedChars, refs, more }: unreferenced is how many could be deleted, unreferencedChars the characters of data they hold, refs up to ${LIST_LIMIT} of their references for delete_unreferenced_${kind}_assets, and more how many were left out of refs.`,
|
|
1012
|
+
inputSchema: object({}),
|
|
1013
|
+
annotations: { title: `Find unreferenced ${noun} assets`, ...READ },
|
|
1014
|
+
handler: async () => {
|
|
1015
|
+
const report = await s.collectAssetGarbage({ dryRun: true, keep: [doc.getDocument()] });
|
|
1016
|
+
return {
|
|
1017
|
+
assets: report.assets,
|
|
1018
|
+
referenced: report.referenced,
|
|
1019
|
+
kept: report.kept,
|
|
1020
|
+
unreferenced: report.unreferenced.length,
|
|
1021
|
+
unreferencedChars: report.unreferencedChars,
|
|
1022
|
+
refs: report.unreferenced.slice(0, LIST_LIMIT),
|
|
1023
|
+
more: Math.max(0, report.unreferenced.length - LIST_LIMIT)
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
name: `delete_unreferenced_${kind}_assets`,
|
|
1029
|
+
title: `Delete unreferenced ${noun} assets`,
|
|
1030
|
+
description: `Deletes stored image data that no saved document or autosave snapshot refers to, to free browser storage. Saved documents and the open ${noun} are never affected, and a write in progress is waited for. Call find_unreferenced_${kind}_assets first to see what would go, and do it when the user asks to free space or clean up storage. It cannot be undone, but nothing refers to the deleted data. Returns { removed, freedChars, kept, remaining }: how many assets were deleted, the characters of data freed, how many unreferenced ones were kept because a write was using them, and how many assets are left.`,
|
|
1031
|
+
inputSchema: object({
|
|
1032
|
+
refs: {
|
|
1033
|
+
type: "array",
|
|
1034
|
+
items: { type: "string", pattern: "^pixerate-asset:" },
|
|
1035
|
+
minItems: 1,
|
|
1036
|
+
uniqueItems: true,
|
|
1037
|
+
description: `Delete only these references from find_unreferenced_${kind}_assets (refs). Default: every unreferenced asset. If any is no longer unreferenced, nothing is deleted and the error names it.`
|
|
1038
|
+
}
|
|
1039
|
+
}),
|
|
1040
|
+
annotations: { title: `Delete unreferenced ${noun} assets`, ...OVERWRITE },
|
|
1041
|
+
handler: async ({ refs }) => {
|
|
1042
|
+
writable();
|
|
1043
|
+
const report = await s.collectAssetGarbage({ keep: [doc.getDocument()], ...refs ? { only: refs } : {} });
|
|
1044
|
+
return {
|
|
1045
|
+
removed: report.removed.length,
|
|
1046
|
+
freedChars: report.freedChars,
|
|
1047
|
+
kept: report.kept,
|
|
1048
|
+
remaining: report.assets - report.removed.length
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
];
|
|
942
1053
|
return {
|
|
943
1054
|
id: options.id ?? `${kind}-document`,
|
|
944
1055
|
...options.namespace !== void 0 ? { namespace: options.namespace } : {},
|
|
945
1056
|
title: options.title ?? `${noun.charAt(0).toUpperCase()}${noun.slice(1)} document`,
|
|
946
1057
|
description: `Get, load${options.history === false ? "" : ", undo, redo"}${store ? ", save and open" : ""} the whole ${noun}.`,
|
|
947
1058
|
...options.skill ? { skill: options.skill } : {},
|
|
948
|
-
tools: [
|
|
1059
|
+
tools: [
|
|
1060
|
+
get,
|
|
1061
|
+
load,
|
|
1062
|
+
...options.history === false ? [] : [undo, redo],
|
|
1063
|
+
...store ? storeTools(store) : [],
|
|
1064
|
+
...store?.collectAssetGarbage && doc.assets ? assetTools(store) : []
|
|
1065
|
+
]
|
|
949
1066
|
};
|
|
950
1067
|
}
|
|
951
1068
|
export {
|
|
@@ -955,6 +1072,8 @@ export {
|
|
|
955
1072
|
InjectedStorageError,
|
|
956
1073
|
StorageUnavailableError,
|
|
957
1074
|
collectAssetRefs,
|
|
1075
|
+
collectEditorAssetGarbage,
|
|
1076
|
+
collectRetiredEditorAssets,
|
|
958
1077
|
createAutosaveController,
|
|
959
1078
|
createDocumentToolProvider,
|
|
960
1079
|
createEditorAssetRegistry,
|
|
@@ -988,6 +1107,7 @@ export {
|
|
|
988
1107
|
serializeEditorDocument,
|
|
989
1108
|
slugifyDocumentName,
|
|
990
1109
|
toEditorDocumentFile,
|
|
1110
|
+
tryCollectEditorAssetGarbage,
|
|
991
1111
|
withEditorAssets,
|
|
992
1112
|
withStorageFailures
|
|
993
1113
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* construction, so SSR and old browsers are safe) and accepts an injectable
|
|
6
6
|
* failure mode for tests.
|
|
7
7
|
*/
|
|
8
|
-
export type StorageOperation = "get" | "set" | "remove";
|
|
8
|
+
export type StorageOperation = "get" | "set" | "remove" | "keys";
|
|
9
9
|
/**
|
|
10
10
|
* Injected failure: return `true` (or an `Error`) to make that operation fail.
|
|
11
11
|
* Example: `failure: (op) => op === "set"` simulates a full disk.
|
|
@@ -19,6 +19,12 @@ export interface StorageAdapter {
|
|
|
19
19
|
get(key: string): Promise<string | null>;
|
|
20
20
|
set(key: string, value: string): Promise<void>;
|
|
21
21
|
remove(key: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Every stored key that starts with `prefix` (default: every key), in no particular order.
|
|
24
|
+
* Optional: custom adapters may leave it out, but asset garbage collection
|
|
25
|
+
* (`collectEditorAssetGarbage`) needs it. All built-in adapters have it.
|
|
26
|
+
*/
|
|
27
|
+
keys?(prefix?: string): Promise<string[]>;
|
|
22
28
|
}
|
|
23
29
|
export interface StorageAdapterOptions {
|
|
24
30
|
failure?: StorageFailureMode;
|
|
@@ -44,6 +50,10 @@ export interface WebStorageLike {
|
|
|
44
50
|
getItem(key: string): string | null;
|
|
45
51
|
setItem(key: string, value: string): void;
|
|
46
52
|
removeItem(key: string): void;
|
|
53
|
+
/** Number of stored items (with `key`, lets the adapter list its keys). */
|
|
54
|
+
readonly length?: number;
|
|
55
|
+
/** The key at `index` (with `length`, lets the adapter list its keys). */
|
|
56
|
+
key?(index: number): string | null;
|
|
47
57
|
}
|
|
48
58
|
export interface LocalStorageAdapterOptions extends StorageAdapterOptions {
|
|
49
59
|
/** Storage to use (default `globalThis.localStorage`, e.g. pass `sessionStorage`). */
|
|
@@ -84,6 +94,8 @@ export interface OpfsDirectoryLike {
|
|
|
84
94
|
create?: boolean;
|
|
85
95
|
}): Promise<OpfsFileLike>;
|
|
86
96
|
removeEntry(name: string): Promise<void>;
|
|
97
|
+
/** Entry names (real directory handles have it); lets the adapter list its keys. */
|
|
98
|
+
keys?(): AsyncIterable<string>;
|
|
87
99
|
}
|
|
88
100
|
export interface OpfsFileLike {
|
|
89
101
|
getFile(): Promise<{
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* construction, so SSR and old browsers are safe) and accepts an injectable
|
|
6
6
|
* failure mode for tests.
|
|
7
7
|
*/
|
|
8
|
-
export type StorageOperation = "get" | "set" | "remove";
|
|
8
|
+
export type StorageOperation = "get" | "set" | "remove" | "keys";
|
|
9
9
|
/**
|
|
10
10
|
* Injected failure: return `true` (or an `Error`) to make that operation fail.
|
|
11
11
|
* Example: `failure: (op) => op === "set"` simulates a full disk.
|
|
@@ -19,6 +19,12 @@ export interface StorageAdapter {
|
|
|
19
19
|
get(key: string): Promise<string | null>;
|
|
20
20
|
set(key: string, value: string): Promise<void>;
|
|
21
21
|
remove(key: string): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Every stored key that starts with `prefix` (default: every key), in no particular order.
|
|
24
|
+
* Optional: custom adapters may leave it out, but asset garbage collection
|
|
25
|
+
* (`collectEditorAssetGarbage`) needs it. All built-in adapters have it.
|
|
26
|
+
*/
|
|
27
|
+
keys?(prefix?: string): Promise<string[]>;
|
|
22
28
|
}
|
|
23
29
|
export interface StorageAdapterOptions {
|
|
24
30
|
failure?: StorageFailureMode;
|
|
@@ -44,6 +50,10 @@ export interface WebStorageLike {
|
|
|
44
50
|
getItem(key: string): string | null;
|
|
45
51
|
setItem(key: string, value: string): void;
|
|
46
52
|
removeItem(key: string): void;
|
|
53
|
+
/** Number of stored items (with `key`, lets the adapter list its keys). */
|
|
54
|
+
readonly length?: number;
|
|
55
|
+
/** The key at `index` (with `length`, lets the adapter list its keys). */
|
|
56
|
+
key?(index: number): string | null;
|
|
47
57
|
}
|
|
48
58
|
export interface LocalStorageAdapterOptions extends StorageAdapterOptions {
|
|
49
59
|
/** Storage to use (default `globalThis.localStorage`, e.g. pass `sessionStorage`). */
|
|
@@ -84,6 +94,8 @@ export interface OpfsDirectoryLike {
|
|
|
84
94
|
create?: boolean;
|
|
85
95
|
}): Promise<OpfsFileLike>;
|
|
86
96
|
removeEntry(name: string): Promise<void>;
|
|
97
|
+
/** Entry names (real directory handles have it); lets the adapter list its keys. */
|
|
98
|
+
keys?(): AsyncIterable<string>;
|
|
87
99
|
}
|
|
88
100
|
export interface OpfsFileLike {
|
|
89
101
|
getFile(): Promise<{
|