@boboddy/sdk 0.4.3 → 0.5.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/client.js +0 -23
- package/dist/definitions/advancement-policies/define-advancement-policy.d.ts +30 -5
- package/dist/definitions/advancement-policies/index.js +11 -1
- package/dist/definitions/pipelines/bindings.d.ts +62 -0
- package/dist/definitions/pipelines/builder-helpers.d.ts +19 -106
- package/dist/definitions/pipelines/chain-graph.d.ts +15 -14
- package/dist/definitions/pipelines/compile-node-definitions.d.ts +27 -0
- package/dist/definitions/pipelines/define-pipeline.d.ts +101 -154
- package/dist/definitions/pipelines/index.d.ts +0 -2
- package/dist/definitions/pipelines/index.js +470 -615
- package/dist/definitions/pipelines/node-input-ctx.d.ts +31 -0
- package/dist/definitions/pipelines/pipeline-definitions-client.d.ts +18 -4
- package/dist/definitions/pipelines/pipeline-states.d.ts +104 -0
- package/dist/definitions/steps/define-code-step.d.ts +50 -0
- package/dist/definitions/steps/define-step.d.ts +22 -1
- package/dist/definitions/steps/index.d.ts +1 -0
- package/dist/definitions/steps/index.js +41 -24
- package/dist/definitions/steps/step-definitions-client.d.ts +29 -5
- package/dist/definitions/validation/index.js +15148 -94
- package/dist/definitions/validation/validate-definition-specs.d.ts +19 -1
- package/dist/generated/index.d.ts +1 -1
- package/dist/generated/sdk.gen.d.ts +1 -4
- package/dist/generated/types.gen.d.ts +296 -878
- package/dist/index.js +510 -615
- package/dist/push/collect-definitions.d.ts +40 -1
- package/dist/push/index.d.ts +2 -2
- package/dist/push/index.js +788 -738
- package/dist/step-execution-plane-client.d.ts +9 -4
- package/package.json +2 -2
- package/dist/definitions/pipelines/builder.d.ts +0 -80
- package/dist/definitions/pipelines/fan-out-builder.d.ts +0 -66
- package/dist/definitions/pipelines/input-accessor.d.ts +0 -25
package/dist/push/index.js
CHANGED
|
@@ -30,7 +30,7 @@ __export(exports_core2, {
|
|
|
30
30
|
safeDecode: () => safeDecode,
|
|
31
31
|
registry: () => registry,
|
|
32
32
|
regexes: () => exports_regexes,
|
|
33
|
-
process: () =>
|
|
33
|
+
process: () => process2,
|
|
34
34
|
prettifyError: () => prettifyError,
|
|
35
35
|
parseAsync: () => parseAsync,
|
|
36
36
|
parse: () => parse,
|
|
@@ -11017,7 +11017,7 @@ function initializeContext(params) {
|
|
|
11017
11017
|
external: params?.external ?? undefined
|
|
11018
11018
|
};
|
|
11019
11019
|
}
|
|
11020
|
-
function
|
|
11020
|
+
function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
11021
11021
|
var _a3;
|
|
11022
11022
|
const def = schema._zod.def;
|
|
11023
11023
|
const seen = ctx.seen.get(schema);
|
|
@@ -11054,7 +11054,7 @@ function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
|
11054
11054
|
if (parent) {
|
|
11055
11055
|
if (!result.ref)
|
|
11056
11056
|
result.ref = parent;
|
|
11057
|
-
|
|
11057
|
+
process2(parent, ctx, params);
|
|
11058
11058
|
ctx.seen.get(parent).isParent = true;
|
|
11059
11059
|
}
|
|
11060
11060
|
}
|
|
@@ -11337,14 +11337,14 @@ function isTransforming(_schema, _ctx) {
|
|
|
11337
11337
|
}
|
|
11338
11338
|
var createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
|
|
11339
11339
|
const ctx = initializeContext({ ...params, processors });
|
|
11340
|
-
|
|
11340
|
+
process2(schema, ctx);
|
|
11341
11341
|
extractDefs(ctx, schema);
|
|
11342
11342
|
return finalize(ctx, schema);
|
|
11343
11343
|
};
|
|
11344
11344
|
var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
|
|
11345
11345
|
const { libraryOptions, target } = params ?? {};
|
|
11346
11346
|
const ctx = initializeContext({ ...libraryOptions ?? {}, target, io, processors });
|
|
11347
|
-
|
|
11347
|
+
process2(schema, ctx);
|
|
11348
11348
|
extractDefs(ctx, schema);
|
|
11349
11349
|
return finalize(ctx, schema);
|
|
11350
11350
|
};
|
|
@@ -11584,7 +11584,7 @@ var arrayProcessor = (schema, ctx, _json, params) => {
|
|
|
11584
11584
|
if (typeof maximum === "number")
|
|
11585
11585
|
json.maxItems = maximum;
|
|
11586
11586
|
json.type = "array";
|
|
11587
|
-
json.items =
|
|
11587
|
+
json.items = process2(def.element, ctx, {
|
|
11588
11588
|
...params,
|
|
11589
11589
|
path: [...params.path, "items"]
|
|
11590
11590
|
});
|
|
@@ -11596,7 +11596,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
11596
11596
|
json.properties = {};
|
|
11597
11597
|
const shape = def.shape;
|
|
11598
11598
|
for (const key in shape) {
|
|
11599
|
-
json.properties[key] =
|
|
11599
|
+
json.properties[key] = process2(shape[key], ctx, {
|
|
11600
11600
|
...params,
|
|
11601
11601
|
path: [...params.path, "properties", key]
|
|
11602
11602
|
});
|
|
@@ -11619,7 +11619,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
11619
11619
|
if (ctx.io === "output")
|
|
11620
11620
|
json.additionalProperties = false;
|
|
11621
11621
|
} else if (def.catchall) {
|
|
11622
|
-
json.additionalProperties =
|
|
11622
|
+
json.additionalProperties = process2(def.catchall, ctx, {
|
|
11623
11623
|
...params,
|
|
11624
11624
|
path: [...params.path, "additionalProperties"]
|
|
11625
11625
|
});
|
|
@@ -11628,7 +11628,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
11628
11628
|
var unionProcessor = (schema, ctx, json, params) => {
|
|
11629
11629
|
const def = schema._zod.def;
|
|
11630
11630
|
const isExclusive = def.inclusive === false;
|
|
11631
|
-
const options = def.options.map((x, i) =>
|
|
11631
|
+
const options = def.options.map((x, i) => process2(x, ctx, {
|
|
11632
11632
|
...params,
|
|
11633
11633
|
path: [...params.path, isExclusive ? "oneOf" : "anyOf", i]
|
|
11634
11634
|
}));
|
|
@@ -11640,11 +11640,11 @@ var unionProcessor = (schema, ctx, json, params) => {
|
|
|
11640
11640
|
};
|
|
11641
11641
|
var intersectionProcessor = (schema, ctx, json, params) => {
|
|
11642
11642
|
const def = schema._zod.def;
|
|
11643
|
-
const a =
|
|
11643
|
+
const a = process2(def.left, ctx, {
|
|
11644
11644
|
...params,
|
|
11645
11645
|
path: [...params.path, "allOf", 0]
|
|
11646
11646
|
});
|
|
11647
|
-
const b =
|
|
11647
|
+
const b = process2(def.right, ctx, {
|
|
11648
11648
|
...params,
|
|
11649
11649
|
path: [...params.path, "allOf", 1]
|
|
11650
11650
|
});
|
|
@@ -11661,11 +11661,11 @@ var tupleProcessor = (schema, ctx, _json, params) => {
|
|
|
11661
11661
|
json.type = "array";
|
|
11662
11662
|
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
11663
11663
|
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
11664
|
-
const prefixItems = def.items.map((x, i) =>
|
|
11664
|
+
const prefixItems = def.items.map((x, i) => process2(x, ctx, {
|
|
11665
11665
|
...params,
|
|
11666
11666
|
path: [...params.path, prefixPath, i]
|
|
11667
11667
|
}));
|
|
11668
|
-
const rest = def.rest ?
|
|
11668
|
+
const rest = def.rest ? process2(def.rest, ctx, {
|
|
11669
11669
|
...params,
|
|
11670
11670
|
path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
|
|
11671
11671
|
}) : null;
|
|
@@ -11705,7 +11705,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
11705
11705
|
const keyBag = keyType._zod.bag;
|
|
11706
11706
|
const patterns = keyBag?.patterns;
|
|
11707
11707
|
if (def.mode === "loose" && patterns && patterns.size > 0) {
|
|
11708
|
-
const valueSchema =
|
|
11708
|
+
const valueSchema = process2(def.valueType, ctx, {
|
|
11709
11709
|
...params,
|
|
11710
11710
|
path: [...params.path, "patternProperties", "*"]
|
|
11711
11711
|
});
|
|
@@ -11715,12 +11715,12 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
11715
11715
|
}
|
|
11716
11716
|
} else {
|
|
11717
11717
|
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
|
|
11718
|
-
json.propertyNames =
|
|
11718
|
+
json.propertyNames = process2(def.keyType, ctx, {
|
|
11719
11719
|
...params,
|
|
11720
11720
|
path: [...params.path, "propertyNames"]
|
|
11721
11721
|
});
|
|
11722
11722
|
}
|
|
11723
|
-
json.additionalProperties =
|
|
11723
|
+
json.additionalProperties = process2(def.valueType, ctx, {
|
|
11724
11724
|
...params,
|
|
11725
11725
|
path: [...params.path, "additionalProperties"]
|
|
11726
11726
|
});
|
|
@@ -11735,7 +11735,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
11735
11735
|
};
|
|
11736
11736
|
var nullableProcessor = (schema, ctx, json, params) => {
|
|
11737
11737
|
const def = schema._zod.def;
|
|
11738
|
-
const inner =
|
|
11738
|
+
const inner = process2(def.innerType, ctx, params);
|
|
11739
11739
|
const seen = ctx.seen.get(schema);
|
|
11740
11740
|
if (ctx.target === "openapi-3.0") {
|
|
11741
11741
|
seen.ref = def.innerType;
|
|
@@ -11746,20 +11746,20 @@ var nullableProcessor = (schema, ctx, json, params) => {
|
|
|
11746
11746
|
};
|
|
11747
11747
|
var nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
11748
11748
|
const def = schema._zod.def;
|
|
11749
|
-
|
|
11749
|
+
process2(def.innerType, ctx, params);
|
|
11750
11750
|
const seen = ctx.seen.get(schema);
|
|
11751
11751
|
seen.ref = def.innerType;
|
|
11752
11752
|
};
|
|
11753
11753
|
var defaultProcessor = (schema, ctx, json, params) => {
|
|
11754
11754
|
const def = schema._zod.def;
|
|
11755
|
-
|
|
11755
|
+
process2(def.innerType, ctx, params);
|
|
11756
11756
|
const seen = ctx.seen.get(schema);
|
|
11757
11757
|
seen.ref = def.innerType;
|
|
11758
11758
|
json.default = JSON.parse(JSON.stringify(def.defaultValue));
|
|
11759
11759
|
};
|
|
11760
11760
|
var prefaultProcessor = (schema, ctx, json, params) => {
|
|
11761
11761
|
const def = schema._zod.def;
|
|
11762
|
-
|
|
11762
|
+
process2(def.innerType, ctx, params);
|
|
11763
11763
|
const seen = ctx.seen.get(schema);
|
|
11764
11764
|
seen.ref = def.innerType;
|
|
11765
11765
|
if (ctx.io === "input")
|
|
@@ -11767,7 +11767,7 @@ var prefaultProcessor = (schema, ctx, json, params) => {
|
|
|
11767
11767
|
};
|
|
11768
11768
|
var catchProcessor = (schema, ctx, json, params) => {
|
|
11769
11769
|
const def = schema._zod.def;
|
|
11770
|
-
|
|
11770
|
+
process2(def.innerType, ctx, params);
|
|
11771
11771
|
const seen = ctx.seen.get(schema);
|
|
11772
11772
|
seen.ref = def.innerType;
|
|
11773
11773
|
let catchValue;
|
|
@@ -11782,32 +11782,32 @@ var pipeProcessor = (schema, ctx, _json, params) => {
|
|
|
11782
11782
|
const def = schema._zod.def;
|
|
11783
11783
|
const inIsTransform = def.in._zod.traits.has("$ZodTransform");
|
|
11784
11784
|
const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
|
|
11785
|
-
|
|
11785
|
+
process2(innerType, ctx, params);
|
|
11786
11786
|
const seen = ctx.seen.get(schema);
|
|
11787
11787
|
seen.ref = innerType;
|
|
11788
11788
|
};
|
|
11789
11789
|
var readonlyProcessor = (schema, ctx, json, params) => {
|
|
11790
11790
|
const def = schema._zod.def;
|
|
11791
|
-
|
|
11791
|
+
process2(def.innerType, ctx, params);
|
|
11792
11792
|
const seen = ctx.seen.get(schema);
|
|
11793
11793
|
seen.ref = def.innerType;
|
|
11794
11794
|
json.readOnly = true;
|
|
11795
11795
|
};
|
|
11796
11796
|
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
11797
11797
|
const def = schema._zod.def;
|
|
11798
|
-
|
|
11798
|
+
process2(def.innerType, ctx, params);
|
|
11799
11799
|
const seen = ctx.seen.get(schema);
|
|
11800
11800
|
seen.ref = def.innerType;
|
|
11801
11801
|
};
|
|
11802
11802
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
11803
11803
|
const def = schema._zod.def;
|
|
11804
|
-
|
|
11804
|
+
process2(def.innerType, ctx, params);
|
|
11805
11805
|
const seen = ctx.seen.get(schema);
|
|
11806
11806
|
seen.ref = def.innerType;
|
|
11807
11807
|
};
|
|
11808
11808
|
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
11809
11809
|
const innerType = schema._zod.innerType;
|
|
11810
|
-
|
|
11810
|
+
process2(innerType, ctx, params);
|
|
11811
11811
|
const seen = ctx.seen.get(schema);
|
|
11812
11812
|
seen.ref = innerType;
|
|
11813
11813
|
};
|
|
@@ -11859,7 +11859,7 @@ function toJSONSchema(input, params) {
|
|
|
11859
11859
|
const defs = {};
|
|
11860
11860
|
for (const entry of registry2._idmap.entries()) {
|
|
11861
11861
|
const [_, schema] = entry;
|
|
11862
|
-
|
|
11862
|
+
process2(schema, ctx2);
|
|
11863
11863
|
}
|
|
11864
11864
|
const schemas = {};
|
|
11865
11865
|
const external = {
|
|
@@ -11882,7 +11882,7 @@ function toJSONSchema(input, params) {
|
|
|
11882
11882
|
return { schemas };
|
|
11883
11883
|
}
|
|
11884
11884
|
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
11885
|
-
|
|
11885
|
+
process2(input, ctx);
|
|
11886
11886
|
extractDefs(ctx, input);
|
|
11887
11887
|
return finalize(ctx, input);
|
|
11888
11888
|
}
|
|
@@ -11928,7 +11928,7 @@ class JSONSchemaGenerator {
|
|
|
11928
11928
|
});
|
|
11929
11929
|
}
|
|
11930
11930
|
process(schema, _params = { path: [], schemaPath: [] }) {
|
|
11931
|
-
return
|
|
11931
|
+
return process2(schema, this.ctx, _params);
|
|
11932
11932
|
}
|
|
11933
11933
|
emit(schema, _params) {
|
|
11934
11934
|
if (_params) {
|
|
@@ -12080,6 +12080,32 @@ ${feature._promptAddition}` : feature._promptAddition;
|
|
|
12080
12080
|
};
|
|
12081
12081
|
return spec;
|
|
12082
12082
|
}
|
|
12083
|
+
// src/definitions/steps/define-code-step.ts
|
|
12084
|
+
function codeStep(config2) {
|
|
12085
|
+
const spec = {
|
|
12086
|
+
key: config2.key,
|
|
12087
|
+
name: config2.name,
|
|
12088
|
+
description: config2.description ?? null,
|
|
12089
|
+
version: config2.version ?? 1,
|
|
12090
|
+
kind: "code",
|
|
12091
|
+
status: config2.status ?? "active",
|
|
12092
|
+
prompt: null,
|
|
12093
|
+
inputSchemaJson: config2.inputSchema ? toJSONSchema(config2.inputSchema) : null,
|
|
12094
|
+
resultSchemaJson: config2.resultSchema ? toJSONSchema(config2.resultSchema) : null,
|
|
12095
|
+
signalExtractorDefinitions: (config2.signals ?? []).map((signal) => ({
|
|
12096
|
+
key: signal.key ?? signal.sourcePath,
|
|
12097
|
+
sourcePath: signal.sourcePath,
|
|
12098
|
+
type: signal.type,
|
|
12099
|
+
required: signal.required ?? true,
|
|
12100
|
+
availableWhenResultStatusIn: signal.availableWhenResultStatusIn ?? null
|
|
12101
|
+
})),
|
|
12102
|
+
opencodeMcpJson: null,
|
|
12103
|
+
opencodePluginJson: null,
|
|
12104
|
+
healthChecksJson: null,
|
|
12105
|
+
entrypoint: { fn: config2.fn }
|
|
12106
|
+
};
|
|
12107
|
+
return spec;
|
|
12108
|
+
}
|
|
12083
12109
|
// src/generated/core/bodySerializer.gen.ts
|
|
12084
12110
|
var jsonBodySerializer = {
|
|
12085
12111
|
bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value)
|
|
@@ -13159,29 +13185,6 @@ class PipelineDefinitions extends HeyApiClient {
|
|
|
13159
13185
|
unarchivePipelineDefinition(options) {
|
|
13160
13186
|
return (options.client ?? this.client).put({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/unarchive", ...options });
|
|
13161
13187
|
}
|
|
13162
|
-
addPipelineStep(options) {
|
|
13163
|
-
return (options.client ?? this.client).post({
|
|
13164
|
-
url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps",
|
|
13165
|
-
...options,
|
|
13166
|
-
headers: {
|
|
13167
|
-
"Content-Type": "application/json",
|
|
13168
|
-
...options.headers
|
|
13169
|
-
}
|
|
13170
|
-
});
|
|
13171
|
-
}
|
|
13172
|
-
removePipelineStep(options) {
|
|
13173
|
-
return (options.client ?? this.client).delete({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}", ...options });
|
|
13174
|
-
}
|
|
13175
|
-
updatePipelineStep(options) {
|
|
13176
|
-
return (options.client ?? this.client).put({
|
|
13177
|
-
url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}",
|
|
13178
|
-
...options,
|
|
13179
|
-
headers: {
|
|
13180
|
-
"Content-Type": "application/json",
|
|
13181
|
-
...options.headers
|
|
13182
|
-
}
|
|
13183
|
-
});
|
|
13184
|
-
}
|
|
13185
13188
|
setPipelineStepAdvancementPolicy(options) {
|
|
13186
13189
|
return (options.client ?? this.client).put({
|
|
13187
13190
|
url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}/advancement-policy",
|
|
@@ -13710,8 +13713,21 @@ var buildStepDefinitionsClient = (stepDefinitions) => {
|
|
|
13710
13713
|
},
|
|
13711
13714
|
upsertFromSpec: async (projectId, spec, options) => {
|
|
13712
13715
|
const body = {
|
|
13713
|
-
|
|
13716
|
+
key: spec.key,
|
|
13717
|
+
name: spec.name,
|
|
13718
|
+
description: spec.description,
|
|
13714
13719
|
prompt: spec.prompt ?? "",
|
|
13720
|
+
version: spec.version,
|
|
13721
|
+
kind: spec.kind,
|
|
13722
|
+
entrypointJson: spec.entrypointJson ?? null,
|
|
13723
|
+
executionMode: spec.executionMode,
|
|
13724
|
+
inputSchemaJson: spec.inputSchemaJson,
|
|
13725
|
+
resultSchemaJson: spec.resultSchemaJson,
|
|
13726
|
+
opencodeMcpJson: spec.opencodeMcpJson,
|
|
13727
|
+
opencodePluginJson: spec.opencodePluginJson,
|
|
13728
|
+
healthChecksJson: spec.healthChecksJson,
|
|
13729
|
+
status: spec.status,
|
|
13730
|
+
signalExtractorDefinitions: spec.signalExtractorDefinitions,
|
|
13715
13731
|
projectId
|
|
13716
13732
|
};
|
|
13717
13733
|
const result = await stepDefinitions.upsertStepDefinition({
|
|
@@ -16168,10 +16184,19 @@ function any2(conditions, outcome) {
|
|
|
16168
16184
|
return { _tag: "rule", mode: "any", conditions, outcome };
|
|
16169
16185
|
}
|
|
16170
16186
|
function when(signal2, operator, value, outcome) {
|
|
16187
|
+
const condition = {
|
|
16188
|
+
_tag: "signal",
|
|
16189
|
+
signal: signal2,
|
|
16190
|
+
operator,
|
|
16191
|
+
value
|
|
16192
|
+
};
|
|
16193
|
+
if (outcome === undefined) {
|
|
16194
|
+
return condition;
|
|
16195
|
+
}
|
|
16171
16196
|
return {
|
|
16172
16197
|
_tag: "rule",
|
|
16173
16198
|
mode: "all",
|
|
16174
|
-
conditions: [
|
|
16199
|
+
conditions: [condition],
|
|
16175
16200
|
outcome
|
|
16176
16201
|
};
|
|
16177
16202
|
}
|
|
@@ -16347,188 +16372,10 @@ function extractInlineStepSignalsListDefinitions(policy) {
|
|
|
16347
16372
|
return [...byKey.values()];
|
|
16348
16373
|
}
|
|
16349
16374
|
|
|
16350
|
-
// src/definitions/
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
nodesByKey.set(nodeDefinition.nodeKey, nodeDefinition);
|
|
16355
|
-
}
|
|
16356
|
-
const outgoing = new Map;
|
|
16357
|
-
const incomingCount = new Map;
|
|
16358
|
-
for (const nodeDefinition of nodeDefinitions) {
|
|
16359
|
-
incomingCount.set(nodeDefinition.nodeKey, 0);
|
|
16360
|
-
}
|
|
16361
|
-
for (const edge of dependencyEdges) {
|
|
16362
|
-
if (!nodesByKey.has(edge.fromNodeKey) || !nodesByKey.has(edge.toNodeKey)) {
|
|
16363
|
-
return null;
|
|
16364
|
-
}
|
|
16365
|
-
if (outgoing.has(edge.fromNodeKey))
|
|
16366
|
-
return null;
|
|
16367
|
-
outgoing.set(edge.fromNodeKey, edge.toNodeKey);
|
|
16368
|
-
incomingCount.set(edge.toNodeKey, (incomingCount.get(edge.toNodeKey) ?? 0) + 1);
|
|
16369
|
-
}
|
|
16370
|
-
for (const count of incomingCount.values()) {
|
|
16371
|
-
if (count > 1)
|
|
16372
|
-
return null;
|
|
16373
|
-
}
|
|
16374
|
-
if (nodeDefinitions.length === 0)
|
|
16375
|
-
return [];
|
|
16376
|
-
const roots = nodeDefinitions.filter((nodeDefinition) => (incomingCount.get(nodeDefinition.nodeKey) ?? 0) === 0);
|
|
16377
|
-
if (roots.length !== 1)
|
|
16378
|
-
return null;
|
|
16379
|
-
const rootNode = roots[0];
|
|
16380
|
-
if (!rootNode)
|
|
16381
|
-
return null;
|
|
16382
|
-
const ordered = [];
|
|
16383
|
-
const visited = new Set;
|
|
16384
|
-
let currentKey = rootNode.nodeKey;
|
|
16385
|
-
while (currentKey !== undefined) {
|
|
16386
|
-
if (visited.has(currentKey))
|
|
16387
|
-
return null;
|
|
16388
|
-
visited.add(currentKey);
|
|
16389
|
-
const currentNode = nodesByKey.get(currentKey);
|
|
16390
|
-
if (!currentNode)
|
|
16391
|
-
return null;
|
|
16392
|
-
ordered.push(currentNode);
|
|
16393
|
-
currentKey = outgoing.get(currentKey);
|
|
16394
|
-
}
|
|
16395
|
-
if (ordered.length !== nodeDefinitions.length)
|
|
16396
|
-
return null;
|
|
16397
|
-
return ordered;
|
|
16398
|
-
}
|
|
16399
|
-
function buildChainDependencyEdges(orderedNodes) {
|
|
16400
|
-
const dependencyEdges = [];
|
|
16401
|
-
for (let index = 0;index < orderedNodes.length - 1; index += 1) {
|
|
16402
|
-
const from = orderedNodes[index];
|
|
16403
|
-
const to = orderedNodes[index + 1];
|
|
16404
|
-
if (!from || !to)
|
|
16405
|
-
continue;
|
|
16406
|
-
dependencyEdges.push({ fromNodeKey: from.nodeKey, toNodeKey: to.nodeKey });
|
|
16407
|
-
}
|
|
16408
|
-
return dependencyEdges;
|
|
16409
|
-
}
|
|
16410
|
-
|
|
16411
|
-
// src/definitions/pipelines/define-pipeline.ts
|
|
16412
|
-
function serializeBinding(binding) {
|
|
16413
|
-
if (binding.source === "pipeline_input") {
|
|
16414
|
-
return { source: "pipeline_input", path: binding.path };
|
|
16415
|
-
}
|
|
16416
|
-
if (binding.source === "work_item") {
|
|
16417
|
-
return { source: "work_item", field: binding.field };
|
|
16418
|
-
}
|
|
16419
|
-
if (binding.source === "step_signal") {
|
|
16420
|
-
return {
|
|
16421
|
-
source: "step_signal",
|
|
16422
|
-
stepKey: binding.step.key,
|
|
16423
|
-
signalKey: binding.signalKey
|
|
16424
|
-
};
|
|
16425
|
-
}
|
|
16426
|
-
if (binding.source === "literal") {
|
|
16427
|
-
return { source: "literal", value: binding.value };
|
|
16428
|
-
}
|
|
16429
|
-
if (binding.source === "signals_list") {
|
|
16430
|
-
return { source: "signals_list", stepKey: binding.fanOutStep.key };
|
|
16431
|
-
}
|
|
16432
|
-
if (binding.source === "fan_out_item") {
|
|
16433
|
-
return { source: "fan_out_item" };
|
|
16434
|
-
}
|
|
16435
|
-
return { source: "step_output", stepKey: binding.step.key };
|
|
16436
|
-
}
|
|
16437
|
-
var isFanOutConfig = (node) => ("nodeType" in node) && node.nodeType === "fanOut";
|
|
16438
|
-
var isCohortGateConfig = (node) => ("nodeType" in node) && node.nodeType === "cohortGate";
|
|
16439
|
-
function serializeInputBindings(input, pipelineInputBindings) {
|
|
16440
|
-
const autoBindings = {
|
|
16441
|
-
workItemTitle: { source: "work_item", field: "title" },
|
|
16442
|
-
workItemDescription: { source: "work_item", field: "description" }
|
|
16443
|
-
};
|
|
16444
|
-
const pipelineBindings = {};
|
|
16445
|
-
for (const [key, binding] of Object.entries(pipelineInputBindings ?? {})) {
|
|
16446
|
-
pipelineBindings[key] = serializeBinding(binding);
|
|
16447
|
-
}
|
|
16448
|
-
const explicitBindings = Object.fromEntries(Object.entries(input ?? {}).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)]));
|
|
16449
|
-
return { ...autoBindings, ...pipelineBindings, ...explicitBindings };
|
|
16450
|
-
}
|
|
16451
|
-
function buildPipelineSpec(config2) {
|
|
16452
|
-
const nodes = config2.nodes;
|
|
16453
|
-
const stepDefMap = new Map;
|
|
16454
|
-
const registerStepDef = (step) => {
|
|
16455
|
-
const mapKey = `${step.key}@v${String(step.version)}`;
|
|
16456
|
-
if (!stepDefMap.has(mapKey)) {
|
|
16457
|
-
stepDefMap.set(mapKey, step);
|
|
16458
|
-
}
|
|
16459
|
-
};
|
|
16460
|
-
for (const node of nodes) {
|
|
16461
|
-
if (isFanOutConfig(node)) {
|
|
16462
|
-
registerStepDef(node.fanOutStep);
|
|
16463
|
-
} else if (!isCohortGateConfig(node)) {
|
|
16464
|
-
registerStepDef(node.step);
|
|
16465
|
-
}
|
|
16466
|
-
}
|
|
16467
|
-
let inputSchemaJson = null;
|
|
16468
|
-
if (config2.input) {
|
|
16469
|
-
try {
|
|
16470
|
-
inputSchemaJson = exports_external.toJSONSchema(config2.input);
|
|
16471
|
-
} catch {
|
|
16472
|
-
inputSchemaJson = null;
|
|
16473
|
-
}
|
|
16474
|
-
}
|
|
16475
|
-
const nodeDefinitions = nodes.map((node) => {
|
|
16476
|
-
if (isCohortGateConfig(node)) {
|
|
16477
|
-
const serializedPolicy = serializeCohortAdvancementPolicy(node.advanceAll);
|
|
16478
|
-
const inlineStepSignalsListDefinitions = extractInlineStepSignalsListDefinitions(node.advanceAll);
|
|
16479
|
-
return {
|
|
16480
|
-
nodeKey: node.nodeKey,
|
|
16481
|
-
kind: "cohortGate",
|
|
16482
|
-
advanceAllPolicyDefinition: serializedPolicy,
|
|
16483
|
-
stepSignalsListDefinitions: [
|
|
16484
|
-
...inlineStepSignalsListDefinitions,
|
|
16485
|
-
...node.stepSignalsListDefinitions ?? []
|
|
16486
|
-
]
|
|
16487
|
-
};
|
|
16488
|
-
}
|
|
16489
|
-
if (isFanOutConfig(node)) {
|
|
16490
|
-
return {
|
|
16491
|
-
nodeKey: node.fanOutStep.key,
|
|
16492
|
-
kind: "fanOut",
|
|
16493
|
-
stepKey: node.fanOutStep.key,
|
|
16494
|
-
stepName: node.fanOutStep.name,
|
|
16495
|
-
stepDescription: node.fanOutStep.description,
|
|
16496
|
-
inputBindingsJson: serializeInputBindings(node.input, config2.pipelineInputBindings),
|
|
16497
|
-
timeoutSeconds: node.timeout ?? null,
|
|
16498
|
-
overSignalKey: node.overSignalKey,
|
|
16499
|
-
advanceEachPolicyDefinition: serializeCohortAdvancementPolicy(node.advanceEach)
|
|
16500
|
-
};
|
|
16501
|
-
}
|
|
16502
|
-
return {
|
|
16503
|
-
nodeKey: node.step.key,
|
|
16504
|
-
kind: "step",
|
|
16505
|
-
stepKey: node.step.key,
|
|
16506
|
-
stepName: node.step.name,
|
|
16507
|
-
stepDescription: node.step.description,
|
|
16508
|
-
inputBindingsJson: serializeInputBindings(node.input, config2.pipelineInputBindings),
|
|
16509
|
-
timeoutSeconds: node.timeout ?? null,
|
|
16510
|
-
advancementPolicyDefinition: serializeAdvancementPolicy(node.advancement),
|
|
16511
|
-
computedSignalDefinitions: extractInlineComputedSignals(node.advancement)
|
|
16512
|
-
};
|
|
16513
|
-
});
|
|
16514
|
-
const dependencyEdges = buildChainDependencyEdges(nodeDefinitions);
|
|
16515
|
-
return {
|
|
16516
|
-
key: config2.key,
|
|
16517
|
-
name: config2.name,
|
|
16518
|
-
description: config2.description ?? null,
|
|
16519
|
-
version: config2.version ?? 1,
|
|
16520
|
-
status: config2.status ?? "active",
|
|
16521
|
-
inputSchemaJson,
|
|
16522
|
-
_stepDefinitions: [...stepDefMap.values()],
|
|
16523
|
-
nodeDefinitions,
|
|
16524
|
-
dependencyEdges
|
|
16525
|
-
};
|
|
16526
|
-
}
|
|
16527
|
-
// src/definitions/advancement-policies/fluent-rules.ts
|
|
16528
|
-
var LEAF_BRAND = Symbol.for("boboddy.fluentRule.leaf");
|
|
16529
|
-
var GROUP_BRAND = Symbol.for("boboddy.fluentRule.group");
|
|
16530
|
-
var SIGNAL_KEY = Symbol.for("boboddy.fluentRule.signalKey");
|
|
16531
|
-
function createSignalRef(signal2) {
|
|
16375
|
+
// src/definitions/advancement-policies/cohort-fluent-rules.ts
|
|
16376
|
+
var LEAF_BRAND = Symbol("boboddy.cohortRule.leaf");
|
|
16377
|
+
var GROUP_BRAND = Symbol("boboddy.cohortRule.group");
|
|
16378
|
+
function createCohortSignalRef(signal2) {
|
|
16532
16379
|
const leaf = (operator, value) => {
|
|
16533
16380
|
const condition = {
|
|
16534
16381
|
_tag: "signal",
|
|
@@ -16538,12 +16385,13 @@ function createSignalRef(signal2) {
|
|
|
16538
16385
|
};
|
|
16539
16386
|
return {
|
|
16540
16387
|
[LEAF_BRAND]: condition,
|
|
16541
|
-
then(outcome) {
|
|
16388
|
+
then(outcome, paramsJson) {
|
|
16542
16389
|
return {
|
|
16543
16390
|
_tag: "rule",
|
|
16544
16391
|
mode: "all",
|
|
16545
16392
|
conditions: [condition],
|
|
16546
|
-
outcome
|
|
16393
|
+
outcome,
|
|
16394
|
+
...paramsJson ? { outcomeJson: paramsJson } : {}
|
|
16547
16395
|
};
|
|
16548
16396
|
}
|
|
16549
16397
|
};
|
|
@@ -16561,109 +16409,172 @@ function createSignalRef(signal2) {
|
|
|
16561
16409
|
doesNotContain: (v) => leaf("doesNotContain", v)
|
|
16562
16410
|
};
|
|
16563
16411
|
}
|
|
16564
|
-
function
|
|
16412
|
+
function createCohortLeafFromCondition(condition) {
|
|
16413
|
+
return {
|
|
16414
|
+
[LEAF_BRAND]: condition,
|
|
16415
|
+
then(outcome, paramsJson) {
|
|
16416
|
+
return {
|
|
16417
|
+
_tag: "rule",
|
|
16418
|
+
mode: "all",
|
|
16419
|
+
conditions: [condition],
|
|
16420
|
+
outcome,
|
|
16421
|
+
...paramsJson ? { outcomeJson: paramsJson } : {}
|
|
16422
|
+
};
|
|
16423
|
+
}
|
|
16424
|
+
};
|
|
16425
|
+
}
|
|
16426
|
+
function extractCohortCondition(ref) {
|
|
16565
16427
|
if (LEAF_BRAND in ref)
|
|
16566
16428
|
return ref[LEAF_BRAND];
|
|
16567
16429
|
const group = ref[GROUP_BRAND];
|
|
16568
16430
|
return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
|
|
16569
16431
|
}
|
|
16570
|
-
function
|
|
16571
|
-
const conditions = refs.map(
|
|
16432
|
+
function createCohortGroup(mode, refs) {
|
|
16433
|
+
const conditions = refs.map(extractCohortCondition);
|
|
16572
16434
|
return {
|
|
16573
16435
|
[GROUP_BRAND]: { mode, conditions },
|
|
16574
|
-
then(outcome) {
|
|
16575
|
-
return {
|
|
16436
|
+
then(outcome, paramsJson) {
|
|
16437
|
+
return {
|
|
16438
|
+
_tag: "rule",
|
|
16439
|
+
mode,
|
|
16440
|
+
conditions,
|
|
16441
|
+
outcome,
|
|
16442
|
+
...paramsJson ? { outcomeJson: paramsJson } : {}
|
|
16443
|
+
};
|
|
16576
16444
|
}
|
|
16577
16445
|
};
|
|
16578
16446
|
}
|
|
16579
|
-
function
|
|
16580
|
-
return
|
|
16581
|
-
}
|
|
16582
|
-
function makeKeyedSignalRef(key) {
|
|
16583
|
-
const ref = createSignalRef(key);
|
|
16584
|
-
return Object.assign(ref, {
|
|
16585
|
-
[SIGNAL_KEY]: key
|
|
16586
|
-
});
|
|
16447
|
+
function makeKeyedCohortSignalRef(key) {
|
|
16448
|
+
return createCohortSignalRef(key);
|
|
16587
16449
|
}
|
|
16588
|
-
function
|
|
16589
|
-
const wrapComputed = (token) => createSignalRef(token);
|
|
16450
|
+
function makeAdvanceEachCtx() {
|
|
16590
16451
|
return {
|
|
16591
|
-
signal: (key) =>
|
|
16452
|
+
signal: (key) => makeKeyedCohortSignalRef(key),
|
|
16592
16453
|
stepSignals: new Proxy({}, {
|
|
16593
16454
|
get(_, key) {
|
|
16594
16455
|
if (typeof key === "string")
|
|
16595
|
-
return
|
|
16456
|
+
return makeKeyedCohortSignalRef(key);
|
|
16596
16457
|
return;
|
|
16597
16458
|
}
|
|
16598
16459
|
}),
|
|
16599
|
-
|
|
16600
|
-
|
|
16601
|
-
sum: (...args) => wrapComputed(Computed.sum(args.map(resolveComputedArg))),
|
|
16602
|
-
min: (...args) => wrapComputed(Computed.min(args.map(resolveComputedArg))),
|
|
16603
|
-
max: (...args) => wrapComputed(Computed.max(args.map(resolveComputedArg))),
|
|
16604
|
-
count: (...args) => wrapComputed(Computed.count(args.map(resolveComputedArg))),
|
|
16605
|
-
booleanAny: (...args) => wrapComputed(Computed.booleanAny(args.map(resolveComputedArg))),
|
|
16606
|
-
booleanAll: (...args) => wrapComputed(Computed.booleanAll(args.map(resolveComputedArg))),
|
|
16607
|
-
all: (...refs) => createGroup("all", refs),
|
|
16608
|
-
any: (...refs) => createGroup("any", refs),
|
|
16609
|
-
route: (pipelineKey, inputJson) => inputJson !== undefined ? { outcome: "route", pipelineKey, inputJson } : { outcome: "route", pipelineKey }
|
|
16460
|
+
all: (...refs) => createCohortGroup("all", refs),
|
|
16461
|
+
any: (...refs) => createCohortGroup("any", refs)
|
|
16610
16462
|
};
|
|
16611
16463
|
}
|
|
16612
|
-
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
|
|
16624
|
-
|
|
16625
|
-
|
|
16626
|
-
|
|
16627
|
-
|
|
16628
|
-
|
|
16629
|
-
|
|
16630
|
-
|
|
16631
|
-
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
if (prop === Symbol.toPrimitive) {
|
|
16635
|
-
return () => {
|
|
16636
|
-
throw new Error(`Pipeline input accessor at path "${pathStr || "<root>"}" cannot be coerced to a primitive. ` + `Pass it to a step input field instead of using it in a string/number expression.`);
|
|
16637
|
-
};
|
|
16638
|
-
}
|
|
16639
|
-
if (typeof prop === "symbol")
|
|
16640
|
-
return;
|
|
16641
|
-
return createProxy([...path, prop]);
|
|
16642
|
-
},
|
|
16643
|
-
has(_t, prop) {
|
|
16644
|
-
return prop === ACCESSOR_BRAND || prop === ACCESSOR_PATH;
|
|
16645
|
-
},
|
|
16646
|
-
ownKeys() {
|
|
16647
|
-
throw new Error(`Pipeline input accessor at path "${pathStr || "<root>"}" cannot be enumerated. ` + `Drill into specific fields instead of spreading the input.`);
|
|
16648
|
-
},
|
|
16649
|
-
set() {
|
|
16650
|
-
throw new Error(`Pipeline input accessor at path "${pathStr || "<root>"}" is read-only.`);
|
|
16651
|
-
}
|
|
16652
|
-
});
|
|
16464
|
+
var branchOutcomeValues = [
|
|
16465
|
+
"continue",
|
|
16466
|
+
"block",
|
|
16467
|
+
"error",
|
|
16468
|
+
"abandoned"
|
|
16469
|
+
];
|
|
16470
|
+
function summarizeTransformOp(op) {
|
|
16471
|
+
if (op.op === "filter") {
|
|
16472
|
+
return `filter_${op.operator}_${JSON.stringify(op.value)}`;
|
|
16473
|
+
}
|
|
16474
|
+
if (op.op === "sortBy") {
|
|
16475
|
+
return `sortBy_${op.direction}`;
|
|
16476
|
+
}
|
|
16477
|
+
return "unique";
|
|
16478
|
+
}
|
|
16479
|
+
function deriveStepSignalsListKey(ops, reducer) {
|
|
16480
|
+
const pluck = ops.find((op) => op.op === "pluck");
|
|
16481
|
+
const base = `${reducer.op}_${pluck?.signalKey ?? "value"}`;
|
|
16482
|
+
const extras = ops.filter((op) => op.op !== "pluck").map(summarizeTransformOp);
|
|
16483
|
+
const reducerExtra = reducer.op === "join" ? `sep_${reducer.separator}` : null;
|
|
16484
|
+
const suffixParts = [...extras, ...reducerExtra ? [reducerExtra] : []];
|
|
16485
|
+
return suffixParts.length > 0 ? `${base}_${suffixParts.join("_")}` : base;
|
|
16653
16486
|
}
|
|
16654
|
-
function
|
|
16655
|
-
|
|
16487
|
+
function createStepSignalsListBuilder(ops) {
|
|
16488
|
+
const withOp = (op) => createStepSignalsListBuilder([...ops, op]);
|
|
16489
|
+
const reduce = (reducer) => {
|
|
16490
|
+
const token = {
|
|
16491
|
+
_tag: "step_signals_list",
|
|
16492
|
+
key: deriveStepSignalsListKey(ops, reducer),
|
|
16493
|
+
ops: [...ops],
|
|
16494
|
+
reducer
|
|
16495
|
+
};
|
|
16496
|
+
return createCohortSignalRef(token);
|
|
16497
|
+
};
|
|
16498
|
+
return {
|
|
16499
|
+
filter: (operator, value) => withOp({ op: "filter", operator, value }),
|
|
16500
|
+
sortBy: (direction = "asc") => withOp({ op: "sortBy", direction }),
|
|
16501
|
+
unique: () => withOp({ op: "unique" }),
|
|
16502
|
+
count: () => reduce({ op: "count" }),
|
|
16503
|
+
sum: () => reduce({ op: "sum" }),
|
|
16504
|
+
avg: () => reduce({ op: "avg" }),
|
|
16505
|
+
min: () => reduce({ op: "min" }),
|
|
16506
|
+
max: () => reduce({ op: "max" }),
|
|
16507
|
+
booleanAll: () => reduce({ op: "booleanAll" }),
|
|
16508
|
+
booleanAny: () => reduce({ op: "booleanAny" }),
|
|
16509
|
+
join: (separator = ",") => reduce({ op: "join", separator }),
|
|
16510
|
+
first: () => reduce({ op: "first" }),
|
|
16511
|
+
last: () => reduce({ op: "last" })
|
|
16512
|
+
};
|
|
16656
16513
|
}
|
|
16657
|
-
function
|
|
16514
|
+
function makeAdvanceAllCtx() {
|
|
16658
16515
|
return {
|
|
16659
|
-
|
|
16660
|
-
|
|
16516
|
+
branchOutcomes: {
|
|
16517
|
+
total: () => createCohortSignalRef("branchCount"),
|
|
16518
|
+
count: (outcome) => createCohortSignalRef(`${outcome}Count`),
|
|
16519
|
+
every: (outcome) => createCohortLeafFromCondition({
|
|
16520
|
+
_tag: "signal",
|
|
16521
|
+
signal: `${outcome}Count`,
|
|
16522
|
+
operator: "equal",
|
|
16523
|
+
value: { fact: "branchCount" }
|
|
16524
|
+
}),
|
|
16525
|
+
some: (outcome) => createCohortLeafFromCondition({
|
|
16526
|
+
_tag: "signal",
|
|
16527
|
+
signal: `${outcome}Count`,
|
|
16528
|
+
operator: "greaterThan",
|
|
16529
|
+
value: 0
|
|
16530
|
+
})
|
|
16531
|
+
},
|
|
16532
|
+
stepSignalsList: {
|
|
16533
|
+
pluck: (signalKey) => createStepSignalsListBuilder([{ op: "pluck", signalKey }])
|
|
16534
|
+
},
|
|
16535
|
+
all: (...refs) => createCohortGroup("all", refs),
|
|
16536
|
+
any: (...refs) => createCohortGroup("any", refs)
|
|
16661
16537
|
};
|
|
16662
16538
|
}
|
|
16663
16539
|
|
|
16664
|
-
// src/definitions/pipelines/
|
|
16665
|
-
|
|
16666
|
-
"
|
|
16540
|
+
// src/definitions/pipelines/bindings.ts
|
|
16541
|
+
function serializeBinding(binding) {
|
|
16542
|
+
if (binding.source === "pipeline_input") {
|
|
16543
|
+
return { source: "pipeline_input", path: binding.path };
|
|
16544
|
+
}
|
|
16545
|
+
if (binding.source === "work_item") {
|
|
16546
|
+
return { source: "work_item", field: binding.field };
|
|
16547
|
+
}
|
|
16548
|
+
if (binding.source === "step_signal") {
|
|
16549
|
+
return {
|
|
16550
|
+
source: "step_signal",
|
|
16551
|
+
stepKey: binding.nodeKey,
|
|
16552
|
+
signalKey: binding.signalKey
|
|
16553
|
+
};
|
|
16554
|
+
}
|
|
16555
|
+
if (binding.source === "literal") {
|
|
16556
|
+
return { source: "literal", value: binding.value };
|
|
16557
|
+
}
|
|
16558
|
+
if (binding.source === "signals_list") {
|
|
16559
|
+
return { source: "signals_list", stepKey: binding.nodeKey };
|
|
16560
|
+
}
|
|
16561
|
+
if (binding.source === "fan_out_item") {
|
|
16562
|
+
return { source: "fan_out_item" };
|
|
16563
|
+
}
|
|
16564
|
+
return { source: "step_output", stepKey: binding.nodeKey };
|
|
16565
|
+
}
|
|
16566
|
+
function serializeInputBindings(input) {
|
|
16567
|
+
const autoBindings = {
|
|
16568
|
+
workItemTitle: { source: "work_item", field: "title" },
|
|
16569
|
+
workItemDescription: { source: "work_item", field: "description" }
|
|
16570
|
+
};
|
|
16571
|
+
const explicitBindings = Object.fromEntries(Object.entries(input).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)]));
|
|
16572
|
+
return { ...autoBindings, ...explicitBindings };
|
|
16573
|
+
}
|
|
16574
|
+
|
|
16575
|
+
// src/definitions/pipelines/work-item-fields.ts
|
|
16576
|
+
var WORK_ITEM_TOP_LEVEL_FIELDS = [
|
|
16577
|
+
"id",
|
|
16667
16578
|
"projectId",
|
|
16668
16579
|
"platform",
|
|
16669
16580
|
"platformId",
|
|
@@ -16704,372 +16615,382 @@ var WORK_ITEM_ACCESSOR = Object.freeze({
|
|
|
16704
16615
|
field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
|
|
16705
16616
|
})
|
|
16706
16617
|
});
|
|
16707
|
-
var WORK_ITEM_FIELD_BINDINGS = {
|
|
16708
|
-
workItemTitle: { source: "work_item", field: "title" },
|
|
16709
|
-
workItemDescription: { source: "work_item", field: "description" }
|
|
16710
|
-
};
|
|
16711
|
-
function makeStepInputCtx(inputSchema) {
|
|
16712
|
-
const baseAccessor = createInputAccessor(inputSchema);
|
|
16713
|
-
const input = new Proxy(baseAccessor, {
|
|
16714
|
-
get(target, prop) {
|
|
16715
|
-
if (typeof prop === "string" && prop in WORK_ITEM_FIELD_BINDINGS) {
|
|
16716
|
-
return WORK_ITEM_FIELD_BINDINGS[prop];
|
|
16717
|
-
}
|
|
16718
|
-
return target[prop];
|
|
16719
|
-
}
|
|
16720
|
-
});
|
|
16721
|
-
return {
|
|
16722
|
-
input,
|
|
16723
|
-
signal(step, key) {
|
|
16724
|
-
return { source: "step_signal", step, signalKey: key };
|
|
16725
|
-
},
|
|
16726
|
-
output(step) {
|
|
16727
|
-
return { source: "step_output", step };
|
|
16728
|
-
},
|
|
16729
|
-
literal: literal2,
|
|
16730
|
-
signalsList(fanOutStep) {
|
|
16731
|
-
return { source: "signals_list", fanOutStep };
|
|
16732
|
-
}
|
|
16733
|
-
};
|
|
16734
|
-
}
|
|
16735
16618
|
function literal2(value) {
|
|
16736
16619
|
return { source: "literal", value };
|
|
16737
16620
|
}
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
|
|
16742
|
-
|
|
16743
|
-
|
|
16744
|
-
|
|
16745
|
-
|
|
16746
|
-
|
|
16747
|
-
|
|
16748
|
-
}
|
|
16749
|
-
function resolveAdditionalStepInputBindings(label, definition) {
|
|
16750
|
-
if (!definition) {
|
|
16751
|
-
return {};
|
|
16752
|
-
}
|
|
16753
|
-
const raw = definition.bindings({
|
|
16754
|
-
workItemField: (fieldName) => ({
|
|
16755
|
-
source: "work_item",
|
|
16756
|
-
field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
|
|
16621
|
+
|
|
16622
|
+
// src/definitions/pipelines/node-input-ctx.ts
|
|
16623
|
+
function makeBaseNodeInputCtx() {
|
|
16624
|
+
return {
|
|
16625
|
+
pipelineInput: (path) => ({ source: "pipeline_input", path }),
|
|
16626
|
+
workItem: WORK_ITEM_ACCESSOR,
|
|
16627
|
+
signal: (nodeKey, signalKey) => ({
|
|
16628
|
+
source: "step_signal",
|
|
16629
|
+
nodeKey,
|
|
16630
|
+
signalKey
|
|
16757
16631
|
}),
|
|
16632
|
+
output: (nodeKey) => ({ source: "step_output", nodeKey }),
|
|
16633
|
+
signalsList: (nodeKey) => ({ source: "signals_list", nodeKey }),
|
|
16758
16634
|
literal: literal2
|
|
16759
|
-
}
|
|
16760
|
-
|
|
16761
|
-
|
|
16762
|
-
|
|
16763
|
-
if (unknown2.length > 0) {
|
|
16764
|
-
throw new Error(`${label}.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((key) => `"${key}"`).join(", ")}`);
|
|
16765
|
-
}
|
|
16766
|
-
}
|
|
16767
|
-
return normalizeInputMapping(raw) ?? {};
|
|
16635
|
+
};
|
|
16636
|
+
}
|
|
16637
|
+
function makeNodeInputCtx() {
|
|
16638
|
+
return makeBaseNodeInputCtx();
|
|
16768
16639
|
}
|
|
16769
|
-
function
|
|
16770
|
-
|
|
16771
|
-
...
|
|
16772
|
-
|
|
16640
|
+
function makeFanOutNodeInputCtx() {
|
|
16641
|
+
return {
|
|
16642
|
+
...makeBaseNodeInputCtx(),
|
|
16643
|
+
item: { source: "fan_out_item" }
|
|
16773
16644
|
};
|
|
16774
|
-
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
16775
16645
|
}
|
|
16776
16646
|
|
|
16777
|
-
// src/definitions/
|
|
16778
|
-
|
|
16779
|
-
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
16783
|
-
|
|
16784
|
-
|
|
16785
|
-
|
|
16786
|
-
|
|
16787
|
-
|
|
16647
|
+
// src/definitions/pipelines/compile-node-definitions.ts
|
|
16648
|
+
function assertTargetExists(ctx, fromKey, toKey) {
|
|
16649
|
+
if (!ctx.stateKeys.has(toKey)) {
|
|
16650
|
+
throw new Error(`Pipeline "${ctx.pipelineKey}": state "${fromKey}" targets unknown state "${toKey}"`);
|
|
16651
|
+
}
|
|
16652
|
+
}
|
|
16653
|
+
function overSignalKeyOf(over) {
|
|
16654
|
+
const lastDot = over.lastIndexOf(".");
|
|
16655
|
+
return lastDot === -1 ? over : over.slice(lastDot + 1);
|
|
16656
|
+
}
|
|
16657
|
+
function compileStepState(stateKey, state, ctx) {
|
|
16658
|
+
ctx.registerStep(state.step);
|
|
16659
|
+
const inputCtx = makeNodeInputCtx();
|
|
16660
|
+
const rawInput = state.input ? state.input(inputCtx) : {};
|
|
16661
|
+
const inputBindingsJson = serializeInputBindings(rawInput);
|
|
16662
|
+
const routeTarget = typeof state.next === "object" ? state.next : null;
|
|
16663
|
+
const defaultOutcome = routeTarget ? {
|
|
16664
|
+
outcome: "route",
|
|
16665
|
+
pipelineKey: routeTarget.routeToPipeline,
|
|
16666
|
+
...routeTarget.input ? { inputJson: routeTarget.input } : {}
|
|
16667
|
+
} : "continue";
|
|
16668
|
+
const rules = state.blockWhen ? [{ _tag: "rule", mode: "all", conditions: [state.blockWhen], outcome: "block" }] : [];
|
|
16669
|
+
const policy = { defaultOutcome, rules };
|
|
16670
|
+
const nodeDefinitions = [
|
|
16671
|
+
{
|
|
16672
|
+
nodeKey: stateKey,
|
|
16673
|
+
kind: "step",
|
|
16674
|
+
stepKey: state.step.key,
|
|
16675
|
+
stepName: state.step.name,
|
|
16676
|
+
stepDescription: state.step.description,
|
|
16677
|
+
inputBindingsJson,
|
|
16678
|
+
timeoutSeconds: state.timeout ?? null,
|
|
16679
|
+
advancementPolicyDefinition: serializeAdvancementPolicy(policy),
|
|
16680
|
+
computedSignalDefinitions: extractInlineComputedSignals(policy)
|
|
16681
|
+
}
|
|
16682
|
+
];
|
|
16683
|
+
const edges = [];
|
|
16684
|
+
if (!routeTarget) {
|
|
16685
|
+
const nextKey = state.next;
|
|
16686
|
+
assertTargetExists(ctx, stateKey, nextKey);
|
|
16687
|
+
edges.push({ fromNodeKey: stateKey, toNodeKey: nextKey });
|
|
16688
|
+
}
|
|
16689
|
+
return { nodeDefinitions, edges };
|
|
16690
|
+
}
|
|
16691
|
+
function compileChoiceState(stateKey, state, ctx) {
|
|
16692
|
+
const stateChoices = state.choices ?? [];
|
|
16693
|
+
if (stateChoices.length === 0 && !state.default) {
|
|
16694
|
+
throw new Error(`Pipeline "${ctx.pipelineKey}": choice state "${stateKey}" requires at least one entry in choices or a default target`);
|
|
16695
|
+
}
|
|
16696
|
+
const choices = stateChoices.map((choiceCase) => {
|
|
16697
|
+
assertTargetExists(ctx, stateKey, choiceCase.next);
|
|
16788
16698
|
return {
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
return {
|
|
16792
|
-
_tag: "rule",
|
|
16793
|
-
mode: "all",
|
|
16794
|
-
conditions: [condition],
|
|
16795
|
-
outcome,
|
|
16796
|
-
...paramsJson ? { outcomeJson: paramsJson } : {}
|
|
16797
|
-
};
|
|
16798
|
-
}
|
|
16699
|
+
conditionJson: serializeCondition(choiceCase.when),
|
|
16700
|
+
targetNodeKey: choiceCase.next
|
|
16799
16701
|
};
|
|
16800
|
-
};
|
|
16702
|
+
});
|
|
16703
|
+
if (state.default)
|
|
16704
|
+
assertTargetExists(ctx, stateKey, state.default);
|
|
16705
|
+
const edges = stateChoices.map((choiceCase) => ({
|
|
16706
|
+
fromNodeKey: stateKey,
|
|
16707
|
+
toNodeKey: choiceCase.next,
|
|
16708
|
+
discriminantJson: { conditionSummary: serializeCondition(choiceCase.when) }
|
|
16709
|
+
}));
|
|
16710
|
+
if (state.default) {
|
|
16711
|
+
edges.push({
|
|
16712
|
+
fromNodeKey: stateKey,
|
|
16713
|
+
toNodeKey: state.default,
|
|
16714
|
+
discriminantJson: { default: true }
|
|
16715
|
+
});
|
|
16716
|
+
}
|
|
16801
16717
|
return {
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
lt: (v) => leaf("lessThan", v),
|
|
16807
|
-
lte: (v) => leaf("lessThanInclusive", v),
|
|
16808
|
-
in: (vs) => leaf("in", vs),
|
|
16809
|
-
notIn: (vs) => leaf("notIn", vs),
|
|
16810
|
-
contains: (v) => leaf("contains", v),
|
|
16811
|
-
doesNotContain: (v) => leaf("doesNotContain", v)
|
|
16718
|
+
nodeDefinitions: [
|
|
16719
|
+
{ nodeKey: stateKey, kind: "choice", choices, default: state.default ?? null }
|
|
16720
|
+
],
|
|
16721
|
+
edges
|
|
16812
16722
|
};
|
|
16813
16723
|
}
|
|
16814
|
-
function
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
|
|
16822
|
-
|
|
16823
|
-
...paramsJson ? { outcomeJson: paramsJson } : {}
|
|
16824
|
-
};
|
|
16825
|
-
}
|
|
16724
|
+
function compileFanOutState(stateKey, state, ctx) {
|
|
16725
|
+
ctx.registerStep(state.step);
|
|
16726
|
+
const inputCtx = makeFanOutNodeInputCtx();
|
|
16727
|
+
const rawInput = state.input ? state.input(inputCtx) : {};
|
|
16728
|
+
const inputBindingsJson = serializeInputBindings(rawInput);
|
|
16729
|
+
const advanceEachResult = state.advanceEach(makeAdvanceEachCtx());
|
|
16730
|
+
const advanceEachPolicy = {
|
|
16731
|
+
default: advanceEachResult.default,
|
|
16732
|
+
...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
|
|
16826
16733
|
};
|
|
16827
|
-
|
|
16828
|
-
|
|
16829
|
-
|
|
16830
|
-
|
|
16831
|
-
|
|
16832
|
-
|
|
16833
|
-
|
|
16834
|
-
|
|
16835
|
-
|
|
16734
|
+
const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
|
|
16735
|
+
const advanceAllPolicy = {
|
|
16736
|
+
default: advanceAllResult.default,
|
|
16737
|
+
...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
|
|
16738
|
+
};
|
|
16739
|
+
const gateKey = `${stateKey}__cohortGate`;
|
|
16740
|
+
if (ctx.stateKeys.has(gateKey)) {
|
|
16741
|
+
throw new Error(`Pipeline "${ctx.pipelineKey}": synthesized cohortGate key "${gateKey}" collides with an author-declared state \u2014 rename state "${stateKey}"`);
|
|
16742
|
+
}
|
|
16743
|
+
assertTargetExists(ctx, stateKey, state.next);
|
|
16836
16744
|
return {
|
|
16837
|
-
|
|
16838
|
-
|
|
16839
|
-
|
|
16840
|
-
|
|
16841
|
-
|
|
16842
|
-
|
|
16843
|
-
|
|
16844
|
-
|
|
16845
|
-
|
|
16846
|
-
|
|
16745
|
+
nodeDefinitions: [
|
|
16746
|
+
{
|
|
16747
|
+
nodeKey: stateKey,
|
|
16748
|
+
kind: "fanOut",
|
|
16749
|
+
stepKey: state.step.key,
|
|
16750
|
+
stepName: state.step.name,
|
|
16751
|
+
stepDescription: state.step.description,
|
|
16752
|
+
inputBindingsJson,
|
|
16753
|
+
timeoutSeconds: state.timeout ?? null,
|
|
16754
|
+
overSignalKey: overSignalKeyOf(state.over),
|
|
16755
|
+
advanceEachPolicyDefinition: serializeCohortAdvancementPolicy(advanceEachPolicy),
|
|
16756
|
+
maxConcurrency: state.maxConcurrency ?? null
|
|
16757
|
+
},
|
|
16758
|
+
{
|
|
16759
|
+
nodeKey: gateKey,
|
|
16760
|
+
kind: "cohortGate",
|
|
16761
|
+
advanceAllPolicyDefinition: serializeCohortAdvancementPolicy(advanceAllPolicy),
|
|
16762
|
+
stepSignalsListDefinitions: extractInlineStepSignalsListDefinitions(advanceAllPolicy)
|
|
16763
|
+
}
|
|
16764
|
+
],
|
|
16765
|
+
edges: [
|
|
16766
|
+
{ fromNodeKey: stateKey, toNodeKey: gateKey },
|
|
16767
|
+
{ fromNodeKey: gateKey, toNodeKey: state.next }
|
|
16768
|
+
]
|
|
16769
|
+
};
|
|
16770
|
+
}
|
|
16771
|
+
function compileParallelState(stateKey, state, ctx) {
|
|
16772
|
+
const branchEntries = Object.entries(state.branches);
|
|
16773
|
+
if (branchEntries.length === 0) {
|
|
16774
|
+
throw new Error(`Pipeline "${ctx.pipelineKey}": parallel state "${stateKey}" requires at least one branch`);
|
|
16775
|
+
}
|
|
16776
|
+
const branches = {};
|
|
16777
|
+
for (const [branchKey, branchConfig] of branchEntries) {
|
|
16778
|
+
ctx.registerStep(branchConfig.step);
|
|
16779
|
+
const inputCtx = makeNodeInputCtx();
|
|
16780
|
+
const rawInput = branchConfig.input ? branchConfig.input(inputCtx) : {};
|
|
16781
|
+
branches[branchKey] = {
|
|
16782
|
+
stepKey: branchConfig.step.key,
|
|
16783
|
+
stepName: branchConfig.step.name,
|
|
16784
|
+
stepDescription: branchConfig.step.description,
|
|
16785
|
+
inputBindingsJson: serializeInputBindings(rawInput)
|
|
16786
|
+
};
|
|
16787
|
+
}
|
|
16788
|
+
let advanceAllPolicyDefinition;
|
|
16789
|
+
if (state.advanceAll) {
|
|
16790
|
+
const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
|
|
16791
|
+
const advanceAllPolicy = {
|
|
16792
|
+
default: advanceAllResult.default,
|
|
16793
|
+
...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
|
|
16794
|
+
};
|
|
16795
|
+
advanceAllPolicyDefinition = serializeCohortAdvancementPolicy(advanceAllPolicy);
|
|
16796
|
+
}
|
|
16797
|
+
assertTargetExists(ctx, stateKey, state.next);
|
|
16798
|
+
return {
|
|
16799
|
+
nodeDefinitions: [
|
|
16800
|
+
{
|
|
16801
|
+
nodeKey: stateKey,
|
|
16802
|
+
kind: "parallel",
|
|
16803
|
+
branches,
|
|
16804
|
+
...advanceAllPolicyDefinition ? { advanceAllPolicyDefinition } : {}
|
|
16805
|
+
}
|
|
16806
|
+
],
|
|
16807
|
+
edges: [{ fromNodeKey: stateKey, toNodeKey: state.next }]
|
|
16847
16808
|
};
|
|
16848
16809
|
}
|
|
16849
|
-
function
|
|
16850
|
-
|
|
16851
|
-
|
|
16852
|
-
|
|
16810
|
+
function compileLoopState(stateKey, state, ctx) {
|
|
16811
|
+
ctx.registerStep(state.step);
|
|
16812
|
+
const inputCtx = makeNodeInputCtx();
|
|
16813
|
+
const rawInput = state.input ? state.input(inputCtx) : {};
|
|
16814
|
+
assertTargetExists(ctx, stateKey, state.next);
|
|
16815
|
+
assertTargetExists(ctx, stateKey, state.onExhausted);
|
|
16853
16816
|
return {
|
|
16854
|
-
|
|
16855
|
-
|
|
16856
|
-
|
|
16857
|
-
|
|
16858
|
-
|
|
16859
|
-
|
|
16817
|
+
nodeDefinitions: [
|
|
16818
|
+
{
|
|
16819
|
+
nodeKey: stateKey,
|
|
16820
|
+
kind: "loop",
|
|
16821
|
+
stepKey: state.step.key,
|
|
16822
|
+
stepName: state.step.name,
|
|
16823
|
+
stepDescription: state.step.description,
|
|
16824
|
+
inputBindingsJson: serializeInputBindings(rawInput),
|
|
16825
|
+
timeoutSeconds: state.timeout ?? null,
|
|
16826
|
+
maxIterations: state.maxIterations,
|
|
16827
|
+
untilConditionJson: serializeCondition(state.until)
|
|
16860
16828
|
}
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
|
|
16829
|
+
],
|
|
16830
|
+
edges: [
|
|
16831
|
+
{ fromNodeKey: stateKey, toNodeKey: state.next, discriminantJson: { loopExit: "next" } },
|
|
16832
|
+
{
|
|
16833
|
+
fromNodeKey: stateKey,
|
|
16834
|
+
toNodeKey: state.onExhausted,
|
|
16835
|
+
discriminantJson: { loopExit: "onExhausted" }
|
|
16836
|
+
}
|
|
16837
|
+
]
|
|
16864
16838
|
};
|
|
16865
16839
|
}
|
|
16866
|
-
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
|
|
16870
|
-
|
|
16871
|
-
|
|
16872
|
-
|
|
16873
|
-
|
|
16874
|
-
|
|
16840
|
+
function compileTerminalState(stateKey, kind) {
|
|
16841
|
+
return { nodeDefinitions: [{ nodeKey: stateKey, kind }], edges: [] };
|
|
16842
|
+
}
|
|
16843
|
+
function assertNoIllegalConvergentEdges(pipelineKey, nodeKindByKey, edges) {
|
|
16844
|
+
const incoming = new Map;
|
|
16845
|
+
for (const edge of edges) {
|
|
16846
|
+
const list = incoming.get(edge.toNodeKey) ?? [];
|
|
16847
|
+
list.push(edge);
|
|
16848
|
+
incoming.set(edge.toNodeKey, list);
|
|
16875
16849
|
}
|
|
16876
|
-
|
|
16877
|
-
|
|
16850
|
+
for (const [targetKey, incomingEdges] of incoming) {
|
|
16851
|
+
if (incomingEdges.length <= 1)
|
|
16852
|
+
continue;
|
|
16853
|
+
const hasInvalidSource = incomingEdges.some((edge) => {
|
|
16854
|
+
const kind = nodeKindByKey.get(edge.fromNodeKey);
|
|
16855
|
+
return kind !== "choice" && kind !== "loop";
|
|
16856
|
+
});
|
|
16857
|
+
if (hasInvalidSource) {
|
|
16858
|
+
throw new Error(`Pipeline "${pipelineKey}": state "${targetKey}" has more than one incoming edge, but not every source is a 'choice'/'loop' state (unconditional convergent edges are not allowed \u2014 see docs/research/flat-pipeline-sdk-and-visual-designer.md \xA76).`);
|
|
16859
|
+
}
|
|
16878
16860
|
}
|
|
16879
|
-
return "unique";
|
|
16880
16861
|
}
|
|
16881
|
-
|
|
16882
|
-
|
|
16883
|
-
|
|
16884
|
-
|
|
16885
|
-
const reducerExtra = reducer.op === "join" ? `sep_${reducer.separator}` : null;
|
|
16886
|
-
const suffixParts = [...extras, ...reducerExtra ? [reducerExtra] : []];
|
|
16887
|
-
return suffixParts.length > 0 ? `${base}_${suffixParts.join("_")}` : base;
|
|
16862
|
+
|
|
16863
|
+
// src/definitions/pipelines/define-pipeline.ts
|
|
16864
|
+
function isWorkingNodeDefinition(node) {
|
|
16865
|
+
return node.kind === "step" || node.kind === "fanOut" || node.kind === "loop";
|
|
16888
16866
|
}
|
|
16889
|
-
function
|
|
16890
|
-
const
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
16894
|
-
|
|
16895
|
-
|
|
16896
|
-
|
|
16897
|
-
|
|
16898
|
-
|
|
16867
|
+
function definePipeline(config2) {
|
|
16868
|
+
const stateEntries = Object.entries(config2.states);
|
|
16869
|
+
if (stateEntries.length === 0) {
|
|
16870
|
+
throw new Error(`Pipeline "${config2.key}" must declare at least one state`);
|
|
16871
|
+
}
|
|
16872
|
+
const startState = config2.states[config2.startAt];
|
|
16873
|
+
if (!startState) {
|
|
16874
|
+
throw new Error(`Pipeline "${config2.key}"'s startAt "${config2.startAt}" does not name a declared state`);
|
|
16875
|
+
}
|
|
16876
|
+
if (startState.kind === "choice" || startState.kind === "succeed" || startState.kind === "fail") {
|
|
16877
|
+
throw new Error(`Pipeline "${config2.key}"'s startAt "${config2.startAt}" names a '${startState.kind}' state, which cannot be an entry point`);
|
|
16878
|
+
}
|
|
16879
|
+
const stateKeys = new Set(Object.keys(config2.states));
|
|
16880
|
+
const stepDefMap = new Map;
|
|
16881
|
+
const registerStep = (step) => {
|
|
16882
|
+
const mapKey = `${step.key}@v${String(step.version)}`;
|
|
16883
|
+
if (!stepDefMap.has(mapKey))
|
|
16884
|
+
stepDefMap.set(mapKey, step);
|
|
16899
16885
|
};
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
count: () => reduce({ op: "count" }),
|
|
16905
|
-
sum: () => reduce({ op: "sum" }),
|
|
16906
|
-
avg: () => reduce({ op: "avg" }),
|
|
16907
|
-
min: () => reduce({ op: "min" }),
|
|
16908
|
-
max: () => reduce({ op: "max" }),
|
|
16909
|
-
booleanAll: () => reduce({ op: "booleanAll" }),
|
|
16910
|
-
booleanAny: () => reduce({ op: "booleanAny" }),
|
|
16911
|
-
join: (separator = ",") => reduce({ op: "join", separator }),
|
|
16912
|
-
first: () => reduce({ op: "first" }),
|
|
16913
|
-
last: () => reduce({ op: "last" })
|
|
16886
|
+
const compileContext = {
|
|
16887
|
+
pipelineKey: config2.key,
|
|
16888
|
+
stateKeys,
|
|
16889
|
+
registerStep
|
|
16914
16890
|
};
|
|
16915
|
-
|
|
16916
|
-
|
|
16891
|
+
const nodeDefinitions = [];
|
|
16892
|
+
const dependencyEdges = [];
|
|
16893
|
+
for (const [stateKey, state] of stateEntries) {
|
|
16894
|
+
const compiled = state.kind === "step" ? compileStepState(stateKey, state, compileContext) : state.kind === "choice" ? compileChoiceState(stateKey, state, compileContext) : state.kind === "fanOut" ? compileFanOutState(stateKey, state, compileContext) : state.kind === "parallel" ? compileParallelState(stateKey, state, compileContext) : state.kind === "loop" ? compileLoopState(stateKey, state, compileContext) : compileTerminalState(stateKey, state.kind);
|
|
16895
|
+
nodeDefinitions.push(...compiled.nodeDefinitions);
|
|
16896
|
+
dependencyEdges.push(...compiled.edges);
|
|
16897
|
+
}
|
|
16898
|
+
const nodeKindByKey = new Map(nodeDefinitions.map((node) => [node.nodeKey, node.kind]));
|
|
16899
|
+
assertNoIllegalConvergentEdges(config2.key, nodeKindByKey, dependencyEdges);
|
|
16900
|
+
let inputSchemaJson = null;
|
|
16901
|
+
if (config2.input) {
|
|
16902
|
+
try {
|
|
16903
|
+
inputSchemaJson = exports_external.toJSONSchema(config2.input);
|
|
16904
|
+
} catch {
|
|
16905
|
+
inputSchemaJson = null;
|
|
16906
|
+
}
|
|
16907
|
+
}
|
|
16917
16908
|
return {
|
|
16918
|
-
|
|
16919
|
-
|
|
16920
|
-
|
|
16921
|
-
|
|
16922
|
-
|
|
16923
|
-
|
|
16924
|
-
|
|
16925
|
-
|
|
16926
|
-
|
|
16927
|
-
some: (outcome) => createCohortLeafFromCondition({
|
|
16928
|
-
_tag: "signal",
|
|
16929
|
-
signal: `${outcome}Count`,
|
|
16930
|
-
operator: "greaterThan",
|
|
16931
|
-
value: 0
|
|
16932
|
-
})
|
|
16933
|
-
},
|
|
16934
|
-
stepSignalsList: {
|
|
16935
|
-
pluck: (signalKey) => createStepSignalsListBuilder([{ op: "pluck", signalKey }])
|
|
16936
|
-
},
|
|
16937
|
-
all: (...refs) => createCohortGroup("all", refs),
|
|
16938
|
-
any: (...refs) => createCohortGroup("any", refs)
|
|
16909
|
+
key: config2.key,
|
|
16910
|
+
name: config2.name ?? config2.key,
|
|
16911
|
+
description: config2.description ?? null,
|
|
16912
|
+
version: config2.version ?? 1,
|
|
16913
|
+
status: config2.status ?? "active",
|
|
16914
|
+
inputSchemaJson,
|
|
16915
|
+
_stepDefinitions: [...stepDefMap.values()],
|
|
16916
|
+
nodeDefinitions,
|
|
16917
|
+
dependencyEdges
|
|
16939
16918
|
};
|
|
16940
16919
|
}
|
|
16941
|
-
|
|
16942
|
-
|
|
16943
|
-
|
|
16944
|
-
|
|
16945
|
-
const ctx = {
|
|
16946
|
-
...baseCtx,
|
|
16947
|
-
item: { source: "fan_out_item" }
|
|
16948
|
-
};
|
|
16949
|
-
const rawInput = config2.input ? config2.input(ctx) : {};
|
|
16950
|
-
const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
|
|
16951
|
-
const fanOutNodeConfig = {
|
|
16952
|
-
nodeType: "fanOut",
|
|
16953
|
-
fanOutStep: step,
|
|
16954
|
-
overSignalKey: config2.over,
|
|
16955
|
-
...input ? { input } : {},
|
|
16956
|
-
...config2.timeout !== undefined ? { timeout: config2.timeout } : {}
|
|
16957
|
-
};
|
|
16958
|
-
const advanceEachCtx = makeAdvanceEachCtx();
|
|
16959
|
-
const advanceEachResult = config2.advance(advanceEachCtx);
|
|
16960
|
-
const advanceEachPolicy = {
|
|
16961
|
-
default: advanceEachResult.default,
|
|
16962
|
-
...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
|
|
16963
|
-
};
|
|
16964
|
-
fanOutNodeConfig.advanceEach = advanceEachPolicy;
|
|
16965
|
-
const cohortGateNodeConfig = {
|
|
16966
|
-
nodeType: "cohortGate",
|
|
16967
|
-
nodeKey: `${step.key}__cohortGate`
|
|
16968
|
-
};
|
|
16969
|
-
const advanceAllCtx = makeAdvanceAllCtx();
|
|
16970
|
-
const advanceAllResult = config2.advanceAll(advanceAllCtx);
|
|
16971
|
-
const advanceAllPolicy = {
|
|
16972
|
-
default: advanceAllResult.default,
|
|
16973
|
-
...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
|
|
16974
|
-
};
|
|
16975
|
-
cohortGateNodeConfig.advanceAll = advanceAllPolicy;
|
|
16976
|
-
nodes.push(fanOutNodeConfig, cohortGateNodeConfig);
|
|
16977
|
-
return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
|
|
16920
|
+
// src/definitions/pipelines/pipeline-definitions-client.ts
|
|
16921
|
+
function createPipelineDefinitionsClient(baseUrl) {
|
|
16922
|
+
const client2 = createClient({ baseUrl });
|
|
16923
|
+
return buildPipelineDefinitionsClient(new PipelineDefinitions({ client: client2 }));
|
|
16978
16924
|
}
|
|
16979
|
-
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
|
|
16988
|
-
stepConfig.timeout = options.timeout;
|
|
16989
|
-
const advanceCtx = makeAdvanceCtx();
|
|
16990
|
-
const result = options.advance(advanceCtx);
|
|
16991
|
-
const policy = {
|
|
16992
|
-
defaultOutcome: result.default,
|
|
16993
|
-
...result.rules !== undefined ? { rules: result.rules } : {}
|
|
16994
|
-
};
|
|
16995
|
-
stepConfig.advancement = policy;
|
|
16996
|
-
nodes.push(stepConfig);
|
|
16997
|
-
return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
|
|
16925
|
+
function resolveStepRef(pipelineKey, nodeKey, stepKey, stepDefMap) {
|
|
16926
|
+
const stepDef = stepDefMap.get(stepKey);
|
|
16927
|
+
if (!stepDef) {
|
|
16928
|
+
throw new Error(`Step "${stepKey}" referenced by node "${nodeKey}" in pipeline "${pipelineKey}" was not found on ` + `the server. Run \`boboddy steps push\` first to push your step definitions.`);
|
|
16929
|
+
}
|
|
16930
|
+
return {
|
|
16931
|
+
stepDefinitionId: stepDef.id,
|
|
16932
|
+
stepDefinitionVersion: stepDef.version
|
|
16933
|
+
};
|
|
16998
16934
|
}
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17003
|
-
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
this.inputSchema = inputSchema;
|
|
17008
|
-
this.meta = meta3;
|
|
17009
|
-
this.nodes = nodes;
|
|
17010
|
-
this.pipelineInputBindings = pipelineInputBindings;
|
|
17011
|
-
this.pipelineStepInputBindings = pipelineStepInputBindings;
|
|
17012
|
-
}
|
|
17013
|
-
step(step, options) {
|
|
17014
|
-
return pushStep(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, options);
|
|
17015
|
-
}
|
|
17016
|
-
fanOutStep(step, config2) {
|
|
17017
|
-
return beginFanOut(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, config2);
|
|
17018
|
-
}
|
|
17019
|
-
build() {
|
|
17020
|
-
const config2 = {
|
|
17021
|
-
key: this.meta.key,
|
|
17022
|
-
name: this.meta.name,
|
|
17023
|
-
description: this.meta.description,
|
|
17024
|
-
version: this.meta.version,
|
|
17025
|
-
status: this.meta.status,
|
|
17026
|
-
input: this.inputSchema,
|
|
17027
|
-
nodes: this.nodes,
|
|
17028
|
-
pipelineInputBindings: this.pipelineInputBindings
|
|
16935
|
+
function buildConfigJson(pipelineKey, node, stepDefMap) {
|
|
16936
|
+
if (node.kind === "choice") {
|
|
16937
|
+
return { choices: node.choices, default: node.default };
|
|
16938
|
+
}
|
|
16939
|
+
if (node.kind === "loop") {
|
|
16940
|
+
return {
|
|
16941
|
+
maxIterations: node.maxIterations,
|
|
16942
|
+
untilConditionJson: node.untilConditionJson
|
|
17029
16943
|
};
|
|
17030
|
-
return buildPipelineSpec(config2);
|
|
17031
16944
|
}
|
|
17032
|
-
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
pipelineStepInputBindings;
|
|
17040
|
-
constructor(meta3) {
|
|
17041
|
-
const { additionalPipelineInput, additionalStepInput, ...rest } = meta3;
|
|
17042
|
-
this.inputSchema = additionalPipelineInput?.schema ?? exports_external.unknown();
|
|
17043
|
-
this.meta = rest;
|
|
17044
|
-
if (additionalPipelineInput) {
|
|
17045
|
-
const raw = additionalPipelineInput.bindings({
|
|
17046
|
-
workItem: WORK_ITEM_ACCESSOR,
|
|
17047
|
-
literal: literal2
|
|
17048
|
-
});
|
|
17049
|
-
if (additionalPipelineInput.schema instanceof exports_external.ZodObject) {
|
|
17050
|
-
const validKeys = new Set(Object.keys(additionalPipelineInput.schema.shape));
|
|
17051
|
-
const unknown2 = Object.keys(raw).filter((k) => !validKeys.has(k));
|
|
17052
|
-
if (unknown2.length > 0) {
|
|
17053
|
-
throw new Error(`additionalPipelineInput.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((k) => `"${k}"`).join(", ")}`);
|
|
17054
|
-
}
|
|
17055
|
-
}
|
|
17056
|
-
this.pipelineInputBindings = normalizeInputMapping(raw) ?? {};
|
|
17057
|
-
} else {
|
|
17058
|
-
this.pipelineInputBindings = {};
|
|
16945
|
+
if (node.kind === "parallel") {
|
|
16946
|
+
const branches = {};
|
|
16947
|
+
for (const [branchKey, branch] of Object.entries(node.branches)) {
|
|
16948
|
+
branches[branchKey] = {
|
|
16949
|
+
...resolveStepRef(pipelineKey, node.nodeKey, branch.stepKey, stepDefMap),
|
|
16950
|
+
inputBindingsJson: branch.inputBindingsJson
|
|
16951
|
+
};
|
|
17059
16952
|
}
|
|
17060
|
-
|
|
16953
|
+
return {
|
|
16954
|
+
branches,
|
|
16955
|
+
...node.advanceAllPolicyDefinition ? { advanceAllPolicyDefinition: node.advanceAllPolicyDefinition } : {}
|
|
16956
|
+
};
|
|
16957
|
+
}
|
|
16958
|
+
if (node.kind === "fanOut") {
|
|
16959
|
+
return {
|
|
16960
|
+
overSignalKey: node.overSignalKey,
|
|
16961
|
+
advanceEachPolicyDefinition: node.advanceEachPolicyDefinition,
|
|
16962
|
+
maxConcurrency: node.maxConcurrency
|
|
16963
|
+
};
|
|
17061
16964
|
}
|
|
17062
|
-
|
|
17063
|
-
return
|
|
16965
|
+
if (node.kind === "cohortGate") {
|
|
16966
|
+
return {
|
|
16967
|
+
advanceAllPolicyDefinition: node.advanceAllPolicyDefinition,
|
|
16968
|
+
stepSignalsListDefinitions: node.stepSignalsListDefinitions
|
|
16969
|
+
};
|
|
17064
16970
|
}
|
|
16971
|
+
return null;
|
|
17065
16972
|
}
|
|
17066
|
-
function
|
|
17067
|
-
|
|
17068
|
-
|
|
17069
|
-
|
|
17070
|
-
|
|
17071
|
-
|
|
17072
|
-
|
|
16973
|
+
function buildGraphNodeInput(pipelineKey, node, stepDefMap) {
|
|
16974
|
+
const working = isWorkingNodeDefinition(node) ? node : null;
|
|
16975
|
+
const stepRef = working ? resolveStepRef(pipelineKey, node.nodeKey, working.stepKey, stepDefMap) : null;
|
|
16976
|
+
const policy = node.kind === "step" ? node.advancementPolicyDefinition : undefined;
|
|
16977
|
+
return {
|
|
16978
|
+
key: node.nodeKey,
|
|
16979
|
+
kind: node.kind,
|
|
16980
|
+
name: working?.stepName ?? node.nodeKey,
|
|
16981
|
+
...stepRef ?? {},
|
|
16982
|
+
description: working?.stepDescription ?? null,
|
|
16983
|
+
inputBindingsJson: working ? working.inputBindingsJson : null,
|
|
16984
|
+
timeoutSeconds: working?.timeoutSeconds ?? null,
|
|
16985
|
+
...policy ? {
|
|
16986
|
+
advancementPolicyRulesJson: policy.rulesJson,
|
|
16987
|
+
advancementPolicyDefaultEventType: policy.defaultEventType,
|
|
16988
|
+
advancementPolicyDefaultEventParamsJson: policy.defaultEventParamsJson,
|
|
16989
|
+
advancementPolicyAllowedEventTypes: policy.allowedEventTypes
|
|
16990
|
+
} : {},
|
|
16991
|
+
configJson: buildConfigJson(pipelineKey, node, stepDefMap),
|
|
16992
|
+
computedSignalDefinitions: node.kind === "step" ? node.computedSignalDefinitions : []
|
|
16993
|
+
};
|
|
17073
16994
|
}
|
|
17074
16995
|
var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
17075
16996
|
return {
|
|
@@ -17090,37 +17011,12 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
|
17090
17011
|
stepDefMap.set(s.key, s);
|
|
17091
17012
|
}
|
|
17092
17013
|
}
|
|
17093
|
-
const
|
|
17094
|
-
|
|
17095
|
-
|
|
17096
|
-
|
|
17097
|
-
|
|
17098
|
-
|
|
17099
|
-
throw new Error(`Pipeline "${spec.key}" has a malformed step graph: its node and ` + `dependency-edge definitions do not form a single connected, ` + `acyclic chain. This should not happen from the \`pipeline()\` ` + `builder \u2014 check for hand-edited or generated pipeline specs.`);
|
|
17100
|
-
}
|
|
17101
|
-
const stepDefinitions = ordered.map((node, index) => {
|
|
17102
|
-
const stepKey = node.stepKey;
|
|
17103
|
-
if (!stepKey) {
|
|
17104
|
-
throw new Error(`Node "${node.nodeKey}" in pipeline "${spec.key}" has no stepKey.`);
|
|
17105
|
-
}
|
|
17106
|
-
const stepDef = stepDefMap.get(stepKey);
|
|
17107
|
-
if (!stepDef) {
|
|
17108
|
-
throw new Error(`Step "${stepKey}" referenced in pipeline "${spec.key}" was not found on the server. ` + `Run \`boboddy steps push\` first to push your step definitions.`);
|
|
17109
|
-
}
|
|
17110
|
-
return {
|
|
17111
|
-
stepDefinitionId: stepDef.id,
|
|
17112
|
-
stepDefinitionVersion: stepDef.version,
|
|
17113
|
-
key: node.nodeKey,
|
|
17114
|
-
name: node.stepName ?? "",
|
|
17115
|
-
description: node.stepDescription ?? null,
|
|
17116
|
-
position: index + 1,
|
|
17117
|
-
inputBindingsJson: node.inputBindingsJson ?? {},
|
|
17118
|
-
timeoutSeconds: node.timeoutSeconds ?? null,
|
|
17119
|
-
retryPolicyJson: null,
|
|
17120
|
-
advancementPolicyDefinition: node.advancementPolicyDefinition,
|
|
17121
|
-
computedSignalDefinitions: node.computedSignalDefinitions ?? []
|
|
17122
|
-
};
|
|
17123
|
-
});
|
|
17014
|
+
const nodeDefinitions = spec.nodeDefinitions.map((node) => buildGraphNodeInput(spec.key, node, stepDefMap));
|
|
17015
|
+
const dependencyEdges = spec.dependencyEdges.map((edge) => ({
|
|
17016
|
+
fromNodeKey: edge.fromNodeKey,
|
|
17017
|
+
toNodeKey: edge.toNodeKey,
|
|
17018
|
+
discriminantJson: edge.discriminantJson ?? null
|
|
17019
|
+
}));
|
|
17124
17020
|
const body = {
|
|
17125
17021
|
projectId,
|
|
17126
17022
|
key: spec.key,
|
|
@@ -17128,7 +17024,8 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
|
17128
17024
|
description: spec.description,
|
|
17129
17025
|
status: spec.status,
|
|
17130
17026
|
inputSchemaJson: spec.inputSchemaJson,
|
|
17131
|
-
|
|
17027
|
+
nodeDefinitions,
|
|
17028
|
+
dependencyEdges
|
|
17132
17029
|
};
|
|
17133
17030
|
const result = await pipelineDefinitions.upsertPipelineDefinition({
|
|
17134
17031
|
body,
|
|
@@ -17186,17 +17083,17 @@ function buildWorkItemAccessor() {
|
|
|
17186
17083
|
}
|
|
17187
17084
|
return accessor;
|
|
17188
17085
|
}
|
|
17189
|
-
function makeAssign(
|
|
17190
|
-
if (typeof
|
|
17191
|
-
throw new Error("assign() requires a pipeline spec produced by
|
|
17086
|
+
function makeAssign(pipeline) {
|
|
17087
|
+
if (typeof pipeline !== "object" || typeof pipeline["key"] !== "string" || !Array.isArray(pipeline["nodeDefinitions"])) {
|
|
17088
|
+
throw new Error("assign() requires a pipeline spec produced by definePipeline(). " + "Pass the default-exported value from a pipeline definition file.");
|
|
17192
17089
|
}
|
|
17193
|
-
return { _tag: "assign", pipeline
|
|
17090
|
+
return { _tag: "assign", pipeline };
|
|
17194
17091
|
}
|
|
17195
|
-
function
|
|
17092
|
+
function extractCondition(ref) {
|
|
17196
17093
|
return ref._condition;
|
|
17197
17094
|
}
|
|
17198
17095
|
function makeGroup(mode, refs) {
|
|
17199
|
-
const conditions = refs.map(
|
|
17096
|
+
const conditions = refs.map(extractCondition);
|
|
17200
17097
|
const condition = { _tag: "group", mode, conditions };
|
|
17201
17098
|
return {
|
|
17202
17099
|
_condition: condition,
|
|
@@ -17508,6 +17405,58 @@ function resolveSourcePath(schema, sourcePath) {
|
|
|
17508
17405
|
}
|
|
17509
17406
|
return { kind: "resolved" };
|
|
17510
17407
|
}
|
|
17408
|
+
// src/definitions/pipelines/chain-graph.ts
|
|
17409
|
+
function tryComputeTopoRanks(nodeDefinitions, dependencyEdges) {
|
|
17410
|
+
const nodeKeys = new Set(nodeDefinitions.map((node) => node.nodeKey));
|
|
17411
|
+
const outgoing = new Map;
|
|
17412
|
+
const inDegree = new Map;
|
|
17413
|
+
for (const key of nodeKeys) {
|
|
17414
|
+
outgoing.set(key, []);
|
|
17415
|
+
inDegree.set(key, 0);
|
|
17416
|
+
}
|
|
17417
|
+
for (const edge of dependencyEdges) {
|
|
17418
|
+
if (!nodeKeys.has(edge.fromNodeKey) || !nodeKeys.has(edge.toNodeKey)) {
|
|
17419
|
+
return null;
|
|
17420
|
+
}
|
|
17421
|
+
outgoing.get(edge.fromNodeKey)?.push(edge.toNodeKey);
|
|
17422
|
+
inDegree.set(edge.toNodeKey, (inDegree.get(edge.toNodeKey) ?? 0) + 1);
|
|
17423
|
+
}
|
|
17424
|
+
const rank = new Map;
|
|
17425
|
+
const queue = [];
|
|
17426
|
+
for (const key of nodeKeys) {
|
|
17427
|
+
if ((inDegree.get(key) ?? 0) === 0) {
|
|
17428
|
+
rank.set(key, 0);
|
|
17429
|
+
queue.push(key);
|
|
17430
|
+
}
|
|
17431
|
+
}
|
|
17432
|
+
let visitedCount = 0;
|
|
17433
|
+
while (queue.length > 0) {
|
|
17434
|
+
const current = queue.shift();
|
|
17435
|
+
if (current === undefined)
|
|
17436
|
+
break;
|
|
17437
|
+
visitedCount += 1;
|
|
17438
|
+
for (const next of outgoing.get(current) ?? []) {
|
|
17439
|
+
rank.set(next, Math.max(rank.get(next) ?? 0, (rank.get(current) ?? 0) + 1));
|
|
17440
|
+
const remaining = (inDegree.get(next) ?? 0) - 1;
|
|
17441
|
+
inDegree.set(next, remaining);
|
|
17442
|
+
if (remaining === 0)
|
|
17443
|
+
queue.push(next);
|
|
17444
|
+
}
|
|
17445
|
+
}
|
|
17446
|
+
if (visitedCount !== nodeKeys.size)
|
|
17447
|
+
return null;
|
|
17448
|
+
return rank;
|
|
17449
|
+
}
|
|
17450
|
+
function tryOrderNodeDefinitionsByTopoRank(nodeDefinitions, dependencyEdges) {
|
|
17451
|
+
const rankByKey = tryComputeTopoRanks(nodeDefinitions, dependencyEdges);
|
|
17452
|
+
if (rankByKey === null)
|
|
17453
|
+
return null;
|
|
17454
|
+
return nodeDefinitions.map((node, declarationIndex) => ({ node, declarationIndex })).sort((left, right) => {
|
|
17455
|
+
const rankDiff = (rankByKey.get(left.node.nodeKey) ?? 0) - (rankByKey.get(right.node.nodeKey) ?? 0);
|
|
17456
|
+
return rankDiff !== 0 ? rankDiff : left.declarationIndex - right.declarationIndex;
|
|
17457
|
+
}).map(({ node }) => node);
|
|
17458
|
+
}
|
|
17459
|
+
|
|
17511
17460
|
// src/definitions/validation/validate-definition-specs.ts
|
|
17512
17461
|
function listPaths(paths, limit = 24) {
|
|
17513
17462
|
if (paths.length === 0)
|
|
@@ -17570,9 +17519,10 @@ function checkHealthChecks(steps) {
|
|
|
17570
17519
|
}
|
|
17571
17520
|
return issues;
|
|
17572
17521
|
}
|
|
17573
|
-
function routeTargets(
|
|
17574
|
-
if (!
|
|
17522
|
+
function routeTargets(node) {
|
|
17523
|
+
if (node.kind !== "step" || !node.advancementPolicyDefinition)
|
|
17575
17524
|
return [];
|
|
17525
|
+
const policy = node.advancementPolicyDefinition;
|
|
17576
17526
|
const keys = [];
|
|
17577
17527
|
if (policy.defaultEventType === "route" && typeof policy.defaultEventParamsJson?.["pipelineKey"] === "string") {
|
|
17578
17528
|
keys.push(policy.defaultEventParamsJson["pipelineKey"]);
|
|
@@ -17587,17 +17537,20 @@ function routeTargets(policy) {
|
|
|
17587
17537
|
function checkRouteTargets(pipelines, knownPipelineKeys) {
|
|
17588
17538
|
const issues = [];
|
|
17589
17539
|
const known = new Set([
|
|
17590
|
-
...pipelines.map((
|
|
17540
|
+
...pipelines.map((pipeline) => pipeline.key),
|
|
17591
17541
|
...knownPipelineKeys
|
|
17592
17542
|
]);
|
|
17593
|
-
for (const
|
|
17594
|
-
for (const node of
|
|
17595
|
-
|
|
17543
|
+
for (const pipeline of pipelines) {
|
|
17544
|
+
for (const node of pipeline.nodeDefinitions) {
|
|
17545
|
+
const stepLabel = (isWorkingNodeDefinition(node) ? node.stepKey : undefined) ?? node.nodeKey;
|
|
17546
|
+
for (const target of routeTargets(node)) {
|
|
17596
17547
|
if (known.has(target))
|
|
17597
17548
|
continue;
|
|
17598
17549
|
issues.push({
|
|
17599
17550
|
check: "route-target",
|
|
17600
|
-
|
|
17551
|
+
pipelineKey: pipeline.key,
|
|
17552
|
+
nodeKey: node.nodeKey,
|
|
17553
|
+
message: `Pipeline "${pipeline.key}" step "${stepLabel}" routes to pipeline ` + `"${target}", but no pipeline with that key was found on the server or ` + `in the current push batch. Push the target pipeline first.`
|
|
17601
17554
|
});
|
|
17602
17555
|
}
|
|
17603
17556
|
}
|
|
@@ -17606,7 +17559,7 @@ function checkRouteTargets(pipelines, knownPipelineKeys) {
|
|
|
17606
17559
|
}
|
|
17607
17560
|
function executionRanks(nodeDefinitions, dependencyEdges) {
|
|
17608
17561
|
const indexes = nodeDefinitions.map((_, index) => index);
|
|
17609
|
-
const orderedNodes =
|
|
17562
|
+
const orderedNodes = tryOrderNodeDefinitionsByTopoRank(nodeDefinitions, dependencyEdges);
|
|
17610
17563
|
if (orderedNodes === null) {
|
|
17611
17564
|
return new Map(indexes.map((index, rank) => [index, rank]));
|
|
17612
17565
|
}
|
|
@@ -17616,70 +17569,81 @@ function executionRanks(nodeDefinitions, dependencyEdges) {
|
|
|
17616
17569
|
}
|
|
17617
17570
|
function bindingSource(binding) {
|
|
17618
17571
|
if (binding.source === "step_signal") {
|
|
17619
|
-
return {
|
|
17572
|
+
return { nodeKey: binding.stepKey, signalKey: binding.signalKey, kind: "signal" };
|
|
17620
17573
|
}
|
|
17621
17574
|
if (binding.source === "step_output") {
|
|
17622
|
-
return {
|
|
17575
|
+
return { nodeKey: binding.stepKey, signalKey: null, kind: "output" };
|
|
17623
17576
|
}
|
|
17624
17577
|
if (binding.source === "signals_list") {
|
|
17625
|
-
return {
|
|
17578
|
+
return { nodeKey: binding.stepKey, signalKey: null, kind: "signals_list" };
|
|
17626
17579
|
}
|
|
17627
17580
|
return null;
|
|
17628
17581
|
}
|
|
17629
|
-
function declaredSignalKeys(
|
|
17630
|
-
const
|
|
17631
|
-
|
|
17632
|
-
|
|
17582
|
+
function declaredSignalKeys(producerNode, stepsByKey) {
|
|
17583
|
+
const stepKey = isWorkingNodeDefinition(producerNode) ? producerNode.stepKey : undefined;
|
|
17584
|
+
const specs = stepKey ? stepsByKey.get(stepKey) : undefined;
|
|
17585
|
+
const computedSignalDefinitions = (producerNode.kind === "step" ? producerNode.computedSignalDefinitions : []) ?? [];
|
|
17633
17586
|
const keys = new Set;
|
|
17634
|
-
for (const spec of specs) {
|
|
17587
|
+
for (const spec of specs ?? []) {
|
|
17635
17588
|
for (const signal2 of spec.signalExtractorDefinitions)
|
|
17636
17589
|
keys.add(signal2.key);
|
|
17637
17590
|
}
|
|
17638
|
-
for (const
|
|
17639
|
-
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
|
|
17591
|
+
for (const computed of computedSignalDefinitions) {
|
|
17592
|
+
keys.add(computed.key);
|
|
17593
|
+
}
|
|
17594
|
+
if (!specs && computedSignalDefinitions.length === 0) {
|
|
17595
|
+
return null;
|
|
17643
17596
|
}
|
|
17644
17597
|
return [...keys];
|
|
17645
17598
|
}
|
|
17646
17599
|
function checkSignalBindings(pipelines, stepsByKey) {
|
|
17647
17600
|
const issues = [];
|
|
17648
|
-
for (const
|
|
17649
|
-
const ranks = executionRanks(
|
|
17650
|
-
const order = [...
|
|
17651
|
-
const orderHint = `
|
|
17652
|
-
|
|
17601
|
+
for (const pipeline of pipelines) {
|
|
17602
|
+
const ranks = executionRanks(pipeline.nodeDefinitions, pipeline.dependencyEdges);
|
|
17603
|
+
const order = [...pipeline.nodeDefinitions.keys()].sort((left, right) => (ranks.get(left) ?? 0) - (ranks.get(right) ?? 0)).map((index) => pipeline.nodeDefinitions[index]?.nodeKey ?? "");
|
|
17604
|
+
const orderHint = `Nodes in "${pipeline.key}", in order: ${order.join(" \u2192 ")}.`;
|
|
17605
|
+
const nodeByKey = new Map(pipeline.nodeDefinitions.map((node) => [node.nodeKey, node]));
|
|
17606
|
+
pipeline.nodeDefinitions.forEach((node, index) => {
|
|
17653
17607
|
const consumerRank = ranks.get(index) ?? index;
|
|
17654
|
-
const where = `Pipeline "${
|
|
17655
|
-
|
|
17608
|
+
const where = `Pipeline "${pipeline.key}" node "${node.nodeKey}"`;
|
|
17609
|
+
const inputBindingsJson = (isWorkingNodeDefinition(node) ? node.inputBindingsJson : {}) ?? {};
|
|
17610
|
+
for (const [field, binding] of Object.entries(inputBindingsJson)) {
|
|
17656
17611
|
const source = bindingSource(binding);
|
|
17657
17612
|
if (!source)
|
|
17658
17613
|
continue;
|
|
17659
|
-
const what = source.kind === "signal" ? `binds input "${field}" to signal "${source.signalKey ?? ""}" of
|
|
17660
|
-
const
|
|
17661
|
-
|
|
17614
|
+
const what = source.kind === "signal" ? `binds input "${field}" to signal "${source.signalKey ?? ""}" of node "${source.nodeKey}"` : source.kind === "signals_list" ? `binds input "${field}" to the signals list of fan-out node "${source.nodeKey}"` : `binds input "${field}" to the output of node "${source.nodeKey}"`;
|
|
17615
|
+
const issueBase = {
|
|
17616
|
+
pipelineKey: pipeline.key,
|
|
17617
|
+
nodeKey: node.nodeKey,
|
|
17618
|
+
targetNodeKey: source.nodeKey
|
|
17619
|
+
};
|
|
17620
|
+
const producerNode = nodeByKey.get(source.nodeKey);
|
|
17621
|
+
const producerRank = producerNode ? ranks.get(pipeline.nodeDefinitions.indexOf(producerNode)) ?? null : null;
|
|
17622
|
+
if (!producerNode || producerRank === null) {
|
|
17662
17623
|
issues.push({
|
|
17663
17624
|
check: "signal-binding",
|
|
17664
|
-
|
|
17625
|
+
...issueBase,
|
|
17626
|
+
message: `${where} ${what}, but no node with that key is in the pipeline. ${orderHint}`
|
|
17665
17627
|
});
|
|
17666
17628
|
continue;
|
|
17667
17629
|
}
|
|
17668
|
-
if (
|
|
17630
|
+
if (producerRank >= consumerRank) {
|
|
17669
17631
|
issues.push({
|
|
17670
17632
|
check: "signal-binding",
|
|
17671
|
-
|
|
17633
|
+
...issueBase,
|
|
17634
|
+
message: `${where} ${what}, but that node does not run before it, so the ` + `value will never exist. ${orderHint}`
|
|
17672
17635
|
});
|
|
17673
17636
|
continue;
|
|
17674
17637
|
}
|
|
17675
17638
|
if (source.signalKey === null)
|
|
17676
17639
|
continue;
|
|
17677
|
-
const available = declaredSignalKeys(
|
|
17640
|
+
const available = declaredSignalKeys(producerNode, stepsByKey);
|
|
17678
17641
|
if (available === null || available.includes(source.signalKey))
|
|
17679
17642
|
continue;
|
|
17680
17643
|
issues.push({
|
|
17681
17644
|
check: "signal-binding",
|
|
17682
|
-
|
|
17645
|
+
...issueBase,
|
|
17646
|
+
message: `${where} ${what}, but "${source.nodeKey}" declares no such signal. ` + `Signals on "${source.nodeKey}": ${available.length > 0 ? listPaths([...available].sort()) : "(none)"}.`
|
|
17683
17647
|
});
|
|
17684
17648
|
}
|
|
17685
17649
|
});
|
|
@@ -17712,14 +17676,38 @@ function assertValidDefinitionSpecs(specs, options = {}) {
|
|
|
17712
17676
|
}
|
|
17713
17677
|
// src/push/collect-definitions.ts
|
|
17714
17678
|
import { existsSync, readdirSync } from "fs";
|
|
17715
|
-
import { join, resolve } from "path";
|
|
17679
|
+
import { join, relative, resolve } from "path";
|
|
17716
17680
|
import { pathToFileURL } from "url";
|
|
17717
|
-
var
|
|
17681
|
+
var INTERNAL_SCRIPT_NAMES = new Set([
|
|
17682
|
+
"push.ts",
|
|
17683
|
+
"push.mjs",
|
|
17684
|
+
"push.js",
|
|
17685
|
+
".boboddy-studio-collect.mjs"
|
|
17686
|
+
]);
|
|
17718
17687
|
function isStepDefinitionSpec(value) {
|
|
17719
17688
|
if (typeof value !== "object" || value === null)
|
|
17720
17689
|
return false;
|
|
17721
17690
|
const obj = value;
|
|
17722
|
-
return typeof obj["key"] === "string" && typeof obj["name"] === "string" && typeof obj["version"] === "number" && obj["kind"] === "user_defined";
|
|
17691
|
+
return typeof obj["key"] === "string" && typeof obj["name"] === "string" && typeof obj["version"] === "number" && (obj["kind"] === "user_defined" || obj["kind"] === "code");
|
|
17692
|
+
}
|
|
17693
|
+
function resolveCodeStepEntrypoint(spec, mod, absoluteFilePath) {
|
|
17694
|
+
if (spec.kind !== "code")
|
|
17695
|
+
return spec;
|
|
17696
|
+
const fn = spec.entrypoint?.fn;
|
|
17697
|
+
if (!fn) {
|
|
17698
|
+
throw new Error(`Code step "${spec.key}" (kind: "code") has no entrypoint.fn to resolve. ` + `Build it with codeStep({ fn, ... }) from "@boboddy/sdk/definitions/steps".`);
|
|
17699
|
+
}
|
|
17700
|
+
const exportName = Object.entries(mod).find(([, exportedValue]) => exportedValue === fn)?.[0];
|
|
17701
|
+
if (!exportName) {
|
|
17702
|
+
throw new Error(`Code step "${spec.key}"'s fn is not a named export of ${absoluteFilePath}. ` + `codeStep({ fn }) requires fn to be exported by name from the same module ` + `so its entrypoint can be resolved to a portable {sourceFile, exportName} pair.`);
|
|
17703
|
+
}
|
|
17704
|
+
const resolved = { ...spec };
|
|
17705
|
+
delete resolved.entrypoint;
|
|
17706
|
+
resolved.entrypointJson = {
|
|
17707
|
+
sourceFile: relative(process.cwd(), absoluteFilePath),
|
|
17708
|
+
exportName
|
|
17709
|
+
};
|
|
17710
|
+
return resolved;
|
|
17723
17711
|
}
|
|
17724
17712
|
function isPipelineDefinitionSpec(value) {
|
|
17725
17713
|
if (typeof value !== "object" || value === null)
|
|
@@ -17730,31 +17718,48 @@ function isPipelineDefinitionSpec(value) {
|
|
|
17730
17718
|
async function importModule(path) {
|
|
17731
17719
|
return await import(pathToFileURL(path).href);
|
|
17732
17720
|
}
|
|
17721
|
+
function listSourceFiles(absDir) {
|
|
17722
|
+
const allFiles = readdirSync(absDir).filter((file2) => file2.endsWith(".ts") || file2.endsWith(".js"));
|
|
17723
|
+
return allFiles.filter((file2) => !INTERNAL_SCRIPT_NAMES.has(file2) && file2 !== DEFAULT_PIPELINE_ASSIGNMENT_FILENAME);
|
|
17724
|
+
}
|
|
17725
|
+
function fileNameWithoutExtension(file2) {
|
|
17726
|
+
return file2.replace(/\.(ts|js)$/, "");
|
|
17727
|
+
}
|
|
17728
|
+
async function collectFile(absoluteFilePath, pipelines, stepMap) {
|
|
17729
|
+
const mod = await importModule(absoluteFilePath);
|
|
17730
|
+
for (const [exportName, value] of Object.entries(mod)) {
|
|
17731
|
+
if (exportName === "default") {
|
|
17732
|
+
if (isPipelineDefinitionSpec(value))
|
|
17733
|
+
pipelines.push(value);
|
|
17734
|
+
continue;
|
|
17735
|
+
}
|
|
17736
|
+
if (isStepDefinitionSpec(value)) {
|
|
17737
|
+
const resolved = resolveCodeStepEntrypoint(value, mod, absoluteFilePath);
|
|
17738
|
+
stepMap.set(`${resolved.key}@v${String(resolved.version)}`, resolved);
|
|
17739
|
+
}
|
|
17740
|
+
}
|
|
17741
|
+
}
|
|
17742
|
+
function foldEmbeddedSteps(dir, spec, stepMap) {
|
|
17743
|
+
for (const embedded of spec._stepDefinitions ?? []) {
|
|
17744
|
+
const key = `${embedded.key}@v${String(embedded.version)}`;
|
|
17745
|
+
if (stepMap.has(key))
|
|
17746
|
+
continue;
|
|
17747
|
+
if (embedded.kind === "code") {
|
|
17748
|
+
throw new Error(`Code step "${embedded.key}" is only referenced inside a pipeline and is not exported by name from any file in "${dir}". codeStep({ fn }) requires fn to be ` + `a named export so its entrypoint can be resolved \u2014 export the step itself, e.g. ` + `\`export const ${embedded.key} = codeStep({ ... })\`.`);
|
|
17749
|
+
}
|
|
17750
|
+
stepMap.set(key, embedded);
|
|
17751
|
+
}
|
|
17752
|
+
}
|
|
17733
17753
|
async function collectDefinitionsFromDirectory(dir) {
|
|
17734
17754
|
const absDir = resolve(dir);
|
|
17735
|
-
const
|
|
17736
|
-
const sourceFiles = allFiles.filter((file2) => !PUSH_SCRIPT_NAMES.has(file2) && file2 !== DEFAULT_PIPELINE_ASSIGNMENT_FILENAME);
|
|
17755
|
+
const sourceFiles = listSourceFiles(absDir);
|
|
17737
17756
|
const pipelines = [];
|
|
17738
17757
|
const stepMap = new Map;
|
|
17739
17758
|
for (const file2 of sourceFiles) {
|
|
17740
|
-
|
|
17741
|
-
for (const [exportName, value] of Object.entries(mod)) {
|
|
17742
|
-
if (exportName === "default") {
|
|
17743
|
-
if (isPipelineDefinitionSpec(value))
|
|
17744
|
-
pipelines.push(value);
|
|
17745
|
-
continue;
|
|
17746
|
-
}
|
|
17747
|
-
if (isStepDefinitionSpec(value)) {
|
|
17748
|
-
stepMap.set(`${value.key}@v${String(value.version)}`, value);
|
|
17749
|
-
}
|
|
17750
|
-
}
|
|
17759
|
+
await collectFile(join(absDir, file2), pipelines, stepMap);
|
|
17751
17760
|
}
|
|
17752
17761
|
for (const spec of pipelines) {
|
|
17753
|
-
|
|
17754
|
-
const key = `${embedded.key}@v${String(embedded.version)}`;
|
|
17755
|
-
if (!stepMap.has(key))
|
|
17756
|
-
stepMap.set(key, embedded);
|
|
17757
|
-
}
|
|
17762
|
+
foldEmbeddedSteps(dir, spec, stepMap);
|
|
17758
17763
|
}
|
|
17759
17764
|
return {
|
|
17760
17765
|
pipelines,
|
|
@@ -17762,6 +17767,50 @@ async function collectDefinitionsFromDirectory(dir) {
|
|
|
17762
17767
|
defaultPipelineAssignment: await collectDefaultPipelineAssignment(absDir)
|
|
17763
17768
|
};
|
|
17764
17769
|
}
|
|
17770
|
+
async function collectDefinitionsFromDirectoryTolerant(dir) {
|
|
17771
|
+
const absDir = resolve(dir);
|
|
17772
|
+
const sourceFiles = listSourceFiles(absDir);
|
|
17773
|
+
const pipelines = [];
|
|
17774
|
+
const stepMap = new Map;
|
|
17775
|
+
const brokenPipelines = [];
|
|
17776
|
+
for (const file2 of sourceFiles) {
|
|
17777
|
+
try {
|
|
17778
|
+
await collectFile(join(absDir, file2), pipelines, stepMap);
|
|
17779
|
+
} catch (error51) {
|
|
17780
|
+
brokenPipelines.push({
|
|
17781
|
+
key: fileNameWithoutExtension(file2),
|
|
17782
|
+
message: error51 instanceof Error ? error51.message : String(error51)
|
|
17783
|
+
});
|
|
17784
|
+
}
|
|
17785
|
+
}
|
|
17786
|
+
const okPipelines = [];
|
|
17787
|
+
for (const spec of pipelines) {
|
|
17788
|
+
try {
|
|
17789
|
+
foldEmbeddedSteps(dir, spec, stepMap);
|
|
17790
|
+
okPipelines.push(spec);
|
|
17791
|
+
} catch (error51) {
|
|
17792
|
+
brokenPipelines.push({
|
|
17793
|
+
key: spec.key,
|
|
17794
|
+
message: error51 instanceof Error ? error51.message : String(error51)
|
|
17795
|
+
});
|
|
17796
|
+
}
|
|
17797
|
+
}
|
|
17798
|
+
let defaultPipelineAssignment2 = null;
|
|
17799
|
+
try {
|
|
17800
|
+
defaultPipelineAssignment2 = await collectDefaultPipelineAssignment(absDir);
|
|
17801
|
+
} catch (error51) {
|
|
17802
|
+
brokenPipelines.push({
|
|
17803
|
+
key: fileNameWithoutExtension(DEFAULT_PIPELINE_ASSIGNMENT_FILENAME),
|
|
17804
|
+
message: error51 instanceof Error ? error51.message : String(error51)
|
|
17805
|
+
});
|
|
17806
|
+
}
|
|
17807
|
+
return {
|
|
17808
|
+
pipelines: okPipelines,
|
|
17809
|
+
steps: [...stepMap.values()],
|
|
17810
|
+
defaultPipelineAssignment: defaultPipelineAssignment2,
|
|
17811
|
+
brokenPipelines
|
|
17812
|
+
};
|
|
17813
|
+
}
|
|
17765
17814
|
async function collectDefaultPipelineAssignment(absDir) {
|
|
17766
17815
|
const path = join(absDir, DEFAULT_PIPELINE_ASSIGNMENT_FILENAME);
|
|
17767
17816
|
if (!existsSync(path))
|
|
@@ -17784,7 +17833,7 @@ async function pushFromDirectory(dir, opts) {
|
|
|
17784
17833
|
log(`Found ${String(pipelines.length)} pipeline(s) and ${String(steps.length)} step(s).`);
|
|
17785
17834
|
const stepsClient = createStepDefinitionsClient(opts.baseUrl);
|
|
17786
17835
|
const pipelinesClient = createPipelineDefinitionsClient(opts.baseUrl);
|
|
17787
|
-
const serverPipelineKeys = pipelines.length > 0 ? (await pipelinesClient.listByProjectId(opts.projectId, { headers })).map((
|
|
17836
|
+
const serverPipelineKeys = pipelines.length > 0 ? (await pipelinesClient.listByProjectId(opts.projectId, { headers })).map((pipeline) => pipeline.key) : [];
|
|
17788
17837
|
assertValidDefinitionSpecs({ pipelines, steps }, { knownPipelineKeys: serverPipelineKeys });
|
|
17789
17838
|
for (const spec of steps) {
|
|
17790
17839
|
await stepsClient.upsertFromSpec(opts.projectId, spec, { headers });
|
|
@@ -17882,5 +17931,6 @@ async function syncDefaultPipelineAssignment(spec, opts, headers, pipelinesClien
|
|
|
17882
17931
|
}
|
|
17883
17932
|
export {
|
|
17884
17933
|
pushFromDirectory,
|
|
17934
|
+
collectDefinitionsFromDirectoryTolerant,
|
|
17885
17935
|
collectDefinitionsFromDirectory
|
|
17886
17936
|
};
|