@vaudience/aigentflow-flow-validator 0.20.0 → 0.21.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 +76 -35
- package/README.md +2 -2
- package/dist/cli.js +157 -1
- package/dist/index.cjs +154 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +154 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/PARITY.md
CHANGED
|
@@ -4,7 +4,47 @@ 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.827.0`** (`SPEC_VERSION` in [`src/spec/aigentflow-spec.json`](./src/spec/aigentflow-spec.json)), which adds the `here` executor scheme, the `strict_templates` flow key and the rule `template_default_piped` (package 0.21.0, below). 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.21.0 — **`here://`, `strict_templates` and `template_default_piped`**, in step
|
|
10
|
+
> with the reference (AIgentFlow v2.827.0; `specVersion` 2.827.0). Three changes.
|
|
11
|
+
>
|
|
12
|
+
> 1. **The `here` executor scheme** (`here://maps/{geocode|reverse_geocode|route|places}`) is
|
|
13
|
+
> added to `executorSchemes`, between `getmd` and `homeassistant`; a `here://` step no longer
|
|
14
|
+
> draws `unknown_executor_scheme`. Fixture `valid-executor-here.yaml` forbids that code.
|
|
15
|
+
> 2. **`strict_templates`**, a new optional top-level boolean, is a known key (`knownKeys.Flow`).
|
|
16
|
+
> A value that is not a boolean is `invalid_type` (field `strict_templates`). A YAML null and
|
|
17
|
+
> the YAML 1.1 words the reference's decoder still reads into a `bool` (`yes`/`no`/`on`/`off`,
|
|
18
|
+
> `y`/`n`, in the usual cases) are accepted; this port cannot tell a quoted scalar from a plain
|
|
19
|
+
> one, so a quoted `"yes"` passes here (looser). Its run-time effect is engine behaviour and out
|
|
20
|
+
> of scope. Fixtures: `valid-strict-templates.yaml`, `invalid-strict-templates-type.yaml`.
|
|
21
|
+
> 3. **`template_default_piped`.** The template function `default` takes the value FIRST
|
|
22
|
+
> (`default .x "fallback"`). A pipeline that passes a value into it as a **non-first command**
|
|
23
|
+
> (`{{ .x | default "w" }}`, mid-chain, parenthesised, inside `if`/`else if`/`range`/`with`/
|
|
24
|
+
> `template`/`block`/`define`, or after a `$v :=` declaration) hands the piped value as the
|
|
25
|
+
> LAST argument, so the fallback is always the result and nothing fails at run time. An
|
|
26
|
+
> **error** at the top level and a **warning** inside a loop body (positional, exactly like
|
|
27
|
+
> `template_syntax_error`; the code survives the demotion), reported at the field of the
|
|
28
|
+
> template with its pipeline in the message. The rule runs only on a template that raised no
|
|
29
|
+
> syntax or function finding, because the reference walks a parse tree that exists only for a
|
|
30
|
+
> template that parses. Message and suggestion are the reference's text (spec section
|
|
31
|
+
> `templateDefaultPiped`). Never flagged: a field (`.query.default`), a variable (`$default`),
|
|
32
|
+
> a string or raw-string literal containing the word, a comment, `default` as the FIRST
|
|
33
|
+
> command (`default .x "w"`, `default (.x | upper) "w"`). The reference's run door tolerates
|
|
34
|
+
> this code (it only refuses a run for other errors); this port has no run door. Fixtures:
|
|
35
|
+
> `invalid-template-default-piped.yaml`, `warn-template-default-piped-loop.yaml`,
|
|
36
|
+
> `valid-template-default-clean.yaml` (forbids the code in both severities: its whole risk is
|
|
37
|
+
> a false positive). The all-correct `valid-minimal.yaml` was rewritten from
|
|
38
|
+
> `{{ .query.name | default "world" }}` to `{{ default .query.name "world" }}`.
|
|
39
|
+
>
|
|
40
|
+
> **Known limits.** There is no Go parse tree here: `src/template/gotmpl-default.ts` is a small
|
|
41
|
+
> tokenizer over each `{{ ... }}` action (string, raw-string and character literals and
|
|
42
|
+
> comments respected; pipelines split on top-level `|`; parentheses recursed). Consequences:
|
|
43
|
+
> (a) the printed pipeline is rebuilt from tokens (whitespace normalised) and matches Go's
|
|
44
|
+
> printing for ordinary pipelines, but may differ in spelling for exotic ones (numeric
|
|
45
|
+
> literals are echoed as written); (b) a `default` reached as a method or chain (`(.x).default`) is not a function call and is not flagged; (c) only the
|
|
46
|
+
> template fields the existing syntax check already walks are covered; (d) the same pipeline
|
|
47
|
+
> text appearing twice in one template is reported once, as in the reference.
|
|
8
48
|
|
|
9
49
|
> package 0.20.0 — **`compliance_residency_unenforced`**, in step with the reference
|
|
10
50
|
> (AIgentFlow v2.821.0; `specVersion` 2.821.0). A **warning**, never an error, and it
|
|
@@ -1021,40 +1061,41 @@ Comparison contract: **error `code` + `valid` verdict**, not message wording. Th
|
|
|
1021
1061
|
|
|
1022
1062
|
## Rule map
|
|
1023
1063
|
|
|
1024
|
-
| Area | Go source | JS module | Tested by
|
|
1025
|
-
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
1026
|
-
| Required fields, start-step existence, per-step executor, reserved `.` in IDs, reserved id `orchestrator` | `validateBasicStructure`, `ValidateFlow` head | `validators/basicStructure.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1027
|
-
| Executor URI shape (the ONE parser) + scheme | `ValidateFlow` executor-URL rule (parser.go), `ParseExecutorURLString` | `validators/executors.ts` | `validate.test.ts`
|
|
1028
|
-
| Query/property/array-item schema + array constraints | `validateQueryParameters`, `validateProperties`, `validateArrayItems`, `validateArrayConstraints` | `validators/querySchema.ts` | `validate.test.ts`
|
|
1029
|
-
| Response-expectation types + array items + `required` | `ValidateFlow` (response block), `validateSemantics` | `validators/responseExpectation.ts` | `validate.test.ts`
|
|
1030
|
-
| Response expectation nothing reads (`response_expectation_unread`, warning) | `validateResponseExpectationIsRead` (validation.go) | `validators/responseExpectation.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1031
|
-
| Error strategy (action, goto, max_delay, backoff, retry_on) | `validateErrorStrategy` | `validators/errorStrategy.ts` | `validate.test.ts`
|
|
1032
|
-
| Retired `budget:` / `max_retries:` (flow, step, loop sub-step) and `campaign.budget_max_per_child` → `unknown_yaml_key` | `retiredGrammarKeys` + `KnownFields(true)` (parser.go) | `validators/retiredKeys.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1033
|
-
| `next` references (save-door sentinels `null`/`orchestrator`), reachability and cycles (`end` is an ordinary step) | `validateNextLogic` (parser.go), `findReachableSteps`, `checkForCycles` | `validators/connectivity.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1034
|
-
| Unknown keys at every level (`unknown_yaml_key`) and value kinds (`invalid_type`) | `KnownFields(true)` over the reference's types (parser.go); `knownKeys` in the spec | `validators/unknownKeys.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1035
|
-
| `next.parallel` + orchestrator-next requirement | `validateNextLogic`, `validateOrchestratorNext` | `validators/nextLogic.ts` | `validate.test.ts`
|
|
1036
|
-
| Expression functions (XOR package/function) | `validateExpressionFunctions` | `validators/expressionFunctions.ts` | `validate.test.ts`
|
|
1037
|
-
| Expression-function catalog (`package:` refused, unknown `function:` refused) | `validateExpressionFunctionCatalog` (parser.go) | `validators/expressionFunctions.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1038
|
-
| Expression-function USE (`{{ fn_* }}` must be in the catalog AND declared) | `validateExpressionFunctionUsage` (parser.go) | `validators/expressionFunctions.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1039
|
-
| Loop / for_each / throttle | `validateLoop`, `validateForEach`, `validateThrottle` | `validators/loopForEachThrottle.ts` | `validate.test.ts`
|
|
1040
|
-
| Loop sub-step id that shadows a loop-result summary field (`loop_sub_step_id_reserved`, warning) | `validateLoopSubStepIDCollision` (validation.loopbody.go) | `validators/loopForEachThrottle.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1041
|
-
| Loop sub-step `next:` targets (same-loop only, sentinels, no parallel) | `validateLoopSubStepNext` (parser.go) | `validators/loopForEachThrottle.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1042
|
-
| Orchestrator structure + campaign requires orchestrator | `validateOrchestrator`, `validateAndNormalizeCampaign` | `validators/orchestratorCampaign.ts` | `validate.test.ts`
|
|
1043
|
-
| Campaign `max_credits_per_child` (decoded as int64, >= 0), `child_flows` | `CampaignConfig.Validate` (domain.campaign.go) | `validators/orchestratorCampaign.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1044
|
-
| `tool_discovery` vocabulary, mock scenario `delay`, empty `output:` entry | `validateToolDiscoveryVocabulary`, `validateMockScenarioDelays`, `validateOutputParameters` (parser.go) | `validators/saveDoor.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1045
|
-
| `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`
|
|
1046
|
-
| 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`
|
|
1047
|
-
| 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`
|
|
1048
|
-
| 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`
|
|
1049
|
-
| Orchestrator `.exons` frontmatter: spec, provider, `requirements.resources`, `tools.allow` withholds | `validateOrchestrator` (parser.go), `validateOrchestratorToolAllowWithholds` | `validators/exons.ts` | `v0150-save-door.test.ts`, `conformance.test.ts`
|
|
1050
|
-
| Credential bindings (`stored/...`, inject_as, exclusivity) | `validateStepCredentialBindings` | `validators/credentialBindings.ts` | `validate.test.ts`
|
|
1051
|
-
| `input_schema` definition + ordering lint | `ValidateInputSchemaDefinition`, `LintInputSchemaFieldOrdering` | `validators/inputSchema.ts` | `validate.test.ts`
|
|
1052
|
-
| Step `output_schema` definition (reuses the input-schema subset) | `ValidateInputSchemaDefinition` (on `step.OutputSchema`, parser.go) | `validators/outputSchema.ts` (+ shared `inputSchema.ts`) | `validate.test.ts`
|
|
1053
|
-
| `quality_gate:` block (rubric/threshold/on_fail/goto) | `FlowParser.validateQualityGate` (parser.go) | `validators/qualityGate.ts` | `validate.test.ts`
|
|
1054
|
-
| Processing-operation type + per-operation config keys | `validateProcessingOperationShape` | `validators/processingOperations.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1055
|
-
| Step `max_duration` the engine cannot apply (`step_max_duration_ignored`, warning) | `validateStepMaxDurationIsApplied` (validation.go), `parseStepMaxDuration` (flowengine.steptimeout.go) | `validators/stepMaxDuration.ts` | `validate.test.ts`, `conformance.test.ts`
|
|
1056
|
-
| An EU-only residency beside an unpinned region-lane `ai://` call (`compliance_residency_unenforced`, warning) | `validateResidencyEnforcement` (validation.residency.go) | `validators/complianceResidency.ts` | `v0200-compliance-residency.test.ts`, `conformance.test.ts`
|
|
1057
|
-
| Go-template syntax | `validateTemplateExpression` (Parse step) | `template/gotmpl-syntax.ts` + `validators/templates.ts` | `gotmpl-syntax.test.ts`, `validate.test.ts`
|
|
1064
|
+
| Area | Go source | JS module | Tested by |
|
|
1065
|
+
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------- |
|
|
1066
|
+
| Required fields, start-step existence, per-step executor, reserved `.` in IDs, reserved id `orchestrator` | `validateBasicStructure`, `ValidateFlow` head | `validators/basicStructure.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1067
|
+
| Executor URI shape (the ONE parser) + scheme | `ValidateFlow` executor-URL rule (parser.go), `ParseExecutorURLString` | `validators/executors.ts` | `validate.test.ts` |
|
|
1068
|
+
| Query/property/array-item schema + array constraints | `validateQueryParameters`, `validateProperties`, `validateArrayItems`, `validateArrayConstraints` | `validators/querySchema.ts` | `validate.test.ts` |
|
|
1069
|
+
| Response-expectation types + array items + `required` | `ValidateFlow` (response block), `validateSemantics` | `validators/responseExpectation.ts` | `validate.test.ts` |
|
|
1070
|
+
| Response expectation nothing reads (`response_expectation_unread`, warning) | `validateResponseExpectationIsRead` (validation.go) | `validators/responseExpectation.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1071
|
+
| Error strategy (action, goto, max_delay, backoff, retry_on) | `validateErrorStrategy` | `validators/errorStrategy.ts` | `validate.test.ts` |
|
|
1072
|
+
| Retired `budget:` / `max_retries:` (flow, step, loop sub-step) and `campaign.budget_max_per_child` → `unknown_yaml_key` | `retiredGrammarKeys` + `KnownFields(true)` (parser.go) | `validators/retiredKeys.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1073
|
+
| `next` references (save-door sentinels `null`/`orchestrator`), reachability and cycles (`end` is an ordinary step) | `validateNextLogic` (parser.go), `findReachableSteps`, `checkForCycles` | `validators/connectivity.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1074
|
+
| Unknown keys at every level (`unknown_yaml_key`) and value kinds (`invalid_type`) | `KnownFields(true)` over the reference's types (parser.go); `knownKeys` in the spec | `validators/unknownKeys.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1075
|
+
| `next.parallel` + orchestrator-next requirement | `validateNextLogic`, `validateOrchestratorNext` | `validators/nextLogic.ts` | `validate.test.ts` |
|
|
1076
|
+
| Expression functions (XOR package/function) | `validateExpressionFunctions` | `validators/expressionFunctions.ts` | `validate.test.ts` |
|
|
1077
|
+
| Expression-function catalog (`package:` refused, unknown `function:` refused) | `validateExpressionFunctionCatalog` (parser.go) | `validators/expressionFunctions.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1078
|
+
| Expression-function USE (`{{ fn_* }}` must be in the catalog AND declared) | `validateExpressionFunctionUsage` (parser.go) | `validators/expressionFunctions.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1079
|
+
| Loop / for_each / throttle | `validateLoop`, `validateForEach`, `validateThrottle` | `validators/loopForEachThrottle.ts` | `validate.test.ts` |
|
|
1080
|
+
| Loop sub-step id that shadows a loop-result summary field (`loop_sub_step_id_reserved`, warning) | `validateLoopSubStepIDCollision` (validation.loopbody.go) | `validators/loopForEachThrottle.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1081
|
+
| Loop sub-step `next:` targets (same-loop only, sentinels, no parallel) | `validateLoopSubStepNext` (parser.go) | `validators/loopForEachThrottle.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1082
|
+
| Orchestrator structure + campaign requires orchestrator | `validateOrchestrator`, `validateAndNormalizeCampaign` | `validators/orchestratorCampaign.ts` | `validate.test.ts` |
|
|
1083
|
+
| Campaign `max_credits_per_child` (decoded as int64, >= 0), `child_flows` | `CampaignConfig.Validate` (domain.campaign.go) | `validators/orchestratorCampaign.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1084
|
+
| `tool_discovery` vocabulary, mock scenario `delay`, empty `output:` entry | `validateToolDiscoveryVocabulary`, `validateMockScenarioDelays`, `validateOutputParameters` (parser.go) | `validators/saveDoor.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1085
|
+
| `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` |
|
|
1086
|
+
| 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` |
|
|
1087
|
+
| 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` |
|
|
1088
|
+
| 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` |
|
|
1089
|
+
| Orchestrator `.exons` frontmatter: spec, provider, `requirements.resources`, `tools.allow` withholds | `validateOrchestrator` (parser.go), `validateOrchestratorToolAllowWithholds` | `validators/exons.ts` | `v0150-save-door.test.ts`, `conformance.test.ts` |
|
|
1090
|
+
| Credential bindings (`stored/...`, inject_as, exclusivity) | `validateStepCredentialBindings` | `validators/credentialBindings.ts` | `validate.test.ts` |
|
|
1091
|
+
| `input_schema` definition + ordering lint | `ValidateInputSchemaDefinition`, `LintInputSchemaFieldOrdering` | `validators/inputSchema.ts` | `validate.test.ts` |
|
|
1092
|
+
| Step `output_schema` definition (reuses the input-schema subset) | `ValidateInputSchemaDefinition` (on `step.OutputSchema`, parser.go) | `validators/outputSchema.ts` (+ shared `inputSchema.ts`) | `validate.test.ts` |
|
|
1093
|
+
| `quality_gate:` block (rubric/threshold/on_fail/goto) | `FlowParser.validateQualityGate` (parser.go) | `validators/qualityGate.ts` | `validate.test.ts` |
|
|
1094
|
+
| Processing-operation type + per-operation config keys | `validateProcessingOperationShape` | `validators/processingOperations.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1095
|
+
| Step `max_duration` the engine cannot apply (`step_max_duration_ignored`, warning) | `validateStepMaxDurationIsApplied` (validation.go), `parseStepMaxDuration` (flowengine.steptimeout.go) | `validators/stepMaxDuration.ts` | `validate.test.ts`, `conformance.test.ts` |
|
|
1096
|
+
| An EU-only residency beside an unpinned region-lane `ai://` call (`compliance_residency_unenforced`, warning) | `validateResidencyEnforcement` (validation.residency.go) | `validators/complianceResidency.ts` | `v0200-compliance-residency.test.ts`, `conformance.test.ts` |
|
|
1097
|
+
| Go-template syntax | `validateTemplateExpression` (Parse step) | `template/gotmpl-syntax.ts` + `validators/templates.ts` | `gotmpl-syntax.test.ts`, `validate.test.ts` |
|
|
1098
|
+
| A value piped into `default` (`template_default_piped`; error, warning in a loop body) | `pipedDefaultPipelines` (validation.templatedefault.go) | `template/gotmpl-default.ts` + `validators/templates.ts` | `v0210-template-default-piped.test.ts`, `conformance.test.ts` |
|
|
1058
1099
|
|
|
1059
1100
|
---
|
|
1060
1101
|
|
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.827.0"`).
|
|
120
120
|
|
|
121
121
|
### Options
|
|
122
122
|
|
|
@@ -183,7 +183,7 @@ interface ValidationIssue {
|
|
|
183
183
|
- **`tool_discovery`** — `eager`/`lazy`/`off` on the flow, the orchestrator, and a step `query` (empty and templated values skipped).
|
|
184
184
|
- **Mock scenarios** — each `delay` must be a Go duration (`100ms`, not `100`), judged by its written spelling: `0` saves, `0.0` does not.
|
|
185
185
|
- **`output:`** — no empty-string entries.
|
|
186
|
-
- **Templates** — Go `text/template` **syntax** across query, processing, and conditions.
|
|
186
|
+
- **Templates** — Go `text/template` **syntax** across query, processing, and conditions; and a value piped into `default` (`{{ .x | default "w" }}`, error; warning in a loop body — `default` takes the value first: `{{ default .x "w" }}`).
|
|
187
187
|
|
|
188
188
|
## What it does **not** check
|
|
189
189
|
|
package/dist/cli.js
CHANGED
|
@@ -307,6 +307,17 @@ function isValidGoDuration(input) {
|
|
|
307
307
|
var RESERVED_STEP_ID_CHAR = ".";
|
|
308
308
|
var RESERVED_STEP_ID_ORCHESTRATOR = "orchestrator";
|
|
309
309
|
var DISPLAY_NAME_MAX_LEN = 80;
|
|
310
|
+
var YAML11_BOOL_WORDS = new Set(
|
|
311
|
+
["y", "yes", "on", "n", "no", "off"].flatMap((w) => [
|
|
312
|
+
w,
|
|
313
|
+
w.toUpperCase(),
|
|
314
|
+
w[0].toUpperCase() + w.slice(1)
|
|
315
|
+
])
|
|
316
|
+
);
|
|
317
|
+
function isBoolLike(v) {
|
|
318
|
+
if (v === null || typeof v === "boolean") return true;
|
|
319
|
+
return typeof v === "string" && YAML11_BOOL_WORDS.has(v);
|
|
320
|
+
}
|
|
310
321
|
function validateBasicStructure(flow, issues) {
|
|
311
322
|
if (!issues.nonEmptyStringOf(flow, "aigentflow_version", "")) {
|
|
312
323
|
issues.error({
|
|
@@ -336,6 +347,14 @@ function validateBasicStructure(flow, issues) {
|
|
|
336
347
|
});
|
|
337
348
|
}
|
|
338
349
|
}
|
|
350
|
+
if (flow.strict_templates !== void 0 && !isBoolLike(flow.strict_templates)) {
|
|
351
|
+
issues.error({
|
|
352
|
+
field: "strict_templates",
|
|
353
|
+
message: "strict_templates must be a boolean (true or false)",
|
|
354
|
+
code: "invalid_type",
|
|
355
|
+
suggestion: "Write 'strict_templates: true' or remove the key"
|
|
356
|
+
});
|
|
357
|
+
}
|
|
339
358
|
const start = issues.stringOf(flow, "start", "");
|
|
340
359
|
if (start === null || start === "") {
|
|
341
360
|
issues.error({
|
|
@@ -436,7 +455,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
436
455
|
// src/spec/aigentflow-spec.json
|
|
437
456
|
var aigentflow_spec_default = {
|
|
438
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.",
|
|
439
|
-
specVersion: "2.
|
|
458
|
+
specVersion: "2.827.0",
|
|
440
459
|
inputSchemaVersion: 1,
|
|
441
460
|
executorSchemes: [
|
|
442
461
|
"a2a",
|
|
@@ -460,6 +479,7 @@ var aigentflow_spec_default = {
|
|
|
460
479
|
"geocoding",
|
|
461
480
|
"geovisualize",
|
|
462
481
|
"getmd",
|
|
482
|
+
"here",
|
|
463
483
|
"homeassistant",
|
|
464
484
|
"hosting",
|
|
465
485
|
"http",
|
|
@@ -1275,6 +1295,13 @@ var aigentflow_spec_default = {
|
|
|
1275
1295
|
}
|
|
1276
1296
|
}
|
|
1277
1297
|
},
|
|
1298
|
+
templateDefaultPiped: {
|
|
1299
|
+
$comment: "template_default_piped (ERROR at the top level, WARNING inside a loop body, like template_syntax_error). The template function `default` takes the value FIRST; a pipeline that passes a value into it as a non-first command hands the piped value as the LAST argument, so the fallback is always the result. Flagged: any pipeline (in any action, including if / else if / range / with / template / block conditions, parenthesised sub-pipelines and variable declarations) in which a command whose first word is the bare identifier `default` is not the FIRST command. Never flagged: a field, a variable, a string literal, a comment, `default` as the first command. The message fills %s with the pipeline.",
|
|
1300
|
+
code: "template_default_piped",
|
|
1301
|
+
function: "default",
|
|
1302
|
+
messageFormat: "`default` takes the value FIRST, and a pipe passes the piped value LAST: `%s` is `default <fallback> <value>`, so the fallback is always the result",
|
|
1303
|
+
suggestion: 'write the value first and the fallback second, as a call: `{{ default .x "fallback" }}`'
|
|
1304
|
+
},
|
|
1278
1305
|
complianceResidency: {
|
|
1279
1306
|
$comment: "compliance_residency_unenforced (WARNING, never an error; needs no catalogue). When compliance.residency is non-empty and every code (trimmed, upper-cased) is in euResidencyCodes, each top-level step and loop sub-step whose executor starts with aiProtocol+protocolSeparator, then the orchestrator (provider = its exons execution.provider), warns when its provider (literal, lower-cased; skipped when empty or containing templateActionOpen) is in regionLaneProviders and the call is not pinned. Pinned = query region (orchestrator.region) a non-empty string after trimming; OR enforce_region true (query: bool true or the strings in enforceRegionTrueStrings; orchestrator: bool true); OR the author named the endpoint: query <provider>+baseUrlParamSuffix a non-empty string after trimming, or executor_config.<provider>.base_url non-empty after trimming. The provider segment of ai://<provider>/<operation> exists only when a slash follows it. regionLaneProviders is the set of providers whose adapter routes on `region`; a provider outside it has no region lane and is never warned. Message, suggestion and site formats fill %s and %q (a Go-quoted string) in order; the message's last placeholder is the residency list as written, printed [A B]. Reference: validateResidencyEnforcement (validation.residency.go).",
|
|
1280
1307
|
code: "compliance_residency_unenforced",
|
|
@@ -1638,6 +1665,7 @@ var aigentflow_spec_default = {
|
|
|
1638
1665
|
rate_limit: "FlowRateLimitConfig",
|
|
1639
1666
|
start: "scalar",
|
|
1640
1667
|
steps: "map<StepDefinition>",
|
|
1668
|
+
strict_templates: "scalar",
|
|
1641
1669
|
tags: "list<scalar>",
|
|
1642
1670
|
template: "FlowTemplateMetadata",
|
|
1643
1671
|
tool_discovery: "scalar",
|
|
@@ -2010,6 +2038,12 @@ var EXAMPLES = {
|
|
|
2010
2038
|
})),
|
|
2011
2039
|
codes: aigentflow_spec_default.examples.codes
|
|
2012
2040
|
};
|
|
2041
|
+
var TEMPLATE_DEFAULT_PIPED = {
|
|
2042
|
+
code: aigentflow_spec_default.templateDefaultPiped.code,
|
|
2043
|
+
function: aigentflow_spec_default.templateDefaultPiped.function,
|
|
2044
|
+
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
2045
|
+
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
2046
|
+
};
|
|
2013
2047
|
|
|
2014
2048
|
// src/validators/executors.ts
|
|
2015
2049
|
var SCHEME_SEPARATOR = "://";
|
|
@@ -3973,6 +4007,117 @@ function splitPipeline(body) {
|
|
|
3973
4007
|
return stages;
|
|
3974
4008
|
}
|
|
3975
4009
|
|
|
4010
|
+
// src/template/gotmpl-default.ts
|
|
4011
|
+
var FUNCTION_NAME = "default";
|
|
4012
|
+
var PIPE = "|";
|
|
4013
|
+
var OPEN_PAREN = "(";
|
|
4014
|
+
var CLOSE_PAREN = ")";
|
|
4015
|
+
var MAX_NESTING_DEPTH = 100;
|
|
4016
|
+
var PIPELINE_KEYWORDS = /^(?:else\s+if|else\s+with|if|range|with|template|block)(?![\w$.])/;
|
|
4017
|
+
var NO_PIPELINE = /^(?:define|else|end|break|continue)(?![\w$.])/;
|
|
4018
|
+
var DECLARATION = /^\$\w*(?:\s*,\s*\$\w*)*\s*:?=/;
|
|
4019
|
+
function pipedDefaultPipelines(src) {
|
|
4020
|
+
const { actions, fatal } = lexActions(src);
|
|
4021
|
+
if (fatal) return [];
|
|
4022
|
+
const found = /* @__PURE__ */ new Set();
|
|
4023
|
+
try {
|
|
4024
|
+
for (const action of actions) {
|
|
4025
|
+
if (action.isComment || action.body === "") continue;
|
|
4026
|
+
scanAction(action.body, found);
|
|
4027
|
+
}
|
|
4028
|
+
} catch (err) {
|
|
4029
|
+
if (!(err instanceof RangeError)) throw err;
|
|
4030
|
+
return [];
|
|
4031
|
+
}
|
|
4032
|
+
return [...found].sort();
|
|
4033
|
+
}
|
|
4034
|
+
function scanAction(body, found) {
|
|
4035
|
+
let rest = body;
|
|
4036
|
+
const kw = PIPELINE_KEYWORDS.exec(rest);
|
|
4037
|
+
if (!kw && NO_PIPELINE.test(rest)) return;
|
|
4038
|
+
if (kw) {
|
|
4039
|
+
rest = rest.slice(kw[0].length).trimStart();
|
|
4040
|
+
const keyword = kw[0].replace(/\s+/g, " ");
|
|
4041
|
+
if (keyword === "template" || keyword === "block") {
|
|
4042
|
+
const q2 = rest[0];
|
|
4043
|
+
if (q2 === '"' || q2 === "`") {
|
|
4044
|
+
const end = scanString(rest, 0, q2);
|
|
4045
|
+
if (end === -1) return;
|
|
4046
|
+
rest = rest.slice(end + 1).trimStart();
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
if (rest === "") return;
|
|
4051
|
+
parsePipeline(rest, 0, false, found, 0);
|
|
4052
|
+
}
|
|
4053
|
+
function parsePipeline(src, start, nested, found, depth) {
|
|
4054
|
+
if (depth > MAX_NESTING_DEPTH) return { text: src.slice(start), end: src.length };
|
|
4055
|
+
let i = start;
|
|
4056
|
+
const n = src.length;
|
|
4057
|
+
let decl = "";
|
|
4058
|
+
const decMatch = DECLARATION.exec(src.slice(i));
|
|
4059
|
+
if (decMatch) {
|
|
4060
|
+
decl = decMatch[0].replace(/\s+/g, " ") + " ";
|
|
4061
|
+
i += decMatch[0].length;
|
|
4062
|
+
}
|
|
4063
|
+
const commands = [];
|
|
4064
|
+
let current = [];
|
|
4065
|
+
let firstWords = [];
|
|
4066
|
+
let pipedDefault = false;
|
|
4067
|
+
const pushCommand = () => {
|
|
4068
|
+
if (current.length > 0) {
|
|
4069
|
+
commands.push(current);
|
|
4070
|
+
firstWords.push(current[0]);
|
|
4071
|
+
}
|
|
4072
|
+
current = [];
|
|
4073
|
+
};
|
|
4074
|
+
while (i < n) {
|
|
4075
|
+
const c = src[i];
|
|
4076
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
4077
|
+
i += 1;
|
|
4078
|
+
continue;
|
|
4079
|
+
}
|
|
4080
|
+
if (c === '"' || c === "`" || c === "'") {
|
|
4081
|
+
const end = scanString(src, i, c);
|
|
4082
|
+
const stop = end === -1 ? n - 1 : end;
|
|
4083
|
+
current.push(src.slice(i, stop + 1));
|
|
4084
|
+
i = stop + 1;
|
|
4085
|
+
continue;
|
|
4086
|
+
}
|
|
4087
|
+
if (c === OPEN_PAREN) {
|
|
4088
|
+
const inner = parsePipeline(src, i + 1, true, found, depth + 1);
|
|
4089
|
+
current.push(`(${inner.text})`);
|
|
4090
|
+
i = inner.end;
|
|
4091
|
+
continue;
|
|
4092
|
+
}
|
|
4093
|
+
if (c === CLOSE_PAREN) {
|
|
4094
|
+
if (nested) {
|
|
4095
|
+
i += 1;
|
|
4096
|
+
break;
|
|
4097
|
+
}
|
|
4098
|
+
i += 1;
|
|
4099
|
+
continue;
|
|
4100
|
+
}
|
|
4101
|
+
if (c === PIPE) {
|
|
4102
|
+
pushCommand();
|
|
4103
|
+
i += 1;
|
|
4104
|
+
continue;
|
|
4105
|
+
}
|
|
4106
|
+
let j = i;
|
|
4107
|
+
while (j < n && !/[\s|()"`']/.test(src[j])) j += 1;
|
|
4108
|
+
if (j === i) j = i + 1;
|
|
4109
|
+
current.push(src.slice(i, j));
|
|
4110
|
+
i = j;
|
|
4111
|
+
}
|
|
4112
|
+
pushCommand();
|
|
4113
|
+
firstWords.forEach((word, idx) => {
|
|
4114
|
+
if (idx > 0 && word === FUNCTION_NAME) pipedDefault = true;
|
|
4115
|
+
});
|
|
4116
|
+
const text = decl + commands.map((cmd) => cmd.join(" ")).join(` ${PIPE} `);
|
|
4117
|
+
if (pipedDefault) found.add(text);
|
|
4118
|
+
return { text, end: i };
|
|
4119
|
+
}
|
|
4120
|
+
|
|
3976
4121
|
// src/validators/processingOperations.ts
|
|
3977
4122
|
var PROCESSING_OP_GUARD_KEY = PROCESSING_OPERATIONS.guardKey;
|
|
3978
4123
|
var PROCESSING_PHASES = ["pre_processing", "post_processing"];
|
|
@@ -4119,6 +4264,17 @@ function checkTemplateString(value, field, stepID, issues, stats, opts, demote =
|
|
|
4119
4264
|
});
|
|
4120
4265
|
}
|
|
4121
4266
|
}
|
|
4267
|
+
if (errors.length > 0) return;
|
|
4268
|
+
for (const pipe of pipedDefaultPipelines(value)) {
|
|
4269
|
+
report({
|
|
4270
|
+
field,
|
|
4271
|
+
message: TEMPLATE_DEFAULT_PIPED.messageFormat.replace("%s", () => pipe),
|
|
4272
|
+
code: TEMPLATE_DEFAULT_PIPED.code,
|
|
4273
|
+
stepId: stepID,
|
|
4274
|
+
context: `Template: ${value}`,
|
|
4275
|
+
suggestion: TEMPLATE_DEFAULT_PIPED.suggestion
|
|
4276
|
+
});
|
|
4277
|
+
}
|
|
4122
4278
|
}
|
|
4123
4279
|
function walkStrings2(basePath, value, stepID, issues, stats, opts, check, demote = false) {
|
|
4124
4280
|
if (isString(value)) {
|
package/dist/index.cjs
CHANGED
|
@@ -304,6 +304,17 @@ function isValidGoDuration(input) {
|
|
|
304
304
|
var RESERVED_STEP_ID_CHAR = ".";
|
|
305
305
|
var RESERVED_STEP_ID_ORCHESTRATOR = "orchestrator";
|
|
306
306
|
var DISPLAY_NAME_MAX_LEN = 80;
|
|
307
|
+
var YAML11_BOOL_WORDS = new Set(
|
|
308
|
+
["y", "yes", "on", "n", "no", "off"].flatMap((w) => [
|
|
309
|
+
w,
|
|
310
|
+
w.toUpperCase(),
|
|
311
|
+
w[0].toUpperCase() + w.slice(1)
|
|
312
|
+
])
|
|
313
|
+
);
|
|
314
|
+
function isBoolLike(v) {
|
|
315
|
+
if (v === null || typeof v === "boolean") return true;
|
|
316
|
+
return typeof v === "string" && YAML11_BOOL_WORDS.has(v);
|
|
317
|
+
}
|
|
307
318
|
function validateBasicStructure(flow, issues) {
|
|
308
319
|
if (!issues.nonEmptyStringOf(flow, "aigentflow_version", "")) {
|
|
309
320
|
issues.error({
|
|
@@ -333,6 +344,14 @@ function validateBasicStructure(flow, issues) {
|
|
|
333
344
|
});
|
|
334
345
|
}
|
|
335
346
|
}
|
|
347
|
+
if (flow.strict_templates !== void 0 && !isBoolLike(flow.strict_templates)) {
|
|
348
|
+
issues.error({
|
|
349
|
+
field: "strict_templates",
|
|
350
|
+
message: "strict_templates must be a boolean (true or false)",
|
|
351
|
+
code: "invalid_type",
|
|
352
|
+
suggestion: "Write 'strict_templates: true' or remove the key"
|
|
353
|
+
});
|
|
354
|
+
}
|
|
336
355
|
const start = issues.stringOf(flow, "start", "");
|
|
337
356
|
if (start === null || start === "") {
|
|
338
357
|
issues.error({
|
|
@@ -432,7 +451,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
432
451
|
|
|
433
452
|
// src/spec/aigentflow-spec.json
|
|
434
453
|
var aigentflow_spec_default = {
|
|
435
|
-
specVersion: "2.
|
|
454
|
+
specVersion: "2.827.0",
|
|
436
455
|
inputSchemaVersion: 1,
|
|
437
456
|
executorSchemes: [
|
|
438
457
|
"a2a",
|
|
@@ -456,6 +475,7 @@ var aigentflow_spec_default = {
|
|
|
456
475
|
"geocoding",
|
|
457
476
|
"geovisualize",
|
|
458
477
|
"getmd",
|
|
478
|
+
"here",
|
|
459
479
|
"homeassistant",
|
|
460
480
|
"hosting",
|
|
461
481
|
"http",
|
|
@@ -1257,6 +1277,11 @@ var aigentflow_spec_default = {
|
|
|
1257
1277
|
}
|
|
1258
1278
|
}
|
|
1259
1279
|
},
|
|
1280
|
+
templateDefaultPiped: {
|
|
1281
|
+
code: "template_default_piped",
|
|
1282
|
+
messageFormat: "`default` takes the value FIRST, and a pipe passes the piped value LAST: `%s` is `default <fallback> <value>`, so the fallback is always the result",
|
|
1283
|
+
suggestion: 'write the value first and the fallback second, as a call: `{{ default .x "fallback" }}`'
|
|
1284
|
+
},
|
|
1260
1285
|
complianceResidency: {
|
|
1261
1286
|
code: "compliance_residency_unenforced",
|
|
1262
1287
|
euResidencyCodes: ["EU", "DE", "FR", "IE", "NL", "IT", "ES"],
|
|
@@ -1555,6 +1580,7 @@ var aigentflow_spec_default = {
|
|
|
1555
1580
|
rate_limit: "FlowRateLimitConfig",
|
|
1556
1581
|
start: "scalar",
|
|
1557
1582
|
steps: "map<StepDefinition>",
|
|
1583
|
+
strict_templates: "scalar",
|
|
1558
1584
|
tags: "list<scalar>",
|
|
1559
1585
|
template: "FlowTemplateMetadata",
|
|
1560
1586
|
tool_discovery: "scalar",
|
|
@@ -1916,6 +1942,11 @@ var EXAMPLES = {
|
|
|
1916
1942
|
name: p.name,
|
|
1917
1943
|
re: new RegExp(p.expr, p.flags)
|
|
1918
1944
|
}))};
|
|
1945
|
+
var TEMPLATE_DEFAULT_PIPED = {
|
|
1946
|
+
code: aigentflow_spec_default.templateDefaultPiped.code,
|
|
1947
|
+
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
1948
|
+
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
1949
|
+
};
|
|
1919
1950
|
|
|
1920
1951
|
// src/validators/executors.ts
|
|
1921
1952
|
var SCHEME_SEPARATOR = "://";
|
|
@@ -3879,6 +3910,117 @@ function splitPipeline(body) {
|
|
|
3879
3910
|
return stages;
|
|
3880
3911
|
}
|
|
3881
3912
|
|
|
3913
|
+
// src/template/gotmpl-default.ts
|
|
3914
|
+
var FUNCTION_NAME = "default";
|
|
3915
|
+
var PIPE = "|";
|
|
3916
|
+
var OPEN_PAREN = "(";
|
|
3917
|
+
var CLOSE_PAREN = ")";
|
|
3918
|
+
var MAX_NESTING_DEPTH = 100;
|
|
3919
|
+
var PIPELINE_KEYWORDS = /^(?:else\s+if|else\s+with|if|range|with|template|block)(?![\w$.])/;
|
|
3920
|
+
var NO_PIPELINE = /^(?:define|else|end|break|continue)(?![\w$.])/;
|
|
3921
|
+
var DECLARATION = /^\$\w*(?:\s*,\s*\$\w*)*\s*:?=/;
|
|
3922
|
+
function pipedDefaultPipelines(src) {
|
|
3923
|
+
const { actions, fatal } = lexActions(src);
|
|
3924
|
+
if (fatal) return [];
|
|
3925
|
+
const found = /* @__PURE__ */ new Set();
|
|
3926
|
+
try {
|
|
3927
|
+
for (const action of actions) {
|
|
3928
|
+
if (action.isComment || action.body === "") continue;
|
|
3929
|
+
scanAction(action.body, found);
|
|
3930
|
+
}
|
|
3931
|
+
} catch (err) {
|
|
3932
|
+
if (!(err instanceof RangeError)) throw err;
|
|
3933
|
+
return [];
|
|
3934
|
+
}
|
|
3935
|
+
return [...found].sort();
|
|
3936
|
+
}
|
|
3937
|
+
function scanAction(body, found) {
|
|
3938
|
+
let rest = body;
|
|
3939
|
+
const kw = PIPELINE_KEYWORDS.exec(rest);
|
|
3940
|
+
if (!kw && NO_PIPELINE.test(rest)) return;
|
|
3941
|
+
if (kw) {
|
|
3942
|
+
rest = rest.slice(kw[0].length).trimStart();
|
|
3943
|
+
const keyword = kw[0].replace(/\s+/g, " ");
|
|
3944
|
+
if (keyword === "template" || keyword === "block") {
|
|
3945
|
+
const q2 = rest[0];
|
|
3946
|
+
if (q2 === '"' || q2 === "`") {
|
|
3947
|
+
const end = scanString(rest, 0, q2);
|
|
3948
|
+
if (end === -1) return;
|
|
3949
|
+
rest = rest.slice(end + 1).trimStart();
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
if (rest === "") return;
|
|
3954
|
+
parsePipeline(rest, 0, false, found, 0);
|
|
3955
|
+
}
|
|
3956
|
+
function parsePipeline(src, start, nested, found, depth) {
|
|
3957
|
+
if (depth > MAX_NESTING_DEPTH) return { text: src.slice(start), end: src.length };
|
|
3958
|
+
let i = start;
|
|
3959
|
+
const n = src.length;
|
|
3960
|
+
let decl = "";
|
|
3961
|
+
const decMatch = DECLARATION.exec(src.slice(i));
|
|
3962
|
+
if (decMatch) {
|
|
3963
|
+
decl = decMatch[0].replace(/\s+/g, " ") + " ";
|
|
3964
|
+
i += decMatch[0].length;
|
|
3965
|
+
}
|
|
3966
|
+
const commands = [];
|
|
3967
|
+
let current = [];
|
|
3968
|
+
let firstWords = [];
|
|
3969
|
+
let pipedDefault = false;
|
|
3970
|
+
const pushCommand = () => {
|
|
3971
|
+
if (current.length > 0) {
|
|
3972
|
+
commands.push(current);
|
|
3973
|
+
firstWords.push(current[0]);
|
|
3974
|
+
}
|
|
3975
|
+
current = [];
|
|
3976
|
+
};
|
|
3977
|
+
while (i < n) {
|
|
3978
|
+
const c = src[i];
|
|
3979
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
3980
|
+
i += 1;
|
|
3981
|
+
continue;
|
|
3982
|
+
}
|
|
3983
|
+
if (c === '"' || c === "`" || c === "'") {
|
|
3984
|
+
const end = scanString(src, i, c);
|
|
3985
|
+
const stop = end === -1 ? n - 1 : end;
|
|
3986
|
+
current.push(src.slice(i, stop + 1));
|
|
3987
|
+
i = stop + 1;
|
|
3988
|
+
continue;
|
|
3989
|
+
}
|
|
3990
|
+
if (c === OPEN_PAREN) {
|
|
3991
|
+
const inner = parsePipeline(src, i + 1, true, found, depth + 1);
|
|
3992
|
+
current.push(`(${inner.text})`);
|
|
3993
|
+
i = inner.end;
|
|
3994
|
+
continue;
|
|
3995
|
+
}
|
|
3996
|
+
if (c === CLOSE_PAREN) {
|
|
3997
|
+
if (nested) {
|
|
3998
|
+
i += 1;
|
|
3999
|
+
break;
|
|
4000
|
+
}
|
|
4001
|
+
i += 1;
|
|
4002
|
+
continue;
|
|
4003
|
+
}
|
|
4004
|
+
if (c === PIPE) {
|
|
4005
|
+
pushCommand();
|
|
4006
|
+
i += 1;
|
|
4007
|
+
continue;
|
|
4008
|
+
}
|
|
4009
|
+
let j = i;
|
|
4010
|
+
while (j < n && !/[\s|()"`']/.test(src[j])) j += 1;
|
|
4011
|
+
if (j === i) j = i + 1;
|
|
4012
|
+
current.push(src.slice(i, j));
|
|
4013
|
+
i = j;
|
|
4014
|
+
}
|
|
4015
|
+
pushCommand();
|
|
4016
|
+
firstWords.forEach((word, idx) => {
|
|
4017
|
+
if (idx > 0 && word === FUNCTION_NAME) pipedDefault = true;
|
|
4018
|
+
});
|
|
4019
|
+
const text = decl + commands.map((cmd) => cmd.join(" ")).join(` ${PIPE} `);
|
|
4020
|
+
if (pipedDefault) found.add(text);
|
|
4021
|
+
return { text, end: i };
|
|
4022
|
+
}
|
|
4023
|
+
|
|
3882
4024
|
// src/validators/processingOperations.ts
|
|
3883
4025
|
var PROCESSING_OP_GUARD_KEY = PROCESSING_OPERATIONS.guardKey;
|
|
3884
4026
|
var PROCESSING_PHASES = ["pre_processing", "post_processing"];
|
|
@@ -4025,6 +4167,17 @@ function checkTemplateString(value, field, stepID, issues, stats, opts, demote =
|
|
|
4025
4167
|
});
|
|
4026
4168
|
}
|
|
4027
4169
|
}
|
|
4170
|
+
if (errors.length > 0) return;
|
|
4171
|
+
for (const pipe of pipedDefaultPipelines(value)) {
|
|
4172
|
+
report({
|
|
4173
|
+
field,
|
|
4174
|
+
message: TEMPLATE_DEFAULT_PIPED.messageFormat.replace("%s", () => pipe),
|
|
4175
|
+
code: TEMPLATE_DEFAULT_PIPED.code,
|
|
4176
|
+
stepId: stepID,
|
|
4177
|
+
context: `Template: ${value}`,
|
|
4178
|
+
suggestion: TEMPLATE_DEFAULT_PIPED.suggestion
|
|
4179
|
+
});
|
|
4180
|
+
}
|
|
4028
4181
|
}
|
|
4029
4182
|
function walkStrings2(basePath, value, stepID, issues, stats, opts, check, demote = false) {
|
|
4030
4183
|
if (isString(value)) {
|