@ultimat3/jobs 30.4.0 → 30.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -212,8 +212,8 @@ Long form: [`docs/history/jobs.md`](../../docs/history/jobs.md), "Moved 2026-10-
212
212
  | `consumer.ts` / `consumer-loop.ts` / `consumer-types.ts` | `consumer()` — a job + `.supervisor` task; one run's turns; the shapes |
213
213
  | `consumer-registry.ts` / `consumer-guard.ts` / `consumer-errors.ts` | the `origin` stamp + `registeredConsumers`; the self-enqueue refusal; `X_CONSUMER_*` |
214
214
  | `register.ts` | `registerJobs`/`registerTasks` over a module namespace + the registrar announcements; an `action-job` projection is `X_ACTION_JOB_UNBRIDGED` |
215
- | `describe.ts` | the JSON projection one handle emits; `describeJobs()` is a map over it |
216
- | `steps.ts` / `-core.ts` / `-wait.ts` / `-unwind.ts` | replay runner; shared core; `waitForEvent`; compensation |
215
+ | `describe.ts` | one handle's JSON projection; `describeJobs()` maps it |
216
+ | `steps.ts` / `-core.ts` / `-wait.ts` / `-unwind.ts` / `step-scope.ts` | replay runner; core; `waitForEvent`; compensation; `currentStep()` |
217
217
  | `steps-timeout.ts` / `steps-suspension.ts` | a step's ceiling; `StepSuspension`, `UnwindReached`, timeout marker |
218
218
  | `steps-api.ts` / `steps-store.ts` / `steps-output.ts` | `StepApi`, `EventLookup`; `StepRecord`, `StepFence`, `StepStore`; what a step answers, `StepOutput` and `jsonForm` |
219
219
  | `type-pins.ts` | compile-time pins for `StepOutput` and `waitForEvent` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/jobs",
3
- "version": "30.4.0",
3
+ "version": "30.6.0",
4
4
  "description": "Durable background work: steps, transactional outbox, cron tasks, one driver interface",
5
5
  "keywords": [
6
6
  "ultimate",
@@ -44,11 +44,11 @@
44
44
  "test": "bun test"
45
45
  },
46
46
  "dependencies": {
47
- "@ultimat3/core": "30.4.0",
48
- "@ultimat3/db": "30.4.0",
49
- "@ultimat3/entity": "30.4.0",
50
- "@ultimat3/http": "30.4.0",
51
- "@ultimat3/schema": "30.4.0",
52
- "@ultimat3/time": "30.4.0"
47
+ "@ultimat3/core": "30.6.0",
48
+ "@ultimat3/db": "30.6.0",
49
+ "@ultimat3/entity": "30.6.0",
50
+ "@ultimat3/http": "30.6.0",
51
+ "@ultimat3/schema": "30.6.0",
52
+ "@ultimat3/time": "30.6.0"
53
53
  }
54
54
  }
package/src/index.ts CHANGED
@@ -430,6 +430,7 @@ export type { ScheduledFire, SchedulerState } from './scheduler-state';
430
430
  export { fireThroughDriver, memorySchedulerState } from './scheduler-state';
431
431
  export type { JobCompleted, JobFailed, JobSettled } from './settled';
432
432
  export { ON_SETTLED_ATTEMPTS, ON_SETTLED_TIMEOUT_MS } from './settled';
433
+ export { currentStep } from './step-scope';
433
434
  export type {
434
435
  CompensateContext,
435
436
  EventLookup,
@@ -0,0 +1,13 @@
1
+ // Which durable step's body is running, ambient across its awaits. Read by an observer that must
2
+ // tell an effect made INSIDE a step (rerun when the step did not complete) from one made between
3
+ // steps (rerun on every replay): `@ultimat3/testing`'s crash proof names the step it ran in.
4
+
5
+ import { asyncContext } from '@ultimat3/core';
6
+
7
+ const scope = asyncContext<string>('a durable step body');
8
+
9
+ /** Run one step body with its name in flight. `steps.ts` is the one caller. */
10
+ export const inStep = <T>(name: string, body: () => T): T => scope.run(name, body);
11
+
12
+ /** The name of the step whose body is running here, or `undefined` outside every step body. */
13
+ export const currentStep = (): string | undefined => scope.get();
package/src/steps.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  UNWINDING_STEP,
22
22
  } from './errors-saga';
23
23
  import { createRunSignal } from './run-signal';
24
+ import { inStep } from './step-scope';
24
25
  import type { EventLookup, StepApi, StepRunOptions } from './steps-api';
25
26
  import { DEFAULT_EVENT_POLL_MS, WAKING_EVENT_POLL_MS } from './steps-api';
26
27
  import type { RunnerCore } from './steps-core';
@@ -297,7 +298,7 @@ export function stepRunner(options: StepRunnerOptions): StepRunner {
297
298
  const signal = composed?.signal ?? runSignal;
298
299
  try {
299
300
  const output = await withStepTimeout(
300
- fn(signal),
301
+ inStep(name, () => fn(signal)),
301
302
  stepTimeoutMs,
302
303
  deadline,
303
304
  () =>