fabricjs-document-engine 0.4.0 → 0.6.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 +87 -3
- package/dist/engine/create-document-engine.cjs +199 -6
- package/dist/engine/create-document-engine.d.cts +42 -0
- package/dist/engine/create-document-engine.d.ts +42 -0
- package/dist/engine/create-document-engine.js +199 -6
- package/dist/engine/errors.cjs +2 -0
- package/dist/engine/errors.d.cts +6 -1
- package/dist/engine/errors.d.ts +6 -1
- package/dist/engine/errors.js +2 -0
- package/dist/export/download-export.cjs +14 -0
- package/dist/export/download-export.d.cts +7 -0
- package/dist/export/download-export.d.ts +7 -0
- package/dist/export/download-export.js +14 -0
- package/dist/export/export-area.cjs +46 -0
- package/dist/export/export-area.js +46 -0
- package/dist/export/export-options.cjs +47 -0
- package/dist/export/export-options.d.cts +20 -0
- package/dist/export/export-options.d.ts +20 -0
- package/dist/export/export-options.js +46 -0
- package/dist/export/preflight-export.cjs +105 -0
- package/dist/export/preflight-export.d.cts +17 -0
- package/dist/export/preflight-export.d.ts +17 -0
- package/dist/export/preflight-export.js +105 -0
- package/dist/export/render-export.cjs +69 -0
- package/dist/export/render-export.js +65 -0
- package/dist/history/apply-state.cjs +1 -1
- package/dist/history/apply-state.js +1 -1
- package/dist/index.cjs +5 -0
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +3 -1
- package/dist/migrations/migrate-document.cjs +65 -0
- package/dist/migrations/migrate-document.d.cts +15 -0
- package/dist/migrations/migrate-document.d.ts +15 -0
- package/dist/migrations/migrate-document.js +63 -0
- package/dist/storage/key-value-storage.cjs +27 -1
- package/dist/storage/key-value-storage.d.cts +2 -1
- package/dist/storage/key-value-storage.d.ts +2 -1
- package/dist/storage/key-value-storage.js +27 -1
- package/dist/storage.d.cts +2 -1
- package/dist/storage.d.ts +2 -1
- package/dist/versions/document-version.cjs +20 -0
- package/dist/versions/document-version.d.cts +25 -0
- package/dist/versions/document-version.d.ts +25 -0
- package/dist/versions/document-version.js +17 -0
- package/package.json +1 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const require_font_check = require("../assets/font-check.cjs");
|
|
2
|
+
const require_image_check = require("../assets/image-check.cjs");
|
|
3
|
+
const require_export_options = require("./export-options.cjs");
|
|
4
|
+
const require_object_ids = require("../fabric/object-ids.cjs");
|
|
5
|
+
const require_walk_objects = require("../fabric/walk-objects.cjs");
|
|
6
|
+
//#region src/export/preflight-export.ts
|
|
7
|
+
function imageElementsOf(object) {
|
|
8
|
+
const elements = [];
|
|
9
|
+
const withElement = object;
|
|
10
|
+
if (typeof withElement.getElement === "function") elements.push(withElement.getElement());
|
|
11
|
+
for (const paint of [object.fill, object.stroke]) {
|
|
12
|
+
const source = paint?.source;
|
|
13
|
+
if (source !== void 0) elements.push(source);
|
|
14
|
+
}
|
|
15
|
+
return elements.filter((element) => typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement);
|
|
16
|
+
}
|
|
17
|
+
function collectImageUses(objects) {
|
|
18
|
+
const uses = /* @__PURE__ */ new Map();
|
|
19
|
+
const pending = objects.map((object) => ({
|
|
20
|
+
object,
|
|
21
|
+
ownerId: require_object_ids.readObjectId(object)
|
|
22
|
+
}));
|
|
23
|
+
while (pending.length > 0) {
|
|
24
|
+
const { object, ownerId } = pending.pop();
|
|
25
|
+
const id = require_object_ids.readObjectId(object) ?? ownerId;
|
|
26
|
+
for (const element of imageElementsOf(object)) {
|
|
27
|
+
const url = element.currentSrc || element.src;
|
|
28
|
+
const use = uses.get(url) ?? {
|
|
29
|
+
element,
|
|
30
|
+
objectIds: /* @__PURE__ */ new Set()
|
|
31
|
+
};
|
|
32
|
+
if (id !== void 0) use.objectIds.add(id);
|
|
33
|
+
uses.set(url, use);
|
|
34
|
+
}
|
|
35
|
+
require_walk_objects.childrenOf(object).forEach((child) => pending.push({
|
|
36
|
+
object: child,
|
|
37
|
+
ownerId: id
|
|
38
|
+
}));
|
|
39
|
+
if (object.clipPath) pending.push({
|
|
40
|
+
object: object.clipPath,
|
|
41
|
+
ownerId: id
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return uses;
|
|
45
|
+
}
|
|
46
|
+
function isBroken(element) {
|
|
47
|
+
return element.complete && element.naturalWidth === 0;
|
|
48
|
+
}
|
|
49
|
+
function shortUrl(url) {
|
|
50
|
+
return url.length > 80 ? `${url.slice(0, 77)}...` : url;
|
|
51
|
+
}
|
|
52
|
+
async function preflightExport(canvas, format, fonts, assetOptions) {
|
|
53
|
+
const problems = [];
|
|
54
|
+
const warnings = [];
|
|
55
|
+
if (format === "json") return {
|
|
56
|
+
ok: true,
|
|
57
|
+
problems,
|
|
58
|
+
warnings
|
|
59
|
+
};
|
|
60
|
+
for (const [url, use] of collectImageUses(canvas.getObjects())) {
|
|
61
|
+
const objectIds = [...use.objectIds];
|
|
62
|
+
if (isBroken(use.element)) problems.push({
|
|
63
|
+
code: "MISSING_IMAGE",
|
|
64
|
+
message: `The image ${shortUrl(url)} did not load`,
|
|
65
|
+
url,
|
|
66
|
+
objectIds
|
|
67
|
+
});
|
|
68
|
+
else if (require_export_options.isRasterFormat(format) && require_image_check.isCrossOriginUrl(url) && !use.element.crossOrigin) problems.push({
|
|
69
|
+
code: "CROSS_ORIGIN_IMAGE",
|
|
70
|
+
message: `The image ${shortUrl(url)} comes from another site without crossOrigin, so the browser blocks exporting it. Load it with crossOrigin: 'anonymous' from a server that allows CORS.`,
|
|
71
|
+
url,
|
|
72
|
+
objectIds
|
|
73
|
+
});
|
|
74
|
+
else if (format === "svg" && !require_image_check.isPortableUrl(url)) warnings.push({
|
|
75
|
+
code: "ASSET_NOT_PORTABLE",
|
|
76
|
+
message: `The SVG links to ${shortUrl(url)}, which only exists in this tab`,
|
|
77
|
+
url,
|
|
78
|
+
objectIds
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const fontSet = typeof document === "undefined" ? void 0 : document.fonts;
|
|
82
|
+
if (fontSet) await fontSet.ready;
|
|
83
|
+
for (const font of await require_font_check.findUnavailableFonts(fonts, assetOptions.loadFont)) {
|
|
84
|
+
const message = `The font "${font.family}" (${font.weight}, ${font.style}) is not available`;
|
|
85
|
+
if (assetOptions.requireFonts) problems.push({
|
|
86
|
+
code: "MISSING_FONT",
|
|
87
|
+
message,
|
|
88
|
+
family: font.family,
|
|
89
|
+
objectIds: font.objectIds
|
|
90
|
+
});
|
|
91
|
+
else warnings.push({
|
|
92
|
+
code: "FONT_UNAVAILABLE",
|
|
93
|
+
message: `${message}, text will use a fallback font`,
|
|
94
|
+
family: font.family,
|
|
95
|
+
objectIds: font.objectIds
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
ok: problems.length === 0,
|
|
100
|
+
problems,
|
|
101
|
+
warnings
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
exports.preflightExport = preflightExport;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AssetWarning } from "../assets/asset-pipeline.cjs";
|
|
2
|
+
import { StaticCanvas } from "fabric";
|
|
3
|
+
//#region src/export/preflight-export.d.ts
|
|
4
|
+
export type ExportProblemCode = "MISSING_IMAGE" | "CROSS_ORIGIN_IMAGE" | "MISSING_FONT";
|
|
5
|
+
export interface ExportProblem {
|
|
6
|
+
code: ExportProblemCode;
|
|
7
|
+
message: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
family?: string;
|
|
10
|
+
objectIds: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface ExportPreflight {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
problems: ExportProblem[];
|
|
15
|
+
warnings: AssetWarning[];
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AssetWarning } from "../assets/asset-pipeline.js";
|
|
2
|
+
import { StaticCanvas } from "fabric";
|
|
3
|
+
//#region src/export/preflight-export.d.ts
|
|
4
|
+
export type ExportProblemCode = "MISSING_IMAGE" | "CROSS_ORIGIN_IMAGE" | "MISSING_FONT";
|
|
5
|
+
export interface ExportProblem {
|
|
6
|
+
code: ExportProblemCode;
|
|
7
|
+
message: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
family?: string;
|
|
10
|
+
objectIds: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface ExportPreflight {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
problems: ExportProblem[];
|
|
15
|
+
warnings: AssetWarning[];
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { findUnavailableFonts } from "../assets/font-check.js";
|
|
2
|
+
import { isCrossOriginUrl, isPortableUrl } from "../assets/image-check.js";
|
|
3
|
+
import { isRasterFormat } from "./export-options.js";
|
|
4
|
+
import { readObjectId } from "../fabric/object-ids.js";
|
|
5
|
+
import { childrenOf } from "../fabric/walk-objects.js";
|
|
6
|
+
//#region src/export/preflight-export.ts
|
|
7
|
+
function imageElementsOf(object) {
|
|
8
|
+
const elements = [];
|
|
9
|
+
const withElement = object;
|
|
10
|
+
if (typeof withElement.getElement === "function") elements.push(withElement.getElement());
|
|
11
|
+
for (const paint of [object.fill, object.stroke]) {
|
|
12
|
+
const source = paint?.source;
|
|
13
|
+
if (source !== void 0) elements.push(source);
|
|
14
|
+
}
|
|
15
|
+
return elements.filter((element) => typeof HTMLImageElement !== "undefined" && element instanceof HTMLImageElement);
|
|
16
|
+
}
|
|
17
|
+
function collectImageUses(objects) {
|
|
18
|
+
const uses = /* @__PURE__ */ new Map();
|
|
19
|
+
const pending = objects.map((object) => ({
|
|
20
|
+
object,
|
|
21
|
+
ownerId: readObjectId(object)
|
|
22
|
+
}));
|
|
23
|
+
while (pending.length > 0) {
|
|
24
|
+
const { object, ownerId } = pending.pop();
|
|
25
|
+
const id = readObjectId(object) ?? ownerId;
|
|
26
|
+
for (const element of imageElementsOf(object)) {
|
|
27
|
+
const url = element.currentSrc || element.src;
|
|
28
|
+
const use = uses.get(url) ?? {
|
|
29
|
+
element,
|
|
30
|
+
objectIds: /* @__PURE__ */ new Set()
|
|
31
|
+
};
|
|
32
|
+
if (id !== void 0) use.objectIds.add(id);
|
|
33
|
+
uses.set(url, use);
|
|
34
|
+
}
|
|
35
|
+
childrenOf(object).forEach((child) => pending.push({
|
|
36
|
+
object: child,
|
|
37
|
+
ownerId: id
|
|
38
|
+
}));
|
|
39
|
+
if (object.clipPath) pending.push({
|
|
40
|
+
object: object.clipPath,
|
|
41
|
+
ownerId: id
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return uses;
|
|
45
|
+
}
|
|
46
|
+
function isBroken(element) {
|
|
47
|
+
return element.complete && element.naturalWidth === 0;
|
|
48
|
+
}
|
|
49
|
+
function shortUrl(url) {
|
|
50
|
+
return url.length > 80 ? `${url.slice(0, 77)}...` : url;
|
|
51
|
+
}
|
|
52
|
+
async function preflightExport(canvas, format, fonts, assetOptions) {
|
|
53
|
+
const problems = [];
|
|
54
|
+
const warnings = [];
|
|
55
|
+
if (format === "json") return {
|
|
56
|
+
ok: true,
|
|
57
|
+
problems,
|
|
58
|
+
warnings
|
|
59
|
+
};
|
|
60
|
+
for (const [url, use] of collectImageUses(canvas.getObjects())) {
|
|
61
|
+
const objectIds = [...use.objectIds];
|
|
62
|
+
if (isBroken(use.element)) problems.push({
|
|
63
|
+
code: "MISSING_IMAGE",
|
|
64
|
+
message: `The image ${shortUrl(url)} did not load`,
|
|
65
|
+
url,
|
|
66
|
+
objectIds
|
|
67
|
+
});
|
|
68
|
+
else if (isRasterFormat(format) && isCrossOriginUrl(url) && !use.element.crossOrigin) problems.push({
|
|
69
|
+
code: "CROSS_ORIGIN_IMAGE",
|
|
70
|
+
message: `The image ${shortUrl(url)} comes from another site without crossOrigin, so the browser blocks exporting it. Load it with crossOrigin: 'anonymous' from a server that allows CORS.`,
|
|
71
|
+
url,
|
|
72
|
+
objectIds
|
|
73
|
+
});
|
|
74
|
+
else if (format === "svg" && !isPortableUrl(url)) warnings.push({
|
|
75
|
+
code: "ASSET_NOT_PORTABLE",
|
|
76
|
+
message: `The SVG links to ${shortUrl(url)}, which only exists in this tab`,
|
|
77
|
+
url,
|
|
78
|
+
objectIds
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const fontSet = typeof document === "undefined" ? void 0 : document.fonts;
|
|
82
|
+
if (fontSet) await fontSet.ready;
|
|
83
|
+
for (const font of await findUnavailableFonts(fonts, assetOptions.loadFont)) {
|
|
84
|
+
const message = `The font "${font.family}" (${font.weight}, ${font.style}) is not available`;
|
|
85
|
+
if (assetOptions.requireFonts) problems.push({
|
|
86
|
+
code: "MISSING_FONT",
|
|
87
|
+
message,
|
|
88
|
+
family: font.family,
|
|
89
|
+
objectIds: font.objectIds
|
|
90
|
+
});
|
|
91
|
+
else warnings.push({
|
|
92
|
+
code: "FONT_UNAVAILABLE",
|
|
93
|
+
message: `${message}, text will use a fallback font`,
|
|
94
|
+
family: font.family,
|
|
95
|
+
objectIds: font.objectIds
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
ok: problems.length === 0,
|
|
100
|
+
problems,
|
|
101
|
+
warnings
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
export { preflightExport };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//#region src/export/render-export.ts
|
|
2
|
+
const identity = [
|
|
3
|
+
1,
|
|
4
|
+
0,
|
|
5
|
+
0,
|
|
6
|
+
1,
|
|
7
|
+
0,
|
|
8
|
+
0
|
|
9
|
+
];
|
|
10
|
+
const mimeTypes = {
|
|
11
|
+
png: "image/png",
|
|
12
|
+
jpeg: "image/jpeg",
|
|
13
|
+
webp: "image/webp",
|
|
14
|
+
svg: "image/svg+xml",
|
|
15
|
+
json: "application/json"
|
|
16
|
+
};
|
|
17
|
+
function chooseBackground(background, format, current) {
|
|
18
|
+
if (background === "transparent") return format === "jpeg" ? "white" : "";
|
|
19
|
+
if (background !== "keep") return background;
|
|
20
|
+
return format === "jpeg" && !(current !== void 0 && current !== null && current !== "") ? "white" : current;
|
|
21
|
+
}
|
|
22
|
+
function withExportView(canvas, background, format, work) {
|
|
23
|
+
const savedTransform = [...canvas.viewportTransform];
|
|
24
|
+
const savedBackground = canvas.backgroundColor;
|
|
25
|
+
canvas.setViewportTransform(identity);
|
|
26
|
+
canvas.backgroundColor = chooseBackground(background, format, savedBackground);
|
|
27
|
+
try {
|
|
28
|
+
return work();
|
|
29
|
+
} finally {
|
|
30
|
+
canvas.backgroundColor = savedBackground;
|
|
31
|
+
canvas.setViewportTransform(savedTransform);
|
|
32
|
+
canvas.requestRenderAll();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function renderRaster(canvas, area, format, scale, quality) {
|
|
36
|
+
return canvas.toDataURL({
|
|
37
|
+
...area,
|
|
38
|
+
format,
|
|
39
|
+
quality,
|
|
40
|
+
multiplier: scale,
|
|
41
|
+
enableRetinaScaling: false
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function renderSvg(canvas, area, scale) {
|
|
45
|
+
return canvas.toSVG({
|
|
46
|
+
viewBox: {
|
|
47
|
+
x: area.left,
|
|
48
|
+
y: area.top,
|
|
49
|
+
width: area.width,
|
|
50
|
+
height: area.height
|
|
51
|
+
},
|
|
52
|
+
width: String(area.width * scale),
|
|
53
|
+
height: String(area.height * scale)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function dataUrlToBlob(dataUrl) {
|
|
57
|
+
const [header, payload = ""] = dataUrl.split(",");
|
|
58
|
+
const mimeType = /data:([^;]+)/.exec(header ?? "")?.[1] ?? "application/octet-stream";
|
|
59
|
+
const binary = atob(payload);
|
|
60
|
+
const bytes = new Uint8Array(binary.length);
|
|
61
|
+
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
|
|
62
|
+
return new Blob([bytes], { type: mimeType });
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
exports.dataUrlToBlob = dataUrlToBlob;
|
|
66
|
+
exports.mimeTypes = mimeTypes;
|
|
67
|
+
exports.renderRaster = renderRaster;
|
|
68
|
+
exports.renderSvg = renderSvg;
|
|
69
|
+
exports.withExportView = withExportView;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region src/export/render-export.ts
|
|
2
|
+
const identity = [
|
|
3
|
+
1,
|
|
4
|
+
0,
|
|
5
|
+
0,
|
|
6
|
+
1,
|
|
7
|
+
0,
|
|
8
|
+
0
|
|
9
|
+
];
|
|
10
|
+
const mimeTypes = {
|
|
11
|
+
png: "image/png",
|
|
12
|
+
jpeg: "image/jpeg",
|
|
13
|
+
webp: "image/webp",
|
|
14
|
+
svg: "image/svg+xml",
|
|
15
|
+
json: "application/json"
|
|
16
|
+
};
|
|
17
|
+
function chooseBackground(background, format, current) {
|
|
18
|
+
if (background === "transparent") return format === "jpeg" ? "white" : "";
|
|
19
|
+
if (background !== "keep") return background;
|
|
20
|
+
return format === "jpeg" && !(current !== void 0 && current !== null && current !== "") ? "white" : current;
|
|
21
|
+
}
|
|
22
|
+
function withExportView(canvas, background, format, work) {
|
|
23
|
+
const savedTransform = [...canvas.viewportTransform];
|
|
24
|
+
const savedBackground = canvas.backgroundColor;
|
|
25
|
+
canvas.setViewportTransform(identity);
|
|
26
|
+
canvas.backgroundColor = chooseBackground(background, format, savedBackground);
|
|
27
|
+
try {
|
|
28
|
+
return work();
|
|
29
|
+
} finally {
|
|
30
|
+
canvas.backgroundColor = savedBackground;
|
|
31
|
+
canvas.setViewportTransform(savedTransform);
|
|
32
|
+
canvas.requestRenderAll();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function renderRaster(canvas, area, format, scale, quality) {
|
|
36
|
+
return canvas.toDataURL({
|
|
37
|
+
...area,
|
|
38
|
+
format,
|
|
39
|
+
quality,
|
|
40
|
+
multiplier: scale,
|
|
41
|
+
enableRetinaScaling: false
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function renderSvg(canvas, area, scale) {
|
|
45
|
+
return canvas.toSVG({
|
|
46
|
+
viewBox: {
|
|
47
|
+
x: area.left,
|
|
48
|
+
y: area.top,
|
|
49
|
+
width: area.width,
|
|
50
|
+
height: area.height
|
|
51
|
+
},
|
|
52
|
+
width: String(area.width * scale),
|
|
53
|
+
height: String(area.height * scale)
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function dataUrlToBlob(dataUrl) {
|
|
57
|
+
const [header, payload = ""] = dataUrl.split(",");
|
|
58
|
+
const mimeType = /data:([^;]+)/.exec(header ?? "")?.[1] ?? "application/octet-stream";
|
|
59
|
+
const binary = atob(payload);
|
|
60
|
+
const bytes = new Uint8Array(binary.length);
|
|
61
|
+
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
|
|
62
|
+
return new Blob([bytes], { type: mimeType });
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { dataUrlToBlob, mimeTypes, renderRaster, renderSvg, withExportView };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_fabric_adapter = require("../fabric/fabric-adapter.cjs");
|
|
2
1
|
const require_object_ids = require("../fabric/object-ids.cjs");
|
|
2
|
+
const require_fabric_adapter = require("../fabric/fabric-adapter.cjs");
|
|
3
3
|
//#region src/history/apply-state.ts
|
|
4
4
|
function arrangeObjects(current, change, created) {
|
|
5
5
|
const arranged = [];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createObjects } from "../fabric/fabric-adapter.js";
|
|
2
1
|
import { readObjectId } from "../fabric/object-ids.js";
|
|
2
|
+
import { createObjects } from "../fabric/fabric-adapter.js";
|
|
3
3
|
//#region src/history/apply-state.ts
|
|
4
4
|
function arrangeObjects(current, change, created) {
|
|
5
5
|
const arranged = [];
|
package/dist/index.cjs
CHANGED
|
@@ -2,14 +2,19 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_errors = require("./engine/errors.cjs");
|
|
3
3
|
const require_document_format = require("./document/document-format.cjs");
|
|
4
4
|
const require_validate_document = require("./document/validate-document.cjs");
|
|
5
|
+
const require_migrate_document = require("./migrations/migrate-document.cjs");
|
|
5
6
|
const require_create_document_engine = require("./engine/create-document-engine.cjs");
|
|
6
7
|
const require_keyboard_shortcuts = require("./history/keyboard-shortcuts.cjs");
|
|
7
8
|
const require_unsaved_changes_warning = require("./save/unsaved-changes-warning.cjs");
|
|
9
|
+
const require_download_export = require("./export/download-export.cjs");
|
|
8
10
|
exports.CURRENT_SCHEMA_VERSION = require_document_format.CURRENT_SCHEMA_VERSION;
|
|
9
11
|
exports.DocumentEngineError = require_errors.DocumentEngineError;
|
|
10
12
|
exports.bindKeyboardShortcuts = require_keyboard_shortcuts.bindKeyboardShortcuts;
|
|
11
13
|
exports.bindUnsavedChangesWarning = require_unsaved_changes_warning.bindUnsavedChangesWarning;
|
|
12
14
|
exports.createConflictError = require_errors.createConflictError;
|
|
13
15
|
exports.createDocumentEngine = require_create_document_engine.createDocumentEngine;
|
|
16
|
+
exports.detectSchemaVersion = require_migrate_document.detectSchemaVersion;
|
|
17
|
+
exports.downloadExport = require_download_export.downloadExport;
|
|
14
18
|
exports.isDocumentEngineError = require_errors.isDocumentEngineError;
|
|
19
|
+
exports.migrateDocument = require_migrate_document.migrateDocument;
|
|
15
20
|
exports.validateDocument = require_validate_document.validateDocument;
|
package/dist/index.d.cts
CHANGED
|
@@ -2,9 +2,12 @@ import { CURRENT_SCHEMA_VERSION, DocumentCanvas, DocumentInfo, FabricDocument, S
|
|
|
2
2
|
import { AssetManifest, FontAsset, ImageAsset } from "./assets/asset-manifest.cjs";
|
|
3
3
|
import { FontLoader } from "./assets/font-check.cjs";
|
|
4
4
|
import { AssetOptions, AssetReport, AssetWarning, AssetWarningCode, UploadRequest } from "./assets/asset-pipeline.cjs";
|
|
5
|
+
import { ExportArea, ExportBackground, ExportFormat, ExportOptions, ExportRect } from "./export/export-options.cjs";
|
|
6
|
+
import { ExportPreflight, ExportProblem, ExportProblemCode } from "./export/preflight-export.cjs";
|
|
5
7
|
import { NewDocumentOptions } from "./document/create-document.cjs";
|
|
6
8
|
import { CustomObjectDefinition } from "./fabric/object-registry.cjs";
|
|
7
9
|
import { HistoryOptions, HistoryState } from "./history/create-history.cjs";
|
|
10
|
+
import { DocumentVersion, VersionKind, VersionOptions, VersionStorage, VersionSummary } from "./versions/document-version.cjs";
|
|
8
11
|
import { RecoveryStore } from "./recovery/recovery-store.cjs";
|
|
9
12
|
import { InterruptedLoad, RecoveryOptions, RecoveryRecord } from "./recovery/recovery-controller.cjs";
|
|
10
13
|
import { AutosaveOptions } from "./save/autosave-scheduler.cjs";
|
|
@@ -13,8 +16,10 @@ import { DocumentEngineError, DocumentEngineErrorDetails, DocumentErrorCode, Doc
|
|
|
13
16
|
import { DocumentStorage, SaveContext, SaveResult } from "./storage/storage-contract.cjs";
|
|
14
17
|
import { SaveOptions, SaveRetryEvent, SaveState, SaveStatus } from "./save/save-controller.cjs";
|
|
15
18
|
import { Unsubscribe } from "./engine/event-emitter.cjs";
|
|
16
|
-
import { DocumentEngine, DocumentEngineEvents, DocumentEngineOptions, LoadOptions, createDocumentEngine } from "./engine/create-document-engine.cjs";
|
|
19
|
+
import { DocumentEngine, DocumentEngineEvents, DocumentEngineOptions, ExportResult, ImportOptions, LoadOptions, createDocumentEngine } from "./engine/create-document-engine.cjs";
|
|
17
20
|
import { validateDocument } from "./document/validate-document.cjs";
|
|
18
21
|
import { KeyboardShortcutOptions, UndoRedoTarget, bindKeyboardShortcuts } from "./history/keyboard-shortcuts.cjs";
|
|
19
22
|
import { UnsavedChangesSource, bindUnsavedChangesWarning } from "./save/unsaved-changes-warning.cjs";
|
|
20
|
-
|
|
23
|
+
import { DownloadableExport, downloadExport } from "./export/download-export.cjs";
|
|
24
|
+
import { Migration, MigrationContext, MigrationResult, detectSchemaVersion, migrateDocument } from "./migrations/migrate-document.cjs";
|
|
25
|
+
export { type AssetManifest, type AssetOptions, type AssetReport, type AssetWarning, type AssetWarningCode, type AutosaveOptions, CURRENT_SCHEMA_VERSION, type CustomObjectDefinition, type DocumentCanvas, type DocumentEngine, DocumentEngineError, type DocumentEngineErrorDetails, type DocumentEngineEvents, type DocumentEngineOptions, type DocumentErrorCode, type DocumentInfo, type DocumentIssue, type DocumentStorage, type DocumentVersion, type DownloadableExport, type ExportArea, type ExportBackground, type ExportFormat, type ExportOptions, type ExportPreflight, type ExportProblem, type ExportProblemCode, type ExportRect, type ExportResult, type FabricDocument, type FontAsset, type FontLoader, type HistoryOptions, type HistoryState, type ImageAsset, type ImportOptions, type InterruptedLoad, type KeyboardShortcutOptions, type LoadOptions, type Migration, type MigrationContext, type MigrationResult, type NewDocumentOptions, type RecoveryOptions, type RecoveryRecord, type RecoveryStore, type RetryOptions, type SaveContext, type SaveOptions, type SaveResult, type SaveRetryEvent, type SaveState, type SaveStatus, type SerializedFabricObject, type UndoRedoTarget, type UnsavedChangesSource, type Unsubscribe, type UploadRequest, type VersionKind, type VersionOptions, type VersionStorage, type VersionSummary, bindKeyboardShortcuts, bindUnsavedChangesWarning, createConflictError, createDocumentEngine, detectSchemaVersion, downloadExport, isDocumentEngineError, migrateDocument, validateDocument };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,12 @@ import { CURRENT_SCHEMA_VERSION, DocumentCanvas, DocumentInfo, FabricDocument, S
|
|
|
2
2
|
import { AssetManifest, FontAsset, ImageAsset } from "./assets/asset-manifest.js";
|
|
3
3
|
import { FontLoader } from "./assets/font-check.js";
|
|
4
4
|
import { AssetOptions, AssetReport, AssetWarning, AssetWarningCode, UploadRequest } from "./assets/asset-pipeline.js";
|
|
5
|
+
import { ExportArea, ExportBackground, ExportFormat, ExportOptions, ExportRect } from "./export/export-options.js";
|
|
6
|
+
import { ExportPreflight, ExportProblem, ExportProblemCode } from "./export/preflight-export.js";
|
|
5
7
|
import { NewDocumentOptions } from "./document/create-document.js";
|
|
6
8
|
import { CustomObjectDefinition } from "./fabric/object-registry.js";
|
|
7
9
|
import { HistoryOptions, HistoryState } from "./history/create-history.js";
|
|
10
|
+
import { DocumentVersion, VersionKind, VersionOptions, VersionStorage, VersionSummary } from "./versions/document-version.js";
|
|
8
11
|
import { RecoveryStore } from "./recovery/recovery-store.js";
|
|
9
12
|
import { InterruptedLoad, RecoveryOptions, RecoveryRecord } from "./recovery/recovery-controller.js";
|
|
10
13
|
import { AutosaveOptions } from "./save/autosave-scheduler.js";
|
|
@@ -13,8 +16,10 @@ import { DocumentEngineError, DocumentEngineErrorDetails, DocumentErrorCode, Doc
|
|
|
13
16
|
import { DocumentStorage, SaveContext, SaveResult } from "./storage/storage-contract.js";
|
|
14
17
|
import { SaveOptions, SaveRetryEvent, SaveState, SaveStatus } from "./save/save-controller.js";
|
|
15
18
|
import { Unsubscribe } from "./engine/event-emitter.js";
|
|
16
|
-
import { DocumentEngine, DocumentEngineEvents, DocumentEngineOptions, LoadOptions, createDocumentEngine } from "./engine/create-document-engine.js";
|
|
19
|
+
import { DocumentEngine, DocumentEngineEvents, DocumentEngineOptions, ExportResult, ImportOptions, LoadOptions, createDocumentEngine } from "./engine/create-document-engine.js";
|
|
17
20
|
import { validateDocument } from "./document/validate-document.js";
|
|
18
21
|
import { KeyboardShortcutOptions, UndoRedoTarget, bindKeyboardShortcuts } from "./history/keyboard-shortcuts.js";
|
|
19
22
|
import { UnsavedChangesSource, bindUnsavedChangesWarning } from "./save/unsaved-changes-warning.js";
|
|
20
|
-
|
|
23
|
+
import { DownloadableExport, downloadExport } from "./export/download-export.js";
|
|
24
|
+
import { Migration, MigrationContext, MigrationResult, detectSchemaVersion, migrateDocument } from "./migrations/migrate-document.js";
|
|
25
|
+
export { type AssetManifest, type AssetOptions, type AssetReport, type AssetWarning, type AssetWarningCode, type AutosaveOptions, CURRENT_SCHEMA_VERSION, type CustomObjectDefinition, type DocumentCanvas, type DocumentEngine, DocumentEngineError, type DocumentEngineErrorDetails, type DocumentEngineEvents, type DocumentEngineOptions, type DocumentErrorCode, type DocumentInfo, type DocumentIssue, type DocumentStorage, type DocumentVersion, type DownloadableExport, type ExportArea, type ExportBackground, type ExportFormat, type ExportOptions, type ExportPreflight, type ExportProblem, type ExportProblemCode, type ExportRect, type ExportResult, type FabricDocument, type FontAsset, type FontLoader, type HistoryOptions, type HistoryState, type ImageAsset, type ImportOptions, type InterruptedLoad, type KeyboardShortcutOptions, type LoadOptions, type Migration, type MigrationContext, type MigrationResult, type NewDocumentOptions, type RecoveryOptions, type RecoveryRecord, type RecoveryStore, type RetryOptions, type SaveContext, type SaveOptions, type SaveResult, type SaveRetryEvent, type SaveState, type SaveStatus, type SerializedFabricObject, type UndoRedoTarget, type UnsavedChangesSource, type Unsubscribe, type UploadRequest, type VersionKind, type VersionOptions, type VersionStorage, type VersionSummary, bindKeyboardShortcuts, bindUnsavedChangesWarning, createConflictError, createDocumentEngine, detectSchemaVersion, downloadExport, isDocumentEngineError, migrateDocument, validateDocument };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { DocumentEngineError, createConflictError, isDocumentEngineError } from "./engine/errors.js";
|
|
2
2
|
import { CURRENT_SCHEMA_VERSION } from "./document/document-format.js";
|
|
3
3
|
import { validateDocument } from "./document/validate-document.js";
|
|
4
|
+
import { detectSchemaVersion, migrateDocument } from "./migrations/migrate-document.js";
|
|
4
5
|
import { createDocumentEngine } from "./engine/create-document-engine.js";
|
|
5
6
|
import { bindKeyboardShortcuts } from "./history/keyboard-shortcuts.js";
|
|
6
7
|
import { bindUnsavedChangesWarning } from "./save/unsaved-changes-warning.js";
|
|
7
|
-
|
|
8
|
+
import { downloadExport } from "./export/download-export.js";
|
|
9
|
+
export { CURRENT_SCHEMA_VERSION, DocumentEngineError, bindKeyboardShortcuts, bindUnsavedChangesWarning, createConflictError, createDocumentEngine, detectSchemaVersion, downloadExport, isDocumentEngineError, migrateDocument, validateDocument };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const require_errors = require("../engine/errors.cjs");
|
|
2
|
+
require("../document/document-format.cjs");
|
|
3
|
+
const require_ids = require("../document/ids.cjs");
|
|
4
|
+
//#region src/migrations/migrate-document.ts
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function positiveNumberOr(value, fallback) {
|
|
9
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
10
|
+
}
|
|
11
|
+
function fromFabricJson(json, context) {
|
|
12
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13
|
+
const document = {
|
|
14
|
+
schemaVersion: 1,
|
|
15
|
+
id: context.id ?? (typeof json.id === "string" && json.id.length > 0 ? json.id : require_ids.createId()),
|
|
16
|
+
createdAt: now,
|
|
17
|
+
updatedAt: now,
|
|
18
|
+
revision: 0,
|
|
19
|
+
canvas: {
|
|
20
|
+
width: positiveNumberOr(json.width, context.canvasWidth),
|
|
21
|
+
height: positiveNumberOr(json.height, context.canvasHeight),
|
|
22
|
+
...json.background === void 0 ? {} : { background: json.background }
|
|
23
|
+
},
|
|
24
|
+
objects: json.objects,
|
|
25
|
+
metadata: { ...context.metadata }
|
|
26
|
+
};
|
|
27
|
+
if (typeof json.version === "string") document.fabricVersion = json.version;
|
|
28
|
+
return document;
|
|
29
|
+
}
|
|
30
|
+
const migrations = { 0: fromFabricJson };
|
|
31
|
+
function detectSchemaVersion(value) {
|
|
32
|
+
if (!isPlainObject(value)) return void 0;
|
|
33
|
+
if (typeof value.schemaVersion === "number" && Number.isInteger(value.schemaVersion)) return value.schemaVersion;
|
|
34
|
+
if (value.schemaVersion === void 0 && Array.isArray(value.objects)) return 0;
|
|
35
|
+
}
|
|
36
|
+
function migrateDocument(value, context, steps = migrations, targetVersion = 1) {
|
|
37
|
+
const startVersion = detectSchemaVersion(value);
|
|
38
|
+
if (startVersion === void 0 || startVersion >= targetVersion || startVersion < 0) return {
|
|
39
|
+
document: value,
|
|
40
|
+
migratedFrom: void 0
|
|
41
|
+
};
|
|
42
|
+
let document = JSON.parse(JSON.stringify(value));
|
|
43
|
+
for (let version = startVersion; version < targetVersion; version += 1) {
|
|
44
|
+
const step = steps[version];
|
|
45
|
+
if (!step) throw new require_errors.DocumentEngineError("MIGRATION_FAILED", `There is no migration from schema version ${version}`, { migrationFrom: version });
|
|
46
|
+
try {
|
|
47
|
+
document = step(document, context);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
50
|
+
throw new require_errors.DocumentEngineError("MIGRATION_FAILED", `Could not migrate the document from schema version ${version} to ${version + 1}: ${reason}`, {
|
|
51
|
+
cause: error,
|
|
52
|
+
migrationFrom: version
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (document.schemaVersion !== version + 1) throw new require_errors.DocumentEngineError("MIGRATION_FAILED", `The migration from schema version ${version} produced version ${String(document.schemaVersion)} instead of ${version + 1}`, { migrationFrom: version });
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
document,
|
|
59
|
+
migratedFrom: startVersion
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
exports.detectSchemaVersion = detectSchemaVersion;
|
|
64
|
+
exports.migrateDocument = migrateDocument;
|
|
65
|
+
exports.migrations = migrations;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/migrations/migrate-document.d.ts
|
|
2
|
+
export interface MigrationContext {
|
|
3
|
+
canvasWidth: number;
|
|
4
|
+
canvasHeight: number;
|
|
5
|
+
id?: string;
|
|
6
|
+
metadata?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export type Migration = (document: Record<string, unknown>, context: MigrationContext) => Record<string, unknown>;
|
|
9
|
+
export interface MigrationResult {
|
|
10
|
+
document: unknown;
|
|
11
|
+
migratedFrom: number | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function detectSchemaVersion(value: unknown): number | undefined;
|
|
14
|
+
export declare function migrateDocument(value: unknown, context: MigrationContext, steps?: Readonly<Record<number, Migration>>, targetVersion?: number): MigrationResult;
|
|
15
|
+
//#endregion
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/migrations/migrate-document.d.ts
|
|
2
|
+
export interface MigrationContext {
|
|
3
|
+
canvasWidth: number;
|
|
4
|
+
canvasHeight: number;
|
|
5
|
+
id?: string;
|
|
6
|
+
metadata?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
export type Migration = (document: Record<string, unknown>, context: MigrationContext) => Record<string, unknown>;
|
|
9
|
+
export interface MigrationResult {
|
|
10
|
+
document: unknown;
|
|
11
|
+
migratedFrom: number | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function detectSchemaVersion(value: unknown): number | undefined;
|
|
14
|
+
export declare function migrateDocument(value: unknown, context: MigrationContext, steps?: Readonly<Record<number, Migration>>, targetVersion?: number): MigrationResult;
|
|
15
|
+
//#endregion
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { DocumentEngineError } from "../engine/errors.js";
|
|
2
|
+
import "../document/document-format.js";
|
|
3
|
+
import { createId } from "../document/ids.js";
|
|
4
|
+
//#region src/migrations/migrate-document.ts
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function positiveNumberOr(value, fallback) {
|
|
9
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
10
|
+
}
|
|
11
|
+
function fromFabricJson(json, context) {
|
|
12
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13
|
+
const document = {
|
|
14
|
+
schemaVersion: 1,
|
|
15
|
+
id: context.id ?? (typeof json.id === "string" && json.id.length > 0 ? json.id : createId()),
|
|
16
|
+
createdAt: now,
|
|
17
|
+
updatedAt: now,
|
|
18
|
+
revision: 0,
|
|
19
|
+
canvas: {
|
|
20
|
+
width: positiveNumberOr(json.width, context.canvasWidth),
|
|
21
|
+
height: positiveNumberOr(json.height, context.canvasHeight),
|
|
22
|
+
...json.background === void 0 ? {} : { background: json.background }
|
|
23
|
+
},
|
|
24
|
+
objects: json.objects,
|
|
25
|
+
metadata: { ...context.metadata }
|
|
26
|
+
};
|
|
27
|
+
if (typeof json.version === "string") document.fabricVersion = json.version;
|
|
28
|
+
return document;
|
|
29
|
+
}
|
|
30
|
+
const migrations = { 0: fromFabricJson };
|
|
31
|
+
function detectSchemaVersion(value) {
|
|
32
|
+
if (!isPlainObject(value)) return void 0;
|
|
33
|
+
if (typeof value.schemaVersion === "number" && Number.isInteger(value.schemaVersion)) return value.schemaVersion;
|
|
34
|
+
if (value.schemaVersion === void 0 && Array.isArray(value.objects)) return 0;
|
|
35
|
+
}
|
|
36
|
+
function migrateDocument(value, context, steps = migrations, targetVersion = 1) {
|
|
37
|
+
const startVersion = detectSchemaVersion(value);
|
|
38
|
+
if (startVersion === void 0 || startVersion >= targetVersion || startVersion < 0) return {
|
|
39
|
+
document: value,
|
|
40
|
+
migratedFrom: void 0
|
|
41
|
+
};
|
|
42
|
+
let document = JSON.parse(JSON.stringify(value));
|
|
43
|
+
for (let version = startVersion; version < targetVersion; version += 1) {
|
|
44
|
+
const step = steps[version];
|
|
45
|
+
if (!step) throw new DocumentEngineError("MIGRATION_FAILED", `There is no migration from schema version ${version}`, { migrationFrom: version });
|
|
46
|
+
try {
|
|
47
|
+
document = step(document, context);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
50
|
+
throw new DocumentEngineError("MIGRATION_FAILED", `Could not migrate the document from schema version ${version} to ${version + 1}: ${reason}`, {
|
|
51
|
+
cause: error,
|
|
52
|
+
migrationFrom: version
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (document.schemaVersion !== version + 1) throw new DocumentEngineError("MIGRATION_FAILED", `The migration from schema version ${version} produced version ${String(document.schemaVersion)} instead of ${version + 1}`, { migrationFrom: version });
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
document,
|
|
59
|
+
migratedFrom: startVersion
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { detectSchemaVersion, migrateDocument, migrations };
|