fabricjs-document-engine 1.0.2 → 1.2.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/README.md +220 -11
- package/README.zh-CN.md +220 -11
- package/dist/assets/asset-manifest.d.cts +3 -0
- package/dist/assets/asset-manifest.d.ts +3 -0
- package/dist/assets/asset-pipeline.cjs +31 -17
- package/dist/assets/asset-pipeline.d.cts +5 -1
- package/dist/assets/asset-pipeline.d.ts +5 -1
- package/dist/assets/asset-pipeline.js +31 -17
- package/dist/assets/font-check.cjs +9 -1
- package/dist/assets/font-check.js +9 -1
- package/dist/assets/image-check.cjs +89 -6
- package/dist/assets/image-check.d.cts +17 -0
- package/dist/assets/image-check.d.ts +17 -0
- package/dist/assets/image-check.js +89 -7
- package/dist/commands/clipboard.cjs +141 -0
- package/dist/commands/clipboard.d.cts +47 -0
- package/dist/commands/clipboard.d.ts +47 -0
- package/dist/commands/clipboard.js +140 -0
- package/dist/commands/layers.cjs +97 -0
- package/dist/commands/layers.d.cts +39 -0
- package/dist/commands/layers.d.ts +39 -0
- package/dist/commands/layers.js +92 -0
- package/dist/document/document-format.d.cts +15 -0
- package/dist/document/document-format.d.ts +15 -0
- package/dist/document/validate-document.cjs +8 -0
- package/dist/document/validate-document.js +8 -0
- package/dist/engine/create-document-engine.cjs +196 -31
- package/dist/engine/create-document-engine.d.cts +44 -7
- package/dist/engine/create-document-engine.d.ts +44 -7
- package/dist/engine/create-document-engine.js +197 -32
- package/dist/engine/errors.d.cts +1 -1
- package/dist/engine/errors.d.ts +1 -1
- package/dist/export/batch-render.cjs +172 -0
- package/dist/export/batch-render.d.cts +43 -0
- package/dist/export/batch-render.d.ts +43 -0
- package/dist/export/batch-render.js +171 -0
- package/dist/export/export-options.cjs +21 -1
- package/dist/export/export-options.d.cts +29 -0
- package/dist/export/export-options.d.ts +29 -0
- package/dist/export/export-options.js +21 -2
- package/dist/export/preflight-export.cjs +11 -2
- package/dist/export/preflight-export.d.cts +1 -1
- package/dist/export/preflight-export.d.ts +1 -1
- package/dist/export/preflight-export.js +11 -2
- package/dist/export/render-export.cjs +57 -4
- package/dist/export/render-export.js +57 -5
- package/dist/export/svg/clip-masks.cjs +39 -0
- package/dist/export/svg/clip-masks.js +37 -0
- package/dist/export/svg/embed-assets.cjs +250 -0
- package/dist/export/svg/embed-assets.js +250 -0
- package/dist/export/svg/overrides.cjs +32 -0
- package/dist/export/svg/overrides.js +32 -0
- package/dist/export/svg/raster-object.cjs +82 -0
- package/dist/export/svg/raster-object.js +81 -0
- package/dist/export/svg/text-decorations.cjs +108 -0
- package/dist/export/svg/text-decorations.js +107 -0
- package/dist/export/svg/text-on-path.cjs +242 -0
- package/dist/export/svg/text-on-path.js +240 -0
- package/dist/fabric/fabric-adapter.cjs +49 -6
- package/dist/fabric/fabric-adapter.js +49 -6
- package/dist/fabric/object-registry.cjs +1 -1
- package/dist/fabric/object-registry.js +1 -1
- package/dist/fabric/page-state.cjs +69 -0
- package/dist/fabric/page-state.js +64 -0
- package/dist/history/apply-state.cjs +19 -2
- package/dist/history/apply-state.js +19 -2
- package/dist/history/create-history.cjs +53 -13
- package/dist/history/create-history.d.cts +8 -0
- package/dist/history/create-history.d.ts +8 -0
- package/dist/history/create-history.js +53 -13
- package/dist/history/snapshot.cjs +10 -1
- package/dist/history/snapshot.js +10 -2
- package/dist/import/svg-import.cjs +256 -0
- package/dist/import/svg-import.d.cts +50 -0
- package/dist/import/svg-import.d.ts +50 -0
- package/dist/import/svg-import.js +256 -0
- package/dist/index.cjs +12 -0
- package/dist/index.d.cts +10 -5
- package/dist/index.d.ts +10 -5
- package/dist/index.js +4 -1
- package/dist/migrations/migrate-document.cjs +2 -1
- package/dist/migrations/migrate-document.js +2 -1
- package/dist/pdf/export-pdf.cjs +381 -0
- package/dist/pdf/export-pdf.d.cts +73 -0
- package/dist/pdf/export-pdf.d.ts +73 -0
- package/dist/pdf/export-pdf.js +381 -0
- package/dist/pdf/fonts.cjs +115 -0
- package/dist/pdf/fonts.d.cts +12 -0
- package/dist/pdf/fonts.d.ts +12 -0
- package/dist/pdf/fonts.js +112 -0
- package/dist/pdf/page-layout.cjs +79 -0
- package/dist/pdf/page-layout.d.cts +51 -0
- package/dist/pdf/page-layout.d.ts +51 -0
- package/dist/pdf/page-layout.js +76 -0
- package/dist/pdf.cjs +7 -0
- package/dist/pdf.d.cts +4 -0
- package/dist/pdf.d.ts +4 -0
- package/dist/pdf.js +3 -0
- package/dist/react/use-layers.cjs +53 -0
- package/dist/react/use-layers.d.cts +10 -0
- package/dist/react/use-layers.d.ts +10 -0
- package/dist/react/use-layers.js +53 -0
- package/dist/react.cjs +2 -0
- package/dist/react.d.cts +3 -1
- package/dist/react.d.ts +3 -1
- package/dist/react.js +2 -1
- package/dist/recovery/recovery-controller.cjs +148 -29
- package/dist/recovery/recovery-controller.d.cts +8 -0
- package/dist/recovery/recovery-controller.d.ts +8 -0
- package/dist/recovery/recovery-controller.js +148 -29
- package/dist/security/content-limits.cjs +43 -3
- package/dist/security/content-limits.d.cts +8 -0
- package/dist/security/content-limits.d.ts +8 -0
- package/dist/security/content-limits.js +39 -4
- package/dist/util/concurrency.cjs +41 -0
- package/dist/util/concurrency.js +40 -0
- package/package.json +30 -3
- package/schema/document-v1.schema.json +5 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_asset_manifest = require("../assets/asset-manifest.cjs");
|
|
2
|
+
const require_page_state = require("../fabric/page-state.cjs");
|
|
2
3
|
const require_errors = require("./errors.cjs");
|
|
3
4
|
const require_content_limits = require("../security/content-limits.cjs");
|
|
4
5
|
const require_image_check = require("../assets/image-check.cjs");
|
|
@@ -9,6 +10,8 @@ const require_object_ids = require("../fabric/object-ids.cjs");
|
|
|
9
10
|
const require_walk_objects = require("../fabric/walk-objects.cjs");
|
|
10
11
|
const require_preflight_export = require("../export/preflight-export.cjs");
|
|
11
12
|
const require_render_export = require("../export/render-export.cjs");
|
|
13
|
+
const require_embed_assets = require("../export/svg/embed-assets.cjs");
|
|
14
|
+
const require_svg_import = require("../import/svg-import.cjs");
|
|
12
15
|
require("../document/document-format.cjs");
|
|
13
16
|
const require_ids = require("../document/ids.cjs");
|
|
14
17
|
const require_create_document = require("../document/create-document.cjs");
|
|
@@ -36,11 +39,21 @@ function createDocumentEngine(options) {
|
|
|
36
39
|
const registry = require_object_registry.createObjectRegistry(options.customObjects);
|
|
37
40
|
const events = require_event_emitter.createEventEmitter();
|
|
38
41
|
const objectsById = /* @__PURE__ */ new Map();
|
|
39
|
-
const assetOptions =
|
|
42
|
+
const assetOptions = {
|
|
43
|
+
...options.assets,
|
|
44
|
+
maxImagePixels: options.limits?.maxImagePixels ?? 67108864
|
|
45
|
+
};
|
|
40
46
|
const uploadedUrls = /* @__PURE__ */ new Map();
|
|
41
47
|
let documentInfo = require_create_document.createDocumentInfo(options.document);
|
|
42
48
|
let activeLoad = null;
|
|
43
49
|
let destroyed = false;
|
|
50
|
+
let mutations = 0;
|
|
51
|
+
let session = 0;
|
|
52
|
+
/** Throws when async work started under `startedIn` would now change another document. */
|
|
53
|
+
function ensureSameSession(startedIn, what) {
|
|
54
|
+
ensureUsable();
|
|
55
|
+
if (session !== startedIn) throw new require_errors.DocumentEngineError("DOCUMENT_CHANGED", `${what} was dropped because another document was opened while it ran`);
|
|
56
|
+
}
|
|
44
57
|
function ensureUsable() {
|
|
45
58
|
if (destroyed) throw new require_errors.DocumentEngineError("ENGINE_DESTROYED", "This document engine was destroyed");
|
|
46
59
|
}
|
|
@@ -65,21 +78,41 @@ function createDocumentEngine(options) {
|
|
|
65
78
|
objectsById.clear();
|
|
66
79
|
canvas.getObjects().forEach(indexObjectTree);
|
|
67
80
|
}
|
|
68
|
-
const handleObjectAdded = ({ target }) =>
|
|
69
|
-
|
|
81
|
+
const handleObjectAdded = ({ target }) => {
|
|
82
|
+
mutations += 1;
|
|
83
|
+
indexObjectTree(target);
|
|
84
|
+
};
|
|
85
|
+
const handleObjectRemoved = ({ target }) => {
|
|
86
|
+
mutations += 1;
|
|
87
|
+
removeObjectTreeFromIndex(target);
|
|
88
|
+
};
|
|
89
|
+
const countMutation = () => {
|
|
90
|
+
mutations += 1;
|
|
91
|
+
};
|
|
70
92
|
canvas.on("object:added", handleObjectAdded);
|
|
71
93
|
canvas.on("object:removed", handleObjectRemoved);
|
|
94
|
+
canvas.on("object:modified", countMutation);
|
|
95
|
+
canvas.on("text:changed", countMutation);
|
|
72
96
|
rebuildIndex();
|
|
73
97
|
const history = require_create_history.createHistory({
|
|
74
98
|
canvas,
|
|
75
99
|
limit: options.history?.limit,
|
|
76
100
|
maxBytes: options.history?.maxBytes,
|
|
77
|
-
|
|
101
|
+
serializeState: () => {
|
|
78
102
|
rebuildIndex();
|
|
79
|
-
|
|
103
|
+
const serialized = require_fabric_adapter.serializeCanvas(canvas, registry.propertiesToInclude());
|
|
104
|
+
return {
|
|
105
|
+
objects: serialized.objects,
|
|
106
|
+
page: {
|
|
107
|
+
width: canvas.getWidth(),
|
|
108
|
+
height: canvas.getHeight(),
|
|
109
|
+
...require_page_state.pickPageFields(serialized)
|
|
110
|
+
}
|
|
111
|
+
};
|
|
80
112
|
},
|
|
81
113
|
onChange: (state) => events.emit("history:change", state),
|
|
82
|
-
onContentChange: () => noteContentChange()
|
|
114
|
+
onContentChange: () => noteContentChange(),
|
|
115
|
+
onLiveChange: () => noteContentChange()
|
|
83
116
|
});
|
|
84
117
|
const saving = require_save_controller.createSaveController({
|
|
85
118
|
getStorage: () => requireStorage(),
|
|
@@ -114,6 +147,7 @@ function createDocumentEngine(options) {
|
|
|
114
147
|
}
|
|
115
148
|
}) : void 0;
|
|
116
149
|
function noteContentChange() {
|
|
150
|
+
mutations += 1;
|
|
117
151
|
saving.noteContentChange();
|
|
118
152
|
recovery?.schedule();
|
|
119
153
|
}
|
|
@@ -136,15 +170,21 @@ function createDocumentEngine(options) {
|
|
|
136
170
|
height: canvas.getHeight()
|
|
137
171
|
},
|
|
138
172
|
objects: serialized.objects,
|
|
139
|
-
assets: require_asset_manifest.buildAssetManifest(
|
|
173
|
+
assets: require_asset_manifest.buildAssetManifest(require_page_state.documentObjects({
|
|
174
|
+
objects: serialized.objects,
|
|
175
|
+
canvas: serialized
|
|
176
|
+
})),
|
|
140
177
|
metadata: { ...documentInfo.metadata }
|
|
141
178
|
};
|
|
142
179
|
if (serialized.version !== void 0) document.fabricVersion = serialized.version;
|
|
143
|
-
|
|
180
|
+
Object.assign(document.canvas, require_page_state.pickPageFields(serialized));
|
|
144
181
|
return document;
|
|
145
182
|
}
|
|
146
|
-
function toLoadError(error,
|
|
147
|
-
if (signal.aborted)
|
|
183
|
+
function toLoadError(error, controller) {
|
|
184
|
+
if (controller.signal.aborted) {
|
|
185
|
+
const reason = cancelledByCaller.has(controller) ? "Loading was cancelled" : destroyed ? "Loading stopped because the engine was destroyed" : "Loading stopped because a newer load started";
|
|
186
|
+
return new require_errors.DocumentEngineError("LOAD_ABORTED", reason, { cause: error });
|
|
187
|
+
}
|
|
148
188
|
if (require_errors.isDocumentEngineError(error)) return error;
|
|
149
189
|
const reason = error instanceof Error ? error.message : String(error);
|
|
150
190
|
return new require_errors.DocumentEngineError("LOAD_FAILED", `Fabric could not load the document: ${reason}`, { cause: error });
|
|
@@ -156,7 +196,7 @@ function createDocumentEngine(options) {
|
|
|
156
196
|
throw new require_errors.DocumentEngineError(issues[0].code, `The document is not valid: ${summary}`, { issues });
|
|
157
197
|
}
|
|
158
198
|
const document = input;
|
|
159
|
-
const unknownTypes = registry.findUnknownTypes(require_walk_objects.collectSerializedTypes(document
|
|
199
|
+
const unknownTypes = registry.findUnknownTypes(require_walk_objects.collectSerializedTypes(require_page_state.documentObjects(document)));
|
|
160
200
|
if (unknownTypes.length > 0) throw new require_errors.DocumentEngineError("UNKNOWN_OBJECT_TYPE", `The document uses object types that are not registered: ${unknownTypes.join(", ")}. Register them with customObjects or engine.registerObject before loading.`, { unknownTypes });
|
|
161
201
|
return document;
|
|
162
202
|
}
|
|
@@ -167,19 +207,50 @@ function createDocumentEngine(options) {
|
|
|
167
207
|
if (!storage || discardUnsavedChanges || !saving.state().isDirty) return;
|
|
168
208
|
throw new require_errors.DocumentEngineError("UNSAVED_CHANGES", "The current document has unsaved changes. Save first, or pass { discardUnsavedChanges: true } to replace it anyway.");
|
|
169
209
|
}
|
|
170
|
-
|
|
210
|
+
const cancelledByCaller = /* @__PURE__ */ new WeakSet();
|
|
211
|
+
const unlinkCallerSignal = /* @__PURE__ */ new WeakMap();
|
|
212
|
+
function startLoad(callerSignal) {
|
|
171
213
|
activeLoad?.abort();
|
|
172
214
|
const controller = new AbortController();
|
|
173
215
|
activeLoad = controller;
|
|
216
|
+
if (callerSignal) {
|
|
217
|
+
const cancel = () => {
|
|
218
|
+
cancelledByCaller.add(controller);
|
|
219
|
+
controller.abort();
|
|
220
|
+
};
|
|
221
|
+
if (callerSignal.aborted) cancel();
|
|
222
|
+
else {
|
|
223
|
+
callerSignal.addEventListener("abort", cancel, { once: true });
|
|
224
|
+
unlinkCallerSignal.set(controller, () => callerSignal.removeEventListener("abort", cancel));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
174
227
|
return controller;
|
|
175
228
|
}
|
|
229
|
+
function finishLoad(controller) {
|
|
230
|
+
unlinkCallerSignal.get(controller)?.();
|
|
231
|
+
unlinkCallerSignal.delete(controller);
|
|
232
|
+
}
|
|
176
233
|
async function loadMigratedDocument(input, loadOptions, importDetails, startedLoad) {
|
|
177
234
|
ensureUsable();
|
|
178
235
|
protectUnsavedChanges(loadOptions.discardUnsavedChanges);
|
|
179
|
-
const controller = startedLoad ?? startLoad();
|
|
236
|
+
const controller = startedLoad ?? startLoad(loadOptions.signal);
|
|
237
|
+
const mutationsAtStart = mutations;
|
|
180
238
|
const documentId = describeDocumentId(input);
|
|
239
|
+
const report = (stage, done, total) => {
|
|
240
|
+
if (controller.signal.aborted) return;
|
|
241
|
+
const progress = {
|
|
242
|
+
documentId,
|
|
243
|
+
stage,
|
|
244
|
+
done,
|
|
245
|
+
total
|
|
246
|
+
};
|
|
247
|
+
loadOptions.onProgress?.(progress);
|
|
248
|
+
events.emit("load:progress", progress);
|
|
249
|
+
};
|
|
181
250
|
events.emit("load:start", { documentId });
|
|
182
251
|
try {
|
|
252
|
+
if (controller.signal.aborted) throw new Error("aborted");
|
|
253
|
+
report("prepare", 0, 1);
|
|
183
254
|
await recovery?.markLoadStarted(documentId);
|
|
184
255
|
if (controller.signal.aborted) throw new Error("aborted");
|
|
185
256
|
const { document: migrated, migratedFrom } = require_migrate_document.migrateDocument(require_content_limits.secureDocument(input, options.limits), {
|
|
@@ -188,13 +259,21 @@ function createDocumentEngine(options) {
|
|
|
188
259
|
...importDetails
|
|
189
260
|
});
|
|
190
261
|
const checked = checkDocument(migrated);
|
|
191
|
-
|
|
262
|
+
report("prepare", 1, 1);
|
|
263
|
+
const { document, warnings } = await require_asset_pipeline.prepareAssetsForLoad(checked, assetOptions, controller.signal, options.limits?.isAllowedUrl, (done, total) => report("images", done, total));
|
|
192
264
|
if (controller.signal.aborted) throw new Error("aborted");
|
|
193
265
|
await history.withoutRecording(() => require_fabric_adapter.loadIntoCanvas(canvas, {
|
|
194
266
|
version: document.fabricVersion,
|
|
195
|
-
|
|
267
|
+
...require_page_state.pickPageFields(document.canvas),
|
|
196
268
|
objects: document.objects
|
|
197
|
-
},
|
|
269
|
+
}, {
|
|
270
|
+
signal: controller.signal,
|
|
271
|
+
onObjects: (done, total) => report("objects", done, total),
|
|
272
|
+
beforeSwap: () => {
|
|
273
|
+
if (mutations !== mutationsAtStart && !loadOptions.discardUnsavedChanges) throw new require_errors.DocumentEngineError("LOAD_CONFLICT", "The canvas was edited while the document loaded, so the load was stopped to keep the edits. Pass { discardUnsavedChanges: true } to replace them.");
|
|
274
|
+
session += 1;
|
|
275
|
+
}
|
|
276
|
+
}));
|
|
198
277
|
if (controller.signal.aborted) throw new Error("aborted");
|
|
199
278
|
if (loadOptions.restoreCanvasSize ?? true) canvas.setDimensions({
|
|
200
279
|
width: document.canvas.width,
|
|
@@ -211,6 +290,7 @@ function createDocumentEngine(options) {
|
|
|
211
290
|
recovery?.cancel();
|
|
212
291
|
saving.startSession(document.revision ?? 0);
|
|
213
292
|
canvas.requestRenderAll();
|
|
293
|
+
report("done", 1, 1);
|
|
214
294
|
if (warnings.length > 0) events.emit("assets:warning", { warnings });
|
|
215
295
|
events.emit("document:change", { documentId: document.id });
|
|
216
296
|
events.emit("load:success", {
|
|
@@ -220,10 +300,11 @@ function createDocumentEngine(options) {
|
|
|
220
300
|
});
|
|
221
301
|
return document;
|
|
222
302
|
} catch (error) {
|
|
223
|
-
const engineError = toLoadError(error, controller
|
|
303
|
+
const engineError = toLoadError(error, controller);
|
|
224
304
|
events.emit("load:error", { error: engineError });
|
|
225
305
|
throw engineError;
|
|
226
306
|
} finally {
|
|
307
|
+
finishLoad(controller);
|
|
227
308
|
if (activeLoad === controller) {
|
|
228
309
|
activeLoad = null;
|
|
229
310
|
await recovery?.markLoadFinished();
|
|
@@ -244,6 +325,43 @@ function createDocumentEngine(options) {
|
|
|
244
325
|
metadata
|
|
245
326
|
});
|
|
246
327
|
}
|
|
328
|
+
function setPage(changes, label = "Change page") {
|
|
329
|
+
ensureUsable();
|
|
330
|
+
const problem = require_content_limits.canvasSizeProblem(changes.width ?? canvas.getWidth(), changes.height ?? canvas.getHeight(), options.limits);
|
|
331
|
+
if (problem) throw new require_errors.DocumentEngineError("UNSAFE_DOCUMENT", `The page was not resized: it would be ${problem}`);
|
|
332
|
+
history.transaction(label, () => {
|
|
333
|
+
const width = changes.width ?? canvas.getWidth();
|
|
334
|
+
const height = changes.height ?? canvas.getHeight();
|
|
335
|
+
if (width !== canvas.getWidth() || height !== canvas.getHeight()) canvas.setDimensions({
|
|
336
|
+
width,
|
|
337
|
+
height
|
|
338
|
+
});
|
|
339
|
+
const live = {};
|
|
340
|
+
if (changes.background !== void 0) live.backgroundColor = changes.background ?? "";
|
|
341
|
+
if (changes.backgroundImage !== void 0) live.backgroundImage = changes.backgroundImage ?? void 0;
|
|
342
|
+
if (changes.overlay !== void 0) live.overlayColor = changes.overlay ?? "";
|
|
343
|
+
if (changes.overlayImage !== void 0) live.overlayImage = changes.overlayImage ?? void 0;
|
|
344
|
+
if (changes.clipPath !== void 0) live.clipPath = changes.clipPath ?? void 0;
|
|
345
|
+
canvas.set(live);
|
|
346
|
+
});
|
|
347
|
+
canvas.requestRenderAll();
|
|
348
|
+
}
|
|
349
|
+
async function importSvg(svg, importOptions = {}) {
|
|
350
|
+
ensureUsable();
|
|
351
|
+
const startedIn = session;
|
|
352
|
+
const result = await require_svg_import.readSvg(svg, importOptions, options.limits);
|
|
353
|
+
try {
|
|
354
|
+
ensureSameSession(startedIn, "The SVG import");
|
|
355
|
+
} catch (error) {
|
|
356
|
+
result.objects.forEach((object) => object.dispose?.());
|
|
357
|
+
throw error;
|
|
358
|
+
}
|
|
359
|
+
if (result.objects.length > 0) {
|
|
360
|
+
history.transaction("Import SVG", () => canvas.add(...result.objects));
|
|
361
|
+
canvas.requestRenderAll();
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
247
365
|
let savesSinceAutomaticVersion = 0;
|
|
248
366
|
let lastVersionTime = 0;
|
|
249
367
|
function nextVersionTimestamp() {
|
|
@@ -322,15 +440,17 @@ function createDocumentEngine(options) {
|
|
|
322
440
|
ensureUsable();
|
|
323
441
|
const source = requireStorage();
|
|
324
442
|
protectUnsavedChanges(loadOptions?.discardUnsavedChanges);
|
|
325
|
-
const controller = startLoad();
|
|
443
|
+
const controller = startLoad(loadOptions?.signal);
|
|
326
444
|
let stored;
|
|
327
445
|
try {
|
|
446
|
+
if (controller.signal.aborted) throw new Error("aborted");
|
|
328
447
|
stored = await source.loadDocument(documentId);
|
|
329
448
|
if (controller.signal.aborted) throw new Error("aborted");
|
|
330
449
|
} catch (error) {
|
|
450
|
+
finishLoad(controller);
|
|
331
451
|
if (activeLoad === controller) activeLoad = null;
|
|
332
452
|
if (controller.signal.aborted) {
|
|
333
|
-
const aborted = toLoadError(error, controller
|
|
453
|
+
const aborted = toLoadError(error, controller);
|
|
334
454
|
events.emit("load:error", { error: aborted });
|
|
335
455
|
throw aborted;
|
|
336
456
|
}
|
|
@@ -355,6 +475,7 @@ function createDocumentEngine(options) {
|
|
|
355
475
|
protectUnsavedChanges(newOptions.discardUnsavedChanges);
|
|
356
476
|
activeLoad?.abort();
|
|
357
477
|
history.withoutRecording(() => canvas.clear());
|
|
478
|
+
session += 1;
|
|
358
479
|
objectsById.clear();
|
|
359
480
|
documentInfo = require_create_document.createDocumentInfo(newOptions);
|
|
360
481
|
uploadedUrls.clear();
|
|
@@ -381,10 +502,12 @@ function createDocumentEngine(options) {
|
|
|
381
502
|
if (isReplaceableImage(object) && require_image_check.isSameUrl(object.getSrc(), oldUrl)) images.push(object);
|
|
382
503
|
});
|
|
383
504
|
if (images.length === 0) return 0;
|
|
505
|
+
const startedIn = session;
|
|
384
506
|
await history.transaction("Replace image", () => Promise.all(images.map(async (image) => {
|
|
385
507
|
const displayedWidth = image.width * image.scaleX;
|
|
386
508
|
const displayedHeight = image.height * image.scaleY;
|
|
387
509
|
await image.setSrc(newUrl, { crossOrigin: image.crossOrigin ?? null });
|
|
510
|
+
ensureSameSession(startedIn, "Replacing the image");
|
|
388
511
|
image.set({
|
|
389
512
|
scaleX: displayedWidth / image.width,
|
|
390
513
|
scaleY: displayedHeight / image.height
|
|
@@ -400,8 +523,8 @@ function createDocumentEngine(options) {
|
|
|
400
523
|
}
|
|
401
524
|
async function checkBeforeExport(exportOptions) {
|
|
402
525
|
ensureUsable();
|
|
403
|
-
const { format } = require_export_options.normalizeExportOptions(exportOptions);
|
|
404
|
-
return require_preflight_export.preflightExport(canvas, format, format === "json" ? [] : fontsOnCanvas(), assetOptions);
|
|
526
|
+
const { format, svg } = require_export_options.normalizeExportOptions(exportOptions);
|
|
527
|
+
return require_preflight_export.preflightExport(canvas, format, format === "json" ? [] : fontsOnCanvas(), assetOptions, svg);
|
|
405
528
|
}
|
|
406
529
|
function describeProblems(check) {
|
|
407
530
|
return check.problems.map((problem) => problem.message).join("; ");
|
|
@@ -430,24 +553,58 @@ function createDocumentEngine(options) {
|
|
|
430
553
|
if (!check.ok) throw new require_errors.DocumentEngineError("EXPORT_BLOCKED", `The export cannot run: ${describeProblems(check)}`, { problems: check.problems });
|
|
431
554
|
const format = settings.format;
|
|
432
555
|
try {
|
|
556
|
+
const warnings = [...check.warnings];
|
|
433
557
|
const rendered = require_render_export.withExportView(canvas, settings.background, format, () => {
|
|
434
558
|
const area = require_export_area.resolveExportArea(canvas, settings.area, settings.padding);
|
|
559
|
+
if (format !== "svg") {
|
|
560
|
+
const problem = require_content_limits.canvasSizeProblem(area.width * settings.scale, area.height * settings.scale, options.limits);
|
|
561
|
+
if (problem) {
|
|
562
|
+
const message = `The export would be ${problem}. Lower the scale or export a smaller area.`;
|
|
563
|
+
throw new require_errors.DocumentEngineError("EXPORT_BLOCKED", message, { problems: [{
|
|
564
|
+
code: "TOO_LARGE",
|
|
565
|
+
message,
|
|
566
|
+
objectIds: []
|
|
567
|
+
}] });
|
|
568
|
+
}
|
|
569
|
+
}
|
|
435
570
|
return {
|
|
436
571
|
area,
|
|
437
|
-
content: format === "svg" ? Promise.resolve(
|
|
572
|
+
content: format === "svg" ? Promise.resolve(require_render_export.renderSvg(canvas, area, settings.scale, settings.svg, options.limits)) : require_render_export.renderRaster(canvas, area, format, settings.scale, settings.quality)
|
|
438
573
|
};
|
|
439
574
|
});
|
|
440
|
-
|
|
575
|
+
let blob;
|
|
576
|
+
if (format === "svg") {
|
|
577
|
+
const { svg, rasterizedObjectIds } = await rendered.content;
|
|
578
|
+
if (rasterizedObjectIds.length > 0) warnings.push({
|
|
579
|
+
code: "CLIP_PATH_RASTERIZED",
|
|
580
|
+
message: "Objects whose clip path has a clip path of its own were drawn as pictures: Fabric cannot write nested clip paths as SVG.",
|
|
581
|
+
objectIds: rasterizedObjectIds
|
|
582
|
+
});
|
|
583
|
+
const embedded = await require_embed_assets.embedSvgAssets(svg, canvas, settings.svg, settings.signal, options.limits?.isAllowedUrl ?? require_content_limits.isSafeImageUrl);
|
|
584
|
+
stopIfCancelled();
|
|
585
|
+
if (settings.svg.embedImages === "require" && embedded.problems.length > 0) {
|
|
586
|
+
const problems = embedded.warnings.filter((warning) => warning.code === "IMAGE_NOT_EMBEDDED").map((warning) => ({
|
|
587
|
+
code: "IMAGE_NOT_EMBEDDED",
|
|
588
|
+
message: warning.message,
|
|
589
|
+
url: warning.url,
|
|
590
|
+
objectIds: warning.objectIds
|
|
591
|
+
}));
|
|
592
|
+
throw new require_errors.DocumentEngineError("EXPORT_BLOCKED", `The export cannot run: ${embedded.problems.join("; ")}`, { problems });
|
|
593
|
+
}
|
|
594
|
+
warnings.push(...embedded.warnings);
|
|
595
|
+
blob = new Blob([embedded.svg], { type: require_render_export.mimeTypes.svg });
|
|
596
|
+
} else blob = await rendered.content;
|
|
441
597
|
return {
|
|
442
598
|
format,
|
|
443
599
|
mimeType: blob.type || require_render_export.mimeTypes[format],
|
|
444
600
|
blob,
|
|
445
601
|
width: Math.round(rendered.area.width * settings.scale),
|
|
446
602
|
height: Math.round(rendered.area.height * settings.scale),
|
|
447
|
-
warnings
|
|
603
|
+
warnings
|
|
448
604
|
};
|
|
449
605
|
} catch (error) {
|
|
450
606
|
if (require_errors.isDocumentEngineError(error)) throw error;
|
|
607
|
+
if (settings.signal?.aborted) throw new require_errors.DocumentEngineError("EXPORT_ABORTED", "The export was cancelled", { cause: error });
|
|
451
608
|
if (error?.name === "SecurityError") {
|
|
452
609
|
const problem = {
|
|
453
610
|
code: "CROSS_ORIGIN_IMAGE",
|
|
@@ -485,12 +642,14 @@ function createDocumentEngine(options) {
|
|
|
485
642
|
if (!recovery) throw new require_errors.DocumentEngineError("RECOVERY_MISSING", "Pass recovery: { store } to createDocumentEngine to use recovery");
|
|
486
643
|
return recovery;
|
|
487
644
|
}
|
|
488
|
-
async function restoreRecovery(documentId,
|
|
645
|
+
async function restoreRecovery(documentId, restoreOptions = {}) {
|
|
489
646
|
const source = requireRecovery();
|
|
490
647
|
const id = documentId ?? documentInfo.id;
|
|
491
|
-
const
|
|
648
|
+
const { sessionId: branch, ...loadOptions } = restoreOptions;
|
|
649
|
+
const record = await source.read(id, branch);
|
|
492
650
|
if (!record) throw new require_errors.DocumentEngineError("RECOVERY_NOT_FOUND", `There is no recovery copy for document "${id}"`);
|
|
493
651
|
const loaded = await loadDocument(await require_recovery_controller.restoreRecordedFiles(record), loadOptions);
|
|
652
|
+
source.adopt(loaded.id, record.sessionId);
|
|
494
653
|
noteContentChange();
|
|
495
654
|
events.emit("recovery:restored", { document: loaded });
|
|
496
655
|
return loaded;
|
|
@@ -511,12 +670,15 @@ function createDocumentEngine(options) {
|
|
|
511
670
|
const { disposed } = canvas;
|
|
512
671
|
if (recovery && saving.state().isDirty && !disposed) recovery.writeNow();
|
|
513
672
|
destroyed = true;
|
|
673
|
+
session += 1;
|
|
514
674
|
activeLoad?.abort();
|
|
515
675
|
history.destroy();
|
|
516
676
|
saving.destroy();
|
|
517
677
|
recovery?.destroy();
|
|
518
678
|
canvas.off("object:added", handleObjectAdded);
|
|
519
679
|
canvas.off("object:removed", handleObjectRemoved);
|
|
680
|
+
canvas.off("object:modified", countMutation);
|
|
681
|
+
canvas.off("text:changed", countMutation);
|
|
520
682
|
objectsById.clear();
|
|
521
683
|
events.clear();
|
|
522
684
|
}
|
|
@@ -524,7 +686,8 @@ function createDocumentEngine(options) {
|
|
|
524
686
|
canvas,
|
|
525
687
|
getDocumentInfo: () => ({
|
|
526
688
|
...documentInfo,
|
|
527
|
-
metadata: { ...documentInfo.metadata }
|
|
689
|
+
metadata: { ...documentInfo.metadata },
|
|
690
|
+
session
|
|
528
691
|
}),
|
|
529
692
|
updateMetadata(changes) {
|
|
530
693
|
ensureUsable();
|
|
@@ -542,6 +705,7 @@ function createDocumentEngine(options) {
|
|
|
542
705
|
loadDocument,
|
|
543
706
|
load,
|
|
544
707
|
importFabricJson,
|
|
708
|
+
importSvg,
|
|
545
709
|
createVersion: (name) => storeVersion(name ?? `Version ${(/* @__PURE__ */ new Date()).toLocaleString()}`, "named"),
|
|
546
710
|
listVersions: async (documentId) => requireVersions().listVersions(documentId ?? documentInfo.id),
|
|
547
711
|
restoreVersion,
|
|
@@ -564,14 +728,15 @@ function createDocumentEngine(options) {
|
|
|
564
728
|
preflightExport: checkBeforeExport,
|
|
565
729
|
flushRecovery: async () => requireRecovery().flush(),
|
|
566
730
|
getRecoverableDocuments: async () => requireRecovery().list(),
|
|
567
|
-
getRecovery: async (documentId) => requireRecovery().read(documentId ?? documentInfo.id),
|
|
731
|
+
getRecovery: async (documentId, sessionId) => requireRecovery().read(documentId ?? documentInfo.id, sessionId),
|
|
568
732
|
restoreRecovery,
|
|
569
|
-
discardRecovery: async (documentId) => requireRecovery().
|
|
733
|
+
discardRecovery: async (documentId, sessionId) => requireRecovery().discard(documentId ?? documentInfo.id, sessionId),
|
|
570
734
|
getInterruptedLoad: async () => requireRecovery().interruptedLoad(),
|
|
571
|
-
transaction(label, work) {
|
|
735
|
+
transaction(label, work, transactionOptions) {
|
|
572
736
|
ensureUsable();
|
|
573
|
-
return history.transaction(label, work);
|
|
737
|
+
return history.transaction(label, work, transactionOptions);
|
|
574
738
|
},
|
|
739
|
+
setPage,
|
|
575
740
|
commit(label = "Edit") {
|
|
576
741
|
ensureUsable();
|
|
577
742
|
return history.commit(label);
|
|
@@ -3,10 +3,11 @@ import { AssetManifest } from "../assets/asset-manifest.cjs";
|
|
|
3
3
|
import { AssetOptions, AssetReport, AssetWarning } from "../assets/asset-pipeline.cjs";
|
|
4
4
|
import { ExportFormat, ExportOptions } from "../export/export-options.cjs";
|
|
5
5
|
import { ExportPreflight } from "../export/preflight-export.cjs";
|
|
6
|
+
import { ContentLimits } from "../security/content-limits.cjs";
|
|
7
|
+
import { SvgImportOptions, SvgImportResult } from "../import/svg-import.cjs";
|
|
6
8
|
import { NewDocumentOptions } from "../document/create-document.cjs";
|
|
7
9
|
import { CustomObjectDefinition } from "../fabric/object-registry.cjs";
|
|
8
|
-
import { HistoryOptions, HistoryState } from "../history/create-history.cjs";
|
|
9
|
-
import { ContentLimits } from "../security/content-limits.cjs";
|
|
10
|
+
import { HistoryOptions, HistoryState, TransactionOptions } from "../history/create-history.cjs";
|
|
10
11
|
import { VersionOptions, VersionSummary } from "../versions/document-version.cjs";
|
|
11
12
|
import { InterruptedLoad, RecoveryOptions, RecoveryRecord } from "../recovery/recovery-controller.cjs";
|
|
12
13
|
import { AutosaveOptions } from "../save/autosave-scheduler.cjs";
|
|
@@ -15,7 +16,7 @@ import { DocumentEngineError } from "./errors.cjs";
|
|
|
15
16
|
import { DocumentStorage } from "../storage/storage-contract.cjs";
|
|
16
17
|
import { SaveOptions, SaveRetryEvent, SaveState } from "../save/save-controller.cjs";
|
|
17
18
|
import { Unsubscribe } from "./event-emitter.cjs";
|
|
18
|
-
import { FabricObject, StaticCanvas } from "fabric";
|
|
19
|
+
import { FabricObject, StaticCanvas, TFiller } from "fabric";
|
|
19
20
|
//#region src/engine/create-document-engine.d.ts
|
|
20
21
|
export interface DocumentEngineOptions {
|
|
21
22
|
canvas: StaticCanvas;
|
|
@@ -33,6 +34,39 @@ export interface DocumentEngineOptions {
|
|
|
33
34
|
export interface LoadOptions {
|
|
34
35
|
restoreCanvasSize?: boolean;
|
|
35
36
|
discardUnsavedChanges?: boolean;
|
|
37
|
+
/** Cancels the load. The canvas keeps its current content and the load rejects with `LOAD_ABORTED`. */
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
/** Called as the load moves through its stages. The `load:progress` event carries the same values. */
|
|
40
|
+
onProgress?: (progress: LoadProgress) => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Where a load is. `prepare` covers validation and migration, `images` counts
|
|
44
|
+
* images checked, `objects` counts top-level objects created, and `done` comes
|
|
45
|
+
* once the canvas shows the document.
|
|
46
|
+
*/
|
|
47
|
+
export type LoadStage = "prepare" | "images" | "objects" | "done";
|
|
48
|
+
export interface LoadProgress {
|
|
49
|
+
documentId: string | undefined;
|
|
50
|
+
stage: LoadStage;
|
|
51
|
+
done: number;
|
|
52
|
+
total: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Page settings for `setPage`. Images and the mask are live Fabric objects;
|
|
56
|
+
* `null` removes one.
|
|
57
|
+
*/
|
|
58
|
+
export interface PageChanges {
|
|
59
|
+
width?: number;
|
|
60
|
+
height?: number;
|
|
61
|
+
background?: string | TFiller | null;
|
|
62
|
+
backgroundImage?: FabricObject | null;
|
|
63
|
+
overlay?: string | TFiller | null;
|
|
64
|
+
overlayImage?: FabricObject | null;
|
|
65
|
+
clipPath?: FabricObject | null;
|
|
66
|
+
}
|
|
67
|
+
export interface RestoreRecoveryOptions extends LoadOptions {
|
|
68
|
+
/** Restore the copy one session wrote. By default the newest copy of the document is used. */
|
|
69
|
+
sessionId?: string;
|
|
36
70
|
}
|
|
37
71
|
export interface NewDocumentRequest extends NewDocumentOptions {
|
|
38
72
|
discardUnsavedChanges?: boolean;
|
|
@@ -54,6 +88,7 @@ export interface DocumentEngineEvents {
|
|
|
54
88
|
"load:start": {
|
|
55
89
|
documentId: string | undefined;
|
|
56
90
|
};
|
|
91
|
+
"load:progress": LoadProgress;
|
|
57
92
|
"document:change": {
|
|
58
93
|
documentId: string;
|
|
59
94
|
};
|
|
@@ -120,6 +155,7 @@ export interface DocumentEngine {
|
|
|
120
155
|
loadDocument(document: unknown, options?: LoadOptions): Promise<FabricDocument>;
|
|
121
156
|
load(documentId: string, options?: LoadOptions): Promise<FabricDocument>;
|
|
122
157
|
importFabricJson(json: string | Record<string, unknown>, options?: ImportOptions): Promise<FabricDocument>;
|
|
158
|
+
importSvg(svg: string, options?: SvgImportOptions): Promise<SvgImportResult>;
|
|
123
159
|
createVersion(name?: string): Promise<VersionSummary>;
|
|
124
160
|
listVersions(documentId?: string): Promise<VersionSummary[]>;
|
|
125
161
|
restoreVersion(versionId: string): Promise<FabricDocument>;
|
|
@@ -128,7 +164,8 @@ export interface DocumentEngine {
|
|
|
128
164
|
isDirty(): boolean;
|
|
129
165
|
getSaveState(): SaveState;
|
|
130
166
|
registerObject(definition: CustomObjectDefinition): void;
|
|
131
|
-
transaction<Result>(label: string, work: () => Result): Result;
|
|
167
|
+
transaction<Result>(label: string, work: () => Result, options?: TransactionOptions): Result;
|
|
168
|
+
setPage(changes: PageChanges, label?: string): void;
|
|
132
169
|
commit(label?: string): boolean;
|
|
133
170
|
undo(): Promise<boolean>;
|
|
134
171
|
redo(): Promise<boolean>;
|
|
@@ -147,9 +184,9 @@ export interface DocumentEngine {
|
|
|
147
184
|
preflightExport(options: ExportOptions): Promise<ExportPreflight>;
|
|
148
185
|
flushRecovery(): Promise<void>;
|
|
149
186
|
getRecoverableDocuments(): Promise<RecoveryRecord[]>;
|
|
150
|
-
getRecovery(documentId?: string): Promise<RecoveryRecord | undefined>;
|
|
151
|
-
restoreRecovery(documentId?: string, options?:
|
|
152
|
-
discardRecovery(documentId?: string): Promise<void>;
|
|
187
|
+
getRecovery(documentId?: string, sessionId?: string): Promise<RecoveryRecord | undefined>;
|
|
188
|
+
restoreRecovery(documentId?: string, options?: RestoreRecoveryOptions): Promise<FabricDocument>;
|
|
189
|
+
discardRecovery(documentId?: string, sessionId?: string): Promise<void>;
|
|
153
190
|
getInterruptedLoad(): Promise<InterruptedLoad | undefined>;
|
|
154
191
|
on<Name extends keyof DocumentEngineEvents>(name: Name, handler: (payload: DocumentEngineEvents[Name]) => void): Unsubscribe;
|
|
155
192
|
destroy(): void;
|
|
@@ -3,10 +3,11 @@ import { AssetManifest } from "../assets/asset-manifest.js";
|
|
|
3
3
|
import { AssetOptions, AssetReport, AssetWarning } from "../assets/asset-pipeline.js";
|
|
4
4
|
import { ExportFormat, ExportOptions } from "../export/export-options.js";
|
|
5
5
|
import { ExportPreflight } from "../export/preflight-export.js";
|
|
6
|
+
import { ContentLimits } from "../security/content-limits.js";
|
|
7
|
+
import { SvgImportOptions, SvgImportResult } from "../import/svg-import.js";
|
|
6
8
|
import { NewDocumentOptions } from "../document/create-document.js";
|
|
7
9
|
import { CustomObjectDefinition } from "../fabric/object-registry.js";
|
|
8
|
-
import { HistoryOptions, HistoryState } from "../history/create-history.js";
|
|
9
|
-
import { ContentLimits } from "../security/content-limits.js";
|
|
10
|
+
import { HistoryOptions, HistoryState, TransactionOptions } from "../history/create-history.js";
|
|
10
11
|
import { VersionOptions, VersionSummary } from "../versions/document-version.js";
|
|
11
12
|
import { InterruptedLoad, RecoveryOptions, RecoveryRecord } from "../recovery/recovery-controller.js";
|
|
12
13
|
import { AutosaveOptions } from "../save/autosave-scheduler.js";
|
|
@@ -15,7 +16,7 @@ import { DocumentEngineError } from "./errors.js";
|
|
|
15
16
|
import { DocumentStorage } from "../storage/storage-contract.js";
|
|
16
17
|
import { SaveOptions, SaveRetryEvent, SaveState } from "../save/save-controller.js";
|
|
17
18
|
import { Unsubscribe } from "./event-emitter.js";
|
|
18
|
-
import { FabricObject, StaticCanvas } from "fabric";
|
|
19
|
+
import { FabricObject, StaticCanvas, TFiller } from "fabric";
|
|
19
20
|
//#region src/engine/create-document-engine.d.ts
|
|
20
21
|
export interface DocumentEngineOptions {
|
|
21
22
|
canvas: StaticCanvas;
|
|
@@ -33,6 +34,39 @@ export interface DocumentEngineOptions {
|
|
|
33
34
|
export interface LoadOptions {
|
|
34
35
|
restoreCanvasSize?: boolean;
|
|
35
36
|
discardUnsavedChanges?: boolean;
|
|
37
|
+
/** Cancels the load. The canvas keeps its current content and the load rejects with `LOAD_ABORTED`. */
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
/** Called as the load moves through its stages. The `load:progress` event carries the same values. */
|
|
40
|
+
onProgress?: (progress: LoadProgress) => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Where a load is. `prepare` covers validation and migration, `images` counts
|
|
44
|
+
* images checked, `objects` counts top-level objects created, and `done` comes
|
|
45
|
+
* once the canvas shows the document.
|
|
46
|
+
*/
|
|
47
|
+
export type LoadStage = "prepare" | "images" | "objects" | "done";
|
|
48
|
+
export interface LoadProgress {
|
|
49
|
+
documentId: string | undefined;
|
|
50
|
+
stage: LoadStage;
|
|
51
|
+
done: number;
|
|
52
|
+
total: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Page settings for `setPage`. Images and the mask are live Fabric objects;
|
|
56
|
+
* `null` removes one.
|
|
57
|
+
*/
|
|
58
|
+
export interface PageChanges {
|
|
59
|
+
width?: number;
|
|
60
|
+
height?: number;
|
|
61
|
+
background?: string | TFiller | null;
|
|
62
|
+
backgroundImage?: FabricObject | null;
|
|
63
|
+
overlay?: string | TFiller | null;
|
|
64
|
+
overlayImage?: FabricObject | null;
|
|
65
|
+
clipPath?: FabricObject | null;
|
|
66
|
+
}
|
|
67
|
+
export interface RestoreRecoveryOptions extends LoadOptions {
|
|
68
|
+
/** Restore the copy one session wrote. By default the newest copy of the document is used. */
|
|
69
|
+
sessionId?: string;
|
|
36
70
|
}
|
|
37
71
|
export interface NewDocumentRequest extends NewDocumentOptions {
|
|
38
72
|
discardUnsavedChanges?: boolean;
|
|
@@ -54,6 +88,7 @@ export interface DocumentEngineEvents {
|
|
|
54
88
|
"load:start": {
|
|
55
89
|
documentId: string | undefined;
|
|
56
90
|
};
|
|
91
|
+
"load:progress": LoadProgress;
|
|
57
92
|
"document:change": {
|
|
58
93
|
documentId: string;
|
|
59
94
|
};
|
|
@@ -120,6 +155,7 @@ export interface DocumentEngine {
|
|
|
120
155
|
loadDocument(document: unknown, options?: LoadOptions): Promise<FabricDocument>;
|
|
121
156
|
load(documentId: string, options?: LoadOptions): Promise<FabricDocument>;
|
|
122
157
|
importFabricJson(json: string | Record<string, unknown>, options?: ImportOptions): Promise<FabricDocument>;
|
|
158
|
+
importSvg(svg: string, options?: SvgImportOptions): Promise<SvgImportResult>;
|
|
123
159
|
createVersion(name?: string): Promise<VersionSummary>;
|
|
124
160
|
listVersions(documentId?: string): Promise<VersionSummary[]>;
|
|
125
161
|
restoreVersion(versionId: string): Promise<FabricDocument>;
|
|
@@ -128,7 +164,8 @@ export interface DocumentEngine {
|
|
|
128
164
|
isDirty(): boolean;
|
|
129
165
|
getSaveState(): SaveState;
|
|
130
166
|
registerObject(definition: CustomObjectDefinition): void;
|
|
131
|
-
transaction<Result>(label: string, work: () => Result): Result;
|
|
167
|
+
transaction<Result>(label: string, work: () => Result, options?: TransactionOptions): Result;
|
|
168
|
+
setPage(changes: PageChanges, label?: string): void;
|
|
132
169
|
commit(label?: string): boolean;
|
|
133
170
|
undo(): Promise<boolean>;
|
|
134
171
|
redo(): Promise<boolean>;
|
|
@@ -147,9 +184,9 @@ export interface DocumentEngine {
|
|
|
147
184
|
preflightExport(options: ExportOptions): Promise<ExportPreflight>;
|
|
148
185
|
flushRecovery(): Promise<void>;
|
|
149
186
|
getRecoverableDocuments(): Promise<RecoveryRecord[]>;
|
|
150
|
-
getRecovery(documentId?: string): Promise<RecoveryRecord | undefined>;
|
|
151
|
-
restoreRecovery(documentId?: string, options?:
|
|
152
|
-
discardRecovery(documentId?: string): Promise<void>;
|
|
187
|
+
getRecovery(documentId?: string, sessionId?: string): Promise<RecoveryRecord | undefined>;
|
|
188
|
+
restoreRecovery(documentId?: string, options?: RestoreRecoveryOptions): Promise<FabricDocument>;
|
|
189
|
+
discardRecovery(documentId?: string, sessionId?: string): Promise<void>;
|
|
153
190
|
getInterruptedLoad(): Promise<InterruptedLoad | undefined>;
|
|
154
191
|
on<Name extends keyof DocumentEngineEvents>(name: Name, handler: (payload: DocumentEngineEvents[Name]) => void): Unsubscribe;
|
|
155
192
|
destroy(): void;
|