fabricjs-document-engine 0.5.0 → 0.7.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 +82 -3
- package/dist/engine/create-document-engine.cjs +107 -4
- package/dist/engine/create-document-engine.d.cts +23 -0
- package/dist/engine/create-document-engine.d.ts +23 -0
- package/dist/engine/create-document-engine.js +107 -4
- package/dist/engine/errors.cjs +1 -0
- package/dist/engine/errors.d.cts +3 -1
- package/dist/engine/errors.d.ts +3 -1
- package/dist/engine/errors.js +1 -0
- package/dist/index.cjs +5 -0
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -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/react/engine-context.cjs +12 -0
- package/dist/react/engine-context.d.cts +10 -0
- package/dist/react/engine-context.d.ts +10 -0
- package/dist/react/engine-context.js +11 -0
- package/dist/react/use-document-engine.cjs +23 -0
- package/dist/react/use-document-engine.d.cts +6 -0
- package/dist/react/use-document-engine.d.ts +6 -0
- package/dist/react/use-document-engine.js +23 -0
- package/dist/react/use-document-event.cjs +12 -0
- package/dist/react/use-document-event.d.cts +4 -0
- package/dist/react/use-document-event.d.ts +4 -0
- package/dist/react/use-document-event.js +12 -0
- package/dist/react/use-document-state.cjs +11 -0
- package/dist/react/use-document-state.d.cts +5 -0
- package/dist/react/use-document-state.d.ts +5 -0
- package/dist/react/use-document-state.js +11 -0
- package/dist/react.cjs +11 -0
- package/dist/react.d.cts +6 -0
- package/dist/react.d.ts +6 -0
- package/dist/react.js +6 -0
- package/dist/state/document-state-store.cjs +114 -0
- package/dist/state/document-state-store.d.cts +28 -0
- package/dist/state/document-state-store.d.ts +28 -0
- package/dist/state/document-state-store.js +114 -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 +26 -4
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AssetWarning } from "../assets/asset-pipeline.cjs";
|
|
2
|
+
import { DocumentEngineError } from "../engine/errors.cjs";
|
|
3
|
+
import { SaveStatus } from "../save/save-controller.cjs";
|
|
4
|
+
import { DocumentEngine } from "../engine/create-document-engine.cjs";
|
|
5
|
+
//#region src/state/document-state-store.d.ts
|
|
6
|
+
export interface DocumentState {
|
|
7
|
+
documentId: string;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
loadError: DocumentEngineError | undefined;
|
|
10
|
+
saveStatus: SaveStatus;
|
|
11
|
+
isDirty: boolean;
|
|
12
|
+
isSaving: boolean;
|
|
13
|
+
revision: number;
|
|
14
|
+
lastSavedAt: string | undefined;
|
|
15
|
+
saveError: DocumentEngineError | undefined;
|
|
16
|
+
canUndo: boolean;
|
|
17
|
+
canRedo: boolean;
|
|
18
|
+
undoLabel: string | undefined;
|
|
19
|
+
redoLabel: string | undefined;
|
|
20
|
+
assetWarnings: AssetWarning[];
|
|
21
|
+
}
|
|
22
|
+
export interface DocumentStateStore {
|
|
23
|
+
getSnapshot(): DocumentState;
|
|
24
|
+
subscribe(listener: () => void): () => void;
|
|
25
|
+
destroy(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare function createDocumentStateStore(engine: DocumentEngine): DocumentStateStore;
|
|
28
|
+
//#endregion
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AssetWarning } from "../assets/asset-pipeline.js";
|
|
2
|
+
import { DocumentEngineError } from "../engine/errors.js";
|
|
3
|
+
import { SaveStatus } from "../save/save-controller.js";
|
|
4
|
+
import { DocumentEngine } from "../engine/create-document-engine.js";
|
|
5
|
+
//#region src/state/document-state-store.d.ts
|
|
6
|
+
export interface DocumentState {
|
|
7
|
+
documentId: string;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
loadError: DocumentEngineError | undefined;
|
|
10
|
+
saveStatus: SaveStatus;
|
|
11
|
+
isDirty: boolean;
|
|
12
|
+
isSaving: boolean;
|
|
13
|
+
revision: number;
|
|
14
|
+
lastSavedAt: string | undefined;
|
|
15
|
+
saveError: DocumentEngineError | undefined;
|
|
16
|
+
canUndo: boolean;
|
|
17
|
+
canRedo: boolean;
|
|
18
|
+
undoLabel: string | undefined;
|
|
19
|
+
redoLabel: string | undefined;
|
|
20
|
+
assetWarnings: AssetWarning[];
|
|
21
|
+
}
|
|
22
|
+
export interface DocumentStateStore {
|
|
23
|
+
getSnapshot(): DocumentState;
|
|
24
|
+
subscribe(listener: () => void): () => void;
|
|
25
|
+
destroy(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare function createDocumentStateStore(engine: DocumentEngine): DocumentStateStore;
|
|
28
|
+
//#endregion
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//#region src/state/document-state-store.ts
|
|
2
|
+
function initialState(engine) {
|
|
3
|
+
const saveState = engine.getSaveState();
|
|
4
|
+
const history = engine.getHistory();
|
|
5
|
+
return {
|
|
6
|
+
documentId: engine.getDocumentInfo().id,
|
|
7
|
+
isLoading: false,
|
|
8
|
+
loadError: void 0,
|
|
9
|
+
saveStatus: saveState.status,
|
|
10
|
+
isDirty: saveState.isDirty,
|
|
11
|
+
isSaving: saveState.isSaving,
|
|
12
|
+
revision: saveState.revision,
|
|
13
|
+
lastSavedAt: saveState.lastSavedAt,
|
|
14
|
+
saveError: saveState.error,
|
|
15
|
+
canUndo: history.undo.length > 0,
|
|
16
|
+
canRedo: history.redo.length > 0,
|
|
17
|
+
undoLabel: history.undo[0],
|
|
18
|
+
redoLabel: history.redo[0],
|
|
19
|
+
assetWarnings: []
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function withLiveEngineValues(engine, previous) {
|
|
23
|
+
return {
|
|
24
|
+
...previous,
|
|
25
|
+
...initialState(engine),
|
|
26
|
+
isLoading: previous.isLoading,
|
|
27
|
+
loadError: previous.loadError,
|
|
28
|
+
assetWarnings: previous.assetWarnings
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function isSameState(first, second) {
|
|
32
|
+
return Object.keys(first).every((key) => first[key] === second[key]);
|
|
33
|
+
}
|
|
34
|
+
function createDocumentStateStore(engine) {
|
|
35
|
+
let state = initialState(engine);
|
|
36
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
37
|
+
let detachFromEngine;
|
|
38
|
+
function update(changes) {
|
|
39
|
+
const next = {
|
|
40
|
+
...state,
|
|
41
|
+
...changes
|
|
42
|
+
};
|
|
43
|
+
if (isSameState(state, next)) return;
|
|
44
|
+
state = next;
|
|
45
|
+
for (const listener of [...listeners]) listener();
|
|
46
|
+
}
|
|
47
|
+
function attachToEngine() {
|
|
48
|
+
const unsubscribers = [
|
|
49
|
+
engine.on("load:start", () => update({
|
|
50
|
+
isLoading: true,
|
|
51
|
+
loadError: void 0
|
|
52
|
+
})),
|
|
53
|
+
engine.on("load:success", ({ warnings }) => update({
|
|
54
|
+
isLoading: false,
|
|
55
|
+
assetWarnings: warnings
|
|
56
|
+
})),
|
|
57
|
+
engine.on("load:error", ({ error }) => {
|
|
58
|
+
if (error.code !== "LOAD_ABORTED") update({
|
|
59
|
+
isLoading: false,
|
|
60
|
+
loadError: error
|
|
61
|
+
});
|
|
62
|
+
}),
|
|
63
|
+
engine.on("document:change", ({ documentId }) => update({
|
|
64
|
+
documentId,
|
|
65
|
+
loadError: void 0
|
|
66
|
+
})),
|
|
67
|
+
engine.on("assets:warning", ({ warnings }) => update({ assetWarnings: warnings })),
|
|
68
|
+
engine.on("save:status", (saveState) => update({
|
|
69
|
+
saveStatus: saveState.status,
|
|
70
|
+
isDirty: saveState.isDirty,
|
|
71
|
+
isSaving: saveState.isSaving,
|
|
72
|
+
revision: saveState.revision,
|
|
73
|
+
lastSavedAt: saveState.lastSavedAt,
|
|
74
|
+
saveError: saveState.error
|
|
75
|
+
})),
|
|
76
|
+
engine.on("history:change", (history) => update({
|
|
77
|
+
canUndo: history.canUndo,
|
|
78
|
+
canRedo: history.canRedo,
|
|
79
|
+
undoLabel: history.undoLabel,
|
|
80
|
+
redoLabel: history.redoLabel
|
|
81
|
+
}))
|
|
82
|
+
];
|
|
83
|
+
return () => unsubscribers.forEach((unsubscribe) => unsubscribe());
|
|
84
|
+
}
|
|
85
|
+
function refreshWhileDetached() {
|
|
86
|
+
if (detachFromEngine) return;
|
|
87
|
+
const fresh = withLiveEngineValues(engine, state);
|
|
88
|
+
if (!isSameState(state, fresh)) state = fresh;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
getSnapshot() {
|
|
92
|
+
refreshWhileDetached();
|
|
93
|
+
return state;
|
|
94
|
+
},
|
|
95
|
+
subscribe(listener) {
|
|
96
|
+
refreshWhileDetached();
|
|
97
|
+
listeners.add(listener);
|
|
98
|
+
detachFromEngine ?? (detachFromEngine = attachToEngine());
|
|
99
|
+
return () => {
|
|
100
|
+
listeners.delete(listener);
|
|
101
|
+
if (listeners.size > 0) return;
|
|
102
|
+
detachFromEngine?.();
|
|
103
|
+
detachFromEngine = void 0;
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
destroy() {
|
|
107
|
+
listeners.clear();
|
|
108
|
+
detachFromEngine?.();
|
|
109
|
+
detachFromEngine = void 0;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { createDocumentStateStore };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_errors = require("../engine/errors.cjs");
|
|
2
|
+
const require_document_version = require("../versions/document-version.cjs");
|
|
2
3
|
//#region src/storage/key-value-storage.ts
|
|
3
4
|
function readRevision(json) {
|
|
4
5
|
if (json === null) return 0;
|
|
@@ -6,7 +7,19 @@ function readRevision(json) {
|
|
|
6
7
|
return typeof revision === "number" ? revision : 0;
|
|
7
8
|
}
|
|
8
9
|
function createKeyValueStorage(store, prefix) {
|
|
10
|
+
const versionMarker = "::version::";
|
|
9
11
|
const keyFor = (id) => `${prefix}${id}`;
|
|
12
|
+
const versionPrefixFor = (documentId) => `${keyFor(documentId)}${versionMarker}`;
|
|
13
|
+
const versionKeyFor = (documentId, versionId) => `${versionPrefixFor(documentId)}${versionId}`;
|
|
14
|
+
function readVersion(documentId, versionId) {
|
|
15
|
+
const json = store.read(versionKeyFor(documentId, versionId));
|
|
16
|
+
if (json === null) throw new require_errors.DocumentEngineError("VERSION_NOT_FOUND", `Document "${documentId}" has no version "${versionId}"`);
|
|
17
|
+
return JSON.parse(json);
|
|
18
|
+
}
|
|
19
|
+
function versionKeysOf(documentId) {
|
|
20
|
+
const versionPrefix = versionPrefixFor(documentId);
|
|
21
|
+
return store.keys().filter((key) => key.startsWith(versionPrefix));
|
|
22
|
+
}
|
|
10
23
|
return {
|
|
11
24
|
async loadDocument(id) {
|
|
12
25
|
const json = store.read(keyFor(id));
|
|
@@ -24,10 +37,23 @@ function createKeyValueStorage(store, prefix) {
|
|
|
24
37
|
return { revision };
|
|
25
38
|
},
|
|
26
39
|
async listDocuments() {
|
|
27
|
-
return store.keys().filter((key) => key.startsWith(prefix)).map((key) => key.slice(prefix.length));
|
|
40
|
+
return store.keys().filter((key) => key.startsWith(prefix) && !key.includes(versionMarker)).map((key) => key.slice(prefix.length));
|
|
28
41
|
},
|
|
29
42
|
async deleteDocument(id) {
|
|
43
|
+
versionKeysOf(id).forEach((key) => store.remove(key));
|
|
30
44
|
store.remove(keyFor(id));
|
|
45
|
+
},
|
|
46
|
+
async saveVersion(version) {
|
|
47
|
+
store.write(versionKeyFor(version.documentId, version.id), JSON.stringify(version));
|
|
48
|
+
},
|
|
49
|
+
async listVersions(documentId) {
|
|
50
|
+
return versionKeysOf(documentId).map((key) => require_document_version.summarize(JSON.parse(store.read(key) ?? "null"))).sort(require_document_version.newestFirst);
|
|
51
|
+
},
|
|
52
|
+
async loadVersion(documentId, versionId) {
|
|
53
|
+
return readVersion(documentId, versionId);
|
|
54
|
+
},
|
|
55
|
+
async deleteVersion(documentId, versionId) {
|
|
56
|
+
store.remove(versionKeyFor(documentId, versionId));
|
|
31
57
|
}
|
|
32
58
|
};
|
|
33
59
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VersionStorage } from "../versions/document-version.cjs";
|
|
1
2
|
import { DocumentStorage } from "./storage-contract.cjs";
|
|
2
3
|
//#region src/storage/key-value-storage.d.ts
|
|
3
4
|
export interface KeyValueStore {
|
|
@@ -6,7 +7,7 @@ export interface KeyValueStore {
|
|
|
6
7
|
remove(key: string): void;
|
|
7
8
|
keys(): string[];
|
|
8
9
|
}
|
|
9
|
-
export interface ManagedDocumentStorage extends DocumentStorage {
|
|
10
|
+
export interface ManagedDocumentStorage extends DocumentStorage, VersionStorage {
|
|
10
11
|
listDocuments(): Promise<string[]>;
|
|
11
12
|
deleteDocument(id: string): Promise<void>;
|
|
12
13
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VersionStorage } from "../versions/document-version.js";
|
|
1
2
|
import { DocumentStorage } from "./storage-contract.js";
|
|
2
3
|
//#region src/storage/key-value-storage.d.ts
|
|
3
4
|
export interface KeyValueStore {
|
|
@@ -6,7 +7,7 @@ export interface KeyValueStore {
|
|
|
6
7
|
remove(key: string): void;
|
|
7
8
|
keys(): string[];
|
|
8
9
|
}
|
|
9
|
-
export interface ManagedDocumentStorage extends DocumentStorage {
|
|
10
|
+
export interface ManagedDocumentStorage extends DocumentStorage, VersionStorage {
|
|
10
11
|
listDocuments(): Promise<string[]>;
|
|
11
12
|
deleteDocument(id: string): Promise<void>;
|
|
12
13
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DocumentEngineError, createConflictError } from "../engine/errors.js";
|
|
2
|
+
import { newestFirst, summarize } from "../versions/document-version.js";
|
|
2
3
|
//#region src/storage/key-value-storage.ts
|
|
3
4
|
function readRevision(json) {
|
|
4
5
|
if (json === null) return 0;
|
|
@@ -6,7 +7,19 @@ function readRevision(json) {
|
|
|
6
7
|
return typeof revision === "number" ? revision : 0;
|
|
7
8
|
}
|
|
8
9
|
function createKeyValueStorage(store, prefix) {
|
|
10
|
+
const versionMarker = "::version::";
|
|
9
11
|
const keyFor = (id) => `${prefix}${id}`;
|
|
12
|
+
const versionPrefixFor = (documentId) => `${keyFor(documentId)}${versionMarker}`;
|
|
13
|
+
const versionKeyFor = (documentId, versionId) => `${versionPrefixFor(documentId)}${versionId}`;
|
|
14
|
+
function readVersion(documentId, versionId) {
|
|
15
|
+
const json = store.read(versionKeyFor(documentId, versionId));
|
|
16
|
+
if (json === null) throw new DocumentEngineError("VERSION_NOT_FOUND", `Document "${documentId}" has no version "${versionId}"`);
|
|
17
|
+
return JSON.parse(json);
|
|
18
|
+
}
|
|
19
|
+
function versionKeysOf(documentId) {
|
|
20
|
+
const versionPrefix = versionPrefixFor(documentId);
|
|
21
|
+
return store.keys().filter((key) => key.startsWith(versionPrefix));
|
|
22
|
+
}
|
|
10
23
|
return {
|
|
11
24
|
async loadDocument(id) {
|
|
12
25
|
const json = store.read(keyFor(id));
|
|
@@ -24,10 +37,23 @@ function createKeyValueStorage(store, prefix) {
|
|
|
24
37
|
return { revision };
|
|
25
38
|
},
|
|
26
39
|
async listDocuments() {
|
|
27
|
-
return store.keys().filter((key) => key.startsWith(prefix)).map((key) => key.slice(prefix.length));
|
|
40
|
+
return store.keys().filter((key) => key.startsWith(prefix) && !key.includes(versionMarker)).map((key) => key.slice(prefix.length));
|
|
28
41
|
},
|
|
29
42
|
async deleteDocument(id) {
|
|
43
|
+
versionKeysOf(id).forEach((key) => store.remove(key));
|
|
30
44
|
store.remove(keyFor(id));
|
|
45
|
+
},
|
|
46
|
+
async saveVersion(version) {
|
|
47
|
+
store.write(versionKeyFor(version.documentId, version.id), JSON.stringify(version));
|
|
48
|
+
},
|
|
49
|
+
async listVersions(documentId) {
|
|
50
|
+
return versionKeysOf(documentId).map((key) => summarize(JSON.parse(store.read(key) ?? "null"))).sort(newestFirst);
|
|
51
|
+
},
|
|
52
|
+
async loadVersion(documentId, versionId) {
|
|
53
|
+
return readVersion(documentId, versionId);
|
|
54
|
+
},
|
|
55
|
+
async deleteVersion(documentId, versionId) {
|
|
56
|
+
store.remove(versionKeyFor(documentId, versionId));
|
|
31
57
|
}
|
|
32
58
|
};
|
|
33
59
|
}
|
package/dist/storage.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { DocumentVersion, VersionStorage, VersionSummary } from "./versions/document-version.cjs";
|
|
1
2
|
import { DocumentStorage, SaveContext, SaveResult } from "./storage/storage-contract.cjs";
|
|
2
3
|
import { KeyValueStore, ManagedDocumentStorage, createKeyValueStorage } from "./storage/key-value-storage.cjs";
|
|
3
4
|
import { createMemoryStorage } from "./storage/memory-storage.cjs";
|
|
4
5
|
import { LocalStorageOptions, createLocalStorage } from "./storage/local-storage.cjs";
|
|
5
|
-
export { type DocumentStorage, type KeyValueStore, type LocalStorageOptions, type ManagedDocumentStorage, type SaveContext, type SaveResult, createKeyValueStorage, createLocalStorage, createMemoryStorage };
|
|
6
|
+
export { type DocumentStorage, type DocumentVersion, type KeyValueStore, type LocalStorageOptions, type ManagedDocumentStorage, type SaveContext, type SaveResult, type VersionStorage, type VersionSummary, createKeyValueStorage, createLocalStorage, createMemoryStorage };
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { DocumentVersion, VersionStorage, VersionSummary } from "./versions/document-version.js";
|
|
1
2
|
import { DocumentStorage, SaveContext, SaveResult } from "./storage/storage-contract.js";
|
|
2
3
|
import { KeyValueStore, ManagedDocumentStorage, createKeyValueStorage } from "./storage/key-value-storage.js";
|
|
3
4
|
import { createMemoryStorage } from "./storage/memory-storage.js";
|
|
4
5
|
import { LocalStorageOptions, createLocalStorage } from "./storage/local-storage.js";
|
|
5
|
-
export { type DocumentStorage, type KeyValueStore, type LocalStorageOptions, type ManagedDocumentStorage, type SaveContext, type SaveResult, createKeyValueStorage, createLocalStorage, createMemoryStorage };
|
|
6
|
+
export { type DocumentStorage, type DocumentVersion, type KeyValueStore, type LocalStorageOptions, type ManagedDocumentStorage, type SaveContext, type SaveResult, type VersionStorage, type VersionSummary, createKeyValueStorage, createLocalStorage, createMemoryStorage };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/versions/document-version.ts
|
|
2
|
+
function summarize(version) {
|
|
3
|
+
const { document: _document, ...summary } = version;
|
|
4
|
+
return summary;
|
|
5
|
+
}
|
|
6
|
+
function newestFirst(first, second) {
|
|
7
|
+
return second.createdAt.localeCompare(first.createdAt);
|
|
8
|
+
}
|
|
9
|
+
function versionsToPrune(versions, keepAuto) {
|
|
10
|
+
return versions.filter((version) => version.kind === "auto").sort(newestFirst).slice(Math.max(0, keepAuto));
|
|
11
|
+
}
|
|
12
|
+
function supportsVersions(storage) {
|
|
13
|
+
const candidate = storage;
|
|
14
|
+
return typeof candidate?.saveVersion === "function" && typeof candidate.listVersions === "function" && typeof candidate.loadVersion === "function" && typeof candidate.deleteVersion === "function";
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
exports.newestFirst = newestFirst;
|
|
18
|
+
exports.summarize = summarize;
|
|
19
|
+
exports.supportsVersions = supportsVersions;
|
|
20
|
+
exports.versionsToPrune = versionsToPrune;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { FabricDocument } from "../document/document-format.cjs";
|
|
2
|
+
//#region src/versions/document-version.d.ts
|
|
3
|
+
export type VersionKind = "named" | "auto";
|
|
4
|
+
export interface VersionSummary {
|
|
5
|
+
id: string;
|
|
6
|
+
documentId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
kind: VersionKind;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
revision: number;
|
|
11
|
+
}
|
|
12
|
+
export interface DocumentVersion extends VersionSummary {
|
|
13
|
+
document: FabricDocument;
|
|
14
|
+
}
|
|
15
|
+
export interface VersionStorage {
|
|
16
|
+
saveVersion(version: DocumentVersion): Promise<void>;
|
|
17
|
+
listVersions(documentId: string): Promise<VersionSummary[]>;
|
|
18
|
+
loadVersion(documentId: string, versionId: string): Promise<DocumentVersion>;
|
|
19
|
+
deleteVersion(documentId: string, versionId: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export interface VersionOptions {
|
|
22
|
+
keepAuto?: number;
|
|
23
|
+
autoEvery?: number;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { FabricDocument } from "../document/document-format.js";
|
|
2
|
+
//#region src/versions/document-version.d.ts
|
|
3
|
+
export type VersionKind = "named" | "auto";
|
|
4
|
+
export interface VersionSummary {
|
|
5
|
+
id: string;
|
|
6
|
+
documentId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
kind: VersionKind;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
revision: number;
|
|
11
|
+
}
|
|
12
|
+
export interface DocumentVersion extends VersionSummary {
|
|
13
|
+
document: FabricDocument;
|
|
14
|
+
}
|
|
15
|
+
export interface VersionStorage {
|
|
16
|
+
saveVersion(version: DocumentVersion): Promise<void>;
|
|
17
|
+
listVersions(documentId: string): Promise<VersionSummary[]>;
|
|
18
|
+
loadVersion(documentId: string, versionId: string): Promise<DocumentVersion>;
|
|
19
|
+
deleteVersion(documentId: string, versionId: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export interface VersionOptions {
|
|
22
|
+
keepAuto?: number;
|
|
23
|
+
autoEvery?: number;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/versions/document-version.ts
|
|
2
|
+
function summarize(version) {
|
|
3
|
+
const { document: _document, ...summary } = version;
|
|
4
|
+
return summary;
|
|
5
|
+
}
|
|
6
|
+
function newestFirst(first, second) {
|
|
7
|
+
return second.createdAt.localeCompare(first.createdAt);
|
|
8
|
+
}
|
|
9
|
+
function versionsToPrune(versions, keepAuto) {
|
|
10
|
+
return versions.filter((version) => version.kind === "auto").sort(newestFirst).slice(Math.max(0, keepAuto));
|
|
11
|
+
}
|
|
12
|
+
function supportsVersions(storage) {
|
|
13
|
+
const candidate = storage;
|
|
14
|
+
return typeof candidate?.saveVersion === "function" && typeof candidate.listVersions === "function" && typeof candidate.loadVersion === "function" && typeof candidate.deleteVersion === "function";
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { newestFirst, summarize, supportsVersions, versionsToPrune };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fabricjs-document-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Turn an existing Fabric.js canvas into a dependable editable document: stable object ids, versioned save and load, undo and redo, safe autosave with conflict protection, custom objects and clear errors.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fabric",
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"editor",
|
|
20
20
|
"typescript",
|
|
21
21
|
"storage",
|
|
22
|
-
"recovery"
|
|
22
|
+
"recovery",
|
|
23
|
+
"react",
|
|
24
|
+
"react hooks"
|
|
23
25
|
],
|
|
24
26
|
"license": "MIT",
|
|
25
27
|
"author": "Sohail Khan (https://me.jscrate.dev)",
|
|
@@ -64,10 +66,20 @@
|
|
|
64
66
|
"default": "./dist/recovery.cjs"
|
|
65
67
|
}
|
|
66
68
|
},
|
|
69
|
+
"./react": {
|
|
70
|
+
"import": {
|
|
71
|
+
"types": "./dist/react.d.ts",
|
|
72
|
+
"default": "./dist/react.js"
|
|
73
|
+
},
|
|
74
|
+
"require": {
|
|
75
|
+
"types": "./dist/react.d.cts",
|
|
76
|
+
"default": "./dist/react.cjs"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
67
79
|
"./package.json": "./package.json"
|
|
68
80
|
},
|
|
69
81
|
"scripts": {
|
|
70
|
-
"build": "tsdown && npm run lint:publint && npm run lint:attw",
|
|
82
|
+
"build": "tsdown && node scripts/verify-build.mjs && npm run lint:publint && npm run lint:attw",
|
|
71
83
|
"dev": "tsdown --watch",
|
|
72
84
|
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.tools.json",
|
|
73
85
|
"test": "vitest run",
|
|
@@ -80,7 +92,8 @@
|
|
|
80
92
|
"release": "changeset publish"
|
|
81
93
|
},
|
|
82
94
|
"peerDependencies": {
|
|
83
|
-
"fabric": "^6.0.0 || ^7.0.0"
|
|
95
|
+
"fabric": "^6.0.0 || ^7.0.0",
|
|
96
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
84
97
|
},
|
|
85
98
|
"publishConfig": {
|
|
86
99
|
"access": "public",
|
|
@@ -101,13 +114,22 @@
|
|
|
101
114
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
102
115
|
"@changesets/cli": "^3.0.3",
|
|
103
116
|
"@types/node": "^22.20.4",
|
|
117
|
+
"@types/react": "^19.3.0",
|
|
118
|
+
"@types/react-dom": "^19.3.0",
|
|
104
119
|
"@vitest/browser-playwright": "^5.0.2",
|
|
105
120
|
"fabric": "^7.4.0",
|
|
106
121
|
"fabric6": "npm:fabric@^6.9.1",
|
|
107
122
|
"playwright": "^1.63.0",
|
|
108
123
|
"publint": "^0.3.24",
|
|
124
|
+
"react": "^19.3.0",
|
|
125
|
+
"react-dom": "^19.3.0",
|
|
109
126
|
"tsdown": "^0.23.0",
|
|
110
127
|
"typescript": "^5.9.3",
|
|
111
128
|
"vitest": "^5.0.2"
|
|
129
|
+
},
|
|
130
|
+
"peerDependenciesMeta": {
|
|
131
|
+
"react": {
|
|
132
|
+
"optional": true
|
|
133
|
+
}
|
|
112
134
|
}
|
|
113
135
|
}
|