@pikku/inspector 0.11.0 → 0.11.2
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/CHANGELOG.md +32 -2
- package/dist/add/add-channel.js +11 -10
- package/dist/add/add-file-with-factory.js +10 -10
- package/dist/add/add-forge-credential.d.ts +8 -0
- package/dist/add/add-forge-credential.js +77 -0
- package/dist/add/add-forge-node.d.ts +7 -0
- package/dist/add/add-forge-node.js +77 -0
- package/dist/add/add-functions.js +158 -51
- package/dist/add/add-http-route.js +28 -4
- package/dist/add/add-mcp-prompt.js +6 -5
- package/dist/add/add-mcp-resource.js +6 -5
- package/dist/add/add-mcp-tool.js +6 -5
- package/dist/add/add-middleware.js +1 -1
- package/dist/add/add-permission.js +1 -1
- package/dist/add/add-queue-worker.js +6 -5
- package/dist/add/add-rpc-invocations.d.ts +3 -0
- package/dist/add/add-rpc-invocations.js +51 -25
- package/dist/add/add-schedule.js +5 -4
- package/dist/add/add-workflow-graph.d.ts +6 -0
- package/dist/add/add-workflow-graph.js +659 -0
- package/dist/add/add-workflow.d.ts +1 -1
- package/dist/add/add-workflow.js +191 -69
- package/dist/error-codes.d.ts +3 -0
- package/dist/error-codes.js +3 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/inspector.js +29 -9
- package/dist/types.d.ts +47 -8
- package/dist/utils/extract-function-name.js +7 -7
- package/dist/utils/extract-function-node.d.ts +10 -0
- package/dist/utils/extract-function-node.js +38 -0
- package/dist/utils/extract-node-value.d.ts +8 -0
- package/dist/utils/extract-node-value.js +24 -0
- package/dist/utils/extract-service-metadata.d.ts +19 -0
- package/dist/utils/extract-service-metadata.js +244 -0
- package/dist/utils/get-files-and-methods.d.ts +3 -3
- package/dist/utils/get-files-and-methods.js +3 -3
- package/dist/utils/get-property-value.d.ts +14 -6
- package/dist/utils/get-property-value.js +55 -43
- package/dist/utils/post-process.d.ts +9 -0
- package/dist/utils/post-process.js +30 -3
- package/dist/utils/serialize-inspector-state.d.ts +42 -6
- package/dist/utils/serialize-inspector-state.js +36 -10
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +898 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +17 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +1284 -0
- package/dist/utils/workflow/dsl/index.d.ts +7 -0
- package/dist/utils/workflow/dsl/index.js +7 -0
- package/dist/utils/workflow/dsl/patterns.d.ts +60 -0
- package/dist/utils/workflow/dsl/patterns.js +218 -0
- package/dist/utils/workflow/dsl/validation.d.ts +30 -0
- package/dist/utils/workflow/dsl/validation.js +142 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +316 -0
- package/dist/utils/workflow/graph/index.d.ts +6 -0
- package/dist/utils/workflow/graph/index.js +6 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +43 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +152 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +229 -0
- package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
- package/dist/utils/write-service-metadata.d.ts +13 -0
- package/dist/utils/write-service-metadata.js +37 -0
- package/dist/visit.js +8 -2
- package/package.json +16 -4
- package/src/add/add-channel.ts +37 -17
- package/src/add/add-file-with-factory.ts +10 -10
- package/src/add/add-forge-credential.ts +119 -0
- package/src/add/add-forge-node.ts +132 -0
- package/src/add/add-functions.ts +199 -69
- package/src/add/add-http-route.ts +34 -5
- package/src/add/add-mcp-prompt.ts +11 -7
- package/src/add/add-mcp-resource.ts +11 -7
- package/src/add/add-mcp-tool.ts +11 -7
- package/src/add/add-middleware.ts +1 -1
- package/src/add/add-permission.ts +1 -1
- package/src/add/add-queue-worker.ts +11 -12
- package/src/add/add-rpc-invocations.ts +61 -31
- package/src/add/add-schedule.ts +10 -5
- package/src/add/add-workflow-graph.ts +864 -0
- package/src/add/add-workflow.ts +212 -116
- package/src/error-codes.ts +3 -0
- package/src/index.ts +12 -0
- package/src/inspector.ts +36 -10
- package/src/types.ts +43 -9
- package/src/utils/extract-function-name.ts +7 -7
- package/src/utils/extract-function-node.ts +58 -0
- package/src/utils/extract-node-value.ts +31 -0
- package/src/utils/extract-service-metadata.ts +353 -0
- package/src/utils/filter-inspector-state.test.ts +3 -3
- package/src/utils/filter-utils.test.ts +45 -51
- package/src/utils/get-files-and-methods.ts +11 -11
- package/src/utils/get-property-value.ts +67 -53
- package/src/utils/permissions.test.ts +3 -3
- package/src/utils/post-process.ts +56 -3
- package/src/utils/serialize-inspector-state.ts +67 -19
- package/src/utils/test-data/inspector-state.json +9 -9
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1180 -0
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +1608 -0
- package/src/utils/workflow/dsl/index.ts +11 -0
- package/src/utils/workflow/dsl/patterns.ts +279 -0
- package/src/utils/workflow/dsl/validation.ts +180 -0
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +415 -0
- package/src/utils/workflow/graph/index.ts +6 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +223 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +280 -0
- package/src/utils/write-service-metadata.ts +51 -0
- package/src/visit.ts +9 -3
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSL (Domain Specific Language) workflow extraction exports
|
|
3
|
+
*/
|
|
4
|
+
export { extractDSLWorkflow } from './extract-dsl-workflow.js';
|
|
5
|
+
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './deserialize-dsl-workflow.js';
|
|
6
|
+
export * from './patterns.js';
|
|
7
|
+
export * from './validation.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSL (Domain Specific Language) workflow extraction exports
|
|
3
|
+
*/
|
|
4
|
+
export { extractDSLWorkflow } from './extract-dsl-workflow.js';
|
|
5
|
+
export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './deserialize-dsl-workflow.js';
|
|
6
|
+
export * from './patterns.js';
|
|
7
|
+
export * from './validation.js';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Pattern detection helpers for simple workflow extraction
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Check if a call expression is workflow.do()
|
|
7
|
+
*/
|
|
8
|
+
export declare function isWorkflowDoCall(node: ts.CallExpression, checker: ts.TypeChecker): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Check if a call expression is workflow.sleep()
|
|
11
|
+
*/
|
|
12
|
+
export declare function isWorkflowSleepCall(node: ts.CallExpression, checker: ts.TypeChecker): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Check if a throw statement throws WorkflowCancelledException
|
|
15
|
+
* Matches: throw new WorkflowCancelledException(...) or throw WorkflowCancelledException(...)
|
|
16
|
+
*/
|
|
17
|
+
export declare function isThrowCancelException(node: ts.ThrowStatement): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Extract the reason string from a throw WorkflowCancelledException statement
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractCancelReason(node: ts.ThrowStatement, checker: ts.TypeChecker): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Check if a call expression is array.filter()
|
|
24
|
+
*/
|
|
25
|
+
export declare function isArrayFilter(node: ts.CallExpression): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a call expression is array.some()
|
|
28
|
+
*/
|
|
29
|
+
export declare function isArraySome(node: ts.CallExpression): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a call expression is array.every()
|
|
32
|
+
*/
|
|
33
|
+
export declare function isArrayEvery(node: ts.CallExpression): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Check if an expression is Promise.all(array.map(...))
|
|
36
|
+
*/
|
|
37
|
+
export declare function isParallelFanout(node: ts.CallExpression): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Check if an expression is Promise.all([...])
|
|
40
|
+
*/
|
|
41
|
+
export declare function isParallelGroup(node: ts.CallExpression): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Check if a for statement is a valid sequential fanout (for..of)
|
|
44
|
+
*/
|
|
45
|
+
export declare function isSequentialFanout(node: ts.ForOfStatement): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Extract the variable name from a for..of statement
|
|
48
|
+
*/
|
|
49
|
+
export declare function extractForOfVariable(node: ts.ForOfStatement): {
|
|
50
|
+
itemVar: string;
|
|
51
|
+
sourceVar: string;
|
|
52
|
+
} | null;
|
|
53
|
+
/**
|
|
54
|
+
* Check if a type is an array type
|
|
55
|
+
*/
|
|
56
|
+
export declare function isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get the source text of a node (for condition expressions)
|
|
59
|
+
*/
|
|
60
|
+
export declare function getSourceText(node: ts.Node): string;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Pattern detection helpers for simple workflow extraction
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Check if a call expression is workflow.do()
|
|
7
|
+
*/
|
|
8
|
+
export function isWorkflowDoCall(node, checker) {
|
|
9
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const propAccess = node.expression;
|
|
13
|
+
return (propAccess.name.text === 'do' &&
|
|
14
|
+
ts.isIdentifier(propAccess.expression) &&
|
|
15
|
+
propAccess.expression.text === 'workflow');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if a call expression is workflow.sleep()
|
|
19
|
+
*/
|
|
20
|
+
export function isWorkflowSleepCall(node, checker) {
|
|
21
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const propAccess = node.expression;
|
|
25
|
+
return (propAccess.name.text === 'sleep' &&
|
|
26
|
+
ts.isIdentifier(propAccess.expression) &&
|
|
27
|
+
propAccess.expression.text === 'workflow');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if a throw statement throws WorkflowCancelledException
|
|
31
|
+
* Matches: throw new WorkflowCancelledException(...) or throw WorkflowCancelledException(...)
|
|
32
|
+
*/
|
|
33
|
+
export function isThrowCancelException(node) {
|
|
34
|
+
const expr = node.expression;
|
|
35
|
+
if (!expr)
|
|
36
|
+
return false;
|
|
37
|
+
// Check for: throw new WorkflowCancelledException(...)
|
|
38
|
+
if (ts.isNewExpression(expr)) {
|
|
39
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
40
|
+
return expr.expression.text === 'WorkflowCancelledException';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Check for: throw WorkflowCancelledException(...) - function call style
|
|
44
|
+
if (ts.isCallExpression(expr)) {
|
|
45
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
46
|
+
return expr.expression.text === 'WorkflowCancelledException';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extract the reason string from a throw WorkflowCancelledException statement
|
|
53
|
+
*/
|
|
54
|
+
export function extractCancelReason(node, checker) {
|
|
55
|
+
const expr = node.expression;
|
|
56
|
+
if (!expr)
|
|
57
|
+
return undefined;
|
|
58
|
+
let args;
|
|
59
|
+
if (ts.isNewExpression(expr) && expr.arguments) {
|
|
60
|
+
args = expr.arguments;
|
|
61
|
+
}
|
|
62
|
+
else if (ts.isCallExpression(expr)) {
|
|
63
|
+
args = expr.arguments;
|
|
64
|
+
}
|
|
65
|
+
if (args && args.length > 0) {
|
|
66
|
+
const firstArg = args[0];
|
|
67
|
+
if (ts.isStringLiteral(firstArg)) {
|
|
68
|
+
return firstArg.text;
|
|
69
|
+
}
|
|
70
|
+
// For template literals or other expressions, return the source text
|
|
71
|
+
return firstArg.getText();
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if a call expression is array.filter()
|
|
77
|
+
*/
|
|
78
|
+
export function isArrayFilter(node) {
|
|
79
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return node.expression.name.text === 'filter';
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if a call expression is array.some()
|
|
86
|
+
*/
|
|
87
|
+
export function isArraySome(node) {
|
|
88
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return node.expression.name.text === 'some';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Check if a call expression is array.every()
|
|
95
|
+
*/
|
|
96
|
+
export function isArrayEvery(node) {
|
|
97
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return node.expression.name.text === 'every';
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if an expression is Promise.all(array.map(...))
|
|
104
|
+
*/
|
|
105
|
+
export function isParallelFanout(node) {
|
|
106
|
+
// Promise.all(...)
|
|
107
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
const propAccess = node.expression;
|
|
111
|
+
if (!ts.isIdentifier(propAccess.expression) ||
|
|
112
|
+
propAccess.expression.text !== 'Promise' ||
|
|
113
|
+
propAccess.name.text !== 'all') {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
// Check if argument is array.map(...)
|
|
117
|
+
const arg = node.arguments[0];
|
|
118
|
+
if (!arg || !ts.isCallExpression(arg)) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (!ts.isPropertyAccessExpression(arg.expression)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return arg.expression.name.text === 'map';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if an expression is Promise.all([...])
|
|
128
|
+
*/
|
|
129
|
+
export function isParallelGroup(node) {
|
|
130
|
+
// Promise.all(...)
|
|
131
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const propAccess = node.expression;
|
|
135
|
+
if (!ts.isIdentifier(propAccess.expression) ||
|
|
136
|
+
propAccess.expression.text !== 'Promise' ||
|
|
137
|
+
propAccess.name.text !== 'all') {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
// Check if argument is an array literal
|
|
141
|
+
const arg = node.arguments[0];
|
|
142
|
+
return !!arg && ts.isArrayLiteralExpression(arg);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Check if a for statement is a valid sequential fanout (for..of)
|
|
146
|
+
*/
|
|
147
|
+
export function isSequentialFanout(node) {
|
|
148
|
+
// Must have const declaration
|
|
149
|
+
if (!ts.isVariableDeclarationList(node.initializer)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
const declList = node.initializer;
|
|
153
|
+
if (!(declList.flags & ts.NodeFlags.Const)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
// Must have exactly one declaration
|
|
157
|
+
if (declList.declarations.length !== 1) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Extract full source path from an expression (e.g., data.memberEmails)
|
|
164
|
+
*/
|
|
165
|
+
function extractSourcePath(expr) {
|
|
166
|
+
if (ts.isIdentifier(expr)) {
|
|
167
|
+
return expr.text;
|
|
168
|
+
}
|
|
169
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
170
|
+
const base = extractSourcePath(expr.expression);
|
|
171
|
+
if (base) {
|
|
172
|
+
return `${base}.${expr.name.text}`;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Extract the variable name from a for..of statement
|
|
179
|
+
*/
|
|
180
|
+
export function extractForOfVariable(node) {
|
|
181
|
+
if (!ts.isVariableDeclarationList(node.initializer)) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const decl = node.initializer.declarations[0];
|
|
185
|
+
if (!ts.isIdentifier(decl.name)) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const itemVar = decl.name.text;
|
|
189
|
+
// Extract source variable with full path (e.g., data.memberEmails)
|
|
190
|
+
const sourceVar = extractSourcePath(node.expression);
|
|
191
|
+
if (!sourceVar) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return { itemVar, sourceVar };
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Check if a type is an array type
|
|
198
|
+
*/
|
|
199
|
+
export function isArrayType(type, checker) {
|
|
200
|
+
// Check if it's an array type
|
|
201
|
+
if (checker.isArrayType(type)) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
// Check if it's a tuple type
|
|
205
|
+
if (type.flags & ts.TypeFlags.Object) {
|
|
206
|
+
const objectType = type;
|
|
207
|
+
if (objectType.objectFlags & ts.ObjectFlags.Tuple) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get the source text of a node (for condition expressions)
|
|
215
|
+
*/
|
|
216
|
+
export function getSourceText(node) {
|
|
217
|
+
return node.getText().trim();
|
|
218
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Validation rules for simple workflows
|
|
4
|
+
*/
|
|
5
|
+
export interface ValidationError {
|
|
6
|
+
message: string;
|
|
7
|
+
node: ts.Node;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Check if a node contains only allowed patterns
|
|
11
|
+
*
|
|
12
|
+
* Allowed patterns:
|
|
13
|
+
* - VariableStatement (const/let declarations)
|
|
14
|
+
* - ExpressionStatement (await workflow.do, await workflow.sleep, await Promise.all)
|
|
15
|
+
* - IfStatement (branches)
|
|
16
|
+
* - SwitchStatement (switch/case)
|
|
17
|
+
* - ForOfStatement (sequential fanout)
|
|
18
|
+
* - ReturnStatement
|
|
19
|
+
* - ThrowStatement (for WorkflowCancelledException)
|
|
20
|
+
* - Block (containers)
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateNoDisallowedPatterns(node: ts.Node): ValidationError[];
|
|
23
|
+
/**
|
|
24
|
+
* Validate that all workflow.do calls are awaited
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateAwaitedCalls(node: ts.Node): ValidationError[];
|
|
27
|
+
/**
|
|
28
|
+
* Combine all validation errors into a single error message
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatValidationErrors(errors: ValidationError[]): string;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Check if a node contains only allowed patterns
|
|
4
|
+
*
|
|
5
|
+
* Allowed patterns:
|
|
6
|
+
* - VariableStatement (const/let declarations)
|
|
7
|
+
* - ExpressionStatement (await workflow.do, await workflow.sleep, await Promise.all)
|
|
8
|
+
* - IfStatement (branches)
|
|
9
|
+
* - SwitchStatement (switch/case)
|
|
10
|
+
* - ForOfStatement (sequential fanout)
|
|
11
|
+
* - ReturnStatement
|
|
12
|
+
* - ThrowStatement (for WorkflowCancelledException)
|
|
13
|
+
* - Block (containers)
|
|
14
|
+
*/
|
|
15
|
+
export function validateNoDisallowedPatterns(node) {
|
|
16
|
+
const errors = [];
|
|
17
|
+
function visitBlock(block) {
|
|
18
|
+
for (const statement of block.statements) {
|
|
19
|
+
if (ts.isVariableStatement(statement) ||
|
|
20
|
+
ts.isExpressionStatement(statement) ||
|
|
21
|
+
ts.isIfStatement(statement) ||
|
|
22
|
+
ts.isSwitchStatement(statement) ||
|
|
23
|
+
ts.isForOfStatement(statement) ||
|
|
24
|
+
ts.isReturnStatement(statement) ||
|
|
25
|
+
ts.isThrowStatement(statement)) {
|
|
26
|
+
// Allowed statement type - recurse into it
|
|
27
|
+
visitNode(statement);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// Unknown/disallowed statement type
|
|
31
|
+
const nodeType = ts.SyntaxKind[statement.kind];
|
|
32
|
+
errors.push({
|
|
33
|
+
message: `Statement type '${nodeType}' is not allowed in simple workflows. Allowed: const/let, if/else, switch/case, for..of, return, throw, and workflow calls. If this should be supported, please report the node type: ${nodeType}`,
|
|
34
|
+
node: statement,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function visitNode(node) {
|
|
40
|
+
// Disallow while and do-while
|
|
41
|
+
if (ts.isWhileStatement(node) || ts.isDoStatement(node)) {
|
|
42
|
+
errors.push({
|
|
43
|
+
message: 'while and do-while loops are not allowed in simple workflows',
|
|
44
|
+
node,
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Disallow for and for-in loops
|
|
49
|
+
if (ts.isForInStatement(node) || ts.isForStatement(node)) {
|
|
50
|
+
errors.push({
|
|
51
|
+
message: 'for and for-in loops are not allowed in simple workflows. Use for-of instead.',
|
|
52
|
+
node,
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Check for inline workflow.do
|
|
57
|
+
if (ts.isCallExpression(node)) {
|
|
58
|
+
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
59
|
+
const propAccess = node.expression;
|
|
60
|
+
if (propAccess.name.text === 'do' &&
|
|
61
|
+
ts.isIdentifier(propAccess.expression) &&
|
|
62
|
+
propAccess.expression.text === 'workflow') {
|
|
63
|
+
const secondArg = node.arguments[1];
|
|
64
|
+
if (secondArg &&
|
|
65
|
+
(ts.isArrowFunction(secondArg) ||
|
|
66
|
+
ts.isFunctionExpression(secondArg))) {
|
|
67
|
+
errors.push({
|
|
68
|
+
message: 'Inline workflow.do with function argument is not allowed in simple workflows. Use RPC form instead.',
|
|
69
|
+
node,
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Recurse into blocks
|
|
77
|
+
if (ts.isBlock(node)) {
|
|
78
|
+
visitBlock(node);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
ts.forEachChild(node, visitNode);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
visitNode(node);
|
|
85
|
+
return errors;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate that all workflow.do calls are awaited
|
|
89
|
+
*/
|
|
90
|
+
export function validateAwaitedCalls(node) {
|
|
91
|
+
const errors = [];
|
|
92
|
+
function visit(node, parentIsAwait = false, insidePromiseAll = false) {
|
|
93
|
+
// Check if this is Promise.all(...) first, before checking for workflow calls
|
|
94
|
+
if (ts.isCallExpression(node) &&
|
|
95
|
+
ts.isPropertyAccessExpression(node.expression)) {
|
|
96
|
+
const propAccess = node.expression;
|
|
97
|
+
if (propAccess.name.text === 'all' &&
|
|
98
|
+
ts.isIdentifier(propAccess.expression) &&
|
|
99
|
+
propAccess.expression.text === 'Promise') {
|
|
100
|
+
// console.log('[DEBUG] Found Promise.all, setting insidePromiseAll=true')
|
|
101
|
+
// Visit children with insidePromiseAll = true
|
|
102
|
+
ts.forEachChild(node, (child) => visit(child, parentIsAwait, true));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Now check for workflow calls
|
|
107
|
+
if (ts.isCallExpression(node)) {
|
|
108
|
+
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
109
|
+
const propAccess = node.expression;
|
|
110
|
+
if ((propAccess.name.text === 'do' || propAccess.name.text === 'sleep') &&
|
|
111
|
+
ts.isIdentifier(propAccess.expression) &&
|
|
112
|
+
propAccess.expression.text === 'workflow') {
|
|
113
|
+
if (!parentIsAwait && !insidePromiseAll) {
|
|
114
|
+
errors.push({
|
|
115
|
+
message: `workflow.${propAccess.name.text}() must be awaited`,
|
|
116
|
+
node,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (ts.isAwaitExpression(node)) {
|
|
124
|
+
// Visit the expression itself with parentIsAwait=true
|
|
125
|
+
visit(node.expression, true, insidePromiseAll);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
ts.forEachChild(node, (child) => visit(child, false, insidePromiseAll));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
visit(node);
|
|
132
|
+
return errors;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Combine all validation errors into a single error message
|
|
136
|
+
*/
|
|
137
|
+
export function formatValidationErrors(errors) {
|
|
138
|
+
if (errors.length === 0) {
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
return errors.map((err) => `- ${err.message}`).join('\n');
|
|
142
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts DSL (Domain Specific Language) step-based format to graph node format
|
|
3
|
+
*/
|
|
4
|
+
import type { WorkflowsMeta } from '@pikku/core/workflow';
|
|
5
|
+
import type { SerializedWorkflowGraph } from './workflow-graph.types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Convert a DSL workflow to graph format
|
|
8
|
+
*/
|
|
9
|
+
export declare function convertDslToGraph(workflowName: string, meta: WorkflowsMeta[string]): SerializedWorkflowGraph;
|
|
10
|
+
/**
|
|
11
|
+
* Convert all DSL workflows to graph format
|
|
12
|
+
*/
|
|
13
|
+
export declare function convertAllDslToGraphs(workflowsMeta: WorkflowsMeta): Record<string, SerializedWorkflowGraph>;
|