@octaviaflow/flow-rules 0.20.0 → 0.21.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.
package/dist/index.cjs CHANGED
@@ -121,6 +121,7 @@ __export(exports_src, {
121
121
  contractDrift: () => contractDrift,
122
122
  contractFieldsFor: () => contractFieldsFor,
123
123
  contractFor: () => contractFor,
124
+ contractValidationErrors: () => contractValidationErrors,
124
125
  contractVocabularyDrift: () => contractVocabularyDrift,
125
126
  declaredKeys: () => declaredKeys,
126
127
  detectRecordFields: () => detectRecordFields,
@@ -5249,6 +5250,21 @@ function missingContractRequired(node) {
5249
5250
  }
5250
5251
  return missing;
5251
5252
  }
5253
+ function contractValidationErrors(node) {
5254
+ if (HIDDEN_STATUS_ACTIONS.has(node.actionDefinitionId))
5255
+ return [];
5256
+ const contract = contractFor(node.actionDefinitionId);
5257
+ if (!contract?.validate)
5258
+ return [];
5259
+ try {
5260
+ const verdict = contract.validate(node.config ?? {});
5261
+ if (verdict.valid)
5262
+ return [];
5263
+ return [...new Set(verdict.errors.map((e) => String(e).trim()).filter(Boolean))];
5264
+ } catch {
5265
+ return [];
5266
+ }
5267
+ }
5252
5268
 
5253
5269
  // src/rules/workflowValidator.ts
5254
5270
  var nodeName = (n) => n.label || n.id;
@@ -5284,8 +5300,19 @@ function validateNode(node, allNodes, allEdges) {
5284
5300
  nodeId: node.id
5285
5301
  });
5286
5302
  }
5303
+ const contractIssues = cfg.missingRequired.length === 0 ? contractValidationErrors(node) : [];
5287
5304
  const contractGaps = missingContractRequired(node);
5288
- if (contractGaps.length > 0) {
5305
+ if (contractIssues.length > 0) {
5306
+ const shown = contractIssues.slice(0, 3).map((e) => e.replace(/[.\s]+$/, ""));
5307
+ const more = contractIssues.length > 3 ? ` (+${contractIssues.length - 3} more)` : "";
5308
+ errors.push({
5309
+ id: `node-${node.id}-contract`,
5310
+ type: "node",
5311
+ severity: "warning",
5312
+ message: `This step will not run as set up: ${shown.join("; ")}${more}. Open the step to fix it.`,
5313
+ nodeId: node.id
5314
+ });
5315
+ } else if (contractGaps.length > 0) {
5289
5316
  const fields = contractGaps.slice(0, 4).join(", ");
5290
5317
  const more = contractGaps.length > 4 ? ` +${contractGaps.length - 4} more` : "";
5291
5318
  errors.push({
package/dist/index.d.ts CHANGED
@@ -21,7 +21,7 @@
21
21
  export * from "./catalog/types";
22
22
  export { actionsRegistry, getAllActions, getActionById, getActionsByCategory, } from "./catalog/index";
23
23
  export { validateNode, validateEdge, validateWorkflow, getNodeValidationStatus, getEdgeValidationStatus, type ValidationError, type ValidationResult, } from "./rules/workflowValidator";
24
- export { getNodeConfigStatus, missingContractRequired, type NodeConfigStatus, type NodeConfigStatusResult, } from "./rules/nodeConfigStatus";
24
+ export { getNodeConfigStatus, missingContractRequired, contractValidationErrors, type NodeConfigStatus, type NodeConfigStatusResult, } from "./rules/nodeConfigStatus";
25
25
  export { generateWorkflowId, generateStepId, generateEdgeId, generateUUIDv4, } from "./ids";
26
26
  export { CONDITION_OPERATORS, ConditionConfig, type ConditionFilter, type ConditionOperator, } from "./catalog/logic/condition/config";
27
27
  export { ForEachConfig, type ForEachConfigType, type ForEachMode, } from "./catalog/logic/for-each/config";
package/dist/index.js CHANGED
@@ -5073,6 +5073,21 @@ function missingContractRequired(node) {
5073
5073
  }
5074
5074
  return missing;
5075
5075
  }
5076
+ function contractValidationErrors(node) {
5077
+ if (HIDDEN_STATUS_ACTIONS.has(node.actionDefinitionId))
5078
+ return [];
5079
+ const contract = contractFor(node.actionDefinitionId);
5080
+ if (!contract?.validate)
5081
+ return [];
5082
+ try {
5083
+ const verdict = contract.validate(node.config ?? {});
5084
+ if (verdict.valid)
5085
+ return [];
5086
+ return [...new Set(verdict.errors.map((e) => String(e).trim()).filter(Boolean))];
5087
+ } catch {
5088
+ return [];
5089
+ }
5090
+ }
5076
5091
 
5077
5092
  // src/rules/workflowValidator.ts
5078
5093
  var nodeName = (n) => n.label || n.id;
@@ -5108,8 +5123,19 @@ function validateNode(node, allNodes, allEdges) {
5108
5123
  nodeId: node.id
5109
5124
  });
5110
5125
  }
5126
+ const contractIssues = cfg.missingRequired.length === 0 ? contractValidationErrors(node) : [];
5111
5127
  const contractGaps = missingContractRequired(node);
5112
- if (contractGaps.length > 0) {
5128
+ if (contractIssues.length > 0) {
5129
+ const shown = contractIssues.slice(0, 3).map((e) => e.replace(/[.\s]+$/, ""));
5130
+ const more = contractIssues.length > 3 ? ` (+${contractIssues.length - 3} more)` : "";
5131
+ errors.push({
5132
+ id: `node-${node.id}-contract`,
5133
+ type: "node",
5134
+ severity: "warning",
5135
+ message: `This step will not run as set up: ${shown.join("; ")}${more}. Open the step to fix it.`,
5136
+ nodeId: node.id
5137
+ });
5138
+ } else if (contractGaps.length > 0) {
5113
5139
  const fields = contractGaps.slice(0, 4).join(", ");
5114
5140
  const more = contractGaps.length > 4 ? ` +${contractGaps.length - 4} more` : "";
5115
5141
  errors.push({
@@ -6231,6 +6257,7 @@ export {
6231
6257
  contractDrift,
6232
6258
  contractFieldsFor,
6233
6259
  contractFor,
6260
+ contractValidationErrors,
6234
6261
  contractVocabularyDrift,
6235
6262
  declaredKeys,
6236
6263
  detectRecordFields,
@@ -43,3 +43,14 @@ export declare function getNodeConfigStatus(node: WorkflowNode): NodeConfigStatu
43
43
  * The drift test pins the list of such keys, so a new one is a decision.
44
44
  */
45
45
  export declare function missingContractRequired(node: WorkflowNode): string[];
46
+ /**
47
+ * What the action's own contract check (`validate()`, the check its config
48
+ * panel runs) says is wrong with this node, deduplicated (DEV-643).
49
+ *
50
+ * `getNodeConfigStatus` only asks whether each required setting is filled in;
51
+ * a Variables step whose `fields` holds a row with no key passed it, so
52
+ * `check_flow` and the canvas called the flow fine while the panel and the
53
+ * MCP's `add_step` said "Each field needs a key". A validator that throws is
54
+ * treated as silent: a broken check must not invent a problem.
55
+ */
56
+ export declare function contractValidationErrors(node: WorkflowNode): string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octaviaflow/flow-rules",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "The flow action catalog and the rules engine behind Flow Doctor \u2014 one definition of what a step accepts and what makes a flow valid, shared by the editor and the server",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",