@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
@@ -1,29 +1,75 @@
1
1
  import * as ts from 'typescript'
2
- import {
3
- getPropertyValue,
4
- getPropertyTags,
5
- } from '../utils/get-property-value.js'
6
- import { PikkuDocs } from '@pikku/core'
7
2
  import { AddWiring, InspectorState } from '../types.js'
8
3
  import { extractFunctionName } from '../utils/extract-function-name.js'
9
- import {
10
- getPropertyAssignmentInitializer,
11
- resolveFunctionDeclaration,
12
- } from '../utils/type-utils.js'
13
- import { resolveMiddleware } from '../utils/middleware.js'
14
- import { extractWireNames } from '../utils/post-process.js'
4
+ import { extractFunctionNode } from '../utils/extract-function-node.js'
15
5
  import { ErrorCode } from '../error-codes.js'
16
- import { WorkflowStepMeta } from '@pikku/core/workflow'
6
+ import { WorkflowStepMeta, WorkflowContext } from '@pikku/core/workflow'
17
7
  import {
18
8
  extractStringLiteral,
19
- extractNumberLiteral,
20
- extractPropertyString,
21
9
  isStringLike,
22
10
  isFunctionLike,
11
+ extractDescription,
12
+ extractDuration,
23
13
  } from '../utils/extract-node-value.js'
14
+ import { getCommonWireMetaData } from '../utils/get-property-value.js'
15
+ import { extractDSLWorkflow } from '../utils/workflow/dsl/extract-dsl-workflow.js'
16
+
17
+ /**
18
+ * Recursively check if any step has inline type (non-serializable)
19
+ */
20
+ function hasInlineSteps(steps: WorkflowStepMeta[]): boolean {
21
+ for (const step of steps) {
22
+ if (step.type === 'inline') {
23
+ return true
24
+ } else if (step.type === 'branch') {
25
+ for (const branch of step.branches) {
26
+ if (hasInlineSteps(branch.steps)) return true
27
+ }
28
+ if (step.elseSteps && hasInlineSteps(step.elseSteps)) return true
29
+ } else if (step.type === 'switch' && step.cases) {
30
+ for (const c of step.cases) {
31
+ if (c.steps && hasInlineSteps(c.steps)) return true
32
+ }
33
+ if (step.defaultSteps && hasInlineSteps(step.defaultSteps)) return true
34
+ } else if (step.type === 'fanout' && step.child) {
35
+ if (hasInlineSteps([step.child])) return true
36
+ } else if (step.type === 'parallel' && step.children) {
37
+ if (hasInlineSteps(step.children)) return true
38
+ }
39
+ }
40
+ return false
41
+ }
24
42
 
25
43
  /**
26
- * Scan for workflow.do() and workflow.sleep() calls to extract workflow steps
44
+ * Recursively collect all RPC names from workflow steps
45
+ */
46
+ function collectInvokedRPCs(
47
+ steps: WorkflowStepMeta[],
48
+ rpcs: Set<string>
49
+ ): void {
50
+ for (const step of steps) {
51
+ if (step.type === 'rpc' && step.rpcName) {
52
+ rpcs.add(step.rpcName)
53
+ } else if (step.type === 'branch') {
54
+ for (const branch of step.branches) {
55
+ collectInvokedRPCs(branch.steps, rpcs)
56
+ }
57
+ if (step.elseSteps) collectInvokedRPCs(step.elseSteps, rpcs)
58
+ } else if (step.type === 'switch' && step.cases) {
59
+ for (const c of step.cases) {
60
+ if (c.steps) collectInvokedRPCs(c.steps, rpcs)
61
+ }
62
+ if (step.defaultSteps) collectInvokedRPCs(step.defaultSteps, rpcs)
63
+ } else if (step.type === 'fanout' && step.child) {
64
+ collectInvokedRPCs([step.child], rpcs)
65
+ } else if (step.type === 'parallel' && step.children) {
66
+ collectInvokedRPCs(step.children, rpcs)
67
+ }
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Scan for workflow.do(), workflow.sleep(), and workflow.cancel() calls to extract workflow steps
27
73
  */
28
74
  function getWorkflowInvocations(
29
75
  node: ts.Node,
@@ -37,7 +83,7 @@ function getWorkflowInvocations(
37
83
  const { name } = node
38
84
 
39
85
  // Check if this is accessing 'do' or 'sleep' property
40
- if (name.text === 'do' || name.text === 'sleep') {
86
+ if (name.text === 'do' || name.text === 'sleep' || name.text === 'cancel') {
41
87
  // Check if the parent is a call expression
42
88
  const parent = node.parent
43
89
  if (ts.isCallExpression(parent) && parent.expression === node) {
@@ -62,7 +108,6 @@ function getWorkflowInvocations(
62
108
  type: 'rpc',
63
109
  stepName,
64
110
  rpcName,
65
- description,
66
111
  })
67
112
  state.rpc.invokedFunctions.add(rpcName)
68
113
  } else if (isFunctionLike(secondArg)) {
@@ -86,18 +131,20 @@ function getWorkflowInvocations(
86
131
  stepName: stepName || '<dynamic>',
87
132
  duration: duration || '<dynamic>',
88
133
  })
134
+ } else if (name.text === 'cancel') {
135
+ // workflow.cancel(reason?)
136
+ steps.push({
137
+ type: 'cancel',
138
+ })
89
139
  }
90
140
  }
91
141
  }
92
142
  }
93
143
 
94
- // Don't recurse into nested functions - only look at top-level workflow calls
144
+ // Recurse into children, including arrow functions (for Promise.all callbacks)
145
+ // but skip function declarations (which would be separate functions)
95
146
  ts.forEachChild(node, (child) => {
96
- if (
97
- ts.isFunctionDeclaration(child) ||
98
- ts.isFunctionExpression(child) ||
99
- ts.isArrowFunction(child)
100
- ) {
147
+ if (ts.isFunctionDeclaration(child)) {
101
148
  return
102
149
  }
103
150
  getWorkflowInvocations(child, checker, state, workflowName, steps)
@@ -105,43 +152,10 @@ function getWorkflowInvocations(
105
152
  }
106
153
 
107
154
  /**
108
- * Extract description from options object
109
- */
110
- function extractDescription(
111
- optionsNode: ts.Node | undefined,
112
- checker: ts.TypeChecker
113
- ): string | null {
114
- if (!optionsNode || !ts.isObjectLiteralExpression(optionsNode)) {
115
- return null
116
- }
117
- return extractPropertyString(optionsNode, 'description', checker)
118
- }
119
-
120
- /**
121
- * Extract duration value (number or string)
122
- */
123
- function extractDuration(
124
- node: ts.Node,
125
- checker: ts.TypeChecker
126
- ): string | number | null {
127
- const numValue = extractNumberLiteral(node)
128
- if (numValue !== null) {
129
- return numValue
130
- }
131
- return extractStringLiteral(node, checker)
132
- }
133
-
134
- /**
135
- * Inspector for wireWorkflow() calls
155
+ * Inspector for pikkuWorkflow() and pikkuSimpleWorkflow() calls
136
156
  * Detects workflow registration and extracts metadata
137
157
  */
138
- export const addWorkflow: AddWiring = (
139
- logger,
140
- node,
141
- checker,
142
- state,
143
- options
144
- ) => {
158
+ export const addWorkflow: AddWiring = (logger, node, checker, state) => {
145
159
  if (!ts.isCallExpression(node)) {
146
160
  return
147
161
  }
@@ -150,8 +164,16 @@ export const addWorkflow: AddWiring = (
150
164
  const firstArg = args[0]
151
165
  const expression = node.expression
152
166
 
153
- // Check if the call is to wireWorkflow
154
- if (!ts.isIdentifier(expression) || expression.text !== 'wireWorkflow') {
167
+ if (!ts.isIdentifier(expression)) {
168
+ return
169
+ }
170
+
171
+ let wrapperType: 'dsl' | 'regular' | null = null
172
+ if (expression.text === 'pikkuWorkflowFunc') {
173
+ wrapperType = 'dsl'
174
+ } else if (expression.text === 'pikkuWorkflowComplexFunc') {
175
+ wrapperType = 'regular'
176
+ } else {
155
177
  return
156
178
  }
157
179
 
@@ -159,73 +181,147 @@ export const addWorkflow: AddWiring = (
159
181
  return
160
182
  }
161
183
 
162
- if (ts.isObjectLiteralExpression(firstArg)) {
163
- const obj = firstArg
164
-
165
- const workflowName = getPropertyValue(obj, 'name') as string | null
166
- const description = getPropertyValue(obj, 'description') as
167
- | string
168
- | undefined
169
- const docs = (getPropertyValue(obj, 'docs') as PikkuDocs) || undefined
170
- const tags = getPropertyTags(obj, 'Workflow', workflowName, logger)
171
-
172
- // --- find the referenced function ---
173
- const funcInitializer = getPropertyAssignmentInitializer(
174
- obj,
175
- 'func',
176
- true,
177
- checker
184
+ // Extract workflow name and metadata using same logic as add-functions
185
+ const { pikkuFuncName, name, exportedName } = extractFunctionName(
186
+ node,
187
+ checker,
188
+ state.rootDir
189
+ )
190
+
191
+ const workflowName = exportedName || name
192
+
193
+ if (!workflowName) {
194
+ logger.critical(
195
+ ErrorCode.MISSING_NAME,
196
+ `Could not determine workflow name from export.`
178
197
  )
198
+ return
199
+ }
179
200
 
180
- if (!workflowName) {
181
- logger.critical(
182
- ErrorCode.MISSING_NAME,
183
- `Wasn't able to determine 'name' property for workflow wiring.`
184
- )
185
- return
186
- }
201
+ // Extract the function node (either direct function or from config.func)
202
+ const { funcNode, resolvedFunc } = extractFunctionNode(firstArg, checker)
187
203
 
188
- if (!funcInitializer) {
189
- logger.critical(
190
- ErrorCode.MISSING_FUNC,
191
- `No valid 'func' property for workflow '${workflowName}'.`
192
- )
193
- return
194
- }
204
+ // Extract metadata if using object form
205
+ let tags: string[] | undefined
206
+ let summary: string | undefined
207
+ let description: string | undefined
208
+ let errors: string[] | undefined
195
209
 
196
- const pikkuFuncName = extractFunctionName(
197
- funcInitializer,
198
- checker,
199
- state.rootDir
200
- ).pikkuFuncName
210
+ if (ts.isObjectLiteralExpression(firstArg)) {
211
+ const metadata = getCommonWireMetaData(
212
+ firstArg,
213
+ 'Workflow',
214
+ workflowName,
215
+ logger
216
+ )
217
+ tags = metadata.tags
218
+ summary = metadata.summary
219
+ description = metadata.description
220
+ errors = metadata.errors
221
+ }
201
222
 
202
- // --- resolve middleware ---
203
- const middleware = resolveMiddleware(state, obj, tags, checker)
223
+ // Validate that we got a valid function
224
+ if (
225
+ ts.isObjectLiteralExpression(firstArg) &&
226
+ (!funcNode || funcNode === firstArg)
227
+ ) {
228
+ logger.critical(
229
+ ErrorCode.MISSING_FUNC,
230
+ `No valid 'func' property for workflow '${workflowName}'.`
231
+ )
232
+ return
233
+ }
204
234
 
205
- // --- track used functions/middleware for service aggregation ---
206
- state.serviceAggregation.usedFunctions.add(pikkuFuncName)
207
- extractWireNames(middleware).forEach((name) =>
208
- state.serviceAggregation.usedMiddleware.add(name)
235
+ if (!resolvedFunc) {
236
+ logger.critical(
237
+ ErrorCode.MISSING_FUNC,
238
+ `Could not resolve workflow function for '${workflowName}'.`
209
239
  )
240
+ return
241
+ }
210
242
 
211
- state.workflows.files.add(node.getSourceFile().fileName)
243
+ // Track workflow file for wiring generation
244
+ if (exportedName) {
245
+ state.workflows.files.set(pikkuFuncName, {
246
+ path: node.getSourceFile().fileName,
247
+ exportedName,
248
+ })
249
+ }
212
250
 
213
- // Extract workflow steps from function body
214
- // Resolve the identifier to the actual function declaration
215
- const resolvedFunc = resolveFunctionDeclaration(funcInitializer, checker)
216
- const steps: WorkflowStepMeta[] = []
217
- if (resolvedFunc) {
218
- getWorkflowInvocations(resolvedFunc, checker, state, workflowName, steps)
251
+ let steps: WorkflowStepMeta[] = []
252
+ let context: WorkflowContext | undefined = undefined
253
+ let dsl: boolean | undefined = undefined
254
+
255
+ // Try DSL workflow extraction first
256
+ // Pass the whole CallExpression node so findWorkflowFunction can find the arrow function
257
+ const result = extractDSLWorkflow(node, checker)
258
+
259
+ if (result.status === 'ok' && result.steps) {
260
+ // Extraction succeeded
261
+ steps = result.steps
262
+ context = result.context
263
+
264
+ // Check if workflow contains inline steps (non-serializable)
265
+ if (hasInlineSteps(steps)) {
266
+ if (wrapperType === 'dsl') {
267
+ // pikkuWorkflowFunc should not have inline steps
268
+ logger.critical(
269
+ ErrorCode.INVALID_DSL_WORKFLOW,
270
+ `Workflow '${workflowName}' uses pikkuWorkflowFunc but contains inline steps which are not allowed in DSL workflows. Use pikkuWorkflowComplexFunc instead.`
271
+ )
272
+ return
273
+ }
274
+ // pikkuWorkflowComplexFunc with inline steps is marked as non-dsl
275
+ dsl = false
276
+ } else {
277
+ // pikkuWorkflowComplexFunc is always non-dsl, pikkuWorkflowFunc is dsl
278
+ dsl = wrapperType === 'dsl'
219
279
  }
220
280
 
221
- state.workflows.meta[workflowName] = {
222
- pikkuFuncName,
223
- workflowName,
224
- description,
225
- docs,
226
- tags,
227
- middleware,
228
- steps,
281
+ // Collect all invoked RPCs from workflow steps
282
+ const rpcs = new Set<string>()
283
+ collectInvokedRPCs(steps, rpcs)
284
+ for (const rpc of rpcs) {
285
+ state.rpc.invokedFunctions.add(rpc)
286
+ }
287
+ } else {
288
+ // DSL extraction failed
289
+ if (wrapperType === 'dsl') {
290
+ // For pikkuWorkflowFunc, this is a critical error
291
+ // But still track RPC invocations for function registration
292
+ getWorkflowInvocations(resolvedFunc, checker, state, workflowName, steps)
293
+ logger.critical(
294
+ ErrorCode.INVALID_DSL_WORKFLOW,
295
+ `Workflow '${workflowName}' uses pikkuWorkflowFunc but does not conform to DSL workflow rules:\n${result.reason || 'Unknown error'}`
296
+ )
297
+ return
298
+ } else {
299
+ // For pikkuWorkflowComplexFunc, fall back to basic extraction
300
+ logger.debug(
301
+ `Workflow '${workflowName}' could not be extracted as DSL workflow: ${result.reason || 'Unknown error'}. Falling back to basic extraction.`
302
+ )
303
+ dsl = false
229
304
  }
230
305
  }
306
+
307
+ /**
308
+ * For non-dsl workflows or pikkuWorkflowComplexFunc, run basic extraction
309
+ * to ensure all RPC invocations are tracked for function registration.
310
+ * This catches RPCs in Promise.all callbacks and other patterns DSL can't extract.
311
+ */
312
+ if (!dsl || wrapperType === 'regular') {
313
+ getWorkflowInvocations(resolvedFunc, checker, state, workflowName, steps)
314
+ }
315
+
316
+ state.workflows.meta[workflowName] = {
317
+ pikkuFuncName,
318
+ workflowName,
319
+ steps,
320
+ context,
321
+ dsl,
322
+ summary,
323
+ description,
324
+ errors,
325
+ tags,
326
+ }
231
327
  }
@@ -11,6 +11,7 @@ export enum ErrorCode {
11
11
  // Validation errors
12
12
  MISSING_NAME = 'PKU111',
13
13
  MISSING_DESCRIPTION = 'PKU123',
14
+ INVALID_VALUE = 'PKU124',
14
15
  MISSING_URI = 'PKU220',
15
16
  MISSING_FUNC = 'PKU236',
16
17
  INVALID_TAGS_TYPE = 'PKU247',
@@ -21,6 +22,7 @@ export enum ErrorCode {
21
22
  CLI_CLIENTSIDE_RENDERER_HAS_SERVICES = 'PKU672',
22
23
  DYNAMIC_STEP_NAME = 'PKU529',
23
24
  WORKFLOW_ORCHESTRATOR_NOT_CONFIGURED = 'PKU600',
25
+ INVALID_DSL_WORKFLOW = 'PKU641',
24
26
 
25
27
  // Configuration errors
26
28
  CONFIG_TYPE_NOT_FOUND = 'PKU426',
@@ -28,6 +30,7 @@ export enum ErrorCode {
28
30
  SCHEMA_NO_ROOT = 'PKU431',
29
31
  SCHEMA_GENERATION_ERROR = 'PKU456',
30
32
  SCHEMA_LOAD_ERROR = 'PKU488',
33
+ INLINE_ZOD_SCHEMA = 'PKU489',
31
34
 
32
35
  // Function errors
33
36
  FUNCTION_METADATA_NOT_FOUND = 'PKU559',
package/src/index.ts CHANGED
@@ -14,3 +14,15 @@ export {
14
14
  } from './utils/serialize-inspector-state.js'
15
15
  export type { SerializableInspectorState } from './utils/serialize-inspector-state.js'
16
16
  export { filterInspectorState } from './utils/filter-inspector-state.js'
17
+ export { writeAllServiceMetadata } from './utils/write-service-metadata.js'
18
+ export type { ServiceMetadata } from './utils/extract-service-metadata.js'
19
+ export {
20
+ convertAllDslToGraphs,
21
+ convertDslToGraph,
22
+ } from './utils/workflow/graph/convert-dsl-to-graph.js'
23
+ export {
24
+ deserializeDslWorkflow,
25
+ deserializeGraphWorkflow,
26
+ deserializeAllDslWorkflows,
27
+ } from './utils/workflow/dsl/deserialize-dsl-workflow.js'
28
+ export type { SerializedWorkflowGraph } from './utils/workflow/graph/workflow-graph.types.js'
package/src/inspector.ts CHANGED
@@ -5,7 +5,10 @@ import { TypesMap } from './types-map.js'
5
5
  import { InspectorState, InspectorLogger, InspectorOptions } from './types.js'
6
6
  import { getFilesAndMethods } from './utils/get-files-and-methods.js'
7
7
  import { findCommonAncestor } from './utils/find-root-dir.js'
8
- import { aggregateRequiredServices } from './utils/post-process.js'
8
+ import {
9
+ aggregateRequiredServices,
10
+ extractServiceInterfaceMetadata,
11
+ } from './utils/post-process.js'
9
12
 
10
13
  /**
11
14
  * Creates an initial/empty inspector state with all required properties initialized
@@ -16,16 +19,17 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
16
19
  return {
17
20
  rootDir,
18
21
  singletonServicesTypeImportMap: new Map(),
19
- sessionServicesTypeImportMap: new Map(),
22
+ wireServicesTypeImportMap: new Map(),
20
23
  userSessionTypeImportMap: new Map(),
21
24
  configTypeImportMap: new Map(),
22
25
  singletonServicesFactories: new Map(),
23
- sessionServicesFactories: new Map(),
24
- sessionServicesMeta: new Map(),
26
+ wireServicesFactories: new Map(),
27
+ wireServicesMeta: new Map(),
25
28
  configFactories: new Map(),
26
29
  filesAndMethods: {},
27
30
  filesAndMethodsErrors: new Map(),
28
31
  typesLookup: new Map(),
32
+ zodLookup: new Map(),
29
33
  functions: {
30
34
  typesMap: new TypesMap(),
31
35
  meta: {},
@@ -60,7 +64,9 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
60
64
  },
61
65
  workflows: {
62
66
  meta: {},
63
- files: new Set(),
67
+ files: new Map(),
68
+ graphMeta: {},
69
+ graphFiles: new Map(),
64
70
  },
65
71
  rpc: {
66
72
  internalMeta: {},
@@ -68,6 +74,7 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
68
74
  exposedMeta: {},
69
75
  exposedFiles: new Map(),
70
76
  invokedFunctions: new Set(),
77
+ usedExternalPackages: new Set(),
71
78
  },
72
79
  mcpEndpoints: {
73
80
  resourcesMeta: {},
@@ -82,6 +89,14 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
82
89
  },
83
90
  files: new Set(),
84
91
  },
92
+ forgeNodes: {
93
+ meta: {},
94
+ files: new Set(),
95
+ },
96
+ forgeCredentials: {
97
+ meta: {},
98
+ files: new Set(),
99
+ },
85
100
  middleware: {
86
101
  meta: {},
87
102
  tagMiddleware: new Map(),
@@ -96,8 +111,9 @@ export function getInitialInspectorState(rootDir: string): InspectorState {
96
111
  usedMiddleware: new Set(),
97
112
  usedPermissions: new Set(),
98
113
  allSingletonServices: [],
99
- allSessionServices: [],
114
+ allWireServices: [],
100
115
  },
116
+ serviceMetadata: [],
101
117
  }
102
118
  }
103
119
 
@@ -127,15 +143,19 @@ export const inspect = (
127
143
  `Got type checker in ${(performance.now() - startChecker).toFixed(2)}ms`
128
144
  )
129
145
 
146
+ // Use provided rootDir or infer from source files
147
+ const rootDir = options.rootDir || findCommonAncestor(routeFiles)
148
+
130
149
  const startSourceFiles = performance.now()
131
- const sourceFiles = program.getSourceFiles()
150
+ // Filter source files to only include files within the project rootDir
151
+ // This prevents picking up types from external packages (including workspace symlinks)
152
+ const sourceFiles = program
153
+ .getSourceFiles()
154
+ .filter((sf) => sf.fileName.startsWith(rootDir))
132
155
  logger.debug(
133
156
  `Got source files in ${(performance.now() - startSourceFiles).toFixed(2)}ms`
134
157
  )
135
158
 
136
- // Infer root directory from source files
137
- const rootDir = findCommonAncestor(routeFiles)
138
-
139
159
  const state = getInitialInspectorState(rootDir)
140
160
 
141
161
  // First sweep: add all functions
@@ -177,6 +197,12 @@ export const inspect = (
177
197
  logger.debug(
178
198
  `Aggregate required services completed in ${(performance.now() - startAggregate).toFixed(2)}ms`
179
199
  )
200
+
201
+ const startServiceMeta = performance.now()
202
+ extractServiceInterfaceMetadata(state, checker)
203
+ logger.debug(
204
+ `Extract service metadata completed in ${(performance.now() - startServiceMeta).toFixed(2)}ms`
205
+ )
180
206
  }
181
207
 
182
208
  return state
package/src/types.ts CHANGED
@@ -6,9 +6,11 @@ import { QueueWorkersMeta } from '@pikku/core/queue'
6
6
  import { WorkflowsMeta } from '@pikku/core/workflow'
7
7
  import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core/mcp'
8
8
  import { CLIMeta } from '@pikku/core/cli'
9
+ import { ForgeNodesMeta, ForgeCredentialsMeta } from '@pikku/core/forge-node'
9
10
  import { TypesMap } from './types-map.js'
10
11
  import { FunctionsMeta, FunctionServicesMeta } from '@pikku/core'
11
12
  import { ErrorCode } from './error-codes.js'
13
+ import type { SerializedWorkflowGraphs } from './utils/workflow/graph/workflow-graph.types.js'
12
14
 
13
15
  export type PathToNameAndType = Map<
14
16
  string,
@@ -56,6 +58,14 @@ export interface InspectorHTTPState {
56
58
  routePermissions: Map<string, PermissionGroupMeta>
57
59
  }
58
60
 
61
+ /**
62
+ * Zod schema reference for deferred conversion to JSON Schema at build time
63
+ */
64
+ export interface ZodSchemaRef {
65
+ variableName: string
66
+ sourceFile: string
67
+ }
68
+
59
69
  export interface InspectorFunctionState {
60
70
  typesMap: TypesMap
61
71
  meta: FunctionsMeta
@@ -116,11 +126,13 @@ export type InspectorFilters = {
116
126
 
117
127
  export type InspectorOptions = Partial<{
118
128
  setupOnly: boolean
129
+ /** Project root directory - used to filter out external package files */
130
+ rootDir: string
119
131
  types: Partial<{
120
132
  configFileType: string
121
133
  userSessionType: string
122
134
  singletonServicesFactoryType: string
123
- sessionServicesFactoryType: string
135
+ wireServicesFactoryType: string
124
136
  }>
125
137
  }>
126
138
 
@@ -147,7 +159,7 @@ export interface InspectorFilesAndMethods {
147
159
  type: string
148
160
  typePath: string
149
161
  }
150
- sessionServicesType?: {
162
+ wireServicesType?: {
151
163
  file: string
152
164
  variable: string
153
165
  type: string
@@ -177,7 +189,7 @@ export interface InspectorFilesAndMethods {
177
189
  type: string
178
190
  typePath: string
179
191
  }
180
- sessionServicesFactory?: {
192
+ wireServicesFactory?: {
181
193
  file: string
182
194
  variable: string
183
195
  type: string
@@ -188,16 +200,17 @@ export interface InspectorFilesAndMethods {
188
200
  export interface InspectorState {
189
201
  rootDir: string // Root directory inferred from source files
190
202
  singletonServicesTypeImportMap: PathToNameAndType
191
- sessionServicesTypeImportMap: PathToNameAndType
203
+ wireServicesTypeImportMap: PathToNameAndType
192
204
  userSessionTypeImportMap: PathToNameAndType
193
205
  configTypeImportMap: PathToNameAndType
194
206
  singletonServicesFactories: PathToNameAndType
195
- sessionServicesFactories: PathToNameAndType
196
- sessionServicesMeta: Map<string, string[]> // variable name -> singleton services consumed
207
+ wireServicesFactories: PathToNameAndType
208
+ wireServicesMeta: Map<string, string[]> // variable name -> singleton services consumed
197
209
  configFactories: PathToNameAndType
198
210
  filesAndMethods: InspectorFilesAndMethods
199
211
  filesAndMethodsErrors: Map<string, PathToNameAndType>
200
212
  typesLookup: Map<string, ts.Type[]> // Lookup for types by name (e.g., function input types, Config type)
213
+ zodLookup: Map<string, ZodSchemaRef> // Lookup for Zod schemas by name for deferred JSON Schema conversion
201
214
  http: InspectorHTTPState
202
215
  functions: InspectorFunctionState
203
216
  channels: InspectorChannelState
@@ -211,14 +224,17 @@ export interface InspectorState {
211
224
  }
212
225
  workflows: {
213
226
  meta: WorkflowsMeta
214
- files: Set<string>
227
+ files: Map<string, { path: string; exportedName: string }>
228
+ graphMeta: SerializedWorkflowGraphs
229
+ graphFiles: Map<string, { path: string; exportedName: string }>
215
230
  }
216
231
  rpc: {
217
232
  internalMeta: Record<string, string>
218
233
  internalFiles: Map<string, { path: string; exportedName: string }>
219
234
  exposedMeta: Record<string, string>
220
235
  exposedFiles: Map<string, { path: string; exportedName: string }>
221
- invokedFunctions: Set<string> // Track functions called via rpc.invoke()
236
+ invokedFunctions: Set<string>
237
+ usedExternalPackages: Set<string>
222
238
  }
223
239
  mcpEndpoints: {
224
240
  resourcesMeta: MCPResourceMeta
@@ -230,6 +246,14 @@ export interface InspectorState {
230
246
  meta: CLIMeta
231
247
  files: Set<string>
232
248
  }
249
+ forgeNodes: {
250
+ meta: ForgeNodesMeta
251
+ files: Set<string>
252
+ }
253
+ forgeCredentials: {
254
+ meta: ForgeCredentialsMeta
255
+ files: Set<string>
256
+ }
233
257
  middleware: InspectorMiddlewareState
234
258
  permissions: InspectorPermissionState
235
259
  serviceAggregation: {
@@ -238,6 +262,16 @@ export interface InspectorState {
238
262
  usedMiddleware: Set<string> // Middleware names used by wired functions
239
263
  usedPermissions: Set<string> // Permission names used by wired functions
240
264
  allSingletonServices: string[] // All services available in SingletonServices type
241
- allSessionServices: string[] // All services available in Services type (excluding SingletonServices)
265
+ allWireServices: string[] // All services available in Services type (excluding SingletonServices)
242
266
  }
267
+ serviceMetadata: Array<{
268
+ name: string
269
+ summary: string
270
+ description: string
271
+ package: string
272
+ path: string
273
+ version: string
274
+ interface: string
275
+ expandedProperties: Record<string, string>
276
+ }>
243
277
  }