@pikku/inspector 0.9.5 → 0.10.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 +14 -0
- package/dist/add/add-channel.d.ts +17 -0
- package/dist/{add-channel.js → add/add-channel.js} +60 -34
- package/dist/add/add-cli.d.ts +9 -0
- package/dist/add/add-cli.js +566 -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 -4
- 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} +2 -2
- package/dist/{add-file-with-factory.js → add/add-file-with-factory.js} +38 -5
- package/dist/add/add-functions.d.ts +6 -0
- package/dist/{add-functions.js → add/add-functions.js} +77 -10
- 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} +26 -13
- package/dist/add/add-mcp-prompt.d.ts +2 -0
- package/dist/add/add-mcp-prompt.js +74 -0
- package/dist/add/add-mcp-resource.d.ts +2 -0
- package/dist/add/add-mcp-resource.js +84 -0
- package/dist/add/add-mcp-tool.d.ts +2 -0
- package/dist/add/add-mcp-tool.js +80 -0
- package/dist/add/add-middleware.d.ts +5 -0
- package/dist/add/add-middleware.js +290 -0
- package/dist/add/add-permission.d.ts +5 -0
- package/dist/add/add-permission.js +292 -0
- package/dist/add/add-queue-worker.d.ts +2 -0
- package/dist/add/add-queue-worker.js +52 -0
- 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} +16 -11
- package/dist/error-codes.d.ts +35 -0
- package/dist/error-codes.js +40 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/inspector.d.ts +2 -3
- package/dist/inspector.js +38 -8
- package/dist/types.d.ts +108 -1
- package/dist/utils/ensure-function-metadata.d.ts +6 -0
- package/dist/utils/ensure-function-metadata.js +18 -0
- package/dist/utils/extract-function-name.d.ts +31 -0
- package/dist/{utils.js → utils/extract-function-name.js} +35 -149
- package/dist/utils/extract-services.d.ts +6 -0
- package/dist/utils/extract-services.js +29 -0
- package/dist/utils/filter-inspector-state.d.ts +6 -0
- package/dist/utils/filter-inspector-state.js +382 -0
- package/dist/utils/filter-utils.d.ts +19 -0
- package/dist/utils/filter-utils.js +109 -0
- package/dist/utils/find-root-dir.d.ts +23 -0
- package/dist/utils/find-root-dir.js +55 -0
- package/dist/utils/get-files-and-methods.d.ts +22 -0
- package/dist/utils/get-files-and-methods.js +61 -0
- package/dist/utils/get-property-value.d.ts +12 -0
- package/dist/{get-property-value.js → utils/get-property-value.js} +20 -0
- package/dist/utils/middleware.d.ts +39 -0
- package/dist/utils/middleware.js +157 -0
- package/dist/utils/permissions.d.ts +43 -0
- package/dist/utils/permissions.js +178 -0
- package/dist/utils/post-process.d.ts +16 -0
- package/dist/utils/post-process.js +132 -0
- package/dist/utils/serialize-inspector-state.d.ts +179 -0
- package/dist/utils/serialize-inspector-state.js +170 -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 +35 -31
- package/package.json +5 -6
- package/src/{add-channel.ts → add/add-channel.ts} +108 -56
- package/src/add/add-cli.ts +822 -0
- package/src/{add-file-extends-core-type.ts → add/add-file-extends-core-type.ts} +23 -5
- 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} +49 -6
- package/src/{add-functions.ts → add/add-functions.ts} +89 -19
- package/src/{add-http-route.ts → add/add-http-route.ts} +66 -32
- package/src/add/add-mcp-prompt.ts +128 -0
- package/src/add/add-mcp-prompt.ts.tmp +0 -0
- package/src/add/add-mcp-resource.ts +145 -0
- package/src/add/add-mcp-resource.ts.tmp +0 -0
- package/src/add/add-mcp-tool.ts +137 -0
- package/src/add/add-middleware.ts +385 -0
- package/src/add/add-permission.ts +391 -0
- package/src/add/add-queue-worker.ts +92 -0
- package/src/{add-rpc-invocations.ts → add/add-rpc-invocations.ts} +1 -1
- package/src/{add-schedule.ts → add/add-schedule.ts} +30 -28
- package/src/error-codes.ts +43 -0
- package/src/index.ts +12 -0
- package/src/inspector.ts +41 -17
- package/src/types.ts +128 -1
- package/src/utils/ensure-function-metadata.ts +24 -0
- package/src/{utils.ts → utils/extract-function-name.ts} +44 -206
- package/src/utils/extract-services.ts +35 -0
- package/src/utils/filter-inspector-state.test.ts +1433 -0
- package/src/utils/filter-inspector-state.ts +526 -0
- package/src/{utils.test.ts → utils/filter-utils.test.ts} +351 -2
- package/src/utils/filter-utils.ts +152 -0
- package/src/utils/find-root-dir.ts +68 -0
- package/src/utils/get-files-and-methods.ts +151 -0
- package/src/{get-property-value.ts → utils/get-property-value.ts} +27 -0
- package/src/utils/middleware.ts +241 -0
- package/src/utils/permissions.test.ts +327 -0
- package/src/utils/permissions.ts +262 -0
- package/src/utils/post-process.ts +178 -0
- package/src/utils/serialize-inspector-state.ts +375 -0
- package/src/utils/test-data/inspector-state.json +1680 -0
- package/src/utils/type-utils.ts +74 -0
- package/src/visit.ts +50 -34
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add-channel.d.ts +0 -13
- package/dist/add-functions.d.ts +0 -7
- package/dist/add-mcp-prompt.d.ts +0 -3
- package/dist/add-mcp-prompt.js +0 -61
- package/dist/add-mcp-resource.d.ts +0 -3
- package/dist/add-mcp-resource.js +0 -68
- package/dist/add-mcp-tool.d.ts +0 -3
- package/dist/add-mcp-tool.js +0 -64
- 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-permission.js +0 -35
- package/dist/add-queue-worker.d.ts +0 -3
- package/dist/add-queue-worker.js +0 -48
- package/dist/add-schedule.d.ts +0 -3
- package/dist/get-property-value.d.ts +0 -3
- package/dist/utils.d.ts +0 -39
- package/src/add-mcp-prompt.ts +0 -104
- package/src/add-mcp-resource.ts +0 -116
- package/src/add-mcp-tool.ts +0 -107
- package/src/add-middleware.ts +0 -51
- package/src/add-permission.ts +0 -53
- package/src/add-queue-worker.ts +0 -92
- /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/src/{does-type-extend-core-type.ts → utils/does-type-extend-core-type.ts} +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import { PathToNameAndType } from '
|
|
2
|
+
import { PathToNameAndType, InspectorState } from '../types.js'
|
|
3
3
|
|
|
4
4
|
export const addFileExtendsCoreType = (
|
|
5
5
|
node: ts.Node,
|
|
6
6
|
checker: ts.TypeChecker,
|
|
7
7
|
methods: PathToNameAndType,
|
|
8
|
-
expectedTypeName: string
|
|
8
|
+
expectedTypeName: string,
|
|
9
|
+
state?: InspectorState
|
|
9
10
|
) => {
|
|
10
11
|
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
|
11
12
|
const fileName = node.getSourceFile().fileName
|
|
@@ -32,13 +33,30 @@ export const addFileExtendsCoreType = (
|
|
|
32
33
|
extendedTypeDeclarationPath = sourceFile.fileName // Get the path of the file where the extended type was declared
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const variables = methods
|
|
36
|
+
const variables = methods.get(fileName) || []
|
|
37
|
+
|
|
38
|
+
if (!typeName) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Found anonymous ${ts.isClassDeclaration(node) ? 'class' : 'interface'} extending ${expectedTypeName} in ${fileName}. ` +
|
|
41
|
+
`Classes and interfaces that extend core types must have a name.`
|
|
42
|
+
)
|
|
43
|
+
}
|
|
36
44
|
variables.push({
|
|
37
|
-
variable:
|
|
45
|
+
variable: typeName,
|
|
38
46
|
type: typeName,
|
|
39
47
|
typePath: extendedTypeDeclarationPath,
|
|
40
48
|
})
|
|
41
|
-
methods
|
|
49
|
+
methods.set(fileName, variables)
|
|
50
|
+
|
|
51
|
+
// Store the type in typesLookup if state is provided
|
|
52
|
+
if (state && node.name) {
|
|
53
|
+
const symbol = checker.getSymbolAtLocation(node.name)
|
|
54
|
+
if (symbol) {
|
|
55
|
+
const declaredType = checker.getDeclaredTypeOfSymbol(symbol)
|
|
56
|
+
// Use the type name as the key in typesLookup
|
|
57
|
+
state.typesLookup.set(typeName, [declaredType])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
42
60
|
}
|
|
43
61
|
}
|
|
44
62
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import { doesTypeExtendsCore } from '
|
|
3
|
-
import { PathToNameAndType } from '
|
|
2
|
+
import { doesTypeExtendsCore } from '../utils/does-type-extend-core-type.js'
|
|
3
|
+
import { PathToNameAndType } from '../types.js'
|
|
4
4
|
|
|
5
5
|
export const addFileWithConfig = (
|
|
6
6
|
node: ts.Node,
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import { PathToNameAndType } from '
|
|
2
|
+
import { PathToNameAndType, InspectorState } from '../types.js'
|
|
3
|
+
import { extractServicesFromFunction } from '../utils/extract-services.js'
|
|
3
4
|
|
|
4
5
|
export const addFileWithFactory = (
|
|
5
6
|
node: ts.Node,
|
|
6
7
|
checker: ts.TypeChecker,
|
|
7
8
|
methods: PathToNameAndType = new Map(),
|
|
8
|
-
expectedTypeName: string
|
|
9
|
+
expectedTypeName: string,
|
|
10
|
+
state?: InspectorState
|
|
9
11
|
) => {
|
|
10
12
|
if (ts.isVariableDeclaration(node)) {
|
|
11
13
|
const fileName = node.getSourceFile().fileName
|
|
@@ -30,13 +32,32 @@ export const addFileWithFactory = (
|
|
|
30
32
|
typeDeclarationPath = sourceFile.fileName // Get the path of the file where the type was declared
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
const variables = methods
|
|
35
|
+
const variables = methods.get(fileName) || []
|
|
34
36
|
variables.push({
|
|
35
37
|
variable: variableName,
|
|
36
38
|
type: typeNameNode.getText(),
|
|
37
39
|
typePath: typeDeclarationPath,
|
|
38
40
|
})
|
|
39
|
-
methods
|
|
41
|
+
methods.set(fileName, variables)
|
|
42
|
+
|
|
43
|
+
// Extract singleton services for CreateSessionServices factories
|
|
44
|
+
if (
|
|
45
|
+
expectedTypeName === 'CreateSessionServices' &&
|
|
46
|
+
state &&
|
|
47
|
+
node.initializer
|
|
48
|
+
) {
|
|
49
|
+
let functionNode: ts.ArrowFunction | ts.FunctionExpression | undefined
|
|
50
|
+
if (ts.isArrowFunction(node.initializer)) {
|
|
51
|
+
functionNode = node.initializer
|
|
52
|
+
} else if (ts.isFunctionExpression(node.initializer)) {
|
|
53
|
+
functionNode = node.initializer
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (functionNode) {
|
|
57
|
+
const servicesMeta = extractServicesFromFunction(functionNode)
|
|
58
|
+
state.sessionServicesMeta.set(variableName, servicesMeta.services)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
40
61
|
}
|
|
41
62
|
|
|
42
63
|
// Handle qualified type names if necessary
|
|
@@ -51,13 +72,35 @@ export const addFileWithFactory = (
|
|
|
51
72
|
typeDeclarationPath = sourceFile.fileName // Get the path of the file where the type was declared
|
|
52
73
|
}
|
|
53
74
|
|
|
54
|
-
const variables = methods
|
|
75
|
+
const variables = methods.get(fileName) || []
|
|
55
76
|
variables.push({
|
|
56
77
|
variable: variableName,
|
|
57
78
|
type: typeNameNode.getText(),
|
|
58
79
|
typePath: typeDeclarationPath,
|
|
59
80
|
})
|
|
60
|
-
methods
|
|
81
|
+
methods.set(fileName, variables)
|
|
82
|
+
|
|
83
|
+
// Extract singleton services for CreateSessionServices factories
|
|
84
|
+
if (
|
|
85
|
+
expectedTypeName === 'CreateSessionServices' &&
|
|
86
|
+
state &&
|
|
87
|
+
node.initializer
|
|
88
|
+
) {
|
|
89
|
+
let functionNode:
|
|
90
|
+
| ts.ArrowFunction
|
|
91
|
+
| ts.FunctionExpression
|
|
92
|
+
| undefined
|
|
93
|
+
if (ts.isArrowFunction(node.initializer)) {
|
|
94
|
+
functionNode = node.initializer
|
|
95
|
+
} else if (ts.isFunctionExpression(node.initializer)) {
|
|
96
|
+
functionNode = node.initializer
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (functionNode) {
|
|
100
|
+
const servicesMeta = extractServicesFromFunction(functionNode)
|
|
101
|
+
state.sessionServicesMeta.set(variableName, servicesMeta.services)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
61
104
|
}
|
|
62
105
|
}
|
|
63
106
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import {
|
|
3
|
-
import { TypesMap } from '
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
getPropertyAssignmentInitializer,
|
|
7
|
-
} from './utils.js'
|
|
2
|
+
import { AddWiring } from '../types.js'
|
|
3
|
+
import { TypesMap } from '../types-map.js'
|
|
4
|
+
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
5
|
+
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
8
6
|
import { FunctionServicesMeta, PikkuDocs } from '@pikku/core'
|
|
9
|
-
import { getPropertyValue } from '
|
|
7
|
+
import { getPropertyValue } from '../utils/get-property-value.js'
|
|
8
|
+
import { resolveMiddleware } from '../utils/middleware.js'
|
|
10
9
|
|
|
11
10
|
const isValidVariableName = (name: string) => {
|
|
12
11
|
const regex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/
|
|
@@ -28,7 +27,8 @@ const nullifyTypes = (type: string | null) => {
|
|
|
28
27
|
const resolveTypeImports = (
|
|
29
28
|
type: ts.Type,
|
|
30
29
|
resolvedTypes: TypesMap,
|
|
31
|
-
isCustom: boolean
|
|
30
|
+
isCustom: boolean,
|
|
31
|
+
checker: ts.TypeChecker
|
|
32
32
|
): string[] => {
|
|
33
33
|
const types: string[] = []
|
|
34
34
|
|
|
@@ -43,10 +43,14 @@ const resolveTypeImports = (
|
|
|
43
43
|
const path = sourceFile.fileName
|
|
44
44
|
|
|
45
45
|
// Skip built-in utility types or TypeScript lib types
|
|
46
|
+
// Skip enum members (but not the enum type itself)
|
|
47
|
+
const isEnumMember = declaration && ts.isEnumMember(declaration)
|
|
48
|
+
|
|
46
49
|
if (
|
|
47
50
|
!path.includes('node_modules/typescript') &&
|
|
48
51
|
symbol.getName() !== '__type' &&
|
|
49
|
-
!isPrimitiveType(currentType)
|
|
52
|
+
!isPrimitiveType(currentType) &&
|
|
53
|
+
!isEnumMember
|
|
50
54
|
) {
|
|
51
55
|
const originalName = symbol.getName()
|
|
52
56
|
// Check if the type is already in the map
|
|
@@ -103,6 +107,32 @@ const resolveTypeImports = (
|
|
|
103
107
|
const typeRef = currentType as ts.TypeReference
|
|
104
108
|
typeRef.typeArguments?.forEach(visitType)
|
|
105
109
|
}
|
|
110
|
+
|
|
111
|
+
// Handle anonymous object types with enum properties (e.g., { userType: UserType })
|
|
112
|
+
// Only traverse into enum property types to avoid over-importing other named types
|
|
113
|
+
if (currentType.flags & ts.TypeFlags.Object) {
|
|
114
|
+
const objectType = currentType as ts.ObjectType
|
|
115
|
+
const typeSymbol = objectType.getSymbol()
|
|
116
|
+
|
|
117
|
+
// Only traverse properties for anonymous object types (no symbol or __type symbol)
|
|
118
|
+
// Skip named types, interfaces, and enums to avoid over-importing
|
|
119
|
+
const isAnonymousObject = !typeSymbol || typeSymbol.getName() === '__type'
|
|
120
|
+
|
|
121
|
+
if (isAnonymousObject) {
|
|
122
|
+
const properties = objectType.getProperties()
|
|
123
|
+
for (const prop of properties) {
|
|
124
|
+
if (prop.valueDeclaration) {
|
|
125
|
+
const propType = checker.getTypeOfSymbolAtLocation(
|
|
126
|
+
prop,
|
|
127
|
+
prop.valueDeclaration
|
|
128
|
+
)
|
|
129
|
+
if (propType) {
|
|
130
|
+
visitType(propType)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
106
136
|
}
|
|
107
137
|
|
|
108
138
|
visitType(type)
|
|
@@ -171,7 +201,7 @@ const getNamesAndTypes = (
|
|
|
171
201
|
|
|
172
202
|
// record the alias in your TypesMap
|
|
173
203
|
const references = rawTypes
|
|
174
|
-
.map((t) => resolveTypeImports(t, typesMap, true))
|
|
204
|
+
.map((t) => resolveTypeImports(t, typesMap, true, checker))
|
|
175
205
|
.flat()
|
|
176
206
|
|
|
177
207
|
typesMap.addCustomType(aliasName, aliasType, references)
|
|
@@ -193,7 +223,7 @@ const getNamesAndTypes = (
|
|
|
193
223
|
return name
|
|
194
224
|
}
|
|
195
225
|
// non-primitive: import/alias it inline
|
|
196
|
-
return resolveTypeImports(t, typesMap, false)
|
|
226
|
+
return resolveTypeImports(t, typesMap, false, checker)
|
|
197
227
|
})
|
|
198
228
|
.flat()
|
|
199
229
|
|
|
@@ -247,12 +277,7 @@ function unwrapPromise(checker: ts.TypeChecker, type: ts.Type): ts.Type {
|
|
|
247
277
|
* Inspect pikkuFunc calls, extract input/output and first-arg destructuring,
|
|
248
278
|
* then push into state.functions.meta.
|
|
249
279
|
*/
|
|
250
|
-
export
|
|
251
|
-
node: ts.Node,
|
|
252
|
-
checker: ts.TypeChecker,
|
|
253
|
-
state: InspectorState,
|
|
254
|
-
logger: InspectorLogger
|
|
255
|
-
) {
|
|
280
|
+
export const addFunctions: AddWiring = (logger, node, checker, state) => {
|
|
256
281
|
if (!ts.isCallExpression(node)) return
|
|
257
282
|
|
|
258
283
|
const { expression, arguments: args, typeArguments } = node
|
|
@@ -276,11 +301,12 @@ export function addFunctions(
|
|
|
276
301
|
if (args.length === 0) return
|
|
277
302
|
|
|
278
303
|
const { pikkuFuncName, name, explicitName, exportedName } =
|
|
279
|
-
extractFunctionName(node, checker)
|
|
304
|
+
extractFunctionName(node, checker, state.rootDir)
|
|
280
305
|
|
|
281
306
|
let tags: string[] | undefined
|
|
282
307
|
let expose: boolean | undefined
|
|
283
308
|
let docs: PikkuDocs | undefined
|
|
309
|
+
let objectNode: ts.ObjectLiteralExpression | undefined
|
|
284
310
|
|
|
285
311
|
// determine the actual handler expression:
|
|
286
312
|
// either the `func` prop or the first argument directly
|
|
@@ -289,6 +315,7 @@ export function addFunctions(
|
|
|
289
315
|
|
|
290
316
|
if (ts.isObjectLiteralExpression(handlerNode)) {
|
|
291
317
|
isDirectFunction = false // This is object format with func property
|
|
318
|
+
objectNode = handlerNode
|
|
292
319
|
tags = (getPropertyValue(handlerNode, 'tags') as string[]) || undefined
|
|
293
320
|
expose = getPropertyValue(handlerNode, 'expose') as boolean | undefined
|
|
294
321
|
docs = getPropertyValue(handlerNode, 'docs') as PikkuDocs | undefined
|
|
@@ -304,6 +331,17 @@ export function addFunctions(
|
|
|
304
331
|
(!ts.isArrowFunction(fnProp) && !ts.isFunctionExpression(fnProp))
|
|
305
332
|
) {
|
|
306
333
|
logger.error(`• No valid 'func' property found for ${pikkuFuncName}.`)
|
|
334
|
+
// Create stub metadata to prevent "function not found" errors in wirings
|
|
335
|
+
state.functions.meta[pikkuFuncName] = {
|
|
336
|
+
pikkuFuncName,
|
|
337
|
+
name,
|
|
338
|
+
services: { optimized: false, services: [] },
|
|
339
|
+
inputSchemaName: null,
|
|
340
|
+
outputSchemaName: null,
|
|
341
|
+
inputs: [],
|
|
342
|
+
outputs: [],
|
|
343
|
+
middleware: undefined,
|
|
344
|
+
}
|
|
307
345
|
return
|
|
308
346
|
}
|
|
309
347
|
handlerNode = fnProp
|
|
@@ -314,6 +352,17 @@ export function addFunctions(
|
|
|
314
352
|
!ts.isFunctionExpression(handlerNode)
|
|
315
353
|
) {
|
|
316
354
|
logger.error(`• Handler for ${name} is not a function.`)
|
|
355
|
+
// Create stub metadata to prevent "function not found" errors in wirings
|
|
356
|
+
state.functions.meta[pikkuFuncName] = {
|
|
357
|
+
pikkuFuncName,
|
|
358
|
+
name,
|
|
359
|
+
services: { optimized: false, services: [] },
|
|
360
|
+
inputSchemaName: null,
|
|
361
|
+
outputSchemaName: null,
|
|
362
|
+
inputs: [],
|
|
363
|
+
outputs: [],
|
|
364
|
+
middleware: undefined,
|
|
365
|
+
}
|
|
317
366
|
return
|
|
318
367
|
}
|
|
319
368
|
|
|
@@ -347,7 +396,7 @@ export function addFunctions(
|
|
|
347
396
|
.map((t) => unwrapPromise(checker, t))
|
|
348
397
|
|
|
349
398
|
// --- Input Extraction ---
|
|
350
|
-
let { names: inputNames } = getNamesAndTypes(
|
|
399
|
+
let { names: inputNames, types: inputTypes } = getNamesAndTypes(
|
|
351
400
|
checker,
|
|
352
401
|
state.functions.typesMap,
|
|
353
402
|
'Input',
|
|
@@ -391,6 +440,11 @@ export function addFunctions(
|
|
|
391
440
|
)
|
|
392
441
|
}
|
|
393
442
|
|
|
443
|
+
// --- resolve middleware ---
|
|
444
|
+
const middleware = objectNode
|
|
445
|
+
? resolveMiddleware(state, objectNode, tags, checker)
|
|
446
|
+
: undefined
|
|
447
|
+
|
|
394
448
|
state.functions.meta[pikkuFuncName] = {
|
|
395
449
|
pikkuFuncName,
|
|
396
450
|
name,
|
|
@@ -403,6 +457,20 @@ export function addFunctions(
|
|
|
403
457
|
tags: tags || undefined,
|
|
404
458
|
docs: docs || undefined,
|
|
405
459
|
isDirectFunction,
|
|
460
|
+
middleware,
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Store the input type for later use
|
|
464
|
+
if (inputTypes.length > 0) {
|
|
465
|
+
state.typesLookup.set(pikkuFuncName, inputTypes)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Store function file location for wiring generation
|
|
469
|
+
if (exportedName) {
|
|
470
|
+
state.functions.files.set(pikkuFuncName, {
|
|
471
|
+
path: node.getSourceFile().fileName,
|
|
472
|
+
exportedName,
|
|
473
|
+
})
|
|
406
474
|
}
|
|
407
475
|
|
|
408
476
|
if (exportedName || explicitName) {
|
|
@@ -419,6 +487,8 @@ export function addFunctions(
|
|
|
419
487
|
path: node.getSourceFile().fileName,
|
|
420
488
|
exportedName,
|
|
421
489
|
})
|
|
490
|
+
// Track exposed RPC function for service aggregation
|
|
491
|
+
state.serviceAggregation.usedFunctions.add(pikkuFuncName)
|
|
422
492
|
}
|
|
423
493
|
|
|
424
494
|
// We add it to internal meta to allow autocomplete for everything
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getPropertyValue,
|
|
4
|
+
getPropertyTags,
|
|
5
|
+
} from '../utils/get-property-value.js'
|
|
3
6
|
import { pathToRegexp } from 'path-to-regexp'
|
|
4
7
|
import { HTTPMethod } from '@pikku/core/http'
|
|
5
|
-
import { PikkuDocs
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from '
|
|
11
|
-
import {
|
|
8
|
+
import { PikkuDocs } from '@pikku/core'
|
|
9
|
+
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
10
|
+
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
11
|
+
import { AddWiring } from '../types.js'
|
|
12
|
+
import { resolveHTTPMiddlewareFromObject } from '../utils/middleware.js'
|
|
13
|
+
import { resolveHTTPPermissionsFromObject } from '../utils/permissions.js'
|
|
14
|
+
import { extractWireNames } from '../utils/post-process.js'
|
|
15
|
+
import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js'
|
|
16
|
+
import { ErrorCode } from '../error-codes.js'
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* Populate metaInputTypes for a given route based on method, input type,
|
|
@@ -39,12 +44,12 @@ export const getInputTypes = (
|
|
|
39
44
|
* Simplified wireHTTP: re-uses function metadata from state.functions.meta
|
|
40
45
|
* instead of re-inferring types here.
|
|
41
46
|
*/
|
|
42
|
-
export const addHTTPRoute = (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
export const addHTTPRoute: AddWiring = (
|
|
48
|
+
logger,
|
|
49
|
+
node,
|
|
50
|
+
checker,
|
|
51
|
+
state,
|
|
52
|
+
options
|
|
48
53
|
) => {
|
|
49
54
|
// only look at calls
|
|
50
55
|
if (!ts.isCallExpression(node)) return
|
|
@@ -67,23 +72,10 @@ export const addHTTPRoute = (
|
|
|
67
72
|
const method =
|
|
68
73
|
(getPropertyValue(obj, 'method') as string)?.toLowerCase() || 'get'
|
|
69
74
|
const docs = (getPropertyValue(obj, 'docs') as PikkuDocs) || undefined
|
|
70
|
-
const tags = (
|
|
75
|
+
const tags = getPropertyTags(obj, 'HTTP route', route, logger)
|
|
71
76
|
const query = (getPropertyValue(obj, 'query') as string[]) || []
|
|
72
77
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
!matchesFilters(
|
|
77
|
-
filters,
|
|
78
|
-
{ tags },
|
|
79
|
-
{ type: PikkuWiringTypes.http, name: route, filePath },
|
|
80
|
-
logger
|
|
81
|
-
)
|
|
82
|
-
) {
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// --- find the referenced function ---
|
|
78
|
+
// --- find the referenced function name first for filtering ---
|
|
87
79
|
const funcInitializer = getPropertyAssignmentInitializer(
|
|
88
80
|
obj,
|
|
89
81
|
'func',
|
|
@@ -91,16 +83,29 @@ export const addHTTPRoute = (
|
|
|
91
83
|
checker
|
|
92
84
|
)
|
|
93
85
|
if (!funcInitializer) {
|
|
94
|
-
|
|
86
|
+
logger.critical(
|
|
87
|
+
ErrorCode.MISSING_FUNC,
|
|
88
|
+
`No valid 'func' property for route '${route}'.`
|
|
89
|
+
)
|
|
95
90
|
return
|
|
96
91
|
}
|
|
97
92
|
|
|
98
|
-
const funcName = extractFunctionName(
|
|
93
|
+
const funcName = extractFunctionName(
|
|
94
|
+
funcInitializer,
|
|
95
|
+
checker,
|
|
96
|
+
state.rootDir
|
|
97
|
+
).pikkuFuncName
|
|
98
|
+
|
|
99
|
+
// Ensure function metadata exists (creates stub for inline functions)
|
|
100
|
+
ensureFunctionMetadata(state, funcName, route)
|
|
99
101
|
|
|
100
102
|
// lookup existing function metadata
|
|
101
103
|
const fnMeta = state.functions.meta[funcName]
|
|
102
104
|
if (!fnMeta) {
|
|
103
|
-
|
|
105
|
+
logger.critical(
|
|
106
|
+
ErrorCode.FUNCTION_METADATA_NOT_FOUND,
|
|
107
|
+
`No function metadata found for '${funcName}'.`
|
|
108
|
+
)
|
|
104
109
|
return
|
|
105
110
|
}
|
|
106
111
|
const input = fnMeta.inputs?.[0] || null
|
|
@@ -114,6 +119,33 @@ export const addHTTPRoute = (
|
|
|
114
119
|
params
|
|
115
120
|
)
|
|
116
121
|
|
|
122
|
+
// --- resolve middleware ---
|
|
123
|
+
const middleware = resolveHTTPMiddlewareFromObject(
|
|
124
|
+
state,
|
|
125
|
+
route,
|
|
126
|
+
obj,
|
|
127
|
+
tags,
|
|
128
|
+
checker
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
// --- resolve permissions ---
|
|
132
|
+
const permissions = resolveHTTPPermissionsFromObject(
|
|
133
|
+
state,
|
|
134
|
+
route,
|
|
135
|
+
obj,
|
|
136
|
+
tags,
|
|
137
|
+
checker
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
// --- track used functions/middleware/permissions for service aggregation ---
|
|
141
|
+
state.serviceAggregation.usedFunctions.add(funcName)
|
|
142
|
+
extractWireNames(middleware).forEach((name) =>
|
|
143
|
+
state.serviceAggregation.usedMiddleware.add(name)
|
|
144
|
+
)
|
|
145
|
+
extractWireNames(permissions).forEach((name) =>
|
|
146
|
+
state.serviceAggregation.usedPermissions.add(name)
|
|
147
|
+
)
|
|
148
|
+
|
|
117
149
|
// --- record route ---
|
|
118
150
|
state.http.files.add(node.getSourceFile().fileName)
|
|
119
151
|
state.http.meta[method][route] = {
|
|
@@ -125,5 +157,7 @@ export const addHTTPRoute = (
|
|
|
125
157
|
inputTypes,
|
|
126
158
|
docs,
|
|
127
159
|
tags,
|
|
160
|
+
middleware,
|
|
161
|
+
permissions,
|
|
128
162
|
}
|
|
129
163
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import {
|
|
3
|
+
getPropertyValue,
|
|
4
|
+
getPropertyTags,
|
|
5
|
+
} from '../utils/get-property-value.js'
|
|
6
|
+
import { extractWireNames } from '../utils/post-process.js'
|
|
7
|
+
import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js'
|
|
8
|
+
import { AddWiring } from '../types.js'
|
|
9
|
+
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
10
|
+
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
11
|
+
import { resolveMiddleware } from '../utils/middleware.js'
|
|
12
|
+
import { resolvePermissions } from '../utils/permissions.js'
|
|
13
|
+
import { ErrorCode } from '../error-codes.js'
|
|
14
|
+
|
|
15
|
+
export const addMCPPrompt: AddWiring = (
|
|
16
|
+
logger,
|
|
17
|
+
node,
|
|
18
|
+
checker,
|
|
19
|
+
state,
|
|
20
|
+
options
|
|
21
|
+
) => {
|
|
22
|
+
if (!ts.isCallExpression(node)) {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const args = node.arguments
|
|
27
|
+
const firstArg = args[0]
|
|
28
|
+
const expression = node.expression
|
|
29
|
+
|
|
30
|
+
// Check if the call is to wireMCPPrompt
|
|
31
|
+
if (!ts.isIdentifier(expression) || expression.text !== 'wireMCPPrompt') {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!firstArg) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
40
|
+
const obj = firstArg
|
|
41
|
+
|
|
42
|
+
const nameValue = getPropertyValue(obj, 'name') as string | null
|
|
43
|
+
const descriptionValue = getPropertyValue(obj, 'description') as
|
|
44
|
+
| string
|
|
45
|
+
| null
|
|
46
|
+
const tags = getPropertyTags(obj, 'MCP prompt', nameValue, logger)
|
|
47
|
+
|
|
48
|
+
const funcInitializer = getPropertyAssignmentInitializer(
|
|
49
|
+
obj,
|
|
50
|
+
'func',
|
|
51
|
+
true,
|
|
52
|
+
checker
|
|
53
|
+
)
|
|
54
|
+
if (!funcInitializer) {
|
|
55
|
+
logger.critical(
|
|
56
|
+
ErrorCode.MISSING_FUNC,
|
|
57
|
+
`No valid 'func' property for MCP prompt '${nameValue}'.`
|
|
58
|
+
)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const pikkuFuncName = extractFunctionName(
|
|
63
|
+
funcInitializer,
|
|
64
|
+
checker,
|
|
65
|
+
state.rootDir
|
|
66
|
+
).pikkuFuncName
|
|
67
|
+
|
|
68
|
+
// Ensure function metadata exists (creates stub for inline functions)
|
|
69
|
+
ensureFunctionMetadata(state, pikkuFuncName, nameValue || undefined)
|
|
70
|
+
|
|
71
|
+
if (!nameValue) {
|
|
72
|
+
logger.critical(
|
|
73
|
+
ErrorCode.MISSING_NAME,
|
|
74
|
+
"MCP prompt is missing the required 'name' property."
|
|
75
|
+
)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!descriptionValue) {
|
|
80
|
+
logger.critical(
|
|
81
|
+
ErrorCode.MISSING_DESCRIPTION,
|
|
82
|
+
`MCP prompt '${nameValue}' is missing a description.`
|
|
83
|
+
)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// lookup existing function metadata
|
|
88
|
+
const fnMeta = state.functions.meta[pikkuFuncName]
|
|
89
|
+
if (!fnMeta) {
|
|
90
|
+
logger.critical(
|
|
91
|
+
ErrorCode.FUNCTION_METADATA_NOT_FOUND,
|
|
92
|
+
`No function metadata found for '${pikkuFuncName}'.`
|
|
93
|
+
)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
const inputSchema = fnMeta.inputs?.[0] || null
|
|
97
|
+
const outputSchema = fnMeta.outputs?.[0] || null
|
|
98
|
+
|
|
99
|
+
// --- resolve middleware ---
|
|
100
|
+
const middleware = resolveMiddleware(state, obj, tags, checker)
|
|
101
|
+
|
|
102
|
+
// --- resolve permissions ---
|
|
103
|
+
const permissions = resolvePermissions(state, obj, tags, checker)
|
|
104
|
+
|
|
105
|
+
// --- track used functions/middleware/permissions for service aggregation ---
|
|
106
|
+
state.serviceAggregation.usedFunctions.add(pikkuFuncName)
|
|
107
|
+
extractWireNames(middleware).forEach((name) =>
|
|
108
|
+
state.serviceAggregation.usedMiddleware.add(name)
|
|
109
|
+
)
|
|
110
|
+
extractWireNames(permissions).forEach((name) =>
|
|
111
|
+
state.serviceAggregation.usedPermissions.add(name)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
state.mcpEndpoints.files.add(node.getSourceFile().fileName)
|
|
115
|
+
|
|
116
|
+
state.mcpEndpoints.promptsMeta[nameValue] = {
|
|
117
|
+
pikkuFuncName,
|
|
118
|
+
name: nameValue,
|
|
119
|
+
description: descriptionValue,
|
|
120
|
+
tags,
|
|
121
|
+
inputSchema,
|
|
122
|
+
outputSchema,
|
|
123
|
+
arguments: [], // Will be populated by CLI during serialization
|
|
124
|
+
middleware,
|
|
125
|
+
permissions,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
File without changes
|