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