@pikku/inspector 0.9.6-next.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/add/add-channel.d.ts +5 -1
  3. package/dist/add/add-channel.js +51 -32
  4. package/dist/add/add-cli.d.ts +4 -0
  5. package/dist/add/add-cli.js +128 -23
  6. package/dist/add/add-file-extends-core-type.js +3 -2
  7. package/dist/add/add-file-with-factory.d.ts +2 -2
  8. package/dist/add/add-file-with-factory.js +87 -1
  9. package/dist/add/add-functions.js +52 -5
  10. package/dist/add/add-http-route.js +19 -12
  11. package/dist/add/add-mcp-prompt.js +20 -13
  12. package/dist/add/add-mcp-resource.js +24 -14
  13. package/dist/add/add-mcp-tool.js +23 -13
  14. package/dist/add/add-middleware.js +51 -12
  15. package/dist/add/add-permission.d.ts +1 -2
  16. package/dist/add/add-permission.js +275 -19
  17. package/dist/add/add-queue-worker.js +10 -12
  18. package/dist/add/add-schedule.js +9 -10
  19. package/dist/error-codes.d.ts +35 -0
  20. package/dist/error-codes.js +40 -0
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.js +3 -0
  23. package/dist/inspector.js +20 -1
  24. package/dist/types.d.ts +31 -3
  25. package/dist/utils/ensure-function-metadata.d.ts +6 -0
  26. package/dist/utils/ensure-function-metadata.js +18 -0
  27. package/dist/utils/extract-function-name.d.ts +2 -2
  28. package/dist/utils/extract-function-name.js +13 -8
  29. package/dist/utils/filter-inspector-state.d.ts +6 -0
  30. package/dist/utils/filter-inspector-state.js +382 -0
  31. package/dist/utils/filter-utils.d.ts +10 -0
  32. package/dist/utils/filter-utils.js +66 -2
  33. package/dist/utils/find-root-dir.d.ts +23 -0
  34. package/dist/utils/find-root-dir.js +55 -0
  35. package/dist/utils/get-files-and-methods.d.ts +2 -1
  36. package/dist/utils/get-files-and-methods.js +4 -3
  37. package/dist/utils/get-property-value.d.ts +9 -0
  38. package/dist/utils/get-property-value.js +20 -0
  39. package/dist/utils/middleware.d.ts +1 -1
  40. package/dist/utils/middleware.js +7 -7
  41. package/dist/utils/permissions.d.ts +43 -0
  42. package/dist/utils/permissions.js +178 -0
  43. package/dist/utils/post-process.d.ts +16 -0
  44. package/dist/utils/post-process.js +132 -0
  45. package/dist/utils/serialize-inspector-state.d.ts +179 -0
  46. package/dist/utils/serialize-inspector-state.js +170 -0
  47. package/dist/visit.js +3 -2
  48. package/package.json +4 -4
  49. package/src/add/add-channel.ts +92 -40
  50. package/src/add/add-cli.ts +188 -29
  51. package/src/add/add-file-extends-core-type.ts +5 -2
  52. package/src/add/add-file-with-factory.ts +114 -2
  53. package/src/add/add-functions.ts +60 -5
  54. package/src/add/add-http-route.ts +46 -21
  55. package/src/add/add-mcp-prompt.ts +42 -21
  56. package/src/add/add-mcp-prompt.ts.tmp +0 -0
  57. package/src/add/add-mcp-resource.ts +50 -24
  58. package/src/add/add-mcp-resource.ts.tmp +0 -0
  59. package/src/add/add-mcp-tool.ts +48 -21
  60. package/src/add/add-middleware.ts +74 -15
  61. package/src/add/add-permission.ts +364 -22
  62. package/src/add/add-queue-worker.ts +22 -25
  63. package/src/add/add-schedule.ts +19 -20
  64. package/src/error-codes.ts +43 -0
  65. package/src/index.ts +7 -0
  66. package/src/inspector.ts +22 -1
  67. package/src/types.ts +38 -3
  68. package/src/utils/ensure-function-metadata.ts +24 -0
  69. package/src/utils/extract-function-name.ts +20 -8
  70. package/src/utils/filter-inspector-state.test.ts +1433 -0
  71. package/src/utils/filter-inspector-state.ts +526 -0
  72. package/src/utils/filter-utils.test.ts +350 -1
  73. package/src/utils/filter-utils.ts +82 -2
  74. package/src/utils/find-root-dir.ts +68 -0
  75. package/src/utils/get-files-and-methods.ts +10 -2
  76. package/src/utils/get-property-value.ts +27 -0
  77. package/src/utils/middleware.ts +14 -7
  78. package/src/utils/permissions.test.ts +327 -0
  79. package/src/utils/permissions.ts +262 -0
  80. package/src/utils/post-process.ts +178 -0
  81. package/src/utils/serialize-inspector-state.ts +375 -0
  82. package/src/utils/test-data/inspector-state.json +1680 -0
  83. package/src/visit.ts +4 -2
  84. package/tsconfig.tsbuildinfo +1 -1
package/src/inspector.ts CHANGED
@@ -3,6 +3,8 @@ import { visitSetup, visitRoutes } from './visit.js'
3
3
  import { TypesMap } from './types-map.js'
4
4
  import { InspectorState, InspectorLogger, InspectorOptions } from './types.js'
5
5
  import { getFilesAndMethods } from './utils/get-files-and-methods.js'
6
+ import { findCommonAncestor } from './utils/find-root-dir.js'
7
+ import { aggregateRequiredServices } from './utils/post-process.js'
6
8
 
7
9
  export const inspect = (
8
10
  logger: InspectorLogger,
@@ -16,13 +18,18 @@ export const inspect = (
16
18
  const checker = program.getTypeChecker()
17
19
  const sourceFiles = program.getSourceFiles()
18
20
 
21
+ // Infer root directory from source files
22
+ const rootDir = findCommonAncestor(routeFiles)
23
+
19
24
  const state: InspectorState = {
25
+ rootDir,
20
26
  singletonServicesTypeImportMap: new Map(),
21
27
  sessionServicesTypeImportMap: new Map(),
22
28
  userSessionTypeImportMap: new Map(),
23
29
  configTypeImportMap: new Map(),
24
30
  singletonServicesFactories: new Map(),
25
31
  sessionServicesFactories: new Map(),
32
+ sessionServicesMeta: new Map(),
26
33
  configFactories: new Map(),
27
34
  filesAndMethods: {},
28
35
  filesAndMethodsErrors: new Map(),
@@ -45,6 +52,7 @@ export const inspect = (
45
52
  },
46
53
  files: new Set(),
47
54
  routeMiddleware: new Map(),
55
+ routePermissions: new Map(),
48
56
  },
49
57
  channels: {
50
58
  files: new Set(),
@@ -72,7 +80,10 @@ export const inspect = (
72
80
  files: new Set(),
73
81
  },
74
82
  cli: {
75
- meta: {},
83
+ meta: {
84
+ programs: {},
85
+ renderers: {},
86
+ },
76
87
  files: new Set(),
77
88
  },
78
89
  middleware: {
@@ -81,6 +92,13 @@ export const inspect = (
81
92
  },
82
93
  permissions: {
83
94
  meta: {},
95
+ tagPermissions: new Map(),
96
+ },
97
+ serviceAggregation: {
98
+ requiredServices: new Set(),
99
+ usedFunctions: new Set(),
100
+ usedMiddleware: new Set(),
101
+ usedPermissions: new Set(),
84
102
  },
85
103
  }
86
104
 
@@ -103,5 +121,8 @@ export const inspect = (
103
121
  state.filesAndMethods = result
104
122
  state.filesAndMethodsErrors = errors
105
123
 
124
+ // Post-processing: Aggregate required services from wired functions/middleware/permissions
125
+ aggregateRequiredServices(state)
126
+
106
127
  return state
107
128
  }
package/src/types.ts CHANGED
@@ -3,10 +3,11 @@ import { ChannelsMeta } from '@pikku/core/channel'
3
3
  import { HTTPWiringsMeta } from '@pikku/core/http'
4
4
  import { ScheduledTasksMeta } from '@pikku/core/scheduler'
5
5
  import { queueWorkersMeta } from '@pikku/core/queue'
6
- import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core'
7
- import { CLIMeta } from '@pikku/core'
6
+ import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core/mcp'
7
+ import { CLIMeta } from '@pikku/core/cli'
8
8
  import { TypesMap } from './types-map.js'
9
9
  import { FunctionsMeta, FunctionServicesMeta } from '@pikku/core'
10
+ import { ErrorCode } from './error-codes.js'
10
11
 
11
12
  export type PathToNameAndType = Map<
12
13
  string,
@@ -31,6 +32,15 @@ export interface MiddlewareGroupMeta {
31
32
  isFactory: boolean // true if wrapped in () => add...()
32
33
  }
33
34
 
35
+ export interface PermissionGroupMeta {
36
+ exportName: string | null // null if not exported
37
+ sourceFile: string
38
+ position: number
39
+ services: FunctionServicesMeta
40
+ permissionCount: number
41
+ isFactory: boolean // true if wrapped in () => add...()
42
+ }
43
+
34
44
  export interface InspectorHTTPState {
35
45
  metaInputTypes: MetaInputTypes
36
46
  meta: HTTPWiringsMeta
@@ -39,6 +49,10 @@ export interface InspectorHTTPState {
39
49
  // Pattern '*' matches all routes (from addHTTPMiddleware('*', [...]))
40
50
  // Pattern '/api/*' matches specific routes (from addHTTPMiddleware('/api/*', [...]))
41
51
  routeMiddleware: Map<string, MiddlewareGroupMeta>
52
+ // HTTP permission calls tracking - route pattern -> group metadata
53
+ // Pattern '*' matches all routes (from addHTTPPermission('*', [...]))
54
+ // Pattern '/api/*' matches specific routes (from addHTTPPermission('/api/*', [...]))
55
+ routePermissions: Map<string, PermissionGroupMeta>
42
56
  }
43
57
 
44
58
  export interface InspectorFunctionState {
@@ -62,6 +76,8 @@ export interface InspectorMiddlewareState {
62
76
  position: number
63
77
  exportedName: string | null
64
78
  factory?: boolean // true if wrapped with pikkuMiddlewareFactory
79
+ name?: string // optional name from pikkuMiddleware({ name: '...' })
80
+ description?: string // optional description from pikkuMiddleware({ description: '...' })
65
81
  }
66
82
  >
67
83
  // Tag-based middleware calls tracking - tag -> group metadata
@@ -70,6 +86,7 @@ export interface InspectorMiddlewareState {
70
86
  }
71
87
 
72
88
  export interface InspectorPermissionState {
89
+ // Individual permission function metadata
73
90
  meta: Record<
74
91
  string,
75
92
  {
@@ -77,14 +94,23 @@ export interface InspectorPermissionState {
77
94
  sourceFile: string
78
95
  position: number
79
96
  exportedName: string | null
97
+ factory?: boolean // true if wrapped with pikkuPermissionFactory
98
+ name?: string // optional name from pikkuPermission({ name: '...' })
99
+ description?: string // optional description from pikkuPermission({ description: '...' })
80
100
  }
81
101
  >
102
+ // Tag-based permission calls tracking - tag -> group metadata
103
+ // e.g., export const adminPermissions = () => addPermission('admin', [...])
104
+ tagPermissions: Map<string, PermissionGroupMeta>
82
105
  }
83
106
 
84
107
  export type InspectorFilters = {
108
+ names?: string[] // Wildcard support: "email-*" matches "email-worker", "email-sender"
85
109
  tags?: string[]
86
110
  types?: string[]
87
111
  directories?: string[]
112
+ httpRoutes?: string[] // HTTP route patterns: "/api/*", "/webhooks/*"
113
+ httpMethods?: string[] // HTTP methods: "GET", "POST", "DELETE", etc.
88
114
  }
89
115
 
90
116
  export type InspectorOptions = Partial<{
@@ -94,7 +120,6 @@ export type InspectorOptions = Partial<{
94
120
  singletonServicesFactoryType: string
95
121
  sessionServicesFactoryType: string
96
122
  }>
97
- filters: InspectorFilters
98
123
  }>
99
124
 
100
125
  export interface InspectorLogger {
@@ -102,6 +127,8 @@ export interface InspectorLogger {
102
127
  error: (message: string) => void
103
128
  warn: (message: string) => void
104
129
  debug: (message: string) => void
130
+ critical: (code: ErrorCode, message: string) => void
131
+ hasCriticalErrors: () => boolean
105
132
  }
106
133
 
107
134
  export type AddWiring = (
@@ -157,12 +184,14 @@ export interface InspectorFilesAndMethods {
157
184
  }
158
185
 
159
186
  export interface InspectorState {
187
+ rootDir: string // Root directory inferred from source files
160
188
  singletonServicesTypeImportMap: PathToNameAndType
161
189
  sessionServicesTypeImportMap: PathToNameAndType
162
190
  userSessionTypeImportMap: PathToNameAndType
163
191
  configTypeImportMap: PathToNameAndType
164
192
  singletonServicesFactories: PathToNameAndType
165
193
  sessionServicesFactories: PathToNameAndType
194
+ sessionServicesMeta: Map<string, string[]> // variable name -> singleton services consumed
166
195
  configFactories: PathToNameAndType
167
196
  filesAndMethods: InspectorFilesAndMethods
168
197
  filesAndMethodsErrors: Map<string, PathToNameAndType>
@@ -197,4 +226,10 @@ export interface InspectorState {
197
226
  }
198
227
  middleware: InspectorMiddlewareState
199
228
  permissions: InspectorPermissionState
229
+ serviceAggregation: {
230
+ requiredServices: Set<string> // All services needed across the app
231
+ usedFunctions: Set<string> // Function names actually wired/exposed
232
+ usedMiddleware: Set<string> // Middleware names used by wired functions
233
+ usedPermissions: Set<string> // Permission names used by wired functions
234
+ }
200
235
  }
@@ -0,0 +1,24 @@
1
+ import { InspectorState } from '../types.js'
2
+
3
+ /**
4
+ * Ensures that function metadata exists for a given pikkuFuncName.
5
+ * Creates stub metadata if it doesn't exist (useful for inline functions).
6
+ */
7
+ export function ensureFunctionMetadata(
8
+ state: InspectorState,
9
+ pikkuFuncName: string,
10
+ fallbackName?: string
11
+ ): void {
12
+ if (!state.functions.meta[pikkuFuncName]) {
13
+ state.functions.meta[pikkuFuncName] = {
14
+ pikkuFuncName,
15
+ name: fallbackName || pikkuFuncName,
16
+ services: { optimized: false, services: [] },
17
+ inputSchemaName: null,
18
+ outputSchemaName: null,
19
+ inputs: [],
20
+ outputs: [],
21
+ middleware: undefined,
22
+ }
23
+ }
24
+ }
@@ -1,4 +1,5 @@
1
1
  import * as ts from 'typescript'
2
+ import { toRelativePath } from './find-root-dir.js'
2
3
 
3
4
  export type ExtractedFunctionName = {
4
5
  pikkuFuncName: string
@@ -16,7 +17,8 @@ export type ExtractedFunctionName = {
16
17
  */
17
18
  export function makeDeterministicAnonName(
18
19
  start: ts.Node,
19
- checker: ts.TypeChecker
20
+ checker: ts.TypeChecker,
21
+ rootDir: string
20
22
  ): string {
21
23
  let node: ts.Node = start
22
24
  let target: ts.Node = start
@@ -45,7 +47,8 @@ export function makeDeterministicAnonName(
45
47
  target = decl.initializer
46
48
  // Return early - we found the function directly
47
49
  const sf = target.getSourceFile()
48
- const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_')
50
+ const relativePath = toRelativePath(sf.fileName, rootDir)
51
+ const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_')
49
52
  const { line, character } = ts.getLineAndCharacterOfPosition(
50
53
  sf,
51
54
  target.getStart()
@@ -91,7 +94,8 @@ export function makeDeterministicAnonName(
91
94
  target = decl.initializer
92
95
  // Return early - we found the function directly
93
96
  const sf = target.getSourceFile()
94
- const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_')
97
+ const relativePath = toRelativePath(sf.fileName, rootDir)
98
+ const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_')
95
99
  const { line, character } = ts.getLineAndCharacterOfPosition(
96
100
  sf,
97
101
  target.getStart()
@@ -103,7 +107,8 @@ export function makeDeterministicAnonName(
103
107
  target = decl
104
108
  // Return early
105
109
  const sf = target.getSourceFile()
106
- const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_')
110
+ const relativePath = toRelativePath(sf.fileName, rootDir)
111
+ const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_')
107
112
  const { line, character } = ts.getLineAndCharacterOfPosition(
108
113
  sf,
109
114
  target.getStart()
@@ -238,7 +243,8 @@ export function makeDeterministicAnonName(
238
243
  }
239
244
 
240
245
  const sf = target.getSourceFile()
241
- const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_')
246
+ const relativePath = toRelativePath(sf.fileName, rootDir)
247
+ const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_')
242
248
  const { line, character } = ts.getLineAndCharacterOfPosition(
243
249
  sf,
244
250
  target.getStart()
@@ -255,7 +261,8 @@ export function makeDeterministicAnonName(
255
261
  */
256
262
  export function extractFunctionName(
257
263
  callExpr: ts.Node,
258
- checker: ts.TypeChecker
264
+ checker: ts.TypeChecker,
265
+ rootDir: string
259
266
  ): ExtractedFunctionName {
260
267
  const parent: any = callExpr.parent
261
268
 
@@ -304,7 +311,8 @@ export function extractFunctionName(
304
311
  // Use the function directly for position calculation
305
312
  result.pikkuFuncName = makeDeterministicAnonName(
306
313
  firstArg,
307
- checker
314
+ checker,
315
+ rootDir
308
316
  )
309
317
 
310
318
  // Continue with name extraction
@@ -678,7 +686,11 @@ export function extractFunctionName(
678
686
  // Generate the deterministic function name based on the original call expression
679
687
  // (the config), not the resolved inner function. This ensures the metadata key
680
688
  // matches what will be looked up at runtime when referencing the config object.
681
- result.pikkuFuncName = makeDeterministicAnonName(originalCallExpr, checker)
689
+ result.pikkuFuncName = makeDeterministicAnonName(
690
+ originalCallExpr,
691
+ checker,
692
+ rootDir
693
+ )
682
694
 
683
695
  // Continue with regular name extraction for remaining cases
684
696
  // 1) const foo = pikkuFunc(...)