@synergenius/flow-weaver 0.8.3 → 0.9.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.
package/dist/validator.js CHANGED
@@ -14,11 +14,13 @@ const ERROR_DOC_URLS = {
14
14
  MISSING_EXIT_CONNECTION: `${DOCS_BASE}/concepts#start-and-exit`,
15
15
  INFERRED_NODE_TYPE: `${DOCS_BASE}/node-conversion`,
16
16
  DUPLICATE_CONNECTION: `${DOCS_BASE}/concepts#connections`,
17
+ STUB_NODE: `${DOCS_BASE}/model-driven#stub-nodes`,
17
18
  };
18
19
  export class WorkflowValidator {
19
20
  errors = [];
20
21
  warnings = [];
21
22
  strictMode = false;
23
+ draftMode = false;
22
24
  /** Look up instance sourceLocation by instance ID */
23
25
  getInstanceLocation(workflow, instanceId) {
24
26
  const instance = workflow.instances.find((inst) => inst.id === instanceId);
@@ -65,6 +67,7 @@ export class WorkflowValidator {
65
67
  this.errors = [];
66
68
  this.warnings = [];
67
69
  this.strictMode = options?.strictMode ?? false;
70
+ this.draftMode = options?.mode === 'draft';
68
71
  const nodeTypeMap = new Map();
69
72
  // Map by both functionName and name to support npm nodes (name='npm/pkg/func', functionName='func')
70
73
  workflow.nodeTypes.forEach((nodeType) => {
@@ -106,7 +109,7 @@ export class WorkflowValidator {
106
109
  workflow.instances.forEach((instance) => {
107
110
  // Check both name (for npm nodes like 'npm/pkg/func') and functionName (for local nodes)
108
111
  const nodeType = workflow.nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
109
- if (nodeType?.inferred) {
112
+ if (nodeType?.inferred && nodeType.variant !== 'STUB') {
110
113
  this.warnings.push({
111
114
  type: 'warning',
112
115
  code: 'INFERRED_NODE_TYPE',
@@ -116,6 +119,19 @@ export class WorkflowValidator {
116
119
  });
117
120
  }
118
121
  });
122
+ // Stub node diagnostics: always emit as errors, draft mode reclassifies at the end
123
+ workflow.instances.forEach((instance) => {
124
+ const nodeType = workflow.nodeTypes.find((nt) => nt.name === instance.nodeType || nt.functionName === instance.nodeType);
125
+ if (nodeType?.variant === 'STUB') {
126
+ this.errors.push({
127
+ type: 'error',
128
+ code: 'STUB_NODE',
129
+ message: `Node "${instance.id}" uses stub type "${instance.nodeType}" which has no implementation. Use draft mode to validate structure, or implement the node.`,
130
+ node: instance.id,
131
+ location: instance.sourceLocation,
132
+ });
133
+ }
134
+ });
119
135
  // Structural validation
120
136
  this.validateStructure(workflow);
121
137
  this.validateDuplicateNodeNames(workflow);
@@ -162,6 +178,30 @@ export class WorkflowValidator {
162
178
  return true;
163
179
  });
164
180
  }
181
+ // Draft mode post-processing: reclassify stub-related errors as warnings
182
+ if (this.draftMode) {
183
+ const stubTypeNames = new Set(workflow.nodeTypes.filter((nt) => nt.variant === 'STUB').map((nt) => nt.functionName));
184
+ // Also index by name for npm-style nodes
185
+ workflow.nodeTypes.filter((nt) => nt.variant === 'STUB').forEach((nt) => {
186
+ stubTypeNames.add(nt.name);
187
+ });
188
+ const stubInstanceIds = new Set(workflow.instances
189
+ .filter((inst) => stubTypeNames.has(inst.nodeType))
190
+ .map((inst) => inst.id));
191
+ const promoted = [];
192
+ this.errors = this.errors.filter((err) => {
193
+ if (err.code === 'STUB_NODE') {
194
+ promoted.push({ ...err, type: 'warning' });
195
+ return false;
196
+ }
197
+ if (err.code === 'MISSING_REQUIRED_INPUT' && err.node && stubInstanceIds.has(err.node)) {
198
+ promoted.push({ ...err, type: 'warning' });
199
+ return false;
200
+ }
201
+ return true;
202
+ });
203
+ this.warnings.push(...promoted);
204
+ }
165
205
  // Attach doc URLs to diagnostics that have mapped error codes
166
206
  for (const diag of [...this.errors, ...this.warnings]) {
167
207
  if (!diag.docUrl && ERROR_DOC_URLS[diag.code]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synergenius/flow-weaver",
3
- "version": "0.8.3",
3
+ "version": "0.9.1",
4
4
  "description": "Deterministic workflow compiler for AI agents. Compiles to standalone TypeScript, no runtime dependencies.",
5
5
  "private": false,
6
6
  "type": "module",