@pikku/inspector 0.11.1 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/dist/add/add-forge-credential.d.ts +8 -0
  3. package/dist/add/add-forge-credential.js +77 -0
  4. package/dist/add/add-forge-node.d.ts +7 -0
  5. package/dist/add/add-forge-node.js +77 -0
  6. package/dist/add/add-functions.js +102 -9
  7. package/dist/add/add-http-route.js +24 -1
  8. package/dist/add/add-rpc-invocations.d.ts +3 -0
  9. package/dist/add/add-rpc-invocations.js +51 -25
  10. package/dist/add/add-workflow-graph.d.ts +6 -0
  11. package/dist/add/add-workflow-graph.js +659 -0
  12. package/dist/add/add-workflow.js +118 -22
  13. package/dist/error-codes.d.ts +3 -1
  14. package/dist/error-codes.js +3 -1
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.js +2 -0
  17. package/dist/inspector.js +19 -3
  18. package/dist/types.d.ts +26 -0
  19. package/dist/utils/extract-function-name.js +7 -7
  20. package/dist/utils/get-property-value.d.ts +2 -1
  21. package/dist/utils/get-property-value.js +6 -2
  22. package/dist/utils/serialize-inspector-state.d.ts +24 -1
  23. package/dist/utils/serialize-inspector-state.js +24 -0
  24. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
  25. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +898 -0
  26. package/dist/{workflow/extract-simple-workflow.d.ts → utils/workflow/dsl/extract-dsl-workflow.d.ts} +4 -2
  27. package/dist/{workflow/extract-simple-workflow.js → utils/workflow/dsl/extract-dsl-workflow.js} +549 -68
  28. package/dist/utils/workflow/dsl/index.d.ts +7 -0
  29. package/dist/utils/workflow/dsl/index.js +7 -0
  30. package/dist/{workflow → utils/workflow/dsl}/patterns.d.ts +21 -0
  31. package/dist/{workflow → utils/workflow/dsl}/patterns.js +90 -10
  32. package/dist/{workflow → utils/workflow/dsl}/validation.d.ts +2 -0
  33. package/dist/{workflow → utils/workflow/dsl}/validation.js +25 -7
  34. package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
  35. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +316 -0
  36. package/dist/utils/workflow/graph/index.d.ts +6 -0
  37. package/dist/utils/workflow/graph/index.js +6 -0
  38. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +43 -0
  39. package/dist/utils/workflow/graph/serialize-workflow-graph.js +152 -0
  40. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +229 -0
  41. package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
  42. package/dist/visit.js +6 -0
  43. package/package.json +14 -2
  44. package/src/add/add-forge-credential.ts +119 -0
  45. package/src/add/add-forge-node.ts +132 -0
  46. package/src/add/add-functions.ts +129 -15
  47. package/src/add/add-http-route.ts +25 -1
  48. package/src/add/add-rpc-invocations.ts +61 -31
  49. package/src/add/add-workflow-graph.ts +864 -0
  50. package/src/add/add-workflow.ts +112 -26
  51. package/src/error-codes.ts +3 -1
  52. package/src/index.ts +10 -0
  53. package/src/inspector.ts +20 -4
  54. package/src/types.ts +25 -1
  55. package/src/utils/extract-function-name.ts +7 -7
  56. package/src/utils/get-property-value.ts +9 -2
  57. package/src/utils/serialize-inspector-state.ts +39 -1
  58. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1180 -0
  59. package/src/{workflow/extract-simple-workflow.ts → utils/workflow/dsl/extract-dsl-workflow.ts} +654 -81
  60. package/src/utils/workflow/dsl/index.ts +11 -0
  61. package/src/{workflow → utils/workflow/dsl}/patterns.ts +108 -11
  62. package/src/{workflow → utils/workflow/dsl}/validation.ts +34 -7
  63. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +415 -0
  64. package/src/utils/workflow/graph/index.ts +6 -0
  65. package/src/utils/workflow/graph/serialize-workflow-graph.ts +223 -0
  66. package/src/utils/workflow/graph/workflow-graph.types.ts +280 -0
  67. package/src/visit.ts +6 -0
  68. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Convert a RefValue (from runtime) to DataRef (serialized)
3
+ */
4
+ function convertRef(ref) {
5
+ return {
6
+ $ref: ref.nodeId,
7
+ path: ref.path,
8
+ };
9
+ }
10
+ /**
11
+ * Check if a value is a runtime RefValue
12
+ */
13
+ function isRefValue(value) {
14
+ return (typeof value === 'object' &&
15
+ value !== null &&
16
+ '__isRef' in value &&
17
+ value.__isRef === true);
18
+ }
19
+ /**
20
+ * Convert input mapping from runtime format to serialized format
21
+ */
22
+ function serializeInputMapping(input) {
23
+ const result = {};
24
+ for (const [key, value] of Object.entries(input)) {
25
+ if (isRefValue(value)) {
26
+ result[key] = convertRef(value);
27
+ }
28
+ else {
29
+ result[key] = value;
30
+ }
31
+ }
32
+ return result;
33
+ }
34
+ /**
35
+ * Convert next config from runtime format to serialized format
36
+ * Runtime uses Record<string, string | string[]> for branching with graph.branch()
37
+ * Serialized uses { conditions: [...], default: ... } for UI-friendly branching
38
+ */
39
+ function serializeNext(next) {
40
+ if (!next)
41
+ return undefined;
42
+ if (typeof next === 'string')
43
+ return next;
44
+ if (Array.isArray(next))
45
+ return next;
46
+ // Record format - convert to conditions format
47
+ // For now, treat keys as branch identifiers (from graph.branch())
48
+ // UI can display these as condition labels
49
+ const conditions = Object.entries(next).map(([key, target]) => ({
50
+ expression: key, // The branch key becomes the expression
51
+ target,
52
+ }));
53
+ return { conditions };
54
+ }
55
+ /**
56
+ * Serialize a workflow graph definition (from runtime) to JSON format
57
+ *
58
+ * @param definition - The runtime definition (with callbacks evaluated)
59
+ * @param rpcNameLookup - Function to get RPC name from a node's func
60
+ */
61
+ export function serializeWorkflowGraph(definition, options) {
62
+ const nodes = {};
63
+ const entryNodeIds = [];
64
+ // Create a ref function that captures refs
65
+ const createRef = (nodeId, path) => ({
66
+ __isRef: true,
67
+ nodeId,
68
+ path,
69
+ });
70
+ // Track which nodes have incoming edges
71
+ const hasIncomingEdge = new Set();
72
+ // First pass: identify nodes with incoming edges
73
+ for (const [_nodeId, node] of Object.entries(definition.graph)) {
74
+ const next = node.next;
75
+ if (!next)
76
+ continue;
77
+ if (typeof next === 'string') {
78
+ hasIncomingEdge.add(next);
79
+ }
80
+ else if (Array.isArray(next)) {
81
+ next.forEach((n) => hasIncomingEdge.add(n));
82
+ }
83
+ else {
84
+ for (const targets of Object.values(next)) {
85
+ if (typeof targets === 'string') {
86
+ hasIncomingEdge.add(targets);
87
+ }
88
+ else {
89
+ targets.forEach((n) => hasIncomingEdge.add(n));
90
+ }
91
+ }
92
+ }
93
+ }
94
+ // Second pass: serialize nodes
95
+ for (const [nodeId, node] of Object.entries(definition.graph)) {
96
+ // Evaluate input callback to get the mapping
97
+ let input = {};
98
+ if (node.input) {
99
+ const rawInput = node.input(createRef);
100
+ input = serializeInputMapping(rawInput);
101
+ }
102
+ // Get RPC name from func
103
+ const rpcName = node.func?.name || 'unknown';
104
+ const funcNode = {
105
+ nodeId,
106
+ rpcName,
107
+ input,
108
+ next: serializeNext(node.next),
109
+ onError: node.onError,
110
+ };
111
+ nodes[nodeId] = funcNode;
112
+ // Entry nodes have no incoming edges
113
+ if (!hasIncomingEdge.has(nodeId)) {
114
+ entryNodeIds.push(nodeId);
115
+ }
116
+ }
117
+ return {
118
+ name: definition.name,
119
+ pikkuFuncName: definition.name, // For graph workflows, pikkuFuncName is the workflow name
120
+ source: 'graph',
121
+ description: options?.description,
122
+ tags: options?.tags,
123
+ wires: definition.wires,
124
+ nodes,
125
+ entryNodeIds,
126
+ };
127
+ }
128
+ /**
129
+ * Deserialize a workflow graph from JSON to runtime format
130
+ * This re-hydrates the JSON so it can be executed
131
+ */
132
+ export function deserializeWorkflowGraph(serialized) {
133
+ const graph = {};
134
+ for (const [nodeId, node] of Object.entries(serialized.nodes)) {
135
+ // Only include FunctionNode properties (nodes with rpcName)
136
+ if ('rpcName' in node) {
137
+ const funcNode = node;
138
+ graph[nodeId] = {
139
+ rpcName: funcNode.rpcName,
140
+ input: funcNode.input ?? {},
141
+ next: funcNode.next,
142
+ onError: funcNode.onError,
143
+ };
144
+ }
145
+ }
146
+ return {
147
+ name: serialized.name,
148
+ wires: serialized.wires,
149
+ graph,
150
+ entryNodeIds: serialized.entryNodeIds,
151
+ };
152
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Serialized types for workflow graphs
3
+ * These are extracted by the inspector and stored as JSON
4
+ * Can be created from code (wireWorkflow) or UI
5
+ */
6
+ /**
7
+ * Reference to data from another node or trigger
8
+ */
9
+ export interface DataRef {
10
+ /** Source: 'trigger' for trigger input, or node ID for node output */
11
+ $ref: string;
12
+ /** Optional path into the data (dot notation: 'body.orderId') */
13
+ path?: string;
14
+ }
15
+ /**
16
+ * Check if value is a DataRef
17
+ */
18
+ export declare const isDataRef: (value: unknown) => value is DataRef;
19
+ /**
20
+ * Reference to a context/state variable
21
+ */
22
+ export interface StateRef {
23
+ /** Context variable name */
24
+ $state: string;
25
+ /** Optional path into the value (dot notation for nested objects) */
26
+ path?: string;
27
+ }
28
+ /**
29
+ * Check if value is a StateRef
30
+ */
31
+ export declare const isStateRef: (value: unknown) => value is StateRef;
32
+ /**
33
+ * Helper functions for building input mappings
34
+ */
35
+ export declare const ref: (nodeId: string, path?: string) => DataRef;
36
+ export declare const state: (name: string, path?: string) => StateRef;
37
+ /**
38
+ * Condition for branching
39
+ */
40
+ export interface BranchCondition {
41
+ /** Expression to evaluate (uses node output references) */
42
+ expression: string;
43
+ /** Target node(s) if condition is true */
44
+ target: string | string[];
45
+ }
46
+ /**
47
+ * Next node configuration
48
+ */
49
+ export type SerializedNext = string | string[] | {
50
+ /** Conditions evaluated in order, first match wins */
51
+ conditions: BranchCondition[];
52
+ /** Default target if no conditions match */
53
+ default?: string | string[];
54
+ };
55
+ /**
56
+ * Node execution options
57
+ */
58
+ export interface NodeOptions {
59
+ /** Number of retry attempts on failure */
60
+ retries?: number;
61
+ /** Delay between retries (e.g., '1s', '5s') */
62
+ retryDelay?: string;
63
+ /** Timeout for node execution (e.g., '30s', '5m') */
64
+ timeout?: string;
65
+ /** If true, execute via queue (async). Default: false (inline) */
66
+ async?: boolean;
67
+ }
68
+ /**
69
+ * Flow node types for control flow (no RPC call)
70
+ */
71
+ export type FlowType = 'sleep' | 'branch' | 'parallel' | 'fanout' | 'inline' | 'switch' | 'filter' | 'arrayPredicate' | 'return' | 'cancel' | 'set';
72
+ import type { ContextVariable, WorkflowContext } from '@pikku/core/workflow';
73
+ export type { ContextVariable, WorkflowContext };
74
+ /**
75
+ * Base node properties shared by all node types
76
+ */
77
+ interface BaseNode {
78
+ /** Node ID */
79
+ nodeId: string;
80
+ /** Step name/description */
81
+ stepName?: string;
82
+ /** Next node(s) - simple, parallel, or conditional */
83
+ next?: SerializedNext;
84
+ /** Error routing - node(s) to execute on error */
85
+ onError?: string | string[];
86
+ /** Execution options */
87
+ options?: NodeOptions;
88
+ }
89
+ /**
90
+ * Function node - calls an RPC
91
+ */
92
+ export interface FunctionNode extends BaseNode {
93
+ /** RPC function name */
94
+ rpcName: string;
95
+ /** Input mapping - values can be literals or DataRefs */
96
+ input?: Record<string, unknown | DataRef>;
97
+ /** Output variable name for storing result */
98
+ outputVar?: string;
99
+ }
100
+ /**
101
+ * Flow node - control flow only, no RPC call
102
+ */
103
+ export interface FlowNode extends BaseNode {
104
+ /** Flow type */
105
+ flow: FlowType;
106
+ /** Flow-specific properties */
107
+ [key: string]: unknown;
108
+ }
109
+ /**
110
+ * Serialized graph node - either a function node or flow node
111
+ */
112
+ export type SerializedGraphNode = FunctionNode | FlowNode;
113
+ /**
114
+ * Type guard for function nodes
115
+ */
116
+ export declare const isFunctionNode: (node: SerializedGraphNode) => node is FunctionNode;
117
+ /**
118
+ * Type guard for flow nodes
119
+ */
120
+ export declare const isFlowNode: (node: SerializedGraphNode) => node is FlowNode;
121
+ /**
122
+ * HTTP wire configuration with startNode
123
+ */
124
+ export interface HttpWire {
125
+ route: string;
126
+ method: 'get' | 'post' | 'put' | 'patch' | 'delete';
127
+ startNode: string;
128
+ }
129
+ /**
130
+ * Channel wire configuration
131
+ */
132
+ export interface ChannelWire {
133
+ name: string;
134
+ onConnect?: string;
135
+ onDisconnect?: string;
136
+ onMessage?: string;
137
+ }
138
+ /**
139
+ * Queue wire configuration
140
+ */
141
+ export interface QueueWire {
142
+ name: string;
143
+ startNode: string;
144
+ }
145
+ /**
146
+ * CLI wire configuration
147
+ */
148
+ export interface CliWire {
149
+ command: string;
150
+ startNode: string;
151
+ }
152
+ /**
153
+ * MCP wire configurations
154
+ */
155
+ export interface McpWires {
156
+ tool?: Array<{
157
+ name: string;
158
+ startNode: string;
159
+ }>;
160
+ prompt?: Array<{
161
+ name: string;
162
+ startNode: string;
163
+ }>;
164
+ resource?: Array<{
165
+ uri: string;
166
+ startNode: string;
167
+ }>;
168
+ }
169
+ /**
170
+ * Schedule wire configuration
171
+ */
172
+ export interface ScheduleWire {
173
+ cron?: string;
174
+ interval?: string;
175
+ startNode: string;
176
+ }
177
+ /**
178
+ * Trigger wire configuration
179
+ */
180
+ export interface TriggerWire {
181
+ name: string;
182
+ startNode: string;
183
+ }
184
+ /**
185
+ * All wire configurations for workflows
186
+ */
187
+ export interface WorkflowWiresConfig {
188
+ http?: HttpWire[];
189
+ channel?: ChannelWire[];
190
+ queue?: QueueWire[];
191
+ cli?: CliWire[];
192
+ mcp?: McpWires;
193
+ schedule?: ScheduleWire[];
194
+ trigger?: TriggerWire[];
195
+ }
196
+ /**
197
+ * Workflow source type
198
+ * - 'dsl': Pure DSL workflow (pikkuWorkflowFunc) - can be round-tripped to code
199
+ * - 'complex': Complex workflow (pikkuWorkflowComplexFunc) - contains inline steps, not serializable
200
+ * - 'graph': Graph-based workflow (pikkuWorkflowGraph)
201
+ */
202
+ export type WorkflowSourceType = 'dsl' | 'complex' | 'graph';
203
+ /**
204
+ * Serialized workflow graph - the canonical JSON format
205
+ */
206
+ export interface SerializedWorkflowGraph {
207
+ /** Workflow name */
208
+ name: string;
209
+ /** Pikku function name (for runtime registration) */
210
+ pikkuFuncName: string;
211
+ /** Source type: 'dsl' for pikkuWorkflowFunc, 'graph' for pikkuWorkflowGraph */
212
+ source: WorkflowSourceType;
213
+ /** Optional description */
214
+ description?: string;
215
+ /** Tags for organization */
216
+ tags?: string[];
217
+ /** Workflow context/state variables (from Zod schema) */
218
+ context?: WorkflowContext;
219
+ /** Wires - how the workflow is triggered */
220
+ wires: WorkflowWiresConfig;
221
+ /** Serialized nodes */
222
+ nodes: Record<string, SerializedGraphNode>;
223
+ /** Entry node(s) - first nodes to execute */
224
+ entryNodeIds: string[];
225
+ }
226
+ /**
227
+ * All workflow graphs (serialized)
228
+ */
229
+ export type SerializedWorkflowGraphs = Record<string, SerializedWorkflowGraph>;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Serialized types for workflow graphs
3
+ * These are extracted by the inspector and stored as JSON
4
+ * Can be created from code (wireWorkflow) or UI
5
+ */
6
+ /**
7
+ * Check if value is a DataRef
8
+ */
9
+ export const isDataRef = (value) => typeof value === 'object' &&
10
+ value !== null &&
11
+ '$ref' in value &&
12
+ typeof value.$ref === 'string';
13
+ /**
14
+ * Check if value is a StateRef
15
+ */
16
+ export const isStateRef = (value) => typeof value === 'object' &&
17
+ value !== null &&
18
+ '$state' in value &&
19
+ typeof value.$state === 'string';
20
+ /**
21
+ * Helper functions for building input mappings
22
+ */
23
+ export const ref = (nodeId, path) => ({
24
+ $ref: nodeId,
25
+ path,
26
+ });
27
+ export const state = (name, path) => ({
28
+ $state: name,
29
+ path,
30
+ });
31
+ /**
32
+ * Type guard for function nodes
33
+ */
34
+ export const isFunctionNode = (node) => 'rpcName' in node;
35
+ /**
36
+ * Type guard for flow nodes
37
+ */
38
+ export const isFlowNode = (node) => 'flow' in node;
package/dist/visit.js CHANGED
@@ -14,6 +14,9 @@ import { addRPCInvocations } from './add/add-rpc-invocations.js';
14
14
  import { addMiddleware } from './add/add-middleware.js';
15
15
  import { addPermission } from './add/add-permission.js';
16
16
  import { addCLI, addCLIRenderers } from './add/add-cli.js';
17
+ import { addForgeNode } from './add/add-forge-node.js';
18
+ import { addForgeCredential } from './add/add-forge-credential.js';
19
+ import { addWorkflowGraph } from './add/add-workflow-graph.js';
17
20
  export const visitSetup = (logger, checker, node, state, options) => {
18
21
  addFileExtendsCoreType(node, checker, state.singletonServicesTypeImportMap, 'CoreSingletonServices', state);
19
22
  addFileExtendsCoreType(node, checker, state.wireServicesTypeImportMap, 'CoreServices', state);
@@ -39,5 +42,8 @@ export const visitRoutes = (logger, checker, node, state, options) => {
39
42
  addMCPResource(logger, node, checker, state, options);
40
43
  addMCPTool(logger, node, checker, state, options);
41
44
  addMCPPrompt(logger, node, checker, state, options);
45
+ addForgeNode(logger, node, checker, state, options);
46
+ addForgeCredential(logger, node, checker, state, options);
47
+ addWorkflowGraph(logger, node, checker, state, options);
42
48
  ts.forEachChild(node, (child) => visitRoutes(logger, checker, child, state, options));
43
49
  };
package/package.json CHANGED
@@ -1,11 +1,23 @@
1
1
  {
2
2
  "name": "@pikku/inspector",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "BUSL-1.1",
6
6
  "type": "module",
7
7
  "module": "dist/index.js",
8
8
  "main": "dist/index.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./workflow-graph": {
16
+ "types": "./dist/utils/workflow/graph/index.d.ts",
17
+ "import": "./dist/utils/workflow/graph/index.js",
18
+ "default": "./dist/utils/workflow/graph/index.js"
19
+ }
20
+ },
9
21
  "scripts": {
10
22
  "tsc": "tsc",
11
23
  "build": "tsc -b",
@@ -16,7 +28,7 @@
16
28
  "test:coverage": "bash run-tests.sh --coverage"
17
29
  },
18
30
  "dependencies": {
19
- "@pikku/core": "^0.11.1",
31
+ "@pikku/core": "^0.11.2",
20
32
  "path-to-regexp": "^8.3.0",
21
33
  "typescript": "^5.9"
22
34
  },
@@ -0,0 +1,119 @@
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
+ }