@lokvis/runtime 0.1.0 → 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/LICENSE +21 -0
- package/dist/asset-store.d.ts +5 -2
- package/dist/asset-store.d.ts.map +1 -1
- package/dist/asset-store.js +93 -3
- package/dist/asset-store.js.map +1 -1
- package/dist/batch-processor.d.ts +200 -0
- package/dist/batch-processor.d.ts.map +1 -0
- package/dist/batch-processor.js +505 -0
- package/dist/batch-processor.js.map +1 -0
- package/dist/capability-registry.d.ts +4 -0
- package/dist/capability-registry.d.ts.map +1 -1
- package/dist/capability-registry.js +19 -2
- package/dist/capability-registry.js.map +1 -1
- package/dist/degradation.d.ts +109 -0
- package/dist/degradation.d.ts.map +1 -0
- package/dist/degradation.js +184 -0
- package/dist/degradation.js.map +1 -0
- package/dist/event-bus.d.ts.map +1 -1
- package/dist/event-bus.js +7 -2
- package/dist/event-bus.js.map +1 -1
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +37 -1
- package/dist/executor.js.map +1 -1
- package/dist/history-store.d.ts +76 -0
- package/dist/history-store.d.ts.map +1 -0
- package/dist/history-store.js +109 -0
- package/dist/history-store.js.map +1 -0
- package/dist/history.d.ts +1 -1
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +1 -1
- package/dist/history.js.map +1 -1
- package/dist/idb-asset-store.js +1 -1
- package/dist/idb-asset-store.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/memory-guard.d.ts +109 -0
- package/dist/memory-guard.d.ts.map +1 -0
- package/dist/memory-guard.js +161 -0
- package/dist/memory-guard.js.map +1 -0
- package/dist/opfs-asset-store.d.ts +35 -4
- package/dist/opfs-asset-store.d.ts.map +1 -1
- package/dist/opfs-asset-store.js +94 -10
- package/dist/opfs-asset-store.js.map +1 -1
- package/dist/runtime.d.ts +139 -4
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +443 -25
- package/dist/runtime.js.map +1 -1
- package/dist/types.d.ts +101 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/worker-host.d.ts +13 -0
- package/dist/worker-host.d.ts.map +1 -1
- package/dist/worker-host.js +47 -15
- package/dist/worker-host.js.map +1 -1
- package/dist/worker-protocol.d.ts +10 -1
- package/dist/worker-protocol.d.ts.map +1 -1
- package/dist/worker-protocol.js.map +1 -1
- package/dist/workflow-builder.d.ts +124 -0
- package/dist/workflow-builder.d.ts.map +1 -0
- package/dist/workflow-builder.js +240 -0
- package/dist/workflow-builder.js.map +1 -0
- package/package.json +3 -2
- package/src/asset-store.ts +103 -3
- package/src/batch-processor.ts +699 -0
- package/src/capability-registry.ts +19 -2
- package/src/degradation.ts +241 -0
- package/src/event-bus.ts +6 -2
- package/src/executor.ts +46 -1
- package/src/history-store.ts +159 -0
- package/src/history.ts +2 -2
- package/src/idb-asset-store.ts +1 -1
- package/src/index.ts +5 -0
- package/src/memory-guard.ts +202 -0
- package/src/opfs-asset-store.ts +118 -10
- package/src/runtime.ts +482 -21
- package/src/types.ts +98 -1
- package/src/worker-host.ts +56 -16
- package/src/worker-protocol.ts +11 -1
- package/src/workflow-builder.ts +303 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 能力降级阶梯(W3.4)
|
|
3
|
+
*
|
|
4
|
+
* 当 MemoryGuard 报告内存压力时,运行时需要按阶梯选择处理策略,
|
|
5
|
+
* 而非"要么全量要么崩溃"。本模块实现四级降级政策(whitepaper T1):
|
|
6
|
+
*
|
|
7
|
+
* L1-full 完整质量——压力低/中,正常处理。
|
|
8
|
+
* L2-tiled 分片处理——压力高,把输入按 tile 切分 + 中间结果溢出 OPFS,
|
|
9
|
+
* 内存里同时只持有一个 tile。
|
|
10
|
+
* L3-degraded 降级输出——压力 critical,先缩小到 maxEdge 再处理,
|
|
11
|
+
* 降低质量,避免 OOM;产出可用但非最佳。
|
|
12
|
+
* L4-reject 拒绝 + 引导——即使降级也无法安全处理(如单张图解码后即超
|
|
13
|
+
* critical 且无法再缩),抛 DegradationRejectedError 携带
|
|
14
|
+
* 用户可读的引导(换更小源图 / 关闭其他 tab / 升级浏览器)。
|
|
15
|
+
*
|
|
16
|
+
* 设计取舍:
|
|
17
|
+
* - 本模块只做"决策"(policy),不直接执行图像操作。执行由 plugin-image
|
|
18
|
+
* / executor 据 decision 调整参数后委托 engine-image。
|
|
19
|
+
* - 决策是纯函数(pickDegradation),便于单测与确定性回归。
|
|
20
|
+
* - L2 是否可走取决于操作是否支持 tiling(canTile);canvas 引擎对几何
|
|
21
|
+
* 变换可 tile,但对依赖全图的滤镜(模糊)与水印 tile 无意义 → 调用方
|
|
22
|
+
* 据操作语义传入 canTile。
|
|
23
|
+
*/
|
|
24
|
+
import type { MemoryPressure } from './memory-guard.js';
|
|
25
|
+
/** 降级阶梯等级 */
|
|
26
|
+
export type DegradationLevel = 'L1-full' | 'L2-tiled' | 'L3-degraded' | 'L4-reject';
|
|
27
|
+
/** L3 降级输出时建议的最大边长(像素)——4K,兼顾可用性与内存 */
|
|
28
|
+
export declare const DEGRADED_MAX_EDGE = 4096;
|
|
29
|
+
/** L3 降级输出时建议的质量(0-100)——70 是视觉可接受的下限 */
|
|
30
|
+
export declare const DEGRADED_QUALITY = 70;
|
|
31
|
+
/** 决策上下文(由调用方据 MemoryGuard + 操作能力构造) */
|
|
32
|
+
export interface DegradationContext {
|
|
33
|
+
/** 当前内存压力等级(来自 MemoryGuard.getPressure) */
|
|
34
|
+
pressure: MemoryPressure;
|
|
35
|
+
/** 是否可溢出中间结果到 OPFS(MemoryGuard.canSpill) */
|
|
36
|
+
canSpill: boolean;
|
|
37
|
+
/** 操作是否支持 tiling / streaming(几何变换通常 true,模糊/水印 false) */
|
|
38
|
+
canTile: boolean;
|
|
39
|
+
/** 输入字节数(用于 L4 判定:输入本身过大时即使降级也无救) */
|
|
40
|
+
inputBytes: number;
|
|
41
|
+
/** 内存预算(字节,用于 L4 引导信息) */
|
|
42
|
+
budget: number;
|
|
43
|
+
/**
|
|
44
|
+
* 解码后位图估算字节(用于 L4 判定:decode 后超 critical 且
|
|
45
|
+
* 已是 maxEdge 缩放后仍超 → reject)。可选,缺省时仅按 pressure 判。
|
|
46
|
+
*
|
|
47
|
+
* **强烈建议提供**:可通过 estimateDecodedBytes(width, height) 计算。
|
|
48
|
+
* 缺省时 pressure=critical + canSpill=true 场景会跳过 L4 判定,
|
|
49
|
+
* 直接返回 L3-degraded,可能导致超大图缩放后仍 OOM。
|
|
50
|
+
*/
|
|
51
|
+
decodedBytes?: number;
|
|
52
|
+
}
|
|
53
|
+
/** 降级决策结果 */
|
|
54
|
+
export interface DegradationDecision {
|
|
55
|
+
level: DegradationLevel;
|
|
56
|
+
/** L3 时建议缩放到的最大边长(像素);其余等级 undefined */
|
|
57
|
+
maxEdge?: number;
|
|
58
|
+
/** L3 时建议质量(0-100);其余等级 undefined */
|
|
59
|
+
quality?: number;
|
|
60
|
+
/** L2/L3 时是否把中间结果溢出到 OPFS */
|
|
61
|
+
spill: boolean;
|
|
62
|
+
/** 决策依据(便于日志 / 事件 / 调试) */
|
|
63
|
+
reason: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 根据内存压力与操作能力选择降级策略(纯函数)。
|
|
67
|
+
*
|
|
68
|
+
* 决策矩阵:
|
|
69
|
+
* pressure canTile canSpill → level
|
|
70
|
+
* low/elevated * * → L1-full
|
|
71
|
+
* high true true → L2-tiled(spill)
|
|
72
|
+
* high true false → L2-tiled(无 spill,仅靠 tile 控峰值)
|
|
73
|
+
* high false * → L3-degraded(降到 maxEdge+quality)
|
|
74
|
+
* critical * * → L3-degraded(若 decodedBytes 缩放后仍超
|
|
75
|
+
* critical → L4-reject)
|
|
76
|
+
*
|
|
77
|
+
* L4 触发条件:pressure=critical 且 decodedBytes 已按 maxEdge 缩放后
|
|
78
|
+
* 估算仍 >= budget 的 critical 阈值(0.95),即"再怎么缩也会撑爆"。
|
|
79
|
+
*
|
|
80
|
+
* **调用方注意**:当 pressure=critical 且 canSpill=true 时,若不提供
|
|
81
|
+
* decodedBytes,函数将跳过 L4 判定直接返回 L3-degraded。这意味着对于
|
|
82
|
+
* 超大单张图(无法通过 spill 分块),L3 缩放后仍可能 OOM。
|
|
83
|
+
* 建议:尽可能通过 estimateDecodedBytes(width, height) 提供 decodedBytes,
|
|
84
|
+
* 以提高 L4 判定精度,避免静默降级后崩溃。
|
|
85
|
+
*/
|
|
86
|
+
export declare function pickDegradation(ctx: DegradationContext): DegradationDecision;
|
|
87
|
+
/**
|
|
88
|
+
* L4 拒绝时抛出的错误,携带用户可读的引导建议。
|
|
89
|
+
*
|
|
90
|
+
* 引导文案面向终端用户(经 UI 展示),而非开发者堆栈。
|
|
91
|
+
*/
|
|
92
|
+
export declare class DegradationRejectedError extends Error {
|
|
93
|
+
readonly level: "L4-reject";
|
|
94
|
+
readonly inputBytes: number;
|
|
95
|
+
readonly budget: number;
|
|
96
|
+
/** 用户可读的引导建议(UI 可直接渲染) */
|
|
97
|
+
readonly guide: string[];
|
|
98
|
+
constructor(inputBytes: number, budget: number, reason: string);
|
|
99
|
+
}
|
|
100
|
+
/** 字节数 → 人类可读(MB/GB) */
|
|
101
|
+
export declare function formatBytes(bytes: number): string;
|
|
102
|
+
/**
|
|
103
|
+
* 把降级决策应用到 resize 参数(若 L3):确保输出最大边不超过 maxEdge。
|
|
104
|
+
*
|
|
105
|
+
* 调用方在 L3 时据此调整 params.width/height,再交给 engine.resize。
|
|
106
|
+
* L1/L2/L4 时原样返回 params。
|
|
107
|
+
*/
|
|
108
|
+
export declare function applyDegradationToResizeParams(params: Record<string, unknown>, decision: DegradationDecision): Record<string, unknown>;
|
|
109
|
+
//# sourceMappingURL=degradation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"degradation.d.ts","sourceRoot":"","sources":["../src/degradation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,aAAa;AACb,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,GAAG,aAAa,GAAG,WAAW,CAAC;AAEpF,uCAAuC;AACvC,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,wCAAwC;AACxC,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,QAAQ,EAAE,cAAc,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,aAAa;AACb,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,gBAAgB,CAAC;IACxB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,kBAAkB,GAAG,mBAAmB,CAyC5E;AAmCD;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,KAAK,EAAG,WAAW,CAAU;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,0BAA0B;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;gBAEb,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAU/D;AAgBD,wBAAwB;AACxB,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,mBAAmB,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiBzB"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 能力降级阶梯(W3.4)
|
|
3
|
+
*
|
|
4
|
+
* 当 MemoryGuard 报告内存压力时,运行时需要按阶梯选择处理策略,
|
|
5
|
+
* 而非"要么全量要么崩溃"。本模块实现四级降级政策(whitepaper T1):
|
|
6
|
+
*
|
|
7
|
+
* L1-full 完整质量——压力低/中,正常处理。
|
|
8
|
+
* L2-tiled 分片处理——压力高,把输入按 tile 切分 + 中间结果溢出 OPFS,
|
|
9
|
+
* 内存里同时只持有一个 tile。
|
|
10
|
+
* L3-degraded 降级输出——压力 critical,先缩小到 maxEdge 再处理,
|
|
11
|
+
* 降低质量,避免 OOM;产出可用但非最佳。
|
|
12
|
+
* L4-reject 拒绝 + 引导——即使降级也无法安全处理(如单张图解码后即超
|
|
13
|
+
* critical 且无法再缩),抛 DegradationRejectedError 携带
|
|
14
|
+
* 用户可读的引导(换更小源图 / 关闭其他 tab / 升级浏览器)。
|
|
15
|
+
*
|
|
16
|
+
* 设计取舍:
|
|
17
|
+
* - 本模块只做"决策"(policy),不直接执行图像操作。执行由 plugin-image
|
|
18
|
+
* / executor 据 decision 调整参数后委托 engine-image。
|
|
19
|
+
* - 决策是纯函数(pickDegradation),便于单测与确定性回归。
|
|
20
|
+
* - L2 是否可走取决于操作是否支持 tiling(canTile);canvas 引擎对几何
|
|
21
|
+
* 变换可 tile,但对依赖全图的滤镜(模糊)与水印 tile 无意义 → 调用方
|
|
22
|
+
* 据操作语义传入 canTile。
|
|
23
|
+
*/
|
|
24
|
+
/** L3 降级输出时建议的最大边长(像素)——4K,兼顾可用性与内存 */
|
|
25
|
+
export const DEGRADED_MAX_EDGE = 4096;
|
|
26
|
+
/** L3 降级输出时建议的质量(0-100)——70 是视觉可接受的下限 */
|
|
27
|
+
export const DEGRADED_QUALITY = 70;
|
|
28
|
+
/**
|
|
29
|
+
* 根据内存压力与操作能力选择降级策略(纯函数)。
|
|
30
|
+
*
|
|
31
|
+
* 决策矩阵:
|
|
32
|
+
* pressure canTile canSpill → level
|
|
33
|
+
* low/elevated * * → L1-full
|
|
34
|
+
* high true true → L2-tiled(spill)
|
|
35
|
+
* high true false → L2-tiled(无 spill,仅靠 tile 控峰值)
|
|
36
|
+
* high false * → L3-degraded(降到 maxEdge+quality)
|
|
37
|
+
* critical * * → L3-degraded(若 decodedBytes 缩放后仍超
|
|
38
|
+
* critical → L4-reject)
|
|
39
|
+
*
|
|
40
|
+
* L4 触发条件:pressure=critical 且 decodedBytes 已按 maxEdge 缩放后
|
|
41
|
+
* 估算仍 >= budget 的 critical 阈值(0.95),即"再怎么缩也会撑爆"。
|
|
42
|
+
*
|
|
43
|
+
* **调用方注意**:当 pressure=critical 且 canSpill=true 时,若不提供
|
|
44
|
+
* decodedBytes,函数将跳过 L4 判定直接返回 L3-degraded。这意味着对于
|
|
45
|
+
* 超大单张图(无法通过 spill 分块),L3 缩放后仍可能 OOM。
|
|
46
|
+
* 建议:尽可能通过 estimateDecodedBytes(width, height) 提供 decodedBytes,
|
|
47
|
+
* 以提高 L4 判定精度,避免静默降级后崩溃。
|
|
48
|
+
*/
|
|
49
|
+
export function pickDegradation(ctx) {
|
|
50
|
+
const { pressure, canTile, canSpill, inputBytes, budget, decodedBytes } = ctx;
|
|
51
|
+
// L1:压力低/中,完整处理
|
|
52
|
+
if (pressure === 'low' || pressure === 'elevated') {
|
|
53
|
+
return {
|
|
54
|
+
level: 'L1-full',
|
|
55
|
+
spill: false,
|
|
56
|
+
reason: `pressure=${pressure}, full quality`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// L4 预判:输入本身已超预算且无法溢出 → 直接拒绝(避免无谓降级后仍 OOM)
|
|
60
|
+
// 仅当输入 blob 已 > budget 且不能溢出时,降级也救不了
|
|
61
|
+
if (pressure === 'critical' && inputBytes > budget && !canSpill) {
|
|
62
|
+
return rejectDecision(inputBytes, budget, 'input exceeds budget and OPFS spill unavailable');
|
|
63
|
+
}
|
|
64
|
+
if (pressure === 'high') {
|
|
65
|
+
// L2:可 tile 则分片处理(优先 spill 以降峰值)
|
|
66
|
+
if (canTile) {
|
|
67
|
+
return {
|
|
68
|
+
level: 'L2-tiled',
|
|
69
|
+
spill: canSpill,
|
|
70
|
+
reason: `pressure=high, tile-based${canSpill ? ' + OPFS spill' : ''}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// 不可 tile(模糊/水印等全图操作)→ L3 降级
|
|
74
|
+
return degradedDecision(canSpill, 'pressure=high but operation not tileable, degrade output');
|
|
75
|
+
}
|
|
76
|
+
// pressure === 'critical'
|
|
77
|
+
// 若提供 decodedBytes 且缩放到 maxEdge 后估算仍超 critical(0.95)→ L4
|
|
78
|
+
if (decodedBytes !== undefined) {
|
|
79
|
+
const scaledEstimate = scaleDecodedEstimate(decodedBytes, DEGRADED_MAX_EDGE);
|
|
80
|
+
if (scaledEstimate >= budget * 0.95) {
|
|
81
|
+
return rejectDecision(inputBytes, budget, `decoded ~${scaledEstimate}B even after ${DEGRADED_MAX_EDGE}px cap exceeds critical`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// 否则 L3:降级输出
|
|
85
|
+
return degradedDecision(canSpill, 'pressure=critical, degrade output to fit budget');
|
|
86
|
+
}
|
|
87
|
+
/** L3 降级输出决策 */
|
|
88
|
+
function degradedDecision(spill, reason) {
|
|
89
|
+
return {
|
|
90
|
+
level: 'L3-degraded',
|
|
91
|
+
maxEdge: DEGRADED_MAX_EDGE,
|
|
92
|
+
quality: DEGRADED_QUALITY,
|
|
93
|
+
spill,
|
|
94
|
+
reason,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** L4 拒绝决策 */
|
|
98
|
+
function rejectDecision(inputBytes, budget, reason) {
|
|
99
|
+
return {
|
|
100
|
+
level: 'L4-reject',
|
|
101
|
+
spill: false,
|
|
102
|
+
reason: `reject: ${reason} (input=${inputBytes}B, budget=${budget}B)`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 估算把位图缩放到 maxEdge 后的解码字节数(按面积比例缩放)。
|
|
107
|
+
* 用于 L4 判定:若缩放后仍超 critical 则无救。
|
|
108
|
+
*/
|
|
109
|
+
function scaleDecodedEstimate(decodedBytes, maxEdge) {
|
|
110
|
+
// decodedBytes ∝ width*height。缩放到 maxEdge 按长边,面积比 = (max/orig)^2。
|
|
111
|
+
// 但我们不知原始 width/height,只有 decodedBytes。保守起见:若 decodedBytes
|
|
112
|
+
// 已大于 maxEdge*maxEdge*4(即缩放后的占用),说明原图比 maxEdge 大,缩放有效;
|
|
113
|
+
// 否则原图已 <= maxEdge,缩放无变化,估算 = decodedBytes。
|
|
114
|
+
const scaledCap = maxEdge * maxEdge * 4;
|
|
115
|
+
return Math.min(decodedBytes, scaledCap);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* L4 拒绝时抛出的错误,携带用户可读的引导建议。
|
|
119
|
+
*
|
|
120
|
+
* 引导文案面向终端用户(经 UI 展示),而非开发者堆栈。
|
|
121
|
+
*/
|
|
122
|
+
export class DegradationRejectedError extends Error {
|
|
123
|
+
level = 'L4-reject';
|
|
124
|
+
inputBytes;
|
|
125
|
+
budget;
|
|
126
|
+
/** 用户可读的引导建议(UI 可直接渲染) */
|
|
127
|
+
guide;
|
|
128
|
+
constructor(inputBytes, budget, reason) {
|
|
129
|
+
super(`Operation rejected to avoid out-of-memory: ${reason}. ` +
|
|
130
|
+
`Input ${formatBytes(inputBytes)} exceeds memory budget ${formatBytes(budget)}.`);
|
|
131
|
+
this.name = 'DegradationRejectedError';
|
|
132
|
+
this.inputBytes = inputBytes;
|
|
133
|
+
this.budget = budget;
|
|
134
|
+
this.guide = buildGuide(inputBytes, budget);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** 构造用户引导建议(按优先级) */
|
|
138
|
+
function buildGuide(inputBytes, budget) {
|
|
139
|
+
const guide = [];
|
|
140
|
+
if (inputBytes > budget) {
|
|
141
|
+
guide.push(`This image (${formatBytes(inputBytes)}) is larger than the in-browser memory budget ` +
|
|
142
|
+
`(${formatBytes(budget)}). Try a smaller source image.`);
|
|
143
|
+
}
|
|
144
|
+
guide.push('Close other browser tabs to free memory, then retry.');
|
|
145
|
+
guide.push('For very large images, use the desktop app or process in smaller batches.');
|
|
146
|
+
return guide;
|
|
147
|
+
}
|
|
148
|
+
/** 字节数 → 人类可读(MB/GB) */
|
|
149
|
+
export function formatBytes(bytes) {
|
|
150
|
+
if (!Number.isFinite(bytes) || bytes < 0)
|
|
151
|
+
return '0B';
|
|
152
|
+
if (bytes < 1024)
|
|
153
|
+
return `${bytes}B`;
|
|
154
|
+
if (bytes < 1024 * 1024)
|
|
155
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
156
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
157
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
158
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 把降级决策应用到 resize 参数(若 L3):确保输出最大边不超过 maxEdge。
|
|
162
|
+
*
|
|
163
|
+
* 调用方在 L3 时据此调整 params.width/height,再交给 engine.resize。
|
|
164
|
+
* L1/L2/L4 时原样返回 params。
|
|
165
|
+
*/
|
|
166
|
+
export function applyDegradationToResizeParams(params, decision) {
|
|
167
|
+
if (decision.level !== 'L3-degraded' || decision.maxEdge === undefined) {
|
|
168
|
+
return params;
|
|
169
|
+
}
|
|
170
|
+
const next = { ...params };
|
|
171
|
+
// 只要任一已指定的边超过 maxEdge,就注入 maxEdge 上限保护内存。
|
|
172
|
+
// undefined 视为"未约束",不参与 veto;已指定且 ≤ maxEdge 的边才排除 maxEdge。
|
|
173
|
+
const w = next.width;
|
|
174
|
+
const h = next.height;
|
|
175
|
+
const wOver = w !== undefined && w > decision.maxEdge;
|
|
176
|
+
const hOver = h !== undefined && h > decision.maxEdge;
|
|
177
|
+
const wUnset = w === undefined;
|
|
178
|
+
const hUnset = h === undefined;
|
|
179
|
+
if (wOver || hOver || (wUnset && hUnset)) {
|
|
180
|
+
next.maxEdge = decision.maxEdge;
|
|
181
|
+
}
|
|
182
|
+
return next;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=degradation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"degradation.js","sourceRoot":"","sources":["../src/degradation.ts"],"names":[],"mappings":"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"}
|
package/dist/event-bus.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,gBAAgB,CAAC;AAI5D,wBAAgB,cAAc,IAAI,QAAQ,
|
|
1
|
+
{"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,gBAAgB,CAAC;AAI5D,wBAAgB,cAAc,IAAI,QAAQ,CAgCzC"}
|
package/dist/event-bus.js
CHANGED
|
@@ -20,8 +20,13 @@ export function createEventBus() {
|
|
|
20
20
|
},
|
|
21
21
|
emit(event) {
|
|
22
22
|
emitter.emit(event.type, event);
|
|
23
|
-
for (const handler of anyHandlers) {
|
|
24
|
-
|
|
23
|
+
for (const handler of [...anyHandlers]) {
|
|
24
|
+
try {
|
|
25
|
+
handler(event);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
console.error(`[event-bus] onAny handler threw for "${event.type}":`, err);
|
|
29
|
+
}
|
|
25
30
|
}
|
|
26
31
|
},
|
|
27
32
|
clear() {
|
package/dist/event-bus.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,IAAI,EAAU,CAAC;IAC/B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAgC,CAAC;IAE5D,OAAO;QACL,EAAE,CAAC,IAAI,EAAE,OAAO;YACd,MAAM,OAAO,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,OAAO,CAAC,CAAU,CAAC,CAAC;YACpD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC1B,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;QAED,KAAK,CAAC,OAAO;YACX,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACzB,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,KAAK;YACR,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAChC,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AAKxB,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,IAAI,EAAU,CAAC;IAC/B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAgC,CAAC;IAE5D,OAAO;QACL,EAAE,CAAC,IAAI,EAAE,OAAO;YACd,MAAM,OAAO,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,OAAO,CAAC,CAAU,CAAC,CAAC;YACpD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC1B,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;QAED,KAAK,CAAC,OAAO;YACX,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACzB,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,KAAK;YACR,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAChC,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC;oBACH,OAAO,CAAC,KAAK,CAAC,CAAC;gBACjB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,KAAK,CAAC,wCAAwC,KAAK,CAAC,IAAI,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC7E,CAAC;YACH,CAAC;QACH,CAAC;QAED,KAAK;YACH,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACpB,WAAW,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/executor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,KAAK,EACL,OAAO,EAEP,QAAQ,EAGR,cAAc,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAWnE,qBAAqB;AACrB,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,UAAU,CAAC;IACvB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,KAAK,EACL,OAAO,EAEP,QAAQ,EAGR,cAAc,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAWnE,qBAAqB;AACrB,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,UAAU,CAAC;IACvB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AA0GD,mBAAmB;AACnB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAsC;IACrD,4CAA4C;IAC5C,OAAO,CAAC,eAAe,CAAiC;gBAE5C,MAAM,EAAE,cAAc;IAIlC,YAAY;IACN,OAAO,CACX,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,GAC1B,OAAO,CAAC,cAAc,CAAC;IA+I1B,WAAW;IACL,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/C,WAAW;IACL,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9C,WAAW;IACL,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAMrB,+BAA+B;IAC/B,OAAO,CAAC,aAAa;CAOtB"}
|
package/dist/executor.js
CHANGED
|
@@ -22,16 +22,49 @@ function createExecutionContext(workflowId, nodeId, signal, onProgress) {
|
|
|
22
22
|
},
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
/** 拓扑排序:将 edges
|
|
25
|
+
/** 拓扑排序:将 edges 转换为线性节点序列。
|
|
26
|
+
*
|
|
27
|
+
* 修复 review 报告:原实现 edge.from 不在 nodeMap 时静默丢弃,
|
|
28
|
+
* 但 edge.to 的入度仍被 +1,导致目标节点入度永远无法归零,
|
|
29
|
+
* 被误判为「环」(demo 用 `__input__` 哨兵边触发此 bug)。
|
|
30
|
+
* 现在显式校验所有 edge 引用必须对应真实节点,否则快速失败。
|
|
31
|
+
*/
|
|
26
32
|
function topologicalSort(nodes, edges) {
|
|
27
33
|
const nodeMap = new Map(nodes.map((n) => [n.id, n]));
|
|
28
34
|
const inDegree = new Map();
|
|
29
35
|
const adjacency = new Map();
|
|
36
|
+
// 校验节点 ID 唯一性
|
|
37
|
+
if (nodeMap.size !== nodes.length) {
|
|
38
|
+
const seen = new Set();
|
|
39
|
+
for (const n of nodes) {
|
|
40
|
+
if (seen.has(n.id)) {
|
|
41
|
+
throw new Error(`Workflow contains duplicate node id: ${n.id}`);
|
|
42
|
+
}
|
|
43
|
+
seen.add(n.id);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
30
46
|
for (const node of nodes) {
|
|
31
47
|
inDegree.set(node.id, 0);
|
|
32
48
|
adjacency.set(node.id, []);
|
|
33
49
|
}
|
|
50
|
+
// 校验 edge 引用必须存在于 nodes 中(防止 `__input__` 等哨兵节点
|
|
51
|
+
// 被静默吞掉,进而导致下游节点入度无法归零 → 误判为环)
|
|
34
52
|
for (const edge of edges) {
|
|
53
|
+
if (!nodeMap.has(edge.from)) {
|
|
54
|
+
throw new Error(`Workflow edge references unknown source node: "${edge.from}". ` +
|
|
55
|
+
`Edge endpoints must reference existing nodes; ` +
|
|
56
|
+
`reserved sentinel nodes (e.g. "__input__") are not supported ` +
|
|
57
|
+
`by the executor — input assets are injected into the first node ` +
|
|
58
|
+
`with in-degree 0.`);
|
|
59
|
+
}
|
|
60
|
+
if (!nodeMap.has(edge.to)) {
|
|
61
|
+
throw new Error(`Workflow edge references unknown target node: "${edge.to}". ` +
|
|
62
|
+
`Edge endpoints must reference existing nodes.`);
|
|
63
|
+
}
|
|
64
|
+
if (edge.from === edge.to) {
|
|
65
|
+
throw new Error(`Workflow contains self-loop on node: "${edge.from}". ` +
|
|
66
|
+
`Self-loops create cycles and are not allowed.`);
|
|
67
|
+
}
|
|
35
68
|
inDegree.set(edge.to, (inDegree.get(edge.to) ?? 0) + 1);
|
|
36
69
|
adjacency.get(edge.from)?.push(edge.to);
|
|
37
70
|
}
|
|
@@ -131,6 +164,9 @@ export class WorkflowExecutor {
|
|
|
131
164
|
}
|
|
132
165
|
const impl = this.config.capabilityRegistry.resolve(capability);
|
|
133
166
|
if (!impl) {
|
|
167
|
+
if (this.config.capabilityRegistry.isStubOnly(capability)) {
|
|
168
|
+
throw new Error(`Capability "${capability}" is not yet available (only stub engine registered). Install a real engine plugin to use this capability.`);
|
|
169
|
+
}
|
|
134
170
|
throw new Error(`No implementation registered for capability "${capability}"`);
|
|
135
171
|
}
|
|
136
172
|
// 执行能力
|
package/dist/executor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgCH,cAAc;AACd,SAAS,sBAAsB,CAC7B,UAAkB,EAClB,MAAc,EACd,MAAmB,EACnB,UAAyD;IAEzD,OAAO;QACL,UAAU;QACV,MAAM;QACN,MAAM;QACN,UAAU;QACV,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACtB,uBAAuB;YACvB,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;gBACtB,OAAO,CAAC,KAAK,CAAC,WAAW,UAAU,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC;YAC/D,CAAC;iBAAM,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;gBAC5B,OAAO,CAAC,IAAI,CAAC,WAAW,UAAU,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History Store —— 历史栈 IndexedDB 持久化(W7.2)
|
|
3
|
+
*
|
|
4
|
+
* 把每个工作流的 HistoryStack 快照(entries + cursor)连同 initialInputs /
|
|
5
|
+
* currentOutputs 一起持久化到独立 Dexie 库 `lokvis-history`,实现跨会话保留:
|
|
6
|
+
* 刷新页面后,undo/redo 历史与"当前输出"游标均可恢复(前提:outputs 引用的
|
|
7
|
+
* 资产仍在 OPFS/IDB 中,由 W6.6 元数据持久化保证)。
|
|
8
|
+
*
|
|
9
|
+
* 与 OpfsMetadataDatabase 同样的降级策略:IndexedDB 不可用时返回 undefined,
|
|
10
|
+
* runtime 退化为仅内存历史(刷新后丢失,与 W2 行为一致)。
|
|
11
|
+
*
|
|
12
|
+
* 持久化时机(由 runtime 驱动):
|
|
13
|
+
* - 历史变更(append/undo/redo/jumpTo/clear/reset):HistoryStack.onChanged 回调
|
|
14
|
+
* → runtime.persistHistory(workflowId) → save
|
|
15
|
+
* - 条目清空(reset/clear):save 检测到 entries 为空时改为 delete,避免残留空记录
|
|
16
|
+
* - disposeWorkflow:stack.reset() 触发 onChanged → persistHistory → delete
|
|
17
|
+
*
|
|
18
|
+
* 恢复时机:
|
|
19
|
+
* - createRuntime 工厂构造完 impl 后调 loadPersistedHistory() 预加载所有快照
|
|
20
|
+
*/
|
|
21
|
+
import Dexie, { type Table } from 'dexie';
|
|
22
|
+
import type { AssetId, HistoryEntry } from '@lokvis/schema';
|
|
23
|
+
/** 持久化的历史记录(每个工作流一条) */
|
|
24
|
+
export interface HistoryRecord {
|
|
25
|
+
/** 主键 = 工作流 ID */
|
|
26
|
+
workflowId: string;
|
|
27
|
+
/** 历史条目(按时间顺序) */
|
|
28
|
+
entries: HistoryEntry[];
|
|
29
|
+
/** 游标:-1 表示无已应用条目;i 表示第 i 条已应用 */
|
|
30
|
+
cursor: number;
|
|
31
|
+
/** 工作流初始输入 AssetId(undo 回到初始时使用) */
|
|
32
|
+
initialInputs: AssetId[];
|
|
33
|
+
/** 工作流当前输出 AssetId(undo/redo 后的"当前"状态) */
|
|
34
|
+
currentOutputs: AssetId[];
|
|
35
|
+
/** 最后更新时间戳(用于排查与潜在 TTL 清理) */
|
|
36
|
+
updatedAt: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 历史持久化数据库。
|
|
40
|
+
*
|
|
41
|
+
* 独立于 OpfsMetadataDatabase('lokvis-opfs-metadata')与 IdbAssetStore
|
|
42
|
+
* ('lokvis-assets'),避免与资产元数据冲突。表以 workflowId 为主键,
|
|
43
|
+
* 额外索引 updatedAt 便于未来按时间清理陈旧记录。
|
|
44
|
+
*/
|
|
45
|
+
export declare class HistoryDatabase extends Dexie {
|
|
46
|
+
history: Table<HistoryRecord, string>;
|
|
47
|
+
constructor(name?: string);
|
|
48
|
+
}
|
|
49
|
+
/** HistoryStore 抽象接口(便于测试注入 mock) */
|
|
50
|
+
export interface HistoryStore {
|
|
51
|
+
/** 保存(或覆盖)一条历史记录 */
|
|
52
|
+
save(record: HistoryRecord): Promise<void>;
|
|
53
|
+
/** 读取指定工作流的历史记录 */
|
|
54
|
+
load(workflowId: string): Promise<HistoryRecord | undefined>;
|
|
55
|
+
/** 读取全部历史记录(用于启动时预加载) */
|
|
56
|
+
loadAll(): Promise<HistoryRecord[]>;
|
|
57
|
+
/** 删除指定工作流的历史记录 */
|
|
58
|
+
delete(workflowId: string): Promise<void>;
|
|
59
|
+
/** 清空所有历史记录 */
|
|
60
|
+
clear(): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
/** HistoryStore 工厂选项 */
|
|
63
|
+
export interface HistoryStoreOptions {
|
|
64
|
+
/** 测试注入:自定义数据库实例 */
|
|
65
|
+
dbInstance?: HistoryDatabase;
|
|
66
|
+
/** 数据库名(默认 'lokvis-history';仅 dbInstance 未注入时生效) */
|
|
67
|
+
dbName?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 创建 HistoryStore。
|
|
71
|
+
*
|
|
72
|
+
* IndexedDB 不可用时返回 undefined,调用方(runtime)据此跳过持久化,
|
|
73
|
+
* 退化为仅内存历史模式。
|
|
74
|
+
*/
|
|
75
|
+
export declare function createHistoryStore(options?: HistoryStoreOptions): HistoryStore | undefined;
|
|
76
|
+
//# sourceMappingURL=history-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-store.d.ts","sourceRoot":"","sources":["../src/history-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,EAAE,KAAK,KAAK,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG5D,wBAAwB;AACxB,MAAM,WAAW,aAAa;IAC5B,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,0CAA0C;IAC1C,cAAc,EAAE,OAAO,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,OAAO,EAAG,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;gBAE3B,IAAI,SAAmB;CAMpC;AAED,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,oBAAoB;IACpB,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,mBAAmB;IACnB,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IAC7D,yBAAyB;IACzB,OAAO,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACpC,mBAAmB;IACnB,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,eAAe;IACf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAwB;AACxB,MAAM,WAAW,mBAAmB;IAClC,oBAAoB;IACpB,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,mBAAwB,GAChC,YAAY,GAAG,SAAS,CAqD1B"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History Store —— 历史栈 IndexedDB 持久化(W7.2)
|
|
3
|
+
*
|
|
4
|
+
* 把每个工作流的 HistoryStack 快照(entries + cursor)连同 initialInputs /
|
|
5
|
+
* currentOutputs 一起持久化到独立 Dexie 库 `lokvis-history`,实现跨会话保留:
|
|
6
|
+
* 刷新页面后,undo/redo 历史与"当前输出"游标均可恢复(前提:outputs 引用的
|
|
7
|
+
* 资产仍在 OPFS/IDB 中,由 W6.6 元数据持久化保证)。
|
|
8
|
+
*
|
|
9
|
+
* 与 OpfsMetadataDatabase 同样的降级策略:IndexedDB 不可用时返回 undefined,
|
|
10
|
+
* runtime 退化为仅内存历史(刷新后丢失,与 W2 行为一致)。
|
|
11
|
+
*
|
|
12
|
+
* 持久化时机(由 runtime 驱动):
|
|
13
|
+
* - 历史变更(append/undo/redo/jumpTo/clear/reset):HistoryStack.onChanged 回调
|
|
14
|
+
* → runtime.persistHistory(workflowId) → save
|
|
15
|
+
* - 条目清空(reset/clear):save 检测到 entries 为空时改为 delete,避免残留空记录
|
|
16
|
+
* - disposeWorkflow:stack.reset() 触发 onChanged → persistHistory → delete
|
|
17
|
+
*
|
|
18
|
+
* 恢复时机:
|
|
19
|
+
* - createRuntime 工厂构造完 impl 后调 loadPersistedHistory() 预加载所有快照
|
|
20
|
+
*/
|
|
21
|
+
import Dexie, {} from 'dexie';
|
|
22
|
+
import { isIdbSupported } from './idb-asset-store.js';
|
|
23
|
+
/**
|
|
24
|
+
* 历史持久化数据库。
|
|
25
|
+
*
|
|
26
|
+
* 独立于 OpfsMetadataDatabase('lokvis-opfs-metadata')与 IdbAssetStore
|
|
27
|
+
* ('lokvis-assets'),避免与资产元数据冲突。表以 workflowId 为主键,
|
|
28
|
+
* 额外索引 updatedAt 便于未来按时间清理陈旧记录。
|
|
29
|
+
*/
|
|
30
|
+
export class HistoryDatabase extends Dexie {
|
|
31
|
+
history;
|
|
32
|
+
constructor(name = 'lokvis-history') {
|
|
33
|
+
super(name);
|
|
34
|
+
this.version(1).stores({
|
|
35
|
+
history: 'workflowId, updatedAt',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 创建 HistoryStore。
|
|
41
|
+
*
|
|
42
|
+
* IndexedDB 不可用时返回 undefined,调用方(runtime)据此跳过持久化,
|
|
43
|
+
* 退化为仅内存历史模式。
|
|
44
|
+
*/
|
|
45
|
+
export function createHistoryStore(options = {}) {
|
|
46
|
+
const db = resolveHistoryDb(options);
|
|
47
|
+
if (!db)
|
|
48
|
+
return undefined;
|
|
49
|
+
return {
|
|
50
|
+
async save(record) {
|
|
51
|
+
try {
|
|
52
|
+
await db.history.put(record);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
// 持久化失败不阻断历史操作,仅 warn 便于排查
|
|
56
|
+
console.warn(`[lokvis] history store save failed for ${record.workflowId}:`, err);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
async load(workflowId) {
|
|
60
|
+
try {
|
|
61
|
+
return await db.history.get(workflowId);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
console.warn(`[lokvis] history store load failed for ${workflowId}:`, err);
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
async loadAll() {
|
|
69
|
+
try {
|
|
70
|
+
return await db.history.toArray();
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
console.warn('[lokvis] history store loadAll failed:', err);
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
async delete(workflowId) {
|
|
78
|
+
try {
|
|
79
|
+
await db.history.delete(workflowId);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
console.warn(`[lokvis] history store delete failed for ${workflowId}:`, err);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
async clear() {
|
|
86
|
+
try {
|
|
87
|
+
await db.history.clear();
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.warn('[lokvis] history store clear failed:', err);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** 解析历史数据库实例(注入优先,否则在 IDB 可用时新建) */
|
|
96
|
+
function resolveHistoryDb(options) {
|
|
97
|
+
if (options.dbInstance)
|
|
98
|
+
return options.dbInstance;
|
|
99
|
+
if (!isIdbSupported())
|
|
100
|
+
return undefined;
|
|
101
|
+
try {
|
|
102
|
+
return new HistoryDatabase(options.dbName);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
console.warn('[lokvis] history database init failed, falling back to memory-only:', err);
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=history-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history-store.js","sourceRoot":"","sources":["../src/history-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,EAAc,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAkBtD;;;;;;GAMG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,OAAO,CAAgC;IAEvC,YAAY,IAAI,GAAG,gBAAgB;QACjC,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACrB,OAAO,EAAE,uBAAuB;SACjC,CAAC,CAAC;IACL,CAAC;CACF;AAwBD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAA+B,EAAE;IAEjC,MAAM,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAE1B,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,MAAM;YACf,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC/B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,2BAA2B;gBAC3B,OAAO,CAAC,IAAI,CACV,0CAA0C,MAAM,CAAC,UAAU,GAAG,EAC9D,GAAG,CACJ,CAAC;YACJ,CAAC;QACH,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,UAAU;YACnB,IAAI,CAAC;gBACH,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CACV,0CAA0C,UAAU,GAAG,EACvD,GAAG,CACJ,CAAC;gBACF,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;QACD,KAAK,CAAC,OAAO;YACX,IAAI,CAAC;gBACH,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACpC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAC;gBAC5D,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,UAAU;YACrB,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACtC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CACV,4CAA4C,UAAU,GAAG,EACzD,GAAG,CACJ,CAAC;YACJ,CAAC;QACH,CAAC;QACD,KAAK,CAAC,KAAK;YACT,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,oCAAoC;AACpC,SAAS,gBAAgB,CACvB,OAA4B;IAE5B,IAAI,OAAO,CAAC,UAAU;QAAE,OAAO,OAAO,CAAC,UAAU,CAAC;IAClD,IAAI,CAAC,cAAc,EAAE;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CACV,qEAAqE,EACrE,GAAG,CACJ,CAAC;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
|