@vaudience/aigentflow-flow-validator 0.22.0 → 0.23.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/PARITY.md +31 -1
- package/README.md +1 -1
- package/dist/cli.js +103 -3
- package/dist/index.cjs +102 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +102 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/PARITY.md
CHANGED
|
@@ -4,7 +4,31 @@ This document maps every rule in this JavaScript validator back to the AIgentFlo
|
|
|
4
4
|
Go reference implementation, records the intentional divergences, and defines the
|
|
5
5
|
discipline for keeping the two in sync.
|
|
6
6
|
|
|
7
|
-
**Tracks AIgentFlow flow schema: `v2.
|
|
7
|
+
**Tracks AIgentFlow flow schema: `v2.831.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), which adds the rule `unknown_function_operation` and a clearer `unknown_executor_scheme` message (package 0.23.0, below). Earlier: the rule `flow_data_key_reserved` (package 0.22.0). Earlier: the `here` executor scheme, the `strict_templates` flow key and the rule `template_default_piped` (package 0.21.0). Earlier: the warning `compliance_residency_unenforced` (package 0.20.0). `orchestrator.region` / `orchestrator.enforce_region` (package 0.19.0). Earlier: the AigentChat executor was removed (package 0.18.0). Earlier additions: the `examples:` block (package 0.17.0), `server_owned_query_key` (package 0.15.1) from the AIgentFlow release after v2.790.0, `credential_endpoint_unpaired` (package 0.15.2) from AIgentFlow v2.793.0 and the release after it, and `display_name_too_long` (package 0.15.3) from AIgentFlow v2.802.0.
|
|
8
|
+
|
|
9
|
+
> package 0.23.0 — **`unknown_function_operation`**, in step with the reference (AIgentFlow
|
|
10
|
+
> v2.831.0; `specVersion` 2.831.0). One new **error**: a step or loop sub-step whose executor
|
|
11
|
+
> is `function://<a>/<b>` where `<a>/<b>` is not one of the 32 registered built-ins. Before
|
|
12
|
+
> this, `function://utils/noop` validated and then failed every run with "function
|
|
13
|
+
> 'utils/noop' not found".
|
|
14
|
+
>
|
|
15
|
+
> | Rule (reference) | Code | Severity | Module |
|
|
16
|
+
> | -------------------------------------------- | ---------------------------- | -------- | ---------------------------------------------------- |
|
|
17
|
+
> | `validateFunctionOperationKnown` (parser.go) | `unknown_function_operation` | error | `validators/executors.ts`, spec `functionOperations` |
|
|
18
|
+
>
|
|
19
|
+
> - **Operation** = authority + `/` + path of the executor URL (so `binary-processor/validate-and-store`).
|
|
20
|
+
> The set is closed and sorted in spec `functionOperations.operations`. Templated executors
|
|
21
|
+
> (containing `{{`), unparsable URLs (`invalid_executor_url` owns them) and other schemes are
|
|
22
|
+
> skipped; the scheme match is exact and case-sensitive.
|
|
23
|
+
> - **Finding.** Field `steps.<id>.executor` (loop sub-step: `steps.<id>.loop.steps[i].executor`,
|
|
24
|
+
> step id `<id>.<sub>`); message `step "<id>" uses function://<op>, which is not a built-in
|
|
25
|
+
function. Available: <sorted list, ", ">`; the suggestion carries the list.
|
|
26
|
+
> - **Divergence 23** (below): the reference tolerates this code on its load door.
|
|
27
|
+
> - **`unknown_executor_scheme` text (still a warning).** For `decide` the message appends
|
|
28
|
+
> `Decision models run through ai://{provider}/decide (for example ai://openai/decide), not
|
|
29
|
+
through a scheme of their own.`; for any other unknown scheme with a known scheme within
|
|
30
|
+
> edit distance 2 (case-insensitive; the closest, ties to the alphabetically first) it
|
|
31
|
+
> appends `Did you mean '<scheme>'?`. No verdict changes.
|
|
8
32
|
|
|
9
33
|
> package 0.22.0 — **`flow_data_key_reserved`**, in step with the reference (AIgentFlow
|
|
10
34
|
> v2.828.0; `specVersion` 2.828.0). One new **error**: a key of the top-level `data:` map
|
|
@@ -1110,6 +1134,7 @@ Comparison contract: **error `code` + `valid` verdict**, not message wording. Th
|
|
|
1110
1134
|
| `tool_discovery` vocabulary, mock scenario `delay`, empty `output:` entry | `validateToolDiscoveryVocabulary`, `validateMockScenarioDelays`, `validateOutputParameters` (parser.go) | `validators/saveDoor.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1111
1135
|
| `executor_config` `${NAME}` references outside the key's scope (`executor_config_env_scope`) | `ValidateExecutorConfigEnvScopes` (parser.go) | `validators/executorConfigEnv.ts` | `v0150-save-door.test.ts`, `conformance.test.ts` |
|
|
1112
1136
|
| A step or loop sub-step `query:` declaring a server-owned key (`server_owned_query_key`) | `validateNoServerOwnedStepQueryKeys` (validation.go), `IsServerOwnedParamKey` | `validators/serverOwnedQueryKeys.ts` | `v0151-save-door.test.ts`, `conformance.test.ts` |
|
|
1137
|
+
| A `function://` operation outside the built-in set (`unknown_function_operation`) | `validateFunctionOperationKnown` (parser.go) | `validators/executors.ts` | `v0230-function-operation.test.ts`, `conformance.test.ts` |
|
|
1113
1138
|
| A top-level `data:` key naming engine state (`flow_data_key_reserved`) | `FlowDataKeyReservation`, `validateFlowDataKeysAreNotReserved` (validation.flowdata.go) | `validators/flowDataKeys.ts` | `v0220-flow-data-keys.test.ts`, `conformance.test.ts` |
|
|
1114
1139
|
| An endpoint a server-supplied credential will never be sent to (`credential_endpoint_unpaired`, warning) | `validateCredentialEndpointPairing`, `validateFamilyCredentialEndpointPairing` (validation.go) | `validators/credentialEndpoint.ts`, `validators/goUrl.ts` | `v0152-credential-endpoint.test.ts`, `conformance.test.ts` |
|
|
1115
1140
|
| The `examples:` block: 58 codes, one walker (ids, inputs, references, expectations, checkpoints, variants) | the examples walker (`ValidateExamples`) | `validators/examples.ts`, `validators/goUrl.ts` | `examples.test.ts`, `conformance.test.ts` |
|
|
@@ -1380,6 +1405,11 @@ number their shared divergences alike._
|
|
|
1380
1405
|
- The recursion bound of 32 on a nested value is the reference's own and is not in the
|
|
1381
1406
|
spec.
|
|
1382
1407
|
|
|
1408
|
+
23. **`unknown_function_operation` is tolerated on the reference's load door (0.23.0).** The
|
|
1409
|
+
reference refuses an unknown `function://` operation when a flow is _saved_ but loads
|
|
1410
|
+
(and then fails at run time) a stored flow that already carries one. This validator
|
|
1411
|
+
answers "would this save", so it is an error here, as for every other save-door rule.
|
|
1412
|
+
|
|
1383
1413
|
---
|
|
1384
1414
|
|
|
1385
1415
|
## The examples block
|
package/README.md
CHANGED
|
@@ -116,7 +116,7 @@ Low-level: parse YAML into `{ flow?, parseErrors, parseWarnings }` without valid
|
|
|
116
116
|
|
|
117
117
|
### `SPEC_VERSION: string`
|
|
118
118
|
|
|
119
|
-
The AIgentFlow flow-schema version this build tracks (e.g. `"2.
|
|
119
|
+
The AIgentFlow flow-schema version this build tracks (e.g. `"2.831.0"`).
|
|
120
120
|
|
|
121
121
|
### Options
|
|
122
122
|
|
package/dist/cli.js
CHANGED
|
@@ -438,7 +438,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
438
438
|
message: "Executor is required for each step",
|
|
439
439
|
code: "missing_required_field",
|
|
440
440
|
stepId: stepID,
|
|
441
|
-
suggestion: "Specify an executor URL (e.g., 'function://
|
|
441
|
+
suggestion: "Specify an executor URL (e.g., 'function://utils/log')"
|
|
442
442
|
});
|
|
443
443
|
} else if (executorText === null) {
|
|
444
444
|
issues.error({
|
|
@@ -455,7 +455,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
455
455
|
// src/spec/aigentflow-spec.json
|
|
456
456
|
var aigentflow_spec_default = {
|
|
457
457
|
$comment: "Vendored enum surface mirrored from the AIgentFlow Go reference implementation. Regenerate / review on every flow-YAML schema change. See PARITY.md. Values here are public protocol/type names only.",
|
|
458
|
-
specVersion: "2.
|
|
458
|
+
specVersion: "2.831.0",
|
|
459
459
|
inputSchemaVersion: 1,
|
|
460
460
|
executorSchemes: [
|
|
461
461
|
"a2a",
|
|
@@ -965,6 +965,47 @@ var aigentflow_spec_default = {
|
|
|
965
965
|
stepFieldFormat: "steps.%s.query.%s",
|
|
966
966
|
loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
|
|
967
967
|
},
|
|
968
|
+
functionOperations: {
|
|
969
|
+
$comment: `unknown_function_operation (ERROR): a step or loop sub-step whose executor is function://<authority>/<path> where authority+"/"+path is not in operations. Templated executors (containing templateActionOpen), unparsable URLs (invalid_executor_url owns them) and other schemes are skipped; the scheme match is exact and case-sensitive, as in the reference. The message is messageFormat with the stepId, the operation, then operations joined by ', '. Reference: validateFunctionOperationKnown (parser.go), functionSchemaOperations.`,
|
|
970
|
+
code: "unknown_function_operation",
|
|
971
|
+
scheme: "function",
|
|
972
|
+
messageFormat: "step %s uses function://%s, which is not a built-in function. Available: %s",
|
|
973
|
+
suggestionFormat: "Use one of the built-in function operations: %s. For custom logic use a script://, http:// or ai:// executor instead.",
|
|
974
|
+
operations: [
|
|
975
|
+
"binary-processor/validate-and-store",
|
|
976
|
+
"data/create",
|
|
977
|
+
"data/from-json",
|
|
978
|
+
"data/to-json",
|
|
979
|
+
"data/validate",
|
|
980
|
+
"demo/adult-processor",
|
|
981
|
+
"demo/calculator",
|
|
982
|
+
"demo/data-processor",
|
|
983
|
+
"demo/finalizer",
|
|
984
|
+
"demo/senior-processor",
|
|
985
|
+
"demo/youth-processor",
|
|
986
|
+
"document/finalize",
|
|
987
|
+
"document/prepare",
|
|
988
|
+
"json/validate",
|
|
989
|
+
"math/basic",
|
|
990
|
+
"math/calculate",
|
|
991
|
+
"math/sum",
|
|
992
|
+
"results-compiler/aggregate",
|
|
993
|
+
"system/info",
|
|
994
|
+
"text/analysis",
|
|
995
|
+
"text/count-characters",
|
|
996
|
+
"text/count-lines",
|
|
997
|
+
"text/count-occurrences",
|
|
998
|
+
"text/count-words",
|
|
999
|
+
"text/find-pattern",
|
|
1000
|
+
"text/generate",
|
|
1001
|
+
"text/replace-text",
|
|
1002
|
+
"text/search",
|
|
1003
|
+
"utils/datetime",
|
|
1004
|
+
"utils/format-date",
|
|
1005
|
+
"utils/log",
|
|
1006
|
+
"utils/parse-date"
|
|
1007
|
+
]
|
|
1008
|
+
},
|
|
968
1009
|
flowDataReservedKeys: {
|
|
969
1010
|
$comment: "flow_data_key_reserved (ERROR, no loop demotion): a key of the top-level data: map that names engine state. data: is seeded into the mission's .data namespace, where the engine keeps its own bookkeeping, so a flow declaring such a key would forge it. Matching is exact and case-sensitive. A key is reserved when it is one of exact (each with its reason), or starts with one of prefixes (checked in order, first wins; each with its reason). The finding's field is fieldFormat with the key; the message is messageFormat with the key (JSON-quoted, as Go's %q) then the reason; the suggestion is verbatim. Findings are sorted by key. Reference: FlowDataKeyReservation, validateFlowDataKeysAreNotReserved (aigentflow#247 review).",
|
|
970
1011
|
code: "flow_data_key_reserved",
|
|
@@ -2080,9 +2121,51 @@ var TEMPLATE_DEFAULT_PIPED = {
|
|
|
2080
2121
|
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
2081
2122
|
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
2082
2123
|
};
|
|
2124
|
+
var FUNCTION_OPERATIONS = {
|
|
2125
|
+
code: aigentflow_spec_default.functionOperations.code,
|
|
2126
|
+
scheme: aigentflow_spec_default.functionOperations.scheme,
|
|
2127
|
+
messageFormat: aigentflow_spec_default.functionOperations.messageFormat,
|
|
2128
|
+
suggestionFormat: aigentflow_spec_default.functionOperations.suggestionFormat,
|
|
2129
|
+
operations: new Set(aigentflow_spec_default.functionOperations.operations),
|
|
2130
|
+
sorted: aigentflow_spec_default.functionOperations.operations
|
|
2131
|
+
};
|
|
2083
2132
|
|
|
2084
2133
|
// src/validators/executors.ts
|
|
2085
2134
|
var SCHEME_SEPARATOR = "://";
|
|
2135
|
+
var UNKNOWN_SCHEME_BASE = (scheme) => `Unknown executor scheme '${scheme}'. It may be valid in a newer AIgentFlow release, or it could be a typo.`;
|
|
2136
|
+
var DECIDE_SCHEME = "decide";
|
|
2137
|
+
var DECIDE_HINT = "Decision models run through ai://{provider}/decide (for example ai://openai/decide), not through a scheme of their own.";
|
|
2138
|
+
var MAX_SCHEME_EDIT_DISTANCE = 2;
|
|
2139
|
+
function editDistance(a, b) {
|
|
2140
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
2141
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2142
|
+
const cur = [i];
|
|
2143
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2144
|
+
cur[j] = Math.min(
|
|
2145
|
+
prev[j] + 1,
|
|
2146
|
+
cur[j - 1] + 1,
|
|
2147
|
+
prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
|
|
2148
|
+
);
|
|
2149
|
+
}
|
|
2150
|
+
prev = cur;
|
|
2151
|
+
}
|
|
2152
|
+
return prev[b.length];
|
|
2153
|
+
}
|
|
2154
|
+
function unknownSchemeMessage(scheme) {
|
|
2155
|
+
const lower = scheme.toLowerCase();
|
|
2156
|
+
const base = UNKNOWN_SCHEME_BASE(scheme);
|
|
2157
|
+
if (lower === DECIDE_SCHEME) return `${base} ${DECIDE_HINT}`;
|
|
2158
|
+
let best;
|
|
2159
|
+
let bestDist = MAX_SCHEME_EDIT_DISTANCE + 1;
|
|
2160
|
+
for (const known of [...EXECUTOR_SCHEMES].sort()) {
|
|
2161
|
+
const d = editDistance(lower, known);
|
|
2162
|
+
if (d < bestDist) {
|
|
2163
|
+
best = known;
|
|
2164
|
+
bestDist = d;
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
return best === void 0 ? base : `${base} Did you mean '${best}'?`;
|
|
2168
|
+
}
|
|
2086
2169
|
function checkExecutor(executor, field, stepID, issues) {
|
|
2087
2170
|
const sepIdx = executor.indexOf(SCHEME_SEPARATOR);
|
|
2088
2171
|
const scheme = sepIdx > 0 ? executor.slice(0, sepIdx) : "";
|
|
@@ -2101,11 +2184,28 @@ function checkExecutor(executor, field, stepID, issues) {
|
|
|
2101
2184
|
if (!EXECUTOR_SCHEMES.has(scheme.toLowerCase())) {
|
|
2102
2185
|
issues.warn({
|
|
2103
2186
|
field,
|
|
2104
|
-
message:
|
|
2187
|
+
message: unknownSchemeMessage(scheme),
|
|
2105
2188
|
code: "unknown_executor_scheme",
|
|
2106
2189
|
stepId: stepID
|
|
2107
2190
|
});
|
|
2191
|
+
return;
|
|
2108
2192
|
}
|
|
2193
|
+
checkFunctionOperation(executor, field, stepID, templated, issues);
|
|
2194
|
+
}
|
|
2195
|
+
function checkFunctionOperation(executor, field, stepID, templated, issues) {
|
|
2196
|
+
if (templated) return;
|
|
2197
|
+
const m = EXECUTOR_URL_PATTERN.exec(executor);
|
|
2198
|
+
if (m === null || m[1] !== FUNCTION_OPERATIONS.scheme) return;
|
|
2199
|
+
const op = `${m[2]}/${m[3]}`;
|
|
2200
|
+
if (FUNCTION_OPERATIONS.operations.has(op)) return;
|
|
2201
|
+
const list = FUNCTION_OPERATIONS.sorted.join(", ");
|
|
2202
|
+
issues.error({
|
|
2203
|
+
field,
|
|
2204
|
+
message: FUNCTION_OPERATIONS.messageFormat.replace("%s", () => JSON.stringify(stepID)).replace("%s", () => op).replace("%s", () => list),
|
|
2205
|
+
code: FUNCTION_OPERATIONS.code,
|
|
2206
|
+
stepId: stepID,
|
|
2207
|
+
suggestion: FUNCTION_OPERATIONS.suggestionFormat.replace("%s", () => list)
|
|
2208
|
+
});
|
|
2109
2209
|
}
|
|
2110
2210
|
function validateExecutors(flow, issues) {
|
|
2111
2211
|
const steps = flow.steps;
|
package/dist/index.cjs
CHANGED
|
@@ -435,7 +435,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
435
435
|
message: "Executor is required for each step",
|
|
436
436
|
code: "missing_required_field",
|
|
437
437
|
stepId: stepID,
|
|
438
|
-
suggestion: "Specify an executor URL (e.g., 'function://
|
|
438
|
+
suggestion: "Specify an executor URL (e.g., 'function://utils/log')"
|
|
439
439
|
});
|
|
440
440
|
} else if (executorText === null) {
|
|
441
441
|
issues.error({
|
|
@@ -451,7 +451,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
451
451
|
|
|
452
452
|
// src/spec/aigentflow-spec.json
|
|
453
453
|
var aigentflow_spec_default = {
|
|
454
|
-
specVersion: "2.
|
|
454
|
+
specVersion: "2.831.0",
|
|
455
455
|
inputSchemaVersion: 1,
|
|
456
456
|
executorSchemes: [
|
|
457
457
|
"a2a",
|
|
@@ -948,6 +948,46 @@ var aigentflow_spec_default = {
|
|
|
948
948
|
stepFieldFormat: "steps.%s.query.%s",
|
|
949
949
|
loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
|
|
950
950
|
},
|
|
951
|
+
functionOperations: {
|
|
952
|
+
code: "unknown_function_operation",
|
|
953
|
+
scheme: "function",
|
|
954
|
+
messageFormat: "step %s uses function://%s, which is not a built-in function. Available: %s",
|
|
955
|
+
suggestionFormat: "Use one of the built-in function operations: %s. For custom logic use a script://, http:// or ai:// executor instead.",
|
|
956
|
+
operations: [
|
|
957
|
+
"binary-processor/validate-and-store",
|
|
958
|
+
"data/create",
|
|
959
|
+
"data/from-json",
|
|
960
|
+
"data/to-json",
|
|
961
|
+
"data/validate",
|
|
962
|
+
"demo/adult-processor",
|
|
963
|
+
"demo/calculator",
|
|
964
|
+
"demo/data-processor",
|
|
965
|
+
"demo/finalizer",
|
|
966
|
+
"demo/senior-processor",
|
|
967
|
+
"demo/youth-processor",
|
|
968
|
+
"document/finalize",
|
|
969
|
+
"document/prepare",
|
|
970
|
+
"json/validate",
|
|
971
|
+
"math/basic",
|
|
972
|
+
"math/calculate",
|
|
973
|
+
"math/sum",
|
|
974
|
+
"results-compiler/aggregate",
|
|
975
|
+
"system/info",
|
|
976
|
+
"text/analysis",
|
|
977
|
+
"text/count-characters",
|
|
978
|
+
"text/count-lines",
|
|
979
|
+
"text/count-occurrences",
|
|
980
|
+
"text/count-words",
|
|
981
|
+
"text/find-pattern",
|
|
982
|
+
"text/generate",
|
|
983
|
+
"text/replace-text",
|
|
984
|
+
"text/search",
|
|
985
|
+
"utils/datetime",
|
|
986
|
+
"utils/format-date",
|
|
987
|
+
"utils/log",
|
|
988
|
+
"utils/parse-date"
|
|
989
|
+
]
|
|
990
|
+
},
|
|
951
991
|
flowDataReservedKeys: {
|
|
952
992
|
code: "flow_data_key_reserved",
|
|
953
993
|
fieldFormat: "data.%s",
|
|
@@ -1982,9 +2022,51 @@ var TEMPLATE_DEFAULT_PIPED = {
|
|
|
1982
2022
|
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
1983
2023
|
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
1984
2024
|
};
|
|
2025
|
+
var FUNCTION_OPERATIONS = {
|
|
2026
|
+
code: aigentflow_spec_default.functionOperations.code,
|
|
2027
|
+
scheme: aigentflow_spec_default.functionOperations.scheme,
|
|
2028
|
+
messageFormat: aigentflow_spec_default.functionOperations.messageFormat,
|
|
2029
|
+
suggestionFormat: aigentflow_spec_default.functionOperations.suggestionFormat,
|
|
2030
|
+
operations: new Set(aigentflow_spec_default.functionOperations.operations),
|
|
2031
|
+
sorted: aigentflow_spec_default.functionOperations.operations
|
|
2032
|
+
};
|
|
1985
2033
|
|
|
1986
2034
|
// src/validators/executors.ts
|
|
1987
2035
|
var SCHEME_SEPARATOR = "://";
|
|
2036
|
+
var UNKNOWN_SCHEME_BASE = (scheme) => `Unknown executor scheme '${scheme}'. It may be valid in a newer AIgentFlow release, or it could be a typo.`;
|
|
2037
|
+
var DECIDE_SCHEME = "decide";
|
|
2038
|
+
var DECIDE_HINT = "Decision models run through ai://{provider}/decide (for example ai://openai/decide), not through a scheme of their own.";
|
|
2039
|
+
var MAX_SCHEME_EDIT_DISTANCE = 2;
|
|
2040
|
+
function editDistance(a, b) {
|
|
2041
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
2042
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2043
|
+
const cur = [i];
|
|
2044
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2045
|
+
cur[j] = Math.min(
|
|
2046
|
+
prev[j] + 1,
|
|
2047
|
+
cur[j - 1] + 1,
|
|
2048
|
+
prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
prev = cur;
|
|
2052
|
+
}
|
|
2053
|
+
return prev[b.length];
|
|
2054
|
+
}
|
|
2055
|
+
function unknownSchemeMessage(scheme) {
|
|
2056
|
+
const lower = scheme.toLowerCase();
|
|
2057
|
+
const base = UNKNOWN_SCHEME_BASE(scheme);
|
|
2058
|
+
if (lower === DECIDE_SCHEME) return `${base} ${DECIDE_HINT}`;
|
|
2059
|
+
let best;
|
|
2060
|
+
let bestDist = MAX_SCHEME_EDIT_DISTANCE + 1;
|
|
2061
|
+
for (const known of [...EXECUTOR_SCHEMES].sort()) {
|
|
2062
|
+
const d = editDistance(lower, known);
|
|
2063
|
+
if (d < bestDist) {
|
|
2064
|
+
best = known;
|
|
2065
|
+
bestDist = d;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
return best === void 0 ? base : `${base} Did you mean '${best}'?`;
|
|
2069
|
+
}
|
|
1988
2070
|
function checkExecutor(executor, field, stepID, issues) {
|
|
1989
2071
|
const sepIdx = executor.indexOf(SCHEME_SEPARATOR);
|
|
1990
2072
|
const scheme = sepIdx > 0 ? executor.slice(0, sepIdx) : "";
|
|
@@ -2003,11 +2085,28 @@ function checkExecutor(executor, field, stepID, issues) {
|
|
|
2003
2085
|
if (!EXECUTOR_SCHEMES.has(scheme.toLowerCase())) {
|
|
2004
2086
|
issues.warn({
|
|
2005
2087
|
field,
|
|
2006
|
-
message:
|
|
2088
|
+
message: unknownSchemeMessage(scheme),
|
|
2007
2089
|
code: "unknown_executor_scheme",
|
|
2008
2090
|
stepId: stepID
|
|
2009
2091
|
});
|
|
2092
|
+
return;
|
|
2010
2093
|
}
|
|
2094
|
+
checkFunctionOperation(executor, field, stepID, templated, issues);
|
|
2095
|
+
}
|
|
2096
|
+
function checkFunctionOperation(executor, field, stepID, templated, issues) {
|
|
2097
|
+
if (templated) return;
|
|
2098
|
+
const m = EXECUTOR_URL_PATTERN.exec(executor);
|
|
2099
|
+
if (m === null || m[1] !== FUNCTION_OPERATIONS.scheme) return;
|
|
2100
|
+
const op = `${m[2]}/${m[3]}`;
|
|
2101
|
+
if (FUNCTION_OPERATIONS.operations.has(op)) return;
|
|
2102
|
+
const list = FUNCTION_OPERATIONS.sorted.join(", ");
|
|
2103
|
+
issues.error({
|
|
2104
|
+
field,
|
|
2105
|
+
message: FUNCTION_OPERATIONS.messageFormat.replace("%s", () => JSON.stringify(stepID)).replace("%s", () => op).replace("%s", () => list),
|
|
2106
|
+
code: FUNCTION_OPERATIONS.code,
|
|
2107
|
+
stepId: stepID,
|
|
2108
|
+
suggestion: FUNCTION_OPERATIONS.suggestionFormat.replace("%s", () => list)
|
|
2109
|
+
});
|
|
2011
2110
|
}
|
|
2012
2111
|
function validateExecutors(flow, issues) {
|
|
2013
2112
|
const steps = flow.steps;
|