@principal-ai/principal-view-core 0.22.1 → 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/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +1 -1
- package/dist/node.js.map +1 -1
- package/dist/storyboard/builder.js +2 -2
- package/dist/storyboard/builder.js.map +1 -1
- package/dist/workflow/example.d.ts.map +1 -1
- package/dist/workflow/example.js +6 -23
- package/dist/workflow/example.js.map +1 -1
- package/dist/workflow/index.d.ts +2 -2
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +1 -1
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/scenario-matcher.d.ts +11 -27
- package/dist/workflow/scenario-matcher.d.ts.map +1 -1
- package/dist/workflow/scenario-matcher.js +26 -123
- package/dist/workflow/scenario-matcher.js.map +1 -1
- package/dist/workflow/types.d.ts +4 -42
- package/dist/workflow/types.d.ts.map +1 -1
- package/dist/workflow/validator.d.ts +34 -2
- package/dist/workflow/validator.d.ts.map +1 -1
- package/dist/workflow/validator.js +199 -116
- package/dist/workflow/validator.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -4
- package/src/node.ts +1 -4
- package/src/storyboard/builder.ts +2 -2
- package/src/workflow/__tests__/validator.test.ts +691 -107
- package/src/workflow/example.ts +6 -23
- package/src/workflow/index.ts +1 -3
- package/src/workflow/scenario-matcher.ts +32 -145
- package/src/workflow/types.ts +6 -61
- package/src/workflow/validator.ts +222 -124
package/dist/workflow/types.d.ts
CHANGED
|
@@ -50,6 +50,8 @@ export interface WorkflowTemplate {
|
|
|
50
50
|
name: string;
|
|
51
51
|
/** Purpose of this workflow view */
|
|
52
52
|
description: string;
|
|
53
|
+
/** Exact span name this workflow applies to */
|
|
54
|
+
spanPattern: string;
|
|
53
55
|
/** How to structure the workflow */
|
|
54
56
|
mode: WorkflowMode;
|
|
55
57
|
/** Scenario selection strategy */
|
|
@@ -70,8 +72,8 @@ export type WorkflowMode = 'span-tree' | 'timeline';
|
|
|
70
72
|
/**
|
|
71
73
|
* Workflow scenario definition
|
|
72
74
|
*
|
|
73
|
-
* Scenarios are mutually exclusive workflow templates
|
|
74
|
-
*
|
|
75
|
+
* Scenarios are mutually exclusive workflow templates.
|
|
76
|
+
* Required events are automatically derived from template.events keys.
|
|
75
77
|
*/
|
|
76
78
|
export interface WorkflowScenario {
|
|
77
79
|
/** Unique scenario identifier (kebab-case) */
|
|
@@ -80,49 +82,9 @@ export interface WorkflowScenario {
|
|
|
80
82
|
priority: number;
|
|
81
83
|
/** What this scenario represents */
|
|
82
84
|
description: string;
|
|
83
|
-
/** Conditions that must be met for this scenario to match */
|
|
84
|
-
condition: ScenarioCondition;
|
|
85
85
|
/** Workflow template content */
|
|
86
86
|
template: ScenarioTemplate;
|
|
87
87
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Scenario matching conditions
|
|
90
|
-
*/
|
|
91
|
-
export interface ScenarioCondition {
|
|
92
|
-
/** Must have these events (supports glob patterns like "*.error") */
|
|
93
|
-
requires?: string[];
|
|
94
|
-
/** Must NOT have these events (supports glob patterns) */
|
|
95
|
-
excludes?: string[];
|
|
96
|
-
/** Assertions on event attributes */
|
|
97
|
-
assertions?: Record<string, Assertion>;
|
|
98
|
-
/** Always matches (use as fallback) */
|
|
99
|
-
default?: boolean;
|
|
100
|
-
/** Match if ANY requires condition met (instead of ALL) */
|
|
101
|
-
any?: boolean;
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Attribute assertion operators
|
|
105
|
-
*/
|
|
106
|
-
export interface Assertion {
|
|
107
|
-
/** Greater than */
|
|
108
|
-
$gt?: number;
|
|
109
|
-
/** Greater than or equal */
|
|
110
|
-
$gte?: number;
|
|
111
|
-
/** Less than */
|
|
112
|
-
$lt?: number;
|
|
113
|
-
/** Less than or equal */
|
|
114
|
-
$lte?: number;
|
|
115
|
-
/** Equal to */
|
|
116
|
-
$eq?: string | number | boolean;
|
|
117
|
-
/** Not equal to */
|
|
118
|
-
$ne?: string | number | boolean;
|
|
119
|
-
/** Attribute exists/doesn't exist */
|
|
120
|
-
$exists?: boolean;
|
|
121
|
-
/** Value in array */
|
|
122
|
-
$in?: (string | number | boolean)[];
|
|
123
|
-
/** Value not in array */
|
|
124
|
-
$nin?: (string | number | boolean)[];
|
|
125
|
-
}
|
|
126
88
|
/**
|
|
127
89
|
* Scenario template content
|
|
128
90
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvE;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IAEb,sBAAsB;IACtB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAE3B,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAEjC,uBAAuB;IACvB,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAExC,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAE/B,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAEhB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IAEf,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IAEb,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IAGpB,oCAAoC;IACpC,IAAI,EAAE,YAAY,CAAC;IAEnB,kCAAkC;IAClC,iBAAiB,EAAE,aAAa,GAAG,QAAQ,CAAC;IAG5C,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAG5B,4CAA4C;IAC5C,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAG9B,qCAAqC;IACrC,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,UAAU,CAAC;AAEf;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAE/B,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IAEX,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IAEjB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IAGpB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEvE;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IAEb,sBAAsB;IACtB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAE3B,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAEjC,uBAAuB;IACvB,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAExC,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAE/B,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAEhB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IAEf,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IAEb,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IAGpB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IAGpB,oCAAoC;IACpC,IAAI,EAAE,YAAY,CAAC;IAEnB,kCAAkC;IAClC,iBAAiB,EAAE,aAAa,GAAG,QAAQ,CAAC;IAG5C,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAG5B,4CAA4C;IAC5C,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAG9B,qCAAqC;IACrC,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,UAAU,CAAC;AAEf;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAE/B,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IAEX,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IAEjB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IAGpB,gCAAgC;IAChC,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC,mDAAmD;IACnD,IAAI,CAAC,EAAE,YAAY,CAAC;IAEpB,0BAA0B;IAC1B,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC;IAErC,mBAAmB;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,uDAAuD;IACvD,QAAQ,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,6BAA6B;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,iDAAiD;IACjD,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,gDAAgD;IAChD,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,2FAA2F;IAC3F,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,QAAQ,EAAE,gBAAgB,CAAC;IAE3B,wBAAwB;IACxB,QAAQ,EAAE,gBAAgB,CAAC;IAE3B,2BAA2B;IAC3B,MAAM,EAAE,SAAS,EAAE,CAAC;IAEpB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAE1B,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAErC,yBAAyB;IACzB,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,qBAAqB;IACrB,IAAI,EAAE,SAAS,CAAC;IAEhB,kBAAkB;IAClB,QAAQ,EAAE,YAAY,EAAE,CAAC;IAEzB,mDAAmD;IACnD,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;IAEnB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IAEb,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IAEnB,mCAAmC;IACnC,QAAQ,EAAE;QACR,iCAAiC;QACjC,UAAU,EAAE,MAAM,CAAC;QAEnB,sBAAsB;QACtB,SAAS,EAAE,MAAM,CAAC;QAElB,qBAAqB;QACrB,QAAQ,EAAE,MAAM,CAAC;QAEjB,2BAA2B;QAC3B,SAAS,CAAC,EAAE;YACV,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;YACvB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;SACtB,CAAC;QAEF,8CAA8C;QAC9C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAElB,gDAAgD;QAChD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,2BAA2B;IAC3B,QAAQ,EAAE,gBAAgB,CAAC;IAE3B,qDAAqD;IACrD,SAAS,EAAE,OAAO,CAAC;IAEnB,yDAAyD;IACzD,mBAAmB,EAAE,gBAAgB,EAAE,CAAC;IAExC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC"}
|
|
@@ -80,6 +80,19 @@ export declare class WorkflowValidator {
|
|
|
80
80
|
* Validate a workflow template
|
|
81
81
|
*/
|
|
82
82
|
validate(context: WorkflowValidationContext): Promise<WorkflowValidationResult>;
|
|
83
|
+
/**
|
|
84
|
+
* Validate multiple workflows for duplicate spanPatterns
|
|
85
|
+
*
|
|
86
|
+
* This is a CLI-level validation that checks for conflicts across workflows.
|
|
87
|
+
* Call this method after validating individual workflows.
|
|
88
|
+
*
|
|
89
|
+
* @param workflows Array of workflow templates with their file paths
|
|
90
|
+
* @returns Array of violations for duplicate spanPatterns
|
|
91
|
+
*/
|
|
92
|
+
static validateSpanPatterns(workflows: Array<{
|
|
93
|
+
workflow: WorkflowTemplate;
|
|
94
|
+
workflowPath: string;
|
|
95
|
+
}>): WorkflowViolation[];
|
|
83
96
|
/**
|
|
84
97
|
* Check schema validity (required fields, valid values)
|
|
85
98
|
*/
|
|
@@ -88,6 +101,10 @@ export declare class WorkflowValidator {
|
|
|
88
101
|
* Check that the referenced canvas file exists
|
|
89
102
|
*/
|
|
90
103
|
private checkCanvasExists;
|
|
104
|
+
/**
|
|
105
|
+
* Check for deprecated fields (condition, condition.requires, etc.)
|
|
106
|
+
*/
|
|
107
|
+
private checkDeprecatedFields;
|
|
91
108
|
/**
|
|
92
109
|
* Check that events referenced in templates exist in the canvas
|
|
93
110
|
*
|
|
@@ -101,9 +118,24 @@ export declare class WorkflowValidator {
|
|
|
101
118
|
*/
|
|
102
119
|
private checkScenarios;
|
|
103
120
|
/**
|
|
104
|
-
* Check
|
|
121
|
+
* Check for subset relationships between scenarios
|
|
122
|
+
*
|
|
123
|
+
* Ensures no scenario's event set is a strict subset of another scenario's event set.
|
|
124
|
+
* This prevents ambiguous matching where a trace could match multiple scenarios.
|
|
125
|
+
*/
|
|
126
|
+
private checkScenarioSubsets;
|
|
127
|
+
/**
|
|
128
|
+
* Check if set A is a strict subset of set B
|
|
129
|
+
*
|
|
130
|
+
* A is a strict subset of B if:
|
|
131
|
+
* 1. All elements of A are in B
|
|
132
|
+
* 2. A has fewer elements than B
|
|
133
|
+
*/
|
|
134
|
+
private isStrictSubset;
|
|
135
|
+
/**
|
|
136
|
+
* Generate helpful suggestion for fixing subset relationship
|
|
105
137
|
*/
|
|
106
|
-
private
|
|
138
|
+
private generateSubsetFixSuggestion;
|
|
107
139
|
/**
|
|
108
140
|
* Check that template uses valid fields (not legacy format)
|
|
109
141
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/workflow/validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/workflow/validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAoB,MAAM,SAAS,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAO/D,MAAM,WAAW,yBAAyB;IACxC,4CAA4C;IAC5C,QAAQ,EAAE,gBAAgB,CAAC;IAE3B,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IAErB,iCAAiC;IACjC,MAAM,CAAC,EAAE,cAAc,CAAC;IAExB,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IAEjB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,oEAAoE;IACpE,aAAa,CAAC,EAAE;QACd,mDAAmD;QACnD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,uCAAuC;QACvC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KACvD,CAAC;IAEF;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAEhC;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IAEf,qBAAqB;IACrB,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAE3B,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IAEb,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAEhB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IAEf,4BAA4B;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,2BAA2B;IAC3B,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAEhC,sBAAsB;IACtB,UAAU,EAAE,MAAM,CAAC;IAEnB,wBAAwB;IACxB,YAAY,EAAE,MAAM,CAAC;IAErB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;CACtB;AAMD,qBAAa,iBAAiB;IAC5B;;OAEG;IACG,QAAQ,CACZ,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,wBAAwB,CAAC;IAgCpC;;;;;;;;OAQG;IACH,MAAM,CAAC,oBAAoB,CACzB,SAAS,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,gBAAgB,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,GACrE,iBAAiB,EAAE;IAuCtB;;OAEG;IACH,OAAO,CAAC,WAAW;IAqKnB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA4BzB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqD7B;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAiH5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAwGtB;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAgD5B;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAgBtB;;OAEG;IACH,OAAO,CAAC,2BAA2B;IA6BnC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAgG9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA2B5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4E3B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAyF9B;;;;;;;OAOG;IACH,OAAO,CAAC,wBAAwB;IAkEhC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kBAAkB;IA6D1B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAsE9B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2B7B;;;;;;;;OAQG;IACH,OAAO,CAAC,qBAAqB;IAmC7B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA4B9B;;;;;OAKG;IACH,OAAO,CAAC,8BAA8B;IA+GtC;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAiBhC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAgEhC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAuC/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA4BxB;;OAEG;IACH,OAAO,CAAC,aAAa;IAKrB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAc3B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,0BAA0B;IAgClC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,+BAA+B;IA4BvC;;;;;OAKG;IACH,OAAO,CAAC,YAAY;CAQrB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,iBAAiB,CAE3D"}
|
|
@@ -16,7 +16,9 @@ export class WorkflowValidator {
|
|
|
16
16
|
// Run all validation rules
|
|
17
17
|
violations.push(...this.checkSchema(context));
|
|
18
18
|
violations.push(...this.checkCanvasExists(context));
|
|
19
|
+
violations.push(...this.checkDeprecatedFields(context));
|
|
19
20
|
violations.push(...this.checkScenarios(context));
|
|
21
|
+
violations.push(...this.checkScenarioSubsets(context));
|
|
20
22
|
// Check event name syntax BEFORE checking event references
|
|
21
23
|
// This ensures we catch unsupported syntax before trying to match events
|
|
22
24
|
violations.push(...this.checkEventNameSyntax(context));
|
|
@@ -34,6 +36,49 @@ export class WorkflowValidator {
|
|
|
34
36
|
}
|
|
35
37
|
return this.aggregateResults(violations);
|
|
36
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate multiple workflows for duplicate spanPatterns
|
|
41
|
+
*
|
|
42
|
+
* This is a CLI-level validation that checks for conflicts across workflows.
|
|
43
|
+
* Call this method after validating individual workflows.
|
|
44
|
+
*
|
|
45
|
+
* @param workflows Array of workflow templates with their file paths
|
|
46
|
+
* @returns Array of violations for duplicate spanPatterns
|
|
47
|
+
*/
|
|
48
|
+
static validateSpanPatterns(workflows) {
|
|
49
|
+
const violations = [];
|
|
50
|
+
const spanPatternMap = new Map(); // spanPattern -> workflow paths
|
|
51
|
+
// Collect all spanPatterns
|
|
52
|
+
for (const { workflow, workflowPath } of workflows) {
|
|
53
|
+
if (!workflow.spanPattern) {
|
|
54
|
+
continue; // Will be caught by individual validation
|
|
55
|
+
}
|
|
56
|
+
if (!spanPatternMap.has(workflow.spanPattern)) {
|
|
57
|
+
spanPatternMap.set(workflow.spanPattern, []);
|
|
58
|
+
}
|
|
59
|
+
spanPatternMap.get(workflow.spanPattern).push(workflowPath);
|
|
60
|
+
}
|
|
61
|
+
// Check for duplicates
|
|
62
|
+
for (const [spanPattern, paths] of spanPatternMap.entries()) {
|
|
63
|
+
if (paths.length > 1) {
|
|
64
|
+
// Duplicate spanPattern found
|
|
65
|
+
for (const path of paths) {
|
|
66
|
+
const otherPaths = paths.filter(p => p !== path);
|
|
67
|
+
violations.push({
|
|
68
|
+
ruleId: 'workflow-span-pattern-duplicate',
|
|
69
|
+
severity: 'error',
|
|
70
|
+
file: path,
|
|
71
|
+
path: 'spanPattern',
|
|
72
|
+
message: `Duplicate spanPattern "${spanPattern}" found in multiple workflows`,
|
|
73
|
+
impact: 'Multiple workflows cannot match the same span - this creates ambiguous workflow selection',
|
|
74
|
+
suggestion: `This spanPattern is also used in:\n${otherPaths.map(p => ` - ${p}`).join('\n')}\n\nEach workflow must have a unique spanPattern. Consider:\n - Using different span names (e.g., "payment.authorize" vs "payment.refund")\n - Merging these workflows into one with multiple scenarios`,
|
|
75
|
+
fixable: false,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return violations;
|
|
81
|
+
}
|
|
37
82
|
/**
|
|
38
83
|
* Check schema validity (required fields, valid values)
|
|
39
84
|
*/
|
|
@@ -104,6 +149,31 @@ export class WorkflowValidator {
|
|
|
104
149
|
fixable: false,
|
|
105
150
|
});
|
|
106
151
|
}
|
|
152
|
+
// Check spanPattern
|
|
153
|
+
if (!workflow.spanPattern) {
|
|
154
|
+
violations.push({
|
|
155
|
+
ruleId: 'workflow-schema-valid',
|
|
156
|
+
severity: 'error',
|
|
157
|
+
file: workflowPath,
|
|
158
|
+
path: 'spanPattern',
|
|
159
|
+
message: 'Missing required field "spanPattern"',
|
|
160
|
+
impact: 'Cannot determine which spans this workflow applies to',
|
|
161
|
+
suggestion: 'Add a spanPattern field specifying the exact span name (e.g., "payment.authorize")',
|
|
162
|
+
fixable: false,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
else if (typeof workflow.spanPattern !== 'string' || workflow.spanPattern.trim() === '') {
|
|
166
|
+
violations.push({
|
|
167
|
+
ruleId: 'workflow-schema-valid',
|
|
168
|
+
severity: 'error',
|
|
169
|
+
file: workflowPath,
|
|
170
|
+
path: 'spanPattern',
|
|
171
|
+
message: 'spanPattern must be a non-empty string',
|
|
172
|
+
impact: 'Cannot match spans with invalid pattern',
|
|
173
|
+
suggestion: 'Provide a valid span name (e.g., "payment.authorize", "checkout.process")',
|
|
174
|
+
fixable: false,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
107
177
|
// Check mode
|
|
108
178
|
const validModes = ['span-tree', 'timeline'];
|
|
109
179
|
if (!workflow.mode) {
|
|
@@ -197,6 +267,60 @@ export class WorkflowValidator {
|
|
|
197
267
|
}
|
|
198
268
|
return violations;
|
|
199
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* Check for deprecated fields (condition, condition.requires, etc.)
|
|
272
|
+
*/
|
|
273
|
+
checkDeprecatedFields(context) {
|
|
274
|
+
const violations = [];
|
|
275
|
+
const { workflow, workflowPath } = context;
|
|
276
|
+
if (!workflow.scenarios || workflow.scenarios.length === 0) {
|
|
277
|
+
return violations;
|
|
278
|
+
}
|
|
279
|
+
workflow.scenarios.forEach((scenario, idx) => {
|
|
280
|
+
// Check if scenario has a 'condition' field
|
|
281
|
+
const scenarioAny = scenario;
|
|
282
|
+
if (scenarioAny.condition) {
|
|
283
|
+
const condition = scenarioAny.condition;
|
|
284
|
+
// Build list of what was in condition
|
|
285
|
+
const conditionFields = [];
|
|
286
|
+
if (condition.requires)
|
|
287
|
+
conditionFields.push('requires');
|
|
288
|
+
if (condition.excludes)
|
|
289
|
+
conditionFields.push('excludes');
|
|
290
|
+
if (condition.assertions)
|
|
291
|
+
conditionFields.push('assertions');
|
|
292
|
+
if (condition.default)
|
|
293
|
+
conditionFields.push('default');
|
|
294
|
+
if (condition.any)
|
|
295
|
+
conditionFields.push('any');
|
|
296
|
+
const fieldsDescription = conditionFields.length > 0
|
|
297
|
+
? ` (${conditionFields.join(', ')})`
|
|
298
|
+
: '';
|
|
299
|
+
violations.push({
|
|
300
|
+
ruleId: 'workflow-deprecated-condition',
|
|
301
|
+
severity: 'error',
|
|
302
|
+
file: workflowPath,
|
|
303
|
+
path: `scenarios[${idx}].condition`,
|
|
304
|
+
message: `The "condition" field is no longer supported${fieldsDescription}`,
|
|
305
|
+
impact: 'Required events are now automatically derived from template.events keys',
|
|
306
|
+
suggestion: `Remove the "condition" field. Required events are inferred from template.events.\n\n` +
|
|
307
|
+
`Migration:\n` +
|
|
308
|
+
` Before:\n` +
|
|
309
|
+
` {\n` +
|
|
310
|
+
` "condition": { "requires": ["event.a", "event.b"] },\n` +
|
|
311
|
+
` "template": { "events": { "event.a": "...", "event.b": "..." }}\n` +
|
|
312
|
+
` }\n\n` +
|
|
313
|
+
` After:\n` +
|
|
314
|
+
` {\n` +
|
|
315
|
+
` "template": { "events": { "event.a": "...", "event.b": "..." }}\n` +
|
|
316
|
+
` }\n\n` +
|
|
317
|
+
`Simply remove the "condition" field - the same events should already be defined in "template.events".`,
|
|
318
|
+
fixable: true,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
return violations;
|
|
323
|
+
}
|
|
200
324
|
/**
|
|
201
325
|
* Check that events referenced in templates exist in the canvas
|
|
202
326
|
*
|
|
@@ -237,18 +361,9 @@ export class WorkflowValidator {
|
|
|
237
361
|
canvasEvents.add(node.pv.eventRef);
|
|
238
362
|
}
|
|
239
363
|
}
|
|
240
|
-
// Extract all event names from workflow scenarios
|
|
364
|
+
// Extract all event names from workflow scenarios (from template.events)
|
|
241
365
|
const workflowEvents = new Set();
|
|
242
366
|
for (const scenario of workflow.scenarios) {
|
|
243
|
-
// From condition.requires
|
|
244
|
-
if (scenario.condition?.requires) {
|
|
245
|
-
for (const eventPattern of scenario.condition.requires) {
|
|
246
|
-
// Skip wildcard patterns for now
|
|
247
|
-
if (!eventPattern.includes('*')) {
|
|
248
|
-
workflowEvents.add(eventPattern);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
367
|
// From template.events
|
|
253
368
|
if (scenario.template?.events) {
|
|
254
369
|
for (const eventName of Object.keys(scenario.template.events)) {
|
|
@@ -327,7 +442,6 @@ export class WorkflowValidator {
|
|
|
327
442
|
}
|
|
328
443
|
const scenarioIds = new Set();
|
|
329
444
|
const priorities = new Set();
|
|
330
|
-
let hasDefault = false;
|
|
331
445
|
workflow.scenarios.forEach((scenario, idx) => {
|
|
332
446
|
// Check for required fields
|
|
333
447
|
if (!scenario.id) {
|
|
@@ -399,27 +513,6 @@ export class WorkflowValidator {
|
|
|
399
513
|
}
|
|
400
514
|
priorities.add(scenario.priority);
|
|
401
515
|
}
|
|
402
|
-
// Check for default scenario
|
|
403
|
-
if (scenario.condition?.default === true) {
|
|
404
|
-
hasDefault = true;
|
|
405
|
-
}
|
|
406
|
-
// Check condition
|
|
407
|
-
if (!scenario.condition) {
|
|
408
|
-
violations.push({
|
|
409
|
-
ruleId: 'workflow-scenario-valid',
|
|
410
|
-
severity: 'error',
|
|
411
|
-
file: workflowPath,
|
|
412
|
-
path: `scenarios[${idx}].condition`,
|
|
413
|
-
message: 'Scenario is missing required "condition" field',
|
|
414
|
-
impact: 'Cannot determine when to use this scenario',
|
|
415
|
-
suggestion: 'Add a condition or set default: true',
|
|
416
|
-
fixable: false,
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
// Validate condition structure
|
|
421
|
-
violations.push(...this.checkConditionStructure(scenario.condition, workflowPath, idx));
|
|
422
|
-
}
|
|
423
516
|
// Check template
|
|
424
517
|
if (!scenario.template) {
|
|
425
518
|
violations.push({
|
|
@@ -438,69 +531,48 @@ export class WorkflowValidator {
|
|
|
438
531
|
violations.push(...this.checkTemplateStructure(scenario.template, workflowPath, idx));
|
|
439
532
|
}
|
|
440
533
|
});
|
|
441
|
-
// Ensure at least one default scenario exists
|
|
442
|
-
if (!hasDefault) {
|
|
443
|
-
violations.push({
|
|
444
|
-
ruleId: 'workflow-scenario-valid',
|
|
445
|
-
severity: 'error',
|
|
446
|
-
file: workflowPath,
|
|
447
|
-
path: 'scenarios',
|
|
448
|
-
message: 'No default scenario defined',
|
|
449
|
-
impact: 'Workflow rendering may fail if no scenario matches',
|
|
450
|
-
suggestion: 'Add a scenario with "condition.default: true" as a fallback',
|
|
451
|
-
fixable: false,
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
534
|
return violations;
|
|
455
535
|
}
|
|
456
536
|
/**
|
|
457
|
-
* Check
|
|
537
|
+
* Check for subset relationships between scenarios
|
|
538
|
+
*
|
|
539
|
+
* Ensures no scenario's event set is a strict subset of another scenario's event set.
|
|
540
|
+
* This prevents ambiguous matching where a trace could match multiple scenarios.
|
|
458
541
|
*/
|
|
459
|
-
|
|
542
|
+
checkScenarioSubsets(context) {
|
|
460
543
|
const violations = [];
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return violations;
|
|
544
|
+
const { workflow, workflowPath } = context;
|
|
545
|
+
if (!workflow.scenarios || workflow.scenarios.length < 2) {
|
|
546
|
+
return violations; // Need at least 2 scenarios to check subsets
|
|
465
547
|
}
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if
|
|
472
|
-
|
|
473
|
-
ruleId: 'workflow-condition-structure',
|
|
474
|
-
severity: 'error',
|
|
475
|
-
file,
|
|
476
|
-
path: `scenarios[${scenarioIdx}].condition.${key}`,
|
|
477
|
-
message: `Invalid condition field "${key}" (legacy format detected)`,
|
|
478
|
-
impact: 'Condition will not work - "event" field is not supported',
|
|
479
|
-
suggestion: 'Use "requires: [...]" array instead of "event: ..." field',
|
|
480
|
-
fixable: false,
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
else if (key === 'attributes') {
|
|
548
|
+
const scenarios = workflow.scenarios;
|
|
549
|
+
for (let i = 0; i < scenarios.length; i++) {
|
|
550
|
+
for (let j = i + 1; j < scenarios.length; j++) {
|
|
551
|
+
const eventsA = new Set(Object.keys(scenarios[i].template?.events || {}));
|
|
552
|
+
const eventsB = new Set(Object.keys(scenarios[j].template?.events || {}));
|
|
553
|
+
// Check if A is a strict subset of B
|
|
554
|
+
if (this.isStrictSubset(eventsA, eventsB)) {
|
|
484
555
|
violations.push({
|
|
485
|
-
ruleId: 'workflow-
|
|
556
|
+
ruleId: 'workflow-scenario-subset',
|
|
486
557
|
severity: 'error',
|
|
487
|
-
file,
|
|
488
|
-
path: `scenarios[${
|
|
489
|
-
message: `
|
|
490
|
-
impact: '
|
|
491
|
-
suggestion:
|
|
558
|
+
file: workflowPath,
|
|
559
|
+
path: `scenarios[${i}]`,
|
|
560
|
+
message: `Scenario "${scenarios[i].id}" is a strict subset of "${scenarios[j].id}"`,
|
|
561
|
+
impact: 'A trace with all events from both scenarios will match both, causing ambiguous scenario selection',
|
|
562
|
+
suggestion: this.generateSubsetFixSuggestion(scenarios[i], scenarios[j]),
|
|
492
563
|
fixable: false,
|
|
493
564
|
});
|
|
494
565
|
}
|
|
495
|
-
|
|
566
|
+
// Check if B is a strict subset of A
|
|
567
|
+
if (this.isStrictSubset(eventsB, eventsA)) {
|
|
496
568
|
violations.push({
|
|
497
|
-
ruleId: 'workflow-
|
|
569
|
+
ruleId: 'workflow-scenario-subset',
|
|
498
570
|
severity: 'error',
|
|
499
|
-
file,
|
|
500
|
-
path: `scenarios[${
|
|
501
|
-
message: `
|
|
502
|
-
impact: '
|
|
503
|
-
suggestion:
|
|
571
|
+
file: workflowPath,
|
|
572
|
+
path: `scenarios[${j}]`,
|
|
573
|
+
message: `Scenario "${scenarios[j].id}" is a strict subset of "${scenarios[i].id}"`,
|
|
574
|
+
impact: 'A trace with all events from both scenarios will match both, causing ambiguous scenario selection',
|
|
575
|
+
suggestion: this.generateSubsetFixSuggestion(scenarios[j], scenarios[i]),
|
|
504
576
|
fixable: false,
|
|
505
577
|
});
|
|
506
578
|
}
|
|
@@ -508,6 +580,51 @@ export class WorkflowValidator {
|
|
|
508
580
|
}
|
|
509
581
|
return violations;
|
|
510
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
* Check if set A is a strict subset of set B
|
|
585
|
+
*
|
|
586
|
+
* A is a strict subset of B if:
|
|
587
|
+
* 1. All elements of A are in B
|
|
588
|
+
* 2. A has fewer elements than B
|
|
589
|
+
*/
|
|
590
|
+
isStrictSubset(A, B) {
|
|
591
|
+
// A must have fewer elements than B
|
|
592
|
+
if (A.size >= B.size) {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
// All elements of A must be in B
|
|
596
|
+
for (const item of A) {
|
|
597
|
+
if (!B.has(item)) {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return true;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Generate helpful suggestion for fixing subset relationship
|
|
605
|
+
*/
|
|
606
|
+
generateSubsetFixSuggestion(subsetScenario, supersetScenario) {
|
|
607
|
+
const subsetEvents = Object.keys(subsetScenario.template?.events || {});
|
|
608
|
+
const supersetEvents = Object.keys(supersetScenario.template?.events || {});
|
|
609
|
+
const extraEvents = supersetEvents.filter(e => !subsetEvents.includes(e));
|
|
610
|
+
return (`Scenario "${subsetScenario.id}" events: [${subsetEvents.join(', ')}]\n` +
|
|
611
|
+
`Scenario "${supersetScenario.id}" events: [${supersetEvents.join(', ')}]\n\n` +
|
|
612
|
+
`Recommended fixes:\n\n` +
|
|
613
|
+
`1. Merge into one scenario with template conditionals:\n` +
|
|
614
|
+
` {\n` +
|
|
615
|
+
` "id": "${subsetScenario.id}",\n` +
|
|
616
|
+
` "template": {\n` +
|
|
617
|
+
` "events": { ${subsetEvents.map(e => `"${e}": "..."`).join(', ')} },\n` +
|
|
618
|
+
` "flow": [\n` +
|
|
619
|
+
` "Base flow steps...",\n` +
|
|
620
|
+
` {{#if ${extraEvents[0]}}}Additional step...{{/if}}\n` +
|
|
621
|
+
` ]\n` +
|
|
622
|
+
` }\n` +
|
|
623
|
+
` }\n\n` +
|
|
624
|
+
`2. Make them mutually exclusive by adding distinguishing events:\n` +
|
|
625
|
+
` - Add an event to "${subsetScenario.id}" that distinguishes it (e.g., "*.timeout", "*.abandoned")\n` +
|
|
626
|
+
` - Or ensure "${supersetScenario.id}" has events that never co-occur with "${subsetScenario.id}"`);
|
|
627
|
+
}
|
|
511
628
|
/**
|
|
512
629
|
* Check that template uses valid fields (not legacy format)
|
|
513
630
|
*/
|
|
@@ -608,40 +725,6 @@ export class WorkflowValidator {
|
|
|
608
725
|
const violations = [];
|
|
609
726
|
const { workflow, workflowPath } = context;
|
|
610
727
|
workflow.scenarios.forEach((scenario, scenarioIdx) => {
|
|
611
|
-
// Check condition.requires
|
|
612
|
-
if (scenario.condition?.requires) {
|
|
613
|
-
scenario.condition.requires.forEach((eventPattern, idx) => {
|
|
614
|
-
if (eventPattern.includes('[') && eventPattern.includes(']')) {
|
|
615
|
-
violations.push({
|
|
616
|
-
ruleId: 'workflow-event-name-syntax',
|
|
617
|
-
severity: 'error',
|
|
618
|
-
file: workflowPath,
|
|
619
|
-
path: `scenarios[${scenarioIdx}].condition.requires[${idx}]`,
|
|
620
|
-
message: `Event name uses unsupported [attribute=value] syntax: "${eventPattern}"`,
|
|
621
|
-
impact: 'Attribute filter syntax is not supported - event will not match',
|
|
622
|
-
suggestion: `Use a distinct event name instead (e.g., "${this.extractBaseEventName(eventPattern)}.${this.extractAttributeValue(eventPattern)}")`,
|
|
623
|
-
fixable: false,
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
// Check condition.excludes
|
|
629
|
-
if (scenario.condition?.excludes) {
|
|
630
|
-
scenario.condition.excludes.forEach((eventPattern, idx) => {
|
|
631
|
-
if (eventPattern.includes('[') && eventPattern.includes(']')) {
|
|
632
|
-
violations.push({
|
|
633
|
-
ruleId: 'workflow-event-name-syntax',
|
|
634
|
-
severity: 'error',
|
|
635
|
-
file: workflowPath,
|
|
636
|
-
path: `scenarios[${scenarioIdx}].condition.excludes[${idx}]`,
|
|
637
|
-
message: `Event name uses unsupported [attribute=value] syntax: "${eventPattern}"`,
|
|
638
|
-
impact: 'Attribute filter syntax is not supported - event will not match',
|
|
639
|
-
suggestion: `Use a distinct event name instead (e.g., "${this.extractBaseEventName(eventPattern)}.${this.extractAttributeValue(eventPattern)}")`,
|
|
640
|
-
fixable: false,
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
728
|
// Check template.events
|
|
646
729
|
if (scenario.template?.events) {
|
|
647
730
|
Object.keys(scenario.template.events).forEach((eventName) => {
|