@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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { extractFunctionName } from '../utils/extract-function-name.js';
|
|
3
|
-
import {
|
|
4
|
-
import { getPropertyValue } from '../utils/get-property-value.js';
|
|
3
|
+
import { extractFunctionNode } from '../utils/extract-function-node.js';
|
|
4
|
+
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
5
5
|
import { resolveMiddleware } from '../utils/middleware.js';
|
|
6
|
+
import { resolvePermissions } from '../utils/permissions.js';
|
|
7
|
+
import { ErrorCode } from '../error-codes.js';
|
|
6
8
|
const isValidVariableName = (name) => {
|
|
7
9
|
const regex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
8
10
|
return regex.test(name);
|
|
@@ -109,7 +111,7 @@ const resolveUnionTypes = (checker, type) => {
|
|
|
109
111
|
// Check if it's a union type AND not part of an intersection
|
|
110
112
|
if (type.isUnion() && !(type.flags & ts.TypeFlags.Intersection)) {
|
|
111
113
|
for (const t of type.types) {
|
|
112
|
-
const name = nullifyTypes(checker.typeToString(t));
|
|
114
|
+
const name = nullifyTypes(checker.typeToString(t, undefined, ts.TypeFormatFlags.NoTruncation));
|
|
113
115
|
if (name) {
|
|
114
116
|
types.push(t);
|
|
115
117
|
names.push(name);
|
|
@@ -117,7 +119,7 @@ const resolveUnionTypes = (checker, type) => {
|
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
else {
|
|
120
|
-
const name = nullifyTypes(checker.typeToString(type));
|
|
122
|
+
const name = nullifyTypes(checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation));
|
|
121
123
|
if (name) {
|
|
122
124
|
types.push(type);
|
|
123
125
|
names.push(name);
|
|
@@ -231,44 +233,95 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
231
233
|
if (args.length === 0)
|
|
232
234
|
return;
|
|
233
235
|
const { pikkuFuncName, name, explicitName, exportedName } = extractFunctionName(node, checker, state.rootDir);
|
|
236
|
+
let title;
|
|
234
237
|
let tags;
|
|
238
|
+
let summary;
|
|
239
|
+
let description;
|
|
240
|
+
let errors;
|
|
235
241
|
let expose;
|
|
236
242
|
let internal;
|
|
237
|
-
let docs;
|
|
238
243
|
let objectNode;
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
if (!
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
|
|
244
|
+
// Extract the function node using shared utility
|
|
245
|
+
const firstArg = args[0];
|
|
246
|
+
const { funcNode: handlerNode, resolvedFunc, isDirectFunction, } = extractFunctionNode(firstArg, checker);
|
|
247
|
+
// Variables to hold zod schema references if provided
|
|
248
|
+
let inputZodSchemaRef = null;
|
|
249
|
+
let outputZodSchemaRef = null;
|
|
250
|
+
// Helper to resolve schema identifier to its actual source file
|
|
251
|
+
const resolveSchemaSourceFile = (identifier) => {
|
|
252
|
+
const symbol = checker.getSymbolAtLocation(identifier);
|
|
253
|
+
if (!symbol)
|
|
254
|
+
return null;
|
|
255
|
+
const decl = symbol.valueDeclaration || symbol.declarations?.[0];
|
|
256
|
+
if (!decl)
|
|
257
|
+
return null;
|
|
258
|
+
// If it's an import specifier, resolve the aliased symbol to get the actual source
|
|
259
|
+
if (ts.isImportSpecifier(decl)) {
|
|
260
|
+
const aliasedSymbol = checker.getAliasedSymbol(symbol);
|
|
261
|
+
if (aliasedSymbol) {
|
|
262
|
+
const aliasedDecl = aliasedSymbol.valueDeclaration || aliasedSymbol.declarations?.[0];
|
|
263
|
+
if (aliasedDecl) {
|
|
264
|
+
return {
|
|
265
|
+
variableName: identifier.text,
|
|
266
|
+
sourceFile: aliasedDecl.getSourceFile().fileName,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Not an import - use the current source file
|
|
272
|
+
return {
|
|
273
|
+
variableName: identifier.text,
|
|
274
|
+
sourceFile: decl.getSourceFile().fileName,
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
// Extract config properties if using object form
|
|
278
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
279
|
+
objectNode = firstArg;
|
|
280
|
+
const metadata = getCommonWireMetaData(firstArg, 'Function', name, logger);
|
|
281
|
+
title = metadata.title;
|
|
282
|
+
tags = metadata.tags;
|
|
283
|
+
summary = metadata.summary;
|
|
284
|
+
description = metadata.description;
|
|
285
|
+
errors = metadata.errors;
|
|
286
|
+
expose = getPropertyValue(firstArg, 'expose');
|
|
287
|
+
internal = getPropertyValue(firstArg, 'internal');
|
|
288
|
+
// Extract zod schema variable names from input/output properties
|
|
289
|
+
for (const prop of firstArg.properties) {
|
|
290
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
291
|
+
const propName = prop.name.text;
|
|
292
|
+
if (propName === 'input' || propName === 'output') {
|
|
293
|
+
if (ts.isIdentifier(prop.initializer)) {
|
|
294
|
+
// Good - it's a variable reference, resolve its actual source file
|
|
295
|
+
const ref = resolveSchemaSourceFile(prop.initializer);
|
|
296
|
+
if (ref) {
|
|
297
|
+
if (propName === 'input') {
|
|
298
|
+
inputZodSchemaRef = ref;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
outputZodSchemaRef = ref;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else if (ts.isCallExpression(prop.initializer)) {
|
|
306
|
+
// Bad - it's an inline expression
|
|
307
|
+
const schemaName = `${name.charAt(0).toUpperCase() + name.slice(1)}${propName.charAt(0).toUpperCase() + propName.slice(1)}`;
|
|
308
|
+
logger.critical(ErrorCode.INLINE_ZOD_SCHEMA, `Inline Zod schemas are not supported for '${propName}' in '${name}'.\n` +
|
|
309
|
+
` Extract to an exported variable:\n` +
|
|
310
|
+
` export const ${schemaName} = ${prop.initializer.getText()}\n` +
|
|
311
|
+
` Then use: ${propName}: ${schemaName}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
266
315
|
}
|
|
267
|
-
handlerNode = fnProp;
|
|
268
316
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
317
|
+
// Pick the handler: use resolvedFunc when it exists and is a function, otherwise fall back to handlerNode
|
|
318
|
+
const handler = resolvedFunc &&
|
|
319
|
+
(ts.isArrowFunction(resolvedFunc) || ts.isFunctionExpression(resolvedFunc))
|
|
320
|
+
? resolvedFunc
|
|
321
|
+
: handlerNode;
|
|
322
|
+
// Validate that we got a valid function
|
|
323
|
+
if (!ts.isArrowFunction(handler) && !ts.isFunctionExpression(handler)) {
|
|
324
|
+
logger.error(`• No valid 'func' property found for ${pikkuFuncName}.`);
|
|
272
325
|
// Create stub metadata to prevent "function not found" errors in wirings
|
|
273
326
|
state.functions.meta[pikkuFuncName] = {
|
|
274
327
|
pikkuFuncName,
|
|
@@ -286,7 +339,7 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
286
339
|
optimized: true,
|
|
287
340
|
services: [],
|
|
288
341
|
};
|
|
289
|
-
const firstParam =
|
|
342
|
+
const firstParam = handler.parameters[0];
|
|
290
343
|
if (firstParam) {
|
|
291
344
|
if (ts.isObjectBindingPattern(firstParam.name)) {
|
|
292
345
|
for (const elem of firstParam.name.elements) {
|
|
@@ -304,24 +357,64 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
304
357
|
services.optimized = false;
|
|
305
358
|
}
|
|
306
359
|
}
|
|
360
|
+
// --- Extract used wires from third parameter ---
|
|
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
|
+
}
|
|
307
375
|
// --- Generics → ts.Type[], unwrapped from Promise ---
|
|
308
376
|
const genericTypes = (typeArguments ?? [])
|
|
309
377
|
.map((tn) => checker.getTypeFromTypeNode(tn))
|
|
310
378
|
.map((t) => unwrapPromise(checker, t));
|
|
379
|
+
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
311
380
|
// --- Input Extraction ---
|
|
312
|
-
let
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
381
|
+
let inputNames = [];
|
|
382
|
+
let inputTypes = [];
|
|
383
|
+
if (inputZodSchemaRef) {
|
|
384
|
+
const schemaName = `${capitalizedName}Input`;
|
|
385
|
+
inputNames = [schemaName];
|
|
386
|
+
state.zodLookup.set(schemaName, inputZodSchemaRef);
|
|
387
|
+
state.functions.typesMap.addCustomType(schemaName, 'unknown', []);
|
|
388
|
+
}
|
|
389
|
+
else if (genericTypes.length >= 1 && genericTypes[0]) {
|
|
390
|
+
// Fall back to extracting from generic type arguments
|
|
391
|
+
const result = getNamesAndTypes(checker, state.functions.typesMap, 'Input', name, genericTypes[0]);
|
|
392
|
+
inputNames = result.names;
|
|
393
|
+
inputTypes = result.types;
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
// Fall back to extracting from the function's second parameter type
|
|
397
|
+
const secondParam = handler.parameters[1];
|
|
398
|
+
if (secondParam) {
|
|
399
|
+
const paramType = checker.getTypeAtLocation(secondParam);
|
|
400
|
+
const result = getNamesAndTypes(checker, state.functions.typesMap, 'Input', pikkuFuncName, paramType);
|
|
401
|
+
inputNames = result.names;
|
|
402
|
+
inputTypes = result.types;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
318
405
|
// --- Output Extraction ---
|
|
319
406
|
let outputNames = [];
|
|
320
|
-
if (
|
|
407
|
+
if (outputZodSchemaRef) {
|
|
408
|
+
const schemaName = `${capitalizedName}Output`;
|
|
409
|
+
outputNames = [schemaName];
|
|
410
|
+
state.zodLookup.set(schemaName, outputZodSchemaRef);
|
|
411
|
+
state.functions.typesMap.addCustomType(schemaName, 'unknown', []);
|
|
412
|
+
}
|
|
413
|
+
else if (genericTypes.length >= 2) {
|
|
321
414
|
outputNames = getNamesAndTypes(checker, state.functions.typesMap, 'Output', name, genericTypes[1]).names;
|
|
322
415
|
}
|
|
323
416
|
else {
|
|
324
|
-
const sig = checker.getSignatureFromDeclaration(
|
|
417
|
+
const sig = checker.getSignatureFromDeclaration(handler);
|
|
325
418
|
if (sig) {
|
|
326
419
|
const rawRet = checker.getReturnTypeOfSignature(sig);
|
|
327
420
|
const unwrapped = unwrapPromise(checker, rawRet);
|
|
@@ -331,29 +424,38 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
331
424
|
if (inputNames.length > 1) {
|
|
332
425
|
logger.warn('More than one input type detected, only the first one will be used as a schema.');
|
|
333
426
|
}
|
|
427
|
+
// Store the input type for later use
|
|
428
|
+
if (inputTypes.length > 0) {
|
|
429
|
+
state.typesLookup.set(pikkuFuncName, inputTypes);
|
|
430
|
+
}
|
|
334
431
|
// --- resolve middleware ---
|
|
335
432
|
const middleware = objectNode
|
|
336
433
|
? resolveMiddleware(state, objectNode, tags, checker)
|
|
337
434
|
: undefined;
|
|
435
|
+
// --- resolve permissions ---
|
|
436
|
+
const permissions = objectNode
|
|
437
|
+
? resolvePermissions(state, objectNode, tags, checker)
|
|
438
|
+
: undefined;
|
|
338
439
|
state.functions.meta[pikkuFuncName] = {
|
|
339
440
|
pikkuFuncName,
|
|
340
441
|
name,
|
|
341
442
|
services,
|
|
443
|
+
usedWires: usedWires.length > 0 ? usedWires : undefined,
|
|
342
444
|
inputSchemaName: inputNames[0] ?? null,
|
|
343
445
|
outputSchemaName: outputNames[0] ?? null,
|
|
344
446
|
inputs: inputNames.filter((n) => n !== 'void') ?? null,
|
|
345
447
|
outputs: outputNames.filter((n) => n !== 'void') ?? null,
|
|
346
448
|
expose: expose || undefined,
|
|
347
449
|
internal: internal || undefined,
|
|
450
|
+
title,
|
|
348
451
|
tags: tags || undefined,
|
|
349
|
-
|
|
350
|
-
|
|
452
|
+
summary,
|
|
453
|
+
description,
|
|
454
|
+
errors,
|
|
351
455
|
middleware,
|
|
456
|
+
permissions,
|
|
457
|
+
isDirectFunction,
|
|
352
458
|
};
|
|
353
|
-
// Store the input type for later use
|
|
354
|
-
if (inputTypes.length > 0) {
|
|
355
|
-
state.typesLookup.set(pikkuFuncName, inputTypes);
|
|
356
|
-
}
|
|
357
459
|
// Store function file location for wiring generation
|
|
358
460
|
if (exportedName) {
|
|
359
461
|
state.functions.files.set(pikkuFuncName, {
|
|
@@ -361,6 +463,11 @@ export const addFunctions = (logger, node, checker, state) => {
|
|
|
361
463
|
exportedName,
|
|
362
464
|
});
|
|
363
465
|
}
|
|
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
|
+
}
|
|
364
471
|
if (exportedName || explicitName) {
|
|
365
472
|
if (!exportedName) {
|
|
366
473
|
logger.error(`• Function with explicit name '${name}' is not exported, this is not allowed.`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { getPropertyValue,
|
|
2
|
+
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
3
3
|
import { pathToRegexp } from 'path-to-regexp';
|
|
4
4
|
import { extractFunctionName } from '../utils/extract-function-name.js';
|
|
5
5
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
|
|
@@ -47,9 +47,30 @@ export const addHTTPRoute = (logger, node, checker, state, options) => {
|
|
|
47
47
|
const keys = pathToRegexp(route).keys;
|
|
48
48
|
const params = keys.filter((k) => k.type === 'param').map((k) => k.name);
|
|
49
49
|
const method = getPropertyValue(obj, 'method')?.toLowerCase() || 'get';
|
|
50
|
-
const
|
|
51
|
-
const tags = getPropertyTags(obj, 'HTTP route', route, logger);
|
|
50
|
+
const { title, tags, summary, description, errors } = getCommonWireMetaData(obj, 'HTTP route', route, logger);
|
|
52
51
|
const query = getPropertyValue(obj, 'query') || [];
|
|
52
|
+
// Check if this is a workflow trigger (workflow: true)
|
|
53
|
+
const isWorkflowTrigger = getPropertyValue(obj, 'workflow') === true;
|
|
54
|
+
if (isWorkflowTrigger) {
|
|
55
|
+
// Workflow triggers don't need func - they're handled by workflow-utils
|
|
56
|
+
// Just record the route for HTTP meta but skip function processing
|
|
57
|
+
state.http.files.add(node.getSourceFile().fileName);
|
|
58
|
+
state.http.meta[method][route] = {
|
|
59
|
+
pikkuFuncName: '', // No function - workflow handles it
|
|
60
|
+
route,
|
|
61
|
+
method: method,
|
|
62
|
+
params: params.length > 0 ? params : undefined,
|
|
63
|
+
query: query.length > 0 ? query : undefined,
|
|
64
|
+
inputTypes: undefined,
|
|
65
|
+
title,
|
|
66
|
+
summary,
|
|
67
|
+
description,
|
|
68
|
+
errors,
|
|
69
|
+
tags,
|
|
70
|
+
workflow: true,
|
|
71
|
+
};
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
53
74
|
// --- find the referenced function name first for filtering ---
|
|
54
75
|
const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
|
|
55
76
|
if (!funcInitializer) {
|
|
@@ -85,7 +106,10 @@ export const addHTTPRoute = (logger, node, checker, state, options) => {
|
|
|
85
106
|
params: params.length > 0 ? params : undefined,
|
|
86
107
|
query: query.length > 0 ? query : undefined,
|
|
87
108
|
inputTypes,
|
|
88
|
-
|
|
109
|
+
title,
|
|
110
|
+
summary,
|
|
111
|
+
description,
|
|
112
|
+
errors,
|
|
89
113
|
tags,
|
|
90
114
|
middleware,
|
|
91
115
|
permissions,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { getPropertyValue,
|
|
2
|
+
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
3
3
|
import { extractWireNames } from '../utils/post-process.js';
|
|
4
4
|
import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js';
|
|
5
5
|
import { extractFunctionName } from '../utils/extract-function-name.js';
|
|
@@ -24,8 +24,7 @@ export const addMCPPrompt = (logger, node, checker, state, options) => {
|
|
|
24
24
|
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
25
25
|
const obj = firstArg;
|
|
26
26
|
const nameValue = getPropertyValue(obj, 'name');
|
|
27
|
-
const
|
|
28
|
-
const tags = getPropertyTags(obj, 'MCP prompt', nameValue, logger);
|
|
27
|
+
const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'MCP prompt', nameValue, logger);
|
|
29
28
|
const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
|
|
30
29
|
if (!funcInitializer) {
|
|
31
30
|
logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for MCP prompt '${nameValue}'.`);
|
|
@@ -38,7 +37,7 @@ export const addMCPPrompt = (logger, node, checker, state, options) => {
|
|
|
38
37
|
logger.critical(ErrorCode.MISSING_NAME, "MCP prompt is missing the required 'name' property.");
|
|
39
38
|
return;
|
|
40
39
|
}
|
|
41
|
-
if (!
|
|
40
|
+
if (!description) {
|
|
42
41
|
logger.critical(ErrorCode.MISSING_DESCRIPTION, `MCP prompt '${nameValue}' is missing a description.`);
|
|
43
42
|
return;
|
|
44
43
|
}
|
|
@@ -62,7 +61,9 @@ export const addMCPPrompt = (logger, node, checker, state, options) => {
|
|
|
62
61
|
state.mcpEndpoints.promptsMeta[nameValue] = {
|
|
63
62
|
pikkuFuncName,
|
|
64
63
|
name: nameValue,
|
|
65
|
-
description
|
|
64
|
+
description,
|
|
65
|
+
summary,
|
|
66
|
+
errors,
|
|
66
67
|
tags,
|
|
67
68
|
inputSchema,
|
|
68
69
|
outputSchema,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { getPropertyValue,
|
|
2
|
+
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
3
3
|
import { extractWireNames } from '../utils/post-process.js';
|
|
4
4
|
import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js';
|
|
5
5
|
import { extractFunctionName } from '../utils/extract-function-name.js';
|
|
@@ -25,9 +25,8 @@ export const addMCPResource = (logger, node, checker, state, options) => {
|
|
|
25
25
|
const obj = firstArg;
|
|
26
26
|
const uriValue = getPropertyValue(obj, 'uri');
|
|
27
27
|
const titleValue = getPropertyValue(obj, 'title');
|
|
28
|
-
const
|
|
28
|
+
const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'MCP resource', uriValue, logger);
|
|
29
29
|
const streamingValue = getPropertyValue(obj, 'streaming');
|
|
30
|
-
const tags = getPropertyTags(obj, 'MCP resource', uriValue, logger);
|
|
31
30
|
if (streamingValue === true) {
|
|
32
31
|
logger.warn(`MCP resource '${uriValue}' has streaming enabled, but streaming is not yet supported.`);
|
|
33
32
|
}
|
|
@@ -47,7 +46,7 @@ export const addMCPResource = (logger, node, checker, state, options) => {
|
|
|
47
46
|
logger.critical(ErrorCode.MISSING_TITLE, `MCP resource '${uriValue}' is missing the required 'title' property.`);
|
|
48
47
|
return;
|
|
49
48
|
}
|
|
50
|
-
if (!
|
|
49
|
+
if (!description) {
|
|
51
50
|
logger.critical(ErrorCode.MISSING_DESCRIPTION, `MCP resource '${uriValue}' is missing a description.`);
|
|
52
51
|
return;
|
|
53
52
|
}
|
|
@@ -72,7 +71,9 @@ export const addMCPResource = (logger, node, checker, state, options) => {
|
|
|
72
71
|
pikkuFuncName,
|
|
73
72
|
uri: uriValue,
|
|
74
73
|
title: titleValue,
|
|
75
|
-
description
|
|
74
|
+
description,
|
|
75
|
+
summary,
|
|
76
|
+
errors,
|
|
76
77
|
...(streamingValue !== null && { streaming: streamingValue }),
|
|
77
78
|
tags,
|
|
78
79
|
inputSchema,
|
package/dist/add/add-mcp-tool.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { getPropertyValue,
|
|
2
|
+
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
3
3
|
import { extractWireNames } from '../utils/post-process.js';
|
|
4
4
|
import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js';
|
|
5
5
|
import { extractFunctionName } from '../utils/extract-function-name.js';
|
|
@@ -25,9 +25,8 @@ export const addMCPTool = (logger, node, checker, state, options) => {
|
|
|
25
25
|
const obj = firstArg;
|
|
26
26
|
const nameValue = getPropertyValue(obj, 'name');
|
|
27
27
|
const titleValue = getPropertyValue(obj, 'title');
|
|
28
|
-
const
|
|
28
|
+
const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'MCP tool', nameValue, logger);
|
|
29
29
|
const streamingValue = getPropertyValue(obj, 'streaming');
|
|
30
|
-
const tags = getPropertyTags(obj, 'MCP tool', nameValue, logger);
|
|
31
30
|
if (streamingValue === true) {
|
|
32
31
|
logger.warn(`MCP tool '${nameValue}' has streaming enabled, but streaming is not yet supported.`);
|
|
33
32
|
}
|
|
@@ -43,7 +42,7 @@ export const addMCPTool = (logger, node, checker, state, options) => {
|
|
|
43
42
|
logger.critical(ErrorCode.MISSING_NAME, "MCP tool is missing the required 'name' property.");
|
|
44
43
|
return;
|
|
45
44
|
}
|
|
46
|
-
if (!
|
|
45
|
+
if (!description) {
|
|
47
46
|
logger.critical(ErrorCode.MISSING_DESCRIPTION, `MCP tool '${nameValue}' is missing a description.`);
|
|
48
47
|
return;
|
|
49
48
|
}
|
|
@@ -68,7 +67,9 @@ export const addMCPTool = (logger, node, checker, state, options) => {
|
|
|
68
67
|
pikkuFuncName,
|
|
69
68
|
name: nameValue,
|
|
70
69
|
title: titleValue || undefined,
|
|
71
|
-
description
|
|
70
|
+
description,
|
|
71
|
+
summary,
|
|
72
|
+
errors,
|
|
72
73
|
...(streamingValue !== null && { streaming: streamingValue }),
|
|
73
74
|
tags,
|
|
74
75
|
inputSchema,
|
|
@@ -92,7 +92,7 @@ export const addMiddleware = (logger, node, checker, state) => {
|
|
|
92
92
|
}
|
|
93
93
|
else {
|
|
94
94
|
// No pikkuMiddleware wrapper found - extract from factory's return value directly
|
|
95
|
-
// Factory pattern: (config) => (services,
|
|
95
|
+
// Factory pattern: (config) => (services, wire, next) => { ... }
|
|
96
96
|
if (ts.isArrowFunction(factoryNode) ||
|
|
97
97
|
ts.isFunctionExpression(factoryNode)) {
|
|
98
98
|
const factoryBody = factoryNode.body;
|
|
@@ -92,7 +92,7 @@ export const addPermission = (logger, node, checker, state) => {
|
|
|
92
92
|
}
|
|
93
93
|
else {
|
|
94
94
|
// No pikkuPermission wrapper found - extract from factory's return value directly
|
|
95
|
-
// Factory pattern: (config) => (services, data,
|
|
95
|
+
// Factory pattern: (config) => (services, data, wire) => { ... }
|
|
96
96
|
if (ts.isArrowFunction(factoryNode) ||
|
|
97
97
|
ts.isFunctionExpression(factoryNode)) {
|
|
98
98
|
const factoryBody = factoryNode.body;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { getPropertyValue,
|
|
2
|
+
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
3
3
|
import { extractFunctionName } from '../utils/extract-function-name.js';
|
|
4
4
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
|
|
5
5
|
import { resolveMiddleware } from '../utils/middleware.js';
|
|
6
6
|
import { extractWireNames } from '../utils/post-process.js';
|
|
7
7
|
import { ErrorCode } from '../error-codes.js';
|
|
8
|
-
export const addQueueWorker = (logger, node, checker, state
|
|
8
|
+
export const addQueueWorker = (logger, node, checker, state) => {
|
|
9
9
|
if (!ts.isCallExpression(node)) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
@@ -22,8 +22,7 @@ export const addQueueWorker = (logger, node, checker, state, options) => {
|
|
|
22
22
|
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
23
23
|
const obj = firstArg;
|
|
24
24
|
const queueName = getPropertyValue(obj, 'queueName');
|
|
25
|
-
const
|
|
26
|
-
const tags = getPropertyTags(obj, 'Queue worker', queueName, logger);
|
|
25
|
+
const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'Queue worker', queueName, logger);
|
|
27
26
|
// --- find the referenced function ---
|
|
28
27
|
const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
|
|
29
28
|
if (!funcInitializer) {
|
|
@@ -44,7 +43,9 @@ export const addQueueWorker = (logger, node, checker, state, options) => {
|
|
|
44
43
|
state.queueWorkers.meta[queueName] = {
|
|
45
44
|
pikkuFuncName,
|
|
46
45
|
queueName,
|
|
47
|
-
|
|
46
|
+
summary,
|
|
47
|
+
description,
|
|
48
|
+
errors,
|
|
48
49
|
tags,
|
|
49
50
|
middleware,
|
|
50
51
|
};
|
|
@@ -2,5 +2,8 @@ import * as ts from 'typescript';
|
|
|
2
2
|
import { InspectorState, InspectorLogger } from '../types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Scan for rpc.invoke() calls to track which functions are actually being invoked
|
|
5
|
+
* Also detects external package usage via:
|
|
6
|
+
* - Namespaced calls: rpc.invoke('namespace:function')
|
|
7
|
+
* - External helper: external('namespace:function')
|
|
5
8
|
*/
|
|
6
9
|
export declare function addRPCInvocations(node: ts.Node, state: InspectorState, logger: InspectorLogger): void;
|
|
@@ -1,35 +1,61 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Helper to extract namespace from a namespaced function reference like 'ext:hello'
|
|
4
|
+
*/
|
|
5
|
+
function extractNamespace(functionRef) {
|
|
6
|
+
const colonIndex = functionRef.indexOf(':');
|
|
7
|
+
if (colonIndex !== -1) {
|
|
8
|
+
return functionRef.substring(0, colonIndex);
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* Scan for rpc.invoke() calls to track which functions are actually being invoked
|
|
14
|
+
* Also detects external package usage via:
|
|
15
|
+
* - Namespaced calls: rpc.invoke('namespace:function')
|
|
16
|
+
* - External helper: external('namespace:function')
|
|
4
17
|
*/
|
|
5
18
|
export function addRPCInvocations(node, state, logger) {
|
|
6
|
-
// Look for
|
|
7
|
-
if (ts.
|
|
8
|
-
const { expression,
|
|
9
|
-
// Check
|
|
10
|
-
if (
|
|
11
|
-
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
// Look for call expressions: external('ext:hello') or rpc.invoke('...')
|
|
20
|
+
if (ts.isCallExpression(node)) {
|
|
21
|
+
const { expression, arguments: args } = node;
|
|
22
|
+
// Check for external('namespace:function') calls
|
|
23
|
+
if (ts.isIdentifier(expression) && expression.text === 'external') {
|
|
24
|
+
const [firstArg] = args;
|
|
25
|
+
if (firstArg && ts.isStringLiteral(firstArg)) {
|
|
26
|
+
const functionRef = firstArg.text;
|
|
27
|
+
logger.debug(`• Found external() call: ${functionRef}`);
|
|
28
|
+
state.rpc.invokedFunctions.add(functionRef);
|
|
29
|
+
const namespace = extractNamespace(functionRef);
|
|
30
|
+
if (namespace) {
|
|
31
|
+
logger.debug(` → External package detected: ${namespace}`);
|
|
32
|
+
state.rpc.usedExternalPackages.add(namespace);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Check for rpc.invoke('...') calls
|
|
37
|
+
if (ts.isPropertyAccessExpression(expression) &&
|
|
38
|
+
expression.name.text === 'invoke' &&
|
|
39
|
+
ts.isIdentifier(expression.expression) &&
|
|
40
|
+
expression.expression.text === 'rpc') {
|
|
41
|
+
const [firstArg] = args;
|
|
42
|
+
if (firstArg) {
|
|
43
|
+
if (ts.isStringLiteral(firstArg)) {
|
|
44
|
+
const functionRef = firstArg.text;
|
|
45
|
+
logger.debug(`• Found RPC invocation: ${functionRef}`);
|
|
46
|
+
state.rpc.invokedFunctions.add(functionRef);
|
|
47
|
+
const namespace = extractNamespace(functionRef);
|
|
48
|
+
if (namespace) {
|
|
49
|
+
logger.debug(` → External package detected: ${namespace}`);
|
|
50
|
+
state.rpc.usedExternalPackages.add(namespace);
|
|
31
51
|
}
|
|
32
52
|
}
|
|
53
|
+
// Handle template literals like `function-${name}`
|
|
54
|
+
else if (ts.isTemplateExpression(firstArg) ||
|
|
55
|
+
ts.isNoSubstitutionTemplateLiteral(firstArg)) {
|
|
56
|
+
logger.warn(`• Found dynamic RPC invocation: ${firstArg.getText()}`);
|
|
57
|
+
logger.warn(`\tYou can only use string literals for RPC function names, with ' or " and not \``);
|
|
58
|
+
}
|
|
33
59
|
}
|
|
34
60
|
}
|
|
35
61
|
}
|
package/dist/add/add-schedule.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import { getPropertyValue,
|
|
2
|
+
import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
|
|
3
3
|
import { extractFunctionName } from '../utils/extract-function-name.js';
|
|
4
4
|
import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
|
|
5
5
|
import { resolveMiddleware } from '../utils/middleware.js';
|
|
@@ -23,8 +23,7 @@ export const addSchedule = (logger, node, checker, state, options) => {
|
|
|
23
23
|
const obj = firstArg;
|
|
24
24
|
const nameValue = getPropertyValue(obj, 'name');
|
|
25
25
|
const scheduleValue = getPropertyValue(obj, 'schedule');
|
|
26
|
-
const
|
|
27
|
-
const tags = getPropertyTags(obj, 'Scheduler', nameValue, logger);
|
|
26
|
+
const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'Scheduler', nameValue, logger);
|
|
28
27
|
const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
|
|
29
28
|
if (!funcInitializer) {
|
|
30
29
|
logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for scheduled task '${nameValue}'.`);
|
|
@@ -44,7 +43,9 @@ export const addSchedule = (logger, node, checker, state, options) => {
|
|
|
44
43
|
pikkuFuncName,
|
|
45
44
|
name: nameValue,
|
|
46
45
|
schedule: scheduleValue,
|
|
47
|
-
|
|
46
|
+
summary,
|
|
47
|
+
description,
|
|
48
|
+
errors,
|
|
48
49
|
tags,
|
|
49
50
|
middleware,
|
|
50
51
|
};
|