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