@actiondock/core 2.5.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/app.d.ts +14 -1
- package/dist/app/app.js +88 -222
- package/dist/app/static-index.d.ts +39 -0
- package/dist/app/static-index.js +164 -0
- package/dist/app/types.d.ts +5 -0
- package/dist/catalog/action-index.js +6 -4
- package/dist/catalog/package-catalog.js +2 -1
- package/dist/doctor/checks/index.d.ts +5 -0
- package/dist/doctor/checks/index.js +5 -0
- package/dist/doctor/checks/project-locate.d.ts +6 -0
- package/dist/doctor/checks/project-locate.js +36 -0
- package/dist/doctor/checks/project.d.ts +6 -0
- package/dist/doctor/checks/project.js +383 -0
- package/dist/doctor/checks/registry.d.ts +13 -0
- package/dist/doctor/checks/registry.js +207 -0
- package/dist/doctor/checks/runtime.d.ts +13 -0
- package/dist/doctor/checks/runtime.js +90 -0
- package/dist/doctor/checks/storage.d.ts +5 -0
- package/dist/doctor/checks/storage.js +34 -0
- package/dist/doctor/context.d.ts +35 -0
- package/dist/doctor/context.js +11 -0
- package/dist/doctor/doctor.d.ts +5 -0
- package/dist/doctor/doctor.js +33 -602
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +22 -0
- package/dist/execution/service.js +14 -9
- package/dist/export/templates.js +42 -30
- package/dist/host/host.d.ts +33 -0
- package/dist/host/host.js +222 -362
- package/dist/host/routing.d.ts +92 -0
- package/dist/host/routing.js +238 -0
- package/dist/host/types.d.ts +8 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/input/advice.d.ts +100 -0
- package/dist/input/advice.js +767 -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 +72 -0
- package/dist/input/flat-errors.js +87 -0
- package/dist/input/flat-materializer.d.ts +29 -0
- package/dist/input/flat-materializer.js +179 -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/ipc/host.js +38 -0
- 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/platform/types.d.ts +7 -0
- package/dist/process/control-arbiter.d.ts +107 -0
- package/dist/process/control-arbiter.js +251 -0
- package/dist/process/diagnostics-sink.d.ts +26 -0
- package/dist/process/diagnostics-sink.js +35 -0
- package/dist/process/index.d.ts +7 -0
- package/dist/process/index.js +7 -0
- package/dist/process/input-dispatcher.d.ts +88 -0
- package/dist/process/input-dispatcher.js +240 -0
- package/dist/process/managed-record.d.ts +78 -0
- package/dist/process/managed-record.js +1 -0
- package/dist/process/process-manager.d.ts +15 -56
- package/dist/process/process-manager.js +119 -707
- package/dist/process/reservation-table.d.ts +39 -0
- package/dist/process/reservation-table.js +80 -0
- package/dist/process/run-executor.d.ts +16 -0
- package/dist/process/run-executor.js +155 -0
- package/dist/process/terminal-output-cache.d.ts +66 -0
- package/dist/process/terminal-output-cache.js +132 -0
- package/dist/profile/client-actions.d.ts +56 -0
- package/dist/profile/client-actions.js +215 -0
- package/dist/profile/client-config.d.ts +20 -0
- package/dist/profile/client-config.js +51 -0
- package/dist/profile/client-health.d.ts +14 -0
- package/dist/profile/client-health.js +69 -0
- package/dist/profile/client-playbooks.d.ts +19 -0
- package/dist/profile/client-playbooks.js +36 -0
- package/dist/profile/client-query.d.ts +30 -0
- package/dist/profile/client-query.js +70 -0
- package/dist/profile/client-runs.d.ts +42 -0
- package/dist/profile/client-runs.js +58 -0
- package/dist/profile/client-state.d.ts +56 -0
- package/dist/profile/client-state.js +83 -0
- package/dist/profile/client-transport.d.ts +61 -0
- package/dist/profile/client-transport.js +156 -0
- package/dist/profile/client.d.ts +23 -142
- package/dist/profile/client.js +28 -588
- package/dist/profile/manager.js +1 -1
- package/dist/project/closure.d.ts +4 -4
- package/dist/project/closure.js +11 -5
- package/dist/project/init.js +2 -2
- package/dist/project/loader.d.ts +0 -25
- package/dist/project/loader.js +1 -154
- package/dist/project/transactions.d.ts +12 -14
- package/dist/project/transactions.js +60 -836
- package/dist/registry/registry.js +6 -4
- package/dist/registry/resolve.js +1 -1
- package/dist/runtime/action-registry.d.ts +45 -0
- package/dist/runtime/action-registry.js +108 -0
- package/dist/runtime/clock.d.ts +5 -17
- package/dist/runtime/clock.js +5 -15
- package/dist/runtime/context.d.ts +3 -0
- package/dist/runtime/context.js +17 -56
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/package-runner-factory.d.ts +82 -0
- package/dist/runtime/package-runner-factory.js +186 -0
- package/dist/runtime/run-persistence.d.ts +85 -0
- package/dist/runtime/run-persistence.js +111 -0
- package/dist/runtime/runner.d.ts +25 -45
- package/dist/runtime/runner.js +130 -354
- package/dist/runtime/standalone.d.ts +25 -2
- package/dist/runtime/standalone.js +305 -87
- 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/dispatcher.d.ts +8 -3
- package/dist/server/dispatcher.js +16 -40
- package/dist/server/routes/runs.js +6 -4
- package/dist/server/security.d.ts +1 -7
- package/dist/server/security.js +2 -13
- package/dist/storage/clock.d.ts +25 -0
- package/dist/storage/clock.js +23 -0
- package/dist/storage/data-dir-lock.d.ts +3 -36
- package/dist/storage/data-dir-lock.js +48 -881
- package/dist/storage/index.d.ts +8 -1
- package/dist/storage/index.js +8 -2
- package/dist/storage/lazy.js +1 -0
- package/dist/storage/lock-core.d.ts +162 -0
- package/dist/storage/lock-core.js +885 -0
- package/dist/storage/sqlite.d.ts +19 -6
- package/dist/storage/sqlite.js +81 -89
- package/dist/storage/types.d.ts +27 -5
- package/dist/storage/types.js +21 -0
- package/dist/storage/utils.d.ts +22 -0
- package/dist/storage/utils.js +34 -0
- package/dist/target/local.d.ts +1 -0
- package/dist/target/local.js +23 -17
- package/dist/target/remote-errors.d.ts +21 -0
- package/dist/target/remote-errors.js +51 -0
- package/dist/target/remote-polling.d.ts +35 -0
- package/dist/target/remote-polling.js +104 -0
- package/dist/target/remote.d.ts +4 -0
- package/dist/target/remote.js +108 -301
- package/dist/target/sse-parser.d.ts +35 -0
- package/dist/target/sse-parser.js +163 -0
- package/dist/target/target.js +2 -0
- package/dist/target/types.d.ts +6 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/net.d.ts +10 -0
- package/dist/utils/net.js +16 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { inputConflict, inputLimitExceeded, invalidJson, InputError, FlatInputError, } from "./flat-errors.js";
|
|
2
|
+
import { decodeFlatInput } from "./flat-decode.js";
|
|
3
|
+
import { validateJsonValue, DEFAULT_MAX_JSON_DEPTH, } from "../json/value-validator.js";
|
|
4
|
+
import { scanJsonDepth } from "./json-depth-scanner.js";
|
|
5
|
+
import { decodeUtf8Strict, stripBom } from "./utf8.js";
|
|
6
|
+
import { readRegularFileBounded, DEFAULT_MAX_INPUT_BYTES, } from "./file-input.js";
|
|
7
|
+
import { readStdinBounded } from "./stdin-input.js";
|
|
8
|
+
export { stripBom } from "./utf8.js";
|
|
9
|
+
/**
|
|
10
|
+
* 从标准输入流中完整读取全部数据并转换为 UTF-8 字符串(保持历史接口兼容)。
|
|
11
|
+
*
|
|
12
|
+
* @param stream 标准输入可读流,默认为 process.stdin
|
|
13
|
+
*/
|
|
14
|
+
export async function readStdin(stream = process.stdin) {
|
|
15
|
+
return readStdinBounded(stream, { strictUtf8: false });
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 针对面向 Agent 场景对结构化输入错误进行隐私脱敏。
|
|
19
|
+
*
|
|
20
|
+
* 脱敏准则:
|
|
21
|
+
* - details.source 严格收敛为稳定枚举:"flat" | "inline-json" | "file" | "stdin"。
|
|
22
|
+
* - 严禁在错误信息和 details 中回显原始 token、原始 JSON 内容、文件绝对路径及异常堆栈。
|
|
23
|
+
* - INPUT_FILE_NOT_FOUND 输出固定为:code: "INPUT_FILE_NOT_FOUND", message: "Input file not found", details: { source: "file" }。
|
|
24
|
+
*/
|
|
25
|
+
function sanitizeError(err, source) {
|
|
26
|
+
if (err instanceof InputError) {
|
|
27
|
+
const code = err.code;
|
|
28
|
+
const rawDetails = err.details && typeof err.details === "object" && !Array.isArray(err.details)
|
|
29
|
+
? err.details
|
|
30
|
+
: {};
|
|
31
|
+
const reason = rawDetails.reason ||
|
|
32
|
+
(code === "INPUT_FILE_NOT_FOUND" ? undefined : "SYNTAX_ERROR");
|
|
33
|
+
if (code === "INPUT_FILE_NOT_FOUND") {
|
|
34
|
+
return new InputError("INPUT_FILE_NOT_FOUND", "Input file not found", {
|
|
35
|
+
source: "file",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
if (code === "INPUT_FILE_READ_FAILED") {
|
|
39
|
+
const msg = source === "stdin"
|
|
40
|
+
? "Failed to read input from stdin"
|
|
41
|
+
: "Failed to read input file";
|
|
42
|
+
return new InputError("INPUT_FILE_READ_FAILED", msg, {
|
|
43
|
+
source,
|
|
44
|
+
...(rawDetails.reason ? { reason: rawDetails.reason } : {}),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (code === "INPUT_CONFLICT") {
|
|
48
|
+
return new InputError("INPUT_CONFLICT", "Input options conflict: multiple input modes specified", { reason: "MULTIPLE_INPUT_MODES" });
|
|
49
|
+
}
|
|
50
|
+
if (code === "INPUT_LIMIT_EXCEEDED") {
|
|
51
|
+
return new InputError("INPUT_LIMIT_EXCEEDED", "Input limit exceeded", {
|
|
52
|
+
source,
|
|
53
|
+
reason: rawDetails.reason || "MAX_INPUT_BYTES",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (code === "FLAT_INPUT_LIMIT_EXCEEDED") {
|
|
57
|
+
return new FlatInputError("FLAT_INPUT_LIMIT_EXCEEDED", "Flat input limit exceeded", {
|
|
58
|
+
source: "flat",
|
|
59
|
+
reason: rawDetails.reason || "MAX_MATERIALIZED_BYTES",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (code === "INVALID_FLAT_ARGUMENT") {
|
|
63
|
+
return new FlatInputError("INVALID_FLAT_ARGUMENT", "Invalid flat argument", {
|
|
64
|
+
source: "flat",
|
|
65
|
+
reason: rawDetails.reason || "INVALID_ARGUMENT",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (code === "INPUT_PATH_CONFLICT") {
|
|
69
|
+
return new FlatInputError("INPUT_PATH_CONFLICT", "Input path conflict", {
|
|
70
|
+
source: "flat",
|
|
71
|
+
reason: rawDetails.reason || "PATH_CONFLICT",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (code === "INVALID_JSON_LITERAL") {
|
|
75
|
+
return new FlatInputError("INVALID_JSON_LITERAL", "Invalid JSON literal", {
|
|
76
|
+
source: "flat",
|
|
77
|
+
reason: rawDetails.reason || "SYNTAX_ERROR",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (code === "INVALID_JSON") {
|
|
81
|
+
let msg = "Invalid JSON syntax";
|
|
82
|
+
if (reason === "INVALID_UTF8") {
|
|
83
|
+
msg = "Invalid UTF-8 encoding in JSON input";
|
|
84
|
+
}
|
|
85
|
+
else if (reason === "MAX_JSON_DEPTH") {
|
|
86
|
+
msg = "Max JSON depth limit exceeded";
|
|
87
|
+
}
|
|
88
|
+
else if (reason === "NON_FINITE_NUMBER") {
|
|
89
|
+
msg = "Number is non-finite or NaN";
|
|
90
|
+
}
|
|
91
|
+
else if (reason === "INVALID_JSON_VALUE") {
|
|
92
|
+
msg = "Invalid JSON value";
|
|
93
|
+
}
|
|
94
|
+
return new InputError("INVALID_JSON", msg, {
|
|
95
|
+
source,
|
|
96
|
+
reason,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return new InputError(code, "Input validation failed", {
|
|
100
|
+
source,
|
|
101
|
+
...(rawDetails.reason ? { reason: rawDetails.reason } : {}),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return err;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 解析完整 JSON 文档字符串。
|
|
108
|
+
* 若解析失败抛出 INVALID_JSON 异常。
|
|
109
|
+
*
|
|
110
|
+
* @param text 待解析文本
|
|
111
|
+
* @param source 输入源描述(如 --input、文件路径或 stdin)
|
|
112
|
+
* @param policy 解析策略选项
|
|
113
|
+
*/
|
|
114
|
+
export function parseJson(text, source, policy) {
|
|
115
|
+
const maxDepth = policy?.maxJsonDepth ?? DEFAULT_MAX_JSON_DEPTH;
|
|
116
|
+
const maxBytes = policy?.maxInputBytes ?? DEFAULT_MAX_INPUT_BYTES;
|
|
117
|
+
// 1. 检查原始字节长度
|
|
118
|
+
const byteLen = Buffer.byteLength(text, "utf8");
|
|
119
|
+
if (byteLen > maxBytes) {
|
|
120
|
+
throw inputLimitExceeded(`Input size (${byteLen} bytes) exceeds limit of ${maxBytes} bytes`, { reason: "MAX_INPUT_BYTES" });
|
|
121
|
+
}
|
|
122
|
+
// 2. 剥离单个开头的 BOM
|
|
123
|
+
const cleaned = stripBom(text);
|
|
124
|
+
// 3. 空白 / 纯 BOM / 空字符串显式判定为语法错误
|
|
125
|
+
if (cleaned.trim() === "") {
|
|
126
|
+
throw invalidJson(`Invalid JSON input from ${source}: Unexpected end of JSON input`, { source, reason: "SYNTAX_ERROR" });
|
|
127
|
+
}
|
|
128
|
+
// 4. JSON 结构深度预检
|
|
129
|
+
const depthRes = scanJsonDepth(cleaned, maxDepth);
|
|
130
|
+
if (!depthRes.valid) {
|
|
131
|
+
throw invalidJson(`Invalid JSON input from ${source}: Max JSON depth limit (${maxDepth}) exceeded`, { source, reason: "MAX_JSON_DEPTH" });
|
|
132
|
+
}
|
|
133
|
+
// 5. 完整 JSON 解析
|
|
134
|
+
let parsed;
|
|
135
|
+
try {
|
|
136
|
+
parsed = JSON.parse(cleaned);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
throw invalidJson(`Invalid JSON input from ${source}: ${err instanceof Error ? err.message : String(err)}`, { source, reason: "SYNTAX_ERROR" });
|
|
140
|
+
}
|
|
141
|
+
// 6. JSON 值结构有限性与合法性校验
|
|
142
|
+
const valRes = validateJsonValue(parsed, { maxDepth });
|
|
143
|
+
if (!valRes.valid) {
|
|
144
|
+
throw invalidJson(`Invalid JSON input from ${source}: ${valRes.reason}`, { source, reason: valRes.code, path: valRes.path });
|
|
145
|
+
}
|
|
146
|
+
return parsed;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* 统一解析 Action 执行入参。
|
|
150
|
+
*
|
|
151
|
+
* 输入模式仲裁规则(Input Mode Arbitration):
|
|
152
|
+
* - effectiveFlat: flatArgs 存在且 length > 0
|
|
153
|
+
* - hasInlineJson: options.input !== undefined(即使为 "")
|
|
154
|
+
* - hasFileJson: options.inputFile !== undefined(包含 "-")
|
|
155
|
+
* - 最多一个为 true;若超过一个,抛出 INPUT_CONFLICT(reason: "MULTIPLE_INPUT_MODES")。
|
|
156
|
+
* - 若三个均为 false,返回默认 {}。
|
|
157
|
+
* - flatArgs = [] 视为未指定。
|
|
158
|
+
* - options.input === "" 为显式 Full JSON 模式,进入解析并得到 INVALID_JSON / SYNTAX_ERROR,不可退化为 {}。
|
|
159
|
+
* - 显式空 JSON 输入(--input ""、0 字节文件、空 stdin、纯空白、纯 BOM)统一返回 INVALID_JSON / SYNTAX_ERROR。
|
|
160
|
+
*
|
|
161
|
+
* @param options 输入解析选项
|
|
162
|
+
* @returns 解析后的 JSON 对象
|
|
163
|
+
*/
|
|
164
|
+
export async function resolveActionInput(options) {
|
|
165
|
+
const effectiveFlat = (options.flatArgs?.length ?? 0) > 0;
|
|
166
|
+
const hasInlineJson = options.input !== undefined;
|
|
167
|
+
const hasFileJson = options.inputFile !== undefined;
|
|
168
|
+
let modeCount = 0;
|
|
169
|
+
if (effectiveFlat)
|
|
170
|
+
modeCount++;
|
|
171
|
+
if (hasInlineJson)
|
|
172
|
+
modeCount++;
|
|
173
|
+
if (hasFileJson)
|
|
174
|
+
modeCount++;
|
|
175
|
+
if (modeCount > 1) {
|
|
176
|
+
const err = inputConflict("Input options conflict: flatArgs, input, and inputFile are mutually exclusive", {
|
|
177
|
+
reason: "MULTIPLE_INPUT_MODES",
|
|
178
|
+
hasFlatArgs: effectiveFlat,
|
|
179
|
+
hasInput: hasInlineJson,
|
|
180
|
+
hasInputFile: hasFileJson,
|
|
181
|
+
});
|
|
182
|
+
if (options.policy?.sanitizeInputErrors) {
|
|
183
|
+
throw sanitizeError(err, "flat");
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
if (modeCount === 0) {
|
|
188
|
+
return {};
|
|
189
|
+
}
|
|
190
|
+
const policy = options.policy;
|
|
191
|
+
const maxBytes = policy?.maxInputBytes ?? DEFAULT_MAX_INPUT_BYTES;
|
|
192
|
+
// 1. 扁平入参模式
|
|
193
|
+
if (effectiveFlat) {
|
|
194
|
+
try {
|
|
195
|
+
return decodeFlatInput(options.flatArgs, options.flatOptions);
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
if (policy?.sanitizeInputErrors) {
|
|
199
|
+
throw sanitizeError(err, "flat");
|
|
200
|
+
}
|
|
201
|
+
throw err;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// 2. 内联 JSON 模式
|
|
205
|
+
if (hasInlineJson) {
|
|
206
|
+
try {
|
|
207
|
+
return parseJson(options.input, "--input", policy);
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
if (policy?.sanitizeInputErrors) {
|
|
211
|
+
throw sanitizeError(err, "inline-json");
|
|
212
|
+
}
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// 3. 文件 / 标准输入 JSON 模式
|
|
217
|
+
if (hasFileJson) {
|
|
218
|
+
const isStdin = options.inputFile === "-";
|
|
219
|
+
if (isStdin) {
|
|
220
|
+
try {
|
|
221
|
+
const text = await readStdinBounded(options.stdin || process.stdin, {
|
|
222
|
+
maxInputBytes: maxBytes,
|
|
223
|
+
signal: options.signal,
|
|
224
|
+
byteStreamOnly: policy?.byteStreamOnly,
|
|
225
|
+
strictUtf8: policy?.strictUtf8 ?? true,
|
|
226
|
+
});
|
|
227
|
+
return parseJson(text, "stdin", policy);
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
if (policy?.sanitizeInputErrors) {
|
|
231
|
+
throw sanitizeError(err, "stdin");
|
|
232
|
+
}
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
const fileBytes = await readRegularFileBounded(options.inputFile, {
|
|
238
|
+
maxInputBytes: maxBytes,
|
|
239
|
+
signal: options.signal,
|
|
240
|
+
});
|
|
241
|
+
const text = policy?.strictUtf8 ?? true
|
|
242
|
+
? decodeUtf8Strict(fileBytes, "full-json-file")
|
|
243
|
+
: fileBytes.toString("utf8");
|
|
244
|
+
return parseJson(text, options.inputFile, policy);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
if (policy?.sanitizeInputErrors) {
|
|
248
|
+
throw sanitizeError(err, "file");
|
|
249
|
+
}
|
|
250
|
+
throw err;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return {};
|
|
254
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON 结构深度预检器。
|
|
3
|
+
*
|
|
4
|
+
* 采用 O(n) 流式单趟字符扫描器,实时维护字符串状态(处理转义符 \" 与 \\)以及字符串外部的容器嵌套深度({ } 与 [ ])。
|
|
5
|
+
* 根容器深度记为 0,每深入一层嵌套加 1。当任意时刻深度超出 maxDepth 时,立即短路返回 MAX_JSON_DEPTH。
|
|
6
|
+
* 标量基础类型(字符串、数字、布尔、null)不增加容器深度。
|
|
7
|
+
*
|
|
8
|
+
* @param text 待扫描的原始 JSON 文本
|
|
9
|
+
* @param maxDepth 最大允许嵌套深度(默认为 256)
|
|
10
|
+
* @returns 校验通过返回 { valid: true },超限返回 { valid: false, reason: "MAX_JSON_DEPTH" }
|
|
11
|
+
*/
|
|
12
|
+
export declare function scanJsonDepth(text: string, maxDepth?: number): {
|
|
13
|
+
valid: true;
|
|
14
|
+
} | {
|
|
15
|
+
valid: false;
|
|
16
|
+
reason: string;
|
|
17
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON 结构深度预检器。
|
|
3
|
+
*
|
|
4
|
+
* 采用 O(n) 流式单趟字符扫描器,实时维护字符串状态(处理转义符 \" 与 \\)以及字符串外部的容器嵌套深度({ } 与 [ ])。
|
|
5
|
+
* 根容器深度记为 0,每深入一层嵌套加 1。当任意时刻深度超出 maxDepth 时,立即短路返回 MAX_JSON_DEPTH。
|
|
6
|
+
* 标量基础类型(字符串、数字、布尔、null)不增加容器深度。
|
|
7
|
+
*
|
|
8
|
+
* @param text 待扫描的原始 JSON 文本
|
|
9
|
+
* @param maxDepth 最大允许嵌套深度(默认为 256)
|
|
10
|
+
* @returns 校验通过返回 { valid: true },超限返回 { valid: false, reason: "MAX_JSON_DEPTH" }
|
|
11
|
+
*/
|
|
12
|
+
export function scanJsonDepth(text, maxDepth = 256) {
|
|
13
|
+
let inString = false;
|
|
14
|
+
let isEscaped = false;
|
|
15
|
+
let containerDepth = -1;
|
|
16
|
+
for (let i = 0; i < text.length; i++) {
|
|
17
|
+
const ch = text.charCodeAt(i);
|
|
18
|
+
if (inString) {
|
|
19
|
+
if (isEscaped) {
|
|
20
|
+
isEscaped = false;
|
|
21
|
+
}
|
|
22
|
+
else if (ch === 0x5c /* \ */) {
|
|
23
|
+
isEscaped = true;
|
|
24
|
+
}
|
|
25
|
+
else if (ch === 0x22 /* " */) {
|
|
26
|
+
inString = false;
|
|
27
|
+
}
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (ch === 0x22 /* " */) {
|
|
31
|
+
inString = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (ch === 0x7b /* { */ || ch === 0x5b /* [ */) {
|
|
35
|
+
containerDepth++;
|
|
36
|
+
if (containerDepth > maxDepth) {
|
|
37
|
+
return { valid: false, reason: "MAX_JSON_DEPTH" };
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (ch === 0x7d /* } */ || ch === 0x5d /* ] */) {
|
|
42
|
+
if (containerDepth >= 0) {
|
|
43
|
+
containerDepth--;
|
|
44
|
+
}
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { valid: true };
|
|
49
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type CliInputAdviceV1 } from "./advice.js";
|
|
2
|
+
/**
|
|
3
|
+
* CLI 输入传输机制元数据契约(v1)。
|
|
4
|
+
*/
|
|
5
|
+
export interface InputTransportV1 {
|
|
6
|
+
version: 1;
|
|
7
|
+
defaultInputMode: "empty-object";
|
|
8
|
+
fullJson: {
|
|
9
|
+
inlineOption: "--input";
|
|
10
|
+
fileOption: "--input-file";
|
|
11
|
+
stdinValue: "-";
|
|
12
|
+
preferredLargeInputMode: "stdin";
|
|
13
|
+
encoding: "utf-8";
|
|
14
|
+
maxInputBytes: number;
|
|
15
|
+
maxJsonDepth: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* CLI 扁平编码规格元数据契约(v1)。
|
|
20
|
+
*/
|
|
21
|
+
export interface CliInputEncodingV1 {
|
|
22
|
+
name: "flat-json-value";
|
|
23
|
+
version: 1;
|
|
24
|
+
scope: "cli-argv";
|
|
25
|
+
root: "object";
|
|
26
|
+
operators: {
|
|
27
|
+
string: "=";
|
|
28
|
+
json: ":=";
|
|
29
|
+
};
|
|
30
|
+
limits: {
|
|
31
|
+
maxAssignments: number;
|
|
32
|
+
maxPathDepth: number;
|
|
33
|
+
maxPathBytes: number;
|
|
34
|
+
maxPropertyKeyBytes: number;
|
|
35
|
+
maxRawValueBytes: number;
|
|
36
|
+
maxJsonLiteralBytes: number;
|
|
37
|
+
maxTotalRawBytes: number;
|
|
38
|
+
maxArrayIndex: number;
|
|
39
|
+
maxMaterializedBytes: number;
|
|
40
|
+
maxJsonDepth: number;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* CLI 输入安全策略元数据契约(v1)。
|
|
45
|
+
*/
|
|
46
|
+
export interface InputPolicyV1 {
|
|
47
|
+
version: 1;
|
|
48
|
+
scope: "cli-pre-target";
|
|
49
|
+
propertyScope: "recursive";
|
|
50
|
+
forbiddenPropertyNames: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* CLI describe --json 输入元数据聚合契约(v1)。
|
|
54
|
+
*/
|
|
55
|
+
export interface CliDescribeInputMetadataV1 {
|
|
56
|
+
inputTransport: InputTransportV1;
|
|
57
|
+
inputEncoding: CliInputEncodingV1;
|
|
58
|
+
inputPolicy: InputPolicyV1;
|
|
59
|
+
inputAdvice: CliInputAdviceV1;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* CLI 输入错误信封结构契约。
|
|
63
|
+
*/
|
|
64
|
+
export interface CliInputErrorEnvelope {
|
|
65
|
+
ok: false;
|
|
66
|
+
error: {
|
|
67
|
+
code: string;
|
|
68
|
+
message: string;
|
|
69
|
+
details?: unknown;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 构造输入传输层元数据(v1)。
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildInputTransportV1(): InputTransportV1;
|
|
76
|
+
/**
|
|
77
|
+
* 构造 CLI 扁平输入编码元数据(v1)。
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildCliInputEncodingV1(): CliInputEncodingV1;
|
|
80
|
+
/**
|
|
81
|
+
* 构造输入策略元数据(v1)。
|
|
82
|
+
* 明确标注 scope 为 "cli-pre-target",防止远端目标未隔离策略造成歧义。
|
|
83
|
+
*/
|
|
84
|
+
export declare function buildInputPolicyV1(): InputPolicyV1;
|
|
85
|
+
/**
|
|
86
|
+
* 聚合构造 CLI describe --json 所需的完整输入元数据。
|
|
87
|
+
*
|
|
88
|
+
* @param schema Action 的 inputSchema 定义
|
|
89
|
+
* @returns 包含 transport、encoding、policy 与 advice 的聚合元数据对象
|
|
90
|
+
*/
|
|
91
|
+
export declare function buildCliDescribeInputMetadataV1(schema: unknown): CliDescribeInputMetadataV1;
|
|
92
|
+
/**
|
|
93
|
+
* 格式化输入异常为 CLI 人类可读文本。
|
|
94
|
+
*/
|
|
95
|
+
export declare function formatInputErrorForCli(err: unknown): string;
|
|
96
|
+
/**
|
|
97
|
+
* 将输入异常转换为统一的 CLI 错误信封结构。
|
|
98
|
+
*/
|
|
99
|
+
export declare function toCliInputErrorEnvelope(err: unknown): CliInputErrorEnvelope;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { DEFAULT_MAX_INPUT_BYTES } from "./file-input.js";
|
|
2
|
+
import { DEFAULT_MAX_JSON_DEPTH } from "../json/value-validator.js";
|
|
3
|
+
import { DEFAULT_MAX_ASSIGNMENTS, DEFAULT_MAX_PATH_DEPTH, DEFAULT_MAX_PATH_LENGTH, DEFAULT_MAX_PROPERTY_KEY_LENGTH, DEFAULT_MAX_RAW_VALUE_LENGTH, DEFAULT_MAX_JSON_LITERAL_LENGTH, DEFAULT_MAX_TOTAL_RAW_BYTES, DEFAULT_MAX_ARRAY_INDEX, DEFAULT_MAX_MATERIALIZED_SIZE_BYTES, } from "./flat-parser.js";
|
|
4
|
+
import { FORBIDDEN_ACTION_INPUT_PROPERTIES } from "./flat-predicates.js";
|
|
5
|
+
import { buildCliInputAdviceV1 } from "./advice.js";
|
|
6
|
+
/**
|
|
7
|
+
* 构造输入传输层元数据(v1)。
|
|
8
|
+
*/
|
|
9
|
+
export function buildInputTransportV1() {
|
|
10
|
+
return {
|
|
11
|
+
version: 1,
|
|
12
|
+
defaultInputMode: "empty-object",
|
|
13
|
+
fullJson: {
|
|
14
|
+
inlineOption: "--input",
|
|
15
|
+
fileOption: "--input-file",
|
|
16
|
+
stdinValue: "-",
|
|
17
|
+
preferredLargeInputMode: "stdin",
|
|
18
|
+
encoding: "utf-8",
|
|
19
|
+
maxInputBytes: DEFAULT_MAX_INPUT_BYTES,
|
|
20
|
+
maxJsonDepth: DEFAULT_MAX_JSON_DEPTH,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 构造 CLI 扁平输入编码元数据(v1)。
|
|
26
|
+
*/
|
|
27
|
+
export function buildCliInputEncodingV1() {
|
|
28
|
+
return {
|
|
29
|
+
name: "flat-json-value",
|
|
30
|
+
version: 1,
|
|
31
|
+
scope: "cli-argv",
|
|
32
|
+
root: "object",
|
|
33
|
+
operators: {
|
|
34
|
+
string: "=",
|
|
35
|
+
json: ":=",
|
|
36
|
+
},
|
|
37
|
+
limits: {
|
|
38
|
+
maxAssignments: DEFAULT_MAX_ASSIGNMENTS,
|
|
39
|
+
maxPathDepth: DEFAULT_MAX_PATH_DEPTH,
|
|
40
|
+
maxPathBytes: DEFAULT_MAX_PATH_LENGTH,
|
|
41
|
+
maxPropertyKeyBytes: DEFAULT_MAX_PROPERTY_KEY_LENGTH,
|
|
42
|
+
maxRawValueBytes: DEFAULT_MAX_RAW_VALUE_LENGTH,
|
|
43
|
+
maxJsonLiteralBytes: DEFAULT_MAX_JSON_LITERAL_LENGTH,
|
|
44
|
+
maxTotalRawBytes: DEFAULT_MAX_TOTAL_RAW_BYTES,
|
|
45
|
+
maxArrayIndex: DEFAULT_MAX_ARRAY_INDEX,
|
|
46
|
+
maxMaterializedBytes: DEFAULT_MAX_MATERIALIZED_SIZE_BYTES,
|
|
47
|
+
maxJsonDepth: DEFAULT_MAX_JSON_DEPTH,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 构造输入策略元数据(v1)。
|
|
53
|
+
* 明确标注 scope 为 "cli-pre-target",防止远端目标未隔离策略造成歧义。
|
|
54
|
+
*/
|
|
55
|
+
export function buildInputPolicyV1() {
|
|
56
|
+
return {
|
|
57
|
+
version: 1,
|
|
58
|
+
scope: "cli-pre-target",
|
|
59
|
+
propertyScope: "recursive",
|
|
60
|
+
forbiddenPropertyNames: FORBIDDEN_ACTION_INPUT_PROPERTIES,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 聚合构造 CLI describe --json 所需的完整输入元数据。
|
|
65
|
+
*
|
|
66
|
+
* @param schema Action 的 inputSchema 定义
|
|
67
|
+
* @returns 包含 transport、encoding、policy 与 advice 的聚合元数据对象
|
|
68
|
+
*/
|
|
69
|
+
export function buildCliDescribeInputMetadataV1(schema) {
|
|
70
|
+
return {
|
|
71
|
+
inputTransport: buildInputTransportV1(),
|
|
72
|
+
inputEncoding: buildCliInputEncodingV1(),
|
|
73
|
+
inputPolicy: buildInputPolicyV1(),
|
|
74
|
+
inputAdvice: buildCliInputAdviceV1(schema),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 格式化输入异常为 CLI 人类可读文本。
|
|
79
|
+
*/
|
|
80
|
+
export function formatInputErrorForCli(err) {
|
|
81
|
+
const message = err?.message || String(err);
|
|
82
|
+
return `Error: ${message}`;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 将输入异常转换为统一的 CLI 错误信封结构。
|
|
86
|
+
*/
|
|
87
|
+
export function toCliInputErrorEnvelope(err) {
|
|
88
|
+
const code = err?.code || "INVALID_ARGUMENT";
|
|
89
|
+
const message = err?.message || String(err);
|
|
90
|
+
const details = err?.details;
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
error: {
|
|
94
|
+
code,
|
|
95
|
+
message,
|
|
96
|
+
...(details !== undefined ? { details } : {}),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 标准输入有界读取选项。
|
|
3
|
+
*/
|
|
4
|
+
export interface ReadStdinBoundedOptions {
|
|
5
|
+
/** 最大允许输入字节数(默认 10MB) */
|
|
6
|
+
maxInputBytes?: number;
|
|
7
|
+
/** 可选的中断信号 */
|
|
8
|
+
signal?: AbortSignal;
|
|
9
|
+
/** 是否仅允许纯字节流(在 CLI 策略下开启,若接收到 string chunk 则立即报错) */
|
|
10
|
+
byteStreamOnly?: boolean;
|
|
11
|
+
/** 是否执行严格 UTF-8 校验与解码(默认 true) */
|
|
12
|
+
strictUtf8?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 以有界大小限制从标准输入流中安全读取全部内容。
|
|
16
|
+
*
|
|
17
|
+
* 核心契约:
|
|
18
|
+
* - 纯字节流策略拦截:若启用 byteStreamOnly 且流发射 string chunk,抛出 INPUT_FILE_READ_FAILED(reason: "INVALID_STREAM_CHUNK_TYPE")。
|
|
19
|
+
* - 终态竞态裁决(First Observed Terminal Event Wins):EOF、limit、abort 谁先观测谁生效,后续事件仅执行清理,不篡改已确定的错误与结果。
|
|
20
|
+
* - 严格 UTF-8 校验:在 strictUtf8 下通过 decodeUtf8Strict 校验并抛出结构化 INVALID_JSON 异常。
|
|
21
|
+
*
|
|
22
|
+
* @param stream 标准输入可读流,默认为 process.stdin
|
|
23
|
+
* @param options 读取配置选项
|
|
24
|
+
* @returns 解码后的 UTF-8 文本
|
|
25
|
+
*/
|
|
26
|
+
export declare function readStdinBounded(stream?: NodeJS.ReadableStream, options?: ReadStdinBoundedOptions): Promise<string>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { inputFileReadFailed, inputLimitExceeded } from "./flat-errors.js";
|
|
2
|
+
import { decodeUtf8Strict } from "./utf8.js";
|
|
3
|
+
import { DEFAULT_MAX_INPUT_BYTES } from "./file-input.js";
|
|
4
|
+
/**
|
|
5
|
+
* 以有界大小限制从标准输入流中安全读取全部内容。
|
|
6
|
+
*
|
|
7
|
+
* 核心契约:
|
|
8
|
+
* - 纯字节流策略拦截:若启用 byteStreamOnly 且流发射 string chunk,抛出 INPUT_FILE_READ_FAILED(reason: "INVALID_STREAM_CHUNK_TYPE")。
|
|
9
|
+
* - 终态竞态裁决(First Observed Terminal Event Wins):EOF、limit、abort 谁先观测谁生效,后续事件仅执行清理,不篡改已确定的错误与结果。
|
|
10
|
+
* - 严格 UTF-8 校验:在 strictUtf8 下通过 decodeUtf8Strict 校验并抛出结构化 INVALID_JSON 异常。
|
|
11
|
+
*
|
|
12
|
+
* @param stream 标准输入可读流,默认为 process.stdin
|
|
13
|
+
* @param options 读取配置选项
|
|
14
|
+
* @returns 解码后的 UTF-8 文本
|
|
15
|
+
*/
|
|
16
|
+
export async function readStdinBounded(stream = process.stdin, options) {
|
|
17
|
+
const maxBytes = options?.maxInputBytes ?? DEFAULT_MAX_INPUT_BYTES;
|
|
18
|
+
const signal = options?.signal;
|
|
19
|
+
const byteStreamOnly = options?.byteStreamOnly ?? false;
|
|
20
|
+
const strictUtf8 = options?.strictUtf8 ?? true;
|
|
21
|
+
// 启动前检查 abort
|
|
22
|
+
if (signal?.aborted) {
|
|
23
|
+
throw signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
24
|
+
}
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
let terminalEvent = null;
|
|
27
|
+
let totalBytes = 0;
|
|
28
|
+
const chunks = [];
|
|
29
|
+
const cleanup = () => {
|
|
30
|
+
stream.removeListener("data", onData);
|
|
31
|
+
stream.removeListener("end", onEnd);
|
|
32
|
+
stream.removeListener("error", onError);
|
|
33
|
+
if (signal) {
|
|
34
|
+
signal.removeEventListener("abort", onAbort);
|
|
35
|
+
}
|
|
36
|
+
if (typeof stream.pause === "function") {
|
|
37
|
+
stream.pause();
|
|
38
|
+
}
|
|
39
|
+
if (typeof stream.destroy === "function") {
|
|
40
|
+
stream.destroy();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const onAbort = () => {
|
|
44
|
+
if (terminalEvent !== null)
|
|
45
|
+
return;
|
|
46
|
+
terminalEvent = "abort";
|
|
47
|
+
cleanup();
|
|
48
|
+
reject(signal?.reason ?? new DOMException("The operation was aborted", "AbortError"));
|
|
49
|
+
};
|
|
50
|
+
const onError = (err) => {
|
|
51
|
+
if (terminalEvent !== null)
|
|
52
|
+
return;
|
|
53
|
+
terminalEvent = "error";
|
|
54
|
+
cleanup();
|
|
55
|
+
reject(inputFileReadFailed("stdin", err));
|
|
56
|
+
};
|
|
57
|
+
const onData = (chunk) => {
|
|
58
|
+
if (terminalEvent !== null)
|
|
59
|
+
return;
|
|
60
|
+
if (typeof chunk === "string") {
|
|
61
|
+
if (byteStreamOnly) {
|
|
62
|
+
terminalEvent = "error";
|
|
63
|
+
cleanup();
|
|
64
|
+
reject(inputFileReadFailed("stdin", new Error("Invalid stream chunk type: expected Buffer, got string"), { reason: "INVALID_STREAM_CHUNK_TYPE" }));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const buf = Buffer.from(chunk, "utf8");
|
|
68
|
+
totalBytes += buf.length;
|
|
69
|
+
if (totalBytes > maxBytes) {
|
|
70
|
+
terminalEvent = "limit";
|
|
71
|
+
cleanup();
|
|
72
|
+
reject(inputLimitExceeded(`Stdin input exceeds maximum limit of ${maxBytes} bytes`, { reason: "MAX_INPUT_BYTES" }));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
chunks.push(buf);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!Buffer.isBuffer(chunk) && !(chunk instanceof Uint8Array)) {
|
|
79
|
+
terminalEvent = "error";
|
|
80
|
+
cleanup();
|
|
81
|
+
reject(inputFileReadFailed("stdin", new Error(`Invalid stream chunk type: expected Buffer, got ${typeof chunk}`), { reason: "INVALID_STREAM_CHUNK_TYPE" }));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const buf = Buffer.isBuffer(chunk)
|
|
85
|
+
? chunk
|
|
86
|
+
: Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
87
|
+
totalBytes += buf.length;
|
|
88
|
+
if (totalBytes > maxBytes) {
|
|
89
|
+
terminalEvent = "limit";
|
|
90
|
+
cleanup();
|
|
91
|
+
reject(inputLimitExceeded(`Stdin input exceeds maximum limit of ${maxBytes} bytes`, { reason: "MAX_INPUT_BYTES" }));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
chunks.push(buf);
|
|
95
|
+
};
|
|
96
|
+
const onEnd = () => {
|
|
97
|
+
if (terminalEvent !== null)
|
|
98
|
+
return;
|
|
99
|
+
terminalEvent = "eof";
|
|
100
|
+
cleanup();
|
|
101
|
+
try {
|
|
102
|
+
const fullBuffer = Buffer.concat(chunks);
|
|
103
|
+
if (strictUtf8) {
|
|
104
|
+
const text = decodeUtf8Strict(fullBuffer, "full-json-stdin");
|
|
105
|
+
resolve(text);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
resolve(fullBuffer.toString("utf8"));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
reject(err);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
if (signal) {
|
|
116
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
117
|
+
}
|
|
118
|
+
stream.on("data", onData);
|
|
119
|
+
stream.on("end", onEnd);
|
|
120
|
+
stream.on("error", onError);
|
|
121
|
+
if (typeof stream.resume === "function") {
|
|
122
|
+
stream.resume();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|