@pikku/inspector 0.11.2 → 0.12.0

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 (182) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/OPTIMIZATION-PLAN.md +195 -0
  3. package/dist/add/add-ai-agent.d.ts +2 -0
  4. package/dist/add/add-ai-agent.js +314 -0
  5. package/dist/add/add-channel.js +69 -61
  6. package/dist/add/add-cli.js +36 -18
  7. package/dist/add/add-file-with-factory.js +2 -0
  8. package/dist/add/add-functions.js +250 -75
  9. package/dist/add/add-http-route.d.ts +19 -10
  10. package/dist/add/add-http-route.js +152 -66
  11. package/dist/add/add-http-routes.d.ts +5 -0
  12. package/dist/add/add-http-routes.js +159 -0
  13. package/dist/add/add-keyed-wiring.d.ts +12 -0
  14. package/dist/add/add-keyed-wiring.js +97 -0
  15. package/dist/add/add-mcp-prompt.js +14 -9
  16. package/dist/add/add-mcp-resource.js +14 -9
  17. package/dist/add/add-middleware.d.ts +1 -4
  18. package/dist/add/add-middleware.js +364 -79
  19. package/dist/add/add-permission.d.ts +1 -1
  20. package/dist/add/add-permission.js +152 -40
  21. package/dist/add/add-queue-worker.js +18 -12
  22. package/dist/add/add-rpc-invocations.js +14 -0
  23. package/dist/add/add-schedule.js +11 -5
  24. package/dist/add/add-secret.d.ts +3 -0
  25. package/dist/add/add-secret.js +82 -0
  26. package/dist/add/add-trigger.d.ts +2 -0
  27. package/dist/add/add-trigger.js +87 -0
  28. package/dist/add/add-variable.d.ts +1 -0
  29. package/dist/add/add-variable.js +8 -0
  30. package/dist/add/add-workflow-graph.d.ts +3 -2
  31. package/dist/add/add-workflow-graph.js +143 -406
  32. package/dist/add/add-workflow.js +6 -4
  33. package/dist/error-codes.d.ts +14 -1
  34. package/dist/error-codes.js +19 -1
  35. package/dist/index.d.ts +9 -8
  36. package/dist/index.js +5 -4
  37. package/dist/inspector.d.ts +1 -1
  38. package/dist/inspector.js +91 -14
  39. package/dist/schema-generator.d.ts +1 -0
  40. package/dist/schema-generator.js +1 -0
  41. package/dist/types-map.js +10 -1
  42. package/dist/types.d.ts +163 -39
  43. package/dist/utils/compute-required-schemas.d.ts +4 -0
  44. package/dist/utils/compute-required-schemas.js +41 -0
  45. package/dist/utils/contract-hashes.d.ts +35 -0
  46. package/dist/utils/contract-hashes.js +202 -0
  47. package/dist/utils/custom-types-generator.d.ts +9 -0
  48. package/dist/utils/custom-types-generator.js +71 -0
  49. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  50. package/dist/utils/detect-schema-vendor.js +76 -0
  51. package/dist/utils/ensure-function-metadata.d.ts +5 -2
  52. package/dist/utils/ensure-function-metadata.js +220 -6
  53. package/dist/utils/extract-function-name.d.ts +5 -16
  54. package/dist/utils/extract-function-name.js +86 -291
  55. package/dist/utils/extract-services.d.ts +2 -1
  56. package/dist/utils/extract-services.js +25 -1
  57. package/dist/utils/filter-inspector-state.js +107 -23
  58. package/dist/utils/get-property-value.d.ts +6 -1
  59. package/dist/utils/get-property-value.js +28 -3
  60. package/dist/utils/hash.d.ts +2 -0
  61. package/dist/utils/hash.js +23 -0
  62. package/dist/utils/middleware.d.ts +7 -30
  63. package/dist/utils/middleware.js +80 -66
  64. package/dist/utils/permissions.d.ts +2 -2
  65. package/dist/utils/permissions.js +10 -10
  66. package/dist/utils/post-process.d.ts +9 -10
  67. package/dist/utils/post-process.js +231 -24
  68. package/dist/utils/resolve-external-package.d.ts +12 -0
  69. package/dist/utils/resolve-external-package.js +34 -0
  70. package/dist/utils/resolve-function-types.d.ts +6 -0
  71. package/dist/utils/resolve-function-types.js +29 -0
  72. package/dist/utils/resolve-identifier.d.ts +10 -0
  73. package/dist/utils/resolve-identifier.js +36 -0
  74. package/dist/utils/resolve-versions.d.ts +2 -0
  75. package/dist/utils/resolve-versions.js +78 -0
  76. package/dist/utils/schema-generator.d.ts +9 -0
  77. package/dist/utils/schema-generator.js +209 -0
  78. package/dist/utils/serialize-inspector-state.d.ts +59 -22
  79. package/dist/utils/serialize-inspector-state.js +92 -20
  80. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  81. package/dist/utils/serialize-mcp-json.js +99 -0
  82. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  83. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  84. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  85. package/dist/utils/serialize-openapi-json.js +151 -0
  86. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  87. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  88. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  89. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  90. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  91. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  92. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  93. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  94. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  95. package/dist/utils/workflow/graph/index.d.ts +2 -0
  96. package/dist/utils/workflow/graph/index.js +2 -0
  97. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  98. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  99. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  100. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  101. package/dist/visit.js +11 -6
  102. package/package.json +14 -4
  103. package/src/add/add-ai-agent.ts +468 -0
  104. package/src/add/add-channel.ts +82 -79
  105. package/src/add/add-cli.ts +49 -20
  106. package/src/add/add-file-with-factory.ts +2 -0
  107. package/src/add/add-functions.ts +330 -86
  108. package/src/add/add-http-route.ts +245 -88
  109. package/src/add/add-http-routes.ts +228 -0
  110. package/src/add/add-keyed-wiring.ts +151 -0
  111. package/src/add/add-mcp-prompt.ts +26 -15
  112. package/src/add/add-mcp-resource.ts +27 -15
  113. package/src/add/add-middleware.ts +482 -80
  114. package/src/add/add-permission.ts +199 -40
  115. package/src/add/add-queue-worker.ts +24 -19
  116. package/src/add/add-rpc-invocations.ts +17 -0
  117. package/src/add/add-schedule.ts +16 -11
  118. package/src/add/add-secret.ts +140 -0
  119. package/src/add/add-trigger.ts +154 -0
  120. package/src/add/add-variable.ts +9 -0
  121. package/src/add/add-workflow-graph.ts +180 -522
  122. package/src/add/add-workflow.ts +5 -4
  123. package/src/error-codes.ts +24 -1
  124. package/src/index.ts +22 -13
  125. package/src/inspector.ts +129 -17
  126. package/src/schema-generator.ts +1 -0
  127. package/src/types-map.ts +12 -1
  128. package/src/types.ts +175 -58
  129. package/src/utils/compute-required-schemas.ts +49 -0
  130. package/src/utils/contract-hashes.test.ts +528 -0
  131. package/src/utils/contract-hashes.ts +290 -0
  132. package/src/utils/custom-types-generator.ts +88 -0
  133. package/src/utils/detect-schema-vendor.ts +90 -0
  134. package/src/utils/ensure-function-metadata.ts +324 -7
  135. package/src/utils/extract-function-name.ts +101 -351
  136. package/src/utils/extract-services.ts +35 -2
  137. package/src/utils/filter-inspector-state.test.ts +34 -20
  138. package/src/utils/filter-inspector-state.ts +140 -31
  139. package/src/utils/get-property-value.ts +42 -4
  140. package/src/utils/hash.ts +26 -0
  141. package/src/utils/middleware.test.ts +204 -0
  142. package/src/utils/middleware.ts +129 -67
  143. package/src/utils/permissions.test.ts +35 -12
  144. package/src/utils/permissions.ts +10 -10
  145. package/src/utils/post-process.ts +283 -43
  146. package/src/utils/resolve-external-package.ts +42 -0
  147. package/src/utils/resolve-function-types.ts +42 -0
  148. package/src/utils/resolve-identifier.ts +46 -0
  149. package/src/utils/resolve-versions.test.ts +249 -0
  150. package/src/utils/resolve-versions.ts +105 -0
  151. package/src/utils/schema-generator.ts +329 -0
  152. package/src/utils/serialize-inspector-state.ts +163 -40
  153. package/src/utils/serialize-mcp-json.ts +145 -0
  154. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  155. package/src/utils/serialize-openapi-json.ts +277 -0
  156. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  157. package/src/utils/test-data/inspector-state.json +69 -66
  158. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  159. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +24 -4
  160. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  161. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  162. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  163. package/src/utils/workflow/graph/index.ts +5 -0
  164. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  165. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  166. package/src/visit.ts +12 -6
  167. package/tsconfig.tsbuildinfo +1 -1
  168. package/dist/add/add-forge-credential.d.ts +0 -8
  169. package/dist/add/add-forge-credential.js +0 -77
  170. package/dist/add/add-forge-node.d.ts +0 -7
  171. package/dist/add/add-forge-node.js +0 -77
  172. package/dist/add/add-mcp-tool.d.ts +0 -2
  173. package/dist/add/add-mcp-tool.js +0 -81
  174. package/dist/utils/extract-service-metadata.d.ts +0 -19
  175. package/dist/utils/extract-service-metadata.js +0 -244
  176. package/dist/utils/write-service-metadata.d.ts +0 -13
  177. package/dist/utils/write-service-metadata.js +0 -37
  178. package/src/add/add-forge-credential.ts +0 -119
  179. package/src/add/add-forge-node.ts +0 -132
  180. package/src/add/add-mcp-tool.ts +0 -141
  181. package/src/utils/extract-service-metadata.ts +0 -353
  182. package/src/utils/write-service-metadata.ts +0 -51
@@ -0,0 +1,204 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import { resolveAIMiddleware } from './middleware.js'
4
+ import { getInitialInspectorState } from '../inspector.js'
5
+ import * as ts from 'typescript'
6
+
7
+ function createSourceFile(code: string): ts.SourceFile {
8
+ return ts.createSourceFile('test.ts', code, ts.ScriptTarget.ESNext, true)
9
+ }
10
+
11
+ function getObjectLiteral(
12
+ sourceFile: ts.SourceFile
13
+ ): ts.ObjectLiteralExpression {
14
+ let result: ts.ObjectLiteralExpression | undefined
15
+ ts.forEachChild(sourceFile, function visit(node) {
16
+ if (ts.isObjectLiteralExpression(node) && !result) {
17
+ result = node
18
+ }
19
+ ts.forEachChild(node, visit)
20
+ })
21
+ if (!result) throw new Error('No object literal found')
22
+ return result
23
+ }
24
+
25
+ function createMockChecker(): ts.TypeChecker {
26
+ return {} as any
27
+ }
28
+
29
+ describe('resolveAIMiddleware', () => {
30
+ test('should return undefined when no aiMiddleware property exists', () => {
31
+ const state = getInitialInspectorState('/test')
32
+ const src = createSourceFile('const x = { name: "test" }')
33
+ const obj = getObjectLiteral(src)
34
+ const checker = createMockChecker()
35
+
36
+ const result = resolveAIMiddleware(state, obj, checker)
37
+
38
+ assert.strictEqual(result, undefined)
39
+ })
40
+
41
+ test('should return undefined when aiMiddleware array is empty', () => {
42
+ const state = getInitialInspectorState('/test')
43
+ const src = createSourceFile('const x = { aiMiddleware: [] }')
44
+ const obj = getObjectLiteral(src)
45
+ const checker = createMockChecker()
46
+
47
+ const result = resolveAIMiddleware(state, obj, checker)
48
+
49
+ assert.strictEqual(result, undefined)
50
+ })
51
+
52
+ test('should resolve aiMiddleware identifiers as wire metadata', () => {
53
+ const state = getInitialInspectorState('/test')
54
+ state.aiMiddleware.definitions['myAIMW'] = {
55
+ services: { optimized: true, services: ['logger'] },
56
+ sourceFile: '/test/middleware.ts',
57
+ position: 0,
58
+ exportedName: 'myAIMW',
59
+ }
60
+
61
+ const code = 'const x = { aiMiddleware: [myAIMW] }'
62
+ const program = ts.createProgram({
63
+ rootNames: ['test.ts'],
64
+ options: { target: ts.ScriptTarget.ESNext },
65
+ host: {
66
+ ...ts.createCompilerHost({}),
67
+ getSourceFile: (name) => {
68
+ if (name === 'test.ts')
69
+ return ts.createSourceFile(name, code, ts.ScriptTarget.ESNext, true)
70
+ return undefined
71
+ },
72
+ fileExists: (name) => name === 'test.ts',
73
+ readFile: (name) => (name === 'test.ts' ? code : undefined),
74
+ },
75
+ })
76
+ const checker = program.getTypeChecker()
77
+ const sourceFile = program.getSourceFile('test.ts')!
78
+ const obj = getObjectLiteral(sourceFile)
79
+
80
+ const result = resolveAIMiddleware(state, obj, checker)
81
+
82
+ assert.ok(result)
83
+ assert.strictEqual(result.length, 1)
84
+ assert.deepStrictEqual(result[0], {
85
+ type: 'wire',
86
+ name: 'myAIMW',
87
+ inline: false,
88
+ })
89
+ })
90
+
91
+ test('should resolve multiple aiMiddleware entries', () => {
92
+ const state = getInitialInspectorState('/test')
93
+ state.aiMiddleware.definitions['firstMW'] = {
94
+ services: { optimized: true, services: ['logger'] },
95
+ sourceFile: '/test/middleware.ts',
96
+ position: 0,
97
+ exportedName: 'firstMW',
98
+ }
99
+ state.aiMiddleware.definitions['secondMW'] = {
100
+ services: { optimized: true, services: [] },
101
+ sourceFile: '/test/middleware.ts',
102
+ position: 100,
103
+ exportedName: 'secondMW',
104
+ }
105
+
106
+ const code = 'const x = { aiMiddleware: [firstMW, secondMW] }'
107
+ const program = ts.createProgram({
108
+ rootNames: ['test.ts'],
109
+ options: { target: ts.ScriptTarget.ESNext },
110
+ host: {
111
+ ...ts.createCompilerHost({}),
112
+ getSourceFile: (name) => {
113
+ if (name === 'test.ts')
114
+ return ts.createSourceFile(name, code, ts.ScriptTarget.ESNext, true)
115
+ return undefined
116
+ },
117
+ fileExists: (name) => name === 'test.ts',
118
+ readFile: (name) => (name === 'test.ts' ? code : undefined),
119
+ },
120
+ })
121
+ const checker = program.getTypeChecker()
122
+ const sourceFile = program.getSourceFile('test.ts')!
123
+ const obj = getObjectLiteral(sourceFile)
124
+
125
+ const result = resolveAIMiddleware(state, obj, checker)
126
+
127
+ assert.ok(result)
128
+ assert.strictEqual(result.length, 2)
129
+ assert.strictEqual(result[0].type, 'wire')
130
+ assert.strictEqual((result[0] as any).name, 'firstMW')
131
+ assert.strictEqual(result[1].type, 'wire')
132
+ assert.strictEqual((result[1] as any).name, 'secondMW')
133
+ })
134
+
135
+ test('should mark inline aiMiddleware (null exportedName)', () => {
136
+ const state = getInitialInspectorState('/test')
137
+ state.aiMiddleware.definitions['inlineMW'] = {
138
+ services: { optimized: true, services: [] },
139
+ sourceFile: '/test/middleware.ts',
140
+ position: 0,
141
+ exportedName: null,
142
+ }
143
+
144
+ const code = 'const x = { aiMiddleware: [inlineMW] }'
145
+ const program = ts.createProgram({
146
+ rootNames: ['test.ts'],
147
+ options: { target: ts.ScriptTarget.ESNext },
148
+ host: {
149
+ ...ts.createCompilerHost({}),
150
+ getSourceFile: (name) => {
151
+ if (name === 'test.ts')
152
+ return ts.createSourceFile(name, code, ts.ScriptTarget.ESNext, true)
153
+ return undefined
154
+ },
155
+ fileExists: (name) => name === 'test.ts',
156
+ readFile: (name) => (name === 'test.ts' ? code : undefined),
157
+ },
158
+ })
159
+ const checker = program.getTypeChecker()
160
+ const sourceFile = program.getSourceFile('test.ts')!
161
+ const obj = getObjectLiteral(sourceFile)
162
+
163
+ const result = resolveAIMiddleware(state, obj, checker)
164
+
165
+ assert.ok(result)
166
+ assert.strictEqual(result.length, 1)
167
+ assert.deepStrictEqual(result[0], {
168
+ type: 'wire',
169
+ name: 'inlineMW',
170
+ inline: true,
171
+ })
172
+ })
173
+
174
+ test('should handle aiMiddleware referencing unknown definitions', () => {
175
+ const state = getInitialInspectorState('/test')
176
+
177
+ const code = 'const x = { aiMiddleware: [unknownMW] }'
178
+ const program = ts.createProgram({
179
+ rootNames: ['test.ts'],
180
+ options: { target: ts.ScriptTarget.ESNext },
181
+ host: {
182
+ ...ts.createCompilerHost({}),
183
+ getSourceFile: (name) => {
184
+ if (name === 'test.ts')
185
+ return ts.createSourceFile(name, code, ts.ScriptTarget.ESNext, true)
186
+ return undefined
187
+ },
188
+ fileExists: (name) => name === 'test.ts',
189
+ readFile: (name) => (name === 'test.ts' ? code : undefined),
190
+ },
191
+ })
192
+ const checker = program.getTypeChecker()
193
+ const sourceFile = program.getSourceFile('test.ts')!
194
+ const obj = getObjectLiteral(sourceFile)
195
+
196
+ const result = resolveAIMiddleware(state, obj, checker)
197
+
198
+ assert.ok(result)
199
+ assert.strictEqual(result.length, 1)
200
+ assert.strictEqual(result[0].type, 'wire')
201
+ assert.strictEqual((result[0] as any).name, 'unknownMW')
202
+ assert.strictEqual((result[0] as any).inline, false)
203
+ })
204
+ })
@@ -3,44 +3,59 @@ import { MiddlewareMetadata } from '@pikku/core'
3
3
  import { extractFunctionName } from './extract-function-name.js'
4
4
  import { InspectorState } from '../types.js'
5
5
 
6
- /**
7
- * Extract middleware pikkuFuncNames from an array literal expression
8
- * Resolves each identifier to its pikkuFuncName using extractFunctionName
9
- * Also handles call expressions (like logCommandInfoAndTime({...}))
10
- */
11
- export function extractMiddlewarePikkuNames(
6
+ export interface MiddlewareRef {
7
+ definitionId: string
8
+ isFactoryCall: boolean
9
+ }
10
+
11
+ export function extractMiddlewareRefs(
12
12
  arrayNode: ts.Expression,
13
13
  checker: ts.TypeChecker,
14
14
  rootDir: string
15
- ): string[] {
15
+ ): MiddlewareRef[] {
16
16
  if (!ts.isArrayLiteralExpression(arrayNode)) {
17
17
  return []
18
18
  }
19
19
 
20
- const names: string[] = []
20
+ const refs: MiddlewareRef[] = []
21
21
  for (const element of arrayNode.elements) {
22
22
  if (ts.isIdentifier(element)) {
23
- // Resolve the identifier to its pikkuFuncName
24
- const { pikkuFuncName } = extractFunctionName(element, checker, rootDir)
25
- names.push(pikkuFuncName)
23
+ const { pikkuFuncId } = extractFunctionName(element, checker, rootDir)
24
+ refs.push({
25
+ definitionId: pikkuFuncId.startsWith('__temp_')
26
+ ? element.text
27
+ : pikkuFuncId,
28
+ isFactoryCall: false,
29
+ })
26
30
  } else if (ts.isCallExpression(element)) {
27
- // Handle call expressions like rateLimiter(10) or logCommandInfoAndTime({...})
28
- // Extract the function being called (e.g., 'rateLimiter' from 'rateLimiter(10)')
29
- const { pikkuFuncName } = extractFunctionName(
31
+ const { pikkuFuncId } = extractFunctionName(
30
32
  element.expression,
31
33
  checker,
32
34
  rootDir
33
35
  )
34
- names.push(pikkuFuncName)
36
+ refs.push({
37
+ definitionId:
38
+ pikkuFuncId.startsWith('__temp_') &&
39
+ ts.isIdentifier(element.expression)
40
+ ? element.expression.text
41
+ : pikkuFuncId,
42
+ isFactoryCall: true,
43
+ })
35
44
  }
36
45
  }
37
- return names
46
+ return refs
47
+ }
48
+
49
+ export function extractMiddlewarePikkuNames(
50
+ arrayNode: ts.Expression,
51
+ checker: ts.TypeChecker,
52
+ rootDir: string
53
+ ): string[] {
54
+ return extractMiddlewareRefs(arrayNode, checker, rootDir).map(
55
+ (r) => r.definitionId
56
+ )
38
57
  }
39
58
 
40
- /**
41
- * Get middleware array from an object literal expression property
42
- * Returns the initializer node for the 'middleware' property if it exists
43
- */
44
59
  export function getMiddlewareNode(
45
60
  obj: ts.ObjectLiteralExpression
46
61
  ): ts.Expression | undefined {
@@ -54,31 +69,17 @@ export function getMiddlewareNode(
54
69
  return middlewareProp?.initializer
55
70
  }
56
71
 
57
- /**
58
- * Check if a route matches a pattern with wildcards
59
- * Pattern can be exact match or use * as wildcard
60
- * e.g., '/api/*' matches '/api/users', '/api/posts/123', etc.
61
- */
62
72
  export function routeMatchesPattern(route: string, pattern: string): boolean {
63
73
  if (route === pattern) return true
64
74
 
65
- // Convert pattern to regex: replace * with .*
66
75
  const regexPattern = pattern
67
- .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars except *
68
- .replace(/\*/g, '.*') // Replace * with .*
76
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
77
+ .replace(/\*/g, '.*')
69
78
 
70
79
  const regex = new RegExp(`^${regexPattern}$`)
71
80
  return regex.test(route)
72
81
  }
73
82
 
74
- /**
75
- * Resolve middleware for an HTTP wiring based on:
76
- * 1. Global HTTP middleware (addd([...]))
77
- * 2. Route-specific HTTP middleware (addHTTPMiddleware('/pattern', [...]))
78
- * 3. Tag-based middleware (addMiddleware('tag', [...]))
79
- * 4. Explicit wiring middleware (wireHTTP({ middleware: [...] }))
80
- * Returns undefined if no middleware is found, otherwise returns array with at least one item
81
- */
82
83
  export function resolveHTTPMiddleware(
83
84
  state: InspectorState,
84
85
  route: string,
@@ -88,10 +89,8 @@ export function resolveHTTPMiddleware(
88
89
  ): MiddlewareMetadata[] | undefined {
89
90
  const resolved: MiddlewareMetadata[] = []
90
91
 
91
- // 1. HTTP route middleware groups (includes '*' for global)
92
92
  for (const [pattern, _groupMeta] of state.http.routeMiddleware.entries()) {
93
93
  if (routeMatchesPattern(route, pattern)) {
94
- // Just reference the group by route pattern
95
94
  resolved.push({
96
95
  type: 'http',
97
96
  route: pattern,
@@ -99,11 +98,9 @@ export function resolveHTTPMiddleware(
99
98
  }
100
99
  }
101
100
 
102
- // 2. Tag-based middleware groups
103
101
  if (tags && tags.length > 0) {
104
102
  for (const tag of tags) {
105
103
  if (state.middleware.tagMiddleware.has(tag)) {
106
- // Just reference the group by tag
107
104
  resolved.push({
108
105
  type: 'tag',
109
106
  tag,
@@ -112,7 +109,6 @@ export function resolveHTTPMiddleware(
112
109
  }
113
110
  }
114
111
 
115
- // 3. Explicit wire middleware (inline is OK here)
116
112
  if (explicitMiddlewareNode) {
117
113
  const middlewareNames = extractMiddlewarePikkuNames(
118
114
  explicitMiddlewareNode,
@@ -120,11 +116,11 @@ export function resolveHTTPMiddleware(
120
116
  state.rootDir
121
117
  )
122
118
  for (const name of middlewareNames) {
123
- const meta = state.middleware.meta[name]
119
+ const def = state.middleware.definitions[name]
124
120
  resolved.push({
125
121
  type: 'wire',
126
122
  name,
127
- inline: meta?.exportedName === null,
123
+ inline: def?.exportedName === null,
128
124
  })
129
125
  }
130
126
  }
@@ -132,11 +128,6 @@ export function resolveHTTPMiddleware(
132
128
  return resolved.length > 0 ? resolved : undefined
133
129
  }
134
130
 
135
- /**
136
- * Resolve tag-based and explicit middleware (common logic for wires and functions)
137
- * 1. Tag-based middleware (addMiddleware('tag', [...]))
138
- * 2. Explicit middleware (wireHTTP/pikkuFunc({ middleware: [...] }))
139
- */
140
131
  function resolveTagAndExplicitMiddleware(
141
132
  state: InspectorState,
142
133
  tags: string[] | undefined,
@@ -145,11 +136,9 @@ function resolveTagAndExplicitMiddleware(
145
136
  ): MiddlewareMetadata[] {
146
137
  const resolved: MiddlewareMetadata[] = []
147
138
 
148
- // 1. Tag-based middleware groups
149
139
  if (tags && tags.length > 0) {
150
140
  for (const tag of tags) {
151
141
  if (state.middleware.tagMiddleware.has(tag)) {
152
- // Just reference the group by tag
153
142
  resolved.push({
154
143
  type: 'tag',
155
144
  tag,
@@ -158,7 +147,6 @@ function resolveTagAndExplicitMiddleware(
158
147
  }
159
148
  }
160
149
 
161
- // 2. Explicit middleware (inline is OK here - used directly in wire/function)
162
150
  if (explicitMiddlewareNode) {
163
151
  const middlewareNames = extractMiddlewarePikkuNames(
164
152
  explicitMiddlewareNode,
@@ -166,11 +154,11 @@ function resolveTagAndExplicitMiddleware(
166
154
  state.rootDir
167
155
  )
168
156
  for (const name of middlewareNames) {
169
- const meta = state.middleware.meta[name]
157
+ const def = state.middleware.definitions[name]
170
158
  resolved.push({
171
159
  type: 'wire',
172
160
  name,
173
- inline: meta?.exportedName === null,
161
+ inline: def?.exportedName === null,
174
162
  })
175
163
  }
176
164
  }
@@ -178,12 +166,6 @@ function resolveTagAndExplicitMiddleware(
178
166
  return resolved
179
167
  }
180
168
 
181
- /**
182
- * Resolve middleware for a function based on:
183
- * 1. Tag-based middleware (addMiddleware('tag', [...]))
184
- * 2. Explicit function middleware (pikkuFunc({ middleware: [...] }))
185
- * Returns undefined if no middleware is found, otherwise returns array with at least one item
186
- */
187
169
  function resolveFunctionMiddlewareInternal(
188
170
  state: InspectorState,
189
171
  tags: string[] | undefined,
@@ -200,10 +182,6 @@ function resolveFunctionMiddlewareInternal(
200
182
  return resolved.length > 0 ? resolved : undefined
201
183
  }
202
184
 
203
- /**
204
- * Convenience wrapper: Extract middleware node from object and resolve
205
- * Use this in add-* files for cleaner code
206
- */
207
185
  export function resolveMiddleware(
208
186
  state: InspectorState,
209
187
  obj: ts.ObjectLiteralExpression,
@@ -219,10 +197,6 @@ export function resolveMiddleware(
219
197
  )
220
198
  }
221
199
 
222
- /**
223
- * Convenience wrapper for HTTP: Extract middleware and resolve with HTTP-specific logic
224
- * Use this in add-http-route.ts for cleaner code
225
- */
226
200
  export function resolveHTTPMiddlewareFromObject(
227
201
  state: InspectorState,
228
202
  route: string,
@@ -239,3 +213,91 @@ export function resolveHTTPMiddlewareFromObject(
239
213
  checker
240
214
  )
241
215
  }
216
+
217
+ function getAIMiddlewareNode(
218
+ obj: ts.ObjectLiteralExpression
219
+ ): ts.Expression | undefined {
220
+ const prop = obj.properties.find(
221
+ (p) =>
222
+ ts.isPropertyAssignment(p) &&
223
+ ts.isIdentifier(p.name) &&
224
+ p.name.text === 'aiMiddleware'
225
+ ) as ts.PropertyAssignment | undefined
226
+ return prop?.initializer
227
+ }
228
+
229
+ export function resolveAIMiddleware(
230
+ state: InspectorState,
231
+ obj: ts.ObjectLiteralExpression,
232
+ checker: ts.TypeChecker
233
+ ): MiddlewareMetadata[] | undefined {
234
+ const explicitNode = getAIMiddlewareNode(obj)
235
+ if (!explicitNode) return undefined
236
+
237
+ const names = extractMiddlewarePikkuNames(
238
+ explicitNode,
239
+ checker,
240
+ state.rootDir
241
+ )
242
+ const resolved: MiddlewareMetadata[] = names.map((name) => {
243
+ const def = state.aiMiddleware.definitions[name]
244
+ return {
245
+ type: 'wire' as const,
246
+ name,
247
+ inline: def?.exportedName === null,
248
+ }
249
+ })
250
+
251
+ return resolved.length > 0 ? resolved : undefined
252
+ }
253
+
254
+ function getChannelMiddlewareNode(
255
+ obj: ts.ObjectLiteralExpression
256
+ ): ts.Expression | undefined {
257
+ const prop = obj.properties.find(
258
+ (p) =>
259
+ ts.isPropertyAssignment(p) &&
260
+ ts.isIdentifier(p.name) &&
261
+ p.name.text === 'channelMiddleware'
262
+ ) as ts.PropertyAssignment | undefined
263
+ return prop?.initializer
264
+ }
265
+
266
+ export function resolveChannelMiddleware(
267
+ state: InspectorState,
268
+ obj: ts.ObjectLiteralExpression,
269
+ tags: string[] | undefined,
270
+ checker: ts.TypeChecker
271
+ ): MiddlewareMetadata[] | undefined {
272
+ const resolved: MiddlewareMetadata[] = []
273
+
274
+ if (tags && tags.length > 0) {
275
+ for (const tag of tags) {
276
+ if (state.channelMiddleware.tagMiddleware.has(tag)) {
277
+ resolved.push({
278
+ type: 'tag',
279
+ tag,
280
+ })
281
+ }
282
+ }
283
+ }
284
+
285
+ const explicitNode = getChannelMiddlewareNode(obj)
286
+ if (explicitNode) {
287
+ const names = extractMiddlewarePikkuNames(
288
+ explicitNode,
289
+ checker,
290
+ state.rootDir
291
+ )
292
+ for (const name of names) {
293
+ const def = state.channelMiddleware.definitions[name]
294
+ resolved.push({
295
+ type: 'wire',
296
+ name,
297
+ inline: def?.exportedName === null,
298
+ })
299
+ }
300
+ }
301
+
302
+ return resolved.length > 0 ? resolved : undefined
303
+ }
@@ -54,7 +54,8 @@ describe('resolveHTTPPermissions', () => {
54
54
  sourceFile: '/test.ts',
55
55
  position: 0,
56
56
  services: { services: [], singletons: [] },
57
- permissionCount: 1,
57
+ count: 1,
58
+ instanceIds: [],
58
59
  isFactory: true,
59
60
  }
60
61
  state.http.routePermissions.set('*', globalPermission)
@@ -79,7 +80,8 @@ describe('resolveHTTPPermissions', () => {
79
80
  sourceFile: '/test.ts',
80
81
  position: 0,
81
82
  services: { services: [], singletons: [] },
82
- permissionCount: 1,
83
+ count: 1,
84
+ instanceIds: [],
83
85
  isFactory: true,
84
86
  }
85
87
  state.http.routePermissions.set('/api/*', apiPermission)
@@ -104,7 +106,8 @@ describe('resolveHTTPPermissions', () => {
104
106
  sourceFile: '/test.ts',
105
107
  position: 0,
106
108
  services: { services: [], singletons: [] },
107
- permissionCount: 1,
109
+ count: 1,
110
+ instanceIds: [],
108
111
  isFactory: true,
109
112
  }
110
113
  state.permissions.tagPermissions.set('admin', adminPermission)
@@ -131,7 +134,8 @@ describe('resolveHTTPPermissions', () => {
131
134
  sourceFile: '/test.ts',
132
135
  position: 0,
133
136
  services: { services: [], singletons: [] },
134
- permissionCount: 1,
137
+ count: 1,
138
+ instanceIds: [],
135
139
  isFactory: true,
136
140
  })
137
141
 
@@ -141,7 +145,8 @@ describe('resolveHTTPPermissions', () => {
141
145
  sourceFile: '/test.ts',
142
146
  position: 0,
143
147
  services: { services: [], singletons: [] },
144
- permissionCount: 1,
148
+ count: 1,
149
+ instanceIds: [],
145
150
  isFactory: true,
146
151
  })
147
152
 
@@ -151,12 +156,13 @@ describe('resolveHTTPPermissions', () => {
151
156
  sourceFile: '/test.ts',
152
157
  position: 0,
153
158
  services: { services: [], singletons: [] },
154
- permissionCount: 1,
159
+ count: 1,
160
+ instanceIds: [],
155
161
  isFactory: true,
156
162
  })
157
163
 
158
164
  // Setup explicit permission
159
- state.permissions.meta['testPermission'] = {
165
+ state.permissions.definitions['testPermission'] = {
160
166
  services: { services: [], singletons: [] },
161
167
  sourceFile: '/test.ts',
162
168
  position: 0,
@@ -197,7 +203,8 @@ describe('resolvePermissions', () => {
197
203
  sourceFile: '/test.ts',
198
204
  position: 0,
199
205
  services: { services: [], singletons: [] },
200
- permissionCount: 1,
206
+ count: 1,
207
+ instanceIds: [],
201
208
  isFactory: true,
202
209
  }
203
210
  state.permissions.tagPermissions.set('mcp', mcpPermission)
@@ -217,7 +224,8 @@ describe('resolvePermissions', () => {
217
224
  sourceFile: '/test.ts',
218
225
  position: 0,
219
226
  services: { services: [], singletons: [] },
220
- permissionCount: 1,
227
+ count: 1,
228
+ instanceIds: [],
221
229
  isFactory: true,
222
230
  })
223
231
  state.permissions.tagPermissions.set('admin', {
@@ -225,7 +233,8 @@ describe('resolvePermissions', () => {
225
233
  sourceFile: '/test.ts',
226
234
  position: 0,
227
235
  services: { services: [], singletons: [] },
228
- permissionCount: 1,
236
+ count: 1,
237
+ instanceIds: [],
229
238
  isFactory: true,
230
239
  })
231
240
  const mockObj = createMockObjectLiteral()
@@ -298,6 +307,13 @@ function createMockState(): InspectorState {
298
307
  meta: {},
299
308
  files: new Set(),
300
309
  },
310
+ workflows: {
311
+ meta: {},
312
+ files: new Map(),
313
+ graphMeta: {},
314
+ graphFiles: new Map(),
315
+ invokedWorkflows: new Set(),
316
+ },
301
317
  rpc: {
302
318
  internalMeta: {},
303
319
  internalFiles: new Map(),
@@ -316,11 +332,18 @@ function createMockState(): InspectorState {
316
332
  files: new Set(),
317
333
  },
318
334
  middleware: {
319
- meta: {},
335
+ definitions: {},
336
+ instances: {},
337
+ tagMiddleware: new Map(),
338
+ },
339
+ channelMiddleware: {
340
+ definitions: {},
341
+ instances: {},
320
342
  tagMiddleware: new Map(),
321
343
  },
322
344
  permissions: {
323
- meta: {},
345
+ definitions: {},
346
+ instances: {},
324
347
  tagPermissions: new Map(),
325
348
  },
326
349
  }