@synergenius/flow-weaver 0.20.1 → 0.20.3
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/annotation-generator.js +7 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/modify-operation.d.ts +15 -0
- package/dist/api/modify-operation.js +177 -0
- package/dist/api/validate.js +17 -0
- package/dist/ast/types.d.ts +2 -0
- package/dist/chevrotain-parser/node-parser.d.ts +2 -0
- package/dist/chevrotain-parser/node-parser.js +27 -1
- package/dist/chevrotain-parser/tokens.d.ts +1 -0
- package/dist/chevrotain-parser/tokens.js +5 -0
- package/dist/cli/commands/modify.d.ts +29 -0
- package/dist/cli/commands/modify.js +57 -0
- package/dist/cli/flow-weaver.mjs +44885 -44640
- package/dist/cli/index.js +73 -2
- package/dist/cli/templates/workflows/aggregator.js +3 -3
- package/dist/cli/templates/workflows/ai-agent.js +3 -3
- package/dist/cli/templates/workflows/ai-chat.js +5 -4
- package/dist/cli/templates/workflows/ai-rag.js +3 -3
- package/dist/cli/templates/workflows/ai-react.js +12 -12
- package/dist/cli/templates/workflows/conditional.js +3 -3
- package/dist/cli/templates/workflows/error-handler.js +2 -2
- package/dist/cli/templates/workflows/foreach.js +3 -3
- package/dist/cli/templates/workflows/sequential.js +7 -4
- package/dist/cli/templates/workflows/webhook.js +3 -3
- package/dist/generated-version.d.ts +1 -1
- package/dist/generated-version.js +1 -1
- package/dist/jsdoc-parser.d.ts +1 -0
- package/dist/jsdoc-parser.js +4 -3
- package/dist/mcp/tools-pattern.js +1 -180
- package/dist/parser.js +1 -0
- package/dist/validator.js +15 -0
- package/docs/reference/advanced-annotations.md +11 -0
- package/docs/reference/cli-reference.md +118 -1
- package/docs/reference/error-codes.md +1 -1
- package/docs/reference/jsdoc-grammar.md +4 -2
- package/docs/reference/marketplace.md +74 -0
- package/package.json +1 -1
|
@@ -596,6 +596,12 @@ export function generateNodeInstanceTag(instance) {
|
|
|
596
596
|
const tagEntries = instance.config.tags.map(t => t.tooltip ? `"${t.label}" "${t.tooltip}"` : `"${t.label}"`).join(', ');
|
|
597
597
|
tagsAttr = ` [tags: ${tagEntries}]`;
|
|
598
598
|
}
|
|
599
|
+
// Generate [suppress: "CODE", "CODE2"] attribute if present
|
|
600
|
+
let suppressAttr = '';
|
|
601
|
+
if (instance.config?.suppressWarnings?.length) {
|
|
602
|
+
const codes = instance.config.suppressWarnings.map(c => `"${c}"`).join(', ');
|
|
603
|
+
suppressAttr = ` [suppress: ${codes}]`;
|
|
604
|
+
}
|
|
599
605
|
// Generate [size: width height] attribute if present
|
|
600
606
|
let sizeAttr = '';
|
|
601
607
|
if (instance.config?.width !== undefined && instance.config?.height !== undefined) {
|
|
@@ -606,7 +612,7 @@ export function generateNodeInstanceTag(instance) {
|
|
|
606
612
|
if (instance.config?.x !== undefined && instance.config?.y !== undefined) {
|
|
607
613
|
positionAttr = ` [position: ${Math.round(instance.config.x)} ${Math.round(instance.config.y)}]`;
|
|
608
614
|
}
|
|
609
|
-
return ` * @node ${instance.id} ${instance.nodeType}${parent}${labelAttr}${portOrderAttr}${portLabelAttr}${exprAttr}${pullExecutionAttr}${minimizedAttr}${colorAttr}${iconAttr}${tagsAttr}${sizeAttr}${positionAttr}`;
|
|
615
|
+
return ` * @node ${instance.id} ${instance.nodeType}${parent}${labelAttr}${portOrderAttr}${portLabelAttr}${exprAttr}${pullExecutionAttr}${minimizedAttr}${colorAttr}${iconAttr}${tagsAttr}${suppressAttr}${sizeAttr}${positionAttr}`;
|
|
610
616
|
}
|
|
611
617
|
/**
|
|
612
618
|
* Generate a TypeScript function signature from a node type definition.
|
package/dist/api/index.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export { transformWorkflow } from './transform.js';
|
|
|
38
38
|
export { type ValidationResult, validateWorkflow } from './validate.js';
|
|
39
39
|
export { validationRuleRegistry } from './validation-registry.js';
|
|
40
40
|
export * from './manipulation/index.js';
|
|
41
|
+
export { applyModifyOperation, validateModifyParams } from './modify-operation.js';
|
|
41
42
|
export { withValidation, withMinimalValidation, withoutValidation, type RemoveOptions, type NodeFilter, type OperationResult, validatePortReference, portReferencesEqual, formatPortReference, generateUniqueNodeId, assertNodeTypeExists, assertNodeExists, assertNodeNotExists, } from './helpers.js';
|
|
42
43
|
export * from './query.js';
|
|
43
44
|
export * from './builder.js';
|
package/dist/api/index.js
CHANGED
|
@@ -37,6 +37,7 @@ export { transformWorkflow } from './transform.js';
|
|
|
37
37
|
export { validateWorkflow } from './validate.js';
|
|
38
38
|
export { validationRuleRegistry } from './validation-registry.js';
|
|
39
39
|
export * from './manipulation/index.js';
|
|
40
|
+
export { applyModifyOperation, validateModifyParams } from './modify-operation.js';
|
|
40
41
|
export { withValidation, withMinimalValidation, withoutValidation, validatePortReference, portReferencesEqual, formatPortReference, generateUniqueNodeId, assertNodeTypeExists, assertNodeExists, assertNodeNotExists, } from './helpers.js';
|
|
41
42
|
export * from './query.js';
|
|
42
43
|
export * from './builder.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { TWorkflowAST } from '../ast/types.js';
|
|
3
|
+
export declare const modifyParamsSchemas: Record<string, z.ZodType>;
|
|
4
|
+
export declare function validateModifyParams(operation: string, params: Record<string, unknown>): {
|
|
5
|
+
success: true;
|
|
6
|
+
} | {
|
|
7
|
+
success: false;
|
|
8
|
+
error: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function applyModifyOperation(ast: TWorkflowAST, operation: string, params: Record<string, unknown>): {
|
|
11
|
+
ast: TWorkflowAST;
|
|
12
|
+
warnings: string[];
|
|
13
|
+
extraData: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=modify-operation.d.ts.map
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { addNode as manipAddNode, removeNode as manipRemoveNode, renameNode as manipRenameNode, addConnection as manipAddConnection, removeConnection as manipRemoveConnection, setNodePosition as manipSetNodePosition, setNodeLabel as manipSetNodeLabel, } from './manipulation/index.js';
|
|
3
|
+
import { findIsolatedNodes } from './query.js';
|
|
4
|
+
export const modifyParamsSchemas = {
|
|
5
|
+
addNode: z.object({
|
|
6
|
+
nodeId: z.string({ required_error: 'nodeId is required' }),
|
|
7
|
+
nodeType: z.string({ required_error: 'nodeType is required' }),
|
|
8
|
+
x: z.number().optional(),
|
|
9
|
+
y: z.number().optional(),
|
|
10
|
+
}),
|
|
11
|
+
removeNode: z.object({
|
|
12
|
+
nodeId: z.string({ required_error: 'nodeId is required' }),
|
|
13
|
+
}),
|
|
14
|
+
renameNode: z.object({
|
|
15
|
+
oldId: z.string({ required_error: 'oldId is required' }),
|
|
16
|
+
newId: z.string({ required_error: 'newId is required' }),
|
|
17
|
+
}),
|
|
18
|
+
addConnection: z.object({
|
|
19
|
+
from: z.string({ required_error: 'from is required (format: "node.port")' }),
|
|
20
|
+
to: z.string({ required_error: 'to is required (format: "node.port")' }),
|
|
21
|
+
}),
|
|
22
|
+
removeConnection: z.object({
|
|
23
|
+
from: z.string({ required_error: 'from is required (format: "node.port")' }),
|
|
24
|
+
to: z.string({ required_error: 'to is required (format: "node.port")' }),
|
|
25
|
+
}),
|
|
26
|
+
setNodePosition: z.object({
|
|
27
|
+
nodeId: z.string({ required_error: 'nodeId is required' }),
|
|
28
|
+
x: z.number({ required_error: 'x is required', invalid_type_error: 'x must be a number' }),
|
|
29
|
+
y: z.number({ required_error: 'y is required', invalid_type_error: 'y must be a number' }),
|
|
30
|
+
}),
|
|
31
|
+
setNodeLabel: z.object({
|
|
32
|
+
nodeId: z.string({ required_error: 'nodeId is required' }),
|
|
33
|
+
label: z.string({ required_error: 'label is required' }),
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
export function validateModifyParams(operation, params) {
|
|
37
|
+
const schema = modifyParamsSchemas[operation];
|
|
38
|
+
if (!schema) {
|
|
39
|
+
return { success: false, error: `Unknown operation: ${operation}` };
|
|
40
|
+
}
|
|
41
|
+
const result = schema.safeParse(params);
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
const messages = result.error.issues.map((i) => i.message).join('; ');
|
|
44
|
+
return { success: false, error: `${operation} params invalid: ${messages}` };
|
|
45
|
+
}
|
|
46
|
+
return { success: true };
|
|
47
|
+
}
|
|
48
|
+
export function applyModifyOperation(ast, operation, params) {
|
|
49
|
+
const p = params;
|
|
50
|
+
const warnings = [];
|
|
51
|
+
const extraData = {};
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- AST manipulation functions use loose typing
|
|
53
|
+
let modifiedAST = ast;
|
|
54
|
+
switch (operation) {
|
|
55
|
+
case 'addNode': {
|
|
56
|
+
const nodeId = p.nodeId;
|
|
57
|
+
const nodeType = p.nodeType;
|
|
58
|
+
const nodeTypeExists = modifiedAST.nodeTypes.some((nt) => nt.name === nodeType || nt.functionName === nodeType);
|
|
59
|
+
if (!nodeTypeExists) {
|
|
60
|
+
warnings.push(`Node type "${nodeType}" is not defined in the file. ` +
|
|
61
|
+
`The node will be added but may not render until the type is defined.`);
|
|
62
|
+
}
|
|
63
|
+
let autoX = typeof p.x === 'number' ? p.x : undefined;
|
|
64
|
+
let autoY = typeof p.y === 'number' ? p.y : undefined;
|
|
65
|
+
if (autoX === undefined || autoY === undefined) {
|
|
66
|
+
const positions = modifiedAST.instances
|
|
67
|
+
.map((inst) => inst.config)
|
|
68
|
+
.filter((c) => c !== undefined &&
|
|
69
|
+
c !== null &&
|
|
70
|
+
typeof c.x === 'number' &&
|
|
71
|
+
typeof c.y === 'number');
|
|
72
|
+
if (positions.length > 0) {
|
|
73
|
+
const maxX = Math.max(...positions.map((pos) => pos.x));
|
|
74
|
+
if (autoX === undefined)
|
|
75
|
+
autoX = maxX + 180;
|
|
76
|
+
if (autoY === undefined)
|
|
77
|
+
autoY = 0;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (autoX === undefined)
|
|
81
|
+
autoX = 0;
|
|
82
|
+
if (autoY === undefined)
|
|
83
|
+
autoY = 0;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
modifiedAST = manipAddNode(modifiedAST, {
|
|
87
|
+
type: 'NodeInstance',
|
|
88
|
+
id: nodeId,
|
|
89
|
+
nodeType,
|
|
90
|
+
config: { x: autoX, y: autoY },
|
|
91
|
+
});
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'removeNode': {
|
|
95
|
+
const nodeId = p.nodeId;
|
|
96
|
+
const removedConnections = modifiedAST.connections
|
|
97
|
+
.filter((c) => c.from.node === nodeId || c.to.node === nodeId)
|
|
98
|
+
.map((c) => ({
|
|
99
|
+
from: `${c.from.node}.${c.from.port}`,
|
|
100
|
+
to: `${c.to.node}.${c.to.port}`,
|
|
101
|
+
}));
|
|
102
|
+
modifiedAST = manipRemoveNode(modifiedAST, nodeId);
|
|
103
|
+
if (removedConnections.length > 0) {
|
|
104
|
+
extraData.removedConnections = removedConnections;
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case 'renameNode': {
|
|
109
|
+
modifiedAST = manipRenameNode(modifiedAST, p.oldId, p.newId);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'addConnection': {
|
|
113
|
+
const from = p.from;
|
|
114
|
+
const to = p.to;
|
|
115
|
+
const [fromNode, fromPort] = from.split('.');
|
|
116
|
+
const [toNode, toPort] = to.split('.');
|
|
117
|
+
if (!fromPort || !toPort) {
|
|
118
|
+
throw new Error('Connection format must be "node.port" (e.g., "Start.execute")');
|
|
119
|
+
}
|
|
120
|
+
const validNodes = [
|
|
121
|
+
'Start',
|
|
122
|
+
'Exit',
|
|
123
|
+
...modifiedAST.instances.map((i) => i.id),
|
|
124
|
+
];
|
|
125
|
+
if (!validNodes.includes(fromNode)) {
|
|
126
|
+
throw new Error(`Source node "${fromNode}" not found. Available: ${validNodes.join(', ')}`);
|
|
127
|
+
}
|
|
128
|
+
if (!validNodes.includes(toNode)) {
|
|
129
|
+
throw new Error(`Target node "${toNode}" not found. Available: ${validNodes.join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
if (fromNode !== 'Start' && fromNode !== 'Exit') {
|
|
132
|
+
const inst = modifiedAST.instances.find((i) => i.id === fromNode);
|
|
133
|
+
const nt = modifiedAST.nodeTypes.find((t) => t.name === inst?.nodeType);
|
|
134
|
+
if (nt && !nt.outputs[fromPort]) {
|
|
135
|
+
throw new Error(`Node "${fromNode}" has no output "${fromPort}". Available: ${Object.keys(nt.outputs).join(', ')}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (toNode !== 'Start' && toNode !== 'Exit') {
|
|
139
|
+
const inst = modifiedAST.instances.find((i) => i.id === toNode);
|
|
140
|
+
const nt = modifiedAST.nodeTypes.find((t) => t.name === inst?.nodeType);
|
|
141
|
+
if (nt && !nt.inputs[toPort]) {
|
|
142
|
+
throw new Error(`Node "${toNode}" has no input "${toPort}". Available: ${Object.keys(nt.inputs).join(', ')}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
modifiedAST = manipAddConnection(modifiedAST, from, to);
|
|
146
|
+
if (modifiedAST.options?.autoConnect) {
|
|
147
|
+
modifiedAST = { ...modifiedAST, options: { ...modifiedAST.options, autoConnect: undefined } };
|
|
148
|
+
warnings.push('autoConnect was disabled because connections were manually modified');
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case 'removeConnection': {
|
|
153
|
+
modifiedAST = manipRemoveConnection(modifiedAST, p.from, p.to);
|
|
154
|
+
if (modifiedAST.options?.autoConnect) {
|
|
155
|
+
modifiedAST = { ...modifiedAST, options: { ...modifiedAST.options, autoConnect: undefined } };
|
|
156
|
+
warnings.push('autoConnect was disabled because connections were manually modified');
|
|
157
|
+
}
|
|
158
|
+
const newlyIsolated = findIsolatedNodes(modifiedAST);
|
|
159
|
+
if (newlyIsolated.length > 0) {
|
|
160
|
+
extraData.newlyIsolatedNodes = newlyIsolated;
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'setNodePosition': {
|
|
165
|
+
modifiedAST = manipSetNodePosition(modifiedAST, p.nodeId, p.x, p.y);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case 'setNodeLabel': {
|
|
169
|
+
modifiedAST = manipSetNodeLabel(modifiedAST, p.nodeId, p.label);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
throw new Error(`Unknown operation: ${operation}`);
|
|
174
|
+
}
|
|
175
|
+
return { ast: modifiedAST, warnings, extraData };
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=modify-operation.js.map
|
package/dist/api/validate.js
CHANGED
|
@@ -38,6 +38,23 @@ export function validateWorkflow(ast, options) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
// Filter warnings from additional rules through per-instance suppressWarnings
|
|
42
|
+
// (core validator already filters its own warnings, but agent/registry rules
|
|
43
|
+
// run after the core validator and need the same treatment)
|
|
44
|
+
const suppressMap = new Map();
|
|
45
|
+
for (const inst of ast.instances) {
|
|
46
|
+
if (inst.config?.suppressWarnings?.length) {
|
|
47
|
+
suppressMap.set(inst.id, new Set(inst.config.suppressWarnings));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (suppressMap.size > 0) {
|
|
51
|
+
result.warnings = result.warnings.filter((w) => {
|
|
52
|
+
if (!w.node)
|
|
53
|
+
return true;
|
|
54
|
+
const codes = suppressMap.get(w.node);
|
|
55
|
+
return !codes || !codes.has(w.code);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
41
58
|
// Re-evaluate validity
|
|
42
59
|
result.valid = result.errors.length === 0;
|
|
43
60
|
return result;
|
package/dist/ast/types.d.ts
CHANGED
|
@@ -345,6 +345,8 @@ export type TNodeInstanceConfig = {
|
|
|
345
345
|
minimized?: boolean;
|
|
346
346
|
width?: number;
|
|
347
347
|
height?: number;
|
|
348
|
+
/** Warning codes to suppress for this instance (e.g., ["UNUSED_OUTPUT_PORT"]) */
|
|
349
|
+
suppressWarnings?: string[];
|
|
348
350
|
};
|
|
349
351
|
/**
|
|
350
352
|
* Node Instance AST - A usage of a node type in a workflow.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Parser for @node declarations using Chevrotain.
|
|
5
5
|
*/
|
|
6
6
|
import { CstParser } from 'chevrotain';
|
|
7
|
-
import { JSDocLexer, NodeTag, Identifier, Dot, Integer, LabelPrefix, ExprPrefix, PortOrderPrefix, PortLabelPrefix, MinimizedKeyword, PullExecutionPrefix, SizePrefix, PositionPrefix, ColorPrefix, IconPrefix, JobPrefix, EnvironmentAttrPrefix, TagsPrefix, StringLiteral, LBracket, RBracket, Comma, Equals, EventEq, CronEq, MatchEq, TimeoutEq, LimitEq, PeriodEq, allTokens, } from './tokens.js';
|
|
7
|
+
import { JSDocLexer, NodeTag, Identifier, Dot, Integer, LabelPrefix, ExprPrefix, PortOrderPrefix, PortLabelPrefix, MinimizedKeyword, PullExecutionPrefix, SizePrefix, PositionPrefix, ColorPrefix, IconPrefix, JobPrefix, EnvironmentAttrPrefix, TagsPrefix, SuppressPrefix, StringLiteral, LBracket, RBracket, Comma, Equals, EventEq, CronEq, MatchEq, TimeoutEq, LimitEq, PeriodEq, allTokens, } from './tokens.js';
|
|
8
8
|
// =============================================================================
|
|
9
9
|
// Parser Definition
|
|
10
10
|
// =============================================================================
|
|
@@ -54,6 +54,7 @@ class NodeParser extends CstParser {
|
|
|
54
54
|
{ ALT: () => this.SUBRULE(this.jobAttr) },
|
|
55
55
|
{ ALT: () => this.SUBRULE(this.environmentAttr) },
|
|
56
56
|
{ ALT: () => this.SUBRULE(this.tagsAttr) },
|
|
57
|
+
{ ALT: () => this.SUBRULE(this.suppressAttr) },
|
|
57
58
|
]);
|
|
58
59
|
},
|
|
59
60
|
});
|
|
@@ -179,6 +180,16 @@ class NodeParser extends CstParser {
|
|
|
179
180
|
},
|
|
180
181
|
});
|
|
181
182
|
});
|
|
183
|
+
// suppress: "CODE", "CODE2"
|
|
184
|
+
suppressAttr = this.RULE('suppressAttr', () => {
|
|
185
|
+
this.CONSUME(SuppressPrefix);
|
|
186
|
+
this.AT_LEAST_ONE_SEP({
|
|
187
|
+
SEP: Comma,
|
|
188
|
+
DEF: () => {
|
|
189
|
+
this.CONSUME(StringLiteral, { LABEL: 'suppressCode' });
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
});
|
|
182
193
|
// "label" ["tooltip"]
|
|
183
194
|
tagEntry = this.RULE('tagEntry', () => {
|
|
184
195
|
this.CONSUME(StringLiteral, { LABEL: 'tagLabel' });
|
|
@@ -217,6 +228,7 @@ class NodeVisitor extends BaseVisitor {
|
|
|
217
228
|
let tags;
|
|
218
229
|
let job;
|
|
219
230
|
let environment;
|
|
231
|
+
let suppress;
|
|
220
232
|
if (ctx.parentScopeRef) {
|
|
221
233
|
parentScope = this.visit(ctx.parentScopeRef);
|
|
222
234
|
}
|
|
@@ -249,6 +261,8 @@ class NodeVisitor extends BaseVisitor {
|
|
|
249
261
|
job = attrs.job;
|
|
250
262
|
if (attrs.environment)
|
|
251
263
|
environment = attrs.environment;
|
|
264
|
+
if (attrs.suppress)
|
|
265
|
+
suppress = [...(suppress || []), ...attrs.suppress];
|
|
252
266
|
}
|
|
253
267
|
}
|
|
254
268
|
return {
|
|
@@ -268,6 +282,7 @@ class NodeVisitor extends BaseVisitor {
|
|
|
268
282
|
...(tags && { tags }),
|
|
269
283
|
...(job && { job }),
|
|
270
284
|
...(environment && { environment }),
|
|
285
|
+
...(suppress && { suppress }),
|
|
271
286
|
};
|
|
272
287
|
}
|
|
273
288
|
parentScopeRef(ctx) {
|
|
@@ -289,6 +304,7 @@ class NodeVisitor extends BaseVisitor {
|
|
|
289
304
|
let tags;
|
|
290
305
|
let job;
|
|
291
306
|
let environment;
|
|
307
|
+
let suppress;
|
|
292
308
|
if (ctx.labelAttr) {
|
|
293
309
|
for (const attr of ctx.labelAttr) {
|
|
294
310
|
label = this.visit(attr);
|
|
@@ -356,6 +372,12 @@ class NodeVisitor extends BaseVisitor {
|
|
|
356
372
|
environment = this.visit(attr);
|
|
357
373
|
}
|
|
358
374
|
}
|
|
375
|
+
if (ctx.suppressAttr) {
|
|
376
|
+
for (const attr of ctx.suppressAttr) {
|
|
377
|
+
const codes = this.visit(attr);
|
|
378
|
+
suppress = [...(suppress || []), ...codes];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
359
381
|
return {
|
|
360
382
|
label,
|
|
361
383
|
expressions,
|
|
@@ -370,6 +392,7 @@ class NodeVisitor extends BaseVisitor {
|
|
|
370
392
|
tags,
|
|
371
393
|
job,
|
|
372
394
|
environment,
|
|
395
|
+
suppress,
|
|
373
396
|
};
|
|
374
397
|
}
|
|
375
398
|
labelAttr(ctx) {
|
|
@@ -466,6 +489,9 @@ class NodeVisitor extends BaseVisitor {
|
|
|
466
489
|
environmentAttr(ctx) {
|
|
467
490
|
return this.unescapeString(ctx.environmentValue[0].image);
|
|
468
491
|
}
|
|
492
|
+
suppressAttr(ctx) {
|
|
493
|
+
return ctx.suppressCode.map((tok) => this.unescapeString(tok.image));
|
|
494
|
+
}
|
|
469
495
|
tagsAttr(ctx) {
|
|
470
496
|
const result = [];
|
|
471
497
|
if (ctx.tagEntry) {
|
|
@@ -51,6 +51,7 @@ export declare const IconPrefix: import("chevrotain").TokenType;
|
|
|
51
51
|
export declare const JobPrefix: import("chevrotain").TokenType;
|
|
52
52
|
export declare const EnvironmentAttrPrefix: import("chevrotain").TokenType;
|
|
53
53
|
export declare const TagsPrefix: import("chevrotain").TokenType;
|
|
54
|
+
export declare const SuppressPrefix: import("chevrotain").TokenType;
|
|
54
55
|
export declare const EventEq: import("chevrotain").TokenType;
|
|
55
56
|
export declare const CronEq: import("chevrotain").TokenType;
|
|
56
57
|
export declare const MatchEq: import("chevrotain").TokenType;
|
|
@@ -200,6 +200,10 @@ export const TagsPrefix = createToken({
|
|
|
200
200
|
name: 'TagsPrefix',
|
|
201
201
|
pattern: /tags:/,
|
|
202
202
|
});
|
|
203
|
+
export const SuppressPrefix = createToken({
|
|
204
|
+
name: 'SuppressPrefix',
|
|
205
|
+
pattern: /suppress:/,
|
|
206
|
+
});
|
|
203
207
|
export const EventEq = createToken({
|
|
204
208
|
name: 'EventEq',
|
|
205
209
|
pattern: /event=/,
|
|
@@ -378,6 +382,7 @@ export const allTokens = [
|
|
|
378
382
|
JobPrefix,
|
|
379
383
|
EnvironmentAttrPrefix,
|
|
380
384
|
TagsPrefix,
|
|
385
|
+
SuppressPrefix,
|
|
381
386
|
EventEq,
|
|
382
387
|
CronEq,
|
|
383
388
|
MatchEq,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare function modifyAddNodeCommand(file: string, opts: {
|
|
2
|
+
nodeId: string;
|
|
3
|
+
nodeType: string;
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
export declare function modifyRemoveNodeCommand(file: string, opts: {
|
|
6
|
+
nodeId: string;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
export declare function modifyAddConnectionCommand(file: string, opts: {
|
|
9
|
+
from: string;
|
|
10
|
+
to: string;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
export declare function modifyRemoveConnectionCommand(file: string, opts: {
|
|
13
|
+
from: string;
|
|
14
|
+
to: string;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
export declare function modifyRenameNodeCommand(file: string, opts: {
|
|
17
|
+
oldId: string;
|
|
18
|
+
newId: string;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
export declare function modifySetPositionCommand(file: string, opts: {
|
|
21
|
+
nodeId: string;
|
|
22
|
+
x: string;
|
|
23
|
+
y: string;
|
|
24
|
+
}): Promise<void>;
|
|
25
|
+
export declare function modifySetLabelCommand(file: string, opts: {
|
|
26
|
+
nodeId: string;
|
|
27
|
+
label: string;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=modify.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { parseWorkflow } from '../../api/index.js';
|
|
4
|
+
import { generateInPlace } from '../../api/generate-in-place.js';
|
|
5
|
+
import { applyModifyOperation, validateModifyParams } from '../../api/modify-operation.js';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
async function readParseModifyWrite(file, operation, params) {
|
|
8
|
+
const validation = validateModifyParams(operation, params);
|
|
9
|
+
if (!validation.success) {
|
|
10
|
+
throw new Error(validation.error);
|
|
11
|
+
}
|
|
12
|
+
const filePath = path.resolve(file);
|
|
13
|
+
const source = fs.readFileSync(filePath, 'utf-8');
|
|
14
|
+
const parseResult = await parseWorkflow(filePath);
|
|
15
|
+
if (parseResult.errors.length > 0) {
|
|
16
|
+
throw new Error(`Parse errors:\n${parseResult.errors.join('\n')}`);
|
|
17
|
+
}
|
|
18
|
+
const { ast: modifiedAST, warnings } = applyModifyOperation(parseResult.ast, operation, params);
|
|
19
|
+
const result = generateInPlace(source, modifiedAST);
|
|
20
|
+
fs.writeFileSync(filePath, result.code, 'utf-8');
|
|
21
|
+
for (const w of warnings) {
|
|
22
|
+
logger.warn(w);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function modifyAddNodeCommand(file, opts) {
|
|
26
|
+
await readParseModifyWrite(file, 'addNode', { nodeId: opts.nodeId, nodeType: opts.nodeType });
|
|
27
|
+
logger.success(`Added node "${opts.nodeId}" (type: ${opts.nodeType}) to ${file}`);
|
|
28
|
+
}
|
|
29
|
+
export async function modifyRemoveNodeCommand(file, opts) {
|
|
30
|
+
await readParseModifyWrite(file, 'removeNode', { nodeId: opts.nodeId });
|
|
31
|
+
logger.success(`Removed node "${opts.nodeId}" from ${file}`);
|
|
32
|
+
}
|
|
33
|
+
export async function modifyAddConnectionCommand(file, opts) {
|
|
34
|
+
await readParseModifyWrite(file, 'addConnection', { from: opts.from, to: opts.to });
|
|
35
|
+
logger.success(`Added connection ${opts.from} -> ${opts.to} in ${file}`);
|
|
36
|
+
}
|
|
37
|
+
export async function modifyRemoveConnectionCommand(file, opts) {
|
|
38
|
+
await readParseModifyWrite(file, 'removeConnection', { from: opts.from, to: opts.to });
|
|
39
|
+
logger.success(`Removed connection ${opts.from} -> ${opts.to} from ${file}`);
|
|
40
|
+
}
|
|
41
|
+
export async function modifyRenameNodeCommand(file, opts) {
|
|
42
|
+
await readParseModifyWrite(file, 'renameNode', { oldId: opts.oldId, newId: opts.newId });
|
|
43
|
+
logger.success(`Renamed node "${opts.oldId}" to "${opts.newId}" in ${file}`);
|
|
44
|
+
}
|
|
45
|
+
export async function modifySetPositionCommand(file, opts) {
|
|
46
|
+
await readParseModifyWrite(file, 'setNodePosition', {
|
|
47
|
+
nodeId: opts.nodeId,
|
|
48
|
+
x: Number(opts.x),
|
|
49
|
+
y: Number(opts.y),
|
|
50
|
+
});
|
|
51
|
+
logger.success(`Set position of "${opts.nodeId}" to (${opts.x}, ${opts.y}) in ${file}`);
|
|
52
|
+
}
|
|
53
|
+
export async function modifySetLabelCommand(file, opts) {
|
|
54
|
+
await readParseModifyWrite(file, 'setNodeLabel', { nodeId: opts.nodeId, label: opts.label });
|
|
55
|
+
logger.success(`Set label of "${opts.nodeId}" to "${opts.label}" in ${file}`);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=modify.js.map
|