@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
|
@@ -14328,10 +14328,19 @@ function any2(conditions, outcome) {
|
|
|
14328
14328
|
return { _tag: "rule", mode: "any", conditions, outcome };
|
|
14329
14329
|
}
|
|
14330
14330
|
function when(signal2, operator, value, outcome) {
|
|
14331
|
+
const condition = {
|
|
14332
|
+
_tag: "signal",
|
|
14333
|
+
signal: signal2,
|
|
14334
|
+
operator,
|
|
14335
|
+
value
|
|
14336
|
+
};
|
|
14337
|
+
if (outcome === undefined) {
|
|
14338
|
+
return condition;
|
|
14339
|
+
}
|
|
14331
14340
|
return {
|
|
14332
14341
|
_tag: "rule",
|
|
14333
14342
|
mode: "all",
|
|
14334
|
-
conditions: [
|
|
14343
|
+
conditions: [condition],
|
|
14335
14344
|
outcome
|
|
14336
14345
|
};
|
|
14337
14346
|
}
|
|
@@ -14507,402 +14516,9 @@ function extractInlineStepSignalsListDefinitions(policy) {
|
|
|
14507
14516
|
return [...byKey.values()];
|
|
14508
14517
|
}
|
|
14509
14518
|
|
|
14510
|
-
// src/definitions/pipelines/chain-graph.ts
|
|
14511
|
-
function tryOrderChainNodeDefinitions(nodeDefinitions, dependencyEdges) {
|
|
14512
|
-
const nodesByKey = new Map;
|
|
14513
|
-
for (const nodeDefinition of nodeDefinitions) {
|
|
14514
|
-
nodesByKey.set(nodeDefinition.nodeKey, nodeDefinition);
|
|
14515
|
-
}
|
|
14516
|
-
const outgoing = new Map;
|
|
14517
|
-
const incomingCount = new Map;
|
|
14518
|
-
for (const nodeDefinition of nodeDefinitions) {
|
|
14519
|
-
incomingCount.set(nodeDefinition.nodeKey, 0);
|
|
14520
|
-
}
|
|
14521
|
-
for (const edge of dependencyEdges) {
|
|
14522
|
-
if (!nodesByKey.has(edge.fromNodeKey) || !nodesByKey.has(edge.toNodeKey)) {
|
|
14523
|
-
return null;
|
|
14524
|
-
}
|
|
14525
|
-
if (outgoing.has(edge.fromNodeKey))
|
|
14526
|
-
return null;
|
|
14527
|
-
outgoing.set(edge.fromNodeKey, edge.toNodeKey);
|
|
14528
|
-
incomingCount.set(edge.toNodeKey, (incomingCount.get(edge.toNodeKey) ?? 0) + 1);
|
|
14529
|
-
}
|
|
14530
|
-
for (const count of incomingCount.values()) {
|
|
14531
|
-
if (count > 1)
|
|
14532
|
-
return null;
|
|
14533
|
-
}
|
|
14534
|
-
if (nodeDefinitions.length === 0)
|
|
14535
|
-
return [];
|
|
14536
|
-
const roots = nodeDefinitions.filter((nodeDefinition) => (incomingCount.get(nodeDefinition.nodeKey) ?? 0) === 0);
|
|
14537
|
-
if (roots.length !== 1)
|
|
14538
|
-
return null;
|
|
14539
|
-
const rootNode = roots[0];
|
|
14540
|
-
if (!rootNode)
|
|
14541
|
-
return null;
|
|
14542
|
-
const ordered = [];
|
|
14543
|
-
const visited = new Set;
|
|
14544
|
-
let currentKey = rootNode.nodeKey;
|
|
14545
|
-
while (currentKey !== undefined) {
|
|
14546
|
-
if (visited.has(currentKey))
|
|
14547
|
-
return null;
|
|
14548
|
-
visited.add(currentKey);
|
|
14549
|
-
const currentNode = nodesByKey.get(currentKey);
|
|
14550
|
-
if (!currentNode)
|
|
14551
|
-
return null;
|
|
14552
|
-
ordered.push(currentNode);
|
|
14553
|
-
currentKey = outgoing.get(currentKey);
|
|
14554
|
-
}
|
|
14555
|
-
if (ordered.length !== nodeDefinitions.length)
|
|
14556
|
-
return null;
|
|
14557
|
-
return ordered;
|
|
14558
|
-
}
|
|
14559
|
-
function buildChainDependencyEdges(orderedNodes) {
|
|
14560
|
-
const dependencyEdges = [];
|
|
14561
|
-
for (let index = 0;index < orderedNodes.length - 1; index += 1) {
|
|
14562
|
-
const from = orderedNodes[index];
|
|
14563
|
-
const to = orderedNodes[index + 1];
|
|
14564
|
-
if (!from || !to)
|
|
14565
|
-
continue;
|
|
14566
|
-
dependencyEdges.push({ fromNodeKey: from.nodeKey, toNodeKey: to.nodeKey });
|
|
14567
|
-
}
|
|
14568
|
-
return dependencyEdges;
|
|
14569
|
-
}
|
|
14570
|
-
|
|
14571
|
-
// src/definitions/pipelines/define-pipeline.ts
|
|
14572
|
-
function serializeBinding(binding) {
|
|
14573
|
-
if (binding.source === "pipeline_input") {
|
|
14574
|
-
return { source: "pipeline_input", path: binding.path };
|
|
14575
|
-
}
|
|
14576
|
-
if (binding.source === "work_item") {
|
|
14577
|
-
return { source: "work_item", field: binding.field };
|
|
14578
|
-
}
|
|
14579
|
-
if (binding.source === "step_signal") {
|
|
14580
|
-
return {
|
|
14581
|
-
source: "step_signal",
|
|
14582
|
-
stepKey: binding.step.key,
|
|
14583
|
-
signalKey: binding.signalKey
|
|
14584
|
-
};
|
|
14585
|
-
}
|
|
14586
|
-
if (binding.source === "literal") {
|
|
14587
|
-
return { source: "literal", value: binding.value };
|
|
14588
|
-
}
|
|
14589
|
-
if (binding.source === "signals_list") {
|
|
14590
|
-
return { source: "signals_list", stepKey: binding.fanOutStep.key };
|
|
14591
|
-
}
|
|
14592
|
-
if (binding.source === "fan_out_item") {
|
|
14593
|
-
return { source: "fan_out_item" };
|
|
14594
|
-
}
|
|
14595
|
-
return { source: "step_output", stepKey: binding.step.key };
|
|
14596
|
-
}
|
|
14597
|
-
var isFanOutConfig = (node) => ("nodeType" in node) && node.nodeType === "fanOut";
|
|
14598
|
-
var isCohortGateConfig = (node) => ("nodeType" in node) && node.nodeType === "cohortGate";
|
|
14599
|
-
function serializeInputBindings(input, pipelineInputBindings) {
|
|
14600
|
-
const autoBindings = {
|
|
14601
|
-
workItemTitle: { source: "work_item", field: "title" },
|
|
14602
|
-
workItemDescription: { source: "work_item", field: "description" }
|
|
14603
|
-
};
|
|
14604
|
-
const pipelineBindings = {};
|
|
14605
|
-
for (const [key, binding] of Object.entries(pipelineInputBindings ?? {})) {
|
|
14606
|
-
pipelineBindings[key] = serializeBinding(binding);
|
|
14607
|
-
}
|
|
14608
|
-
const explicitBindings = Object.fromEntries(Object.entries(input ?? {}).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)]));
|
|
14609
|
-
return { ...autoBindings, ...pipelineBindings, ...explicitBindings };
|
|
14610
|
-
}
|
|
14611
|
-
function buildPipelineSpec(config2) {
|
|
14612
|
-
const nodes = config2.nodes;
|
|
14613
|
-
const stepDefMap = new Map;
|
|
14614
|
-
const registerStepDef = (step) => {
|
|
14615
|
-
const mapKey = `${step.key}@v${String(step.version)}`;
|
|
14616
|
-
if (!stepDefMap.has(mapKey)) {
|
|
14617
|
-
stepDefMap.set(mapKey, step);
|
|
14618
|
-
}
|
|
14619
|
-
};
|
|
14620
|
-
for (const node of nodes) {
|
|
14621
|
-
if (isFanOutConfig(node)) {
|
|
14622
|
-
registerStepDef(node.fanOutStep);
|
|
14623
|
-
} else if (!isCohortGateConfig(node)) {
|
|
14624
|
-
registerStepDef(node.step);
|
|
14625
|
-
}
|
|
14626
|
-
}
|
|
14627
|
-
let inputSchemaJson = null;
|
|
14628
|
-
if (config2.input) {
|
|
14629
|
-
try {
|
|
14630
|
-
inputSchemaJson = exports_external.toJSONSchema(config2.input);
|
|
14631
|
-
} catch {
|
|
14632
|
-
inputSchemaJson = null;
|
|
14633
|
-
}
|
|
14634
|
-
}
|
|
14635
|
-
const nodeDefinitions = nodes.map((node) => {
|
|
14636
|
-
if (isCohortGateConfig(node)) {
|
|
14637
|
-
const serializedPolicy = serializeCohortAdvancementPolicy(node.advanceAll);
|
|
14638
|
-
const inlineStepSignalsListDefinitions = extractInlineStepSignalsListDefinitions(node.advanceAll);
|
|
14639
|
-
return {
|
|
14640
|
-
nodeKey: node.nodeKey,
|
|
14641
|
-
kind: "cohortGate",
|
|
14642
|
-
advanceAllPolicyDefinition: serializedPolicy,
|
|
14643
|
-
stepSignalsListDefinitions: [
|
|
14644
|
-
...inlineStepSignalsListDefinitions,
|
|
14645
|
-
...node.stepSignalsListDefinitions ?? []
|
|
14646
|
-
]
|
|
14647
|
-
};
|
|
14648
|
-
}
|
|
14649
|
-
if (isFanOutConfig(node)) {
|
|
14650
|
-
return {
|
|
14651
|
-
nodeKey: node.fanOutStep.key,
|
|
14652
|
-
kind: "fanOut",
|
|
14653
|
-
stepKey: node.fanOutStep.key,
|
|
14654
|
-
stepName: node.fanOutStep.name,
|
|
14655
|
-
stepDescription: node.fanOutStep.description,
|
|
14656
|
-
inputBindingsJson: serializeInputBindings(node.input, config2.pipelineInputBindings),
|
|
14657
|
-
timeoutSeconds: node.timeout ?? null,
|
|
14658
|
-
overSignalKey: node.overSignalKey,
|
|
14659
|
-
advanceEachPolicyDefinition: serializeCohortAdvancementPolicy(node.advanceEach)
|
|
14660
|
-
};
|
|
14661
|
-
}
|
|
14662
|
-
return {
|
|
14663
|
-
nodeKey: node.step.key,
|
|
14664
|
-
kind: "step",
|
|
14665
|
-
stepKey: node.step.key,
|
|
14666
|
-
stepName: node.step.name,
|
|
14667
|
-
stepDescription: node.step.description,
|
|
14668
|
-
inputBindingsJson: serializeInputBindings(node.input, config2.pipelineInputBindings),
|
|
14669
|
-
timeoutSeconds: node.timeout ?? null,
|
|
14670
|
-
advancementPolicyDefinition: serializeAdvancementPolicy(node.advancement),
|
|
14671
|
-
computedSignalDefinitions: extractInlineComputedSignals(node.advancement)
|
|
14672
|
-
};
|
|
14673
|
-
});
|
|
14674
|
-
const dependencyEdges = buildChainDependencyEdges(nodeDefinitions);
|
|
14675
|
-
return {
|
|
14676
|
-
key: config2.key,
|
|
14677
|
-
name: config2.name,
|
|
14678
|
-
description: config2.description ?? null,
|
|
14679
|
-
version: config2.version ?? 1,
|
|
14680
|
-
status: config2.status ?? "active",
|
|
14681
|
-
inputSchemaJson,
|
|
14682
|
-
_stepDefinitions: [...stepDefMap.values()],
|
|
14683
|
-
nodeDefinitions,
|
|
14684
|
-
dependencyEdges
|
|
14685
|
-
};
|
|
14686
|
-
}
|
|
14687
|
-
// src/definitions/advancement-policies/fluent-rules.ts
|
|
14688
|
-
var LEAF_BRAND = Symbol.for("boboddy.fluentRule.leaf");
|
|
14689
|
-
var GROUP_BRAND = Symbol.for("boboddy.fluentRule.group");
|
|
14690
|
-
var SIGNAL_KEY = Symbol.for("boboddy.fluentRule.signalKey");
|
|
14691
|
-
function createSignalRef(signal2) {
|
|
14692
|
-
const leaf = (operator, value) => {
|
|
14693
|
-
const condition = {
|
|
14694
|
-
_tag: "signal",
|
|
14695
|
-
signal: signal2,
|
|
14696
|
-
operator,
|
|
14697
|
-
value
|
|
14698
|
-
};
|
|
14699
|
-
return {
|
|
14700
|
-
[LEAF_BRAND]: condition,
|
|
14701
|
-
then(outcome) {
|
|
14702
|
-
return {
|
|
14703
|
-
_tag: "rule",
|
|
14704
|
-
mode: "all",
|
|
14705
|
-
conditions: [condition],
|
|
14706
|
-
outcome
|
|
14707
|
-
};
|
|
14708
|
-
}
|
|
14709
|
-
};
|
|
14710
|
-
};
|
|
14711
|
-
return {
|
|
14712
|
-
eq: (v) => leaf("equal", v),
|
|
14713
|
-
ne: (v) => leaf("notEqual", v),
|
|
14714
|
-
gt: (v) => leaf("greaterThan", v),
|
|
14715
|
-
gte: (v) => leaf("greaterThanInclusive", v),
|
|
14716
|
-
lt: (v) => leaf("lessThan", v),
|
|
14717
|
-
lte: (v) => leaf("lessThanInclusive", v),
|
|
14718
|
-
in: (vs) => leaf("in", vs),
|
|
14719
|
-
notIn: (vs) => leaf("notIn", vs),
|
|
14720
|
-
contains: (v) => leaf("contains", v),
|
|
14721
|
-
doesNotContain: (v) => leaf("doesNotContain", v)
|
|
14722
|
-
};
|
|
14723
|
-
}
|
|
14724
|
-
function extractCondition(ref) {
|
|
14725
|
-
if (LEAF_BRAND in ref)
|
|
14726
|
-
return ref[LEAF_BRAND];
|
|
14727
|
-
const group = ref[GROUP_BRAND];
|
|
14728
|
-
return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
|
|
14729
|
-
}
|
|
14730
|
-
function createGroup(mode, refs) {
|
|
14731
|
-
const conditions = refs.map(extractCondition);
|
|
14732
|
-
return {
|
|
14733
|
-
[GROUP_BRAND]: { mode, conditions },
|
|
14734
|
-
then(outcome) {
|
|
14735
|
-
return { _tag: "rule", mode, conditions, outcome };
|
|
14736
|
-
}
|
|
14737
|
-
};
|
|
14738
|
-
}
|
|
14739
|
-
function resolveComputedArg(arg) {
|
|
14740
|
-
return arg[SIGNAL_KEY];
|
|
14741
|
-
}
|
|
14742
|
-
function makeKeyedSignalRef(key) {
|
|
14743
|
-
const ref = createSignalRef(key);
|
|
14744
|
-
return Object.assign(ref, {
|
|
14745
|
-
[SIGNAL_KEY]: key
|
|
14746
|
-
});
|
|
14747
|
-
}
|
|
14748
|
-
function makeAdvanceCtx() {
|
|
14749
|
-
const wrapComputed = (token) => createSignalRef(token);
|
|
14750
|
-
return {
|
|
14751
|
-
signal: (key) => makeKeyedSignalRef(key),
|
|
14752
|
-
stepSignals: new Proxy({}, {
|
|
14753
|
-
get(_, key) {
|
|
14754
|
-
if (typeof key === "string")
|
|
14755
|
-
return makeKeyedSignalRef(key);
|
|
14756
|
-
return;
|
|
14757
|
-
}
|
|
14758
|
-
}),
|
|
14759
|
-
avg: (...args) => wrapComputed(Computed.average(args.map(resolveComputedArg))),
|
|
14760
|
-
weightedAvg: (...args) => wrapComputed(Computed.weightedAverage(args.map(resolveComputedArg))),
|
|
14761
|
-
sum: (...args) => wrapComputed(Computed.sum(args.map(resolveComputedArg))),
|
|
14762
|
-
min: (...args) => wrapComputed(Computed.min(args.map(resolveComputedArg))),
|
|
14763
|
-
max: (...args) => wrapComputed(Computed.max(args.map(resolveComputedArg))),
|
|
14764
|
-
count: (...args) => wrapComputed(Computed.count(args.map(resolveComputedArg))),
|
|
14765
|
-
booleanAny: (...args) => wrapComputed(Computed.booleanAny(args.map(resolveComputedArg))),
|
|
14766
|
-
booleanAll: (...args) => wrapComputed(Computed.booleanAll(args.map(resolveComputedArg))),
|
|
14767
|
-
all: (...refs) => createGroup("all", refs),
|
|
14768
|
-
any: (...refs) => createGroup("any", refs),
|
|
14769
|
-
route: (pipelineKey, inputJson) => inputJson !== undefined ? { outcome: "route", pipelineKey, inputJson } : { outcome: "route", pipelineKey }
|
|
14770
|
-
};
|
|
14771
|
-
}
|
|
14772
|
-
|
|
14773
|
-
// src/definitions/pipelines/input-accessor.ts
|
|
14774
|
-
var ACCESSOR_BRAND = Symbol.for("boboddy.inputAccessor.brand");
|
|
14775
|
-
var ACCESSOR_PATH = Symbol.for("boboddy.inputAccessor.path");
|
|
14776
|
-
function createInputAccessor(_schema) {
|
|
14777
|
-
return createProxy([]);
|
|
14778
|
-
}
|
|
14779
|
-
function createProxy(path) {
|
|
14780
|
-
const pathStr = path.join(".");
|
|
14781
|
-
const target = Object.freeze({});
|
|
14782
|
-
return new Proxy(target, {
|
|
14783
|
-
get(_t, prop) {
|
|
14784
|
-
if (prop === ACCESSOR_BRAND)
|
|
14785
|
-
return true;
|
|
14786
|
-
if (prop === ACCESSOR_PATH)
|
|
14787
|
-
return pathStr;
|
|
14788
|
-
if (prop === "toJSON") {
|
|
14789
|
-
return () => ({
|
|
14790
|
-
source: "pipeline_input",
|
|
14791
|
-
path: pathStr
|
|
14792
|
-
});
|
|
14793
|
-
}
|
|
14794
|
-
if (prop === Symbol.toPrimitive) {
|
|
14795
|
-
return () => {
|
|
14796
|
-
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.`);
|
|
14797
|
-
};
|
|
14798
|
-
}
|
|
14799
|
-
if (typeof prop === "symbol")
|
|
14800
|
-
return;
|
|
14801
|
-
return createProxy([...path, prop]);
|
|
14802
|
-
},
|
|
14803
|
-
has(_t, prop) {
|
|
14804
|
-
return prop === ACCESSOR_BRAND || prop === ACCESSOR_PATH;
|
|
14805
|
-
},
|
|
14806
|
-
ownKeys() {
|
|
14807
|
-
throw new Error(`Pipeline input accessor at path "${pathStr || "<root>"}" cannot be enumerated. ` + `Drill into specific fields instead of spreading the input.`);
|
|
14808
|
-
},
|
|
14809
|
-
set() {
|
|
14810
|
-
throw new Error(`Pipeline input accessor at path "${pathStr || "<root>"}" is read-only.`);
|
|
14811
|
-
}
|
|
14812
|
-
});
|
|
14813
|
-
}
|
|
14814
|
-
function isInputAccessor(value) {
|
|
14815
|
-
return typeof value === "object" && value !== null && value[ACCESSOR_BRAND] === true;
|
|
14816
|
-
}
|
|
14817
|
-
function materializeAccessor(accessor) {
|
|
14818
|
-
return {
|
|
14819
|
-
source: "pipeline_input",
|
|
14820
|
-
path: accessor[ACCESSOR_PATH]
|
|
14821
|
-
};
|
|
14822
|
-
}
|
|
14823
|
-
|
|
14824
|
-
// src/definitions/pipelines/builder-helpers.ts
|
|
14825
|
-
var WORK_ITEM_ACCESSOR = Object.freeze({
|
|
14826
|
-
title: Object.freeze({ source: "work_item", field: "title" }),
|
|
14827
|
-
description: Object.freeze({
|
|
14828
|
-
source: "work_item",
|
|
14829
|
-
field: "description"
|
|
14830
|
-
}),
|
|
14831
|
-
field: (fieldName) => Object.freeze({ source: "work_item", field: `fields.${fieldName}` })
|
|
14832
|
-
});
|
|
14833
|
-
var WORK_ITEM_FIELD_BINDINGS = {
|
|
14834
|
-
workItemTitle: { source: "work_item", field: "title" },
|
|
14835
|
-
workItemDescription: { source: "work_item", field: "description" }
|
|
14836
|
-
};
|
|
14837
|
-
function makeStepInputCtx(inputSchema) {
|
|
14838
|
-
const baseAccessor = createInputAccessor(inputSchema);
|
|
14839
|
-
const input = new Proxy(baseAccessor, {
|
|
14840
|
-
get(target, prop) {
|
|
14841
|
-
if (typeof prop === "string" && prop in WORK_ITEM_FIELD_BINDINGS) {
|
|
14842
|
-
return WORK_ITEM_FIELD_BINDINGS[prop];
|
|
14843
|
-
}
|
|
14844
|
-
return target[prop];
|
|
14845
|
-
}
|
|
14846
|
-
});
|
|
14847
|
-
return {
|
|
14848
|
-
input,
|
|
14849
|
-
signal(step, key) {
|
|
14850
|
-
return { source: "step_signal", step, signalKey: key };
|
|
14851
|
-
},
|
|
14852
|
-
output(step) {
|
|
14853
|
-
return { source: "step_output", step };
|
|
14854
|
-
},
|
|
14855
|
-
literal: literal2,
|
|
14856
|
-
signalsList(fanOutStep) {
|
|
14857
|
-
return { source: "signals_list", fanOutStep };
|
|
14858
|
-
}
|
|
14859
|
-
};
|
|
14860
|
-
}
|
|
14861
|
-
function literal2(value) {
|
|
14862
|
-
return { source: "literal", value };
|
|
14863
|
-
}
|
|
14864
|
-
function normalizeInputMapping(mapping) {
|
|
14865
|
-
if (!mapping)
|
|
14866
|
-
return;
|
|
14867
|
-
const out = {};
|
|
14868
|
-
for (const [key, value] of Object.entries(mapping)) {
|
|
14869
|
-
if (value === undefined)
|
|
14870
|
-
continue;
|
|
14871
|
-
out[key] = isInputAccessor(value) ? materializeAccessor(value) : value;
|
|
14872
|
-
}
|
|
14873
|
-
return out;
|
|
14874
|
-
}
|
|
14875
|
-
function resolveAdditionalStepInputBindings(label, definition) {
|
|
14876
|
-
if (!definition) {
|
|
14877
|
-
return {};
|
|
14878
|
-
}
|
|
14879
|
-
const raw = definition.bindings({
|
|
14880
|
-
workItemField: (fieldName) => ({
|
|
14881
|
-
source: "work_item",
|
|
14882
|
-
field: `fields.${fieldName}`
|
|
14883
|
-
}),
|
|
14884
|
-
literal: literal2
|
|
14885
|
-
});
|
|
14886
|
-
if (definition.schema instanceof exports_external.ZodObject) {
|
|
14887
|
-
const validKeys = new Set(Object.keys(definition.schema.shape));
|
|
14888
|
-
const unknown2 = Object.keys(raw).filter((key) => !validKeys.has(key));
|
|
14889
|
-
if (unknown2.length > 0) {
|
|
14890
|
-
throw new Error(`${label}.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((key) => `"${key}"`).join(", ")}`);
|
|
14891
|
-
}
|
|
14892
|
-
}
|
|
14893
|
-
return normalizeInputMapping(raw) ?? {};
|
|
14894
|
-
}
|
|
14895
|
-
function mergeStepBindings(pipelineBindings, explicitBindings) {
|
|
14896
|
-
const merged = {
|
|
14897
|
-
...pipelineBindings,
|
|
14898
|
-
...explicitBindings ?? {}
|
|
14899
|
-
};
|
|
14900
|
-
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
14901
|
-
}
|
|
14902
|
-
|
|
14903
14519
|
// src/definitions/advancement-policies/cohort-fluent-rules.ts
|
|
14904
|
-
var
|
|
14905
|
-
var
|
|
14520
|
+
var LEAF_BRAND = Symbol("boboddy.cohortRule.leaf");
|
|
14521
|
+
var GROUP_BRAND = Symbol("boboddy.cohortRule.group");
|
|
14906
14522
|
function createCohortSignalRef(signal2) {
|
|
14907
14523
|
const leaf = (operator, value) => {
|
|
14908
14524
|
const condition = {
|
|
@@ -14912,7 +14528,7 @@ function createCohortSignalRef(signal2) {
|
|
|
14912
14528
|
value
|
|
14913
14529
|
};
|
|
14914
14530
|
return {
|
|
14915
|
-
[
|
|
14531
|
+
[LEAF_BRAND]: condition,
|
|
14916
14532
|
then(outcome, paramsJson) {
|
|
14917
14533
|
return {
|
|
14918
14534
|
_tag: "rule",
|
|
@@ -14939,7 +14555,7 @@ function createCohortSignalRef(signal2) {
|
|
|
14939
14555
|
}
|
|
14940
14556
|
function createCohortLeafFromCondition(condition) {
|
|
14941
14557
|
return {
|
|
14942
|
-
[
|
|
14558
|
+
[LEAF_BRAND]: condition,
|
|
14943
14559
|
then(outcome, paramsJson) {
|
|
14944
14560
|
return {
|
|
14945
14561
|
_tag: "rule",
|
|
@@ -14952,15 +14568,15 @@ function createCohortLeafFromCondition(condition) {
|
|
|
14952
14568
|
};
|
|
14953
14569
|
}
|
|
14954
14570
|
function extractCohortCondition(ref) {
|
|
14955
|
-
if (
|
|
14956
|
-
return ref[
|
|
14957
|
-
const group = ref[
|
|
14571
|
+
if (LEAF_BRAND in ref)
|
|
14572
|
+
return ref[LEAF_BRAND];
|
|
14573
|
+
const group = ref[GROUP_BRAND];
|
|
14958
14574
|
return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
|
|
14959
14575
|
}
|
|
14960
14576
|
function createCohortGroup(mode, refs) {
|
|
14961
14577
|
const conditions = refs.map(extractCohortCondition);
|
|
14962
14578
|
return {
|
|
14963
|
-
[
|
|
14579
|
+
[GROUP_BRAND]: { mode, conditions },
|
|
14964
14580
|
then(outcome, paramsJson) {
|
|
14965
14581
|
return {
|
|
14966
14582
|
_tag: "rule",
|
|
@@ -15065,132 +14681,385 @@ function makeAdvanceAllCtx() {
|
|
|
15065
14681
|
};
|
|
15066
14682
|
}
|
|
15067
14683
|
|
|
15068
|
-
// src/definitions/pipelines/
|
|
15069
|
-
function
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
|
|
14684
|
+
// src/definitions/pipelines/bindings.ts
|
|
14685
|
+
function serializeBinding(binding) {
|
|
14686
|
+
if (binding.source === "pipeline_input") {
|
|
14687
|
+
return { source: "pipeline_input", path: binding.path };
|
|
14688
|
+
}
|
|
14689
|
+
if (binding.source === "work_item") {
|
|
14690
|
+
return { source: "work_item", field: binding.field };
|
|
14691
|
+
}
|
|
14692
|
+
if (binding.source === "step_signal") {
|
|
14693
|
+
return {
|
|
14694
|
+
source: "step_signal",
|
|
14695
|
+
stepKey: binding.nodeKey,
|
|
14696
|
+
signalKey: binding.signalKey
|
|
14697
|
+
};
|
|
14698
|
+
}
|
|
14699
|
+
if (binding.source === "literal") {
|
|
14700
|
+
return { source: "literal", value: binding.value };
|
|
14701
|
+
}
|
|
14702
|
+
if (binding.source === "signals_list") {
|
|
14703
|
+
return { source: "signals_list", stepKey: binding.nodeKey };
|
|
14704
|
+
}
|
|
14705
|
+
if (binding.source === "fan_out_item") {
|
|
14706
|
+
return { source: "fan_out_item" };
|
|
14707
|
+
}
|
|
14708
|
+
return { source: "step_output", stepKey: binding.nodeKey };
|
|
14709
|
+
}
|
|
14710
|
+
function serializeInputBindings(input) {
|
|
14711
|
+
const autoBindings = {
|
|
14712
|
+
workItemTitle: { source: "work_item", field: "title" },
|
|
14713
|
+
workItemDescription: { source: "work_item", field: "description" }
|
|
14714
|
+
};
|
|
14715
|
+
const explicitBindings = Object.fromEntries(Object.entries(input).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)]));
|
|
14716
|
+
return { ...autoBindings, ...explicitBindings };
|
|
14717
|
+
}
|
|
14718
|
+
|
|
14719
|
+
// src/definitions/pipelines/work-item-fields.ts
|
|
14720
|
+
var WORK_ITEM_TOP_LEVEL_FIELDS = [
|
|
14721
|
+
"id",
|
|
14722
|
+
"projectId",
|
|
14723
|
+
"platform",
|
|
14724
|
+
"platformId",
|
|
14725
|
+
"platformKey",
|
|
14726
|
+
"url",
|
|
14727
|
+
"title",
|
|
14728
|
+
"description",
|
|
14729
|
+
"sourceCreatedAt",
|
|
14730
|
+
"sourceUpdatedAt",
|
|
14731
|
+
"createdByUserId",
|
|
14732
|
+
"parentWorkItemId",
|
|
14733
|
+
"createdAt",
|
|
14734
|
+
"updatedAt"
|
|
14735
|
+
];
|
|
14736
|
+
var WORK_ITEM_FIELDS_PATH_PREFIX = "fields.";
|
|
14737
|
+
function resolveWorkItemFieldPath(record2, path) {
|
|
14738
|
+
if (typeof record2 !== "object" || record2 === null)
|
|
14739
|
+
return;
|
|
14740
|
+
const asRecord = record2;
|
|
14741
|
+
if (path.startsWith(WORK_ITEM_FIELDS_PATH_PREFIX)) {
|
|
14742
|
+
const fieldName = path.slice(WORK_ITEM_FIELDS_PATH_PREFIX.length);
|
|
14743
|
+
const fields = asRecord["fields"];
|
|
14744
|
+
if (typeof fields !== "object" || fields === null)
|
|
14745
|
+
return;
|
|
14746
|
+
return fields[fieldName];
|
|
14747
|
+
}
|
|
14748
|
+
return asRecord[path];
|
|
14749
|
+
}
|
|
14750
|
+
|
|
14751
|
+
// src/definitions/pipelines/builder-helpers.ts
|
|
14752
|
+
var WORK_ITEM_ACCESSOR = Object.freeze({
|
|
14753
|
+
...Object.fromEntries(WORK_ITEM_TOP_LEVEL_FIELDS.map((field) => [
|
|
14754
|
+
field,
|
|
14755
|
+
Object.freeze({ source: "work_item", field })
|
|
14756
|
+
])),
|
|
14757
|
+
field: (fieldName) => Object.freeze({
|
|
14758
|
+
source: "work_item",
|
|
14759
|
+
field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
|
|
14760
|
+
})
|
|
14761
|
+
});
|
|
14762
|
+
function literal2(value) {
|
|
14763
|
+
return { source: "literal", value };
|
|
14764
|
+
}
|
|
14765
|
+
|
|
14766
|
+
// src/definitions/pipelines/node-input-ctx.ts
|
|
14767
|
+
function makeBaseNodeInputCtx() {
|
|
14768
|
+
return {
|
|
14769
|
+
pipelineInput: (path) => ({ source: "pipeline_input", path }),
|
|
14770
|
+
workItem: WORK_ITEM_ACCESSOR,
|
|
14771
|
+
signal: (nodeKey, signalKey) => ({
|
|
14772
|
+
source: "step_signal",
|
|
14773
|
+
nodeKey,
|
|
14774
|
+
signalKey
|
|
14775
|
+
}),
|
|
14776
|
+
output: (nodeKey) => ({ source: "step_output", nodeKey }),
|
|
14777
|
+
signalsList: (nodeKey) => ({ source: "signals_list", nodeKey }),
|
|
14778
|
+
literal: literal2
|
|
14779
|
+
};
|
|
14780
|
+
}
|
|
14781
|
+
function makeNodeInputCtx() {
|
|
14782
|
+
return makeBaseNodeInputCtx();
|
|
14783
|
+
}
|
|
14784
|
+
function makeFanOutNodeInputCtx() {
|
|
14785
|
+
return {
|
|
14786
|
+
...makeBaseNodeInputCtx(),
|
|
15073
14787
|
item: { source: "fan_out_item" }
|
|
15074
14788
|
};
|
|
15075
|
-
|
|
15076
|
-
|
|
15077
|
-
|
|
15078
|
-
|
|
15079
|
-
|
|
15080
|
-
|
|
15081
|
-
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
const
|
|
15085
|
-
|
|
14789
|
+
}
|
|
14790
|
+
|
|
14791
|
+
// src/definitions/pipelines/compile-node-definitions.ts
|
|
14792
|
+
function assertTargetExists(ctx, fromKey, toKey) {
|
|
14793
|
+
if (!ctx.stateKeys.has(toKey)) {
|
|
14794
|
+
throw new Error(`Pipeline "${ctx.pipelineKey}": state "${fromKey}" targets unknown state "${toKey}"`);
|
|
14795
|
+
}
|
|
14796
|
+
}
|
|
14797
|
+
function overSignalKeyOf(over) {
|
|
14798
|
+
const lastDot = over.lastIndexOf(".");
|
|
14799
|
+
return lastDot === -1 ? over : over.slice(lastDot + 1);
|
|
14800
|
+
}
|
|
14801
|
+
function compileStepState(stateKey, state, ctx) {
|
|
14802
|
+
ctx.registerStep(state.step);
|
|
14803
|
+
const inputCtx = makeNodeInputCtx();
|
|
14804
|
+
const rawInput = state.input ? state.input(inputCtx) : {};
|
|
14805
|
+
const inputBindingsJson = serializeInputBindings(rawInput);
|
|
14806
|
+
const routeTarget = typeof state.next === "object" ? state.next : null;
|
|
14807
|
+
const defaultOutcome = routeTarget ? {
|
|
14808
|
+
outcome: "route",
|
|
14809
|
+
pipelineKey: routeTarget.routeToPipeline,
|
|
14810
|
+
...routeTarget.input ? { inputJson: routeTarget.input } : {}
|
|
14811
|
+
} : "continue";
|
|
14812
|
+
const rules = state.blockWhen ? [{ _tag: "rule", mode: "all", conditions: [state.blockWhen], outcome: "block" }] : [];
|
|
14813
|
+
const policy = { defaultOutcome, rules };
|
|
14814
|
+
const nodeDefinitions = [
|
|
14815
|
+
{
|
|
14816
|
+
nodeKey: stateKey,
|
|
14817
|
+
kind: "step",
|
|
14818
|
+
stepKey: state.step.key,
|
|
14819
|
+
stepName: state.step.name,
|
|
14820
|
+
stepDescription: state.step.description,
|
|
14821
|
+
inputBindingsJson,
|
|
14822
|
+
timeoutSeconds: state.timeout ?? null,
|
|
14823
|
+
advancementPolicyDefinition: serializeAdvancementPolicy(policy),
|
|
14824
|
+
computedSignalDefinitions: extractInlineComputedSignals(policy)
|
|
14825
|
+
}
|
|
14826
|
+
];
|
|
14827
|
+
const edges = [];
|
|
14828
|
+
if (!routeTarget) {
|
|
14829
|
+
const nextKey = state.next;
|
|
14830
|
+
assertTargetExists(ctx, stateKey, nextKey);
|
|
14831
|
+
edges.push({ fromNodeKey: stateKey, toNodeKey: nextKey });
|
|
14832
|
+
}
|
|
14833
|
+
return { nodeDefinitions, edges };
|
|
14834
|
+
}
|
|
14835
|
+
function compileChoiceState(stateKey, state, ctx) {
|
|
14836
|
+
const stateChoices = state.choices ?? [];
|
|
14837
|
+
if (stateChoices.length === 0 && !state.default) {
|
|
14838
|
+
throw new Error(`Pipeline "${ctx.pipelineKey}": choice state "${stateKey}" requires at least one entry in choices or a default target`);
|
|
14839
|
+
}
|
|
14840
|
+
const choices = stateChoices.map((choiceCase) => {
|
|
14841
|
+
assertTargetExists(ctx, stateKey, choiceCase.next);
|
|
14842
|
+
return {
|
|
14843
|
+
conditionJson: serializeCondition(choiceCase.when),
|
|
14844
|
+
targetNodeKey: choiceCase.next
|
|
14845
|
+
};
|
|
14846
|
+
});
|
|
14847
|
+
if (state.default)
|
|
14848
|
+
assertTargetExists(ctx, stateKey, state.default);
|
|
14849
|
+
const edges = stateChoices.map((choiceCase) => ({
|
|
14850
|
+
fromNodeKey: stateKey,
|
|
14851
|
+
toNodeKey: choiceCase.next,
|
|
14852
|
+
discriminantJson: { conditionSummary: serializeCondition(choiceCase.when) }
|
|
14853
|
+
}));
|
|
14854
|
+
if (state.default) {
|
|
14855
|
+
edges.push({
|
|
14856
|
+
fromNodeKey: stateKey,
|
|
14857
|
+
toNodeKey: state.default,
|
|
14858
|
+
discriminantJson: { default: true }
|
|
14859
|
+
});
|
|
14860
|
+
}
|
|
14861
|
+
return {
|
|
14862
|
+
nodeDefinitions: [
|
|
14863
|
+
{ nodeKey: stateKey, kind: "choice", choices, default: state.default ?? null }
|
|
14864
|
+
],
|
|
14865
|
+
edges
|
|
14866
|
+
};
|
|
14867
|
+
}
|
|
14868
|
+
function compileFanOutState(stateKey, state, ctx) {
|
|
14869
|
+
ctx.registerStep(state.step);
|
|
14870
|
+
const inputCtx = makeFanOutNodeInputCtx();
|
|
14871
|
+
const rawInput = state.input ? state.input(inputCtx) : {};
|
|
14872
|
+
const inputBindingsJson = serializeInputBindings(rawInput);
|
|
14873
|
+
const advanceEachResult = state.advanceEach(makeAdvanceEachCtx());
|
|
15086
14874
|
const advanceEachPolicy = {
|
|
15087
14875
|
default: advanceEachResult.default,
|
|
15088
14876
|
...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
|
|
15089
14877
|
};
|
|
15090
|
-
|
|
15091
|
-
const cohortGateNodeConfig = {
|
|
15092
|
-
nodeType: "cohortGate",
|
|
15093
|
-
nodeKey: `${step.key}__cohortGate`
|
|
15094
|
-
};
|
|
15095
|
-
const advanceAllCtx = makeAdvanceAllCtx();
|
|
15096
|
-
const advanceAllResult = config2.advanceAll(advanceAllCtx);
|
|
14878
|
+
const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
|
|
15097
14879
|
const advanceAllPolicy = {
|
|
15098
14880
|
default: advanceAllResult.default,
|
|
15099
14881
|
...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
|
|
15100
14882
|
};
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
}
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
|
|
15123
|
-
|
|
15124
|
-
|
|
15125
|
-
|
|
15126
|
-
|
|
15127
|
-
|
|
15128
|
-
|
|
15129
|
-
|
|
15130
|
-
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
}
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
name: this.meta.name,
|
|
15149
|
-
description: this.meta.description,
|
|
15150
|
-
version: this.meta.version,
|
|
15151
|
-
status: this.meta.status,
|
|
15152
|
-
input: this.inputSchema,
|
|
15153
|
-
nodes: this.nodes,
|
|
15154
|
-
pipelineInputBindings: this.pipelineInputBindings
|
|
14883
|
+
const gateKey = `${stateKey}__cohortGate`;
|
|
14884
|
+
if (ctx.stateKeys.has(gateKey)) {
|
|
14885
|
+
throw new Error(`Pipeline "${ctx.pipelineKey}": synthesized cohortGate key "${gateKey}" collides with an author-declared state \u2014 rename state "${stateKey}"`);
|
|
14886
|
+
}
|
|
14887
|
+
assertTargetExists(ctx, stateKey, state.next);
|
|
14888
|
+
return {
|
|
14889
|
+
nodeDefinitions: [
|
|
14890
|
+
{
|
|
14891
|
+
nodeKey: stateKey,
|
|
14892
|
+
kind: "fanOut",
|
|
14893
|
+
stepKey: state.step.key,
|
|
14894
|
+
stepName: state.step.name,
|
|
14895
|
+
stepDescription: state.step.description,
|
|
14896
|
+
inputBindingsJson,
|
|
14897
|
+
timeoutSeconds: state.timeout ?? null,
|
|
14898
|
+
overSignalKey: overSignalKeyOf(state.over),
|
|
14899
|
+
advanceEachPolicyDefinition: serializeCohortAdvancementPolicy(advanceEachPolicy),
|
|
14900
|
+
maxConcurrency: state.maxConcurrency ?? null
|
|
14901
|
+
},
|
|
14902
|
+
{
|
|
14903
|
+
nodeKey: gateKey,
|
|
14904
|
+
kind: "cohortGate",
|
|
14905
|
+
advanceAllPolicyDefinition: serializeCohortAdvancementPolicy(advanceAllPolicy),
|
|
14906
|
+
stepSignalsListDefinitions: extractInlineStepSignalsListDefinitions(advanceAllPolicy)
|
|
14907
|
+
}
|
|
14908
|
+
],
|
|
14909
|
+
edges: [
|
|
14910
|
+
{ fromNodeKey: stateKey, toNodeKey: gateKey },
|
|
14911
|
+
{ fromNodeKey: gateKey, toNodeKey: state.next }
|
|
14912
|
+
]
|
|
14913
|
+
};
|
|
14914
|
+
}
|
|
14915
|
+
function compileParallelState(stateKey, state, ctx) {
|
|
14916
|
+
const branchEntries = Object.entries(state.branches);
|
|
14917
|
+
if (branchEntries.length === 0) {
|
|
14918
|
+
throw new Error(`Pipeline "${ctx.pipelineKey}": parallel state "${stateKey}" requires at least one branch`);
|
|
14919
|
+
}
|
|
14920
|
+
const branches = {};
|
|
14921
|
+
for (const [branchKey, branchConfig] of branchEntries) {
|
|
14922
|
+
ctx.registerStep(branchConfig.step);
|
|
14923
|
+
const inputCtx = makeNodeInputCtx();
|
|
14924
|
+
const rawInput = branchConfig.input ? branchConfig.input(inputCtx) : {};
|
|
14925
|
+
branches[branchKey] = {
|
|
14926
|
+
stepKey: branchConfig.step.key,
|
|
14927
|
+
stepName: branchConfig.step.name,
|
|
14928
|
+
stepDescription: branchConfig.step.description,
|
|
14929
|
+
inputBindingsJson: serializeInputBindings(rawInput)
|
|
15155
14930
|
};
|
|
15156
|
-
|
|
14931
|
+
}
|
|
14932
|
+
let advanceAllPolicyDefinition;
|
|
14933
|
+
if (state.advanceAll) {
|
|
14934
|
+
const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
|
|
14935
|
+
const advanceAllPolicy = {
|
|
14936
|
+
default: advanceAllResult.default,
|
|
14937
|
+
...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
|
|
14938
|
+
};
|
|
14939
|
+
advanceAllPolicyDefinition = serializeCohortAdvancementPolicy(advanceAllPolicy);
|
|
14940
|
+
}
|
|
14941
|
+
assertTargetExists(ctx, stateKey, state.next);
|
|
14942
|
+
return {
|
|
14943
|
+
nodeDefinitions: [
|
|
14944
|
+
{
|
|
14945
|
+
nodeKey: stateKey,
|
|
14946
|
+
kind: "parallel",
|
|
14947
|
+
branches,
|
|
14948
|
+
...advanceAllPolicyDefinition ? { advanceAllPolicyDefinition } : {}
|
|
14949
|
+
}
|
|
14950
|
+
],
|
|
14951
|
+
edges: [{ fromNodeKey: stateKey, toNodeKey: state.next }]
|
|
14952
|
+
};
|
|
14953
|
+
}
|
|
14954
|
+
function compileLoopState(stateKey, state, ctx) {
|
|
14955
|
+
ctx.registerStep(state.step);
|
|
14956
|
+
const inputCtx = makeNodeInputCtx();
|
|
14957
|
+
const rawInput = state.input ? state.input(inputCtx) : {};
|
|
14958
|
+
assertTargetExists(ctx, stateKey, state.next);
|
|
14959
|
+
assertTargetExists(ctx, stateKey, state.onExhausted);
|
|
14960
|
+
return {
|
|
14961
|
+
nodeDefinitions: [
|
|
14962
|
+
{
|
|
14963
|
+
nodeKey: stateKey,
|
|
14964
|
+
kind: "loop",
|
|
14965
|
+
stepKey: state.step.key,
|
|
14966
|
+
stepName: state.step.name,
|
|
14967
|
+
stepDescription: state.step.description,
|
|
14968
|
+
inputBindingsJson: serializeInputBindings(rawInput),
|
|
14969
|
+
timeoutSeconds: state.timeout ?? null,
|
|
14970
|
+
maxIterations: state.maxIterations,
|
|
14971
|
+
untilConditionJson: serializeCondition(state.until)
|
|
14972
|
+
}
|
|
14973
|
+
],
|
|
14974
|
+
edges: [
|
|
14975
|
+
{ fromNodeKey: stateKey, toNodeKey: state.next, discriminantJson: { loopExit: "next" } },
|
|
14976
|
+
{
|
|
14977
|
+
fromNodeKey: stateKey,
|
|
14978
|
+
toNodeKey: state.onExhausted,
|
|
14979
|
+
discriminantJson: { loopExit: "onExhausted" }
|
|
14980
|
+
}
|
|
14981
|
+
]
|
|
14982
|
+
};
|
|
14983
|
+
}
|
|
14984
|
+
function compileTerminalState(stateKey, kind) {
|
|
14985
|
+
return { nodeDefinitions: [{ nodeKey: stateKey, kind }], edges: [] };
|
|
14986
|
+
}
|
|
14987
|
+
function assertNoIllegalConvergentEdges(pipelineKey, nodeKindByKey, edges) {
|
|
14988
|
+
const incoming = new Map;
|
|
14989
|
+
for (const edge of edges) {
|
|
14990
|
+
const list = incoming.get(edge.toNodeKey) ?? [];
|
|
14991
|
+
list.push(edge);
|
|
14992
|
+
incoming.set(edge.toNodeKey, list);
|
|
14993
|
+
}
|
|
14994
|
+
for (const [targetKey, incomingEdges] of incoming) {
|
|
14995
|
+
if (incomingEdges.length <= 1)
|
|
14996
|
+
continue;
|
|
14997
|
+
const hasInvalidSource = incomingEdges.some((edge) => {
|
|
14998
|
+
const kind = nodeKindByKey.get(edge.fromNodeKey);
|
|
14999
|
+
return kind !== "choice" && kind !== "loop";
|
|
15000
|
+
});
|
|
15001
|
+
if (hasInvalidSource) {
|
|
15002
|
+
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).`);
|
|
15003
|
+
}
|
|
15157
15004
|
}
|
|
15158
15005
|
}
|
|
15159
15006
|
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
|
|
15163
|
-
|
|
15164
|
-
|
|
15165
|
-
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
this.inputSchema = additionalPipelineInput?.schema ?? exports_external.unknown();
|
|
15169
|
-
this.meta = rest;
|
|
15170
|
-
if (additionalPipelineInput) {
|
|
15171
|
-
const raw = additionalPipelineInput.bindings({
|
|
15172
|
-
workItem: WORK_ITEM_ACCESSOR,
|
|
15173
|
-
literal: literal2
|
|
15174
|
-
});
|
|
15175
|
-
if (additionalPipelineInput.schema instanceof exports_external.ZodObject) {
|
|
15176
|
-
const validKeys = new Set(Object.keys(additionalPipelineInput.schema.shape));
|
|
15177
|
-
const unknown2 = Object.keys(raw).filter((k) => !validKeys.has(k));
|
|
15178
|
-
if (unknown2.length > 0) {
|
|
15179
|
-
throw new Error(`additionalPipelineInput.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((k) => `"${k}"`).join(", ")}`);
|
|
15180
|
-
}
|
|
15181
|
-
}
|
|
15182
|
-
this.pipelineInputBindings = normalizeInputMapping(raw) ?? {};
|
|
15183
|
-
} else {
|
|
15184
|
-
this.pipelineInputBindings = {};
|
|
15185
|
-
}
|
|
15186
|
-
this.pipelineStepInputBindings = resolveAdditionalStepInputBindings("additionalStepInput", additionalStepInput);
|
|
15007
|
+
// src/definitions/pipelines/define-pipeline.ts
|
|
15008
|
+
function isWorkingNodeDefinition(node) {
|
|
15009
|
+
return node.kind === "step" || node.kind === "fanOut" || node.kind === "loop";
|
|
15010
|
+
}
|
|
15011
|
+
function definePipeline(config2) {
|
|
15012
|
+
const stateEntries = Object.entries(config2.states);
|
|
15013
|
+
if (stateEntries.length === 0) {
|
|
15014
|
+
throw new Error(`Pipeline "${config2.key}" must declare at least one state`);
|
|
15187
15015
|
}
|
|
15188
|
-
|
|
15189
|
-
|
|
15016
|
+
const startState = config2.states[config2.startAt];
|
|
15017
|
+
if (!startState) {
|
|
15018
|
+
throw new Error(`Pipeline "${config2.key}"'s startAt "${config2.startAt}" does not name a declared state`);
|
|
15190
15019
|
}
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15020
|
+
if (startState.kind === "choice" || startState.kind === "succeed" || startState.kind === "fail") {
|
|
15021
|
+
throw new Error(`Pipeline "${config2.key}"'s startAt "${config2.startAt}" names a '${startState.kind}' state, which cannot be an entry point`);
|
|
15022
|
+
}
|
|
15023
|
+
const stateKeys = new Set(Object.keys(config2.states));
|
|
15024
|
+
const stepDefMap = new Map;
|
|
15025
|
+
const registerStep = (step) => {
|
|
15026
|
+
const mapKey = `${step.key}@v${String(step.version)}`;
|
|
15027
|
+
if (!stepDefMap.has(mapKey))
|
|
15028
|
+
stepDefMap.set(mapKey, step);
|
|
15029
|
+
};
|
|
15030
|
+
const compileContext = {
|
|
15031
|
+
pipelineKey: config2.key,
|
|
15032
|
+
stateKeys,
|
|
15033
|
+
registerStep
|
|
15034
|
+
};
|
|
15035
|
+
const nodeDefinitions = [];
|
|
15036
|
+
const dependencyEdges = [];
|
|
15037
|
+
for (const [stateKey, state] of stateEntries) {
|
|
15038
|
+
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);
|
|
15039
|
+
nodeDefinitions.push(...compiled.nodeDefinitions);
|
|
15040
|
+
dependencyEdges.push(...compiled.edges);
|
|
15041
|
+
}
|
|
15042
|
+
const nodeKindByKey = new Map(nodeDefinitions.map((node) => [node.nodeKey, node.kind]));
|
|
15043
|
+
assertNoIllegalConvergentEdges(config2.key, nodeKindByKey, dependencyEdges);
|
|
15044
|
+
let inputSchemaJson = null;
|
|
15045
|
+
if (config2.input) {
|
|
15046
|
+
try {
|
|
15047
|
+
inputSchemaJson = exports_external.toJSONSchema(config2.input);
|
|
15048
|
+
} catch {
|
|
15049
|
+
inputSchemaJson = null;
|
|
15050
|
+
}
|
|
15051
|
+
}
|
|
15052
|
+
return {
|
|
15053
|
+
key: config2.key,
|
|
15054
|
+
name: config2.name ?? config2.key,
|
|
15055
|
+
description: config2.description ?? null,
|
|
15056
|
+
version: config2.version ?? 1,
|
|
15057
|
+
status: config2.status ?? "active",
|
|
15058
|
+
inputSchemaJson,
|
|
15059
|
+
_stepDefinitions: [...stepDefMap.values()],
|
|
15060
|
+
nodeDefinitions,
|
|
15061
|
+
dependencyEdges
|
|
15062
|
+
};
|
|
15194
15063
|
}
|
|
15195
15064
|
// src/generated/core/bodySerializer.gen.ts
|
|
15196
15065
|
var jsonBodySerializer = {
|
|
@@ -16271,29 +16140,6 @@ class PipelineDefinitions extends HeyApiClient {
|
|
|
16271
16140
|
unarchivePipelineDefinition(options) {
|
|
16272
16141
|
return (options.client ?? this.client).put({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/unarchive", ...options });
|
|
16273
16142
|
}
|
|
16274
|
-
addPipelineStep(options) {
|
|
16275
|
-
return (options.client ?? this.client).post({
|
|
16276
|
-
url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps",
|
|
16277
|
-
...options,
|
|
16278
|
-
headers: {
|
|
16279
|
-
"Content-Type": "application/json",
|
|
16280
|
-
...options.headers
|
|
16281
|
-
}
|
|
16282
|
-
});
|
|
16283
|
-
}
|
|
16284
|
-
removePipelineStep(options) {
|
|
16285
|
-
return (options.client ?? this.client).delete({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}", ...options });
|
|
16286
|
-
}
|
|
16287
|
-
updatePipelineStep(options) {
|
|
16288
|
-
return (options.client ?? this.client).put({
|
|
16289
|
-
url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}",
|
|
16290
|
-
...options,
|
|
16291
|
-
headers: {
|
|
16292
|
-
"Content-Type": "application/json",
|
|
16293
|
-
...options.headers
|
|
16294
|
-
}
|
|
16295
|
-
});
|
|
16296
|
-
}
|
|
16297
16143
|
setPipelineStepAdvancementPolicy(options) {
|
|
16298
16144
|
return (options.client ?? this.client).put({
|
|
16299
16145
|
url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}/advancement-policy",
|
|
@@ -16800,6 +16646,76 @@ function createPipelineDefinitionsClient(baseUrl) {
|
|
|
16800
16646
|
const client2 = createClient({ baseUrl });
|
|
16801
16647
|
return buildPipelineDefinitionsClient(new PipelineDefinitions({ client: client2 }));
|
|
16802
16648
|
}
|
|
16649
|
+
function resolveStepRef(pipelineKey, nodeKey, stepKey, stepDefMap) {
|
|
16650
|
+
const stepDef = stepDefMap.get(stepKey);
|
|
16651
|
+
if (!stepDef) {
|
|
16652
|
+
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.`);
|
|
16653
|
+
}
|
|
16654
|
+
return {
|
|
16655
|
+
stepDefinitionId: stepDef.id,
|
|
16656
|
+
stepDefinitionVersion: stepDef.version
|
|
16657
|
+
};
|
|
16658
|
+
}
|
|
16659
|
+
function buildConfigJson(pipelineKey, node, stepDefMap) {
|
|
16660
|
+
if (node.kind === "choice") {
|
|
16661
|
+
return { choices: node.choices, default: node.default };
|
|
16662
|
+
}
|
|
16663
|
+
if (node.kind === "loop") {
|
|
16664
|
+
return {
|
|
16665
|
+
maxIterations: node.maxIterations,
|
|
16666
|
+
untilConditionJson: node.untilConditionJson
|
|
16667
|
+
};
|
|
16668
|
+
}
|
|
16669
|
+
if (node.kind === "parallel") {
|
|
16670
|
+
const branches = {};
|
|
16671
|
+
for (const [branchKey, branch] of Object.entries(node.branches)) {
|
|
16672
|
+
branches[branchKey] = {
|
|
16673
|
+
...resolveStepRef(pipelineKey, node.nodeKey, branch.stepKey, stepDefMap),
|
|
16674
|
+
inputBindingsJson: branch.inputBindingsJson
|
|
16675
|
+
};
|
|
16676
|
+
}
|
|
16677
|
+
return {
|
|
16678
|
+
branches,
|
|
16679
|
+
...node.advanceAllPolicyDefinition ? { advanceAllPolicyDefinition: node.advanceAllPolicyDefinition } : {}
|
|
16680
|
+
};
|
|
16681
|
+
}
|
|
16682
|
+
if (node.kind === "fanOut") {
|
|
16683
|
+
return {
|
|
16684
|
+
overSignalKey: node.overSignalKey,
|
|
16685
|
+
advanceEachPolicyDefinition: node.advanceEachPolicyDefinition,
|
|
16686
|
+
maxConcurrency: node.maxConcurrency
|
|
16687
|
+
};
|
|
16688
|
+
}
|
|
16689
|
+
if (node.kind === "cohortGate") {
|
|
16690
|
+
return {
|
|
16691
|
+
advanceAllPolicyDefinition: node.advanceAllPolicyDefinition,
|
|
16692
|
+
stepSignalsListDefinitions: node.stepSignalsListDefinitions
|
|
16693
|
+
};
|
|
16694
|
+
}
|
|
16695
|
+
return null;
|
|
16696
|
+
}
|
|
16697
|
+
function buildGraphNodeInput(pipelineKey, node, stepDefMap) {
|
|
16698
|
+
const working = isWorkingNodeDefinition(node) ? node : null;
|
|
16699
|
+
const stepRef = working ? resolveStepRef(pipelineKey, node.nodeKey, working.stepKey, stepDefMap) : null;
|
|
16700
|
+
const policy = node.kind === "step" ? node.advancementPolicyDefinition : undefined;
|
|
16701
|
+
return {
|
|
16702
|
+
key: node.nodeKey,
|
|
16703
|
+
kind: node.kind,
|
|
16704
|
+
name: working?.stepName ?? node.nodeKey,
|
|
16705
|
+
...stepRef ?? {},
|
|
16706
|
+
description: working?.stepDescription ?? null,
|
|
16707
|
+
inputBindingsJson: working ? working.inputBindingsJson : null,
|
|
16708
|
+
timeoutSeconds: working?.timeoutSeconds ?? null,
|
|
16709
|
+
...policy ? {
|
|
16710
|
+
advancementPolicyRulesJson: policy.rulesJson,
|
|
16711
|
+
advancementPolicyDefaultEventType: policy.defaultEventType,
|
|
16712
|
+
advancementPolicyDefaultEventParamsJson: policy.defaultEventParamsJson,
|
|
16713
|
+
advancementPolicyAllowedEventTypes: policy.allowedEventTypes
|
|
16714
|
+
} : {},
|
|
16715
|
+
configJson: buildConfigJson(pipelineKey, node, stepDefMap),
|
|
16716
|
+
computedSignalDefinitions: node.kind === "step" ? node.computedSignalDefinitions : []
|
|
16717
|
+
};
|
|
16718
|
+
}
|
|
16803
16719
|
var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
16804
16720
|
return {
|
|
16805
16721
|
listByProjectId: async (projectId, options) => {
|
|
@@ -16819,37 +16735,12 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
|
16819
16735
|
stepDefMap.set(s.key, s);
|
|
16820
16736
|
}
|
|
16821
16737
|
}
|
|
16822
|
-
const
|
|
16823
|
-
|
|
16824
|
-
|
|
16825
|
-
|
|
16826
|
-
|
|
16827
|
-
|
|
16828
|
-
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.`);
|
|
16829
|
-
}
|
|
16830
|
-
const stepDefinitions = ordered.map((node, index) => {
|
|
16831
|
-
const stepKey = node.stepKey;
|
|
16832
|
-
if (!stepKey) {
|
|
16833
|
-
throw new Error(`Node "${node.nodeKey}" in pipeline "${spec.key}" has no stepKey.`);
|
|
16834
|
-
}
|
|
16835
|
-
const stepDef = stepDefMap.get(stepKey);
|
|
16836
|
-
if (!stepDef) {
|
|
16837
|
-
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.`);
|
|
16838
|
-
}
|
|
16839
|
-
return {
|
|
16840
|
-
stepDefinitionId: stepDef.id,
|
|
16841
|
-
stepDefinitionVersion: stepDef.version,
|
|
16842
|
-
key: node.nodeKey,
|
|
16843
|
-
name: node.stepName ?? "",
|
|
16844
|
-
description: node.stepDescription ?? null,
|
|
16845
|
-
position: index + 1,
|
|
16846
|
-
inputBindingsJson: node.inputBindingsJson ?? {},
|
|
16847
|
-
timeoutSeconds: node.timeoutSeconds ?? null,
|
|
16848
|
-
retryPolicyJson: null,
|
|
16849
|
-
advancementPolicyDefinition: node.advancementPolicyDefinition,
|
|
16850
|
-
computedSignalDefinitions: node.computedSignalDefinitions ?? []
|
|
16851
|
-
};
|
|
16852
|
-
});
|
|
16738
|
+
const nodeDefinitions = spec.nodeDefinitions.map((node) => buildGraphNodeInput(spec.key, node, stepDefMap));
|
|
16739
|
+
const dependencyEdges = spec.dependencyEdges.map((edge) => ({
|
|
16740
|
+
fromNodeKey: edge.fromNodeKey,
|
|
16741
|
+
toNodeKey: edge.toNodeKey,
|
|
16742
|
+
discriminantJson: edge.discriminantJson ?? null
|
|
16743
|
+
}));
|
|
16853
16744
|
const body = {
|
|
16854
16745
|
projectId,
|
|
16855
16746
|
key: spec.key,
|
|
@@ -16857,7 +16748,8 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
|
|
|
16857
16748
|
description: spec.description,
|
|
16858
16749
|
status: spec.status,
|
|
16859
16750
|
inputSchemaJson: spec.inputSchemaJson,
|
|
16860
|
-
|
|
16751
|
+
nodeDefinitions,
|
|
16752
|
+
dependencyEdges
|
|
16861
16753
|
};
|
|
16862
16754
|
const result = await pipelineDefinitions.upsertPipelineDefinition({
|
|
16863
16755
|
body,
|
|
@@ -16892,7 +16784,7 @@ function makeLeaf(fact, path, operator, value) {
|
|
|
16892
16784
|
};
|
|
16893
16785
|
}
|
|
16894
16786
|
function makeFieldRef(fact, path) {
|
|
16895
|
-
|
|
16787
|
+
const untyped = {
|
|
16896
16788
|
eq: (v) => makeLeaf(fact, path, "equal", v),
|
|
16897
16789
|
ne: (v) => makeLeaf(fact, path, "notEqual", v),
|
|
16898
16790
|
gt: (v) => makeLeaf(fact, path, "greaterThan", v),
|
|
@@ -16904,18 +16796,28 @@ function makeFieldRef(fact, path) {
|
|
|
16904
16796
|
contains: (v) => makeLeaf(fact, path, "contains", v),
|
|
16905
16797
|
doesNotContain: (v) => makeLeaf(fact, path, "doesNotContain", v)
|
|
16906
16798
|
};
|
|
16799
|
+
return untyped;
|
|
16907
16800
|
}
|
|
16908
|
-
function
|
|
16909
|
-
|
|
16910
|
-
|
|
16801
|
+
function buildWorkItemAccessor() {
|
|
16802
|
+
const accessor = {
|
|
16803
|
+
field: (name) => makeFieldRef("workItem", `$.${WORK_ITEM_FIELDS_PATH_PREFIX}${name}`)
|
|
16804
|
+
};
|
|
16805
|
+
for (const key of WORK_ITEM_TOP_LEVEL_FIELDS) {
|
|
16806
|
+
accessor[key] = makeFieldRef("workItem", `$.${key}`);
|
|
16911
16807
|
}
|
|
16912
|
-
return
|
|
16808
|
+
return accessor;
|
|
16913
16809
|
}
|
|
16914
|
-
function
|
|
16810
|
+
function makeAssign(pipeline) {
|
|
16811
|
+
if (typeof pipeline !== "object" || typeof pipeline["key"] !== "string" || !Array.isArray(pipeline["nodeDefinitions"])) {
|
|
16812
|
+
throw new Error("assign() requires a pipeline spec produced by definePipeline(). " + "Pass the default-exported value from a pipeline definition file.");
|
|
16813
|
+
}
|
|
16814
|
+
return { _tag: "assign", pipeline };
|
|
16815
|
+
}
|
|
16816
|
+
function extractCondition(ref) {
|
|
16915
16817
|
return ref._condition;
|
|
16916
16818
|
}
|
|
16917
16819
|
function makeGroup(mode, refs) {
|
|
16918
|
-
const conditions = refs.map(
|
|
16820
|
+
const conditions = refs.map(extractCondition);
|
|
16919
16821
|
const condition = { _tag: "group", mode, conditions };
|
|
16920
16822
|
return {
|
|
16921
16823
|
_condition: condition,
|
|
@@ -16926,9 +16828,7 @@ function makeGroup(mode, refs) {
|
|
|
16926
16828
|
}
|
|
16927
16829
|
function buildCtx() {
|
|
16928
16830
|
return {
|
|
16929
|
-
workItem:
|
|
16930
|
-
field: (name) => makeFieldRef("workItem", `$.fields.${name}`)
|
|
16931
|
-
},
|
|
16831
|
+
workItem: buildWorkItemAccessor(),
|
|
16932
16832
|
context: {
|
|
16933
16833
|
isNew: makeFieldRef("context", "$.isNew")
|
|
16934
16834
|
},
|
|
@@ -17007,19 +16907,19 @@ function isDefaultPipelineAssignmentSpec(value) {
|
|
|
17007
16907
|
return value["_tag"] === "default_pipeline_assignment";
|
|
17008
16908
|
}
|
|
17009
16909
|
export {
|
|
16910
|
+
serializeInputBindings,
|
|
17010
16911
|
serializeDefaultPipelineAssignment,
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
16912
|
+
resolveWorkItemFieldPath,
|
|
16913
|
+
makeNodeInputCtx,
|
|
16914
|
+
makeFanOutNodeInputCtx,
|
|
16915
|
+
isWorkingNodeDefinition,
|
|
17015
16916
|
isDefaultPipelineAssignmentSpec,
|
|
16917
|
+
definePipeline,
|
|
17016
16918
|
defaultPipelineAssignment,
|
|
17017
16919
|
createPipelineDefinitionsClient,
|
|
17018
|
-
|
|
17019
|
-
|
|
16920
|
+
WORK_ITEM_TOP_LEVEL_FIELDS,
|
|
16921
|
+
WORK_ITEM_FIELDS_PATH_PREFIX,
|
|
17020
16922
|
Rule,
|
|
17021
|
-
PipelineStepBuilder,
|
|
17022
|
-
PipelineBuilder,
|
|
17023
16923
|
DEFAULT_PIPELINE_ASSIGNMENT_FILENAME,
|
|
17024
16924
|
Computed
|
|
17025
16925
|
};
|