@pikku/inspector 0.11.0 → 0.11.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 (77) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/add/add-channel.js +11 -10
  3. package/dist/add/add-file-with-factory.js +10 -10
  4. package/dist/add/add-functions.js +57 -43
  5. package/dist/add/add-http-route.js +5 -4
  6. package/dist/add/add-mcp-prompt.js +6 -5
  7. package/dist/add/add-mcp-resource.js +6 -5
  8. package/dist/add/add-mcp-tool.js +6 -5
  9. package/dist/add/add-middleware.js +1 -1
  10. package/dist/add/add-permission.js +1 -1
  11. package/dist/add/add-queue-worker.js +6 -5
  12. package/dist/add/add-schedule.js +5 -4
  13. package/dist/add/add-workflow.d.ts +1 -1
  14. package/dist/add/add-workflow.js +92 -66
  15. package/dist/error-codes.d.ts +1 -0
  16. package/dist/error-codes.js +1 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +1 -0
  19. package/dist/inspector.js +10 -6
  20. package/dist/types.d.ts +21 -8
  21. package/dist/utils/extract-function-node.d.ts +10 -0
  22. package/dist/utils/extract-function-node.js +38 -0
  23. package/dist/utils/extract-node-value.d.ts +8 -0
  24. package/dist/utils/extract-node-value.js +24 -0
  25. package/dist/utils/extract-service-metadata.d.ts +19 -0
  26. package/dist/utils/extract-service-metadata.js +244 -0
  27. package/dist/utils/get-files-and-methods.d.ts +3 -3
  28. package/dist/utils/get-files-and-methods.js +3 -3
  29. package/dist/utils/get-property-value.d.ts +13 -6
  30. package/dist/utils/get-property-value.js +51 -43
  31. package/dist/utils/post-process.d.ts +9 -0
  32. package/dist/utils/post-process.js +30 -3
  33. package/dist/utils/serialize-inspector-state.d.ts +18 -5
  34. package/dist/utils/serialize-inspector-state.js +12 -10
  35. package/dist/utils/write-service-metadata.d.ts +13 -0
  36. package/dist/utils/write-service-metadata.js +37 -0
  37. package/dist/visit.js +2 -2
  38. package/dist/workflow/extract-simple-workflow.d.ts +15 -0
  39. package/dist/workflow/extract-simple-workflow.js +803 -0
  40. package/dist/workflow/patterns.d.ts +39 -0
  41. package/dist/workflow/patterns.js +138 -0
  42. package/dist/workflow/validation.d.ts +28 -0
  43. package/dist/workflow/validation.js +124 -0
  44. package/package.json +4 -4
  45. package/src/add/add-channel.ts +37 -17
  46. package/src/add/add-file-with-factory.ts +10 -10
  47. package/src/add/add-functions.ts +72 -56
  48. package/src/add/add-http-route.ts +10 -5
  49. package/src/add/add-mcp-prompt.ts +11 -7
  50. package/src/add/add-mcp-resource.ts +11 -7
  51. package/src/add/add-mcp-tool.ts +11 -7
  52. package/src/add/add-middleware.ts +1 -1
  53. package/src/add/add-permission.ts +1 -1
  54. package/src/add/add-queue-worker.ts +11 -12
  55. package/src/add/add-schedule.ts +10 -5
  56. package/src/add/add-workflow.ts +120 -110
  57. package/src/error-codes.ts +1 -0
  58. package/src/index.ts +2 -0
  59. package/src/inspector.ts +16 -6
  60. package/src/types.ts +18 -8
  61. package/src/utils/extract-function-node.ts +58 -0
  62. package/src/utils/extract-node-value.ts +31 -0
  63. package/src/utils/extract-service-metadata.ts +353 -0
  64. package/src/utils/filter-inspector-state.test.ts +3 -3
  65. package/src/utils/filter-utils.test.ts +45 -51
  66. package/src/utils/get-files-and-methods.ts +11 -11
  67. package/src/utils/get-property-value.ts +60 -53
  68. package/src/utils/permissions.test.ts +3 -3
  69. package/src/utils/post-process.ts +56 -3
  70. package/src/utils/serialize-inspector-state.ts +28 -18
  71. package/src/utils/test-data/inspector-state.json +9 -9
  72. package/src/utils/write-service-metadata.ts +51 -0
  73. package/src/visit.ts +3 -3
  74. package/src/workflow/extract-simple-workflow.ts +1035 -0
  75. package/src/workflow/patterns.ts +182 -0
  76. package/src/workflow/validation.ts +153 -0
  77. package/tsconfig.tsbuildinfo +1 -1
@@ -1,20 +1,19 @@
1
1
  import * as ts from 'typescript';
2
- import { getPropertyValue, getPropertyTags, } from '../utils/get-property-value.js';
3
2
  import { extractFunctionName } from '../utils/extract-function-name.js';
4
- import { getPropertyAssignmentInitializer, resolveFunctionDeclaration, } from '../utils/type-utils.js';
5
- import { resolveMiddleware } from '../utils/middleware.js';
6
- import { extractWireNames } from '../utils/post-process.js';
3
+ import { extractFunctionNode } from '../utils/extract-function-node.js';
7
4
  import { ErrorCode } from '../error-codes.js';
8
- import { extractStringLiteral, extractNumberLiteral, extractPropertyString, isStringLike, isFunctionLike, } from '../utils/extract-node-value.js';
5
+ import { extractStringLiteral, isStringLike, isFunctionLike, extractDescription, extractDuration, } from '../utils/extract-node-value.js';
6
+ import { extractSimpleWorkflow } from '../workflow/extract-simple-workflow.js';
7
+ import { getCommonWireMetaData } from '../utils/get-property-value.js';
9
8
  /**
10
- * Scan for workflow.do() and workflow.sleep() calls to extract workflow steps
9
+ * Scan for workflow.do(), workflow.sleep(), and workflow.cancel() calls to extract workflow steps
11
10
  */
12
11
  function getWorkflowInvocations(node, checker, state, workflowName, steps) {
13
12
  // Look for property access expressions: workflow.do or workflow.sleep
14
13
  if (ts.isPropertyAccessExpression(node)) {
15
14
  const { name } = node;
16
15
  // Check if this is accessing 'do' or 'sleep' property
17
- if (name.text === 'do' || name.text === 'sleep') {
16
+ if (name.text === 'do' || name.text === 'sleep' || name.text === 'cancel') {
18
17
  // Check if the parent is a call expression
19
18
  const parent = node.parent;
20
19
  if (ts.isCallExpression(parent) && parent.expression === node) {
@@ -34,7 +33,6 @@ function getWorkflowInvocations(node, checker, state, workflowName, steps) {
34
33
  type: 'rpc',
35
34
  stepName,
36
35
  rpcName,
37
- description,
38
36
  });
39
37
  state.rpc.invokedFunctions.add(rpcName);
40
38
  }
@@ -59,6 +57,12 @@ function getWorkflowInvocations(node, checker, state, workflowName, steps) {
59
57
  duration: duration || '<dynamic>',
60
58
  });
61
59
  }
60
+ else if (name.text === 'cancel') {
61
+ // workflow.cancel(reason?)
62
+ steps.push({
63
+ type: 'cancel',
64
+ });
65
+ }
62
66
  }
63
67
  }
64
68
  }
@@ -73,80 +77,102 @@ function getWorkflowInvocations(node, checker, state, workflowName, steps) {
73
77
  });
74
78
  }
75
79
  /**
76
- * Extract description from options object
77
- */
78
- function extractDescription(optionsNode, checker) {
79
- if (!optionsNode || !ts.isObjectLiteralExpression(optionsNode)) {
80
- return null;
81
- }
82
- return extractPropertyString(optionsNode, 'description', checker);
83
- }
84
- /**
85
- * Extract duration value (number or string)
86
- */
87
- function extractDuration(node, checker) {
88
- const numValue = extractNumberLiteral(node);
89
- if (numValue !== null) {
90
- return numValue;
91
- }
92
- return extractStringLiteral(node, checker);
93
- }
94
- /**
95
- * Inspector for wireWorkflow() calls
80
+ * Inspector for pikkuWorkflow() and pikkuSimpleWorkflow() calls
96
81
  * Detects workflow registration and extracts metadata
97
82
  */
98
- export const addWorkflow = (logger, node, checker, state, options) => {
83
+ export const addWorkflow = (logger, node, checker, state) => {
99
84
  if (!ts.isCallExpression(node)) {
100
85
  return;
101
86
  }
102
87
  const args = node.arguments;
103
88
  const firstArg = args[0];
104
89
  const expression = node.expression;
105
- // Check if the call is to wireWorkflow
106
- if (!ts.isIdentifier(expression) || expression.text !== 'wireWorkflow') {
90
+ if (!ts.isIdentifier(expression)) {
91
+ return;
92
+ }
93
+ let wrapperType = null;
94
+ if (expression.text === 'pikkuWorkflowFunc') {
95
+ wrapperType = 'regular';
96
+ }
97
+ else if (expression.text === 'pikkuSimpleWorkflowFunc') {
98
+ wrapperType = 'simple';
99
+ }
100
+ else {
107
101
  return;
108
102
  }
109
103
  if (!firstArg) {
110
104
  return;
111
105
  }
106
+ // Extract workflow name and metadata using same logic as add-functions
107
+ const { pikkuFuncName, name, exportedName } = extractFunctionName(node, checker, state.rootDir);
108
+ const workflowName = exportedName || name;
109
+ if (!workflowName) {
110
+ logger.critical(ErrorCode.MISSING_NAME, `Could not determine workflow name from export.`);
111
+ return;
112
+ }
113
+ // Extract the function node (either direct function or from config.func)
114
+ const { funcNode, resolvedFunc } = extractFunctionNode(firstArg, checker);
115
+ // Extract metadata if using object form
116
+ let tags;
117
+ let summary;
118
+ let description;
119
+ let errors;
112
120
  if (ts.isObjectLiteralExpression(firstArg)) {
113
- const obj = firstArg;
114
- const workflowName = getPropertyValue(obj, 'name');
115
- const description = getPropertyValue(obj, 'description');
116
- const docs = getPropertyValue(obj, 'docs') || undefined;
117
- const tags = getPropertyTags(obj, 'Workflow', workflowName, logger);
118
- // --- find the referenced function ---
119
- const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
120
- if (!workflowName) {
121
- logger.critical(ErrorCode.MISSING_NAME, `Wasn't able to determine 'name' property for workflow wiring.`);
122
- return;
123
- }
124
- if (!funcInitializer) {
125
- logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for workflow '${workflowName}'.`);
121
+ const metadata = getCommonWireMetaData(firstArg, 'Workflow', workflowName, logger);
122
+ tags = metadata.tags;
123
+ summary = metadata.summary;
124
+ description = metadata.description;
125
+ errors = metadata.errors;
126
+ }
127
+ // Validate that we got a valid function
128
+ if (ts.isObjectLiteralExpression(firstArg) &&
129
+ (!funcNode || funcNode === firstArg)) {
130
+ logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for workflow '${workflowName}'.`);
131
+ return;
132
+ }
133
+ if (!resolvedFunc) {
134
+ logger.critical(ErrorCode.MISSING_FUNC, `Could not resolve workflow function for '${workflowName}'.`);
135
+ return;
136
+ }
137
+ // Track workflow file for wiring generation
138
+ if (exportedName) {
139
+ state.workflows.files.set(pikkuFuncName, {
140
+ path: node.getSourceFile().fileName,
141
+ exportedName,
142
+ });
143
+ }
144
+ let steps = [];
145
+ let simple = undefined;
146
+ // Try simple workflow extraction first
147
+ // Pass the whole CallExpression node so findWorkflowFunction can find the arrow function
148
+ const result = extractSimpleWorkflow(node, checker);
149
+ if (result.status === 'ok' && result.steps) {
150
+ // Simple extraction succeeded
151
+ steps = result.steps;
152
+ simple = true;
153
+ }
154
+ else {
155
+ // Simple extraction failed
156
+ if (wrapperType === 'simple') {
157
+ // For pikkuSimpleWorkflowFunc, this is a critical error
158
+ logger.critical(ErrorCode.INVALID_SIMPLE_WORKFLOW, `Workflow '${workflowName}' uses pikkuSimpleWorkflowFunc but does not conform to simple workflow DSL:\n${result.reason || 'Unknown error'}`);
126
159
  return;
127
160
  }
128
- const pikkuFuncName = extractFunctionName(funcInitializer, checker, state.rootDir).pikkuFuncName;
129
- // --- resolve middleware ---
130
- const middleware = resolveMiddleware(state, obj, tags, checker);
131
- // --- track used functions/middleware for service aggregation ---
132
- state.serviceAggregation.usedFunctions.add(pikkuFuncName);
133
- extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
134
- state.workflows.files.add(node.getSourceFile().fileName);
135
- // Extract workflow steps from function body
136
- // Resolve the identifier to the actual function declaration
137
- const resolvedFunc = resolveFunctionDeclaration(funcInitializer, checker);
138
- const steps = [];
139
- if (resolvedFunc) {
140
- getWorkflowInvocations(resolvedFunc, checker, state, workflowName, steps);
161
+ else {
162
+ // For pikkuWorkflowFunc, fall back to basic extraction
163
+ logger.debug(`Workflow '${workflowName}' could not be extracted as simple workflow: ${result.reason || 'Unknown error'}. Falling back to basic extraction.`);
164
+ simple = false;
141
165
  }
142
- state.workflows.meta[workflowName] = {
143
- pikkuFuncName,
144
- workflowName,
145
- description,
146
- docs,
147
- tags,
148
- middleware,
149
- steps,
150
- };
151
166
  }
167
+ getWorkflowInvocations(resolvedFunc, checker, state, workflowName, steps);
168
+ state.workflows.meta[workflowName] = {
169
+ pikkuFuncName,
170
+ workflowName,
171
+ steps,
172
+ simple,
173
+ summary,
174
+ description,
175
+ errors,
176
+ tags,
177
+ };
152
178
  };
@@ -19,6 +19,7 @@ export declare enum ErrorCode {
19
19
  CLI_CLIENTSIDE_RENDERER_HAS_SERVICES = "PKU672",
20
20
  DYNAMIC_STEP_NAME = "PKU529",
21
21
  WORKFLOW_ORCHESTRATOR_NOT_CONFIGURED = "PKU600",
22
+ INVALID_SIMPLE_WORKFLOW = "PKU641",
22
23
  CONFIG_TYPE_NOT_FOUND = "PKU426",
23
24
  CONFIG_TYPE_UNDEFINED = "PKU427",
24
25
  SCHEMA_NO_ROOT = "PKU431",
@@ -21,6 +21,7 @@ export var ErrorCode;
21
21
  ErrorCode["CLI_CLIENTSIDE_RENDERER_HAS_SERVICES"] = "PKU672";
22
22
  ErrorCode["DYNAMIC_STEP_NAME"] = "PKU529";
23
23
  ErrorCode["WORKFLOW_ORCHESTRATOR_NOT_CONFIGURED"] = "PKU600";
24
+ ErrorCode["INVALID_SIMPLE_WORKFLOW"] = "PKU641";
24
25
  // Configuration errors
25
26
  ErrorCode["CONFIG_TYPE_NOT_FOUND"] = "PKU426";
26
27
  ErrorCode["CONFIG_TYPE_UNDEFINED"] = "PKU427";
package/dist/index.d.ts CHANGED
@@ -8,3 +8,5 @@ export { ErrorCode } from './error-codes.js';
8
8
  export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
9
9
  export type { SerializableInspectorState } from './utils/serialize-inspector-state.js';
10
10
  export { filterInspectorState } from './utils/filter-inspector-state.js';
11
+ export { writeAllServiceMetadata } from './utils/write-service-metadata.js';
12
+ export type { ServiceMetadata } from './utils/extract-service-metadata.js';
package/dist/index.js CHANGED
@@ -3,3 +3,4 @@ export { getFilesAndMethods } from './utils/get-files-and-methods.js';
3
3
  export { ErrorCode } from './error-codes.js';
4
4
  export { serializeInspectorState, deserializeInspectorState, } from './utils/serialize-inspector-state.js';
5
5
  export { filterInspectorState } from './utils/filter-inspector-state.js';
6
+ export { writeAllServiceMetadata } from './utils/write-service-metadata.js';
package/dist/inspector.js CHANGED
@@ -4,7 +4,7 @@ import { visitSetup, visitRoutes } from './visit.js';
4
4
  import { TypesMap } from './types-map.js';
5
5
  import { getFilesAndMethods } from './utils/get-files-and-methods.js';
6
6
  import { findCommonAncestor } from './utils/find-root-dir.js';
7
- import { aggregateRequiredServices } from './utils/post-process.js';
7
+ import { aggregateRequiredServices, extractServiceInterfaceMetadata, } from './utils/post-process.js';
8
8
  /**
9
9
  * Creates an initial/empty inspector state with all required properties initialized
10
10
  * @param rootDir - The root directory for the project
@@ -14,12 +14,12 @@ export function getInitialInspectorState(rootDir) {
14
14
  return {
15
15
  rootDir,
16
16
  singletonServicesTypeImportMap: new Map(),
17
- sessionServicesTypeImportMap: new Map(),
17
+ wireServicesTypeImportMap: new Map(),
18
18
  userSessionTypeImportMap: new Map(),
19
19
  configTypeImportMap: new Map(),
20
20
  singletonServicesFactories: new Map(),
21
- sessionServicesFactories: new Map(),
22
- sessionServicesMeta: new Map(),
21
+ wireServicesFactories: new Map(),
22
+ wireServicesMeta: new Map(),
23
23
  configFactories: new Map(),
24
24
  filesAndMethods: {},
25
25
  filesAndMethodsErrors: new Map(),
@@ -58,7 +58,7 @@ export function getInitialInspectorState(rootDir) {
58
58
  },
59
59
  workflows: {
60
60
  meta: {},
61
- files: new Set(),
61
+ files: new Map(),
62
62
  },
63
63
  rpc: {
64
64
  internalMeta: {},
@@ -94,8 +94,9 @@ export function getInitialInspectorState(rootDir) {
94
94
  usedMiddleware: new Set(),
95
95
  usedPermissions: new Set(),
96
96
  allSingletonServices: [],
97
- allSessionServices: [],
97
+ allWireServices: [],
98
98
  },
99
+ serviceMetadata: [],
99
100
  };
100
101
  }
101
102
  export const inspect = (logger, routeFiles, options = {}) => {
@@ -144,6 +145,9 @@ export const inspect = (logger, routeFiles, options = {}) => {
144
145
  const startAggregate = performance.now();
145
146
  aggregateRequiredServices(state);
146
147
  logger.debug(`Aggregate required services completed in ${(performance.now() - startAggregate).toFixed(2)}ms`);
148
+ const startServiceMeta = performance.now();
149
+ extractServiceInterfaceMetadata(state, checker);
150
+ logger.debug(`Extract service metadata completed in ${(performance.now() - startServiceMeta).toFixed(2)}ms`);
147
151
  }
148
152
  return state;
149
153
  };
package/dist/types.d.ts CHANGED
@@ -92,7 +92,7 @@ export type InspectorOptions = Partial<{
92
92
  configFileType: string;
93
93
  userSessionType: string;
94
94
  singletonServicesFactoryType: string;
95
- sessionServicesFactoryType: string;
95
+ wireServicesFactoryType: string;
96
96
  }>;
97
97
  }>;
98
98
  export interface InspectorLogger {
@@ -111,7 +111,7 @@ export interface InspectorFilesAndMethods {
111
111
  type: string;
112
112
  typePath: string;
113
113
  };
114
- sessionServicesType?: {
114
+ wireServicesType?: {
115
115
  file: string;
116
116
  variable: string;
117
117
  type: string;
@@ -141,7 +141,7 @@ export interface InspectorFilesAndMethods {
141
141
  type: string;
142
142
  typePath: string;
143
143
  };
144
- sessionServicesFactory?: {
144
+ wireServicesFactory?: {
145
145
  file: string;
146
146
  variable: string;
147
147
  type: string;
@@ -151,12 +151,12 @@ export interface InspectorFilesAndMethods {
151
151
  export interface InspectorState {
152
152
  rootDir: string;
153
153
  singletonServicesTypeImportMap: PathToNameAndType;
154
- sessionServicesTypeImportMap: PathToNameAndType;
154
+ wireServicesTypeImportMap: PathToNameAndType;
155
155
  userSessionTypeImportMap: PathToNameAndType;
156
156
  configTypeImportMap: PathToNameAndType;
157
157
  singletonServicesFactories: PathToNameAndType;
158
- sessionServicesFactories: PathToNameAndType;
159
- sessionServicesMeta: Map<string, string[]>;
158
+ wireServicesFactories: PathToNameAndType;
159
+ wireServicesMeta: Map<string, string[]>;
160
160
  configFactories: PathToNameAndType;
161
161
  filesAndMethods: InspectorFilesAndMethods;
162
162
  filesAndMethodsErrors: Map<string, PathToNameAndType>;
@@ -174,7 +174,10 @@ export interface InspectorState {
174
174
  };
175
175
  workflows: {
176
176
  meta: WorkflowsMeta;
177
- files: Set<string>;
177
+ files: Map<string, {
178
+ path: string;
179
+ exportedName: string;
180
+ }>;
178
181
  };
179
182
  rpc: {
180
183
  internalMeta: Record<string, string>;
@@ -207,6 +210,16 @@ export interface InspectorState {
207
210
  usedMiddleware: Set<string>;
208
211
  usedPermissions: Set<string>;
209
212
  allSingletonServices: string[];
210
- allSessionServices: string[];
213
+ allWireServices: string[];
211
214
  };
215
+ serviceMetadata: Array<{
216
+ name: string;
217
+ summary: string;
218
+ description: string;
219
+ package: string;
220
+ path: string;
221
+ version: string;
222
+ interface: string;
223
+ expandedProperties: Record<string, string>;
224
+ }>;
212
225
  }
@@ -0,0 +1,10 @@
1
+ import * as ts from 'typescript';
2
+ /**
3
+ * Extracts the actual function node from a pikkuFunc/pikkuWorkflowFunc call
4
+ * Handles both direct function form and config object form { func: ... }
5
+ */
6
+ export declare function extractFunctionNode(firstArg: ts.Expression, checker: ts.TypeChecker): {
7
+ funcNode: ts.Node;
8
+ resolvedFunc: ts.Node | null;
9
+ isDirectFunction: boolean;
10
+ };
@@ -0,0 +1,38 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyAssignmentInitializer, resolveFunctionDeclaration, } from './type-utils.js';
3
+ /**
4
+ * Extracts the actual function node from a pikkuFunc/pikkuWorkflowFunc call
5
+ * Handles both direct function form and config object form { func: ... }
6
+ */
7
+ export function extractFunctionNode(firstArg, checker) {
8
+ let funcNode = firstArg;
9
+ let isDirectFunction = true;
10
+ // Check if first argument is a config object with 'func' property
11
+ if (ts.isObjectLiteralExpression(firstArg)) {
12
+ isDirectFunction = false;
13
+ const funcInitializer = getPropertyAssignmentInitializer(firstArg, 'func', true, checker);
14
+ if (funcInitializer) {
15
+ funcNode = funcInitializer;
16
+ }
17
+ else {
18
+ // Return the original node if no func property found
19
+ // Caller should handle validation
20
+ funcNode = firstArg;
21
+ }
22
+ }
23
+ // Resolve identifier to get the actual function node
24
+ if (ts.isIdentifier(funcNode)) {
25
+ const symbol = checker.getSymbolAtLocation(funcNode);
26
+ const decl = symbol?.valueDeclaration || symbol?.declarations?.[0];
27
+ if (decl && ts.isVariableDeclaration(decl) && decl.initializer) {
28
+ funcNode = decl.initializer;
29
+ }
30
+ }
31
+ // Resolve function declaration for deeper analysis
32
+ const resolvedFunc = resolveFunctionDeclaration(funcNode, checker);
33
+ return {
34
+ funcNode,
35
+ resolvedFunc,
36
+ isDirectFunction,
37
+ };
38
+ }
@@ -22,3 +22,11 @@ export declare function extractNumberLiteral(node: ts.Node): number | null;
22
22
  * Returns the extracted value or null if not found/cannot extract
23
23
  */
24
24
  export declare function extractPropertyString(objNode: ts.ObjectLiteralExpression, propertyName: string, checker: ts.TypeChecker): string | null;
25
+ /**
26
+ * Extract description from options object
27
+ */
28
+ export declare function extractDescription(optionsNode: ts.Node | undefined, checker: ts.TypeChecker): string | null;
29
+ /**
30
+ * Extract duration value (number or string)
31
+ */
32
+ export declare function extractDuration(node: ts.Node, checker: ts.TypeChecker): string | number | null;
@@ -77,3 +77,27 @@ export function extractPropertyString(objNode, propertyName, checker) {
77
77
  }
78
78
  return null;
79
79
  }
80
+ /**
81
+ * Extract description from options object
82
+ */
83
+ export function extractDescription(optionsNode, checker) {
84
+ if (!optionsNode || !ts.isObjectLiteralExpression(optionsNode)) {
85
+ return null;
86
+ }
87
+ return extractPropertyString(optionsNode, 'description', checker);
88
+ }
89
+ /**
90
+ * Extract duration value (number or string)
91
+ */
92
+ export function extractDuration(node, checker) {
93
+ const numValue = extractNumberLiteral(node);
94
+ if (numValue !== null) {
95
+ return numValue;
96
+ }
97
+ try {
98
+ return extractStringLiteral(node, checker);
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
@@ -0,0 +1,19 @@
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[];