@pikku/inspector 0.11.2 → 0.12.1
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 +36 -1
- package/OPTIMIZATION-PLAN.md +195 -0
- package/dist/add/add-ai-agent.d.ts +2 -0
- package/dist/add/add-ai-agent.js +314 -0
- package/dist/add/add-channel.js +81 -61
- package/dist/add/add-cli.d.ts +1 -1
- package/dist/add/add-cli.js +42 -19
- package/dist/add/add-file-extends-core-type.d.ts +1 -1
- package/dist/add/add-file-with-config.d.ts +1 -1
- package/dist/add/add-file-with-factory.d.ts +1 -1
- package/dist/add/add-file-with-factory.js +2 -0
- package/dist/add/add-functions.d.ts +1 -1
- package/dist/add/add-functions.js +256 -82
- package/dist/add/add-http-route.d.ts +20 -10
- package/dist/add/add-http-route.js +156 -66
- package/dist/add/add-http-routes.d.ts +5 -0
- package/dist/add/add-http-routes.js +160 -0
- package/dist/add/add-keyed-wiring.d.ts +12 -0
- package/dist/add/add-keyed-wiring.js +97 -0
- package/dist/add/add-mcp-prompt.d.ts +1 -1
- package/dist/add/add-mcp-prompt.js +14 -9
- package/dist/add/add-mcp-resource.d.ts +1 -1
- package/dist/add/add-mcp-resource.js +14 -9
- package/dist/add/add-middleware.d.ts +1 -4
- package/dist/add/add-middleware.js +364 -79
- package/dist/add/add-permission.d.ts +1 -1
- package/dist/add/add-permission.js +152 -40
- package/dist/add/add-queue-worker.d.ts +1 -1
- package/dist/add/add-queue-worker.js +18 -12
- package/dist/add/add-rpc-invocations.d.ts +3 -3
- package/dist/add/add-rpc-invocations.js +24 -10
- package/dist/add/add-schedule.d.ts +1 -1
- package/dist/add/add-schedule.js +11 -5
- package/dist/add/add-secret.d.ts +3 -0
- package/dist/add/add-secret.js +82 -0
- package/dist/add/add-trigger.d.ts +2 -0
- package/dist/add/add-trigger.js +87 -0
- package/dist/add/add-variable.d.ts +1 -0
- package/dist/add/add-variable.js +8 -0
- package/dist/add/add-wire-addon.d.ts +7 -0
- package/dist/add/add-wire-addon.js +70 -0
- package/dist/add/add-workflow-graph.d.ts +3 -2
- package/dist/add/add-workflow-graph.js +143 -406
- package/dist/add/add-workflow.d.ts +1 -1
- package/dist/add/add-workflow.js +6 -4
- package/dist/error-codes.d.ts +15 -1
- package/dist/error-codes.js +20 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +5 -4
- package/dist/inspector.d.ts +2 -2
- package/dist/inspector.js +95 -15
- package/dist/schema-generator.d.ts +1 -0
- package/dist/schema-generator.js +1 -0
- package/dist/types-map.js +10 -1
- package/dist/types.d.ts +180 -50
- package/dist/utils/compute-required-schemas.d.ts +4 -0
- package/dist/utils/compute-required-schemas.js +40 -0
- package/dist/utils/contract-hashes.d.ts +52 -0
- package/dist/utils/contract-hashes.js +269 -0
- package/dist/utils/custom-types-generator.d.ts +9 -0
- package/dist/utils/custom-types-generator.js +71 -0
- package/dist/utils/detect-schema-vendor.d.ts +22 -0
- package/dist/utils/detect-schema-vendor.js +76 -0
- package/dist/utils/does-type-extend-core-type.d.ts +1 -1
- package/dist/utils/ensure-function-metadata.d.ts +6 -3
- package/dist/utils/ensure-function-metadata.js +220 -6
- package/dist/utils/extract-function-name.d.ts +5 -16
- package/dist/utils/extract-function-name.js +86 -291
- package/dist/utils/extract-services.d.ts +2 -1
- package/dist/utils/extract-services.js +25 -1
- package/dist/utils/filter-inspector-state.d.ts +1 -1
- package/dist/utils/filter-inspector-state.js +107 -23
- package/dist/utils/filter-utils.d.ts +2 -2
- package/dist/utils/get-files-and-methods.d.ts +1 -1
- package/dist/utils/get-property-value.d.ts +6 -1
- package/dist/utils/get-property-value.js +28 -3
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +23 -0
- package/dist/utils/middleware.d.ts +9 -32
- package/dist/utils/middleware.js +80 -66
- package/dist/utils/permissions.d.ts +4 -4
- package/dist/utils/permissions.js +10 -10
- package/dist/utils/post-process.d.ts +11 -11
- package/dist/utils/post-process.js +247 -24
- package/dist/utils/resolve-addon-package.d.ts +16 -0
- package/dist/utils/resolve-addon-package.js +34 -0
- package/dist/utils/resolve-function-types.d.ts +6 -0
- package/dist/utils/resolve-function-types.js +29 -0
- package/dist/utils/resolve-identifier.d.ts +10 -0
- package/dist/utils/resolve-identifier.js +36 -0
- package/dist/utils/resolve-versions.d.ts +2 -0
- package/dist/utils/resolve-versions.js +78 -0
- package/dist/utils/schema-generator.d.ts +9 -0
- package/dist/utils/schema-generator.js +209 -0
- package/dist/utils/serialize-inspector-state.d.ts +70 -23
- package/dist/utils/serialize-inspector-state.js +98 -22
- package/dist/utils/serialize-mcp-json.d.ts +2 -0
- package/dist/utils/serialize-mcp-json.js +99 -0
- package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
- package/dist/utils/serialize-middleware-groups-meta.js +28 -0
- package/dist/utils/serialize-openapi-json.d.ts +85 -0
- package/dist/utils/serialize-openapi-json.js +151 -0
- package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
- package/dist/utils/serialize-permissions-groups-meta.js +31 -0
- package/dist/utils/validate-auth-sessionless.d.ts +3 -0
- package/dist/utils/validate-auth-sessionless.js +14 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
- package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
- package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
- package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
- package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
- package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
- package/dist/utils/workflow/graph/index.d.ts +2 -0
- package/dist/utils/workflow/graph/index.js +2 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
- package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
- package/dist/visit.d.ts +1 -1
- package/dist/visit.js +13 -6
- package/package.json +14 -4
- package/src/add/add-ai-agent.ts +468 -0
- package/src/add/add-channel.ts +103 -79
- package/src/add/add-cli.ts +68 -24
- package/src/add/add-file-extends-core-type.ts +1 -1
- package/src/add/add-file-with-config.ts +1 -1
- package/src/add/add-file-with-factory.ts +3 -1
- package/src/add/add-functions.ts +349 -103
- package/src/add/add-http-route.ts +263 -89
- package/src/add/add-http-routes.ts +229 -0
- package/src/add/add-keyed-wiring.ts +151 -0
- package/src/add/add-mcp-prompt.ts +27 -16
- package/src/add/add-mcp-resource.ts +28 -16
- package/src/add/add-middleware.ts +482 -80
- package/src/add/add-permission.ts +199 -40
- package/src/add/add-queue-worker.ts +25 -20
- package/src/add/add-rpc-invocations.ts +28 -11
- package/src/add/add-schedule.ts +17 -12
- package/src/add/add-secret.ts +140 -0
- package/src/add/add-trigger.ts +154 -0
- package/src/add/add-variable.ts +9 -0
- package/src/add/add-wire-addon.ts +80 -0
- package/src/add/add-workflow-graph.ts +180 -522
- package/src/add/add-workflow.ts +7 -6
- package/src/error-codes.ts +25 -1
- package/src/index.ts +23 -13
- package/src/inspector.ts +139 -19
- package/src/schema-generator.ts +1 -0
- package/src/types-map.ts +12 -1
- package/src/types.ts +199 -69
- package/src/utils/compute-required-schemas.ts +48 -0
- package/src/utils/contract-hashes.test.ts +553 -0
- package/src/utils/contract-hashes.ts +386 -0
- package/src/utils/custom-types-generator.ts +88 -0
- package/src/utils/detect-schema-vendor.ts +90 -0
- package/src/utils/does-type-extend-core-type.ts +1 -1
- package/src/utils/ensure-function-metadata.ts +325 -8
- package/src/utils/extract-function-name.ts +101 -351
- package/src/utils/extract-services.ts +35 -2
- package/src/utils/filter-inspector-state.test.ts +37 -25
- package/src/utils/filter-inspector-state.ts +146 -32
- package/src/utils/filter-utils.test.ts +1 -1
- package/src/utils/filter-utils.ts +2 -2
- package/src/utils/get-files-and-methods.ts +1 -1
- package/src/utils/get-property-value.ts +42 -4
- package/src/utils/hash.ts +26 -0
- package/src/utils/middleware.test.ts +204 -0
- package/src/utils/middleware.ts +131 -69
- package/src/utils/permissions.test.ts +35 -12
- package/src/utils/permissions.ts +12 -12
- package/src/utils/post-process.ts +306 -44
- package/src/utils/resolve-addon-package.ts +49 -0
- package/src/utils/resolve-function-types.ts +42 -0
- package/src/utils/resolve-identifier.ts +46 -0
- package/src/utils/resolve-versions.test.ts +249 -0
- package/src/utils/resolve-versions.ts +105 -0
- package/src/utils/schema-generator.ts +329 -0
- package/src/utils/serialize-inspector-state.ts +184 -43
- package/src/utils/serialize-mcp-json.ts +145 -0
- package/src/utils/serialize-middleware-groups-meta.ts +33 -0
- package/src/utils/serialize-openapi-json.ts +277 -0
- package/src/utils/serialize-permissions-groups-meta.ts +35 -0
- package/src/utils/test-data/inspector-state.json +69 -66
- package/src/utils/validate-auth-sessionless.ts +29 -0
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +26 -6
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
- package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
- package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
- package/src/utils/workflow/graph/index.ts +5 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
- package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
- package/src/visit.ts +19 -7
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-forge-credential.d.ts +0 -8
- package/dist/add/add-forge-credential.js +0 -77
- package/dist/add/add-forge-node.d.ts +0 -7
- package/dist/add/add-forge-node.js +0 -77
- package/dist/add/add-mcp-tool.d.ts +0 -2
- package/dist/add/add-mcp-tool.js +0 -81
- package/dist/utils/extract-service-metadata.d.ts +0 -19
- package/dist/utils/extract-service-metadata.js +0 -244
- package/dist/utils/write-service-metadata.d.ts +0 -13
- package/dist/utils/write-service-metadata.js +0 -37
- package/src/add/add-forge-credential.ts +0 -119
- package/src/add/add-forge-node.ts +0 -132
- package/src/add/add-mcp-tool.ts +0 -141
- package/src/utils/extract-service-metadata.ts +0 -353
- package/src/utils/write-service-metadata.ts +0 -51
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import {
|
|
2
|
+
import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
|
|
3
|
+
import { extractFunctionName, funcIdToTypeName, } from '../utils/extract-function-name.js';
|
|
3
4
|
import { extractFunctionNode } from '../utils/extract-function-node.js';
|
|
5
|
+
import { extractUsedWires } from '../utils/extract-services.js';
|
|
6
|
+
import { formatVersionedId } from '@pikku/core';
|
|
4
7
|
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
5
8
|
import { resolveMiddleware } from '../utils/middleware.js';
|
|
6
9
|
import { resolvePermissions } from '../utils/permissions.js';
|
|
10
|
+
import { extractWireNames } from '../utils/post-process.js';
|
|
7
11
|
import { ErrorCode } from '../error-codes.js';
|
|
8
12
|
const isValidVariableName = (name) => {
|
|
9
13
|
const regex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
@@ -13,7 +17,8 @@ const nullifyTypes = (type) => {
|
|
|
13
17
|
if (type === 'void' ||
|
|
14
18
|
type === 'undefined' ||
|
|
15
19
|
type === 'unknown' ||
|
|
16
|
-
type === 'any'
|
|
20
|
+
type === 'any' ||
|
|
21
|
+
type === 'null') {
|
|
17
22
|
return null;
|
|
18
23
|
}
|
|
19
24
|
return type;
|
|
@@ -131,8 +136,8 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
|
|
|
131
136
|
if (!type) {
|
|
132
137
|
return { names: [], types: [] };
|
|
133
138
|
}
|
|
134
|
-
// 1) Handle an explicit void (or undefined) type up front
|
|
135
|
-
if (type.flags & ts.TypeFlags.VoidLike) {
|
|
139
|
+
// 1) Handle an explicit void (or undefined or null) type up front
|
|
140
|
+
if (type.flags & (ts.TypeFlags.VoidLike | ts.TypeFlags.Null)) {
|
|
136
141
|
return {
|
|
137
142
|
names: [],
|
|
138
143
|
types: [],
|
|
@@ -147,11 +152,9 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
|
|
|
147
152
|
const firstName = rawNames[0];
|
|
148
153
|
if (rawNames.length > 1 || (firstName && !isValidVariableName(firstName))) {
|
|
149
154
|
const aliasType = rawNames.join(' | ');
|
|
150
|
-
const aliasName =
|
|
155
|
+
const aliasName = funcIdToTypeName(funcName) + direction;
|
|
151
156
|
// record the alias in your TypesMap
|
|
152
|
-
const references = rawTypes
|
|
153
|
-
.map((t) => resolveTypeImports(t, typesMap, true, checker))
|
|
154
|
-
.flat();
|
|
157
|
+
const references = rawTypes.flatMap((t) => resolveTypeImports(t, typesMap, true, checker));
|
|
155
158
|
typesMap.addCustomType(aliasName, aliasType, references);
|
|
156
159
|
return {
|
|
157
160
|
names: [aliasName],
|
|
@@ -159,8 +162,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
|
|
|
159
162
|
};
|
|
160
163
|
}
|
|
161
164
|
// 4) Single, valid name → inline it
|
|
162
|
-
const uniqueNames = rawNames
|
|
163
|
-
.map((name, i) => {
|
|
165
|
+
const uniqueNames = rawNames.flatMap((name, i) => {
|
|
164
166
|
const t = rawTypes[i];
|
|
165
167
|
if (!t) {
|
|
166
168
|
throw new Error(`Expected type for name "${name}" in ${funcName}`);
|
|
@@ -170,8 +172,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
|
|
|
170
172
|
}
|
|
171
173
|
// non-primitive: import/alias it inline
|
|
172
174
|
return resolveTypeImports(t, typesMap, false, checker);
|
|
173
|
-
})
|
|
174
|
-
.flat();
|
|
175
|
+
});
|
|
175
176
|
return {
|
|
176
177
|
names: uniqueNames,
|
|
177
178
|
types: rawTypes,
|
|
@@ -186,7 +187,6 @@ const isPrimitiveType = (type) => {
|
|
|
186
187
|
ts.TypeFlags.Void |
|
|
187
188
|
ts.TypeFlags.Undefined |
|
|
188
189
|
ts.TypeFlags.Null |
|
|
189
|
-
ts.TypeFlags.Any |
|
|
190
190
|
ts.TypeFlags.Unknown |
|
|
191
191
|
ts.TypeFlags.VoidLike;
|
|
192
192
|
return (type.flags & primitiveFlags) !== 0;
|
|
@@ -213,7 +213,7 @@ function unwrapPromise(checker, type) {
|
|
|
213
213
|
* Inspect pikkuFunc calls, extract input/output and first-arg destructuring,
|
|
214
214
|
* then push into state.functions.meta.
|
|
215
215
|
*/
|
|
216
|
-
export const addFunctions = (logger, node, checker, state) => {
|
|
216
|
+
export const addFunctions = (logger, node, checker, state, options) => {
|
|
217
217
|
if (!ts.isCallExpression(node))
|
|
218
218
|
return;
|
|
219
219
|
const { expression, arguments: args, typeArguments } = node;
|
|
@@ -232,80 +232,141 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
232
232
|
}
|
|
233
233
|
if (args.length === 0)
|
|
234
234
|
return;
|
|
235
|
-
|
|
235
|
+
let { pikkuFuncId, name, explicitName, exportedName } = extractFunctionName(node, checker, state.rootDir);
|
|
236
|
+
if (!pikkuFuncId || pikkuFuncId.startsWith('__temp_')) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
236
239
|
let title;
|
|
237
240
|
let tags;
|
|
238
241
|
let summary;
|
|
239
242
|
let description;
|
|
240
243
|
let errors;
|
|
241
244
|
let expose;
|
|
242
|
-
let
|
|
245
|
+
let remote;
|
|
246
|
+
let mcp;
|
|
247
|
+
let readonly_;
|
|
248
|
+
let requiresApproval;
|
|
249
|
+
let version;
|
|
243
250
|
let objectNode;
|
|
251
|
+
let nodeDisplayName = null;
|
|
252
|
+
let nodeCategory = null;
|
|
253
|
+
let nodeType = null;
|
|
254
|
+
let nodeErrorOutput = null;
|
|
244
255
|
// Extract the function node using shared utility
|
|
245
256
|
const firstArg = args[0];
|
|
246
257
|
const { funcNode: handlerNode, resolvedFunc, isDirectFunction, } = extractFunctionNode(firstArg, checker);
|
|
247
|
-
// Variables to hold
|
|
248
|
-
let
|
|
249
|
-
let
|
|
250
|
-
// Helper to resolve schema identifier to its actual source file
|
|
251
|
-
|
|
258
|
+
// Variables to hold schema references if provided
|
|
259
|
+
let inputSchemaRef = null;
|
|
260
|
+
let outputSchemaRef = null;
|
|
261
|
+
// Helper to resolve schema identifier to its actual source file and detect vendor.
|
|
262
|
+
// Logs a fatal error and returns null if vendor cannot be determined.
|
|
263
|
+
const resolveSchemaRef = (identifier, context) => {
|
|
252
264
|
const symbol = checker.getSymbolAtLocation(identifier);
|
|
253
265
|
if (!symbol)
|
|
254
266
|
return null;
|
|
255
267
|
const decl = symbol.valueDeclaration || symbol.declarations?.[0];
|
|
256
268
|
if (!decl)
|
|
257
269
|
return null;
|
|
270
|
+
let sourceFile;
|
|
258
271
|
// If it's an import specifier, resolve the aliased symbol to get the actual source
|
|
259
272
|
if (ts.isImportSpecifier(decl)) {
|
|
260
273
|
const aliasedSymbol = checker.getAliasedSymbol(symbol);
|
|
261
274
|
if (aliasedSymbol) {
|
|
262
275
|
const aliasedDecl = aliasedSymbol.valueDeclaration || aliasedSymbol.declarations?.[0];
|
|
263
276
|
if (aliasedDecl) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
277
|
+
sourceFile = aliasedDecl.getSourceFile().fileName;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
return null;
|
|
268
281
|
}
|
|
269
282
|
}
|
|
283
|
+
else {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
sourceFile = decl.getSourceFile().fileName;
|
|
270
289
|
}
|
|
271
|
-
|
|
290
|
+
const vendor = detectSchemaVendorOrError(identifier, checker, logger, context, sourceFile);
|
|
291
|
+
if (!vendor)
|
|
292
|
+
return null;
|
|
272
293
|
return {
|
|
273
294
|
variableName: identifier.text,
|
|
274
|
-
sourceFile
|
|
295
|
+
sourceFile,
|
|
296
|
+
vendor,
|
|
275
297
|
};
|
|
276
298
|
};
|
|
277
299
|
// Extract config properties if using object form
|
|
278
300
|
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
279
301
|
objectNode = firstArg;
|
|
280
302
|
const metadata = getCommonWireMetaData(firstArg, 'Function', name, logger);
|
|
303
|
+
if (metadata.disabled)
|
|
304
|
+
return;
|
|
281
305
|
title = metadata.title;
|
|
282
306
|
tags = metadata.tags;
|
|
283
307
|
summary = metadata.summary;
|
|
284
308
|
description = metadata.description;
|
|
285
309
|
errors = metadata.errors;
|
|
286
310
|
expose = getPropertyValue(firstArg, 'expose');
|
|
287
|
-
|
|
288
|
-
|
|
311
|
+
remote = getPropertyValue(firstArg, 'remote');
|
|
312
|
+
mcp = getPropertyValue(firstArg, 'mcp');
|
|
313
|
+
readonly_ = getPropertyValue(firstArg, 'readonly');
|
|
314
|
+
requiresApproval = getPropertyValue(firstArg, 'requiresApproval');
|
|
315
|
+
const versionRaw = getPropertyValue(firstArg, 'version');
|
|
316
|
+
if (versionRaw !== null && versionRaw !== undefined) {
|
|
317
|
+
const parsed = Number(versionRaw);
|
|
318
|
+
if (Number.isInteger(parsed) && parsed >= 1) {
|
|
319
|
+
version = parsed;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Extract node config from nested object
|
|
323
|
+
for (const prop of firstArg.properties) {
|
|
324
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
325
|
+
ts.isIdentifier(prop.name) &&
|
|
326
|
+
prop.name.text === 'node' &&
|
|
327
|
+
ts.isObjectLiteralExpression(prop.initializer)) {
|
|
328
|
+
const nodeObj = prop.initializer;
|
|
329
|
+
nodeDisplayName = getPropertyValue(nodeObj, 'displayName');
|
|
330
|
+
nodeCategory = getPropertyValue(nodeObj, 'category');
|
|
331
|
+
nodeType = getPropertyValue(nodeObj, 'type');
|
|
332
|
+
nodeErrorOutput = getPropertyValue(nodeObj, 'errorOutput');
|
|
333
|
+
if (!nodeDisplayName) {
|
|
334
|
+
logger.critical(ErrorCode.MISSING_NAME, `Function '${name}' node config is missing the required 'displayName' property.`);
|
|
335
|
+
}
|
|
336
|
+
if (!nodeCategory) {
|
|
337
|
+
logger.critical(ErrorCode.MISSING_NAME, `Function '${name}' node config is missing the required 'category' property.`);
|
|
338
|
+
}
|
|
339
|
+
if (!nodeType) {
|
|
340
|
+
logger.critical(ErrorCode.MISSING_NAME, `Function '${name}' node config is missing the required 'type' property.`);
|
|
341
|
+
}
|
|
342
|
+
else if (!['trigger', 'action', 'end'].includes(nodeType)) {
|
|
343
|
+
logger.critical(ErrorCode.INVALID_VALUE, `Function '${name}' node config has invalid type '${nodeType}'. Must be 'trigger', 'action', or 'end'.`);
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Extract schema variable names from input/output properties
|
|
289
349
|
for (const prop of firstArg.properties) {
|
|
290
350
|
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
291
351
|
const propName = prop.name.text;
|
|
292
352
|
if (propName === 'input' || propName === 'output') {
|
|
293
353
|
if (ts.isIdentifier(prop.initializer)) {
|
|
294
|
-
// Good - it's a variable reference, resolve its actual source file
|
|
295
|
-
const
|
|
354
|
+
// Good - it's a variable reference, resolve its actual source file and vendor
|
|
355
|
+
const context = `Function '${name}' ${propName}`;
|
|
356
|
+
const ref = resolveSchemaRef(prop.initializer, context);
|
|
296
357
|
if (ref) {
|
|
297
358
|
if (propName === 'input') {
|
|
298
|
-
|
|
359
|
+
inputSchemaRef = ref;
|
|
299
360
|
}
|
|
300
361
|
else {
|
|
301
|
-
|
|
362
|
+
outputSchemaRef = ref;
|
|
302
363
|
}
|
|
303
364
|
}
|
|
304
365
|
}
|
|
305
366
|
else if (ts.isCallExpression(prop.initializer)) {
|
|
306
367
|
// Bad - it's an inline expression
|
|
307
|
-
const schemaName = `${
|
|
308
|
-
logger.critical(ErrorCode.
|
|
368
|
+
const schemaName = `${funcIdToTypeName(name)}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
|
|
369
|
+
logger.critical(ErrorCode.INLINE_SCHEMA, `Inline schemas are not supported for '${propName}' in '${name}'.\n` +
|
|
309
370
|
` Extract to an exported variable:\n` +
|
|
310
371
|
` export const ${schemaName} = ${prop.initializer.getText()}\n` +
|
|
311
372
|
` Then use: ${propName}: ${schemaName}`);
|
|
@@ -314,6 +375,12 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
314
375
|
}
|
|
315
376
|
}
|
|
316
377
|
}
|
|
378
|
+
if (version !== undefined) {
|
|
379
|
+
const baseName = explicitName || exportedName || pikkuFuncId;
|
|
380
|
+
pikkuFuncId = formatVersionedId(baseName, version);
|
|
381
|
+
}
|
|
382
|
+
const isMCPToolFunc = expression.text === 'pikkuMCPToolFunc';
|
|
383
|
+
const mcpEnabled = mcp || isMCPToolFunc;
|
|
317
384
|
// Pick the handler: use resolvedFunc when it exists and is a function, otherwise fall back to handlerNode
|
|
318
385
|
const handler = resolvedFunc &&
|
|
319
386
|
(ts.isArrowFunction(resolvedFunc) || ts.isFunctionExpression(resolvedFunc))
|
|
@@ -321,10 +388,11 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
321
388
|
: handlerNode;
|
|
322
389
|
// Validate that we got a valid function
|
|
323
390
|
if (!ts.isArrowFunction(handler) && !ts.isFunctionExpression(handler)) {
|
|
324
|
-
logger.error(`• No valid 'func' property found for ${
|
|
391
|
+
logger.error(`• No valid 'func' property found for ${pikkuFuncId}.`);
|
|
325
392
|
// Create stub metadata to prevent "function not found" errors in wirings
|
|
326
|
-
state.functions.meta[
|
|
327
|
-
|
|
393
|
+
state.functions.meta[pikkuFuncId] = {
|
|
394
|
+
pikkuFuncId,
|
|
395
|
+
functionType: 'user',
|
|
328
396
|
name,
|
|
329
397
|
services: { optimized: false, services: [] },
|
|
330
398
|
inputSchemaName: null,
|
|
@@ -353,37 +421,24 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
353
421
|
}
|
|
354
422
|
}
|
|
355
423
|
}
|
|
356
|
-
else if (ts.isIdentifier(firstParam.name)
|
|
424
|
+
else if (ts.isIdentifier(firstParam.name) &&
|
|
425
|
+
!firstParam.name.text.startsWith('_')) {
|
|
357
426
|
services.optimized = false;
|
|
358
427
|
}
|
|
359
428
|
}
|
|
360
|
-
|
|
361
|
-
const usedWires = [];
|
|
362
|
-
const thirdParam = handler.parameters[2];
|
|
363
|
-
if (thirdParam && ts.isObjectBindingPattern(thirdParam.name)) {
|
|
364
|
-
for (const elem of thirdParam.name.elements) {
|
|
365
|
-
const propertyName = elem.propertyName && ts.isIdentifier(elem.propertyName)
|
|
366
|
-
? elem.propertyName.text
|
|
367
|
-
: ts.isIdentifier(elem.name)
|
|
368
|
-
? elem.name.text
|
|
369
|
-
: undefined;
|
|
370
|
-
if (propertyName) {
|
|
371
|
-
usedWires.push(propertyName);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
429
|
+
const wires = extractUsedWires(handler, 2);
|
|
375
430
|
// --- Generics → ts.Type[], unwrapped from Promise ---
|
|
376
431
|
const genericTypes = (typeArguments ?? [])
|
|
377
432
|
.map((tn) => checker.getTypeFromTypeNode(tn))
|
|
378
433
|
.map((t) => unwrapPromise(checker, t));
|
|
379
|
-
const capitalizedName =
|
|
434
|
+
const capitalizedName = funcIdToTypeName(name);
|
|
380
435
|
// --- Input Extraction ---
|
|
381
436
|
let inputNames = [];
|
|
382
437
|
let inputTypes = [];
|
|
383
|
-
if (
|
|
438
|
+
if (inputSchemaRef) {
|
|
384
439
|
const schemaName = `${capitalizedName}Input`;
|
|
385
440
|
inputNames = [schemaName];
|
|
386
|
-
state.
|
|
441
|
+
state.schemaLookup.set(schemaName, inputSchemaRef);
|
|
387
442
|
state.functions.typesMap.addCustomType(schemaName, 'unknown', []);
|
|
388
443
|
}
|
|
389
444
|
else if (genericTypes.length >= 1 && genericTypes[0]) {
|
|
@@ -397,17 +452,17 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
397
452
|
const secondParam = handler.parameters[1];
|
|
398
453
|
if (secondParam) {
|
|
399
454
|
const paramType = checker.getTypeAtLocation(secondParam);
|
|
400
|
-
const result = getNamesAndTypes(checker, state.functions.typesMap, 'Input',
|
|
455
|
+
const result = getNamesAndTypes(checker, state.functions.typesMap, 'Input', pikkuFuncId, paramType);
|
|
401
456
|
inputNames = result.names;
|
|
402
457
|
inputTypes = result.types;
|
|
403
458
|
}
|
|
404
459
|
}
|
|
405
460
|
// --- Output Extraction ---
|
|
406
461
|
let outputNames = [];
|
|
407
|
-
if (
|
|
462
|
+
if (outputSchemaRef) {
|
|
408
463
|
const schemaName = `${capitalizedName}Output`;
|
|
409
464
|
outputNames = [schemaName];
|
|
410
|
-
state.
|
|
465
|
+
state.schemaLookup.set(schemaName, outputSchemaRef);
|
|
411
466
|
state.functions.typesMap.addCustomType(schemaName, 'unknown', []);
|
|
412
467
|
}
|
|
413
468
|
else if (genericTypes.length >= 2) {
|
|
@@ -418,35 +473,100 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
418
473
|
if (sig) {
|
|
419
474
|
const rawRet = checker.getReturnTypeOfSignature(sig);
|
|
420
475
|
const unwrapped = unwrapPromise(checker, rawRet);
|
|
421
|
-
outputNames = getNamesAndTypes(checker, state.functions.typesMap, 'Output',
|
|
476
|
+
outputNames = getNamesAndTypes(checker, state.functions.typesMap, 'Output', pikkuFuncId, unwrapped).names;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const mcpOutputTypes = {
|
|
480
|
+
pikkuMCPResourceFunc: 'MCPResourceResponse',
|
|
481
|
+
pikkuMCPToolFunc: 'MCPToolResponse',
|
|
482
|
+
pikkuMCPPromptFunc: 'MCPPromptResponse',
|
|
483
|
+
};
|
|
484
|
+
const mcpOutputType = mcpOutputTypes[expression.text];
|
|
485
|
+
if (mcpOutputType && outputNames[0] !== mcpOutputType) {
|
|
486
|
+
let resolved = false;
|
|
487
|
+
const rawSymbol = checker.getSymbolAtLocation(expression);
|
|
488
|
+
const funcSymbol = rawSymbol && rawSymbol.flags & ts.SymbolFlags.Alias
|
|
489
|
+
? checker.getAliasedSymbol(rawSymbol)
|
|
490
|
+
: rawSymbol;
|
|
491
|
+
const funcDecls = funcSymbol?.getDeclarations() || [];
|
|
492
|
+
for (const funcDecl of funcDecls) {
|
|
493
|
+
if (resolved)
|
|
494
|
+
break;
|
|
495
|
+
const mcpTypeSymbol = checker.resolveName(mcpOutputType, funcDecl, ts.SymbolFlags.Type, false);
|
|
496
|
+
if (mcpTypeSymbol) {
|
|
497
|
+
const aliased = mcpTypeSymbol.flags & ts.SymbolFlags.Alias
|
|
498
|
+
? checker.getAliasedSymbol(mcpTypeSymbol)
|
|
499
|
+
: mcpTypeSymbol;
|
|
500
|
+
const typeDecl = aliased?.getDeclarations()?.[0];
|
|
501
|
+
if (typeDecl) {
|
|
502
|
+
const path = typeDecl.getSourceFile().fileName;
|
|
503
|
+
if (!state.functions.typesMap.exists(mcpOutputType, path)) {
|
|
504
|
+
state.functions.typesMap.addType(mcpOutputType, path);
|
|
505
|
+
}
|
|
506
|
+
resolved = true;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (!resolved) {
|
|
511
|
+
state.functions.typesMap.addCustomType(mcpOutputType, mcpOutputType, []);
|
|
422
512
|
}
|
|
513
|
+
outputNames = [mcpOutputType];
|
|
423
514
|
}
|
|
424
515
|
if (inputNames.length > 1) {
|
|
425
516
|
logger.warn('More than one input type detected, only the first one will be used as a schema.');
|
|
426
517
|
}
|
|
427
518
|
// Store the input type for later use
|
|
428
519
|
if (inputTypes.length > 0) {
|
|
429
|
-
state.typesLookup.set(
|
|
520
|
+
state.typesLookup.set(pikkuFuncId, inputTypes);
|
|
430
521
|
}
|
|
431
522
|
// --- resolve middleware ---
|
|
432
|
-
|
|
523
|
+
let middleware = objectNode
|
|
433
524
|
? resolveMiddleware(state, objectNode, tags, checker)
|
|
434
525
|
: undefined;
|
|
435
526
|
// --- resolve permissions ---
|
|
436
|
-
|
|
527
|
+
let permissions = objectNode
|
|
437
528
|
? resolvePermissions(state, objectNode, tags, checker)
|
|
438
529
|
: undefined;
|
|
439
|
-
|
|
440
|
-
|
|
530
|
+
if (options.tags?.length) {
|
|
531
|
+
tags = [...new Set([...(tags || []), ...options.tags])];
|
|
532
|
+
const tagEntries = options.tags.map((tag) => ({
|
|
533
|
+
type: 'tag',
|
|
534
|
+
tag,
|
|
535
|
+
}));
|
|
536
|
+
const existingMiddlewareTags = new Set((middleware || [])
|
|
537
|
+
.filter((m) => m.type === 'tag')
|
|
538
|
+
.map((m) => m.tag));
|
|
539
|
+
const newMiddleware = tagEntries.filter((e) => !existingMiddlewareTags.has(e.tag));
|
|
540
|
+
if (newMiddleware.length > 0) {
|
|
541
|
+
middleware = [...(middleware || []), ...newMiddleware];
|
|
542
|
+
}
|
|
543
|
+
const existingPermissionTags = new Set((permissions || [])
|
|
544
|
+
.filter((p) => p.type === 'tag')
|
|
545
|
+
.map((p) => p.tag));
|
|
546
|
+
const newPermissions = tagEntries.filter((e) => !existingPermissionTags.has(e.tag));
|
|
547
|
+
if (newPermissions.length > 0) {
|
|
548
|
+
permissions = [...(permissions || []), ...newPermissions];
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const sessionless = expression.text !== 'pikkuFunc';
|
|
552
|
+
state.functions.meta[pikkuFuncId] = {
|
|
553
|
+
pikkuFuncId,
|
|
554
|
+
functionType: 'user',
|
|
555
|
+
funcWrapper: expression.text,
|
|
556
|
+
sessionless,
|
|
441
557
|
name,
|
|
442
558
|
services,
|
|
443
|
-
|
|
559
|
+
wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
|
|
444
560
|
inputSchemaName: inputNames[0] ?? null,
|
|
445
561
|
outputSchemaName: outputNames[0] ?? null,
|
|
446
562
|
inputs: inputNames.filter((n) => n !== 'void') ?? null,
|
|
447
563
|
outputs: outputNames.filter((n) => n !== 'void') ?? null,
|
|
448
564
|
expose: expose || undefined,
|
|
449
|
-
|
|
565
|
+
remote: remote || undefined,
|
|
566
|
+
mcp: mcpEnabled || undefined,
|
|
567
|
+
readonly: readonly_ || undefined,
|
|
568
|
+
requiresApproval: requiresApproval || undefined,
|
|
569
|
+
version,
|
|
450
570
|
title,
|
|
451
571
|
tags: tags || undefined,
|
|
452
572
|
summary,
|
|
@@ -456,42 +576,96 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
456
576
|
permissions,
|
|
457
577
|
isDirectFunction,
|
|
458
578
|
};
|
|
579
|
+
// Populate node metadata if node config is present
|
|
580
|
+
if (nodeDisplayName && nodeCategory && nodeType) {
|
|
581
|
+
state.nodes.files.add(node.getSourceFile().fileName);
|
|
582
|
+
state.nodes.meta[pikkuFuncId] = {
|
|
583
|
+
name: pikkuFuncId,
|
|
584
|
+
displayName: nodeDisplayName,
|
|
585
|
+
category: nodeCategory,
|
|
586
|
+
type: nodeType,
|
|
587
|
+
rpc: pikkuFuncId,
|
|
588
|
+
description,
|
|
589
|
+
errorOutput: nodeErrorOutput ?? false,
|
|
590
|
+
inputSchemaName: inputNames[0] ?? null,
|
|
591
|
+
outputSchemaName: outputNames[0] ?? null,
|
|
592
|
+
tags,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
if (mcpEnabled) {
|
|
596
|
+
if (!description) {
|
|
597
|
+
logger.critical(ErrorCode.MISSING_DESCRIPTION, `MCP tool '${name}' is missing a description.`);
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
state.mcpEndpoints.files.add(node.getSourceFile().fileName);
|
|
601
|
+
state.mcpEndpoints.toolsMeta[name] = {
|
|
602
|
+
pikkuFuncId,
|
|
603
|
+
name,
|
|
604
|
+
title: title || undefined,
|
|
605
|
+
description,
|
|
606
|
+
summary,
|
|
607
|
+
errors,
|
|
608
|
+
tags,
|
|
609
|
+
inputSchema: inputNames[0] ?? null,
|
|
610
|
+
outputSchema: outputNames[0] ?? null,
|
|
611
|
+
middleware,
|
|
612
|
+
permissions,
|
|
613
|
+
};
|
|
614
|
+
state.serviceAggregation.usedFunctions.add(pikkuFuncId);
|
|
615
|
+
extractWireNames(middleware).forEach((n) => state.serviceAggregation.usedMiddleware.add(n));
|
|
616
|
+
extractWireNames(permissions).forEach((n) => state.serviceAggregation.usedPermissions.add(n));
|
|
617
|
+
}
|
|
618
|
+
// Workflow functions don't get registered as RPC functions,
|
|
619
|
+
// they are their own type handled by add-workflow
|
|
620
|
+
if (expression.text.includes('Workflow')) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
// Trigger and channel connect/disconnect functions are not callable via RPC
|
|
624
|
+
const nonRPCPatterns = [
|
|
625
|
+
/Trigger/i,
|
|
626
|
+
/ChannelConnection/i,
|
|
627
|
+
/ChannelDisconnection/i,
|
|
628
|
+
];
|
|
629
|
+
if (nonRPCPatterns.some((pattern) => pattern.test(expression.text))) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
459
632
|
// Store function file location for wiring generation
|
|
460
633
|
if (exportedName) {
|
|
461
|
-
state.functions.files.set(
|
|
634
|
+
state.functions.files.set(pikkuFuncId, {
|
|
462
635
|
path: node.getSourceFile().fileName,
|
|
463
636
|
exportedName,
|
|
464
637
|
});
|
|
465
638
|
}
|
|
466
|
-
// Workflow functions don't get registered as RPC functions,
|
|
467
|
-
// they are their own type handled by add-workdflow
|
|
468
|
-
if (expression.text.includes('Workflow')) {
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
639
|
if (exportedName || explicitName) {
|
|
472
640
|
if (!exportedName) {
|
|
473
641
|
logger.error(`• Function with explicit name '${name}' is not exported, this is not allowed.`);
|
|
474
642
|
return;
|
|
475
643
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
state.rpc.invokedFunctions.add(pikkuFuncName);
|
|
644
|
+
if (remote) {
|
|
645
|
+
state.rpc.invokedFunctions.add(pikkuFuncId);
|
|
479
646
|
}
|
|
480
647
|
if (expose) {
|
|
481
|
-
state.rpc.exposedMeta[name] =
|
|
648
|
+
state.rpc.exposedMeta[name] = pikkuFuncId;
|
|
482
649
|
state.rpc.exposedFiles.set(name, {
|
|
483
650
|
path: node.getSourceFile().fileName,
|
|
484
651
|
exportedName,
|
|
485
652
|
});
|
|
486
653
|
// Track exposed RPC function for service aggregation
|
|
487
|
-
state.serviceAggregation.usedFunctions.add(
|
|
654
|
+
state.serviceAggregation.usedFunctions.add(pikkuFuncId);
|
|
488
655
|
}
|
|
489
656
|
// We add it to internal meta to allow autocomplete for everything
|
|
490
|
-
state.rpc.internalMeta[name] =
|
|
657
|
+
state.rpc.internalMeta[name] = pikkuFuncId;
|
|
658
|
+
if (version !== undefined) {
|
|
659
|
+
state.rpc.internalMeta[pikkuFuncId] = pikkuFuncId;
|
|
660
|
+
state.rpc.invokedFunctions.add(pikkuFuncId);
|
|
661
|
+
}
|
|
491
662
|
// But we only import the actual function if it's actually invoked to keep
|
|
492
663
|
// bundle size down
|
|
493
|
-
if (state.rpc.invokedFunctions.has(
|
|
494
|
-
|
|
664
|
+
if (state.rpc.invokedFunctions.has(pikkuFuncId) ||
|
|
665
|
+
expose ||
|
|
666
|
+
remote ||
|
|
667
|
+
mcpEnabled) {
|
|
668
|
+
state.rpc.internalFiles.set(pikkuFuncId, {
|
|
495
669
|
path: node.getSourceFile().fileName,
|
|
496
670
|
exportedName,
|
|
497
671
|
});
|
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import type { AddWiring, InspectorState } from '../types.js';
|
|
3
|
+
import type { InspectorLogger } from '../types.js';
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
4
|
-
* query and params. Returns undefined (we only mutate metaTypes).
|
|
5
|
+
* Parameters for registering an HTTP route
|
|
5
6
|
*/
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
export interface RegisterHTTPRouteParams {
|
|
8
|
+
obj: ts.ObjectLiteralExpression;
|
|
9
|
+
state: InspectorState;
|
|
10
|
+
checker: ts.TypeChecker;
|
|
11
|
+
logger: InspectorLogger;
|
|
12
|
+
sourceFile: ts.SourceFile;
|
|
13
|
+
basePath?: string;
|
|
14
|
+
inheritedTags?: string[];
|
|
15
|
+
inheritedAuth?: boolean;
|
|
16
|
+
}
|
|
11
17
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
18
|
+
* Shared function to register an HTTP route in the inspector state.
|
|
19
|
+
* Used by both wireHTTP and wireHTTPRoutes.
|
|
20
|
+
*/
|
|
21
|
+
export declare function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath, inheritedTags, inheritedAuth, }: RegisterHTTPRouteParams): void;
|
|
22
|
+
/**
|
|
23
|
+
* Process wireHTTP calls
|
|
14
24
|
*/
|
|
15
25
|
export declare const addHTTPRoute: AddWiring;
|