@visactor/vseed 0.4.1 → 0.4.3
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/cjs/index.cjs +646 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/builder/builder/buildSpec.d.ts +1 -2
- package/dist/esm/builder/builder/buildSpec.js.map +1 -1
- package/dist/esm/builder/builder/builder.d.ts +1116 -106
- package/dist/esm/builder/builder/builder.js +10 -0
- package/dist/esm/builder/builder/builder.js.map +1 -1
- package/dist/esm/builder/builder/prepare.d.ts +12 -0
- package/dist/esm/builder/builder/prepare.js +269 -0
- package/dist/esm/builder/builder/prepare.js.map +1 -0
- package/dist/esm/dataReshape/constant.d.ts +1 -0
- package/dist/esm/dataReshape/constant.js +2 -1
- package/dist/esm/dataReshape/constant.js.map +1 -1
- package/dist/esm/dataSelector/selector.d.ts +71 -2
- package/dist/esm/dataSelector/selector.js +96 -32
- package/dist/esm/dataSelector/selector.js.map +1 -1
- package/dist/esm/pipeline/advanced/chart/pipes/default/defaultMeasures.js +2 -1
- package/dist/esm/pipeline/advanced/chart/pipes/default/defaultMeasures.js.map +1 -1
- package/dist/esm/pipeline/advanced/table/pipes/default/defaultMeasures.js +2 -1
- package/dist/esm/pipeline/advanced/table/pipes/default/defaultMeasures.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationHorizontalLine.js +10 -4
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationHorizontalLine.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationPointCommon.d.ts +8 -2
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationPointCommon.js +27 -5
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationPointCommon.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationPointOfDualAxis.js +8 -4
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationPointOfDualAxis.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationVerticalLine.js +10 -4
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationVerticalLine.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/utils.d.ts +9 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/utils.js +7 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/utils.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/markStyle/barStyle.js +3 -2
- package/dist/esm/pipeline/spec/chart/pipes/markStyle/barStyle.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/markStyle/lineStyle.js +5 -2
- package/dist/esm/pipeline/spec/chart/pipes/markStyle/lineStyle.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/markStyle/pointStyle.js +3 -2
- package/dist/esm/pipeline/spec/chart/pipes/markStyle/pointStyle.js.map +1 -1
- package/dist/esm/pipeline/spec/table/pipes/cellStyle/common.d.ts +4 -1
- package/dist/esm/pipeline/spec/table/pipes/cellStyle/common.js +12 -1
- package/dist/esm/pipeline/spec/table/pipes/cellStyle/common.js.map +1 -1
- package/dist/esm/pipeline/spec/table/pipes/cellStyle/pivot.js +6 -3
- package/dist/esm/pipeline/spec/table/pipes/cellStyle/pivot.js.map +1 -1
- package/dist/esm/pipeline/spec/table/pipes/cellStyle/table.js +7 -3
- package/dist/esm/pipeline/spec/table/pipes/cellStyle/table.js.map +1 -1
- package/dist/esm/pipeline/utils/index.d.ts +1 -0
- package/dist/esm/pipeline/utils/index.js +1 -0
- package/dist/esm/pipeline/utils/sandbox/builtin-utils.d.ts +6 -0
- package/dist/esm/pipeline/utils/sandbox/builtin-utils.js +337 -0
- package/dist/esm/pipeline/utils/sandbox/builtin-utils.js.map +1 -0
- package/dist/esm/pipeline/utils/sandbox/execute.d.ts +73 -0
- package/dist/esm/pipeline/utils/sandbox/execute.js +716 -0
- package/dist/esm/pipeline/utils/sandbox/execute.js.map +1 -0
- package/dist/esm/pipeline/utils/sandbox/index.d.ts +7 -0
- package/dist/esm/pipeline/utils/sandbox/index.js +3 -0
- package/dist/esm/types/builder/builder.d.ts +1 -0
- package/dist/esm/types/builder/builder.js.map +1 -1
- package/dist/esm/types/chartType/area/zArea.d.ts +682 -18
- package/dist/esm/types/chartType/areaPercent/zAreaPercent.d.ts +682 -18
- package/dist/esm/types/chartType/bar/zBar.d.ts +362 -10
- package/dist/esm/types/chartType/barParallel/zBarParallel.d.ts +362 -10
- package/dist/esm/types/chartType/barPercent/zBarPercent.d.ts +362 -10
- package/dist/esm/types/chartType/boxPlot/zBoxPlot.d.ts +210 -14
- package/dist/esm/types/chartType/circlePacking/zCirclePacking.d.ts +2 -2
- package/dist/esm/types/chartType/column/zColumn.d.ts +362 -10
- package/dist/esm/types/chartType/columnParallel/zColumnParallel.d.ts +362 -10
- package/dist/esm/types/chartType/columnPercent/zColumnPercent.d.ts +362 -10
- package/dist/esm/types/chartType/donut/zDonut.d.ts +2 -2
- package/dist/esm/types/chartType/dualAxis/zDualAxis.d.ts +842 -22
- package/dist/esm/types/chartType/funnel/zFunnel.d.ts +2 -2
- package/dist/esm/types/chartType/heatmap/zHeatmap.d.ts +2 -2
- package/dist/esm/types/chartType/histogram/zHistogram.d.ts +362 -10
- package/dist/esm/types/chartType/line/zLine.d.ts +522 -14
- package/dist/esm/types/chartType/pie/zPie.d.ts +2 -2
- package/dist/esm/types/chartType/pivotTable/zPivotTable.d.ts +241 -4
- package/dist/esm/types/chartType/pivotTable/zPivotTable.js +1 -1
- package/dist/esm/types/chartType/pivotTable/zPivotTable.js.map +1 -1
- package/dist/esm/types/chartType/raceBar/zRaceBar.d.ts +362 -10
- package/dist/esm/types/chartType/raceColumn/zRaceColumn.d.ts +362 -10
- package/dist/esm/types/chartType/raceScatter/zRaceScatter.d.ts +362 -10
- package/dist/esm/types/chartType/radar/zRadar.d.ts +2 -2
- package/dist/esm/types/chartType/rose/zRose.d.ts +2 -2
- package/dist/esm/types/chartType/roseParallel/zRoseParallel.d.ts +2 -2
- package/dist/esm/types/chartType/scatter/zScatter.d.ts +362 -10
- package/dist/esm/types/chartType/sunburst/zSunburst.d.ts +2 -2
- package/dist/esm/types/chartType/table/zTable.d.ts +241 -4
- package/dist/esm/types/chartType/table/zTable.js +1 -1
- package/dist/esm/types/chartType/table/zTable.js.map +1 -1
- package/dist/esm/types/chartType/treeMap/zTreeMap.d.ts +2 -2
- package/dist/esm/types/dataSelector/selector.d.ts +594 -3
- package/dist/esm/types/dataSelector/selector.js +58 -2
- package/dist/esm/types/dataSelector/selector.js.map +1 -1
- package/dist/esm/types/properties/annotation/annotation.d.ts +200 -4
- package/dist/esm/types/properties/annotation/annotationHorizontalLine.d.ts +12 -0
- package/dist/esm/types/properties/annotation/annotationPoint.d.ts +19 -0
- package/dist/esm/types/properties/annotation/annotationVerticalLine.d.ts +12 -0
- package/dist/esm/types/properties/annotation/zAnnotationHorizontalLine.d.ts +10 -0
- package/dist/esm/types/properties/annotation/zAnnotationHorizontalLine.js +2 -0
- package/dist/esm/types/properties/annotation/zAnnotationHorizontalLine.js.map +1 -1
- package/dist/esm/types/properties/annotation/zAnnotationPoint.d.ts +80 -2
- package/dist/esm/types/properties/annotation/zAnnotationPoint.js +2 -1
- package/dist/esm/types/properties/annotation/zAnnotationPoint.js.map +1 -1
- package/dist/esm/types/properties/annotation/zAnnotationVerticalLine.d.ts +10 -0
- package/dist/esm/types/properties/annotation/zAnnotationVerticalLine.js +2 -0
- package/dist/esm/types/properties/annotation/zAnnotationVerticalLine.js.map +1 -1
- package/dist/esm/types/properties/cellStyle/bodyCellStyle.d.ts +105 -2
- package/dist/esm/types/properties/cellStyle/bodyCellStyle.js +2 -1
- package/dist/esm/types/properties/cellStyle/bodyCellStyle.js.map +1 -1
- package/dist/esm/types/properties/cellStyle/cellStyle.d.ts +83 -2
- package/dist/esm/types/properties/config/annotation/zAnnotation.d.ts +156 -0
- package/dist/esm/types/properties/config/area.d.ts +160 -4
- package/dist/esm/types/properties/config/bar.d.ts +240 -6
- package/dist/esm/types/properties/config/boxplot.d.ts +80 -2
- package/dist/esm/types/properties/config/circlePacking.d.ts +2 -2
- package/dist/esm/types/properties/config/column.d.ts +240 -6
- package/dist/esm/types/properties/config/config.d.ts +1300 -52
- package/dist/esm/types/properties/config/dualAxis.d.ts +80 -2
- package/dist/esm/types/properties/config/funnel.d.ts +2 -2
- package/dist/esm/types/properties/config/heatmap.d.ts +2 -2
- package/dist/esm/types/properties/config/histogram.d.ts +80 -2
- package/dist/esm/types/properties/config/label/zLabel.d.ts +2 -2
- package/dist/esm/types/properties/config/label/zPieLabel.d.ts +2 -2
- package/dist/esm/types/properties/config/line.d.ts +80 -2
- package/dist/esm/types/properties/config/pie.d.ts +6 -6
- package/dist/esm/types/properties/config/race.d.ts +240 -6
- package/dist/esm/types/properties/config/rose.d.ts +4 -4
- package/dist/esm/types/properties/config/scatter.d.ts +80 -2
- package/dist/esm/types/properties/config/sunburst.d.ts +2 -2
- package/dist/esm/types/properties/config/treeMap.d.ts +2 -2
- package/dist/esm/types/properties/markStyle/areaStyle.d.ts +19 -0
- package/dist/esm/types/properties/markStyle/barStyle.d.ts +99 -2
- package/dist/esm/types/properties/markStyle/barStyle.js +2 -1
- package/dist/esm/types/properties/markStyle/barStyle.js.map +1 -1
- package/dist/esm/types/properties/markStyle/boxPlotStyle.d.ts +2 -2
- package/dist/esm/types/properties/markStyle/lineStyle.d.ts +99 -2
- package/dist/esm/types/properties/markStyle/lineStyle.js +2 -1
- package/dist/esm/types/properties/markStyle/lineStyle.js.map +1 -1
- package/dist/esm/types/properties/markStyle/markStyle.d.ts +648 -24
- package/dist/esm/types/properties/markStyle/outlierStyle.d.ts +2 -2
- package/dist/esm/types/properties/markStyle/pointStyle.d.ts +99 -2
- package/dist/esm/types/properties/markStyle/pointStyle.js +2 -1
- package/dist/esm/types/properties/markStyle/pointStyle.js.map +1 -1
- package/dist/esm/types/properties/markStyle/zAreaStyle.d.ts +80 -2
- package/dist/esm/types/properties/markStyle/zAreaStyle.js +2 -1
- package/dist/esm/types/properties/markStyle/zAreaStyle.js.map +1 -1
- package/dist/esm/types/properties/theme/customTheme.d.ts +2600 -104
- package/dist/esm/types/sandbox.d.ts +19 -0
- package/dist/esm/types/sandbox.js +0 -0
- package/dist/esm/types/zVseed.d.ts +7 -25058
- package/dist/esm/types/zVseed.js.map +1 -1
- package/dist/umd/index.js +1786 -260
- package/dist/umd/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
import { BUILTIN_UTILS_SOURCE } from "./builtin-utils.js";
|
|
2
|
+
class WorkerPool {
|
|
3
|
+
workers = [];
|
|
4
|
+
availableWorkers = [];
|
|
5
|
+
maxSize;
|
|
6
|
+
isInitialized = false;
|
|
7
|
+
constructor(maxSize = 2){
|
|
8
|
+
this.maxSize = maxSize;
|
|
9
|
+
}
|
|
10
|
+
async initialize() {
|
|
11
|
+
if (this.isInitialized) return;
|
|
12
|
+
for(let i = 0; i < this.maxSize; i++)try {
|
|
13
|
+
const worker = await this.createSecureWorker();
|
|
14
|
+
this.workers.push(worker);
|
|
15
|
+
this.availableWorkers.push(worker);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
this.workers.forEach((w)=>w.terminate());
|
|
18
|
+
this.workers = [];
|
|
19
|
+
this.availableWorkers = [];
|
|
20
|
+
throw new Error(`Failed to initialize Worker pool: ${error instanceof Error ? error.message : String(error)}`);
|
|
21
|
+
}
|
|
22
|
+
this.isInitialized = true;
|
|
23
|
+
}
|
|
24
|
+
createSecureWorker() {
|
|
25
|
+
const libraryLoadCode = `// 内置工具库(lodash/Ramda 兼容 API)\n${BUILTIN_UTILS_SOURCE}`;
|
|
26
|
+
const workerCode = `
|
|
27
|
+
// ============================================
|
|
28
|
+
// 阶段 1: 立即执行安全加固 (IIFE)
|
|
29
|
+
// ============================================
|
|
30
|
+
(function initSecureSandbox() {
|
|
31
|
+
'use strict';
|
|
32
|
+
|
|
33
|
+
// 1.1 保存必要的原生引用(在被删除前)
|
|
34
|
+
const nativeImportScripts = self.importScripts;
|
|
35
|
+
const nativePostMessage = self.postMessage.bind(self);
|
|
36
|
+
|
|
37
|
+
// 1.2 立即删除危险 API
|
|
38
|
+
delete self.importScripts;
|
|
39
|
+
delete self.fetch;
|
|
40
|
+
delete self.XMLHttpRequest;
|
|
41
|
+
delete self.WebSocket;
|
|
42
|
+
|
|
43
|
+
// 1.3 冻结关键原型链(防止污染和篡改)
|
|
44
|
+
Object.freeze(Object.prototype);
|
|
45
|
+
Object.freeze(Array.prototype);
|
|
46
|
+
Object.freeze(Function.prototype);
|
|
47
|
+
Object.freeze(String.prototype);
|
|
48
|
+
Object.freeze(Number.prototype);
|
|
49
|
+
Object.freeze(Boolean.prototype);
|
|
50
|
+
|
|
51
|
+
// ============================================
|
|
52
|
+
// 阶段 2: 加载工具库
|
|
53
|
+
// ============================================
|
|
54
|
+
try {
|
|
55
|
+
${libraryLoadCode}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
nativePostMessage({
|
|
58
|
+
initError: 'Failed to load utility library: ' + error.message
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 验证库是否加载成功
|
|
64
|
+
const utilityLib = self._;
|
|
65
|
+
if (!utilityLib) {
|
|
66
|
+
nativePostMessage({
|
|
67
|
+
initError: 'Builtin utility library (_ or R) not found after loading'
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================
|
|
73
|
+
// 阶段 3: 创建安全上下文
|
|
74
|
+
// ============================================
|
|
75
|
+
const createSafeContext = (lib) => {
|
|
76
|
+
const allowedGlobals = {
|
|
77
|
+
// 工具库
|
|
78
|
+
_: lib,
|
|
79
|
+
R: lib,
|
|
80
|
+
|
|
81
|
+
// 基础类型构造函数(只读)
|
|
82
|
+
Array: Array,
|
|
83
|
+
Object: Object,
|
|
84
|
+
String: String,
|
|
85
|
+
Number: Number,
|
|
86
|
+
Boolean: Boolean,
|
|
87
|
+
Date: Date,
|
|
88
|
+
|
|
89
|
+
// 数学和工具
|
|
90
|
+
Math: Math,
|
|
91
|
+
JSON: JSON,
|
|
92
|
+
|
|
93
|
+
// 类型检查
|
|
94
|
+
parseInt: parseInt,
|
|
95
|
+
parseFloat: parseFloat,
|
|
96
|
+
isNaN: isNaN,
|
|
97
|
+
isFinite: isFinite,
|
|
98
|
+
|
|
99
|
+
// 错误类型(用于调试)
|
|
100
|
+
Error: Error,
|
|
101
|
+
TypeError: TypeError,
|
|
102
|
+
RangeError: RangeError,
|
|
103
|
+
|
|
104
|
+
// 只读的数据引用
|
|
105
|
+
data: null,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// 使用 Proxy 严格控制访问
|
|
109
|
+
return new Proxy(allowedGlobals, {
|
|
110
|
+
get(target, prop) {
|
|
111
|
+
if (prop === Symbol.unscopables) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (prop in target) {
|
|
116
|
+
return target[prop];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 拒绝访问任何未定义的属性
|
|
120
|
+
throw new ReferenceError(
|
|
121
|
+
\`'\${String(prop)}' is not defined. \\n\` +
|
|
122
|
+
\`Only these globals are available: \${Object.keys(allowedGlobals).join(', ')}\`
|
|
123
|
+
);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
set(target, prop, value) {
|
|
127
|
+
// 只允许设置 data 属性(用于传递执行数据)
|
|
128
|
+
if (prop === 'data') {
|
|
129
|
+
target[prop] = value;
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 拒绝设置其他属性
|
|
134
|
+
throw new TypeError(
|
|
135
|
+
\`Cannot set property '\${String(prop)}' on global context\`
|
|
136
|
+
);
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
has(target, prop) {
|
|
140
|
+
return prop in target;
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// 防止 getOwnPropertyDescriptor 等元编程操作
|
|
144
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
145
|
+
if (prop in target) {
|
|
146
|
+
return Object.getOwnPropertyDescriptor(target, prop);
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const safeContext = createSafeContext(utilityLib);
|
|
154
|
+
|
|
155
|
+
// ============================================
|
|
156
|
+
// 阶段 4: 代码执行引擎
|
|
157
|
+
// ============================================
|
|
158
|
+
const executeUserCodeSafely = (code, data, timeout) => {
|
|
159
|
+
// 设置超时保护(内层防御)
|
|
160
|
+
let timeoutId = null;
|
|
161
|
+
let isTimedOut = false;
|
|
162
|
+
|
|
163
|
+
if (timeout > 0) {
|
|
164
|
+
timeoutId = setTimeout(() => {
|
|
165
|
+
isTimedOut = true;
|
|
166
|
+
}, timeout);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
// 超时检查函数
|
|
171
|
+
// ⚠️ 注意:这只能防御"合作式"的代码(包含 I/O、迭代、递归)
|
|
172
|
+
// 对于纯计算死循环(while(true){}),只有外层 worker.terminate() 能终止
|
|
173
|
+
const checkTimeout = () => {
|
|
174
|
+
if (isTimedOut) {
|
|
175
|
+
throw new Error(\`Execution timeout (exceeded \${timeout}ms)\`);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// 更新安全上下文中的数据
|
|
180
|
+
safeContext.data = data;
|
|
181
|
+
|
|
182
|
+
// 提取安全上下文的所有变量名和值
|
|
183
|
+
const contextKeys = Object.keys(safeContext);
|
|
184
|
+
const contextValues = contextKeys.map(key => safeContext[key]);
|
|
185
|
+
|
|
186
|
+
// 显式遮蔽 Worker 全局对象中的危险变量(设为 undefined)
|
|
187
|
+
const shadowedGlobals = [
|
|
188
|
+
'self', // Worker 全局对象本身
|
|
189
|
+
'postMessage', // 通信通道(防止滥用)
|
|
190
|
+
'close', // Worker 终止方法
|
|
191
|
+
'importScripts', // 动态加载脚本(已删除,但显式遮蔽更安全)
|
|
192
|
+
'addEventListener', // 事件监听器
|
|
193
|
+
'removeEventListener',
|
|
194
|
+
'dispatchEvent',
|
|
195
|
+
'onmessage', // 消息处理器
|
|
196
|
+
'onerror', // 错误处理器
|
|
197
|
+
'onmessageerror',
|
|
198
|
+
'setTimeout', // ⚠️ 遮蔽定时器,防止资源泄漏
|
|
199
|
+
'clearTimeout',
|
|
200
|
+
'setInterval', // ⚠️ 防止用户创建大量定时器
|
|
201
|
+
'clearInterval',
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
// 包装用户代码:在严格模式下执行,通过参数注入变量
|
|
205
|
+
const wrappedCode = \`
|
|
206
|
+
"use strict";
|
|
207
|
+
// 用户代码在这里执行,可以直接访问注入的变量(_, R, data, Math, etc.)
|
|
208
|
+
\${code}
|
|
209
|
+
\`;
|
|
210
|
+
|
|
211
|
+
// 创建函数:参数是上下文变量名 + checkTimeout + 遮蔽的全局变量名
|
|
212
|
+
const userFunction = new Function(
|
|
213
|
+
...contextKeys, // 安全上下文变量
|
|
214
|
+
'checkTimeout', // 超时检查函数(用户可选调用)
|
|
215
|
+
...shadowedGlobals, // 显式遮蔽的危险全局变量
|
|
216
|
+
wrappedCode
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// 执行并获取结果
|
|
220
|
+
const result = userFunction(
|
|
221
|
+
...contextValues, // 安全上下文的值
|
|
222
|
+
checkTimeout, // 超时检查函数
|
|
223
|
+
...shadowedGlobals.map(() => undefined) // 所有危险全局变量设为 undefined
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// 清除超时
|
|
227
|
+
if (timeoutId) {
|
|
228
|
+
clearTimeout(timeoutId);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 验证返回值(只检查危险类型,不限制结构)
|
|
232
|
+
const validateResultType = (result) => {
|
|
233
|
+
const type = typeof result
|
|
234
|
+
|
|
235
|
+
// 禁止返回函数、Symbol
|
|
236
|
+
if (type === 'function' || type === 'symbol') {
|
|
237
|
+
throw new TypeError(
|
|
238
|
+
\`Code must not return \${type}. Returned types must be serializable.\`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// 禁止返回 Promise
|
|
243
|
+
if (result && typeof result.then === 'function') {
|
|
244
|
+
throw new TypeError(
|
|
245
|
+
\`Code must not return a Promise. Async operations are not allowed.\`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 如果是数组,检查元素的危险类型
|
|
250
|
+
if (Array.isArray(result)) {
|
|
251
|
+
for (let i = 0; i < result.length; i++) {
|
|
252
|
+
const item = result[i];
|
|
253
|
+
const itemType = typeof item;
|
|
254
|
+
|
|
255
|
+
if (itemType === 'function' || itemType === 'symbol') {
|
|
256
|
+
throw new TypeError(
|
|
257
|
+
\`Array element at index \${i} has forbidden type: \${itemType}\`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (item && typeof item.then === 'function') {
|
|
262
|
+
throw new TypeError(
|
|
263
|
+
\`Array element at index \${i} is a Promise. Async operations are not allowed.\`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
validateResultType(result);
|
|
271
|
+
|
|
272
|
+
return result;
|
|
273
|
+
|
|
274
|
+
} catch (error) {
|
|
275
|
+
throw error;
|
|
276
|
+
} finally {
|
|
277
|
+
// 清理数据引用(防止内存泄漏)
|
|
278
|
+
safeContext.data = null;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
// ============================================
|
|
283
|
+
// 阶段 5: 消息处理
|
|
284
|
+
// ============================================
|
|
285
|
+
self.onmessage = function(event) {
|
|
286
|
+
// ⚠️ 防御性检查:确保消息格式正确
|
|
287
|
+
if (!event || !event.data) {
|
|
288
|
+
nativePostMessage({
|
|
289
|
+
taskId: 'unknown',
|
|
290
|
+
success: false,
|
|
291
|
+
error: 'Invalid message format: event.data is null or undefined'
|
|
292
|
+
});
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const { code, data, timeout, taskId } = event.data;
|
|
297
|
+
|
|
298
|
+
// 验证必需字段
|
|
299
|
+
if (!taskId) {
|
|
300
|
+
nativePostMessage({
|
|
301
|
+
taskId: 'unknown',
|
|
302
|
+
success: false,
|
|
303
|
+
error: 'Invalid message: taskId is required'
|
|
304
|
+
});
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const result = executeUserCodeSafely(code, data, timeout);
|
|
310
|
+
|
|
311
|
+
nativePostMessage({
|
|
312
|
+
taskId,
|
|
313
|
+
success: true,
|
|
314
|
+
result: result
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
} catch (error) {
|
|
318
|
+
nativePostMessage({
|
|
319
|
+
taskId,
|
|
320
|
+
success: false,
|
|
321
|
+
error: error.message || String(error),
|
|
322
|
+
errorType: error.constructor.name
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// 错误捕获
|
|
328
|
+
self.onerror = function(event) {
|
|
329
|
+
nativePostMessage({
|
|
330
|
+
globalError: event.message || 'Unknown worker error'
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// 发送初始化成功消息
|
|
335
|
+
nativePostMessage({ initialized: true });
|
|
336
|
+
|
|
337
|
+
})(); // 立即执行
|
|
338
|
+
`;
|
|
339
|
+
const blob = new Blob([
|
|
340
|
+
workerCode
|
|
341
|
+
], {
|
|
342
|
+
type: "application/javascript"
|
|
343
|
+
});
|
|
344
|
+
const blobURL = URL.createObjectURL(blob);
|
|
345
|
+
const worker = new Worker(blobURL);
|
|
346
|
+
URL.revokeObjectURL(blobURL);
|
|
347
|
+
return new Promise((resolve, reject)=>{
|
|
348
|
+
let isSettled = false;
|
|
349
|
+
const cleanup = ()=>{
|
|
350
|
+
if (!isSettled) {
|
|
351
|
+
isSettled = true;
|
|
352
|
+
worker.onmessage = null;
|
|
353
|
+
worker.onerror = null;
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
const timeout = setTimeout(()=>{
|
|
357
|
+
cleanup();
|
|
358
|
+
worker.terminate();
|
|
359
|
+
reject(new Error('Worker initialization timeout'));
|
|
360
|
+
}, 10000);
|
|
361
|
+
worker.onmessage = (e)=>{
|
|
362
|
+
if (e.data.initialized) {
|
|
363
|
+
clearTimeout(timeout);
|
|
364
|
+
cleanup();
|
|
365
|
+
resolve(worker);
|
|
366
|
+
} else if (e.data.initError) {
|
|
367
|
+
clearTimeout(timeout);
|
|
368
|
+
cleanup();
|
|
369
|
+
worker.terminate();
|
|
370
|
+
reject(new Error(e.data.initError));
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
worker.onerror = (errorEvent)=>{
|
|
374
|
+
clearTimeout(timeout);
|
|
375
|
+
cleanup();
|
|
376
|
+
worker.terminate();
|
|
377
|
+
const errorMessage = errorEvent.message || errorEvent.error?.message || 'Unknown worker initialization error';
|
|
378
|
+
reject(new Error(`Worker initialization failed: ${errorMessage}`));
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
async acquire() {
|
|
383
|
+
if (!this.isInitialized) await this.initialize();
|
|
384
|
+
if (0 === this.availableWorkers.length) try {
|
|
385
|
+
const tempWorker = await this.createSecureWorker();
|
|
386
|
+
return tempWorker;
|
|
387
|
+
} catch (error) {
|
|
388
|
+
throw new Error(`Failed to create temporary Worker: ${error instanceof Error ? error.message : String(error)}`);
|
|
389
|
+
}
|
|
390
|
+
return this.availableWorkers.pop();
|
|
391
|
+
}
|
|
392
|
+
release(worker) {
|
|
393
|
+
if (this.workers.includes(worker)) this.availableWorkers.push(worker);
|
|
394
|
+
else worker.terminate();
|
|
395
|
+
}
|
|
396
|
+
terminate() {
|
|
397
|
+
this.workers.forEach((worker)=>worker.terminate());
|
|
398
|
+
this.workers = [];
|
|
399
|
+
this.availableWorkers = [];
|
|
400
|
+
this.isInitialized = false;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
let globalWorkerPool = null;
|
|
404
|
+
let poolInitPromise = null;
|
|
405
|
+
let poolConfig = {
|
|
406
|
+
poolSize: 2
|
|
407
|
+
};
|
|
408
|
+
function getOrCreateWorkerPool() {
|
|
409
|
+
if (globalWorkerPool) return Promise.resolve(globalWorkerPool);
|
|
410
|
+
if (poolInitPromise) return poolInitPromise;
|
|
411
|
+
poolInitPromise = (async ()=>{
|
|
412
|
+
try {
|
|
413
|
+
const pool = new WorkerPool(poolConfig.poolSize);
|
|
414
|
+
await pool.initialize();
|
|
415
|
+
globalWorkerPool = pool;
|
|
416
|
+
return pool;
|
|
417
|
+
} catch (error) {
|
|
418
|
+
poolInitPromise = null;
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
})();
|
|
422
|
+
return poolInitPromise;
|
|
423
|
+
}
|
|
424
|
+
async function initializeWorkerPool(options = {}) {
|
|
425
|
+
poolConfig = {
|
|
426
|
+
poolSize: options.poolSize ?? 2
|
|
427
|
+
};
|
|
428
|
+
await getOrCreateWorkerPool();
|
|
429
|
+
}
|
|
430
|
+
function terminateWorkerPool() {
|
|
431
|
+
if (globalWorkerPool) {
|
|
432
|
+
globalWorkerPool.terminate();
|
|
433
|
+
globalWorkerPool = null;
|
|
434
|
+
}
|
|
435
|
+
poolInitPromise = null;
|
|
436
|
+
}
|
|
437
|
+
function validateCodeSafety(code) {
|
|
438
|
+
if (!code || 0 === code.trim().length) throw new Error('Code cannot be empty');
|
|
439
|
+
if (code.length > 50000) throw new Error('Code is too long (max 50KB)');
|
|
440
|
+
if (!/\breturn\b/.test(code)) throw new Error('Code must contain a return statement');
|
|
441
|
+
const forbiddenPatterns = [
|
|
442
|
+
{
|
|
443
|
+
pattern: /\beval\b/gi,
|
|
444
|
+
description: 'eval()'
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
pattern: /\bFunction\s*\(/gi,
|
|
448
|
+
description: 'Function constructor'
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
pattern: /\bnew\s+Function\b/gi,
|
|
452
|
+
description: 'new Function()'
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
pattern: /\bimportScripts\b/gi,
|
|
456
|
+
description: 'importScripts()'
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
pattern: /\bfetch\b/gi,
|
|
460
|
+
description: 'fetch()'
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
pattern: /\bXMLHttpRequest\b/gi,
|
|
464
|
+
description: 'XMLHttpRequest'
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
pattern: /\bWebSocket\b/gi,
|
|
468
|
+
description: 'WebSocket'
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
pattern: /\blocalStorage\b/gi,
|
|
472
|
+
description: 'localStorage'
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
pattern: /\bsessionStorage\b/gi,
|
|
476
|
+
description: 'sessionStorage'
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
pattern: /\bindexedDB\b/gi,
|
|
480
|
+
description: 'indexedDB'
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
pattern: /\bwindow\b/gi,
|
|
484
|
+
description: 'window'
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
pattern: /\bdocument\b/gi,
|
|
488
|
+
description: 'document'
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
pattern: /\bnavigator\b/gi,
|
|
492
|
+
description: 'navigator'
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
pattern: /\blocation\b/gi,
|
|
496
|
+
description: 'location'
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
pattern: /\brequire\b/gi,
|
|
500
|
+
description: 'require()'
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
pattern: /\bimport\s+/gi,
|
|
504
|
+
description: 'import statement'
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
pattern: /\bexport\s+/gi,
|
|
508
|
+
description: 'export statement'
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
pattern: /\bconstructor\b/gi,
|
|
512
|
+
description: 'constructor access'
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
pattern: /\[['"]constructor['"]\]/gi,
|
|
516
|
+
description: 'constructor via bracket notation'
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
pattern: /\['constructor'\]/gi,
|
|
520
|
+
description: 'constructor string access'
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
pattern: /\b__proto__\b/gi,
|
|
524
|
+
description: '__proto__'
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
pattern: /\bprototype\b/gi,
|
|
528
|
+
description: 'prototype manipulation'
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
pattern: /Object\.setPrototypeOf/gi,
|
|
532
|
+
description: 'Object.setPrototypeOf()'
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
pattern: /Object\.getPrototypeOf/gi,
|
|
536
|
+
description: 'Object.getPrototypeOf()'
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
pattern: /Object\.create\s*\(\s*null\s*\)/gi,
|
|
540
|
+
description: 'Object.create(null)'
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
pattern: /\bReflect\./gi,
|
|
544
|
+
description: 'Reflect API'
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
pattern: /\bProxy\b/gi,
|
|
548
|
+
description: 'Proxy constructor'
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
pattern: /\bglobal\b/gi,
|
|
552
|
+
description: 'global object'
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
pattern: /\bglobalThis\b/gi,
|
|
556
|
+
description: 'globalThis'
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
pattern: /\bself\s*\[/gi,
|
|
560
|
+
description: 'self[] access'
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
pattern: /\bthis\s*\.\s*constructor/gi,
|
|
564
|
+
description: 'this.constructor'
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
pattern: /\basync\s+function/gi,
|
|
568
|
+
description: 'async function'
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
pattern: /\bawait\b/gi,
|
|
572
|
+
description: 'await keyword'
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
pattern: /\bnew\s+Promise\b/gi,
|
|
576
|
+
description: 'new Promise()'
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
pattern: /\.then\s*\(/gi,
|
|
580
|
+
description: 'Promise.then()'
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
pattern: /\bfunction\s*\*/gi,
|
|
584
|
+
description: 'generator function'
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
pattern: /\byield\b/gi,
|
|
588
|
+
description: 'yield keyword'
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
pattern: /\.apply\s*\(/gi,
|
|
592
|
+
description: 'Function.apply()'
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
pattern: /\.call\s*\(/gi,
|
|
596
|
+
description: 'Function.call()'
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
pattern: /\.bind\s*\(/gi,
|
|
600
|
+
description: 'Function.bind()'
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
pattern: /\bWorker\b/gi,
|
|
604
|
+
description: 'Worker constructor'
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
pattern: /\bSharedWorker\b/gi,
|
|
608
|
+
description: 'SharedWorker'
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
pattern: /\bServiceWorker\b/gi,
|
|
612
|
+
description: 'ServiceWorker'
|
|
613
|
+
}
|
|
614
|
+
];
|
|
615
|
+
for (const { pattern, description } of forbiddenPatterns)if (pattern.test(code)) throw new Error(`Security violation: Code contains forbidden pattern "${description}". For AI-generated code, please regenerate without this pattern.`);
|
|
616
|
+
const suspiciousPatterns = [
|
|
617
|
+
/['"]con['"] *\+ *['"]structor['"]/gi,
|
|
618
|
+
/['"]ev['"] *\+ *['"]al['"]/gi,
|
|
619
|
+
/['"]__pro['"] *\+ *['"]to__['"]/gi,
|
|
620
|
+
/\b(self|window|global)\s*\[\s*['"][a-z]+['"]\s*\+\s*['"][a-z]+['"]\s*\]/gi
|
|
621
|
+
];
|
|
622
|
+
for (const pattern of suspiciousPatterns)if (pattern.test(code)) throw new Error('Security violation: Code contains suspicious string concatenation that may bypass security checks.');
|
|
623
|
+
const aiCodeWarnings = [
|
|
624
|
+
{
|
|
625
|
+
pattern: /\[['"][a-z_$]+['"]\]/gi,
|
|
626
|
+
count: 0,
|
|
627
|
+
threshold: 10,
|
|
628
|
+
description: 'excessive bracket notation'
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
pattern: /\s*\+\s*['"]/gi,
|
|
632
|
+
count: 0,
|
|
633
|
+
threshold: 5,
|
|
634
|
+
description: 'excessive string concatenation'
|
|
635
|
+
}
|
|
636
|
+
];
|
|
637
|
+
for (const warning of aiCodeWarnings){
|
|
638
|
+
const matches = code.match(warning.pattern);
|
|
639
|
+
if (matches && matches.length > warning.threshold) throw new Error(`Security warning: Code contains ${matches.length} instances of ${warning.description}, which is unusual for AI-generated code. Please regenerate.`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
async function executeFilterCode(options) {
|
|
643
|
+
const { code, data, timeout = 2000 } = options;
|
|
644
|
+
if ('undefined' == typeof Worker) return {
|
|
645
|
+
success: false,
|
|
646
|
+
data: [],
|
|
647
|
+
error: 'Web Worker is not supported in this environment'
|
|
648
|
+
};
|
|
649
|
+
try {
|
|
650
|
+
validateCodeSafety(code);
|
|
651
|
+
if (!Array.isArray(data)) throw new Error('Input data must be an array');
|
|
652
|
+
if (data.length > 100000) console.warn(`[vseed] Large dataset detected: ${data.length} items. Consider pagination for better performance.`);
|
|
653
|
+
const dataSize = JSON.stringify(data).length;
|
|
654
|
+
if (dataSize > 10485760) throw new Error(`Input data is too large (${(dataSize / 1024 / 1024).toFixed(1)}MB). Maximum allowed is 10MB.`);
|
|
655
|
+
const pool = await getOrCreateWorkerPool();
|
|
656
|
+
const worker = await pool.acquire();
|
|
657
|
+
const taskId = `task_${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
|
658
|
+
let shouldReleaseWorker = true;
|
|
659
|
+
try {
|
|
660
|
+
const result = await new Promise((resolve, reject)=>{
|
|
661
|
+
let isSettled = false;
|
|
662
|
+
const cleanup = ()=>{
|
|
663
|
+
if (!isSettled) {
|
|
664
|
+
isSettled = true;
|
|
665
|
+
worker.removeEventListener('message', messageHandler);
|
|
666
|
+
worker.removeEventListener('error', errorHandler);
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
const outerTimeoutId = setTimeout(()=>{
|
|
670
|
+
cleanup();
|
|
671
|
+
shouldReleaseWorker = false;
|
|
672
|
+
worker.terminate();
|
|
673
|
+
reject(new Error(`Execution timeout (exceeded ${timeout}ms) - outer guard`));
|
|
674
|
+
}, timeout + 1000);
|
|
675
|
+
const messageHandler = (e)=>{
|
|
676
|
+
if (e.data.taskId !== taskId) return;
|
|
677
|
+
clearTimeout(outerTimeoutId);
|
|
678
|
+
cleanup();
|
|
679
|
+
if (e.data.success) resolve(e.data.result);
|
|
680
|
+
else reject(new Error(`Execution failed: ${e.data.error}${e.data.errorType ? ` (${e.data.errorType})` : ''}`));
|
|
681
|
+
};
|
|
682
|
+
const errorHandler = (errorEvent)=>{
|
|
683
|
+
clearTimeout(outerTimeoutId);
|
|
684
|
+
cleanup();
|
|
685
|
+
shouldReleaseWorker = false;
|
|
686
|
+
worker.terminate();
|
|
687
|
+
const errorMessage = errorEvent.message || errorEvent.error?.message || 'Unknown worker error';
|
|
688
|
+
reject(new Error(`Worker error: ${errorMessage}`));
|
|
689
|
+
};
|
|
690
|
+
worker.addEventListener('message', messageHandler);
|
|
691
|
+
worker.addEventListener('error', errorHandler);
|
|
692
|
+
worker.postMessage({
|
|
693
|
+
taskId,
|
|
694
|
+
code,
|
|
695
|
+
data,
|
|
696
|
+
timeout
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
return {
|
|
700
|
+
success: true,
|
|
701
|
+
data: result
|
|
702
|
+
};
|
|
703
|
+
} finally{
|
|
704
|
+
if (shouldReleaseWorker && globalWorkerPool) globalWorkerPool.release(worker);
|
|
705
|
+
}
|
|
706
|
+
} catch (error) {
|
|
707
|
+
return {
|
|
708
|
+
success: false,
|
|
709
|
+
data: [],
|
|
710
|
+
error: error instanceof Error ? error.message : String(error)
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
export { executeFilterCode, initializeWorkerPool, terminateWorkerPool, validateCodeSafety };
|
|
715
|
+
|
|
716
|
+
//# sourceMappingURL=execute.js.map
|