@actiondock/core 2.6.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -68
- package/dist/catalog/action-catalog.d.ts +56 -0
- package/dist/catalog/action-catalog.js +202 -0
- package/dist/catalog/discovery.d.ts +60 -0
- package/dist/catalog/discovery.js +119 -0
- package/dist/catalog/graph.d.ts +190 -0
- package/dist/catalog/graph.js +589 -0
- package/dist/catalog/index.d.ts +5 -5
- package/dist/catalog/index.js +5 -5
- package/dist/catalog/resolve-action.d.ts +43 -0
- package/dist/catalog/resolve-action.js +119 -0
- package/dist/catalog/resolve-playbook.d.ts +26 -0
- package/dist/catalog/resolve-playbook.js +114 -0
- package/dist/doctor/checks/project.js +27 -8
- package/dist/doctor/checks/registry.js +2 -2
- package/dist/doctor/checks/runtime.js +34 -1
- package/dist/errors.d.ts +122 -10
- package/dist/errors.js +123 -10
- package/dist/execution/service.d.ts +18 -16
- package/dist/execution/service.js +183 -252
- package/dist/execution/types.d.ts +25 -69
- package/dist/execution/types.js +2 -1
- package/dist/filter/intent.d.ts +8 -9
- package/dist/filter/intent.js +49 -10
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.js +4 -0
- package/dist/host/host.d.ts +74 -32
- package/dist/host/host.js +585 -336
- package/dist/host/routing.d.ts +15 -48
- package/dist/host/routing.js +90 -153
- package/dist/host/types.d.ts +50 -17
- package/dist/index.d.ts +21 -22
- package/dist/index.js +19 -21
- package/dist/input/advice.d.ts +104 -0
- package/dist/input/advice.js +716 -0
- package/dist/input/describe.d.ts +106 -0
- package/dist/input/describe.js +176 -0
- package/dist/input/file-input.d.ts +54 -0
- package/dist/input/file-input.js +124 -0
- package/dist/input/flat-decode.d.ts +11 -0
- package/dist/input/flat-decode.js +13 -0
- package/dist/input/flat-errors.d.ts +71 -0
- package/dist/input/flat-errors.js +87 -0
- package/dist/input/flat-materializer.d.ts +29 -0
- package/dist/input/flat-materializer.js +177 -0
- package/dist/input/flat-parser.d.ts +50 -0
- package/dist/input/flat-parser.js +169 -0
- package/dist/input/flat-predicates.d.ts +34 -0
- package/dist/input/flat-predicates.js +50 -0
- package/dist/input/index.d.ts +14 -0
- package/dist/input/index.js +14 -0
- package/dist/input/input-resolver.d.ts +63 -0
- package/dist/input/input-resolver.js +254 -0
- package/dist/input/json-depth-scanner.d.ts +17 -0
- package/dist/input/json-depth-scanner.js +49 -0
- package/dist/input/metadata.d.ts +99 -0
- package/dist/input/metadata.js +99 -0
- package/dist/input/stdin-input.d.ts +26 -0
- package/dist/input/stdin-input.js +125 -0
- package/dist/input/utf8.d.ts +19 -0
- package/dist/input/utf8.js +36 -0
- package/dist/input/validation-mapper.d.ts +27 -0
- package/dist/input/validation-mapper.js +144 -0
- package/dist/invocation/index.d.ts +2 -0
- package/dist/invocation/index.js +2 -0
- package/dist/invocation/policy.d.ts +157 -0
- package/dist/invocation/policy.js +231 -0
- package/dist/invocation/types.d.ts +172 -0
- package/dist/invocation/types.js +97 -0
- package/dist/ipc/diagnostic.d.ts +0 -12
- package/dist/ipc/diagnostic.js +0 -21
- package/dist/ipc/host.d.ts +4 -4
- package/dist/ipc/host.js +44 -47
- package/dist/ipc/index.d.ts +1 -1
- package/dist/ipc/index.js +1 -1
- package/dist/ipc/service.d.ts +40 -0
- package/dist/ipc/service.js +349 -0
- package/dist/ipc/types.d.ts +21 -15
- package/dist/json/index.d.ts +1 -0
- package/dist/json/index.js +1 -0
- package/dist/json/value-validator.d.ts +88 -0
- package/dist/json/value-validator.js +330 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/module-loader.d.ts +63 -0
- package/dist/node/module-loader.js +112 -0
- package/dist/package/index.d.ts +23 -0
- package/dist/package/index.js +18 -0
- package/dist/{app/app.d.ts → package/runtime.d.ts} +41 -20
- package/dist/{app/app.js → package/runtime.js} +140 -95
- package/dist/{app → package}/static-index.js +58 -41
- package/dist/package/storage-view.d.ts +13 -0
- package/dist/package/storage-view.js +22 -0
- package/dist/{app → package}/types.d.ts +76 -57
- package/dist/package/types.js +22 -0
- package/dist/platform/index.d.ts +1 -1
- package/dist/platform/index.js +1 -1
- package/dist/platform/node.d.ts +48 -0
- package/dist/platform/node.js +96 -0
- package/dist/platform/types.d.ts +0 -9
- package/dist/process/control-arbiter.d.ts +2 -1
- package/dist/process/control-arbiter.js +1 -2
- package/dist/process/driver.d.ts +9 -8
- package/dist/process/driver.js +11 -22
- package/dist/process/index.d.ts +2 -0
- package/dist/process/index.js +2 -0
- package/dist/process/managed-record.d.ts +2 -2
- package/dist/process/process-driver.d.ts +109 -0
- package/dist/process/process-driver.js +650 -0
- package/dist/process/process-executor.d.ts +47 -0
- package/dist/process/process-executor.js +296 -0
- package/dist/process/process-manager.js +48 -26
- package/dist/process/run-executor.js +12 -12
- package/dist/profile/client-actions.js +2 -2
- package/dist/profile/client-health.js +3 -2
- package/dist/profile/client-query.js +5 -6
- package/dist/profile/client-transport.d.ts +19 -17
- package/dist/profile/client-transport.js +48 -80
- package/dist/profile/client.d.ts +2 -2
- package/dist/profile/client.js +1 -1
- package/dist/profile/index.d.ts +1 -0
- package/dist/profile/index.js +1 -0
- package/dist/profile/manager.d.ts +5 -0
- package/dist/profile/manager.js +41 -6
- package/dist/project/closure.js +2 -2
- package/dist/project/index.d.ts +8 -1
- package/dist/project/index.js +10 -1
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +1 -1
- package/dist/project/loader.js +26 -29
- package/dist/project/transactions.js +5 -10
- package/dist/registry/index.d.ts +3 -1
- package/dist/registry/index.js +2 -1
- package/dist/registry/lock.d.ts +28 -7
- package/dist/registry/lock.js +136 -16
- package/dist/registry/registry.d.ts +1 -5
- package/dist/registry/registry.js +1 -53
- package/dist/registry/scan.d.ts +0 -15
- package/dist/registry/scan.js +0 -59
- package/dist/registry/store.d.ts +42 -0
- package/dist/registry/store.js +43 -0
- package/dist/registry/types.d.ts +0 -24
- package/dist/runtime/action-collection.d.ts +1 -1
- package/dist/runtime/action-registry.d.ts +0 -4
- package/dist/runtime/action-registry.js +0 -7
- package/dist/runtime/context.js +5 -11
- package/dist/runtime/events.js +3 -6
- package/dist/runtime/identity.d.ts +22 -0
- package/dist/runtime/identity.js +10 -0
- package/dist/runtime/index.d.ts +1 -2
- package/dist/runtime/index.js +1 -2
- package/dist/runtime/module-loader.d.ts +2 -34
- package/dist/runtime/module-loader.js +1 -16
- package/dist/runtime/process.d.ts +6 -24
- package/dist/runtime/process.js +1 -118
- package/dist/runtime/run-persistence.js +2 -5
- package/dist/runtime/runner.d.ts +33 -103
- package/dist/runtime/runner.js +142 -404
- package/dist/runtime/standalone.d.ts +37 -9
- package/dist/runtime/standalone.js +195 -136
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/validator.d.ts +10 -0
- package/dist/schema/validator.js +48 -54
- package/dist/server/body.d.ts +16 -8
- package/dist/server/body.js +33 -16
- package/dist/server/dispatcher.d.ts +6 -10
- package/dist/server/dispatcher.js +24 -32
- package/dist/server/http-server.d.ts +87 -0
- package/dist/server/http-server.js +304 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.js +10 -0
- package/dist/server/mcp-endpoint.d.ts +61 -0
- package/dist/server/mcp-endpoint.js +118 -0
- package/dist/server/routes/actions.js +34 -28
- package/dist/server/routes/common.d.ts +10 -14
- package/dist/server/routes/common.js +32 -56
- package/dist/server/routes/config.js +54 -64
- package/dist/server/routes/doctor.d.ts +1 -1
- package/dist/server/routes/doctor.js +10 -18
- package/dist/server/routes/health.d.ts +1 -1
- package/dist/server/routes/health.js +2 -3
- package/dist/server/routes/info.d.ts +2 -2
- package/dist/server/routes/info.js +12 -13
- package/dist/server/routes/playbooks.js +16 -29
- package/dist/server/routes/runs.js +95 -117
- package/dist/server/routes/state.js +63 -62
- package/dist/server/server.d.ts +2 -2
- package/dist/server/server.js +43 -227
- package/dist/server/types.d.ts +8 -19
- package/dist/service/factory.d.ts +12 -0
- package/dist/service/factory.js +85 -0
- package/dist/{target → service}/index.d.ts +1 -1
- package/dist/{target → service}/index.js +1 -1
- package/dist/service/local.d.ts +27 -0
- package/dist/service/local.js +138 -0
- package/dist/{target → service}/remote-errors.d.ts +3 -6
- package/dist/service/remote-errors.js +38 -0
- package/dist/{target → service}/remote-polling.d.ts +2 -2
- package/dist/{target → service}/remote-polling.js +8 -8
- package/dist/service/remote.d.ts +34 -0
- package/dist/service/remote.js +634 -0
- package/dist/service/sse-stream.d.ts +24 -0
- package/dist/service/sse-stream.js +139 -0
- package/dist/service/types.d.ts +272 -0
- package/dist/service/types.js +32 -0
- package/dist/storage/data-dir-lock.js +10 -5
- package/dist/storage/driver.d.ts +2 -1
- package/dist/storage/driver.js +3 -52
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/lock-core.d.ts +2 -6
- package/dist/storage/lock-core.js +4 -17
- package/dist/storage/params.d.ts +2 -8
- package/dist/storage/params.js +2 -8
- package/dist/storage/sqlite-driver.d.ts +40 -0
- package/dist/storage/sqlite-driver.js +116 -0
- package/dist/storage/sqlite.js +21 -6
- package/dist/storage/types.d.ts +4 -4
- package/dist/storage/types.js +2 -2
- package/dist/utils/index.d.ts +53 -0
- package/dist/utils/index.js +99 -4
- package/dist/utils/process.d.ts +10 -0
- package/dist/utils/process.js +21 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +41 -3
- package/dist/app/index.d.ts +0 -2
- package/dist/app/index.js +0 -2
- package/dist/app/types.js +0 -1
- package/dist/catalog/action-index.d.ts +0 -9
- package/dist/catalog/action-index.js +0 -95
- package/dist/catalog/action-resolver.d.ts +0 -20
- package/dist/catalog/action-resolver.js +0 -95
- package/dist/catalog/location-registry.d.ts +0 -15
- package/dist/catalog/location-registry.js +0 -138
- package/dist/catalog/package-catalog.d.ts +0 -7
- package/dist/catalog/package-catalog.js +0 -77
- package/dist/catalog/types.d.ts +0 -68
- package/dist/catalog/types.js +0 -1
- package/dist/export/index.d.ts +0 -1
- package/dist/export/index.js +0 -1
- package/dist/export/templates.d.ts +0 -71
- package/dist/export/templates.js +0 -623
- package/dist/ipc/target.d.ts +0 -74
- package/dist/ipc/target.js +0 -365
- package/dist/platform/default.d.ts +0 -38
- package/dist/platform/default.js +0 -78
- package/dist/project/resolver.d.ts +0 -116
- package/dist/project/resolver.js +0 -376
- package/dist/registry/resolve.d.ts +0 -62
- package/dist/registry/resolve.js +0 -195
- package/dist/runtime/execution-manager.d.ts +0 -48
- package/dist/runtime/execution-manager.js +0 -66
- package/dist/runtime/package-runner-factory.d.ts +0 -82
- package/dist/runtime/package-runner-factory.js +0 -186
- package/dist/target/local.d.ts +0 -53
- package/dist/target/local.js +0 -349
- package/dist/target/remote-errors.js +0 -51
- package/dist/target/remote.d.ts +0 -86
- package/dist/target/remote.js +0 -818
- package/dist/target/target.d.ts +0 -6
- package/dist/target/target.js +0 -37
- package/dist/target/types.d.ts +0 -261
- package/dist/target/types.js +0 -35
- /package/dist/{app → package}/static-index.d.ts +0 -0
- /package/dist/{target → service}/sse-parser.d.ts +0 -0
- /package/dist/{target → service}/sse-parser.js +0 -0
|
@@ -10,8 +10,18 @@ export interface ValidationResult {
|
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* 递归检查数据中是否包含危险的原型污染属性键名(__proto__、constructor、prototype)。
|
|
13
|
+
* 内部委托 validateActionInputValue 执行迭代遍历,确保环路安全、有向无环图安全且无调用栈溢出风险。
|
|
13
14
|
*/
|
|
14
15
|
export declare function hasDangerousKeys(data: unknown): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 内部纯 Schema 校验器。
|
|
18
|
+
* 仅处理布尔 Schema 与 Ajv 规则校验,严禁执行 dangerous-key 遍历。
|
|
19
|
+
*
|
|
20
|
+
* @param schema 期望匹配的 JSON Schema 对象
|
|
21
|
+
* @param data 待校验的原始数据
|
|
22
|
+
* @returns 包含 valid 状态与错误信息列表的 ValidationResult
|
|
23
|
+
*/
|
|
24
|
+
export declare function validateSchemaOnly(schema: JsonSchema | undefined, data: unknown): ValidationResult;
|
|
15
25
|
/**
|
|
16
26
|
* 校验指定数据是否符合给定的 JSON Schema 契约。
|
|
17
27
|
*
|
package/dist/schema/validator.js
CHANGED
|
@@ -18,75 +18,36 @@ const validatorCache = new WeakMap();
|
|
|
18
18
|
/**
|
|
19
19
|
* 递归检查数据中是否包含危险的原型污染属性键名(__proto__、constructor、prototype)。
|
|
20
20
|
*/
|
|
21
|
+
import { validateActionInputValue } from "../json/value-validator.js";
|
|
22
|
+
/**
|
|
23
|
+
* 递归检查数据中是否包含危险的原型污染属性键名(__proto__、constructor、prototype)。
|
|
24
|
+
* 内部委托 validateActionInputValue 执行迭代遍历,确保环路安全、有向无环图安全且无调用栈溢出风险。
|
|
25
|
+
*/
|
|
21
26
|
export function hasDangerousKeys(data) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
if (Array.isArray(data)) {
|
|
26
|
-
for (const item of data) {
|
|
27
|
-
if (hasDangerousKeys(item))
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
const obj = data;
|
|
33
|
-
const keys = Object.getOwnPropertyNames(obj);
|
|
34
|
-
for (const key of keys) {
|
|
35
|
-
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
if (hasDangerousKeys(obj[key])) {
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
27
|
+
const result = validateActionInputValue(data);
|
|
28
|
+
return !result.valid && result.kind === "input-policy";
|
|
43
29
|
}
|
|
44
30
|
/**
|
|
45
|
-
*
|
|
31
|
+
* 内部纯 Schema 校验器。
|
|
32
|
+
* 仅处理布尔 Schema 与 Ajv 规则校验,严禁执行 dangerous-key 遍历。
|
|
46
33
|
*
|
|
47
|
-
*
|
|
48
|
-
* 1. 严格契约校验:当 Schema 中定义 additionalProperties: false 时,包含未声明属性的输入将被直接拒绝(返回 valid: false),系统不会静默过滤或篡改用户输入。
|
|
49
|
-
* 2. 危险原型键硬拦截:输入数据或其任意嵌套对象中若出现 __proto__、constructor 或 prototype 键名,即使 Schema 允许亦立即拒绝,彻底杜绝原型污染。
|
|
50
|
-
* 3. 布尔 Schema:false 显式拒绝所有输入;true 或未定义时允许所有合法非污染数据。
|
|
51
|
-
* 4. 无原型对象支持:完全支持 Object.create(null) 创建的无原型对象校验。
|
|
52
|
-
*
|
|
53
|
-
* @param schema 期望匹配的 JSON Schema 对象(未提供或为空对象/true 时默认通过;为 false 时拒绝所有数据)
|
|
34
|
+
* @param schema 期望匹配的 JSON Schema 对象
|
|
54
35
|
* @param data 待校验的原始数据
|
|
55
36
|
* @returns 包含 valid 状态与错误信息列表的 ValidationResult
|
|
56
37
|
*/
|
|
57
|
-
export function
|
|
58
|
-
// 1. 布尔 Schema 支持:false 拒绝一切输入;true/undefined 允许一切输入
|
|
38
|
+
export function validateSchemaOnly(schema, data) {
|
|
59
39
|
if (schema === false) {
|
|
60
40
|
return {
|
|
61
41
|
valid: false,
|
|
62
42
|
errors: ["Schema is false, rejecting all data"],
|
|
63
43
|
};
|
|
64
44
|
}
|
|
65
|
-
if (schema === true ||
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
errors: ["Data contains prohibited prototype pollution keys (__proto__, constructor, or prototype)"],
|
|
70
|
-
};
|
|
71
|
-
}
|
|
45
|
+
if (schema === true ||
|
|
46
|
+
!schema ||
|
|
47
|
+
typeof schema !== "object" ||
|
|
48
|
+
Object.keys(schema).length === 0) {
|
|
72
49
|
return { valid: true };
|
|
73
50
|
}
|
|
74
|
-
if (typeof schema !== "object" || Object.keys(schema).length === 0) {
|
|
75
|
-
if (hasDangerousKeys(data)) {
|
|
76
|
-
return {
|
|
77
|
-
valid: false,
|
|
78
|
-
errors: ["Data contains prohibited prototype pollution keys (__proto__, constructor, or prototype)"],
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
return { valid: true };
|
|
82
|
-
}
|
|
83
|
-
// 2. 危险原型键拦截
|
|
84
|
-
if (hasDangerousKeys(data)) {
|
|
85
|
-
return {
|
|
86
|
-
valid: false,
|
|
87
|
-
errors: ["Data contains prohibited prototype pollution keys (__proto__, constructor, or prototype)"],
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
51
|
try {
|
|
91
52
|
let validate = validatorCache.get(schema);
|
|
92
53
|
if (!validate) {
|
|
@@ -120,3 +81,36 @@ export function validateSchema(schema, data) {
|
|
|
120
81
|
};
|
|
121
82
|
}
|
|
122
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* 校验指定数据是否符合给定的 JSON Schema 契约。
|
|
86
|
+
*
|
|
87
|
+
* 行为与策略约定:
|
|
88
|
+
* 1. 严格契约校验:当 Schema 中定义 additionalProperties: false 时,包含未声明属性的输入将被直接拒绝(返回 valid: false),系统不会静默过滤或篡改用户输入。
|
|
89
|
+
* 2. 危险原型键硬拦截:输入数据或其任意嵌套对象中若出现 __proto__、constructor 或 prototype 键名,即使 Schema 允许亦立即拒绝,彻底杜绝原型污染。
|
|
90
|
+
* 3. 布尔 Schema:false 显式拒绝所有输入;true 或未定义时允许所有合法非污染数据。
|
|
91
|
+
* 4. 无原型对象支持:完全支持 Object.create(null) 创建的无原型对象校验。
|
|
92
|
+
*
|
|
93
|
+
* @param schema 期望匹配的 JSON Schema 对象(未提供或为空对象/true 时默认通过;为 false 时拒绝所有数据)
|
|
94
|
+
* @param data 待校验的原始数据
|
|
95
|
+
* @returns 包含 valid 状态与错误信息列表的 ValidationResult
|
|
96
|
+
*/
|
|
97
|
+
export function validateSchema(schema, data) {
|
|
98
|
+
// 1. 布尔 Schema 支持:false 拒绝一切输入
|
|
99
|
+
if (schema === false) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
errors: ["Schema is false, rejecting all data"],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// 2. 危险原型键拦截(保持历史兼容,包含 schema 为 true / undefined / {} 的场景)
|
|
106
|
+
if (hasDangerousKeys(data)) {
|
|
107
|
+
return {
|
|
108
|
+
valid: false,
|
|
109
|
+
errors: [
|
|
110
|
+
"Data contains prohibited prototype pollution keys (__proto__, constructor, or prototype)",
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// 3. 委托纯 Schema 校验器处理其余规则校验
|
|
115
|
+
return validateSchemaOnly(schema, data);
|
|
116
|
+
}
|
package/dist/server/body.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ActionDockError } from "../errors.js";
|
|
1
2
|
/**
|
|
2
3
|
* 默认单次 HTTP 请求体最大字节上限(1 MiB)。
|
|
3
4
|
*/
|
|
@@ -5,15 +6,13 @@ export declare const DEFAULT_MAX_BODY_BYTES: number;
|
|
|
5
6
|
/**
|
|
6
7
|
* 请求体超出最大允许体积时抛出的异常(对应 HTTP 413 Payload Too Large)。
|
|
7
8
|
*/
|
|
8
|
-
export declare class RequestTooLargeError extends
|
|
9
|
-
code: string;
|
|
9
|
+
export declare class RequestTooLargeError extends ActionDockError {
|
|
10
10
|
constructor(message?: string);
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* 请求体非合法 JSON 格式时抛出的异常(对应 HTTP 400 Bad Request)。
|
|
14
14
|
*/
|
|
15
|
-
export declare class InvalidJsonError extends
|
|
16
|
-
code: string;
|
|
15
|
+
export declare class InvalidJsonError extends ActionDockError {
|
|
17
16
|
constructor(message?: string);
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
@@ -24,12 +23,21 @@ export interface ReadJsonBodyOptions {
|
|
|
24
23
|
maxBytes?: number;
|
|
25
24
|
}
|
|
26
25
|
/**
|
|
27
|
-
* 流式安全地读取 HTTP
|
|
26
|
+
* 流式安全地读取 HTTP 请求体为原始字节数组(有界读取单一事实源)。
|
|
28
27
|
*
|
|
29
28
|
* 防护机制:
|
|
30
|
-
* 1. 快速拒绝(Fast-path):若请求头中存在
|
|
31
|
-
* 2. 流式计数器(Chunk Streaming
|
|
32
|
-
*
|
|
29
|
+
* 1. 快速拒绝(Fast-path):若请求头中存在 Content-Length 且超过 maxBytes,立即抛出 RequestTooLargeError,不进行内存分配。
|
|
30
|
+
* 2. 流式计数器(Chunk Streaming):读取 ReadableStream 过程中实时累计字节数,中途超出上限即刻截断并释放流锁,防止大文件 DoS 与内存耗尽(OOM)。
|
|
31
|
+
*
|
|
32
|
+
* @param req 传入的 Request 对象
|
|
33
|
+
* @param options 读取配置选项
|
|
34
|
+
* @returns 原始字节数组(空请求体返回空数组)
|
|
35
|
+
* @throws {RequestTooLargeError} 若请求体体积超限
|
|
36
|
+
*/
|
|
37
|
+
export declare function readBodyWithLimit(req: Request, options?: ReadJsonBodyOptions): Promise<Uint8Array>;
|
|
38
|
+
/**
|
|
39
|
+
* 流式安全地读取 HTTP 请求体并解析为 JSON 对象。
|
|
40
|
+
* 防护机制见 readBodyWithLimit;此外处理空请求体与 UTF-8 字符集解码。
|
|
33
41
|
*
|
|
34
42
|
* @param req 传入的 Request 对象
|
|
35
43
|
* @param options 读取配置选项
|
package/dist/server/body.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ActionDockError } from "../errors.js";
|
|
1
2
|
/**
|
|
2
3
|
* 默认单次 HTTP 请求体最大字节上限(1 MiB)。
|
|
3
4
|
*/
|
|
@@ -5,38 +6,36 @@ export const DEFAULT_MAX_BODY_BYTES = 1024 * 1024; // 1 MiB
|
|
|
5
6
|
/**
|
|
6
7
|
* 请求体超出最大允许体积时抛出的异常(对应 HTTP 413 Payload Too Large)。
|
|
7
8
|
*/
|
|
8
|
-
export class RequestTooLargeError extends
|
|
9
|
-
code = "REQUEST_TOO_LARGE";
|
|
9
|
+
export class RequestTooLargeError extends ActionDockError {
|
|
10
10
|
constructor(message = "Request body exceeds maximum allowed size") {
|
|
11
|
-
super(message);
|
|
11
|
+
super("REQUEST_TOO_LARGE", message);
|
|
12
12
|
this.name = "RequestTooLargeError";
|
|
13
|
+
Object.setPrototypeOf(this, RequestTooLargeError.prototype);
|
|
13
14
|
}
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* 请求体非合法 JSON 格式时抛出的异常(对应 HTTP 400 Bad Request)。
|
|
17
18
|
*/
|
|
18
|
-
export class InvalidJsonError extends
|
|
19
|
-
code = "INVALID_JSON";
|
|
19
|
+
export class InvalidJsonError extends ActionDockError {
|
|
20
20
|
constructor(message = "Failed to parse request body as JSON") {
|
|
21
|
-
super(message);
|
|
21
|
+
super("INVALID_JSON", message);
|
|
22
22
|
this.name = "InvalidJsonError";
|
|
23
|
+
Object.setPrototypeOf(this, InvalidJsonError.prototype);
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
|
-
* 流式安全地读取 HTTP
|
|
27
|
+
* 流式安全地读取 HTTP 请求体为原始字节数组(有界读取单一事实源)。
|
|
27
28
|
*
|
|
28
29
|
* 防护机制:
|
|
29
|
-
* 1. 快速拒绝(Fast-path):若请求头中存在
|
|
30
|
-
* 2. 流式计数器(Chunk Streaming
|
|
31
|
-
* 3. 安全解码:处理空请求体与 UTF-8 字符集解码。
|
|
30
|
+
* 1. 快速拒绝(Fast-path):若请求头中存在 Content-Length 且超过 maxBytes,立即抛出 RequestTooLargeError,不进行内存分配。
|
|
31
|
+
* 2. 流式计数器(Chunk Streaming):读取 ReadableStream 过程中实时累计字节数,中途超出上限即刻截断并释放流锁,防止大文件 DoS 与内存耗尽(OOM)。
|
|
32
32
|
*
|
|
33
33
|
* @param req 传入的 Request 对象
|
|
34
34
|
* @param options 读取配置选项
|
|
35
|
-
* @returns
|
|
35
|
+
* @returns 原始字节数组(空请求体返回空数组)
|
|
36
36
|
* @throws {RequestTooLargeError} 若请求体体积超限
|
|
37
|
-
* @throws {InvalidJsonError} 若请求体非合法 JSON
|
|
38
37
|
*/
|
|
39
|
-
export async function
|
|
38
|
+
export async function readBodyWithLimit(req, options = {}) {
|
|
40
39
|
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BODY_BYTES;
|
|
41
40
|
// 1. 快速检查 Content-Length 头部
|
|
42
41
|
const contentLengthHeader = req.headers.get("content-length");
|
|
@@ -48,7 +47,7 @@ export async function readJsonBody(req, options = {}) {
|
|
|
48
47
|
}
|
|
49
48
|
// 2. 流式读取并实时统计字节数
|
|
50
49
|
if (!req.body) {
|
|
51
|
-
return
|
|
50
|
+
return new Uint8Array(0);
|
|
52
51
|
}
|
|
53
52
|
const reader = req.body.getReader();
|
|
54
53
|
const chunks = [];
|
|
@@ -61,6 +60,7 @@ export async function readJsonBody(req, options = {}) {
|
|
|
61
60
|
if (value) {
|
|
62
61
|
totalBytes += value.byteLength;
|
|
63
62
|
if (totalBytes > maxBytes) {
|
|
63
|
+
await reader.cancel();
|
|
64
64
|
throw new RequestTooLargeError();
|
|
65
65
|
}
|
|
66
66
|
chunks.push(value);
|
|
@@ -71,15 +71,32 @@ export async function readJsonBody(req, options = {}) {
|
|
|
71
71
|
reader.releaseLock();
|
|
72
72
|
}
|
|
73
73
|
if (chunks.length === 0 || totalBytes === 0) {
|
|
74
|
-
return
|
|
74
|
+
return new Uint8Array(0);
|
|
75
75
|
}
|
|
76
|
-
// 3.
|
|
76
|
+
// 3. 拼接字节数组
|
|
77
77
|
const merged = new Uint8Array(totalBytes);
|
|
78
78
|
let offset = 0;
|
|
79
79
|
for (const chunk of chunks) {
|
|
80
80
|
merged.set(chunk, offset);
|
|
81
81
|
offset += chunk.byteLength;
|
|
82
82
|
}
|
|
83
|
+
return merged;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 流式安全地读取 HTTP 请求体并解析为 JSON 对象。
|
|
87
|
+
* 防护机制见 readBodyWithLimit;此外处理空请求体与 UTF-8 字符集解码。
|
|
88
|
+
*
|
|
89
|
+
* @param req 传入的 Request 对象
|
|
90
|
+
* @param options 读取配置选项
|
|
91
|
+
* @returns 解析后的 JSON 对象
|
|
92
|
+
* @throws {RequestTooLargeError} 若请求体体积超限
|
|
93
|
+
* @throws {InvalidJsonError} 若请求体非合法 JSON
|
|
94
|
+
*/
|
|
95
|
+
export async function readJsonBody(req, options = {}) {
|
|
96
|
+
const merged = await readBodyWithLimit(req, options);
|
|
97
|
+
if (merged.byteLength === 0) {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
83
100
|
const text = new TextDecoder("utf-8").decode(merged);
|
|
84
101
|
if (!text.trim()) {
|
|
85
102
|
return {};
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
export type FetchDispatcherProvider = () => unknown;
|
|
5
5
|
/**
|
|
6
6
|
* 注册全局用于忽略服务端证书校验的调度器提供者。
|
|
7
|
-
* 唯一注册通道:平台适配层(如 @actiondock/runtime-node 的
|
|
8
|
-
* installInsecureDispatcher)显式调用完成注册,不再读取全局符号注入。
|
|
9
7
|
*/
|
|
10
8
|
export declare function setInsecureDispatcherProvider(provider?: FetchDispatcherProvider): void;
|
|
11
9
|
/**
|
|
@@ -13,17 +11,15 @@ export declare function setInsecureDispatcherProvider(provider?: FetchDispatcher
|
|
|
13
11
|
*/
|
|
14
12
|
export declare function getInsecureDispatcherProvider(): FetchDispatcherProvider | undefined;
|
|
15
13
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* 1. 优先调用显式注册的提供者(如由 @actiondock/runtime-node 显式安装);
|
|
19
|
-
* 2. 若在 Node 环境下且无注册提供者,动态加载 @actiondock/runtime-node
|
|
20
|
-
* (其入口聚合点会显式完成安装),绝不让 core 包产生静态硬编译依赖;
|
|
21
|
-
* 3. 仍不可得时返回 undefined,由调用方回退默认 fetch,绝不在 core 内
|
|
22
|
-
* 直接探测加载 undici,避免与平台适配层形成层次倒置与隐藏依赖环。
|
|
14
|
+
* 获取用于忽略服务端证书校验的全局单例 Undici Agent 调度器。
|
|
15
|
+
* 显式配置 bodyTimeout: 0 以防止 SSE 长连接与流式事件读取出现 UND_ERR_BODY_TIMEOUT 空闲超时断流。
|
|
23
16
|
*/
|
|
24
17
|
export declare function getInsecureDispatcher(): unknown;
|
|
18
|
+
/**
|
|
19
|
+
* 显式安装平台调度器:向 core 注册本模块的提供者函数。
|
|
20
|
+
*/
|
|
21
|
+
export declare function installInsecureDispatcher(): void;
|
|
25
22
|
/**
|
|
26
23
|
* 显式强制重置并销毁调度器连接池(仅用于单元测试重置或进程退出)。
|
|
27
|
-
* core 自身不再持有任何兜底连接池,关闭语义完全委托已注册的平台提供者宿主。
|
|
28
24
|
*/
|
|
29
25
|
export declare function closeInsecureDispatcher(): Promise<void>;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Agent } from "undici";
|
|
2
2
|
let insecureDispatcherProvider;
|
|
3
|
+
let globalInsecureAgent;
|
|
3
4
|
/**
|
|
4
5
|
* 注册全局用于忽略服务端证书校验的调度器提供者。
|
|
5
|
-
* 唯一注册通道:平台适配层(如 @actiondock/runtime-node 的
|
|
6
|
-
* installInsecureDispatcher)显式调用完成注册,不再读取全局符号注入。
|
|
7
6
|
*/
|
|
8
7
|
export function setInsecureDispatcherProvider(provider) {
|
|
9
8
|
insecureDispatcherProvider = provider;
|
|
@@ -15,48 +14,41 @@ export function getInsecureDispatcherProvider() {
|
|
|
15
14
|
return insecureDispatcherProvider;
|
|
16
15
|
}
|
|
17
16
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* 1. 优先调用显式注册的提供者(如由 @actiondock/runtime-node 显式安装);
|
|
21
|
-
* 2. 若在 Node 环境下且无注册提供者,动态加载 @actiondock/runtime-node
|
|
22
|
-
* (其入口聚合点会显式完成安装),绝不让 core 包产生静态硬编译依赖;
|
|
23
|
-
* 3. 仍不可得时返回 undefined,由调用方回退默认 fetch,绝不在 core 内
|
|
24
|
-
* 直接探测加载 undici,避免与平台适配层形成层次倒置与隐藏依赖环。
|
|
17
|
+
* 获取用于忽略服务端证书校验的全局单例 Undici Agent 调度器。
|
|
18
|
+
* 显式配置 bodyTimeout: 0 以防止 SSE 长连接与流式事件读取出现 UND_ERR_BODY_TIMEOUT 空闲超时断流。
|
|
25
19
|
*/
|
|
26
20
|
export function getInsecureDispatcher() {
|
|
27
21
|
if (insecureDispatcherProvider) {
|
|
28
22
|
return insecureDispatcherProvider();
|
|
29
23
|
}
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// 忽略未安装 @actiondock/runtime-node 的情况
|
|
40
|
-
}
|
|
24
|
+
if (!globalInsecureAgent || globalInsecureAgent.closed || globalInsecureAgent.destroyed) {
|
|
25
|
+
globalInsecureAgent = new Agent({
|
|
26
|
+
connect: {
|
|
27
|
+
rejectUnauthorized: false,
|
|
28
|
+
},
|
|
29
|
+
bodyTimeout: 0,
|
|
30
|
+
});
|
|
41
31
|
}
|
|
42
|
-
return
|
|
32
|
+
return globalInsecureAgent;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 显式安装平台调度器:向 core 注册本模块的提供者函数。
|
|
36
|
+
*/
|
|
37
|
+
export function installInsecureDispatcher() {
|
|
38
|
+
setInsecureDispatcherProvider(getInsecureDispatcher);
|
|
43
39
|
}
|
|
44
40
|
/**
|
|
45
41
|
* 显式强制重置并销毁调度器连接池(仅用于单元测试重置或进程退出)。
|
|
46
|
-
* core 自身不再持有任何兜底连接池,关闭语义完全委托已注册的平台提供者宿主。
|
|
47
42
|
*/
|
|
48
43
|
export async function closeInsecureDispatcher() {
|
|
49
|
-
if (
|
|
50
|
-
|
|
44
|
+
if (globalInsecureAgent) {
|
|
45
|
+
const agent = globalInsecureAgent;
|
|
46
|
+
globalInsecureAgent = undefined;
|
|
51
47
|
try {
|
|
52
|
-
|
|
53
|
-
const runtimeNode = req("@actiondock/runtime-node");
|
|
54
|
-
if (typeof runtimeNode?.closeInsecureDispatcher === "function") {
|
|
55
|
-
await runtimeNode.closeInsecureDispatcher();
|
|
56
|
-
}
|
|
48
|
+
await agent.close();
|
|
57
49
|
}
|
|
58
|
-
catch {
|
|
59
|
-
|
|
50
|
+
catch (err) {
|
|
51
|
+
console.warn("[actiondock] closeInsecureDispatcher failed to close agent:", err);
|
|
60
52
|
}
|
|
61
53
|
}
|
|
62
54
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
|
2
|
+
import { type Server as HttpsServer } from "node:https";
|
|
3
|
+
import type { ServerTlsOptions } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Web 标准 Request 处理函数。
|
|
6
|
+
*/
|
|
7
|
+
export type WebRequestHandler = (request: Request) => Promise<Response> | Response;
|
|
8
|
+
/**
|
|
9
|
+
* Node HTTP 服务端配置选项。
|
|
10
|
+
*/
|
|
11
|
+
export interface NodeHttpServerOptions {
|
|
12
|
+
port?: number;
|
|
13
|
+
host?: string;
|
|
14
|
+
fetch: WebRequestHandler;
|
|
15
|
+
baseOrigin?: string;
|
|
16
|
+
tls?: ServerTlsOptions;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 将 Node.js IncomingMessage 请求转化为标准的 Web Request 对象。
|
|
20
|
+
*/
|
|
21
|
+
export declare function createWebRequest(req: IncomingMessage, options?: {
|
|
22
|
+
baseOrigin?: string;
|
|
23
|
+
res?: ServerResponse;
|
|
24
|
+
} | ServerResponse): Request;
|
|
25
|
+
/**
|
|
26
|
+
* 将标准的 Web Response 响应流式写回 Node.js ServerResponse 客户端输出。
|
|
27
|
+
*/
|
|
28
|
+
export declare function sendWebResponse(webResponse: Response, res: ServerResponse): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* 创建适用于 Node.js 原生 http.createServer 的请求监听函数。
|
|
31
|
+
*/
|
|
32
|
+
export declare function createRequestListener(handler: WebRequestHandler, options?: {
|
|
33
|
+
baseOrigin?: string;
|
|
34
|
+
}): (req: IncomingMessage, res: ServerResponse) => void;
|
|
35
|
+
/**
|
|
36
|
+
* 基于 Node.js node:http 的轻量级 HTTP 服务端封装。
|
|
37
|
+
* 接收 Node HTTP 请求并转化为标准 Web Request,支持 Web Response 流式输出。
|
|
38
|
+
*/
|
|
39
|
+
export declare class NodeHttpServer {
|
|
40
|
+
private server;
|
|
41
|
+
private listening;
|
|
42
|
+
private portNumber;
|
|
43
|
+
private hostAddress;
|
|
44
|
+
private tlsOptions?;
|
|
45
|
+
private persistentErrorHandler?;
|
|
46
|
+
constructor(optionsOrHandler: WebRequestHandler | NodeHttpServerOptions);
|
|
47
|
+
/**
|
|
48
|
+
* 底层原生 Node.js http.Server / https.Server 实例。
|
|
49
|
+
*/
|
|
50
|
+
get rawServer(): Server | HttpsServer;
|
|
51
|
+
/**
|
|
52
|
+
* 绑定的监听端口号。
|
|
53
|
+
*/
|
|
54
|
+
get port(): number;
|
|
55
|
+
/**
|
|
56
|
+
* 绑定的主机地址。
|
|
57
|
+
*/
|
|
58
|
+
get host(): string;
|
|
59
|
+
/**
|
|
60
|
+
* 完整的 HTTP 访问基础路径。
|
|
61
|
+
*/
|
|
62
|
+
get url(): string;
|
|
63
|
+
/**
|
|
64
|
+
* 服务端当前是否正在监听连接。
|
|
65
|
+
*/
|
|
66
|
+
get isListening(): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* 启动监听。若未显式传入端口,则默认使用实例配置或随机可用端口(0)。
|
|
69
|
+
*/
|
|
70
|
+
listen(port?: number, host?: string): Promise<{
|
|
71
|
+
port: number;
|
|
72
|
+
host: string;
|
|
73
|
+
url: string;
|
|
74
|
+
}>;
|
|
75
|
+
/**
|
|
76
|
+
* 优雅关闭服务端并释放端口与活动连接。
|
|
77
|
+
*/
|
|
78
|
+
close(): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* 停止服务端监听(兼容 stop 契约)。
|
|
81
|
+
*/
|
|
82
|
+
stop(): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* 便捷工厂方法:创建并启动监听。
|
|
85
|
+
*/
|
|
86
|
+
static start(optionsOrHandler: WebRequestHandler | NodeHttpServerOptions, port?: number, host?: string): Promise<NodeHttpServer>;
|
|
87
|
+
}
|