@flowgram-vue/history-storage 0.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/dist/index.js ADDED
@@ -0,0 +1,302 @@
1
+ import { definePluginCreator } from '@flowgram-vue/core';
2
+ import { inject, injectable, ContainerModule } from 'inversify';
3
+ import { DisposableCollection } from '@flowgram-vue/utils';
4
+ import { HistoryManager, HistoryStackChangeType, HistoryStack } from '@flowgram-vue/history';
5
+ import Dexie, { liveQuery } from 'dexie';
6
+ import { groupBy } from 'lodash-es';
7
+ import { reactive, watchEffect, toValue } from 'vue';
8
+
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __decorateClass = (decorators, target, key, kind) => {
12
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
13
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
14
+ if (decorator = decorators[i])
15
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
16
+ if (kind && result) __defProp(target, key, result);
17
+ return result;
18
+ };
19
+ var HistoryDatabase = class extends Dexie {
20
+ constructor(databaseName = "ide-history-storage") {
21
+ super(databaseName);
22
+ this.resourceStorageLimit = 100;
23
+ this.version(1).stores({
24
+ history: "++id, &uuid, resourceURI",
25
+ operation: "++id, &uuid, historyId, uri, resourceURI"
26
+ });
27
+ }
28
+ /**
29
+ * 某个uri下所有的history记录
30
+ * @param resourceURI 资源uri
31
+ * @returns
32
+ */
33
+ allHistoryByResourceURI(resourceURI) {
34
+ return this.history.where({ resourceURI }).toArray();
35
+ }
36
+ /**
37
+ * 根据uuid获取历史
38
+ * @param uuid
39
+ * @returns
40
+ */
41
+ getHistoryByUUID(uuid) {
42
+ return this.history.get({ uuid });
43
+ }
44
+ /**
45
+ * 某个uri下所有的operation记录
46
+ * @param resourceURI 资源uri
47
+ * @returns
48
+ */
49
+ allOperationByResourceURI(resourceURI) {
50
+ return this.operation.where({ resourceURI }).toArray();
51
+ }
52
+ /**
53
+ * 添加历史记录
54
+ * @param history 历史记录
55
+ * @param operations 操作记录
56
+ * @returns
57
+ */
58
+ addHistoryRecord(history, operations) {
59
+ return this.transaction("rw", this.history, this.operation, async () => {
60
+ const count = await this.history.where({ resourceURI: history.resourceURI }).count();
61
+ if (count >= this.resourceStorageLimit) {
62
+ const limit = count - this.resourceStorageLimit;
63
+ const items = await this.history.where({ resourceURI: history.resourceURI }).limit(limit).toArray();
64
+ const ids = items.map((i) => i.id);
65
+ const uuid = items.map((i) => i.uuid);
66
+ await Promise.all([
67
+ this.history.bulkDelete(ids),
68
+ ...uuid.map(async (uuid2) => {
69
+ await this.operation.where({ historyId: uuid2 }).delete();
70
+ })
71
+ ]);
72
+ }
73
+ return Promise.all([this.history.add(history), this.operation.bulkAdd(operations)]);
74
+ });
75
+ }
76
+ /**
77
+ * 更新历史记录
78
+ * @param historyRecord
79
+ * @returns
80
+ */
81
+ async updateHistoryByUUID(uuid, historyRecord) {
82
+ const history = await this.getHistoryByUUID(uuid);
83
+ if (!history) {
84
+ console.warn("no history record found");
85
+ return;
86
+ }
87
+ return this.history.update(history.id, historyRecord);
88
+ }
89
+ /**
90
+ * 添加操作记录
91
+ * @param record 操作记录
92
+ * @returns
93
+ */
94
+ addOperationRecord(record) {
95
+ return this.operation.add(record);
96
+ }
97
+ /**
98
+ * 更新操作记录
99
+ * @param record 操作记录
100
+ * @returns
101
+ */
102
+ async updateOperationRecord(record) {
103
+ const op = await this.operation.where({ uuid: record.uuid }).first();
104
+ if (!op) {
105
+ console.warn("no operation record found");
106
+ return;
107
+ }
108
+ return this.operation.put({
109
+ id: op.id,
110
+ ...record
111
+ });
112
+ }
113
+ /**
114
+ * 重置数据库
115
+ * @returns
116
+ */
117
+ reset() {
118
+ return this.transaction("rw", this.history, this.operation, async () => {
119
+ await Promise.all(this.tables.map((table) => table.clear()));
120
+ });
121
+ }
122
+ /**
123
+ * 清空某个资源下所有的数据
124
+ * @param resourceURI
125
+ * @returns
126
+ */
127
+ resetByResourceURI(resourceURI) {
128
+ return this.transaction("rw", this.history, this.operation, async () => {
129
+ await Promise.all(this.tables.map((table) => table.where({ resourceURI }).delete()));
130
+ });
131
+ }
132
+ };
133
+
134
+ // src/history-storage-manager.ts
135
+ var HistoryStorageManager = class {
136
+ constructor() {
137
+ this._toDispose = new DisposableCollection();
138
+ }
139
+ /**
140
+ * 初始化
141
+ * @param ctx
142
+ */
143
+ onInit(_ctx, opts) {
144
+ this.db = new HistoryDatabase(opts?.databaseName);
145
+ if (opts?.resourceStorageLimit) {
146
+ this.db.resourceStorageLimit = opts.resourceStorageLimit;
147
+ }
148
+ this._toDispose.push(
149
+ this.historyManager.historyStack.onChange((event) => {
150
+ if (event.type === HistoryStackChangeType.ADD) {
151
+ const [history, operations] = this.historyItemToRecord(event.service, event.value);
152
+ this.db.addHistoryRecord(history, operations).catch(console.error);
153
+ }
154
+ if ([HistoryStackChangeType.ADD_OPERATION, HistoryStackChangeType.UPDATE_OPERATION].includes(
155
+ event.type
156
+ )) {
157
+ const {
158
+ service,
159
+ value: { historyItem }
160
+ } = event;
161
+ this.db.updateHistoryByUUID(historyItem.id, {
162
+ resourceJSON: service.getSnapshot() || ""
163
+ }).catch(console.error);
164
+ }
165
+ if (event.type === HistoryStackChangeType.ADD_OPERATION) {
166
+ const operationRecord = this.historyOperationToRecord(
167
+ event.value.historyItem,
168
+ event.value.operation
169
+ );
170
+ this.db.addOperationRecord(operationRecord).catch(console.error);
171
+ }
172
+ if (event.type === HistoryStackChangeType.UPDATE_OPERATION) {
173
+ const operationRecord = this.historyOperationToRecord(
174
+ event.value.historyItem,
175
+ event.value.operation
176
+ );
177
+ this.db.updateOperationRecord(operationRecord).catch(console.error);
178
+ }
179
+ })
180
+ );
181
+ }
182
+ /**
183
+ * 内存历史转数据表记录
184
+ * @param historyItem
185
+ * @returns
186
+ */
187
+ historyItemToRecord(historyService, historyItem) {
188
+ const operations = historyItem.operations.map(
189
+ (op) => this.historyOperationToRecord(historyItem, op)
190
+ );
191
+ return [
192
+ {
193
+ uuid: historyItem.id,
194
+ timestamp: historyItem.timestamp,
195
+ type: historyItem.type,
196
+ resourceURI: historyItem.uri?.toString() || "",
197
+ resourceJSON: historyService.getSnapshot() || ""
198
+ },
199
+ operations
200
+ ];
201
+ }
202
+ /**
203
+ * 内存操作转数据表操作
204
+ * @param historyItem
205
+ * @param op
206
+ * @returns
207
+ */
208
+ historyOperationToRecord(historyItem, op) {
209
+ return {
210
+ uuid: op.id,
211
+ type: op.type,
212
+ timestamp: op.timestamp,
213
+ label: op.label || "",
214
+ uri: op?.uri?.toString() || "",
215
+ resourceURI: historyItem.uri?.toString() || "",
216
+ description: op.description || "",
217
+ value: JSON.stringify(op.value),
218
+ historyId: historyItem.id
219
+ };
220
+ }
221
+ /**
222
+ * 销毁
223
+ */
224
+ dispose() {
225
+ this._toDispose.dispose();
226
+ }
227
+ };
228
+ __decorateClass([
229
+ inject(HistoryManager)
230
+ ], HistoryStorageManager.prototype, "historyManager", 2);
231
+ HistoryStorageManager = __decorateClass([
232
+ injectable()
233
+ ], HistoryStorageManager);
234
+ var HistoryStorageContainerModule = new ContainerModule((bind) => {
235
+ bind(HistoryStorageManager).toSelf().inSingletonScope();
236
+ });
237
+
238
+ // src/create-history-storage-plugin.ts
239
+ var createHistoryStoragePlugin = definePluginCreator({
240
+ onBind: ({ bind, rebind }) => {
241
+ },
242
+ onInit(ctx, opts) {
243
+ const historyStorageManager = ctx.get(HistoryStorageManager);
244
+ historyStorageManager.onInit(ctx, opts);
245
+ },
246
+ onDispose(ctx) {
247
+ const historyStorageManager = ctx.get(HistoryStorageManager);
248
+ historyStorageManager.dispose();
249
+ },
250
+ containerModules: [HistoryStorageContainerModule]
251
+ });
252
+ function useStorageHistoryItems(historyStorageManager, resourceURI) {
253
+ const state = reactive({
254
+ items: []
255
+ });
256
+ watchEffect((onCleanup) => {
257
+ const uri = toValue(resourceURI);
258
+ const observable = liveQuery(async () => {
259
+ const [historyItems, operations] = await Promise.all([
260
+ historyStorageManager.db.allHistoryByResourceURI(uri),
261
+ historyStorageManager.db.allOperationByResourceURI(uri)
262
+ ]);
263
+ const grouped = groupBy(
264
+ operations.map((o) => ({
265
+ id: o.uuid,
266
+ timestamp: o.timestamp,
267
+ type: o.type,
268
+ label: o.label,
269
+ description: o.description,
270
+ value: o.value ? JSON.parse(o.value) : void 0,
271
+ uri: o.uri,
272
+ historyId: o.historyId
273
+ })),
274
+ "historyId"
275
+ );
276
+ return historyItems.sort((a, b) => b.id - a.id).map(
277
+ (historyItem) => ({
278
+ id: historyItem.uuid,
279
+ type: historyItem.type,
280
+ timestamp: historyItem.timestamp,
281
+ operations: grouped[historyItem.uuid] || [],
282
+ time: HistoryStack.dateFormat(historyItem.timestamp),
283
+ uri: historyItem.resourceURI
284
+ })
285
+ );
286
+ });
287
+ const subscription = observable.subscribe({
288
+ next: (value) => {
289
+ state.items = value || [];
290
+ },
291
+ error: (err) => {
292
+ console.error(err);
293
+ }
294
+ });
295
+ onCleanup(() => subscription.unsubscribe());
296
+ });
297
+ return state;
298
+ }
299
+
300
+ export { HistoryDatabase, HistoryStorageContainerModule, HistoryStorageManager, createHistoryStoragePlugin, useStorageHistoryItems };
301
+ //# sourceMappingURL=index.js.map
302
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/history-database.ts","../src/history-storage-manager.ts","../src/history-storage-container-module.ts","../src/create-history-storage-plugin.ts","../src/use-storage-hisotry-items.ts"],"names":["uuid"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport Dexie, { type Table } from 'dexie';\n\nimport { HistoryOperationRecord, HistoryRecord } from './types';\n\n/**\n * 历史数据库\n */\nexport class HistoryDatabase extends Dexie {\n readonly history: Table<HistoryRecord>;\n\n readonly operation: Table<HistoryOperationRecord>;\n\n resourceStorageLimit: number = 100;\n\n constructor(databaseName: string = 'ide-history-storage') {\n super(databaseName);\n this.version(1).stores({\n history: '++id, &uuid, resourceURI',\n operation: '++id, &uuid, historyId, uri, resourceURI',\n });\n }\n\n /**\n * 某个uri下所有的history记录\n * @param resourceURI 资源uri\n * @returns\n */\n allHistoryByResourceURI(resourceURI: string) {\n return this.history.where({ resourceURI }).toArray();\n }\n\n /**\n * 根据uuid获取历史\n * @param uuid\n * @returns\n */\n getHistoryByUUID(uuid: string) {\n return this.history.get({ uuid });\n }\n\n /**\n * 某个uri下所有的operation记录\n * @param resourceURI 资源uri\n * @returns\n */\n allOperationByResourceURI(resourceURI: string) {\n return this.operation.where({ resourceURI }).toArray();\n }\n\n /**\n * 添加历史记录\n * @param history 历史记录\n * @param operations 操作记录\n * @returns\n */\n addHistoryRecord(history: HistoryRecord, operations: HistoryOperationRecord[]) {\n return this.transaction('rw', this.history, this.operation, async () => {\n const count = await this.history.where({ resourceURI: history.resourceURI }).count();\n if (count >= this.resourceStorageLimit) {\n const limit = count - this.resourceStorageLimit;\n const items = await this.history\n .where({ resourceURI: history.resourceURI })\n .limit(limit)\n .toArray();\n const ids = items.map(i => i.id);\n const uuid = items.map(i => i.uuid);\n await Promise.all([\n this.history.bulkDelete(ids),\n ...uuid.map(async uuid => {\n await this.operation.where({ historyId: uuid }).delete();\n }),\n ]);\n }\n\n return Promise.all([this.history.add(history), this.operation.bulkAdd(operations)]);\n });\n }\n\n /**\n * 更新历史记录\n * @param historyRecord\n * @returns\n */\n async updateHistoryByUUID(uuid: string, historyRecord: Partial<HistoryRecord>) {\n const history = await this.getHistoryByUUID(uuid);\n if (!history) {\n console.warn('no history record found');\n return;\n }\n return this.history.update(history.id, historyRecord);\n }\n\n /**\n * 添加操作记录\n * @param record 操作记录\n * @returns\n */\n addOperationRecord(record: HistoryOperationRecord) {\n return this.operation.add(record);\n }\n\n /**\n * 更新操作记录\n * @param record 操作记录\n * @returns\n */\n async updateOperationRecord(record: HistoryOperationRecord) {\n const op = await this.operation.where({ uuid: record.uuid }).first();\n if (!op) {\n console.warn('no operation record found');\n return;\n }\n return this.operation.put({\n id: op.id,\n ...record,\n });\n }\n\n /**\n * 重置数据库\n * @returns\n */\n reset() {\n return this.transaction('rw', this.history, this.operation, async () => {\n await Promise.all(this.tables.map(table => table.clear()));\n });\n }\n\n /**\n * 清空某个资源下所有的数据\n * @param resourceURI\n * @returns\n */\n resetByResourceURI(resourceURI: string) {\n return this.transaction('rw', this.history, this.operation, async () => {\n await Promise.all(this.tables.map(table => table.where({ resourceURI }).delete()));\n });\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { inject, injectable } from 'inversify';\nimport { DisposableCollection } from '@flowgram-vue/utils';\nimport {\n HistoryItem,\n HistoryManager,\n HistoryOperation,\n HistoryStackChangeType,\n HistoryService,\n HistoryStackAddOperationEvent,\n HistoryStackUpdateOperationEvent,\n} from '@flowgram-vue/history';\nimport { PluginContext } from '@flowgram-vue/core';\n\nimport { HistoryOperationRecord, HistoryRecord, HistoryStoragePluginOptions } from './types';\nimport { HistoryDatabase } from './history-database';\n\n/**\n * 历史存储管理\n */\n@injectable()\nexport class HistoryStorageManager {\n private _toDispose = new DisposableCollection();\n\n db: HistoryDatabase;\n\n @inject(HistoryManager)\n protected historyManager: HistoryManager;\n\n /**\n * 初始化\n * @param ctx\n */\n onInit(_ctx: PluginContext, opts: HistoryStoragePluginOptions) {\n this.db = new HistoryDatabase(opts?.databaseName);\n\n if (opts?.resourceStorageLimit) {\n this.db.resourceStorageLimit = opts.resourceStorageLimit;\n }\n\n this._toDispose.push(\n this.historyManager.historyStack.onChange(event => {\n if (event.type === HistoryStackChangeType.ADD) {\n const [history, operations] = this.historyItemToRecord(event.service, event.value);\n this.db.addHistoryRecord(history, operations).catch(console.error);\n }\n\n // operation merge的时候需要更新snapshot\n if (\n [HistoryStackChangeType.ADD_OPERATION, HistoryStackChangeType.UPDATE_OPERATION].includes(\n event.type,\n )\n ) {\n const {\n service,\n value: { historyItem },\n } = event as HistoryStackAddOperationEvent | HistoryStackUpdateOperationEvent;\n // 更新快照\n this.db\n .updateHistoryByUUID(historyItem.id, {\n resourceJSON: service.getSnapshot() || '',\n })\n .catch(console.error);\n }\n\n if (event.type === HistoryStackChangeType.ADD_OPERATION) {\n const operationRecord: HistoryOperationRecord = this.historyOperationToRecord(\n event.value.historyItem,\n event.value.operation,\n );\n this.db.addOperationRecord(operationRecord).catch(console.error);\n }\n if (event.type === HistoryStackChangeType.UPDATE_OPERATION) {\n const operationRecord: HistoryOperationRecord = this.historyOperationToRecord(\n event.value.historyItem,\n event.value.operation,\n );\n this.db.updateOperationRecord(operationRecord).catch(console.error);\n }\n }),\n );\n }\n\n /**\n * 内存历史转数据表记录\n * @param historyItem\n * @returns\n */\n historyItemToRecord(\n historyService: HistoryService,\n historyItem: HistoryItem,\n ): [HistoryRecord, HistoryOperationRecord[]] {\n const operations = historyItem.operations.map(op =>\n this.historyOperationToRecord(historyItem, op),\n );\n\n return [\n {\n uuid: historyItem.id,\n timestamp: historyItem.timestamp,\n type: historyItem.type,\n resourceURI: historyItem.uri?.toString() || '',\n resourceJSON: historyService.getSnapshot() || '',\n },\n operations,\n ];\n }\n\n /**\n * 内存操作转数据表操作\n * @param historyItem\n * @param op\n * @returns\n */\n historyOperationToRecord(historyItem: HistoryItem, op: HistoryOperation): HistoryOperationRecord {\n return {\n uuid: op.id,\n type: op.type,\n timestamp: op.timestamp,\n label: op.label || '',\n uri: op?.uri?.toString() || '',\n resourceURI: historyItem.uri?.toString() || '',\n description: op.description || '',\n value: JSON.stringify(op.value),\n historyId: historyItem.id,\n };\n }\n\n /**\n * 销毁\n */\n dispose() {\n this._toDispose.dispose();\n }\n}\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { ContainerModule } from 'inversify';\n\nimport { HistoryStorageManager } from './history-storage-manager';\n\nexport const HistoryStorageContainerModule = new ContainerModule(bind => {\n bind(HistoryStorageManager).toSelf().inSingletonScope();\n});\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { definePluginCreator } from '@flowgram-vue/core';\n\nimport { HistoryStoragePluginOptions } from './types';\nimport { HistoryStorageManager } from './history-storage-manager';\nimport { HistoryStorageContainerModule } from './history-storage-container-module';\n\nexport const createHistoryStoragePlugin = definePluginCreator<HistoryStoragePluginOptions>({\n onBind: ({ bind, rebind }) => {},\n onInit(ctx, opts): void {\n const historyStorageManager = ctx.get<HistoryStorageManager>(HistoryStorageManager);\n historyStorageManager.onInit(ctx, opts);\n },\n onDispose(ctx) {\n const historyStorageManager = ctx.get<HistoryStorageManager>(HistoryStorageManager);\n historyStorageManager.dispose();\n },\n containerModules: [HistoryStorageContainerModule],\n});\n","/**\n * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n */\n\nimport { liveQuery } from 'dexie';\nimport { groupBy } from 'lodash-es';\nimport { reactive, toValue, watchEffect } from 'vue';\nimport type { MaybeRefOrGetter } from 'vue';\nimport { HistoryItem, HistoryOperation, HistoryStack } from '@flowgram-vue/history';\n\nimport { HistoryStorageManager } from './history-storage-manager';\n\nexport function useStorageHistoryItems(\n historyStorageManager: HistoryStorageManager,\n resourceURI: MaybeRefOrGetter<string>\n): {\n items: HistoryItem[];\n} {\n const state = reactive<{ items: HistoryItem[] }>({\n items: [],\n });\n\n watchEffect((onCleanup) => {\n const uri = toValue(resourceURI);\n const observable = liveQuery(async () => {\n const [historyItems, operations] = await Promise.all([\n historyStorageManager.db.allHistoryByResourceURI(uri),\n historyStorageManager.db.allOperationByResourceURI(uri),\n ]);\n\n const grouped = groupBy<HistoryOperation>(\n operations.map((o) => ({\n id: o.uuid,\n timestamp: o.timestamp,\n type: o.type,\n label: o.label,\n description: o.description,\n value: o.value ? JSON.parse(o.value) : undefined,\n uri: o.uri,\n historyId: o.historyId,\n })),\n 'historyId'\n );\n return historyItems\n .sort((a, b) => (b.id as number) - (a.id as number))\n .map(\n (historyItem) =>\n ({\n id: historyItem.uuid,\n type: historyItem.type,\n timestamp: historyItem.timestamp,\n operations: grouped[historyItem.uuid] || [],\n time: HistoryStack.dateFormat(historyItem.timestamp),\n uri: historyItem.resourceURI,\n }) as HistoryItem\n );\n });\n\n const subscription = observable.subscribe({\n next: (value) => {\n state.items = value || [];\n },\n error: (err) => {\n console.error(err);\n },\n });\n\n onCleanup(() => subscription.unsubscribe());\n });\n\n return state;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@flowgram-vue/history-storage",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "homepage": "https://flowgram.ai/",
7
+ "repository": "https://github.com/Crayon-hua/flowgram-vue",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "main": "./dist/index.cjs",
16
+ "types": "./dist/index.d.ts",
17
+ "files": [
18
+ "src",
19
+ "dist"
20
+ ],
21
+ "dependencies": {
22
+ "dexie": "4.0.4",
23
+ "inversify": "^6.0.1",
24
+ "lodash-es": "^4.17.21",
25
+ "nanoid": "^5.0.9",
26
+ "reflect-metadata": "~0.2.2",
27
+ "@flowgram-vue/core": "0.2.0",
28
+ "@flowgram-vue/history": "0.2.0",
29
+ "@flowgram-vue/utils": "0.2.0"
30
+ },
31
+ "peerDependencies": {
32
+ "vue": "^3.5.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/lodash-es": "^4.17.12",
36
+ "@vitest/coverage-v8": "^3.2.4",
37
+ "eslint": "^9.0.0",
38
+ "fake-indexeddb": "5.0.2",
39
+ "jsdom": "^26.1.0",
40
+ "typescript": "^5.8.3",
41
+ "vitest": "^3.2.4",
42
+ "vue": "^3.5.18",
43
+ "@flowgram-vue/build-config": "0.2.0",
44
+ "@flowgram-vue/eslint-config": "0.2.0",
45
+ "@flowgram-vue/ts-config": "0.2.0"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "registry": "https://registry.npmjs.org/"
50
+ },
51
+ "scripts": {
52
+ "build": "flowgram-build",
53
+ "test": "vitest run",
54
+ "lint": "eslint .",
55
+ "ts-check": "tsc --noEmit",
56
+ "type-check": "tsc --noEmit",
57
+ "build:fast": "flowgram-build --fast",
58
+ "build:watch": "flowgram-build --watch"
59
+ },
60
+ "module": "./dist/index.js"
61
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export const MOCK_RESOURCE_URI1 = 'resource-uri1'
7
+ export const MOCK_RESOURCE_URI2 = 'resource-uri2'
8
+
9
+ export const MOCK_HISTORY1 = {
10
+ resourceURI: MOCK_RESOURCE_URI1,
11
+ uuid: 'history1',
12
+ timestamp: 111,
13
+ type: 'push',
14
+ resourceJSON: 'resourceJSON',
15
+ }
16
+
17
+ export const MOCK_HISTORY2 = {
18
+ resourceURI: MOCK_RESOURCE_URI2,
19
+ uuid: 'history2',
20
+ timestamp: 111,
21
+ type: 'push',
22
+ resourceJSON: 'resourceJSON',
23
+ }
24
+
25
+ export const MOCK_OPERATION1 = {
26
+ historyId: 'history1',
27
+ uri: 'test-1',
28
+ uuid: 'operation1',
29
+ type: 'addFromNode',
30
+ value: 'value1',
31
+ resourceURI: MOCK_RESOURCE_URI1,
32
+ label: 'operation1-label',
33
+ description: 'operation1-description',
34
+ timestamp: 1,
35
+ }
36
+
37
+ export const MOCK_OPERATION2 = {
38
+ historyId: 'history1',
39
+ uri: 'test-2',
40
+ uuid: 'operation2',
41
+ type: 'deleteFromNode',
42
+ value: 'value2',
43
+ resourceURI: MOCK_RESOURCE_URI1,
44
+ label: 'operation2-label',
45
+ description: 'operation2-description',
46
+ timestamp: 2,
47
+ }
48
+
49
+ export const MOCK_OPERATION3 = {
50
+ historyId: 'history1',
51
+ uri: 'test-3',
52
+ uuid: 'operation3',
53
+ type: 'addText',
54
+ value: 'value3',
55
+ resourceURI: MOCK_RESOURCE_URI1,
56
+ label: 'operation3-label',
57
+ description: 'operation3-description',
58
+ timestamp: 3,
59
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { describe, it, beforeEach } from 'vitest';
7
+ import { cloneDeep, omit } from 'lodash-es';
8
+
9
+ import { HistoryOperationRecord, HistoryRecord } from '../types';
10
+ import { HistoryDatabase } from '../history-database';
11
+ import {
12
+ MOCK_HISTORY1,
13
+ MOCK_HISTORY2,
14
+ MOCK_OPERATION1,
15
+ MOCK_OPERATION2,
16
+ MOCK_OPERATION3,
17
+ MOCK_RESOURCE_URI1,
18
+ } from '../__mocks__';
19
+
20
+ describe('history-database', () => {
21
+ let db: HistoryDatabase;
22
+ let history1: HistoryRecord;
23
+ let history2: HistoryRecord;
24
+ let operation1: HistoryOperationRecord;
25
+ let operation2: HistoryOperationRecord;
26
+
27
+ beforeEach(async () => {
28
+ db = new HistoryDatabase();
29
+ await db.reset();
30
+ history1 = cloneDeep(MOCK_HISTORY1);
31
+ history2 = cloneDeep(MOCK_HISTORY2);
32
+ operation1 = cloneDeep(MOCK_OPERATION1);
33
+ operation2 = cloneDeep(MOCK_OPERATION2);
34
+ });
35
+
36
+ it('addHistoryRecord allHistoryByResourceURI allOperationByResourceURI', async () => {
37
+ const operations = [operation1, operation2];
38
+ const res = await db.addHistoryRecord(history1, operations);
39
+ await db.addHistoryRecord(history2, []);
40
+ expect(res.length).toEqual(2);
41
+ const [dbHistory] = await db.allHistoryByResourceURI(MOCK_RESOURCE_URI1);
42
+ expect(MOCK_HISTORY1).toEqual(omit(dbHistory, ['id']));
43
+ const dbOperations = await db.allOperationByResourceURI(MOCK_RESOURCE_URI1);
44
+ expect(operations).toEqual(dbOperations.map((o) => omit(o, ['id'])));
45
+
46
+ const operation3 = cloneDeep(MOCK_OPERATION3);
47
+
48
+ await db.addOperationRecord(operation3);
49
+
50
+ const dbOperations3 = await db.allOperationByResourceURI(MOCK_RESOURCE_URI1);
51
+ expect([MOCK_OPERATION1, MOCK_OPERATION2, MOCK_OPERATION3]).toEqual(
52
+ dbOperations3.map((o) => omit(o, ['id']))
53
+ );
54
+ });
55
+
56
+ it('getHistoryByUUID', async () => {
57
+ await db.addHistoryRecord(history1, []);
58
+ const res = await db.getHistoryByUUID(history1.uuid);
59
+ expect(omit(res, ['id'])).toEqual(MOCK_HISTORY1);
60
+ });
61
+
62
+ it('updateHistoryByUUID', async () => {
63
+ await db.addHistoryRecord(history1, []);
64
+ const dbHistory = await db.getHistoryByUUID(history1.uuid);
65
+ if (!dbHistory) {
66
+ throw new Error('no dbHistory');
67
+ }
68
+ const resourceJSON = 'newResourceJSON';
69
+ await db.updateHistoryByUUID(dbHistory.uuid, {
70
+ resourceJSON,
71
+ });
72
+ const [dbHistory1] = await db.allHistoryByResourceURI(MOCK_RESOURCE_URI1);
73
+ expect(dbHistory1.resourceJSON).toEqual(resourceJSON);
74
+ });
75
+
76
+ it('addOperationRecord', async () => {
77
+ await db.addOperationRecord(operation1);
78
+ await db.addOperationRecord(operation2);
79
+ const dbOperations = await db.allOperationByResourceURI(MOCK_RESOURCE_URI1);
80
+ expect([MOCK_OPERATION1, MOCK_OPERATION2]).toEqual(dbOperations.map((o) => omit(o, ['id'])));
81
+ });
82
+
83
+ it('updateOperationRecord', async () => {
84
+ await db.addOperationRecord(operation1);
85
+ await db.allOperationByResourceURI(MOCK_RESOURCE_URI1);
86
+
87
+ await db.updateOperationRecord({ ...MOCK_OPERATION2, uuid: MOCK_OPERATION1.uuid });
88
+
89
+ const [dbUpdatedOperation1] = await db.allOperationByResourceURI(MOCK_RESOURCE_URI1);
90
+ expect(omit(MOCK_OPERATION2, ['uuid'])).toEqual(omit(dbUpdatedOperation1, ['id', 'uuid']));
91
+ });
92
+
93
+ it('reset', async () => {
94
+ await db.addHistoryRecord(history1, [operation1, operation2]);
95
+ await db.reset();
96
+ const dbOperation = await db.allOperationByResourceURI(MOCK_RESOURCE_URI1);
97
+ const dbHistory = await db.allHistoryByResourceURI(MOCK_RESOURCE_URI1);
98
+ expect(dbOperation.length).toEqual(0);
99
+ expect(dbHistory.length).toEqual(0);
100
+ });
101
+
102
+ it('resetByResourceURI', async () => {
103
+ await db.addHistoryRecord(history1, [operation1, operation2]);
104
+ await db.resetByResourceURI(MOCK_RESOURCE_URI1);
105
+ const dbOperation = await db.allOperationByResourceURI(MOCK_RESOURCE_URI1);
106
+ const dbHistory = await db.allHistoryByResourceURI(MOCK_RESOURCE_URI1);
107
+ expect(dbOperation.length).toEqual(0);
108
+ expect(dbHistory.length).toEqual(0);
109
+ });
110
+
111
+ it('resourceStorageLimit', async () => {
112
+ db.resourceStorageLimit = 1;
113
+ await db.addHistoryRecord(history1, []);
114
+ await db.addHistoryRecord(history2, []);
115
+ const res = await db.allHistoryByResourceURI(MOCK_RESOURCE_URI1);
116
+ expect(res.length).toEqual(1);
117
+ });
118
+ });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { definePluginCreator } from '@flowgram-vue/core';
7
+
8
+ import { HistoryStoragePluginOptions } from './types';
9
+ import { HistoryStorageManager } from './history-storage-manager';
10
+ import { HistoryStorageContainerModule } from './history-storage-container-module';
11
+
12
+ export const createHistoryStoragePlugin = definePluginCreator<HistoryStoragePluginOptions>({
13
+ onBind: ({ bind, rebind }) => {},
14
+ onInit(ctx, opts): void {
15
+ const historyStorageManager = ctx.get<HistoryStorageManager>(HistoryStorageManager);
16
+ historyStorageManager.onInit(ctx, opts);
17
+ },
18
+ onDispose(ctx) {
19
+ const historyStorageManager = ctx.get<HistoryStorageManager>(HistoryStorageManager);
20
+ historyStorageManager.dispose();
21
+ },
22
+ containerModules: [HistoryStorageContainerModule],
23
+ });