@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,264 +1,30 @@
1
1
  import * as ts from 'typescript'
2
- import { toRelativePath } from './find-root-dir.js'
2
+ import { randomUUID } from 'crypto'
3
3
 
4
4
  export type ExtractedFunctionName = {
5
- pikkuFuncName: string
5
+ pikkuFuncId: string
6
6
  name: string
7
7
  explicitName: string | null
8
8
  exportedName: string | null
9
- localName: string | null
10
9
  propertyName: string | null
10
+ isHelper: boolean
11
11
  }
12
12
 
13
- /**
14
- * Generate a deterministic "anonymous" name for any expression node,
15
- * but if it's an Identifier pointing to a function, resolve it back
16
- * to the function's declaration (so you get the true source location).
17
- */
18
- export function makeDeterministicAnonName(
19
- start: ts.Node,
20
- checker: ts.TypeChecker,
21
- rootDir: string
13
+ export function makeContextBasedId(
14
+ wiringType: string,
15
+ ...segments: string[]
22
16
  ): string {
23
- let node: ts.Node = start
24
- let target: ts.Node = start
25
-
26
- // Handle the case where we're starting with an identifier directly
27
- if (ts.isIdentifier(node)) {
28
- const sym = checker.getSymbolAtLocation(node)
29
- if (sym) {
30
- let resolvedSym = sym
31
- if (resolvedSym.flags & ts.SymbolFlags.Alias) {
32
- resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym
33
- }
34
-
35
- const decls = resolvedSym.declarations ?? []
36
- if (decls.length > 0) {
37
- // Start with the declaration, not the reference
38
- const decl = decls[0]!
39
-
40
- // If it's a variable declaration with a function initializer, use the function directly
41
- if (
42
- ts.isVariableDeclaration(decl) &&
43
- decl.initializer &&
44
- (ts.isFunctionExpression(decl.initializer) ||
45
- ts.isArrowFunction(decl.initializer))
46
- ) {
47
- target = decl.initializer
48
- // Return early - we found the function directly
49
- const sf = target.getSourceFile()
50
- const relativePath = toRelativePath(sf.fileName, rootDir)
51
- const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_')
52
- const { line, character } = ts.getLineAndCharacterOfPosition(
53
- sf,
54
- target.getStart()
55
- )
56
- return `pikkuFn_${file}_L${line + 1}C${character + 1}`
57
- }
58
- // Otherwise continue resolution with the declaration
59
- node = decl
60
- target = decl!
61
- }
62
- }
63
- }
64
-
65
- // In an object literal property value, first try to resolve the identifier
66
- if (
67
- ts.isPropertyAssignment(node.parent) &&
68
- node === node.parent.initializer &&
69
- ts.isIdentifier(node)
70
- ) {
71
- const sym = checker.getSymbolAtLocation(node)
72
- if (sym) {
73
- // Process the symbol to find the real declaration
74
- let resolvedSym = sym
75
- if (resolvedSym.flags & ts.SymbolFlags.Alias) {
76
- resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym
77
- }
78
-
79
- const decls = resolvedSym.declarations ?? []
80
- if (decls.length > 0) {
81
- // Found a declaration - use it as our new target
82
- const decl = decls[0]
83
-
84
- if (!decl) {
85
- throw new Error('No declaration found')
86
- }
87
-
88
- // If it's a variable declaration with an initializer function, use that
89
- if (ts.isVariableDeclaration(decl) && decl.initializer) {
90
- if (
91
- ts.isFunctionExpression(decl.initializer) ||
92
- ts.isArrowFunction(decl.initializer)
93
- ) {
94
- target = decl.initializer
95
- // Return early - we found the function directly
96
- const sf = target.getSourceFile()
97
- const relativePath = toRelativePath(sf.fileName, rootDir)
98
- const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_')
99
- const { line, character } = ts.getLineAndCharacterOfPosition(
100
- sf,
101
- target.getStart()
102
- )
103
- return `pikkuFn_${file}_L${line + 1}C${character + 1}`
104
- }
105
- } else if (ts.isFunctionDeclaration(decl)) {
106
- // Already a function declaration
107
- target = decl
108
- // Return early
109
- const sf = target.getSourceFile()
110
- const relativePath = toRelativePath(sf.fileName, rootDir)
111
- const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_')
112
- const { line, character } = ts.getLineAndCharacterOfPosition(
113
- sf,
114
- target.getStart()
115
- )
116
- return `pikkuFn_${file}_L${line + 1}C${character + 1}`
117
- }
118
-
119
- // If we didn't return early, continue with this declaration
120
- node = decl
121
- target = decl
122
- }
123
- }
124
- }
125
-
126
- const seen = new Set<ts.Node>()
127
- for (let depth = 0; depth < 10; depth++) {
128
- if (!ts.isIdentifier(node) || seen.has(node)) break
129
- seen.add(node)
130
-
131
- let sym = checker.getSymbolAtLocation(node)
132
- if (!sym) break
133
- if (sym.flags & ts.SymbolFlags.Alias) {
134
- sym = checker.getAliasedSymbol(sym) ?? sym
135
- }
136
-
137
- const allDecls = sym.declarations ?? []
138
- // prefer real .ts/.tsx implementation files
139
- const implDecls = allDecls.filter(
140
- (d) => !d.getSourceFile().isDeclarationFile
141
- )
142
- const decls = implDecls.length ? implDecls : allDecls
143
-
144
- let didResolve = false
145
- for (const decl of decls) {
146
- // 1) direct function foo() {} or function-expression
147
- if (
148
- ts.isFunctionDeclaration(decl) ||
149
- ts.isFunctionExpression(decl) ||
150
- ts.isArrowFunction(decl)
151
- ) {
152
- target = decl
153
- didResolve = true
154
- break
155
- }
156
-
157
- // 2) const foo = () => {} or foo = function() {}
158
- if (ts.isVariableDeclaration(decl) && decl.initializer) {
159
- const init = decl.initializer
160
- if (ts.isFunctionExpression(init) || ts.isArrowFunction(init)) {
161
- target = init
162
- didResolve = true
163
- break
164
- }
165
- // 2b) const foo = bar; (follow the next identifier)
166
- if (ts.isIdentifier(init)) {
167
- node = init
168
- target = init
169
- didResolve = true
170
- break
171
- }
172
- }
173
-
174
- // 3) Handle shorthand property assignments: { foo } (equivalent to { foo: foo })
175
- if (ts.isShorthandPropertyAssignment(decl)) {
176
- // Get the symbol for the shorthand property
177
- const shorthandSym = checker.getShorthandAssignmentValueSymbol(decl)
178
- if (
179
- shorthandSym &&
180
- shorthandSym.declarations &&
181
- shorthandSym.declarations.length > 0
182
- ) {
183
- // Use the first declaration as our new target
184
- const shorthandDecl = shorthandSym.declarations[0]!
185
- target = shorthandDecl
186
-
187
- if (!shorthandDecl) {
188
- throw new Error('No shorthand declaration found')
189
- }
190
-
191
- // Check the type of declaration and extract the appropriate identifier to continue resolving
192
- if (
193
- ts.isVariableDeclaration(shorthandDecl) &&
194
- ts.isIdentifier(shorthandDecl.name)
195
- ) {
196
- node = shorthandDecl.name
197
- didResolve = true
198
- break
199
- } else if (
200
- ts.isFunctionDeclaration(shorthandDecl) &&
201
- shorthandDecl.name &&
202
- ts.isIdentifier(shorthandDecl.name)
203
- ) {
204
- node = shorthandDecl.name
205
- didResolve = true
206
- break
207
- } else if (
208
- ts.isParameter(shorthandDecl) &&
209
- ts.isIdentifier(shorthandDecl.name)
210
- ) {
211
- node = shorthandDecl.name
212
- didResolve = true
213
- break
214
- } else if (
215
- ts.isPropertyDeclaration(shorthandDecl) &&
216
- ts.isIdentifier(shorthandDecl.name)
217
- ) {
218
- node = shorthandDecl.name
219
- didResolve = true
220
- break
221
- } else if (
222
- ts.isMethodDeclaration(shorthandDecl) &&
223
- ts.isIdentifier(shorthandDecl.name)
224
- ) {
225
- node = shorthandDecl.name
226
- didResolve = true
227
- break
228
- }
229
- }
230
- }
231
-
232
- // 4) Handle method declarations in classes/objects
233
- if (ts.isMethodDeclaration(decl)) {
234
- target = decl
235
- didResolve = true
236
- break
237
- }
238
-
239
- // you can add more cases here if your setup uses imports, etc.
240
- }
241
-
242
- if (!didResolve) break
243
- }
17
+ return [wiringType, ...segments].join(':')
18
+ }
244
19
 
245
- const sf = target.getSourceFile()
246
- const relativePath = toRelativePath(sf.fileName, rootDir)
247
- const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_')
248
- const { line, character } = ts.getLineAndCharacterOfPosition(
249
- sf,
250
- target.getStart()
251
- )
252
- return `pikkuFn_${file}_L${line + 1}C${character + 1}`
20
+ export function funcIdToTypeName(id: string): string {
21
+ return id
22
+ .split(/[^a-zA-Z0-9]/)
23
+ .filter(Boolean)
24
+ .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
25
+ .join('')
253
26
  }
254
27
 
255
- /**
256
- * Updated function to extract and prioritize function names correctly
257
- * This function follows the priority:
258
- * 1. Object with a name property
259
- * 2. Exported name
260
- * 3. Fallback to deterministic name
261
- */
262
28
  export function extractFunctionName(
263
29
  callExpr: ts.Node,
264
30
  checker: ts.TypeChecker,
@@ -268,12 +34,46 @@ export function extractFunctionName(
268
34
 
269
35
  // Initialize the result
270
36
  const result: ExtractedFunctionName = {
271
- pikkuFuncName: '', // Will be populated later
272
- name: '', // This will hold our "best" name based on priority
37
+ pikkuFuncId: '',
38
+ name: '',
273
39
  exportedName: null,
274
- localName: null,
275
40
  propertyName: null,
276
41
  explicitName: null,
42
+ isHelper: false,
43
+ }
44
+
45
+ const workflowHelpers = new Set([
46
+ 'workflow',
47
+ 'workflowStart',
48
+ 'workflowStatus',
49
+ 'graphStart',
50
+ ])
51
+
52
+ if (
53
+ ts.isCallExpression(callExpr) &&
54
+ ts.isIdentifier(callExpr.expression) &&
55
+ workflowHelpers.has(callExpr.expression.text)
56
+ ) {
57
+ const helperName = callExpr.expression.text
58
+ const [firstArg, secondArg] = callExpr.arguments
59
+ if (firstArg && ts.isStringLiteral(firstArg)) {
60
+ const workflowName = firstArg.text
61
+ let funcName: string
62
+ if (
63
+ helperName === 'graphStart' &&
64
+ secondArg &&
65
+ ts.isStringLiteral(secondArg)
66
+ ) {
67
+ funcName = `${helperName}:${workflowName}:${secondArg.text}`
68
+ } else {
69
+ funcName = `${helperName}:${workflowName}`
70
+ }
71
+ result.pikkuFuncId = funcName
72
+ result.name = funcName
73
+ result.explicitName = funcName
74
+ result.isHelper = true
75
+ return result
76
+ }
277
77
  }
278
78
 
279
79
  // Special case for wireHTTP: if this is an identifier within an object literal,
@@ -308,33 +108,24 @@ export function extractFunctionName(
308
108
  (ts.isArrowFunction(firstArg) ||
309
109
  ts.isFunctionExpression(firstArg))
310
110
  ) {
311
- // Use the function directly for position calculation
312
- result.pikkuFuncName = makeDeterministicAnonName(
313
- firstArg,
314
- checker,
315
- rootDir
316
- )
317
-
318
111
  // Continue with name extraction
319
112
  if (ts.isIdentifier(parent.name)) {
320
113
  result.propertyName = parent.name.text
321
114
  }
322
115
 
323
- // Check if the variable is exported
324
116
  if (
325
117
  ts.isVariableDeclaration(decl) &&
326
- isNamedExport(decl) &&
118
+ isNamedExport(decl, checker) &&
327
119
  ts.isIdentifier(decl.name)
328
120
  ) {
329
121
  result.exportedName = decl.name.text
330
- // CRITICAL FIX: Use exported name as pikkuFuncName for consistency
331
- result.pikkuFuncName = decl.name.text
332
- } else if (ts.isIdentifier(decl.name)) {
333
- // If not exported, still capture the variable name
334
- result.localName = decl.name.text
122
+ result.pikkuFuncId = decl.name.text
123
+ }
124
+
125
+ if (!result.pikkuFuncId) {
126
+ result.pikkuFuncId = `__temp_${randomUUID()}`
335
127
  }
336
128
 
337
- // Apply name priority logic
338
129
  populateNameByPriority(result)
339
130
  return result
340
131
  }
@@ -344,10 +135,6 @@ export function extractFunctionName(
344
135
  }
345
136
  }
346
137
 
347
- // Keep track of the original call expression for position-based naming
348
- let originalCallExpr = callExpr
349
-
350
- // For direct pikku function calls where callExpr is the call expression itself
351
138
  if (ts.isCallExpression(callExpr)) {
352
139
  const { expression, arguments: args } = callExpr
353
140
 
@@ -429,13 +216,10 @@ export function extractFunctionName(
429
216
 
430
217
  // Check if the variable is exported
431
218
  if (
432
- isNamedExport(funcDecl) &&
219
+ isNamedExport(funcDecl, checker) &&
433
220
  ts.isIdentifier(funcDecl.name)
434
221
  ) {
435
222
  result.exportedName = funcDecl.name.text
436
- } else if (ts.isIdentifier(funcDecl.name)) {
437
- // If not exported, still capture the variable name
438
- result.localName = funcDecl.name.text
439
223
  }
440
224
 
441
225
  break
@@ -444,17 +228,11 @@ export function extractFunctionName(
444
228
  ts.isFunctionExpression(funcDecl.initializer) ||
445
229
  ts.isArrowFunction(funcDecl.initializer)
446
230
  ) {
447
- // mainFunc = funcDecl.initializer
448
-
449
- // Check if the variable is exported
450
231
  if (
451
- isNamedExport(funcDecl) &&
232
+ isNamedExport(funcDecl, checker) &&
452
233
  ts.isIdentifier(funcDecl.name)
453
234
  ) {
454
235
  result.exportedName = funcDecl.name.text
455
- } else if (ts.isIdentifier(funcDecl.name)) {
456
- // If not exported, still capture the variable name
457
- result.localName = funcDecl.name.text
458
236
  }
459
237
 
460
238
  break
@@ -471,12 +249,6 @@ export function extractFunctionName(
471
249
  ts.isIdentifier(funcDecl.name)
472
250
  ) {
473
251
  result.exportedName = funcDecl.name.text
474
- } else if (
475
- funcDecl.name &&
476
- ts.isIdentifier(funcDecl.name)
477
- ) {
478
- // If not exported, still capture the function name
479
- result.localName = funcDecl.name.text
480
252
  }
481
253
 
482
254
  break
@@ -531,13 +303,10 @@ export function extractFunctionName(
531
303
 
532
304
  // Check if the variable is exported
533
305
  if (
534
- isNamedExport(shorthandDecl) &&
306
+ isNamedExport(shorthandDecl, checker) &&
535
307
  ts.isIdentifier(shorthandDecl.name)
536
308
  ) {
537
309
  result.exportedName = shorthandDecl.name.text
538
- } else if (ts.isIdentifier(shorthandDecl.name)) {
539
- // If not exported, still capture the variable name
540
- result.localName = shorthandDecl.name.text
541
310
  }
542
311
 
543
312
  break
@@ -546,17 +315,11 @@ export function extractFunctionName(
546
315
  ts.isFunctionExpression(shorthandDecl.initializer) ||
547
316
  ts.isArrowFunction(shorthandDecl.initializer)
548
317
  ) {
549
- // mainFunc = shorthandDecl.initializer
550
-
551
- // Check if the variable is exported
552
318
  if (
553
- isNamedExport(shorthandDecl) &&
319
+ isNamedExport(shorthandDecl, checker) &&
554
320
  ts.isIdentifier(shorthandDecl.name)
555
321
  ) {
556
322
  result.exportedName = shorthandDecl.name.text
557
- } else if (ts.isIdentifier(shorthandDecl.name)) {
558
- // If not exported, still capture the variable name
559
- result.localName = shorthandDecl.name.text
560
323
  }
561
324
 
562
325
  break
@@ -573,12 +336,6 @@ export function extractFunctionName(
573
336
  ts.isIdentifier(shorthandDecl.name)
574
337
  ) {
575
338
  result.exportedName = shorthandDecl.name.text
576
- } else if (
577
- shorthandDecl.name &&
578
- ts.isIdentifier(shorthandDecl.name)
579
- ) {
580
- // If not exported, still capture the function name
581
- result.localName = shorthandDecl.name.text
582
339
  }
583
340
 
584
341
  break
@@ -610,10 +367,6 @@ export function extractFunctionName(
610
367
  ts.isIdentifier(decl.initializer.expression) &&
611
368
  decl.initializer.expression.text.startsWith('pikku')
612
369
  ) {
613
- // Update originalCallExpr to use the call expression position
614
- // instead of the variable declaration position
615
- originalCallExpr = decl.initializer
616
-
617
370
  // Check for object with 'override' property in first argument
618
371
  const firstArg = decl.initializer.arguments[0]
619
372
  if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
@@ -642,13 +395,8 @@ export function extractFunctionName(
642
395
  }
643
396
 
644
397
  // Check if the variable is exported
645
- if (isNamedExport(decl) && ts.isIdentifier(decl.name)) {
398
+ if (isNamedExport(decl, checker) && ts.isIdentifier(decl.name)) {
646
399
  result.exportedName = decl.name.text
647
- } else if (ts.isIdentifier(decl.name)) {
648
- // If not explicitly set by name property above, set functionName
649
- if (!result.localName) {
650
- result.localName = decl.name.text
651
- }
652
400
  }
653
401
  } else if (
654
402
  ts.isFunctionExpression(decl.initializer) ||
@@ -657,10 +405,8 @@ export function extractFunctionName(
657
405
  // mainFunc = decl.initializer
658
406
 
659
407
  // Check if the variable is exported
660
- if (isNamedExport(decl) && ts.isIdentifier(decl.name)) {
408
+ if (isNamedExport(decl, checker) && ts.isIdentifier(decl.name)) {
661
409
  result.exportedName = decl.name.text
662
- } else if (ts.isIdentifier(decl.name)) {
663
- result.localName = decl.name.text
664
410
  }
665
411
  }
666
412
  } else if (ts.isFunctionDeclaration(decl)) {
@@ -675,31 +421,16 @@ export function extractFunctionName(
675
421
  ts.isIdentifier(decl.name)
676
422
  ) {
677
423
  result.exportedName = decl.name.text
678
- } else if (decl.name && ts.isIdentifier(decl.name)) {
679
- result.localName = decl.name.text
680
424
  }
681
425
  }
682
426
  }
683
427
  }
684
428
  }
685
429
 
686
- // Generate the deterministic function name based on the original call expression
687
- // (the config), not the resolved inner function. This ensures the metadata key
688
- // matches what will be looked up at runtime when referencing the config object.
689
- result.pikkuFuncName = makeDeterministicAnonName(
690
- originalCallExpr,
691
- checker,
692
- rootDir
693
- )
694
-
695
- // Continue with regular name extraction for remaining cases
696
430
  // 1) const foo = pikkuFunc(...)
697
431
  if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
698
- if (isNamedExport(parent)) {
432
+ if (isNamedExport(parent, checker)) {
699
433
  result.exportedName = parent.name.text
700
- } else {
701
- // Still capture the variable name even if not exported
702
- result.localName = parent.name.text
703
434
  }
704
435
  }
705
436
  // 2) { foo: pikkuFunc(...) }
@@ -714,8 +445,8 @@ export function extractFunctionName(
714
445
  result.propertyName = parent.name.text
715
446
  }
716
447
  // 3) Handle any remaining cases for pikkuFunc({ override: '…', func: … })
717
- else if (ts.isCallExpression(originalCallExpr)) {
718
- const firstArg = originalCallExpr.arguments[0]
448
+ else if (ts.isCallExpression(callExpr)) {
449
+ const firstArg = callExpr.arguments[0]
719
450
  if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
720
451
  for (const prop of firstArg.properties) {
721
452
  if (
@@ -735,9 +466,12 @@ export function extractFunctionName(
735
466
  // Apply name priority logic
736
467
  populateNameByPriority(result)
737
468
 
738
- // CRITICAL FIX: If we have an exported name, use it as the pikkuFuncName for consistent lookup
739
- if (result.exportedName && !result.explicitName) {
740
- result.pikkuFuncName = result.exportedName
469
+ if (result.explicitName) {
470
+ result.pikkuFuncId = result.explicitName
471
+ } else if (result.exportedName) {
472
+ result.pikkuFuncId = result.exportedName
473
+ } else {
474
+ result.pikkuFuncId = `__temp_${randomUUID()}`
741
475
  }
742
476
 
743
477
  return result
@@ -755,35 +489,51 @@ export function populateNameByPriority(result: ExtractedFunctionName): void {
755
489
  else if (result.exportedName) {
756
490
  result.name = result.exportedName
757
491
  }
758
- // Priority 3: If we have a property name, use that
759
- // else if (result.propertyName) {
760
- // result.name = result.propertyName
761
- // }
762
492
  // Fallback: Use the deterministic name, but we could shorten it in the future
763
493
  else {
764
- // For now, just use the full pikkuFuncName
765
- result.name = result.pikkuFuncName
494
+ // For now, just use the full pikkuFuncId
495
+ result.name = result.pikkuFuncId
766
496
  }
767
497
  }
768
498
 
769
499
  /**
770
500
  * Helper function to check if a variable declaration is a named export
771
501
  */
772
- export function isNamedExport(declaration: ts.VariableDeclaration): boolean {
502
+ export function isNamedExport(
503
+ declaration: ts.VariableDeclaration,
504
+ checker?: ts.TypeChecker
505
+ ): boolean {
773
506
  let parent: any = declaration.parent
774
507
  if (!parent) return false
775
508
 
776
- // Check if it's part of a variable declaration list
777
509
  if (ts.isVariableDeclarationList(parent)) {
778
510
  parent = parent.parent
779
511
  if (!parent) return false
780
512
 
781
- // Check if it's in an export declaration
782
513
  if (ts.isVariableStatement(parent)) {
783
- return (
784
- parent.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ??
785
- false
786
- )
514
+ if (
515
+ parent.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
516
+ ) {
517
+ return true
518
+ }
519
+ }
520
+ }
521
+
522
+ if (checker && ts.isIdentifier(declaration.name)) {
523
+ const sourceFile = declaration.getSourceFile()
524
+ const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile)
525
+ if (sourceFileSymbol) {
526
+ const exports = checker.getExportsOfModule(sourceFileSymbol)
527
+ const declSymbol = checker.getSymbolAtLocation(declaration.name)
528
+ if (declSymbol) {
529
+ return exports.some((exp) => {
530
+ let resolved = exp
531
+ if (resolved.flags & ts.SymbolFlags.Alias) {
532
+ resolved = checker.getAliasedSymbol(resolved) ?? resolved
533
+ }
534
+ return resolved === declSymbol
535
+ })
536
+ }
787
537
  }
788
538
  }
789
539
 
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript'
2
- import { FunctionServicesMeta } from '@pikku/core'
2
+ import { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core'
3
3
 
4
4
  /**
5
5
  * Extract services from a function's first parameter destructuring pattern
@@ -26,10 +26,43 @@ export function extractServicesFromFunction(
26
26
  services.services.push(original)
27
27
  }
28
28
  }
29
- } else if (ts.isIdentifier(firstParam.name)) {
29
+ } else if (
30
+ ts.isIdentifier(firstParam.name) &&
31
+ !firstParam.name.text.startsWith('_')
32
+ ) {
30
33
  services.optimized = false
31
34
  }
32
35
  }
33
36
 
34
37
  return services
35
38
  }
39
+
40
+ export function extractUsedWires(
41
+ handlerNode: ts.FunctionExpression | ts.ArrowFunction,
42
+ paramIndex: number
43
+ ): FunctionWiresMeta {
44
+ const param = handlerNode.parameters[paramIndex]
45
+ if (param && ts.isObjectBindingPattern(param.name)) {
46
+ const wires: string[] = []
47
+ for (const elem of param.name.elements) {
48
+ const propertyName =
49
+ elem.propertyName && ts.isIdentifier(elem.propertyName)
50
+ ? elem.propertyName.text
51
+ : ts.isIdentifier(elem.name)
52
+ ? elem.name.text
53
+ : undefined
54
+ if (propertyName) {
55
+ wires.push(propertyName)
56
+ }
57
+ }
58
+ return { optimized: true, wires }
59
+ }
60
+ if (
61
+ param &&
62
+ ts.isIdentifier(param.name) &&
63
+ !param.name.text.startsWith('_')
64
+ ) {
65
+ return { optimized: false, wires: [] }
66
+ }
67
+ return { optimized: true, wires: [] }
68
+ }