@boboddy/sdk 0.3.1 → 0.4.2

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.
@@ -13135,7 +13135,7 @@ class StepExecutions extends HeyApiClient {
13135
13135
  class PipelineDefinitions extends HeyApiClient {
13136
13136
  createPipelineDefinition(options) {
13137
13137
  return (options.client ?? this.client).post({
13138
- url: "/api/linear-pipeline-definitions",
13138
+ url: "/api/pipeline-definitions",
13139
13139
  ...options,
13140
13140
  headers: {
13141
13141
  "Content-Type": "application/json",
@@ -13145,7 +13145,7 @@ class PipelineDefinitions extends HeyApiClient {
13145
13145
  }
13146
13146
  upsertPipelineDefinition(options) {
13147
13147
  return (options.client ?? this.client).put({
13148
- url: "/api/linear-pipeline-definitions",
13148
+ url: "/api/pipeline-definitions",
13149
13149
  ...options,
13150
13150
  headers: {
13151
13151
  "Content-Type": "application/json",
@@ -13154,14 +13154,14 @@ class PipelineDefinitions extends HeyApiClient {
13154
13154
  });
13155
13155
  }
13156
13156
  archivePipelineDefinition(options) {
13157
- return (options.client ?? this.client).put({ url: "/api/linear-pipeline-definitions/{linearPipelineDefinitionId}/archive", ...options });
13157
+ return (options.client ?? this.client).put({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/archive", ...options });
13158
13158
  }
13159
13159
  unarchivePipelineDefinition(options) {
13160
- return (options.client ?? this.client).put({ url: "/api/linear-pipeline-definitions/{linearPipelineDefinitionId}/unarchive", ...options });
13160
+ return (options.client ?? this.client).put({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/unarchive", ...options });
13161
13161
  }
13162
13162
  addPipelineStep(options) {
13163
13163
  return (options.client ?? this.client).post({
13164
- url: "/api/linear-pipeline-definitions/{linearPipelineDefinitionId}/steps",
13164
+ url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps",
13165
13165
  ...options,
13166
13166
  headers: {
13167
13167
  "Content-Type": "application/json",
@@ -13170,11 +13170,11 @@ class PipelineDefinitions extends HeyApiClient {
13170
13170
  });
13171
13171
  }
13172
13172
  removePipelineStep(options) {
13173
- return (options.client ?? this.client).delete({ url: "/api/linear-pipeline-definitions/{linearPipelineDefinitionId}/steps/{linearPipelineStepDefinitionId}", ...options });
13173
+ return (options.client ?? this.client).delete({ url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}", ...options });
13174
13174
  }
13175
13175
  updatePipelineStep(options) {
13176
13176
  return (options.client ?? this.client).put({
13177
- url: "/api/linear-pipeline-definitions/{linearPipelineDefinitionId}/steps/{linearPipelineStepDefinitionId}",
13177
+ url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}",
13178
13178
  ...options,
13179
13179
  headers: {
13180
13180
  "Content-Type": "application/json",
@@ -13184,7 +13184,7 @@ class PipelineDefinitions extends HeyApiClient {
13184
13184
  }
13185
13185
  setPipelineStepAdvancementPolicy(options) {
13186
13186
  return (options.client ?? this.client).put({
13187
- url: "/api/linear-pipeline-definitions/{linearPipelineDefinitionId}/steps/{linearPipelineStepDefinitionId}/advancement-policy",
13187
+ url: "/api/pipeline-definitions/{pipelineDefinitionId}/steps/{pipelineStepDefinitionId}/advancement-policy",
13188
13188
  ...options,
13189
13189
  headers: {
13190
13190
  "Content-Type": "application/json",
@@ -13193,20 +13193,20 @@ class PipelineDefinitions extends HeyApiClient {
13193
13193
  });
13194
13194
  }
13195
13195
  getPipelineDefinition(options) {
13196
- return (options.client ?? this.client).get({ url: "/api/linear-pipeline-definitions/{linearPipelineDefinitionId}", ...options });
13196
+ return (options.client ?? this.client).get({ url: "/api/pipeline-definitions/{pipelineDefinitionId}", ...options });
13197
13197
  }
13198
13198
  listPipelineDefinitions(options) {
13199
- return (options.client ?? this.client).get({ url: "/api/projects/{projectId}/linear-pipeline-definitions", ...options });
13199
+ return (options.client ?? this.client).get({ url: "/api/projects/{projectId}/pipeline-definitions", ...options });
13200
13200
  }
13201
13201
  }
13202
13202
 
13203
13203
  class PipelineExecutions extends HeyApiClient {
13204
13204
  listPipelineExecutions(options) {
13205
- return (options.client ?? this.client).get({ url: "/api/linear-pipeline-executions", ...options });
13205
+ return (options.client ?? this.client).get({ url: "/api/pipeline-executions", ...options });
13206
13206
  }
13207
13207
  createPipelineExecution(options) {
13208
13208
  return (options.client ?? this.client).post({
13209
- url: "/api/linear-pipeline-executions",
13209
+ url: "/api/pipeline-executions",
13210
13210
  ...options,
13211
13211
  headers: {
13212
13212
  "Content-Type": "application/json",
@@ -13215,17 +13215,17 @@ class PipelineExecutions extends HeyApiClient {
13215
13215
  });
13216
13216
  }
13217
13217
  startPipelineExecution(options) {
13218
- return (options.client ?? this.client).put({ url: "/api/linear-pipeline-executions/{linearPipelineExecutionId}/start", ...options });
13218
+ return (options.client ?? this.client).put({ url: "/api/pipeline-executions/{pipelineExecutionId}/start", ...options });
13219
13219
  }
13220
13220
  queueFirstPipelineStepRun(options) {
13221
- return (options.client ?? this.client).post({ url: "/api/linear-pipeline-executions/{linearPipelineExecutionId}/step-runs/first", ...options });
13221
+ return (options.client ?? this.client).post({ url: "/api/pipeline-executions/{pipelineExecutionId}/step-runs/first", ...options });
13222
13222
  }
13223
13223
  markPipelineStepRunRunning(options) {
13224
- return (options.client ?? this.client).put({ url: "/api/linear-pipeline-executions/{linearPipelineExecutionId}/step-runs/{linearPipelineStepRunId}/running", ...options });
13224
+ return (options.client ?? this.client).put({ url: "/api/pipeline-executions/{pipelineExecutionId}/step-runs/{pipelineStepRunId}/running", ...options });
13225
13225
  }
13226
13226
  applyPipelineStepResult(options) {
13227
13227
  return (options.client ?? this.client).post({
13228
- url: "/api/linear-pipeline-executions/{linearPipelineExecutionId}/step-runs/{linearPipelineStepRunId}/results",
13228
+ url: "/api/pipeline-executions/{pipelineExecutionId}/step-runs/{pipelineStepRunId}/results",
13229
13229
  ...options,
13230
13230
  headers: {
13231
13231
  "Content-Type": "application/json",
@@ -13235,7 +13235,17 @@ class PipelineExecutions extends HeyApiClient {
13235
13235
  }
13236
13236
  acceptPipelineStepRun(options) {
13237
13237
  return (options.client ?? this.client).post({
13238
- url: "/api/linear-pipeline-executions/{linearPipelineExecutionId}/step-runs/{linearPipelineStepRunId}/accept",
13238
+ url: "/api/pipeline-executions/{pipelineExecutionId}/step-runs/{pipelineStepRunId}/accept",
13239
+ ...options,
13240
+ headers: {
13241
+ "Content-Type": "application/json",
13242
+ ...options.headers
13243
+ }
13244
+ });
13245
+ }
13246
+ acceptPipelineCohort(options) {
13247
+ return (options.client ?? this.client).post({
13248
+ url: "/api/pipeline-executions/{pipelineExecutionId}/cohorts/{originNodeDefinitionId}/accept",
13239
13249
  ...options,
13240
13250
  headers: {
13241
13251
  "Content-Type": "application/json",
@@ -13245,7 +13255,7 @@ class PipelineExecutions extends HeyApiClient {
13245
13255
  }
13246
13256
  rerunPipelineExecution(options) {
13247
13257
  return (options.client ?? this.client).post({
13248
- url: "/api/linear-pipeline-executions/{linearPipelineExecutionId}/rerun",
13258
+ url: "/api/pipeline-executions/{pipelineExecutionId}/rerun",
13249
13259
  ...options,
13250
13260
  headers: {
13251
13261
  "Content-Type": "application/json",
@@ -13254,19 +13264,32 @@ class PipelineExecutions extends HeyApiClient {
13254
13264
  });
13255
13265
  }
13256
13266
  cancelPipelineExecution(options) {
13257
- return (options.client ?? this.client).put({ url: "/api/linear-pipeline-executions/{linearPipelineExecutionId}/cancel", ...options });
13267
+ return (options.client ?? this.client).put({ url: "/api/pipeline-executions/{pipelineExecutionId}/cancel", ...options });
13268
+ }
13269
+ cancelPipelineExecutions(options) {
13270
+ return (options.client ?? this.client).put({
13271
+ url: "/api/pipeline-executions/batch/cancel",
13272
+ ...options,
13273
+ headers: {
13274
+ "Content-Type": "application/json",
13275
+ ...options.headers
13276
+ }
13277
+ });
13258
13278
  }
13259
13279
  getPipelineExecution(options) {
13260
- return (options.client ?? this.client).get({ url: "/api/linear-pipeline-executions/{linearPipelineExecutionId}", ...options });
13280
+ return (options.client ?? this.client).get({ url: "/api/pipeline-executions/{pipelineExecutionId}", ...options });
13281
+ }
13282
+ getPipelineExecutionRoutedChain(options) {
13283
+ return (options.client ?? this.client).get({ url: "/api/pipeline-executions/{pipelineExecutionId}/routed-chain", ...options });
13261
13284
  }
13262
13285
  countPipelineExecutionsByPipeline(options) {
13263
- return (options.client ?? this.client).get({ url: "/api/linear-pipeline-executions/counts/by-pipeline", ...options });
13286
+ return (options.client ?? this.client).get({ url: "/api/pipeline-executions/counts/by-pipeline", ...options });
13264
13287
  }
13265
13288
  listPipelineDefinitionStepRollups(options) {
13266
- return (options.client ?? this.client).get({ url: "/api/linear-pipeline-executions/step-rollups/by-definition/{linearPipelineDefinitionId}", ...options });
13289
+ return (options.client ?? this.client).get({ url: "/api/pipeline-executions/step-rollups/by-definition/{pipelineDefinitionId}", ...options });
13267
13290
  }
13268
13291
  listPipelineDefinitionStepRuns(options) {
13269
- return (options.client ?? this.client).get({ url: "/api/linear-pipeline-executions/step-runs/by-definition/{linearPipelineDefinitionId}", ...options });
13292
+ return (options.client ?? this.client).get({ url: "/api/pipeline-executions/step-runs/by-definition/{pipelineDefinitionId}", ...options });
13270
13293
  }
13271
13294
  }
13272
13295
 
@@ -13277,6 +13300,19 @@ class WorkItems extends HeyApiClient {
13277
13300
  getWorkItem(options) {
13278
13301
  return (options.client ?? this.client).get({ url: "/api/work-items/{workItemId}", ...options });
13279
13302
  }
13303
+ editWorkItem(options) {
13304
+ return (options.client ?? this.client).patch({
13305
+ url: "/api/work-items/{workItemId}",
13306
+ ...options,
13307
+ headers: {
13308
+ "Content-Type": "application/json",
13309
+ ...options.headers
13310
+ }
13311
+ });
13312
+ }
13313
+ listWorkItemChildren(options) {
13314
+ return (options.client ?? this.client).get({ url: "/api/work-items/{workItemId}/children", ...options });
13315
+ }
13280
13316
  createWorkItem(options) {
13281
13317
  return (options.client ?? this.client).post({
13282
13318
  url: "/api/work-items",
@@ -13327,6 +13363,16 @@ class WorkItems extends HeyApiClient {
13327
13363
  }
13328
13364
  });
13329
13365
  }
13366
+ setWorkItemParent(options) {
13367
+ return (options.client ?? this.client).patch({
13368
+ url: "/api/work-items/{workItemId}/parent",
13369
+ ...options,
13370
+ headers: {
13371
+ "Content-Type": "application/json",
13372
+ ...options.headers
13373
+ }
13374
+ });
13375
+ }
13330
13376
  }
13331
13377
 
13332
13378
  class WorkItemComments extends HeyApiClient {
@@ -13368,6 +13414,25 @@ class WorkItemComments extends HeyApiClient {
13368
13414
  }
13369
13415
  }
13370
13416
 
13417
+ class WorkItemBlocks extends HeyApiClient {
13418
+ listWorkItemBlocks(options) {
13419
+ return (options.client ?? this.client).get({ url: "/api/work-item-blocks", ...options });
13420
+ }
13421
+ createWorkItemBlock(options) {
13422
+ return (options.client ?? this.client).post({
13423
+ url: "/api/work-item-blocks",
13424
+ ...options,
13425
+ headers: {
13426
+ "Content-Type": "application/json",
13427
+ ...options.headers
13428
+ }
13429
+ });
13430
+ }
13431
+ deleteWorkItemBlock(options) {
13432
+ return (options.client ?? this.client).delete({ url: "/api/work-item-blocks/{blockId}", ...options });
13433
+ }
13434
+ }
13435
+
13371
13436
  class ProjectContext extends HeyApiClient {
13372
13437
  listProjectContextEntries(options) {
13373
13438
  return (options.client ?? this.client).get({ url: "/api/projects/{projectId}/context-entries", ...options });
@@ -13576,6 +13641,10 @@ class BoboddyClient extends HeyApiClient {
13576
13641
  get workItemComments() {
13577
13642
  return this._workItemComments ??= new WorkItemComments({ client: this.client });
13578
13643
  }
13644
+ _workItemBlocks;
13645
+ get workItemBlocks() {
13646
+ return this._workItemBlocks ??= new WorkItemBlocks({ client: this.client });
13647
+ }
13579
13648
  _projectContext;
13580
13649
  get projectContext() {
13581
13650
  return this._projectContext ??= new ProjectContext({ client: this.client });
@@ -16205,6 +16274,140 @@ function extractInlineComputedSignals(policy) {
16205
16274
  return [...byKey.values()];
16206
16275
  }
16207
16276
 
16277
+ // src/definitions/advancement-policies/cohort-advancement-policy.ts
16278
+ var cohortAdvancementEventTypeValues = ["continue", "block"];
16279
+ function serializeCohortCondition(condition) {
16280
+ if (condition._tag === "signal") {
16281
+ return {
16282
+ fact: typeof condition.signal === "string" ? condition.signal : condition.signal.key,
16283
+ operator: condition.operator,
16284
+ value: condition.value
16285
+ };
16286
+ }
16287
+ if (condition._tag === "all") {
16288
+ return { all: condition.conditions.map(serializeCohortCondition) };
16289
+ }
16290
+ return { any: condition.conditions.map(serializeCohortCondition) };
16291
+ }
16292
+ function serializeCohortRule(rule) {
16293
+ return {
16294
+ conditions: { [rule.mode]: rule.conditions.map(serializeCohortCondition) },
16295
+ event: {
16296
+ type: rule.outcome,
16297
+ ...rule.outcomeJson ? { params: rule.outcomeJson } : {}
16298
+ }
16299
+ };
16300
+ }
16301
+ function serializeCohortAdvancementPolicy(policy) {
16302
+ if (!policy) {
16303
+ return { rules: [], defaultEventType: "continue", defaultEventParamsJson: null };
16304
+ }
16305
+ return {
16306
+ rules: (policy.rules ?? []).map(serializeCohortRule),
16307
+ defaultEventType: policy.default,
16308
+ defaultEventParamsJson: policy.defaultParamsJson ?? null
16309
+ };
16310
+ }
16311
+ function visitCohortSignalConditions(conditions, visit) {
16312
+ for (const c of conditions) {
16313
+ if (c._tag === "signal") {
16314
+ visit(c);
16315
+ } else {
16316
+ visitCohortSignalConditions(c.conditions, visit);
16317
+ }
16318
+ }
16319
+ }
16320
+ function isSameStepSignalsListDefinition(a, b) {
16321
+ return JSON.stringify(a.ops) === JSON.stringify(b.ops) && JSON.stringify(a.reducer) === JSON.stringify(b.reducer);
16322
+ }
16323
+ function extractInlineStepSignalsListDefinitions(policy) {
16324
+ if (!policy?.rules)
16325
+ return [];
16326
+ const byKey = new Map;
16327
+ for (const rule of policy.rules) {
16328
+ visitCohortSignalConditions(rule.conditions, (cond) => {
16329
+ if (typeof cond.signal === "string")
16330
+ return;
16331
+ const inline = cond.signal;
16332
+ const def = {
16333
+ key: inline.key,
16334
+ ops: inline.ops,
16335
+ reducer: inline.reducer
16336
+ };
16337
+ const existing = byKey.get(def.key);
16338
+ if (existing) {
16339
+ if (!isSameStepSignalsListDefinition(existing, def)) {
16340
+ throw new Error(`Conflicting inline stepSignalsList definitions for key "${def.key}"`);
16341
+ }
16342
+ return;
16343
+ }
16344
+ byKey.set(def.key, def);
16345
+ });
16346
+ }
16347
+ return [...byKey.values()];
16348
+ }
16349
+
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
+
16208
16411
  // src/definitions/pipelines/define-pipeline.ts
16209
16412
  function serializeBinding(binding) {
16210
16413
  if (binding.source === "pipeline_input") {
@@ -16223,15 +16426,42 @@ function serializeBinding(binding) {
16223
16426
  if (binding.source === "literal") {
16224
16427
  return { source: "literal", value: binding.value };
16225
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
+ }
16226
16435
  return { source: "step_output", stepKey: binding.step.key };
16227
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
+ }
16228
16451
  function buildPipelineSpec(config2) {
16229
- const steps = config2.steps;
16452
+ const nodes = config2.nodes;
16230
16453
  const stepDefMap = new Map;
16231
- for (const stepConfig of steps) {
16232
- const mapKey = `${stepConfig.step.key}@v${String(stepConfig.step.version)}`;
16454
+ const registerStepDef = (step) => {
16455
+ const mapKey = `${step.key}@v${String(step.version)}`;
16233
16456
  if (!stepDefMap.has(mapKey)) {
16234
- stepDefMap.set(mapKey, stepConfig.step);
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);
16235
16465
  }
16236
16466
  }
16237
16467
  let inputSchemaJson = null;
@@ -16242,6 +16472,46 @@ function buildPipelineSpec(config2) {
16242
16472
  inputSchemaJson = null;
16243
16473
  }
16244
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);
16245
16515
  return {
16246
16516
  key: config2.key,
16247
16517
  name: config2.name,
@@ -16250,31 +16520,8 @@ function buildPipelineSpec(config2) {
16250
16520
  status: config2.status ?? "active",
16251
16521
  inputSchemaJson,
16252
16522
  _stepDefinitions: [...stepDefMap.values()],
16253
- steps: steps.map((stepConfig, index) => {
16254
- const autoBindings = {
16255
- workItemTitle: { source: "work_item", field: "title" },
16256
- workItemDescription: { source: "work_item", field: "description" }
16257
- };
16258
- const pipelineBindings = {};
16259
- for (const [key, binding] of Object.entries(config2.pipelineInputBindings ?? {})) {
16260
- pipelineBindings[key] = serializeBinding(binding);
16261
- }
16262
- const explicitStepBindings = Object.fromEntries(Object.entries(stepConfig.input ?? {}).filter((entry) => entry[1] !== undefined).map(([key, binding]) => [key, serializeBinding(binding)]));
16263
- return {
16264
- stepKey: stepConfig.step.key,
16265
- stepName: stepConfig.step.name,
16266
- stepDescription: stepConfig.step.description,
16267
- position: index + 1,
16268
- inputBindingsJson: {
16269
- ...autoBindings,
16270
- ...pipelineBindings,
16271
- ...explicitStepBindings
16272
- },
16273
- timeoutSeconds: stepConfig.timeout ?? null,
16274
- advancementPolicyDefinition: serializeAdvancementPolicy(stepConfig.advancement),
16275
- computedSignalDefinitions: extractInlineComputedSignals(stepConfig.advancement)
16276
- };
16277
- })
16523
+ nodeDefinitions,
16524
+ dependencyEdges
16278
16525
  };
16279
16526
  }
16280
16527
  // src/definitions/advancement-policies/fluent-rules.ts
@@ -16445,7 +16692,10 @@ function makeStepInputCtx(inputSchema) {
16445
16692
  output(step) {
16446
16693
  return { source: "step_output", step };
16447
16694
  },
16448
- literal: literal2
16695
+ literal: literal2,
16696
+ signalsList(fanOutStep) {
16697
+ return { source: "signals_list", fanOutStep };
16698
+ }
16449
16699
  };
16450
16700
  }
16451
16701
  function literal2(value) {
@@ -16490,61 +16740,247 @@ function mergeStepBindings(pipelineBindings, explicitBindings) {
16490
16740
  return Object.keys(merged).length > 0 ? merged : undefined;
16491
16741
  }
16492
16742
 
16493
- // src/definitions/pipelines/builder.ts
16494
- class PipelineStepAdvancementBuilder {
16495
- inputSchema;
16496
- meta;
16497
- steps;
16498
- pipelineInputBindings;
16499
- pipelineStepInputBindings;
16500
- constructor(inputSchema, meta3, steps, pipelineInputBindings = {}, pipelineStepInputBindings = {}) {
16501
- this.inputSchema = inputSchema;
16502
- this.meta = meta3;
16503
- this.steps = steps;
16504
- this.pipelineInputBindings = pipelineInputBindings;
16505
- this.pipelineStepInputBindings = pipelineStepInputBindings;
16506
- }
16507
- advance(callback) {
16508
- const last = this.steps.at(-1);
16509
- if (!last)
16510
- throw new Error("Internal error: no steps available");
16511
- const ctx = makeAdvanceCtx();
16512
- const result = callback(ctx);
16513
- const policy = {
16514
- defaultOutcome: result.default,
16515
- ...result.rules !== undefined ? { rules: result.rules } : {}
16743
+ // src/definitions/advancement-policies/cohort-fluent-rules.ts
16744
+ var LEAF_BRAND2 = Symbol("boboddy.cohortRule.leaf");
16745
+ var GROUP_BRAND2 = Symbol("boboddy.cohortRule.group");
16746
+ function createCohortSignalRef(signal2) {
16747
+ const leaf = (operator, value) => {
16748
+ const condition = {
16749
+ _tag: "signal",
16750
+ signal: signal2,
16751
+ operator,
16752
+ value
16516
16753
  };
16517
- last.advancement = policy;
16518
- return new PipelineStepBuilder(this.inputSchema, this.meta, this.steps, this.pipelineInputBindings, this.pipelineStepInputBindings);
16519
- }
16754
+ return {
16755
+ [LEAF_BRAND2]: condition,
16756
+ then(outcome, paramsJson) {
16757
+ return {
16758
+ _tag: "rule",
16759
+ mode: "all",
16760
+ conditions: [condition],
16761
+ outcome,
16762
+ ...paramsJson ? { outcomeJson: paramsJson } : {}
16763
+ };
16764
+ }
16765
+ };
16766
+ };
16767
+ return {
16768
+ eq: (v) => leaf("equal", v),
16769
+ ne: (v) => leaf("notEqual", v),
16770
+ gt: (v) => leaf("greaterThan", v),
16771
+ gte: (v) => leaf("greaterThanInclusive", v),
16772
+ lt: (v) => leaf("lessThan", v),
16773
+ lte: (v) => leaf("lessThanInclusive", v),
16774
+ in: (vs) => leaf("in", vs),
16775
+ notIn: (vs) => leaf("notIn", vs),
16776
+ contains: (v) => leaf("contains", v),
16777
+ doesNotContain: (v) => leaf("doesNotContain", v)
16778
+ };
16779
+ }
16780
+ function createCohortLeafFromCondition(condition) {
16781
+ return {
16782
+ [LEAF_BRAND2]: condition,
16783
+ then(outcome, paramsJson) {
16784
+ return {
16785
+ _tag: "rule",
16786
+ mode: "all",
16787
+ conditions: [condition],
16788
+ outcome,
16789
+ ...paramsJson ? { outcomeJson: paramsJson } : {}
16790
+ };
16791
+ }
16792
+ };
16793
+ }
16794
+ function extractCohortCondition(ref) {
16795
+ if (LEAF_BRAND2 in ref)
16796
+ return ref[LEAF_BRAND2];
16797
+ const group = ref[GROUP_BRAND2];
16798
+ return group.mode === "all" ? { _tag: "all", conditions: group.conditions } : { _tag: "any", conditions: group.conditions };
16799
+ }
16800
+ function createCohortGroup(mode, refs) {
16801
+ const conditions = refs.map(extractCohortCondition);
16802
+ return {
16803
+ [GROUP_BRAND2]: { mode, conditions },
16804
+ then(outcome, paramsJson) {
16805
+ return {
16806
+ _tag: "rule",
16807
+ mode,
16808
+ conditions,
16809
+ outcome,
16810
+ ...paramsJson ? { outcomeJson: paramsJson } : {}
16811
+ };
16812
+ }
16813
+ };
16814
+ }
16815
+ function makeKeyedCohortSignalRef(key) {
16816
+ return createCohortSignalRef(key);
16817
+ }
16818
+ function makeAdvanceEachCtx() {
16819
+ return {
16820
+ signal: (key) => makeKeyedCohortSignalRef(key),
16821
+ stepSignals: new Proxy({}, {
16822
+ get(_, key) {
16823
+ if (typeof key === "string")
16824
+ return makeKeyedCohortSignalRef(key);
16825
+ return;
16826
+ }
16827
+ }),
16828
+ all: (...refs) => createCohortGroup("all", refs),
16829
+ any: (...refs) => createCohortGroup("any", refs)
16830
+ };
16831
+ }
16832
+ var branchOutcomeValues = [
16833
+ "continue",
16834
+ "block",
16835
+ "error",
16836
+ "abandoned"
16837
+ ];
16838
+ function summarizeTransformOp(op) {
16839
+ if (op.op === "filter") {
16840
+ return `filter_${op.operator}_${JSON.stringify(op.value)}`;
16841
+ }
16842
+ if (op.op === "sortBy") {
16843
+ return `sortBy_${op.direction}`;
16844
+ }
16845
+ return "unique";
16846
+ }
16847
+ function deriveStepSignalsListKey(ops, reducer) {
16848
+ const pluck = ops.find((op) => op.op === "pluck");
16849
+ const base = `${reducer.op}_${pluck?.signalKey ?? "value"}`;
16850
+ const extras = ops.filter((op) => op.op !== "pluck").map(summarizeTransformOp);
16851
+ const reducerExtra = reducer.op === "join" ? `sep_${reducer.separator}` : null;
16852
+ const suffixParts = [...extras, ...reducerExtra ? [reducerExtra] : []];
16853
+ return suffixParts.length > 0 ? `${base}_${suffixParts.join("_")}` : base;
16854
+ }
16855
+ function createStepSignalsListBuilder(ops) {
16856
+ const withOp = (op) => createStepSignalsListBuilder([...ops, op]);
16857
+ const reduce = (reducer) => {
16858
+ const token = {
16859
+ _tag: "step_signals_list",
16860
+ key: deriveStepSignalsListKey(ops, reducer),
16861
+ ops: [...ops],
16862
+ reducer
16863
+ };
16864
+ return createCohortSignalRef(token);
16865
+ };
16866
+ return {
16867
+ filter: (operator, value) => withOp({ op: "filter", operator, value }),
16868
+ sortBy: (direction = "asc") => withOp({ op: "sortBy", direction }),
16869
+ unique: () => withOp({ op: "unique" }),
16870
+ count: () => reduce({ op: "count" }),
16871
+ sum: () => reduce({ op: "sum" }),
16872
+ avg: () => reduce({ op: "avg" }),
16873
+ min: () => reduce({ op: "min" }),
16874
+ max: () => reduce({ op: "max" }),
16875
+ booleanAll: () => reduce({ op: "booleanAll" }),
16876
+ booleanAny: () => reduce({ op: "booleanAny" }),
16877
+ join: (separator = ",") => reduce({ op: "join", separator }),
16878
+ first: () => reduce({ op: "first" }),
16879
+ last: () => reduce({ op: "last" })
16880
+ };
16881
+ }
16882
+ function makeAdvanceAllCtx() {
16883
+ return {
16884
+ branchOutcomes: {
16885
+ total: () => createCohortSignalRef("branchCount"),
16886
+ count: (outcome) => createCohortSignalRef(`${outcome}Count`),
16887
+ every: (outcome) => createCohortLeafFromCondition({
16888
+ _tag: "signal",
16889
+ signal: `${outcome}Count`,
16890
+ operator: "equal",
16891
+ value: { fact: "branchCount" }
16892
+ }),
16893
+ some: (outcome) => createCohortLeafFromCondition({
16894
+ _tag: "signal",
16895
+ signal: `${outcome}Count`,
16896
+ operator: "greaterThan",
16897
+ value: 0
16898
+ })
16899
+ },
16900
+ stepSignalsList: {
16901
+ pluck: (signalKey) => createStepSignalsListBuilder([{ op: "pluck", signalKey }])
16902
+ },
16903
+ all: (...refs) => createCohortGroup("all", refs),
16904
+ any: (...refs) => createCohortGroup("any", refs)
16905
+ };
16906
+ }
16907
+
16908
+ // src/definitions/pipelines/fan-out-builder.ts
16909
+ function beginFanOut(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings, step, config2) {
16910
+ const baseCtx = makeStepInputCtx(inputSchema);
16911
+ const ctx = {
16912
+ ...baseCtx,
16913
+ item: { source: "fan_out_item" }
16914
+ };
16915
+ const rawInput = config2.input ? config2.input(ctx) : {};
16916
+ const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
16917
+ const fanOutNodeConfig = {
16918
+ nodeType: "fanOut",
16919
+ fanOutStep: step,
16920
+ overSignalKey: config2.over,
16921
+ ...input ? { input } : {},
16922
+ ...config2.timeout !== undefined ? { timeout: config2.timeout } : {}
16923
+ };
16924
+ const advanceEachCtx = makeAdvanceEachCtx();
16925
+ const advanceEachResult = config2.advance(advanceEachCtx);
16926
+ const advanceEachPolicy = {
16927
+ default: advanceEachResult.default,
16928
+ ...advanceEachResult.rules !== undefined ? { rules: advanceEachResult.rules } : {}
16929
+ };
16930
+ fanOutNodeConfig.advanceEach = advanceEachPolicy;
16931
+ const cohortGateNodeConfig = {
16932
+ nodeType: "cohortGate",
16933
+ nodeKey: `${step.key}__cohortGate`
16934
+ };
16935
+ const advanceAllCtx = makeAdvanceAllCtx();
16936
+ const advanceAllResult = config2.advanceAll(advanceAllCtx);
16937
+ const advanceAllPolicy = {
16938
+ default: advanceAllResult.default,
16939
+ ...advanceAllResult.rules !== undefined ? { rules: advanceAllResult.rules } : {}
16940
+ };
16941
+ cohortGateNodeConfig.advanceAll = advanceAllPolicy;
16942
+ nodes.push(fanOutNodeConfig, cohortGateNodeConfig);
16943
+ return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
16944
+ }
16945
+
16946
+ // src/definitions/pipelines/builder.ts
16947
+ function pushStep(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings, step, rawOptions) {
16948
+ const options = rawOptions;
16949
+ const ctx = makeStepInputCtx(inputSchema);
16950
+ const rawInput = options.input ? options.input(ctx) : {};
16951
+ const input = mergeStepBindings(pipelineStepInputBindings, normalizeInputMapping(rawInput));
16952
+ const stepConfig = { step, input };
16953
+ if (options.timeout !== undefined)
16954
+ stepConfig.timeout = options.timeout;
16955
+ const advanceCtx = makeAdvanceCtx();
16956
+ const result = options.advance(advanceCtx);
16957
+ const policy = {
16958
+ defaultOutcome: result.default,
16959
+ ...result.rules !== undefined ? { rules: result.rules } : {}
16960
+ };
16961
+ stepConfig.advancement = policy;
16962
+ nodes.push(stepConfig);
16963
+ return new PipelineStepBuilder(inputSchema, meta3, nodes, pipelineInputBindings, pipelineStepInputBindings);
16520
16964
  }
16521
16965
 
16522
16966
  class PipelineStepBuilder {
16523
16967
  inputSchema;
16524
16968
  meta;
16525
- steps;
16969
+ nodes;
16526
16970
  pipelineInputBindings;
16527
16971
  pipelineStepInputBindings;
16528
- constructor(inputSchema, meta3, steps, pipelineInputBindings = {}, pipelineStepInputBindings = {}) {
16972
+ constructor(inputSchema, meta3, nodes, pipelineInputBindings = {}, pipelineStepInputBindings = {}) {
16529
16973
  this.inputSchema = inputSchema;
16530
16974
  this.meta = meta3;
16531
- this.steps = steps;
16975
+ this.nodes = nodes;
16532
16976
  this.pipelineInputBindings = pipelineInputBindings;
16533
16977
  this.pipelineStepInputBindings = pipelineStepInputBindings;
16534
16978
  }
16535
- step(step, mapper, configFn) {
16536
- const ctx = makeStepInputCtx(this.inputSchema);
16537
- const rawInput = mapper ? mapper(ctx) : {};
16538
- const input = mergeStepBindings(this.pipelineStepInputBindings, normalizeInputMapping(rawInput));
16539
- const stepConfig = { step, input };
16540
- if (configFn) {
16541
- const cfg = {};
16542
- configFn(cfg);
16543
- if (cfg.timeout !== undefined)
16544
- stepConfig.timeout = cfg.timeout;
16545
- }
16546
- this.steps.push(stepConfig);
16547
- return new PipelineStepAdvancementBuilder(this.inputSchema, this.meta, this.steps, this.pipelineInputBindings, this.pipelineStepInputBindings);
16979
+ step(step, options) {
16980
+ return pushStep(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, options);
16981
+ }
16982
+ fanOutStep(step, config2) {
16983
+ return beginFanOut(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, config2);
16548
16984
  }
16549
16985
  build() {
16550
16986
  const config2 = {
@@ -16554,7 +16990,7 @@ class PipelineStepBuilder {
16554
16990
  version: this.meta.version,
16555
16991
  status: this.meta.status,
16556
16992
  input: this.inputSchema,
16557
- steps: this.steps,
16993
+ nodes: this.nodes,
16558
16994
  pipelineInputBindings: this.pipelineInputBindings
16559
16995
  };
16560
16996
  return buildPipelineSpec(config2);
@@ -16564,7 +17000,7 @@ class PipelineStepBuilder {
16564
17000
  class PipelineBuilder {
16565
17001
  inputSchema;
16566
17002
  meta;
16567
- steps = [];
17003
+ nodes = [];
16568
17004
  pipelineInputBindings;
16569
17005
  pipelineStepInputBindings;
16570
17006
  constructor(meta3) {
@@ -16589,19 +17025,8 @@ class PipelineBuilder {
16589
17025
  }
16590
17026
  this.pipelineStepInputBindings = resolveAdditionalStepInputBindings("additionalStepInput", additionalStepInput);
16591
17027
  }
16592
- step(step, mapper, configFn) {
16593
- const ctx = makeStepInputCtx(this.inputSchema);
16594
- const rawInput = mapper ? mapper(ctx) : {};
16595
- const input = mergeStepBindings(this.pipelineStepInputBindings, normalizeInputMapping(rawInput));
16596
- const stepConfig = { step, input };
16597
- if (configFn) {
16598
- const cfg = {};
16599
- configFn(cfg);
16600
- if (cfg.timeout !== undefined)
16601
- stepConfig.timeout = cfg.timeout;
16602
- }
16603
- this.steps.push(stepConfig);
16604
- return new PipelineStepAdvancementBuilder(this.inputSchema, this.meta, this.steps, this.pipelineInputBindings, this.pipelineStepInputBindings);
17028
+ step(step, options) {
17029
+ return pushStep(this.inputSchema, this.meta, this.nodes, this.pipelineInputBindings, this.pipelineStepInputBindings, step, options);
16605
17030
  }
16606
17031
  }
16607
17032
  function pipeline(meta3) {
@@ -16631,23 +17056,35 @@ var buildPipelineDefinitionsClient = (pipelineDefinitions) => {
16631
17056
  stepDefMap.set(s.key, s);
16632
17057
  }
16633
17058
  }
16634
- const stepDefinitions = spec.steps.map((step) => {
16635
- const stepDef = stepDefMap.get(step.stepKey);
17059
+ const nonStepNode = spec.nodeDefinitions.find((node) => node.kind !== "step");
17060
+ if (nonStepNode) {
17061
+ 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.`);
17062
+ }
17063
+ const ordered = tryOrderChainNodeDefinitions(spec.nodeDefinitions, spec.dependencyEdges);
17064
+ if (ordered === null) {
17065
+ throw new Error(`Pipeline "${spec.key}" has a malformed step graph: its node and ` + `dependency-edge definitions do not form a single connected, ` + `acyclic chain. This should not happen from the \`pipeline()\` ` + `builder \u2014 check for hand-edited or generated pipeline specs.`);
17066
+ }
17067
+ const stepDefinitions = ordered.map((node, index) => {
17068
+ const stepKey = node.stepKey;
17069
+ if (!stepKey) {
17070
+ throw new Error(`Node "${node.nodeKey}" in pipeline "${spec.key}" has no stepKey.`);
17071
+ }
17072
+ const stepDef = stepDefMap.get(stepKey);
16636
17073
  if (!stepDef) {
16637
- throw new Error(`Step "${step.stepKey}" referenced in pipeline "${spec.key}" was not found on the server. ` + `Run \`boboddy steps push\` first to push your step definitions.`);
17074
+ throw new Error(`Step "${stepKey}" referenced in pipeline "${spec.key}" was not found on the server. ` + `Run \`boboddy steps push\` first to push your step definitions.`);
16638
17075
  }
16639
17076
  return {
16640
17077
  stepDefinitionId: stepDef.id,
16641
17078
  stepDefinitionVersion: stepDef.version,
16642
- key: step.stepKey,
16643
- name: step.stepName,
16644
- description: step.stepDescription,
16645
- position: step.position,
16646
- inputBindingsJson: step.inputBindingsJson,
16647
- timeoutSeconds: step.timeoutSeconds,
17079
+ key: node.nodeKey,
17080
+ name: node.stepName ?? "",
17081
+ description: node.stepDescription ?? null,
17082
+ position: index + 1,
17083
+ inputBindingsJson: node.inputBindingsJson ?? {},
17084
+ timeoutSeconds: node.timeoutSeconds ?? null,
16648
17085
  retryPolicyJson: null,
16649
- advancementPolicyDefinition: step.advancementPolicyDefinition,
16650
- computedSignalDefinitions: step.computedSignalDefinitions
17086
+ advancementPolicyDefinition: node.advancementPolicyDefinition,
17087
+ computedSignalDefinitions: node.computedSignalDefinitions ?? []
16651
17088
  };
16652
17089
  });
16653
17090
  const body = {
@@ -16682,7 +17119,12 @@ function makeLeaf(fact, path, operator, value) {
16682
17119
  return {
16683
17120
  _condition: condition,
16684
17121
  then(outcome) {
16685
- return { _tag: "assignment_rule", conditions: [condition], mode: "all", outcome };
17122
+ return {
17123
+ _tag: "assignment_rule",
17124
+ conditions: [condition],
17125
+ mode: "all",
17126
+ outcome
17127
+ };
16686
17128
  }
16687
17129
  };
16688
17130
  }
@@ -16701,7 +17143,7 @@ function makeFieldRef(fact, path) {
16701
17143
  };
16702
17144
  }
16703
17145
  function makeAssign(pipeline2) {
16704
- if (typeof pipeline2 !== "object" || typeof pipeline2["key"] !== "string" || !Array.isArray(pipeline2["steps"])) {
17146
+ if (typeof pipeline2 !== "object" || typeof pipeline2["key"] !== "string" || !Array.isArray(pipeline2["nodeDefinitions"])) {
16705
17147
  throw new Error("assign() requires a pipeline spec produced by pipeline().build(). " + "Pass the default-exported value from a pipeline definition file.");
16706
17148
  }
16707
17149
  return { _tag: "assign", pipeline: pipeline2 };
@@ -16789,7 +17231,7 @@ function serializeDefaultPipelineAssignment(spec) {
16789
17231
  return serializeAssignmentRule(rule);
16790
17232
  });
16791
17233
  return {
16792
- linearPipelineDefinitionKey: primaryKey,
17234
+ pipelineDefinitionKey: primaryKey,
16793
17235
  rulesJson: { rules: serializedRules },
16794
17236
  defaultEventType: defaultType,
16795
17237
  defaultEventParamsJson: defaultParams,
@@ -17087,6 +17529,8 @@ function checkHealthChecks(steps) {
17087
17529
  return issues;
17088
17530
  }
17089
17531
  function routeTargets(policy) {
17532
+ if (!policy)
17533
+ return [];
17090
17534
  const keys = [];
17091
17535
  if (policy.defaultEventType === "route" && typeof policy.defaultEventParamsJson?.["pipelineKey"] === "string") {
17092
17536
  keys.push(policy.defaultEventParamsJson["pipelineKey"]);
@@ -17105,36 +17549,42 @@ function checkRouteTargets(pipelines, knownPipelineKeys) {
17105
17549
  ...knownPipelineKeys
17106
17550
  ]);
17107
17551
  for (const pipeline2 of pipelines) {
17108
- for (const step of pipeline2.steps) {
17109
- for (const target of routeTargets(step.advancementPolicyDefinition)) {
17552
+ for (const node of pipeline2.nodeDefinitions) {
17553
+ for (const target of routeTargets(node.advancementPolicyDefinition)) {
17110
17554
  if (known.has(target))
17111
17555
  continue;
17112
17556
  issues.push({
17113
17557
  check: "route-target",
17114
- message: `Pipeline "${pipeline2.key}" step "${step.stepKey}" 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.`
17558
+ 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.`
17115
17559
  });
17116
17560
  }
17117
17561
  }
17118
17562
  }
17119
17563
  return issues;
17120
17564
  }
17121
- function executionRanks(steps) {
17122
- const positions = steps.map((step) => step.position);
17123
- const usable = positions.every((value) => Number.isInteger(value) && value > 0) && new Set(positions).size === positions.length;
17124
- const indexes = steps.map((_, index) => index);
17125
- const ordered = usable ? [...indexes].sort((left, right) => (positions[left] ?? 0) - (positions[right] ?? 0)) : indexes;
17565
+ function executionRanks(nodeDefinitions, dependencyEdges) {
17566
+ const indexes = nodeDefinitions.map((_, index) => index);
17567
+ const orderedNodes = tryOrderChainNodeDefinitions(nodeDefinitions, dependencyEdges);
17568
+ if (orderedNodes === null) {
17569
+ return new Map(indexes.map((index, rank) => [index, rank]));
17570
+ }
17571
+ const indexByNodeKey = new Map(nodeDefinitions.map((node, index) => [node.nodeKey, index]));
17572
+ const ordered = orderedNodes.map((node) => indexByNodeKey.get(node.nodeKey) ?? 0);
17126
17573
  return new Map(ordered.map((index, rank) => [index, rank]));
17127
17574
  }
17128
17575
  function bindingSource(binding) {
17129
17576
  if (binding.source === "step_signal") {
17130
- return { stepKey: binding.stepKey, signalKey: binding.signalKey };
17577
+ return { stepKey: binding.stepKey, signalKey: binding.signalKey, kind: "signal" };
17131
17578
  }
17132
17579
  if (binding.source === "step_output") {
17133
- return { stepKey: binding.stepKey, signalKey: null };
17580
+ return { stepKey: binding.stepKey, signalKey: null, kind: "output" };
17581
+ }
17582
+ if (binding.source === "signals_list") {
17583
+ return { stepKey: binding.stepKey, signalKey: null, kind: "signals_list" };
17134
17584
  }
17135
17585
  return null;
17136
17586
  }
17137
- function declaredSignalKeys(stepKey, stepsByKey, pipelineSteps) {
17587
+ function declaredSignalKeys(stepKey, stepsByKey, pipelineNodeDefinitions) {
17138
17588
  const specs = stepsByKey.get(stepKey);
17139
17589
  if (!specs || specs.length === 0)
17140
17590
  return null;
@@ -17143,10 +17593,10 @@ function declaredSignalKeys(stepKey, stepsByKey, pipelineSteps) {
17143
17593
  for (const signal2 of spec.signalExtractorDefinitions)
17144
17594
  keys.add(signal2.key);
17145
17595
  }
17146
- for (const step of pipelineSteps) {
17147
- if (step.stepKey !== stepKey)
17596
+ for (const node of pipelineNodeDefinitions) {
17597
+ if (node.stepKey !== stepKey)
17148
17598
  continue;
17149
- for (const computed of step.computedSignalDefinitions)
17599
+ for (const computed of node.computedSignalDefinitions ?? [])
17150
17600
  keys.add(computed.key);
17151
17601
  }
17152
17602
  return [...keys];
@@ -17154,18 +17604,18 @@ function declaredSignalKeys(stepKey, stepsByKey, pipelineSteps) {
17154
17604
  function checkSignalBindings(pipelines, stepsByKey) {
17155
17605
  const issues = [];
17156
17606
  for (const pipeline2 of pipelines) {
17157
- const ranks = executionRanks(pipeline2.steps);
17158
- const order = [...pipeline2.steps.keys()].sort((left, right) => (ranks.get(left) ?? 0) - (ranks.get(right) ?? 0)).map((index) => pipeline2.steps[index]?.stepKey ?? "");
17607
+ const ranks = executionRanks(pipeline2.nodeDefinitions, pipeline2.dependencyEdges);
17608
+ const order = [...pipeline2.nodeDefinitions.keys()].sort((left, right) => (ranks.get(left) ?? 0) - (ranks.get(right) ?? 0)).map((index) => pipeline2.nodeDefinitions[index]?.stepKey ?? "");
17159
17609
  const orderHint = `Steps in "${pipeline2.key}", in order: ${order.join(" \u2192 ")}.`;
17160
- pipeline2.steps.forEach((step, index) => {
17610
+ pipeline2.nodeDefinitions.forEach((node, index) => {
17161
17611
  const consumerRank = ranks.get(index) ?? index;
17162
- const where = `Pipeline "${pipeline2.key}" step "${step.stepKey}"`;
17163
- for (const [field, binding] of Object.entries(step.inputBindingsJson)) {
17612
+ const where = `Pipeline "${pipeline2.key}" step "${node.stepKey ?? node.nodeKey}"`;
17613
+ for (const [field, binding] of Object.entries(node.inputBindingsJson ?? {})) {
17164
17614
  const source = bindingSource(binding);
17165
17615
  if (!source)
17166
17616
  continue;
17167
- const what = source.signalKey ? `binds input "${field}" to signal "${source.signalKey}" of step "${source.stepKey}"` : `binds input "${field}" to the output of step "${source.stepKey}"`;
17168
- const producerRanks = pipeline2.steps.map((candidate, candidateIndex) => candidate.stepKey === source.stepKey ? ranks.get(candidateIndex) ?? candidateIndex : null).filter((rank) => rank !== null);
17617
+ 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}"`;
17618
+ const producerRanks = pipeline2.nodeDefinitions.map((candidate, candidateIndex) => candidate.stepKey === source.stepKey ? ranks.get(candidateIndex) ?? candidateIndex : null).filter((rank) => rank !== null);
17169
17619
  if (producerRanks.length === 0) {
17170
17620
  issues.push({
17171
17621
  check: "signal-binding",
@@ -17182,7 +17632,7 @@ function checkSignalBindings(pipelines, stepsByKey) {
17182
17632
  }
17183
17633
  if (source.signalKey === null)
17184
17634
  continue;
17185
- const available = declaredSignalKeys(source.stepKey, stepsByKey, pipeline2.steps);
17635
+ const available = declaredSignalKeys(source.stepKey, stepsByKey, pipeline2.nodeDefinitions);
17186
17636
  if (available === null || available.includes(source.signalKey))
17187
17637
  continue;
17188
17638
  issues.push({
@@ -17233,7 +17683,7 @@ function isPipelineDefinitionSpec(value) {
17233
17683
  if (typeof value !== "object" || value === null)
17234
17684
  return false;
17235
17685
  const obj = value;
17236
- return typeof obj["key"] === "string" && typeof obj["name"] === "string" && typeof obj["version"] === "number" && Array.isArray(obj["steps"]);
17686
+ return typeof obj["key"] === "string" && typeof obj["name"] === "string" && typeof obj["version"] === "number" && Array.isArray(obj["nodeDefinitions"]);
17237
17687
  }
17238
17688
  async function importModule(path) {
17239
17689
  return await import(pathToFileURL(path).href);
@@ -17331,7 +17781,7 @@ async function syncDefaultPipelineAssignment(spec, opts, headers, pipelinesClien
17331
17781
  p.id
17332
17782
  ]));
17333
17783
  const referencedKeys = new Set;
17334
- referencedKeys.add(serialized.linearPipelineDefinitionKey);
17784
+ referencedKeys.add(serialized.pipelineDefinitionKey);
17335
17785
  for (const rule of serialized.rulesJson.rules) {
17336
17786
  if (rule.event.type === "assign" && typeof rule.event.params?.["pipelineKey"] === "string") {
17337
17787
  referencedKeys.add(rule.event.params["pipelineKey"]);
@@ -17342,9 +17792,9 @@ async function syncDefaultPipelineAssignment(spec, opts, headers, pipelinesClien
17342
17792
  throw new Error(`${DEFAULT_PIPELINE_ASSIGNMENT_FILENAME} references pipeline "${key}", ` + `but no pipeline with that key was found on the server. ` + `Push the pipeline first with \`boboddy pipelines push\`.`);
17343
17793
  }
17344
17794
  }
17345
- const linearPipelineDefinitionId = pipelineKeyToId.get(serialized.linearPipelineDefinitionKey);
17346
- if (!linearPipelineDefinitionId) {
17347
- throw new Error(`Pipeline key "${serialized.linearPipelineDefinitionKey}" was not found on the server.`);
17795
+ const pipelineDefinitionId = pipelineKeyToId.get(serialized.pipelineDefinitionKey);
17796
+ if (!pipelineDefinitionId) {
17797
+ throw new Error(`Pipeline key "${serialized.pipelineDefinitionKey}" was not found on the server.`);
17348
17798
  }
17349
17799
  const resolvedRules = serialized.rulesJson.rules.map((rule) => {
17350
17800
  if (rule.event.type === "assign" && typeof rule.event.params?.["pipelineKey"] === "string") {
@@ -17373,7 +17823,7 @@ async function syncDefaultPipelineAssignment(spec, opts, headers, pipelinesClien
17373
17823
  path: { projectId: opts.projectId },
17374
17824
  body: {
17375
17825
  defaultPipelineAssignment: {
17376
- linearPipelineDefinitionId,
17826
+ pipelineDefinitionId,
17377
17827
  rulesJson: { rules: resolvedRules },
17378
17828
  defaultEventType: serialized.defaultEventType,
17379
17829
  defaultEventParamsJson: serialized.defaultEventParamsJson,
@@ -17385,7 +17835,7 @@ async function syncDefaultPipelineAssignment(spec, opts, headers, pipelinesClien
17385
17835
  if (result.error) {
17386
17836
  throw new Error(`Failed to update project default pipeline assignment: ${JSON.stringify(result.error)}`);
17387
17837
  }
17388
- log(`\u2713 default pipeline assignment \u2192 synced (primary pipeline: ${serialized.linearPipelineDefinitionKey})`);
17838
+ log(`\u2713 default pipeline assignment \u2192 synced (primary pipeline: ${serialized.pipelineDefinitionKey})`);
17389
17839
  return true;
17390
17840
  }
17391
17841
  export {