@pikku/inspector 0.12.21 → 0.12.23
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 +30 -0
- package/dist/add/add-addon-bans.d.ts +7 -0
- package/dist/add/add-addon-bans.js +65 -0
- package/dist/add/add-channel.js +47 -6
- package/dist/add/add-cli.js +17 -0
- package/dist/add/add-functions.js +16 -8
- package/dist/add/add-http-route.d.ts +11 -1
- package/dist/add/add-http-route.js +37 -0
- package/dist/add/add-http-routes.d.ts +0 -3
- package/dist/add/add-http-routes.js +179 -36
- package/dist/add/add-workflow.js +16 -2
- package/dist/error-codes.d.ts +15 -1
- package/dist/error-codes.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/inspector.js +22 -6
- package/dist/types.d.ts +53 -2
- package/dist/utils/extract-node-value.js +19 -2
- package/dist/utils/get-exported-variable-name.d.ts +2 -0
- package/dist/utils/get-exported-variable-name.js +34 -0
- package/dist/utils/load-addon-functions-meta.js +98 -0
- package/dist/utils/resolve-addon-package.js +3 -1
- package/dist/utils/resolve-ref-contract.d.ts +21 -0
- package/dist/utils/resolve-ref-contract.js +46 -0
- package/dist/utils/serialize-inspector-state.d.ts +1 -0
- package/dist/utils/serialize-inspector-state.js +9 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +15 -0
- package/dist/visit.js +24 -19
- package/package.json +2 -2
- package/src/add/add-addon-bans.ts +84 -0
- package/src/add/add-auth.test.ts +3 -0
- package/src/add/add-channel.ts +66 -7
- package/src/add/add-cli-renderers.test.ts +1 -0
- package/src/add/add-cli.ts +30 -0
- package/src/add/add-functions.test.ts +13 -0
- package/src/add/add-functions.ts +14 -10
- package/src/add/add-http-route.ts +75 -1
- package/src/add/add-http-routes.ts +283 -41
- package/src/add/add-workflow-fanout.test.ts +106 -0
- package/src/add/add-workflow.test.ts +3 -0
- package/src/add/add-workflow.ts +16 -2
- package/src/add/addon-bans.test.ts +121 -0
- package/src/add/addon-contracts.test.ts +221 -0
- package/src/add/pii-check.test.ts +4 -0
- package/src/add/wire-name-literal.test.ts +3 -0
- package/src/error-codes.ts +18 -0
- package/src/index.ts +1 -0
- package/src/inspector.ts +25 -6
- package/src/types.ts +75 -2
- package/src/utils/extract-node-value.test.ts +49 -1
- package/src/utils/extract-node-value.ts +19 -2
- package/src/utils/filter-inspector-state.test.ts +1 -0
- package/src/utils/filter-utils.test.ts +1 -0
- package/src/utils/get-exported-variable-name.ts +48 -0
- package/src/utils/load-addon-functions-meta.ts +164 -0
- package/src/utils/resolve-addon-package.ts +6 -1
- package/src/utils/resolve-ref-contract.ts +71 -0
- package/src/utils/resolve-versions.test.ts +1 -0
- package/src/utils/serialize-inspector-state.ts +10 -0
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +16 -0
- package/src/visit.ts +26 -19
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import type { AddWiring } from '../types.js'
|
|
3
|
+
import { ErrorCode } from '../error-codes.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wiring helpers an addon must not call. Addons declare contracts with the
|
|
7
|
+
* define* helpers and export functions; the consuming app does the wiring via
|
|
8
|
+
* refHTTP / refChannel / refCLI. Service declarations remain allowed.
|
|
9
|
+
*/
|
|
10
|
+
const BANNED_WIRINGS = new Set([
|
|
11
|
+
'wireAddon',
|
|
12
|
+
'wireChannel',
|
|
13
|
+
'wireCLI',
|
|
14
|
+
'wireGateway',
|
|
15
|
+
'wireHTTP',
|
|
16
|
+
'wireHTTPRoutes',
|
|
17
|
+
'wireMCPPrompt',
|
|
18
|
+
'wireMCPResource',
|
|
19
|
+
'wireQueueWorker',
|
|
20
|
+
'wireScheduler',
|
|
21
|
+
'wireTrigger',
|
|
22
|
+
'wireTriggerSource',
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
const CONTRACT_DEFINERS = new Set([
|
|
26
|
+
'defineHTTPRoutes',
|
|
27
|
+
'defineChannelRoutes',
|
|
28
|
+
'defineCLICommands',
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
const hasHandlerProperty = (node: ts.Node): boolean => {
|
|
32
|
+
let found = false
|
|
33
|
+
const visit = (current: ts.Node) => {
|
|
34
|
+
if (found) return
|
|
35
|
+
if (
|
|
36
|
+
ts.isPropertyAssignment(current) &&
|
|
37
|
+
(ts.isIdentifier(current.name) || ts.isStringLiteral(current.name)) &&
|
|
38
|
+
(current.name.text === 'middleware' ||
|
|
39
|
+
current.name.text === 'permissions')
|
|
40
|
+
) {
|
|
41
|
+
found = true
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
ts.forEachChild(current, visit)
|
|
45
|
+
}
|
|
46
|
+
visit(node)
|
|
47
|
+
return found
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Enforce addon authoring rules. Only runs when inspecting an addon package
|
|
52
|
+
* (options.isAddon). Addons cannot wire transports, and their contracts cannot
|
|
53
|
+
* carry middleware or permissions — those are the consuming app's concern.
|
|
54
|
+
*/
|
|
55
|
+
export const checkAddonBans: AddWiring = (
|
|
56
|
+
logger,
|
|
57
|
+
node,
|
|
58
|
+
_checker,
|
|
59
|
+
_state,
|
|
60
|
+
options
|
|
61
|
+
) => {
|
|
62
|
+
if (!options.isAddon) return
|
|
63
|
+
if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression)) return
|
|
64
|
+
|
|
65
|
+
const name = node.expression.text
|
|
66
|
+
|
|
67
|
+
if (BANNED_WIRINGS.has(name)) {
|
|
68
|
+
logger.critical(
|
|
69
|
+
ErrorCode.ADDON_WIRING_NOT_ALLOWED,
|
|
70
|
+
`Addons must not call '${name}'. Declare contracts with define* and export functions; the consuming app wires them via refHTTP / refChannel / refCLI.`
|
|
71
|
+
)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (CONTRACT_DEFINERS.has(name)) {
|
|
76
|
+
const [arg] = node.arguments
|
|
77
|
+
if (arg && hasHandlerProperty(arg)) {
|
|
78
|
+
logger.critical(
|
|
79
|
+
ErrorCode.ADDON_CONTRACT_HANDLERS_NOT_ALLOWED,
|
|
80
|
+
`Addon contract '${name}' must not declare middleware or permissions — these are applied by the consuming app, not the addon.`
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/add/add-auth.test.ts
CHANGED
|
@@ -13,6 +13,9 @@ const makeLogger = (criticals: Array<{ code: ErrorCode; message: string }>) =>
|
|
|
13
13
|
info: () => {},
|
|
14
14
|
warn: () => {},
|
|
15
15
|
error: () => {},
|
|
16
|
+
diagnostic: ({ code, message }) => {
|
|
17
|
+
criticals.push({ code, message })
|
|
18
|
+
},
|
|
16
19
|
critical: (code: ErrorCode, message: string) => {
|
|
17
20
|
criticals.push({ code, message })
|
|
18
21
|
},
|
package/src/add/add-channel.ts
CHANGED
|
@@ -21,6 +21,8 @@ import { resolveIdentifier } from '../utils/resolve-identifier.js'
|
|
|
21
21
|
import { resolveFunctionMeta } from '../utils/resolve-function-meta.js'
|
|
22
22
|
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
23
23
|
import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js'
|
|
24
|
+
import { getExportedVariableName } from '../utils/get-exported-variable-name.js'
|
|
25
|
+
import { resolveRefContract } from '../utils/resolve-ref-contract.js'
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* Safely get the "initializer" expression of a property-like AST node:
|
|
@@ -40,6 +42,16 @@ function getInitializerOf(
|
|
|
40
42
|
return undefined
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
function getObjectPropertyName(
|
|
46
|
+
name: ts.PropertyName | undefined
|
|
47
|
+
): string | null {
|
|
48
|
+
if (!name) return null
|
|
49
|
+
if (ts.isIdentifier(name)) return name.text
|
|
50
|
+
if (ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text
|
|
51
|
+
if (ts.isComputedPropertyName(name)) return null
|
|
52
|
+
return name.getText()
|
|
53
|
+
}
|
|
54
|
+
|
|
43
55
|
/**
|
|
44
56
|
* Resolve a handler expression (Identifier, CallExpression, or { func })
|
|
45
57
|
* into its underlying function name.
|
|
@@ -116,6 +128,27 @@ function getHandlerNameFromExpression(
|
|
|
116
128
|
return null
|
|
117
129
|
}
|
|
118
130
|
|
|
131
|
+
function extractExportedChannelRoutes(
|
|
132
|
+
logger: {
|
|
133
|
+
error: (msg: string) => void
|
|
134
|
+
critical: (code: ErrorCode, msg: string) => void
|
|
135
|
+
},
|
|
136
|
+
routes: ts.ObjectLiteralExpression,
|
|
137
|
+
state: InspectorState,
|
|
138
|
+
checker: ts.TypeChecker
|
|
139
|
+
): Record<string, ChannelMessageMeta> {
|
|
140
|
+
const wrapper = ts.factory.createObjectLiteralExpression([
|
|
141
|
+
ts.factory.createPropertyAssignment(
|
|
142
|
+
'onMessageWiring',
|
|
143
|
+
ts.factory.createObjectLiteralExpression([
|
|
144
|
+
ts.factory.createPropertyAssignment('contract', routes),
|
|
145
|
+
])
|
|
146
|
+
),
|
|
147
|
+
])
|
|
148
|
+
|
|
149
|
+
return addMessagesRoutes(logger, wrapper, state, checker).contract ?? {}
|
|
150
|
+
}
|
|
151
|
+
|
|
119
152
|
/**
|
|
120
153
|
* Build out the nested message-routes by looking up each handler
|
|
121
154
|
* in state.functions.meta instead of re-inferring it here.
|
|
@@ -155,9 +188,24 @@ export function addMessagesRoutes(
|
|
|
155
188
|
}
|
|
156
189
|
}
|
|
157
190
|
|
|
191
|
+
const refContract = resolveRefContract(
|
|
192
|
+
chanInit,
|
|
193
|
+
'refChannel',
|
|
194
|
+
state.exportedContracts.addonChannel
|
|
195
|
+
)
|
|
196
|
+
if (refContract) {
|
|
197
|
+
const refChannelKey = getObjectPropertyName(chanElem.name)
|
|
198
|
+
if (!refChannelKey) continue
|
|
199
|
+
result[refChannelKey] = {
|
|
200
|
+
...refContract.contract,
|
|
201
|
+
}
|
|
202
|
+
continue
|
|
203
|
+
}
|
|
204
|
+
|
|
158
205
|
if (!ts.isObjectLiteralExpression(chanInit)) continue
|
|
159
206
|
|
|
160
|
-
const channelKey = chanElem.name
|
|
207
|
+
const channelKey = getObjectPropertyName(chanElem.name)
|
|
208
|
+
if (!channelKey) continue
|
|
161
209
|
result[channelKey] = {}
|
|
162
210
|
|
|
163
211
|
for (const routeElem of chanInit.properties) {
|
|
@@ -168,11 +216,8 @@ export function addMessagesRoutes(
|
|
|
168
216
|
const routeName = routeElem.name
|
|
169
217
|
if (!routeName) continue
|
|
170
218
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (ts.isStringLiteral(routeName)) {
|
|
174
|
-
routeKey = routeName.text
|
|
175
|
-
}
|
|
219
|
+
const routeKey = getObjectPropertyName(routeName)
|
|
220
|
+
if (!routeKey) continue
|
|
176
221
|
|
|
177
222
|
// For shorthand properties, we need to resolve the identifier to its declaration
|
|
178
223
|
if (ts.isShorthandPropertyAssignment(routeElem)) {
|
|
@@ -529,6 +574,18 @@ export const addChannel: AddWiring = (
|
|
|
529
574
|
options
|
|
530
575
|
) => {
|
|
531
576
|
if (!ts.isCallExpression(node)) return
|
|
577
|
+
if (
|
|
578
|
+
ts.isIdentifier(node.expression) &&
|
|
579
|
+
node.expression.text === 'defineChannelRoutes'
|
|
580
|
+
) {
|
|
581
|
+
const exportName = getExportedVariableName(node, options.sourceFile)
|
|
582
|
+
const [firstArg] = node.arguments
|
|
583
|
+
if (exportName && firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
584
|
+
state.exportedContracts.channel[exportName] =
|
|
585
|
+
extractExportedChannelRoutes(logger, firstArg, state, checker)
|
|
586
|
+
}
|
|
587
|
+
return
|
|
588
|
+
}
|
|
532
589
|
const { expression, arguments: args } = node
|
|
533
590
|
if (!ts.isIdentifier(expression) || expression.text !== 'wireChannel') return
|
|
534
591
|
const first = args[0]
|
|
@@ -664,7 +721,9 @@ export const addChannel: AddWiring = (
|
|
|
664
721
|
state.serviceAggregation.usedFunctions.add(message.pikkuFuncId)
|
|
665
722
|
}
|
|
666
723
|
|
|
667
|
-
for (const channelHandlers of Object.values(
|
|
724
|
+
for (const channelHandlers of Object.values(
|
|
725
|
+
messageWirings as Record<string, Record<string, ChannelMessageMeta>>
|
|
726
|
+
)) {
|
|
668
727
|
for (const handler of Object.values(channelHandlers)) {
|
|
669
728
|
state.serviceAggregation.usedFunctions.add(handler.pikkuFuncId)
|
|
670
729
|
}
|
package/src/add/add-cli.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { resolveIdentifier } from '../utils/resolve-identifier.js'
|
|
|
19
19
|
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
20
20
|
import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js'
|
|
21
21
|
import { extractServicesFromFunction } from '../utils/extract-services.js'
|
|
22
|
+
import { getExportedVariableName } from '../utils/get-exported-variable-name.js'
|
|
23
|
+
import { resolveRefContract } from '../utils/resolve-ref-contract.js'
|
|
22
24
|
|
|
23
25
|
// Track if we've warned about missing Config type to avoid duplicate warnings
|
|
24
26
|
const configTypeWarningShown = new Set<string>()
|
|
@@ -34,6 +36,25 @@ export const addCLI: AddWiring = (
|
|
|
34
36
|
options
|
|
35
37
|
) => {
|
|
36
38
|
if (!ts.isCallExpression(node)) return
|
|
39
|
+
if (
|
|
40
|
+
ts.isIdentifier(node.expression) &&
|
|
41
|
+
node.expression.text === 'defineCLICommands'
|
|
42
|
+
) {
|
|
43
|
+
const exportName = getExportedVariableName(node, options.sourceFile)
|
|
44
|
+
const [firstArg] = node.arguments
|
|
45
|
+
if (exportName && firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
46
|
+
inspectorState.exportedContracts.cli[exportName] = processCommands(
|
|
47
|
+
logger,
|
|
48
|
+
firstArg,
|
|
49
|
+
node.getSourceFile(),
|
|
50
|
+
typeChecker,
|
|
51
|
+
exportName,
|
|
52
|
+
inspectorState,
|
|
53
|
+
options
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
return
|
|
57
|
+
}
|
|
37
58
|
// Check if this is a wireCLI call
|
|
38
59
|
if (!node || !node.expression) {
|
|
39
60
|
return
|
|
@@ -214,6 +235,15 @@ function processCommands(
|
|
|
214
235
|
programTags
|
|
215
236
|
)
|
|
216
237
|
Object.assign(commands, spreadCommands)
|
|
238
|
+
} else {
|
|
239
|
+
const refCommands = resolveRefContract(
|
|
240
|
+
prop.expression,
|
|
241
|
+
'refCLI',
|
|
242
|
+
inspectorState.exportedContracts.addonCli
|
|
243
|
+
)
|
|
244
|
+
if (refCommands) {
|
|
245
|
+
Object.assign(commands, refCommands.contract)
|
|
246
|
+
}
|
|
217
247
|
}
|
|
218
248
|
continue
|
|
219
249
|
}
|
|
@@ -39,6 +39,9 @@ describe('addFunctions duplicate name handling', () => {
|
|
|
39
39
|
info: () => {},
|
|
40
40
|
warn: () => {},
|
|
41
41
|
error: () => {},
|
|
42
|
+
diagnostic: ({ code, message }) => {
|
|
43
|
+
criticals.push({ code, message })
|
|
44
|
+
},
|
|
42
45
|
critical: (code: ErrorCode, message: string) => {
|
|
43
46
|
criticals.push({ code, message })
|
|
44
47
|
},
|
|
@@ -91,6 +94,9 @@ describe('addFunctions duplicate name handling', () => {
|
|
|
91
94
|
info: () => {},
|
|
92
95
|
warn: () => {},
|
|
93
96
|
error: () => {},
|
|
97
|
+
diagnostic: ({ code, message }) => {
|
|
98
|
+
criticals.push({ code, message })
|
|
99
|
+
},
|
|
94
100
|
critical: (code: ErrorCode, message: string) => {
|
|
95
101
|
criticals.push({ code, message })
|
|
96
102
|
},
|
|
@@ -142,6 +148,7 @@ describe('addFunctions duplicate name handling', () => {
|
|
|
142
148
|
info: () => {},
|
|
143
149
|
warn: () => {},
|
|
144
150
|
error: () => {},
|
|
151
|
+
diagnostic: () => {},
|
|
145
152
|
critical: () => {},
|
|
146
153
|
hasCriticalErrors: () => false,
|
|
147
154
|
}
|
|
@@ -204,6 +211,9 @@ describe('addFunctions duplicate name handling', () => {
|
|
|
204
211
|
info: () => {},
|
|
205
212
|
warn: () => {},
|
|
206
213
|
error: () => {},
|
|
214
|
+
diagnostic: ({ code, message }) => {
|
|
215
|
+
criticals.push({ code, message })
|
|
216
|
+
},
|
|
207
217
|
critical: (code: ErrorCode, message: string) => {
|
|
208
218
|
criticals.push({ code, message })
|
|
209
219
|
},
|
|
@@ -250,6 +260,7 @@ describe('addFunctions implementationHash', () => {
|
|
|
250
260
|
info: () => {},
|
|
251
261
|
warn: () => {},
|
|
252
262
|
error: () => {},
|
|
263
|
+
diagnostic: () => {},
|
|
253
264
|
critical: () => {},
|
|
254
265
|
hasCriticalErrors: () => false,
|
|
255
266
|
}
|
|
@@ -296,6 +307,7 @@ describe('addFunctions implementationHash', () => {
|
|
|
296
307
|
info: () => {},
|
|
297
308
|
warn: () => {},
|
|
298
309
|
error: () => {},
|
|
310
|
+
diagnostic: () => {},
|
|
299
311
|
critical: () => {},
|
|
300
312
|
hasCriticalErrors: () => false,
|
|
301
313
|
}
|
|
@@ -349,6 +361,7 @@ describe('pikkuChannelConnectionFunc generic mapping', () => {
|
|
|
349
361
|
info: () => {},
|
|
350
362
|
warn: () => {},
|
|
351
363
|
error: () => {},
|
|
364
|
+
diagnostic: () => {},
|
|
352
365
|
critical: () => {},
|
|
353
366
|
hasCriticalErrors: () => false,
|
|
354
367
|
}
|
package/src/add/add-functions.ts
CHANGED
|
@@ -931,23 +931,27 @@ export const addFunctions: AddWiring = (
|
|
|
931
931
|
.map((f) => f.path)
|
|
932
932
|
|
|
933
933
|
if (secretPaths.length > 0) {
|
|
934
|
-
logger.
|
|
935
|
-
|
|
936
|
-
|
|
934
|
+
logger.diagnostic({
|
|
935
|
+
severity: 'error',
|
|
936
|
+
code: ErrorCode.PII_IN_OUTPUT,
|
|
937
|
+
message:
|
|
938
|
+
`Function '${name}' exposes secret-classified field(s) in its return type: ` +
|
|
937
939
|
secretPaths.map((p) => `'${p}'`).join(', ') +
|
|
938
940
|
`.\n Secret fields must never appear in function output. ` +
|
|
939
|
-
`Strip these fields before returning or change the column classification
|
|
940
|
-
)
|
|
941
|
+
`Strip these fields before returning or change the column classification.`,
|
|
942
|
+
})
|
|
941
943
|
}
|
|
942
944
|
|
|
943
945
|
if (sessionless && privatePaths.length > 0) {
|
|
944
|
-
logger.
|
|
945
|
-
|
|
946
|
-
|
|
946
|
+
logger.diagnostic({
|
|
947
|
+
severity: 'error',
|
|
948
|
+
code: ErrorCode.PII_IN_OUTPUT,
|
|
949
|
+
message:
|
|
950
|
+
`Sessionless function '${name}' exposes private-classified field(s) in its return type: ` +
|
|
947
951
|
privatePaths.map((p) => `'${p}'`).join(', ') +
|
|
948
952
|
`.\n Private fields are only safe to return from authenticated (sessioned) functions. ` +
|
|
949
|
-
`Either require a session (use pikkuFunc) or mark the column @public if it is safe to expose publicly
|
|
950
|
-
)
|
|
953
|
+
`Either require a session (use pikkuFunc) or mark the column @public if it is safe to expose publicly.`,
|
|
954
|
+
})
|
|
951
955
|
}
|
|
952
956
|
}
|
|
953
957
|
}
|
|
@@ -13,7 +13,11 @@ import {
|
|
|
13
13
|
getPropertyAssignmentInitializer,
|
|
14
14
|
extractTypeKeys,
|
|
15
15
|
} from '../utils/type-utils.js'
|
|
16
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
AddWiring,
|
|
18
|
+
ExportedHTTPRouteConfigMeta,
|
|
19
|
+
InspectorState,
|
|
20
|
+
} from '../types.js'
|
|
17
21
|
import { resolveHTTPMiddlewareFromObject } from '../utils/middleware.js'
|
|
18
22
|
import { resolveHTTPPermissionsFromObject } from '../utils/permissions.js'
|
|
19
23
|
import { extractWireNames } from '../utils/post-process.js'
|
|
@@ -40,6 +44,16 @@ export interface RegisterHTTPRouteParams {
|
|
|
40
44
|
inheritedAuth?: boolean
|
|
41
45
|
}
|
|
42
46
|
|
|
47
|
+
export interface RegisterHTTPRouteMetaParams {
|
|
48
|
+
route: ExportedHTTPRouteConfigMeta
|
|
49
|
+
state: InspectorState
|
|
50
|
+
logger: InspectorLogger
|
|
51
|
+
sourceFile: ts.SourceFile
|
|
52
|
+
basePath?: string
|
|
53
|
+
inheritedTags?: string[]
|
|
54
|
+
inheritedAuth?: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
43
57
|
/**
|
|
44
58
|
* Extract header schema reference from headers property
|
|
45
59
|
*/
|
|
@@ -421,6 +435,66 @@ export function registerHTTPRoute({
|
|
|
421
435
|
}
|
|
422
436
|
}
|
|
423
437
|
|
|
438
|
+
export function registerHTTPRouteMeta({
|
|
439
|
+
route,
|
|
440
|
+
state,
|
|
441
|
+
logger,
|
|
442
|
+
sourceFile,
|
|
443
|
+
basePath = '',
|
|
444
|
+
inheritedTags = [],
|
|
445
|
+
inheritedAuth,
|
|
446
|
+
}: RegisterHTTPRouteMetaParams): void {
|
|
447
|
+
const method = route.method.toLowerCase()
|
|
448
|
+
const fullRoute = basePath + route.route
|
|
449
|
+
const tags = [...inheritedTags, ...(route.tags || [])]
|
|
450
|
+
const funcName = route.func.pikkuFuncId
|
|
451
|
+
const fnMeta = resolveFunctionMeta(state, funcName)
|
|
452
|
+
|
|
453
|
+
if (!fnMeta) {
|
|
454
|
+
logger.critical(
|
|
455
|
+
ErrorCode.FUNCTION_METADATA_NOT_FOUND,
|
|
456
|
+
`No function metadata found for '${funcName}'.`
|
|
457
|
+
)
|
|
458
|
+
return
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let params: string[] = []
|
|
462
|
+
try {
|
|
463
|
+
const keys = pathToRegexp(fullRoute).keys
|
|
464
|
+
params = keys.filter((k) => k.type === 'param').map((k) => k.name)
|
|
465
|
+
} catch (error) {
|
|
466
|
+
logger.error(
|
|
467
|
+
`Failed to parse route '${fullRoute}': ${error instanceof Error ? error.message : error}`
|
|
468
|
+
)
|
|
469
|
+
return
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (!route.func.packageName) {
|
|
473
|
+
computeInputTypes(
|
|
474
|
+
state.http.metaInputTypes,
|
|
475
|
+
method,
|
|
476
|
+
fnMeta.inputs?.[0] || null,
|
|
477
|
+
[],
|
|
478
|
+
params
|
|
479
|
+
)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
state.serviceAggregation.usedFunctions.add(funcName)
|
|
483
|
+
state.http.files.add(sourceFile.fileName)
|
|
484
|
+
state.http.meta[method][fullRoute] = {
|
|
485
|
+
pikkuFuncId: funcName,
|
|
486
|
+
...(route.func.packageName && { packageName: route.func.packageName }),
|
|
487
|
+
route: fullRoute,
|
|
488
|
+
sourceFile: sourceFile.fileName,
|
|
489
|
+
method: method as HTTPMethod,
|
|
490
|
+
params: params.length > 0 ? params : undefined,
|
|
491
|
+
inputTypes: undefined,
|
|
492
|
+
tags: tags.length > 0 ? tags : undefined,
|
|
493
|
+
sse: route.sse ? true : undefined,
|
|
494
|
+
groupBasePath: basePath || undefined,
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
424
498
|
/**
|
|
425
499
|
* Process wireHTTP calls
|
|
426
500
|
*/
|