@pikku/inspector 0.12.75 → 0.12.82

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 (223) hide show
  1. package/CHANGELOG.md +357 -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-middleware.js +66 -42
  8. package/dist/add/add-wire-addon.js +7 -4
  9. package/dist/add/add-wire-remote-addon.js +1 -0
  10. package/dist/error-codes.d.ts +1 -0
  11. package/dist/error-codes.js +1 -0
  12. package/dist/index.d.ts +3 -1
  13. package/dist/index.js +3 -1
  14. package/dist/inspector.js +5 -0
  15. package/dist/surface/collect-surface.d.ts +58 -0
  16. package/dist/surface/collect-surface.js +613 -0
  17. package/dist/types.d.ts +36 -12
  18. package/dist/utils/filter-inspector-state.js +14 -0
  19. package/dist/utils/load-addon-functions-meta.js +24 -11
  20. package/dist/utils/post-process.js +15 -0
  21. package/dist/utils/read-module-specifiers.d.ts +8 -0
  22. package/dist/utils/read-module-specifiers.js +23 -0
  23. package/dist/utils/read-tsconfig-out-dir.d.ts +5 -0
  24. package/dist/utils/read-tsconfig-out-dir.js +12 -0
  25. package/dist/utils/resolve-deploy-target.d.ts +7 -0
  26. package/dist/utils/resolve-deploy-target.js +15 -7
  27. package/dist/utils/serialize-inspector-state.d.ts +6 -0
  28. package/dist/utils/serialize-inspector-state.js +10 -0
  29. package/dist/visit.js +4 -0
  30. package/package.json +16 -6
  31. package/run-tests.sh +21 -6
  32. package/src/add/add-addon-bans.ts +0 -85
  33. package/src/add/add-agent-scorer.test.ts +0 -197
  34. package/src/add/add-agent-scorer.ts +0 -83
  35. package/src/add/add-agent.test.ts +0 -87
  36. package/src/add/add-agent.ts +0 -638
  37. package/src/add/add-approval-description.ts +0 -76
  38. package/src/add/add-auth.test.ts +0 -791
  39. package/src/add/add-auth.ts +0 -424
  40. package/src/add/add-channel.ts +0 -778
  41. package/src/add/add-cli-renderers.test.ts +0 -74
  42. package/src/add/add-cli.ts +0 -1006
  43. package/src/add/add-credential.ts +0 -186
  44. package/src/add/add-feature.test.ts +0 -306
  45. package/src/add/add-feature.ts +0 -253
  46. package/src/add/add-file-extends-core-type.ts +0 -66
  47. package/src/add/add-file-with-config.ts +0 -45
  48. package/src/add/add-file-with-factory.test.ts +0 -134
  49. package/src/add/add-file-with-factory.ts +0 -288
  50. package/src/add/add-functions.test.ts +0 -743
  51. package/src/add/add-functions.ts +0 -1548
  52. package/src/add/add-gateway.ts +0 -143
  53. package/src/add/add-http-route-ref.test.ts +0 -149
  54. package/src/add/add-http-route.ts +0 -530
  55. package/src/add/add-http-routes.ts +0 -471
  56. package/src/add/add-keyed-wiring.ts +0 -180
  57. package/src/add/add-mcp-prompt.ts +0 -157
  58. package/src/add/add-mcp-resource.ts +0 -175
  59. package/src/add/add-middleware.test.ts +0 -196
  60. package/src/add/add-middleware.ts +0 -879
  61. package/src/add/add-permission-auth.test.ts +0 -59
  62. package/src/add/add-permission.ts +0 -311
  63. package/src/add/add-personas.test.ts +0 -406
  64. package/src/add/add-personas.ts +0 -287
  65. package/src/add/add-queue-worker.ts +0 -117
  66. package/src/add/add-rpc-invocations.test.ts +0 -287
  67. package/src/add/add-rpc-invocations.ts +0 -221
  68. package/src/add/add-scenario-step.test.ts +0 -509
  69. package/src/add/add-schedule.ts +0 -120
  70. package/src/add/add-scope.test.ts +0 -419
  71. package/src/add/add-scope.ts +0 -221
  72. package/src/add/add-secret.ts +0 -10
  73. package/src/add/add-subject-scenario-steps.test.ts +0 -161
  74. package/src/add/add-system-role.test.ts +0 -297
  75. package/src/add/add-system-role.ts +0 -162
  76. package/src/add/add-trigger.ts +0 -165
  77. package/src/add/add-variable.ts +0 -10
  78. package/src/add/add-wire-addon.test.ts +0 -185
  79. package/src/add/add-wire-addon.ts +0 -146
  80. package/src/add/add-wire-remote-addon.ts +0 -80
  81. package/src/add/add-workflow-edge-cases.test.ts +0 -201
  82. package/src/add/add-workflow-fanout.test.ts +0 -166
  83. package/src/add/add-workflow-graph-foreach.test.ts +0 -174
  84. package/src/add/add-workflow-graph-input.test.ts +0 -94
  85. package/src/add/add-workflow-graph-notes.test.ts +0 -74
  86. package/src/add/add-workflow-graph.ts +0 -644
  87. package/src/add/add-workflow-on-error.test.ts +0 -118
  88. package/src/add/add-workflow-return-switch.test.ts +0 -121
  89. package/src/add/add-workflow-silent-drops.test.ts +0 -261
  90. package/src/add/add-workflow-sleep-expression.test.ts +0 -96
  91. package/src/add/add-workflow.test.ts +0 -155
  92. package/src/add/add-workflow.ts +0 -482
  93. package/src/add/addon-bans.test.ts +0 -121
  94. package/src/add/addon-contracts.test.ts +0 -221
  95. package/src/add/allow-escape-hatches.test.ts +0 -144
  96. package/src/add/audit-meta.test.ts +0 -75
  97. package/src/add/classified-sink-check.test.ts +0 -347
  98. package/src/add/config-value-metadata.test.ts +0 -263
  99. package/src/add/dynamic-import-check.test.ts +0 -119
  100. package/src/add/expect-eventually-scenario-only.test.ts +0 -108
  101. package/src/add/function-body-span.test.ts +0 -96
  102. package/src/add/inline-wiring-function.test.ts +0 -104
  103. package/src/add/pii-check.test.ts +0 -258
  104. package/src/add/rpc-type-cast.test.ts +0 -123
  105. package/src/add/scenario-instrumentation.test.ts +0 -89
  106. package/src/add/scenario-instrumentation.ts +0 -42
  107. package/src/add/scenario-marker.test.ts +0 -89
  108. package/src/add/scenario-not-extractable.test.ts +0 -159
  109. package/src/add/scenario-services.test.ts +0 -103
  110. package/src/add/scenario-skip.test.ts +0 -83
  111. package/src/add/validate-workflow-graph-addons.test.ts +0 -102
  112. package/src/add/wire-name-literal.test.ts +0 -117
  113. package/src/error-codes-emitted.test.ts +0 -76
  114. package/src/error-codes.ts +0 -145
  115. package/src/index.ts +0 -66
  116. package/src/inspector.ts +0 -575
  117. package/src/schema-generator.ts +0 -1
  118. package/src/source-file-cache.ts +0 -73
  119. package/src/tsconfig-paths.test.ts +0 -90
  120. package/src/types-map.ts +0 -141
  121. package/src/types.ts +0 -775
  122. package/src/utils/annotate-http-route-auth.test.ts +0 -161
  123. package/src/utils/annotate-http-route-auth.ts +0 -53
  124. package/src/utils/check-classified-sinks.ts +0 -215
  125. package/src/utils/check-pii-output.ts +0 -120
  126. package/src/utils/compute-diagnostics.test.ts +0 -106
  127. package/src/utils/compute-required-schemas.ts +0 -48
  128. package/src/utils/contract-hashes.test.ts +0 -553
  129. package/src/utils/contract-hashes.ts +0 -387
  130. package/src/utils/custom-types-generator.test.ts +0 -124
  131. package/src/utils/custom-types-generator.ts +0 -150
  132. package/src/utils/detect-schema-vendor.test.ts +0 -123
  133. package/src/utils/detect-schema-vendor.ts +0 -120
  134. package/src/utils/does-type-extend-core-type.ts +0 -53
  135. package/src/utils/ensure-function-metadata.ts +0 -385
  136. package/src/utils/extract-function-name.ts +0 -612
  137. package/src/utils/extract-function-node.ts +0 -70
  138. package/src/utils/extract-node-value.test.ts +0 -117
  139. package/src/utils/extract-node-value.ts +0 -172
  140. package/src/utils/extract-secret-usage.test.ts +0 -81
  141. package/src/utils/extract-secret-usage.ts +0 -73
  142. package/src/utils/extract-services.ts +0 -70
  143. package/src/utils/extract-surface-usage.test.ts +0 -147
  144. package/src/utils/extract-surface-usage.ts +0 -121
  145. package/src/utils/filter-inspector-state.test.ts +0 -2232
  146. package/src/utils/filter-inspector-state.ts +0 -1298
  147. package/src/utils/filter-utils.test.ts +0 -843
  148. package/src/utils/filter-utils.ts +0 -161
  149. package/src/utils/find-root-dir.ts +0 -68
  150. package/src/utils/find-zod-transform.test.ts +0 -70
  151. package/src/utils/find-zod-transform.ts +0 -103
  152. package/src/utils/get-exported-variable-name.ts +0 -48
  153. package/src/utils/get-files-and-methods.ts +0 -157
  154. package/src/utils/get-property-value.test.ts +0 -141
  155. package/src/utils/get-property-value.ts +0 -331
  156. package/src/utils/hash.ts +0 -26
  157. package/src/utils/literal-properties.ts +0 -97
  158. package/src/utils/load-addon-functions-meta.test.ts +0 -157
  159. package/src/utils/load-addon-functions-meta.ts +0 -508
  160. package/src/utils/load-addon-scopes.test.ts +0 -138
  161. package/src/utils/middleware.test.ts +0 -204
  162. package/src/utils/middleware.ts +0 -303
  163. package/src/utils/nested-project-filter.test.ts +0 -53
  164. package/src/utils/nested-project-filter.ts +0 -28
  165. package/src/utils/permissions-shorthand.test.ts +0 -79
  166. package/src/utils/permissions.test.ts +0 -125
  167. package/src/utils/permissions.ts +0 -129
  168. package/src/utils/post-process.test.ts +0 -1094
  169. package/src/utils/post-process.ts +0 -1604
  170. package/src/utils/resolve-addon-package.ts +0 -106
  171. package/src/utils/resolve-core-type.test.ts +0 -142
  172. package/src/utils/resolve-core-type.ts +0 -35
  173. package/src/utils/resolve-deploy-target.test.ts +0 -135
  174. package/src/utils/resolve-deploy-target.ts +0 -65
  175. package/src/utils/resolve-function-meta.ts +0 -25
  176. package/src/utils/resolve-function-types.ts +0 -42
  177. package/src/utils/resolve-identifier.ts +0 -46
  178. package/src/utils/resolve-ref-contract.ts +0 -71
  179. package/src/utils/resolve-versions.test.ts +0 -499
  180. package/src/utils/resolve-versions.ts +0 -228
  181. package/src/utils/schema-cache.test.ts +0 -124
  182. package/src/utils/schema-generator.test.ts +0 -272
  183. package/src/utils/schema-generator.ts +0 -780
  184. package/src/utils/secret-alias-services.test.ts +0 -146
  185. package/src/utils/secret-alias-services.ts +0 -58
  186. package/src/utils/serialize-inspector-state.ts +0 -742
  187. package/src/utils/serialize-mcp-json.ts +0 -150
  188. package/src/utils/serialize-middleware-groups-meta.ts +0 -36
  189. package/src/utils/serialize-openapi-json.ts +0 -277
  190. package/src/utils/serialize-permissions-groups-meta.ts +0 -7
  191. package/src/utils/single-declaration.ts +0 -57
  192. package/src/utils/type-utils.ts +0 -145
  193. package/src/utils/validate-auth-sessionless.ts +0 -30
  194. package/src/utils/validate-exposed-functions-gated.test.ts +0 -230
  195. package/src/utils/validate-exposed-functions-gated.ts +0 -103
  196. package/src/utils/validate-tags-resolve-to-middleware.test.ts +0 -108
  197. package/src/utils/validate-tags-resolve-to-middleware.ts +0 -48
  198. package/src/utils/workflow/derive-workflow-plan.test.ts +0 -178
  199. package/src/utils/workflow/derive-workflow-plan.ts +0 -117
  200. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +0 -1465
  201. package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +0 -71
  202. package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +0 -99
  203. package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +0 -161
  204. package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +0 -121
  205. package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +0 -116
  206. package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +0 -145
  207. package/src/utils/workflow/dsl/extract-approval-policy.test.ts +0 -71
  208. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +0 -2254
  209. package/src/utils/workflow/dsl/index.ts +0 -11
  210. package/src/utils/workflow/dsl/patterns.ts +0 -440
  211. package/src/utils/workflow/dsl/validation.ts +0 -203
  212. package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +0 -35
  213. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +0 -543
  214. package/src/utils/workflow/graph/fanout-node-identity.test.ts +0 -105
  215. package/src/utils/workflow/graph/finalize-workflow-wires.ts +0 -310
  216. package/src/utils/workflow/graph/finalize-workflows.test.ts +0 -50
  217. package/src/utils/workflow/graph/finalize-workflows.ts +0 -130
  218. package/src/utils/workflow/graph/index.ts +0 -28
  219. package/src/utils/workflow/graph/serialize-workflow-graph.ts +0 -240
  220. package/src/utils/workflow/graph/workflow-graph.types.ts +0 -281
  221. package/src/visit.ts +0 -181
  222. package/tsconfig.json +0 -19
  223. 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
- }