@boboddy/sdk 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/client.js +0 -23
  2. package/dist/definitions/advancement-policies/define-advancement-policy.d.ts +30 -5
  3. package/dist/definitions/advancement-policies/index.js +11 -1
  4. package/dist/definitions/pipelines/bindings.d.ts +62 -0
  5. package/dist/definitions/pipelines/builder-helpers.d.ts +19 -106
  6. package/dist/definitions/pipelines/chain-graph.d.ts +15 -14
  7. package/dist/definitions/pipelines/compile-node-definitions.d.ts +27 -0
  8. package/dist/definitions/pipelines/define-pipeline.d.ts +101 -154
  9. package/dist/definitions/pipelines/index.d.ts +0 -2
  10. package/dist/definitions/pipelines/index.js +470 -615
  11. package/dist/definitions/pipelines/node-input-ctx.d.ts +31 -0
  12. package/dist/definitions/pipelines/pipeline-definitions-client.d.ts +18 -4
  13. package/dist/definitions/pipelines/pipeline-states.d.ts +104 -0
  14. package/dist/definitions/steps/define-code-step.d.ts +50 -0
  15. package/dist/definitions/steps/define-step.d.ts +22 -1
  16. package/dist/definitions/steps/index.d.ts +1 -0
  17. package/dist/definitions/steps/index.js +41 -24
  18. package/dist/definitions/steps/step-definitions-client.d.ts +29 -5
  19. package/dist/definitions/validation/index.js +15148 -94
  20. package/dist/definitions/validation/validate-definition-specs.d.ts +19 -1
  21. package/dist/generated/index.d.ts +1 -1
  22. package/dist/generated/sdk.gen.d.ts +1 -4
  23. package/dist/generated/types.gen.d.ts +296 -878
  24. package/dist/index.js +510 -615
  25. package/dist/push/collect-definitions.d.ts +40 -1
  26. package/dist/push/index.d.ts +2 -2
  27. package/dist/push/index.js +788 -738
  28. package/dist/step-execution-plane-client.d.ts +9 -4
  29. package/package.json +2 -2
  30. package/dist/definitions/pipelines/builder.d.ts +0 -80
  31. package/dist/definitions/pipelines/fan-out-builder.d.ts +0 -66
  32. package/dist/definitions/pipelines/input-accessor.d.ts +0 -25
@@ -30,7 +30,7 @@ __export(exports_core2, {
30
30
  safeDecode: () => safeDecode,
31
31
  registry: () => registry,
32
32
  regexes: () => exports_regexes,
33
- process: () => 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 process(schema, ctx, _params = { path: [], schemaPath: [] }) {
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
- process(parent, ctx, params);
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
- process(schema, ctx);
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
- process(schema, ctx);
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 = process(def.element, ctx, {
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] = process(shape[key], ctx, {
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 = process(def.catchall, ctx, {
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) => process(x, ctx, {
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 = process(def.left, ctx, {
11643
+ const a = process2(def.left, ctx, {
11644
11644
  ...params,
11645
11645
  path: [...params.path, "allOf", 0]
11646
11646
  });
11647
- const b = process(def.right, ctx, {
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) => process(x, ctx, {
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 ? process(def.rest, ctx, {
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 = process(def.valueType, ctx, {
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 = process(def.keyType, ctx, {
11718
+ json.propertyNames = process2(def.keyType, ctx, {
11719
11719
  ...params,
11720
11720
  path: [...params.path, "propertyNames"]
11721
11721
  });
11722
11722
  }
11723
- json.additionalProperties = process(def.valueType, ctx, {
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 = process(def.innerType, ctx, params);
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
- process(def.innerType, ctx, params);
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
- process(def.innerType, ctx, params);
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
- process(def.innerType, ctx, params);
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
- process(def.innerType, ctx, params);
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
- process(innerType, ctx, params);
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
- process(def.innerType, ctx, params);
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
- process(def.innerType, ctx, params);
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
- process(def.innerType, ctx, params);
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
- process(innerType, ctx, params);
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
- process(schema, ctx2);
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
- process(input, ctx);
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 process(schema, this.ctx, _params);
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
- ...spec,
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: [{ _tag: "signal", signal: signal2, operator, value }],
16199
+ conditions: [condition],
16175
16200
  outcome
16176
16201
  };
16177
16202
  }
@@ -16347,188 +16372,10 @@ function extractInlineStepSignalsListDefinitions(policy) {
16347
16372
  return [...byKey.values()];
16348
16373
  }
16349
16374
 
16350
- // src/definitions/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) {
16375
+ // src/definitions/advancement-policies/cohort-fluent-rules.ts
16376
+ var LEAF_BRAND = Symbol("boboddy.cohortRule.leaf");
16377
+ var GROUP_BRAND = Symbol("boboddy.cohortRule.group");
16378
+ function createCohortSignalRef(signal2) {
16532
16379
  const leaf = (operator, value) => {
16533
16380
  const condition = {
16534
16381
  _tag: "signal",
@@ -16538,12 +16385,13 @@ function createSignalRef(signal2) {
16538
16385
  };
16539
16386
  return {
16540
16387
  [LEAF_BRAND]: condition,
16541
- then(outcome) {
16388
+ then(outcome, paramsJson) {
16542
16389
  return {
16543
16390
  _tag: "rule",
16544
16391
  mode: "all",
16545
16392
  conditions: [condition],
16546
- outcome
16393
+ outcome,
16394
+ ...paramsJson ? { outcomeJson: paramsJson } : {}
16547
16395
  };
16548
16396
  }
16549
16397
  };
@@ -16561,109 +16409,172 @@ function createSignalRef(signal2) {
16561
16409
  doesNotContain: (v) => leaf("doesNotContain", v)
16562
16410
  };
16563
16411
  }
16564
- function extractCondition(ref) {
16412
+ function createCohortLeafFromCondition(condition) {
16413
+ return {
16414
+ [LEAF_BRAND]: condition,
16415
+ then(outcome, paramsJson) {
16416
+ return {
16417
+ _tag: "rule",
16418
+ mode: "all",
16419
+ conditions: [condition],
16420
+ outcome,
16421
+ ...paramsJson ? { outcomeJson: paramsJson } : {}
16422
+ };
16423
+ }
16424
+ };
16425
+ }
16426
+ function extractCohortCondition(ref) {
16565
16427
  if (LEAF_BRAND in ref)
16566
16428
  return ref[LEAF_BRAND];
16567
16429
  const group = ref[GROUP_BRAND];
16568
16430
  return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
16569
16431
  }
16570
- function createGroup(mode, refs) {
16571
- const conditions = refs.map(extractCondition);
16432
+ function createCohortGroup(mode, refs) {
16433
+ const conditions = refs.map(extractCohortCondition);
16572
16434
  return {
16573
16435
  [GROUP_BRAND]: { mode, conditions },
16574
- then(outcome) {
16575
- return { _tag: "rule", mode, conditions, outcome };
16436
+ then(outcome, paramsJson) {
16437
+ return {
16438
+ _tag: "rule",
16439
+ mode,
16440
+ conditions,
16441
+ outcome,
16442
+ ...paramsJson ? { outcomeJson: paramsJson } : {}
16443
+ };
16576
16444
  }
16577
16445
  };
16578
16446
  }
16579
- function 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
- });
16447
+ function makeKeyedCohortSignalRef(key) {
16448
+ return createCohortSignalRef(key);
16587
16449
  }
16588
- function makeAdvanceCtx() {
16589
- const wrapComputed = (token) => createSignalRef(token);
16450
+ function makeAdvanceEachCtx() {
16590
16451
  return {
16591
- signal: (key) => makeKeyedSignalRef(key),
16452
+ signal: (key) => makeKeyedCohortSignalRef(key),
16592
16453
  stepSignals: new Proxy({}, {
16593
16454
  get(_, key) {
16594
16455
  if (typeof key === "string")
16595
- return makeKeyedSignalRef(key);
16456
+ return makeKeyedCohortSignalRef(key);
16596
16457
  return;
16597
16458
  }
16598
16459
  }),
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 }
16460
+ all: (...refs) => createCohortGroup("all", refs),
16461
+ any: (...refs) => createCohortGroup("any", refs)
16610
16462
  };
16611
16463
  }
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
- });
16464
+ var branchOutcomeValues = [
16465
+ "continue",
16466
+ "block",
16467
+ "error",
16468
+ "abandoned"
16469
+ ];
16470
+ function summarizeTransformOp(op) {
16471
+ if (op.op === "filter") {
16472
+ return `filter_${op.operator}_${JSON.stringify(op.value)}`;
16473
+ }
16474
+ if (op.op === "sortBy") {
16475
+ return `sortBy_${op.direction}`;
16476
+ }
16477
+ return "unique";
16478
+ }
16479
+ function deriveStepSignalsListKey(ops, reducer) {
16480
+ const pluck = ops.find((op) => op.op === "pluck");
16481
+ const base = `${reducer.op}_${pluck?.signalKey ?? "value"}`;
16482
+ const extras = ops.filter((op) => op.op !== "pluck").map(summarizeTransformOp);
16483
+ const reducerExtra = reducer.op === "join" ? `sep_${reducer.separator}` : null;
16484
+ const suffixParts = [...extras, ...reducerExtra ? [reducerExtra] : []];
16485
+ return suffixParts.length > 0 ? `${base}_${suffixParts.join("_")}` : base;
16653
16486
  }
16654
- function isInputAccessor(value) {
16655
- return typeof value === "object" && value !== null && value[ACCESSOR_BRAND] === true;
16487
+ function createStepSignalsListBuilder(ops) {
16488
+ const withOp = (op) => createStepSignalsListBuilder([...ops, op]);
16489
+ const reduce = (reducer) => {
16490
+ const token = {
16491
+ _tag: "step_signals_list",
16492
+ key: deriveStepSignalsListKey(ops, reducer),
16493
+ ops: [...ops],
16494
+ reducer
16495
+ };
16496
+ return createCohortSignalRef(token);
16497
+ };
16498
+ return {
16499
+ filter: (operator, value) => withOp({ op: "filter", operator, value }),
16500
+ sortBy: (direction = "asc") => withOp({ op: "sortBy", direction }),
16501
+ unique: () => withOp({ op: "unique" }),
16502
+ count: () => reduce({ op: "count" }),
16503
+ sum: () => reduce({ op: "sum" }),
16504
+ avg: () => reduce({ op: "avg" }),
16505
+ min: () => reduce({ op: "min" }),
16506
+ max: () => reduce({ op: "max" }),
16507
+ booleanAll: () => reduce({ op: "booleanAll" }),
16508
+ booleanAny: () => reduce({ op: "booleanAny" }),
16509
+ join: (separator = ",") => reduce({ op: "join", separator }),
16510
+ first: () => reduce({ op: "first" }),
16511
+ last: () => reduce({ op: "last" })
16512
+ };
16656
16513
  }
16657
- function materializeAccessor(accessor) {
16514
+ function makeAdvanceAllCtx() {
16658
16515
  return {
16659
- source: "pipeline_input",
16660
- path: accessor[ACCESSOR_PATH]
16516
+ branchOutcomes: {
16517
+ total: () => createCohortSignalRef("branchCount"),
16518
+ count: (outcome) => createCohortSignalRef(`${outcome}Count`),
16519
+ every: (outcome) => createCohortLeafFromCondition({
16520
+ _tag: "signal",
16521
+ signal: `${outcome}Count`,
16522
+ operator: "equal",
16523
+ value: { fact: "branchCount" }
16524
+ }),
16525
+ some: (outcome) => createCohortLeafFromCondition({
16526
+ _tag: "signal",
16527
+ signal: `${outcome}Count`,
16528
+ operator: "greaterThan",
16529
+ value: 0
16530
+ })
16531
+ },
16532
+ stepSignalsList: {
16533
+ pluck: (signalKey) => createStepSignalsListBuilder([{ op: "pluck", signalKey }])
16534
+ },
16535
+ all: (...refs) => createCohortGroup("all", refs),
16536
+ any: (...refs) => createCohortGroup("any", refs)
16661
16537
  };
16662
16538
  }
16663
16539
 
16664
- // src/definitions/pipelines/work-item-fields.ts
16665
- var WORK_ITEM_TOP_LEVEL_FIELDS = [
16666
- "id",
16540
+ // src/definitions/pipelines/bindings.ts
16541
+ function serializeBinding(binding) {
16542
+ if (binding.source === "pipeline_input") {
16543
+ return { source: "pipeline_input", path: binding.path };
16544
+ }
16545
+ if (binding.source === "work_item") {
16546
+ return { source: "work_item", field: binding.field };
16547
+ }
16548
+ if (binding.source === "step_signal") {
16549
+ return {
16550
+ source: "step_signal",
16551
+ stepKey: binding.nodeKey,
16552
+ signalKey: binding.signalKey
16553
+ };
16554
+ }
16555
+ if (binding.source === "literal") {
16556
+ return { source: "literal", value: binding.value };
16557
+ }
16558
+ if (binding.source === "signals_list") {
16559
+ return { source: "signals_list", stepKey: binding.nodeKey };
16560
+ }
16561
+ if (binding.source === "fan_out_item") {
16562
+ return { source: "fan_out_item" };
16563
+ }
16564
+ return { source: "step_output", stepKey: binding.nodeKey };
16565
+ }
16566
+ function serializeInputBindings(input) {
16567
+ const autoBindings = {
16568
+ workItemTitle: { source: "work_item", field: "title" },
16569
+ workItemDescription: { source: "work_item", field: "description" }
16570
+ };
16571
+ const explicitBindings = Object.fromEntries(Object.entries(input).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)]));
16572
+ return { ...autoBindings, ...explicitBindings };
16573
+ }
16574
+
16575
+ // src/definitions/pipelines/work-item-fields.ts
16576
+ var WORK_ITEM_TOP_LEVEL_FIELDS = [
16577
+ "id",
16667
16578
  "projectId",
16668
16579
  "platform",
16669
16580
  "platformId",
@@ -16704,372 +16615,382 @@ var WORK_ITEM_ACCESSOR = Object.freeze({
16704
16615
  field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
16705
16616
  })
16706
16617
  });
16707
- var WORK_ITEM_FIELD_BINDINGS = {
16708
- workItemTitle: { source: "work_item", field: "title" },
16709
- workItemDescription: { source: "work_item", field: "description" }
16710
- };
16711
- function makeStepInputCtx(inputSchema) {
16712
- const baseAccessor = createInputAccessor(inputSchema);
16713
- const input = new Proxy(baseAccessor, {
16714
- get(target, prop) {
16715
- if (typeof prop === "string" && prop in WORK_ITEM_FIELD_BINDINGS) {
16716
- return WORK_ITEM_FIELD_BINDINGS[prop];
16717
- }
16718
- return target[prop];
16719
- }
16720
- });
16721
- return {
16722
- input,
16723
- signal(step, key) {
16724
- return { source: "step_signal", step, signalKey: key };
16725
- },
16726
- output(step) {
16727
- return { source: "step_output", step };
16728
- },
16729
- literal: literal2,
16730
- signalsList(fanOutStep) {
16731
- return { source: "signals_list", fanOutStep };
16732
- }
16733
- };
16734
- }
16735
16618
  function literal2(value) {
16736
16619
  return { source: "literal", value };
16737
16620
  }
16738
- function normalizeInputMapping(mapping) {
16739
- if (!mapping)
16740
- return;
16741
- const out = {};
16742
- for (const [key, value] of Object.entries(mapping)) {
16743
- if (value === undefined)
16744
- continue;
16745
- out[key] = isInputAccessor(value) ? materializeAccessor(value) : value;
16746
- }
16747
- return out;
16748
- }
16749
- function resolveAdditionalStepInputBindings(label, definition) {
16750
- if (!definition) {
16751
- return {};
16752
- }
16753
- const raw = definition.bindings({
16754
- workItemField: (fieldName) => ({
16755
- source: "work_item",
16756
- field: `${WORK_ITEM_FIELDS_PATH_PREFIX}${fieldName}`
16621
+
16622
+ // src/definitions/pipelines/node-input-ctx.ts
16623
+ function makeBaseNodeInputCtx() {
16624
+ return {
16625
+ pipelineInput: (path) => ({ source: "pipeline_input", path }),
16626
+ workItem: WORK_ITEM_ACCESSOR,
16627
+ signal: (nodeKey, signalKey) => ({
16628
+ source: "step_signal",
16629
+ nodeKey,
16630
+ signalKey
16757
16631
  }),
16632
+ output: (nodeKey) => ({ source: "step_output", nodeKey }),
16633
+ signalsList: (nodeKey) => ({ source: "signals_list", nodeKey }),
16758
16634
  literal: literal2
16759
- });
16760
- if (definition.schema instanceof exports_external.ZodObject) {
16761
- const validKeys = new Set(Object.keys(definition.schema.shape));
16762
- const unknown2 = Object.keys(raw).filter((key) => !validKeys.has(key));
16763
- if (unknown2.length > 0) {
16764
- throw new Error(`${label}.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((key) => `"${key}"`).join(", ")}`);
16765
- }
16766
- }
16767
- return normalizeInputMapping(raw) ?? {};
16635
+ };
16636
+ }
16637
+ function makeNodeInputCtx() {
16638
+ return makeBaseNodeInputCtx();
16768
16639
  }
16769
- function mergeStepBindings(pipelineBindings, explicitBindings) {
16770
- const merged = {
16771
- ...pipelineBindings,
16772
- ...explicitBindings ?? {}
16640
+ function makeFanOutNodeInputCtx() {
16641
+ return {
16642
+ ...makeBaseNodeInputCtx(),
16643
+ item: { source: "fan_out_item" }
16773
16644
  };
16774
- return Object.keys(merged).length > 0 ? merged : undefined;
16775
16645
  }
16776
16646
 
16777
- // src/definitions/advancement-policies/cohort-fluent-rules.ts
16778
- var LEAF_BRAND2 = Symbol("boboddy.cohortRule.leaf");
16779
- var GROUP_BRAND2 = Symbol("boboddy.cohortRule.group");
16780
- function createCohortSignalRef(signal2) {
16781
- const leaf = (operator, value) => {
16782
- const condition = {
16783
- _tag: "signal",
16784
- signal: signal2,
16785
- operator,
16786
- value
16787
- };
16647
+ // src/definitions/pipelines/compile-node-definitions.ts
16648
+ function assertTargetExists(ctx, fromKey, toKey) {
16649
+ if (!ctx.stateKeys.has(toKey)) {
16650
+ throw new Error(`Pipeline "${ctx.pipelineKey}": state "${fromKey}" targets unknown state "${toKey}"`);
16651
+ }
16652
+ }
16653
+ function overSignalKeyOf(over) {
16654
+ const lastDot = over.lastIndexOf(".");
16655
+ return lastDot === -1 ? over : over.slice(lastDot + 1);
16656
+ }
16657
+ function compileStepState(stateKey, state, ctx) {
16658
+ ctx.registerStep(state.step);
16659
+ const inputCtx = makeNodeInputCtx();
16660
+ const rawInput = state.input ? state.input(inputCtx) : {};
16661
+ const inputBindingsJson = serializeInputBindings(rawInput);
16662
+ const routeTarget = typeof state.next === "object" ? state.next : null;
16663
+ const defaultOutcome = routeTarget ? {
16664
+ outcome: "route",
16665
+ pipelineKey: routeTarget.routeToPipeline,
16666
+ ...routeTarget.input ? { inputJson: routeTarget.input } : {}
16667
+ } : "continue";
16668
+ const rules = state.blockWhen ? [{ _tag: "rule", mode: "all", conditions: [state.blockWhen], outcome: "block" }] : [];
16669
+ const policy = { defaultOutcome, rules };
16670
+ const nodeDefinitions = [
16671
+ {
16672
+ nodeKey: stateKey,
16673
+ kind: "step",
16674
+ stepKey: state.step.key,
16675
+ stepName: state.step.name,
16676
+ stepDescription: state.step.description,
16677
+ inputBindingsJson,
16678
+ timeoutSeconds: state.timeout ?? null,
16679
+ advancementPolicyDefinition: serializeAdvancementPolicy(policy),
16680
+ computedSignalDefinitions: extractInlineComputedSignals(policy)
16681
+ }
16682
+ ];
16683
+ const edges = [];
16684
+ if (!routeTarget) {
16685
+ const nextKey = state.next;
16686
+ assertTargetExists(ctx, stateKey, nextKey);
16687
+ edges.push({ fromNodeKey: stateKey, toNodeKey: nextKey });
16688
+ }
16689
+ return { nodeDefinitions, edges };
16690
+ }
16691
+ function compileChoiceState(stateKey, state, ctx) {
16692
+ const stateChoices = state.choices ?? [];
16693
+ if (stateChoices.length === 0 && !state.default) {
16694
+ throw new Error(`Pipeline "${ctx.pipelineKey}": choice state "${stateKey}" requires at least one entry in choices or a default target`);
16695
+ }
16696
+ const choices = stateChoices.map((choiceCase) => {
16697
+ assertTargetExists(ctx, stateKey, choiceCase.next);
16788
16698
  return {
16789
- [LEAF_BRAND2]: condition,
16790
- then(outcome, paramsJson) {
16791
- return {
16792
- _tag: "rule",
16793
- mode: "all",
16794
- conditions: [condition],
16795
- outcome,
16796
- ...paramsJson ? { outcomeJson: paramsJson } : {}
16797
- };
16798
- }
16699
+ conditionJson: serializeCondition(choiceCase.when),
16700
+ targetNodeKey: choiceCase.next
16799
16701
  };
16800
- };
16702
+ });
16703
+ if (state.default)
16704
+ assertTargetExists(ctx, stateKey, state.default);
16705
+ const edges = stateChoices.map((choiceCase) => ({
16706
+ fromNodeKey: stateKey,
16707
+ toNodeKey: choiceCase.next,
16708
+ discriminantJson: { conditionSummary: serializeCondition(choiceCase.when) }
16709
+ }));
16710
+ if (state.default) {
16711
+ edges.push({
16712
+ fromNodeKey: stateKey,
16713
+ toNodeKey: state.default,
16714
+ discriminantJson: { default: true }
16715
+ });
16716
+ }
16801
16717
  return {
16802
- eq: (v) => leaf("equal", v),
16803
- ne: (v) => leaf("notEqual", v),
16804
- gt: (v) => leaf("greaterThan", v),
16805
- gte: (v) => leaf("greaterThanInclusive", v),
16806
- lt: (v) => leaf("lessThan", v),
16807
- lte: (v) => leaf("lessThanInclusive", v),
16808
- in: (vs) => leaf("in", vs),
16809
- notIn: (vs) => leaf("notIn", vs),
16810
- contains: (v) => leaf("contains", v),
16811
- doesNotContain: (v) => leaf("doesNotContain", v)
16718
+ nodeDefinitions: [
16719
+ { nodeKey: stateKey, kind: "choice", choices, default: state.default ?? null }
16720
+ ],
16721
+ edges
16812
16722
  };
16813
16723
  }
16814
- function createCohortLeafFromCondition(condition) {
16815
- return {
16816
- [LEAF_BRAND2]: condition,
16817
- then(outcome, paramsJson) {
16818
- return {
16819
- _tag: "rule",
16820
- mode: "all",
16821
- conditions: [condition],
16822
- outcome,
16823
- ...paramsJson ? { outcomeJson: paramsJson } : {}
16824
- };
16825
- }
16724
+ function compileFanOutState(stateKey, state, ctx) {
16725
+ ctx.registerStep(state.step);
16726
+ const inputCtx = makeFanOutNodeInputCtx();
16727
+ const rawInput = state.input ? state.input(inputCtx) : {};
16728
+ const inputBindingsJson = serializeInputBindings(rawInput);
16729
+ const advanceEachResult = state.advanceEach(makeAdvanceEachCtx());
16730
+ const advanceEachPolicy = {
16731
+ default: advanceEachResult.default,
16732
+ ...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
16826
16733
  };
16827
- }
16828
- function extractCohortCondition(ref) {
16829
- if (LEAF_BRAND2 in ref)
16830
- return ref[LEAF_BRAND2];
16831
- const group = ref[GROUP_BRAND2];
16832
- return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
16833
- }
16834
- function createCohortGroup(mode, refs) {
16835
- const conditions = refs.map(extractCohortCondition);
16734
+ const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
16735
+ const advanceAllPolicy = {
16736
+ default: advanceAllResult.default,
16737
+ ...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
16738
+ };
16739
+ const gateKey = `${stateKey}__cohortGate`;
16740
+ if (ctx.stateKeys.has(gateKey)) {
16741
+ throw new Error(`Pipeline "${ctx.pipelineKey}": synthesized cohortGate key "${gateKey}" collides with an author-declared state \u2014 rename state "${stateKey}"`);
16742
+ }
16743
+ assertTargetExists(ctx, stateKey, state.next);
16836
16744
  return {
16837
- [GROUP_BRAND2]: { mode, conditions },
16838
- then(outcome, paramsJson) {
16839
- return {
16840
- _tag: "rule",
16841
- mode,
16842
- conditions,
16843
- outcome,
16844
- ...paramsJson ? { outcomeJson: paramsJson } : {}
16845
- };
16846
- }
16745
+ nodeDefinitions: [
16746
+ {
16747
+ nodeKey: stateKey,
16748
+ kind: "fanOut",
16749
+ stepKey: state.step.key,
16750
+ stepName: state.step.name,
16751
+ stepDescription: state.step.description,
16752
+ inputBindingsJson,
16753
+ timeoutSeconds: state.timeout ?? null,
16754
+ overSignalKey: overSignalKeyOf(state.over),
16755
+ advanceEachPolicyDefinition: serializeCohortAdvancementPolicy(advanceEachPolicy),
16756
+ maxConcurrency: state.maxConcurrency ?? null
16757
+ },
16758
+ {
16759
+ nodeKey: gateKey,
16760
+ kind: "cohortGate",
16761
+ advanceAllPolicyDefinition: serializeCohortAdvancementPolicy(advanceAllPolicy),
16762
+ stepSignalsListDefinitions: extractInlineStepSignalsListDefinitions(advanceAllPolicy)
16763
+ }
16764
+ ],
16765
+ edges: [
16766
+ { fromNodeKey: stateKey, toNodeKey: gateKey },
16767
+ { fromNodeKey: gateKey, toNodeKey: state.next }
16768
+ ]
16769
+ };
16770
+ }
16771
+ function compileParallelState(stateKey, state, ctx) {
16772
+ const branchEntries = Object.entries(state.branches);
16773
+ if (branchEntries.length === 0) {
16774
+ throw new Error(`Pipeline "${ctx.pipelineKey}": parallel state "${stateKey}" requires at least one branch`);
16775
+ }
16776
+ const branches = {};
16777
+ for (const [branchKey, branchConfig] of branchEntries) {
16778
+ ctx.registerStep(branchConfig.step);
16779
+ const inputCtx = makeNodeInputCtx();
16780
+ const rawInput = branchConfig.input ? branchConfig.input(inputCtx) : {};
16781
+ branches[branchKey] = {
16782
+ stepKey: branchConfig.step.key,
16783
+ stepName: branchConfig.step.name,
16784
+ stepDescription: branchConfig.step.description,
16785
+ inputBindingsJson: serializeInputBindings(rawInput)
16786
+ };
16787
+ }
16788
+ let advanceAllPolicyDefinition;
16789
+ if (state.advanceAll) {
16790
+ const advanceAllResult = state.advanceAll(makeAdvanceAllCtx());
16791
+ const advanceAllPolicy = {
16792
+ default: advanceAllResult.default,
16793
+ ...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
16794
+ };
16795
+ advanceAllPolicyDefinition = serializeCohortAdvancementPolicy(advanceAllPolicy);
16796
+ }
16797
+ assertTargetExists(ctx, stateKey, state.next);
16798
+ return {
16799
+ nodeDefinitions: [
16800
+ {
16801
+ nodeKey: stateKey,
16802
+ kind: "parallel",
16803
+ branches,
16804
+ ...advanceAllPolicyDefinition ? { advanceAllPolicyDefinition } : {}
16805
+ }
16806
+ ],
16807
+ edges: [{ fromNodeKey: stateKey, toNodeKey: state.next }]
16847
16808
  };
16848
16809
  }
16849
- function makeKeyedCohortSignalRef(key) {
16850
- return createCohortSignalRef(key);
16851
- }
16852
- function makeAdvanceEachCtx() {
16810
+ function compileLoopState(stateKey, state, ctx) {
16811
+ ctx.registerStep(state.step);
16812
+ const inputCtx = makeNodeInputCtx();
16813
+ const rawInput = state.input ? state.input(inputCtx) : {};
16814
+ assertTargetExists(ctx, stateKey, state.next);
16815
+ assertTargetExists(ctx, stateKey, state.onExhausted);
16853
16816
  return {
16854
- signal: (key) => makeKeyedCohortSignalRef(key),
16855
- stepSignals: new Proxy({}, {
16856
- get(_, key) {
16857
- if (typeof key === "string")
16858
- return makeKeyedCohortSignalRef(key);
16859
- 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)
16860
16828
  }
16861
- }),
16862
- all: (...refs) => createCohortGroup("all", refs),
16863
- any: (...refs) => createCohortGroup("any", refs)
16829
+ ],
16830
+ edges: [
16831
+ { fromNodeKey: stateKey, toNodeKey: state.next, discriminantJson: { loopExit: "next" } },
16832
+ {
16833
+ fromNodeKey: stateKey,
16834
+ toNodeKey: state.onExhausted,
16835
+ discriminantJson: { loopExit: "onExhausted" }
16836
+ }
16837
+ ]
16864
16838
  };
16865
16839
  }
16866
- var branchOutcomeValues = [
16867
- "continue",
16868
- "block",
16869
- "error",
16870
- "abandoned"
16871
- ];
16872
- function summarizeTransformOp(op) {
16873
- if (op.op === "filter") {
16874
- return `filter_${op.operator}_${JSON.stringify(op.value)}`;
16840
+ function compileTerminalState(stateKey, kind) {
16841
+ return { nodeDefinitions: [{ nodeKey: stateKey, kind }], edges: [] };
16842
+ }
16843
+ function assertNoIllegalConvergentEdges(pipelineKey, nodeKindByKey, edges) {
16844
+ const incoming = new Map;
16845
+ for (const edge of edges) {
16846
+ const list = incoming.get(edge.toNodeKey) ?? [];
16847
+ list.push(edge);
16848
+ incoming.set(edge.toNodeKey, list);
16875
16849
  }
16876
- if (op.op === "sortBy") {
16877
- return `sortBy_${op.direction}`;
16850
+ for (const [targetKey, incomingEdges] of incoming) {
16851
+ if (incomingEdges.length <= 1)
16852
+ continue;
16853
+ const hasInvalidSource = incomingEdges.some((edge) => {
16854
+ const kind = nodeKindByKey.get(edge.fromNodeKey);
16855
+ return kind !== "choice" && kind !== "loop";
16856
+ });
16857
+ if (hasInvalidSource) {
16858
+ throw new Error(`Pipeline "${pipelineKey}": state "${targetKey}" has more than one incoming edge, but not every source is a 'choice'/'loop' state (unconditional convergent edges are not allowed \u2014 see docs/research/flat-pipeline-sdk-and-visual-designer.md \xA76).`);
16859
+ }
16878
16860
  }
16879
- return "unique";
16880
16861
  }
16881
- function deriveStepSignalsListKey(ops, reducer) {
16882
- const pluck = ops.find((op) => op.op === "pluck");
16883
- const base = `${reducer.op}_${pluck?.signalKey ?? "value"}`;
16884
- const extras = ops.filter((op) => op.op !== "pluck").map(summarizeTransformOp);
16885
- const reducerExtra = reducer.op === "join" ? `sep_${reducer.separator}` : null;
16886
- const suffixParts = [...extras, ...reducerExtra ? [reducerExtra] : []];
16887
- return suffixParts.length > 0 ? `${base}_${suffixParts.join("_")}` : base;
16862
+
16863
+ // src/definitions/pipelines/define-pipeline.ts
16864
+ function isWorkingNodeDefinition(node) {
16865
+ return node.kind === "step" || node.kind === "fanOut" || node.kind === "loop";
16888
16866
  }
16889
- function createStepSignalsListBuilder(ops) {
16890
- const withOp = (op) => createStepSignalsListBuilder([...ops, op]);
16891
- const reduce = (reducer) => {
16892
- const token = {
16893
- _tag: "step_signals_list",
16894
- key: deriveStepSignalsListKey(ops, reducer),
16895
- ops: [...ops],
16896
- reducer
16897
- };
16898
- return createCohortSignalRef(token);
16867
+ function definePipeline(config2) {
16868
+ const stateEntries = Object.entries(config2.states);
16869
+ if (stateEntries.length === 0) {
16870
+ throw new Error(`Pipeline "${config2.key}" must declare at least one state`);
16871
+ }
16872
+ const startState = config2.states[config2.startAt];
16873
+ if (!startState) {
16874
+ throw new Error(`Pipeline "${config2.key}"'s startAt "${config2.startAt}" does not name a declared state`);
16875
+ }
16876
+ if (startState.kind === "choice" || startState.kind === "succeed" || startState.kind === "fail") {
16877
+ throw new Error(`Pipeline "${config2.key}"'s startAt "${config2.startAt}" names a '${startState.kind}' state, which cannot be an entry point`);
16878
+ }
16879
+ const stateKeys = new Set(Object.keys(config2.states));
16880
+ const stepDefMap = new Map;
16881
+ const registerStep = (step) => {
16882
+ const mapKey = `${step.key}@v${String(step.version)}`;
16883
+ if (!stepDefMap.has(mapKey))
16884
+ stepDefMap.set(mapKey, step);
16899
16885
  };
16900
- return {
16901
- filter: (operator, value) => withOp({ op: "filter", operator, value }),
16902
- sortBy: (direction = "asc") => withOp({ op: "sortBy", direction }),
16903
- unique: () => withOp({ op: "unique" }),
16904
- count: () => reduce({ op: "count" }),
16905
- sum: () => reduce({ op: "sum" }),
16906
- avg: () => reduce({ op: "avg" }),
16907
- min: () => reduce({ op: "min" }),
16908
- max: () => reduce({ op: "max" }),
16909
- booleanAll: () => reduce({ op: "booleanAll" }),
16910
- booleanAny: () => reduce({ op: "booleanAny" }),
16911
- join: (separator = ",") => reduce({ op: "join", separator }),
16912
- first: () => reduce({ op: "first" }),
16913
- last: () => reduce({ op: "last" })
16886
+ const compileContext = {
16887
+ pipelineKey: config2.key,
16888
+ stateKeys,
16889
+ registerStep
16914
16890
  };
16915
- }
16916
- function makeAdvanceAllCtx() {
16891
+ const nodeDefinitions = [];
16892
+ const dependencyEdges = [];
16893
+ for (const [stateKey, state] of stateEntries) {
16894
+ const compiled = state.kind === "step" ? compileStepState(stateKey, state, compileContext) : state.kind === "choice" ? compileChoiceState(stateKey, state, compileContext) : state.kind === "fanOut" ? compileFanOutState(stateKey, state, compileContext) : state.kind === "parallel" ? compileParallelState(stateKey, state, compileContext) : state.kind === "loop" ? compileLoopState(stateKey, state, compileContext) : compileTerminalState(stateKey, state.kind);
16895
+ nodeDefinitions.push(...compiled.nodeDefinitions);
16896
+ dependencyEdges.push(...compiled.edges);
16897
+ }
16898
+ const nodeKindByKey = new Map(nodeDefinitions.map((node) => [node.nodeKey, node.kind]));
16899
+ assertNoIllegalConvergentEdges(config2.key, nodeKindByKey, dependencyEdges);
16900
+ let inputSchemaJson = null;
16901
+ if (config2.input) {
16902
+ try {
16903
+ inputSchemaJson = exports_external.toJSONSchema(config2.input);
16904
+ } catch {
16905
+ inputSchemaJson = null;
16906
+ }
16907
+ }
16917
16908
  return {
16918
- branchOutcomes: {
16919
- total: () => createCohortSignalRef("branchCount"),
16920
- count: (outcome) => createCohortSignalRef(`${outcome}Count`),
16921
- every: (outcome) => createCohortLeafFromCondition({
16922
- _tag: "signal",
16923
- signal: `${outcome}Count`,
16924
- operator: "equal",
16925
- value: { fact: "branchCount" }
16926
- }),
16927
- some: (outcome) => createCohortLeafFromCondition({
16928
- _tag: "signal",
16929
- signal: `${outcome}Count`,
16930
- operator: "greaterThan",
16931
- value: 0
16932
- })
16933
- },
16934
- stepSignalsList: {
16935
- pluck: (signalKey) => createStepSignalsListBuilder([{ op: "pluck", signalKey }])
16936
- },
16937
- all: (...refs) => createCohortGroup("all", refs),
16938
- any: (...refs) => createCohortGroup("any", refs)
16909
+ key: config2.key,
16910
+ name: config2.name ?? config2.key,
16911
+ description: config2.description ?? null,
16912
+ version: config2.version ?? 1,
16913
+ status: config2.status ?? "active",
16914
+ inputSchemaJson,
16915
+ _stepDefinitions: [...stepDefMap.values()],
16916
+ nodeDefinitions,
16917
+ dependencyEdges
16939
16918
  };
16940
16919
  }
16941
-
16942
- // src/definitions/pipelines/fan-out-builder.ts
16943
- function beginFanOut(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings, step, config2) {
16944
- const baseCtx = makeStepInputCtx(inputSchema);
16945
- const ctx = {
16946
- ...baseCtx,
16947
- item: { source: "fan_out_item" }
16948
- };
16949
- const rawInput = config2.input ? config2.input(ctx) : {};
16950
- const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
16951
- const fanOutNodeConfig = {
16952
- nodeType: "fanOut",
16953
- fanOutStep: step,
16954
- overSignalKey: config2.over,
16955
- ...input ? { input } : {},
16956
- ...config2.timeout !== undefined ? { timeout: config2.timeout } : {}
16957
- };
16958
- const advanceEachCtx = makeAdvanceEachCtx();
16959
- const advanceEachResult = config2.advance(advanceEachCtx);
16960
- const advanceEachPolicy = {
16961
- default: advanceEachResult.default,
16962
- ...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
16963
- };
16964
- fanOutNodeConfig.advanceEach = advanceEachPolicy;
16965
- const cohortGateNodeConfig = {
16966
- nodeType: "cohortGate",
16967
- nodeKey: `${step.key}__cohortGate`
16968
- };
16969
- const advanceAllCtx = makeAdvanceAllCtx();
16970
- const advanceAllResult = config2.advanceAll(advanceAllCtx);
16971
- const advanceAllPolicy = {
16972
- default: advanceAllResult.default,
16973
- ...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
16974
- };
16975
- cohortGateNodeConfig.advanceAll = advanceAllPolicy;
16976
- nodes.push(fanOutNodeConfig, cohortGateNodeConfig);
16977
- return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
16920
+ // src/definitions/pipelines/pipeline-definitions-client.ts
16921
+ function createPipelineDefinitionsClient(baseUrl) {
16922
+ const client2 = createClient({ baseUrl });
16923
+ return buildPipelineDefinitionsClient(new PipelineDefinitions({ client: client2 }));
16978
16924
  }
16979
-
16980
- // src/definitions/pipelines/builder.ts
16981
- function pushStep(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings, step, rawOptions) {
16982
- const options = rawOptions;
16983
- const ctx = makeStepInputCtx(inputSchema);
16984
- const rawInput = options.input ? options.input(ctx) : {};
16985
- const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
16986
- const stepConfig = { step, input };
16987
- if (options.timeout !== undefined)
16988
- stepConfig.timeout = options.timeout;
16989
- const advanceCtx = makeAdvanceCtx();
16990
- const result = options.advance(advanceCtx);
16991
- const policy = {
16992
- defaultOutcome: result.default,
16993
- ...result.rules !== undefined ? { rules: result.rules } : {}
16994
- };
16995
- stepConfig.advancement = policy;
16996
- nodes.push(stepConfig);
16997
- return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
16925
+ function resolveStepRef(pipelineKey, nodeKey, stepKey, stepDefMap) {
16926
+ const stepDef = stepDefMap.get(stepKey);
16927
+ if (!stepDef) {
16928
+ throw new Error(`Step "${stepKey}" referenced by node "${nodeKey}" in pipeline "${pipelineKey}" was not found on ` + `the server. Run \`boboddy steps push\` first to push your step definitions.`);
16929
+ }
16930
+ return {
16931
+ stepDefinitionId: stepDef.id,
16932
+ stepDefinitionVersion: stepDef.version
16933
+ };
16998
16934
  }
16999
-
17000
- class PipelineStepBuilder {
17001
- inputSchema;
17002
- meta;
17003
- nodes;
17004
- pipelineInputBindings;
17005
- pipelineStepInputBindings;
17006
- constructor(inputSchema, meta3, nodes, pipelineInputBindings = {}, pipelineStepInputBindings = {}) {
17007
- this.inputSchema = inputSchema;
17008
- this.meta = meta3;
17009
- this.nodes = nodes;
17010
- this.pipelineInputBindings = pipelineInputBindings;
17011
- this.pipelineStepInputBindings = pipelineStepInputBindings;
17012
- }
17013
- step(step, options) {
17014
- return pushStep(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, options);
17015
- }
17016
- fanOutStep(step, config2) {
17017
- return beginFanOut(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, config2);
17018
- }
17019
- build() {
17020
- const config2 = {
17021
- key: this.meta.key,
17022
- name: this.meta.name,
17023
- description: this.meta.description,
17024
- version: this.meta.version,
17025
- status: this.meta.status,
17026
- input: this.inputSchema,
17027
- nodes: this.nodes,
17028
- pipelineInputBindings: this.pipelineInputBindings
16935
+ function buildConfigJson(pipelineKey, node, stepDefMap) {
16936
+ if (node.kind === "choice") {
16937
+ return { choices: node.choices, default: node.default };
16938
+ }
16939
+ if (node.kind === "loop") {
16940
+ return {
16941
+ maxIterations: node.maxIterations,
16942
+ untilConditionJson: node.untilConditionJson
17029
16943
  };
17030
- return buildPipelineSpec(config2);
17031
16944
  }
17032
- }
17033
-
17034
- class PipelineBuilder {
17035
- inputSchema;
17036
- meta;
17037
- nodes = [];
17038
- pipelineInputBindings;
17039
- pipelineStepInputBindings;
17040
- constructor(meta3) {
17041
- const { additionalPipelineInput, additionalStepInput, ...rest } = meta3;
17042
- this.inputSchema = additionalPipelineInput?.schema ?? exports_external.unknown();
17043
- this.meta = rest;
17044
- if (additionalPipelineInput) {
17045
- const raw = additionalPipelineInput.bindings({
17046
- workItem: WORK_ITEM_ACCESSOR,
17047
- literal: literal2
17048
- });
17049
- if (additionalPipelineInput.schema instanceof exports_external.ZodObject) {
17050
- const validKeys = new Set(Object.keys(additionalPipelineInput.schema.shape));
17051
- const unknown2 = Object.keys(raw).filter((k) => !validKeys.has(k));
17052
- if (unknown2.length > 0) {
17053
- throw new Error(`additionalPipelineInput.bindings returned key${unknown2.length > 1 ? "s" : ""} not in schema: ${unknown2.map((k) => `"${k}"`).join(", ")}`);
17054
- }
17055
- }
17056
- this.pipelineInputBindings = normalizeInputMapping(raw) ?? {};
17057
- } else {
17058
- this.pipelineInputBindings = {};
16945
+ if (node.kind === "parallel") {
16946
+ const branches = {};
16947
+ for (const [branchKey, branch] of Object.entries(node.branches)) {
16948
+ branches[branchKey] = {
16949
+ ...resolveStepRef(pipelineKey, node.nodeKey, branch.stepKey, stepDefMap),
16950
+ inputBindingsJson: branch.inputBindingsJson
16951
+ };
17059
16952
  }
17060
- this.pipelineStepInputBindings = resolveAdditionalStepInputBindings("additionalStepInput", additionalStepInput);
16953
+ return {
16954
+ branches,
16955
+ ...node.advanceAllPolicyDefinition ? { advanceAllPolicyDefinition: node.advanceAllPolicyDefinition } : {}
16956
+ };
16957
+ }
16958
+ if (node.kind === "fanOut") {
16959
+ return {
16960
+ overSignalKey: node.overSignalKey,
16961
+ advanceEachPolicyDefinition: node.advanceEachPolicyDefinition,
16962
+ maxConcurrency: node.maxConcurrency
16963
+ };
17061
16964
  }
17062
- step(step, options) {
17063
- return pushStep(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, options);
16965
+ if (node.kind === "cohortGate") {
16966
+ return {
16967
+ advanceAllPolicyDefinition: node.advanceAllPolicyDefinition,
16968
+ stepSignalsListDefinitions: node.stepSignalsListDefinitions
16969
+ };
17064
16970
  }
16971
+ return null;
17065
16972
  }
17066
- function pipeline(meta3) {
17067
- return new PipelineBuilder(meta3);
17068
- }
17069
- // src/definitions/pipelines/pipeline-definitions-client.ts
17070
- function createPipelineDefinitionsClient(baseUrl) {
17071
- const client2 = createClient({ baseUrl });
17072
- return buildPipelineDefinitionsClient(new PipelineDefinitions({ client: client2 }));
16973
+ function buildGraphNodeInput(pipelineKey, node, stepDefMap) {
16974
+ const working = isWorkingNodeDefinition(node) ? node : null;
16975
+ const stepRef = working ? resolveStepRef(pipelineKey, node.nodeKey, working.stepKey, stepDefMap) : null;
16976
+ const policy = node.kind === "step" ? node.advancementPolicyDefinition : undefined;
16977
+ return {
16978
+ key: node.nodeKey,
16979
+ kind: node.kind,
16980
+ name: working?.stepName ?? node.nodeKey,
16981
+ ...stepRef ?? {},
16982
+ description: working?.stepDescription ?? null,
16983
+ inputBindingsJson: working ? working.inputBindingsJson : null,
16984
+ timeoutSeconds: working?.timeoutSeconds ?? null,
16985
+ ...policy ? {
16986
+ advancementPolicyRulesJson: policy.rulesJson,
16987
+ advancementPolicyDefaultEventType: policy.defaultEventType,
16988
+ advancementPolicyDefaultEventParamsJson: policy.defaultEventParamsJson,
16989
+ advancementPolicyAllowedEventTypes: policy.allowedEventTypes
16990
+ } : {},
16991
+ configJson: buildConfigJson(pipelineKey, node, stepDefMap),
16992
+ computedSignalDefinitions: node.kind === "step" ? node.computedSignalDefinitions : []
16993
+ };
17073
16994
  }
17074
16995
  var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17075
16996
  return {
@@ -17090,37 +17011,12 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17090
17011
  stepDefMap.set(s.key, s);
17091
17012
  }
17092
17013
  }
17093
- const nonStepNode = spec.nodeDefinitions.find((node) => node.kind !== "step");
17094
- if (nonStepNode) {
17095
- throw new Error(`Pipeline "${spec.key}" contains a "${nonStepNode.kind}" node ("${nonStepNode.nodeKey}"), ` + `but fan-out pipelines cannot be pushed via the current wire contract yet. ` + `Only "step"-only (chain) pipelines can be pushed with \`upsertFromSpec\` today.`);
17096
- }
17097
- const ordered = tryOrderChainNodeDefinitions(spec.nodeDefinitions, spec.dependencyEdges);
17098
- if (ordered === null) {
17099
- throw new Error(`Pipeline "${spec.key}" has a malformed step graph: its node and ` + `dependency-edge definitions do not form a single connected, ` + `acyclic chain. This should not happen from the \`pipeline()\` ` + `builder \u2014 check for hand-edited or generated pipeline specs.`);
17100
- }
17101
- const stepDefinitions = ordered.map((node, index) => {
17102
- const stepKey = node.stepKey;
17103
- if (!stepKey) {
17104
- throw new Error(`Node "${node.nodeKey}" in pipeline "${spec.key}" has no stepKey.`);
17105
- }
17106
- const stepDef = stepDefMap.get(stepKey);
17107
- if (!stepDef) {
17108
- throw new Error(`Step "${stepKey}" referenced in pipeline "${spec.key}" was not found on the server. ` + `Run \`boboddy steps push\` first to push your step definitions.`);
17109
- }
17110
- return {
17111
- stepDefinitionId: stepDef.id,
17112
- stepDefinitionVersion: stepDef.version,
17113
- key: node.nodeKey,
17114
- name: node.stepName ?? "",
17115
- description: node.stepDescription ?? null,
17116
- position: index + 1,
17117
- inputBindingsJson: node.inputBindingsJson ?? {},
17118
- timeoutSeconds: node.timeoutSeconds ?? null,
17119
- retryPolicyJson: null,
17120
- advancementPolicyDefinition: node.advancementPolicyDefinition,
17121
- computedSignalDefinitions: node.computedSignalDefinitions ?? []
17122
- };
17123
- });
17014
+ const nodeDefinitions = spec.nodeDefinitions.map((node) => buildGraphNodeInput(spec.key, node, stepDefMap));
17015
+ const dependencyEdges = spec.dependencyEdges.map((edge) => ({
17016
+ fromNodeKey: edge.fromNodeKey,
17017
+ toNodeKey: edge.toNodeKey,
17018
+ discriminantJson: edge.discriminantJson ?? null
17019
+ }));
17124
17020
  const body = {
17125
17021
  projectId,
17126
17022
  key: spec.key,
@@ -17128,7 +17024,8 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
17128
17024
  description: spec.description,
17129
17025
  status: spec.status,
17130
17026
  inputSchemaJson: spec.inputSchemaJson,
17131
- stepDefinitions
17027
+ nodeDefinitions,
17028
+ dependencyEdges
17132
17029
  };
17133
17030
  const result = await pipelineDefinitions.upsertPipelineDefinition({
17134
17031
  body,
@@ -17186,17 +17083,17 @@ function buildWorkItemAccessor() {
17186
17083
  }
17187
17084
  return accessor;
17188
17085
  }
17189
- function makeAssign(pipeline2) {
17190
- if (typeof pipeline2 !== "object" || typeof pipeline2["key"] !== "string" || !Array.isArray(pipeline2["nodeDefinitions"])) {
17191
- throw new Error("assign() requires a pipeline spec produced by pipeline().build(). " + "Pass the default-exported value from a pipeline definition file.");
17086
+ function makeAssign(pipeline) {
17087
+ if (typeof pipeline !== "object" || typeof pipeline["key"] !== "string" || !Array.isArray(pipeline["nodeDefinitions"])) {
17088
+ throw new Error("assign() requires a pipeline spec produced by definePipeline(). " + "Pass the default-exported value from a pipeline definition file.");
17192
17089
  }
17193
- return { _tag: "assign", pipeline: pipeline2 };
17090
+ return { _tag: "assign", pipeline };
17194
17091
  }
17195
- function extractCondition2(ref) {
17092
+ function extractCondition(ref) {
17196
17093
  return ref._condition;
17197
17094
  }
17198
17095
  function makeGroup(mode, refs) {
17199
- const conditions = refs.map(extractCondition2);
17096
+ const conditions = refs.map(extractCondition);
17200
17097
  const condition = { _tag: "group", mode, conditions };
17201
17098
  return {
17202
17099
  _condition: condition,
@@ -17508,6 +17405,58 @@ function resolveSourcePath(schema, sourcePath) {
17508
17405
  }
17509
17406
  return { kind: "resolved" };
17510
17407
  }
17408
+ // src/definitions/pipelines/chain-graph.ts
17409
+ function tryComputeTopoRanks(nodeDefinitions, dependencyEdges) {
17410
+ const nodeKeys = new Set(nodeDefinitions.map((node) => node.nodeKey));
17411
+ const outgoing = new Map;
17412
+ const inDegree = new Map;
17413
+ for (const key of nodeKeys) {
17414
+ outgoing.set(key, []);
17415
+ inDegree.set(key, 0);
17416
+ }
17417
+ for (const edge of dependencyEdges) {
17418
+ if (!nodeKeys.has(edge.fromNodeKey) || !nodeKeys.has(edge.toNodeKey)) {
17419
+ return null;
17420
+ }
17421
+ outgoing.get(edge.fromNodeKey)?.push(edge.toNodeKey);
17422
+ inDegree.set(edge.toNodeKey, (inDegree.get(edge.toNodeKey) ?? 0) + 1);
17423
+ }
17424
+ const rank = new Map;
17425
+ const queue = [];
17426
+ for (const key of nodeKeys) {
17427
+ if ((inDegree.get(key) ?? 0) === 0) {
17428
+ rank.set(key, 0);
17429
+ queue.push(key);
17430
+ }
17431
+ }
17432
+ let visitedCount = 0;
17433
+ while (queue.length > 0) {
17434
+ const current = queue.shift();
17435
+ if (current === undefined)
17436
+ break;
17437
+ visitedCount += 1;
17438
+ for (const next of outgoing.get(current) ?? []) {
17439
+ rank.set(next, Math.max(rank.get(next) ?? 0, (rank.get(current) ?? 0) + 1));
17440
+ const remaining = (inDegree.get(next) ?? 0) - 1;
17441
+ inDegree.set(next, remaining);
17442
+ if (remaining === 0)
17443
+ queue.push(next);
17444
+ }
17445
+ }
17446
+ if (visitedCount !== nodeKeys.size)
17447
+ return null;
17448
+ return rank;
17449
+ }
17450
+ function tryOrderNodeDefinitionsByTopoRank(nodeDefinitions, dependencyEdges) {
17451
+ const rankByKey = tryComputeTopoRanks(nodeDefinitions, dependencyEdges);
17452
+ if (rankByKey === null)
17453
+ return null;
17454
+ return nodeDefinitions.map((node, declarationIndex) => ({ node, declarationIndex })).sort((left, right) => {
17455
+ const rankDiff = (rankByKey.get(left.node.nodeKey) ?? 0) - (rankByKey.get(right.node.nodeKey) ?? 0);
17456
+ return rankDiff !== 0 ? rankDiff : left.declarationIndex - right.declarationIndex;
17457
+ }).map(({ node }) => node);
17458
+ }
17459
+
17511
17460
  // src/definitions/validation/validate-definition-specs.ts
17512
17461
  function listPaths(paths, limit = 24) {
17513
17462
  if (paths.length === 0)
@@ -17570,9 +17519,10 @@ function checkHealthChecks(steps) {
17570
17519
  }
17571
17520
  return issues;
17572
17521
  }
17573
- function routeTargets(policy) {
17574
- if (!policy)
17522
+ function routeTargets(node) {
17523
+ if (node.kind !== "step" || !node.advancementPolicyDefinition)
17575
17524
  return [];
17525
+ const policy = node.advancementPolicyDefinition;
17576
17526
  const keys = [];
17577
17527
  if (policy.defaultEventType === "route" && typeof policy.defaultEventParamsJson?.["pipelineKey"] === "string") {
17578
17528
  keys.push(policy.defaultEventParamsJson["pipelineKey"]);
@@ -17587,17 +17537,20 @@ function routeTargets(policy) {
17587
17537
  function checkRouteTargets(pipelines, knownPipelineKeys) {
17588
17538
  const issues = [];
17589
17539
  const known = new Set([
17590
- ...pipelines.map((pipeline2) => pipeline2.key),
17540
+ ...pipelines.map((pipeline) => pipeline.key),
17591
17541
  ...knownPipelineKeys
17592
17542
  ]);
17593
- for (const pipeline2 of pipelines) {
17594
- for (const node of pipeline2.nodeDefinitions) {
17595
- for (const target of routeTargets(node.advancementPolicyDefinition)) {
17543
+ for (const pipeline of pipelines) {
17544
+ for (const node of pipeline.nodeDefinitions) {
17545
+ const stepLabel = (isWorkingNodeDefinition(node) ? node.stepKey : undefined) ?? node.nodeKey;
17546
+ for (const target of routeTargets(node)) {
17596
17547
  if (known.has(target))
17597
17548
  continue;
17598
17549
  issues.push({
17599
17550
  check: "route-target",
17600
- message: `Pipeline "${pipeline2.key}" step "${node.stepKey ?? node.nodeKey}" 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.`
17551
+ pipelineKey: pipeline.key,
17552
+ nodeKey: node.nodeKey,
17553
+ message: `Pipeline "${pipeline.key}" step "${stepLabel}" routes to pipeline ` + `"${target}", but no pipeline with that key was found on the server or ` + `in the current push batch. Push the target pipeline first.`
17601
17554
  });
17602
17555
  }
17603
17556
  }
@@ -17606,7 +17559,7 @@ function checkRouteTargets(pipelines, knownPipelineKeys) {
17606
17559
  }
17607
17560
  function executionRanks(nodeDefinitions, dependencyEdges) {
17608
17561
  const indexes = nodeDefinitions.map((_, index) => index);
17609
- const orderedNodes = tryOrderChainNodeDefinitions(nodeDefinitions, dependencyEdges);
17562
+ const orderedNodes = tryOrderNodeDefinitionsByTopoRank(nodeDefinitions, dependencyEdges);
17610
17563
  if (orderedNodes === null) {
17611
17564
  return new Map(indexes.map((index, rank) => [index, rank]));
17612
17565
  }
@@ -17616,70 +17569,81 @@ function executionRanks(nodeDefinitions, dependencyEdges) {
17616
17569
  }
17617
17570
  function bindingSource(binding) {
17618
17571
  if (binding.source === "step_signal") {
17619
- return { stepKey: binding.stepKey, signalKey: binding.signalKey, kind: "signal" };
17572
+ return { nodeKey: binding.stepKey, signalKey: binding.signalKey, kind: "signal" };
17620
17573
  }
17621
17574
  if (binding.source === "step_output") {
17622
- return { stepKey: binding.stepKey, signalKey: null, kind: "output" };
17575
+ return { nodeKey: binding.stepKey, signalKey: null, kind: "output" };
17623
17576
  }
17624
17577
  if (binding.source === "signals_list") {
17625
- return { stepKey: binding.stepKey, signalKey: null, kind: "signals_list" };
17578
+ return { nodeKey: binding.stepKey, signalKey: null, kind: "signals_list" };
17626
17579
  }
17627
17580
  return null;
17628
17581
  }
17629
- function declaredSignalKeys(stepKey, stepsByKey, pipelineNodeDefinitions) {
17630
- const specs = stepsByKey.get(stepKey);
17631
- if (!specs || specs.length === 0)
17632
- return null;
17582
+ function declaredSignalKeys(producerNode, stepsByKey) {
17583
+ const stepKey = isWorkingNodeDefinition(producerNode) ? producerNode.stepKey : undefined;
17584
+ const specs = stepKey ? stepsByKey.get(stepKey) : undefined;
17585
+ const computedSignalDefinitions = (producerNode.kind === "step" ? producerNode.computedSignalDefinitions : []) ?? [];
17633
17586
  const keys = new Set;
17634
- for (const spec of specs) {
17587
+ for (const spec of specs ?? []) {
17635
17588
  for (const signal2 of spec.signalExtractorDefinitions)
17636
17589
  keys.add(signal2.key);
17637
17590
  }
17638
- for (const node of pipelineNodeDefinitions) {
17639
- if (node.stepKey !== stepKey)
17640
- continue;
17641
- for (const computed of node.computedSignalDefinitions ?? [])
17642
- keys.add(computed.key);
17591
+ for (const computed of computedSignalDefinitions) {
17592
+ keys.add(computed.key);
17593
+ }
17594
+ if (!specs && computedSignalDefinitions.length === 0) {
17595
+ return null;
17643
17596
  }
17644
17597
  return [...keys];
17645
17598
  }
17646
17599
  function checkSignalBindings(pipelines, stepsByKey) {
17647
17600
  const issues = [];
17648
- for (const pipeline2 of pipelines) {
17649
- const ranks = executionRanks(pipeline2.nodeDefinitions, pipeline2.dependencyEdges);
17650
- const order = [...pipeline2.nodeDefinitions.keys()].sort((left, right) => (ranks.get(left) ?? 0) - (ranks.get(right) ?? 0)).map((index) => pipeline2.nodeDefinitions[index]?.stepKey ?? "");
17651
- const orderHint = `Steps in "${pipeline2.key}", in order: ${order.join(" \u2192 ")}.`;
17652
- pipeline2.nodeDefinitions.forEach((node, index) => {
17601
+ for (const pipeline of pipelines) {
17602
+ const ranks = executionRanks(pipeline.nodeDefinitions, pipeline.dependencyEdges);
17603
+ const order = [...pipeline.nodeDefinitions.keys()].sort((left, right) => (ranks.get(left) ?? 0) - (ranks.get(right) ?? 0)).map((index) => pipeline.nodeDefinitions[index]?.nodeKey ?? "");
17604
+ const orderHint = `Nodes in "${pipeline.key}", in order: ${order.join(" \u2192 ")}.`;
17605
+ const nodeByKey = new Map(pipeline.nodeDefinitions.map((node) => [node.nodeKey, node]));
17606
+ pipeline.nodeDefinitions.forEach((node, index) => {
17653
17607
  const consumerRank = ranks.get(index) ?? index;
17654
- const where = `Pipeline "${pipeline2.key}" step "${node.stepKey ?? node.nodeKey}"`;
17655
- for (const [field, binding] of Object.entries(node.inputBindingsJson ?? {})) {
17608
+ const where = `Pipeline "${pipeline.key}" node "${node.nodeKey}"`;
17609
+ const inputBindingsJson = (isWorkingNodeDefinition(node) ? node.inputBindingsJson : {}) ?? {};
17610
+ for (const [field, binding] of Object.entries(inputBindingsJson)) {
17656
17611
  const source = bindingSource(binding);
17657
17612
  if (!source)
17658
17613
  continue;
17659
- const what = source.kind === "signal" ? `binds input "${field}" to signal "${source.signalKey ?? ""}" of step "${source.stepKey}"` : source.kind === "signals_list" ? `binds input "${field}" to the signals list of fan-out step "${source.stepKey}"` : `binds input "${field}" to the output of step "${source.stepKey}"`;
17660
- const producerRanks = pipeline2.nodeDefinitions.map((candidate, candidateIndex) => candidate.stepKey === source.stepKey ? ranks.get(candidateIndex) ?? candidateIndex : null).filter((rank) => rank !== null);
17661
- if (producerRanks.length === 0) {
17614
+ const what = source.kind === "signal" ? `binds input "${field}" to signal "${source.signalKey ?? ""}" of node "${source.nodeKey}"` : source.kind === "signals_list" ? `binds input "${field}" to the signals list of fan-out node "${source.nodeKey}"` : `binds input "${field}" to the output of node "${source.nodeKey}"`;
17615
+ const issueBase = {
17616
+ pipelineKey: pipeline.key,
17617
+ nodeKey: node.nodeKey,
17618
+ targetNodeKey: source.nodeKey
17619
+ };
17620
+ const producerNode = nodeByKey.get(source.nodeKey);
17621
+ const producerRank = producerNode ? ranks.get(pipeline.nodeDefinitions.indexOf(producerNode)) ?? null : null;
17622
+ if (!producerNode || producerRank === null) {
17662
17623
  issues.push({
17663
17624
  check: "signal-binding",
17664
- message: `${where} ${what}, but no step with that key is in the pipeline. ${orderHint}`
17625
+ ...issueBase,
17626
+ message: `${where} ${what}, but no node with that key is in the pipeline. ${orderHint}`
17665
17627
  });
17666
17628
  continue;
17667
17629
  }
17668
- if (!producerRanks.some((rank) => rank < consumerRank)) {
17630
+ if (producerRank >= consumerRank) {
17669
17631
  issues.push({
17670
17632
  check: "signal-binding",
17671
- message: `${where} ${what}, but that step does not run before it, so the ` + `value will never exist. ${orderHint}`
17633
+ ...issueBase,
17634
+ message: `${where} ${what}, but that node does not run before it, so the ` + `value will never exist. ${orderHint}`
17672
17635
  });
17673
17636
  continue;
17674
17637
  }
17675
17638
  if (source.signalKey === null)
17676
17639
  continue;
17677
- const available = declaredSignalKeys(source.stepKey, stepsByKey, pipeline2.nodeDefinitions);
17640
+ const available = declaredSignalKeys(producerNode, stepsByKey);
17678
17641
  if (available === null || available.includes(source.signalKey))
17679
17642
  continue;
17680
17643
  issues.push({
17681
17644
  check: "signal-binding",
17682
- message: `${where} ${what}, but "${source.stepKey}" declares no such signal. ` + `Signals on "${source.stepKey}": ${available.length > 0 ? listPaths([...available].sort()) : "(none)"}.`
17645
+ ...issueBase,
17646
+ message: `${where} ${what}, but "${source.nodeKey}" declares no such signal. ` + `Signals on "${source.nodeKey}": ${available.length > 0 ? listPaths([...available].sort()) : "(none)"}.`
17683
17647
  });
17684
17648
  }
17685
17649
  });
@@ -17712,14 +17676,38 @@ function assertValidDefinitionSpecs(specs, options = {}) {
17712
17676
  }
17713
17677
  // src/push/collect-definitions.ts
17714
17678
  import { existsSync, readdirSync } from "fs";
17715
- import { join, resolve } from "path";
17679
+ import { join, relative, resolve } from "path";
17716
17680
  import { pathToFileURL } from "url";
17717
- var PUSH_SCRIPT_NAMES = new Set(["push.ts", "push.mjs", "push.js"]);
17681
+ var INTERNAL_SCRIPT_NAMES = new Set([
17682
+ "push.ts",
17683
+ "push.mjs",
17684
+ "push.js",
17685
+ ".boboddy-studio-collect.mjs"
17686
+ ]);
17718
17687
  function isStepDefinitionSpec(value) {
17719
17688
  if (typeof value !== "object" || value === null)
17720
17689
  return false;
17721
17690
  const obj = value;
17722
- return typeof obj["key"] === "string" && typeof obj["name"] === "string" && typeof obj["version"] === "number" && obj["kind"] === "user_defined";
17691
+ return typeof obj["key"] === "string" && typeof obj["name"] === "string" && typeof obj["version"] === "number" && (obj["kind"] === "user_defined" || obj["kind"] === "code");
17692
+ }
17693
+ function resolveCodeStepEntrypoint(spec, mod, absoluteFilePath) {
17694
+ if (spec.kind !== "code")
17695
+ return spec;
17696
+ const fn = spec.entrypoint?.fn;
17697
+ if (!fn) {
17698
+ throw new Error(`Code step "${spec.key}" (kind: "code") has no entrypoint.fn to resolve. ` + `Build it with codeStep({ fn, ... }) from "@boboddy/sdk/definitions/steps".`);
17699
+ }
17700
+ const exportName = Object.entries(mod).find(([, exportedValue]) => exportedValue === fn)?.[0];
17701
+ if (!exportName) {
17702
+ throw new Error(`Code step "${spec.key}"'s fn is not a named export of ${absoluteFilePath}. ` + `codeStep({ fn }) requires fn to be exported by name from the same module ` + `so its entrypoint can be resolved to a portable {sourceFile, exportName} pair.`);
17703
+ }
17704
+ const resolved = { ...spec };
17705
+ delete resolved.entrypoint;
17706
+ resolved.entrypointJson = {
17707
+ sourceFile: relative(process.cwd(), absoluteFilePath),
17708
+ exportName
17709
+ };
17710
+ return resolved;
17723
17711
  }
17724
17712
  function isPipelineDefinitionSpec(value) {
17725
17713
  if (typeof value !== "object" || value === null)
@@ -17730,31 +17718,48 @@ function isPipelineDefinitionSpec(value) {
17730
17718
  async function importModule(path) {
17731
17719
  return await import(pathToFileURL(path).href);
17732
17720
  }
17721
+ function listSourceFiles(absDir) {
17722
+ const allFiles = readdirSync(absDir).filter((file2) => file2.endsWith(".ts") || file2.endsWith(".js"));
17723
+ return allFiles.filter((file2) => !INTERNAL_SCRIPT_NAMES.has(file2) && file2 !== DEFAULT_PIPELINE_ASSIGNMENT_FILENAME);
17724
+ }
17725
+ function fileNameWithoutExtension(file2) {
17726
+ return file2.replace(/\.(ts|js)$/, "");
17727
+ }
17728
+ async function collectFile(absoluteFilePath, pipelines, stepMap) {
17729
+ const mod = await importModule(absoluteFilePath);
17730
+ for (const [exportName, value] of Object.entries(mod)) {
17731
+ if (exportName === "default") {
17732
+ if (isPipelineDefinitionSpec(value))
17733
+ pipelines.push(value);
17734
+ continue;
17735
+ }
17736
+ if (isStepDefinitionSpec(value)) {
17737
+ const resolved = resolveCodeStepEntrypoint(value, mod, absoluteFilePath);
17738
+ stepMap.set(`${resolved.key}@v${String(resolved.version)}`, resolved);
17739
+ }
17740
+ }
17741
+ }
17742
+ function foldEmbeddedSteps(dir, spec, stepMap) {
17743
+ for (const embedded of spec._stepDefinitions ?? []) {
17744
+ const key = `${embedded.key}@v${String(embedded.version)}`;
17745
+ if (stepMap.has(key))
17746
+ continue;
17747
+ if (embedded.kind === "code") {
17748
+ throw new Error(`Code step "${embedded.key}" is only referenced inside a pipeline and is not exported by name from any file in "${dir}". codeStep({ fn }) requires fn to be ` + `a named export so its entrypoint can be resolved \u2014 export the step itself, e.g. ` + `\`export const ${embedded.key} = codeStep({ ... })\`.`);
17749
+ }
17750
+ stepMap.set(key, embedded);
17751
+ }
17752
+ }
17733
17753
  async function collectDefinitionsFromDirectory(dir) {
17734
17754
  const absDir = resolve(dir);
17735
- const allFiles = readdirSync(absDir).filter((file2) => file2.endsWith(".ts") || file2.endsWith(".js"));
17736
- const sourceFiles = allFiles.filter((file2) => !PUSH_SCRIPT_NAMES.has(file2) && file2 !== DEFAULT_PIPELINE_ASSIGNMENT_FILENAME);
17755
+ const sourceFiles = listSourceFiles(absDir);
17737
17756
  const pipelines = [];
17738
17757
  const stepMap = new Map;
17739
17758
  for (const file2 of sourceFiles) {
17740
- const mod = await importModule(join(absDir, file2));
17741
- for (const [exportName, value] of Object.entries(mod)) {
17742
- if (exportName === "default") {
17743
- if (isPipelineDefinitionSpec(value))
17744
- pipelines.push(value);
17745
- continue;
17746
- }
17747
- if (isStepDefinitionSpec(value)) {
17748
- stepMap.set(`${value.key}@v${String(value.version)}`, value);
17749
- }
17750
- }
17759
+ await collectFile(join(absDir, file2), pipelines, stepMap);
17751
17760
  }
17752
17761
  for (const spec of pipelines) {
17753
- for (const embedded of spec._stepDefinitions ?? []) {
17754
- const key = `${embedded.key}@v${String(embedded.version)}`;
17755
- if (!stepMap.has(key))
17756
- stepMap.set(key, embedded);
17757
- }
17762
+ foldEmbeddedSteps(dir, spec, stepMap);
17758
17763
  }
17759
17764
  return {
17760
17765
  pipelines,
@@ -17762,6 +17767,50 @@ async function collectDefinitionsFromDirectory(dir) {
17762
17767
  defaultPipelineAssignment: await collectDefaultPipelineAssignment(absDir)
17763
17768
  };
17764
17769
  }
17770
+ async function collectDefinitionsFromDirectoryTolerant(dir) {
17771
+ const absDir = resolve(dir);
17772
+ const sourceFiles = listSourceFiles(absDir);
17773
+ const pipelines = [];
17774
+ const stepMap = new Map;
17775
+ const brokenPipelines = [];
17776
+ for (const file2 of sourceFiles) {
17777
+ try {
17778
+ await collectFile(join(absDir, file2), pipelines, stepMap);
17779
+ } catch (error51) {
17780
+ brokenPipelines.push({
17781
+ key: fileNameWithoutExtension(file2),
17782
+ message: error51 instanceof Error ? error51.message : String(error51)
17783
+ });
17784
+ }
17785
+ }
17786
+ const okPipelines = [];
17787
+ for (const spec of pipelines) {
17788
+ try {
17789
+ foldEmbeddedSteps(dir, spec, stepMap);
17790
+ okPipelines.push(spec);
17791
+ } catch (error51) {
17792
+ brokenPipelines.push({
17793
+ key: spec.key,
17794
+ message: error51 instanceof Error ? error51.message : String(error51)
17795
+ });
17796
+ }
17797
+ }
17798
+ let defaultPipelineAssignment2 = null;
17799
+ try {
17800
+ defaultPipelineAssignment2 = await collectDefaultPipelineAssignment(absDir);
17801
+ } catch (error51) {
17802
+ brokenPipelines.push({
17803
+ key: fileNameWithoutExtension(DEFAULT_PIPELINE_ASSIGNMENT_FILENAME),
17804
+ message: error51 instanceof Error ? error51.message : String(error51)
17805
+ });
17806
+ }
17807
+ return {
17808
+ pipelines: okPipelines,
17809
+ steps: [...stepMap.values()],
17810
+ defaultPipelineAssignment: defaultPipelineAssignment2,
17811
+ brokenPipelines
17812
+ };
17813
+ }
17765
17814
  async function collectDefaultPipelineAssignment(absDir) {
17766
17815
  const path = join(absDir, DEFAULT_PIPELINE_ASSIGNMENT_FILENAME);
17767
17816
  if (!existsSync(path))
@@ -17784,7 +17833,7 @@ async function pushFromDirectory(dir, opts) {
17784
17833
  log(`Found ${String(pipelines.length)} pipeline(s) and ${String(steps.length)} step(s).`);
17785
17834
  const stepsClient = createStepDefinitionsClient(opts.baseUrl);
17786
17835
  const pipelinesClient = createPipelineDefinitionsClient(opts.baseUrl);
17787
- const serverPipelineKeys = pipelines.length > 0 ? (await pipelinesClient.listByProjectId(opts.projectId, { headers })).map((pipeline2) => pipeline2.key) : [];
17836
+ const serverPipelineKeys = pipelines.length > 0 ? (await pipelinesClient.listByProjectId(opts.projectId, { headers })).map((pipeline) => pipeline.key) : [];
17788
17837
  assertValidDefinitionSpecs({ pipelines, steps }, { knownPipelineKeys: serverPipelineKeys });
17789
17838
  for (const spec of steps) {
17790
17839
  await stepsClient.upsertFromSpec(opts.projectId, spec, { headers });
@@ -17882,5 +17931,6 @@ async function syncDefaultPipelineAssignment(spec, opts, headers, pipelinesClien
17882
17931
  }
17883
17932
  export {
17884
17933
  pushFromDirectory,
17934
+ collectDefinitionsFromDirectoryTolerant,
17885
17935
  collectDefinitionsFromDirectory
17886
17936
  };