@coco-vexel/coco-office 0.0.0-stage → 1.0.1
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/LICENSE +202 -0
- package/README.md +120 -2
- package/THIRD_PARTY_NOTICES.md +1188 -0
- package/dist/excel-worker-browser.cjs +6544 -0
- package/dist/excel-worker-browser.cjs.map +1 -0
- package/dist/excel-worker-browser.d.cts +2 -0
- package/dist/excel-worker-browser.d.ts +2 -0
- package/dist/excel-worker-browser.js +6523 -0
- package/dist/excel-worker-browser.js.map +1 -0
- package/dist/excel-worker.cjs +6547 -0
- package/dist/excel-worker.cjs.map +1 -0
- package/dist/excel-worker.d.cts +2 -0
- package/dist/excel-worker.d.ts +2 -0
- package/dist/excel-worker.js +6526 -0
- package/dist/excel-worker.js.map +1 -0
- package/dist/excel.cjs +12523 -0
- package/dist/excel.cjs.map +1 -0
- package/dist/excel.d.cts +2252 -0
- package/dist/excel.d.ts +2252 -0
- package/dist/excel.js +12368 -0
- package/dist/excel.js.map +1 -0
- package/dist/index.cjs +19 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel-worker-browser.cjs +12304 -0
- package/dist/kernel-worker-browser.cjs.map +1 -0
- package/dist/kernel-worker-browser.d.cts +2 -0
- package/dist/kernel-worker-browser.d.ts +2 -0
- package/dist/kernel-worker-browser.js +12283 -0
- package/dist/kernel-worker-browser.js.map +1 -0
- package/dist/kernel-worker.cjs +12309 -0
- package/dist/kernel-worker.cjs.map +1 -0
- package/dist/kernel-worker.d.cts +2 -0
- package/dist/kernel-worker.d.ts +2 -0
- package/dist/kernel-worker.js +12288 -0
- package/dist/kernel-worker.js.map +1 -0
- package/dist/pack.cjs +459 -0
- package/dist/pack.cjs.map +1 -0
- package/dist/pack.d.cts +119 -0
- package/dist/pack.d.ts +119 -0
- package/dist/pack.js +408 -0
- package/dist/pack.js.map +1 -0
- package/dist/ppt.cjs +7427 -0
- package/dist/ppt.cjs.map +1 -0
- package/dist/ppt.d.cts +1432 -0
- package/dist/ppt.d.ts +1432 -0
- package/dist/ppt.js +7355 -0
- package/dist/ppt.js.map +1 -0
- package/dist/shared.cjs +105 -0
- package/dist/shared.cjs.map +1 -0
- package/dist/shared.d.cts +31 -0
- package/dist/shared.d.ts +31 -0
- package/dist/shared.js +81 -0
- package/dist/shared.js.map +1 -0
- package/dist/types-BVkkdcfh.d.cts +54 -0
- package/dist/types-BVkkdcfh.d.ts +54 -0
- package/dist/types-C2vQfM-b.d.cts +102 -0
- package/dist/types-C2vQfM-b.d.ts +102 -0
- package/dist/view-DAkeR0RW.d.ts +1199 -0
- package/dist/view-DX2YAG5A.d.cts +1199 -0
- package/dist/word-view.cjs +1824 -0
- package/dist/word-view.cjs.map +1 -0
- package/dist/word-view.d.cts +2 -0
- package/dist/word-view.d.ts +2 -0
- package/dist/word-view.js +1791 -0
- package/dist/word-view.js.map +1 -0
- package/dist/word.cjs +14111 -0
- package/dist/word.cjs.map +1 -0
- package/dist/word.d.cts +3042 -0
- package/dist/word.d.ts +3042 -0
- package/dist/word.js +14016 -0
- package/dist/word.js.map +1 -0
- package/package.json +103 -3
package/dist/shared.cjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
23
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
24
|
+
var __export = (target, all) => {
|
|
25
|
+
for (var name in all)
|
|
26
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
27
|
+
};
|
|
28
|
+
var __copyProps = (to, from, except, desc) => {
|
|
29
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
30
|
+
for (let key of __getOwnPropNames(from))
|
|
31
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
32
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
33
|
+
}
|
|
34
|
+
return to;
|
|
35
|
+
};
|
|
36
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
38
|
+
|
|
39
|
+
// src/shared/index.ts
|
|
40
|
+
var shared_exports = {};
|
|
41
|
+
__export(shared_exports, {
|
|
42
|
+
InMemorySnapshotStorage: () => InMemorySnapshotStorage
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(shared_exports);
|
|
45
|
+
|
|
46
|
+
// src/shared/snapshot/in-memory.ts
|
|
47
|
+
var _InMemorySnapshotStorage = class _InMemorySnapshotStorage {
|
|
48
|
+
constructor() {
|
|
49
|
+
/** 嵌套 Map:docId → (snapshotId → entry) */
|
|
50
|
+
__publicField(this, "store", /* @__PURE__ */ new Map());
|
|
51
|
+
}
|
|
52
|
+
getBucket(docId, createIfMissing) {
|
|
53
|
+
let bucket = this.store.get(docId);
|
|
54
|
+
if (!bucket && createIfMissing) {
|
|
55
|
+
bucket = /* @__PURE__ */ new Map();
|
|
56
|
+
this.store.set(docId, bucket);
|
|
57
|
+
}
|
|
58
|
+
return bucket;
|
|
59
|
+
}
|
|
60
|
+
async save(docId, id, payload, meta) {
|
|
61
|
+
const bucket = this.getBucket(docId, true);
|
|
62
|
+
bucket.set(id, { payload, pinned: (meta == null ? void 0 : meta.pinned) === true });
|
|
63
|
+
}
|
|
64
|
+
async load(docId, id) {
|
|
65
|
+
const bucket = this.getBucket(docId, false);
|
|
66
|
+
const entry = bucket == null ? void 0 : bucket.get(id);
|
|
67
|
+
if (!entry) {
|
|
68
|
+
throw new Error(`snapshot not found: docId=${docId} id=${id}`);
|
|
69
|
+
}
|
|
70
|
+
return entry.payload;
|
|
71
|
+
}
|
|
72
|
+
async delete(docId, id) {
|
|
73
|
+
var _a;
|
|
74
|
+
(_a = this.getBucket(docId, false)) == null ? void 0 : _a.delete(id);
|
|
75
|
+
}
|
|
76
|
+
async list(docId) {
|
|
77
|
+
const bucket = this.getBucket(docId, false);
|
|
78
|
+
if (!bucket) return [];
|
|
79
|
+
const infos = Array.from(bucket.values()).map((entry) => {
|
|
80
|
+
const base = entry.payload.info;
|
|
81
|
+
return entry.pinned ? __spreadProps(__spreadValues({}, base), { pinned: true }) : base;
|
|
82
|
+
});
|
|
83
|
+
return infos.sort((a, b) => {
|
|
84
|
+
const na = extractIdNum(a.id);
|
|
85
|
+
const nb = extractIdNum(b.id);
|
|
86
|
+
if (na !== null && nb !== null) return na - nb;
|
|
87
|
+
return a.id.localeCompare(b.id);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async clear(docId) {
|
|
91
|
+
this.store.delete(docId);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
__name(_InMemorySnapshotStorage, "InMemorySnapshotStorage");
|
|
95
|
+
var InMemorySnapshotStorage = _InMemorySnapshotStorage;
|
|
96
|
+
function extractIdNum(id) {
|
|
97
|
+
const m = /^snap-(\d+)$/.exec(id);
|
|
98
|
+
return m ? Number(m[1]) : null;
|
|
99
|
+
}
|
|
100
|
+
__name(extractIdNum, "extractIdNum");
|
|
101
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
102
|
+
0 && (module.exports = {
|
|
103
|
+
InMemorySnapshotStorage
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=shared.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/index.ts","../src/shared/snapshot/in-memory.ts"],"sourcesContent":["export * from './snapshot/index.js';\n","/**\n * InMemorySnapshotStorage —— 默认实现,进程内 Map,刷新即失。\n *\n * 适用:测试 / CLI / 不要持久化的场景。宿主持久化请自行实现 SnapshotStorageAdapter\n * 并通过 `LoadOptions.snapshotStorage` 或 `WordDocument.setDefaultSnapshotStorage`\n * 注入(详见 docs/03.2 §8.5)。\n *\n * docId 命名空间隔离由嵌套 Map 实现:{ docId → { snapshotId → entry } }。\n *\n * **pinned 支持(#108,docs/03.2 §3.3)**:\n * - `save` 接受 `meta.pinned`,存进 entry 并在 `list` 的 SnapshotInfo 上还原\n * - `list` 里 `pinned === true` 的快照标志 SnapshotApi 的 FIFO 跳过它们\n */\nimport type { SnapshotInfo, SnapshotPayload, SnapshotStorageAdapter } from './types.js';\n\n/** Map 里的条目:payload + pinned bit(bit 与 payload 解耦,便于未来扩展 meta) */\ninterface StoredEntry {\n payload: SnapshotPayload;\n pinned: boolean;\n}\n\nexport class InMemorySnapshotStorage implements SnapshotStorageAdapter {\n /** 嵌套 Map:docId → (snapshotId → entry) */\n private readonly store = new Map<string, Map<string, StoredEntry>>();\n\n private getBucket(docId: string, createIfMissing: boolean): Map<string, StoredEntry> | undefined {\n let bucket = this.store.get(docId);\n if (!bucket && createIfMissing) {\n bucket = new Map();\n this.store.set(docId, bucket);\n }\n return bucket;\n }\n\n async save(\n docId: string,\n id: string,\n payload: SnapshotPayload,\n meta?: { pinned?: boolean },\n ): Promise<void> {\n const bucket = this.getBucket(docId, true)!;\n bucket.set(id, { payload, pinned: meta?.pinned === true });\n }\n\n async load(docId: string, id: string): Promise<SnapshotPayload> {\n const bucket = this.getBucket(docId, false);\n const entry = bucket?.get(id);\n if (!entry) {\n throw new Error(`snapshot not found: docId=${docId} id=${id}`);\n }\n return entry.payload;\n }\n\n async delete(docId: string, id: string): Promise<void> {\n // 幂等:不存在时静默\n this.getBucket(docId, false)?.delete(id);\n }\n\n async list(docId: string): Promise<SnapshotInfo[]> {\n const bucket = this.getBucket(docId, false);\n if (!bucket) return [];\n // [契约遵守] 按 id 自增数字升序返回(snap-0, snap-1, ...);\n // SnapshotStorageAdapter.list 契约要求,FIFO 淘汰依赖此顺序。\n // 截取 suffix 数字比较,非数字走字典序回退。\n // pinned 体现在 SnapshotInfo.pinned 字段(不参与排序,仅作为 FIFO 淘汰的筛选器)\n const infos = Array.from(bucket.values()).map((entry) => {\n // 重新构造 info 以携带 pinned(payload.info 来自 SnapshotApi,本 adapter 持有 pinned 真相)\n const base = entry.payload.info;\n return entry.pinned ? { ...base, pinned: true } : base;\n });\n return infos.sort((a, b) => {\n const na = extractIdNum(a.id);\n const nb = extractIdNum(b.id);\n if (na !== null && nb !== null) return na - nb;\n return a.id.localeCompare(b.id);\n });\n }\n\n async clear(docId: string): Promise<void> {\n this.store.delete(docId);\n }\n}\n\n/** 从 'snap-123' 提取 123;非 'snap-\\d+' 模式返 null */\nfunction extractIdNum(id: string): number | null {\n const m = /^snap-(\\d+)$/.exec(id);\n return m ? Number(m[1]) : null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACqBO,IAAM,2BAAN,MAAM,yBAA0D;AAAA,EAAhE;AAEL;AAAA,wBAAiB,SAAQ,oBAAI,IAAsC;AAAA;AAAA,EAE3D,UAAU,OAAe,iBAAgE;AAC/F,QAAI,SAAS,KAAK,MAAM,IAAI,KAAK;AACjC,QAAI,CAAC,UAAU,iBAAiB;AAC9B,eAAS,oBAAI,IAAI;AACjB,WAAK,MAAM,IAAI,OAAO,MAAM;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KACJ,OACA,IACA,SACA,MACe;AACf,UAAM,SAAS,KAAK,UAAU,OAAO,IAAI;AACzC,WAAO,IAAI,IAAI,EAAE,SAAS,SAAQ,6BAAM,YAAW,KAAK,CAAC;AAAA,EAC3D;AAAA,EAEA,MAAM,KAAK,OAAe,IAAsC;AAC9D,UAAM,SAAS,KAAK,UAAU,OAAO,KAAK;AAC1C,UAAM,QAAQ,iCAAQ,IAAI;AAC1B,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,6BAA6B,KAAK,OAAO,EAAE,EAAE;AAAA,IAC/D;AACA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,OAAO,OAAe,IAA2B;AArDzD;AAuDI,eAAK,UAAU,OAAO,KAAK,MAA3B,mBAA8B,OAAO;AAAA,EACvC;AAAA,EAEA,MAAM,KAAK,OAAwC;AACjD,UAAM,SAAS,KAAK,UAAU,OAAO,KAAK;AAC1C,QAAI,CAAC,OAAQ,QAAO,CAAC;AAKrB,UAAM,QAAQ,MAAM,KAAK,OAAO,OAAO,CAAC,EAAE,IAAI,CAAC,UAAU;AAEvD,YAAM,OAAO,MAAM,QAAQ;AAC3B,aAAO,MAAM,SAAS,iCAAK,OAAL,EAAW,QAAQ,KAAK,KAAI;AAAA,IACpD,CAAC;AACD,WAAO,MAAM,KAAK,CAAC,GAAG,MAAM;AAC1B,YAAM,KAAK,aAAa,EAAE,EAAE;AAC5B,YAAM,KAAK,aAAa,EAAE,EAAE;AAC5B,UAAI,OAAO,QAAQ,OAAO,KAAM,QAAO,KAAK;AAC5C,aAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,IAChC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAM,OAA8B;AACxC,SAAK,MAAM,OAAO,KAAK;AAAA,EACzB;AACF;AA5DuE;AAAhE,IAAM,0BAAN;AA+DP,SAAS,aAAa,IAA2B;AAC/C,QAAM,IAAI,eAAe,KAAK,EAAE;AAChC,SAAO,IAAI,OAAO,EAAE,CAAC,CAAC,IAAI;AAC5B;AAHS;","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { S as SnapshotStorageAdapter, a as SnapshotPayload, b as SnapshotInfo } from './types-C2vQfM-b.cjs';
|
|
2
|
+
export { C as ChangeLog } from './types-C2vQfM-b.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* InMemorySnapshotStorage —— 默认实现,进程内 Map,刷新即失。
|
|
6
|
+
*
|
|
7
|
+
* 适用:测试 / CLI / 不要持久化的场景。宿主持久化请自行实现 SnapshotStorageAdapter
|
|
8
|
+
* 并通过 `LoadOptions.snapshotStorage` 或 `WordDocument.setDefaultSnapshotStorage`
|
|
9
|
+
* 注入(详见 docs/03.2 §8.5)。
|
|
10
|
+
*
|
|
11
|
+
* docId 命名空间隔离由嵌套 Map 实现:{ docId → { snapshotId → entry } }。
|
|
12
|
+
*
|
|
13
|
+
* **pinned 支持(#108,docs/03.2 §3.3)**:
|
|
14
|
+
* - `save` 接受 `meta.pinned`,存进 entry 并在 `list` 的 SnapshotInfo 上还原
|
|
15
|
+
* - `list` 里 `pinned === true` 的快照标志 SnapshotApi 的 FIFO 跳过它们
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare class InMemorySnapshotStorage implements SnapshotStorageAdapter {
|
|
19
|
+
/** 嵌套 Map:docId → (snapshotId → entry) */
|
|
20
|
+
private readonly store;
|
|
21
|
+
private getBucket;
|
|
22
|
+
save(docId: string, id: string, payload: SnapshotPayload, meta?: {
|
|
23
|
+
pinned?: boolean;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
load(docId: string, id: string): Promise<SnapshotPayload>;
|
|
26
|
+
delete(docId: string, id: string): Promise<void>;
|
|
27
|
+
list(docId: string): Promise<SnapshotInfo[]>;
|
|
28
|
+
clear(docId: string): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { InMemorySnapshotStorage, SnapshotInfo, SnapshotPayload, SnapshotStorageAdapter };
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { S as SnapshotStorageAdapter, a as SnapshotPayload, b as SnapshotInfo } from './types-C2vQfM-b.js';
|
|
2
|
+
export { C as ChangeLog } from './types-C2vQfM-b.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* InMemorySnapshotStorage —— 默认实现,进程内 Map,刷新即失。
|
|
6
|
+
*
|
|
7
|
+
* 适用:测试 / CLI / 不要持久化的场景。宿主持久化请自行实现 SnapshotStorageAdapter
|
|
8
|
+
* 并通过 `LoadOptions.snapshotStorage` 或 `WordDocument.setDefaultSnapshotStorage`
|
|
9
|
+
* 注入(详见 docs/03.2 §8.5)。
|
|
10
|
+
*
|
|
11
|
+
* docId 命名空间隔离由嵌套 Map 实现:{ docId → { snapshotId → entry } }。
|
|
12
|
+
*
|
|
13
|
+
* **pinned 支持(#108,docs/03.2 §3.3)**:
|
|
14
|
+
* - `save` 接受 `meta.pinned`,存进 entry 并在 `list` 的 SnapshotInfo 上还原
|
|
15
|
+
* - `list` 里 `pinned === true` 的快照标志 SnapshotApi 的 FIFO 跳过它们
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare class InMemorySnapshotStorage implements SnapshotStorageAdapter {
|
|
19
|
+
/** 嵌套 Map:docId → (snapshotId → entry) */
|
|
20
|
+
private readonly store;
|
|
21
|
+
private getBucket;
|
|
22
|
+
save(docId: string, id: string, payload: SnapshotPayload, meta?: {
|
|
23
|
+
pinned?: boolean;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
load(docId: string, id: string): Promise<SnapshotPayload>;
|
|
26
|
+
delete(docId: string, id: string): Promise<void>;
|
|
27
|
+
list(docId: string): Promise<SnapshotInfo[]>;
|
|
28
|
+
clear(docId: string): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { InMemorySnapshotStorage, SnapshotInfo, SnapshotPayload, SnapshotStorageAdapter };
|
package/dist/shared.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
21
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
22
|
+
|
|
23
|
+
// src/shared/snapshot/in-memory.ts
|
|
24
|
+
var _InMemorySnapshotStorage = class _InMemorySnapshotStorage {
|
|
25
|
+
constructor() {
|
|
26
|
+
/** 嵌套 Map:docId → (snapshotId → entry) */
|
|
27
|
+
__publicField(this, "store", /* @__PURE__ */ new Map());
|
|
28
|
+
}
|
|
29
|
+
getBucket(docId, createIfMissing) {
|
|
30
|
+
let bucket = this.store.get(docId);
|
|
31
|
+
if (!bucket && createIfMissing) {
|
|
32
|
+
bucket = /* @__PURE__ */ new Map();
|
|
33
|
+
this.store.set(docId, bucket);
|
|
34
|
+
}
|
|
35
|
+
return bucket;
|
|
36
|
+
}
|
|
37
|
+
async save(docId, id, payload, meta) {
|
|
38
|
+
const bucket = this.getBucket(docId, true);
|
|
39
|
+
bucket.set(id, { payload, pinned: (meta == null ? void 0 : meta.pinned) === true });
|
|
40
|
+
}
|
|
41
|
+
async load(docId, id) {
|
|
42
|
+
const bucket = this.getBucket(docId, false);
|
|
43
|
+
const entry = bucket == null ? void 0 : bucket.get(id);
|
|
44
|
+
if (!entry) {
|
|
45
|
+
throw new Error(`snapshot not found: docId=${docId} id=${id}`);
|
|
46
|
+
}
|
|
47
|
+
return entry.payload;
|
|
48
|
+
}
|
|
49
|
+
async delete(docId, id) {
|
|
50
|
+
var _a;
|
|
51
|
+
(_a = this.getBucket(docId, false)) == null ? void 0 : _a.delete(id);
|
|
52
|
+
}
|
|
53
|
+
async list(docId) {
|
|
54
|
+
const bucket = this.getBucket(docId, false);
|
|
55
|
+
if (!bucket) return [];
|
|
56
|
+
const infos = Array.from(bucket.values()).map((entry) => {
|
|
57
|
+
const base = entry.payload.info;
|
|
58
|
+
return entry.pinned ? __spreadProps(__spreadValues({}, base), { pinned: true }) : base;
|
|
59
|
+
});
|
|
60
|
+
return infos.sort((a, b) => {
|
|
61
|
+
const na = extractIdNum(a.id);
|
|
62
|
+
const nb = extractIdNum(b.id);
|
|
63
|
+
if (na !== null && nb !== null) return na - nb;
|
|
64
|
+
return a.id.localeCompare(b.id);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async clear(docId) {
|
|
68
|
+
this.store.delete(docId);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
__name(_InMemorySnapshotStorage, "InMemorySnapshotStorage");
|
|
72
|
+
var InMemorySnapshotStorage = _InMemorySnapshotStorage;
|
|
73
|
+
function extractIdNum(id) {
|
|
74
|
+
const m = /^snap-(\d+)$/.exec(id);
|
|
75
|
+
return m ? Number(m[1]) : null;
|
|
76
|
+
}
|
|
77
|
+
__name(extractIdNum, "extractIdNum");
|
|
78
|
+
export {
|
|
79
|
+
InMemorySnapshotStorage
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/snapshot/in-memory.ts"],"sourcesContent":["/**\n * InMemorySnapshotStorage —— 默认实现,进程内 Map,刷新即失。\n *\n * 适用:测试 / CLI / 不要持久化的场景。宿主持久化请自行实现 SnapshotStorageAdapter\n * 并通过 `LoadOptions.snapshotStorage` 或 `WordDocument.setDefaultSnapshotStorage`\n * 注入(详见 docs/03.2 §8.5)。\n *\n * docId 命名空间隔离由嵌套 Map 实现:{ docId → { snapshotId → entry } }。\n *\n * **pinned 支持(#108,docs/03.2 §3.3)**:\n * - `save` 接受 `meta.pinned`,存进 entry 并在 `list` 的 SnapshotInfo 上还原\n * - `list` 里 `pinned === true` 的快照标志 SnapshotApi 的 FIFO 跳过它们\n */\nimport type { SnapshotInfo, SnapshotPayload, SnapshotStorageAdapter } from './types.js';\n\n/** Map 里的条目:payload + pinned bit(bit 与 payload 解耦,便于未来扩展 meta) */\ninterface StoredEntry {\n payload: SnapshotPayload;\n pinned: boolean;\n}\n\nexport class InMemorySnapshotStorage implements SnapshotStorageAdapter {\n /** 嵌套 Map:docId → (snapshotId → entry) */\n private readonly store = new Map<string, Map<string, StoredEntry>>();\n\n private getBucket(docId: string, createIfMissing: boolean): Map<string, StoredEntry> | undefined {\n let bucket = this.store.get(docId);\n if (!bucket && createIfMissing) {\n bucket = new Map();\n this.store.set(docId, bucket);\n }\n return bucket;\n }\n\n async save(\n docId: string,\n id: string,\n payload: SnapshotPayload,\n meta?: { pinned?: boolean },\n ): Promise<void> {\n const bucket = this.getBucket(docId, true)!;\n bucket.set(id, { payload, pinned: meta?.pinned === true });\n }\n\n async load(docId: string, id: string): Promise<SnapshotPayload> {\n const bucket = this.getBucket(docId, false);\n const entry = bucket?.get(id);\n if (!entry) {\n throw new Error(`snapshot not found: docId=${docId} id=${id}`);\n }\n return entry.payload;\n }\n\n async delete(docId: string, id: string): Promise<void> {\n // 幂等:不存在时静默\n this.getBucket(docId, false)?.delete(id);\n }\n\n async list(docId: string): Promise<SnapshotInfo[]> {\n const bucket = this.getBucket(docId, false);\n if (!bucket) return [];\n // [契约遵守] 按 id 自增数字升序返回(snap-0, snap-1, ...);\n // SnapshotStorageAdapter.list 契约要求,FIFO 淘汰依赖此顺序。\n // 截取 suffix 数字比较,非数字走字典序回退。\n // pinned 体现在 SnapshotInfo.pinned 字段(不参与排序,仅作为 FIFO 淘汰的筛选器)\n const infos = Array.from(bucket.values()).map((entry) => {\n // 重新构造 info 以携带 pinned(payload.info 来自 SnapshotApi,本 adapter 持有 pinned 真相)\n const base = entry.payload.info;\n return entry.pinned ? { ...base, pinned: true } : base;\n });\n return infos.sort((a, b) => {\n const na = extractIdNum(a.id);\n const nb = extractIdNum(b.id);\n if (na !== null && nb !== null) return na - nb;\n return a.id.localeCompare(b.id);\n });\n }\n\n async clear(docId: string): Promise<void> {\n this.store.delete(docId);\n }\n}\n\n/** 从 'snap-123' 提取 123;非 'snap-\\d+' 模式返 null */\nfunction extractIdNum(id: string): number | null {\n const m = /^snap-(\\d+)$/.exec(id);\n return m ? Number(m[1]) : null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAqBO,IAAM,2BAAN,MAAM,yBAA0D;AAAA,EAAhE;AAEL;AAAA,wBAAiB,SAAQ,oBAAI,IAAsC;AAAA;AAAA,EAE3D,UAAU,OAAe,iBAAgE;AAC/F,QAAI,SAAS,KAAK,MAAM,IAAI,KAAK;AACjC,QAAI,CAAC,UAAU,iBAAiB;AAC9B,eAAS,oBAAI,IAAI;AACjB,WAAK,MAAM,IAAI,OAAO,MAAM;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KACJ,OACA,IACA,SACA,MACe;AACf,UAAM,SAAS,KAAK,UAAU,OAAO,IAAI;AACzC,WAAO,IAAI,IAAI,EAAE,SAAS,SAAQ,6BAAM,YAAW,KAAK,CAAC;AAAA,EAC3D;AAAA,EAEA,MAAM,KAAK,OAAe,IAAsC;AAC9D,UAAM,SAAS,KAAK,UAAU,OAAO,KAAK;AAC1C,UAAM,QAAQ,iCAAQ,IAAI;AAC1B,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,6BAA6B,KAAK,OAAO,EAAE,EAAE;AAAA,IAC/D;AACA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,OAAO,OAAe,IAA2B;AArDzD;AAuDI,eAAK,UAAU,OAAO,KAAK,MAA3B,mBAA8B,OAAO;AAAA,EACvC;AAAA,EAEA,MAAM,KAAK,OAAwC;AACjD,UAAM,SAAS,KAAK,UAAU,OAAO,KAAK;AAC1C,QAAI,CAAC,OAAQ,QAAO,CAAC;AAKrB,UAAM,QAAQ,MAAM,KAAK,OAAO,OAAO,CAAC,EAAE,IAAI,CAAC,UAAU;AAEvD,YAAM,OAAO,MAAM,QAAQ;AAC3B,aAAO,MAAM,SAAS,iCAAK,OAAL,EAAW,QAAQ,KAAK,KAAI;AAAA,IACpD,CAAC;AACD,WAAO,MAAM,KAAK,CAAC,GAAG,MAAM;AAC1B,YAAM,KAAK,aAAa,EAAE,EAAE;AAC5B,YAAM,KAAK,aAAa,EAAE,EAAE;AAC5B,UAAI,OAAO,QAAQ,OAAO,KAAM,QAAO,KAAK;AAC5C,aAAO,EAAE,GAAG,cAAc,EAAE,EAAE;AAAA,IAChC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAM,OAA8B;AACxC,SAAK,MAAM,OAAO,KAAK;AAAA,EACzB;AACF;AA5DuE;AAAhE,IAAM,0BAAN;AA+DP,SAAS,aAAa,IAA2B;AAC/C,QAAM,IAAI,eAAe,KAAK,EAAE;AAChC,SAAO,IAAI,OAAO,EAAE,CAAC,CAAC,IAAI;AAC5B;AAHS;","names":[]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package / Part 数据模型 —— CocoOffice 的 OOXML 统一内存表示。
|
|
3
|
+
*
|
|
4
|
+
* 设计背景:docs/01 §3.2(Package 抽象)、docs/03 §13.4(dirty 追踪)。
|
|
5
|
+
*
|
|
6
|
+
* 一个 Part 要么是 XML 类(`xmlDom` 有值、`bytes` 无值),
|
|
7
|
+
* 要么是二进制类(`bytes` 有值、`xmlDom` 无值)。
|
|
8
|
+
* 这种"择一"约束由 `createXmlPart` / `createBinaryPart` 工厂函数在构造期守住。
|
|
9
|
+
*/
|
|
10
|
+
/** OOXML Package 内的单一 Part:路径 + MIME contentType + payload + dirty 元信息 */
|
|
11
|
+
interface Part {
|
|
12
|
+
/** Part 路径,相对包根,**无前导斜杠**。例:'word/document.xml' */
|
|
13
|
+
readonly path: string;
|
|
14
|
+
/**
|
|
15
|
+
* MIME content type。例:'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml'
|
|
16
|
+
* 二进制 part 如图片:'image/png'
|
|
17
|
+
*/
|
|
18
|
+
readonly contentType: string;
|
|
19
|
+
/**
|
|
20
|
+
* XML 类 part 的 DOM(slimdom Document 兼容 lib.dom Document)。
|
|
21
|
+
* 与 `bytes` 互斥 —— 仅择一赋值。
|
|
22
|
+
*/
|
|
23
|
+
xmlDom?: Document;
|
|
24
|
+
/**
|
|
25
|
+
* 二进制 part 的字节(如图片、字体)。
|
|
26
|
+
* 与 `xmlDom` 互斥 —— 仅择一赋值。
|
|
27
|
+
*/
|
|
28
|
+
bytes?: Uint8Array;
|
|
29
|
+
/**
|
|
30
|
+
* 装载时保留的原始字节。`_dirty === false` 时,`toZip` / `toFlatOpc` 直接复用此字节,零 serialize。
|
|
31
|
+
* 二进制 part 初始化时通常 === `bytes`。
|
|
32
|
+
* 装载后首次被改动(markDirty 置 true)前为只读快照。
|
|
33
|
+
*/
|
|
34
|
+
_originalBytes?: Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* 修改标记。
|
|
37
|
+
* - `false`:自装载以来未变更,输出时可复用 `_originalBytes`
|
|
38
|
+
* - `true`:已被改过,输出时需要重新序列化
|
|
39
|
+
*
|
|
40
|
+
* 由 Plan.apply 或外部工具通过 `markDirty(part)` 置 true;读操作不应改变它。
|
|
41
|
+
*/
|
|
42
|
+
_dirty: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 归一化后的 Package:路径到 Part 的 Map。
|
|
46
|
+
*
|
|
47
|
+
* - 外部 API(zip / Flat OPC)经 adapter 归一到 Package,内部只面对这个形态
|
|
48
|
+
* - 同一 Package 内 path 唯一,由 Map 天然保证
|
|
49
|
+
*/
|
|
50
|
+
interface Package {
|
|
51
|
+
parts: Map<string, Part>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type { Part as P, Package as a };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package / Part 数据模型 —— CocoOffice 的 OOXML 统一内存表示。
|
|
3
|
+
*
|
|
4
|
+
* 设计背景:docs/01 §3.2(Package 抽象)、docs/03 §13.4(dirty 追踪)。
|
|
5
|
+
*
|
|
6
|
+
* 一个 Part 要么是 XML 类(`xmlDom` 有值、`bytes` 无值),
|
|
7
|
+
* 要么是二进制类(`bytes` 有值、`xmlDom` 无值)。
|
|
8
|
+
* 这种"择一"约束由 `createXmlPart` / `createBinaryPart` 工厂函数在构造期守住。
|
|
9
|
+
*/
|
|
10
|
+
/** OOXML Package 内的单一 Part:路径 + MIME contentType + payload + dirty 元信息 */
|
|
11
|
+
interface Part {
|
|
12
|
+
/** Part 路径,相对包根,**无前导斜杠**。例:'word/document.xml' */
|
|
13
|
+
readonly path: string;
|
|
14
|
+
/**
|
|
15
|
+
* MIME content type。例:'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml'
|
|
16
|
+
* 二进制 part 如图片:'image/png'
|
|
17
|
+
*/
|
|
18
|
+
readonly contentType: string;
|
|
19
|
+
/**
|
|
20
|
+
* XML 类 part 的 DOM(slimdom Document 兼容 lib.dom Document)。
|
|
21
|
+
* 与 `bytes` 互斥 —— 仅择一赋值。
|
|
22
|
+
*/
|
|
23
|
+
xmlDom?: Document;
|
|
24
|
+
/**
|
|
25
|
+
* 二进制 part 的字节(如图片、字体)。
|
|
26
|
+
* 与 `xmlDom` 互斥 —— 仅择一赋值。
|
|
27
|
+
*/
|
|
28
|
+
bytes?: Uint8Array;
|
|
29
|
+
/**
|
|
30
|
+
* 装载时保留的原始字节。`_dirty === false` 时,`toZip` / `toFlatOpc` 直接复用此字节,零 serialize。
|
|
31
|
+
* 二进制 part 初始化时通常 === `bytes`。
|
|
32
|
+
* 装载后首次被改动(markDirty 置 true)前为只读快照。
|
|
33
|
+
*/
|
|
34
|
+
_originalBytes?: Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* 修改标记。
|
|
37
|
+
* - `false`:自装载以来未变更,输出时可复用 `_originalBytes`
|
|
38
|
+
* - `true`:已被改过,输出时需要重新序列化
|
|
39
|
+
*
|
|
40
|
+
* 由 Plan.apply 或外部工具通过 `markDirty(part)` 置 true;读操作不应改变它。
|
|
41
|
+
*/
|
|
42
|
+
_dirty: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 归一化后的 Package:路径到 Part 的 Map。
|
|
46
|
+
*
|
|
47
|
+
* - 外部 API(zip / Flat OPC)经 adapter 归一到 Package,内部只面对这个形态
|
|
48
|
+
* - 同一 Package 内 path 唯一,由 Map 天然保证
|
|
49
|
+
*/
|
|
50
|
+
interface Package {
|
|
51
|
+
parts: Map<string, Part>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type { Part as P, Package as a };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 快照子系统 —— 与文档类型无关的存储层类型定义。
|
|
3
|
+
*
|
|
4
|
+
* 设计背景:docs/03.2-快照系统.md(尤其 §2 API 形态、§8 存储适配层)。
|
|
5
|
+
*
|
|
6
|
+
* 层次:
|
|
7
|
+
* - 本文件:SnapshotInfo / ChangeLog / SnapshotPayload / SnapshotStorageAdapter
|
|
8
|
+
* - `./in-memory.ts`:默认 InMemorySnapshotStorage 实现
|
|
9
|
+
* - 后续 #21 在 WordDocument 上挂 SnapshotApi 子 API,消费本适配器
|
|
10
|
+
*/
|
|
11
|
+
/** 消费方通过 `wordDoc.snapshots.recordChange()` 累加的变更日志项 */
|
|
12
|
+
interface ChangeLog {
|
|
13
|
+
/** 时间戳,Date.now() */
|
|
14
|
+
timestamp: number;
|
|
15
|
+
/** 工具名,例 'replace_paragraphs' */
|
|
16
|
+
toolName: string;
|
|
17
|
+
/** 工具自定义摘要(结构由消费方决定,可 JSON.stringify) */
|
|
18
|
+
summary?: unknown;
|
|
19
|
+
}
|
|
20
|
+
/** 快照元信息(`list` 只返回这些,不含 parts 字节) */
|
|
21
|
+
interface SnapshotInfo {
|
|
22
|
+
/** 自增 id,'snap-0' / 'snap-1' / ... */
|
|
23
|
+
id: string;
|
|
24
|
+
/** 时间戳,Date.now() */
|
|
25
|
+
timestamp: number;
|
|
26
|
+
/** 用户提供的 take 原因 */
|
|
27
|
+
reason?: string;
|
|
28
|
+
/** 用户提供的 take 详细结构 */
|
|
29
|
+
details?: unknown;
|
|
30
|
+
/** 当前 OOXML Package 全部 parts 的稳定 SHA-256 指纹。旧快照可缺省。 */
|
|
31
|
+
contentHash?: string;
|
|
32
|
+
/** 逻辑去重域;仅同一 dedupeKey 的快照之间允许复用 id。 */
|
|
33
|
+
dedupeKey?: string;
|
|
34
|
+
/** take 时一次性吸入的主线程 changelog */
|
|
35
|
+
changesCaptured: ChangeLog[];
|
|
36
|
+
/**
|
|
37
|
+
* Pinned 标记(#108,docs/03.2 §3.3):true 时该快照不参与 FIFO 淘汰。
|
|
38
|
+
*
|
|
39
|
+
* - `undefined` / `false` 等价,均视为普通快照
|
|
40
|
+
* - 用于保护 `WordAgent.create` 的 session-start 快照不被 plugin 的
|
|
41
|
+
* 常规 take 挤掉
|
|
42
|
+
* - 向后兼容:旧 payload / 旧 adapter 未带该字段时视为未 pinned
|
|
43
|
+
*/
|
|
44
|
+
pinned?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** 快照完整载荷(info + parts 字节) */
|
|
47
|
+
interface SnapshotPayload {
|
|
48
|
+
info: SnapshotInfo;
|
|
49
|
+
parts: Array<{
|
|
50
|
+
path: string;
|
|
51
|
+
contentType: string;
|
|
52
|
+
bytes: Uint8Array;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 存储适配器接口。宿主(WPS 面板 / Electron main / 浏览器 / Node server)提供实现。
|
|
57
|
+
*
|
|
58
|
+
* **语义承诺**:
|
|
59
|
+
* - `save` / `load` 捆装 info + parts 为一次写入 / 一次读取
|
|
60
|
+
* - `list` **仅返 meta**(SnapshotInfo 数组),不加载 parts(避免大档进主线程)
|
|
61
|
+
* - `delete(docId, id)`:不存在时不抛错,幂等
|
|
62
|
+
* - `clear(docId)`:清除该 docId 下所有快照,不影响其它 docId
|
|
63
|
+
* - 所有方法 async(允许底层存储异步)
|
|
64
|
+
*
|
|
65
|
+
* **pinned 支持(#108)**:
|
|
66
|
+
* - `save` 接受可选 `meta.pinned`,adapter 需把该 bit 与 payload 一起持久化,
|
|
67
|
+
* 并在 `list` 返回的 `SnapshotInfo.pinned` 中还原
|
|
68
|
+
* - 不支持 pinned 的旧 adapter 仍然合法:`list` 返回的 `pinned`
|
|
69
|
+
* 默认为 `undefined`,被 SnapshotApi 视为未 pinned(向后兼容)
|
|
70
|
+
* - 具体的 "FIFO 淘汰时跳过 pinned" 逻辑在 **SnapshotApi 主线程侧**,
|
|
71
|
+
* adapter 只负责存取该 bit
|
|
72
|
+
*
|
|
73
|
+
* SDK 不做:重试 / 超时 / 并发互斥。异常透传给消费方。
|
|
74
|
+
*/
|
|
75
|
+
interface SnapshotStorageAdapter {
|
|
76
|
+
/**
|
|
77
|
+
* 保存快照。
|
|
78
|
+
*
|
|
79
|
+
* @param meta - 可选;目前仅 `pinned`(#108),表示本快照不应被 FIFO 淘汰。
|
|
80
|
+
* 适配器应把 `pinned` 与 payload 一起持久化,并在 `list` 时体现在
|
|
81
|
+
* `SnapshotInfo.pinned` 上。未实现 pinned 的旧 adapter 可忽略该参数。
|
|
82
|
+
*/
|
|
83
|
+
save(docId: string, id: string, payload: SnapshotPayload, meta?: {
|
|
84
|
+
pinned?: boolean;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
load(docId: string, id: string): Promise<SnapshotPayload>;
|
|
87
|
+
delete(docId: string, id: string): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* 列出某 docId 下所有快照元信息。
|
|
90
|
+
*
|
|
91
|
+
* **必须按 id 自增数字升序返回(snap-0, snap-1, ..., snap-N)**。
|
|
92
|
+
* SnapshotApi 的 FIFO 淘汰(按"最早快照"删)依赖该顺序;若 adapter 返回顺序与
|
|
93
|
+
* id 自增序不一致,FIFO 会淘汰错对象。
|
|
94
|
+
*
|
|
95
|
+
* `SnapshotInfo.pinned` 反映 adapter 保存时收到的 `meta.pinned`;未实现 pinned
|
|
96
|
+
* 的旧 adapter 不带该字段(视为未 pinned)。
|
|
97
|
+
*/
|
|
98
|
+
list(docId: string): Promise<SnapshotInfo[]>;
|
|
99
|
+
clear(docId: string): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type { ChangeLog as C, SnapshotStorageAdapter as S, SnapshotPayload as a, SnapshotInfo as b };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 快照子系统 —— 与文档类型无关的存储层类型定义。
|
|
3
|
+
*
|
|
4
|
+
* 设计背景:docs/03.2-快照系统.md(尤其 §2 API 形态、§8 存储适配层)。
|
|
5
|
+
*
|
|
6
|
+
* 层次:
|
|
7
|
+
* - 本文件:SnapshotInfo / ChangeLog / SnapshotPayload / SnapshotStorageAdapter
|
|
8
|
+
* - `./in-memory.ts`:默认 InMemorySnapshotStorage 实现
|
|
9
|
+
* - 后续 #21 在 WordDocument 上挂 SnapshotApi 子 API,消费本适配器
|
|
10
|
+
*/
|
|
11
|
+
/** 消费方通过 `wordDoc.snapshots.recordChange()` 累加的变更日志项 */
|
|
12
|
+
interface ChangeLog {
|
|
13
|
+
/** 时间戳,Date.now() */
|
|
14
|
+
timestamp: number;
|
|
15
|
+
/** 工具名,例 'replace_paragraphs' */
|
|
16
|
+
toolName: string;
|
|
17
|
+
/** 工具自定义摘要(结构由消费方决定,可 JSON.stringify) */
|
|
18
|
+
summary?: unknown;
|
|
19
|
+
}
|
|
20
|
+
/** 快照元信息(`list` 只返回这些,不含 parts 字节) */
|
|
21
|
+
interface SnapshotInfo {
|
|
22
|
+
/** 自增 id,'snap-0' / 'snap-1' / ... */
|
|
23
|
+
id: string;
|
|
24
|
+
/** 时间戳,Date.now() */
|
|
25
|
+
timestamp: number;
|
|
26
|
+
/** 用户提供的 take 原因 */
|
|
27
|
+
reason?: string;
|
|
28
|
+
/** 用户提供的 take 详细结构 */
|
|
29
|
+
details?: unknown;
|
|
30
|
+
/** 当前 OOXML Package 全部 parts 的稳定 SHA-256 指纹。旧快照可缺省。 */
|
|
31
|
+
contentHash?: string;
|
|
32
|
+
/** 逻辑去重域;仅同一 dedupeKey 的快照之间允许复用 id。 */
|
|
33
|
+
dedupeKey?: string;
|
|
34
|
+
/** take 时一次性吸入的主线程 changelog */
|
|
35
|
+
changesCaptured: ChangeLog[];
|
|
36
|
+
/**
|
|
37
|
+
* Pinned 标记(#108,docs/03.2 §3.3):true 时该快照不参与 FIFO 淘汰。
|
|
38
|
+
*
|
|
39
|
+
* - `undefined` / `false` 等价,均视为普通快照
|
|
40
|
+
* - 用于保护 `WordAgent.create` 的 session-start 快照不被 plugin 的
|
|
41
|
+
* 常规 take 挤掉
|
|
42
|
+
* - 向后兼容:旧 payload / 旧 adapter 未带该字段时视为未 pinned
|
|
43
|
+
*/
|
|
44
|
+
pinned?: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** 快照完整载荷(info + parts 字节) */
|
|
47
|
+
interface SnapshotPayload {
|
|
48
|
+
info: SnapshotInfo;
|
|
49
|
+
parts: Array<{
|
|
50
|
+
path: string;
|
|
51
|
+
contentType: string;
|
|
52
|
+
bytes: Uint8Array;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 存储适配器接口。宿主(WPS 面板 / Electron main / 浏览器 / Node server)提供实现。
|
|
57
|
+
*
|
|
58
|
+
* **语义承诺**:
|
|
59
|
+
* - `save` / `load` 捆装 info + parts 为一次写入 / 一次读取
|
|
60
|
+
* - `list` **仅返 meta**(SnapshotInfo 数组),不加载 parts(避免大档进主线程)
|
|
61
|
+
* - `delete(docId, id)`:不存在时不抛错,幂等
|
|
62
|
+
* - `clear(docId)`:清除该 docId 下所有快照,不影响其它 docId
|
|
63
|
+
* - 所有方法 async(允许底层存储异步)
|
|
64
|
+
*
|
|
65
|
+
* **pinned 支持(#108)**:
|
|
66
|
+
* - `save` 接受可选 `meta.pinned`,adapter 需把该 bit 与 payload 一起持久化,
|
|
67
|
+
* 并在 `list` 返回的 `SnapshotInfo.pinned` 中还原
|
|
68
|
+
* - 不支持 pinned 的旧 adapter 仍然合法:`list` 返回的 `pinned`
|
|
69
|
+
* 默认为 `undefined`,被 SnapshotApi 视为未 pinned(向后兼容)
|
|
70
|
+
* - 具体的 "FIFO 淘汰时跳过 pinned" 逻辑在 **SnapshotApi 主线程侧**,
|
|
71
|
+
* adapter 只负责存取该 bit
|
|
72
|
+
*
|
|
73
|
+
* SDK 不做:重试 / 超时 / 并发互斥。异常透传给消费方。
|
|
74
|
+
*/
|
|
75
|
+
interface SnapshotStorageAdapter {
|
|
76
|
+
/**
|
|
77
|
+
* 保存快照。
|
|
78
|
+
*
|
|
79
|
+
* @param meta - 可选;目前仅 `pinned`(#108),表示本快照不应被 FIFO 淘汰。
|
|
80
|
+
* 适配器应把 `pinned` 与 payload 一起持久化,并在 `list` 时体现在
|
|
81
|
+
* `SnapshotInfo.pinned` 上。未实现 pinned 的旧 adapter 可忽略该参数。
|
|
82
|
+
*/
|
|
83
|
+
save(docId: string, id: string, payload: SnapshotPayload, meta?: {
|
|
84
|
+
pinned?: boolean;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
load(docId: string, id: string): Promise<SnapshotPayload>;
|
|
87
|
+
delete(docId: string, id: string): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* 列出某 docId 下所有快照元信息。
|
|
90
|
+
*
|
|
91
|
+
* **必须按 id 自增数字升序返回(snap-0, snap-1, ..., snap-N)**。
|
|
92
|
+
* SnapshotApi 的 FIFO 淘汰(按"最早快照"删)依赖该顺序;若 adapter 返回顺序与
|
|
93
|
+
* id 自增序不一致,FIFO 会淘汰错对象。
|
|
94
|
+
*
|
|
95
|
+
* `SnapshotInfo.pinned` 反映 adapter 保存时收到的 `meta.pinned`;未实现 pinned
|
|
96
|
+
* 的旧 adapter 不带该字段(视为未 pinned)。
|
|
97
|
+
*/
|
|
98
|
+
list(docId: string): Promise<SnapshotInfo[]>;
|
|
99
|
+
clear(docId: string): Promise<void>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type { ChangeLog as C, SnapshotStorageAdapter as S, SnapshotPayload as a, SnapshotInfo as b };
|