@pikku/inspector 0.11.1 → 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 (189) hide show
  1. package/CHANGELOG.md +26 -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 +327 -59
  9. package/dist/add/add-http-route.d.ts +19 -10
  10. package/dist/add/add-http-route.js +153 -44
  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.d.ts +3 -0
  23. package/dist/add/add-rpc-invocations.js +65 -25
  24. package/dist/add/add-schedule.js +11 -5
  25. package/dist/add/add-secret.d.ts +3 -0
  26. package/dist/add/add-secret.js +82 -0
  27. package/dist/add/add-trigger.d.ts +2 -0
  28. package/dist/add/add-trigger.js +87 -0
  29. package/dist/add/add-variable.d.ts +1 -0
  30. package/dist/add/add-variable.js +8 -0
  31. package/dist/add/add-workflow-graph.d.ts +7 -0
  32. package/dist/add/add-workflow-graph.js +396 -0
  33. package/dist/add/add-workflow.js +124 -26
  34. package/dist/error-codes.d.ts +16 -1
  35. package/dist/error-codes.js +21 -1
  36. package/dist/index.d.ts +9 -5
  37. package/dist/index.js +5 -2
  38. package/dist/inspector.d.ts +1 -1
  39. package/dist/inspector.js +106 -13
  40. package/dist/schema-generator.d.ts +1 -0
  41. package/dist/schema-generator.js +1 -0
  42. package/dist/types-map.js +10 -1
  43. package/dist/types.d.ts +180 -30
  44. package/dist/utils/compute-required-schemas.d.ts +4 -0
  45. package/dist/utils/compute-required-schemas.js +41 -0
  46. package/dist/utils/contract-hashes.d.ts +35 -0
  47. package/dist/utils/contract-hashes.js +202 -0
  48. package/dist/utils/custom-types-generator.d.ts +9 -0
  49. package/dist/utils/custom-types-generator.js +71 -0
  50. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  51. package/dist/utils/detect-schema-vendor.js +76 -0
  52. package/dist/utils/ensure-function-metadata.d.ts +5 -2
  53. package/dist/utils/ensure-function-metadata.js +220 -6
  54. package/dist/utils/extract-function-name.d.ts +5 -16
  55. package/dist/utils/extract-function-name.js +93 -298
  56. package/dist/utils/extract-services.d.ts +2 -1
  57. package/dist/utils/extract-services.js +25 -1
  58. package/dist/utils/filter-inspector-state.js +107 -23
  59. package/dist/utils/get-property-value.d.ts +8 -2
  60. package/dist/utils/get-property-value.js +33 -4
  61. package/dist/utils/hash.d.ts +2 -0
  62. package/dist/utils/hash.js +23 -0
  63. package/dist/utils/middleware.d.ts +7 -30
  64. package/dist/utils/middleware.js +80 -66
  65. package/dist/utils/permissions.d.ts +2 -2
  66. package/dist/utils/permissions.js +10 -10
  67. package/dist/utils/post-process.d.ts +9 -10
  68. package/dist/utils/post-process.js +231 -24
  69. package/dist/utils/resolve-external-package.d.ts +12 -0
  70. package/dist/utils/resolve-external-package.js +34 -0
  71. package/dist/utils/resolve-function-types.d.ts +6 -0
  72. package/dist/utils/resolve-function-types.js +29 -0
  73. package/dist/utils/resolve-identifier.d.ts +10 -0
  74. package/dist/utils/resolve-identifier.js +36 -0
  75. package/dist/utils/resolve-versions.d.ts +2 -0
  76. package/dist/utils/resolve-versions.js +78 -0
  77. package/dist/utils/schema-generator.d.ts +9 -0
  78. package/dist/utils/schema-generator.js +209 -0
  79. package/dist/utils/serialize-inspector-state.d.ts +73 -13
  80. package/dist/utils/serialize-inspector-state.js +102 -6
  81. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  82. package/dist/utils/serialize-mcp-json.js +99 -0
  83. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  84. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  85. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  86. package/dist/utils/serialize-openapi-json.js +151 -0
  87. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  88. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  89. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
  90. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +830 -0
  91. package/dist/{workflow/extract-simple-workflow.d.ts → utils/workflow/dsl/extract-dsl-workflow.d.ts} +4 -2
  92. package/dist/{workflow/extract-simple-workflow.js → utils/workflow/dsl/extract-dsl-workflow.js} +572 -72
  93. package/dist/utils/workflow/dsl/index.d.ts +7 -0
  94. package/dist/utils/workflow/dsl/index.js +7 -0
  95. package/dist/{workflow → utils/workflow/dsl}/patterns.d.ts +21 -0
  96. package/dist/{workflow → utils/workflow/dsl}/patterns.js +90 -10
  97. package/dist/{workflow → utils/workflow/dsl}/validation.d.ts +2 -0
  98. package/dist/{workflow → utils/workflow/dsl}/validation.js +25 -7
  99. package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
  100. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +318 -0
  101. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  102. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  103. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  104. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  105. package/dist/utils/workflow/graph/index.d.ts +8 -0
  106. package/dist/utils/workflow/graph/index.js +8 -0
  107. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +35 -0
  108. package/dist/utils/workflow/graph/serialize-workflow-graph.js +150 -0
  109. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +203 -0
  110. package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
  111. package/dist/visit.js +13 -2
  112. package/package.json +26 -4
  113. package/src/add/add-ai-agent.ts +468 -0
  114. package/src/add/add-channel.ts +82 -79
  115. package/src/add/add-cli.ts +49 -20
  116. package/src/add/add-file-with-factory.ts +2 -0
  117. package/src/add/add-functions.ts +429 -71
  118. package/src/add/add-http-route.ts +246 -65
  119. package/src/add/add-http-routes.ts +228 -0
  120. package/src/add/add-keyed-wiring.ts +151 -0
  121. package/src/add/add-mcp-prompt.ts +26 -15
  122. package/src/add/add-mcp-resource.ts +27 -15
  123. package/src/add/add-middleware.ts +482 -80
  124. package/src/add/add-permission.ts +199 -40
  125. package/src/add/add-queue-worker.ts +24 -19
  126. package/src/add/add-rpc-invocations.ts +78 -31
  127. package/src/add/add-schedule.ts +16 -11
  128. package/src/add/add-secret.ts +140 -0
  129. package/src/add/add-trigger.ts +154 -0
  130. package/src/add/add-variable.ts +9 -0
  131. package/src/add/add-workflow-graph.ts +522 -0
  132. package/src/add/add-workflow.ts +117 -30
  133. package/src/error-codes.ts +26 -1
  134. package/src/index.ts +27 -8
  135. package/src/inspector.ts +145 -17
  136. package/src/schema-generator.ts +1 -0
  137. package/src/types-map.ts +12 -1
  138. package/src/types.ts +192 -51
  139. package/src/utils/compute-required-schemas.ts +49 -0
  140. package/src/utils/contract-hashes.test.ts +528 -0
  141. package/src/utils/contract-hashes.ts +290 -0
  142. package/src/utils/custom-types-generator.ts +88 -0
  143. package/src/utils/detect-schema-vendor.ts +90 -0
  144. package/src/utils/ensure-function-metadata.ts +324 -7
  145. package/src/utils/extract-function-name.ts +108 -358
  146. package/src/utils/extract-services.ts +35 -2
  147. package/src/utils/filter-inspector-state.test.ts +34 -20
  148. package/src/utils/filter-inspector-state.ts +140 -31
  149. package/src/utils/get-property-value.ts +50 -5
  150. package/src/utils/hash.ts +26 -0
  151. package/src/utils/middleware.test.ts +204 -0
  152. package/src/utils/middleware.ts +129 -67
  153. package/src/utils/permissions.test.ts +35 -12
  154. package/src/utils/permissions.ts +10 -10
  155. package/src/utils/post-process.ts +283 -43
  156. package/src/utils/resolve-external-package.ts +42 -0
  157. package/src/utils/resolve-function-types.ts +42 -0
  158. package/src/utils/resolve-identifier.ts +46 -0
  159. package/src/utils/resolve-versions.test.ts +249 -0
  160. package/src/utils/resolve-versions.ts +105 -0
  161. package/src/utils/schema-generator.ts +329 -0
  162. package/src/utils/serialize-inspector-state.ts +181 -20
  163. package/src/utils/serialize-mcp-json.ts +145 -0
  164. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  165. package/src/utils/serialize-openapi-json.ts +277 -0
  166. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  167. package/src/utils/test-data/inspector-state.json +69 -66
  168. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1104 -0
  169. package/src/{workflow/extract-simple-workflow.ts → utils/workflow/dsl/extract-dsl-workflow.ts} +678 -85
  170. package/src/utils/workflow/dsl/index.ts +11 -0
  171. package/src/{workflow → utils/workflow/dsl}/patterns.ts +108 -11
  172. package/src/{workflow → utils/workflow/dsl}/validation.ts +34 -7
  173. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +422 -0
  174. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  175. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  176. package/src/utils/workflow/graph/index.ts +11 -0
  177. package/src/utils/workflow/graph/serialize-workflow-graph.ts +216 -0
  178. package/src/utils/workflow/graph/workflow-graph.types.ts +231 -0
  179. package/src/visit.ts +14 -2
  180. package/tsconfig.tsbuildinfo +1 -1
  181. package/dist/add/add-mcp-tool.d.ts +0 -2
  182. package/dist/add/add-mcp-tool.js +0 -81
  183. package/dist/utils/extract-service-metadata.d.ts +0 -19
  184. package/dist/utils/extract-service-metadata.js +0 -244
  185. package/dist/utils/write-service-metadata.d.ts +0 -13
  186. package/dist/utils/write-service-metadata.js +0 -37
  187. package/src/add/add-mcp-tool.ts +0 -141
  188. package/src/utils/extract-service-metadata.ts +0 -353
  189. package/src/utils/write-service-metadata.ts +0 -51
@@ -1,214 +1,55 @@
1
1
  import * as ts from 'typescript';
2
- import { toRelativePath } from './find-root-dir.js';
3
- /**
4
- * Generate a deterministic "anonymous" name for any expression node,
5
- * but if it's an Identifier pointing to a function, resolve it back
6
- * to the function's declaration (so you get the true source location).
7
- */
8
- export function makeDeterministicAnonName(start, checker, rootDir) {
9
- let node = start;
10
- let target = start;
11
- // Handle the case where we're starting with an identifier directly
12
- if (ts.isIdentifier(node)) {
13
- const sym = checker.getSymbolAtLocation(node);
14
- if (sym) {
15
- let resolvedSym = sym;
16
- if (resolvedSym.flags & ts.SymbolFlags.Alias) {
17
- resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym;
18
- }
19
- const decls = resolvedSym.declarations ?? [];
20
- if (decls.length > 0) {
21
- // Start with the declaration, not the reference
22
- const decl = decls[0];
23
- // If it's a variable declaration with a function initializer, use the function directly
24
- if (ts.isVariableDeclaration(decl) &&
25
- decl.initializer &&
26
- (ts.isFunctionExpression(decl.initializer) ||
27
- ts.isArrowFunction(decl.initializer))) {
28
- target = decl.initializer;
29
- // Return early - we found the function directly
30
- const sf = target.getSourceFile();
31
- const relativePath = toRelativePath(sf.fileName, rootDir);
32
- const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_');
33
- const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
34
- return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
35
- }
36
- // Otherwise continue resolution with the declaration
37
- node = decl;
38
- target = decl;
39
- }
40
- }
41
- }
42
- // In an object literal property value, first try to resolve the identifier
43
- if (ts.isPropertyAssignment(node.parent) &&
44
- node === node.parent.initializer &&
45
- ts.isIdentifier(node)) {
46
- const sym = checker.getSymbolAtLocation(node);
47
- if (sym) {
48
- // Process the symbol to find the real declaration
49
- let resolvedSym = sym;
50
- if (resolvedSym.flags & ts.SymbolFlags.Alias) {
51
- resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym;
52
- }
53
- const decls = resolvedSym.declarations ?? [];
54
- if (decls.length > 0) {
55
- // Found a declaration - use it as our new target
56
- const decl = decls[0];
57
- if (!decl) {
58
- throw new Error('No declaration found');
59
- }
60
- // If it's a variable declaration with an initializer function, use that
61
- if (ts.isVariableDeclaration(decl) && decl.initializer) {
62
- if (ts.isFunctionExpression(decl.initializer) ||
63
- ts.isArrowFunction(decl.initializer)) {
64
- target = decl.initializer;
65
- // Return early - we found the function directly
66
- const sf = target.getSourceFile();
67
- const relativePath = toRelativePath(sf.fileName, rootDir);
68
- const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_');
69
- const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
70
- return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
71
- }
72
- }
73
- else if (ts.isFunctionDeclaration(decl)) {
74
- // Already a function declaration
75
- target = decl;
76
- // Return early
77
- const sf = target.getSourceFile();
78
- const relativePath = toRelativePath(sf.fileName, rootDir);
79
- const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_');
80
- const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
81
- return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
82
- }
83
- // If we didn't return early, continue with this declaration
84
- node = decl;
85
- target = decl;
86
- }
87
- }
88
- }
89
- const seen = new Set();
90
- for (let depth = 0; depth < 10; depth++) {
91
- if (!ts.isIdentifier(node) || seen.has(node))
92
- break;
93
- seen.add(node);
94
- let sym = checker.getSymbolAtLocation(node);
95
- if (!sym)
96
- break;
97
- if (sym.flags & ts.SymbolFlags.Alias) {
98
- sym = checker.getAliasedSymbol(sym) ?? sym;
99
- }
100
- const allDecls = sym.declarations ?? [];
101
- // prefer real .ts/.tsx implementation files
102
- const implDecls = allDecls.filter((d) => !d.getSourceFile().isDeclarationFile);
103
- const decls = implDecls.length ? implDecls : allDecls;
104
- let didResolve = false;
105
- for (const decl of decls) {
106
- // 1) direct function foo() {} or function-expression
107
- if (ts.isFunctionDeclaration(decl) ||
108
- ts.isFunctionExpression(decl) ||
109
- ts.isArrowFunction(decl)) {
110
- target = decl;
111
- didResolve = true;
112
- break;
113
- }
114
- // 2) const foo = () => {} or foo = function() {}
115
- if (ts.isVariableDeclaration(decl) && decl.initializer) {
116
- const init = decl.initializer;
117
- if (ts.isFunctionExpression(init) || ts.isArrowFunction(init)) {
118
- target = init;
119
- didResolve = true;
120
- break;
121
- }
122
- // 2b) const foo = bar; (follow the next identifier)
123
- if (ts.isIdentifier(init)) {
124
- node = init;
125
- target = init;
126
- didResolve = true;
127
- break;
128
- }
129
- }
130
- // 3) Handle shorthand property assignments: { foo } (equivalent to { foo: foo })
131
- if (ts.isShorthandPropertyAssignment(decl)) {
132
- // Get the symbol for the shorthand property
133
- const shorthandSym = checker.getShorthandAssignmentValueSymbol(decl);
134
- if (shorthandSym &&
135
- shorthandSym.declarations &&
136
- shorthandSym.declarations.length > 0) {
137
- // Use the first declaration as our new target
138
- const shorthandDecl = shorthandSym.declarations[0];
139
- target = shorthandDecl;
140
- if (!shorthandDecl) {
141
- throw new Error('No shorthand declaration found');
142
- }
143
- // Check the type of declaration and extract the appropriate identifier to continue resolving
144
- if (ts.isVariableDeclaration(shorthandDecl) &&
145
- ts.isIdentifier(shorthandDecl.name)) {
146
- node = shorthandDecl.name;
147
- didResolve = true;
148
- break;
149
- }
150
- else if (ts.isFunctionDeclaration(shorthandDecl) &&
151
- shorthandDecl.name &&
152
- ts.isIdentifier(shorthandDecl.name)) {
153
- node = shorthandDecl.name;
154
- didResolve = true;
155
- break;
156
- }
157
- else if (ts.isParameter(shorthandDecl) &&
158
- ts.isIdentifier(shorthandDecl.name)) {
159
- node = shorthandDecl.name;
160
- didResolve = true;
161
- break;
162
- }
163
- else if (ts.isPropertyDeclaration(shorthandDecl) &&
164
- ts.isIdentifier(shorthandDecl.name)) {
165
- node = shorthandDecl.name;
166
- didResolve = true;
167
- break;
168
- }
169
- else if (ts.isMethodDeclaration(shorthandDecl) &&
170
- ts.isIdentifier(shorthandDecl.name)) {
171
- node = shorthandDecl.name;
172
- didResolve = true;
173
- break;
174
- }
175
- }
176
- }
177
- // 4) Handle method declarations in classes/objects
178
- if (ts.isMethodDeclaration(decl)) {
179
- target = decl;
180
- didResolve = true;
181
- break;
182
- }
183
- // you can add more cases here if your setup uses imports, etc.
184
- }
185
- if (!didResolve)
186
- break;
187
- }
188
- const sf = target.getSourceFile();
189
- const relativePath = toRelativePath(sf.fileName, rootDir);
190
- const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_');
191
- const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
192
- return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
2
+ import { randomUUID } from 'crypto';
3
+ export function makeContextBasedId(wiringType, ...segments) {
4
+ return [wiringType, ...segments].join(':');
5
+ }
6
+ export function funcIdToTypeName(id) {
7
+ return id
8
+ .split(/[^a-zA-Z0-9]/)
9
+ .filter(Boolean)
10
+ .map((s) => s.charAt(0).toUpperCase() + s.slice(1))
11
+ .join('');
193
12
  }
194
- /**
195
- * Updated function to extract and prioritize function names correctly
196
- * This function follows the priority:
197
- * 1. Object with a name property
198
- * 2. Exported name
199
- * 3. Fallback to deterministic name
200
- */
201
13
  export function extractFunctionName(callExpr, checker, rootDir) {
202
14
  const parent = callExpr.parent;
203
15
  // Initialize the result
204
16
  const result = {
205
- pikkuFuncName: '', // Will be populated later
206
- name: '', // This will hold our "best" name based on priority
17
+ pikkuFuncId: '',
18
+ name: '',
207
19
  exportedName: null,
208
- localName: null,
209
20
  propertyName: null,
210
21
  explicitName: null,
22
+ isHelper: false,
211
23
  };
24
+ const workflowHelpers = new Set([
25
+ 'workflow',
26
+ 'workflowStart',
27
+ 'workflowStatus',
28
+ 'graphStart',
29
+ ]);
30
+ if (ts.isCallExpression(callExpr) &&
31
+ ts.isIdentifier(callExpr.expression) &&
32
+ workflowHelpers.has(callExpr.expression.text)) {
33
+ const helperName = callExpr.expression.text;
34
+ const [firstArg, secondArg] = callExpr.arguments;
35
+ if (firstArg && ts.isStringLiteral(firstArg)) {
36
+ const workflowName = firstArg.text;
37
+ let funcName;
38
+ if (helperName === 'graphStart' &&
39
+ secondArg &&
40
+ ts.isStringLiteral(secondArg)) {
41
+ funcName = `${helperName}:${workflowName}:${secondArg.text}`;
42
+ }
43
+ else {
44
+ funcName = `${helperName}:${workflowName}`;
45
+ }
46
+ result.pikkuFuncId = funcName;
47
+ result.name = funcName;
48
+ result.explicitName = funcName;
49
+ result.isHelper = true;
50
+ return result;
51
+ }
52
+ }
212
53
  // Special case for wireHTTP: if this is an identifier within an object literal,
213
54
  // it might be coming from the HTTP route handling flow
214
55
  if (ts.isIdentifier(callExpr) &&
@@ -234,25 +75,19 @@ export function extractFunctionName(callExpr, checker, rootDir) {
234
75
  if (firstArg &&
235
76
  (ts.isArrowFunction(firstArg) ||
236
77
  ts.isFunctionExpression(firstArg))) {
237
- // Use the function directly for position calculation
238
- result.pikkuFuncName = makeDeterministicAnonName(firstArg, checker, rootDir);
239
78
  // Continue with name extraction
240
79
  if (ts.isIdentifier(parent.name)) {
241
80
  result.propertyName = parent.name.text;
242
81
  }
243
- // Check if the variable is exported
244
82
  if (ts.isVariableDeclaration(decl) &&
245
- isNamedExport(decl) &&
83
+ isNamedExport(decl, checker) &&
246
84
  ts.isIdentifier(decl.name)) {
247
85
  result.exportedName = decl.name.text;
248
- // CRITICAL FIX: Use exported name as pikkuFuncName for consistency
249
- result.pikkuFuncName = decl.name.text;
86
+ result.pikkuFuncId = decl.name.text;
250
87
  }
251
- else if (ts.isIdentifier(decl.name)) {
252
- // If not exported, still capture the variable name
253
- result.localName = decl.name.text;
88
+ if (!result.pikkuFuncId) {
89
+ result.pikkuFuncId = `__temp_${randomUUID()}`;
254
90
  }
255
- // Apply name priority logic
256
91
  populateNameByPriority(result);
257
92
  return result;
258
93
  }
@@ -261,9 +96,6 @@ export function extractFunctionName(callExpr, checker, rootDir) {
261
96
  }
262
97
  }
263
98
  }
264
- // Keep track of the original call expression for position-based naming
265
- let originalCallExpr = callExpr;
266
- // For direct pikku function calls where callExpr is the call expression itself
267
99
  if (ts.isCallExpression(callExpr)) {
268
100
  const { expression, arguments: args } = callExpr;
269
101
  // Check if this is a pikku function call (pikkuFunc, pikkuSessionlessFunc, etc)
@@ -274,9 +106,9 @@ export function extractFunctionName(callExpr, checker, rootDir) {
274
106
  for (const prop of firstArg.properties) {
275
107
  if (ts.isPropertyAssignment(prop) &&
276
108
  ts.isIdentifier(prop.name) &&
277
- prop.name.text === 'name' &&
109
+ prop.name.text === 'override' &&
278
110
  ts.isStringLiteral(prop.initializer)) {
279
- // Priority 1: Object with name property
111
+ // Priority 1: Object with override property
280
112
  result.explicitName = prop.initializer.text;
281
113
  break;
282
114
  }
@@ -327,29 +159,19 @@ export function extractFunctionName(callExpr, checker, rootDir) {
327
159
  ts.isFunctionExpression(firstArg))) {
328
160
  // mainFunc = firstArg
329
161
  // Check if the variable is exported
330
- if (isNamedExport(funcDecl) &&
162
+ if (isNamedExport(funcDecl, checker) &&
331
163
  ts.isIdentifier(funcDecl.name)) {
332
164
  result.exportedName = funcDecl.name.text;
333
165
  }
334
- else if (ts.isIdentifier(funcDecl.name)) {
335
- // If not exported, still capture the variable name
336
- result.localName = funcDecl.name.text;
337
- }
338
166
  break;
339
167
  }
340
168
  }
341
169
  else if (ts.isFunctionExpression(funcDecl.initializer) ||
342
170
  ts.isArrowFunction(funcDecl.initializer)) {
343
- // mainFunc = funcDecl.initializer
344
- // Check if the variable is exported
345
- if (isNamedExport(funcDecl) &&
171
+ if (isNamedExport(funcDecl, checker) &&
346
172
  ts.isIdentifier(funcDecl.name)) {
347
173
  result.exportedName = funcDecl.name.text;
348
174
  }
349
- else if (ts.isIdentifier(funcDecl.name)) {
350
- // If not exported, still capture the variable name
351
- result.localName = funcDecl.name.text;
352
- }
353
175
  break;
354
176
  }
355
177
  }
@@ -361,11 +183,6 @@ export function extractFunctionName(callExpr, checker, rootDir) {
361
183
  ts.isIdentifier(funcDecl.name)) {
362
184
  result.exportedName = funcDecl.name.text;
363
185
  }
364
- else if (funcDecl.name &&
365
- ts.isIdentifier(funcDecl.name)) {
366
- // If not exported, still capture the function name
367
- result.localName = funcDecl.name.text;
368
- }
369
186
  break;
370
187
  }
371
188
  }
@@ -407,29 +224,19 @@ export function extractFunctionName(callExpr, checker, rootDir) {
407
224
  ts.isFunctionExpression(firstArg))) {
408
225
  // mainFunc = firstArg
409
226
  // Check if the variable is exported
410
- if (isNamedExport(shorthandDecl) &&
227
+ if (isNamedExport(shorthandDecl, checker) &&
411
228
  ts.isIdentifier(shorthandDecl.name)) {
412
229
  result.exportedName = shorthandDecl.name.text;
413
230
  }
414
- else if (ts.isIdentifier(shorthandDecl.name)) {
415
- // If not exported, still capture the variable name
416
- result.localName = shorthandDecl.name.text;
417
- }
418
231
  break;
419
232
  }
420
233
  }
421
234
  else if (ts.isFunctionExpression(shorthandDecl.initializer) ||
422
235
  ts.isArrowFunction(shorthandDecl.initializer)) {
423
- // mainFunc = shorthandDecl.initializer
424
- // Check if the variable is exported
425
- if (isNamedExport(shorthandDecl) &&
236
+ if (isNamedExport(shorthandDecl, checker) &&
426
237
  ts.isIdentifier(shorthandDecl.name)) {
427
238
  result.exportedName = shorthandDecl.name.text;
428
239
  }
429
- else if (ts.isIdentifier(shorthandDecl.name)) {
430
- // If not exported, still capture the variable name
431
- result.localName = shorthandDecl.name.text;
432
- }
433
240
  break;
434
241
  }
435
242
  }
@@ -441,11 +248,6 @@ export function extractFunctionName(callExpr, checker, rootDir) {
441
248
  ts.isIdentifier(shorthandDecl.name)) {
442
249
  result.exportedName = shorthandDecl.name.text;
443
250
  }
444
- else if (shorthandDecl.name &&
445
- ts.isIdentifier(shorthandDecl.name)) {
446
- // If not exported, still capture the function name
447
- result.localName = shorthandDecl.name.text;
448
- }
449
251
  break;
450
252
  }
451
253
  }
@@ -472,18 +274,15 @@ export function extractFunctionName(callExpr, checker, rootDir) {
472
274
  if (ts.isCallExpression(decl.initializer) &&
473
275
  ts.isIdentifier(decl.initializer.expression) &&
474
276
  decl.initializer.expression.text.startsWith('pikku')) {
475
- // Update originalCallExpr to use the call expression position
476
- // instead of the variable declaration position
477
- originalCallExpr = decl.initializer;
478
- // Check for object with 'name' property in first argument
277
+ // Check for object with 'override' property in first argument
479
278
  const firstArg = decl.initializer.arguments[0];
480
279
  if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
481
280
  for (const prop of firstArg.properties) {
482
281
  if (ts.isPropertyAssignment(prop) &&
483
282
  ts.isIdentifier(prop.name) &&
484
- prop.name.text === 'name' &&
283
+ prop.name.text === 'override' &&
485
284
  ts.isStringLiteral(prop.initializer)) {
486
- // Priority 1: Object with name property
285
+ // Priority 1: Object with override property
487
286
  result.explicitName = prop.initializer.text;
488
287
  break;
489
288
  }
@@ -497,26 +296,17 @@ export function extractFunctionName(callExpr, checker, rootDir) {
497
296
  }
498
297
  }
499
298
  // Check if the variable is exported
500
- if (isNamedExport(decl) && ts.isIdentifier(decl.name)) {
299
+ if (isNamedExport(decl, checker) && ts.isIdentifier(decl.name)) {
501
300
  result.exportedName = decl.name.text;
502
301
  }
503
- else if (ts.isIdentifier(decl.name)) {
504
- // If not explicitly set by name property above, set functionName
505
- if (!result.localName) {
506
- result.localName = decl.name.text;
507
- }
508
- }
509
302
  }
510
303
  else if (ts.isFunctionExpression(decl.initializer) ||
511
304
  ts.isArrowFunction(decl.initializer)) {
512
305
  // mainFunc = decl.initializer
513
306
  // Check if the variable is exported
514
- if (isNamedExport(decl) && ts.isIdentifier(decl.name)) {
307
+ if (isNamedExport(decl, checker) && ts.isIdentifier(decl.name)) {
515
308
  result.exportedName = decl.name.text;
516
309
  }
517
- else if (ts.isIdentifier(decl.name)) {
518
- result.localName = decl.name.text;
519
- }
520
310
  }
521
311
  }
522
312
  else if (ts.isFunctionDeclaration(decl)) {
@@ -527,27 +317,15 @@ export function extractFunctionName(callExpr, checker, rootDir) {
527
317
  ts.isIdentifier(decl.name)) {
528
318
  result.exportedName = decl.name.text;
529
319
  }
530
- else if (decl.name && ts.isIdentifier(decl.name)) {
531
- result.localName = decl.name.text;
532
- }
533
320
  }
534
321
  }
535
322
  }
536
323
  }
537
- // Generate the deterministic function name based on the original call expression
538
- // (the config), not the resolved inner function. This ensures the metadata key
539
- // matches what will be looked up at runtime when referencing the config object.
540
- result.pikkuFuncName = makeDeterministicAnonName(originalCallExpr, checker, rootDir);
541
- // Continue with regular name extraction for remaining cases
542
324
  // 1) const foo = pikkuFunc(...)
543
325
  if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
544
- if (isNamedExport(parent)) {
326
+ if (isNamedExport(parent, checker)) {
545
327
  result.exportedName = parent.name.text;
546
328
  }
547
- else {
548
- // Still capture the variable name even if not exported
549
- result.localName = parent.name.text;
550
- }
551
329
  }
552
330
  // 2) { foo: pikkuFunc(...) }
553
331
  else if (ts.isPropertyAssignment(parent) && ts.isIdentifier(parent.name)) {
@@ -558,14 +336,14 @@ export function extractFunctionName(callExpr, checker, rootDir) {
558
336
  ts.isIdentifier(parent.name)) {
559
337
  result.propertyName = parent.name.text;
560
338
  }
561
- // 3) Handle any remaining cases for pikkuFunc({ name: '…', func: … })
562
- else if (ts.isCallExpression(originalCallExpr)) {
563
- const firstArg = originalCallExpr.arguments[0];
339
+ // 3) Handle any remaining cases for pikkuFunc({ override: '…', func: … })
340
+ else if (ts.isCallExpression(callExpr)) {
341
+ const firstArg = callExpr.arguments[0];
564
342
  if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
565
343
  for (const prop of firstArg.properties) {
566
344
  if (ts.isPropertyAssignment(prop) &&
567
345
  ts.isIdentifier(prop.name) &&
568
- prop.name.text === 'name' &&
346
+ prop.name.text === 'override' &&
569
347
  ts.isStringLiteral(prop.initializer) &&
570
348
  !result.explicitName // Only set if not already set
571
349
  ) {
@@ -577,9 +355,14 @@ export function extractFunctionName(callExpr, checker, rootDir) {
577
355
  }
578
356
  // Apply name priority logic
579
357
  populateNameByPriority(result);
580
- // CRITICAL FIX: If we have an exported name, use it as the pikkuFuncName for consistent lookup
581
- if (result.exportedName && !result.explicitName) {
582
- result.pikkuFuncName = result.exportedName;
358
+ if (result.explicitName) {
359
+ result.pikkuFuncId = result.explicitName;
360
+ }
361
+ else if (result.exportedName) {
362
+ result.pikkuFuncId = result.exportedName;
363
+ }
364
+ else {
365
+ result.pikkuFuncId = `__temp_${randomUUID()}`;
583
366
  }
584
367
  return result;
585
368
  }
@@ -595,32 +378,44 @@ export function populateNameByPriority(result) {
595
378
  else if (result.exportedName) {
596
379
  result.name = result.exportedName;
597
380
  }
598
- // Priority 3: If we have a property name, use that
599
- // else if (result.propertyName) {
600
- // result.name = result.propertyName
601
- // }
602
381
  // Fallback: Use the deterministic name, but we could shorten it in the future
603
382
  else {
604
- // For now, just use the full pikkuFuncName
605
- result.name = result.pikkuFuncName;
383
+ // For now, just use the full pikkuFuncId
384
+ result.name = result.pikkuFuncId;
606
385
  }
607
386
  }
608
387
  /**
609
388
  * Helper function to check if a variable declaration is a named export
610
389
  */
611
- export function isNamedExport(declaration) {
390
+ export function isNamedExport(declaration, checker) {
612
391
  let parent = declaration.parent;
613
392
  if (!parent)
614
393
  return false;
615
- // Check if it's part of a variable declaration list
616
394
  if (ts.isVariableDeclarationList(parent)) {
617
395
  parent = parent.parent;
618
396
  if (!parent)
619
397
  return false;
620
- // Check if it's in an export declaration
621
398
  if (ts.isVariableStatement(parent)) {
622
- return (parent.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ??
623
- false);
399
+ if (parent.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
400
+ return true;
401
+ }
402
+ }
403
+ }
404
+ if (checker && ts.isIdentifier(declaration.name)) {
405
+ const sourceFile = declaration.getSourceFile();
406
+ const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile);
407
+ if (sourceFileSymbol) {
408
+ const exports = checker.getExportsOfModule(sourceFileSymbol);
409
+ const declSymbol = checker.getSymbolAtLocation(declaration.name);
410
+ if (declSymbol) {
411
+ return exports.some((exp) => {
412
+ let resolved = exp;
413
+ if (resolved.flags & ts.SymbolFlags.Alias) {
414
+ resolved = checker.getAliasedSymbol(resolved) ?? resolved;
415
+ }
416
+ return resolved === declSymbol;
417
+ });
418
+ }
624
419
  }
625
420
  }
626
421
  return false;
@@ -1,6 +1,7 @@
1
1
  import * as ts from 'typescript';
2
- import { FunctionServicesMeta } from '@pikku/core';
2
+ import { FunctionServicesMeta, FunctionWiresMeta } from '@pikku/core';
3
3
  /**
4
4
  * Extract services from a function's first parameter destructuring pattern
5
5
  */
6
6
  export declare function extractServicesFromFunction(handlerNode: ts.FunctionExpression | ts.ArrowFunction): FunctionServicesMeta;
7
+ export declare function extractUsedWires(handlerNode: ts.FunctionExpression | ts.ArrowFunction, paramIndex: number): FunctionWiresMeta;
@@ -21,9 +21,33 @@ export function extractServicesFromFunction(handlerNode) {
21
21
  }
22
22
  }
23
23
  }
24
- else if (ts.isIdentifier(firstParam.name)) {
24
+ else if (ts.isIdentifier(firstParam.name) &&
25
+ !firstParam.name.text.startsWith('_')) {
25
26
  services.optimized = false;
26
27
  }
27
28
  }
28
29
  return services;
29
30
  }
31
+ export function extractUsedWires(handlerNode, paramIndex) {
32
+ const param = handlerNode.parameters[paramIndex];
33
+ if (param && ts.isObjectBindingPattern(param.name)) {
34
+ const wires = [];
35
+ for (const elem of param.name.elements) {
36
+ const propertyName = elem.propertyName && ts.isIdentifier(elem.propertyName)
37
+ ? elem.propertyName.text
38
+ : ts.isIdentifier(elem.name)
39
+ ? elem.name.text
40
+ : undefined;
41
+ if (propertyName) {
42
+ wires.push(propertyName);
43
+ }
44
+ }
45
+ return { optimized: true, wires };
46
+ }
47
+ if (param &&
48
+ ts.isIdentifier(param.name) &&
49
+ !param.name.text.startsWith('_')) {
50
+ return { optimized: false, wires: [] };
51
+ }
52
+ return { optimized: true, wires: [] };
53
+ }