@pikku/inspector 0.12.2 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/add/add-ai-agent.js +4 -0
- package/dist/add/add-approval-description.d.ts +5 -0
- package/dist/add/add-approval-description.js +52 -0
- package/dist/add/add-channel.js +42 -4
- package/dist/add/add-cli.js +73 -13
- package/dist/add/add-file-with-factory.js +1 -0
- package/dist/add/add-functions.js +22 -3
- package/dist/add/add-gateway.js +5 -0
- package/dist/add/add-http-route.js +5 -0
- package/dist/add/add-mcp-prompt.js +5 -0
- package/dist/add/add-mcp-resource.js +5 -0
- package/dist/add/add-queue-worker.js +5 -0
- package/dist/add/add-schedule.js +5 -0
- package/dist/add/add-wire-addon.js +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/inspector.js +7 -0
- package/dist/types.d.ts +10 -0
- package/dist/utils/load-addon-functions-meta.d.ts +12 -0
- package/dist/utils/load-addon-functions-meta.js +76 -0
- package/dist/utils/post-process.js +26 -0
- package/dist/utils/resolve-function-meta.d.ts +11 -0
- package/dist/utils/resolve-function-meta.js +17 -0
- package/dist/utils/serialize-inspector-state.d.ts +2 -0
- package/dist/utils/serialize-inspector-state.js +4 -0
- package/dist/utils/serialize-mcp-json.js +13 -7
- package/dist/visit.js +2 -0
- package/package.json +2 -2
- package/src/add/add-ai-agent.ts +6 -0
- package/src/add/add-approval-description.ts +76 -0
- package/src/add/add-channel.ts +44 -4
- package/src/add/add-cli.ts +108 -21
- package/src/add/add-file-with-factory.ts +1 -0
- package/src/add/add-functions.ts +28 -3
- package/src/add/add-gateway.ts +6 -0
- package/src/add/add-http-route.ts +6 -0
- package/src/add/add-mcp-prompt.ts +6 -0
- package/src/add/add-mcp-resource.ts +6 -0
- package/src/add/add-queue-worker.ts +6 -0
- package/src/add/add-schedule.ts +6 -0
- package/src/add/add-wire-addon.ts +8 -0
- package/src/index.ts +1 -0
- package/src/inspector.ts +12 -0
- package/src/types.ts +11 -0
- package/src/utils/load-addon-functions-meta.ts +94 -0
- package/src/utils/post-process.ts +25 -0
- package/src/utils/resolve-function-meta.ts +25 -0
- package/src/utils/serialize-inspector-state.ts +6 -0
- package/src/utils/serialize-mcp-json.ts +12 -7
- package/src/visit.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -73,6 +73,7 @@ export interface InspectorFunctionState {
|
|
|
73
73
|
path: string;
|
|
74
74
|
exportedName: string;
|
|
75
75
|
}>;
|
|
76
|
+
approvalDescriptions: Record<string, InspectorApprovalDescriptionDefinition>;
|
|
76
77
|
}
|
|
77
78
|
export interface InspectorChannelState {
|
|
78
79
|
meta: ChannelsMeta;
|
|
@@ -108,6 +109,13 @@ export interface InspectorChannelMiddlewareState {
|
|
|
108
109
|
export interface InspectorAIMiddlewareState {
|
|
109
110
|
definitions: Record<string, InspectorMiddlewareDefinition>;
|
|
110
111
|
}
|
|
112
|
+
export interface InspectorApprovalDescriptionDefinition {
|
|
113
|
+
services: FunctionServicesMeta;
|
|
114
|
+
wires?: FunctionWiresMeta;
|
|
115
|
+
sourceFile: string;
|
|
116
|
+
position: number;
|
|
117
|
+
exportedName: string | null;
|
|
118
|
+
}
|
|
111
119
|
export interface InspectorPermissionDefinition {
|
|
112
120
|
services: FunctionServicesMeta;
|
|
113
121
|
wires?: FunctionWiresMeta;
|
|
@@ -309,6 +317,7 @@ export interface InspectorState {
|
|
|
309
317
|
wireAddonDeclarations: Map<string, {
|
|
310
318
|
package: string;
|
|
311
319
|
rpcEndpoint?: string;
|
|
320
|
+
mcp?: boolean;
|
|
312
321
|
secretOverrides?: Record<string, string>;
|
|
313
322
|
variableOverrides?: Record<string, string>;
|
|
314
323
|
}>;
|
|
@@ -383,4 +392,5 @@ export interface InspectorState {
|
|
|
383
392
|
requiredSchemas: Set<string>;
|
|
384
393
|
openAPISpec: Record<string, any> | null;
|
|
385
394
|
diagnostics: InspectorDiagnostic[];
|
|
395
|
+
addonFunctions: Record<string, FunctionsMeta>;
|
|
386
396
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { InspectorState, InspectorLogger } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* After the setup sweep discovers wireAddon() declarations, load each addon
|
|
4
|
+
* package's function metadata so that wiring handlers (channels, HTTP routes,
|
|
5
|
+
* schedules, etc.) can look up addon function types during the routes sweep.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadAddonFunctionsMeta(logger: InspectorLogger, state: InspectorState): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Load addon schemas into state.schemas. Called after generateAllSchemas
|
|
10
|
+
* to ensure addon schemas aren't overwritten.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadAddonSchemas(logger: InspectorLogger, state: InspectorState): Promise<void>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFile, readdir } from 'fs/promises';
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* After the setup sweep discovers wireAddon() declarations, load each addon
|
|
6
|
+
* package's function metadata so that wiring handlers (channels, HTTP routes,
|
|
7
|
+
* schedules, etc.) can look up addon function types during the routes sweep.
|
|
8
|
+
*/
|
|
9
|
+
export async function loadAddonFunctionsMeta(logger, state) {
|
|
10
|
+
const { wireAddonDeclarations } = state.rpc;
|
|
11
|
+
if (wireAddonDeclarations.size === 0)
|
|
12
|
+
return;
|
|
13
|
+
const require = createRequire(join(state.rootDir, 'package.json'));
|
|
14
|
+
for (const [namespace, decl] of wireAddonDeclarations) {
|
|
15
|
+
try {
|
|
16
|
+
const metaPath = require.resolve(`${decl.package}/.pikku/function/pikku-functions-meta.gen.json`);
|
|
17
|
+
const raw = await readFile(metaPath, 'utf-8');
|
|
18
|
+
const meta = JSON.parse(raw);
|
|
19
|
+
state.addonFunctions[namespace] = meta;
|
|
20
|
+
logger.debug(`Loaded ${Object.keys(meta).length} addon functions for '${namespace}' from ${decl.package}`);
|
|
21
|
+
// If wireAddon has mcp: true, expose addon functions with mcp: true as MCP tools
|
|
22
|
+
if (decl.mcp) {
|
|
23
|
+
for (const [funcName, funcMeta] of Object.entries(meta)) {
|
|
24
|
+
if (funcMeta.mcp) {
|
|
25
|
+
const toolName = `${namespace}:${funcName}`;
|
|
26
|
+
state.mcpEndpoints.toolsMeta[toolName] = {
|
|
27
|
+
pikkuFuncId: `${namespace}:${funcName}`,
|
|
28
|
+
name: toolName,
|
|
29
|
+
description: funcMeta.description || funcMeta.title || funcName,
|
|
30
|
+
inputSchema: funcMeta.inputSchemaName ?? null,
|
|
31
|
+
outputSchema: funcMeta.outputSchemaName ?? null,
|
|
32
|
+
tags: funcMeta.tags,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
logger.warn(`Failed to load addon function metadata for '${namespace}' (${decl.package}): ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load addon schemas into state.schemas. Called after generateAllSchemas
|
|
45
|
+
* to ensure addon schemas aren't overwritten.
|
|
46
|
+
*/
|
|
47
|
+
export async function loadAddonSchemas(logger, state) {
|
|
48
|
+
const { wireAddonDeclarations } = state.rpc;
|
|
49
|
+
if (wireAddonDeclarations.size === 0)
|
|
50
|
+
return;
|
|
51
|
+
const require = createRequire(join(state.rootDir, 'package.json'));
|
|
52
|
+
for (const [namespace, decl] of wireAddonDeclarations) {
|
|
53
|
+
try {
|
|
54
|
+
const metaPath = require.resolve(`${decl.package}/.pikku/function/pikku-functions-meta.gen.json`);
|
|
55
|
+
const schemasDir = join(dirname(metaPath), '..', 'schemas', 'schemas');
|
|
56
|
+
try {
|
|
57
|
+
const schemaFiles = await readdir(schemasDir);
|
|
58
|
+
for (const file of schemaFiles) {
|
|
59
|
+
if (!file.endsWith('.schema.json'))
|
|
60
|
+
continue;
|
|
61
|
+
const schemaName = file.replace('.schema.json', '');
|
|
62
|
+
if (!state.schemas[schemaName]) {
|
|
63
|
+
const schemaRaw = await readFile(join(schemasDir, file), 'utf-8');
|
|
64
|
+
state.schemas[schemaName] = JSON.parse(schemaRaw);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// No schemas directory — that's fine
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logger.warn(`Failed to load addon schemas for '${namespace}' (${decl.package}): ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -217,6 +217,32 @@ export function computeResolvedIOTypes(state) {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
state.resolvedIOTypes[pikkuFuncId] = { inputType, outputType };
|
|
220
|
+
if (meta.inputSchemaName && inputType !== 'null') {
|
|
221
|
+
meta.inputSchemaName = inputType;
|
|
222
|
+
}
|
|
223
|
+
if (meta.outputSchemaName && outputType !== 'null') {
|
|
224
|
+
meta.outputSchemaName = outputType;
|
|
225
|
+
}
|
|
226
|
+
if (meta.inputs) {
|
|
227
|
+
meta.inputs = meta.inputs.map((name) => {
|
|
228
|
+
try {
|
|
229
|
+
return functions.typesMap.getTypeMeta(name).uniqueName;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return name;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (meta.outputs) {
|
|
237
|
+
meta.outputs = meta.outputs.map((name) => {
|
|
238
|
+
try {
|
|
239
|
+
return functions.typesMap.getTypeMeta(name).uniqueName;
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return name;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
220
246
|
}
|
|
221
247
|
}
|
|
222
248
|
const serializeGroupMap = (groupMap) => {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FunctionMeta, FunctionsMeta } from '@pikku/core';
|
|
2
|
+
/**
|
|
3
|
+
* Look up function metadata by pikkuFuncId, checking both local functions
|
|
4
|
+
* and addon functions. Addon functions use namespaced IDs like 'namespace:funcName'.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveFunctionMeta(state: {
|
|
7
|
+
functions: {
|
|
8
|
+
meta: FunctionsMeta;
|
|
9
|
+
};
|
|
10
|
+
addonFunctions: Record<string, FunctionsMeta>;
|
|
11
|
+
}, pikkuFuncId: string): FunctionMeta | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Look up function metadata by pikkuFuncId, checking both local functions
|
|
3
|
+
* and addon functions. Addon functions use namespaced IDs like 'namespace:funcName'.
|
|
4
|
+
*/
|
|
5
|
+
export function resolveFunctionMeta(state, pikkuFuncId) {
|
|
6
|
+
// Check local functions first
|
|
7
|
+
const local = state.functions.meta[pikkuFuncId];
|
|
8
|
+
if (local)
|
|
9
|
+
return local;
|
|
10
|
+
// Check addon functions (namespaced like 'swaggerPetstore:addPet')
|
|
11
|
+
const colonIndex = pikkuFuncId.indexOf(':');
|
|
12
|
+
if (colonIndex === -1)
|
|
13
|
+
return undefined;
|
|
14
|
+
const namespace = pikkuFuncId.substring(0, colonIndex);
|
|
15
|
+
const funcName = pikkuFuncId.substring(colonIndex + 1);
|
|
16
|
+
return state.addonFunctions[namespace]?.[funcName];
|
|
17
|
+
}
|
|
@@ -100,6 +100,7 @@ export interface SerializableInspectorState {
|
|
|
100
100
|
path: string;
|
|
101
101
|
exportedName: string;
|
|
102
102
|
}]>;
|
|
103
|
+
approvalDescriptions: InspectorState['functions']['approvalDescriptions'];
|
|
103
104
|
};
|
|
104
105
|
http: {
|
|
105
106
|
metaInputTypes: Array<[
|
|
@@ -253,6 +254,7 @@ export interface SerializableInspectorState {
|
|
|
253
254
|
requiredSchemas: string[];
|
|
254
255
|
openAPISpec: Record<string, any> | null;
|
|
255
256
|
diagnostics: InspectorDiagnostic[];
|
|
257
|
+
addonFunctions: InspectorState['addonFunctions'];
|
|
256
258
|
}
|
|
257
259
|
/**
|
|
258
260
|
* Serializes InspectorState to a JSON-compatible format
|
|
@@ -32,6 +32,7 @@ export function serializeInspectorState(state) {
|
|
|
32
32
|
typesMap: serializeTypesMap(state.functions.typesMap),
|
|
33
33
|
meta: state.functions.meta,
|
|
34
34
|
files: Array.from(state.functions.files.entries()),
|
|
35
|
+
approvalDescriptions: state.functions.approvalDescriptions,
|
|
35
36
|
},
|
|
36
37
|
http: {
|
|
37
38
|
metaInputTypes: Array.from(state.http.metaInputTypes.entries()),
|
|
@@ -137,6 +138,7 @@ export function serializeInspectorState(state) {
|
|
|
137
138
|
requiredSchemas: Array.from(state.requiredSchemas),
|
|
138
139
|
openAPISpec: state.openAPISpec,
|
|
139
140
|
diagnostics: state.diagnostics,
|
|
141
|
+
addonFunctions: state.addonFunctions,
|
|
140
142
|
};
|
|
141
143
|
}
|
|
142
144
|
/**
|
|
@@ -173,6 +175,7 @@ export function deserializeInspectorState(data) {
|
|
|
173
175
|
typesMap: deserializeTypesMap(data.functions.typesMap),
|
|
174
176
|
meta: data.functions.meta,
|
|
175
177
|
files: new Map(data.functions.files),
|
|
178
|
+
approvalDescriptions: data.functions.approvalDescriptions || {},
|
|
176
179
|
},
|
|
177
180
|
http: {
|
|
178
181
|
metaInputTypes: new Map(data.http.metaInputTypes),
|
|
@@ -288,5 +291,6 @@ export function deserializeInspectorState(data) {
|
|
|
288
291
|
requiredSchemas: new Set(data.requiredSchemas || []),
|
|
289
292
|
openAPISpec: data.openAPISpec || null,
|
|
290
293
|
diagnostics: data.diagnostics || [],
|
|
294
|
+
addonFunctions: data.addonFunctions || {},
|
|
291
295
|
};
|
|
292
296
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { resolveFunctionMeta } from './resolve-function-meta.js';
|
|
1
2
|
export const serializeMCPJson = (logger, state) => {
|
|
2
3
|
const { mcpEndpoints, functions, schemas } = state;
|
|
3
|
-
const {
|
|
4
|
+
const { typesMap } = functions;
|
|
4
5
|
const { resourcesMeta, toolsMeta, promptsMeta } = mcpEndpoints;
|
|
5
6
|
const tools = [];
|
|
6
7
|
const resources = [];
|
|
@@ -19,9 +20,14 @@ export const serializeMCPJson = (logger, state) => {
|
|
|
19
20
|
].includes(typeName)) {
|
|
20
21
|
return undefined;
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
// Try local typesMap first, fall back to direct schema lookup (for addon types)
|
|
24
|
+
let uniqueName;
|
|
25
|
+
try {
|
|
26
|
+
uniqueName = typesMap.getUniqueName(typeName);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Type not in local typesMap — try direct schema lookup (addon schemas)
|
|
30
|
+
uniqueName = typeName;
|
|
25
31
|
}
|
|
26
32
|
const schema = schemas[uniqueName];
|
|
27
33
|
if (!schema) {
|
|
@@ -31,7 +37,7 @@ export const serializeMCPJson = (logger, state) => {
|
|
|
31
37
|
return schema;
|
|
32
38
|
};
|
|
33
39
|
for (const [name, endpointMeta] of Object.entries(resourcesMeta)) {
|
|
34
|
-
const functionMeta =
|
|
40
|
+
const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId);
|
|
35
41
|
if (!functionMeta) {
|
|
36
42
|
logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping resource ${name}.`);
|
|
37
43
|
continue;
|
|
@@ -50,7 +56,7 @@ export const serializeMCPJson = (logger, state) => {
|
|
|
50
56
|
});
|
|
51
57
|
}
|
|
52
58
|
for (const [name, endpointMeta] of Object.entries(toolsMeta)) {
|
|
53
|
-
const functionMeta =
|
|
59
|
+
const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId);
|
|
54
60
|
if (!functionMeta) {
|
|
55
61
|
logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping tool ${name}.`);
|
|
56
62
|
continue;
|
|
@@ -68,7 +74,7 @@ export const serializeMCPJson = (logger, state) => {
|
|
|
68
74
|
});
|
|
69
75
|
}
|
|
70
76
|
for (const [name, endpointMeta] of Object.entries(promptsMeta)) {
|
|
71
|
-
const functionMeta =
|
|
77
|
+
const functionMeta = resolveFunctionMeta(state, endpointMeta.pikkuFuncId);
|
|
72
78
|
if (!functionMeta) {
|
|
73
79
|
logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping prompt ${name}.`);
|
|
74
80
|
continue;
|
package/dist/visit.js
CHANGED
|
@@ -21,6 +21,7 @@ import { addSecret, addOAuth2Credential } from './add/add-secret.js';
|
|
|
21
21
|
import { addVariable } from './add/add-variable.js';
|
|
22
22
|
import { addWorkflowGraph } from './add/add-workflow-graph.js';
|
|
23
23
|
import { addAIAgent } from './add/add-ai-agent.js';
|
|
24
|
+
import { addApprovalDescription } from './add/add-approval-description.js';
|
|
24
25
|
export const visitSetup = (logger, checker, node, state, options) => {
|
|
25
26
|
addFileExtendsCoreType(node, checker, state.singletonServicesTypeImportMap, 'CoreSingletonServices', state);
|
|
26
27
|
addFileExtendsCoreType(node, checker, state.wireServicesTypeImportMap, 'CoreServices', state);
|
|
@@ -33,6 +34,7 @@ export const visitSetup = (logger, checker, node, state, options) => {
|
|
|
33
34
|
addWireAddon(node, state, logger);
|
|
34
35
|
addMiddleware(logger, node, checker, state, options);
|
|
35
36
|
addPermission(logger, node, checker, state, options);
|
|
37
|
+
addApprovalDescription(logger, node, checker, state, options);
|
|
36
38
|
addWorkflow(logger, node, checker, state, options);
|
|
37
39
|
ts.forEachChild(node, (child) => visitSetup(logger, checker, child, state, options));
|
|
38
40
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"type": "module",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
|
|
37
|
-
"@pikku/core": "^0.12.
|
|
37
|
+
"@pikku/core": "^0.12.3",
|
|
38
38
|
"path-to-regexp": "^8.3.0",
|
|
39
39
|
"ts-json-schema-generator": "^2.5.0",
|
|
40
40
|
"tsx": "^4.21.0",
|
package/src/add/add-ai-agent.ts
CHANGED
|
@@ -268,6 +268,9 @@ export const addAIAgent: AddWiring = (
|
|
|
268
268
|
| number
|
|
269
269
|
| null
|
|
270
270
|
const toolChoiceValue = getPropertyValue(obj, 'toolChoice') as string | null
|
|
271
|
+
const dynamicWorkflowsValue = getPropertyValue(obj, 'dynamicWorkflows') as
|
|
272
|
+
| string
|
|
273
|
+
| null
|
|
271
274
|
const toolsValue = resolveToolReferences(
|
|
272
275
|
obj,
|
|
273
276
|
checker,
|
|
@@ -455,6 +458,9 @@ export const addAIAgent: AddWiring = (
|
|
|
455
458
|
}),
|
|
456
459
|
...(toolsValue !== null && { tools: toolsValue }),
|
|
457
460
|
...(agentsValue !== null && { agents: agentsValue }),
|
|
461
|
+
...(dynamicWorkflowsValue !== null && {
|
|
462
|
+
dynamicWorkflows: dynamicWorkflowsValue as 'read' | 'always' | 'ask',
|
|
463
|
+
}),
|
|
458
464
|
tags,
|
|
459
465
|
inputSchema,
|
|
460
466
|
outputSchema,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import type { AddWiring } from '../types.js'
|
|
3
|
+
import { extractFunctionName } from '../utils/extract-function-name.js'
|
|
4
|
+
import {
|
|
5
|
+
extractServicesFromFunction,
|
|
6
|
+
extractUsedWires,
|
|
7
|
+
} from '../utils/extract-services.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Inspect pikkuApprovalDescription() calls and extract metadata
|
|
11
|
+
*/
|
|
12
|
+
export const addApprovalDescription: AddWiring = (
|
|
13
|
+
logger,
|
|
14
|
+
node,
|
|
15
|
+
checker,
|
|
16
|
+
state
|
|
17
|
+
) => {
|
|
18
|
+
if (!ts.isCallExpression(node)) return
|
|
19
|
+
|
|
20
|
+
const { expression, arguments: args } = node
|
|
21
|
+
|
|
22
|
+
if (!ts.isIdentifier(expression)) return
|
|
23
|
+
if (expression.text !== 'pikkuApprovalDescription') return
|
|
24
|
+
|
|
25
|
+
const arg = args[0]
|
|
26
|
+
if (!arg) return
|
|
27
|
+
|
|
28
|
+
let actualHandler: ts.ArrowFunction | ts.FunctionExpression
|
|
29
|
+
|
|
30
|
+
if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
|
|
31
|
+
actualHandler = arg
|
|
32
|
+
} else {
|
|
33
|
+
logger.error(`• Handler for pikkuApprovalDescription is not a function.`)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const services = extractServicesFromFunction(actualHandler)
|
|
38
|
+
const wires = extractUsedWires(actualHandler, 1)
|
|
39
|
+
let { pikkuFuncId, exportedName } = extractFunctionName(
|
|
40
|
+
node,
|
|
41
|
+
checker,
|
|
42
|
+
state.rootDir
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if (pikkuFuncId.startsWith('__temp_')) {
|
|
46
|
+
if (
|
|
47
|
+
ts.isVariableDeclaration(node.parent) &&
|
|
48
|
+
ts.isIdentifier(node.parent.name)
|
|
49
|
+
) {
|
|
50
|
+
pikkuFuncId = node.parent.name.text
|
|
51
|
+
} else if (
|
|
52
|
+
ts.isPropertyAssignment(node.parent) &&
|
|
53
|
+
ts.isIdentifier(node.parent.name)
|
|
54
|
+
) {
|
|
55
|
+
pikkuFuncId = node.parent.name.text
|
|
56
|
+
} else {
|
|
57
|
+
logger.error(
|
|
58
|
+
`• pikkuApprovalDescription() must be assigned to a variable or object property. ` +
|
|
59
|
+
`Extract it to a const: const myApproval = pikkuApprovalDescription(...)`
|
|
60
|
+
)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
state.functions.approvalDescriptions[pikkuFuncId] = {
|
|
66
|
+
services,
|
|
67
|
+
wires: wires.wires.length > 0 || !wires.optimized ? wires : undefined,
|
|
68
|
+
sourceFile: node.getSourceFile().fileName,
|
|
69
|
+
position: node.getStart(),
|
|
70
|
+
exportedName,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
logger.debug(
|
|
74
|
+
`• Found approval description '${pikkuFuncId}' with services: ${services.services.join(', ')}`
|
|
75
|
+
)
|
|
76
|
+
}
|
package/src/add/add-channel.ts
CHANGED
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
} from '../utils/middleware.js'
|
|
19
19
|
import { extractWireNames } from '../utils/post-process.js'
|
|
20
20
|
import { resolveIdentifier } from '../utils/resolve-identifier.js'
|
|
21
|
+
import { resolveFunctionMeta } from '../utils/resolve-function-meta.js'
|
|
22
|
+
import { resolveAddonName } from '../utils/resolve-addon-package.js'
|
|
21
23
|
import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js'
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -92,6 +94,13 @@ function getHandlerNameFromExpression(
|
|
|
92
94
|
|
|
93
95
|
// Handle call expressions
|
|
94
96
|
if (ts.isCallExpression(expr)) {
|
|
97
|
+
// Handle addon('namespace:funcName') calls
|
|
98
|
+
if (ts.isIdentifier(expr.expression) && expr.expression.text === 'addon') {
|
|
99
|
+
const [firstArg] = expr.arguments
|
|
100
|
+
if (firstArg && ts.isStringLiteral(firstArg)) {
|
|
101
|
+
return firstArg.text
|
|
102
|
+
}
|
|
103
|
+
}
|
|
95
104
|
const { pikkuFuncId } = extractFunctionName(expr, checker, rootDir)
|
|
96
105
|
return pikkuFuncId
|
|
97
106
|
}
|
|
@@ -460,11 +469,11 @@ export function addMessagesRoutes(
|
|
|
460
469
|
continue
|
|
461
470
|
}
|
|
462
471
|
|
|
463
|
-
const fnMeta = state
|
|
472
|
+
const fnMeta = resolveFunctionMeta(state, handlerName)
|
|
464
473
|
if (!fnMeta) {
|
|
465
474
|
logger.critical(
|
|
466
475
|
ErrorCode.FUNCTION_METADATA_NOT_FOUND,
|
|
467
|
-
`No function metadata found for handler '${handlerName}'
|
|
476
|
+
`No function metadata found for channel handler '${handlerName}' on route '${routeKey}'. If this is an inline function, it must be exported for the inspector to discover it.`
|
|
468
477
|
)
|
|
469
478
|
continue
|
|
470
479
|
}
|
|
@@ -478,8 +487,17 @@ export function addMessagesRoutes(
|
|
|
478
487
|
? resolveMiddleware(state, init, routeTags, checker)
|
|
479
488
|
: undefined
|
|
480
489
|
|
|
490
|
+
// Resolve package name for addon functions (e.g. 'swaggerPetstore:addPet')
|
|
491
|
+
const colonIdx = handlerName.indexOf(':')
|
|
492
|
+
const addonNs =
|
|
493
|
+
colonIdx !== -1 ? handlerName.substring(0, colonIdx) : null
|
|
494
|
+
const packageName = addonNs
|
|
495
|
+
? state.rpc.wireAddonDeclarations.get(addonNs)?.package
|
|
496
|
+
: undefined
|
|
497
|
+
|
|
481
498
|
result[channelKey]![routeKey] = {
|
|
482
499
|
pikkuFuncId: handlerName,
|
|
500
|
+
packageName,
|
|
483
501
|
middleware: routeMiddleware,
|
|
484
502
|
}
|
|
485
503
|
}
|
|
@@ -564,8 +582,12 @@ export const addChannel: AddWiring = (
|
|
|
564
582
|
)
|
|
565
583
|
return
|
|
566
584
|
}
|
|
585
|
+
const msgPackageName = ts.isIdentifier(onMsgProp)
|
|
586
|
+
? resolveAddonName(onMsgProp, checker, state.rpc.wireAddonDeclarations)
|
|
587
|
+
: null
|
|
567
588
|
message = {
|
|
568
589
|
pikkuFuncId: msgFuncId,
|
|
590
|
+
...(msgPackageName && { packageName: msgPackageName }),
|
|
569
591
|
}
|
|
570
592
|
}
|
|
571
593
|
|
|
@@ -579,15 +601,20 @@ export const addChannel: AddWiring = (
|
|
|
579
601
|
// --- track used functions/middleware for service aggregation ---
|
|
580
602
|
// Track connect/disconnect/message handlers
|
|
581
603
|
let connectFuncId: string | undefined
|
|
604
|
+
let connectPackageName: string | null = null
|
|
582
605
|
if (connect) {
|
|
583
606
|
const extracted = extractFunctionName(connect, checker, state.rootDir)
|
|
584
607
|
connectFuncId = extracted.pikkuFuncId.startsWith('__temp_')
|
|
585
608
|
? makeContextBasedId('channel', name, 'connect')
|
|
586
609
|
: extracted.pikkuFuncId
|
|
610
|
+
connectPackageName = ts.isIdentifier(connect)
|
|
611
|
+
? resolveAddonName(connect, checker, state.rpc.wireAddonDeclarations)
|
|
612
|
+
: null
|
|
587
613
|
state.serviceAggregation.usedFunctions.add(connectFuncId)
|
|
588
614
|
}
|
|
589
615
|
|
|
590
616
|
let disconnectFuncId: string | undefined
|
|
617
|
+
let disconnectPackageName: string | null = null
|
|
591
618
|
if (disconnect) {
|
|
592
619
|
const extracted = extractFunctionName(
|
|
593
620
|
disconnect as any,
|
|
@@ -597,6 +624,9 @@ export const addChannel: AddWiring = (
|
|
|
597
624
|
disconnectFuncId = extracted.pikkuFuncId.startsWith('__temp_')
|
|
598
625
|
? makeContextBasedId('channel', name, 'disconnect')
|
|
599
626
|
: extracted.pikkuFuncId
|
|
627
|
+
disconnectPackageName = ts.isIdentifier(disconnect)
|
|
628
|
+
? resolveAddonName(disconnect, checker, state.rpc.wireAddonDeclarations)
|
|
629
|
+
: null
|
|
600
630
|
state.serviceAggregation.usedFunctions.add(disconnectFuncId)
|
|
601
631
|
}
|
|
602
632
|
|
|
@@ -634,8 +664,18 @@ export const addChannel: AddWiring = (
|
|
|
634
664
|
input: null,
|
|
635
665
|
params: params.length ? params : undefined,
|
|
636
666
|
query: query?.length ? query : undefined,
|
|
637
|
-
connect: connectFuncId
|
|
638
|
-
|
|
667
|
+
connect: connectFuncId
|
|
668
|
+
? {
|
|
669
|
+
pikkuFuncId: connectFuncId,
|
|
670
|
+
...(connectPackageName && { packageName: connectPackageName }),
|
|
671
|
+
}
|
|
672
|
+
: null,
|
|
673
|
+
disconnect: disconnectFuncId
|
|
674
|
+
? {
|
|
675
|
+
pikkuFuncId: disconnectFuncId,
|
|
676
|
+
...(disconnectPackageName && { packageName: disconnectPackageName }),
|
|
677
|
+
}
|
|
678
|
+
: null,
|
|
639
679
|
message,
|
|
640
680
|
messageWirings,
|
|
641
681
|
binary: binary === undefined ? undefined : binary,
|