@pikku/inspector 0.11.0 → 0.11.2
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.
- package/CHANGELOG.md +32 -2
- package/dist/add/add-channel.js +11 -10
- package/dist/add/add-file-with-factory.js +10 -10
- package/dist/add/add-forge-credential.d.ts +8 -0
- package/dist/add/add-forge-credential.js +77 -0
- package/dist/add/add-forge-node.d.ts +7 -0
- package/dist/add/add-forge-node.js +77 -0
- package/dist/add/add-functions.js +158 -51
- package/dist/add/add-http-route.js +28 -4
- package/dist/add/add-mcp-prompt.js +6 -5
- package/dist/add/add-mcp-resource.js +6 -5
- package/dist/add/add-mcp-tool.js +6 -5
- package/dist/add/add-middleware.js +1 -1
- package/dist/add/add-permission.js +1 -1
- package/dist/add/add-queue-worker.js +6 -5
- package/dist/add/add-rpc-invocations.d.ts +3 -0
- package/dist/add/add-rpc-invocations.js +51 -25
- package/dist/add/add-schedule.js +5 -4
- package/dist/add/add-workflow-graph.d.ts +6 -0
- package/dist/add/add-workflow-graph.js +659 -0
- package/dist/add/add-workflow.d.ts +1 -1
- package/dist/add/add-workflow.js +191 -69
- package/dist/error-codes.d.ts +3 -0
- package/dist/error-codes.js +3 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/dist/inspector.js +29 -9
- package/dist/types.d.ts +47 -8
- package/dist/utils/extract-function-name.js +7 -7
- package/dist/utils/extract-function-node.d.ts +10 -0
- package/dist/utils/extract-function-node.js +38 -0
- package/dist/utils/extract-node-value.d.ts +8 -0
- package/dist/utils/extract-node-value.js +24 -0
- package/dist/utils/extract-service-metadata.d.ts +19 -0
- package/dist/utils/extract-service-metadata.js +244 -0
- package/dist/utils/get-files-and-methods.d.ts +3 -3
- package/dist/utils/get-files-and-methods.js +3 -3
- package/dist/utils/get-property-value.d.ts +14 -6
- package/dist/utils/get-property-value.js +55 -43
- package/dist/utils/post-process.d.ts +9 -0
- package/dist/utils/post-process.js +30 -3
- package/dist/utils/serialize-inspector-state.d.ts +42 -6
- package/dist/utils/serialize-inspector-state.js +36 -10
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +898 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +17 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +1284 -0
- package/dist/utils/workflow/dsl/index.d.ts +7 -0
- package/dist/utils/workflow/dsl/index.js +7 -0
- package/dist/utils/workflow/dsl/patterns.d.ts +60 -0
- package/dist/utils/workflow/dsl/patterns.js +218 -0
- package/dist/utils/workflow/dsl/validation.d.ts +30 -0
- package/dist/utils/workflow/dsl/validation.js +142 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +316 -0
- package/dist/utils/workflow/graph/index.d.ts +6 -0
- package/dist/utils/workflow/graph/index.js +6 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +43 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +152 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +229 -0
- package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
- package/dist/utils/write-service-metadata.d.ts +13 -0
- package/dist/utils/write-service-metadata.js +37 -0
- package/dist/visit.js +8 -2
- package/package.json +16 -4
- package/src/add/add-channel.ts +37 -17
- package/src/add/add-file-with-factory.ts +10 -10
- package/src/add/add-forge-credential.ts +119 -0
- package/src/add/add-forge-node.ts +132 -0
- package/src/add/add-functions.ts +199 -69
- package/src/add/add-http-route.ts +34 -5
- package/src/add/add-mcp-prompt.ts +11 -7
- package/src/add/add-mcp-resource.ts +11 -7
- package/src/add/add-mcp-tool.ts +11 -7
- package/src/add/add-middleware.ts +1 -1
- package/src/add/add-permission.ts +1 -1
- package/src/add/add-queue-worker.ts +11 -12
- package/src/add/add-rpc-invocations.ts +61 -31
- package/src/add/add-schedule.ts +10 -5
- package/src/add/add-workflow-graph.ts +864 -0
- package/src/add/add-workflow.ts +212 -116
- package/src/error-codes.ts +3 -0
- package/src/index.ts +12 -0
- package/src/inspector.ts +36 -10
- package/src/types.ts +43 -9
- package/src/utils/extract-function-name.ts +7 -7
- package/src/utils/extract-function-node.ts +58 -0
- package/src/utils/extract-node-value.ts +31 -0
- package/src/utils/extract-service-metadata.ts +353 -0
- package/src/utils/filter-inspector-state.test.ts +3 -3
- package/src/utils/filter-utils.test.ts +45 -51
- package/src/utils/get-files-and-methods.ts +11 -11
- package/src/utils/get-property-value.ts +67 -53
- package/src/utils/permissions.test.ts +3 -3
- package/src/utils/post-process.ts +56 -3
- package/src/utils/serialize-inspector-state.ts +67 -19
- package/src/utils/test-data/inspector-state.json +9 -9
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1180 -0
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +1608 -0
- package/src/utils/workflow/dsl/index.ts +11 -0
- package/src/utils/workflow/dsl/patterns.ts +279 -0
- package/src/utils/workflow/dsl/validation.ts +180 -0
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +415 -0
- package/src/utils/workflow/graph/index.ts +6 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +223 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +280 -0
- package/src/utils/write-service-metadata.ts +51 -0
- package/src/visit.ts +9 -3
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -360,10 +360,10 @@ export function extractFunctionName(
|
|
|
360
360
|
if (
|
|
361
361
|
ts.isPropertyAssignment(prop) &&
|
|
362
362
|
ts.isIdentifier(prop.name) &&
|
|
363
|
-
prop.name.text === '
|
|
363
|
+
prop.name.text === 'override' &&
|
|
364
364
|
ts.isStringLiteral(prop.initializer)
|
|
365
365
|
) {
|
|
366
|
-
// Priority 1: Object with
|
|
366
|
+
// Priority 1: Object with override property
|
|
367
367
|
result.explicitName = prop.initializer.text
|
|
368
368
|
break
|
|
369
369
|
}
|
|
@@ -614,17 +614,17 @@ export function extractFunctionName(
|
|
|
614
614
|
// instead of the variable declaration position
|
|
615
615
|
originalCallExpr = decl.initializer
|
|
616
616
|
|
|
617
|
-
// Check for object with '
|
|
617
|
+
// Check for object with 'override' property in first argument
|
|
618
618
|
const firstArg = decl.initializer.arguments[0]
|
|
619
619
|
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
620
620
|
for (const prop of firstArg.properties) {
|
|
621
621
|
if (
|
|
622
622
|
ts.isPropertyAssignment(prop) &&
|
|
623
623
|
ts.isIdentifier(prop.name) &&
|
|
624
|
-
prop.name.text === '
|
|
624
|
+
prop.name.text === 'override' &&
|
|
625
625
|
ts.isStringLiteral(prop.initializer)
|
|
626
626
|
) {
|
|
627
|
-
// Priority 1: Object with
|
|
627
|
+
// Priority 1: Object with override property
|
|
628
628
|
result.explicitName = prop.initializer.text
|
|
629
629
|
break
|
|
630
630
|
}
|
|
@@ -713,7 +713,7 @@ export function extractFunctionName(
|
|
|
713
713
|
) {
|
|
714
714
|
result.propertyName = parent.name.text
|
|
715
715
|
}
|
|
716
|
-
// 3) Handle any remaining cases for pikkuFunc({
|
|
716
|
+
// 3) Handle any remaining cases for pikkuFunc({ override: '…', func: … })
|
|
717
717
|
else if (ts.isCallExpression(originalCallExpr)) {
|
|
718
718
|
const firstArg = originalCallExpr.arguments[0]
|
|
719
719
|
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
@@ -721,7 +721,7 @@ export function extractFunctionName(
|
|
|
721
721
|
if (
|
|
722
722
|
ts.isPropertyAssignment(prop) &&
|
|
723
723
|
ts.isIdentifier(prop.name) &&
|
|
724
|
-
prop.name.text === '
|
|
724
|
+
prop.name.text === 'override' &&
|
|
725
725
|
ts.isStringLiteral(prop.initializer) &&
|
|
726
726
|
!result.explicitName // Only set if not already set
|
|
727
727
|
) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import {
|
|
3
|
+
getPropertyAssignmentInitializer,
|
|
4
|
+
resolveFunctionDeclaration,
|
|
5
|
+
} from './type-utils.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Extracts the actual function node from a pikkuFunc/pikkuWorkflowFunc call
|
|
9
|
+
* Handles both direct function form and config object form { func: ... }
|
|
10
|
+
*/
|
|
11
|
+
export function extractFunctionNode(
|
|
12
|
+
firstArg: ts.Expression,
|
|
13
|
+
checker: ts.TypeChecker
|
|
14
|
+
): {
|
|
15
|
+
funcNode: ts.Node
|
|
16
|
+
resolvedFunc: ts.Node | null
|
|
17
|
+
isDirectFunction: boolean
|
|
18
|
+
} {
|
|
19
|
+
let funcNode: ts.Node = firstArg
|
|
20
|
+
let isDirectFunction = true
|
|
21
|
+
|
|
22
|
+
// Check if first argument is a config object with 'func' property
|
|
23
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
24
|
+
isDirectFunction = false
|
|
25
|
+
const funcInitializer = getPropertyAssignmentInitializer(
|
|
26
|
+
firstArg,
|
|
27
|
+
'func',
|
|
28
|
+
true,
|
|
29
|
+
checker
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if (funcInitializer) {
|
|
33
|
+
funcNode = funcInitializer
|
|
34
|
+
} else {
|
|
35
|
+
// Return the original node if no func property found
|
|
36
|
+
// Caller should handle validation
|
|
37
|
+
funcNode = firstArg
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Resolve identifier to get the actual function node
|
|
42
|
+
if (ts.isIdentifier(funcNode)) {
|
|
43
|
+
const symbol = checker.getSymbolAtLocation(funcNode)
|
|
44
|
+
const decl = symbol?.valueDeclaration || symbol?.declarations?.[0]
|
|
45
|
+
if (decl && ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
46
|
+
funcNode = decl.initializer
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Resolve function declaration for deeper analysis
|
|
51
|
+
const resolvedFunc = resolveFunctionDeclaration(funcNode, checker)
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
funcNode,
|
|
55
|
+
resolvedFunc,
|
|
56
|
+
isDirectFunction,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -99,3 +99,34 @@ export function extractPropertyString(
|
|
|
99
99
|
}
|
|
100
100
|
return null
|
|
101
101
|
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Extract description from options object
|
|
105
|
+
*/
|
|
106
|
+
export function extractDescription(
|
|
107
|
+
optionsNode: ts.Node | undefined,
|
|
108
|
+
checker: ts.TypeChecker
|
|
109
|
+
): string | null {
|
|
110
|
+
if (!optionsNode || !ts.isObjectLiteralExpression(optionsNode)) {
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
return extractPropertyString(optionsNode, 'description', checker)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Extract duration value (number or string)
|
|
118
|
+
*/
|
|
119
|
+
export function extractDuration(
|
|
120
|
+
node: ts.Node,
|
|
121
|
+
checker: ts.TypeChecker
|
|
122
|
+
): string | number | null {
|
|
123
|
+
const numValue = extractNumberLiteral(node)
|
|
124
|
+
if (numValue !== null) {
|
|
125
|
+
return numValue
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
return extractStringLiteral(node, checker)
|
|
129
|
+
} catch {
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -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
|
-
|
|
35
|
+
wireServicesTypeImportMap: new Map(),
|
|
36
36
|
userSessionTypeImportMap: new Map(),
|
|
37
37
|
configTypeImportMap: new Map(),
|
|
38
38
|
singletonServicesFactories: new Map(),
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
wireServicesFactories: new Map(),
|
|
40
|
+
wireServicesMeta: new Map(),
|
|
41
41
|
configFactories: new Map(),
|
|
42
42
|
filesAndMethods: {},
|
|
43
43
|
filesAndMethodsErrors: new Map(),
|