@pikku/inspector 0.12.74 → 0.12.81

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 (219) hide show
  1. package/CHANGELOG.md +347 -0
  2. package/dist/add/add-analytics.d.ts +9 -0
  3. package/dist/add/add-analytics.js +178 -0
  4. package/dist/add/add-auth.js +0 -54
  5. package/dist/add/add-feature-flag.d.ts +2 -0
  6. package/dist/add/add-feature-flag.js +162 -0
  7. package/dist/add/add-wire-addon.js +1 -0
  8. package/dist/add/add-wire-remote-addon.js +1 -0
  9. package/dist/index.d.ts +3 -1
  10. package/dist/index.js +3 -1
  11. package/dist/inspector.js +4 -0
  12. package/dist/surface/collect-surface.d.ts +58 -0
  13. package/dist/surface/collect-surface.js +613 -0
  14. package/dist/types.d.ts +20 -11
  15. package/dist/utils/filter-inspector-state.js +23 -13
  16. package/dist/utils/post-process.js +15 -0
  17. package/dist/utils/read-module-specifiers.d.ts +8 -0
  18. package/dist/utils/read-module-specifiers.js +23 -0
  19. package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
  20. package/dist/utils/read-tsconfig-out-dir.js +12 -0
  21. package/dist/utils/resolve-deploy-target.d.ts +7 -0
  22. package/dist/utils/resolve-deploy-target.js +15 -7
  23. package/dist/utils/serialize-inspector-state.d.ts +5 -0
  24. package/dist/utils/serialize-inspector-state.js +8 -0
  25. package/dist/visit.js +4 -0
  26. package/package.json +16 -6
  27. package/run-tests.sh +21 -6
  28. package/src/add/add-addon-bans.ts +0 -85
  29. package/src/add/add-agent-scorer.test.ts +0 -197
  30. package/src/add/add-agent-scorer.ts +0 -83
  31. package/src/add/add-agent.test.ts +0 -87
  32. package/src/add/add-agent.ts +0 -638
  33. package/src/add/add-approval-description.ts +0 -76
  34. package/src/add/add-auth.test.ts +0 -791
  35. package/src/add/add-auth.ts +0 -424
  36. package/src/add/add-channel.ts +0 -778
  37. package/src/add/add-cli-renderers.test.ts +0 -74
  38. package/src/add/add-cli.ts +0 -1006
  39. package/src/add/add-credential.ts +0 -186
  40. package/src/add/add-feature.test.ts +0 -306
  41. package/src/add/add-feature.ts +0 -253
  42. package/src/add/add-file-extends-core-type.ts +0 -66
  43. package/src/add/add-file-with-config.ts +0 -45
  44. package/src/add/add-file-with-factory.test.ts +0 -134
  45. package/src/add/add-file-with-factory.ts +0 -288
  46. package/src/add/add-functions.test.ts +0 -743
  47. package/src/add/add-functions.ts +0 -1548
  48. package/src/add/add-gateway.ts +0 -143
  49. package/src/add/add-http-route-ref.test.ts +0 -149
  50. package/src/add/add-http-route.ts +0 -530
  51. package/src/add/add-http-routes.ts +0 -471
  52. package/src/add/add-keyed-wiring.ts +0 -180
  53. package/src/add/add-mcp-prompt.ts +0 -157
  54. package/src/add/add-mcp-resource.ts +0 -175
  55. package/src/add/add-middleware.test.ts +0 -196
  56. package/src/add/add-middleware.ts +0 -879
  57. package/src/add/add-permission-auth.test.ts +0 -59
  58. package/src/add/add-permission.ts +0 -311
  59. package/src/add/add-personas.test.ts +0 -406
  60. package/src/add/add-personas.ts +0 -287
  61. package/src/add/add-queue-worker.ts +0 -117
  62. package/src/add/add-rpc-invocations.test.ts +0 -287
  63. package/src/add/add-rpc-invocations.ts +0 -221
  64. package/src/add/add-scenario-step.test.ts +0 -509
  65. package/src/add/add-schedule.ts +0 -120
  66. package/src/add/add-scope.test.ts +0 -419
  67. package/src/add/add-scope.ts +0 -221
  68. package/src/add/add-secret.ts +0 -10
  69. package/src/add/add-subject-scenario-steps.test.ts +0 -161
  70. package/src/add/add-system-role.test.ts +0 -297
  71. package/src/add/add-system-role.ts +0 -162
  72. package/src/add/add-trigger.ts +0 -165
  73. package/src/add/add-variable.ts +0 -10
  74. package/src/add/add-wire-addon.test.ts +0 -185
  75. package/src/add/add-wire-addon.ts +0 -146
  76. package/src/add/add-wire-remote-addon.ts +0 -80
  77. package/src/add/add-workflow-edge-cases.test.ts +0 -201
  78. package/src/add/add-workflow-fanout.test.ts +0 -166
  79. package/src/add/add-workflow-graph-foreach.test.ts +0 -174
  80. package/src/add/add-workflow-graph-input.test.ts +0 -94
  81. package/src/add/add-workflow-graph-notes.test.ts +0 -74
  82. package/src/add/add-workflow-graph.ts +0 -644
  83. package/src/add/add-workflow-on-error.test.ts +0 -118
  84. package/src/add/add-workflow-return-switch.test.ts +0 -121
  85. package/src/add/add-workflow-silent-drops.test.ts +0 -261
  86. package/src/add/add-workflow-sleep-expression.test.ts +0 -96
  87. package/src/add/add-workflow.test.ts +0 -155
  88. package/src/add/add-workflow.ts +0 -482
  89. package/src/add/addon-bans.test.ts +0 -121
  90. package/src/add/addon-contracts.test.ts +0 -221
  91. package/src/add/allow-escape-hatches.test.ts +0 -144
  92. package/src/add/audit-meta.test.ts +0 -75
  93. package/src/add/classified-sink-check.test.ts +0 -347
  94. package/src/add/config-value-metadata.test.ts +0 -263
  95. package/src/add/dynamic-import-check.test.ts +0 -119
  96. package/src/add/expect-eventually-scenario-only.test.ts +0 -108
  97. package/src/add/function-body-span.test.ts +0 -96
  98. package/src/add/inline-wiring-function.test.ts +0 -104
  99. package/src/add/pii-check.test.ts +0 -258
  100. package/src/add/rpc-type-cast.test.ts +0 -123
  101. package/src/add/scenario-instrumentation.test.ts +0 -89
  102. package/src/add/scenario-instrumentation.ts +0 -42
  103. package/src/add/scenario-marker.test.ts +0 -89
  104. package/src/add/scenario-not-extractable.test.ts +0 -159
  105. package/src/add/scenario-services.test.ts +0 -103
  106. package/src/add/scenario-skip.test.ts +0 -83
  107. package/src/add/validate-workflow-graph-addons.test.ts +0 -102
  108. package/src/add/wire-name-literal.test.ts +0 -117
  109. package/src/error-codes-emitted.test.ts +0 -76
  110. package/src/error-codes.ts +0 -145
  111. package/src/index.ts +0 -66
  112. package/src/inspector.ts +0 -575
  113. package/src/schema-generator.ts +0 -1
  114. package/src/source-file-cache.ts +0 -73
  115. package/src/tsconfig-paths.test.ts +0 -90
  116. package/src/types-map.ts +0 -141
  117. package/src/types.ts +0 -775
  118. package/src/utils/annotate-http-route-auth.test.ts +0 -161
  119. package/src/utils/annotate-http-route-auth.ts +0 -53
  120. package/src/utils/check-classified-sinks.ts +0 -215
  121. package/src/utils/check-pii-output.ts +0 -120
  122. package/src/utils/compute-diagnostics.test.ts +0 -106
  123. package/src/utils/compute-required-schemas.ts +0 -48
  124. package/src/utils/contract-hashes.test.ts +0 -553
  125. package/src/utils/contract-hashes.ts +0 -387
  126. package/src/utils/custom-types-generator.test.ts +0 -124
  127. package/src/utils/custom-types-generator.ts +0 -150
  128. package/src/utils/detect-schema-vendor.test.ts +0 -123
  129. package/src/utils/detect-schema-vendor.ts +0 -120
  130. package/src/utils/does-type-extend-core-type.ts +0 -53
  131. package/src/utils/ensure-function-metadata.ts +0 -385
  132. package/src/utils/extract-function-name.ts +0 -612
  133. package/src/utils/extract-function-node.ts +0 -70
  134. package/src/utils/extract-node-value.test.ts +0 -117
  135. package/src/utils/extract-node-value.ts +0 -172
  136. package/src/utils/extract-secret-usage.test.ts +0 -81
  137. package/src/utils/extract-secret-usage.ts +0 -73
  138. package/src/utils/extract-services.ts +0 -70
  139. package/src/utils/extract-surface-usage.test.ts +0 -147
  140. package/src/utils/extract-surface-usage.ts +0 -121
  141. package/src/utils/filter-inspector-state.test.ts +0 -2207
  142. package/src/utils/filter-inspector-state.ts +0 -1302
  143. package/src/utils/filter-utils.test.ts +0 -843
  144. package/src/utils/filter-utils.ts +0 -161
  145. package/src/utils/find-root-dir.ts +0 -68
  146. package/src/utils/find-zod-transform.test.ts +0 -70
  147. package/src/utils/find-zod-transform.ts +0 -103
  148. package/src/utils/get-exported-variable-name.ts +0 -48
  149. package/src/utils/get-files-and-methods.ts +0 -157
  150. package/src/utils/get-property-value.test.ts +0 -141
  151. package/src/utils/get-property-value.ts +0 -331
  152. package/src/utils/hash.ts +0 -26
  153. package/src/utils/literal-properties.ts +0 -97
  154. package/src/utils/load-addon-functions-meta.test.ts +0 -157
  155. package/src/utils/load-addon-functions-meta.ts +0 -508
  156. package/src/utils/load-addon-scopes.test.ts +0 -138
  157. package/src/utils/middleware.test.ts +0 -204
  158. package/src/utils/middleware.ts +0 -303
  159. package/src/utils/nested-project-filter.test.ts +0 -53
  160. package/src/utils/nested-project-filter.ts +0 -28
  161. package/src/utils/permissions-shorthand.test.ts +0 -79
  162. package/src/utils/permissions.test.ts +0 -125
  163. package/src/utils/permissions.ts +0 -129
  164. package/src/utils/post-process.test.ts +0 -1094
  165. package/src/utils/post-process.ts +0 -1604
  166. package/src/utils/resolve-addon-package.ts +0 -106
  167. package/src/utils/resolve-core-type.test.ts +0 -142
  168. package/src/utils/resolve-core-type.ts +0 -35
  169. package/src/utils/resolve-deploy-target.test.ts +0 -135
  170. package/src/utils/resolve-deploy-target.ts +0 -65
  171. package/src/utils/resolve-function-meta.ts +0 -25
  172. package/src/utils/resolve-function-types.ts +0 -42
  173. package/src/utils/resolve-identifier.ts +0 -46
  174. package/src/utils/resolve-ref-contract.ts +0 -71
  175. package/src/utils/resolve-versions.test.ts +0 -499
  176. package/src/utils/resolve-versions.ts +0 -228
  177. package/src/utils/schema-cache.test.ts +0 -124
  178. package/src/utils/schema-generator.test.ts +0 -272
  179. package/src/utils/schema-generator.ts +0 -780
  180. package/src/utils/secret-alias-services.test.ts +0 -146
  181. package/src/utils/secret-alias-services.ts +0 -58
  182. package/src/utils/serialize-inspector-state.ts +0 -742
  183. package/src/utils/serialize-mcp-json.ts +0 -150
  184. package/src/utils/serialize-middleware-groups-meta.ts +0 -36
  185. package/src/utils/serialize-openapi-json.ts +0 -277
  186. package/src/utils/serialize-permissions-groups-meta.ts +0 -7
  187. package/src/utils/single-declaration.ts +0 -57
  188. package/src/utils/type-utils.ts +0 -145
  189. package/src/utils/validate-auth-sessionless.ts +0 -30
  190. package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
  191. package/src/utils/validate-exposed-functions-gated.ts +0 -103
  192. package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
  193. package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
  194. package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
  195. package/src/utils/workflow/derive-workflow-plan.ts +0 -117
  196. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
  197. package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
  198. package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
  199. package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
  200. package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
  201. package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
  202. package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
  203. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
  204. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
  205. package/src/utils/workflow/dsl/index.ts +0 -11
  206. package/src/utils/workflow/dsl/patterns.ts +0 -440
  207. package/src/utils/workflow/dsl/validation.ts +0 -203
  208. package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
  209. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
  210. package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
  211. package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
  212. package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
  213. package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
  214. package/src/utils/workflow/graph/index.ts +0 -28
  215. package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
  216. package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
  217. package/src/visit.ts +0 -181
  218. package/tsconfig.json +0 -19
  219. package/tsconfig.tsbuildinfo +0 -1
@@ -1,778 +0,0 @@
1
- import * as ts from 'typescript'
2
- import { ErrorCode } from '../error-codes.js'
3
- import {
4
- getPropertyValue,
5
- getCommonWireMetaData,
6
- } from '../utils/get-property-value.js'
7
- import { pathToRegexp } from 'path-to-regexp'
8
- import {
9
- extractFunctionName,
10
- makeContextBasedId,
11
- } from '../utils/extract-function-name.js'
12
- import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
13
- import type { ChannelMessageMeta, ChannelMeta } from '@pikku/core/channel'
14
- import type { InspectorState, AddWiring } from '../types.js'
15
- import {
16
- resolveMiddleware,
17
- resolveChannelMiddleware,
18
- } from '../utils/middleware.js'
19
- import { extractWireNames } from '../utils/post-process.js'
20
- import { resolveIdentifier } from '../utils/resolve-identifier.js'
21
- import { resolveFunctionMeta } from '../utils/resolve-function-meta.js'
22
- import { resolveAddonName } from '../utils/resolve-addon-package.js'
23
- import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js'
24
- import { getExportedVariableName } from '../utils/get-exported-variable-name.js'
25
- import { resolveRefContract } from '../utils/resolve-ref-contract.js'
26
-
27
- /**
28
- * Safely get the "initializer" expression of a property-like AST node:
29
- * - for `foo: expr`, returns `expr`
30
- * - for `{ foo }` shorthand, returns the identifier `foo`
31
- * - otherwise, returns undefined
32
- */
33
- function getInitializerOf(
34
- elem: ts.ObjectLiteralElementLike
35
- ): ts.Expression | undefined {
36
- if (ts.isPropertyAssignment(elem)) {
37
- return elem.initializer
38
- }
39
- if (ts.isShorthandPropertyAssignment(elem)) {
40
- return elem.name
41
- }
42
- return undefined
43
- }
44
-
45
- function getObjectPropertyName(
46
- name: ts.PropertyName | undefined
47
- ): string | null {
48
- if (!name) return null
49
- if (ts.isIdentifier(name)) return name.text
50
- if (ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text
51
- if (ts.isComputedPropertyName(name)) return null
52
- return name.getText()
53
- }
54
-
55
- /**
56
- * Resolve a handler expression (Identifier, CallExpression, or { func })
57
- * into its underlying function name.
58
- */
59
- function getHandlerNameFromExpression(
60
- expr: ts.Expression,
61
- checker: ts.TypeChecker,
62
- rootDir: string
63
- ): string | null {
64
- // Handle direct identifier case (which includes shorthand properties)
65
- if (ts.isIdentifier(expr)) {
66
- const sym = checker.getSymbolAtLocation(expr)
67
- if (sym) {
68
- let resolvedSym = sym
69
- if (resolvedSym.flags & ts.SymbolFlags.Alias) {
70
- resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym
71
- }
72
-
73
- // Try to get declarations
74
- const decls = resolvedSym.declarations ?? []
75
- if (decls.length > 0) {
76
- const decl = decls[0]!
77
-
78
- // For variable declarations, look at the initializer
79
- if (ts.isVariableDeclaration(decl) && decl.initializer) {
80
- if (
81
- ts.isCallExpression(decl.initializer) ||
82
- ts.isArrowFunction(decl.initializer) ||
83
- ts.isFunctionExpression(decl.initializer)
84
- ) {
85
- // Extract function name from the declaration's initializer
86
- const { pikkuFuncId } = extractFunctionName(
87
- decl.initializer,
88
- checker,
89
- rootDir
90
- )
91
- return pikkuFuncId
92
- }
93
- }
94
- // For function declarations, use directly
95
- else if (ts.isFunctionDeclaration(decl)) {
96
- const { pikkuFuncId } = extractFunctionName(decl, checker, rootDir)
97
- return pikkuFuncId
98
- }
99
- }
100
- }
101
-
102
- // Fallback: try to extract directly from the identifier
103
- const { pikkuFuncId } = extractFunctionName(expr, checker, rootDir)
104
- return pikkuFuncId
105
- }
106
-
107
- // Handle call expressions
108
- if (ts.isCallExpression(expr)) {
109
- // Handle ref('name') calls
110
- if (ts.isIdentifier(expr.expression) && expr.expression.text === 'ref') {
111
- const [firstArg] = expr.arguments
112
- if (firstArg && ts.isStringLiteral(firstArg)) {
113
- return firstArg.text
114
- }
115
- }
116
- const { pikkuFuncId } = extractFunctionName(expr, checker, rootDir)
117
- return pikkuFuncId
118
- }
119
-
120
- // Handle object literals with 'func' property
121
- if (ts.isObjectLiteralExpression(expr)) {
122
- const fnProp = getPropertyAssignmentInitializer(expr, 'func', true, checker)
123
- if (fnProp) {
124
- return getHandlerNameFromExpression(fnProp, checker, rootDir)
125
- }
126
- }
127
-
128
- return null
129
- }
130
-
131
- function extractExportedChannelRoutes(
132
- logger: {
133
- error: (msg: string) => void
134
- critical: (code: ErrorCode, msg: string) => void
135
- },
136
- routes: ts.ObjectLiteralExpression,
137
- state: InspectorState,
138
- checker: ts.TypeChecker
139
- ): Record<string, ChannelMessageMeta> {
140
- const wrapper = ts.factory.createObjectLiteralExpression([
141
- ts.factory.createPropertyAssignment(
142
- 'onMessageWiring',
143
- ts.factory.createObjectLiteralExpression([
144
- ts.factory.createPropertyAssignment('contract', routes),
145
- ])
146
- ),
147
- ])
148
-
149
- return addMessagesRoutes(logger, wrapper, state, checker).contract ?? {}
150
- }
151
-
152
- /**
153
- * Build out the nested message-routes by looking up each handler
154
- * in state.functions.meta instead of re-inferring it here.
155
- */
156
- export function addMessagesRoutes(
157
- logger: {
158
- error: (msg: string) => void
159
- critical: (code: ErrorCode, msg: string) => void
160
- },
161
- obj: ts.ObjectLiteralExpression,
162
- state: InspectorState,
163
- checker: ts.TypeChecker
164
- ): ChannelMeta['messageWirings'] {
165
- const result: ChannelMeta['messageWirings'] = {}
166
- const onMsgRouteProp = getPropertyAssignmentInitializer(
167
- obj,
168
- 'onMessageWiring',
169
- true,
170
- checker
171
- )
172
- if (!onMsgRouteProp) return result
173
-
174
- if (!onMsgRouteProp || !ts.isObjectLiteralExpression(onMsgRouteProp))
175
- return result
176
-
177
- for (const chanElem of onMsgRouteProp.properties) {
178
- let chanInit = getInitializerOf(chanElem)
179
- if (!chanInit) continue
180
-
181
- // If the value is an identifier, resolve it (handles defineChannelRoutes)
182
- if (ts.isIdentifier(chanInit)) {
183
- const resolved = resolveIdentifier(chanInit, checker, [
184
- 'defineChannelRoutes',
185
- ])
186
- if (resolved && ts.isObjectLiteralExpression(resolved)) {
187
- chanInit = resolved
188
- }
189
- }
190
-
191
- const refContract = resolveRefContract(
192
- chanInit,
193
- 'refChannel',
194
- state.exportedContracts.addonChannel
195
- )
196
- if (refContract) {
197
- const refChannelKey = getObjectPropertyName(chanElem.name)
198
- if (!refChannelKey) continue
199
- result[refChannelKey] = {
200
- ...refContract.contract,
201
- }
202
- continue
203
- }
204
-
205
- if (!ts.isObjectLiteralExpression(chanInit)) continue
206
-
207
- const channelKey = getObjectPropertyName(chanElem.name)
208
- if (!channelKey) continue
209
- result[channelKey] = {}
210
-
211
- for (const routeElem of chanInit.properties) {
212
- const init = getInitializerOf(routeElem)
213
- if (!init) continue
214
-
215
- // Get the route key, stripping quotes if it's a string literal
216
- const routeName = routeElem.name
217
- if (!routeName) continue
218
-
219
- const routeKey = getObjectPropertyName(routeName)
220
- if (!routeKey) continue
221
-
222
- // For shorthand properties, we need to resolve the identifier to its declaration
223
- if (ts.isShorthandPropertyAssignment(routeElem)) {
224
- // Get the symbol for the shorthand property
225
- const shorthandSym =
226
- checker.getShorthandAssignmentValueSymbol(routeElem)
227
-
228
- if (
229
- shorthandSym &&
230
- shorthandSym.declarations &&
231
- shorthandSym.declarations.length > 0
232
- ) {
233
- const shorthandDecl = shorthandSym.declarations[0]
234
- if (!shorthandDecl) {
235
- throw new Error(
236
- `No declaration found for shorthand property '${routeKey}'`
237
- )
238
- }
239
-
240
- // Handle import specifiers
241
- if (ts.isImportSpecifier(shorthandDecl)) {
242
- // Get the imported symbol
243
- const importedSymbol = checker.getSymbolAtLocation(
244
- shorthandDecl.name
245
- )
246
- if (importedSymbol) {
247
- // Try to resolve the alias to get the original symbol
248
- let resolvedSymbol = importedSymbol
249
- if (resolvedSymbol.flags & ts.SymbolFlags.Alias) {
250
- resolvedSymbol =
251
- checker.getAliasedSymbol(resolvedSymbol) ?? resolvedSymbol
252
- }
253
-
254
- // Try to get the declarations of the resolved symbol
255
- const importDecls = resolvedSymbol.declarations ?? []
256
- if (importDecls.length > 0) {
257
- const importDecl = importDecls[0]!
258
-
259
- // Handle different kinds of declarations
260
- if (
261
- ts.isVariableDeclaration(importDecl) &&
262
- importDecl.initializer
263
- ) {
264
- // Extract from the initializer if it's a function
265
- if (
266
- ts.isArrowFunction(importDecl.initializer) ||
267
- ts.isFunctionExpression(importDecl.initializer) ||
268
- ts.isCallExpression(importDecl.initializer)
269
- ) {
270
- const { pikkuFuncId } = extractFunctionName(
271
- importDecl.initializer,
272
- checker,
273
- state.rootDir
274
- )
275
- const handlerName = pikkuFuncId
276
-
277
- // Look up in the registry
278
- const fnMeta = state.functions.meta[handlerName]
279
- if (fnMeta) {
280
- // Resolve middleware for this route
281
- const routeTags = ts.isObjectLiteralExpression(init)
282
- ? getCommonWireMetaData(
283
- init,
284
- 'Channel message',
285
- routeKey,
286
- logger,
287
- checker
288
- ).tags
289
- : undefined
290
- const routeMiddleware = ts.isObjectLiteralExpression(init)
291
- ? resolveMiddleware(state, init, routeTags, checker)
292
- : undefined
293
-
294
- result[channelKey]![routeKey] = {
295
- pikkuFuncId: handlerName,
296
- middleware: routeMiddleware,
297
- }
298
- continue
299
- }
300
- }
301
- } else if (ts.isFunctionDeclaration(importDecl)) {
302
- // Extract from the function declaration
303
- const { pikkuFuncId } = extractFunctionName(
304
- importDecl,
305
- checker,
306
- state.rootDir
307
- )
308
- const handlerName = pikkuFuncId
309
-
310
- // Look up in the registry
311
- const fnMeta = state.functions.meta[handlerName]
312
- if (fnMeta) {
313
- // Resolve middleware for this route
314
- const routeTags = ts.isObjectLiteralExpression(init)
315
- ? getCommonWireMetaData(
316
- init,
317
- 'Channel message',
318
- routeKey,
319
- logger,
320
- checker
321
- ).tags
322
- : undefined
323
- const routeMiddleware = ts.isObjectLiteralExpression(init)
324
- ? resolveMiddleware(state, init, routeTags, checker)
325
- : undefined
326
-
327
- result[channelKey]![routeKey] = {
328
- pikkuFuncId: handlerName,
329
- middleware: routeMiddleware,
330
- }
331
- continue
332
- }
333
- } else if (ts.isExportSpecifier(importDecl)) {
334
- // For re-exports, we need to follow another level of indirection
335
- const exportSymbol = checker.getSymbolAtLocation(
336
- importDecl.name
337
- )
338
- if (exportSymbol) {
339
- let resolvedExportSymbol = exportSymbol
340
- if (resolvedExportSymbol.flags & ts.SymbolFlags.Alias) {
341
- resolvedExportSymbol =
342
- checker.getAliasedSymbol(resolvedExportSymbol) ??
343
- resolvedExportSymbol
344
- }
345
-
346
- const exportDecls = resolvedExportSymbol.declarations ?? []
347
- if (exportDecls.length > 0) {
348
- const exportDecl = exportDecls[0]!
349
-
350
- if (
351
- ts.isVariableDeclaration(exportDecl) &&
352
- exportDecl.initializer
353
- ) {
354
- const { pikkuFuncId } = extractFunctionName(
355
- exportDecl.initializer,
356
- checker,
357
- state.rootDir
358
- )
359
- const handlerName = pikkuFuncId
360
-
361
- const fnMeta = state.functions.meta[handlerName]
362
- if (fnMeta) {
363
- // Resolve middleware for this route
364
- const routeTags = ts.isObjectLiteralExpression(init)
365
- ? getCommonWireMetaData(
366
- init,
367
- 'Channel message',
368
- routeKey,
369
- logger,
370
- checker
371
- ).tags
372
- : undefined
373
- const routeMiddleware = ts.isObjectLiteralExpression(
374
- init
375
- )
376
- ? resolveMiddleware(state, init, routeTags, checker)
377
- : undefined
378
-
379
- result[channelKey]![routeKey] = {
380
- pikkuFuncId: handlerName,
381
- middleware: routeMiddleware,
382
- }
383
- continue
384
- }
385
- } else if (ts.isFunctionDeclaration(exportDecl)) {
386
- const { pikkuFuncId } = extractFunctionName(
387
- exportDecl,
388
- checker,
389
- state.rootDir
390
- )
391
- const handlerName = pikkuFuncId
392
-
393
- const fnMeta = state.functions.meta[handlerName]
394
- if (fnMeta) {
395
- // Resolve middleware for this route
396
- const routeTags = ts.isObjectLiteralExpression(init)
397
- ? getCommonWireMetaData(
398
- init,
399
- 'Channel message',
400
- routeKey,
401
- logger,
402
- checker
403
- ).tags
404
- : undefined
405
- const routeMiddleware = ts.isObjectLiteralExpression(
406
- init
407
- )
408
- ? resolveMiddleware(state, init, routeTags, checker)
409
- : undefined
410
-
411
- result[channelKey]![routeKey] = {
412
- pikkuFuncId: handlerName,
413
- middleware: routeMiddleware,
414
- }
415
- continue
416
- }
417
- }
418
- }
419
- }
420
- }
421
- }
422
- }
423
-
424
- // As a fallback, try to look up by name
425
- const funcName = shorthandDecl.name.getText()
426
-
427
- // Look for any function in the registry that ends with this name
428
- const possibleMatch = Object.keys(state.functions.meta).find(
429
- (key) => {
430
- const parts = key.split('_')
431
- const filename = parts[parts.length - 3] || ''
432
- return filename.endsWith(funcName)
433
- }
434
- )
435
-
436
- if (possibleMatch) {
437
- const fnMeta = state.functions.meta[possibleMatch]
438
- if (!fnMeta) {
439
- logger.critical(
440
- ErrorCode.FUNCTION_METADATA_NOT_FOUND,
441
- `No function metadata found for handler '${possibleMatch}'`
442
- )
443
- continue
444
- }
445
- result[channelKey]![routeKey] = {
446
- pikkuFuncId: possibleMatch,
447
- }
448
- continue
449
- }
450
- } else {
451
- // Handle other declaration types (variable, function, etc.)
452
- let actualFunction: ts.Node | undefined = undefined
453
-
454
- if (ts.isVariableDeclaration(shorthandDecl)) {
455
- // Check if it has an initializer
456
- if (shorthandDecl.initializer) {
457
- // If it's a function expression or similar, use that
458
- if (
459
- ts.isArrowFunction(shorthandDecl.initializer) ||
460
- ts.isFunctionExpression(shorthandDecl.initializer) ||
461
- ts.isCallExpression(shorthandDecl.initializer)
462
- ) {
463
- actualFunction = shorthandDecl.initializer
464
- }
465
- }
466
- } else if (ts.isFunctionDeclaration(shorthandDecl)) {
467
- actualFunction = shorthandDecl
468
- }
469
-
470
- // If we found the actual function, extract its name
471
- if (actualFunction) {
472
- // Extract the function name directly from the actual function
473
- const { pikkuFuncId } = extractFunctionName(
474
- actualFunction,
475
- checker,
476
- state.rootDir
477
- )
478
- const handlerName = pikkuFuncId
479
-
480
- // Now use this handlerName to look up in the registry
481
- const fnMeta = state.functions.meta[handlerName]
482
-
483
- if (fnMeta) {
484
- // Resolve middleware for this route
485
- const routeTags = ts.isObjectLiteralExpression(init)
486
- ? getCommonWireMetaData(
487
- init,
488
- 'Channel message',
489
- routeKey,
490
- logger,
491
- checker
492
- ).tags
493
- : undefined
494
- const routeMiddleware = ts.isObjectLiteralExpression(init)
495
- ? resolveMiddleware(state, init, routeTags, checker)
496
- : undefined
497
-
498
- result[channelKey]![routeKey] = {
499
- pikkuFuncId: handlerName,
500
- middleware: routeMiddleware,
501
- }
502
- continue // Skip the normal processing below
503
- }
504
- }
505
- }
506
- }
507
- }
508
-
509
- // Normal processing for non-shorthand properties
510
- const handlerName = getHandlerNameFromExpression(
511
- init,
512
- checker,
513
- state.rootDir
514
- )
515
- if (!handlerName) {
516
- logger.error(
517
- `Could not resolve handler for message route '${routeKey}'`
518
- )
519
- continue
520
- }
521
-
522
- const fnMeta = resolveFunctionMeta(state, handlerName)
523
- if (!fnMeta) {
524
- logger.critical(
525
- ErrorCode.FUNCTION_METADATA_NOT_FOUND,
526
- `No function metadata found for channel handler '${handlerName}' on route '${routeKey}'. If this is an inline function, it must be exported for the inspector to discover it.`
527
- )
528
- continue
529
- }
530
-
531
- // Resolve middleware and permissions for this route
532
- // Check if the route config is an object literal with middleware/permissions
533
- const routeTags = ts.isObjectLiteralExpression(init)
534
- ? getCommonWireMetaData(
535
- init,
536
- 'Channel message',
537
- routeKey,
538
- logger,
539
- checker
540
- ).tags
541
- : undefined
542
- const routeMiddleware = ts.isObjectLiteralExpression(init)
543
- ? resolveMiddleware(state, init, routeTags, checker)
544
- : undefined
545
-
546
- // Resolve package name for addon functions (e.g. 'swaggerPetstore:addPet')
547
- const colonIdx = handlerName.indexOf(':')
548
- const addonNs =
549
- colonIdx !== -1 ? handlerName.substring(0, colonIdx) : null
550
- const packageName = addonNs
551
- ? state.rpc.wireAddonDeclarations.get(addonNs)?.package
552
- : undefined
553
-
554
- result[channelKey]![routeKey] = {
555
- pikkuFuncId: handlerName,
556
- packageName,
557
- middleware: routeMiddleware,
558
- }
559
- }
560
- }
561
-
562
- return result
563
- }
564
-
565
- /**
566
- * Inspect addChannel calls, look up all handlers in state.functions.meta,
567
- * and emit one entry into state.channels.meta.
568
- */
569
- export const addChannel: AddWiring = (
570
- logger,
571
- node,
572
- checker,
573
- state,
574
- options
575
- ) => {
576
- if (!ts.isCallExpression(node)) return
577
- if (
578
- ts.isIdentifier(node.expression) &&
579
- node.expression.text === 'defineChannelRoutes'
580
- ) {
581
- const exportName = getExportedVariableName(node, options.sourceFile)
582
- const [firstArg] = node.arguments
583
- if (exportName && firstArg && ts.isObjectLiteralExpression(firstArg)) {
584
- state.exportedContracts.channel[exportName] =
585
- extractExportedChannelRoutes(logger, firstArg, state, checker)
586
- }
587
- return
588
- }
589
- const { expression, arguments: args } = node
590
- if (!ts.isIdentifier(expression) || expression.text !== 'wireChannel') return
591
- const first = args[0]
592
- if (!first || !ts.isObjectLiteralExpression(first)) return
593
-
594
- const obj = first
595
- const name = getPropertyValue(obj, 'name') as string | undefined
596
- const route = (getPropertyValue(obj, 'route') as string) ?? ''
597
-
598
- if (!name) {
599
- logger.critical(ErrorCode.MISSING_CHANNEL_NAME, 'Channel name is required')
600
- return
601
- }
602
-
603
- // path parameters
604
- const params = route
605
- ? pathToRegexp(route)
606
- .keys.filter((k) => k.type === 'param')
607
- .map((k) => k.name)
608
- : []
609
-
610
- const { disabled, tags, summary, description, errors } =
611
- getCommonWireMetaData(obj, 'Channel', name, logger, checker)
612
-
613
- if (disabled) return
614
-
615
- const query = getPropertyValue(obj, 'query') as string[] | []
616
- const binary = getPropertyValue(obj, 'binary') as boolean | undefined
617
-
618
- const connect = getPropertyAssignmentInitializer(
619
- obj,
620
- 'onConnect',
621
- true,
622
- checker
623
- )
624
- const disconnect = getPropertyAssignmentInitializer(
625
- obj,
626
- 'onDisconnect',
627
- true,
628
- checker
629
- )
630
-
631
- // default onMessage handler
632
- let message: ChannelMessageMeta | null = null
633
- const onMsgProp = getPropertyAssignmentInitializer(
634
- obj,
635
- 'onMessage',
636
- true,
637
- checker
638
- )
639
-
640
- if (onMsgProp) {
641
- const extracted = extractFunctionName(onMsgProp, checker, state.rootDir)
642
- const msgFuncId = extracted.pikkuFuncId.startsWith('__temp_')
643
- ? makeContextBasedId('channel', name, 'message')
644
- : extracted.pikkuFuncId
645
- const fnMeta = state.functions.meta[msgFuncId]
646
- if (!fnMeta) {
647
- logger.critical(
648
- ErrorCode.FUNCTION_METADATA_NOT_FOUND,
649
- `No function metadata found for onMessage handler '${msgFuncId}'`
650
- )
651
- return
652
- }
653
- const msgPackageName = ts.isIdentifier(onMsgProp)
654
- ? resolveAddonName(onMsgProp, checker, state.rpc.wireAddonDeclarations)
655
- : null
656
- message = {
657
- pikkuFuncId: msgFuncId,
658
- ...(msgPackageName && { packageName: msgPackageName }),
659
- }
660
- }
661
-
662
- // nested message-routes
663
- const messageWirings = addMessagesRoutes(logger, obj, state, checker)
664
-
665
- // --- resolve middleware ---
666
- const middleware = resolveMiddleware(state, obj, tags, checker)
667
- const channelMiddleware = resolveChannelMiddleware(state, obj, tags, checker)
668
-
669
- // --- track used functions/middleware for service aggregation ---
670
- // Track connect/disconnect/message handlers
671
- let connectFuncId: string | undefined
672
- let connectPackageName: string | null = null
673
- if (connect) {
674
- const extracted = extractFunctionName(connect, checker, state.rootDir)
675
- connectFuncId = extracted.pikkuFuncId.startsWith('__temp_')
676
- ? makeContextBasedId('channel', name, 'connect')
677
- : extracted.pikkuFuncId
678
- connectPackageName = ts.isIdentifier(connect)
679
- ? resolveAddonName(connect, checker, state.rpc.wireAddonDeclarations)
680
- : null
681
- state.serviceAggregation.usedFunctions.add(connectFuncId)
682
- }
683
-
684
- let disconnectFuncId: string | undefined
685
- let disconnectPackageName: string | null = null
686
- if (disconnect) {
687
- const extracted = extractFunctionName(
688
- disconnect as any,
689
- checker,
690
- state.rootDir
691
- )
692
- disconnectFuncId = extracted.pikkuFuncId.startsWith('__temp_')
693
- ? makeContextBasedId('channel', name, 'disconnect')
694
- : extracted.pikkuFuncId
695
- disconnectPackageName = ts.isIdentifier(disconnect)
696
- ? resolveAddonName(disconnect, checker, state.rpc.wireAddonDeclarations)
697
- : null
698
- state.serviceAggregation.usedFunctions.add(disconnectFuncId)
699
- }
700
-
701
- // Synthesize function meta for connect/disconnect handlers that are defined
702
- // with pikkuChannelConnectionFunc/pikkuChannelDisconnectionFunc (not pikkuFunc/
703
- // pikkuSessionlessFunc), so the runtime has correct sessionless info without
704
- // needing to inject it at runtime.
705
- {
706
- const routeAuth = getPropertyValue(obj, 'auth')
707
- const sessionless = routeAuth === false ? true : undefined
708
- for (const funcId of [connectFuncId, disconnectFuncId]) {
709
- if (funcId && !state.functions.meta[funcId]) {
710
- state.functions.meta[funcId] = {
711
- pikkuFuncId: funcId,
712
- sessionless,
713
- inputSchemaName: null,
714
- outputSchemaName: null,
715
- }
716
- }
717
- }
718
- }
719
-
720
- if (message) {
721
- state.serviceAggregation.usedFunctions.add(message.pikkuFuncId)
722
- }
723
-
724
- for (const channelHandlers of Object.values(
725
- messageWirings as Record<string, Record<string, ChannelMessageMeta>>
726
- )) {
727
- for (const handler of Object.values(channelHandlers)) {
728
- state.serviceAggregation.usedFunctions.add(handler.pikkuFuncId)
729
- }
730
- }
731
- extractWireNames(middleware).forEach((name) =>
732
- state.serviceAggregation.usedMiddleware.add(name)
733
- )
734
-
735
- // --- validate auth/sessionless ---
736
- const handlersToValidate = [
737
- connectFuncId,
738
- disconnectFuncId,
739
- message?.pikkuFuncId,
740
- ].filter(Boolean) as string[]
741
- for (const funcId of handlersToValidate) {
742
- if (
743
- !validateAuthSessionless(logger, obj, state, funcId, `Channel '${name}'`)
744
- ) {
745
- return
746
- }
747
- }
748
-
749
- state.channels.files.add(node.getSourceFile().fileName)
750
- state.channels.meta[name] = {
751
- name,
752
- route,
753
- input: null,
754
- params: params.length ? params : undefined,
755
- query: query?.length ? query : undefined,
756
- connect: connectFuncId
757
- ? {
758
- pikkuFuncId: connectFuncId,
759
- ...(connectPackageName && { packageName: connectPackageName }),
760
- }
761
- : null,
762
- disconnect: disconnectFuncId
763
- ? {
764
- pikkuFuncId: disconnectFuncId,
765
- ...(disconnectPackageName && { packageName: disconnectPackageName }),
766
- }
767
- : null,
768
- message,
769
- messageWirings,
770
- binary: binary === undefined ? undefined : binary,
771
- summary,
772
- description,
773
- errors,
774
- tags: tags ?? undefined,
775
- middleware,
776
- channelMiddleware,
777
- }
778
- }