@pikku/inspector 0.9.5 → 0.9.6-next.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.
- package/CHANGELOG.md +8 -0
- package/dist/{add-channel.d.ts → add/add-channel.d.ts} +2 -2
- package/dist/{add-channel.js → add/add-channel.js} +12 -5
- package/dist/add/add-cli.d.ts +5 -0
- package/dist/add/add-cli.js +461 -0
- package/dist/{add-file-extends-core-type.d.ts → add/add-file-extends-core-type.d.ts} +2 -2
- package/dist/{add-file-extends-core-type.js → add/add-file-extends-core-type.js} +17 -5
- package/dist/{add-file-with-config.d.ts → add/add-file-with-config.d.ts} +1 -1
- package/dist/{add-file-with-config.js → add/add-file-with-config.js} +1 -1
- package/dist/{add-file-with-factory.d.ts → add/add-file-with-factory.d.ts} +1 -1
- package/dist/{add-file-with-factory.js → add/add-file-with-factory.js} +4 -4
- package/dist/add/add-functions.d.ts +6 -0
- package/dist/{add-functions.js → add/add-functions.js} +25 -5
- package/dist/{add-http-route.d.ts → add/add-http-route.d.ts} +2 -3
- package/dist/{add-http-route.js → add/add-http-route.js} +10 -4
- package/dist/add/add-mcp-prompt.d.ts +2 -0
- package/dist/{add-mcp-prompt.js → add/add-mcp-prompt.js} +10 -4
- package/dist/add/add-mcp-resource.d.ts +2 -0
- package/dist/{add-mcp-resource.js → add/add-mcp-resource.js} +10 -4
- package/dist/add/add-mcp-tool.d.ts +2 -0
- package/dist/{add-mcp-tool.js → add/add-mcp-tool.js} +10 -4
- package/dist/add/add-middleware.d.ts +5 -0
- package/dist/add/add-middleware.js +251 -0
- package/dist/add/add-permission.d.ts +6 -0
- package/dist/{add-permission.js → add/add-permission.js} +4 -3
- package/dist/add/add-queue-worker.d.ts +2 -0
- package/dist/{add-queue-worker.js → add/add-queue-worker.js} +10 -4
- package/dist/{add-rpc-invocations.d.ts → add/add-rpc-invocations.d.ts} +1 -1
- package/dist/add/add-schedule.d.ts +2 -0
- package/dist/{add-schedule.js → add/add-schedule.js} +10 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/inspector.d.ts +2 -3
- package/dist/inspector.js +19 -8
- package/dist/types.d.ts +79 -0
- package/dist/{utils.d.ts → utils/extract-function-name.d.ts} +7 -15
- package/dist/{utils.js → utils/extract-function-name.js} +23 -142
- package/dist/utils/extract-services.d.ts +6 -0
- package/dist/utils/extract-services.js +29 -0
- package/dist/utils/filter-utils.d.ts +9 -0
- package/dist/utils/filter-utils.js +45 -0
- package/dist/utils/get-files-and-methods.d.ts +21 -0
- package/dist/utils/get-files-and-methods.js +60 -0
- package/dist/utils/middleware.d.ts +39 -0
- package/dist/utils/middleware.js +157 -0
- package/dist/utils/type-utils.d.ts +3 -0
- package/dist/utils/type-utils.js +50 -0
- package/dist/visit.d.ts +3 -3
- package/dist/visit.js +33 -30
- package/package.json +3 -4
- package/src/{add-channel.ts → add/add-channel.ts} +19 -19
- package/src/add/add-cli.ts +663 -0
- package/src/{add-file-extends-core-type.ts → add/add-file-extends-core-type.ts} +21 -6
- package/src/{add-file-with-config.ts → add/add-file-with-config.ts} +2 -2
- package/src/{add-file-with-factory.ts → add/add-file-with-factory.ts} +5 -5
- package/src/{add-functions.ts → add/add-functions.ts} +29 -14
- package/src/{add-http-route.ts → add/add-http-route.ts} +23 -14
- package/src/{add-mcp-prompt.ts → add/add-mcp-prompt.ts} +18 -15
- package/src/{add-mcp-resource.ts → add/add-mcp-resource.ts} +18 -15
- package/src/{add-mcp-tool.ts → add/add-mcp-tool.ts} +18 -15
- package/src/add/add-middleware.ts +326 -0
- package/src/{add-permission.ts → add/add-permission.ts} +4 -8
- package/src/{add-queue-worker.ts → add/add-queue-worker.ts} +17 -14
- package/src/{add-rpc-invocations.ts → add/add-rpc-invocations.ts} +1 -1
- package/src/{add-schedule.ts → add/add-schedule.ts} +17 -14
- package/src/index.ts +5 -0
- package/src/inspector.ts +20 -17
- package/src/types.ts +92 -0
- package/src/{utils.ts → utils/extract-function-name.ts} +25 -199
- package/src/utils/extract-services.ts +35 -0
- package/src/{utils.test.ts → utils/filter-utils.test.ts} +2 -2
- package/src/utils/filter-utils.ts +72 -0
- package/src/utils/get-files-and-methods.ts +143 -0
- package/src/utils/middleware.ts +234 -0
- package/src/utils/type-utils.ts +74 -0
- package/src/visit.ts +47 -33
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add-functions.d.ts +0 -7
- package/dist/add-mcp-prompt.d.ts +0 -3
- package/dist/add-mcp-resource.d.ts +0 -3
- package/dist/add-mcp-tool.d.ts +0 -3
- package/dist/add-middleware.d.ts +0 -7
- package/dist/add-middleware.js +0 -35
- package/dist/add-permission.d.ts +0 -7
- package/dist/add-queue-worker.d.ts +0 -3
- package/dist/add-schedule.d.ts +0 -3
- package/src/add-middleware.ts +0 -51
- /package/dist/{add-rpc-invocations.js → add/add-rpc-invocations.js} +0 -0
- /package/dist/{does-type-extend-core-type.d.ts → utils/does-type-extend-core-type.d.ts} +0 -0
- /package/dist/{does-type-extend-core-type.js → utils/does-type-extend-core-type.js} +0 -0
- /package/dist/{get-property-value.d.ts → utils/get-property-value.d.ts} +0 -0
- /package/dist/{get-property-value.js → utils/get-property-value.js} +0 -0
- /package/src/{does-type-extend-core-type.ts → utils/does-type-extend-core-type.ts} +0 -0
- /package/src/{get-property-value.ts → utils/get-property-value.ts} +0 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PathToNameAndType,
|
|
3
|
+
InspectorState,
|
|
4
|
+
InspectorOptions,
|
|
5
|
+
} from '../types.js'
|
|
6
|
+
|
|
7
|
+
interface Meta {
|
|
8
|
+
file: string
|
|
9
|
+
variable: string
|
|
10
|
+
type: string
|
|
11
|
+
typePath: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type FilesAndMethods = {
|
|
15
|
+
userSessionType: Meta
|
|
16
|
+
sessionServicesType: Meta
|
|
17
|
+
singletonServicesType: Meta
|
|
18
|
+
pikkuConfigFactory: Meta
|
|
19
|
+
singletonServicesFactory: Meta
|
|
20
|
+
sessionServicesFactory: Meta
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type FilesAndMethodsErrors = Map<string, PathToNameAndType>
|
|
24
|
+
|
|
25
|
+
const getMetaTypes = (
|
|
26
|
+
type: string,
|
|
27
|
+
map: PathToNameAndType,
|
|
28
|
+
desiredType?: string,
|
|
29
|
+
errors?: FilesAndMethodsErrors
|
|
30
|
+
): Meta | undefined => {
|
|
31
|
+
if (desiredType) {
|
|
32
|
+
for (const [file, meta] of map.entries()) {
|
|
33
|
+
for (const { type: entryType, variable, typePath } of meta) {
|
|
34
|
+
if (entryType === desiredType) {
|
|
35
|
+
if (entryType === null || typePath === null) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Unknown state due to metaType calculation: entryType or typePath is null for ${desiredType} in ${file}`
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
return { file, variable, type: entryType, typePath }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (errors) {
|
|
45
|
+
errors.set(`No ${desiredType} found that extends ${type}`, map)
|
|
46
|
+
}
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const totalValues = Array.from(map.values()).flat()
|
|
51
|
+
|
|
52
|
+
if (totalValues.length === 0) {
|
|
53
|
+
const helpMessage =
|
|
54
|
+
type === 'CoreConfig'
|
|
55
|
+
? `No ${type} found. Make sure you have exported a createConfig function in your codebase:\n\n` +
|
|
56
|
+
`export const createConfig: CreateConfig<Config> = async () => {\n` +
|
|
57
|
+
` return {}\n` +
|
|
58
|
+
`}\n\n` +
|
|
59
|
+
`Possible issues:\n` +
|
|
60
|
+
`- srcDirectories in pikku.config.json doesn't include the file with the createConfig method`
|
|
61
|
+
: `No ${type} found`
|
|
62
|
+
if (errors) {
|
|
63
|
+
errors.set(helpMessage, map)
|
|
64
|
+
}
|
|
65
|
+
} else if (totalValues.length > 1) {
|
|
66
|
+
if (errors) {
|
|
67
|
+
errors.set(`More than one ${type} found`, map)
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
const entry = Array.from(map.entries())[0]
|
|
71
|
+
if (entry) {
|
|
72
|
+
const [file, [{ type: entryType, variable, typePath }]] = entry
|
|
73
|
+
if (entryType === null || typePath === null) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Unknown state due to metaType calculation: entryType or typePath is null for ${type} in ${file}`
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
return { file, type: entryType, variable, typePath }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const getFilesAndMethods = (
|
|
86
|
+
{
|
|
87
|
+
singletonServicesTypeImportMap,
|
|
88
|
+
sessionServicesTypeImportMap,
|
|
89
|
+
userSessionTypeImportMap,
|
|
90
|
+
sessionServicesFactories,
|
|
91
|
+
singletonServicesFactories,
|
|
92
|
+
configFactories,
|
|
93
|
+
}: InspectorState,
|
|
94
|
+
{
|
|
95
|
+
configFileType,
|
|
96
|
+
userSessionType,
|
|
97
|
+
singletonServicesFactoryType,
|
|
98
|
+
sessionServicesFactoryType,
|
|
99
|
+
}: InspectorOptions['types'] = {}
|
|
100
|
+
): { result: Partial<FilesAndMethods>; errors: FilesAndMethodsErrors } => {
|
|
101
|
+
const errors: FilesAndMethodsErrors = new Map()
|
|
102
|
+
|
|
103
|
+
const result: Partial<FilesAndMethods> = {
|
|
104
|
+
userSessionType: getMetaTypes(
|
|
105
|
+
'CoreUserSession',
|
|
106
|
+
userSessionTypeImportMap,
|
|
107
|
+
userSessionType,
|
|
108
|
+
errors
|
|
109
|
+
),
|
|
110
|
+
singletonServicesType: getMetaTypes(
|
|
111
|
+
'CoreSingletonServices',
|
|
112
|
+
singletonServicesTypeImportMap,
|
|
113
|
+
undefined,
|
|
114
|
+
errors
|
|
115
|
+
),
|
|
116
|
+
sessionServicesType: getMetaTypes(
|
|
117
|
+
'CoreServices',
|
|
118
|
+
sessionServicesTypeImportMap,
|
|
119
|
+
undefined,
|
|
120
|
+
errors
|
|
121
|
+
),
|
|
122
|
+
pikkuConfigFactory: getMetaTypes(
|
|
123
|
+
'CoreConfig',
|
|
124
|
+
configFactories,
|
|
125
|
+
configFileType,
|
|
126
|
+
errors
|
|
127
|
+
),
|
|
128
|
+
singletonServicesFactory: getMetaTypes(
|
|
129
|
+
'CreateSingletonServices',
|
|
130
|
+
singletonServicesFactories,
|
|
131
|
+
singletonServicesFactoryType,
|
|
132
|
+
errors
|
|
133
|
+
),
|
|
134
|
+
sessionServicesFactory: getMetaTypes(
|
|
135
|
+
'CreateSessionServices',
|
|
136
|
+
sessionServicesFactories,
|
|
137
|
+
sessionServicesFactoryType,
|
|
138
|
+
errors
|
|
139
|
+
),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { result, errors }
|
|
143
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import { MiddlewareMetadata } from '@pikku/core'
|
|
3
|
+
import { extractFunctionName } from './extract-function-name.js'
|
|
4
|
+
import { InspectorState } from '../types.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Extract middleware pikkuFuncNames from an array literal expression
|
|
8
|
+
* Resolves each identifier to its pikkuFuncName using extractFunctionName
|
|
9
|
+
* Also handles call expressions (like logCommandInfoAndTime({...}))
|
|
10
|
+
*/
|
|
11
|
+
export function extractMiddlewarePikkuNames(
|
|
12
|
+
arrayNode: ts.Expression,
|
|
13
|
+
checker: ts.TypeChecker
|
|
14
|
+
): string[] {
|
|
15
|
+
if (!ts.isArrayLiteralExpression(arrayNode)) {
|
|
16
|
+
return []
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const names: string[] = []
|
|
20
|
+
for (const element of arrayNode.elements) {
|
|
21
|
+
if (ts.isIdentifier(element)) {
|
|
22
|
+
// Resolve the identifier to its pikkuFuncName
|
|
23
|
+
const { pikkuFuncName } = extractFunctionName(element, checker)
|
|
24
|
+
names.push(pikkuFuncName)
|
|
25
|
+
} else if (ts.isCallExpression(element)) {
|
|
26
|
+
// Handle call expressions like logCommandInfoAndTime({...})
|
|
27
|
+
// These create inline middleware, so we use the call expression itself as the name
|
|
28
|
+
const { pikkuFuncName } = extractFunctionName(element, checker)
|
|
29
|
+
names.push(pikkuFuncName)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return names
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get middleware array from an object literal expression property
|
|
37
|
+
* Returns the initializer node for the 'middleware' property if it exists
|
|
38
|
+
*/
|
|
39
|
+
export function getMiddlewareNode(
|
|
40
|
+
obj: ts.ObjectLiteralExpression
|
|
41
|
+
): ts.Expression | undefined {
|
|
42
|
+
const middlewareProp = obj.properties.find(
|
|
43
|
+
(p) =>
|
|
44
|
+
ts.isPropertyAssignment(p) &&
|
|
45
|
+
ts.isIdentifier(p.name) &&
|
|
46
|
+
p.name.text === 'middleware'
|
|
47
|
+
) as ts.PropertyAssignment | undefined
|
|
48
|
+
|
|
49
|
+
return middlewareProp?.initializer
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a route matches a pattern with wildcards
|
|
54
|
+
* Pattern can be exact match or use * as wildcard
|
|
55
|
+
* e.g., '/api/*' matches '/api/users', '/api/posts/123', etc.
|
|
56
|
+
*/
|
|
57
|
+
export function routeMatchesPattern(route: string, pattern: string): boolean {
|
|
58
|
+
if (route === pattern) return true
|
|
59
|
+
|
|
60
|
+
// Convert pattern to regex: replace * with .*
|
|
61
|
+
const regexPattern = pattern
|
|
62
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape regex special chars except *
|
|
63
|
+
.replace(/\*/g, '.*') // Replace * with .*
|
|
64
|
+
|
|
65
|
+
const regex = new RegExp(`^${regexPattern}$`)
|
|
66
|
+
return regex.test(route)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Resolve middleware for an HTTP wiring based on:
|
|
71
|
+
* 1. Global HTTP middleware (addd([...]))
|
|
72
|
+
* 2. Route-specific HTTP middleware (addHTTPMiddleware('/pattern', [...]))
|
|
73
|
+
* 3. Tag-based middleware (addMiddleware('tag', [...]))
|
|
74
|
+
* 4. Explicit wiring middleware (wireHTTP({ middleware: [...] }))
|
|
75
|
+
* Returns undefined if no middleware is found, otherwise returns array with at least one item
|
|
76
|
+
*/
|
|
77
|
+
export function resolveHTTPMiddleware(
|
|
78
|
+
state: InspectorState,
|
|
79
|
+
route: string,
|
|
80
|
+
tags: string[] | undefined,
|
|
81
|
+
explicitMiddlewareNode: ts.Expression | undefined,
|
|
82
|
+
checker: ts.TypeChecker
|
|
83
|
+
): MiddlewareMetadata[] | undefined {
|
|
84
|
+
const resolved: MiddlewareMetadata[] = []
|
|
85
|
+
|
|
86
|
+
// 1. HTTP route middleware groups (includes '*' for global)
|
|
87
|
+
for (const [pattern, _groupMeta] of state.http.routeMiddleware.entries()) {
|
|
88
|
+
if (routeMatchesPattern(route, pattern)) {
|
|
89
|
+
// Just reference the group by route pattern
|
|
90
|
+
resolved.push({
|
|
91
|
+
type: 'http',
|
|
92
|
+
route: pattern,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 2. Tag-based middleware groups
|
|
98
|
+
if (tags && tags.length > 0) {
|
|
99
|
+
for (const tag of tags) {
|
|
100
|
+
if (state.middleware.tagMiddleware.has(tag)) {
|
|
101
|
+
// Just reference the group by tag
|
|
102
|
+
resolved.push({
|
|
103
|
+
type: 'tag',
|
|
104
|
+
tag,
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 3. Explicit wire middleware (inline is OK here)
|
|
111
|
+
if (explicitMiddlewareNode) {
|
|
112
|
+
const middlewareNames = extractMiddlewarePikkuNames(
|
|
113
|
+
explicitMiddlewareNode,
|
|
114
|
+
checker
|
|
115
|
+
)
|
|
116
|
+
for (const name of middlewareNames) {
|
|
117
|
+
const meta = state.middleware.meta[name]
|
|
118
|
+
resolved.push({
|
|
119
|
+
type: 'wire',
|
|
120
|
+
name,
|
|
121
|
+
inline: meta?.exportedName === null,
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return resolved.length > 0 ? resolved : undefined
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Resolve tag-based and explicit middleware (common logic for wires and functions)
|
|
131
|
+
* 1. Tag-based middleware (addMiddleware('tag', [...]))
|
|
132
|
+
* 2. Explicit middleware (wireHTTP/pikkuFunc({ middleware: [...] }))
|
|
133
|
+
*/
|
|
134
|
+
function resolveTagAndExplicitMiddleware(
|
|
135
|
+
state: InspectorState,
|
|
136
|
+
tags: string[] | undefined,
|
|
137
|
+
explicitMiddlewareNode: ts.Expression | undefined,
|
|
138
|
+
checker: ts.TypeChecker
|
|
139
|
+
): MiddlewareMetadata[] {
|
|
140
|
+
const resolved: MiddlewareMetadata[] = []
|
|
141
|
+
|
|
142
|
+
// 1. Tag-based middleware groups
|
|
143
|
+
if (tags && tags.length > 0) {
|
|
144
|
+
for (const tag of tags) {
|
|
145
|
+
if (state.middleware.tagMiddleware.has(tag)) {
|
|
146
|
+
// Just reference the group by tag
|
|
147
|
+
resolved.push({
|
|
148
|
+
type: 'tag',
|
|
149
|
+
tag,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 2. Explicit middleware (inline is OK here - used directly in wire/function)
|
|
156
|
+
if (explicitMiddlewareNode) {
|
|
157
|
+
const middlewareNames = extractMiddlewarePikkuNames(
|
|
158
|
+
explicitMiddlewareNode,
|
|
159
|
+
checker
|
|
160
|
+
)
|
|
161
|
+
for (const name of middlewareNames) {
|
|
162
|
+
const meta = state.middleware.meta[name]
|
|
163
|
+
resolved.push({
|
|
164
|
+
type: 'wire',
|
|
165
|
+
name,
|
|
166
|
+
inline: meta?.exportedName === null,
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return resolved
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Resolve middleware for a function based on:
|
|
176
|
+
* 1. Tag-based middleware (addMiddleware('tag', [...]))
|
|
177
|
+
* 2. Explicit function middleware (pikkuFunc({ middleware: [...] }))
|
|
178
|
+
* Returns undefined if no middleware is found, otherwise returns array with at least one item
|
|
179
|
+
*/
|
|
180
|
+
function resolveFunctionMiddlewareInternal(
|
|
181
|
+
state: InspectorState,
|
|
182
|
+
tags: string[] | undefined,
|
|
183
|
+
explicitMiddlewareNode: ts.Expression | undefined,
|
|
184
|
+
checker: ts.TypeChecker
|
|
185
|
+
): MiddlewareMetadata[] | undefined {
|
|
186
|
+
const resolved = resolveTagAndExplicitMiddleware(
|
|
187
|
+
state,
|
|
188
|
+
tags,
|
|
189
|
+
explicitMiddlewareNode,
|
|
190
|
+
checker
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return resolved.length > 0 ? resolved : undefined
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Convenience wrapper: Extract middleware node from object and resolve
|
|
198
|
+
* Use this in add-* files for cleaner code
|
|
199
|
+
*/
|
|
200
|
+
export function resolveMiddleware(
|
|
201
|
+
state: InspectorState,
|
|
202
|
+
obj: ts.ObjectLiteralExpression,
|
|
203
|
+
tags: string[] | undefined,
|
|
204
|
+
checker: ts.TypeChecker
|
|
205
|
+
): MiddlewareMetadata[] | undefined {
|
|
206
|
+
const explicitMiddlewareNode = getMiddlewareNode(obj)
|
|
207
|
+
return resolveFunctionMiddlewareInternal(
|
|
208
|
+
state,
|
|
209
|
+
tags,
|
|
210
|
+
explicitMiddlewareNode,
|
|
211
|
+
checker
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Convenience wrapper for HTTP: Extract middleware and resolve with HTTP-specific logic
|
|
217
|
+
* Use this in add-http-route.ts for cleaner code
|
|
218
|
+
*/
|
|
219
|
+
export function resolveHTTPMiddlewareFromObject(
|
|
220
|
+
state: InspectorState,
|
|
221
|
+
route: string,
|
|
222
|
+
obj: ts.ObjectLiteralExpression,
|
|
223
|
+
tags: string[] | undefined,
|
|
224
|
+
checker: ts.TypeChecker
|
|
225
|
+
): MiddlewareMetadata[] | undefined {
|
|
226
|
+
const explicitMiddlewareNode = getMiddlewareNode(obj)
|
|
227
|
+
return resolveHTTPMiddleware(
|
|
228
|
+
state,
|
|
229
|
+
route,
|
|
230
|
+
tags,
|
|
231
|
+
explicitMiddlewareNode,
|
|
232
|
+
checker
|
|
233
|
+
)
|
|
234
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
export const extractTypeKeys = (type: ts.Type): string[] => {
|
|
4
|
+
return type.getProperties().map((symbol) => symbol.getName())
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function getPropertyAssignmentInitializer(
|
|
8
|
+
obj: ts.ObjectLiteralExpression,
|
|
9
|
+
propName: string,
|
|
10
|
+
followShorthand = false,
|
|
11
|
+
checker?: ts.TypeChecker
|
|
12
|
+
): ts.Expression | undefined {
|
|
13
|
+
for (const prop of obj.properties) {
|
|
14
|
+
// ① foo: () => {}
|
|
15
|
+
if (
|
|
16
|
+
ts.isPropertyAssignment(prop) &&
|
|
17
|
+
ts.isIdentifier(prop.name) &&
|
|
18
|
+
prop.name.text === propName
|
|
19
|
+
) {
|
|
20
|
+
return prop.initializer
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ② foo() { … }
|
|
24
|
+
if (
|
|
25
|
+
ts.isMethodDeclaration(prop) &&
|
|
26
|
+
ts.isIdentifier(prop.name) &&
|
|
27
|
+
prop.name.text === propName
|
|
28
|
+
) {
|
|
29
|
+
return prop.name // the method node *is* the function
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ③ { foo } (shorthand)
|
|
33
|
+
if (
|
|
34
|
+
followShorthand &&
|
|
35
|
+
ts.isShorthandPropertyAssignment(prop) &&
|
|
36
|
+
prop.name.text === propName
|
|
37
|
+
) {
|
|
38
|
+
if (!checker) return prop.name // best effort without a checker
|
|
39
|
+
|
|
40
|
+
let sym = checker.getSymbolAtLocation(prop.name)
|
|
41
|
+
if (sym && sym.flags & ts.SymbolFlags.Alias) {
|
|
42
|
+
sym = checker.getAliasedSymbol(sym)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const decl = sym?.declarations?.[0]
|
|
46
|
+
|
|
47
|
+
// const foo = () => {}
|
|
48
|
+
if (
|
|
49
|
+
decl &&
|
|
50
|
+
ts.isVariableDeclaration(decl) &&
|
|
51
|
+
decl.initializer &&
|
|
52
|
+
(ts.isArrowFunction(decl.initializer) ||
|
|
53
|
+
ts.isFunctionExpression(decl.initializer))
|
|
54
|
+
) {
|
|
55
|
+
return decl.initializer
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// function foo() {}
|
|
59
|
+
if (
|
|
60
|
+
decl &&
|
|
61
|
+
(ts.isFunctionDeclaration(decl) ||
|
|
62
|
+
ts.isArrowFunction(decl) ||
|
|
63
|
+
ts.isFunctionExpression(decl))
|
|
64
|
+
) {
|
|
65
|
+
return decl as ts.Expression
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// fallback – just give back the identifier
|
|
69
|
+
return prop.name
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return undefined
|
|
74
|
+
}
|
package/src/visit.ts
CHANGED
|
@@ -1,45 +1,57 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import { addFileWithFactory } from './add-file-with-factory.js'
|
|
3
|
-
import { addFileExtendsCoreType } from './add-file-extends-core-type.js'
|
|
4
|
-
import { addHTTPRoute } from './add-http-route.js'
|
|
5
|
-
import { addSchedule } from './add-schedule.js'
|
|
6
|
-
import { addQueueWorker } from './add-queue-worker.js'
|
|
7
|
-
import { addMCPResource } from './add-mcp-resource.js'
|
|
8
|
-
import { addMCPTool } from './add-mcp-tool.js'
|
|
9
|
-
import { addMCPPrompt } from './add-mcp-prompt.js'
|
|
10
|
-
import {
|
|
11
|
-
import { addFunctions } from './add-functions.js'
|
|
12
|
-
import { addChannel } from './add-channel.js'
|
|
13
|
-
import { addRPCInvocations } from './add-rpc-invocations.js'
|
|
14
|
-
import { addMiddleware } from './add-middleware.js'
|
|
15
|
-
import { addPermission } from './add-permission.js'
|
|
2
|
+
import { addFileWithFactory } from './add/add-file-with-factory.js'
|
|
3
|
+
import { addFileExtendsCoreType } from './add/add-file-extends-core-type.js'
|
|
4
|
+
import { addHTTPRoute } from './add/add-http-route.js'
|
|
5
|
+
import { addSchedule } from './add/add-schedule.js'
|
|
6
|
+
import { addQueueWorker } from './add/add-queue-worker.js'
|
|
7
|
+
import { addMCPResource } from './add/add-mcp-resource.js'
|
|
8
|
+
import { addMCPTool } from './add/add-mcp-tool.js'
|
|
9
|
+
import { addMCPPrompt } from './add/add-mcp-prompt.js'
|
|
10
|
+
import { InspectorState, InspectorLogger, InspectorOptions } from './types.js'
|
|
11
|
+
import { addFunctions } from './add/add-functions.js'
|
|
12
|
+
import { addChannel } from './add/add-channel.js'
|
|
13
|
+
import { addRPCInvocations } from './add/add-rpc-invocations.js'
|
|
14
|
+
import { addMiddleware } from './add/add-middleware.js'
|
|
15
|
+
import { addPermission } from './add/add-permission.js'
|
|
16
|
+
import { addCLI } from './add/add-cli.js'
|
|
16
17
|
|
|
17
18
|
export const visitSetup = (
|
|
19
|
+
logger: InspectorLogger,
|
|
18
20
|
checker: ts.TypeChecker,
|
|
19
21
|
node: ts.Node,
|
|
20
22
|
state: InspectorState,
|
|
21
|
-
|
|
22
|
-
logger: InspectorLogger
|
|
23
|
+
options: InspectorOptions
|
|
23
24
|
) => {
|
|
24
25
|
addFileExtendsCoreType(
|
|
25
26
|
node,
|
|
26
27
|
checker,
|
|
27
28
|
state.singletonServicesTypeImportMap,
|
|
28
|
-
'CoreSingletonServices'
|
|
29
|
+
'CoreSingletonServices',
|
|
30
|
+
state
|
|
29
31
|
)
|
|
30
32
|
|
|
31
33
|
addFileExtendsCoreType(
|
|
32
34
|
node,
|
|
33
35
|
checker,
|
|
34
36
|
state.sessionServicesTypeImportMap,
|
|
35
|
-
'CoreServices'
|
|
37
|
+
'CoreServices',
|
|
38
|
+
state
|
|
36
39
|
)
|
|
37
40
|
|
|
38
41
|
addFileExtendsCoreType(
|
|
39
42
|
node,
|
|
40
43
|
checker,
|
|
41
44
|
state.userSessionTypeImportMap,
|
|
42
|
-
'CoreUserSession'
|
|
45
|
+
'CoreUserSession',
|
|
46
|
+
state
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
addFileExtendsCoreType(
|
|
50
|
+
node,
|
|
51
|
+
checker,
|
|
52
|
+
state.configTypeImportMap,
|
|
53
|
+
'CoreConfig',
|
|
54
|
+
state
|
|
43
55
|
)
|
|
44
56
|
|
|
45
57
|
addFileWithFactory(
|
|
@@ -59,30 +71,32 @@ export const visitSetup = (
|
|
|
59
71
|
addFileWithFactory(node, checker, state.configFactories, 'CreateConfig')
|
|
60
72
|
addRPCInvocations(node, state, logger)
|
|
61
73
|
|
|
74
|
+
addMiddleware(logger, node, checker, state, options)
|
|
75
|
+
addPermission(logger, node, checker, state, options)
|
|
76
|
+
|
|
62
77
|
ts.forEachChild(node, (child) =>
|
|
63
|
-
visitSetup(checker, child, state,
|
|
78
|
+
visitSetup(logger, checker, child, state, options)
|
|
64
79
|
)
|
|
65
80
|
}
|
|
66
81
|
|
|
67
82
|
export const visitRoutes = (
|
|
83
|
+
logger: InspectorLogger,
|
|
68
84
|
checker: ts.TypeChecker,
|
|
69
85
|
node: ts.Node,
|
|
70
86
|
state: InspectorState,
|
|
71
|
-
|
|
72
|
-
logger: InspectorLogger
|
|
87
|
+
options: InspectorOptions
|
|
73
88
|
) => {
|
|
74
|
-
addFunctions(node, checker, state,
|
|
75
|
-
addHTTPRoute(node, checker, state,
|
|
76
|
-
addSchedule(node, checker, state,
|
|
77
|
-
addQueueWorker(node, checker, state,
|
|
78
|
-
addChannel(node, checker, state,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
addPermission(node, checker, state, logger)
|
|
89
|
+
addFunctions(logger, node, checker, state, options)
|
|
90
|
+
addHTTPRoute(logger, node, checker, state, options)
|
|
91
|
+
addSchedule(logger, node, checker, state, options)
|
|
92
|
+
addQueueWorker(logger, node, checker, state, options)
|
|
93
|
+
addChannel(logger, node, checker, state, options)
|
|
94
|
+
addCLI(logger, node, checker, state, options)
|
|
95
|
+
addMCPResource(logger, node, checker, state, options)
|
|
96
|
+
addMCPTool(logger, node, checker, state, options)
|
|
97
|
+
addMCPPrompt(logger, node, checker, state, options)
|
|
84
98
|
|
|
85
99
|
ts.forEachChild(node, (child) =>
|
|
86
|
-
visitRoutes(checker, child, state,
|
|
100
|
+
visitRoutes(logger, checker, child, state, options)
|
|
87
101
|
)
|
|
88
102
|
}
|