@pikku/inspector 0.9.5 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/add/add-channel.d.ts +17 -0
  3. package/dist/{add-channel.js → add/add-channel.js} +60 -34
  4. package/dist/add/add-cli.d.ts +9 -0
  5. package/dist/add/add-cli.js +566 -0
  6. package/dist/{add-file-extends-core-type.d.ts → add/add-file-extends-core-type.d.ts} +2 -2
  7. package/dist/{add-file-extends-core-type.js → add/add-file-extends-core-type.js} +17 -4
  8. package/dist/{add-file-with-config.d.ts → add/add-file-with-config.d.ts} +1 -1
  9. package/dist/{add-file-with-config.js → add/add-file-with-config.js} +1 -1
  10. package/dist/{add-file-with-factory.d.ts → add/add-file-with-factory.d.ts} +2 -2
  11. package/dist/{add-file-with-factory.js → add/add-file-with-factory.js} +38 -5
  12. package/dist/add/add-functions.d.ts +6 -0
  13. package/dist/{add-functions.js → add/add-functions.js} +77 -10
  14. package/dist/{add-http-route.d.ts → add/add-http-route.d.ts} +2 -3
  15. package/dist/{add-http-route.js → add/add-http-route.js} +26 -13
  16. package/dist/add/add-mcp-prompt.d.ts +2 -0
  17. package/dist/add/add-mcp-prompt.js +74 -0
  18. package/dist/add/add-mcp-resource.d.ts +2 -0
  19. package/dist/add/add-mcp-resource.js +84 -0
  20. package/dist/add/add-mcp-tool.d.ts +2 -0
  21. package/dist/add/add-mcp-tool.js +80 -0
  22. package/dist/add/add-middleware.d.ts +5 -0
  23. package/dist/add/add-middleware.js +290 -0
  24. package/dist/add/add-permission.d.ts +5 -0
  25. package/dist/add/add-permission.js +292 -0
  26. package/dist/add/add-queue-worker.d.ts +2 -0
  27. package/dist/add/add-queue-worker.js +52 -0
  28. package/dist/{add-rpc-invocations.d.ts → add/add-rpc-invocations.d.ts} +1 -1
  29. package/dist/add/add-schedule.d.ts +2 -0
  30. package/dist/{add-schedule.js → add/add-schedule.js} +16 -11
  31. package/dist/error-codes.d.ts +35 -0
  32. package/dist/error-codes.js +40 -0
  33. package/dist/index.d.ts +6 -0
  34. package/dist/index.js +4 -0
  35. package/dist/inspector.d.ts +2 -3
  36. package/dist/inspector.js +38 -8
  37. package/dist/types.d.ts +108 -1
  38. package/dist/utils/ensure-function-metadata.d.ts +6 -0
  39. package/dist/utils/ensure-function-metadata.js +18 -0
  40. package/dist/utils/extract-function-name.d.ts +31 -0
  41. package/dist/{utils.js → utils/extract-function-name.js} +35 -149
  42. package/dist/utils/extract-services.d.ts +6 -0
  43. package/dist/utils/extract-services.js +29 -0
  44. package/dist/utils/filter-inspector-state.d.ts +6 -0
  45. package/dist/utils/filter-inspector-state.js +382 -0
  46. package/dist/utils/filter-utils.d.ts +19 -0
  47. package/dist/utils/filter-utils.js +109 -0
  48. package/dist/utils/find-root-dir.d.ts +23 -0
  49. package/dist/utils/find-root-dir.js +55 -0
  50. package/dist/utils/get-files-and-methods.d.ts +22 -0
  51. package/dist/utils/get-files-and-methods.js +61 -0
  52. package/dist/utils/get-property-value.d.ts +12 -0
  53. package/dist/{get-property-value.js → utils/get-property-value.js} +20 -0
  54. package/dist/utils/middleware.d.ts +39 -0
  55. package/dist/utils/middleware.js +157 -0
  56. package/dist/utils/permissions.d.ts +43 -0
  57. package/dist/utils/permissions.js +178 -0
  58. package/dist/utils/post-process.d.ts +16 -0
  59. package/dist/utils/post-process.js +132 -0
  60. package/dist/utils/serialize-inspector-state.d.ts +179 -0
  61. package/dist/utils/serialize-inspector-state.js +170 -0
  62. package/dist/utils/type-utils.d.ts +3 -0
  63. package/dist/utils/type-utils.js +50 -0
  64. package/dist/visit.d.ts +3 -3
  65. package/dist/visit.js +35 -31
  66. package/package.json +5 -6
  67. package/src/{add-channel.ts → add/add-channel.ts} +108 -56
  68. package/src/add/add-cli.ts +822 -0
  69. package/src/{add-file-extends-core-type.ts → add/add-file-extends-core-type.ts} +23 -5
  70. package/src/{add-file-with-config.ts → add/add-file-with-config.ts} +2 -2
  71. package/src/{add-file-with-factory.ts → add/add-file-with-factory.ts} +49 -6
  72. package/src/{add-functions.ts → add/add-functions.ts} +89 -19
  73. package/src/{add-http-route.ts → add/add-http-route.ts} +66 -32
  74. package/src/add/add-mcp-prompt.ts +128 -0
  75. package/src/add/add-mcp-prompt.ts.tmp +0 -0
  76. package/src/add/add-mcp-resource.ts +145 -0
  77. package/src/add/add-mcp-resource.ts.tmp +0 -0
  78. package/src/add/add-mcp-tool.ts +137 -0
  79. package/src/add/add-middleware.ts +385 -0
  80. package/src/add/add-permission.ts +391 -0
  81. package/src/add/add-queue-worker.ts +92 -0
  82. package/src/{add-rpc-invocations.ts → add/add-rpc-invocations.ts} +1 -1
  83. package/src/{add-schedule.ts → add/add-schedule.ts} +30 -28
  84. package/src/error-codes.ts +43 -0
  85. package/src/index.ts +12 -0
  86. package/src/inspector.ts +41 -17
  87. package/src/types.ts +128 -1
  88. package/src/utils/ensure-function-metadata.ts +24 -0
  89. package/src/{utils.ts → utils/extract-function-name.ts} +44 -206
  90. package/src/utils/extract-services.ts +35 -0
  91. package/src/utils/filter-inspector-state.test.ts +1433 -0
  92. package/src/utils/filter-inspector-state.ts +526 -0
  93. package/src/{utils.test.ts → utils/filter-utils.test.ts} +351 -2
  94. package/src/utils/filter-utils.ts +152 -0
  95. package/src/utils/find-root-dir.ts +68 -0
  96. package/src/utils/get-files-and-methods.ts +151 -0
  97. package/src/{get-property-value.ts → utils/get-property-value.ts} +27 -0
  98. package/src/utils/middleware.ts +241 -0
  99. package/src/utils/permissions.test.ts +327 -0
  100. package/src/utils/permissions.ts +262 -0
  101. package/src/utils/post-process.ts +178 -0
  102. package/src/utils/serialize-inspector-state.ts +375 -0
  103. package/src/utils/test-data/inspector-state.json +1680 -0
  104. package/src/utils/type-utils.ts +74 -0
  105. package/src/visit.ts +50 -34
  106. package/tsconfig.tsbuildinfo +1 -1
  107. package/dist/add-channel.d.ts +0 -13
  108. package/dist/add-functions.d.ts +0 -7
  109. package/dist/add-mcp-prompt.d.ts +0 -3
  110. package/dist/add-mcp-prompt.js +0 -61
  111. package/dist/add-mcp-resource.d.ts +0 -3
  112. package/dist/add-mcp-resource.js +0 -68
  113. package/dist/add-mcp-tool.d.ts +0 -3
  114. package/dist/add-mcp-tool.js +0 -64
  115. package/dist/add-middleware.d.ts +0 -7
  116. package/dist/add-middleware.js +0 -35
  117. package/dist/add-permission.d.ts +0 -7
  118. package/dist/add-permission.js +0 -35
  119. package/dist/add-queue-worker.d.ts +0 -3
  120. package/dist/add-queue-worker.js +0 -48
  121. package/dist/add-schedule.d.ts +0 -3
  122. package/dist/get-property-value.d.ts +0 -3
  123. package/dist/utils.d.ts +0 -39
  124. package/src/add-mcp-prompt.ts +0 -104
  125. package/src/add-mcp-resource.ts +0 -116
  126. package/src/add-mcp-tool.ts +0 -107
  127. package/src/add-middleware.ts +0 -51
  128. package/src/add-permission.ts +0 -53
  129. package/src/add-queue-worker.ts +0 -92
  130. /package/dist/{add-rpc-invocations.js → add/add-rpc-invocations.js} +0 -0
  131. /package/dist/{does-type-extend-core-type.d.ts → utils/does-type-extend-core-type.d.ts} +0 -0
  132. /package/dist/{does-type-extend-core-type.js → utils/does-type-extend-core-type.js} +0 -0
  133. /package/src/{does-type-extend-core-type.ts → utils/does-type-extend-core-type.ts} +0 -0
@@ -0,0 +1,391 @@
1
+ import * as ts from 'typescript'
2
+ import { AddWiring } from '../types.js'
3
+ import {
4
+ extractFunctionName,
5
+ isNamedExport,
6
+ } from '../utils/extract-function-name.js'
7
+ import { extractServicesFromFunction } from '../utils/extract-services.js'
8
+ import { extractPermissionPikkuNames } from '../utils/permissions.js'
9
+ import { getPropertyValue } from '../utils/get-property-value.js'
10
+ import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
11
+
12
+ /**
13
+ * Inspect pikkuPermission calls, addPermission calls, and addHTTPPermission calls
14
+ */
15
+ export const addPermission: AddWiring = (logger, node, checker, state) => {
16
+ if (!ts.isCallExpression(node)) return
17
+
18
+ const { expression, arguments: args } = node
19
+
20
+ // only handle specific function calls
21
+ if (!ts.isIdentifier(expression)) {
22
+ return
23
+ }
24
+
25
+ // Handle pikkuPermission(...) - individual permission function definition
26
+ if (expression.text === 'pikkuPermission') {
27
+ const arg = args[0]
28
+ if (!arg) return
29
+
30
+ let actualHandler: ts.ArrowFunction | ts.FunctionExpression
31
+ let name: string | undefined
32
+ let description: string | undefined
33
+
34
+ // Check if using object syntax: pikkuPermission({ func: ..., name: '...', description: '...' })
35
+ if (ts.isObjectLiteralExpression(arg)) {
36
+ // Extract name and description metadata
37
+ const nameValue = getPropertyValue(arg, 'name')
38
+ const descValue = getPropertyValue(arg, 'description')
39
+ name = typeof nameValue === 'string' ? nameValue : undefined
40
+ description = typeof descValue === 'string' ? descValue : undefined
41
+
42
+ // Extract the func property
43
+ const fnProp = getPropertyAssignmentInitializer(
44
+ arg,
45
+ 'func',
46
+ true,
47
+ checker
48
+ )
49
+ if (
50
+ !fnProp ||
51
+ (!ts.isArrowFunction(fnProp) && !ts.isFunctionExpression(fnProp))
52
+ ) {
53
+ logger.error(
54
+ `• pikkuPermission object missing required 'func' property.`
55
+ )
56
+ return
57
+ }
58
+ actualHandler = fnProp
59
+ } else if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
60
+ actualHandler = arg
61
+ } else {
62
+ logger.error(`• Handler for pikkuPermission is not a function.`)
63
+ return
64
+ }
65
+
66
+ const services = extractServicesFromFunction(actualHandler)
67
+ const { pikkuFuncName, exportedName } = extractFunctionName(
68
+ node,
69
+ checker,
70
+ state.rootDir
71
+ )
72
+ state.permissions.meta[pikkuFuncName] = {
73
+ services,
74
+ sourceFile: node.getSourceFile().fileName,
75
+ position: node.getStart(),
76
+ exportedName,
77
+ name,
78
+ description,
79
+ }
80
+
81
+ logger.debug(
82
+ `• Found permission with services: ${services.services.join(', ')}${name ? ` (name: ${name})` : ''}${description ? ` (description: ${description})` : ''}`
83
+ )
84
+ return
85
+ }
86
+
87
+ // Handle pikkuPermissionFactory(...) - permission factory function
88
+ if (expression.text === 'pikkuPermissionFactory') {
89
+ const factoryNode = args[0]
90
+ if (!factoryNode) return
91
+
92
+ if (
93
+ !ts.isArrowFunction(factoryNode) &&
94
+ !ts.isFunctionExpression(factoryNode)
95
+ ) {
96
+ logger.error(`• Handler for pikkuPermissionFactory is not a function.`)
97
+ return
98
+ }
99
+
100
+ // Extract services by looking inside the factory function body
101
+ // The factory should return pikkuPermission(...), so we need to find that call
102
+ // If no wrapper is found, extract from the factory's returned function directly
103
+ let services = { optimized: false, services: [] as string[] }
104
+
105
+ const findPikkuPermissionCall = (
106
+ node: ts.Node
107
+ ): ts.CallExpression | undefined => {
108
+ if (ts.isCallExpression(node)) {
109
+ const expr = node.expression
110
+ if (ts.isIdentifier(expr) && expr.text === 'pikkuPermission') {
111
+ return node
112
+ }
113
+ }
114
+ return ts.forEachChild(node, findPikkuPermissionCall)
115
+ }
116
+
117
+ const pikkuPermissionCall = findPikkuPermissionCall(factoryNode)
118
+ if (pikkuPermissionCall && pikkuPermissionCall.arguments[0]) {
119
+ const permissionHandler = pikkuPermissionCall.arguments[0]
120
+ if (
121
+ ts.isArrowFunction(permissionHandler) ||
122
+ ts.isFunctionExpression(permissionHandler)
123
+ ) {
124
+ services = extractServicesFromFunction(permissionHandler)
125
+ }
126
+ } else {
127
+ // No pikkuPermission wrapper found - extract from factory's return value directly
128
+ // Factory pattern: (config) => (services, data, session) => { ... }
129
+ if (
130
+ ts.isArrowFunction(factoryNode) ||
131
+ ts.isFunctionExpression(factoryNode)
132
+ ) {
133
+ const factoryBody = factoryNode.body
134
+ // Check if the body is an arrow function (direct return)
135
+ if (
136
+ ts.isArrowFunction(factoryBody) ||
137
+ ts.isFunctionExpression(factoryBody)
138
+ ) {
139
+ services = extractServicesFromFunction(factoryBody)
140
+ }
141
+ }
142
+ }
143
+
144
+ const { pikkuFuncName, exportedName } = extractFunctionName(
145
+ node,
146
+ checker,
147
+ state.rootDir
148
+ )
149
+ state.permissions.meta[pikkuFuncName] = {
150
+ services,
151
+ sourceFile: node.getSourceFile().fileName,
152
+ position: node.getStart(),
153
+ exportedName,
154
+ factory: true,
155
+ }
156
+
157
+ logger.debug(
158
+ `• Found permission factory with services: ${services.services.join(', ')}`
159
+ )
160
+ return
161
+ }
162
+
163
+ // Handle addPermission('tag', [permission1, permission2])
164
+ // Supports two patterns:
165
+ // 1. export const x = () => addPermission('tag', [...]) (factory - tree-shakeable)
166
+ // 2. export const x = addPermission('tag', [...]) (direct - no tree-shaking)
167
+ if (expression.text === 'addPermission') {
168
+ const tagArg = args[0]
169
+ const permissionsArrayArg = args[1]
170
+
171
+ if (!tagArg || !permissionsArrayArg) return
172
+
173
+ // Extract tag name
174
+ let tag: string | undefined
175
+ if (ts.isStringLiteral(tagArg)) {
176
+ tag = tagArg.text
177
+ }
178
+
179
+ if (!tag) {
180
+ logger.warn(`• addPermission call without valid tag string`)
181
+ return
182
+ }
183
+
184
+ // Check if permissions is a literal array or object
185
+ if (
186
+ !ts.isArrayLiteralExpression(permissionsArrayArg) &&
187
+ !ts.isObjectLiteralExpression(permissionsArrayArg)
188
+ ) {
189
+ logger.error(
190
+ `• addPermission('${tag}', ...) must have a literal array or object as second argument`
191
+ )
192
+ return
193
+ }
194
+
195
+ // Extract permission pikkuFuncNames from array
196
+ const permissionNames = extractPermissionPikkuNames(
197
+ permissionsArrayArg,
198
+ checker,
199
+ state.rootDir
200
+ )
201
+
202
+ if (permissionNames.length === 0) {
203
+ logger.warn(`• addPermission('${tag}', ...) has empty permissions array`)
204
+ return
205
+ }
206
+
207
+ // Collect services from all permissions in the group
208
+ const allServices = new Set<string>()
209
+ for (const permissionName of permissionNames) {
210
+ const permissionMeta = state.permissions.meta[permissionName]
211
+ if (permissionMeta && permissionMeta.services) {
212
+ for (const service of permissionMeta.services.services) {
213
+ allServices.add(service)
214
+ }
215
+ }
216
+ }
217
+
218
+ // Check if this call is wrapped in a factory function
219
+ // We need to walk up the tree to see if the parent is: const x = () => addPermission(...)
220
+ let isFactory = false
221
+ let exportedName: string | null = null
222
+ let parent = node.parent
223
+
224
+ // Check if parent is arrow function: () => addPermission(...)
225
+ if (parent && ts.isArrowFunction(parent)) {
226
+ // Check if arrow function has no parameters
227
+ if (parent.parameters.length === 0) {
228
+ isFactory = true
229
+
230
+ // For factories, we need to check the arrow function's parent for the export name
231
+ // const apiTagPermissions = () => addPermission(...)
232
+ const arrowParent = parent.parent
233
+ if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
234
+ if (ts.isIdentifier(arrowParent.name)) {
235
+ // Check if it's exported
236
+ if (isNamedExport(arrowParent)) {
237
+ exportedName = arrowParent.name.text
238
+ }
239
+ }
240
+ }
241
+ }
242
+ }
243
+
244
+ // If not a factory, get export name from the call expression itself
245
+ if (!isFactory) {
246
+ const extracted = extractFunctionName(node, checker, state.rootDir)
247
+ exportedName = extracted.exportedName
248
+ }
249
+
250
+ // Log warning if not using factory pattern
251
+ if (!isFactory && exportedName) {
252
+ logger.warn(
253
+ `• Permission group '${exportedName}' for tag '${tag}' is not wrapped in a factory function. ` +
254
+ `For tree-shaking, use: export const ${exportedName} = () => addPermission('${tag}', [...])`
255
+ )
256
+ }
257
+
258
+ // Store group metadata
259
+ state.permissions.tagPermissions.set(tag, {
260
+ exportName: exportedName,
261
+ sourceFile: node.getSourceFile().fileName,
262
+ position: node.getStart(),
263
+ services: {
264
+ optimized: false,
265
+ services: Array.from(allServices),
266
+ },
267
+ permissionCount: permissionNames.length,
268
+ isFactory,
269
+ })
270
+
271
+ logger.debug(
272
+ `• Found tag permission group: ${tag} -> [${permissionNames.join(', ')}] (${isFactory ? 'factory' : 'direct'})`
273
+ )
274
+ return
275
+ }
276
+
277
+ // Handle addHTTPPermission(pattern, [permission1, permission2])
278
+ // Supports two patterns:
279
+ // 1. export const x = () => addHTTPPermission('*', [...]) (factory - tree-shakeable)
280
+ // 2. export const x = addHTTPPermission('*', [...]) (direct - no tree-shaking)
281
+ if (expression.text === 'addHTTPPermission') {
282
+ const patternArg = args[0]
283
+ const permissionsArrayArg = args[1]
284
+
285
+ if (!patternArg || !permissionsArrayArg) return
286
+
287
+ // Extract route pattern
288
+ let pattern: string | undefined
289
+ if (ts.isStringLiteral(patternArg)) {
290
+ pattern = patternArg.text
291
+ }
292
+
293
+ if (!pattern) {
294
+ logger.warn(`• addHTTPPermission call without valid pattern string`)
295
+ return
296
+ }
297
+
298
+ // Check if permissions is a literal array or object
299
+ if (
300
+ !ts.isArrayLiteralExpression(permissionsArrayArg) &&
301
+ !ts.isObjectLiteralExpression(permissionsArrayArg)
302
+ ) {
303
+ logger.error(
304
+ `• addHTTPPermission('${pattern}', ...) must have a literal array or object as second argument`
305
+ )
306
+ return
307
+ }
308
+
309
+ // Extract permission pikkuFuncNames from array
310
+ const permissionNames = extractPermissionPikkuNames(
311
+ permissionsArrayArg,
312
+ checker,
313
+ state.rootDir
314
+ )
315
+
316
+ if (permissionNames.length === 0) {
317
+ logger.warn(
318
+ `• addHTTPPermission('${pattern}', ...) has empty permissions array`
319
+ )
320
+ return
321
+ }
322
+
323
+ // Collect services from all permissions in the group
324
+ const allServices = new Set<string>()
325
+ for (const permissionName of permissionNames) {
326
+ const permissionMeta = state.permissions.meta[permissionName]
327
+ if (permissionMeta && permissionMeta.services) {
328
+ for (const service of permissionMeta.services.services) {
329
+ allServices.add(service)
330
+ }
331
+ }
332
+ }
333
+
334
+ // Check if this call is wrapped in a factory function
335
+ let isFactory = false
336
+ let exportedName: string | null = null
337
+ let parent = node.parent
338
+
339
+ // Check if parent is arrow function: () => addHTTPPermission(...)
340
+ if (parent && ts.isArrowFunction(parent)) {
341
+ // Check if arrow function has no parameters
342
+ if (parent.parameters.length === 0) {
343
+ isFactory = true
344
+
345
+ // For factories, we need to check the arrow function's parent for the export name
346
+ // const apiRoutePermissions = () => addHTTPPermission(...)
347
+ const arrowParent = parent.parent
348
+ if (arrowParent && ts.isVariableDeclaration(arrowParent)) {
349
+ if (ts.isIdentifier(arrowParent.name)) {
350
+ // Check if it's exported
351
+ if (isNamedExport(arrowParent)) {
352
+ exportedName = arrowParent.name.text
353
+ }
354
+ }
355
+ }
356
+ }
357
+ }
358
+
359
+ // If not a factory, get export name from the call expression itself
360
+ if (!isFactory) {
361
+ const extracted = extractFunctionName(node, checker, state.rootDir)
362
+ exportedName = extracted.exportedName
363
+ }
364
+
365
+ // Log warning if not using factory pattern
366
+ if (!isFactory && exportedName) {
367
+ logger.warn(
368
+ `• HTTP permission group '${exportedName}' for pattern '${pattern}' is not wrapped in a factory function. ` +
369
+ `For tree-shaking, use: export const ${exportedName} = () => addHTTPPermission('${pattern}', [...])`
370
+ )
371
+ }
372
+
373
+ // Store group metadata
374
+ state.http.routePermissions.set(pattern, {
375
+ exportName: exportedName,
376
+ sourceFile: node.getSourceFile().fileName,
377
+ position: node.getStart(),
378
+ services: {
379
+ optimized: false,
380
+ services: Array.from(allServices),
381
+ },
382
+ permissionCount: permissionNames.length,
383
+ isFactory,
384
+ })
385
+
386
+ logger.debug(
387
+ `• Found HTTP route permission group: ${pattern} -> [${permissionNames.join(', ')}] (${isFactory ? 'factory' : 'direct'})`
388
+ )
389
+ return
390
+ }
391
+ }
@@ -0,0 +1,92 @@
1
+ import * as ts from 'typescript'
2
+ import {
3
+ getPropertyValue,
4
+ getPropertyTags,
5
+ } from '../utils/get-property-value.js'
6
+ import { PikkuDocs } from '@pikku/core'
7
+ import { AddWiring } from '../types.js'
8
+ import { extractFunctionName } from '../utils/extract-function-name.js'
9
+ import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
10
+ import { resolveMiddleware } from '../utils/middleware.js'
11
+ import { extractWireNames } from '../utils/post-process.js'
12
+ import { ErrorCode } from '../error-codes.js'
13
+
14
+ export const addQueueWorker: AddWiring = (
15
+ logger,
16
+ node,
17
+ checker,
18
+ state,
19
+ options
20
+ ) => {
21
+ if (!ts.isCallExpression(node)) {
22
+ return
23
+ }
24
+
25
+ const args = node.arguments
26
+ const firstArg = args[0]
27
+ const expression = node.expression
28
+
29
+ // Check if the call is to addQueueWorker
30
+ if (!ts.isIdentifier(expression) || expression.text !== 'wireQueueWorker') {
31
+ return
32
+ }
33
+
34
+ if (!firstArg) {
35
+ return
36
+ }
37
+
38
+ if (ts.isObjectLiteralExpression(firstArg)) {
39
+ const obj = firstArg
40
+
41
+ const queueName = getPropertyValue(obj, 'queueName') as string | null
42
+ const docs = (getPropertyValue(obj, 'docs') as PikkuDocs) || undefined
43
+ const tags = getPropertyTags(obj, 'Queue worker', queueName, logger)
44
+
45
+ // --- find the referenced function ---
46
+ const funcInitializer = getPropertyAssignmentInitializer(
47
+ obj,
48
+ 'func',
49
+ true,
50
+ checker
51
+ )
52
+ if (!funcInitializer) {
53
+ logger.critical(
54
+ ErrorCode.MISSING_FUNC,
55
+ `No valid 'func' property for queue processor '${queueName}'.`
56
+ )
57
+ return
58
+ }
59
+
60
+ const pikkuFuncName = extractFunctionName(
61
+ funcInitializer,
62
+ checker,
63
+ state.rootDir
64
+ ).pikkuFuncName
65
+
66
+ if (!queueName) {
67
+ logger.critical(
68
+ ErrorCode.MISSING_QUEUE_NAME,
69
+ `No 'queueName' provided for queue processor function '${pikkuFuncName}'.`
70
+ )
71
+ return
72
+ }
73
+
74
+ // --- resolve middleware ---
75
+ const middleware = resolveMiddleware(state, obj, tags, checker)
76
+
77
+ // --- track used functions/middleware for service aggregation ---
78
+ state.serviceAggregation.usedFunctions.add(pikkuFuncName)
79
+ extractWireNames(middleware).forEach((name) =>
80
+ state.serviceAggregation.usedMiddleware.add(name)
81
+ )
82
+
83
+ state.queueWorkers.files.add(node.getSourceFile().fileName)
84
+ state.queueWorkers.meta[queueName] = {
85
+ pikkuFuncName,
86
+ queueName,
87
+ docs,
88
+ tags,
89
+ middleware,
90
+ }
91
+ }
92
+ }
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript'
2
- import { InspectorState, InspectorLogger } from './types.js'
2
+ import { InspectorState, InspectorLogger } from '../types.js'
3
3
 
4
4
  /**
5
5
  * Scan for rpc.invoke() calls to track which functions are actually being invoked
@@ -1,19 +1,22 @@
1
1
  import * as ts from 'typescript'
2
- import { getPropertyValue } from './get-property-value.js'
3
- import { PikkuDocs, PikkuWiringTypes } from '@pikku/core'
4
- import { InspectorFilters, InspectorState, InspectorLogger } from './types.js'
5
2
  import {
6
- extractFunctionName,
7
- getPropertyAssignmentInitializer,
8
- matchesFilters,
9
- } from './utils.js'
3
+ getPropertyValue,
4
+ getPropertyTags,
5
+ } from '../utils/get-property-value.js'
6
+ import { PikkuDocs } from '@pikku/core'
7
+ import { AddWiring } from '../types.js'
8
+ import { extractFunctionName } from '../utils/extract-function-name.js'
9
+ import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
10
+ import { resolveMiddleware } from '../utils/middleware.js'
11
+ import { extractWireNames } from '../utils/post-process.js'
10
12
 
11
- export const addSchedule = (
12
- node: ts.Node,
13
- checker: ts.TypeChecker,
14
- state: InspectorState,
15
- filters: InspectorFilters,
16
- logger: InspectorLogger
13
+ import { ErrorCode } from '../error-codes.js'
14
+ export const addSchedule: AddWiring = (
15
+ logger,
16
+ node,
17
+ checker,
18
+ state,
19
+ options
17
20
  ) => {
18
21
  if (!ts.isCallExpression(node)) {
19
22
  return
@@ -38,7 +41,7 @@ export const addSchedule = (
38
41
  const nameValue = getPropertyValue(obj, 'name') as string | null
39
42
  const scheduleValue = getPropertyValue(obj, 'schedule') as string | null
40
43
  const docs = (getPropertyValue(obj, 'docs') as PikkuDocs) || undefined
41
- const tags = (getPropertyValue(obj, 'tags') as string[]) || undefined
44
+ const tags = getPropertyTags(obj, 'Scheduler', nameValue, logger)
42
45
 
43
46
  const funcInitializer = getPropertyAssignmentInitializer(
44
47
  obj,
@@ -47,33 +50,31 @@ export const addSchedule = (
47
50
  checker
48
51
  )
49
52
  if (!funcInitializer) {
50
- console.error(
51
- `• No valid 'func' property for scheduled task '${nameValue}'.`
53
+ logger.critical(
54
+ ErrorCode.MISSING_FUNC,
55
+ `No valid 'func' property for scheduled task '${nameValue}'.`
52
56
  )
53
57
  return
54
58
  }
55
59
 
56
60
  const pikkuFuncName = extractFunctionName(
57
61
  funcInitializer,
58
- checker
62
+ checker,
63
+ state.rootDir
59
64
  ).pikkuFuncName
60
65
 
61
66
  if (!nameValue || !scheduleValue) {
62
67
  return
63
68
  }
64
69
 
65
- const filePath = node.getSourceFile().fileName
70
+ // --- resolve middleware ---
71
+ const middleware = resolveMiddleware(state, obj, tags, checker)
66
72
 
67
- if (
68
- !matchesFilters(
69
- filters,
70
- { tags },
71
- { type: PikkuWiringTypes.scheduler, name: nameValue, filePath },
72
- logger
73
- )
74
- ) {
75
- return
76
- }
73
+ // --- track used functions/middleware for service aggregation ---
74
+ state.serviceAggregation.usedFunctions.add(pikkuFuncName)
75
+ extractWireNames(middleware).forEach((name) =>
76
+ state.serviceAggregation.usedMiddleware.add(name)
77
+ )
77
78
 
78
79
  state.scheduledTasks.files.add(node.getSourceFile().fileName)
79
80
  state.scheduledTasks.meta[nameValue] = {
@@ -82,6 +83,7 @@ export const addSchedule = (
82
83
  schedule: scheduleValue,
83
84
  docs,
84
85
  tags,
86
+ middleware,
85
87
  }
86
88
  }
87
89
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Error code system for Pikku CLI and Inspector
3
+ *
4
+ * Each error has a unique code and links to documentation at pikku.dev
5
+ *
6
+ * Error codes use random 3-digit numbers to avoid implying a sequential order.
7
+ * Each code links to detailed documentation and troubleshooting steps.
8
+ */
9
+
10
+ export enum ErrorCode {
11
+ // Validation errors
12
+ MISSING_NAME = 'PKU111',
13
+ MISSING_DESCRIPTION = 'PKU123',
14
+ MISSING_URI = 'PKU220',
15
+ MISSING_FUNC = 'PKU236',
16
+ INVALID_TAGS_TYPE = 'PKU247',
17
+ INVALID_HANDLER = 'PKU300',
18
+ MISSING_TITLE = 'PKU370',
19
+ MISSING_QUEUE_NAME = 'PKU384',
20
+ MISSING_CHANNEL_NAME = 'PKU400',
21
+ CLI_CLIENTSIDE_RENDERER_HAS_SERVICES = 'PKU672',
22
+
23
+ // Configuration errors
24
+ CONFIG_TYPE_NOT_FOUND = 'PKU426',
25
+ CONFIG_TYPE_UNDEFINED = 'PKU427',
26
+ SCHEMA_NO_ROOT = 'PKU431',
27
+ SCHEMA_GENERATION_ERROR = 'PKU456',
28
+ SCHEMA_LOAD_ERROR = 'PKU488',
29
+
30
+ // Function errors
31
+ FUNCTION_METADATA_NOT_FOUND = 'PKU559',
32
+ HANDLER_NOT_RESOLVED = 'PKU568',
33
+
34
+ // Middleware/Permission errors
35
+ MIDDLEWARE_HANDLER_INVALID = 'PKU685',
36
+ MIDDLEWARE_TAG_INVALID = 'PKU715',
37
+ MIDDLEWARE_EMPTY_ARRAY = 'PKU736',
38
+ MIDDLEWARE_PATTERN_INVALID = 'PKU787',
39
+ PERMISSION_HANDLER_INVALID = 'PKU835',
40
+ PERMISSION_TAG_INVALID = 'PKU836',
41
+ PERMISSION_EMPTY_ARRAY = 'PKU937',
42
+ PERMISSION_PATTERN_INVALID = 'PKU975',
43
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,16 @@
1
1
  export { inspect } from './inspector.js'
2
+ export { getFilesAndMethods } from './utils/get-files-and-methods.js'
2
3
  export type { TypesMap } from './types-map.js'
3
4
  export type * from './types.js'
4
5
  export type { InspectorState } from './types.js'
6
+ export type {
7
+ FilesAndMethods,
8
+ FilesAndMethodsErrors,
9
+ } from './utils/get-files-and-methods.js'
10
+ export { ErrorCode } from './error-codes.js'
11
+ export {
12
+ serializeInspectorState,
13
+ deserializeInspectorState,
14
+ } from './utils/serialize-inspector-state.js'
15
+ export type { SerializableInspectorState } from './utils/serialize-inspector-state.js'
16
+ export { filterInspectorState } from './utils/filter-inspector-state.js'