duron 0.3.0-beta.3 → 0.3.0-beta.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/adapters/postgres/base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/adapters/postgres/base.ts"],"names":[],"mappings":"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"}
@@ -539,7 +539,9 @@ export class PostgresBaseAdapter extends Adapter {
539
539
  step_existed AS (
540
540
  SELECT EXISTS(
541
541
  SELECT 1 FROM ${this.tables.jobStepsTable} s
542
- WHERE s.job_id = ${jobId} AND s.name = ${name}
542
+ WHERE s.job_id = ${jobId}
543
+ AND s.name = ${name}
544
+ AND s.parent_step_id IS NOT DISTINCT FROM ${parentStepId}
543
545
  ) AS existed
544
546
  ),
545
547
  upserted_step AS (
@@ -569,7 +571,7 @@ export class PostgresBaseAdapter extends Adapter {
569
571
  0,
570
572
  NULL
571
573
  WHERE EXISTS (SELECT 1 FROM job_check)
572
- ON CONFLICT (job_id, name) DO UPDATE
574
+ ON CONFLICT (job_id, name, parent_step_id) DO UPDATE
573
575
  SET
574
576
  timeout_ms = ${timeoutMs},
575
577
  expires_at = now() + interval '${sql.raw(timeoutMs.toString())} milliseconds',
@@ -609,6 +611,7 @@ export class PostgresBaseAdapter extends Adapter {
609
611
  INNER JOIN job_check jc ON s.job_id = jc.id
610
612
  WHERE s.job_id = ${jobId}
611
613
  AND s.name = ${name}
614
+ AND s.parent_step_id IS NOT DISTINCT FROM ${parentStepId}
612
615
  AND NOT EXISTS (SELECT 1 FROM final_upserted)
613
616
  )
614
617
  SELECT * FROM final_upserted
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/adapters/postgres/schema.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,UAAU,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA4Eb,IAAI;uBAAS,iBAAiB;2BAAa,MAAM;;;;;;8BAAjD,IAAI;2BAAS,iBAAiB;+BAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwE1F"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/adapters/postgres/schema.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,UAAU,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA4Eb,IAAI;uBAAS,iBAAiB;2BAAa,MAAM;;;;;;8BAAjD,IAAI;2BAAS,iBAAiB;+BAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyE1F"}
@@ -79,7 +79,7 @@ export default function createSchema(schemaName) {
79
79
  index('idx_job_steps_job_status').on(table.job_id, table.status),
80
80
  index('idx_job_steps_job_name').on(table.job_id, table.name),
81
81
  index('idx_job_steps_output_fts').using('gin', sql `to_tsvector('english', ${table.output}::text)`),
82
- unique('unique_job_step_name').on(table.job_id, table.name),
82
+ unique('unique_job_step_name_parent').on(table.job_id, table.name, table.parent_step_id).nullsNotDistinct(),
83
83
  check('job_steps_status_check', sql `${table.status} IN ${sql.raw(`(${STEP_STATUSES.map((s) => `'${s}'`).join(',')})`)}`),
84
84
  ]);
85
85
  const metricsTable = schema.table('metrics', {
@@ -40,7 +40,7 @@ export declare class StepManager {
40
40
  #private;
41
41
  constructor(options: StepManagerOptions);
42
42
  setJobSpan(span: Span): void;
43
- setRunFn(runFn: StepHandlerContext['run']): void;
43
+ setRunFnFactory(factory: (parentStepId: string | null, abortSignal: AbortSignal) => StepHandlerContext['run']): void;
44
44
  createActionContext<TInput extends z.ZodObject, TOutput extends z.ZodObject, TVariables = Record<string, unknown>>(job: {
45
45
  id: string;
46
46
  input: z.infer<TInput>;
@@ -1 +1 @@
1
- {"version":3,"file":"step-manager.d.ts","sourceRoot":"","sources":["../src/step-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B,OAAO,EACL,KAAK,MAAM,EACX,KAAK,oBAAoB,EAGzB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAgC,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAoE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAWlH,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAIpF,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAC7C,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,OAAO,CAAA;CAClB;AAMD,qBAAa,SAAS;;gBAYR,OAAO,EAAE,OAAO;IAoBtB,WAAW,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,MAAM,GAAG,IAAW,EAClC,QAAQ,GAAE,OAAe;;;;;;;;;;IAyBrB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;IAoB7F,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;CAG3E;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,gBAAgB,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAMD,qBAAa,WAAW;;gBAyBV,OAAO,EAAE,kBAAkB;IAmBvC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAQ5B,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC,GAAG,IAAI;IAoBhD,mBAAmB,CAAC,MAAM,SAAS,CAAC,CAAC,SAAS,EAAE,OAAO,SAAS,CAAC,CAAC,SAAS,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/G,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAC9D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAC3C,SAAS,EAAE,UAAU,EACrB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,cAAc,GAC7B,oBAAoB,CAAC,MAAM,EAAE,UAAU,CAAC;IAO3C,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc;IA6BlD,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAQlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAkT7B"}
1
+ {"version":3,"file":"step-manager.d.ts","sourceRoot":"","sources":["../src/step-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B,OAAO,EACL,KAAK,MAAM,EACX,KAAK,oBAAoB,EAGzB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAgC,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAoE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAWlH,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAIpF,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAC7C,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,OAAO,CAAA;CAClB;AAMD,qBAAa,SAAS;;gBAYR,OAAO,EAAE,OAAO;IAoBtB,WAAW,CACf,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,MAAM,GAAG,IAAW,EAClC,QAAQ,GAAE,OAAe;;;;;;;;;;IAyBrB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;IAoB7F,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;CAG3E;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,gBAAgB,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAMD,qBAAa,WAAW;;gBAyBV,OAAO,EAAE,kBAAkB;IAqBvC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAU5B,eAAe,CAAC,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,WAAW,KAAK,kBAAkB,CAAC,KAAK,CAAC,GAAG,IAAI;IAoBpH,mBAAmB,CAAC,MAAM,SAAS,CAAC,CAAC,SAAS,EAAE,OAAO,SAAS,CAAC,CAAC,SAAS,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/G,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAC9D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAC3C,SAAS,EAAE,UAAU,EACrB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,cAAc,GAC7B,oBAAoB,CAAC,MAAM,EAAE,UAAU,CAAC;IAO3C,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc;IA6BlD,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAQlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAkT7B"}
@@ -50,7 +50,7 @@ export class StepManager {
50
50
  #historySteps = new Set();
51
51
  #stepSpans = new Map();
52
52
  #jobSpan = null;
53
- #runFn = null;
53
+ #runFnFactory = null;
54
54
  constructor(options) {
55
55
  this.#jobId = options.jobId;
56
56
  this.#actionName = options.actionName;
@@ -58,18 +58,19 @@ export class StepManager {
58
58
  this.#telemetry = options.telemetry;
59
59
  this.#stepStore = new StepStore(options.adapter);
60
60
  this.#queue = fastq.promise(async (task) => {
61
- if (this.#historySteps.has(task.name)) {
61
+ const stepKey = `${task.parentStepId ?? 'root'}:${task.name}`;
62
+ if (this.#historySteps.has(stepKey)) {
62
63
  throw new StepAlreadyExecutedError(task.name, this.#jobId, this.#actionName);
63
64
  }
64
- this.#historySteps.add(task.name);
65
+ this.#historySteps.add(stepKey);
65
66
  return this.#executeStep(task.name, task.cb, task.options, task.abortSignal, task.parentStepId, task.parallel);
66
67
  }, options.concurrencyLimit);
67
68
  }
68
69
  setJobSpan(span) {
69
70
  this.#jobSpan = span;
70
71
  }
71
- setRunFn(runFn) {
72
- this.#runFn = runFn;
72
+ setRunFnFactory(factory) {
73
+ this.#runFnFactory = factory;
73
74
  }
74
75
  createActionContext(job, action, variables, abortSignal, logger, observeContext) {
75
76
  return new ActionContext(this, job, action, variables, abortSignal, logger, observeContext);
@@ -181,7 +182,7 @@ export class StepManager {
181
182
  });
182
183
  return childPromise;
183
184
  },
184
- run: this.#runFn,
185
+ run: this.#runFnFactory(step.id, childSignal),
185
186
  };
186
187
  try {
187
188
  const abortPromise = waitForAbort(stepSignal);
@@ -310,7 +311,9 @@ class ActionContext {
310
311
  this.#input = job.input ?? {};
311
312
  this.step = this.step.bind(this);
312
313
  this.run = this.run.bind(this);
313
- this.#stepManager.setRunFn(this.run);
314
+ this.#stepManager.setRunFnFactory((parentStepId, abortSignal) => {
315
+ return (stepDef, input, options) => this.#runInternal(stepDef, input, options, parentStepId, abortSignal);
316
+ });
314
317
  }
315
318
  get input() {
316
319
  return this.#input;
@@ -345,6 +348,9 @@ class ActionContext {
345
348
  });
346
349
  }
347
350
  async run(stepDef, input, options = {}) {
351
+ return this.#runInternal(stepDef, input, options, null, this.#abortSignal);
352
+ }
353
+ async #runInternal(stepDef, input, options = {}, parentStepId, abortSignal) {
348
354
  const validatedInput = stepDef.input
349
355
  ? stepDef.input.parse(input, {
350
356
  error: () => 'Error parsing step input',
@@ -367,7 +373,6 @@ class ActionContext {
367
373
  var: this.#variables,
368
374
  logger: this.#logger,
369
375
  jobId: this.#jobId,
370
- run: this.run.bind(this),
371
376
  };
372
377
  return stepDef.handler(extendedCtx);
373
378
  };
@@ -375,8 +380,8 @@ class ActionContext {
375
380
  name: stepName,
376
381
  cb: wrappedCb,
377
382
  options: parsedOptions,
378
- abortSignal: this.#abortSignal,
379
- parentStepId: null,
383
+ abortSignal,
384
+ parentStepId,
380
385
  parallel: parsedOptions.parallel,
381
386
  });
382
387
  }
@@ -3,6 +3,8 @@ CREATE SCHEMA IF NOT EXISTS "duron";
3
3
  CREATE TABLE "duron"."job_steps" (
4
4
  "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
5
5
  "job_id" uuid NOT NULL,
6
+ "parent_step_id" uuid,
7
+ "branch" boolean DEFAULT false NOT NULL,
6
8
  "name" text NOT NULL,
7
9
  "status" text DEFAULT 'active' NOT NULL,
8
10
  "output" jsonb,
@@ -17,7 +19,7 @@ CREATE TABLE "duron"."job_steps" (
17
19
  "history_failed_attempts" jsonb DEFAULT '{}' NOT NULL,
18
20
  "created_at" timestamp with time zone DEFAULT now() NOT NULL,
19
21
  "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
20
- CONSTRAINT "unique_job_step_name" UNIQUE("job_id","name"),
22
+ CONSTRAINT "unique_job_step_name_parent" UNIQUE NULLS NOT DISTINCT("job_id","name","parent_step_id"),
21
23
  CONSTRAINT "job_steps_status_check" CHECK ("status" IN ('active','completed','failed','cancelled'))
22
24
  );
23
25
  --> statement-breakpoint
@@ -41,10 +43,24 @@ CREATE TABLE "duron"."jobs" (
41
43
  CONSTRAINT "jobs_status_check" CHECK ("status" IN ('created','active','completed','failed','cancelled'))
42
44
  );
43
45
  --> statement-breakpoint
46
+ CREATE TABLE "duron"."metrics" (
47
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
48
+ "job_id" uuid NOT NULL,
49
+ "step_id" uuid,
50
+ "name" text NOT NULL,
51
+ "value" double precision NOT NULL,
52
+ "attributes" jsonb DEFAULT '{}' NOT NULL,
53
+ "type" text NOT NULL,
54
+ "timestamp" timestamp with time zone DEFAULT now() NOT NULL,
55
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
56
+ CONSTRAINT "metrics_type_check" CHECK ("type" IN ('metric', 'span_event', 'span_attribute'))
57
+ );
58
+ --> statement-breakpoint
44
59
  CREATE INDEX "idx_job_steps_job_id" ON "duron"."job_steps" ("job_id");--> statement-breakpoint
45
60
  CREATE INDEX "idx_job_steps_status" ON "duron"."job_steps" ("status");--> statement-breakpoint
46
61
  CREATE INDEX "idx_job_steps_name" ON "duron"."job_steps" ("name");--> statement-breakpoint
47
62
  CREATE INDEX "idx_job_steps_expires_at" ON "duron"."job_steps" ("expires_at");--> statement-breakpoint
63
+ CREATE INDEX "idx_job_steps_parent_step_id" ON "duron"."job_steps" ("parent_step_id");--> statement-breakpoint
48
64
  CREATE INDEX "idx_job_steps_job_status" ON "duron"."job_steps" ("job_id","status");--> statement-breakpoint
49
65
  CREATE INDEX "idx_job_steps_job_name" ON "duron"."job_steps" ("job_id","name");--> statement-breakpoint
50
66
  CREATE INDEX "idx_job_steps_output_fts" ON "duron"."job_steps" USING gin (to_tsvector('english', "output"::text));--> statement-breakpoint
@@ -61,4 +77,15 @@ CREATE INDEX "idx_jobs_action_status" ON "duron"."jobs" ("action_name","status")
61
77
  CREATE INDEX "idx_jobs_action_group" ON "duron"."jobs" ("action_name","group_key");--> statement-breakpoint
62
78
  CREATE INDEX "idx_jobs_input_fts" ON "duron"."jobs" USING gin (to_tsvector('english', "input"::text));--> statement-breakpoint
63
79
  CREATE INDEX "idx_jobs_output_fts" ON "duron"."jobs" USING gin (to_tsvector('english', "output"::text));--> statement-breakpoint
64
- ALTER TABLE "duron"."job_steps" ADD CONSTRAINT "job_steps_job_id_jobs_id_fkey" FOREIGN KEY ("job_id") REFERENCES "duron"."jobs"("id") ON DELETE CASCADE;
80
+ CREATE INDEX "idx_metrics_job_id" ON "duron"."metrics" ("job_id");--> statement-breakpoint
81
+ CREATE INDEX "idx_metrics_step_id" ON "duron"."metrics" ("step_id");--> statement-breakpoint
82
+ CREATE INDEX "idx_metrics_name" ON "duron"."metrics" ("name");--> statement-breakpoint
83
+ CREATE INDEX "idx_metrics_type" ON "duron"."metrics" ("type");--> statement-breakpoint
84
+ CREATE INDEX "idx_metrics_timestamp" ON "duron"."metrics" ("timestamp");--> statement-breakpoint
85
+ CREATE INDEX "idx_metrics_job_step" ON "duron"."metrics" ("job_id","step_id");--> statement-breakpoint
86
+ CREATE INDEX "idx_metrics_job_name" ON "duron"."metrics" ("job_id","name");--> statement-breakpoint
87
+ CREATE INDEX "idx_metrics_job_type" ON "duron"."metrics" ("job_id","type");--> statement-breakpoint
88
+ CREATE INDEX "idx_metrics_attributes" ON "duron"."metrics" USING gin ("attributes");--> statement-breakpoint
89
+ ALTER TABLE "duron"."job_steps" ADD CONSTRAINT "job_steps_job_id_jobs_id_fkey" FOREIGN KEY ("job_id") REFERENCES "duron"."jobs"("id") ON DELETE CASCADE;--> statement-breakpoint
90
+ ALTER TABLE "duron"."metrics" ADD CONSTRAINT "metrics_job_id_jobs_id_fkey" FOREIGN KEY ("job_id") REFERENCES "duron"."jobs"("id") ON DELETE CASCADE;--> statement-breakpoint
91
+ ALTER TABLE "duron"."metrics" ADD CONSTRAINT "metrics_step_id_job_steps_id_fkey" FOREIGN KEY ("step_id") REFERENCES "duron"."job_steps"("id") ON DELETE CASCADE;
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "version": "8",
3
3
  "dialect": "postgres",
4
- "id": "677e77a4-16f7-4931-a58b-2ca2184ce1be",
5
- "prevIds": ["4dab3234-6e6b-4a65-a5c5-c898bf2f3911"],
4
+ "id": "4e93c0bf-b135-40b4-b130-0cf0968bffe3",
5
+ "prevIds": [
6
+ "00000000-0000-0000-0000-000000000000"
7
+ ],
6
8
  "ddl": [
7
9
  {
8
10
  "name": "duron",
@@ -1266,10 +1268,14 @@
1266
1268
  },
1267
1269
  {
1268
1270
  "nameExplicit": false,
1269
- "columns": ["job_id"],
1271
+ "columns": [
1272
+ "job_id"
1273
+ ],
1270
1274
  "schemaTo": "duron",
1271
1275
  "tableTo": "jobs",
1272
- "columnsTo": ["id"],
1276
+ "columnsTo": [
1277
+ "id"
1278
+ ],
1273
1279
  "onUpdate": "NO ACTION",
1274
1280
  "onDelete": "CASCADE",
1275
1281
  "name": "job_steps_job_id_jobs_id_fkey",
@@ -1279,10 +1285,14 @@
1279
1285
  },
1280
1286
  {
1281
1287
  "nameExplicit": false,
1282
- "columns": ["job_id"],
1288
+ "columns": [
1289
+ "job_id"
1290
+ ],
1283
1291
  "schemaTo": "duron",
1284
1292
  "tableTo": "jobs",
1285
- "columnsTo": ["id"],
1293
+ "columnsTo": [
1294
+ "id"
1295
+ ],
1286
1296
  "onUpdate": "NO ACTION",
1287
1297
  "onDelete": "CASCADE",
1288
1298
  "name": "metrics_job_id_jobs_id_fkey",
@@ -1292,10 +1302,14 @@
1292
1302
  },
1293
1303
  {
1294
1304
  "nameExplicit": false,
1295
- "columns": ["step_id"],
1305
+ "columns": [
1306
+ "step_id"
1307
+ ],
1296
1308
  "schemaTo": "duron",
1297
1309
  "tableTo": "job_steps",
1298
- "columnsTo": ["id"],
1310
+ "columnsTo": [
1311
+ "id"
1312
+ ],
1299
1313
  "onUpdate": "NO ACTION",
1300
1314
  "onDelete": "CASCADE",
1301
1315
  "name": "metrics_step_id_job_steps_id_fkey",
@@ -1304,7 +1318,9 @@
1304
1318
  "table": "metrics"
1305
1319
  },
1306
1320
  {
1307
- "columns": ["id"],
1321
+ "columns": [
1322
+ "id"
1323
+ ],
1308
1324
  "nameExplicit": false,
1309
1325
  "name": "job_steps_pkey",
1310
1326
  "schema": "duron",
@@ -1312,7 +1328,9 @@
1312
1328
  "entityType": "pks"
1313
1329
  },
1314
1330
  {
1315
- "columns": ["id"],
1331
+ "columns": [
1332
+ "id"
1333
+ ],
1316
1334
  "nameExplicit": false,
1317
1335
  "name": "jobs_pkey",
1318
1336
  "schema": "duron",
@@ -1320,7 +1338,9 @@
1320
1338
  "entityType": "pks"
1321
1339
  },
1322
1340
  {
1323
- "columns": ["id"],
1341
+ "columns": [
1342
+ "id"
1343
+ ],
1324
1344
  "nameExplicit": false,
1325
1345
  "name": "metrics_pkey",
1326
1346
  "schema": "duron",
@@ -1329,9 +1349,13 @@
1329
1349
  },
1330
1350
  {
1331
1351
  "nameExplicit": true,
1332
- "columns": ["job_id", "name"],
1333
- "nullsNotDistinct": false,
1334
- "name": "unique_job_step_name",
1352
+ "columns": [
1353
+ "job_id",
1354
+ "name",
1355
+ "parent_step_id"
1356
+ ],
1357
+ "nullsNotDistinct": true,
1358
+ "name": "unique_job_step_name_parent",
1335
1359
  "entityType": "uniques",
1336
1360
  "schema": "duron",
1337
1361
  "table": "job_steps"
@@ -1359,4 +1383,4 @@
1359
1383
  }
1360
1384
  ],
1361
1385
  "renames": []
1362
- }
1386
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "duron",
3
- "version": "0.3.0-beta.3",
3
+ "version": "0.3.0-beta.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -788,7 +788,9 @@ export class PostgresBaseAdapter<Database extends DrizzleDatabase, Connection> e
788
788
  step_existed AS (
789
789
  SELECT EXISTS(
790
790
  SELECT 1 FROM ${this.tables.jobStepsTable} s
791
- WHERE s.job_id = ${jobId} AND s.name = ${name}
791
+ WHERE s.job_id = ${jobId}
792
+ AND s.name = ${name}
793
+ AND s.parent_step_id IS NOT DISTINCT FROM ${parentStepId}
792
794
  ) AS existed
793
795
  ),
794
796
  upserted_step AS (
@@ -818,7 +820,7 @@ export class PostgresBaseAdapter<Database extends DrizzleDatabase, Connection> e
818
820
  0,
819
821
  NULL
820
822
  WHERE EXISTS (SELECT 1 FROM job_check)
821
- ON CONFLICT (job_id, name) DO UPDATE
823
+ ON CONFLICT (job_id, name, parent_step_id) DO UPDATE
822
824
  SET
823
825
  timeout_ms = ${timeoutMs},
824
826
  expires_at = now() + interval '${sql.raw(timeoutMs.toString())} milliseconds',
@@ -858,6 +860,7 @@ export class PostgresBaseAdapter<Database extends DrizzleDatabase, Connection> e
858
860
  INNER JOIN job_check jc ON s.job_id = jc.id
859
861
  WHERE s.job_id = ${jobId}
860
862
  AND s.name = ${name}
863
+ AND s.parent_step_id IS NOT DISTINCT FROM ${parentStepId}
861
864
  AND NOT EXISTS (SELECT 1 FROM final_upserted)
862
865
  )
863
866
  SELECT * FROM final_upserted
@@ -117,8 +117,9 @@ export default function createSchema(schemaName: string) {
117
117
  index('idx_job_steps_job_status').on(table.job_id, table.status),
118
118
  index('idx_job_steps_job_name').on(table.job_id, table.name),
119
119
  index('idx_job_steps_output_fts').using('gin', sql`to_tsvector('english', ${table.output}::text)`),
120
- // Unique constraint
121
- unique('unique_job_step_name').on(table.job_id, table.name),
120
+ // Unique constraint - step name is unique within a parent (name + parentStepId)
121
+ // nullsNotDistinct ensures NULL parent_step_id values are treated as equal for uniqueness
122
+ unique('unique_job_step_name_parent').on(table.job_id, table.name, table.parent_step_id).nullsNotDistinct(),
122
123
  check(
123
124
  'job_steps_status_check',
124
125
  sql`${table.status} IN ${sql.raw(`(${STEP_STATUSES.map((s) => `'${s}'`).join(',')})`)}`,
@@ -148,14 +148,14 @@ export class StepManager {
148
148
  #telemetry: TelemetryAdapter
149
149
  #queue: fastq.queueAsPromised<TaskStep, any>
150
150
  #logger: Logger
151
- // each step name should be executed only once per action job
151
+ // each step name should be executed only once per parent (name + parentStepId)
152
152
  #historySteps = new Set<string>()
153
153
  // Store step spans for nested step tracking
154
154
  #stepSpans = new Map<string, Span>()
155
155
  // Store the job span for creating step spans
156
156
  #jobSpan: Span | null = null
157
- // Store the run function for executing step definitions from inline steps
158
- #runFn: StepHandlerContext['run'] | null = null
157
+ // Factory function to create run functions with the correct parent step ID and abort signal
158
+ #runFnFactory: ((parentStepId: string | null, abortSignal: AbortSignal) => StepHandlerContext['run']) | null = null
159
159
 
160
160
  // ============================================================================
161
161
  // Constructor
@@ -173,10 +173,12 @@ export class StepManager {
173
173
  this.#telemetry = options.telemetry
174
174
  this.#stepStore = new StepStore(options.adapter)
175
175
  this.#queue = fastq.promise(async (task: TaskStep) => {
176
- if (this.#historySteps.has(task.name)) {
176
+ // Create composite key: name + parentStepId (allows same name under different parents)
177
+ const stepKey = `${task.parentStepId ?? 'root'}:${task.name}`
178
+ if (this.#historySteps.has(stepKey)) {
177
179
  throw new StepAlreadyExecutedError(task.name, this.#jobId, this.#actionName)
178
180
  }
179
- this.#historySteps.add(task.name)
181
+ this.#historySteps.add(stepKey)
180
182
  return this.#executeStep(task.name, task.cb, task.options, task.abortSignal, task.parentStepId, task.parallel)
181
183
  }, options.concurrencyLimit)
182
184
  }
@@ -190,11 +192,13 @@ export class StepManager {
190
192
  }
191
193
 
192
194
  /**
193
- * Set the run function for executing step definitions from inline steps.
195
+ * Set the run function factory for executing step definitions from inline steps.
194
196
  * Called from ActionContext after it's initialized.
197
+ *
198
+ * @param factory - A function that creates run functions with the correct parent step ID and abort signal
195
199
  */
196
- setRunFn(runFn: StepHandlerContext['run']): void {
197
- this.#runFn = runFn
200
+ setRunFnFactory(factory: (parentStepId: string | null, abortSignal: AbortSignal) => StepHandlerContext['run']): void {
201
+ this.#runFnFactory = factory
198
202
  }
199
203
 
200
204
  // ============================================================================
@@ -436,7 +440,7 @@ export class StepManager {
436
440
 
437
441
  return childPromise
438
442
  },
439
- run: this.#runFn!,
443
+ run: this.#runFnFactory!(step.id, childSignal),
440
444
  }
441
445
 
442
446
  try {
@@ -623,8 +627,10 @@ class ActionContext<TInput extends z.ZodObject, TOutput extends z.ZodObject, TVa
623
627
  this.#input = job.input ?? {}
624
628
  this.step = this.step.bind(this)
625
629
  this.run = this.run.bind(this)
626
- // Set the run function on the step manager so inline steps can call step definitions
627
- this.#stepManager.setRunFn(this.run)
630
+ // Set the run function factory so inline steps can call step definitions with correct parent
631
+ this.#stepManager.setRunFnFactory((parentStepId, abortSignal) => {
632
+ return (stepDef, input, options) => this.#runInternal(stepDef, input, options, parentStepId, abortSignal)
633
+ })
628
634
  }
629
635
 
630
636
  // ============================================================================
@@ -707,6 +713,7 @@ class ActionContext<TInput extends z.ZodObject, TOutput extends z.ZodObject, TVa
707
713
 
708
714
  /**
709
715
  * Execute a reusable step definition created with createStep().
716
+ * This is the public method called from action handlers.
710
717
  *
711
718
  * @param stepDef - The step definition to execute
712
719
  * @param input - The input data for the step (validated against the step's input schema)
@@ -717,6 +724,20 @@ class ActionContext<TInput extends z.ZodObject, TOutput extends z.ZodObject, TVa
717
724
  stepDef: StepDefinition<TStepInput, TResult, TVariables>,
718
725
  input: z.input<TStepInput>,
719
726
  options: Partial<z.input<typeof StepOptionsSchema>> = {},
727
+ ): Promise<TResult> {
728
+ return this.#runInternal(stepDef, input, options, null, this.#abortSignal)
729
+ }
730
+
731
+ /**
732
+ * Internal method to execute a step definition with explicit parent step ID and abort signal.
733
+ * Used by both the public run method and the run functions passed to step contexts.
734
+ */
735
+ async #runInternal<TStepInput extends z.ZodObject, TResult>(
736
+ stepDef: StepDefinition<TStepInput, TResult, TVariables>,
737
+ input: z.input<TStepInput>,
738
+ options: Partial<z.input<typeof StepOptionsSchema>> = {},
739
+ parentStepId: string | null,
740
+ abortSignal: AbortSignal,
720
741
  ): Promise<TResult> {
721
742
  // Validate input against the step's schema if provided
722
743
  // After parsing, validatedInput is z.output<TStepInput> (same as z.infer<TStepInput>)
@@ -749,7 +770,6 @@ class ActionContext<TInput extends z.ZodObject, TOutput extends z.ZodObject, TVa
749
770
  var: this.#variables,
750
771
  logger: this.#logger,
751
772
  jobId: this.#jobId,
752
- run: this.run.bind(this), // Allow nested step definitions to call ctx.run()
753
773
  }
754
774
  return stepDef.handler(extendedCtx)
755
775
  }
@@ -758,8 +778,8 @@ class ActionContext<TInput extends z.ZodObject, TOutput extends z.ZodObject, TVa
758
778
  name: stepName,
759
779
  cb: wrappedCb,
760
780
  options: parsedOptions,
761
- abortSignal: this.#abortSignal,
762
- parentStepId: null, // Root steps have no parent
781
+ abortSignal,
782
+ parentStepId,
763
783
  parallel: parsedOptions.parallel,
764
784
  })
765
785
  }