@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/dist/inspector.js CHANGED
@@ -2,6 +2,8 @@ import * as ts from 'typescript';
2
2
  import { visitSetup, visitRoutes } from './visit.js';
3
3
  import { TypesMap } from './types-map.js';
4
4
  import { getFilesAndMethods } from './utils/get-files-and-methods.js';
5
+ import { findCommonAncestor } from './utils/find-root-dir.js';
6
+ import { aggregateRequiredServices } from './utils/post-process.js';
5
7
  export const inspect = (logger, routeFiles, options = {}) => {
6
8
  const program = ts.createProgram(routeFiles, {
7
9
  target: ts.ScriptTarget.ESNext,
@@ -9,13 +11,17 @@ export const inspect = (logger, routeFiles, options = {}) => {
9
11
  });
10
12
  const checker = program.getTypeChecker();
11
13
  const sourceFiles = program.getSourceFiles();
14
+ // Infer root directory from source files
15
+ const rootDir = findCommonAncestor(routeFiles);
12
16
  const state = {
17
+ rootDir,
13
18
  singletonServicesTypeImportMap: new Map(),
14
19
  sessionServicesTypeImportMap: new Map(),
15
20
  userSessionTypeImportMap: new Map(),
16
21
  configTypeImportMap: new Map(),
17
22
  singletonServicesFactories: new Map(),
18
23
  sessionServicesFactories: new Map(),
24
+ sessionServicesMeta: new Map(),
19
25
  configFactories: new Map(),
20
26
  filesAndMethods: {},
21
27
  filesAndMethodsErrors: new Map(),
@@ -38,6 +44,7 @@ export const inspect = (logger, routeFiles, options = {}) => {
38
44
  },
39
45
  files: new Set(),
40
46
  routeMiddleware: new Map(),
47
+ routePermissions: new Map(),
41
48
  },
42
49
  channels: {
43
50
  files: new Set(),
@@ -65,7 +72,10 @@ export const inspect = (logger, routeFiles, options = {}) => {
65
72
  files: new Set(),
66
73
  },
67
74
  cli: {
68
- meta: {},
75
+ meta: {
76
+ programs: {},
77
+ renderers: {},
78
+ },
69
79
  files: new Set(),
70
80
  },
71
81
  middleware: {
@@ -74,6 +84,13 @@ export const inspect = (logger, routeFiles, options = {}) => {
74
84
  },
75
85
  permissions: {
76
86
  meta: {},
87
+ tagPermissions: new Map(),
88
+ },
89
+ serviceAggregation: {
90
+ requiredServices: new Set(),
91
+ usedFunctions: new Set(),
92
+ usedMiddleware: new Set(),
93
+ usedPermissions: new Set(),
77
94
  },
78
95
  };
79
96
  // First sweep: add all functions
@@ -88,5 +105,7 @@ export const inspect = (logger, routeFiles, options = {}) => {
88
105
  const { result, errors } = getFilesAndMethods(state, options.types);
89
106
  state.filesAndMethods = result;
90
107
  state.filesAndMethodsErrors = errors;
108
+ // Post-processing: Aggregate required services from wired functions/middleware/permissions
109
+ aggregateRequiredServices(state);
91
110
  return state;
92
111
  };
package/dist/types.d.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
  export type PathToNameAndType = Map<string, {
11
12
  variable: string;
12
13
  type: string | null;
@@ -25,11 +26,20 @@ export interface MiddlewareGroupMeta {
25
26
  middlewareCount: number;
26
27
  isFactory: boolean;
27
28
  }
29
+ export interface PermissionGroupMeta {
30
+ exportName: string | null;
31
+ sourceFile: string;
32
+ position: number;
33
+ services: FunctionServicesMeta;
34
+ permissionCount: number;
35
+ isFactory: boolean;
36
+ }
28
37
  export interface InspectorHTTPState {
29
38
  metaInputTypes: MetaInputTypes;
30
39
  meta: HTTPWiringsMeta;
31
40
  files: Set<string>;
32
41
  routeMiddleware: Map<string, MiddlewareGroupMeta>;
42
+ routePermissions: Map<string, PermissionGroupMeta>;
33
43
  }
34
44
  export interface InspectorFunctionState {
35
45
  typesMap: TypesMap;
@@ -50,6 +60,8 @@ export interface InspectorMiddlewareState {
50
60
  position: number;
51
61
  exportedName: string | null;
52
62
  factory?: boolean;
63
+ name?: string;
64
+ description?: string;
53
65
  }>;
54
66
  tagMiddleware: Map<string, MiddlewareGroupMeta>;
55
67
  }
@@ -59,12 +71,19 @@ export interface InspectorPermissionState {
59
71
  sourceFile: string;
60
72
  position: number;
61
73
  exportedName: string | null;
74
+ factory?: boolean;
75
+ name?: string;
76
+ description?: string;
62
77
  }>;
78
+ tagPermissions: Map<string, PermissionGroupMeta>;
63
79
  }
64
80
  export type InspectorFilters = {
81
+ names?: string[];
65
82
  tags?: string[];
66
83
  types?: string[];
67
84
  directories?: string[];
85
+ httpRoutes?: string[];
86
+ httpMethods?: string[];
68
87
  };
69
88
  export type InspectorOptions = Partial<{
70
89
  types: Partial<{
@@ -73,13 +92,14 @@ export type InspectorOptions = Partial<{
73
92
  singletonServicesFactoryType: string;
74
93
  sessionServicesFactoryType: string;
75
94
  }>;
76
- filters: InspectorFilters;
77
95
  }>;
78
96
  export interface InspectorLogger {
79
97
  info: (message: string) => void;
80
98
  error: (message: string) => void;
81
99
  warn: (message: string) => void;
82
100
  debug: (message: string) => void;
101
+ critical: (code: ErrorCode, message: string) => void;
102
+ hasCriticalErrors: () => boolean;
83
103
  }
84
104
  export type AddWiring = (logger: InspectorLogger, node: ts.Node, checker: ts.TypeChecker, state: InspectorState, options: InspectorOptions) => void;
85
105
  export interface InspectorFilesAndMethods {
@@ -127,12 +147,14 @@ export interface InspectorFilesAndMethods {
127
147
  };
128
148
  }
129
149
  export interface InspectorState {
150
+ rootDir: string;
130
151
  singletonServicesTypeImportMap: PathToNameAndType;
131
152
  sessionServicesTypeImportMap: PathToNameAndType;
132
153
  userSessionTypeImportMap: PathToNameAndType;
133
154
  configTypeImportMap: PathToNameAndType;
134
155
  singletonServicesFactories: PathToNameAndType;
135
156
  sessionServicesFactories: PathToNameAndType;
157
+ sessionServicesMeta: Map<string, string[]>;
136
158
  configFactories: PathToNameAndType;
137
159
  filesAndMethods: InspectorFilesAndMethods;
138
160
  filesAndMethodsErrors: Map<string, PathToNameAndType>;
@@ -173,4 +195,10 @@ export interface InspectorState {
173
195
  };
174
196
  middleware: InspectorMiddlewareState;
175
197
  permissions: InspectorPermissionState;
198
+ serviceAggregation: {
199
+ requiredServices: Set<string>;
200
+ usedFunctions: Set<string>;
201
+ usedMiddleware: Set<string>;
202
+ usedPermissions: Set<string>;
203
+ };
176
204
  }
@@ -0,0 +1,6 @@
1
+ import { InspectorState } from '../types.js';
2
+ /**
3
+ * Ensures that function metadata exists for a given pikkuFuncName.
4
+ * Creates stub metadata if it doesn't exist (useful for inline functions).
5
+ */
6
+ export declare function ensureFunctionMetadata(state: InspectorState, pikkuFuncName: string, fallbackName?: string): void;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Ensures that function metadata exists for a given pikkuFuncName.
3
+ * Creates stub metadata if it doesn't exist (useful for inline functions).
4
+ */
5
+ export function ensureFunctionMetadata(state, pikkuFuncName, fallbackName) {
6
+ if (!state.functions.meta[pikkuFuncName]) {
7
+ state.functions.meta[pikkuFuncName] = {
8
+ pikkuFuncName,
9
+ name: fallbackName || pikkuFuncName,
10
+ services: { optimized: false, services: [] },
11
+ inputSchemaName: null,
12
+ outputSchemaName: null,
13
+ inputs: [],
14
+ outputs: [],
15
+ middleware: undefined,
16
+ };
17
+ }
18
+ }
@@ -12,7 +12,7 @@ export type ExtractedFunctionName = {
12
12
  * but if it's an Identifier pointing to a function, resolve it back
13
13
  * to the function's declaration (so you get the true source location).
14
14
  */
15
- export declare function makeDeterministicAnonName(start: ts.Node, checker: ts.TypeChecker): string;
15
+ export declare function makeDeterministicAnonName(start: ts.Node, checker: ts.TypeChecker, rootDir: string): string;
16
16
  /**
17
17
  * Updated function to extract and prioritize function names correctly
18
18
  * This function follows the priority:
@@ -20,7 +20,7 @@ export declare function makeDeterministicAnonName(start: ts.Node, checker: ts.Ty
20
20
  * 2. Exported name
21
21
  * 3. Fallback to deterministic name
22
22
  */
23
- export declare function extractFunctionName(callExpr: ts.Node, checker: ts.TypeChecker): ExtractedFunctionName;
23
+ export declare function extractFunctionName(callExpr: ts.Node, checker: ts.TypeChecker, rootDir: string): ExtractedFunctionName;
24
24
  /**
25
25
  * Helper function to populate the 'name' field based on priority
26
26
  */
@@ -1,10 +1,11 @@
1
1
  import * as ts from 'typescript';
2
+ import { toRelativePath } from './find-root-dir.js';
2
3
  /**
3
4
  * Generate a deterministic "anonymous" name for any expression node,
4
5
  * but if it's an Identifier pointing to a function, resolve it back
5
6
  * to the function's declaration (so you get the true source location).
6
7
  */
7
- export function makeDeterministicAnonName(start, checker) {
8
+ export function makeDeterministicAnonName(start, checker, rootDir) {
8
9
  let node = start;
9
10
  let target = start;
10
11
  // Handle the case where we're starting with an identifier directly
@@ -27,7 +28,8 @@ export function makeDeterministicAnonName(start, checker) {
27
28
  target = decl.initializer;
28
29
  // Return early - we found the function directly
29
30
  const sf = target.getSourceFile();
30
- const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_');
31
+ const relativePath = toRelativePath(sf.fileName, rootDir);
32
+ const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_');
31
33
  const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
32
34
  return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
33
35
  }
@@ -62,7 +64,8 @@ export function makeDeterministicAnonName(start, checker) {
62
64
  target = decl.initializer;
63
65
  // Return early - we found the function directly
64
66
  const sf = target.getSourceFile();
65
- const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_');
67
+ const relativePath = toRelativePath(sf.fileName, rootDir);
68
+ const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_');
66
69
  const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
67
70
  return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
68
71
  }
@@ -72,7 +75,8 @@ export function makeDeterministicAnonName(start, checker) {
72
75
  target = decl;
73
76
  // Return early
74
77
  const sf = target.getSourceFile();
75
- const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_');
78
+ const relativePath = toRelativePath(sf.fileName, rootDir);
79
+ const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_');
76
80
  const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
77
81
  return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
78
82
  }
@@ -182,7 +186,8 @@ export function makeDeterministicAnonName(start, checker) {
182
186
  break;
183
187
  }
184
188
  const sf = target.getSourceFile();
185
- const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_');
189
+ const relativePath = toRelativePath(sf.fileName, rootDir);
190
+ const file = relativePath.replace(/[^A-Za-z0-9_]/g, '_');
186
191
  const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
187
192
  return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
188
193
  }
@@ -193,7 +198,7 @@ export function makeDeterministicAnonName(start, checker) {
193
198
  * 2. Exported name
194
199
  * 3. Fallback to deterministic name
195
200
  */
196
- export function extractFunctionName(callExpr, checker) {
201
+ export function extractFunctionName(callExpr, checker, rootDir) {
197
202
  const parent = callExpr.parent;
198
203
  // Initialize the result
199
204
  const result = {
@@ -230,7 +235,7 @@ export function extractFunctionName(callExpr, checker) {
230
235
  (ts.isArrowFunction(firstArg) ||
231
236
  ts.isFunctionExpression(firstArg))) {
232
237
  // Use the function directly for position calculation
233
- result.pikkuFuncName = makeDeterministicAnonName(firstArg, checker);
238
+ result.pikkuFuncName = makeDeterministicAnonName(firstArg, checker, rootDir);
234
239
  // Continue with name extraction
235
240
  if (ts.isIdentifier(parent.name)) {
236
241
  result.propertyName = parent.name.text;
@@ -532,7 +537,7 @@ export function extractFunctionName(callExpr, checker) {
532
537
  // Generate the deterministic function name based on the original call expression
533
538
  // (the config), not the resolved inner function. This ensures the metadata key
534
539
  // matches what will be looked up at runtime when referencing the config object.
535
- result.pikkuFuncName = makeDeterministicAnonName(originalCallExpr, checker);
540
+ result.pikkuFuncName = makeDeterministicAnonName(originalCallExpr, checker, rootDir);
536
541
  // Continue with regular name extraction for remaining cases
537
542
  // 1) const foo = pikkuFunc(...)
538
543
  if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
@@ -0,0 +1,6 @@
1
+ import { InspectorState, InspectorFilters, InspectorLogger } from '../types.js';
2
+ /**
3
+ * Filters inspector state based on provided filters
4
+ * This is applied post-inspection to support the inspect-once, filter-many pattern
5
+ */
6
+ export declare function filterInspectorState(state: InspectorState | Omit<InspectorState, 'typesLookup'>, filters: InspectorFilters, logger: InspectorLogger): typeof state;