@actiondock/core 2.7.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 +102 -10
- package/dist/errors.js +103 -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 -24
- package/dist/index.js +19 -23
- package/dist/input/advice.d.ts +5 -1
- package/dist/input/advice.js +163 -214
- package/dist/input/describe.d.ts +1 -1
- package/dist/input/flat-errors.d.ts +5 -6
- package/dist/input/flat-errors.js +6 -6
- package/dist/input/flat-materializer.js +42 -44
- 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/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 +2 -2
- package/dist/profile/client-query.js +5 -6
- package/dist/profile/client-transport.d.ts +19 -17
- package/dist/profile/client-transport.js +47 -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 -93
- package/dist/runtime/runner.js +132 -364
- package/dist/runtime/standalone.d.ts +23 -8
- package/dist/runtime/standalone.js +100 -147
- 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 -635
- 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
package/dist/input/advice.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isFlatPathPropertyName, isForbiddenActionInputPropertyName, } from "./flat-predicates.js";
|
|
1
|
+
import { isFlatSafePropertyName, isFlatPathPropertyName, isForbiddenActionInputPropertyName, } from "./flat-predicates.js";
|
|
2
2
|
const RECOGNIZED_SCHEMA_TYPES = new Set([
|
|
3
3
|
"object",
|
|
4
4
|
"array",
|
|
@@ -54,6 +54,93 @@ function createMalformedAdvice(message) {
|
|
|
54
54
|
fields: [],
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* 构造无字段可用、推荐 full-json 的 ok 状态报告骨架(absent / any 语义共用单一事实源)。
|
|
59
|
+
* 字段顺序与既有契约保持一致,禁止调整键序。
|
|
60
|
+
*/
|
|
61
|
+
function createNonFlatOkAdvice(schemaState) {
|
|
62
|
+
return {
|
|
63
|
+
version: 1,
|
|
64
|
+
analysisMode: "declared-properties-only",
|
|
65
|
+
analysisStatus: "ok",
|
|
66
|
+
schemaState,
|
|
67
|
+
inputFeasibility: "possible-or-unknown",
|
|
68
|
+
flatAvailable: false,
|
|
69
|
+
requiredSatisfiable: null,
|
|
70
|
+
flatCandidate: false,
|
|
71
|
+
schemaRecommendedMode: "full-json",
|
|
72
|
+
fields: [],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 校验 schema 节点的 type 关键字结构合法性(根级与属性级重复逻辑的单一事实源)。
|
|
77
|
+
*
|
|
78
|
+
* 判定规则:
|
|
79
|
+
* - 字符串型 type 必须属于 RECOGNIZED_SCHEMA_TYPES;
|
|
80
|
+
* - 数组型 type 不得为空、元素必须是受认可字符串且不得重复;
|
|
81
|
+
* - 其余形态(数字、对象等)非法。
|
|
82
|
+
*
|
|
83
|
+
* @param type 待校验的 type 关键字值
|
|
84
|
+
* @returns 非法时返回结构化原因,合法时返回 undefined
|
|
85
|
+
*/
|
|
86
|
+
function validateTypeKeyword(type) {
|
|
87
|
+
if (typeof type === "string") {
|
|
88
|
+
if (!RECOGNIZED_SCHEMA_TYPES.has(type)) {
|
|
89
|
+
return { code: "UNRECOGNIZED_TYPE", value: type };
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
if (Array.isArray(type)) {
|
|
94
|
+
if (type.length === 0) {
|
|
95
|
+
return { code: "EMPTY_TYPE_ARRAY" };
|
|
96
|
+
}
|
|
97
|
+
const seen = new Set();
|
|
98
|
+
for (const t of type) {
|
|
99
|
+
if (typeof t !== "string" || !RECOGNIZED_SCHEMA_TYPES.has(t)) {
|
|
100
|
+
return { code: "UNRECOGNIZED_TYPE_IN_ARRAY", value: String(t) };
|
|
101
|
+
}
|
|
102
|
+
if (seen.has(t)) {
|
|
103
|
+
return { code: "DUPLICATE_TYPE", value: t };
|
|
104
|
+
}
|
|
105
|
+
seen.add(t);
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
return { code: "NOT_STRING_OR_ARRAY" };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 依 TypeKeywordIssue 构造根级 type 关键字 malformed 消息(措辞与历史实现逐字节一致)。
|
|
113
|
+
*/
|
|
114
|
+
function formatRootTypeKeywordMessage(issue) {
|
|
115
|
+
switch (issue.code) {
|
|
116
|
+
case "UNRECOGNIZED_TYPE":
|
|
117
|
+
return `Unrecognized type keyword: '${issue.value}'`;
|
|
118
|
+
case "UNRECOGNIZED_TYPE_IN_ARRAY":
|
|
119
|
+
return `Unrecognized type in type array: '${issue.value}'`;
|
|
120
|
+
case "EMPTY_TYPE_ARRAY":
|
|
121
|
+
return "Type array must not be empty";
|
|
122
|
+
case "DUPLICATE_TYPE":
|
|
123
|
+
return `Duplicate type in type array: '${issue.value}'`;
|
|
124
|
+
case "NOT_STRING_OR_ARRAY":
|
|
125
|
+
return "Type keyword must be a string or array of strings";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 依 TypeKeywordIssue 构造属性级 type 关键字 malformed 消息(措辞与历史实现逐字节一致)。
|
|
130
|
+
*/
|
|
131
|
+
function formatPropertyTypeKeywordMessage(issue, propKey) {
|
|
132
|
+
switch (issue.code) {
|
|
133
|
+
case "UNRECOGNIZED_TYPE":
|
|
134
|
+
case "UNRECOGNIZED_TYPE_IN_ARRAY":
|
|
135
|
+
return `Unrecognized type '${issue.value}' in property '${propKey}'`;
|
|
136
|
+
case "EMPTY_TYPE_ARRAY":
|
|
137
|
+
return `Type array in property '${propKey}' must not be empty`;
|
|
138
|
+
case "DUPLICATE_TYPE":
|
|
139
|
+
return `Duplicate type '${issue.value}' in property '${propKey}'`;
|
|
140
|
+
case "NOT_STRING_OR_ARRAY":
|
|
141
|
+
return `Type in property '${propKey}' must be string or array of strings`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
57
144
|
/**
|
|
58
145
|
* 依据 inputSchema 构造 v1 版本的 Action 输入编码顾问报告(机器契约)。
|
|
59
146
|
*
|
|
@@ -64,18 +151,7 @@ export function buildCliInputAdviceV1(schema) {
|
|
|
64
151
|
// 1. Schema Sanity 阶段(Section 44)
|
|
65
152
|
// 1.1 根形状检查(Section 44.1)
|
|
66
153
|
if (schema === undefined) {
|
|
67
|
-
return
|
|
68
|
-
version: 1,
|
|
69
|
-
analysisMode: "declared-properties-only",
|
|
70
|
-
analysisStatus: "ok",
|
|
71
|
-
schemaState: "absent",
|
|
72
|
-
inputFeasibility: "possible-or-unknown",
|
|
73
|
-
flatAvailable: false,
|
|
74
|
-
requiredSatisfiable: null,
|
|
75
|
-
flatCandidate: false,
|
|
76
|
-
schemaRecommendedMode: "full-json",
|
|
77
|
-
fields: [],
|
|
78
|
-
};
|
|
154
|
+
return createNonFlatOkAdvice("absent");
|
|
79
155
|
}
|
|
80
156
|
if (typeof schema === "boolean") {
|
|
81
157
|
if (schema === false) {
|
|
@@ -93,18 +169,7 @@ export function buildCliInputAdviceV1(schema) {
|
|
|
93
169
|
fields: [],
|
|
94
170
|
};
|
|
95
171
|
}
|
|
96
|
-
return
|
|
97
|
-
version: 1,
|
|
98
|
-
analysisMode: "declared-properties-only",
|
|
99
|
-
analysisStatus: "ok",
|
|
100
|
-
schemaState: "any",
|
|
101
|
-
inputFeasibility: "possible-or-unknown",
|
|
102
|
-
flatAvailable: false,
|
|
103
|
-
requiredSatisfiable: null,
|
|
104
|
-
flatCandidate: false,
|
|
105
|
-
schemaRecommendedMode: "full-json",
|
|
106
|
-
fields: [],
|
|
107
|
-
};
|
|
172
|
+
return createNonFlatOkAdvice("any");
|
|
108
173
|
}
|
|
109
174
|
if (!isPlainObject(schema)) {
|
|
110
175
|
return {
|
|
@@ -124,30 +189,11 @@ export function buildCliInputAdviceV1(schema) {
|
|
|
124
189
|
}
|
|
125
190
|
const s = schema;
|
|
126
191
|
// 1.2 关键字形状检查(Section 44.2)
|
|
127
|
-
// 检查 type
|
|
192
|
+
// 检查 type(结构校验单一事实源,根级消息模板独立持有)
|
|
128
193
|
if (s.type !== undefined) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
else if (Array.isArray(s.type)) {
|
|
135
|
-
if (s.type.length === 0) {
|
|
136
|
-
return createMalformedAdvice("Type array must not be empty");
|
|
137
|
-
}
|
|
138
|
-
const seen = new Set();
|
|
139
|
-
for (const t of s.type) {
|
|
140
|
-
if (typeof t !== "string" || !RECOGNIZED_SCHEMA_TYPES.has(t)) {
|
|
141
|
-
return createMalformedAdvice(`Unrecognized type in type array: '${String(t)}'`);
|
|
142
|
-
}
|
|
143
|
-
if (seen.has(t)) {
|
|
144
|
-
return createMalformedAdvice(`Duplicate type in type array: '${t}'`);
|
|
145
|
-
}
|
|
146
|
-
seen.add(t);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
return createMalformedAdvice("Type keyword must be a string or array of strings");
|
|
194
|
+
const issue = validateTypeKeyword(s.type);
|
|
195
|
+
if (issue) {
|
|
196
|
+
return createMalformedAdvice(formatRootTypeKeywordMessage(issue));
|
|
151
197
|
}
|
|
152
198
|
}
|
|
153
199
|
// 检查 properties
|
|
@@ -161,28 +207,9 @@ export function buildCliInputAdviceV1(schema) {
|
|
|
161
207
|
}
|
|
162
208
|
if (isPlainObject(propVal)) {
|
|
163
209
|
if (propVal.type !== undefined) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
else if (Array.isArray(propVal.type)) {
|
|
170
|
-
if (propVal.type.length === 0) {
|
|
171
|
-
return createMalformedAdvice(`Type array in property '${propKey}' must not be empty`);
|
|
172
|
-
}
|
|
173
|
-
const seen = new Set();
|
|
174
|
-
for (const t of propVal.type) {
|
|
175
|
-
if (typeof t !== "string" || !RECOGNIZED_SCHEMA_TYPES.has(t)) {
|
|
176
|
-
return createMalformedAdvice(`Unrecognized type '${String(t)}' in property '${propKey}'`);
|
|
177
|
-
}
|
|
178
|
-
if (seen.has(t)) {
|
|
179
|
-
return createMalformedAdvice(`Duplicate type '${t}' in property '${propKey}'`);
|
|
180
|
-
}
|
|
181
|
-
seen.add(t);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
return createMalformedAdvice(`Type in property '${propKey}' must be string or array of strings`);
|
|
210
|
+
const issue = validateTypeKeyword(propVal.type);
|
|
211
|
+
if (issue) {
|
|
212
|
+
return createMalformedAdvice(formatPropertyTypeKeywordMessage(issue, propKey));
|
|
186
213
|
}
|
|
187
214
|
}
|
|
188
215
|
if (propVal.enum !== undefined) {
|
|
@@ -280,18 +307,7 @@ export function buildCliInputAdviceV1(schema) {
|
|
|
280
307
|
}
|
|
281
308
|
// 2. 语义分类阶段(Section 45)
|
|
282
309
|
if (Object.keys(s).length === 0) {
|
|
283
|
-
return
|
|
284
|
-
version: 1,
|
|
285
|
-
analysisMode: "declared-properties-only",
|
|
286
|
-
analysisStatus: "ok",
|
|
287
|
-
schemaState: "any",
|
|
288
|
-
inputFeasibility: "possible-or-unknown",
|
|
289
|
-
flatAvailable: false,
|
|
290
|
-
requiredSatisfiable: null,
|
|
291
|
-
flatCandidate: false,
|
|
292
|
-
schemaRecommendedMode: "full-json",
|
|
293
|
-
fields: [],
|
|
294
|
-
};
|
|
310
|
+
return createNonFlatOkAdvice("any");
|
|
295
311
|
}
|
|
296
312
|
const hasComplexKeyword = isTupleItems ||
|
|
297
313
|
COMPLEX_KEYWORDS.some((kw) => s[kw] !== undefined);
|
|
@@ -413,83 +429,37 @@ export function buildCliInputAdviceV1(schema) {
|
|
|
413
429
|
// enum/const 绝不改变 operator 选择,仅显式单一 schema.type 决定 operator。
|
|
414
430
|
// 无显式 type 时不推断类型,推荐 full-json,flatSafe: false。
|
|
415
431
|
if (typeof propType === "string") {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
assignmentTemplate: `${key}=TEXT`,
|
|
426
|
-
...(desc ? { description: desc } : {}),
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
else if (propType === "number" || propType === "integer") {
|
|
430
|
-
fields.push({
|
|
431
|
-
path: key,
|
|
432
|
-
inputAllowed: true,
|
|
433
|
-
flatSafe: true,
|
|
434
|
-
required: isReq,
|
|
435
|
-
type: propType,
|
|
436
|
-
operator: ":=",
|
|
437
|
-
encoding: "json-number",
|
|
438
|
-
assignmentTemplate: `${key}:=NUMBER`,
|
|
439
|
-
...(desc ? { description: desc } : {}),
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
else if (propType === "boolean") {
|
|
443
|
-
fields.push({
|
|
444
|
-
path: key,
|
|
445
|
-
inputAllowed: true,
|
|
446
|
-
flatSafe: true,
|
|
447
|
-
required: isReq,
|
|
448
|
-
type: "boolean",
|
|
449
|
-
operator: ":=",
|
|
450
|
-
encoding: "json-boolean",
|
|
451
|
-
assignmentTemplate: `${key}:=BOOLEAN`,
|
|
452
|
-
...(desc ? { description: desc } : {}),
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
else if (propType === "null") {
|
|
456
|
-
fields.push({
|
|
457
|
-
path: key,
|
|
458
|
-
inputAllowed: true,
|
|
459
|
-
flatSafe: true,
|
|
460
|
-
required: isReq,
|
|
461
|
-
type: "null",
|
|
462
|
-
operator: ":=",
|
|
463
|
-
encoding: "json-null",
|
|
464
|
-
assignmentTemplate: `${key}:=null`,
|
|
465
|
-
...(desc ? { description: desc } : {}),
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
else if (propType === "array") {
|
|
469
|
-
fields.push({
|
|
470
|
-
path: key,
|
|
471
|
-
inputAllowed: true,
|
|
472
|
-
flatSafe: true,
|
|
473
|
-
required: isReq,
|
|
474
|
-
type: "array",
|
|
475
|
-
operator: ":=",
|
|
432
|
+
// Flat-safe 显式单一类型的编码参数表(string/number/integer/boolean/null/array/object)。
|
|
433
|
+
// 各分支仅在 type、encoding、assignmentTemplate 与 hint 上差异,结构完全一致。
|
|
434
|
+
const flatSafeTypeTable = {
|
|
435
|
+
string: { encoding: "string", assignmentTemplate: `${key}=TEXT` },
|
|
436
|
+
number: { encoding: "json-number", assignmentTemplate: `${key}:=NUMBER` },
|
|
437
|
+
integer: { encoding: "json-number", assignmentTemplate: `${key}:=NUMBER` },
|
|
438
|
+
boolean: { encoding: "json-boolean", assignmentTemplate: `${key}:=BOOLEAN` },
|
|
439
|
+
null: { encoding: "json-null", assignmentTemplate: `${key}:=null` },
|
|
440
|
+
array: {
|
|
476
441
|
encoding: "json-array",
|
|
477
442
|
assignmentTemplate: `${key}:=JSON`,
|
|
478
443
|
hint: "建议使用 --input-file 或标准输入传递数组结构",
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
444
|
+
},
|
|
445
|
+
object: {
|
|
446
|
+
encoding: "json-object",
|
|
447
|
+
assignmentTemplate: `${key}:=JSON`,
|
|
448
|
+
hint: "大型结构建议使用 --input-file",
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
const flatSafeEntry = flatSafeTypeTable[propType];
|
|
452
|
+
if (flatSafeEntry) {
|
|
483
453
|
fields.push({
|
|
484
454
|
path: key,
|
|
485
455
|
inputAllowed: true,
|
|
486
456
|
flatSafe: true,
|
|
487
457
|
required: isReq,
|
|
488
|
-
type:
|
|
489
|
-
operator: ":=",
|
|
490
|
-
encoding:
|
|
491
|
-
assignmentTemplate:
|
|
492
|
-
hint:
|
|
458
|
+
type: propType,
|
|
459
|
+
operator: propType === "string" ? "=" : ":=",
|
|
460
|
+
encoding: flatSafeEntry.encoding,
|
|
461
|
+
assignmentTemplate: flatSafeEntry.assignmentTemplate,
|
|
462
|
+
...(flatSafeEntry.hint ? { hint: flatSafeEntry.hint } : {}),
|
|
493
463
|
...(desc ? { description: desc } : {}),
|
|
494
464
|
});
|
|
495
465
|
}
|
|
@@ -599,13 +569,31 @@ export function buildCliInputAdviceV1(schema) {
|
|
|
599
569
|
fields,
|
|
600
570
|
};
|
|
601
571
|
}
|
|
602
|
-
|
|
572
|
+
/**
|
|
573
|
+
* 扁平编码通用指引文案。
|
|
574
|
+
* 属性名安全判定统一引用 flat-predicates 的单一事实源(含禁止属性排除),
|
|
575
|
+
* 禁止在本文件另建独立正则分叉。
|
|
576
|
+
*/
|
|
603
577
|
export const FLAT_ENCODING_GUIDELINES = [
|
|
604
578
|
"字符串: path=TEXT",
|
|
605
579
|
"JSON 标量与结构: path:=JSON (例如 count:=1, enabled:=true)",
|
|
606
580
|
"数组元素: path.INDEX=... (例如 items.0=first)",
|
|
607
581
|
"提示: 复杂嵌套或大段文本建议使用 --input 或 --input-file",
|
|
608
582
|
];
|
|
583
|
+
/**
|
|
584
|
+
* 构造无扁平能力、无字段明细的空结果建议报告(各降级早退分支共用单一事实源)。
|
|
585
|
+
* 键序与既有契约保持一致,禁止调整。
|
|
586
|
+
*/
|
|
587
|
+
function createEmptyFlatAdvice(notes) {
|
|
588
|
+
return {
|
|
589
|
+
flatSupported: false,
|
|
590
|
+
hasFlatFields: false,
|
|
591
|
+
fields: [],
|
|
592
|
+
requiredTemplates: [],
|
|
593
|
+
optionalTemplates: [],
|
|
594
|
+
notes,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
609
597
|
/**
|
|
610
598
|
* 依据 inputSchema 构造统一的 Action 输入编码建议。
|
|
611
599
|
*
|
|
@@ -615,62 +603,27 @@ export const FLAT_ENCODING_GUIDELINES = [
|
|
|
615
603
|
export function buildActionInputAdvice(schema) {
|
|
616
604
|
if (typeof schema === "boolean") {
|
|
617
605
|
if (schema === false) {
|
|
618
|
-
return
|
|
619
|
-
flatSupported: false,
|
|
620
|
-
hasFlatFields: false,
|
|
621
|
-
fields: [],
|
|
622
|
-
requiredTemplates: [],
|
|
623
|
-
optionalTemplates: [],
|
|
624
|
-
notes: ["布尔模式 false:拒绝所有输入,任何调用参数均判定为非法"],
|
|
625
|
-
};
|
|
606
|
+
return createEmptyFlatAdvice(["布尔模式 false:拒绝所有输入,任何调用参数均判定为非法"]);
|
|
626
607
|
}
|
|
627
|
-
return
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
fields: [],
|
|
631
|
-
requiredTemplates: [],
|
|
632
|
-
optionalTemplates: [],
|
|
633
|
-
notes: [
|
|
634
|
-
"布尔模式 true:接受任意合法 JSON 输入;调用时无需指定必填参数,非对象根输入请使用 --input-file 或 --input",
|
|
635
|
-
],
|
|
636
|
-
};
|
|
608
|
+
return createEmptyFlatAdvice([
|
|
609
|
+
"布尔模式 true:接受任意合法 JSON 输入;调用时无需指定必填参数,非对象根输入请使用 --input-file 或 --input",
|
|
610
|
+
]);
|
|
637
611
|
}
|
|
638
612
|
if (!schema || typeof schema !== "object") {
|
|
639
|
-
return {
|
|
640
|
-
flatSupported: false,
|
|
641
|
-
hasFlatFields: false,
|
|
642
|
-
fields: [],
|
|
643
|
-
requiredTemplates: [],
|
|
644
|
-
optionalTemplates: [],
|
|
645
|
-
notes: ["根模式未声明输入字段,直接调用或使用 --input-file / --input '{}'"],
|
|
646
|
-
};
|
|
613
|
+
return createEmptyFlatAdvice(["根模式未声明输入字段,直接调用或使用 --input-file / --input '{}'"]);
|
|
647
614
|
}
|
|
648
615
|
const s = schema;
|
|
649
616
|
// 校验根模式是否为 object 类型
|
|
650
617
|
if (s.type !== undefined && s.type !== "object") {
|
|
651
|
-
return
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
fields: [],
|
|
655
|
-
requiredTemplates: [],
|
|
656
|
-
optionalTemplates: [],
|
|
657
|
-
notes: [
|
|
658
|
-
`根模式类型为 '${s.type}',不支持扁平赋值,建议使用 --input-file 或 --input`,
|
|
659
|
-
],
|
|
660
|
-
};
|
|
618
|
+
return createEmptyFlatAdvice([
|
|
619
|
+
`根模式类型为 '${s.type}',不支持扁平赋值,建议使用 --input-file 或 --input`,
|
|
620
|
+
]);
|
|
661
621
|
}
|
|
662
622
|
const properties = (s.properties || {});
|
|
663
623
|
const required = Array.isArray(s.required) ? s.required : [];
|
|
664
624
|
const propKeys = Object.keys(properties);
|
|
665
625
|
if (propKeys.length === 0) {
|
|
666
|
-
return {
|
|
667
|
-
flatSupported: false,
|
|
668
|
-
hasFlatFields: false,
|
|
669
|
-
fields: [],
|
|
670
|
-
requiredTemplates: [],
|
|
671
|
-
optionalTemplates: [],
|
|
672
|
-
notes: ["根模式无声明属性,不支持扁平赋值,建议使用 --input-file 或 --input '{}'"],
|
|
673
|
-
};
|
|
626
|
+
return createEmptyFlatAdvice(["根模式无声明属性,不支持扁平赋值,建议使用 --input-file 或 --input '{}'"]);
|
|
674
627
|
}
|
|
675
628
|
const fields = [];
|
|
676
629
|
const requiredTemplates = [];
|
|
@@ -680,10 +633,21 @@ export function buildActionInputAdvice(schema) {
|
|
|
680
633
|
const prop = properties[key] || {};
|
|
681
634
|
const typeStr = prop.type ? String(prop.type) : "any";
|
|
682
635
|
const isReq = required.includes(key);
|
|
683
|
-
const isFlatSafe =
|
|
636
|
+
const isFlatSafe = isFlatSafePropertyName(key);
|
|
684
637
|
let assignmentTemplate;
|
|
685
638
|
let hint;
|
|
686
639
|
if (isFlatSafe) {
|
|
640
|
+
// 数组元素标量类型到扁平元素赋值模板的参数表(共用同一守卫与拼接规则)。
|
|
641
|
+
// 命中时模板形态为 `${key}.0<operator><占位符>`,未命中回退数组整体 JSON 模板。
|
|
642
|
+
const itemsTypeTemplates = {
|
|
643
|
+
string: `${key}.0=TEXT`,
|
|
644
|
+
number: `${key}.0:=NUMBER`,
|
|
645
|
+
integer: `${key}.0:=NUMBER`,
|
|
646
|
+
boolean: `${key}.0:=BOOLEAN`,
|
|
647
|
+
};
|
|
648
|
+
const itemsTemplate = prop.type === "array" && prop.items && typeof prop.items === "object"
|
|
649
|
+
? itemsTypeTemplates[prop.items.type]
|
|
650
|
+
: undefined;
|
|
687
651
|
if (prop.type === "string") {
|
|
688
652
|
assignmentTemplate = `${key}=TEXT`;
|
|
689
653
|
}
|
|
@@ -693,23 +657,8 @@ export function buildActionInputAdvice(schema) {
|
|
|
693
657
|
else if (prop.type === "boolean") {
|
|
694
658
|
assignmentTemplate = `${key}:=BOOLEAN`;
|
|
695
659
|
}
|
|
696
|
-
else if (
|
|
697
|
-
|
|
698
|
-
typeof prop.items === "object" &&
|
|
699
|
-
prop.items.type === "string") {
|
|
700
|
-
assignmentTemplate = `${key}.0=TEXT`;
|
|
701
|
-
}
|
|
702
|
-
else if (prop.type === "array" &&
|
|
703
|
-
prop.items &&
|
|
704
|
-
typeof prop.items === "object" &&
|
|
705
|
-
(prop.items.type === "number" || prop.items.type === "integer")) {
|
|
706
|
-
assignmentTemplate = `${key}.0:=NUMBER`;
|
|
707
|
-
}
|
|
708
|
-
else if (prop.type === "array" &&
|
|
709
|
-
prop.items &&
|
|
710
|
-
typeof prop.items === "object" &&
|
|
711
|
-
prop.items.type === "boolean") {
|
|
712
|
-
assignmentTemplate = `${key}.0:=BOOLEAN`;
|
|
660
|
+
else if (itemsTemplate) {
|
|
661
|
+
assignmentTemplate = itemsTemplate;
|
|
713
662
|
}
|
|
714
663
|
else if (prop.type === "array") {
|
|
715
664
|
assignmentTemplate = `${key}:=JSON`;
|
package/dist/input/describe.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { INVALID_JSON, INVALID_JSON_LITERAL, INVALID_FLAT_ARGUMENT, INPUT_PATH_CONFLICT, FLAT_INPUT_LIMIT_EXCEEDED, INPUT_LIMIT_EXCEEDED, INPUT_POLICY_VIOLATION, INPUT_CONFLICT, INPUT_FILE_NOT_FOUND, INPUT_FILE_READ_FAILED, INPUT_NOT_JSON, INPUT_VALIDATION_FAILED, };
|
|
1
|
+
import { ActionDockError, type ErrorCode } from "../errors.js";
|
|
2
|
+
export { INVALID_JSON, INVALID_JSON_LITERAL, INVALID_FLAT_ARGUMENT, INPUT_PATH_CONFLICT, FLAT_INPUT_LIMIT_EXCEEDED, INPUT_LIMIT_EXCEEDED, INPUT_POLICY_VIOLATION, INPUT_CONFLICT, INPUT_FILE_NOT_FOUND, INPUT_FILE_READ_FAILED, INPUT_NOT_JSON, INPUT_VALIDATION_FAILED, } from "../errors.js";
|
|
3
3
|
/** 输入大小或深度超出限制的根因类型 */
|
|
4
4
|
export type InputLimitExceededReason = "MAX_INPUT_BYTES" | "MAX_JSON_DEPTH" | string;
|
|
5
5
|
/** 扁平入参安全阈值超限的根因类型 */
|
|
@@ -19,16 +19,15 @@ export type InputValidationSource = "flat-json-literal" | "flat-materialized" |
|
|
|
19
19
|
/**
|
|
20
20
|
* 输入领域通用结构化异常类。
|
|
21
21
|
*/
|
|
22
|
-
export declare class InputError extends
|
|
23
|
-
readonly code: string;
|
|
22
|
+
export declare class InputError extends ActionDockError {
|
|
24
23
|
readonly details?: Record<string, unknown> | string[];
|
|
25
|
-
constructor(code:
|
|
24
|
+
constructor(code: ErrorCode, message: string, details?: Record<string, unknown> | string[]);
|
|
26
25
|
}
|
|
27
26
|
/**
|
|
28
27
|
* 扁平输入结构化异常类(继承 InputError 保持既有捕获兼容)。
|
|
29
28
|
*/
|
|
30
29
|
export declare class FlatInputError extends InputError {
|
|
31
|
-
constructor(code:
|
|
30
|
+
constructor(code: ErrorCode, message: string, details?: Record<string, unknown>);
|
|
32
31
|
}
|
|
33
32
|
/**
|
|
34
33
|
* 构造 INVALID_JSON 异常(用于完整 JSON 文档解析失败)。
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { INVALID_JSON, INVALID_JSON_LITERAL, INVALID_FLAT_ARGUMENT, INPUT_PATH_CONFLICT, FLAT_INPUT_LIMIT_EXCEEDED, INPUT_LIMIT_EXCEEDED, INPUT_POLICY_VIOLATION, INPUT_CONFLICT, INPUT_FILE_NOT_FOUND, INPUT_FILE_READ_FAILED,
|
|
2
|
-
|
|
1
|
+
import { ActionDockError, INVALID_JSON, INVALID_JSON_LITERAL, INVALID_FLAT_ARGUMENT, INPUT_PATH_CONFLICT, FLAT_INPUT_LIMIT_EXCEEDED, INPUT_LIMIT_EXCEEDED, INPUT_POLICY_VIOLATION, INPUT_CONFLICT, INPUT_FILE_NOT_FOUND, INPUT_FILE_READ_FAILED, } from "../errors.js";
|
|
2
|
+
// 错误码常量对外转引单一事实源:仅保留工厂函数实际引用的导入绑定,
|
|
3
|
+
// 对外输出统一经由本条 export-from 语句完成,不再另列本地重导出清单。
|
|
4
|
+
export { INVALID_JSON, INVALID_JSON_LITERAL, INVALID_FLAT_ARGUMENT, INPUT_PATH_CONFLICT, FLAT_INPUT_LIMIT_EXCEEDED, INPUT_LIMIT_EXCEEDED, INPUT_POLICY_VIOLATION, INPUT_CONFLICT, INPUT_FILE_NOT_FOUND, INPUT_FILE_READ_FAILED, INPUT_NOT_JSON, INPUT_VALIDATION_FAILED, } from "../errors.js";
|
|
3
5
|
/**
|
|
4
6
|
* 输入领域通用结构化异常类。
|
|
5
7
|
*/
|
|
6
|
-
export class InputError extends
|
|
7
|
-
code;
|
|
8
|
+
export class InputError extends ActionDockError {
|
|
8
9
|
details;
|
|
9
10
|
constructor(code, message, details) {
|
|
10
|
-
super(message);
|
|
11
|
+
super(code, message, details);
|
|
11
12
|
this.name = "InputError";
|
|
12
|
-
this.code = code;
|
|
13
13
|
this.details = details;
|
|
14
14
|
Object.setPrototypeOf(this, InputError.prototype);
|
|
15
15
|
}
|
|
@@ -64,6 +64,44 @@ function materializeNode(node) {
|
|
|
64
64
|
*/
|
|
65
65
|
export function materializeFlatInput(assignments, options) {
|
|
66
66
|
const root = createNode("", NodeState.OBJECT);
|
|
67
|
+
/**
|
|
68
|
+
* 叶子赋值四态推进(字符串段与数字段共用单一事实源)。
|
|
69
|
+
* - UNSET:置为 VALUE 并写入值;
|
|
70
|
+
* - VALUE:重复赋值,抛 DUPLICATE_ASSIGNMENT;
|
|
71
|
+
* - OBJECT/ARRAY:值写入既有容器,抛 LEAF_CONTAINER_CONFLICT。
|
|
72
|
+
*/
|
|
73
|
+
const assignLeaf = (child, currentPathStr, operator, value, valueLength) => {
|
|
74
|
+
if (child.state === NodeState.UNSET) {
|
|
75
|
+
child.state = NodeState.VALUE;
|
|
76
|
+
child.value = value;
|
|
77
|
+
}
|
|
78
|
+
else if (child.state === NodeState.VALUE) {
|
|
79
|
+
throw inputPathConflict(`Duplicate assignment to leaf path "${currentPathStr}"`, { path: currentPathStr, operator, valueLength, reason: "DUPLICATE_ASSIGNMENT" });
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
throw inputPathConflict(`Path conflict at "${currentPathStr}": cannot assign value to existing container (${child.state.toLowerCase()})`, { path: currentPathStr, operator, valueLength, reason: "LEAF_CONTAINER_CONFLICT" });
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* 容器下钻三态推进(字符串段与数字段共用单一事实源)。
|
|
87
|
+
* - UNSET:按下一路径段形态初始化为 OBJECT 或 ARRAY;
|
|
88
|
+
* - VALUE:穿透既有值节点,抛 LEAF_CONTAINER_CONFLICT;
|
|
89
|
+
* - 容器形态与期望不符:抛 OBJECT_ARRAY_CONFLICT。
|
|
90
|
+
* 错误消息中下一访问段的措辞由 accessNoun 提供(字符串段为 property,数字段为 index/property)。
|
|
91
|
+
*/
|
|
92
|
+
const descend = (child, currentPathStr, nextSeg, operator, valueLength, accessNoun) => {
|
|
93
|
+
const nextExpectedState = typeof nextSeg === "number" ? NodeState.ARRAY : NodeState.OBJECT;
|
|
94
|
+
if (child.state === NodeState.UNSET) {
|
|
95
|
+
child.state = nextExpectedState;
|
|
96
|
+
}
|
|
97
|
+
else if (child.state === NodeState.VALUE) {
|
|
98
|
+
throw inputPathConflict(`Path conflict at "${currentPathStr}": cannot access ${accessNoun} "${nextSeg}" on existing value`, { path: currentPathStr, operator, valueLength, reason: "LEAF_CONTAINER_CONFLICT" });
|
|
99
|
+
}
|
|
100
|
+
else if (child.state !== nextExpectedState) {
|
|
101
|
+
throw inputPathConflict(`Path conflict at "${currentPathStr}": expected ${nextExpectedState.toLowerCase()} but found ${child.state.toLowerCase()}`, { path: currentPathStr, operator, valueLength, reason: "OBJECT_ARRAY_CONFLICT" });
|
|
102
|
+
}
|
|
103
|
+
return child;
|
|
104
|
+
};
|
|
67
105
|
for (const assignment of assignments) {
|
|
68
106
|
const { path, value, operator, rawValue } = assignment;
|
|
69
107
|
const valueLength = Buffer.byteLength(rawValue, "utf8");
|
|
@@ -90,30 +128,10 @@ export function materializeFlatInput(assignments, options) {
|
|
|
90
128
|
current.objectChildren[seg] = child;
|
|
91
129
|
}
|
|
92
130
|
if (isLast) {
|
|
93
|
-
|
|
94
|
-
child.state = NodeState.VALUE;
|
|
95
|
-
child.value = value;
|
|
96
|
-
}
|
|
97
|
-
else if (child.state === NodeState.VALUE) {
|
|
98
|
-
throw inputPathConflict(`Duplicate assignment to leaf path "${currentPathStr}"`, { path: currentPathStr, operator, valueLength, reason: "DUPLICATE_ASSIGNMENT" });
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
throw inputPathConflict(`Path conflict at "${currentPathStr}": cannot assign value to existing container (${child.state.toLowerCase()})`, { path: currentPathStr, operator, valueLength, reason: "LEAF_CONTAINER_CONFLICT" });
|
|
102
|
-
}
|
|
131
|
+
assignLeaf(child, currentPathStr, operator, value, valueLength);
|
|
103
132
|
}
|
|
104
133
|
else {
|
|
105
|
-
|
|
106
|
-
const nextExpectedState = typeof nextSeg === "number" ? NodeState.ARRAY : NodeState.OBJECT;
|
|
107
|
-
if (child.state === NodeState.UNSET) {
|
|
108
|
-
child.state = nextExpectedState;
|
|
109
|
-
}
|
|
110
|
-
else if (child.state === NodeState.VALUE) {
|
|
111
|
-
throw inputPathConflict(`Path conflict at "${currentPathStr}": cannot access property "${nextSeg}" on existing value`, { path: currentPathStr, operator, valueLength, reason: "LEAF_CONTAINER_CONFLICT" });
|
|
112
|
-
}
|
|
113
|
-
else if (child.state !== nextExpectedState) {
|
|
114
|
-
throw inputPathConflict(`Path conflict at "${currentPathStr}": expected ${nextExpectedState.toLowerCase()} but found ${child.state.toLowerCase()}`, { path: currentPathStr, operator, valueLength, reason: "OBJECT_ARRAY_CONFLICT" });
|
|
115
|
-
}
|
|
116
|
-
current = child;
|
|
134
|
+
current = descend(child, currentPathStr, path[i + 1], operator, valueLength, "property");
|
|
117
135
|
}
|
|
118
136
|
}
|
|
119
137
|
else {
|
|
@@ -132,30 +150,10 @@ export function materializeFlatInput(assignments, options) {
|
|
|
132
150
|
current.arrayChildren.set(seg, child);
|
|
133
151
|
}
|
|
134
152
|
if (isLast) {
|
|
135
|
-
|
|
136
|
-
child.state = NodeState.VALUE;
|
|
137
|
-
child.value = value;
|
|
138
|
-
}
|
|
139
|
-
else if (child.state === NodeState.VALUE) {
|
|
140
|
-
throw inputPathConflict(`Duplicate assignment to leaf path "${currentPathStr}"`, { path: currentPathStr, operator, valueLength, reason: "DUPLICATE_ASSIGNMENT" });
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
throw inputPathConflict(`Path conflict at "${currentPathStr}": cannot assign value to existing container (${child.state.toLowerCase()})`, { path: currentPathStr, operator, valueLength, reason: "LEAF_CONTAINER_CONFLICT" });
|
|
144
|
-
}
|
|
153
|
+
assignLeaf(child, currentPathStr, operator, value, valueLength);
|
|
145
154
|
}
|
|
146
155
|
else {
|
|
147
|
-
|
|
148
|
-
const nextExpectedState = typeof nextSeg === "number" ? NodeState.ARRAY : NodeState.OBJECT;
|
|
149
|
-
if (child.state === NodeState.UNSET) {
|
|
150
|
-
child.state = nextExpectedState;
|
|
151
|
-
}
|
|
152
|
-
else if (child.state === NodeState.VALUE) {
|
|
153
|
-
throw inputPathConflict(`Path conflict at "${currentPathStr}": cannot access index/property "${nextSeg}" on existing value`, { path: currentPathStr, operator, valueLength, reason: "LEAF_CONTAINER_CONFLICT" });
|
|
154
|
-
}
|
|
155
|
-
else if (child.state !== nextExpectedState) {
|
|
156
|
-
throw inputPathConflict(`Path conflict at "${currentPathStr}": expected ${nextExpectedState.toLowerCase()} but found ${child.state.toLowerCase()}`, { path: currentPathStr, operator, valueLength, reason: "OBJECT_ARRAY_CONFLICT" });
|
|
157
|
-
}
|
|
158
|
-
current = child;
|
|
156
|
+
current = descend(child, currentPathStr, path[i + 1], operator, valueLength, "index/property");
|
|
159
157
|
}
|
|
160
158
|
}
|
|
161
159
|
}
|