@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
@@ -1,14 +1,58 @@
1
1
  import * as ts from 'typescript'
2
- import { AddWiring } from '../types.js'
2
+ import type { AddWiring, InspectorState } from '../types.js'
3
3
  import {
4
4
  extractFunctionName,
5
5
  isNamedExport,
6
+ makeContextBasedId,
6
7
  } from '../utils/extract-function-name.js'
7
- import { extractServicesFromFunction } from '../utils/extract-services.js'
8
+ import {
9
+ extractServicesFromFunction,
10
+ extractUsedWires,
11
+ } from '../utils/extract-services.js'
8
12
  import { extractPermissionPikkuNames } from '../utils/permissions.js'
9
13
  import { getPropertyValue } from '../utils/get-property-value.js'
10
14
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
11
15
 
16
+ function renameTempDefinitions(
17
+ state: InspectorState,
18
+ definitionIds: string[],
19
+ groupType: string,
20
+ groupKey: string
21
+ ): void {
22
+ const tempIndices = definitionIds
23
+ .map((name, i) => (name.startsWith('__temp_') ? i : -1))
24
+ .filter((i) => i >= 0)
25
+
26
+ for (const idx of tempIndices) {
27
+ const oldId = definitionIds[idx]
28
+ const newId =
29
+ tempIndices.length === 1
30
+ ? makeContextBasedId(groupType, groupKey)
31
+ : makeContextBasedId(groupType, groupKey, String(idx))
32
+ const existing = state.permissions.definitions[oldId]
33
+ if (existing) {
34
+ delete state.permissions.definitions[oldId]
35
+ state.permissions.definitions[newId] = existing
36
+ }
37
+ definitionIds[idx] = newId
38
+ }
39
+ }
40
+
41
+ function isInsidePermissionFactory(node: ts.Node): boolean {
42
+ let current = node.parent
43
+ while (current) {
44
+ if (
45
+ ts.isCallExpression(current) &&
46
+ ts.isIdentifier(current.expression) &&
47
+ current.expression.text === 'pikkuPermissionFactory'
48
+ ) {
49
+ return true
50
+ }
51
+ current = current.parent
52
+ }
53
+ return false
54
+ }
55
+
12
56
  /**
13
57
  * Inspect pikkuPermission calls, addPermission calls, and addHTTPPermission calls
14
58
  */
@@ -24,6 +68,9 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
24
68
 
25
69
  // Handle pikkuPermission(...) - individual permission function definition
26
70
  if (expression.text === 'pikkuPermission') {
71
+ // Skip if nested inside pikkuPermissionFactory — the factory handler extracts services itself
72
+ if (isInsidePermissionFactory(node)) return
73
+
27
74
  const arg = args[0]
28
75
  if (!arg) return
29
76
 
@@ -31,15 +78,12 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
31
78
  let name: string | undefined
32
79
  let description: string | undefined
33
80
 
34
- // Check if using object syntax: pikkuPermission({ func: ..., name: '...', description: '...' })
35
81
  if (ts.isObjectLiteralExpression(arg)) {
36
- // Extract name and description metadata
37
82
  const nameValue = getPropertyValue(arg, 'name')
38
83
  const descValue = getPropertyValue(arg, 'description')
39
84
  name = typeof nameValue === 'string' ? nameValue : undefined
40
85
  description = typeof descValue === 'string' ? descValue : undefined
41
86
 
42
- // Extract the func property
43
87
  const fnProp = getPropertyAssignmentInitializer(
44
88
  arg,
45
89
  'func',
@@ -64,22 +108,129 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
64
108
  }
65
109
 
66
110
  const services = extractServicesFromFunction(actualHandler)
67
- const { pikkuFuncName, exportedName } = extractFunctionName(
111
+ const wires = extractUsedWires(actualHandler, 2)
112
+ let { pikkuFuncId, exportedName } = extractFunctionName(
68
113
  node,
69
114
  checker,
70
115
  state.rootDir
71
116
  )
72
- state.permissions.meta[pikkuFuncName] = {
117
+ if (pikkuFuncId.startsWith('__temp_')) {
118
+ if (
119
+ ts.isVariableDeclaration(node.parent) &&
120
+ ts.isIdentifier(node.parent.name)
121
+ ) {
122
+ pikkuFuncId = node.parent.name.text
123
+ } else if (
124
+ ts.isPropertyAssignment(node.parent) &&
125
+ ts.isIdentifier(node.parent.name)
126
+ ) {
127
+ pikkuFuncId = node.parent.name.text
128
+ } else {
129
+ logger.error(
130
+ `• pikkuPermission() must be assigned to a variable or object property. ` +
131
+ `Extract it to a const: const myPermission = pikkuPermission(...)`
132
+ )
133
+ return
134
+ }
135
+ }
136
+ const dataParam = actualHandler.parameters[1]
137
+ const dataParamName =
138
+ dataParam && ts.isIdentifier(dataParam.name) ? dataParam.name.text : null
139
+ const requiresData = !(dataParamName && dataParamName.startsWith('_'))
140
+
141
+ state.permissions.definitions[pikkuFuncId] = {
73
142
  services,
143
+ wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
74
144
  sourceFile: node.getSourceFile().fileName,
75
145
  position: node.getStart(),
76
146
  exportedName,
77
147
  name,
78
148
  description,
149
+ ...(requiresData ? {} : { requiresData: false }),
79
150
  }
80
151
 
81
152
  logger.debug(
82
- `• Found permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}`
153
+ `• Found permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}${!requiresData ? ' (auth-only)' : ''}`
154
+ )
155
+ return
156
+ }
157
+
158
+ if (expression.text === 'pikkuAuth') {
159
+ if (isInsidePermissionFactory(node)) return
160
+
161
+ const arg = args[0]
162
+ if (!arg) return
163
+
164
+ let actualHandler: ts.ArrowFunction | ts.FunctionExpression
165
+ let name: string | undefined
166
+ let description: string | undefined
167
+
168
+ if (ts.isObjectLiteralExpression(arg)) {
169
+ const nameValue = getPropertyValue(arg, 'name')
170
+ const descValue = getPropertyValue(arg, 'description')
171
+ name = typeof nameValue === 'string' ? nameValue : undefined
172
+ description = typeof descValue === 'string' ? descValue : undefined
173
+
174
+ const fnProp = getPropertyAssignmentInitializer(
175
+ arg,
176
+ 'func',
177
+ true,
178
+ checker
179
+ )
180
+ if (
181
+ !fnProp ||
182
+ (!ts.isArrowFunction(fnProp) && !ts.isFunctionExpression(fnProp))
183
+ ) {
184
+ logger.error(`• pikkuAuth object missing required 'func' property.`)
185
+ return
186
+ }
187
+ actualHandler = fnProp
188
+ } else if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
189
+ actualHandler = arg
190
+ } else {
191
+ logger.error(`• Handler for pikkuAuth is not a function.`)
192
+ return
193
+ }
194
+
195
+ const services = extractServicesFromFunction(actualHandler)
196
+ const wires = extractUsedWires(actualHandler, 1)
197
+ let { pikkuFuncId, exportedName } = extractFunctionName(
198
+ node,
199
+ checker,
200
+ state.rootDir
201
+ )
202
+ if (pikkuFuncId.startsWith('__temp_')) {
203
+ if (
204
+ ts.isVariableDeclaration(node.parent) &&
205
+ ts.isIdentifier(node.parent.name)
206
+ ) {
207
+ pikkuFuncId = node.parent.name.text
208
+ } else if (
209
+ ts.isPropertyAssignment(node.parent) &&
210
+ ts.isIdentifier(node.parent.name)
211
+ ) {
212
+ pikkuFuncId = node.parent.name.text
213
+ } else {
214
+ logger.error(
215
+ `• pikkuAuth() must be assigned to a variable or object property. ` +
216
+ `Extract it to a const: const myAuth = pikkuAuth(...)`
217
+ )
218
+ return
219
+ }
220
+ }
221
+ state.permissions.definitions[pikkuFuncId] = {
222
+ services,
223
+ wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
224
+ sourceFile: node.getSourceFile().fileName,
225
+ position: node.getStart(),
226
+ exportedName,
227
+ name,
228
+ description,
229
+ requiresData: false,
230
+ }
231
+
232
+ logger.debug(
233
+ `• Found auth permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}`
83
234
  )
84
235
  return
85
236
  }
@@ -101,6 +252,10 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
101
252
  // The factory should return pikkuPermission(...), so we need to find that call
102
253
  // If no wrapper is found, extract from the factory's returned function directly
103
254
  let services = { optimized: false, services: [] as string[] }
255
+ let wires: ReturnType<typeof extractUsedWires> = {
256
+ optimized: true,
257
+ wires: [],
258
+ }
104
259
 
105
260
  const findPikkuPermissionCall = (
106
261
  node: ts.Node
@@ -122,32 +277,51 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
122
277
  ts.isFunctionExpression(permissionHandler)
123
278
  ) {
124
279
  services = extractServicesFromFunction(permissionHandler)
280
+ wires = extractUsedWires(permissionHandler, 2)
125
281
  }
126
282
  } else {
127
- // No pikkuPermission wrapper found - extract from factory's return value directly
128
- // Factory pattern: (config) => (services, data, wire) => { ... }
129
283
  if (
130
284
  ts.isArrowFunction(factoryNode) ||
131
285
  ts.isFunctionExpression(factoryNode)
132
286
  ) {
133
287
  const factoryBody = factoryNode.body
134
- // Check if the body is an arrow function (direct return)
135
288
  if (
136
289
  ts.isArrowFunction(factoryBody) ||
137
290
  ts.isFunctionExpression(factoryBody)
138
291
  ) {
139
292
  services = extractServicesFromFunction(factoryBody)
293
+ wires = extractUsedWires(factoryBody, 2)
140
294
  }
141
295
  }
142
296
  }
143
297
 
144
- const { pikkuFuncName, exportedName } = extractFunctionName(
298
+ let { pikkuFuncId, exportedName } = extractFunctionName(
145
299
  node,
146
300
  checker,
147
301
  state.rootDir
148
302
  )
149
- state.permissions.meta[pikkuFuncName] = {
303
+ if (pikkuFuncId.startsWith('__temp_')) {
304
+ if (
305
+ ts.isVariableDeclaration(node.parent) &&
306
+ ts.isIdentifier(node.parent.name)
307
+ ) {
308
+ pikkuFuncId = node.parent.name.text
309
+ } else if (
310
+ ts.isPropertyAssignment(node.parent) &&
311
+ ts.isIdentifier(node.parent.name)
312
+ ) {
313
+ pikkuFuncId = node.parent.name.text
314
+ } else {
315
+ logger.error(
316
+ `• pikkuPermissionFactory() must be assigned to a variable or object property. ` +
317
+ `Extract it to a const: const myPermission = pikkuPermissionFactory(...)`
318
+ )
319
+ return
320
+ }
321
+ }
322
+ state.permissions.definitions[pikkuFuncId] = {
150
323
  services,
324
+ wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
151
325
  sourceFile: node.getSourceFile().fileName,
152
326
  position: node.getStart(),
153
327
  exportedName,
@@ -192,7 +366,7 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
192
366
  return
193
367
  }
194
368
 
195
- // Extract permission pikkuFuncNames from array
369
+ // Extract permission pikkuFuncIds from array
196
370
  const permissionNames = extractPermissionPikkuNames(
197
371
  permissionsArrayArg,
198
372
  checker,
@@ -204,10 +378,11 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
204
378
  return
205
379
  }
206
380
 
207
- // Collect services from all permissions in the group
381
+ renameTempDefinitions(state, permissionNames, 'tag', tag)
382
+
208
383
  const allServices = new Set<string>()
209
384
  for (const permissionName of permissionNames) {
210
- const permissionMeta = state.permissions.meta[permissionName]
385
+ const permissionMeta = state.permissions.definitions[permissionName]
211
386
  if (permissionMeta && permissionMeta.services) {
212
387
  for (const service of permissionMeta.services.services) {
213
388
  allServices.add(service)
@@ -215,24 +390,17 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
215
390
  }
216
391
  }
217
392
 
218
- // Check if this call is wrapped in a factory function
219
- // We need to walk up the tree to see if the parent is: const x = () => addPermission(...)
220
393
  let isFactory = false
221
394
  let exportedName: string | null = null
222
395
  let parent = node.parent
223
396
 
224
- // Check if parent is arrow function: () => addPermission(...)
225
397
  if (parent && ts.isArrowFunction(parent)) {
226
- // Check if arrow function has no parameters
227
398
  if (parent.parameters.length === 0) {
228
399
  isFactory = true
229
400
 
230
- // For factories, we need to check the arrow function's parent for the export name
231
- // const apiTagPermissions = () => addPermission(...)
232
401
  const arrowParent = parent.parent
233
402
  if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
234
403
  if (ts.isIdentifier(arrowParent.name)) {
235
- // Check if it's exported
236
404
  if (isNamedExport(arrowParent)) {
237
405
  exportedName = arrowParent.name.text
238
406
  }
@@ -241,13 +409,11 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
241
409
  }
242
410
  }
243
411
 
244
- // If not a factory, get export name from the call expression itself
245
412
  if (!isFactory) {
246
413
  const extracted = extractFunctionName(node, checker, state.rootDir)
247
414
  exportedName = extracted.exportedName
248
415
  }
249
416
 
250
- // Log warning if not using factory pattern
251
417
  if (!isFactory && exportedName) {
252
418
  logger.warn(
253
419
  `• Permission group '${exportedName}' for tag '${tag}' is not wrapped in a factory function. ` +
@@ -255,7 +421,6 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
255
421
  )
256
422
  }
257
423
 
258
- // Store group metadata
259
424
  state.permissions.tagPermissions.set(tag, {
260
425
  exportName: exportedName,
261
426
  sourceFile: node.getSourceFile().fileName,
@@ -264,7 +429,8 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
264
429
  optimized: false,
265
430
  services: Array.from(allServices),
266
431
  },
267
- permissionCount: permissionNames.length,
432
+ count: permissionNames.length,
433
+ instanceIds: permissionNames,
268
434
  isFactory,
269
435
  })
270
436
 
@@ -306,7 +472,7 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
306
472
  return
307
473
  }
308
474
 
309
- // Extract permission pikkuFuncNames from array
475
+ // Extract permission pikkuFuncIds from array
310
476
  const permissionNames = extractPermissionPikkuNames(
311
477
  permissionsArrayArg,
312
478
  checker,
@@ -320,10 +486,11 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
320
486
  return
321
487
  }
322
488
 
323
- // Collect services from all permissions in the group
489
+ renameTempDefinitions(state, permissionNames, 'http', pattern)
490
+
324
491
  const allServices = new Set<string>()
325
492
  for (const permissionName of permissionNames) {
326
- const permissionMeta = state.permissions.meta[permissionName]
493
+ const permissionMeta = state.permissions.definitions[permissionName]
327
494
  if (permissionMeta && permissionMeta.services) {
328
495
  for (const service of permissionMeta.services.services) {
329
496
  allServices.add(service)
@@ -331,23 +498,17 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
331
498
  }
332
499
  }
333
500
 
334
- // Check if this call is wrapped in a factory function
335
501
  let isFactory = false
336
502
  let exportedName: string | null = null
337
503
  let parent = node.parent
338
504
 
339
- // Check if parent is arrow function: () => addHTTPPermission(...)
340
505
  if (parent && ts.isArrowFunction(parent)) {
341
- // Check if arrow function has no parameters
342
506
  if (parent.parameters.length === 0) {
343
507
  isFactory = true
344
508
 
345
- // For factories, we need to check the arrow function's parent for the export name
346
- // const apiRoutePermissions = () => addHTTPPermission(...)
347
509
  const arrowParent = parent.parent
348
510
  if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
349
511
  if (ts.isIdentifier(arrowParent.name)) {
350
- // Check if it's exported
351
512
  if (isNamedExport(arrowParent)) {
352
513
  exportedName = arrowParent.name.text
353
514
  }
@@ -356,13 +517,11 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
356
517
  }
357
518
  }
358
519
 
359
- // If not a factory, get export name from the call expression itself
360
520
  if (!isFactory) {
361
521
  const extracted = extractFunctionName(node, checker, state.rootDir)
362
522
  exportedName = extracted.exportedName
363
523
  }
364
524
 
365
- // Log warning if not using factory pattern
366
525
  if (!isFactory && exportedName) {
367
526
  logger.warn(
368
527
  `• HTTP permission group '${exportedName}' for pattern '${pattern}' is not wrapped in a factory function. ` +
@@ -370,7 +529,6 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
370
529
  )
371
530
  }
372
531
 
373
- // Store group metadata
374
532
  state.http.routePermissions.set(pattern, {
375
533
  exportName: exportedName,
376
534
  sourceFile: node.getSourceFile().fileName,
@@ -379,7 +537,8 @@ export const addPermission: AddWiring = (logger, node, checker, state) => {
379
537
  optimized: false,
380
538
  services: Array.from(allServices),
381
539
  },
382
- permissionCount: permissionNames.length,
540
+ count: permissionNames.length,
541
+ instanceIds: permissionNames,
383
542
  isFactory,
384
543
  })
385
544
 
@@ -4,7 +4,10 @@ import {
4
4
  getCommonWireMetaData,
5
5
  } from '../utils/get-property-value.js'
6
6
  import { AddWiring } from '../types.js'
7
- import { extractFunctionName } from '../utils/extract-function-name.js'
7
+ import {
8
+ extractFunctionName,
9
+ makeContextBasedId,
10
+ } from '../utils/extract-function-name.js'
8
11
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
9
12
  import { resolveMiddleware } from '../utils/middleware.js'
10
13
  import { extractWireNames } from '../utils/post-process.js'
@@ -31,13 +34,11 @@ export const addQueueWorker: AddWiring = (logger, node, checker, state) => {
31
34
  if (ts.isObjectLiteralExpression(firstArg)) {
32
35
  const obj = firstArg
33
36
 
34
- const queueName = getPropertyValue(obj, 'queueName') as string | null
35
- const { tags, summary, description, errors } = getCommonWireMetaData(
36
- obj,
37
- 'Queue worker',
38
- queueName,
39
- logger
40
- )
37
+ const name = getPropertyValue(obj, 'name') as string | null
38
+ const { disabled, tags, summary, description, errors } =
39
+ getCommonWireMetaData(obj, 'Queue worker', name, logger)
40
+
41
+ if (disabled) return
41
42
 
42
43
  // --- find the referenced function ---
43
44
  const funcInitializer = getPropertyAssignmentInitializer(
@@ -49,21 +50,25 @@ export const addQueueWorker: AddWiring = (logger, node, checker, state) => {
49
50
  if (!funcInitializer) {
50
51
  logger.critical(
51
52
  ErrorCode.MISSING_FUNC,
52
- `No valid 'func' property for queue processor '${queueName}'.`
53
+ `No valid 'func' property for queue processor '${name}'.`
53
54
  )
54
55
  return
55
56
  }
56
57
 
57
- const pikkuFuncName = extractFunctionName(
58
+ const extracted = extractFunctionName(
58
59
  funcInitializer,
59
60
  checker,
60
61
  state.rootDir
61
- ).pikkuFuncName
62
+ )
63
+ let pikkuFuncId = extracted.pikkuFuncId
64
+ if (pikkuFuncId.startsWith('__temp_') && name) {
65
+ pikkuFuncId = makeContextBasedId('queue', name)
66
+ }
62
67
 
63
- if (!queueName) {
68
+ if (!name) {
64
69
  logger.critical(
65
70
  ErrorCode.MISSING_QUEUE_NAME,
66
- `No 'queueName' provided for queue processor function '${pikkuFuncName}'.`
71
+ `No 'name' provided for queue processor function '${pikkuFuncId}'.`
67
72
  )
68
73
  return
69
74
  }
@@ -72,15 +77,15 @@ export const addQueueWorker: AddWiring = (logger, node, checker, state) => {
72
77
  const middleware = resolveMiddleware(state, obj, tags, checker)
73
78
 
74
79
  // --- track used functions/middleware for service aggregation ---
75
- state.serviceAggregation.usedFunctions.add(pikkuFuncName)
76
- extractWireNames(middleware).forEach((name) =>
77
- state.serviceAggregation.usedMiddleware.add(name)
80
+ state.serviceAggregation.usedFunctions.add(pikkuFuncId)
81
+ extractWireNames(middleware).forEach((n) =>
82
+ state.serviceAggregation.usedMiddleware.add(n)
78
83
  )
79
84
 
80
85
  state.queueWorkers.files.add(node.getSourceFile().fileName)
81
- state.queueWorkers.meta[queueName] = {
82
- pikkuFuncName,
83
- queueName,
86
+ state.queueWorkers.meta[name] = {
87
+ pikkuFuncId,
88
+ name,
84
89
  summary,
85
90
  description,
86
91
  errors,
@@ -43,6 +43,23 @@ export function addRPCInvocations(
43
43
  }
44
44
  }
45
45
 
46
+ // Check for workflow('...'), workflowStart('...'), workflowRun('...'), workflowStatus('...'), graphStart('...') calls
47
+ if (
48
+ ts.isIdentifier(expression) &&
49
+ (expression.text === 'workflow' ||
50
+ expression.text === 'workflowStart' ||
51
+ expression.text === 'workflowRun' ||
52
+ expression.text === 'workflowStatus' ||
53
+ expression.text === 'graphStart')
54
+ ) {
55
+ const [firstArg] = args
56
+ if (firstArg && ts.isStringLiteral(firstArg)) {
57
+ const workflowName = firstArg.text
58
+ logger.debug(`• Found ${expression.text}() call: ${workflowName}`)
59
+ state.workflows.invokedWorkflows.add(workflowName)
60
+ }
61
+ }
62
+
46
63
  // Check for rpc.invoke('...') calls
47
64
  if (
48
65
  ts.isPropertyAccessExpression(expression) &&
@@ -4,7 +4,10 @@ import {
4
4
  getCommonWireMetaData,
5
5
  } from '../utils/get-property-value.js'
6
6
  import { AddWiring } from '../types.js'
7
- import { extractFunctionName } from '../utils/extract-function-name.js'
7
+ import {
8
+ extractFunctionName,
9
+ makeContextBasedId,
10
+ } from '../utils/extract-function-name.js'
8
11
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
9
12
  import { resolveMiddleware } from '../utils/middleware.js'
10
13
  import { extractWireNames } from '../utils/post-process.js'
@@ -39,12 +42,10 @@ export const addSchedule: AddWiring = (
39
42
 
40
43
  const nameValue = getPropertyValue(obj, 'name') as string | null
41
44
  const scheduleValue = getPropertyValue(obj, 'schedule') as string | null
42
- const { tags, summary, description, errors } = getCommonWireMetaData(
43
- obj,
44
- 'Scheduler',
45
- nameValue,
46
- logger
47
- )
45
+ const { disabled, tags, summary, description, errors } =
46
+ getCommonWireMetaData(obj, 'Scheduler', nameValue, logger)
47
+
48
+ if (disabled) return
48
49
 
49
50
  const funcInitializer = getPropertyAssignmentInitializer(
50
51
  obj,
@@ -60,11 +61,15 @@ export const addSchedule: AddWiring = (
60
61
  return
61
62
  }
62
63
 
63
- const pikkuFuncName = extractFunctionName(
64
+ const extracted = extractFunctionName(
64
65
  funcInitializer,
65
66
  checker,
66
67
  state.rootDir
67
- ).pikkuFuncName
68
+ )
69
+ let pikkuFuncId = extracted.pikkuFuncId
70
+ if (pikkuFuncId.startsWith('__temp_') && nameValue) {
71
+ pikkuFuncId = makeContextBasedId('scheduler', nameValue)
72
+ }
68
73
 
69
74
  if (!nameValue || !scheduleValue) {
70
75
  return
@@ -74,14 +79,14 @@ export const addSchedule: AddWiring = (
74
79
  const middleware = resolveMiddleware(state, obj, tags, checker)
75
80
 
76
81
  // --- track used functions/middleware for service aggregation ---
77
- state.serviceAggregation.usedFunctions.add(pikkuFuncName)
82
+ state.serviceAggregation.usedFunctions.add(pikkuFuncId)
78
83
  extractWireNames(middleware).forEach((name) =>
79
84
  state.serviceAggregation.usedMiddleware.add(name)
80
85
  )
81
86
 
82
87
  state.scheduledTasks.files.add(node.getSourceFile().fileName)
83
88
  state.scheduledTasks.meta[nameValue] = {
84
- pikkuFuncName,
89
+ pikkuFuncId,
85
90
  name: nameValue,
86
91
  schedule: scheduleValue,
87
92
  summary,