@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
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import { getPropertyValue } from '../utils/get-property-value.js'
|
|
3
|
+
import { AddWiring } from '../types.js'
|
|
4
|
+
import { ErrorCode } from '../error-codes.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inspector for wireForgeCredential calls.
|
|
8
|
+
* Extracts metadata for Forge package credential declarations.
|
|
9
|
+
* Note: wireForgeCredential is metadata-only - no runtime behavior.
|
|
10
|
+
* Schema is stored as the variable name reference; actual Zod→JSON Schema conversion happens at CLI build time.
|
|
11
|
+
*/
|
|
12
|
+
export const addForgeCredential: AddWiring = (
|
|
13
|
+
logger,
|
|
14
|
+
node,
|
|
15
|
+
_checker,
|
|
16
|
+
state,
|
|
17
|
+
_options
|
|
18
|
+
) => {
|
|
19
|
+
if (!ts.isCallExpression(node)) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const args = node.arguments
|
|
24
|
+
const firstArg = args[0]
|
|
25
|
+
const expression = node.expression
|
|
26
|
+
|
|
27
|
+
// Check if the call is to wireForgeCredential
|
|
28
|
+
if (
|
|
29
|
+
!ts.isIdentifier(expression) ||
|
|
30
|
+
expression.text !== 'wireForgeCredential'
|
|
31
|
+
) {
|
|
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 displayNameValue = getPropertyValue(obj, 'displayName') as
|
|
44
|
+
| string
|
|
45
|
+
| null
|
|
46
|
+
const descriptionValue = getPropertyValue(obj, 'description') as
|
|
47
|
+
| string
|
|
48
|
+
| null
|
|
49
|
+
const secretIdValue = getPropertyValue(obj, 'secretId') as string | null
|
|
50
|
+
|
|
51
|
+
// Get schema variable name for later runtime import
|
|
52
|
+
let schemaVariableName: string | null = null
|
|
53
|
+
for (const prop of obj.properties) {
|
|
54
|
+
if (
|
|
55
|
+
ts.isPropertyAssignment(prop) &&
|
|
56
|
+
ts.isIdentifier(prop.name) &&
|
|
57
|
+
prop.name.text === 'schema'
|
|
58
|
+
) {
|
|
59
|
+
if (ts.isIdentifier(prop.initializer)) {
|
|
60
|
+
schemaVariableName = prop.initializer.text
|
|
61
|
+
}
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Validate required fields
|
|
67
|
+
if (!nameValue) {
|
|
68
|
+
logger.critical(
|
|
69
|
+
ErrorCode.MISSING_NAME,
|
|
70
|
+
"Forge credential is missing the required 'name' property."
|
|
71
|
+
)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!displayNameValue) {
|
|
76
|
+
logger.critical(
|
|
77
|
+
ErrorCode.MISSING_NAME,
|
|
78
|
+
`Forge credential '${nameValue}' is missing the required 'displayName' property.`
|
|
79
|
+
)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!secretIdValue) {
|
|
84
|
+
logger.critical(
|
|
85
|
+
ErrorCode.MISSING_NAME,
|
|
86
|
+
`Forge credential '${nameValue}' is missing the required 'secretId' property.`
|
|
87
|
+
)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!schemaVariableName) {
|
|
92
|
+
logger.critical(
|
|
93
|
+
ErrorCode.MISSING_NAME,
|
|
94
|
+
`Forge credential '${nameValue}' is missing the required 'schema' property or schema is not a variable reference.`
|
|
95
|
+
)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const sourceFile = node.getSourceFile().fileName
|
|
100
|
+
|
|
101
|
+
state.forgeCredentials.files.add(sourceFile)
|
|
102
|
+
|
|
103
|
+
// Register the zod schema in the central zodLookup for deferred conversion
|
|
104
|
+
const schemaLookupName = `ForgeCredential_${nameValue}`
|
|
105
|
+
state.zodLookup.set(schemaLookupName, {
|
|
106
|
+
variableName: schemaVariableName,
|
|
107
|
+
sourceFile,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// Store metadata - schema conversion happens later in schema-generator
|
|
111
|
+
state.forgeCredentials.meta[nameValue] = {
|
|
112
|
+
name: nameValue,
|
|
113
|
+
displayName: displayNameValue,
|
|
114
|
+
description: descriptionValue || undefined,
|
|
115
|
+
secretId: secretIdValue,
|
|
116
|
+
schema: schemaLookupName,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import {
|
|
3
|
+
getPropertyValue,
|
|
4
|
+
getCommonWireMetaData,
|
|
5
|
+
} from '../utils/get-property-value.js'
|
|
6
|
+
import { AddWiring } from '../types.js'
|
|
7
|
+
import { ErrorCode } from '../error-codes.js'
|
|
8
|
+
import type { ForgeNodeType } from '@pikku/core/forge-node'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Inspector for wireForgeNode calls.
|
|
12
|
+
* Extracts metadata for Forge workflow builder nodes.
|
|
13
|
+
* Note: wireForgeNode is metadata-only - no runtime behavior.
|
|
14
|
+
*/
|
|
15
|
+
export const addForgeNode: 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 wireForgeNode
|
|
31
|
+
if (!ts.isIdentifier(expression) || expression.text !== 'wireForgeNode') {
|
|
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 displayNameValue = getPropertyValue(obj, 'displayName') as
|
|
44
|
+
| string
|
|
45
|
+
| null
|
|
46
|
+
const categoryValue = getPropertyValue(obj, 'category') as string | null
|
|
47
|
+
const typeValue = getPropertyValue(obj, 'type') as ForgeNodeType | null
|
|
48
|
+
const rpcValue = getPropertyValue(obj, 'rpc') as string | null
|
|
49
|
+
const errorOutputValue = getPropertyValue(obj, 'errorOutput') as
|
|
50
|
+
| boolean
|
|
51
|
+
| null
|
|
52
|
+
|
|
53
|
+
const { tags, description } = getCommonWireMetaData(
|
|
54
|
+
obj,
|
|
55
|
+
'Forge node',
|
|
56
|
+
nameValue,
|
|
57
|
+
logger
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
// Validate required fields
|
|
61
|
+
if (!nameValue) {
|
|
62
|
+
logger.critical(
|
|
63
|
+
ErrorCode.MISSING_NAME,
|
|
64
|
+
"Forge node is missing the required 'name' property."
|
|
65
|
+
)
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!displayNameValue) {
|
|
70
|
+
logger.critical(
|
|
71
|
+
ErrorCode.MISSING_NAME,
|
|
72
|
+
`Forge node '${nameValue}' is missing the required 'displayName' property.`
|
|
73
|
+
)
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!categoryValue) {
|
|
78
|
+
logger.critical(
|
|
79
|
+
ErrorCode.MISSING_NAME,
|
|
80
|
+
`Forge node '${nameValue}' is missing the required 'category' property.`
|
|
81
|
+
)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!typeValue) {
|
|
86
|
+
logger.critical(
|
|
87
|
+
ErrorCode.MISSING_NAME,
|
|
88
|
+
`Forge node '${nameValue}' is missing the required 'type' property.`
|
|
89
|
+
)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!['trigger', 'action', 'end'].includes(typeValue)) {
|
|
94
|
+
logger.critical(
|
|
95
|
+
ErrorCode.INVALID_VALUE,
|
|
96
|
+
`Forge node '${nameValue}' has invalid type '${typeValue}'. Must be 'trigger', 'action', or 'end'.`
|
|
97
|
+
)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!rpcValue) {
|
|
102
|
+
logger.critical(
|
|
103
|
+
ErrorCode.MISSING_NAME,
|
|
104
|
+
`Forge node '${nameValue}' is missing the required 'rpc' property.`
|
|
105
|
+
)
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Get function metadata for input/output schemas
|
|
110
|
+
const fnMeta = state.functions.meta[rpcValue]
|
|
111
|
+
const inputSchemaName = fnMeta?.inputs?.[0] || null
|
|
112
|
+
const outputSchemaName = fnMeta?.outputs?.[0] || null
|
|
113
|
+
|
|
114
|
+
// Note: Category validation against forge.node.categories config
|
|
115
|
+
// is done at CLI build time, not during inspection
|
|
116
|
+
|
|
117
|
+
state.forgeNodes.files.add(node.getSourceFile().fileName)
|
|
118
|
+
|
|
119
|
+
state.forgeNodes.meta[nameValue] = {
|
|
120
|
+
name: nameValue,
|
|
121
|
+
displayName: displayNameValue,
|
|
122
|
+
category: categoryValue,
|
|
123
|
+
type: typeValue,
|
|
124
|
+
rpc: rpcValue,
|
|
125
|
+
description,
|
|
126
|
+
errorOutput: errorOutputValue ?? false,
|
|
127
|
+
inputSchemaName,
|
|
128
|
+
outputSchemaName,
|
|
129
|
+
tags,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
package/src/add/add-functions.ts
CHANGED
|
@@ -2,10 +2,15 @@ import * as ts from 'typescript'
|
|
|
2
2
|
import { AddWiring } from '../types.js'
|
|
3
3
|
import { TypesMap } from '../types-map.js'
|
|
4
4
|
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
5
|
-
import {
|
|
6
|
-
import { FunctionServicesMeta
|
|
7
|
-
import {
|
|
5
|
+
import { extractFunctionNode } from '../utils/extract-function-node.js'
|
|
6
|
+
import { FunctionServicesMeta } from '@pikku/core'
|
|
7
|
+
import {
|
|
8
|
+
getPropertyValue,
|
|
9
|
+
getCommonWireMetaData,
|
|
10
|
+
} from '../utils/get-property-value.js'
|
|
8
11
|
import { resolveMiddleware } from '../utils/middleware.js'
|
|
12
|
+
import { resolvePermissions } from '../utils/permissions.js'
|
|
13
|
+
import { ErrorCode } from '../error-codes.js'
|
|
9
14
|
|
|
10
15
|
const isValidVariableName = (name: string) => {
|
|
11
16
|
const regex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/
|
|
@@ -149,14 +154,18 @@ const resolveUnionTypes = (
|
|
|
149
154
|
// Check if it's a union type AND not part of an intersection
|
|
150
155
|
if (type.isUnion() && !(type.flags & ts.TypeFlags.Intersection)) {
|
|
151
156
|
for (const t of type.types) {
|
|
152
|
-
const name = nullifyTypes(
|
|
157
|
+
const name = nullifyTypes(
|
|
158
|
+
checker.typeToString(t, undefined, ts.TypeFormatFlags.NoTruncation)
|
|
159
|
+
)
|
|
153
160
|
if (name) {
|
|
154
161
|
types.push(t)
|
|
155
162
|
names.push(name)
|
|
156
163
|
}
|
|
157
164
|
}
|
|
158
165
|
} else {
|
|
159
|
-
const name = nullifyTypes(
|
|
166
|
+
const name = nullifyTypes(
|
|
167
|
+
checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)
|
|
168
|
+
)
|
|
160
169
|
if (name) {
|
|
161
170
|
types.push(type)
|
|
162
171
|
names.push(name)
|
|
@@ -303,57 +312,114 @@ export const addFunctions: AddWiring = (logger, node, checker, state) => {
|
|
|
303
312
|
const { pikkuFuncName, name, explicitName, exportedName } =
|
|
304
313
|
extractFunctionName(node, checker, state.rootDir)
|
|
305
314
|
|
|
315
|
+
let title: string | undefined
|
|
306
316
|
let tags: string[] | undefined
|
|
317
|
+
let summary: string | undefined
|
|
318
|
+
let description: string | undefined
|
|
319
|
+
let errors: string[] | undefined
|
|
307
320
|
let expose: boolean | undefined
|
|
308
321
|
let internal: boolean | undefined
|
|
309
|
-
let docs: PikkuDocs | undefined
|
|
310
322
|
let objectNode: ts.ObjectLiteralExpression | undefined
|
|
311
323
|
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
)
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
324
|
+
// Extract the function node using shared utility
|
|
325
|
+
const firstArg = args[0]!
|
|
326
|
+
const {
|
|
327
|
+
funcNode: handlerNode,
|
|
328
|
+
resolvedFunc,
|
|
329
|
+
isDirectFunction,
|
|
330
|
+
} = extractFunctionNode(firstArg, checker)
|
|
331
|
+
|
|
332
|
+
// Variables to hold zod schema references if provided
|
|
333
|
+
let inputZodSchemaRef: { variableName: string; sourceFile: string } | null =
|
|
334
|
+
null
|
|
335
|
+
let outputZodSchemaRef: { variableName: string; sourceFile: string } | null =
|
|
336
|
+
null
|
|
337
|
+
|
|
338
|
+
// Helper to resolve schema identifier to its actual source file
|
|
339
|
+
const resolveSchemaSourceFile = (
|
|
340
|
+
identifier: ts.Identifier
|
|
341
|
+
): { variableName: string; sourceFile: string } | null => {
|
|
342
|
+
const symbol = checker.getSymbolAtLocation(identifier)
|
|
343
|
+
if (!symbol) return null
|
|
344
|
+
|
|
345
|
+
const decl = symbol.valueDeclaration || symbol.declarations?.[0]
|
|
346
|
+
if (!decl) return null
|
|
347
|
+
|
|
348
|
+
// If it's an import specifier, resolve the aliased symbol to get the actual source
|
|
349
|
+
if (ts.isImportSpecifier(decl)) {
|
|
350
|
+
const aliasedSymbol = checker.getAliasedSymbol(symbol)
|
|
351
|
+
if (aliasedSymbol) {
|
|
352
|
+
const aliasedDecl =
|
|
353
|
+
aliasedSymbol.valueDeclaration || aliasedSymbol.declarations?.[0]
|
|
354
|
+
if (aliasedDecl) {
|
|
355
|
+
return {
|
|
356
|
+
variableName: identifier.text,
|
|
357
|
+
sourceFile: aliasedDecl.getSourceFile().fileName,
|
|
358
|
+
}
|
|
359
|
+
}
|
|
346
360
|
}
|
|
347
|
-
return
|
|
348
361
|
}
|
|
349
|
-
|
|
362
|
+
|
|
363
|
+
// Not an import - use the current source file
|
|
364
|
+
return {
|
|
365
|
+
variableName: identifier.text,
|
|
366
|
+
sourceFile: decl.getSourceFile().fileName,
|
|
367
|
+
}
|
|
350
368
|
}
|
|
351
369
|
|
|
352
|
-
if
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
370
|
+
// Extract config properties if using object form
|
|
371
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
372
|
+
objectNode = firstArg
|
|
373
|
+
const metadata = getCommonWireMetaData(firstArg, 'Function', name, logger)
|
|
374
|
+
title = metadata.title
|
|
375
|
+
tags = metadata.tags
|
|
376
|
+
summary = metadata.summary
|
|
377
|
+
description = metadata.description
|
|
378
|
+
errors = metadata.errors
|
|
379
|
+
expose = getPropertyValue(firstArg, 'expose') as boolean | undefined
|
|
380
|
+
internal = getPropertyValue(firstArg, 'internal') as boolean | undefined
|
|
381
|
+
|
|
382
|
+
// Extract zod schema variable names from input/output properties
|
|
383
|
+
for (const prop of firstArg.properties) {
|
|
384
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
385
|
+
const propName = prop.name.text
|
|
386
|
+
if (propName === 'input' || propName === 'output') {
|
|
387
|
+
if (ts.isIdentifier(prop.initializer)) {
|
|
388
|
+
// Good - it's a variable reference, resolve its actual source file
|
|
389
|
+
const ref = resolveSchemaSourceFile(prop.initializer)
|
|
390
|
+
if (ref) {
|
|
391
|
+
if (propName === 'input') {
|
|
392
|
+
inputZodSchemaRef = ref
|
|
393
|
+
} else {
|
|
394
|
+
outputZodSchemaRef = ref
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
} else if (ts.isCallExpression(prop.initializer)) {
|
|
398
|
+
// Bad - it's an inline expression
|
|
399
|
+
const schemaName = `${name.charAt(0).toUpperCase() + name.slice(1)}${propName.charAt(0).toUpperCase() + propName.slice(1)}`
|
|
400
|
+
logger.critical(
|
|
401
|
+
ErrorCode.INLINE_ZOD_SCHEMA,
|
|
402
|
+
`Inline Zod schemas are not supported for '${propName}' in '${name}'.\n` +
|
|
403
|
+
` Extract to an exported variable:\n` +
|
|
404
|
+
` export const ${schemaName} = ${prop.initializer.getText()}\n` +
|
|
405
|
+
` Then use: ${propName}: ${schemaName}`
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Pick the handler: use resolvedFunc when it exists and is a function, otherwise fall back to handlerNode
|
|
414
|
+
const handler =
|
|
415
|
+
resolvedFunc &&
|
|
416
|
+
(ts.isArrowFunction(resolvedFunc) || ts.isFunctionExpression(resolvedFunc))
|
|
417
|
+
? resolvedFunc
|
|
418
|
+
: handlerNode
|
|
419
|
+
|
|
420
|
+
// Validate that we got a valid function
|
|
421
|
+
if (!ts.isArrowFunction(handler) && !ts.isFunctionExpression(handler)) {
|
|
422
|
+
logger.error(`• No valid 'func' property found for ${pikkuFuncName}.`)
|
|
357
423
|
// Create stub metadata to prevent "function not found" errors in wirings
|
|
358
424
|
state.functions.meta[pikkuFuncName] = {
|
|
359
425
|
pikkuFuncName,
|
|
@@ -373,7 +439,7 @@ export const addFunctions: AddWiring = (logger, node, checker, state) => {
|
|
|
373
439
|
services: [],
|
|
374
440
|
}
|
|
375
441
|
|
|
376
|
-
const firstParam =
|
|
442
|
+
const firstParam = handler.parameters[0]
|
|
377
443
|
if (firstParam) {
|
|
378
444
|
if (ts.isObjectBindingPattern(firstParam.name)) {
|
|
379
445
|
for (const elem of firstParam.name.elements) {
|
|
@@ -392,28 +458,76 @@ export const addFunctions: AddWiring = (logger, node, checker, state) => {
|
|
|
392
458
|
}
|
|
393
459
|
}
|
|
394
460
|
|
|
461
|
+
// --- Extract used wires from third parameter ---
|
|
462
|
+
const usedWires: string[] = []
|
|
463
|
+
const thirdParam = handler.parameters[2]
|
|
464
|
+
if (thirdParam && ts.isObjectBindingPattern(thirdParam.name)) {
|
|
465
|
+
for (const elem of thirdParam.name.elements) {
|
|
466
|
+
const propertyName =
|
|
467
|
+
elem.propertyName && ts.isIdentifier(elem.propertyName)
|
|
468
|
+
? elem.propertyName.text
|
|
469
|
+
: ts.isIdentifier(elem.name)
|
|
470
|
+
? elem.name.text
|
|
471
|
+
: undefined
|
|
472
|
+
if (propertyName) {
|
|
473
|
+
usedWires.push(propertyName)
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
395
478
|
// --- Generics → ts.Type[], unwrapped from Promise ---
|
|
396
479
|
const genericTypes: ts.Type[] = (typeArguments ?? [])
|
|
397
480
|
.map((tn) => checker.getTypeFromTypeNode(tn))
|
|
398
481
|
.map((t) => unwrapPromise(checker, t))
|
|
399
482
|
|
|
483
|
+
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1)
|
|
484
|
+
|
|
400
485
|
// --- Input Extraction ---
|
|
401
|
-
let
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
486
|
+
let inputNames: string[] = []
|
|
487
|
+
let inputTypes: ts.Type[] = []
|
|
488
|
+
|
|
489
|
+
if (inputZodSchemaRef) {
|
|
490
|
+
const schemaName = `${capitalizedName}Input`
|
|
491
|
+
inputNames = [schemaName]
|
|
492
|
+
state.zodLookup.set(schemaName, inputZodSchemaRef)
|
|
493
|
+
state.functions.typesMap.addCustomType(schemaName, 'unknown', [])
|
|
494
|
+
} else if (genericTypes.length >= 1 && genericTypes[0]) {
|
|
495
|
+
// Fall back to extracting from generic type arguments
|
|
496
|
+
const result = getNamesAndTypes(
|
|
497
|
+
checker,
|
|
498
|
+
state.functions.typesMap,
|
|
499
|
+
'Input',
|
|
500
|
+
name,
|
|
501
|
+
genericTypes[0]
|
|
502
|
+
)
|
|
503
|
+
inputNames = result.names
|
|
504
|
+
inputTypes = result.types
|
|
505
|
+
} else {
|
|
506
|
+
// Fall back to extracting from the function's second parameter type
|
|
507
|
+
const secondParam = handler.parameters[1]
|
|
508
|
+
if (secondParam) {
|
|
509
|
+
const paramType = checker.getTypeAtLocation(secondParam)
|
|
510
|
+
const result = getNamesAndTypes(
|
|
511
|
+
checker,
|
|
512
|
+
state.functions.typesMap,
|
|
513
|
+
'Input',
|
|
514
|
+
pikkuFuncName,
|
|
515
|
+
paramType
|
|
516
|
+
)
|
|
517
|
+
inputNames = result.names
|
|
518
|
+
inputTypes = result.types
|
|
519
|
+
}
|
|
520
|
+
}
|
|
413
521
|
|
|
414
522
|
// --- Output Extraction ---
|
|
415
523
|
let outputNames: string[] = []
|
|
416
|
-
|
|
524
|
+
|
|
525
|
+
if (outputZodSchemaRef) {
|
|
526
|
+
const schemaName = `${capitalizedName}Output`
|
|
527
|
+
outputNames = [schemaName]
|
|
528
|
+
state.zodLookup.set(schemaName, outputZodSchemaRef)
|
|
529
|
+
state.functions.typesMap.addCustomType(schemaName, 'unknown', [])
|
|
530
|
+
} else if (genericTypes.length >= 2) {
|
|
417
531
|
outputNames = getNamesAndTypes(
|
|
418
532
|
checker,
|
|
419
533
|
state.functions.typesMap,
|
|
@@ -422,7 +536,7 @@ export const addFunctions: AddWiring = (logger, node, checker, state) => {
|
|
|
422
536
|
genericTypes[1]
|
|
423
537
|
).names
|
|
424
538
|
} else {
|
|
425
|
-
const sig = checker.getSignatureFromDeclaration(
|
|
539
|
+
const sig = checker.getSignatureFromDeclaration(handler)
|
|
426
540
|
if (sig) {
|
|
427
541
|
const rawRet = checker.getReturnTypeOfSignature(sig)
|
|
428
542
|
const unwrapped = unwrapPromise(checker, rawRet)
|
|
@@ -442,30 +556,40 @@ export const addFunctions: AddWiring = (logger, node, checker, state) => {
|
|
|
442
556
|
)
|
|
443
557
|
}
|
|
444
558
|
|
|
559
|
+
// Store the input type for later use
|
|
560
|
+
if (inputTypes.length > 0) {
|
|
561
|
+
state.typesLookup.set(pikkuFuncName, inputTypes)
|
|
562
|
+
}
|
|
563
|
+
|
|
445
564
|
// --- resolve middleware ---
|
|
446
565
|
const middleware = objectNode
|
|
447
566
|
? resolveMiddleware(state, objectNode, tags, checker)
|
|
448
567
|
: undefined
|
|
449
568
|
|
|
569
|
+
// --- resolve permissions ---
|
|
570
|
+
const permissions = objectNode
|
|
571
|
+
? resolvePermissions(state, objectNode, tags, checker)
|
|
572
|
+
: undefined
|
|
573
|
+
|
|
450
574
|
state.functions.meta[pikkuFuncName] = {
|
|
451
575
|
pikkuFuncName,
|
|
452
576
|
name,
|
|
453
577
|
services,
|
|
578
|
+
usedWires: usedWires.length > 0 ? usedWires : undefined,
|
|
454
579
|
inputSchemaName: inputNames[0] ?? null,
|
|
455
580
|
outputSchemaName: outputNames[0] ?? null,
|
|
456
581
|
inputs: inputNames.filter((n) => n !== 'void') ?? null,
|
|
457
582
|
outputs: outputNames.filter((n) => n !== 'void') ?? null,
|
|
458
583
|
expose: expose || undefined,
|
|
459
584
|
internal: internal || undefined,
|
|
585
|
+
title,
|
|
460
586
|
tags: tags || undefined,
|
|
461
|
-
|
|
462
|
-
|
|
587
|
+
summary,
|
|
588
|
+
description,
|
|
589
|
+
errors,
|
|
463
590
|
middleware,
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
// Store the input type for later use
|
|
467
|
-
if (inputTypes.length > 0) {
|
|
468
|
-
state.typesLookup.set(pikkuFuncName, inputTypes)
|
|
591
|
+
permissions,
|
|
592
|
+
isDirectFunction,
|
|
469
593
|
}
|
|
470
594
|
|
|
471
595
|
// Store function file location for wiring generation
|
|
@@ -476,6 +600,12 @@ export const addFunctions: AddWiring = (logger, node, checker, state) => {
|
|
|
476
600
|
})
|
|
477
601
|
}
|
|
478
602
|
|
|
603
|
+
// Workflow functions don't get registered as RPC functions,
|
|
604
|
+
// they are their own type handled by add-workdflow
|
|
605
|
+
if (expression.text.includes('Workflow')) {
|
|
606
|
+
return
|
|
607
|
+
}
|
|
608
|
+
|
|
479
609
|
if (exportedName || explicitName) {
|
|
480
610
|
if (!exportedName) {
|
|
481
611
|
logger.error(
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as ts from 'typescript'
|
|
2
2
|
import {
|
|
3
3
|
getPropertyValue,
|
|
4
|
-
|
|
4
|
+
getCommonWireMetaData,
|
|
5
5
|
} from '../utils/get-property-value.js'
|
|
6
6
|
import { pathToRegexp } from 'path-to-regexp'
|
|
7
7
|
import { HTTPMethod } from '@pikku/core/http'
|
|
8
|
-
import { PikkuDocs } from '@pikku/core'
|
|
9
8
|
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
10
9
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
|
|
11
10
|
import { AddWiring } from '../types.js'
|
|
@@ -71,10 +70,37 @@ export const addHTTPRoute: AddWiring = (
|
|
|
71
70
|
|
|
72
71
|
const method =
|
|
73
72
|
(getPropertyValue(obj, 'method') as string)?.toLowerCase() || 'get'
|
|
74
|
-
const
|
|
75
|
-
|
|
73
|
+
const { title, tags, summary, description, errors } = getCommonWireMetaData(
|
|
74
|
+
obj,
|
|
75
|
+
'HTTP route',
|
|
76
|
+
route,
|
|
77
|
+
logger
|
|
78
|
+
)
|
|
76
79
|
const query = (getPropertyValue(obj, 'query') as string[]) || []
|
|
77
80
|
|
|
81
|
+
// Check if this is a workflow trigger (workflow: true)
|
|
82
|
+
const isWorkflowTrigger = getPropertyValue(obj, 'workflow') === true
|
|
83
|
+
if (isWorkflowTrigger) {
|
|
84
|
+
// Workflow triggers don't need func - they're handled by workflow-utils
|
|
85
|
+
// Just record the route for HTTP meta but skip function processing
|
|
86
|
+
state.http.files.add(node.getSourceFile().fileName)
|
|
87
|
+
state.http.meta[method][route] = {
|
|
88
|
+
pikkuFuncName: '', // No function - workflow handles it
|
|
89
|
+
route,
|
|
90
|
+
method: method as HTTPMethod,
|
|
91
|
+
params: params.length > 0 ? params : undefined,
|
|
92
|
+
query: query.length > 0 ? query : undefined,
|
|
93
|
+
inputTypes: undefined,
|
|
94
|
+
title,
|
|
95
|
+
summary,
|
|
96
|
+
description,
|
|
97
|
+
errors,
|
|
98
|
+
tags,
|
|
99
|
+
workflow: true,
|
|
100
|
+
}
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
78
104
|
// --- find the referenced function name first for filtering ---
|
|
79
105
|
const funcInitializer = getPropertyAssignmentInitializer(
|
|
80
106
|
obj,
|
|
@@ -155,7 +181,10 @@ export const addHTTPRoute: AddWiring = (
|
|
|
155
181
|
params: params.length > 0 ? params : undefined,
|
|
156
182
|
query: query.length > 0 ? query : undefined,
|
|
157
183
|
inputTypes,
|
|
158
|
-
|
|
184
|
+
title,
|
|
185
|
+
summary,
|
|
186
|
+
description,
|
|
187
|
+
errors,
|
|
159
188
|
tags,
|
|
160
189
|
middleware,
|
|
161
190
|
permissions,
|