@pikku/inspector 0.11.0 → 0.11.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 (77) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/add/add-channel.js +11 -10
  3. package/dist/add/add-file-with-factory.js +10 -10
  4. package/dist/add/add-functions.js +57 -43
  5. package/dist/add/add-http-route.js +5 -4
  6. package/dist/add/add-mcp-prompt.js +6 -5
  7. package/dist/add/add-mcp-resource.js +6 -5
  8. package/dist/add/add-mcp-tool.js +6 -5
  9. package/dist/add/add-middleware.js +1 -1
  10. package/dist/add/add-permission.js +1 -1
  11. package/dist/add/add-queue-worker.js +6 -5
  12. package/dist/add/add-schedule.js +5 -4
  13. package/dist/add/add-workflow.d.ts +1 -1
  14. package/dist/add/add-workflow.js +92 -66
  15. package/dist/error-codes.d.ts +1 -0
  16. package/dist/error-codes.js +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +1 -0
  19. package/dist/inspector.js +10 -6
  20. package/dist/types.d.ts +21 -8
  21. package/dist/utils/extract-function-node.d.ts +10 -0
  22. package/dist/utils/extract-function-node.js +38 -0
  23. package/dist/utils/extract-node-value.d.ts +8 -0
  24. package/dist/utils/extract-node-value.js +24 -0
  25. package/dist/utils/extract-service-metadata.d.ts +19 -0
  26. package/dist/utils/extract-service-metadata.js +244 -0
  27. package/dist/utils/get-files-and-methods.d.ts +3 -3
  28. package/dist/utils/get-files-and-methods.js +3 -3
  29. package/dist/utils/get-property-value.d.ts +13 -6
  30. package/dist/utils/get-property-value.js +51 -43
  31. package/dist/utils/post-process.d.ts +9 -0
  32. package/dist/utils/post-process.js +30 -3
  33. package/dist/utils/serialize-inspector-state.d.ts +18 -5
  34. package/dist/utils/serialize-inspector-state.js +12 -10
  35. package/dist/utils/write-service-metadata.d.ts +13 -0
  36. package/dist/utils/write-service-metadata.js +37 -0
  37. package/dist/visit.js +2 -2
  38. package/dist/workflow/extract-simple-workflow.d.ts +15 -0
  39. package/dist/workflow/extract-simple-workflow.js +803 -0
  40. package/dist/workflow/patterns.d.ts +39 -0
  41. package/dist/workflow/patterns.js +138 -0
  42. package/dist/workflow/validation.d.ts +28 -0
  43. package/dist/workflow/validation.js +124 -0
  44. package/package.json +4 -4
  45. package/src/add/add-channel.ts +37 -17
  46. package/src/add/add-file-with-factory.ts +10 -10
  47. package/src/add/add-functions.ts +72 -56
  48. package/src/add/add-http-route.ts +10 -5
  49. package/src/add/add-mcp-prompt.ts +11 -7
  50. package/src/add/add-mcp-resource.ts +11 -7
  51. package/src/add/add-mcp-tool.ts +11 -7
  52. package/src/add/add-middleware.ts +1 -1
  53. package/src/add/add-permission.ts +1 -1
  54. package/src/add/add-queue-worker.ts +11 -12
  55. package/src/add/add-schedule.ts +10 -5
  56. package/src/add/add-workflow.ts +120 -110
  57. package/src/error-codes.ts +1 -0
  58. package/src/index.ts +2 -0
  59. package/src/inspector.ts +16 -6
  60. package/src/types.ts +18 -8
  61. package/src/utils/extract-function-node.ts +58 -0
  62. package/src/utils/extract-node-value.ts +31 -0
  63. package/src/utils/extract-service-metadata.ts +353 -0
  64. package/src/utils/filter-inspector-state.test.ts +3 -3
  65. package/src/utils/filter-utils.test.ts +45 -51
  66. package/src/utils/get-files-and-methods.ts +11 -11
  67. package/src/utils/get-property-value.ts +60 -53
  68. package/src/utils/permissions.test.ts +3 -3
  69. package/src/utils/post-process.ts +56 -3
  70. package/src/utils/serialize-inspector-state.ts +28 -18
  71. package/src/utils/test-data/inspector-state.json +9 -9
  72. package/src/utils/write-service-metadata.ts +51 -0
  73. package/src/visit.ts +3 -3
  74. package/src/workflow/extract-simple-workflow.ts +1035 -0
  75. package/src/workflow/patterns.ts +182 -0
  76. package/src/workflow/validation.ts +153 -0
  77. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,353 @@
1
+ import * as ts from 'typescript'
2
+ import * as path from 'path'
3
+ import * as fs from 'fs'
4
+
5
+ export interface ServiceMetadata {
6
+ name: string
7
+ summary: string
8
+ description: string
9
+ package: string
10
+ path: string
11
+ version: string
12
+ interface: string
13
+ expandedProperties: Record<string, string>
14
+ }
15
+
16
+ /**
17
+ * Extract JSDoc comment information from a TypeScript node
18
+ */
19
+ function extractJSDoc(node: ts.Node): { summary: string; description: string } {
20
+ const jsDocTags = ts.getJSDocTags(node)
21
+ const jsDocComments = ts.getJSDocCommentsAndTags(node)
22
+
23
+ let summary = ''
24
+ let description = ''
25
+
26
+ const summaryTag = jsDocTags.find((tag) => tag.tagName.text === 'summary')
27
+ if (summaryTag && summaryTag.comment) {
28
+ summary =
29
+ typeof summaryTag.comment === 'string'
30
+ ? summaryTag.comment
31
+ : summaryTag.comment.map((c) => c.text).join('')
32
+ }
33
+
34
+ for (const comment of jsDocComments) {
35
+ if (ts.isJSDoc(comment) && comment.comment) {
36
+ const commentText =
37
+ typeof comment.comment === 'string'
38
+ ? comment.comment
39
+ : comment.comment.map((c) => c.text).join('')
40
+
41
+ if (!summary && commentText) {
42
+ const lines = commentText
43
+ .split('\n')
44
+ .map((l) => l.trim())
45
+ .filter(Boolean)
46
+ if (lines.length > 0) {
47
+ summary = lines[0]
48
+ if (lines.length > 1) {
49
+ description = lines.slice(1).join('\n').trim()
50
+ }
51
+ }
52
+ } else {
53
+ description = commentText
54
+ }
55
+ break
56
+ }
57
+ }
58
+
59
+ return { summary, description }
60
+ }
61
+
62
+ /**
63
+ * Serialize a TypeScript type to a string representation
64
+ */
65
+ function serializeTypeToString(
66
+ node: ts.Node,
67
+ sourceFile: ts.SourceFile,
68
+ checker: ts.TypeChecker
69
+ ): string {
70
+ const nodeSourceFile = node.getSourceFile()
71
+
72
+ if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
73
+ return node.getText(nodeSourceFile)
74
+ }
75
+
76
+ if (ts.isClassDeclaration(node)) {
77
+ return serializePublicClassMembers(node, nodeSourceFile, checker)
78
+ }
79
+
80
+ const type = checker.getTypeAtLocation(node)
81
+ return checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation)
82
+ }
83
+
84
+ /**
85
+ * Extract public members from a class and serialize them
86
+ */
87
+ function serializePublicClassMembers(
88
+ classNode: ts.ClassDeclaration,
89
+ sourceFile: ts.SourceFile,
90
+ checker: ts.TypeChecker
91
+ ): string {
92
+ const className = classNode.name?.text || 'UnnamedClass'
93
+ const publicMembers: string[] = []
94
+
95
+ for (const member of classNode.members) {
96
+ const modifiers = ts.canHaveModifiers(member)
97
+ ? ts.getModifiers(member)
98
+ : undefined
99
+ const isPublic = !modifiers?.some(
100
+ (mod) =>
101
+ mod.kind === ts.SyntaxKind.PrivateKeyword ||
102
+ mod.kind === ts.SyntaxKind.ProtectedKeyword
103
+ )
104
+
105
+ if (!isPublic) continue
106
+
107
+ if (
108
+ ts.isMethodDeclaration(member) ||
109
+ ts.isPropertyDeclaration(member) ||
110
+ ts.isConstructorDeclaration(member)
111
+ ) {
112
+ const memberSignature = getMemberSignature(member, sourceFile, checker)
113
+ if (memberSignature) {
114
+ publicMembers.push(memberSignature)
115
+ }
116
+ }
117
+ }
118
+
119
+ return `class ${className} {\n ${publicMembers.join('\n ')}\n}`
120
+ }
121
+
122
+ /**
123
+ * Extract a clean signature for a class member (without implementation)
124
+ */
125
+ function getMemberSignature(
126
+ member: ts.ClassElement,
127
+ sourceFile: ts.SourceFile,
128
+ checker: ts.TypeChecker
129
+ ): string | null {
130
+ if (ts.isPropertyDeclaration(member)) {
131
+ const name = member.name.getText(sourceFile)
132
+ const type = member.type ? member.type.getText(sourceFile) : 'any'
133
+ const optional = member.questionToken ? '?' : ''
134
+ return `${name}${optional}: ${type};`
135
+ }
136
+
137
+ if (ts.isMethodDeclaration(member)) {
138
+ const name = member.name.getText(sourceFile)
139
+ const typeParams = member.typeParameters
140
+ ? `<${member.typeParameters.map((tp) => tp.getText(sourceFile)).join(', ')}>`
141
+ : ''
142
+ const params = member.parameters
143
+ .map((p) => p.getText(sourceFile))
144
+ .join(', ')
145
+ const returnType = member.type ? member.type.getText(sourceFile) : 'void'
146
+ const optional = member.questionToken ? '?' : ''
147
+ return `${name}${optional}${typeParams}(${params}): ${returnType};`
148
+ }
149
+
150
+ if (ts.isConstructorDeclaration(member)) {
151
+ const params = member.parameters
152
+ .map((p) => p.getText(sourceFile))
153
+ .join(', ')
154
+ return `constructor(${params});`
155
+ }
156
+
157
+ return null
158
+ }
159
+
160
+ /**
161
+ * Find the nearest package.json and extract package name and version
162
+ */
163
+ function getPackageInfo(filePath: string): {
164
+ packageName: string
165
+ version: string
166
+ } {
167
+ let currentDir = path.dirname(filePath)
168
+ const root = path.parse(currentDir).root
169
+
170
+ while (currentDir !== root) {
171
+ const packageJsonPath = path.join(currentDir, 'package.json')
172
+ if (fs.existsSync(packageJsonPath)) {
173
+ try {
174
+ const packageJson = JSON.parse(
175
+ fs.readFileSync(packageJsonPath, 'utf-8')
176
+ )
177
+ return {
178
+ packageName: packageJson.name || 'unknown',
179
+ version: packageJson.version || '0.0.0',
180
+ }
181
+ } catch (err) {
182
+ // If we can't parse the package.json, continue searching
183
+ }
184
+ }
185
+ currentDir = path.dirname(currentDir)
186
+ }
187
+
188
+ return { packageName: 'unknown', version: '0.0.0' }
189
+ }
190
+
191
+ /**
192
+ * Expand a type to show all its properties including inherited ones
193
+ * Returns a Record mapping property names to their type strings
194
+ */
195
+ function expandInterfaceProperties(
196
+ type: ts.Type,
197
+ checker: ts.TypeChecker,
198
+ maxDepth: number = 2,
199
+ currentDepth: number = 0,
200
+ visited: Set<ts.Type> = new Set()
201
+ ): Record<string, string> {
202
+ const result: Record<string, string> = {}
203
+
204
+ if (visited.has(type) || currentDepth >= maxDepth) {
205
+ return result
206
+ }
207
+ visited.add(type)
208
+
209
+ const properties = type.getProperties()
210
+
211
+ for (const prop of properties) {
212
+ const propName = prop.getName()
213
+ const propDecl = prop.valueDeclaration || prop.declarations?.[0]
214
+
215
+ if (!propDecl) continue
216
+
217
+ try {
218
+ const propType = checker.getTypeOfSymbolAtLocation(prop, propDecl)
219
+ const isOptional = !!(prop.flags & ts.SymbolFlags.Optional)
220
+
221
+ let typeString = checker.typeToString(
222
+ propType,
223
+ propDecl,
224
+ ts.TypeFormatFlags.NoTruncation |
225
+ ts.TypeFormatFlags.UseFullyQualifiedType
226
+ )
227
+
228
+ if (isOptional && !typeString.includes('undefined')) {
229
+ typeString = `${typeString} | undefined`
230
+ }
231
+
232
+ result[propName] = typeString
233
+ } catch (err) {
234
+ result[propName] = 'any'
235
+ }
236
+ }
237
+
238
+ return result
239
+ }
240
+
241
+ /**
242
+ * Extract metadata for a service from its TypeScript declaration
243
+ */
244
+ export function extractServiceMetadata(
245
+ serviceName: string,
246
+ type: ts.Type,
247
+ checker: ts.TypeChecker,
248
+ rootDir: string
249
+ ): ServiceMetadata | null {
250
+ const property = type.getProperty(serviceName)
251
+ if (!property) {
252
+ return null
253
+ }
254
+
255
+ const declaration = property.valueDeclaration || property.declarations?.[0]
256
+ if (!declaration) {
257
+ return null
258
+ }
259
+
260
+ const sourceFile = declaration.getSourceFile()
261
+ const filePath = sourceFile.fileName
262
+
263
+ const serviceType = checker.getTypeOfSymbolAtLocation(property, declaration)
264
+ let typeDeclaration: ts.Node | null = null
265
+
266
+ if (serviceType.symbol) {
267
+ const typeDecl =
268
+ serviceType.symbol.valueDeclaration ||
269
+ serviceType.symbol.declarations?.[0]
270
+ if (
271
+ typeDecl &&
272
+ (ts.isInterfaceDeclaration(typeDecl) ||
273
+ ts.isClassDeclaration(typeDecl) ||
274
+ ts.isTypeAliasDeclaration(typeDecl))
275
+ ) {
276
+ typeDeclaration = typeDecl
277
+ }
278
+ }
279
+
280
+ let summary = ''
281
+ let description = ''
282
+
283
+ if (typeDeclaration) {
284
+ const jsDoc = extractJSDoc(typeDeclaration)
285
+ summary = jsDoc.summary
286
+ description = jsDoc.description
287
+ } else if (
288
+ ts.isPropertySignature(declaration) ||
289
+ ts.isPropertyDeclaration(declaration)
290
+ ) {
291
+ const jsDoc = extractJSDoc(declaration)
292
+ summary = jsDoc.summary
293
+ description = jsDoc.description
294
+ }
295
+
296
+ let interfaceString = ''
297
+ if (typeDeclaration) {
298
+ interfaceString = serializeTypeToString(
299
+ typeDeclaration,
300
+ sourceFile,
301
+ checker
302
+ )
303
+ } else {
304
+ interfaceString = checker.typeToString(
305
+ serviceType,
306
+ declaration,
307
+ ts.TypeFormatFlags.NoTruncation
308
+ )
309
+ }
310
+
311
+ const { packageName, version } = getPackageInfo(filePath)
312
+ const relativePath = path.relative(rootDir, filePath)
313
+ const expandedProperties = expandInterfaceProperties(serviceType, checker)
314
+
315
+ return {
316
+ name: serviceName,
317
+ summary,
318
+ description,
319
+ package: packageName,
320
+ path: relativePath,
321
+ version,
322
+ interface: interfaceString,
323
+ expandedProperties,
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Extract metadata for all services in a type
329
+ */
330
+ export function extractAllServiceMetadata(
331
+ servicesType: ts.Type,
332
+ checker: ts.TypeChecker,
333
+ rootDir: string
334
+ ): ServiceMetadata[] {
335
+ const metadata: ServiceMetadata[] = []
336
+ const serviceNames = servicesType
337
+ .getProperties()
338
+ .map((prop) => prop.getName())
339
+
340
+ for (const serviceName of serviceNames) {
341
+ const serviceMeta = extractServiceMetadata(
342
+ serviceName,
343
+ servicesType,
344
+ checker,
345
+ rootDir
346
+ )
347
+ if (serviceMeta) {
348
+ metadata.push(serviceMeta)
349
+ }
350
+ }
351
+
352
+ return metadata
353
+ }
@@ -32,12 +32,12 @@ function createMockInspectorState(): Omit<InspectorState, 'typesLookup'> {
32
32
  return {
33
33
  rootDir: '/test/project',
34
34
  singletonServicesTypeImportMap: new Map(),
35
- sessionServicesTypeImportMap: new Map(),
35
+ wireServicesTypeImportMap: new Map(),
36
36
  userSessionTypeImportMap: new Map(),
37
37
  configTypeImportMap: new Map(),
38
38
  singletonServicesFactories: new Map(),
39
- sessionServicesFactories: new Map(),
40
- sessionServicesMeta: new Map(),
39
+ wireServicesFactories: new Map(),
40
+ wireServicesMeta: new Map(),
41
41
  configFactories: new Map(),
42
42
  filesAndMethods: {},
43
43
  filesAndMethodsErrors: new Map(),