@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.
Files changed (211) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/OPTIMIZATION-PLAN.md +195 -0
  3. package/dist/add/add-ai-agent.d.ts +2 -0
  4. package/dist/add/add-ai-agent.js +314 -0
  5. package/dist/add/add-channel.js +81 -61
  6. package/dist/add/add-cli.d.ts +1 -1
  7. package/dist/add/add-cli.js +42 -19
  8. package/dist/add/add-file-extends-core-type.d.ts +1 -1
  9. package/dist/add/add-file-with-config.d.ts +1 -1
  10. package/dist/add/add-file-with-factory.d.ts +1 -1
  11. package/dist/add/add-file-with-factory.js +2 -0
  12. package/dist/add/add-functions.d.ts +1 -1
  13. package/dist/add/add-functions.js +256 -82
  14. package/dist/add/add-http-route.d.ts +20 -10
  15. package/dist/add/add-http-route.js +156 -66
  16. package/dist/add/add-http-routes.d.ts +5 -0
  17. package/dist/add/add-http-routes.js +160 -0
  18. package/dist/add/add-keyed-wiring.d.ts +12 -0
  19. package/dist/add/add-keyed-wiring.js +97 -0
  20. package/dist/add/add-mcp-prompt.d.ts +1 -1
  21. package/dist/add/add-mcp-prompt.js +14 -9
  22. package/dist/add/add-mcp-resource.d.ts +1 -1
  23. package/dist/add/add-mcp-resource.js +14 -9
  24. package/dist/add/add-middleware.d.ts +1 -4
  25. package/dist/add/add-middleware.js +364 -79
  26. package/dist/add/add-permission.d.ts +1 -1
  27. package/dist/add/add-permission.js +152 -40
  28. package/dist/add/add-queue-worker.d.ts +1 -1
  29. package/dist/add/add-queue-worker.js +18 -12
  30. package/dist/add/add-rpc-invocations.d.ts +3 -3
  31. package/dist/add/add-rpc-invocations.js +24 -10
  32. package/dist/add/add-schedule.d.ts +1 -1
  33. package/dist/add/add-schedule.js +11 -5
  34. package/dist/add/add-secret.d.ts +3 -0
  35. package/dist/add/add-secret.js +82 -0
  36. package/dist/add/add-trigger.d.ts +2 -0
  37. package/dist/add/add-trigger.js +87 -0
  38. package/dist/add/add-variable.d.ts +1 -0
  39. package/dist/add/add-variable.js +8 -0
  40. package/dist/add/add-wire-addon.d.ts +7 -0
  41. package/dist/add/add-wire-addon.js +70 -0
  42. package/dist/add/add-workflow-graph.d.ts +3 -2
  43. package/dist/add/add-workflow-graph.js +143 -406
  44. package/dist/add/add-workflow.d.ts +1 -1
  45. package/dist/add/add-workflow.js +6 -4
  46. package/dist/error-codes.d.ts +15 -1
  47. package/dist/error-codes.js +20 -1
  48. package/dist/index.d.ts +9 -8
  49. package/dist/index.js +5 -4
  50. package/dist/inspector.d.ts +2 -2
  51. package/dist/inspector.js +95 -15
  52. package/dist/schema-generator.d.ts +1 -0
  53. package/dist/schema-generator.js +1 -0
  54. package/dist/types-map.js +10 -1
  55. package/dist/types.d.ts +180 -50
  56. package/dist/utils/compute-required-schemas.d.ts +4 -0
  57. package/dist/utils/compute-required-schemas.js +40 -0
  58. package/dist/utils/contract-hashes.d.ts +52 -0
  59. package/dist/utils/contract-hashes.js +269 -0
  60. package/dist/utils/custom-types-generator.d.ts +9 -0
  61. package/dist/utils/custom-types-generator.js +71 -0
  62. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  63. package/dist/utils/detect-schema-vendor.js +76 -0
  64. package/dist/utils/does-type-extend-core-type.d.ts +1 -1
  65. package/dist/utils/ensure-function-metadata.d.ts +6 -3
  66. package/dist/utils/ensure-function-metadata.js +220 -6
  67. package/dist/utils/extract-function-name.d.ts +5 -16
  68. package/dist/utils/extract-function-name.js +86 -291
  69. package/dist/utils/extract-services.d.ts +2 -1
  70. package/dist/utils/extract-services.js +25 -1
  71. package/dist/utils/filter-inspector-state.d.ts +1 -1
  72. package/dist/utils/filter-inspector-state.js +107 -23
  73. package/dist/utils/filter-utils.d.ts +2 -2
  74. package/dist/utils/get-files-and-methods.d.ts +1 -1
  75. package/dist/utils/get-property-value.d.ts +6 -1
  76. package/dist/utils/get-property-value.js +28 -3
  77. package/dist/utils/hash.d.ts +2 -0
  78. package/dist/utils/hash.js +23 -0
  79. package/dist/utils/middleware.d.ts +9 -32
  80. package/dist/utils/middleware.js +80 -66
  81. package/dist/utils/permissions.d.ts +4 -4
  82. package/dist/utils/permissions.js +10 -10
  83. package/dist/utils/post-process.d.ts +11 -11
  84. package/dist/utils/post-process.js +247 -24
  85. package/dist/utils/resolve-addon-package.d.ts +16 -0
  86. package/dist/utils/resolve-addon-package.js +34 -0
  87. package/dist/utils/resolve-function-types.d.ts +6 -0
  88. package/dist/utils/resolve-function-types.js +29 -0
  89. package/dist/utils/resolve-identifier.d.ts +10 -0
  90. package/dist/utils/resolve-identifier.js +36 -0
  91. package/dist/utils/resolve-versions.d.ts +2 -0
  92. package/dist/utils/resolve-versions.js +78 -0
  93. package/dist/utils/schema-generator.d.ts +9 -0
  94. package/dist/utils/schema-generator.js +209 -0
  95. package/dist/utils/serialize-inspector-state.d.ts +70 -23
  96. package/dist/utils/serialize-inspector-state.js +98 -22
  97. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  98. package/dist/utils/serialize-mcp-json.js +99 -0
  99. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  100. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  101. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  102. package/dist/utils/serialize-openapi-json.js +151 -0
  103. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  104. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  105. package/dist/utils/validate-auth-sessionless.d.ts +3 -0
  106. package/dist/utils/validate-auth-sessionless.js +14 -0
  107. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  108. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  109. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  110. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  111. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  112. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  113. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  114. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  115. package/dist/utils/workflow/graph/index.d.ts +2 -0
  116. package/dist/utils/workflow/graph/index.js +2 -0
  117. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  118. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  119. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  120. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  121. package/dist/visit.d.ts +1 -1
  122. package/dist/visit.js +13 -6
  123. package/package.json +14 -4
  124. package/src/add/add-ai-agent.ts +468 -0
  125. package/src/add/add-channel.ts +103 -79
  126. package/src/add/add-cli.ts +68 -24
  127. package/src/add/add-file-extends-core-type.ts +1 -1
  128. package/src/add/add-file-with-config.ts +1 -1
  129. package/src/add/add-file-with-factory.ts +3 -1
  130. package/src/add/add-functions.ts +349 -103
  131. package/src/add/add-http-route.ts +263 -89
  132. package/src/add/add-http-routes.ts +229 -0
  133. package/src/add/add-keyed-wiring.ts +151 -0
  134. package/src/add/add-mcp-prompt.ts +27 -16
  135. package/src/add/add-mcp-resource.ts +28 -16
  136. package/src/add/add-middleware.ts +482 -80
  137. package/src/add/add-permission.ts +199 -40
  138. package/src/add/add-queue-worker.ts +25 -20
  139. package/src/add/add-rpc-invocations.ts +28 -11
  140. package/src/add/add-schedule.ts +17 -12
  141. package/src/add/add-secret.ts +140 -0
  142. package/src/add/add-trigger.ts +154 -0
  143. package/src/add/add-variable.ts +9 -0
  144. package/src/add/add-wire-addon.ts +80 -0
  145. package/src/add/add-workflow-graph.ts +180 -522
  146. package/src/add/add-workflow.ts +7 -6
  147. package/src/error-codes.ts +25 -1
  148. package/src/index.ts +23 -13
  149. package/src/inspector.ts +139 -19
  150. package/src/schema-generator.ts +1 -0
  151. package/src/types-map.ts +12 -1
  152. package/src/types.ts +199 -69
  153. package/src/utils/compute-required-schemas.ts +48 -0
  154. package/src/utils/contract-hashes.test.ts +553 -0
  155. package/src/utils/contract-hashes.ts +386 -0
  156. package/src/utils/custom-types-generator.ts +88 -0
  157. package/src/utils/detect-schema-vendor.ts +90 -0
  158. package/src/utils/does-type-extend-core-type.ts +1 -1
  159. package/src/utils/ensure-function-metadata.ts +325 -8
  160. package/src/utils/extract-function-name.ts +101 -351
  161. package/src/utils/extract-services.ts +35 -2
  162. package/src/utils/filter-inspector-state.test.ts +37 -25
  163. package/src/utils/filter-inspector-state.ts +146 -32
  164. package/src/utils/filter-utils.test.ts +1 -1
  165. package/src/utils/filter-utils.ts +2 -2
  166. package/src/utils/get-files-and-methods.ts +1 -1
  167. package/src/utils/get-property-value.ts +42 -4
  168. package/src/utils/hash.ts +26 -0
  169. package/src/utils/middleware.test.ts +204 -0
  170. package/src/utils/middleware.ts +131 -69
  171. package/src/utils/permissions.test.ts +35 -12
  172. package/src/utils/permissions.ts +12 -12
  173. package/src/utils/post-process.ts +306 -44
  174. package/src/utils/resolve-addon-package.ts +49 -0
  175. package/src/utils/resolve-function-types.ts +42 -0
  176. package/src/utils/resolve-identifier.ts +46 -0
  177. package/src/utils/resolve-versions.test.ts +249 -0
  178. package/src/utils/resolve-versions.ts +105 -0
  179. package/src/utils/schema-generator.ts +329 -0
  180. package/src/utils/serialize-inspector-state.ts +184 -43
  181. package/src/utils/serialize-mcp-json.ts +145 -0
  182. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  183. package/src/utils/serialize-openapi-json.ts +277 -0
  184. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  185. package/src/utils/test-data/inspector-state.json +69 -66
  186. package/src/utils/validate-auth-sessionless.ts +29 -0
  187. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  188. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +26 -6
  189. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  190. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  191. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  192. package/src/utils/workflow/graph/index.ts +5 -0
  193. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  194. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  195. package/src/visit.ts +19 -7
  196. package/tsconfig.tsbuildinfo +1 -1
  197. package/dist/add/add-forge-credential.d.ts +0 -8
  198. package/dist/add/add-forge-credential.js +0 -77
  199. package/dist/add/add-forge-node.d.ts +0 -7
  200. package/dist/add/add-forge-node.js +0 -77
  201. package/dist/add/add-mcp-tool.d.ts +0 -2
  202. package/dist/add/add-mcp-tool.js +0 -81
  203. package/dist/utils/extract-service-metadata.d.ts +0 -19
  204. package/dist/utils/extract-service-metadata.js +0 -244
  205. package/dist/utils/write-service-metadata.d.ts +0 -13
  206. package/dist/utils/write-service-metadata.js +0 -37
  207. package/src/add/add-forge-credential.ts +0 -119
  208. package/src/add/add-forge-node.ts +0 -132
  209. package/src/add/add-mcp-tool.ts +0 -141
  210. package/src/utils/extract-service-metadata.ts +0 -353
  211. package/src/utils/write-service-metadata.ts +0 -51
@@ -0,0 +1,99 @@
1
+ export const serializeMCPJson = (logger, state) => {
2
+ const { mcpEndpoints, functions, schemas } = state;
3
+ const { meta: functionsMeta, typesMap } = functions;
4
+ const { resourcesMeta, toolsMeta, promptsMeta } = mcpEndpoints;
5
+ const tools = [];
6
+ const resources = [];
7
+ const prompts = [];
8
+ const loadSchema = (typeName) => {
9
+ if (!typeName ||
10
+ [
11
+ 'boolean',
12
+ 'string',
13
+ 'number',
14
+ 'null',
15
+ 'undefined',
16
+ 'void',
17
+ 'unknown',
18
+ 'never',
19
+ ].includes(typeName)) {
20
+ return undefined;
21
+ }
22
+ const uniqueName = typesMap.getUniqueName(typeName);
23
+ if (!uniqueName) {
24
+ return undefined;
25
+ }
26
+ const schema = schemas[uniqueName];
27
+ if (!schema) {
28
+ logger.warn(`Serialize MCP: Could not find schema for type: ${uniqueName}`);
29
+ return undefined;
30
+ }
31
+ return schema;
32
+ };
33
+ for (const [name, endpointMeta] of Object.entries(resourcesMeta)) {
34
+ const functionMeta = functionsMeta[endpointMeta.pikkuFuncId];
35
+ if (!functionMeta) {
36
+ logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping resource ${name}.`);
37
+ continue;
38
+ }
39
+ const inputType = functionMeta.inputs?.[0];
40
+ const outputType = functionMeta.outputs?.[0];
41
+ const parameters = loadSchema(inputType);
42
+ const returns = loadSchema(outputType);
43
+ resources.push({
44
+ uri: name,
45
+ name,
46
+ description: endpointMeta.description,
47
+ ...(parameters && { parameters }),
48
+ ...(returns && { returns }),
49
+ ...(endpointMeta.streaming && { streaming: true }),
50
+ });
51
+ }
52
+ for (const [name, endpointMeta] of Object.entries(toolsMeta)) {
53
+ const functionMeta = functionsMeta[endpointMeta.pikkuFuncId];
54
+ if (!functionMeta) {
55
+ logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping tool ${name}.`);
56
+ continue;
57
+ }
58
+ const inputType = functionMeta.inputs?.[0];
59
+ const outputType = functionMeta.outputs?.[0];
60
+ const parameters = loadSchema(inputType);
61
+ const returns = loadSchema(outputType);
62
+ tools.push({
63
+ name,
64
+ description: endpointMeta.description,
65
+ ...(parameters && { parameters }),
66
+ ...(returns && { returns }),
67
+ ...(endpointMeta.streaming && { streaming: true }),
68
+ });
69
+ }
70
+ for (const [name, endpointMeta] of Object.entries(promptsMeta)) {
71
+ const functionMeta = functionsMeta[endpointMeta.pikkuFuncId];
72
+ if (!functionMeta) {
73
+ logger.warn(`Function ${endpointMeta.pikkuFuncId} not found in functionsMeta. Skipping prompt ${name}.`);
74
+ continue;
75
+ }
76
+ const inputType = functionMeta.inputs?.[0];
77
+ const inputSchema = loadSchema(inputType);
78
+ const argumentsArray = [];
79
+ if (inputSchema &&
80
+ typeof inputSchema === 'object' &&
81
+ !(inputSchema instanceof Array)) {
82
+ const properties = inputSchema.properties;
83
+ const required = inputSchema.required || [];
84
+ for (const [propName, propSchema] of Object.entries(properties)) {
85
+ argumentsArray.push({
86
+ name: propName,
87
+ description: propSchema.description || `${propName} parameter`,
88
+ required: required.includes(propName),
89
+ });
90
+ }
91
+ }
92
+ prompts.push({
93
+ name,
94
+ description: endpointMeta.description,
95
+ arguments: argumentsArray,
96
+ });
97
+ }
98
+ return JSON.stringify({ tools, resources, prompts }, null, 2);
99
+ };
@@ -0,0 +1,12 @@
1
+ import type { InspectorState } from '../types.js';
2
+ export declare const serializeMiddlewareGroupsMeta: (state: InspectorState) => {
3
+ definitions: Record<string, import("../types.js").InspectorMiddlewareDefinition>;
4
+ instances: Record<string, import("../types.js").InspectorMiddlewareInstance>;
5
+ httpGroups: Record<string, any>;
6
+ tagGroups: Record<string, any>;
7
+ channelMiddleware: {
8
+ definitions: Record<string, import("../types.js").InspectorMiddlewareDefinition>;
9
+ instances: Record<string, import("../types.js").InspectorMiddlewareInstance>;
10
+ tagGroups: Record<string, any>;
11
+ };
12
+ };
@@ -0,0 +1,28 @@
1
+ const serializeGroupMap = (groupMap) => {
2
+ const result = {};
3
+ for (const [key, meta] of groupMap.entries()) {
4
+ result[key] = {
5
+ exportName: meta.exportName,
6
+ sourceFile: meta.sourceFile,
7
+ position: meta.position,
8
+ services: meta.services,
9
+ count: meta.count,
10
+ instanceIds: meta.instanceIds,
11
+ isFactory: meta.isFactory,
12
+ };
13
+ }
14
+ return result;
15
+ };
16
+ export const serializeMiddlewareGroupsMeta = (state) => {
17
+ return {
18
+ definitions: state.middleware.definitions,
19
+ instances: state.middleware.instances,
20
+ httpGroups: serializeGroupMap(state.http.routeMiddleware),
21
+ tagGroups: serializeGroupMap(state.middleware.tagMiddleware),
22
+ channelMiddleware: {
23
+ definitions: state.channelMiddleware.definitions,
24
+ instances: state.channelMiddleware.instances,
25
+ tagGroups: serializeGroupMap(state.channelMiddleware.tagMiddleware),
26
+ },
27
+ };
28
+ };
@@ -0,0 +1,85 @@
1
+ import type { FunctionsMeta } from '@pikku/core';
2
+ import type { HTTPWiringsMeta } from '@pikku/core/http';
3
+ import type { InspectorLogger } from '../types.js';
4
+ interface ErrorDetails {
5
+ status: number;
6
+ message: string;
7
+ }
8
+ interface OpenAPISpec {
9
+ openapi: string;
10
+ info: {
11
+ title: string;
12
+ version: string;
13
+ description?: string;
14
+ termsOfService?: string;
15
+ contact?: {
16
+ name?: string;
17
+ url?: string;
18
+ email?: string;
19
+ };
20
+ license?: {
21
+ name: string;
22
+ url?: string;
23
+ };
24
+ };
25
+ servers: {
26
+ url: string;
27
+ description?: string;
28
+ }[];
29
+ paths: Record<string, any>;
30
+ components: {
31
+ schemas: Record<string, any>;
32
+ responses?: Record<string, any>;
33
+ parameters?: Record<string, any>;
34
+ examples?: Record<string, any>;
35
+ requestBodies?: Record<string, any>;
36
+ headers?: Record<string, any>;
37
+ securitySchemes?: Record<string, any>;
38
+ };
39
+ security?: {
40
+ [key: string]: any[];
41
+ }[];
42
+ tags?: {
43
+ name: string;
44
+ description?: string;
45
+ }[];
46
+ externalDocs?: {
47
+ description?: string;
48
+ url: string;
49
+ };
50
+ }
51
+ export interface OpenAPISpecInfo {
52
+ info: {
53
+ title: string;
54
+ version: string;
55
+ description: string;
56
+ termsOfService?: string;
57
+ contact?: {
58
+ name?: string;
59
+ url?: string;
60
+ email?: string;
61
+ };
62
+ license?: {
63
+ name: string;
64
+ url?: string;
65
+ };
66
+ };
67
+ servers: {
68
+ url: string;
69
+ description?: string;
70
+ }[];
71
+ tags?: {
72
+ name: string;
73
+ description?: string;
74
+ }[];
75
+ externalDocs?: {
76
+ description?: string;
77
+ url: string;
78
+ };
79
+ securitySchemes?: Record<string, any>;
80
+ security?: {
81
+ [key: string]: any[];
82
+ }[];
83
+ }
84
+ export declare function generateOpenAPISpec(logger: InspectorLogger, functionsMeta: FunctionsMeta, httpMeta: HTTPWiringsMeta, schemas: Record<string, any>, additionalInfo: OpenAPISpecInfo, errors?: Map<any, ErrorDetails>): Promise<OpenAPISpec>;
85
+ export {};
@@ -0,0 +1,151 @@
1
+ import _convertSchema from '@openapi-contrib/json-schema-to-openapi-schema';
2
+ const convertSchema = 'default' in _convertSchema ? _convertSchema.default : _convertSchema;
3
+ const getErrorResponseForConstructorName = (constructorName, errors) => {
4
+ const entries = Array.from(errors.entries());
5
+ const foundError = entries.find(([e]) => e.name === constructorName);
6
+ if (foundError) {
7
+ return foundError[1];
8
+ }
9
+ return undefined;
10
+ };
11
+ const convertSchemasToBodyPayloads = async (functionsMeta, routesMeta, schemas) => {
12
+ const requiredSchemas = new Set();
13
+ for (const routeMeta of Object.values(routesMeta)) {
14
+ for (const { inputTypes, pikkuFuncId } of Object.values(routeMeta)) {
15
+ const output = functionsMeta[pikkuFuncId]?.outputs?.[0];
16
+ if (inputTypes?.body) {
17
+ requiredSchemas.add(inputTypes?.body);
18
+ }
19
+ if (output) {
20
+ requiredSchemas.add(output);
21
+ }
22
+ }
23
+ }
24
+ const convertedEntries = await Promise.all(Object.entries(schemas).map(async ([key, schema]) => {
25
+ if (requiredSchemas.has(key)) {
26
+ const convertedSchema = await convertSchema(schema, {
27
+ convertUnreferencedDefinitions: false,
28
+ dereference: { circular: 'ignore' },
29
+ });
30
+ return [key, convertedSchema];
31
+ }
32
+ return;
33
+ }));
34
+ return Object.fromEntries(convertedEntries.filter((s) => !!s));
35
+ };
36
+ export async function generateOpenAPISpec(logger, functionsMeta, httpMeta, schemas, additionalInfo, errors) {
37
+ const paths = {};
38
+ const errorsMap = errors ?? new Map();
39
+ for (const routeMeta of Object.values(httpMeta)) {
40
+ for (const meta of Object.values(routeMeta)) {
41
+ const { route, method, inputTypes, pikkuFuncId, params, query, errors, description, tags, } = meta;
42
+ const functionMeta = functionsMeta[pikkuFuncId];
43
+ if (!functionMeta) {
44
+ logger.error(`• No function metadata found for '${pikkuFuncId}' in route '${route}'.`);
45
+ continue;
46
+ }
47
+ const output = functionMeta.outputs ? functionMeta.outputs[0] : undefined;
48
+ const path = route.replace(/:(\w+)/g, '{$1}');
49
+ if (!paths[path]) {
50
+ paths[path] = {};
51
+ }
52
+ const responses = {};
53
+ errors?.forEach((error) => {
54
+ const errorResponse = getErrorResponseForConstructorName(error, errorsMap);
55
+ if (errorResponse) {
56
+ responses[errorResponse.status] = {
57
+ description: errorResponse.message,
58
+ };
59
+ }
60
+ });
61
+ const operation = {
62
+ description: description ||
63
+ `This endpoint handles the ${method.toUpperCase()} request for the route ${route}.`,
64
+ tags: tags || [route.split('/')[1] || 'default'],
65
+ parameters: [],
66
+ responses: {
67
+ ...responses,
68
+ '200': {
69
+ description: 'Successful response',
70
+ content: output
71
+ ? {
72
+ 'application/json': {
73
+ schema: typeof output === 'string' &&
74
+ ['boolean', 'string', 'number'].includes(output)
75
+ ? { type: output }
76
+ : { $ref: `#/components/schemas/${output}` },
77
+ },
78
+ }
79
+ : undefined,
80
+ },
81
+ },
82
+ };
83
+ const bodyType = inputTypes?.body;
84
+ if (bodyType) {
85
+ operation.requestBody = {
86
+ required: true,
87
+ content: {
88
+ 'application/json': {
89
+ schema: typeof bodyType === 'string' &&
90
+ ['boolean', 'string', 'number'].includes(bodyType)
91
+ ? { type: bodyType }
92
+ : { $ref: `#/components/schemas/${bodyType}` },
93
+ },
94
+ },
95
+ };
96
+ }
97
+ if (params) {
98
+ operation.parameters = params.map((param) => ({
99
+ name: param,
100
+ in: 'path',
101
+ required: true,
102
+ schema: { type: 'string' },
103
+ }));
104
+ }
105
+ if (query) {
106
+ operation.parameters.push(...query.map((query) => ({
107
+ name: query,
108
+ in: 'query',
109
+ required: false,
110
+ schema: { type: 'string' },
111
+ })));
112
+ }
113
+ paths[path][method] = operation;
114
+ }
115
+ }
116
+ return {
117
+ openapi: '3.1.0',
118
+ info: additionalInfo.info,
119
+ servers: additionalInfo.servers,
120
+ paths,
121
+ components: {
122
+ schemas: await convertSchemasToBodyPayloads(functionsMeta, httpMeta, schemas),
123
+ responses: {},
124
+ parameters: {},
125
+ examples: {},
126
+ requestBodies: {},
127
+ headers: {},
128
+ securitySchemes: additionalInfo.securitySchemes || {
129
+ ApiKeyAuth: {
130
+ type: 'apiKey',
131
+ in: 'header',
132
+ name: 'x-api-key',
133
+ },
134
+ BearerAuth: {
135
+ type: 'http',
136
+ scheme: 'bearer',
137
+ },
138
+ },
139
+ },
140
+ security: additionalInfo.security || [
141
+ {
142
+ ApiKeyAuth: [],
143
+ },
144
+ {
145
+ BearerAuth: [],
146
+ },
147
+ ],
148
+ tags: additionalInfo.tags,
149
+ externalDocs: additionalInfo.externalDocs,
150
+ };
151
+ }
@@ -0,0 +1,6 @@
1
+ import type { InspectorState } from '../types.js';
2
+ export declare const serializePermissionsGroupsMeta: (state: InspectorState) => {
3
+ definitions: Record<string, import("../types.js").InspectorPermissionDefinition>;
4
+ httpGroups: Record<string, any>;
5
+ tagGroups: Record<string, any>;
6
+ };
@@ -0,0 +1,31 @@
1
+ export const serializePermissionsGroupsMeta = (state) => {
2
+ const httpGroups = {};
3
+ for (const [pattern, meta] of state.http.routePermissions.entries()) {
4
+ httpGroups[pattern] = {
5
+ exportName: meta.exportName,
6
+ sourceFile: meta.sourceFile,
7
+ position: meta.position,
8
+ services: meta.services,
9
+ count: meta.count,
10
+ instanceIds: meta.instanceIds,
11
+ isFactory: meta.isFactory,
12
+ };
13
+ }
14
+ const tagGroups = {};
15
+ for (const [tag, meta] of state.permissions.tagPermissions.entries()) {
16
+ tagGroups[tag] = {
17
+ exportName: meta.exportName,
18
+ sourceFile: meta.sourceFile,
19
+ position: meta.position,
20
+ services: meta.services,
21
+ count: meta.count,
22
+ instanceIds: meta.instanceIds,
23
+ isFactory: meta.isFactory,
24
+ };
25
+ }
26
+ return {
27
+ definitions: state.permissions.definitions,
28
+ httpGroups,
29
+ tagGroups,
30
+ };
31
+ };
@@ -0,0 +1,3 @@
1
+ import * as ts from 'typescript';
2
+ import type { InspectorLogger, InspectorState } from '../types.js';
3
+ export declare function validateAuthSessionless(logger: InspectorLogger, obj: ts.ObjectLiteralExpression, state: InspectorState, funcName: string, wireDescription: string, inheritedAuth?: boolean): boolean;
@@ -0,0 +1,14 @@
1
+ import { getPropertyValue } from './get-property-value.js';
2
+ import { ErrorCode } from '../error-codes.js';
3
+ export function validateAuthSessionless(logger, obj, state, funcName, wireDescription, inheritedAuth) {
4
+ const fnMeta = state.functions.meta[funcName];
5
+ if (!fnMeta)
6
+ return true;
7
+ const routeAuth = getPropertyValue(obj, 'auth');
8
+ const resolvedAuth = routeAuth === true || routeAuth === false ? routeAuth : inheritedAuth;
9
+ if (resolvedAuth === false && fnMeta.sessionless === false) {
10
+ logger.critical(ErrorCode.AUTH_DISABLED_REQUIRES_SESSIONLESS, `${wireDescription} has auth disabled but function '${funcName}' uses pikkuFunc (requires session). Use pikkuSessionlessFunc instead.`);
11
+ return false;
12
+ }
13
+ return true;
14
+ }
@@ -89,6 +89,15 @@ function valueToCode(value, itemVar) {
89
89
  if (isTemplateRef(value)) {
90
90
  return templateRefToCode(value, itemVar);
91
91
  }
92
+ if (Array.isArray(value)) {
93
+ const elements = value.map((v) => valueToCode(v, itemVar));
94
+ return `[${elements.join(', ')}]`;
95
+ }
96
+ if (typeof value === 'object' && value !== null) {
97
+ const entries = Object.entries(value);
98
+ const props = entries.map(([k, v]) => `${k}: ${valueToCode(v, itemVar)}`);
99
+ return `{ ${props.join(', ')} }`;
100
+ }
92
101
  return JSON.stringify(value);
93
102
  }
94
103
  /**
@@ -626,6 +635,26 @@ function templateRefToGraphCode(tmpl, outputVarToNodeId) {
626
635
  .replace(/\r/g, '\\r');
627
636
  return `template('${templateStr}', [${refs.join(', ')}])`;
628
637
  }
638
+ function valueToGraphCode(value, outputVarToNodeId, refTracker) {
639
+ if (isDataRef(value)) {
640
+ refTracker.hasRefs = true;
641
+ return dataRefToGraphRef(value, outputVarToNodeId);
642
+ }
643
+ if (isTemplateRef(value)) {
644
+ refTracker.hasRefs = true;
645
+ return templateRefToGraphCode(value, outputVarToNodeId);
646
+ }
647
+ if (Array.isArray(value)) {
648
+ const elements = value.map((v) => valueToGraphCode(v, outputVarToNodeId, refTracker));
649
+ return `[${elements.join(', ')}]`;
650
+ }
651
+ if (typeof value === 'object' && value !== null) {
652
+ const entries = Object.entries(value);
653
+ const props = entries.map(([k, v]) => `${k}: ${valueToGraphCode(v, outputVarToNodeId, refTracker)}`);
654
+ return `{ ${props.join(', ')} }`;
655
+ }
656
+ return JSON.stringify(value);
657
+ }
629
658
  /**
630
659
  * Convert input object to graph input code using ref()
631
660
  * @param input - The input mapping
@@ -635,73 +664,15 @@ function inputToGraphCode(input, outputVarToNodeId) {
635
664
  const entries = Object.entries(input);
636
665
  if (entries.length === 0)
637
666
  return { hasRefs: false, code: '{}' };
638
- let hasRefs = false;
667
+ const refTracker = { hasRefs: false };
639
668
  const lines = entries.map(([key, value]) => {
640
- if (isDataRef(value)) {
641
- hasRefs = true;
642
- return ` ${key}: ${dataRefToGraphRef(value, outputVarToNodeId)},`;
643
- }
644
- if (isTemplateRef(value)) {
645
- hasRefs = true;
646
- return ` ${key}: ${templateRefToGraphCode(value, outputVarToNodeId)},`;
647
- }
648
- return ` ${key}: ${JSON.stringify(value)},`;
669
+ return ` ${key}: ${valueToGraphCode(value, outputVarToNodeId, refTracker)},`;
649
670
  });
650
671
  return {
651
- hasRefs,
672
+ hasRefs: refTracker.hasRefs,
652
673
  code: `{\n${lines.join('\n')}\n }`,
653
674
  };
654
675
  }
655
- /**
656
- * Serialize wires to code
657
- */
658
- function wiresToCode(wires) {
659
- if (!wires || Object.keys(wires).length === 0)
660
- return '{}';
661
- const parts = [];
662
- if (wires.http && wires.http.length > 0) {
663
- const httpItems = wires.http.map((h) => `{ route: '${h.route}', method: '${h.method}', startNode: '${h.startNode}' }`);
664
- parts.push(`http: [${httpItems.join(', ')}]`);
665
- }
666
- if (wires.channel && wires.channel.length > 0) {
667
- const channelItems = wires.channel.map((c) => {
668
- const channelParts = [`name: '${c.name}'`];
669
- if (c.onConnect)
670
- channelParts.push(`onConnect: '${c.onConnect}'`);
671
- if (c.onDisconnect)
672
- channelParts.push(`onDisconnect: '${c.onDisconnect}'`);
673
- if (c.onMessage)
674
- channelParts.push(`onMessage: '${c.onMessage}'`);
675
- return `{ ${channelParts.join(', ')} }`;
676
- });
677
- parts.push(`channel: [${channelItems.join(', ')}]`);
678
- }
679
- if (wires.queue && wires.queue.length > 0) {
680
- const queueItems = wires.queue.map((q) => `{ name: '${q.name}', startNode: '${q.startNode}' }`);
681
- parts.push(`queue: [${queueItems.join(', ')}]`);
682
- }
683
- if (wires.cli && wires.cli.length > 0) {
684
- const cliItems = wires.cli.map((c) => `{ command: '${c.command}', startNode: '${c.startNode}' }`);
685
- parts.push(`cli: [${cliItems.join(', ')}]`);
686
- }
687
- if (wires.schedule && wires.schedule.length > 0) {
688
- const scheduleItems = wires.schedule.map((s) => {
689
- const scheduleParts = [];
690
- if (s.cron)
691
- scheduleParts.push(`cron: '${s.cron}'`);
692
- if (s.interval)
693
- scheduleParts.push(`interval: '${s.interval}'`);
694
- scheduleParts.push(`startNode: '${s.startNode}'`);
695
- return `{ ${scheduleParts.join(', ')} }`;
696
- });
697
- parts.push(`schedule: [${scheduleItems.join(', ')}]`);
698
- }
699
- if (wires.trigger && wires.trigger.length > 0) {
700
- const triggerItems = wires.trigger.map((t) => `{ name: '${t.name}', startNode: '${t.startNode}' }`);
701
- parts.push(`trigger: [${triggerItems.join(', ')}]`);
702
- }
703
- return `{ ${parts.join(', ')} }`;
704
- }
705
676
  /**
706
677
  * Check if a node is a flow node (non-RPC control flow)
707
678
  */
@@ -740,7 +711,7 @@ export function deserializeGraphWorkflow(workflow, options = {}) {
740
711
  const { pikkuImportPath = '../.pikku/workflow/pikku-workflow-types.gen.js' } = options;
741
712
  const lines = [];
742
713
  // Import statement
743
- lines.push(`import { pikkuWorkflowGraph, wireWorkflow } from '${pikkuImportPath}'`);
714
+ lines.push(`import { pikkuWorkflowGraph } from '${pikkuImportPath}'`);
744
715
  lines.push('');
745
716
  // Add description as comment if present
746
717
  if (workflow.description) {
@@ -811,26 +782,7 @@ export function deserializeGraphWorkflow(workflow, options = {}) {
811
782
  nodeConfigs.push(` ${nodeId}: {\n ${configParts.join(',\n ')},\n }`);
812
783
  }
813
784
  }
814
- // Compute entry node (first node with no incoming edges from RPC nodes)
815
- const rpcNodeIds = new Set(Object.keys(nodeRpcMap));
816
- const nodesWithIncomingEdges = new Set();
817
- for (const [nodeId, node] of Object.entries(workflow.nodes)) {
818
- if (!rpcNodeIds.has(nodeId))
819
- continue;
820
- if ('next' in node && node.next) {
821
- const nextId = node.next;
822
- // Follow through flow nodes to find the actual next RPC node
823
- const actualNextId = flowNodeIds.has(nextId)
824
- ? findNextRpcNode(nextId, workflow.nodes, flowNodeIds)
825
- : nextId;
826
- if (actualNextId && rpcNodeIds.has(actualNextId)) {
827
- nodesWithIncomingEdges.add(actualNextId);
828
- }
829
- }
830
- }
831
- // Entry node is the first RPC node with no incoming edges
832
- const entryNode = Object.keys(nodeRpcMap).find((id) => !nodesWithIncomingEdges.has(id));
833
- // Generate the pikkuWorkflowGraph call
785
+ // Generate the pikkuWorkflowGraph call (builds graph and registers with core)
834
786
  lines.push(`export const ${workflow.name} = pikkuWorkflowGraph({`);
835
787
  lines.push(` name: '${workflow.name}',`);
836
788
  if (workflow.description) {
@@ -851,12 +803,6 @@ export function deserializeGraphWorkflow(workflow, options = {}) {
851
803
  else {
852
804
  lines.push(` nodes: {},`);
853
805
  }
854
- // Generate wires with api entry point
855
- if (entryNode) {
856
- lines.push(` wires: {`);
857
- lines.push(` api: '${entryNode}',`);
858
- lines.push(` },`);
859
- }
860
806
  // Generate config (node configurations)
861
807
  if (nodeConfigs.length > 0) {
862
808
  lines.push(` config: {`);
@@ -865,20 +811,6 @@ export function deserializeGraphWorkflow(workflow, options = {}) {
865
811
  }
866
812
  lines.push(`})`);
867
813
  lines.push('');
868
- // Always generate wireWorkflow to register the graph workflow
869
- // (needed for testing even without explicit wires)
870
- if (workflow.wires && Object.keys(workflow.wires).length > 0) {
871
- lines.push(`wireWorkflow({`);
872
- lines.push(` wires: ${wiresToCode(workflow.wires)},`);
873
- lines.push(` graph: ${workflow.name},`);
874
- lines.push(`})`);
875
- }
876
- else {
877
- lines.push(`wireWorkflow({`);
878
- lines.push(` graph: ${workflow.name},`);
879
- lines.push(`})`);
880
- }
881
- lines.push('');
882
814
  return lines.join('\n');
883
815
  }
884
816
  /**
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript';
2
- import { WorkflowStepMeta, WorkflowContext } from '@pikku/core/workflow';
2
+ import type { WorkflowStepMeta, WorkflowContext } from '@pikku/core/workflow';
3
3
  /**
4
4
  * Result of simple workflow extraction
5
5
  */
@@ -1112,6 +1112,25 @@ function extractInputSources(node, context) {
1112
1112
  }
1113
1113
  return Object.keys(inputs).length > 0 ? inputs : undefined;
1114
1114
  }
1115
+ function inputSourceToInlineValue(source) {
1116
+ switch (source.from) {
1117
+ case 'literal':
1118
+ return source.value;
1119
+ case 'input':
1120
+ return { $ref: 'trigger', path: source.path };
1121
+ case 'outputVar':
1122
+ return { $ref: source.name, path: source.path };
1123
+ case 'item':
1124
+ return { $ref: '$item', path: source.path };
1125
+ case 'template':
1126
+ return {
1127
+ $template: {
1128
+ parts: source.parts,
1129
+ expressions: source.expressions.map(inputSourceToInlineValue),
1130
+ },
1131
+ };
1132
+ }
1133
+ }
1115
1134
  /**
1116
1135
  * Extract a single input source
1117
1136
  */
@@ -1174,8 +1193,8 @@ function extractInputSource(node, context) {
1174
1193
  if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
1175
1194
  const propName = prop.name.text;
1176
1195
  const propSource = extractInputSource(prop.initializer, context);
1177
- if (propSource && propSource.from === 'literal') {
1178
- obj[propName] = propSource.value;
1196
+ if (propSource) {
1197
+ obj[propName] = inputSourceToInlineValue(propSource);
1179
1198
  }
1180
1199
  }
1181
1200
  }
@@ -1186,8 +1205,8 @@ function extractInputSource(node, context) {
1186
1205
  const arr = [];
1187
1206
  for (const elem of node.elements) {
1188
1207
  const elemSource = extractInputSource(elem, context);
1189
- if (elemSource && elemSource.from === 'literal') {
1190
- arr.push(elemSource.value);
1208
+ if (elemSource) {
1209
+ arr.push(inputSourceToInlineValue(elemSource));
1191
1210
  }
1192
1211
  }
1193
1212
  return { from: 'literal', value: arr };