@boboddy/sdk 0.4.2 → 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 +36 -94
- 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-default-pipeline-assignment.d.ts +64 -14
- package/dist/definitions/pipelines/define-pipeline.d.ts +101 -154
- package/dist/definitions/pipelines/index.d.ts +1 -2
- package/dist/definitions/pipelines/index.js +485 -585
- package/dist/definitions/pipelines/node-input-ctx.d.ts +31 -0
- package/dist/definitions/pipelines/pipeline-definitions-client.d.ts +42 -12
- package/dist/definitions/pipelines/pipeline-states.d.ts +104 -0
- package/dist/definitions/pipelines/work-item-fields.d.ts +98 -0
- package/dist/definitions/steps/define-code-step.d.ts +50 -0
- package/dist/definitions/steps/define-step.d.ts +40 -8
- 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 +47 -11
- 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 +469 -875
- package/dist/index.js +525 -585
- package/dist/push/collect-definitions.d.ts +40 -1
- package/dist/push/index.d.ts +2 -2
- package/dist/push/index.js +756 -664
- 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,402 +16372,9 @@ function extractInlineStepSignalsListDefinitions(policy) {
|
|
|
16347
16372
|
return [...byKey.values()];
|
|
16348
16373
|
}
|
|
16349
16374
|
|
|
16350
|
-
// src/definitions/pipelines/chain-graph.ts
|
|
16351
|
-
function tryOrderChainNodeDefinitions(nodeDefinitions, dependencyEdges) {
|
|
16352
|
-
const nodesByKey = new Map;
|
|
16353
|
-
for (const nodeDefinition of nodeDefinitions) {
|
|
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) {
|
|
16532
|
-
const leaf = (operator, value) => {
|
|
16533
|
-
const condition = {
|
|
16534
|
-
_tag: "signal",
|
|
16535
|
-
signal: signal2,
|
|
16536
|
-
operator,
|
|
16537
|
-
value
|
|
16538
|
-
};
|
|
16539
|
-
return {
|
|
16540
|
-
[LEAF_BRAND]: condition,
|
|
16541
|
-
then(outcome) {
|
|
16542
|
-
return {
|
|
16543
|
-
_tag: "rule",
|
|
16544
|
-
mode: "all",
|
|
16545
|
-
conditions: [condition],
|
|
16546
|
-
outcome
|
|
16547
|
-
};
|
|
16548
|
-
}
|
|
16549
|
-
};
|
|
16550
|
-
};
|
|
16551
|
-
return {
|
|
16552
|
-
eq: (v) => leaf("equal", v),
|
|
16553
|
-
ne: (v) => leaf("notEqual", v),
|
|
16554
|
-
gt: (v) => leaf("greaterThan", v),
|
|
16555
|
-
gte: (v) => leaf("greaterThanInclusive", v),
|
|
16556
|
-
lt: (v) => leaf("lessThan", v),
|
|
16557
|
-
lte: (v) => leaf("lessThanInclusive", v),
|
|
16558
|
-
in: (vs) => leaf("in", vs),
|
|
16559
|
-
notIn: (vs) => leaf("notIn", vs),
|
|
16560
|
-
contains: (v) => leaf("contains", v),
|
|
16561
|
-
doesNotContain: (v) => leaf("doesNotContain", v)
|
|
16562
|
-
};
|
|
16563
|
-
}
|
|
16564
|
-
function extractCondition(ref) {
|
|
16565
|
-
if (LEAF_BRAND in ref)
|
|
16566
|
-
return ref[LEAF_BRAND];
|
|
16567
|
-
const group = ref[GROUP_BRAND];
|
|
16568
|
-
return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
|
|
16569
|
-
}
|
|
16570
|
-
function createGroup(mode, refs) {
|
|
16571
|
-
const conditions = refs.map(extractCondition);
|
|
16572
|
-
return {
|
|
16573
|
-
[GROUP_BRAND]: { mode, conditions },
|
|
16574
|
-
then(outcome) {
|
|
16575
|
-
return { _tag: "rule", mode, conditions, outcome };
|
|
16576
|
-
}
|
|
16577
|
-
};
|
|
16578
|
-
}
|
|
16579
|
-
function resolveComputedArg(arg) {
|
|
16580
|
-
return arg[SIGNAL_KEY];
|
|
16581
|
-
}
|
|
16582
|
-
function makeKeyedSignalRef(key) {
|
|
16583
|
-
const ref = createSignalRef(key);
|
|
16584
|
-
return Object.assign(ref, {
|
|
16585
|
-
[SIGNAL_KEY]: key
|
|
16586
|
-
});
|
|
16587
|
-
}
|
|
16588
|
-
function makeAdvanceCtx() {
|
|
16589
|
-
const wrapComputed = (token) => createSignalRef(token);
|
|
16590
|
-
return {
|
|
16591
|
-
signal: (key) => makeKeyedSignalRef(key),
|
|
16592
|
-
stepSignals: new Proxy({}, {
|
|
16593
|
-
get(_, key) {
|
|
16594
|
-
if (typeof key === "string")
|
|
16595
|
-
return makeKeyedSignalRef(key);
|
|
16596
|
-
return;
|
|
16597
|
-
}
|
|
16598
|
-
}),
|
|
16599
|
-
avg: (...args) => wrapComputed(Computed.average(args.map(resolveComputedArg))),
|
|
16600
|
-
weightedAvg: (...args) => wrapComputed(Computed.weightedAverage(args.map(resolveComputedArg))),
|
|
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 }
|
|
16610
|
-
};
|
|
16611
|
-
}
|
|
16612
|
-
|
|
16613
|
-
// src/definitions/pipelines/input-accessor.ts
|
|
16614
|
-
var ACCESSOR_BRAND = Symbol.for("boboddy.inputAccessor.brand");
|
|
16615
|
-
var ACCESSOR_PATH = Symbol.for("boboddy.inputAccessor.path");
|
|
16616
|
-
function createInputAccessor(_schema) {
|
|
16617
|
-
return createProxy([]);
|
|
16618
|
-
}
|
|
16619
|
-
function createProxy(path) {
|
|
16620
|
-
const pathStr = path.join(".");
|
|
16621
|
-
const target = Object.freeze({});
|
|
16622
|
-
return new Proxy(target, {
|
|
16623
|
-
get(_t, prop) {
|
|
16624
|
-
if (prop === ACCESSOR_BRAND)
|
|
16625
|
-
return true;
|
|
16626
|
-
if (prop === ACCESSOR_PATH)
|
|
16627
|
-
return pathStr;
|
|
16628
|
-
if (prop === "toJSON") {
|
|
16629
|
-
return () => ({
|
|
16630
|
-
source: "pipeline_input",
|
|
16631
|
-
path: pathStr
|
|
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
|
-
});
|
|
16653
|
-
}
|
|
16654
|
-
function isInputAccessor(value) {
|
|
16655
|
-
return typeof value === "object" && value !== null && value[ACCESSOR_BRAND] === true;
|
|
16656
|
-
}
|
|
16657
|
-
function materializeAccessor(accessor) {
|
|
16658
|
-
return {
|
|
16659
|
-
source: "pipeline_input",
|
|
16660
|
-
path: accessor[ACCESSOR_PATH]
|
|
16661
|
-
};
|
|
16662
|
-
}
|
|
16663
|
-
|
|
16664
|
-
// src/definitions/pipelines/builder-helpers.ts
|
|
16665
|
-
var WORK_ITEM_ACCESSOR = Object.freeze({
|
|
16666
|
-
title: Object.freeze({ source: "work_item", field: "title" }),
|
|
16667
|
-
description: Object.freeze({
|
|
16668
|
-
source: "work_item",
|
|
16669
|
-
field: "description"
|
|
16670
|
-
}),
|
|
16671
|
-
field: (fieldName) => Object.freeze({ source: "work_item", field: `fields.${fieldName}` })
|
|
16672
|
-
});
|
|
16673
|
-
var WORK_ITEM_FIELD_BINDINGS = {
|
|
16674
|
-
workItemTitle: { source: "work_item", field: "title" },
|
|
16675
|
-
workItemDescription: { source: "work_item", field: "description" }
|
|
16676
|
-
};
|
|
16677
|
-
function makeStepInputCtx(inputSchema) {
|
|
16678
|
-
const baseAccessor = createInputAccessor(inputSchema);
|
|
16679
|
-
const input = new Proxy(baseAccessor, {
|
|
16680
|
-
get(target, prop) {
|
|
16681
|
-
if (typeof prop === "string" && prop in WORK_ITEM_FIELD_BINDINGS) {
|
|
16682
|
-
return WORK_ITEM_FIELD_BINDINGS[prop];
|
|
16683
|
-
}
|
|
16684
|
-
return target[prop];
|
|
16685
|
-
}
|
|
16686
|
-
});
|
|
16687
|
-
return {
|
|
16688
|
-
input,
|
|
16689
|
-
signal(step, key) {
|
|
16690
|
-
return { source: "step_signal", step, signalKey: key };
|
|
16691
|
-
},
|
|
16692
|
-
output(step) {
|
|
16693
|
-
return { source: "step_output", step };
|
|
16694
|
-
},
|
|
16695
|
-
literal: literal2,
|
|
16696
|
-
signalsList(fanOutStep) {
|
|
16697
|
-
return { source: "signals_list", fanOutStep };
|
|
16698
|
-
}
|
|
16699
|
-
};
|
|
16700
|
-
}
|
|
16701
|
-
function literal2(value) {
|
|
16702
|
-
return { source: "literal", value };
|
|
16703
|
-
}
|
|
16704
|
-
function normalizeInputMapping(mapping) {
|
|
16705
|
-
if (!mapping)
|
|
16706
|
-
return;
|
|
16707
|
-
const out = {};
|
|
16708
|
-
for (const [key, value] of Object.entries(mapping)) {
|
|
16709
|
-
if (value === undefined)
|
|
16710
|
-
continue;
|
|
16711
|
-
out[key] = isInputAccessor(value) ? materializeAccessor(value) : value;
|
|
16712
|
-
}
|
|
16713
|
-
return out;
|
|
16714
|
-
}
|
|
16715
|
-
function resolveAdditionalStepInputBindings(label, definition) {
|
|
16716
|
-
if (!definition) {
|
|
16717
|
-
return {};
|
|
16718
|
-
}
|
|
16719
|
-
const raw = definition.bindings({
|
|
16720
|
-
workItemField: (fieldName) => ({
|
|
16721
|
-
source: "work_item",
|
|
16722
|
-
field: `fields.${fieldName}`
|
|
16723
|
-
}),
|
|
16724
|
-
literal: literal2
|
|
16725
|
-
});
|
|
16726
|
-
if (definition.schema instanceof exports_external.ZodObject) {
|
|
16727
|
-
const validKeys = new Set(Object.keys(definition.schema.shape));
|
|
16728
|
-
const unknown2 = Object.keys(raw).filter((key) => !validKeys.has(key));
|
|
16729
|
-
if (unknown2.length > 0) {
|
|
16730
|
-
throw new Error(`${label}.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((key) => `"${key}"`).join(", ")}`);
|
|
16731
|
-
}
|
|
16732
|
-
}
|
|
16733
|
-
return normalizeInputMapping(raw) ?? {};
|
|
16734
|
-
}
|
|
16735
|
-
function mergeStepBindings(pipelineBindings, explicitBindings) {
|
|
16736
|
-
const merged = {
|
|
16737
|
-
...pipelineBindings,
|
|
16738
|
-
...explicitBindings ?? {}
|
|
16739
|
-
};
|
|
16740
|
-
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
16741
|
-
}
|
|
16742
|
-
|
|
16743
16375
|
// src/definitions/advancement-policies/cohort-fluent-rules.ts
|
|
16744
|
-
var
|
|
16745
|
-
var
|
|
16376
|
+
var LEAF_BRAND = Symbol("boboddy.cohortRule.leaf");
|
|
16377
|
+
var GROUP_BRAND = Symbol("boboddy.cohortRule.group");
|
|
16746
16378
|
function createCohortSignalRef(signal2) {
|
|
16747
16379
|
const leaf = (operator, value) => {
|
|
16748
16380
|
const condition = {
|
|
@@ -16752,7 +16384,7 @@ function createCohortSignalRef(signal2) {
|
|
|
16752
16384
|
value
|
|
16753
16385
|
};
|
|
16754
16386
|
return {
|
|
16755
|
-
[
|
|
16387
|
+
[LEAF_BRAND]: condition,
|
|
16756
16388
|
then(outcome, paramsJson) {
|
|
16757
16389
|
return {
|
|
16758
16390
|
_tag: "rule",
|
|
@@ -16779,7 +16411,7 @@ function createCohortSignalRef(signal2) {
|
|
|
16779
16411
|
}
|
|
16780
16412
|
function createCohortLeafFromCondition(condition) {
|
|
16781
16413
|
return {
|
|
16782
|
-
[
|
|
16414
|
+
[LEAF_BRAND]: condition,
|
|
16783
16415
|
then(outcome, paramsJson) {
|
|
16784
16416
|
return {
|
|
16785
16417
|
_tag: "rule",
|
|
@@ -16792,15 +16424,15 @@ function createCohortLeafFromCondition(condition) {
|
|
|
16792
16424
|
};
|
|
16793
16425
|
}
|
|
16794
16426
|
function extractCohortCondition(ref) {
|
|
16795
|
-
if (
|
|
16796
|
-
return ref[
|
|
16797
|
-
const group = ref[
|
|
16427
|
+
if (LEAF_BRAND in ref)
|
|
16428
|
+
return ref[LEAF_BRAND];
|
|
16429
|
+
const group = ref[GROUP_BRAND];
|
|
16798
16430
|
return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
|
|
16799
16431
|
}
|
|
16800
16432
|
function createCohortGroup(mode, refs) {
|
|
16801
16433
|
const conditions = refs.map(extractCohortCondition);
|
|
16802
16434
|
return {
|
|
16803
|
-
[
|
|
16435
|
+
[GROUP_BRAND]: { mode, conditions },
|
|
16804
16436
|
then(outcome, paramsJson) {
|
|
16805
16437
|
return {
|
|
16806
16438
|
_tag: "rule",
|
|
@@ -16905,138 +16537,461 @@ function makeAdvanceAllCtx() {
|
|
|
16905
16537
|
};
|
|
16906
16538
|
}
|
|
16907
16539
|
|
|
16908
|
-
// src/definitions/pipelines/
|
|
16909
|
-
function
|
|
16910
|
-
|
|
16911
|
-
|
|
16912
|
-
|
|
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",
|
|
16578
|
+
"projectId",
|
|
16579
|
+
"platform",
|
|
16580
|
+
"platformId",
|
|
16581
|
+
"platformKey",
|
|
16582
|
+
"url",
|
|
16583
|
+
"title",
|
|
16584
|
+
"description",
|
|
16585
|
+
"sourceCreatedAt",
|
|
16586
|
+
"sourceUpdatedAt",
|
|
16587
|
+
"createdByUserId",
|
|
16588
|
+
"parentWorkItemId",
|
|
16589
|
+
"createdAt",
|
|
16590
|
+
"updatedAt"
|
|
16591
|
+
];
|
|
16592
|
+
var WORK_ITEM_FIELDS_PATH_PREFIX = "fields.";
|
|
16593
|
+
function resolveWorkItemFieldPath(record2, path) {
|
|
16594
|
+
if (typeof record2 !== "object" || record2 === null)
|
|
16595
|
+
return;
|
|
16596
|
+
const asRecord = record2;
|
|
16597
|
+
if (path.startsWith(WORK_ITEM_FIELDS_PATH_PREFIX)) {
|
|
16598
|
+
const fieldName = path.slice(WORK_ITEM_FIELDS_PATH_PREFIX.length);
|
|
16599
|
+
const fields = asRecord["fields"];
|
|
16600
|
+
if (typeof fields !== "object" || fields === null)
|
|
16601
|
+
return;
|
|
16602
|
+
return fields[fieldName];
|
|
16603
|
+
}
|
|
16604
|
+
return asRecord[path];
|
|
16605
|
+
}
|
|
16606
|
+
|
|
16607
|
+
// src/definitions/pipelines/builder-helpers.ts
|
|
16608
|
+
var WORK_ITEM_ACCESSOR = Object.freeze({
|
|
16609
|
+
...Object.fromEntries(WORK_ITEM_TOP_LEVEL_FIELDS.map((field) => [
|
|
16610
|
+
field,
|
|
16611
|
+
Object.freeze({ source: "work_item", field })
|
|
16612
|
+
])),
|
|
16613
|
+
field: (fieldName) => Object.freeze({
|
|
16614
|
+
source: "work_item",
|
|
16615
|
+
field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
|
|
16616
|
+
})
|
|
16617
|
+
});
|
|
16618
|
+
function literal2(value) {
|
|
16619
|
+
return { source: "literal", value };
|
|
16620
|
+
}
|
|
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
|
|
16631
|
+
}),
|
|
16632
|
+
output: (nodeKey) => ({ source: "step_output", nodeKey }),
|
|
16633
|
+
signalsList: (nodeKey) => ({ source: "signals_list", nodeKey }),
|
|
16634
|
+
literal: literal2
|
|
16635
|
+
};
|
|
16636
|
+
}
|
|
16637
|
+
function makeNodeInputCtx() {
|
|
16638
|
+
return makeBaseNodeInputCtx();
|
|
16639
|
+
}
|
|
16640
|
+
function makeFanOutNodeInputCtx() {
|
|
16641
|
+
return {
|
|
16642
|
+
...makeBaseNodeInputCtx(),
|
|
16913
16643
|
item: { source: "fan_out_item" }
|
|
16914
16644
|
};
|
|
16915
|
-
|
|
16916
|
-
|
|
16917
|
-
|
|
16918
|
-
|
|
16919
|
-
|
|
16920
|
-
|
|
16921
|
-
|
|
16922
|
-
|
|
16923
|
-
|
|
16924
|
-
const
|
|
16925
|
-
|
|
16645
|
+
}
|
|
16646
|
+
|
|
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);
|
|
16698
|
+
return {
|
|
16699
|
+
conditionJson: serializeCondition(choiceCase.when),
|
|
16700
|
+
targetNodeKey: choiceCase.next
|
|
16701
|
+
};
|
|
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
|
+
}
|
|
16717
|
+
return {
|
|
16718
|
+
nodeDefinitions: [
|
|
16719
|
+
{ nodeKey: stateKey, kind: "choice", choices, default: state.default ?? null }
|
|
16720
|
+
],
|
|
16721
|
+
edges
|
|
16722
|
+
};
|
|
16723
|
+
}
|
|
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());
|
|
16926
16730
|
const advanceEachPolicy = {
|
|
16927
16731
|
default: advanceEachResult.default,
|
|
16928
16732
|
...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
|
|
16929
16733
|
};
|
|
16930
|
-
|
|
16931
|
-
const cohortGateNodeConfig = {
|
|
16932
|
-
nodeType: "cohortGate",
|
|
16933
|
-
nodeKey: `${step.key}__cohortGate`
|
|
16934
|
-
};
|
|
16935
|
-
const advanceAllCtx = makeAdvanceAllCtx();
|
|
16936
|
-
const advanceAllResult = config2.advanceAll(advanceAllCtx);
|
|
16734
|
+
const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
|
|
16937
16735
|
const advanceAllPolicy = {
|
|
16938
16736
|
default: advanceAllResult.default,
|
|
16939
16737
|
...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
|
|
16940
16738
|
};
|
|
16941
|
-
|
|
16942
|
-
|
|
16943
|
-
|
|
16944
|
-
}
|
|
16945
|
-
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
|
|
16950
|
-
|
|
16951
|
-
|
|
16952
|
-
|
|
16953
|
-
|
|
16954
|
-
|
|
16955
|
-
|
|
16956
|
-
|
|
16957
|
-
|
|
16958
|
-
|
|
16959
|
-
|
|
16960
|
-
|
|
16961
|
-
|
|
16962
|
-
|
|
16963
|
-
|
|
16964
|
-
|
|
16965
|
-
|
|
16966
|
-
|
|
16967
|
-
|
|
16968
|
-
|
|
16969
|
-
|
|
16970
|
-
|
|
16971
|
-
|
|
16972
|
-
|
|
16973
|
-
|
|
16974
|
-
|
|
16975
|
-
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
}
|
|
16979
|
-
|
|
16980
|
-
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
|
|
16988
|
-
|
|
16989
|
-
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
|
|
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);
|
|
16744
|
+
return {
|
|
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 } : {}
|
|
16995
16794
|
};
|
|
16996
|
-
|
|
16795
|
+
advanceAllPolicyDefinition = serializeCohortAdvancementPolicy(advanceAllPolicy);
|
|
16997
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 }]
|
|
16808
|
+
};
|
|
16998
16809
|
}
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17003
|
-
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
const unknown2 = Object.keys(raw).filter((k) => !validKeys.has(k));
|
|
17018
|
-
if (unknown2.length > 0) {
|
|
17019
|
-
throw new Error(`additionalPipelineInput.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((k) => `"${k}"`).join(", ")}`);
|
|
17020
|
-
}
|
|
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);
|
|
16816
|
+
return {
|
|
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)
|
|
17021
16828
|
}
|
|
17022
|
-
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
|
|
17026
|
-
|
|
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
|
+
]
|
|
16838
|
+
};
|
|
16839
|
+
}
|
|
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);
|
|
17027
16849
|
}
|
|
17028
|
-
|
|
17029
|
-
|
|
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
|
+
}
|
|
17030
16860
|
}
|
|
17031
16861
|
}
|
|
17032
|
-
|
|
17033
|
-
|
|
16862
|
+
|
|
16863
|
+
// src/definitions/pipelines/define-pipeline.ts
|
|
16864
|
+
function isWorkingNodeDefinition(node) {
|
|
16865
|
+
return node.kind === "step" || node.kind === "fanOut" || node.kind === "loop";
|
|
16866
|
+
}
|
|
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);
|
|
16885
|
+
};
|
|
16886
|
+
const compileContext = {
|
|
16887
|
+
pipelineKey: config2.key,
|
|
16888
|
+
stateKeys,
|
|
16889
|
+
registerStep
|
|
16890
|
+
};
|
|
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
|
+
}
|
|
16908
|
+
return {
|
|
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
|
|
16918
|
+
};
|
|
17034
16919
|
}
|
|
17035
16920
|
// src/definitions/pipelines/pipeline-definitions-client.ts
|
|
17036
16921
|
function createPipelineDefinitionsClient(baseUrl) {
|
|
17037
16922
|
const client2 = createClient({ baseUrl });
|
|
17038
16923
|
return buildPipelineDefinitionsClient(new PipelineDefinitions({ client: client2 }));
|
|
17039
16924
|
}
|
|
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
|
+
};
|
|
16934
|
+
}
|
|
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
|
|
16943
|
+
};
|
|
16944
|
+
}
|
|
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
|
+
};
|
|
16952
|
+
}
|
|
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
|
+
};
|
|
16964
|
+
}
|
|
16965
|
+
if (node.kind === "cohortGate") {
|
|
16966
|
+
return {
|
|
16967
|
+
advanceAllPolicyDefinition: node.advanceAllPolicyDefinition,
|
|
16968
|
+
stepSignalsListDefinitions: node.stepSignalsListDefinitions
|
|
16969
|
+
};
|
|
16970
|
+
}
|
|
16971
|
+
return null;
|
|
16972
|
+
}
|
|
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
|
+
};
|
|
16994
|
+
}
|
|
17040
16995
|
var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
17041
16996
|
return {
|
|
17042
16997
|
listByProjectId: async (projectId, options) => {
|
|
@@ -17056,37 +17011,12 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
|
17056
17011
|
stepDefMap.set(s.key, s);
|
|
17057
17012
|
}
|
|
17058
17013
|
}
|
|
17059
|
-
const
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
17065
|
-
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.`);
|
|
17066
|
-
}
|
|
17067
|
-
const stepDefinitions = ordered.map((node, index) => {
|
|
17068
|
-
const stepKey = node.stepKey;
|
|
17069
|
-
if (!stepKey) {
|
|
17070
|
-
throw new Error(`Node "${node.nodeKey}" in pipeline "${spec.key}" has no stepKey.`);
|
|
17071
|
-
}
|
|
17072
|
-
const stepDef = stepDefMap.get(stepKey);
|
|
17073
|
-
if (!stepDef) {
|
|
17074
|
-
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.`);
|
|
17075
|
-
}
|
|
17076
|
-
return {
|
|
17077
|
-
stepDefinitionId: stepDef.id,
|
|
17078
|
-
stepDefinitionVersion: stepDef.version,
|
|
17079
|
-
key: node.nodeKey,
|
|
17080
|
-
name: node.stepName ?? "",
|
|
17081
|
-
description: node.stepDescription ?? null,
|
|
17082
|
-
position: index + 1,
|
|
17083
|
-
inputBindingsJson: node.inputBindingsJson ?? {},
|
|
17084
|
-
timeoutSeconds: node.timeoutSeconds ?? null,
|
|
17085
|
-
retryPolicyJson: null,
|
|
17086
|
-
advancementPolicyDefinition: node.advancementPolicyDefinition,
|
|
17087
|
-
computedSignalDefinitions: node.computedSignalDefinitions ?? []
|
|
17088
|
-
};
|
|
17089
|
-
});
|
|
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
|
+
}));
|
|
17090
17020
|
const body = {
|
|
17091
17021
|
projectId,
|
|
17092
17022
|
key: spec.key,
|
|
@@ -17094,7 +17024,8 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
|
17094
17024
|
description: spec.description,
|
|
17095
17025
|
status: spec.status,
|
|
17096
17026
|
inputSchemaJson: spec.inputSchemaJson,
|
|
17097
|
-
|
|
17027
|
+
nodeDefinitions,
|
|
17028
|
+
dependencyEdges
|
|
17098
17029
|
};
|
|
17099
17030
|
const result = await pipelineDefinitions.upsertPipelineDefinition({
|
|
17100
17031
|
body,
|
|
@@ -17129,7 +17060,7 @@ function makeLeaf(fact, path, operator, value) {
|
|
|
17129
17060
|
};
|
|
17130
17061
|
}
|
|
17131
17062
|
function makeFieldRef(fact, path) {
|
|
17132
|
-
|
|
17063
|
+
const untyped = {
|
|
17133
17064
|
eq: (v) => makeLeaf(fact, path, "equal", v),
|
|
17134
17065
|
ne: (v) => makeLeaf(fact, path, "notEqual", v),
|
|
17135
17066
|
gt: (v) => makeLeaf(fact, path, "greaterThan", v),
|
|
@@ -17141,18 +17072,28 @@ function makeFieldRef(fact, path) {
|
|
|
17141
17072
|
contains: (v) => makeLeaf(fact, path, "contains", v),
|
|
17142
17073
|
doesNotContain: (v) => makeLeaf(fact, path, "doesNotContain", v)
|
|
17143
17074
|
};
|
|
17075
|
+
return untyped;
|
|
17076
|
+
}
|
|
17077
|
+
function buildWorkItemAccessor() {
|
|
17078
|
+
const accessor = {
|
|
17079
|
+
field: (name) => makeFieldRef("workItem", `$.${WORK_ITEM_FIELDS_PATH_PREFIX}${name}`)
|
|
17080
|
+
};
|
|
17081
|
+
for (const key of WORK_ITEM_TOP_LEVEL_FIELDS) {
|
|
17082
|
+
accessor[key] = makeFieldRef("workItem", `$.${key}`);
|
|
17083
|
+
}
|
|
17084
|
+
return accessor;
|
|
17144
17085
|
}
|
|
17145
|
-
function makeAssign(
|
|
17146
|
-
if (typeof
|
|
17147
|
-
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.");
|
|
17148
17089
|
}
|
|
17149
|
-
return { _tag: "assign", pipeline
|
|
17090
|
+
return { _tag: "assign", pipeline };
|
|
17150
17091
|
}
|
|
17151
|
-
function
|
|
17092
|
+
function extractCondition(ref) {
|
|
17152
17093
|
return ref._condition;
|
|
17153
17094
|
}
|
|
17154
17095
|
function makeGroup(mode, refs) {
|
|
17155
|
-
const conditions = refs.map(
|
|
17096
|
+
const conditions = refs.map(extractCondition);
|
|
17156
17097
|
const condition = { _tag: "group", mode, conditions };
|
|
17157
17098
|
return {
|
|
17158
17099
|
_condition: condition,
|
|
@@ -17163,9 +17104,7 @@ function makeGroup(mode, refs) {
|
|
|
17163
17104
|
}
|
|
17164
17105
|
function buildCtx() {
|
|
17165
17106
|
return {
|
|
17166
|
-
workItem:
|
|
17167
|
-
field: (name) => makeFieldRef("workItem", `$.fields.${name}`)
|
|
17168
|
-
},
|
|
17107
|
+
workItem: buildWorkItemAccessor(),
|
|
17169
17108
|
context: {
|
|
17170
17109
|
isNew: makeFieldRef("context", "$.isNew")
|
|
17171
17110
|
},
|
|
@@ -17466,6 +17405,58 @@ function resolveSourcePath(schema, sourcePath) {
|
|
|
17466
17405
|
}
|
|
17467
17406
|
return { kind: "resolved" };
|
|
17468
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
|
+
|
|
17469
17460
|
// src/definitions/validation/validate-definition-specs.ts
|
|
17470
17461
|
function listPaths(paths, limit = 24) {
|
|
17471
17462
|
if (paths.length === 0)
|
|
@@ -17528,9 +17519,10 @@ function checkHealthChecks(steps) {
|
|
|
17528
17519
|
}
|
|
17529
17520
|
return issues;
|
|
17530
17521
|
}
|
|
17531
|
-
function routeTargets(
|
|
17532
|
-
if (!
|
|
17522
|
+
function routeTargets(node) {
|
|
17523
|
+
if (node.kind !== "step" || !node.advancementPolicyDefinition)
|
|
17533
17524
|
return [];
|
|
17525
|
+
const policy = node.advancementPolicyDefinition;
|
|
17534
17526
|
const keys = [];
|
|
17535
17527
|
if (policy.defaultEventType === "route" && typeof policy.defaultEventParamsJson?.["pipelineKey"] === "string") {
|
|
17536
17528
|
keys.push(policy.defaultEventParamsJson["pipelineKey"]);
|
|
@@ -17545,17 +17537,20 @@ function routeTargets(policy) {
|
|
|
17545
17537
|
function checkRouteTargets(pipelines, knownPipelineKeys) {
|
|
17546
17538
|
const issues = [];
|
|
17547
17539
|
const known = new Set([
|
|
17548
|
-
...pipelines.map((
|
|
17540
|
+
...pipelines.map((pipeline) => pipeline.key),
|
|
17549
17541
|
...knownPipelineKeys
|
|
17550
17542
|
]);
|
|
17551
|
-
for (const
|
|
17552
|
-
for (const node of
|
|
17553
|
-
|
|
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)) {
|
|
17554
17547
|
if (known.has(target))
|
|
17555
17548
|
continue;
|
|
17556
17549
|
issues.push({
|
|
17557
17550
|
check: "route-target",
|
|
17558
|
-
|
|
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.`
|
|
17559
17554
|
});
|
|
17560
17555
|
}
|
|
17561
17556
|
}
|
|
@@ -17564,7 +17559,7 @@ function checkRouteTargets(pipelines, knownPipelineKeys) {
|
|
|
17564
17559
|
}
|
|
17565
17560
|
function executionRanks(nodeDefinitions, dependencyEdges) {
|
|
17566
17561
|
const indexes = nodeDefinitions.map((_, index) => index);
|
|
17567
|
-
const orderedNodes =
|
|
17562
|
+
const orderedNodes = tryOrderNodeDefinitionsByTopoRank(nodeDefinitions, dependencyEdges);
|
|
17568
17563
|
if (orderedNodes === null) {
|
|
17569
17564
|
return new Map(indexes.map((index, rank) => [index, rank]));
|
|
17570
17565
|
}
|
|
@@ -17574,70 +17569,81 @@ function executionRanks(nodeDefinitions, dependencyEdges) {
|
|
|
17574
17569
|
}
|
|
17575
17570
|
function bindingSource(binding) {
|
|
17576
17571
|
if (binding.source === "step_signal") {
|
|
17577
|
-
return {
|
|
17572
|
+
return { nodeKey: binding.stepKey, signalKey: binding.signalKey, kind: "signal" };
|
|
17578
17573
|
}
|
|
17579
17574
|
if (binding.source === "step_output") {
|
|
17580
|
-
return {
|
|
17575
|
+
return { nodeKey: binding.stepKey, signalKey: null, kind: "output" };
|
|
17581
17576
|
}
|
|
17582
17577
|
if (binding.source === "signals_list") {
|
|
17583
|
-
return {
|
|
17578
|
+
return { nodeKey: binding.stepKey, signalKey: null, kind: "signals_list" };
|
|
17584
17579
|
}
|
|
17585
17580
|
return null;
|
|
17586
17581
|
}
|
|
17587
|
-
function declaredSignalKeys(
|
|
17588
|
-
const
|
|
17589
|
-
|
|
17590
|
-
|
|
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 : []) ?? [];
|
|
17591
17586
|
const keys = new Set;
|
|
17592
|
-
for (const spec of specs) {
|
|
17587
|
+
for (const spec of specs ?? []) {
|
|
17593
17588
|
for (const signal2 of spec.signalExtractorDefinitions)
|
|
17594
17589
|
keys.add(signal2.key);
|
|
17595
17590
|
}
|
|
17596
|
-
for (const
|
|
17597
|
-
|
|
17598
|
-
|
|
17599
|
-
|
|
17600
|
-
|
|
17591
|
+
for (const computed of computedSignalDefinitions) {
|
|
17592
|
+
keys.add(computed.key);
|
|
17593
|
+
}
|
|
17594
|
+
if (!specs && computedSignalDefinitions.length === 0) {
|
|
17595
|
+
return null;
|
|
17601
17596
|
}
|
|
17602
17597
|
return [...keys];
|
|
17603
17598
|
}
|
|
17604
17599
|
function checkSignalBindings(pipelines, stepsByKey) {
|
|
17605
17600
|
const issues = [];
|
|
17606
|
-
for (const
|
|
17607
|
-
const ranks = executionRanks(
|
|
17608
|
-
const order = [...
|
|
17609
|
-
const orderHint = `
|
|
17610
|
-
|
|
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) => {
|
|
17611
17607
|
const consumerRank = ranks.get(index) ?? index;
|
|
17612
|
-
const where = `Pipeline "${
|
|
17613
|
-
|
|
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)) {
|
|
17614
17611
|
const source = bindingSource(binding);
|
|
17615
17612
|
if (!source)
|
|
17616
17613
|
continue;
|
|
17617
|
-
const what = source.kind === "signal" ? `binds input "${field}" to signal "${source.signalKey ?? ""}" of
|
|
17618
|
-
const
|
|
17619
|
-
|
|
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) {
|
|
17620
17623
|
issues.push({
|
|
17621
17624
|
check: "signal-binding",
|
|
17622
|
-
|
|
17625
|
+
...issueBase,
|
|
17626
|
+
message: `${where} ${what}, but no node with that key is in the pipeline. ${orderHint}`
|
|
17623
17627
|
});
|
|
17624
17628
|
continue;
|
|
17625
17629
|
}
|
|
17626
|
-
if (
|
|
17630
|
+
if (producerRank >= consumerRank) {
|
|
17627
17631
|
issues.push({
|
|
17628
17632
|
check: "signal-binding",
|
|
17629
|
-
|
|
17633
|
+
...issueBase,
|
|
17634
|
+
message: `${where} ${what}, but that node does not run before it, so the ` + `value will never exist. ${orderHint}`
|
|
17630
17635
|
});
|
|
17631
17636
|
continue;
|
|
17632
17637
|
}
|
|
17633
17638
|
if (source.signalKey === null)
|
|
17634
17639
|
continue;
|
|
17635
|
-
const available = declaredSignalKeys(
|
|
17640
|
+
const available = declaredSignalKeys(producerNode, stepsByKey);
|
|
17636
17641
|
if (available === null || available.includes(source.signalKey))
|
|
17637
17642
|
continue;
|
|
17638
17643
|
issues.push({
|
|
17639
17644
|
check: "signal-binding",
|
|
17640
|
-
|
|
17645
|
+
...issueBase,
|
|
17646
|
+
message: `${where} ${what}, but "${source.nodeKey}" declares no such signal. ` + `Signals on "${source.nodeKey}": ${available.length > 0 ? listPaths([...available].sort()) : "(none)"}.`
|
|
17641
17647
|
});
|
|
17642
17648
|
}
|
|
17643
17649
|
});
|
|
@@ -17670,14 +17676,38 @@ function assertValidDefinitionSpecs(specs, options = {}) {
|
|
|
17670
17676
|
}
|
|
17671
17677
|
// src/push/collect-definitions.ts
|
|
17672
17678
|
import { existsSync, readdirSync } from "fs";
|
|
17673
|
-
import { join, resolve } from "path";
|
|
17679
|
+
import { join, relative, resolve } from "path";
|
|
17674
17680
|
import { pathToFileURL } from "url";
|
|
17675
|
-
var
|
|
17681
|
+
var INTERNAL_SCRIPT_NAMES = new Set([
|
|
17682
|
+
"push.ts",
|
|
17683
|
+
"push.mjs",
|
|
17684
|
+
"push.js",
|
|
17685
|
+
".boboddy-studio-collect.mjs"
|
|
17686
|
+
]);
|
|
17676
17687
|
function isStepDefinitionSpec(value) {
|
|
17677
17688
|
if (typeof value !== "object" || value === null)
|
|
17678
17689
|
return false;
|
|
17679
17690
|
const obj = value;
|
|
17680
|
-
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;
|
|
17681
17711
|
}
|
|
17682
17712
|
function isPipelineDefinitionSpec(value) {
|
|
17683
17713
|
if (typeof value !== "object" || value === null)
|
|
@@ -17688,31 +17718,48 @@ function isPipelineDefinitionSpec(value) {
|
|
|
17688
17718
|
async function importModule(path) {
|
|
17689
17719
|
return await import(pathToFileURL(path).href);
|
|
17690
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
|
+
}
|
|
17691
17753
|
async function collectDefinitionsFromDirectory(dir) {
|
|
17692
17754
|
const absDir = resolve(dir);
|
|
17693
|
-
const
|
|
17694
|
-
const sourceFiles = allFiles.filter((file2) => !PUSH_SCRIPT_NAMES.has(file2) && file2 !== DEFAULT_PIPELINE_ASSIGNMENT_FILENAME);
|
|
17755
|
+
const sourceFiles = listSourceFiles(absDir);
|
|
17695
17756
|
const pipelines = [];
|
|
17696
17757
|
const stepMap = new Map;
|
|
17697
17758
|
for (const file2 of sourceFiles) {
|
|
17698
|
-
|
|
17699
|
-
for (const [exportName, value] of Object.entries(mod)) {
|
|
17700
|
-
if (exportName === "default") {
|
|
17701
|
-
if (isPipelineDefinitionSpec(value))
|
|
17702
|
-
pipelines.push(value);
|
|
17703
|
-
continue;
|
|
17704
|
-
}
|
|
17705
|
-
if (isStepDefinitionSpec(value)) {
|
|
17706
|
-
stepMap.set(`${value.key}@v${String(value.version)}`, value);
|
|
17707
|
-
}
|
|
17708
|
-
}
|
|
17759
|
+
await collectFile(join(absDir, file2), pipelines, stepMap);
|
|
17709
17760
|
}
|
|
17710
17761
|
for (const spec of pipelines) {
|
|
17711
|
-
|
|
17712
|
-
const key = `${embedded.key}@v${String(embedded.version)}`;
|
|
17713
|
-
if (!stepMap.has(key))
|
|
17714
|
-
stepMap.set(key, embedded);
|
|
17715
|
-
}
|
|
17762
|
+
foldEmbeddedSteps(dir, spec, stepMap);
|
|
17716
17763
|
}
|
|
17717
17764
|
return {
|
|
17718
17765
|
pipelines,
|
|
@@ -17720,6 +17767,50 @@ async function collectDefinitionsFromDirectory(dir) {
|
|
|
17720
17767
|
defaultPipelineAssignment: await collectDefaultPipelineAssignment(absDir)
|
|
17721
17768
|
};
|
|
17722
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
|
+
}
|
|
17723
17814
|
async function collectDefaultPipelineAssignment(absDir) {
|
|
17724
17815
|
const path = join(absDir, DEFAULT_PIPELINE_ASSIGNMENT_FILENAME);
|
|
17725
17816
|
if (!existsSync(path))
|
|
@@ -17742,7 +17833,7 @@ async function pushFromDirectory(dir, opts) {
|
|
|
17742
17833
|
log(`Found ${String(pipelines.length)} pipeline(s) and ${String(steps.length)} step(s).`);
|
|
17743
17834
|
const stepsClient = createStepDefinitionsClient(opts.baseUrl);
|
|
17744
17835
|
const pipelinesClient = createPipelineDefinitionsClient(opts.baseUrl);
|
|
17745
|
-
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) : [];
|
|
17746
17837
|
assertValidDefinitionSpecs({ pipelines, steps }, { knownPipelineKeys: serverPipelineKeys });
|
|
17747
17838
|
for (const spec of steps) {
|
|
17748
17839
|
await stepsClient.upsertFromSpec(opts.projectId, spec, { headers });
|
|
@@ -17840,5 +17931,6 @@ async function syncDefaultPipelineAssignment(spec, opts, headers, pipelinesClien
|
|
|
17840
17931
|
}
|
|
17841
17932
|
export {
|
|
17842
17933
|
pushFromDirectory,
|
|
17934
|
+
collectDefinitionsFromDirectoryTolerant,
|
|
17843
17935
|
collectDefinitionsFromDirectory
|
|
17844
17936
|
};
|