@openapi-flow/core 0.1.0 → 0.2.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/README.md +27 -5
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/arguments/list-api-request-media-types.d.ts +2 -0
- package/dist/arguments/list-api-request-media-types.js +8 -0
- package/dist/arguments/request-contract.d.ts +4 -0
- package/dist/arguments/request-contract.js +38 -0
- package/dist/arguments/request-media-type-priority.d.ts +2 -0
- package/dist/arguments/request-media-type-priority.js +8 -0
- package/dist/arguments/select-default-api-request-media-type.d.ts +3 -0
- package/dist/arguments/select-default-api-request-media-type.js +17 -0
- package/dist/arguments/validate-api-arguments.d.ts +2 -0
- package/dist/arguments/validate-api-arguments.js +140 -0
- package/dist/bindings/combine-binding-schemas.d.ts +4 -0
- package/dist/bindings/combine-binding-schemas.js +17 -0
- package/dist/bindings/create-native-array-item-schema.d.ts +4 -0
- package/dist/bindings/create-native-array-item-schema.js +16 -0
- package/dist/bindings/json-pointer.d.ts +3 -0
- package/dist/bindings/json-pointer.js +24 -0
- package/dist/bindings/materialize-api-arguments.d.ts +4 -0
- package/dist/bindings/materialize-api-arguments.js +54 -0
- package/dist/bindings/native-binding-schema.d.ts +3 -0
- package/dist/bindings/native-binding-schema.js +6 -0
- package/dist/bindings/project-array-item-schema.d.ts +5 -0
- package/dist/bindings/project-array-item-schema.js +67 -0
- package/dist/bindings/request-binding-schema.d.ts +2 -0
- package/dist/bindings/request-binding-schema.js +23 -0
- package/dist/bindings/request-container-kind.d.ts +3 -0
- package/dist/bindings/request-container-kind.js +17 -0
- package/dist/bindings/resources/clone-schema-resource.d.ts +3 -0
- package/dist/bindings/resources/clone-schema-resource.js +38 -0
- package/dist/bindings/resources/create-schema-resource-context.d.ts +4 -0
- package/dist/bindings/resources/create-schema-resource-context.js +130 -0
- package/dist/bindings/response-binding-schema.d.ts +3 -0
- package/dist/bindings/response-binding-schema.js +21 -0
- package/dist/bindings/schema-allows-container.d.ts +3 -0
- package/dist/bindings/schema-allows-container.js +20 -0
- package/dist/bindings/schema-at-pointer.d.ts +4 -0
- package/dist/bindings/schema-at-pointer.js +89 -0
- package/dist/bindings/validate-api-binding-assignments.d.ts +2 -0
- package/dist/bindings/validate-api-binding-assignments.js +81 -0
- package/dist/bindings/validate-api-binding-gaps.d.ts +2 -0
- package/dist/bindings/validate-api-binding-gaps.js +10 -0
- package/dist/bindings/validate-api-binding-plan.d.ts +3 -0
- package/dist/bindings/validate-api-binding-plan.js +11 -0
- package/dist/bindings/validate-native-output-contracts.d.ts +3 -0
- package/dist/bindings/validate-native-output-contracts.js +16 -0
- package/dist/internal-api.d.ts +30 -0
- package/dist/internal-api.js +25 -0
- package/dist/openapi/catalog/create-api-catalog.d.ts +2 -0
- package/dist/openapi/catalog/create-api-catalog.js +34 -0
- package/dist/openapi/catalog/list-api-operations.d.ts +2 -0
- package/dist/openapi/catalog/list-api-operations.js +3 -0
- package/dist/openapi/common/check-oas-value.d.ts +3 -0
- package/dist/openapi/common/check-oas-value.js +6 -0
- package/dist/openapi/common/check-schema-value.d.ts +2 -0
- package/dist/openapi/common/check-schema-value.js +12 -0
- package/dist/openapi/common/create-oas-value-validator.d.ts +3 -0
- package/dist/openapi/common/create-oas-value-validator.js +39 -0
- package/dist/openapi/common/normalize-oas-value-schema.d.ts +4 -0
- package/dist/openapi/common/normalize-oas-value-schema.js +50 -0
- package/dist/openapi/common/operation-metadata.d.ts +2 -0
- package/dist/openapi/common/operation-metadata.js +25 -0
- package/dist/openapi/common/operation-reference.d.ts +3 -0
- package/dist/openapi/{operation-reference.js → common/operation-reference.js} +1 -4
- package/dist/openapi/common/parse-spec-utils.d.ts +3 -0
- package/dist/openapi/common/parse-spec-utils.js +12 -0
- package/dist/openapi/common/resolve-openapi-security.d.ts +3 -0
- package/dist/openapi/common/resolve-openapi-security.js +28 -0
- package/dist/openapi/common/unsupported-operation-error.d.ts +4 -0
- package/dist/openapi/common/unsupported-operation-error.js +8 -0
- package/dist/openapi/common/validate-spec.d.ts +3 -0
- package/dist/openapi/{validate-spec.js → common/validate-spec.js} +2 -1
- package/dist/openapi/inbound/callback/parse-callback-operations.d.ts +3 -0
- package/dist/openapi/inbound/callback/parse-callback-operations.js +31 -0
- package/dist/openapi/inbound/inbound-path-item-operations.d.ts +4 -0
- package/dist/openapi/inbound/inbound-path-item-operations.js +31 -0
- package/dist/openapi/inbound/list-inbound-operations.d.ts +4 -0
- package/dist/openapi/inbound/list-inbound-operations.js +13 -0
- package/dist/openapi/inbound/webhook/parse-webhook-operations.d.ts +3 -0
- package/dist/openapi/inbound/webhook/parse-webhook-operations.js +13 -0
- package/dist/openapi/request/create-operation-catalog.d.ts +2 -0
- package/dist/openapi/request/create-operation-catalog.js +24 -0
- package/dist/openapi/request/encode-form-field.d.ts +2 -0
- package/dist/openapi/request/encode-form-field.js +6 -0
- package/dist/openapi/request/map-http-operation-contract.d.ts +4 -0
- package/dist/openapi/request/map-http-operation-contract.js +21 -0
- package/dist/openapi/request/operation-mapping.d.ts +3 -0
- package/dist/openapi/request/operation-mapping.js +10 -0
- package/dist/openapi/request/parse-operation-responses.d.ts +3 -0
- package/dist/openapi/request/parse-operation-responses.js +30 -0
- package/dist/openapi/request/parse-request-body.d.ts +3 -0
- package/dist/openapi/request/parse-request-body.js +29 -0
- package/dist/openapi/request/parse-request-operations.d.ts +7 -0
- package/dist/openapi/request/parse-request-operations.js +50 -0
- package/dist/openapi/request/parse-request-parameters.d.ts +3 -0
- package/dist/openapi/request/parse-request-parameters.js +93 -0
- package/dist/openapi/request/parse-security.d.ts +3 -0
- package/dist/openapi/request/parse-security.js +31 -0
- package/dist/openapi/request/request-operation-reference.d.ts +1 -0
- package/dist/openapi/request/request-operation-reference.js +4 -0
- package/dist/openapi/request/response-contract.d.ts +6 -0
- package/dist/openapi/request/response-contract.js +32 -0
- package/dist/openapi/request/scalar-response-schema.d.ts +2 -0
- package/dist/openapi/request/scalar-response-schema.js +20 -0
- package/dist/openapi/request/serialize-form-body.d.ts +3 -0
- package/dist/openapi/request/serialize-form-body.js +89 -0
- package/dist/openapi/request/serialize-parameter.d.ts +5 -0
- package/dist/openapi/request/serialize-parameter.js +145 -0
- package/dist/openapi/resolve-api-operations.d.ts +3 -0
- package/dist/openapi/resolve-api-operations.js +84 -0
- package/dist/openapi/response/api-response-validation-value.d.ts +3 -0
- package/dist/openapi/response/api-response-validation-value.js +21 -0
- package/dist/openapi/response/create-api-response-schema.d.ts +4 -0
- package/dist/openapi/response/create-api-response-schema.js +114 -0
- package/dist/openapi/response/create-response-array-item-schema.d.ts +4 -0
- package/dist/openapi/response/create-response-array-item-schema.js +12 -0
- package/dist/openapi/response/create-response-value-schema.d.ts +4 -0
- package/dist/openapi/response/create-response-value-schema.js +8 -0
- package/dist/openapi/response/list-api-response-pointers.d.ts +4 -0
- package/dist/openapi/response/list-api-response-pointers.js +30 -0
- package/dist/openapi/response/list-declared-schema-pointers.d.ts +4 -0
- package/dist/openapi/response/list-declared-schema-pointers.js +91 -0
- package/dist/public-api.d.ts +49 -7
- package/dist/public-api.js +35 -5
- package/dist/runtime-api.d.ts +6 -0
- package/dist/runtime-api.js +7 -0
- package/dist/schemas/create-api-argument-generation-contract.d.ts +4 -0
- package/dist/schemas/create-api-argument-generation-contract.js +72 -0
- package/dist/schemas/create-api-arguments-schema.d.ts +3 -0
- package/dist/schemas/create-api-arguments-schema.js +4 -0
- package/dist/schemas/create-api-request-schema.d.ts +4 -0
- package/dist/schemas/create-api-request-schema.js +50 -0
- package/dist/schemas/create-oas-value-schema.d.ts +6 -0
- package/dist/schemas/create-oas-value-schema.js +25 -0
- package/dist/schemas/operation-plan-schema.d.ts +8 -0
- package/dist/schemas/operation-plan-schema.js +99 -0
- package/dist/schemas/project-literal-array-schema.d.ts +4 -0
- package/dist/schemas/project-literal-array-schema.js +55 -0
- package/dist/schemas/project-literal-input-schema.d.ts +3 -0
- package/dist/schemas/project-literal-input-schema.js +129 -0
- package/dist/types/api-argument-generation.d.ts +13 -0
- package/dist/types/api-arguments.d.ts +42 -0
- package/dist/types/api-arguments.js +1 -0
- package/dist/types/api-bindings.d.ts +34 -0
- package/dist/types/api-bindings.js +1 -0
- package/dist/types/api-catalog.d.ts +34 -0
- package/dist/types/api-catalog.js +1 -0
- package/dist/types/api-operation.d.ts +26 -0
- package/dist/types/api-operation.js +1 -0
- package/dist/types/api-response-pointers.d.ts +20 -0
- package/dist/types/api-response-pointers.js +1 -0
- package/dist/types/api-response.d.ts +14 -0
- package/dist/types/api-response.js +1 -0
- package/dist/types/array-projection.d.ts +5 -0
- package/dist/types/array-projection.js +1 -0
- package/dist/types/catalog.d.ts +17 -0
- package/dist/types/catalog.js +1 -0
- package/dist/types/inbound.d.ts +18 -0
- package/dist/types/inbound.js +1 -0
- package/dist/types/legacy-plan.d.ts +47 -0
- package/dist/types/legacy-plan.js +1 -0
- package/dist/types/node-output.d.ts +11 -0
- package/dist/types/node-output.js +1 -0
- package/dist/types/oas-value-validation.d.ts +8 -0
- package/dist/types/oas-value-validation.js +1 -0
- package/dist/types/openapi.d.ts +7 -23
- package/dist/types/request.d.ts +41 -0
- package/dist/types/request.js +1 -0
- package/dist/types/reviewable-workflow.d.ts +53 -0
- package/dist/types/reviewable-workflow.js +1 -0
- package/dist/types/schema-dialect.d.ts +1 -0
- package/dist/types/schema-dialect.js +1 -0
- package/dist/types/schema-json.d.ts +4 -0
- package/dist/types/schema-json.js +1 -0
- package/dist/types/schema-resource.d.ts +12 -0
- package/dist/types/schema-resource.js +1 -0
- package/dist/types/security.d.ts +17 -0
- package/dist/types/security.js +1 -0
- package/dist/types/workflow-capability-planning.d.ts +10 -0
- package/dist/types/workflow-capability-planning.js +1 -0
- package/dist/types/workflow-plan.d.ts +104 -0
- package/dist/types/workflow-plan.js +1 -0
- package/dist/utils/is-object.d.ts +2 -0
- package/dist/utils/is-object.js +3 -0
- package/dist/utils/json-values-equal.d.ts +2 -0
- package/dist/utils/json-values-equal.js +16 -0
- package/dist/workflow/assert-api-call-identities.d.ts +1 -0
- package/dist/workflow/assert-api-call-identities.js +6 -0
- package/dist/workflow/create-native-output-contracts.d.ts +3 -0
- package/dist/workflow/create-native-output-contracts.js +23 -0
- package/dist/workflow/create-reviewable-node-contracts.d.ts +3 -0
- package/dist/workflow/create-reviewable-node-contracts.js +36 -0
- package/dist/workflow/create-reviewable-workflow-graph-plan.d.ts +2 -0
- package/dist/workflow/create-reviewable-workflow-graph-plan.js +29 -0
- package/dist/workflow/create-workflow-graph-plan.d.ts +2 -0
- package/dist/workflow/create-workflow-graph-plan.js +17 -0
- package/dist/workflow/create-workflow-node-contracts.d.ts +2 -0
- package/dist/workflow/create-workflow-node-contracts.js +50 -0
- package/dist/workflow/create-workflow-planning-capabilities.d.ts +3 -0
- package/dist/workflow/create-workflow-planning-capabilities.js +20 -0
- package/dist/workflow/parse-native-node-parameters.d.ts +3 -0
- package/dist/workflow/parse-native-node-parameters.js +7 -0
- package/dist/workflow/validate-exclusive-output-rejoin.d.ts +3 -0
- package/dist/workflow/validate-exclusive-output-rejoin.js +26 -0
- package/dist/workflow/validate-native-api-response-references.d.ts +2 -0
- package/dist/workflow/validate-native-api-response-references.js +26 -0
- package/dist/workflow/validate-reviewable-workflow-graph-plan.d.ts +2 -0
- package/dist/workflow/validate-reviewable-workflow-graph-plan.js +98 -0
- package/dist/workflow/validate-workflow-edges.d.ts +2 -0
- package/dist/workflow/validate-workflow-edges.js +19 -0
- package/dist/workflow/validate-workflow-graph-plan.d.ts +3 -0
- package/dist/workflow/validate-workflow-graph-plan.js +41 -0
- package/dist/workflow/validate-workflow-topology.d.ts +2 -0
- package/dist/workflow/validate-workflow-topology.js +105 -0
- package/package.json +19 -25
- package/dist/openapi/operation-reference.d.ts +0 -3
- package/dist/openapi/parse-spec-utils.d.ts +0 -5
- package/dist/openapi/parse-spec-utils.js +0 -52
- package/dist/openapi/parse-spec.d.ts +0 -5
- package/dist/openapi/parse-spec.js +0 -182
- package/dist/openapi/validate-spec.d.ts +0 -3
- package/dist/planning/generate-workflow.d.ts +0 -2
- package/dist/planning/generate-workflow.js +0 -126
- package/dist/schemas/planning-schemas.d.ts +0 -62
- package/dist/schemas/planning-schemas.js +0 -48
- package/dist/types/workflow.d.ts +0 -75
- package/dist/utils/validation.d.ts +0 -7
- package/dist/utils/validation.js +0 -44
- package/dist/utils/workflow-id.d.ts +0 -1
- package/dist/utils/workflow-id.js +0 -8
- package/dist/workflow/compile-sequence.d.ts +0 -2
- package/dist/workflow/compile-sequence.js +0 -167
- package/dist/workflow/compile-workflow.d.ts +0 -4
- package/dist/workflow/compile-workflow.js +0 -128
- package/dist/workflow/sequence-url.d.ts +0 -3
- package/dist/workflow/sequence-url.js +0 -81
- package/dist/workflow/workflow-helpers.d.ts +0 -6
- package/dist/workflow/workflow-helpers.js +0 -81
- /package/dist/types/{workflow.js → api-argument-generation.js} +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ApiSecurityRequirement } from './api-operation.js';
|
|
2
|
+
import type { JsonObject } from './openapi.js';
|
|
3
|
+
export interface ResolveOpenApiSecurityInput {
|
|
4
|
+
operationRef: string;
|
|
5
|
+
security: ApiSecurityRequirement[];
|
|
6
|
+
securitySchemes: JsonObject;
|
|
7
|
+
requirementIndex?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ResolvedSecurityScheme {
|
|
10
|
+
name: string;
|
|
11
|
+
scopes: string[];
|
|
12
|
+
definition: JsonObject;
|
|
13
|
+
}
|
|
14
|
+
export interface ResolvedOpenApiSecurity {
|
|
15
|
+
requirementIndex: number;
|
|
16
|
+
schemes: ResolvedSecurityScheme[];
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WorkflowCapability, PlannedNativeNode } from './workflow-plan.js';
|
|
2
|
+
export interface WorkflowCapabilityPlanningContext {
|
|
3
|
+
apiCallIds: string[];
|
|
4
|
+
}
|
|
5
|
+
export interface WorkflowCapabilityPlanningInput extends WorkflowCapabilityPlanningContext {
|
|
6
|
+
capabilities: WorkflowCapability[];
|
|
7
|
+
}
|
|
8
|
+
export interface ValidateNativeApiResponseReferencesInput extends WorkflowCapabilityPlanningInput {
|
|
9
|
+
nativeNodes: PlannedNativeNode[];
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { ApiOperationContract } from './api-operation.js';
|
|
3
|
+
import type { ApiCallArguments, JsonValueObject } from './api-arguments.js';
|
|
4
|
+
import type { BindingSchema } from './api-bindings.js';
|
|
5
|
+
import type { WorkflowCapabilityPlanningContext } from './workflow-capability-planning.js';
|
|
6
|
+
export interface WorkflowApiMaterial {
|
|
7
|
+
operation: ApiOperationContract;
|
|
8
|
+
arguments: ApiCallArguments;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkflowResponseReference {
|
|
11
|
+
source: 'response';
|
|
12
|
+
nodeId: string;
|
|
13
|
+
/** RFC 6901 pointer into the API response body, not n8n's envelope. */
|
|
14
|
+
pointer: string;
|
|
15
|
+
}
|
|
16
|
+
export interface WorkflowNodeOutputReference {
|
|
17
|
+
source: 'node-output';
|
|
18
|
+
nodeId: string;
|
|
19
|
+
/** RFC 6901 pointer into the declared native JSON item. */
|
|
20
|
+
pointer: string;
|
|
21
|
+
}
|
|
22
|
+
export type WorkflowValueReference = WorkflowResponseReference | WorkflowNodeOutputReference;
|
|
23
|
+
export interface WorkflowCapability {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
parametersSchema: z.ZodType<JsonValueObject>;
|
|
27
|
+
/** Optional generation contract specialized by the capability, never by field-name guessing. */
|
|
28
|
+
createPlanningParametersSchema?: (context: WorkflowCapabilityPlanningContext) => z.ZodType<JsonValueObject>;
|
|
29
|
+
inputPorts: (parameters: JsonValueObject) => string[];
|
|
30
|
+
outputPorts: (parameters: JsonValueObject) => string[];
|
|
31
|
+
responseReferences: (parameters: JsonValueObject) => WorkflowResponseReference[];
|
|
32
|
+
/** Native provenance required by this implementation, never inferred from values. */
|
|
33
|
+
nodeOutputReferences?: (parameters: JsonValueObject) => WorkflowNodeOutputReference[];
|
|
34
|
+
/** Dependencies required on a specific incoming port, before a join combines routes. */
|
|
35
|
+
inputReferences?: (parameters: JsonValueObject) => Record<string, WorkflowValueReference[]>;
|
|
36
|
+
waitsForAllInputs: boolean;
|
|
37
|
+
exclusiveOutputPorts: boolean;
|
|
38
|
+
/** Rejoin alternatives from this declared exclusive producer, not independent branches. */
|
|
39
|
+
rejoinsExclusiveOutputs?: (parameters: JsonValueObject) => string;
|
|
40
|
+
/** Explicit native JSON output contract; absent means no bindable output. */
|
|
41
|
+
outputSchema?: (parameters: JsonValueObject) => BindingSchema;
|
|
42
|
+
}
|
|
43
|
+
export interface PlannedNativeNode {
|
|
44
|
+
id: string;
|
|
45
|
+
capability: string;
|
|
46
|
+
parameters: JsonValueObject;
|
|
47
|
+
}
|
|
48
|
+
export interface WorkflowPlanEdge {
|
|
49
|
+
from: string;
|
|
50
|
+
output: string;
|
|
51
|
+
to: string;
|
|
52
|
+
input: string;
|
|
53
|
+
}
|
|
54
|
+
export interface WorkflowPlanGap {
|
|
55
|
+
description: string;
|
|
56
|
+
}
|
|
57
|
+
export interface WorkflowGraphProposal {
|
|
58
|
+
nativeNodes: PlannedNativeNode[];
|
|
59
|
+
edges: WorkflowPlanEdge[];
|
|
60
|
+
gaps: WorkflowPlanGap[];
|
|
61
|
+
}
|
|
62
|
+
export interface WorkflowGraphPlan extends WorkflowGraphProposal {
|
|
63
|
+
starts: string[];
|
|
64
|
+
}
|
|
65
|
+
export interface CreateWorkflowGraphPlanInput {
|
|
66
|
+
proposal: WorkflowGraphProposal;
|
|
67
|
+
materials: WorkflowApiMaterial[];
|
|
68
|
+
capabilities: WorkflowCapability[];
|
|
69
|
+
}
|
|
70
|
+
export interface ValidateWorkflowGraphPlanInput {
|
|
71
|
+
plan: WorkflowGraphPlan;
|
|
72
|
+
materials: WorkflowApiMaterial[];
|
|
73
|
+
capabilities: WorkflowCapability[];
|
|
74
|
+
}
|
|
75
|
+
export interface WorkflowRouteState {
|
|
76
|
+
branches: Map<string, string>;
|
|
77
|
+
completed: Set<string>;
|
|
78
|
+
}
|
|
79
|
+
export interface WorkflowPlanNodeContract {
|
|
80
|
+
id: string;
|
|
81
|
+
inputs: string[];
|
|
82
|
+
outputs: string[];
|
|
83
|
+
references: WorkflowValueReference[];
|
|
84
|
+
inputReferences?: Record<string, WorkflowValueReference[]>;
|
|
85
|
+
dependencyNodeIds: string[];
|
|
86
|
+
waitsForAllInputs: boolean;
|
|
87
|
+
exclusiveOutputPorts: boolean;
|
|
88
|
+
rejoinsExclusiveOutputs?: string;
|
|
89
|
+
}
|
|
90
|
+
export interface WorkflowNodeContractsInput {
|
|
91
|
+
materials: WorkflowApiMaterial[];
|
|
92
|
+
nativeNodes: PlannedNativeNode[];
|
|
93
|
+
capabilities: WorkflowCapability[];
|
|
94
|
+
}
|
|
95
|
+
export interface ValidateWorkflowTopologyInput {
|
|
96
|
+
contracts: WorkflowPlanNodeContract[];
|
|
97
|
+
apiIds: Set<string>;
|
|
98
|
+
edges: WorkflowPlanEdge[];
|
|
99
|
+
starts: string[];
|
|
100
|
+
}
|
|
101
|
+
export interface ValidateWorkflowEdgesInput {
|
|
102
|
+
contracts: WorkflowPlanNodeContract[];
|
|
103
|
+
edges: WorkflowPlanEdge[];
|
|
104
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Compare JSON values without normalizing their types or object key order. */
|
|
2
|
+
export function jsonValuesEqual(left, right) {
|
|
3
|
+
if (left === right)
|
|
4
|
+
return true;
|
|
5
|
+
if (Array.isArray(left) || Array.isArray(right))
|
|
6
|
+
return (Array.isArray(left) &&
|
|
7
|
+
Array.isArray(right) &&
|
|
8
|
+
left.length === right.length &&
|
|
9
|
+
left.every((value, index) => jsonValuesEqual(value, right[index])));
|
|
10
|
+
if (!left || !right || typeof left !== 'object' || typeof right !== 'object')
|
|
11
|
+
return false;
|
|
12
|
+
const keys = Object.keys(left);
|
|
13
|
+
return (keys.length === Object.keys(right).length &&
|
|
14
|
+
keys.every((key) => Object.hasOwn(right, key) &&
|
|
15
|
+
jsonValuesEqual(Reflect.get(left, key), Reflect.get(right, key))));
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function assertApiCallIdentities(apiCallIds: string[]): void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export function assertApiCallIdentities(apiCallIds) {
|
|
2
|
+
if (!Array.isArray(apiCallIds) ||
|
|
3
|
+
apiCallIds.some((id) => typeof id !== 'string' || !id.trim()) ||
|
|
4
|
+
new Set(apiCallIds).size !== apiCallIds.length)
|
|
5
|
+
throw new Error('planning.apiCallIds must contain unique non-empty call IDs');
|
|
6
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { CreateNativeOutputContractsInput, NativeOutputContract } from '../types/node-output.js';
|
|
2
|
+
/** Derive contracts from actual capabilities/parameters, never model guesses. */
|
|
3
|
+
export declare function createNativeOutputContracts({ nativeNodes, capabilities, }: CreateNativeOutputContractsInput): NativeOutputContract[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { parseNativeNodeParameters } from './parse-native-node-parameters.js';
|
|
2
|
+
import { validateNativeOutputContracts } from '../bindings/validate-native-output-contracts.js';
|
|
3
|
+
/** Derive contracts from actual capabilities/parameters, never model guesses. */
|
|
4
|
+
export function createNativeOutputContracts({ nativeNodes, capabilities, }) {
|
|
5
|
+
const registry = new Map(capabilities.map((item) => [item.name, item]));
|
|
6
|
+
if (registry.size !== capabilities.length)
|
|
7
|
+
throw new Error('capabilities contains duplicate names');
|
|
8
|
+
const ids = new Set();
|
|
9
|
+
const outputs = nativeNodes.flatMap((planned) => {
|
|
10
|
+
if (!planned.id.trim() || ids.has(planned.id))
|
|
11
|
+
throw new Error('native nodes require unique non-empty IDs');
|
|
12
|
+
ids.add(planned.id);
|
|
13
|
+
const capability = registry.get(planned.capability);
|
|
14
|
+
if (!capability)
|
|
15
|
+
throw new Error(`node ${planned.id}: unknown capability ${planned.capability}`);
|
|
16
|
+
const parameters = parseNativeNodeParameters(planned, capability);
|
|
17
|
+
return capability.outputSchema
|
|
18
|
+
? [{ nodeId: planned.id, schema: capability.outputSchema(parameters) }]
|
|
19
|
+
: [];
|
|
20
|
+
});
|
|
21
|
+
validateNativeOutputContracts(outputs);
|
|
22
|
+
return outputs;
|
|
23
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ValidateReviewableWorkflowGraphPlanInput } from '../types/reviewable-workflow.js';
|
|
2
|
+
import type { WorkflowPlanNodeContract } from '../types/workflow-plan.js';
|
|
3
|
+
export declare function createReviewableNodeContracts({ plan, materials, capabilities, }: ValidateReviewableWorkflowGraphPlanInput): WorkflowPlanNodeContract[];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createWorkflowNodeContracts } from './create-workflow-node-contracts.js';
|
|
2
|
+
export function createReviewableNodeContracts({ plan, materials, capabilities, }) {
|
|
3
|
+
const blocked = new Set(plan.blockedCalls.map((item) => item.callId));
|
|
4
|
+
const ready = materials.filter((item) => item.status === 'ready');
|
|
5
|
+
const contracts = createWorkflowNodeContracts({
|
|
6
|
+
materials: ready,
|
|
7
|
+
nativeNodes: plan.nativeNodes,
|
|
8
|
+
capabilities,
|
|
9
|
+
});
|
|
10
|
+
for (const contract of contracts)
|
|
11
|
+
if (blocked.has(contract.id)) {
|
|
12
|
+
contract.dependencyNodeIds = contract.references.map((reference) => reference.nodeId);
|
|
13
|
+
contract.references = [];
|
|
14
|
+
}
|
|
15
|
+
for (const material of materials.filter((item) => item.status === 'blocked'))
|
|
16
|
+
contracts.push({
|
|
17
|
+
id: material.callId,
|
|
18
|
+
inputs: ['main'],
|
|
19
|
+
outputs: ['main'],
|
|
20
|
+
references: [],
|
|
21
|
+
dependencyNodeIds: material.bindings.map((binding) => binding.sourceNodeId),
|
|
22
|
+
waitsForAllInputs: false,
|
|
23
|
+
exclusiveOutputPorts: false,
|
|
24
|
+
});
|
|
25
|
+
for (const gap of plan.gaps)
|
|
26
|
+
contracts.push({
|
|
27
|
+
id: gap.id,
|
|
28
|
+
inputs: ['main'],
|
|
29
|
+
outputs: ['main'],
|
|
30
|
+
references: [],
|
|
31
|
+
dependencyNodeIds: [],
|
|
32
|
+
waitsForAllInputs: false,
|
|
33
|
+
exclusiveOutputPorts: false,
|
|
34
|
+
});
|
|
35
|
+
return contracts;
|
|
36
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createReviewableNodeContracts } from './create-reviewable-node-contracts.js';
|
|
2
|
+
import { validateReviewableWorkflowGraphPlan } from './validate-reviewable-workflow-graph-plan.js';
|
|
3
|
+
export function createReviewableWorkflowGraphPlan(input) {
|
|
4
|
+
const { proposal, materials } = input;
|
|
5
|
+
const plan = {
|
|
6
|
+
nativeNodes: proposal.nativeNodes,
|
|
7
|
+
edges: proposal.edges,
|
|
8
|
+
gaps: [
|
|
9
|
+
...input.gaps,
|
|
10
|
+
...proposal.additionalGaps.map((gap) => ({
|
|
11
|
+
...gap,
|
|
12
|
+
stage: 'workflow-graph',
|
|
13
|
+
})),
|
|
14
|
+
],
|
|
15
|
+
blockedCalls: [
|
|
16
|
+
...materials
|
|
17
|
+
.filter((item) => item.status === 'blocked')
|
|
18
|
+
.map((item) => ({ callId: item.callId, gapIds: item.gapIds })),
|
|
19
|
+
...proposal.blockedCalls,
|
|
20
|
+
],
|
|
21
|
+
starts: [],
|
|
22
|
+
};
|
|
23
|
+
const incoming = new Set(plan.edges.map((edge) => edge.to));
|
|
24
|
+
plan.starts = createReviewableNodeContracts({ ...input, plan })
|
|
25
|
+
.filter((item) => !incoming.has(item.id))
|
|
26
|
+
.map((item) => item.id);
|
|
27
|
+
validateReviewableWorkflowGraphPlan({ ...input, plan });
|
|
28
|
+
return plan;
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { validateWorkflowGraphPlan } from './validate-workflow-graph-plan.js';
|
|
2
|
+
export function createWorkflowGraphPlan(input) {
|
|
3
|
+
const incomingIds = new Set(input.proposal.edges.map((edge) => edge.to));
|
|
4
|
+
const nodeIds = [
|
|
5
|
+
...input.materials.map((item) => item.arguments.callId),
|
|
6
|
+
...input.proposal.nativeNodes.map((item) => item.id),
|
|
7
|
+
];
|
|
8
|
+
const plan = {
|
|
9
|
+
...input.proposal,
|
|
10
|
+
starts: input.proposal.gaps.length
|
|
11
|
+
? []
|
|
12
|
+
: nodeIds.filter((id) => !incomingIds.has(id)),
|
|
13
|
+
};
|
|
14
|
+
if (!plan.gaps.length)
|
|
15
|
+
validateWorkflowGraphPlan({ ...input, plan });
|
|
16
|
+
return plan;
|
|
17
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseNativeNodeParameters } from './parse-native-node-parameters.js';
|
|
2
|
+
export function createWorkflowNodeContracts({ materials, nativeNodes, capabilities, }) {
|
|
3
|
+
const registry = new Map(capabilities.map((item) => [item.name, item]));
|
|
4
|
+
if (registry.size !== capabilities.length)
|
|
5
|
+
throw new Error('capabilities contains duplicate names');
|
|
6
|
+
const contracts = materials.map((item) => ({
|
|
7
|
+
id: item.arguments.callId,
|
|
8
|
+
inputs: ['main'],
|
|
9
|
+
outputs: ['main'],
|
|
10
|
+
references: item.arguments.bindings.map((binding) => ({
|
|
11
|
+
source: materials.some((source) => source.arguments.callId === binding.sourceNodeId)
|
|
12
|
+
? 'response'
|
|
13
|
+
: 'node-output',
|
|
14
|
+
nodeId: binding.sourceNodeId,
|
|
15
|
+
pointer: binding.sourcePointer,
|
|
16
|
+
})),
|
|
17
|
+
dependencyNodeIds: [],
|
|
18
|
+
waitsForAllInputs: false,
|
|
19
|
+
exclusiveOutputPorts: false,
|
|
20
|
+
}));
|
|
21
|
+
for (const native of nativeNodes) {
|
|
22
|
+
const capability = registry.get(native.capability);
|
|
23
|
+
if (!capability)
|
|
24
|
+
throw new Error(`node ${native.id}: unknown capability ${native.capability}`);
|
|
25
|
+
const parameters = parseNativeNodeParameters(native, capability);
|
|
26
|
+
contracts.push({
|
|
27
|
+
id: native.id,
|
|
28
|
+
inputs: capability.inputPorts(parameters),
|
|
29
|
+
outputs: capability.outputPorts(parameters),
|
|
30
|
+
references: [
|
|
31
|
+
...capability.responseReferences(parameters),
|
|
32
|
+
...(capability.nodeOutputReferences
|
|
33
|
+
? capability.nodeOutputReferences(parameters)
|
|
34
|
+
: []),
|
|
35
|
+
],
|
|
36
|
+
...(capability.inputReferences
|
|
37
|
+
? { inputReferences: capability.inputReferences(parameters) }
|
|
38
|
+
: {}),
|
|
39
|
+
dependencyNodeIds: [],
|
|
40
|
+
waitsForAllInputs: capability.waitsForAllInputs,
|
|
41
|
+
exclusiveOutputPorts: capability.exclusiveOutputPorts,
|
|
42
|
+
...(capability.rejoinsExclusiveOutputs
|
|
43
|
+
? {
|
|
44
|
+
rejoinsExclusiveOutputs: capability.rejoinsExclusiveOutputs(parameters),
|
|
45
|
+
}
|
|
46
|
+
: {}),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return contracts;
|
|
50
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { WorkflowCapability } from '../types/workflow-plan.js';
|
|
2
|
+
import type { WorkflowCapabilityPlanningInput } from '../types/workflow-capability-planning.js';
|
|
3
|
+
export declare function createWorkflowPlanningCapabilities({ capabilities, apiCallIds, }: WorkflowCapabilityPlanningInput): WorkflowCapability[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { assertApiCallIdentities } from './assert-api-call-identities.js';
|
|
2
|
+
export function createWorkflowPlanningCapabilities({ capabilities, apiCallIds, }) {
|
|
3
|
+
assertApiCallIdentities(apiCallIds);
|
|
4
|
+
if (!Array.isArray(capabilities))
|
|
5
|
+
throw new Error('planning.capabilities must be an array');
|
|
6
|
+
if (new Set(capabilities.map((item) => item.name)).size !== capabilities.length)
|
|
7
|
+
throw new Error('planning.capabilities contains duplicate names');
|
|
8
|
+
return capabilities.map((capability) => {
|
|
9
|
+
if (capability.createPlanningParametersSchema === undefined)
|
|
10
|
+
return capability;
|
|
11
|
+
if (typeof capability.createPlanningParametersSchema !== 'function')
|
|
12
|
+
throw new Error(`planning.capabilities[${capability.name}].createPlanningParametersSchema must be a function`);
|
|
13
|
+
const parametersSchema = capability.createPlanningParametersSchema({
|
|
14
|
+
apiCallIds: [...apiCallIds],
|
|
15
|
+
});
|
|
16
|
+
if (!parametersSchema || typeof parametersSchema.safeParse !== 'function')
|
|
17
|
+
throw new Error(`planning.capabilities[${capability.name}].createPlanningParametersSchema must return a Zod schema`);
|
|
18
|
+
return { ...capability, parametersSchema };
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { JsonValueObject } from '../types/api-arguments.js';
|
|
2
|
+
import type { PlannedNativeNode, WorkflowCapability } from '../types/workflow-plan.js';
|
|
3
|
+
export declare function parseNativeNodeParameters(planned: PlannedNativeNode, capability: WorkflowCapability): JsonValueObject;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsonValuesEqual } from '../utils/json-values-equal.js';
|
|
2
|
+
export function parseNativeNodeParameters(planned, capability) {
|
|
3
|
+
const parameters = capability.parametersSchema.parse(planned.parameters);
|
|
4
|
+
if (!jsonValuesEqual(parameters, planned.parameters))
|
|
5
|
+
throw new Error(`node ${planned.id}: parameter schema must validate without defaults, coercion or transformation`);
|
|
6
|
+
return parameters;
|
|
7
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { WorkflowPlanNodeContract, WorkflowRouteState } from '../types/workflow-plan.js';
|
|
2
|
+
/** Each input represents a distinct alternative, with the same enclosing route set. */
|
|
3
|
+
export declare function validateExclusiveOutputRejoin(current: WorkflowPlanNodeContract, source: WorkflowPlanNodeContract | undefined, inputs: WorkflowRouteState[][]): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Each input represents a distinct alternative, with the same enclosing route set. */
|
|
2
|
+
export function validateExclusiveOutputRejoin(current, source, inputs) {
|
|
3
|
+
if (!source?.exclusiveOutputPorts ||
|
|
4
|
+
current.waitsForAllInputs ||
|
|
5
|
+
inputs.length !== source.outputs.length)
|
|
6
|
+
throw new Error(`node ${current.id}: rejoin requires a declared exclusive producer and every output alternative`);
|
|
7
|
+
const ports = new Set();
|
|
8
|
+
let enclosing;
|
|
9
|
+
for (const routes of inputs) {
|
|
10
|
+
const alternatives = new Set(routes.map((route) => route.branches.get(source.id)));
|
|
11
|
+
const port = alternatives.values().next().value;
|
|
12
|
+
if (alternatives.size !== 1 ||
|
|
13
|
+
port === undefined ||
|
|
14
|
+
!source.outputs.includes(port) ||
|
|
15
|
+
ports.has(port))
|
|
16
|
+
throw new Error(`node ${current.id}: rejoin inputs must cover distinct outputs of ${source.id}`);
|
|
17
|
+
ports.add(port);
|
|
18
|
+
const contexts = routes
|
|
19
|
+
.map((route) => JSON.stringify([...route.branches].filter(([id]) => id !== source.id).sort()))
|
|
20
|
+
.sort();
|
|
21
|
+
if (enclosing !== undefined &&
|
|
22
|
+
JSON.stringify(contexts) !== JSON.stringify(enclosing))
|
|
23
|
+
throw new Error(`node ${current.id}: rejoin alternatives have unmatched enclosing routes`);
|
|
24
|
+
enclosing = contexts;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { assertApiCallIdentities } from './assert-api-call-identities.js';
|
|
2
|
+
import { parseNativeNodeParameters } from './parse-native-node-parameters.js';
|
|
3
|
+
export function validateNativeApiResponseReferences({ nativeNodes, capabilities, apiCallIds, }) {
|
|
4
|
+
assertApiCallIdentities(apiCallIds);
|
|
5
|
+
const registry = new Map(capabilities.map((capability) => [capability.name, capability]));
|
|
6
|
+
if (registry.size !== capabilities.length)
|
|
7
|
+
throw new Error('planning.capabilities contains duplicate names');
|
|
8
|
+
const available = new Set(apiCallIds);
|
|
9
|
+
for (const native of nativeNodes) {
|
|
10
|
+
const capability = registry.get(native.capability);
|
|
11
|
+
if (!capability)
|
|
12
|
+
throw new Error(`node ${native.id}: unknown capability ${native.capability}`);
|
|
13
|
+
const parameters = parseNativeNodeParameters(native, capability);
|
|
14
|
+
const references = [
|
|
15
|
+
...capability.responseReferences(parameters),
|
|
16
|
+
...Object.values(capability.inputReferences
|
|
17
|
+
? capability.inputReferences(parameters)
|
|
18
|
+
: {})
|
|
19
|
+
.flat()
|
|
20
|
+
.filter((reference) => reference.source === 'response'),
|
|
21
|
+
];
|
|
22
|
+
for (const [index, reference] of references.entries())
|
|
23
|
+
if (!available.has(reference.nodeId))
|
|
24
|
+
throw new Error(`node ${native.id}: responseReferences[${index}].nodeId ${JSON.stringify(reference.nodeId)} is not a supplied API callId; available ${JSON.stringify(apiCallIds)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { validateApiArguments } from '../arguments/validate-api-arguments.js';
|
|
2
|
+
import { validateApiBindingAssignments } from '../bindings/validate-api-binding-assignments.js';
|
|
3
|
+
import { validateApiBindingGaps } from '../bindings/validate-api-binding-gaps.js';
|
|
4
|
+
import { createReviewableNodeContracts } from './create-reviewable-node-contracts.js';
|
|
5
|
+
import { validateWorkflowTopology } from './validate-workflow-topology.js';
|
|
6
|
+
import { createNativeOutputContracts } from './create-native-output-contracts.js';
|
|
7
|
+
export function validateReviewableWorkflowGraphPlan(input) {
|
|
8
|
+
const { plan, materials } = input;
|
|
9
|
+
const gaps = new Map(plan.gaps.map((gap) => [gap.id, gap]));
|
|
10
|
+
if (gaps.size !== plan.gaps.length ||
|
|
11
|
+
plan.gaps.some((gap) => !gap.id.trim() ||
|
|
12
|
+
!gap.description.trim() ||
|
|
13
|
+
!['api-selection', 'api-bindings', 'workflow-graph'].includes(gap.stage)))
|
|
14
|
+
throw new Error('review gaps require unique IDs, stages and descriptions');
|
|
15
|
+
const ids = materials.map((item) => item.status === 'ready' ? item.arguments.callId : item.callId);
|
|
16
|
+
if (new Set(ids).size !== ids.length || ids.some((id) => !id.trim()))
|
|
17
|
+
throw new Error('review materials require unique non-empty callIds');
|
|
18
|
+
const blocked = new Map(plan.blockedCalls.map((item) => [item.callId, item.gapIds]));
|
|
19
|
+
if (blocked.size !== plan.blockedCalls.length)
|
|
20
|
+
throw new Error('review blockedCalls contains duplicate callIds');
|
|
21
|
+
for (const [callId, gapIds] of blocked) {
|
|
22
|
+
if (!ids.includes(callId) ||
|
|
23
|
+
!gapIds.length ||
|
|
24
|
+
new Set(gapIds).size !== gapIds.length ||
|
|
25
|
+
gapIds.some((id) => !gaps.has(id)))
|
|
26
|
+
throw new Error(`review blocked call ${callId} requires declared gap IDs`);
|
|
27
|
+
}
|
|
28
|
+
for (const item of materials) {
|
|
29
|
+
if (item.status === 'blocked') {
|
|
30
|
+
if (!item.gapIds.length ||
|
|
31
|
+
item.gapIds.some((id) => !blocked.get(item.callId)?.includes(id)))
|
|
32
|
+
throw new Error(`review plan omitted blocked requirement for ${item.callId}`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const args = item.arguments;
|
|
36
|
+
const result = validateApiArguments({
|
|
37
|
+
operation: item.operation,
|
|
38
|
+
values: args.values,
|
|
39
|
+
bindings: args.bindings,
|
|
40
|
+
requestMediaType: args.requestMediaType,
|
|
41
|
+
});
|
|
42
|
+
if (args.unresolvedInputs.length || !result.valid)
|
|
43
|
+
throw new Error(`review ready material ${args.callId} has unresolved request inputs`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (materials.length)
|
|
47
|
+
validateApiBindingAssignments({
|
|
48
|
+
nativeOutputs: createNativeOutputContracts({
|
|
49
|
+
nativeNodes: plan.nativeNodes,
|
|
50
|
+
capabilities: input.capabilities,
|
|
51
|
+
}),
|
|
52
|
+
materials: materials.map((item) => ({
|
|
53
|
+
callId: item.status === 'ready' ? item.arguments.callId : item.callId,
|
|
54
|
+
operation: item.operation,
|
|
55
|
+
requestMediaType: item.status === 'ready'
|
|
56
|
+
? item.arguments.requestMediaType
|
|
57
|
+
: item.requestMediaType,
|
|
58
|
+
})),
|
|
59
|
+
calls: materials.map((item) => ({
|
|
60
|
+
callId: item.status === 'ready' ? item.arguments.callId : item.callId,
|
|
61
|
+
bindings: item.status === 'ready' ? item.arguments.bindings : item.bindings,
|
|
62
|
+
})),
|
|
63
|
+
});
|
|
64
|
+
for (const gap of plan.gaps) {
|
|
65
|
+
if (gap.stage !== 'api-bindings')
|
|
66
|
+
continue;
|
|
67
|
+
if (typeof gap.callId !== 'string' ||
|
|
68
|
+
typeof gap.targetPointer !== 'string' ||
|
|
69
|
+
!blocked.get(gap.callId)?.includes(gap.id))
|
|
70
|
+
throw new Error('api-bindings review gap requires its declared blocked callId and request targetPointer');
|
|
71
|
+
validateApiBindingGaps({
|
|
72
|
+
materials: materials.map((item) => ({
|
|
73
|
+
callId: item.status === 'ready' ? item.arguments.callId : item.callId,
|
|
74
|
+
operation: item.operation,
|
|
75
|
+
requestMediaType: item.status === 'ready'
|
|
76
|
+
? item.arguments.requestMediaType
|
|
77
|
+
: item.requestMediaType,
|
|
78
|
+
})),
|
|
79
|
+
plan: {
|
|
80
|
+
calls: [],
|
|
81
|
+
gaps: [
|
|
82
|
+
{
|
|
83
|
+
callId: gap.callId,
|
|
84
|
+
targetPointer: gap.targetPointer,
|
|
85
|
+
description: gap.description,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const apiIds = new Set(ids.filter((id) => !blocked.has(id)));
|
|
92
|
+
validateWorkflowTopology({
|
|
93
|
+
contracts: createReviewableNodeContracts(input),
|
|
94
|
+
apiIds,
|
|
95
|
+
edges: plan.edges,
|
|
96
|
+
starts: plan.starts,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function validateWorkflowEdges({ contracts, edges, }) {
|
|
2
|
+
const nodes = new Map(contracts.map((item) => [item.id, item]));
|
|
3
|
+
if (nodes.size !== contracts.length ||
|
|
4
|
+
contracts.some((item) => !item.id.trim()))
|
|
5
|
+
throw new Error('plan node IDs must be unique and non-empty');
|
|
6
|
+
const edgeKeys = new Set();
|
|
7
|
+
for (const edge of edges) {
|
|
8
|
+
const from = nodes.get(edge.from);
|
|
9
|
+
const to = nodes.get(edge.to);
|
|
10
|
+
if (!from || !to)
|
|
11
|
+
throw new Error('plan edge references a missing node');
|
|
12
|
+
if (!from.outputs.includes(edge.output) || !to.inputs.includes(edge.input))
|
|
13
|
+
throw new Error(`plan edge ${edge.from} -> ${edge.to} has an unknown port`);
|
|
14
|
+
const key = JSON.stringify([edge.from, edge.output, edge.to, edge.input]);
|
|
15
|
+
if (edgeKeys.has(key))
|
|
16
|
+
throw new Error('plan has duplicate edges');
|
|
17
|
+
edgeKeys.add(key);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ValidateWorkflowGraphPlanInput } from '../types/workflow-plan.js';
|
|
2
|
+
/** Reject invalid graph proposals; never repair or infer edges. */
|
|
3
|
+
export declare function validateWorkflowGraphPlan({ plan, materials, capabilities, }: ValidateWorkflowGraphPlanInput): void;
|