@pikku/inspector 0.11.2 → 0.12.0

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 (182) hide show
  1. package/CHANGELOG.md +11 -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 +69 -61
  6. package/dist/add/add-cli.js +36 -18
  7. package/dist/add/add-file-with-factory.js +2 -0
  8. package/dist/add/add-functions.js +250 -75
  9. package/dist/add/add-http-route.d.ts +19 -10
  10. package/dist/add/add-http-route.js +152 -66
  11. package/dist/add/add-http-routes.d.ts +5 -0
  12. package/dist/add/add-http-routes.js +159 -0
  13. package/dist/add/add-keyed-wiring.d.ts +12 -0
  14. package/dist/add/add-keyed-wiring.js +97 -0
  15. package/dist/add/add-mcp-prompt.js +14 -9
  16. package/dist/add/add-mcp-resource.js +14 -9
  17. package/dist/add/add-middleware.d.ts +1 -4
  18. package/dist/add/add-middleware.js +364 -79
  19. package/dist/add/add-permission.d.ts +1 -1
  20. package/dist/add/add-permission.js +152 -40
  21. package/dist/add/add-queue-worker.js +18 -12
  22. package/dist/add/add-rpc-invocations.js +14 -0
  23. package/dist/add/add-schedule.js +11 -5
  24. package/dist/add/add-secret.d.ts +3 -0
  25. package/dist/add/add-secret.js +82 -0
  26. package/dist/add/add-trigger.d.ts +2 -0
  27. package/dist/add/add-trigger.js +87 -0
  28. package/dist/add/add-variable.d.ts +1 -0
  29. package/dist/add/add-variable.js +8 -0
  30. package/dist/add/add-workflow-graph.d.ts +3 -2
  31. package/dist/add/add-workflow-graph.js +143 -406
  32. package/dist/add/add-workflow.js +6 -4
  33. package/dist/error-codes.d.ts +14 -1
  34. package/dist/error-codes.js +19 -1
  35. package/dist/index.d.ts +9 -8
  36. package/dist/index.js +5 -4
  37. package/dist/inspector.d.ts +1 -1
  38. package/dist/inspector.js +91 -14
  39. package/dist/schema-generator.d.ts +1 -0
  40. package/dist/schema-generator.js +1 -0
  41. package/dist/types-map.js +10 -1
  42. package/dist/types.d.ts +163 -39
  43. package/dist/utils/compute-required-schemas.d.ts +4 -0
  44. package/dist/utils/compute-required-schemas.js +41 -0
  45. package/dist/utils/contract-hashes.d.ts +35 -0
  46. package/dist/utils/contract-hashes.js +202 -0
  47. package/dist/utils/custom-types-generator.d.ts +9 -0
  48. package/dist/utils/custom-types-generator.js +71 -0
  49. package/dist/utils/detect-schema-vendor.d.ts +22 -0
  50. package/dist/utils/detect-schema-vendor.js +76 -0
  51. package/dist/utils/ensure-function-metadata.d.ts +5 -2
  52. package/dist/utils/ensure-function-metadata.js +220 -6
  53. package/dist/utils/extract-function-name.d.ts +5 -16
  54. package/dist/utils/extract-function-name.js +86 -291
  55. package/dist/utils/extract-services.d.ts +2 -1
  56. package/dist/utils/extract-services.js +25 -1
  57. package/dist/utils/filter-inspector-state.js +107 -23
  58. package/dist/utils/get-property-value.d.ts +6 -1
  59. package/dist/utils/get-property-value.js +28 -3
  60. package/dist/utils/hash.d.ts +2 -0
  61. package/dist/utils/hash.js +23 -0
  62. package/dist/utils/middleware.d.ts +7 -30
  63. package/dist/utils/middleware.js +80 -66
  64. package/dist/utils/permissions.d.ts +2 -2
  65. package/dist/utils/permissions.js +10 -10
  66. package/dist/utils/post-process.d.ts +9 -10
  67. package/dist/utils/post-process.js +231 -24
  68. package/dist/utils/resolve-external-package.d.ts +12 -0
  69. package/dist/utils/resolve-external-package.js +34 -0
  70. package/dist/utils/resolve-function-types.d.ts +6 -0
  71. package/dist/utils/resolve-function-types.js +29 -0
  72. package/dist/utils/resolve-identifier.d.ts +10 -0
  73. package/dist/utils/resolve-identifier.js +36 -0
  74. package/dist/utils/resolve-versions.d.ts +2 -0
  75. package/dist/utils/resolve-versions.js +78 -0
  76. package/dist/utils/schema-generator.d.ts +9 -0
  77. package/dist/utils/schema-generator.js +209 -0
  78. package/dist/utils/serialize-inspector-state.d.ts +59 -22
  79. package/dist/utils/serialize-inspector-state.js +92 -20
  80. package/dist/utils/serialize-mcp-json.d.ts +2 -0
  81. package/dist/utils/serialize-mcp-json.js +99 -0
  82. package/dist/utils/serialize-middleware-groups-meta.d.ts +12 -0
  83. package/dist/utils/serialize-middleware-groups-meta.js +28 -0
  84. package/dist/utils/serialize-openapi-json.d.ts +85 -0
  85. package/dist/utils/serialize-openapi-json.js +151 -0
  86. package/dist/utils/serialize-permissions-groups-meta.d.ts +6 -0
  87. package/dist/utils/serialize-permissions-groups-meta.js +31 -0
  88. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +34 -102
  89. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +23 -4
  90. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +12 -10
  91. package/dist/utils/workflow/graph/finalize-workflow-wires.d.ts +3 -0
  92. package/dist/utils/workflow/graph/finalize-workflow-wires.js +276 -0
  93. package/dist/utils/workflow/graph/finalize-workflows.d.ts +2 -0
  94. package/dist/utils/workflow/graph/finalize-workflows.js +75 -0
  95. package/dist/utils/workflow/graph/index.d.ts +2 -0
  96. package/dist/utils/workflow/graph/index.js +2 -0
  97. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +0 -8
  98. package/dist/utils/workflow/graph/serialize-workflow-graph.js +1 -3
  99. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +53 -79
  100. package/dist/utils/workflow/graph/workflow-graph.types.js +1 -1
  101. package/dist/visit.js +11 -6
  102. package/package.json +14 -4
  103. package/src/add/add-ai-agent.ts +468 -0
  104. package/src/add/add-channel.ts +82 -79
  105. package/src/add/add-cli.ts +49 -20
  106. package/src/add/add-file-with-factory.ts +2 -0
  107. package/src/add/add-functions.ts +330 -86
  108. package/src/add/add-http-route.ts +245 -88
  109. package/src/add/add-http-routes.ts +228 -0
  110. package/src/add/add-keyed-wiring.ts +151 -0
  111. package/src/add/add-mcp-prompt.ts +26 -15
  112. package/src/add/add-mcp-resource.ts +27 -15
  113. package/src/add/add-middleware.ts +482 -80
  114. package/src/add/add-permission.ts +199 -40
  115. package/src/add/add-queue-worker.ts +24 -19
  116. package/src/add/add-rpc-invocations.ts +17 -0
  117. package/src/add/add-schedule.ts +16 -11
  118. package/src/add/add-secret.ts +140 -0
  119. package/src/add/add-trigger.ts +154 -0
  120. package/src/add/add-variable.ts +9 -0
  121. package/src/add/add-workflow-graph.ts +180 -522
  122. package/src/add/add-workflow.ts +5 -4
  123. package/src/error-codes.ts +24 -1
  124. package/src/index.ts +22 -13
  125. package/src/inspector.ts +129 -17
  126. package/src/schema-generator.ts +1 -0
  127. package/src/types-map.ts +12 -1
  128. package/src/types.ts +175 -58
  129. package/src/utils/compute-required-schemas.ts +49 -0
  130. package/src/utils/contract-hashes.test.ts +528 -0
  131. package/src/utils/contract-hashes.ts +290 -0
  132. package/src/utils/custom-types-generator.ts +88 -0
  133. package/src/utils/detect-schema-vendor.ts +90 -0
  134. package/src/utils/ensure-function-metadata.ts +324 -7
  135. package/src/utils/extract-function-name.ts +101 -351
  136. package/src/utils/extract-services.ts +35 -2
  137. package/src/utils/filter-inspector-state.test.ts +34 -20
  138. package/src/utils/filter-inspector-state.ts +140 -31
  139. package/src/utils/get-property-value.ts +42 -4
  140. package/src/utils/hash.ts +26 -0
  141. package/src/utils/middleware.test.ts +204 -0
  142. package/src/utils/middleware.ts +129 -67
  143. package/src/utils/permissions.test.ts +35 -12
  144. package/src/utils/permissions.ts +10 -10
  145. package/src/utils/post-process.ts +283 -43
  146. package/src/utils/resolve-external-package.ts +42 -0
  147. package/src/utils/resolve-function-types.ts +42 -0
  148. package/src/utils/resolve-identifier.ts +46 -0
  149. package/src/utils/resolve-versions.test.ts +249 -0
  150. package/src/utils/resolve-versions.ts +105 -0
  151. package/src/utils/schema-generator.ts +329 -0
  152. package/src/utils/serialize-inspector-state.ts +163 -40
  153. package/src/utils/serialize-mcp-json.ts +145 -0
  154. package/src/utils/serialize-middleware-groups-meta.ts +33 -0
  155. package/src/utils/serialize-openapi-json.ts +277 -0
  156. package/src/utils/serialize-permissions-groups-meta.ts +35 -0
  157. package/src/utils/test-data/inspector-state.json +69 -66
  158. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +43 -119
  159. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +24 -4
  160. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +17 -10
  161. package/src/utils/workflow/graph/finalize-workflow-wires.ts +310 -0
  162. package/src/utils/workflow/graph/finalize-workflows.ts +100 -0
  163. package/src/utils/workflow/graph/index.ts +5 -0
  164. package/src/utils/workflow/graph/serialize-workflow-graph.ts +1 -8
  165. package/src/utils/workflow/graph/workflow-graph.types.ts +29 -78
  166. package/src/visit.ts +12 -6
  167. package/tsconfig.tsbuildinfo +1 -1
  168. package/dist/add/add-forge-credential.d.ts +0 -8
  169. package/dist/add/add-forge-credential.js +0 -77
  170. package/dist/add/add-forge-node.d.ts +0 -7
  171. package/dist/add/add-forge-node.js +0 -77
  172. package/dist/add/add-mcp-tool.d.ts +0 -2
  173. package/dist/add/add-mcp-tool.js +0 -81
  174. package/dist/utils/extract-service-metadata.d.ts +0 -19
  175. package/dist/utils/extract-service-metadata.js +0 -244
  176. package/dist/utils/write-service-metadata.d.ts +0 -13
  177. package/dist/utils/write-service-metadata.js +0 -37
  178. package/src/add/add-forge-credential.ts +0 -119
  179. package/src/add/add-forge-node.ts +0 -132
  180. package/src/add/add-mcp-tool.ts +0 -141
  181. package/src/utils/extract-service-metadata.ts +0 -353
  182. package/src/utils/write-service-metadata.ts +0 -51
@@ -1,77 +0,0 @@
1
- import * as ts from 'typescript';
2
- import { getPropertyValue } from '../utils/get-property-value.js';
3
- import { ErrorCode } from '../error-codes.js';
4
- /**
5
- * Inspector for wireForgeCredential calls.
6
- * Extracts metadata for Forge package credential declarations.
7
- * Note: wireForgeCredential is metadata-only - no runtime behavior.
8
- * Schema is stored as the variable name reference; actual Zod→JSON Schema conversion happens at CLI build time.
9
- */
10
- export const addForgeCredential = (logger, node, _checker, state, _options) => {
11
- if (!ts.isCallExpression(node)) {
12
- return;
13
- }
14
- const args = node.arguments;
15
- const firstArg = args[0];
16
- const expression = node.expression;
17
- // Check if the call is to wireForgeCredential
18
- if (!ts.isIdentifier(expression) ||
19
- expression.text !== 'wireForgeCredential') {
20
- return;
21
- }
22
- if (!firstArg) {
23
- return;
24
- }
25
- if (ts.isObjectLiteralExpression(firstArg)) {
26
- const obj = firstArg;
27
- const nameValue = getPropertyValue(obj, 'name');
28
- const displayNameValue = getPropertyValue(obj, 'displayName');
29
- const descriptionValue = getPropertyValue(obj, 'description');
30
- const secretIdValue = getPropertyValue(obj, 'secretId');
31
- // Get schema variable name for later runtime import
32
- let schemaVariableName = null;
33
- for (const prop of obj.properties) {
34
- if (ts.isPropertyAssignment(prop) &&
35
- ts.isIdentifier(prop.name) &&
36
- prop.name.text === 'schema') {
37
- if (ts.isIdentifier(prop.initializer)) {
38
- schemaVariableName = prop.initializer.text;
39
- }
40
- break;
41
- }
42
- }
43
- // Validate required fields
44
- if (!nameValue) {
45
- logger.critical(ErrorCode.MISSING_NAME, "Forge credential is missing the required 'name' property.");
46
- return;
47
- }
48
- if (!displayNameValue) {
49
- logger.critical(ErrorCode.MISSING_NAME, `Forge credential '${nameValue}' is missing the required 'displayName' property.`);
50
- return;
51
- }
52
- if (!secretIdValue) {
53
- logger.critical(ErrorCode.MISSING_NAME, `Forge credential '${nameValue}' is missing the required 'secretId' property.`);
54
- return;
55
- }
56
- if (!schemaVariableName) {
57
- logger.critical(ErrorCode.MISSING_NAME, `Forge credential '${nameValue}' is missing the required 'schema' property or schema is not a variable reference.`);
58
- return;
59
- }
60
- const sourceFile = node.getSourceFile().fileName;
61
- state.forgeCredentials.files.add(sourceFile);
62
- // Register the zod schema in the central zodLookup for deferred conversion
63
- const schemaLookupName = `ForgeCredential_${nameValue}`;
64
- state.zodLookup.set(schemaLookupName, {
65
- variableName: schemaVariableName,
66
- sourceFile,
67
- });
68
- // Store metadata - schema conversion happens later in schema-generator
69
- state.forgeCredentials.meta[nameValue] = {
70
- name: nameValue,
71
- displayName: displayNameValue,
72
- description: descriptionValue || undefined,
73
- secretId: secretIdValue,
74
- schema: schemaLookupName,
75
- };
76
- }
77
- };
@@ -1,7 +0,0 @@
1
- import { AddWiring } from '../types.js';
2
- /**
3
- * Inspector for wireForgeNode calls.
4
- * Extracts metadata for Forge workflow builder nodes.
5
- * Note: wireForgeNode is metadata-only - no runtime behavior.
6
- */
7
- export declare const addForgeNode: AddWiring;
@@ -1,77 +0,0 @@
1
- import * as ts from 'typescript';
2
- import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
3
- import { ErrorCode } from '../error-codes.js';
4
- /**
5
- * Inspector for wireForgeNode calls.
6
- * Extracts metadata for Forge workflow builder nodes.
7
- * Note: wireForgeNode is metadata-only - no runtime behavior.
8
- */
9
- export const addForgeNode = (logger, node, checker, state, _options) => {
10
- if (!ts.isCallExpression(node)) {
11
- return;
12
- }
13
- const args = node.arguments;
14
- const firstArg = args[0];
15
- const expression = node.expression;
16
- // Check if the call is to wireForgeNode
17
- if (!ts.isIdentifier(expression) || expression.text !== 'wireForgeNode') {
18
- return;
19
- }
20
- if (!firstArg) {
21
- return;
22
- }
23
- if (ts.isObjectLiteralExpression(firstArg)) {
24
- const obj = firstArg;
25
- const nameValue = getPropertyValue(obj, 'name');
26
- const displayNameValue = getPropertyValue(obj, 'displayName');
27
- const categoryValue = getPropertyValue(obj, 'category');
28
- const typeValue = getPropertyValue(obj, 'type');
29
- const rpcValue = getPropertyValue(obj, 'rpc');
30
- const errorOutputValue = getPropertyValue(obj, 'errorOutput');
31
- const { tags, description } = getCommonWireMetaData(obj, 'Forge node', nameValue, logger);
32
- // Validate required fields
33
- if (!nameValue) {
34
- logger.critical(ErrorCode.MISSING_NAME, "Forge node is missing the required 'name' property.");
35
- return;
36
- }
37
- if (!displayNameValue) {
38
- logger.critical(ErrorCode.MISSING_NAME, `Forge node '${nameValue}' is missing the required 'displayName' property.`);
39
- return;
40
- }
41
- if (!categoryValue) {
42
- logger.critical(ErrorCode.MISSING_NAME, `Forge node '${nameValue}' is missing the required 'category' property.`);
43
- return;
44
- }
45
- if (!typeValue) {
46
- logger.critical(ErrorCode.MISSING_NAME, `Forge node '${nameValue}' is missing the required 'type' property.`);
47
- return;
48
- }
49
- if (!['trigger', 'action', 'end'].includes(typeValue)) {
50
- logger.critical(ErrorCode.INVALID_VALUE, `Forge node '${nameValue}' has invalid type '${typeValue}'. Must be 'trigger', 'action', or 'end'.`);
51
- return;
52
- }
53
- if (!rpcValue) {
54
- logger.critical(ErrorCode.MISSING_NAME, `Forge node '${nameValue}' is missing the required 'rpc' property.`);
55
- return;
56
- }
57
- // Get function metadata for input/output schemas
58
- const fnMeta = state.functions.meta[rpcValue];
59
- const inputSchemaName = fnMeta?.inputs?.[0] || null;
60
- const outputSchemaName = fnMeta?.outputs?.[0] || null;
61
- // Note: Category validation against forge.node.categories config
62
- // is done at CLI build time, not during inspection
63
- state.forgeNodes.files.add(node.getSourceFile().fileName);
64
- state.forgeNodes.meta[nameValue] = {
65
- name: nameValue,
66
- displayName: displayNameValue,
67
- category: categoryValue,
68
- type: typeValue,
69
- rpc: rpcValue,
70
- description,
71
- errorOutput: errorOutputValue ?? false,
72
- inputSchemaName,
73
- outputSchemaName,
74
- tags,
75
- };
76
- }
77
- };
@@ -1,2 +0,0 @@
1
- import { AddWiring } from '../types.js';
2
- export declare const addMCPTool: AddWiring;
@@ -1,81 +0,0 @@
1
- import * as ts from 'typescript';
2
- import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
3
- import { extractWireNames } from '../utils/post-process.js';
4
- import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js';
5
- import { extractFunctionName } from '../utils/extract-function-name.js';
6
- import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
7
- import { resolveMiddleware } from '../utils/middleware.js';
8
- import { resolvePermissions } from '../utils/permissions.js';
9
- import { ErrorCode } from '../error-codes.js';
10
- export const addMCPTool = (logger, node, checker, state, options) => {
11
- if (!ts.isCallExpression(node)) {
12
- return;
13
- }
14
- const args = node.arguments;
15
- const firstArg = args[0];
16
- const expression = node.expression;
17
- // Check if the call is to wireMCPTool
18
- if (!ts.isIdentifier(expression) || expression.text !== 'wireMCPTool') {
19
- return;
20
- }
21
- if (!firstArg) {
22
- return;
23
- }
24
- if (ts.isObjectLiteralExpression(firstArg)) {
25
- const obj = firstArg;
26
- const nameValue = getPropertyValue(obj, 'name');
27
- const titleValue = getPropertyValue(obj, 'title');
28
- const { tags, summary, description, errors } = getCommonWireMetaData(obj, 'MCP tool', nameValue, logger);
29
- const streamingValue = getPropertyValue(obj, 'streaming');
30
- if (streamingValue === true) {
31
- logger.warn(`MCP tool '${nameValue}' has streaming enabled, but streaming is not yet supported.`);
32
- }
33
- const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
34
- if (!funcInitializer) {
35
- logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for MCP tool '${nameValue}'.`);
36
- return;
37
- }
38
- const pikkuFuncName = extractFunctionName(funcInitializer, checker, state.rootDir).pikkuFuncName;
39
- // Ensure function metadata exists (creates stub for inline functions)
40
- ensureFunctionMetadata(state, pikkuFuncName, nameValue || undefined);
41
- if (!nameValue) {
42
- logger.critical(ErrorCode.MISSING_NAME, "MCP tool is missing the required 'name' property.");
43
- return;
44
- }
45
- if (!description) {
46
- logger.critical(ErrorCode.MISSING_DESCRIPTION, `MCP tool '${nameValue}' is missing a description.`);
47
- return;
48
- }
49
- // lookup existing function metadata
50
- const fnMeta = state.functions.meta[pikkuFuncName];
51
- if (!fnMeta) {
52
- logger.critical(ErrorCode.FUNCTION_METADATA_NOT_FOUND, `No function metadata found for '${pikkuFuncName}'.`);
53
- return;
54
- }
55
- const inputSchema = fnMeta.inputs?.[0] || null;
56
- const outputSchema = fnMeta.outputs?.[0] || null;
57
- // --- resolve middleware ---
58
- const middleware = resolveMiddleware(state, obj, tags, checker);
59
- // --- resolve permissions ---
60
- const permissions = resolvePermissions(state, obj, tags, checker);
61
- // --- track used functions/middleware/permissions for service aggregation ---
62
- state.serviceAggregation.usedFunctions.add(pikkuFuncName);
63
- extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
64
- extractWireNames(permissions).forEach((name) => state.serviceAggregation.usedPermissions.add(name));
65
- state.mcpEndpoints.files.add(node.getSourceFile().fileName);
66
- state.mcpEndpoints.toolsMeta[nameValue] = {
67
- pikkuFuncName,
68
- name: nameValue,
69
- title: titleValue || undefined,
70
- description,
71
- summary,
72
- errors,
73
- ...(streamingValue !== null && { streaming: streamingValue }),
74
- tags,
75
- inputSchema,
76
- outputSchema,
77
- middleware,
78
- permissions,
79
- };
80
- }
81
- };
@@ -1,19 +0,0 @@
1
- import * as ts from 'typescript';
2
- export interface ServiceMetadata {
3
- name: string;
4
- summary: string;
5
- description: string;
6
- package: string;
7
- path: string;
8
- version: string;
9
- interface: string;
10
- expandedProperties: Record<string, string>;
11
- }
12
- /**
13
- * Extract metadata for a service from its TypeScript declaration
14
- */
15
- export declare function extractServiceMetadata(serviceName: string, type: ts.Type, checker: ts.TypeChecker, rootDir: string): ServiceMetadata | null;
16
- /**
17
- * Extract metadata for all services in a type
18
- */
19
- export declare function extractAllServiceMetadata(servicesType: ts.Type, checker: ts.TypeChecker, rootDir: string): ServiceMetadata[];
@@ -1,244 +0,0 @@
1
- import * as ts from 'typescript';
2
- import * as path from 'path';
3
- import * as fs from 'fs';
4
- /**
5
- * Extract JSDoc comment information from a TypeScript node
6
- */
7
- function extractJSDoc(node) {
8
- const jsDocTags = ts.getJSDocTags(node);
9
- const jsDocComments = ts.getJSDocCommentsAndTags(node);
10
- let summary = '';
11
- let description = '';
12
- const summaryTag = jsDocTags.find((tag) => tag.tagName.text === 'summary');
13
- if (summaryTag && summaryTag.comment) {
14
- summary =
15
- typeof summaryTag.comment === 'string'
16
- ? summaryTag.comment
17
- : summaryTag.comment.map((c) => c.text).join('');
18
- }
19
- for (const comment of jsDocComments) {
20
- if (ts.isJSDoc(comment) && comment.comment) {
21
- const commentText = typeof comment.comment === 'string'
22
- ? comment.comment
23
- : comment.comment.map((c) => c.text).join('');
24
- if (!summary && commentText) {
25
- const lines = commentText
26
- .split('\n')
27
- .map((l) => l.trim())
28
- .filter(Boolean);
29
- if (lines.length > 0) {
30
- summary = lines[0];
31
- if (lines.length > 1) {
32
- description = lines.slice(1).join('\n').trim();
33
- }
34
- }
35
- }
36
- else {
37
- description = commentText;
38
- }
39
- break;
40
- }
41
- }
42
- return { summary, description };
43
- }
44
- /**
45
- * Serialize a TypeScript type to a string representation
46
- */
47
- function serializeTypeToString(node, sourceFile, checker) {
48
- const nodeSourceFile = node.getSourceFile();
49
- if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) {
50
- return node.getText(nodeSourceFile);
51
- }
52
- if (ts.isClassDeclaration(node)) {
53
- return serializePublicClassMembers(node, nodeSourceFile, checker);
54
- }
55
- const type = checker.getTypeAtLocation(node);
56
- return checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation);
57
- }
58
- /**
59
- * Extract public members from a class and serialize them
60
- */
61
- function serializePublicClassMembers(classNode, sourceFile, checker) {
62
- const className = classNode.name?.text || 'UnnamedClass';
63
- const publicMembers = [];
64
- for (const member of classNode.members) {
65
- const modifiers = ts.canHaveModifiers(member)
66
- ? ts.getModifiers(member)
67
- : undefined;
68
- const isPublic = !modifiers?.some((mod) => mod.kind === ts.SyntaxKind.PrivateKeyword ||
69
- mod.kind === ts.SyntaxKind.ProtectedKeyword);
70
- if (!isPublic)
71
- continue;
72
- if (ts.isMethodDeclaration(member) ||
73
- ts.isPropertyDeclaration(member) ||
74
- ts.isConstructorDeclaration(member)) {
75
- const memberSignature = getMemberSignature(member, sourceFile, checker);
76
- if (memberSignature) {
77
- publicMembers.push(memberSignature);
78
- }
79
- }
80
- }
81
- return `class ${className} {\n ${publicMembers.join('\n ')}\n}`;
82
- }
83
- /**
84
- * Extract a clean signature for a class member (without implementation)
85
- */
86
- function getMemberSignature(member, sourceFile, checker) {
87
- if (ts.isPropertyDeclaration(member)) {
88
- const name = member.name.getText(sourceFile);
89
- const type = member.type ? member.type.getText(sourceFile) : 'any';
90
- const optional = member.questionToken ? '?' : '';
91
- return `${name}${optional}: ${type};`;
92
- }
93
- if (ts.isMethodDeclaration(member)) {
94
- const name = member.name.getText(sourceFile);
95
- const typeParams = member.typeParameters
96
- ? `<${member.typeParameters.map((tp) => tp.getText(sourceFile)).join(', ')}>`
97
- : '';
98
- const params = member.parameters
99
- .map((p) => p.getText(sourceFile))
100
- .join(', ');
101
- const returnType = member.type ? member.type.getText(sourceFile) : 'void';
102
- const optional = member.questionToken ? '?' : '';
103
- return `${name}${optional}${typeParams}(${params}): ${returnType};`;
104
- }
105
- if (ts.isConstructorDeclaration(member)) {
106
- const params = member.parameters
107
- .map((p) => p.getText(sourceFile))
108
- .join(', ');
109
- return `constructor(${params});`;
110
- }
111
- return null;
112
- }
113
- /**
114
- * Find the nearest package.json and extract package name and version
115
- */
116
- function getPackageInfo(filePath) {
117
- let currentDir = path.dirname(filePath);
118
- const root = path.parse(currentDir).root;
119
- while (currentDir !== root) {
120
- const packageJsonPath = path.join(currentDir, 'package.json');
121
- if (fs.existsSync(packageJsonPath)) {
122
- try {
123
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
124
- return {
125
- packageName: packageJson.name || 'unknown',
126
- version: packageJson.version || '0.0.0',
127
- };
128
- }
129
- catch (err) {
130
- // If we can't parse the package.json, continue searching
131
- }
132
- }
133
- currentDir = path.dirname(currentDir);
134
- }
135
- return { packageName: 'unknown', version: '0.0.0' };
136
- }
137
- /**
138
- * Expand a type to show all its properties including inherited ones
139
- * Returns a Record mapping property names to their type strings
140
- */
141
- function expandInterfaceProperties(type, checker, maxDepth = 2, currentDepth = 0, visited = new Set()) {
142
- const result = {};
143
- if (visited.has(type) || currentDepth >= maxDepth) {
144
- return result;
145
- }
146
- visited.add(type);
147
- const properties = type.getProperties();
148
- for (const prop of properties) {
149
- const propName = prop.getName();
150
- const propDecl = prop.valueDeclaration || prop.declarations?.[0];
151
- if (!propDecl)
152
- continue;
153
- try {
154
- const propType = checker.getTypeOfSymbolAtLocation(prop, propDecl);
155
- const isOptional = !!(prop.flags & ts.SymbolFlags.Optional);
156
- let typeString = checker.typeToString(propType, propDecl, ts.TypeFormatFlags.NoTruncation |
157
- ts.TypeFormatFlags.UseFullyQualifiedType);
158
- if (isOptional && !typeString.includes('undefined')) {
159
- typeString = `${typeString} | undefined`;
160
- }
161
- result[propName] = typeString;
162
- }
163
- catch (err) {
164
- result[propName] = 'any';
165
- }
166
- }
167
- return result;
168
- }
169
- /**
170
- * Extract metadata for a service from its TypeScript declaration
171
- */
172
- export function extractServiceMetadata(serviceName, type, checker, rootDir) {
173
- const property = type.getProperty(serviceName);
174
- if (!property) {
175
- return null;
176
- }
177
- const declaration = property.valueDeclaration || property.declarations?.[0];
178
- if (!declaration) {
179
- return null;
180
- }
181
- const sourceFile = declaration.getSourceFile();
182
- const filePath = sourceFile.fileName;
183
- const serviceType = checker.getTypeOfSymbolAtLocation(property, declaration);
184
- let typeDeclaration = null;
185
- if (serviceType.symbol) {
186
- const typeDecl = serviceType.symbol.valueDeclaration ||
187
- serviceType.symbol.declarations?.[0];
188
- if (typeDecl &&
189
- (ts.isInterfaceDeclaration(typeDecl) ||
190
- ts.isClassDeclaration(typeDecl) ||
191
- ts.isTypeAliasDeclaration(typeDecl))) {
192
- typeDeclaration = typeDecl;
193
- }
194
- }
195
- let summary = '';
196
- let description = '';
197
- if (typeDeclaration) {
198
- const jsDoc = extractJSDoc(typeDeclaration);
199
- summary = jsDoc.summary;
200
- description = jsDoc.description;
201
- }
202
- else if (ts.isPropertySignature(declaration) ||
203
- ts.isPropertyDeclaration(declaration)) {
204
- const jsDoc = extractJSDoc(declaration);
205
- summary = jsDoc.summary;
206
- description = jsDoc.description;
207
- }
208
- let interfaceString = '';
209
- if (typeDeclaration) {
210
- interfaceString = serializeTypeToString(typeDeclaration, sourceFile, checker);
211
- }
212
- else {
213
- interfaceString = checker.typeToString(serviceType, declaration, ts.TypeFormatFlags.NoTruncation);
214
- }
215
- const { packageName, version } = getPackageInfo(filePath);
216
- const relativePath = path.relative(rootDir, filePath);
217
- const expandedProperties = expandInterfaceProperties(serviceType, checker);
218
- return {
219
- name: serviceName,
220
- summary,
221
- description,
222
- package: packageName,
223
- path: relativePath,
224
- version,
225
- interface: interfaceString,
226
- expandedProperties,
227
- };
228
- }
229
- /**
230
- * Extract metadata for all services in a type
231
- */
232
- export function extractAllServiceMetadata(servicesType, checker, rootDir) {
233
- const metadata = [];
234
- const serviceNames = servicesType
235
- .getProperties()
236
- .map((prop) => prop.getName());
237
- for (const serviceName of serviceNames) {
238
- const serviceMeta = extractServiceMetadata(serviceName, servicesType, checker, rootDir);
239
- if (serviceMeta) {
240
- metadata.push(serviceMeta);
241
- }
242
- }
243
- return metadata;
244
- }
@@ -1,13 +0,0 @@
1
- import { ServiceMetadata } from './extract-service-metadata.js';
2
- /**
3
- * Write service metadata to a JSON file in .pikku/services directory
4
- */
5
- export declare function writeServiceMetadata(serviceMeta: ServiceMetadata, outDir: string): void;
6
- /**
7
- * Write all service metadata files
8
- */
9
- export declare function writeAllServiceMetadata(servicesMetadata: ServiceMetadata[], outDir: string): void;
10
- /**
11
- * Clean up services directory (remove old service JSON files)
12
- */
13
- export declare function cleanServicesDirectory(outDir: string): void;
@@ -1,37 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- /**
4
- * Write service metadata to a JSON file in .pikku/services directory
5
- */
6
- export function writeServiceMetadata(serviceMeta, outDir) {
7
- const servicesDir = path.join(outDir, 'services');
8
- if (!fs.existsSync(servicesDir)) {
9
- fs.mkdirSync(servicesDir, { recursive: true });
10
- }
11
- const fileName = `${serviceMeta.name}.gen.json`;
12
- const filePath = path.join(servicesDir, fileName);
13
- const jsonContent = JSON.stringify(serviceMeta, null, 2);
14
- fs.writeFileSync(filePath, jsonContent, 'utf-8');
15
- }
16
- /**
17
- * Write all service metadata files
18
- */
19
- export function writeAllServiceMetadata(servicesMetadata, outDir) {
20
- for (const serviceMeta of servicesMetadata) {
21
- writeServiceMetadata(serviceMeta, outDir);
22
- }
23
- }
24
- /**
25
- * Clean up services directory (remove old service JSON files)
26
- */
27
- export function cleanServicesDirectory(outDir) {
28
- const servicesDir = path.join(outDir, 'services');
29
- if (fs.existsSync(servicesDir)) {
30
- const files = fs.readdirSync(servicesDir);
31
- for (const file of files) {
32
- if (file.endsWith('.gen.json')) {
33
- fs.unlinkSync(path.join(servicesDir, file));
34
- }
35
- }
36
- }
37
- }
@@ -1,119 +0,0 @@
1
- import * as ts from 'typescript'
2
- import { getPropertyValue } from '../utils/get-property-value.js'
3
- import { AddWiring } from '../types.js'
4
- import { ErrorCode } from '../error-codes.js'
5
-
6
- /**
7
- * Inspector for wireForgeCredential calls.
8
- * Extracts metadata for Forge package credential declarations.
9
- * Note: wireForgeCredential is metadata-only - no runtime behavior.
10
- * Schema is stored as the variable name reference; actual Zod→JSON Schema conversion happens at CLI build time.
11
- */
12
- export const addForgeCredential: AddWiring = (
13
- logger,
14
- node,
15
- _checker,
16
- state,
17
- _options
18
- ) => {
19
- if (!ts.isCallExpression(node)) {
20
- return
21
- }
22
-
23
- const args = node.arguments
24
- const firstArg = args[0]
25
- const expression = node.expression
26
-
27
- // Check if the call is to wireForgeCredential
28
- if (
29
- !ts.isIdentifier(expression) ||
30
- expression.text !== 'wireForgeCredential'
31
- ) {
32
- return
33
- }
34
-
35
- if (!firstArg) {
36
- return
37
- }
38
-
39
- if (ts.isObjectLiteralExpression(firstArg)) {
40
- const obj = firstArg
41
-
42
- const nameValue = getPropertyValue(obj, 'name') as string | null
43
- const displayNameValue = getPropertyValue(obj, 'displayName') as
44
- | string
45
- | null
46
- const descriptionValue = getPropertyValue(obj, 'description') as
47
- | string
48
- | null
49
- const secretIdValue = getPropertyValue(obj, 'secretId') as string | null
50
-
51
- // Get schema variable name for later runtime import
52
- let schemaVariableName: string | null = null
53
- for (const prop of obj.properties) {
54
- if (
55
- ts.isPropertyAssignment(prop) &&
56
- ts.isIdentifier(prop.name) &&
57
- prop.name.text === 'schema'
58
- ) {
59
- if (ts.isIdentifier(prop.initializer)) {
60
- schemaVariableName = prop.initializer.text
61
- }
62
- break
63
- }
64
- }
65
-
66
- // Validate required fields
67
- if (!nameValue) {
68
- logger.critical(
69
- ErrorCode.MISSING_NAME,
70
- "Forge credential is missing the required 'name' property."
71
- )
72
- return
73
- }
74
-
75
- if (!displayNameValue) {
76
- logger.critical(
77
- ErrorCode.MISSING_NAME,
78
- `Forge credential '${nameValue}' is missing the required 'displayName' property.`
79
- )
80
- return
81
- }
82
-
83
- if (!secretIdValue) {
84
- logger.critical(
85
- ErrorCode.MISSING_NAME,
86
- `Forge credential '${nameValue}' is missing the required 'secretId' property.`
87
- )
88
- return
89
- }
90
-
91
- if (!schemaVariableName) {
92
- logger.critical(
93
- ErrorCode.MISSING_NAME,
94
- `Forge credential '${nameValue}' is missing the required 'schema' property or schema is not a variable reference.`
95
- )
96
- return
97
- }
98
-
99
- const sourceFile = node.getSourceFile().fileName
100
-
101
- state.forgeCredentials.files.add(sourceFile)
102
-
103
- // Register the zod schema in the central zodLookup for deferred conversion
104
- const schemaLookupName = `ForgeCredential_${nameValue}`
105
- state.zodLookup.set(schemaLookupName, {
106
- variableName: schemaVariableName,
107
- sourceFile,
108
- })
109
-
110
- // Store metadata - schema conversion happens later in schema-generator
111
- state.forgeCredentials.meta[nameValue] = {
112
- name: nameValue,
113
- displayName: displayNameValue,
114
- description: descriptionValue || undefined,
115
- secretId: secretIdValue,
116
- schema: schemaLookupName,
117
- }
118
- }
119
- }