@pikku/inspector 0.11.2 → 0.12.1

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 (211) hide show
  1. package/CHANGELOG.md +36 -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 +81 -61
  6. package/dist/add/add-cli.d.ts +1 -1
  7. package/dist/add/add-cli.js +42 -19
  8. package/dist/add/add-file-extends-core-type.d.ts +1 -1
  9. package/dist/add/add-file-with-config.d.ts +1 -1
  10. package/dist/add/add-file-with-factory.d.ts +1 -1
  11. package/dist/add/add-file-with-factory.js +2 -0
  12. package/dist/add/add-functions.d.ts +1 -1
  13. package/dist/add/add-functions.js +256 -82
  14. package/dist/add/add-http-route.d.ts +20 -10
  15. package/dist/add/add-http-route.js +156 -66
  16. package/dist/add/add-http-routes.d.ts +5 -0
  17. package/dist/add/add-http-routes.js +160 -0
  18. package/dist/add/add-keyed-wiring.d.ts +12 -0
  19. package/dist/add/add-keyed-wiring.js +97 -0
  20. package/dist/add/add-mcp-prompt.d.ts +1 -1
  21. package/dist/add/add-mcp-prompt.js +14 -9
  22. package/dist/add/add-mcp-resource.d.ts +1 -1
  23. package/dist/add/add-mcp-resource.js +14 -9
  24. package/dist/add/add-middleware.d.ts +1 -4
  25. package/dist/add/add-middleware.js +364 -79
  26. package/dist/add/add-permission.d.ts +1 -1
  27. package/dist/add/add-permission.js +152 -40
  28. package/dist/add/add-queue-worker.d.ts +1 -1
  29. package/dist/add/add-queue-worker.js +18 -12
  30. package/dist/add/add-rpc-invocations.d.ts +3 -3
  31. package/dist/add/add-rpc-invocations.js +24 -10
  32. package/dist/add/add-schedule.d.ts +1 -1
  33. package/dist/add/add-schedule.js +11 -5
  34. package/dist/add/add-secret.d.ts +3 -0
  35. package/dist/add/add-secret.js +82 -0
  36. package/dist/add/add-trigger.d.ts +2 -0
  37. package/dist/add/add-trigger.js +87 -0
  38. package/dist/add/add-variable.d.ts +1 -0
  39. package/dist/add/add-variable.js +8 -0
  40. package/dist/add/add-wire-addon.d.ts +7 -0
  41. package/dist/add/add-wire-addon.js +70 -0
  42. package/dist/add/add-workflow-graph.d.ts +3 -2
  43. package/dist/add/add-workflow-graph.js +143 -406
  44. package/dist/add/add-workflow.d.ts +1 -1
  45. package/dist/add/add-workflow.js +6 -4
  46. package/dist/error-codes.d.ts +15 -1
  47. package/dist/error-codes.js +20 -1
  48. package/dist/index.d.ts +9 -8
  49. package/dist/index.js +5 -4
  50. package/dist/inspector.d.ts +2 -2
  51. package/dist/inspector.js +95 -15
  52. package/dist/schema-generator.d.ts +1 -0
  53. package/dist/schema-generator.js +1 -0
  54. package/dist/types-map.js +10 -1
  55. package/dist/types.d.ts +180 -50
  56. package/dist/utils/compute-required-schemas.d.ts +4 -0
  57. package/dist/utils/compute-required-schemas.js +40 -0
  58. package/dist/utils/contract-hashes.d.ts +52 -0
  59. package/dist/utils/contract-hashes.js +269 -0
  60. package/dist/utils/custom-types-generator.d.ts +9 -0
  61. package/dist/utils/custom-types-generator.js +71 -0
  62. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  63. package/dist/utils/detect-schema-vendor.js +76 -0
  64. package/dist/utils/does-type-extend-core-type.d.ts +1 -1
  65. package/dist/utils/ensure-function-metadata.d.ts +6 -3
  66. package/dist/utils/ensure-function-metadata.js +220 -6
  67. package/dist/utils/extract-function-name.d.ts +5 -16
  68. package/dist/utils/extract-function-name.js +86 -291
  69. package/dist/utils/extract-services.d.ts +2 -1
  70. package/dist/utils/extract-services.js +25 -1
  71. package/dist/utils/filter-inspector-state.d.ts +1 -1
  72. package/dist/utils/filter-inspector-state.js +107 -23
  73. package/dist/utils/filter-utils.d.ts +2 -2
  74. package/dist/utils/get-files-and-methods.d.ts +1 -1
  75. package/dist/utils/get-property-value.d.ts +6 -1
  76. package/dist/utils/get-property-value.js +28 -3
  77. package/dist/utils/hash.d.ts +2 -0
  78. package/dist/utils/hash.js +23 -0
  79. package/dist/utils/middleware.d.ts +9 -32
  80. package/dist/utils/middleware.js +80 -66
  81. package/dist/utils/permissions.d.ts +4 -4
  82. package/dist/utils/permissions.js +10 -10
  83. package/dist/utils/post-process.d.ts +11 -11
  84. package/dist/utils/post-process.js +247 -24
  85. package/dist/utils/resolve-addon-package.d.ts +16 -0
  86. package/dist/utils/resolve-addon-package.js +34 -0
  87. package/dist/utils/resolve-function-types.d.ts +6 -0
  88. package/dist/utils/resolve-function-types.js +29 -0
  89. package/dist/utils/resolve-identifier.d.ts +10 -0
  90. package/dist/utils/resolve-identifier.js +36 -0
  91. package/dist/utils/resolve-versions.d.ts +2 -0
  92. package/dist/utils/resolve-versions.js +78 -0
  93. package/dist/utils/schema-generator.d.ts +9 -0
  94. package/dist/utils/schema-generator.js +209 -0
  95. package/dist/utils/serialize-inspector-state.d.ts +70 -23
  96. package/dist/utils/serialize-inspector-state.js +98 -22
  97. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  98. package/dist/utils/serialize-mcp-json.js +99 -0
  99. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  100. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  101. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  102. package/dist/utils/serialize-openapi-json.js +151 -0
  103. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  104. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  105. package/dist/utils/validate-auth-sessionless.d.ts +3 -0
  106. package/dist/utils/validate-auth-sessionless.js +14 -0
  107. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  108. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  109. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  110. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  111. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  112. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  113. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  114. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  115. package/dist/utils/workflow/graph/index.d.ts +2 -0
  116. package/dist/utils/workflow/graph/index.js +2 -0
  117. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  118. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  119. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  120. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  121. package/dist/visit.d.ts +1 -1
  122. package/dist/visit.js +13 -6
  123. package/package.json +14 -4
  124. package/src/add/add-ai-agent.ts +468 -0
  125. package/src/add/add-channel.ts +103 -79
  126. package/src/add/add-cli.ts +68 -24
  127. package/src/add/add-file-extends-core-type.ts +1 -1
  128. package/src/add/add-file-with-config.ts +1 -1
  129. package/src/add/add-file-with-factory.ts +3 -1
  130. package/src/add/add-functions.ts +349 -103
  131. package/src/add/add-http-route.ts +263 -89
  132. package/src/add/add-http-routes.ts +229 -0
  133. package/src/add/add-keyed-wiring.ts +151 -0
  134. package/src/add/add-mcp-prompt.ts +27 -16
  135. package/src/add/add-mcp-resource.ts +28 -16
  136. package/src/add/add-middleware.ts +482 -80
  137. package/src/add/add-permission.ts +199 -40
  138. package/src/add/add-queue-worker.ts +25 -20
  139. package/src/add/add-rpc-invocations.ts +28 -11
  140. package/src/add/add-schedule.ts +17 -12
  141. package/src/add/add-secret.ts +140 -0
  142. package/src/add/add-trigger.ts +154 -0
  143. package/src/add/add-variable.ts +9 -0
  144. package/src/add/add-wire-addon.ts +80 -0
  145. package/src/add/add-workflow-graph.ts +180 -522
  146. package/src/add/add-workflow.ts +7 -6
  147. package/src/error-codes.ts +25 -1
  148. package/src/index.ts +23 -13
  149. package/src/inspector.ts +139 -19
  150. package/src/schema-generator.ts +1 -0
  151. package/src/types-map.ts +12 -1
  152. package/src/types.ts +199 -69
  153. package/src/utils/compute-required-schemas.ts +48 -0
  154. package/src/utils/contract-hashes.test.ts +553 -0
  155. package/src/utils/contract-hashes.ts +386 -0
  156. package/src/utils/custom-types-generator.ts +88 -0
  157. package/src/utils/detect-schema-vendor.ts +90 -0
  158. package/src/utils/does-type-extend-core-type.ts +1 -1
  159. package/src/utils/ensure-function-metadata.ts +325 -8
  160. package/src/utils/extract-function-name.ts +101 -351
  161. package/src/utils/extract-services.ts +35 -2
  162. package/src/utils/filter-inspector-state.test.ts +37 -25
  163. package/src/utils/filter-inspector-state.ts +146 -32
  164. package/src/utils/filter-utils.test.ts +1 -1
  165. package/src/utils/filter-utils.ts +2 -2
  166. package/src/utils/get-files-and-methods.ts +1 -1
  167. package/src/utils/get-property-value.ts +42 -4
  168. package/src/utils/hash.ts +26 -0
  169. package/src/utils/middleware.test.ts +204 -0
  170. package/src/utils/middleware.ts +131 -69
  171. package/src/utils/permissions.test.ts +35 -12
  172. package/src/utils/permissions.ts +12 -12
  173. package/src/utils/post-process.ts +306 -44
  174. package/src/utils/resolve-addon-package.ts +49 -0
  175. package/src/utils/resolve-function-types.ts +42 -0
  176. package/src/utils/resolve-identifier.ts +46 -0
  177. package/src/utils/resolve-versions.test.ts +249 -0
  178. package/src/utils/resolve-versions.ts +105 -0
  179. package/src/utils/schema-generator.ts +329 -0
  180. package/src/utils/serialize-inspector-state.ts +184 -43
  181. package/src/utils/serialize-mcp-json.ts +145 -0
  182. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  183. package/src/utils/serialize-openapi-json.ts +277 -0
  184. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  185. package/src/utils/test-data/inspector-state.json +69 -66
  186. package/src/utils/validate-auth-sessionless.ts +29 -0
  187. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  188. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +26 -6
  189. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  190. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  191. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  192. package/src/utils/workflow/graph/index.ts +5 -0
  193. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  194. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  195. package/src/visit.ts +19 -7
  196. package/tsconfig.tsbuildinfo +1 -1
  197. package/dist/add/add-forge-credential.d.ts +0 -8
  198. package/dist/add/add-forge-credential.js +0 -77
  199. package/dist/add/add-forge-node.d.ts +0 -7
  200. package/dist/add/add-forge-node.js +0 -77
  201. package/dist/add/add-mcp-tool.d.ts +0 -2
  202. package/dist/add/add-mcp-tool.js +0 -81
  203. package/dist/utils/extract-service-metadata.d.ts +0 -19
  204. package/dist/utils/extract-service-metadata.js +0 -244
  205. package/dist/utils/write-service-metadata.d.ts +0 -13
  206. package/dist/utils/write-service-metadata.js +0 -37
  207. package/src/add/add-forge-credential.ts +0 -119
  208. package/src/add/add-forge-node.ts +0 -132
  209. package/src/add/add-mcp-tool.ts +0 -141
  210. package/src/utils/extract-service-metadata.ts +0 -353
  211. package/src/utils/write-service-metadata.ts +0 -51
@@ -2,10 +2,12 @@ import * as ts from 'typescript';
2
2
  import { ErrorCode } from '../error-codes.js';
3
3
  import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
4
4
  import { pathToRegexp } from 'path-to-regexp';
5
- import { extractFunctionName } from '../utils/extract-function-name.js';
5
+ import { extractFunctionName, makeContextBasedId, } from '../utils/extract-function-name.js';
6
6
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
7
- import { resolveMiddleware } from '../utils/middleware.js';
7
+ import { resolveMiddleware, resolveChannelMiddleware, } from '../utils/middleware.js';
8
8
  import { extractWireNames } from '../utils/post-process.js';
9
+ import { resolveIdentifier } from '../utils/resolve-identifier.js';
10
+ import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js';
9
11
  /**
10
12
  * Safely get the "initializer" expression of a property-like AST node:
11
13
  * - for `foo: expr`, returns `expr`
@@ -44,25 +46,25 @@ function getHandlerNameFromExpression(expr, checker, rootDir) {
44
46
  ts.isArrowFunction(decl.initializer) ||
45
47
  ts.isFunctionExpression(decl.initializer)) {
46
48
  // Extract function name from the declaration's initializer
47
- const { pikkuFuncName } = extractFunctionName(decl.initializer, checker, rootDir);
48
- return pikkuFuncName;
49
+ const { pikkuFuncId } = extractFunctionName(decl.initializer, checker, rootDir);
50
+ return pikkuFuncId;
49
51
  }
50
52
  }
51
53
  // For function declarations, use directly
52
54
  else if (ts.isFunctionDeclaration(decl)) {
53
- const { pikkuFuncName } = extractFunctionName(decl, checker, rootDir);
54
- return pikkuFuncName;
55
+ const { pikkuFuncId } = extractFunctionName(decl, checker, rootDir);
56
+ return pikkuFuncId;
55
57
  }
56
58
  }
57
59
  }
58
60
  // Fallback: try to extract directly from the identifier
59
- const { pikkuFuncName } = extractFunctionName(expr, checker, rootDir);
60
- return pikkuFuncName;
61
+ const { pikkuFuncId } = extractFunctionName(expr, checker, rootDir);
62
+ return pikkuFuncId;
61
63
  }
62
64
  // Handle call expressions
63
65
  if (ts.isCallExpression(expr)) {
64
- const { pikkuFuncName } = extractFunctionName(expr, checker, rootDir);
65
- return pikkuFuncName;
66
+ const { pikkuFuncId } = extractFunctionName(expr, checker, rootDir);
67
+ return pikkuFuncId;
66
68
  }
67
69
  // Handle object literals with 'func' property
68
70
  if (ts.isObjectLiteralExpression(expr)) {
@@ -85,8 +87,19 @@ export function addMessagesRoutes(logger, obj, state, checker) {
85
87
  if (!onMsgRouteProp || !ts.isObjectLiteralExpression(onMsgRouteProp))
86
88
  return result;
87
89
  for (const chanElem of onMsgRouteProp.properties) {
88
- const chanInit = getInitializerOf(chanElem);
89
- if (!chanInit || !ts.isObjectLiteralExpression(chanInit))
90
+ let chanInit = getInitializerOf(chanElem);
91
+ if (!chanInit)
92
+ continue;
93
+ // If the value is an identifier, resolve it (handles defineChannelRoutes)
94
+ if (ts.isIdentifier(chanInit)) {
95
+ const resolved = resolveIdentifier(chanInit, checker, [
96
+ 'defineChannelRoutes',
97
+ ]);
98
+ if (resolved && ts.isObjectLiteralExpression(resolved)) {
99
+ chanInit = resolved;
100
+ }
101
+ }
102
+ if (!ts.isObjectLiteralExpression(chanInit))
90
103
  continue;
91
104
  const channelKey = chanElem.name.getText();
92
105
  result[channelKey] = {};
@@ -136,8 +149,8 @@ export function addMessagesRoutes(logger, obj, state, checker) {
136
149
  if (ts.isArrowFunction(importDecl.initializer) ||
137
150
  ts.isFunctionExpression(importDecl.initializer) ||
138
151
  ts.isCallExpression(importDecl.initializer)) {
139
- const { pikkuFuncName } = extractFunctionName(importDecl.initializer, checker, state.rootDir);
140
- const handlerName = pikkuFuncName;
152
+ const { pikkuFuncId } = extractFunctionName(importDecl.initializer, checker, state.rootDir);
153
+ const handlerName = pikkuFuncId;
141
154
  // Look up in the registry
142
155
  const fnMeta = state.functions.meta[handlerName];
143
156
  if (fnMeta) {
@@ -149,7 +162,7 @@ export function addMessagesRoutes(logger, obj, state, checker) {
149
162
  ? resolveMiddleware(state, init, routeTags, checker)
150
163
  : undefined;
151
164
  result[channelKey][routeKey] = {
152
- pikkuFuncName: handlerName,
165
+ pikkuFuncId: handlerName,
153
166
  middleware: routeMiddleware,
154
167
  };
155
168
  continue;
@@ -158,8 +171,8 @@ export function addMessagesRoutes(logger, obj, state, checker) {
158
171
  }
159
172
  else if (ts.isFunctionDeclaration(importDecl)) {
160
173
  // Extract from the function declaration
161
- const { pikkuFuncName } = extractFunctionName(importDecl, checker, state.rootDir);
162
- const handlerName = pikkuFuncName;
174
+ const { pikkuFuncId } = extractFunctionName(importDecl, checker, state.rootDir);
175
+ const handlerName = pikkuFuncId;
163
176
  // Look up in the registry
164
177
  const fnMeta = state.functions.meta[handlerName];
165
178
  if (fnMeta) {
@@ -171,7 +184,7 @@ export function addMessagesRoutes(logger, obj, state, checker) {
171
184
  ? resolveMiddleware(state, init, routeTags, checker)
172
185
  : undefined;
173
186
  result[channelKey][routeKey] = {
174
- pikkuFuncName: handlerName,
187
+ pikkuFuncId: handlerName,
175
188
  middleware: routeMiddleware,
176
189
  };
177
190
  continue;
@@ -192,8 +205,8 @@ export function addMessagesRoutes(logger, obj, state, checker) {
192
205
  const exportDecl = exportDecls[0];
193
206
  if (ts.isVariableDeclaration(exportDecl) &&
194
207
  exportDecl.initializer) {
195
- const { pikkuFuncName } = extractFunctionName(exportDecl.initializer, checker, state.rootDir);
196
- const handlerName = pikkuFuncName;
208
+ const { pikkuFuncId } = extractFunctionName(exportDecl.initializer, checker, state.rootDir);
209
+ const handlerName = pikkuFuncId;
197
210
  const fnMeta = state.functions.meta[handlerName];
198
211
  if (fnMeta) {
199
212
  // Resolve middleware for this route
@@ -204,15 +217,15 @@ export function addMessagesRoutes(logger, obj, state, checker) {
204
217
  ? resolveMiddleware(state, init, routeTags, checker)
205
218
  : undefined;
206
219
  result[channelKey][routeKey] = {
207
- pikkuFuncName: handlerName,
220
+ pikkuFuncId: handlerName,
208
221
  middleware: routeMiddleware,
209
222
  };
210
223
  continue;
211
224
  }
212
225
  }
213
226
  else if (ts.isFunctionDeclaration(exportDecl)) {
214
- const { pikkuFuncName } = extractFunctionName(exportDecl, checker, state.rootDir);
215
- const handlerName = pikkuFuncName;
227
+ const { pikkuFuncId } = extractFunctionName(exportDecl, checker, state.rootDir);
228
+ const handlerName = pikkuFuncId;
216
229
  const fnMeta = state.functions.meta[handlerName];
217
230
  if (fnMeta) {
218
231
  // Resolve middleware for this route
@@ -223,7 +236,7 @@ export function addMessagesRoutes(logger, obj, state, checker) {
223
236
  ? resolveMiddleware(state, init, routeTags, checker)
224
237
  : undefined;
225
238
  result[channelKey][routeKey] = {
226
- pikkuFuncName: handlerName,
239
+ pikkuFuncId: handlerName,
227
240
  middleware: routeMiddleware,
228
241
  };
229
242
  continue;
@@ -249,7 +262,7 @@ export function addMessagesRoutes(logger, obj, state, checker) {
249
262
  continue;
250
263
  }
251
264
  result[channelKey][routeKey] = {
252
- pikkuFuncName: possibleMatch,
265
+ pikkuFuncId: possibleMatch,
253
266
  };
254
267
  continue;
255
268
  }
@@ -274,8 +287,8 @@ export function addMessagesRoutes(logger, obj, state, checker) {
274
287
  // If we found the actual function, extract its name
275
288
  if (actualFunction) {
276
289
  // Extract the function name directly from the actual function
277
- const { pikkuFuncName } = extractFunctionName(actualFunction, checker, state.rootDir);
278
- const handlerName = pikkuFuncName;
290
+ const { pikkuFuncId } = extractFunctionName(actualFunction, checker, state.rootDir);
291
+ const handlerName = pikkuFuncId;
279
292
  // Now use this handlerName to look up in the registry
280
293
  const fnMeta = state.functions.meta[handlerName];
281
294
  if (fnMeta) {
@@ -287,7 +300,7 @@ export function addMessagesRoutes(logger, obj, state, checker) {
287
300
  ? resolveMiddleware(state, init, routeTags, checker)
288
301
  : undefined;
289
302
  result[channelKey][routeKey] = {
290
- pikkuFuncName: handlerName,
303
+ pikkuFuncId: handlerName,
291
304
  middleware: routeMiddleware,
292
305
  };
293
306
  continue; // Skip the normal processing below
@@ -316,7 +329,7 @@ export function addMessagesRoutes(logger, obj, state, checker) {
316
329
  ? resolveMiddleware(state, init, routeTags, checker)
317
330
  : undefined;
318
331
  result[channelKey][routeKey] = {
319
- pikkuFuncName: handlerName,
332
+ pikkuFuncId: handlerName,
320
333
  middleware: routeMiddleware,
321
334
  };
322
335
  }
@@ -349,7 +362,9 @@ export const addChannel = (logger, node, checker, state, options) => {
349
362
  .keys.filter((k) => k.type === 'param')
350
363
  .map((k) => k.name)
351
364
  : [];
352
- const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'Channel', name, logger);
365
+ const { disabled, tags, summary, description, errors } = getCommonWireMetaData(obj, 'Channel', name, logger);
366
+ if (disabled)
367
+ return;
353
368
  const query = getPropertyValue(obj, 'query');
354
369
  const connect = getPropertyAssignmentInitializer(obj, 'onConnect', true, checker);
355
370
  const disconnect = getPropertyAssignmentInitializer(obj, 'onDisconnect', true, checker);
@@ -357,42 +372,62 @@ export const addChannel = (logger, node, checker, state, options) => {
357
372
  let message = null;
358
373
  const onMsgProp = getPropertyAssignmentInitializer(obj, 'onMessage', true, checker);
359
374
  if (onMsgProp) {
360
- const { pikkuFuncName } = extractFunctionName(onMsgProp, checker, state.rootDir);
361
- const fnMeta = state.functions.meta[pikkuFuncName];
375
+ const extracted = extractFunctionName(onMsgProp, checker, state.rootDir);
376
+ const msgFuncId = extracted.pikkuFuncId.startsWith('__temp_')
377
+ ? makeContextBasedId('channel', name, 'message')
378
+ : extracted.pikkuFuncId;
379
+ const fnMeta = state.functions.meta[msgFuncId];
362
380
  if (!fnMeta) {
363
- logger.critical(ErrorCode.FUNCTION_METADATA_NOT_FOUND, `No function metadata found for onMessage handler '${pikkuFuncName}'`);
381
+ logger.critical(ErrorCode.FUNCTION_METADATA_NOT_FOUND, `No function metadata found for onMessage handler '${msgFuncId}'`);
364
382
  return;
365
383
  }
366
384
  message = {
367
- pikkuFuncName,
385
+ pikkuFuncId: msgFuncId,
368
386
  };
369
387
  }
370
388
  // nested message-routes
371
389
  const messageWirings = addMessagesRoutes(logger, obj, state, checker);
372
390
  // --- resolve middleware ---
373
391
  const middleware = resolveMiddleware(state, obj, tags, checker);
392
+ const channelMiddleware = resolveChannelMiddleware(state, obj, tags, checker);
374
393
  // --- track used functions/middleware for service aggregation ---
375
394
  // Track connect/disconnect/message handlers
395
+ let connectFuncId;
376
396
  if (connect) {
377
- const connectFuncName = extractFunctionName(connect, checker, state.rootDir).pikkuFuncName;
378
- state.serviceAggregation.usedFunctions.add(connectFuncName);
397
+ const extracted = extractFunctionName(connect, checker, state.rootDir);
398
+ connectFuncId = extracted.pikkuFuncId.startsWith('__temp_')
399
+ ? makeContextBasedId('channel', name, 'connect')
400
+ : extracted.pikkuFuncId;
401
+ state.serviceAggregation.usedFunctions.add(connectFuncId);
379
402
  }
403
+ let disconnectFuncId;
380
404
  if (disconnect) {
381
- const disconnectFuncName = extractFunctionName(disconnect, checker, state.rootDir).pikkuFuncName;
382
- state.serviceAggregation.usedFunctions.add(disconnectFuncName);
405
+ const extracted = extractFunctionName(disconnect, checker, state.rootDir);
406
+ disconnectFuncId = extracted.pikkuFuncId.startsWith('__temp_')
407
+ ? makeContextBasedId('channel', name, 'disconnect')
408
+ : extracted.pikkuFuncId;
409
+ state.serviceAggregation.usedFunctions.add(disconnectFuncId);
383
410
  }
384
411
  if (message) {
385
- state.serviceAggregation.usedFunctions.add(message.pikkuFuncName);
412
+ state.serviceAggregation.usedFunctions.add(message.pikkuFuncId);
386
413
  }
387
- // Track message wiring handlers
388
414
  for (const channelHandlers of Object.values(messageWirings)) {
389
415
  for (const handler of Object.values(channelHandlers)) {
390
- state.serviceAggregation.usedFunctions.add(handler.pikkuFuncName);
416
+ state.serviceAggregation.usedFunctions.add(handler.pikkuFuncId);
391
417
  }
392
418
  }
393
- // Track middleware
394
419
  extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
395
- // record into state
420
+ // --- validate auth/sessionless ---
421
+ const handlersToValidate = [
422
+ connectFuncId,
423
+ disconnectFuncId,
424
+ message?.pikkuFuncId,
425
+ ].filter(Boolean);
426
+ for (const funcId of handlersToValidate) {
427
+ if (!validateAuthSessionless(logger, obj, state, funcId, `Channel '${name}'`)) {
428
+ return;
429
+ }
430
+ }
396
431
  state.channels.files.add(node.getSourceFile().fileName);
397
432
  state.channels.meta[name] = {
398
433
  name,
@@ -400,24 +435,8 @@ export const addChannel = (logger, node, checker, state, options) => {
400
435
  input: null,
401
436
  params: params.length ? params : undefined,
402
437
  query: query?.length ? query : undefined,
403
- // inputTypes: getInputTypes(
404
- // state.channels.metaInputTypes,
405
- // 'get',
406
- // null, // TODO
407
- // query,
408
- // params
409
- // ),
410
- connect: connect
411
- ? {
412
- pikkuFuncName: extractFunctionName(connect, checker, state.rootDir)
413
- .pikkuFuncName,
414
- }
415
- : null,
416
- disconnect: disconnect
417
- ? {
418
- pikkuFuncName: extractFunctionName(disconnect, checker, state.rootDir).pikkuFuncName,
419
- }
420
- : null,
438
+ connect: connectFuncId ? { pikkuFuncId: connectFuncId } : null,
439
+ disconnect: disconnectFuncId ? { pikkuFuncId: disconnectFuncId } : null,
421
440
  message,
422
441
  messageWirings,
423
442
  summary,
@@ -425,5 +444,6 @@ export const addChannel = (logger, node, checker, state, options) => {
425
444
  errors,
426
445
  tags: tags ?? undefined,
427
446
  middleware,
447
+ channelMiddleware,
428
448
  };
429
449
  };
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Adds CLI command metadata to the inspector state
4
4
  */
@@ -3,6 +3,8 @@ import { extractFunctionName } from '../utils/extract-function-name.js';
3
3
  import { resolveMiddleware } from '../utils/middleware.js';
4
4
  import { extractWireNames } from '../utils/post-process.js';
5
5
  import { getPropertyValue } from '../utils/get-property-value.js';
6
+ import { resolveIdentifier } from '../utils/resolve-identifier.js';
7
+ import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js';
6
8
  // Track if we've warned about missing Config type to avoid duplicate warnings
7
9
  const configTypeWarningShown = new Set();
8
10
  /**
@@ -68,6 +70,9 @@ function processCLIConfig(logger, node, sourceFile, typeChecker, inspectorState,
68
70
  if (!programName) {
69
71
  return null;
70
72
  }
73
+ if (getPropertyValue(node, 'disabled') === true) {
74
+ return null;
75
+ }
71
76
  // Second pass: process other properties with program tags available
72
77
  for (const prop of node.properties) {
73
78
  if (!ts.isPropertyAssignment(prop))
@@ -92,7 +97,7 @@ function processCLIConfig(logger, node, sourceFile, typeChecker, inspectorState,
92
97
  break;
93
98
  case 'render':
94
99
  // Extract the actual renderer function name
95
- programMeta.defaultRenderName = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncName;
100
+ programMeta.defaultRenderName = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncId;
96
101
  break;
97
102
  }
98
103
  }
@@ -105,6 +110,20 @@ function processCommands(logger, node, sourceFile, typeChecker, programName, ins
105
110
  const commands = {};
106
111
  let defaultCommandName = null;
107
112
  for (const prop of node.properties) {
113
+ // Handle spread assignments: { ...externalCommands }
114
+ if (ts.isSpreadAssignment(prop)) {
115
+ let spreadTarget = prop.expression;
116
+ if (ts.isIdentifier(prop.expression)) {
117
+ spreadTarget = resolveIdentifier(prop.expression, typeChecker, [
118
+ 'defineCLICommands',
119
+ ]);
120
+ }
121
+ if (spreadTarget && ts.isObjectLiteralExpression(spreadTarget)) {
122
+ const spreadCommands = processCommands(logger, spreadTarget, sourceFile, typeChecker, programName, inspectorState, options, programTags);
123
+ Object.assign(commands, spreadCommands);
124
+ }
125
+ continue;
126
+ }
108
127
  if (!ts.isPropertyAssignment(prop))
109
128
  continue;
110
129
  const commandName = getPropertyName(prop);
@@ -138,7 +157,7 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
138
157
  ts.isArrowFunction(node) ||
139
158
  ts.isFunctionExpression(node)) {
140
159
  return {
141
- pikkuFuncName: extractFunctionName(node, typeChecker, inspectorState.rootDir).pikkuFuncName,
160
+ pikkuFuncId: extractFunctionName(node, typeChecker, inspectorState.rootDir).pikkuFuncId,
142
161
  positionals: [],
143
162
  options: {},
144
163
  };
@@ -160,12 +179,12 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
160
179
  return null;
161
180
  }
162
181
  const meta = {
163
- pikkuFuncName: '',
182
+ pikkuFuncId: '',
164
183
  positionals: [],
165
184
  options: {},
166
185
  };
167
- // First pass: extract pikkuFuncName and tags so we can use them when processing options/middleware
168
- let pikkuFuncName;
186
+ // First pass: extract pikkuFuncId and tags so we can use them when processing options/middleware
187
+ let pikkuFuncId;
169
188
  let optionsNode;
170
189
  let tags;
171
190
  for (const prop of node.properties) {
@@ -175,8 +194,8 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
175
194
  continue;
176
195
  const propName = prop.name.text;
177
196
  if (propName === 'func') {
178
- pikkuFuncName = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncName;
179
- meta.pikkuFuncName = pikkuFuncName;
197
+ pikkuFuncId = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncId;
198
+ meta.pikkuFuncId = pikkuFuncId;
180
199
  }
181
200
  else if (propName === 'options' &&
182
201
  ts.isObjectLiteralExpression(prop.initializer)) {
@@ -220,12 +239,12 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
220
239
  // Already handled in first pass
221
240
  break;
222
241
  case 'render':
223
- meta.renderName = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncName;
242
+ meta.renderName = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncId;
224
243
  break;
225
244
  case 'options':
226
- // Process with pikkuFuncName from first pass
245
+ // Process with pikkuFuncId from first pass
227
246
  if (optionsNode) {
228
- meta.options = processOptions(logger, optionsNode, typeChecker, inspectorState, options, pikkuFuncName);
247
+ meta.options = processOptions(logger, optionsNode, typeChecker, inspectorState, options, pikkuFuncId);
229
248
  }
230
249
  break;
231
250
  case 'subcommands':
@@ -252,16 +271,20 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
252
271
  break;
253
272
  }
254
273
  }
274
+ // --- validate auth/sessionless ---
275
+ if (meta.pikkuFuncId &&
276
+ !validateAuthSessionless(logger, node, inspectorState, meta.pikkuFuncId, `CLI command '${name}'`)) {
277
+ return null;
278
+ }
255
279
  // --- track used functions/middleware for service aggregation ---
256
- inspectorState.serviceAggregation.usedFunctions.add(meta.pikkuFuncName);
280
+ inspectorState.serviceAggregation.usedFunctions.add(meta.pikkuFuncId);
257
281
  extractWireNames(meta.middleware).forEach((name) => inspectorState.serviceAggregation.usedMiddleware.add(name));
258
- // Note: subcommands are tracked recursively when they're processed
259
282
  return meta;
260
283
  }
261
284
  /**
262
285
  * Processes CLI options and extracts enum values from function input types
263
286
  */
264
- function processOptions(logger, node, typeChecker, inspectorState, inspectorOptions, pikkuFuncName) {
287
+ function processOptions(logger, node, typeChecker, inspectorState, inspectorOptions, pikkuFuncId) {
265
288
  const options = {};
266
289
  for (const prop of node.properties) {
267
290
  if (!ts.isPropertyAssignment(prop))
@@ -318,10 +341,10 @@ function processOptions(logger, node, typeChecker, inspectorState, inspectorOpti
318
341
  }
319
342
  }
320
343
  // Extract enum values from the function input type if available
321
- // Get the input type if we have a pikkuFuncName
344
+ // Get the input type if we have a pikkuFuncId
322
345
  let inputTypes;
323
- if (pikkuFuncName) {
324
- inputTypes = inspectorState.typesLookup.get(pikkuFuncName);
346
+ if (pikkuFuncId) {
347
+ inputTypes = inspectorState.typesLookup.get(pikkuFuncId);
325
348
  }
326
349
  let derivedChoices = null;
327
350
  if (inputTypes && inputTypes.length > 0) {
@@ -527,7 +550,7 @@ export const addCLIRenderers = (logger, node, typeChecker, inspectorState, optio
527
550
  if (args.length === 0)
528
551
  return;
529
552
  // Extract renderer name
530
- const { pikkuFuncName, exportedName } = extractFunctionName(node, typeChecker, inspectorState.rootDir);
553
+ const { pikkuFuncId, exportedName } = extractFunctionName(node, typeChecker, inspectorState.rootDir);
531
554
  // Get the source file path
532
555
  const sourceFile = node.getSourceFile();
533
556
  const filePath = sourceFile.fileName;
@@ -553,8 +576,8 @@ export const addCLIRenderers = (logger, node, typeChecker, inspectorState, optio
553
576
  }
554
577
  }
555
578
  // Store renderer metadata
556
- inspectorState.cli.meta.renderers[pikkuFuncName] = {
557
- name: pikkuFuncName,
579
+ inspectorState.cli.meta.renderers[pikkuFuncId] = {
580
+ name: pikkuFuncId,
558
581
  exportedName: exportedName ?? undefined,
559
582
  services,
560
583
  filePath,
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType, InspectorState } from '../types.js';
2
+ import type { PathToNameAndType, InspectorState } from '../types.js';
3
3
  export declare const addFileExtendsCoreType: (node: ts.Node, checker: ts.TypeChecker, methods: PathToNameAndType, expectedTypeName: string, state?: InspectorState) => void;
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType } from '../types.js';
2
+ import type { PathToNameAndType } from '../types.js';
3
3
  export declare const addFileWithConfig: (node: ts.Node, checker: ts.TypeChecker, configs: PathToNameAndType) => void;
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType, InspectorState } from '../types.js';
2
+ import type { PathToNameAndType, InspectorState } from '../types.js';
3
3
  export declare const addFileWithFactory: (node: ts.Node, checker: ts.TypeChecker, methods: PathToNameAndType | undefined, expectedTypeName: string, state?: InspectorState) => void;
@@ -3,7 +3,9 @@ import { extractServicesFromFunction } from '../utils/extract-services.js';
3
3
  // Mapping of wrapper function names to their corresponding types
4
4
  const wrapperFunctionMap = {
5
5
  pikkuConfig: 'CreateConfig',
6
+ pikkuAddonConfig: 'CreateConfig',
6
7
  pikkuServices: 'CreateSingletonServices',
8
+ pikkuAddonServices: 'CreateSingletonServices',
7
9
  pikkuWireServices: 'CreateWireServices',
8
10
  };
9
11
  export const addFileWithFactory = (node, checker, methods = new Map(), expectedTypeName, state) => {
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Inspect pikkuFunc calls, extract input/output and first-arg destructuring,
4
4
  * then push into state.functions.meta.