@ultimat3/jobs 1.0.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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/package.json +38 -0
  4. package/src/clock.d.ts +7 -0
  5. package/src/clock.d.ts.map +1 -0
  6. package/src/clock.js +21 -0
  7. package/src/clock.js.map +1 -0
  8. package/src/clock.ts +23 -0
  9. package/src/describe.ts +61 -0
  10. package/src/driver-memory.d.ts +9 -0
  11. package/src/driver-memory.d.ts.map +1 -0
  12. package/src/driver-memory.js +189 -0
  13. package/src/driver-memory.js.map +1 -0
  14. package/src/driver-memory.ts +216 -0
  15. package/src/driver-nats.d.ts +7 -0
  16. package/src/driver-nats.d.ts.map +1 -0
  17. package/src/driver-nats.js +51 -0
  18. package/src/driver-nats.js.map +1 -0
  19. package/src/driver-nats.ts +73 -0
  20. package/src/driver-pg-sql.d.ts +19 -0
  21. package/src/driver-pg-sql.d.ts.map +1 -0
  22. package/src/driver-pg-sql.js +160 -0
  23. package/src/driver-pg-sql.js.map +1 -0
  24. package/src/driver-pg-sql.ts +170 -0
  25. package/src/driver-pg.d.ts +17 -0
  26. package/src/driver-pg.d.ts.map +1 -0
  27. package/src/driver-pg.js +246 -0
  28. package/src/driver-pg.js.map +1 -0
  29. package/src/driver-pg.ts +356 -0
  30. package/src/driver-redis.d.ts +7 -0
  31. package/src/driver-redis.d.ts.map +1 -0
  32. package/src/driver-redis.js +54 -0
  33. package/src/driver-redis.js.map +1 -0
  34. package/src/driver-redis.ts +76 -0
  35. package/src/driver.d.ts +114 -0
  36. package/src/driver.d.ts.map +1 -0
  37. package/src/driver.js +14 -0
  38. package/src/driver.js.map +1 -0
  39. package/src/driver.ts +143 -0
  40. package/src/errors.d.ts +63 -0
  41. package/src/errors.d.ts.map +1 -0
  42. package/src/errors.js +105 -0
  43. package/src/errors.js.map +1 -0
  44. package/src/errors.ts +165 -0
  45. package/src/events.d.ts +35 -0
  46. package/src/events.d.ts.map +1 -0
  47. package/src/events.js +92 -0
  48. package/src/events.js.map +1 -0
  49. package/src/events.ts +134 -0
  50. package/src/index.d.ts +32 -0
  51. package/src/index.d.ts.map +1 -0
  52. package/src/index.js +18 -0
  53. package/src/index.js.map +1 -0
  54. package/src/index.ts +172 -0
  55. package/src/inspect.d.ts +82 -0
  56. package/src/inspect.d.ts.map +1 -0
  57. package/src/inspect.js +113 -0
  58. package/src/inspect.js.map +1 -0
  59. package/src/inspect.ts +213 -0
  60. package/src/job.d.ts +71 -0
  61. package/src/job.d.ts.map +1 -0
  62. package/src/job.js +99 -0
  63. package/src/job.js.map +1 -0
  64. package/src/job.ts +261 -0
  65. package/src/limits.d.ts +47 -0
  66. package/src/limits.d.ts.map +1 -0
  67. package/src/limits.js +0 -0
  68. package/src/limits.js.map +1 -0
  69. package/src/limits.ts +0 -0
  70. package/src/outbox.d.ts +81 -0
  71. package/src/outbox.d.ts.map +1 -0
  72. package/src/outbox.js +202 -0
  73. package/src/outbox.js.map +1 -0
  74. package/src/outbox.ts +336 -0
  75. package/src/register.ts +40 -0
  76. package/src/retry.d.ts +40 -0
  77. package/src/retry.d.ts.map +1 -0
  78. package/src/retry.js +59 -0
  79. package/src/retry.js.map +1 -0
  80. package/src/retry.ts +90 -0
  81. package/src/scheduler.d.ts +79 -0
  82. package/src/scheduler.d.ts.map +1 -0
  83. package/src/scheduler.js +183 -0
  84. package/src/scheduler.js.map +1 -0
  85. package/src/scheduler.ts +417 -0
  86. package/src/steps.d.ts +86 -0
  87. package/src/steps.d.ts.map +1 -0
  88. package/src/steps.js +227 -0
  89. package/src/steps.js.map +1 -0
  90. package/src/steps.ts +339 -0
  91. package/src/worker.d.ts +68 -0
  92. package/src/worker.d.ts.map +1 -0
  93. package/src/worker.js +273 -0
  94. package/src/worker.js.map +1 -0
  95. package/src/worker.ts +356 -0
package/src/events.ts ADDED
@@ -0,0 +1,134 @@
1
+ // The event bus `step.waitForEvent` consumes. Events are stored, not just broadcast: a step
2
+ // that suspends at 12:00 and resumes at 12:00:30 must still see an event published at
3
+ // 12:00:10, so a fire-and-forget emitter would silently strand every waiting run.
4
+
5
+ import type { Clock } from '@ultimat3/core';
6
+ import { logger, systemClock, uuid } from '@ultimat3/core';
7
+ import type { DurationInput } from './clock';
8
+ import { nowMs, toMs } from './clock';
9
+ import type { EventLookup } from './steps';
10
+
11
+ export interface JobEvent {
12
+ readonly id: string;
13
+ readonly name: string;
14
+ readonly payload: unknown;
15
+ /** Ties the event to one waiting run — usually an entity id. */
16
+ readonly correlationKey?: string;
17
+ readonly publishedAt: number;
18
+ readonly expiresAt: number;
19
+ }
20
+
21
+ export interface PublishOptions {
22
+ readonly correlationKey?: string;
23
+ /** How long the event stays matchable. Default 7d — longer than any sane wait. */
24
+ readonly ttl?: DurationInput;
25
+ }
26
+
27
+ export interface EventBus extends EventLookup {
28
+ publish(name: string, payload: unknown, options?: PublishOptions): Promise<JobEvent>;
29
+ list(name?: string): Promise<readonly JobEvent[]>;
30
+ purgeExpired(): number;
31
+ size(): number;
32
+ }
33
+
34
+ export interface MemoryEventBusOptions {
35
+ readonly clock?: Clock;
36
+ readonly defaultTtl?: DurationInput;
37
+ readonly maxEvents?: number;
38
+ }
39
+
40
+ export function createMemoryEventBus(options: MemoryEventBusOptions = {}): EventBus {
41
+ const clock = options.clock ?? systemClock;
42
+ const defaultTtl = options.defaultTtl ?? 604_800_000;
43
+ const maxEvents = options.maxEvents ?? 10_000;
44
+ const events = new Map<string, JobEvent>();
45
+
46
+ const purgeExpired = (): number => {
47
+ const at = nowMs(clock);
48
+ let removed = 0;
49
+ for (const [id, event] of events) {
50
+ if (event.expiresAt <= at) {
51
+ events.delete(id);
52
+ removed += 1;
53
+ }
54
+ }
55
+ return removed;
56
+ };
57
+
58
+ return {
59
+ publish(name, payload, publishOptions = {}) {
60
+ purgeExpired();
61
+ const at = nowMs(clock);
62
+ const event: JobEvent = {
63
+ id: uuid(),
64
+ name,
65
+ payload,
66
+ publishedAt: at,
67
+ expiresAt: at + toMs(publishOptions.ttl ?? defaultTtl),
68
+ ...(publishOptions.correlationKey === undefined
69
+ ? {}
70
+ : { correlationKey: publishOptions.correlationKey }),
71
+ };
72
+ events.set(event.id, event);
73
+ while (events.size > maxEvents) {
74
+ const oldest = events.keys().next();
75
+ if (oldest.done === true) break;
76
+ events.delete(oldest.value);
77
+ }
78
+ logger.debug('jobs.event.published', {
79
+ event: name,
80
+ correlationKey: publishOptions.correlationKey ?? null,
81
+ });
82
+ return Promise.resolve(event);
83
+ },
84
+
85
+ /**
86
+ * Earliest matching event at or after `afterMs`, so a resumed step consumes events in
87
+ * publication order rather than jumping to the newest one.
88
+ */
89
+ find(name, correlationKey, afterMs) {
90
+ const at = nowMs(clock);
91
+ let best: JobEvent | undefined;
92
+ for (const event of events.values()) {
93
+ if (event.name !== name) continue;
94
+ if (event.expiresAt <= at) continue;
95
+ if (event.publishedAt < afterMs) continue;
96
+ if (correlationKey !== undefined && event.correlationKey !== correlationKey) continue;
97
+ if (best === undefined || event.publishedAt < best.publishedAt) best = event;
98
+ }
99
+ return Promise.resolve(
100
+ best === undefined ? undefined : { payload: best.payload, publishedAt: best.publishedAt },
101
+ );
102
+ },
103
+
104
+ list(name) {
105
+ const all = [...events.values()]
106
+ .filter((event) => name === undefined || event.name === name)
107
+ .sort((a, b) => a.publishedAt - b.publishedAt);
108
+ return Promise.resolve(all);
109
+ },
110
+
111
+ purgeExpired,
112
+ size: () => events.size,
113
+ };
114
+ }
115
+
116
+ let ambientBus: EventBus = createMemoryEventBus();
117
+
118
+ /** Swapped at boot for the NATS/Redis-streams bus in a multi-node deployment. */
119
+ export function setEventBus(bus: EventBus): void {
120
+ ambientBus = bus;
121
+ }
122
+
123
+ export function eventBus(): EventBus {
124
+ return ambientBus;
125
+ }
126
+
127
+ /** The one function app code calls to unblock a waiting step. */
128
+ export function publishEvent(
129
+ name: string,
130
+ payload: unknown,
131
+ options?: PublishOptions,
132
+ ): Promise<JobEvent> {
133
+ return ambientBus.publish(name, payload, options);
134
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ export type { ClaimedJob, ClaimOptions, ConflictPolicy, EnqueueRequest, EnqueueResult, JobDriver, JobFilter, JobIntrospection, JobRecord, JobState, NackOptions, QueueStats, } from './driver';
2
+ export { DEFAULT_QUEUE, DEFAULT_VISIBILITY_TIMEOUT_MS, jobDriver, setJobDriver, } from './driver';
3
+ export type { MemoryDriverOptions } from './driver-memory';
4
+ export { createMemoryDriver } from './driver-memory';
5
+ export type { NatsDriverOptions } from './driver-nats';
6
+ export { createNatsDriver } from './driver-nats';
7
+ export type { PgDriverOptions, PgExecutor } from './driver-pg';
8
+ export { createPgDriver, createPgLeader } from './driver-pg';
9
+ export { SQL_ACK, SQL_ADVISORY_UNLOCK, SQL_CLAIM, SQL_ENQUEUE, SQL_HEARTBEAT, SQL_JOBS_TABLE, SQL_NACK, SQL_STATS, SQL_STEP_GET, SQL_STEP_PUT, SQL_TRY_ADVISORY_LOCK, } from './driver-pg-sql';
10
+ export type { RedisDriverOptions } from './driver-redis';
11
+ export { createRedisDriver } from './driver-redis';
12
+ export type { JobErrorCode } from './errors';
13
+ export { DriverUnavailableError, IdempotencyRequiredError, JOB_ERROR_CODES, JobDuplicateError, JobMaxAttemptsError, JobsNotImplementedError, JobTimeoutError, OutboxNoTxError, StepDuplicateError, } from './errors';
14
+ export type { EventBus, JobEvent, MemoryEventBusOptions, PublishOptions } from './events';
15
+ export { createMemoryEventBus, eventBus, publishEvent, setEventBus } from './events';
16
+ export type { DeadLetterEntry, JobsManifest, JobTrace, QueueDepthReport, StepTrace, } from './inspect';
17
+ export { inspectDeadLetters, inspectJob, inspectJobList, inspectManifest, inspectQueues, retryFromStep, } from './inspect';
18
+ export type { AnyJobHandle, JobDefinition, JobHandle, JobRunArgs } from './job';
19
+ export { describeJobs, getJob, job, nameJobs, registeredJobs, resetJobs } from './job';
20
+ export type { Lease, LimitConfig, Limiter, LimitKey, LimitReason, LimitSnapshot, RateLimit, } from './limits';
21
+ export { createLimiter, NO_TENANT, tenantKeyFrom } from './limits';
22
+ export type { EnqueueOptions, JobsFacade, OutboxDeps, OutboxRecord, OutboxRelay, OutboxStore, RelayOptions, } from './outbox';
23
+ export { createJobsFacade, createMemoryOutboxStore, createOutboxRelay, enqueueInTx, SQL_OUTBOX_CLAIM, SQL_OUTBOX_MARK_PUBLISHED, SQL_OUTBOX_STAGE, SQL_OUTBOX_TABLE, } from './outbox';
24
+ export type { BackoffStrategy, Random, RetryDecision, RetryPolicy } from './retry';
25
+ export { backoffDelayMs, DEFAULT_RETRY, nextRetry, retrySchedule } from './retry';
26
+ export type { CatchUpPolicy, CronResolver, DispatchedOccurrence, LeaderElection, Scheduler, SchedulerOptions, SchedulerState, TaskDefinition, TaskEnqueueEntry, TaskHandle, } from './scheduler';
27
+ export { createMemorySchedulerState, createScheduler, getTask, nameTasks, registeredTasks, resetTasks, soleLeader, task, } from './scheduler';
28
+ export type { EventLookup, StepApi, StepRecord, StepRunner, StepRunnerOptions, StepStatus, StepStore, WaitForEventOptions, } from './steps';
29
+ export { createMemoryStepStore, createStepRunner, isStepSuspension, StepSuspension, } from './steps';
30
+ export type { ExecuteJobOptions, JobExecution, JobOutcome, Worker, WorkerOptions, WorkerStats, } from './worker';
31
+ export { createWorker, executeJob } from './worker';
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
package/src/index.js ADDED
@@ -0,0 +1,18 @@
1
+ // Public API of @ultimat3/jobs. Explicit, no `export *`.
2
+ export { DEFAULT_QUEUE, DEFAULT_VISIBILITY_TIMEOUT_MS, jobDriver, setJobDriver, } from './driver';
3
+ export { createMemoryDriver } from './driver-memory';
4
+ export { createNatsDriver } from './driver-nats';
5
+ export { createPgDriver, createPgLeader } from './driver-pg';
6
+ export { SQL_ACK, SQL_ADVISORY_UNLOCK, SQL_CLAIM, SQL_ENQUEUE, SQL_HEARTBEAT, SQL_JOBS_TABLE, SQL_NACK, SQL_STATS, SQL_STEP_GET, SQL_STEP_PUT, SQL_TRY_ADVISORY_LOCK, } from './driver-pg-sql';
7
+ export { createRedisDriver } from './driver-redis';
8
+ export { DriverUnavailableError, IdempotencyRequiredError, JOB_ERROR_CODES, JobDuplicateError, JobMaxAttemptsError, JobsNotImplementedError, JobTimeoutError, OutboxNoTxError, StepDuplicateError, } from './errors';
9
+ export { createMemoryEventBus, eventBus, publishEvent, setEventBus } from './events';
10
+ export { inspectDeadLetters, inspectJob, inspectJobList, inspectManifest, inspectQueues, retryFromStep, } from './inspect';
11
+ export { describeJobs, getJob, job, nameJobs, registeredJobs, resetJobs } from './job';
12
+ export { createLimiter, NO_TENANT, tenantKeyFrom } from './limits';
13
+ export { createJobsFacade, createMemoryOutboxStore, createOutboxRelay, enqueueInTx, SQL_OUTBOX_CLAIM, SQL_OUTBOX_MARK_PUBLISHED, SQL_OUTBOX_STAGE, SQL_OUTBOX_TABLE, } from './outbox';
14
+ export { backoffDelayMs, DEFAULT_RETRY, nextRetry, retrySchedule } from './retry';
15
+ export { createMemorySchedulerState, createScheduler, getTask, nameTasks, registeredTasks, resetTasks, soleLeader, task, } from './scheduler';
16
+ export { createMemoryStepStore, createStepRunner, isStepSuspension, StepSuspension, } from './steps';
17
+ export { createWorker, executeJob } from './worker';
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,yDAAyD;AAgBzD,OAAO,EACL,aAAa,EACb,6BAA6B,EAC7B,SAAS,EACT,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EACL,OAAO,EACP,mBAAmB,EACnB,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQrF,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAUvF,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAUnE,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,WAAW,EACX,gBAAgB,EAChB,yBAAyB,EACzB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAalF,OAAO,EACL,0BAA0B,EAC1B,eAAe,EACf,OAAO,EACP,SAAS,EACT,eAAe,EACf,UAAU,EACV,UAAU,EACV,IAAI,GACL,MAAM,aAAa,CAAC;AAWrB,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,GACf,MAAM,SAAS,CAAC;AASjB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC"}
package/src/index.ts ADDED
@@ -0,0 +1,172 @@
1
+ // Public API of @ultimat3/jobs. Explicit, no `export *`.
2
+ //
3
+ // `registerJob`/`registerJobs`/`registerTask`/`registerTasks`/`nameJobs`/`nameTasks` are
4
+ // deliberately absent. `defineApi({ jobs, tasks })` is where a module is handed over and nothing
5
+ // else registers (CLAUDE.md); it reaches them through core's registrar table, which the
6
+ // side-effect import below fills. Exporting them would offer a second registration path that
7
+ // bypasses `defineApi`'s own result — the ambiguity axiom 1 exists to refuse.
8
+ import './register';
9
+
10
+ /** Re-exported so a `job`/`task` file needs one import, not two. Same object as schema's. */
11
+ export type { Infer } from '@ultimat3/schema';
12
+ export { t } from '@ultimat3/schema';
13
+ export type { JobDescriptor } from './describe';
14
+ export type {
15
+ ClaimedJob,
16
+ ClaimOptions,
17
+ ConflictPolicy,
18
+ EnqueueRequest,
19
+ EnqueueResult,
20
+ JobDriver,
21
+ JobFilter,
22
+ JobIntrospection,
23
+ JobRecord,
24
+ JobState,
25
+ NackOptions,
26
+ QueueStats,
27
+ } from './driver';
28
+ export {
29
+ DEFAULT_QUEUE,
30
+ DEFAULT_VISIBILITY_TIMEOUT_MS,
31
+ jobDriver,
32
+ resetJobDriver,
33
+ setJobDriver,
34
+ } from './driver';
35
+ export type { MemoryDriverOptions } from './driver-memory';
36
+ export { createMemoryDriver } from './driver-memory';
37
+ export type { NatsDriverOptions } from './driver-nats';
38
+ export { createNatsDriver } from './driver-nats';
39
+ export type { PgDriverOptions, PgExecutor } from './driver-pg';
40
+ export { createPgDriver, createPgLeader } from './driver-pg';
41
+ export {
42
+ SQL_ACK,
43
+ SQL_ADVISORY_UNLOCK,
44
+ SQL_CLAIM,
45
+ SQL_ENQUEUE,
46
+ SQL_HEARTBEAT,
47
+ SQL_JOBS_TABLE,
48
+ SQL_NACK,
49
+ SQL_STATS,
50
+ SQL_STEP_GET,
51
+ SQL_STEP_PUT,
52
+ SQL_TRY_ADVISORY_LOCK,
53
+ } from './driver-pg-sql';
54
+ export type { RedisDriverOptions } from './driver-redis';
55
+ export { createRedisDriver } from './driver-redis';
56
+ export type { JobErrorCode } from './errors';
57
+ export {
58
+ DriverUnavailableError,
59
+ IdempotencyRequiredError,
60
+ JOB_ERROR_CODES,
61
+ JOB_ERROR_TITLES,
62
+ JobDuplicateError,
63
+ JobMaxAttemptsError,
64
+ JobNameTakenError,
65
+ JobsNotImplementedError,
66
+ JobTimeoutError,
67
+ OutboxNoTxError,
68
+ StepDuplicateError,
69
+ } from './errors';
70
+ export type { EventBus, JobEvent, MemoryEventBusOptions, PublishOptions } from './events';
71
+ export { createMemoryEventBus, eventBus, publishEvent, setEventBus } from './events';
72
+ export type {
73
+ DeadLetterEntry,
74
+ JobsManifest,
75
+ JobTrace,
76
+ QueueDepthReport,
77
+ StepTrace,
78
+ } from './inspect';
79
+ export {
80
+ inspectDeadLetters,
81
+ inspectJob,
82
+ inspectJobList,
83
+ inspectManifest,
84
+ inspectQueues,
85
+ retryFromStep,
86
+ } from './inspect';
87
+ export type { AnyJobHandle, JobActor, JobDefinition, JobHandle, JobRunArgs } from './job';
88
+ export { describeJobs, getJob, isJobHandle, job, registeredJobs, resetJobs } from './job';
89
+ export type {
90
+ Lease,
91
+ LimitConfig,
92
+ Limiter,
93
+ LimitKey,
94
+ LimitReason,
95
+ LimitSnapshot,
96
+ RateLimit,
97
+ } from './limits';
98
+ export { createLimiter, NO_TENANT, tenantKeyFrom } from './limits';
99
+ export type {
100
+ EnqueueOptions,
101
+ JobsFacade,
102
+ OutboxDeps,
103
+ OutboxRecord,
104
+ OutboxRelay,
105
+ OutboxStore,
106
+ RelayOptions,
107
+ } from './outbox';
108
+ export {
109
+ createJobsFacade,
110
+ createMemoryOutboxStore,
111
+ createOutboxRelay,
112
+ enqueueInTx,
113
+ jobsFacade,
114
+ resetJobsFacade,
115
+ SQL_OUTBOX_CLAIM,
116
+ SQL_OUTBOX_MARK_PUBLISHED,
117
+ SQL_OUTBOX_STAGE,
118
+ SQL_OUTBOX_TABLE,
119
+ setJobsFacade,
120
+ } from './outbox';
121
+
122
+ export type { BackoffStrategy, Random, RetryDecision, RetryPolicy } from './retry';
123
+ export { backoffDelayMs, DEFAULT_RETRY, nextRetry, retrySchedule } from './retry';
124
+ export type {
125
+ CatchUpPolicy,
126
+ CronResolver,
127
+ DispatchedOccurrence,
128
+ LeaderElection,
129
+ Scheduler,
130
+ SchedulerOptions,
131
+ SchedulerState,
132
+ TaskDefinition,
133
+ TaskDescriptor,
134
+ TaskEnqueueEntry,
135
+ TaskHandle,
136
+ TaskJobResult,
137
+ } from './scheduler';
138
+ export {
139
+ createMemorySchedulerState,
140
+ createScheduler,
141
+ getTask,
142
+ isTaskHandle,
143
+ registeredTasks,
144
+ resetTasks,
145
+ soleLeader,
146
+ task,
147
+ } from './scheduler';
148
+ export type {
149
+ EventLookup,
150
+ StepApi,
151
+ StepRecord,
152
+ StepRunner,
153
+ StepRunnerOptions,
154
+ StepStatus,
155
+ StepStore,
156
+ WaitForEventOptions,
157
+ } from './steps';
158
+ export {
159
+ createMemoryStepStore,
160
+ createStepRunner,
161
+ isStepSuspension,
162
+ StepSuspension,
163
+ } from './steps';
164
+ export type {
165
+ ExecuteJobOptions,
166
+ JobExecution,
167
+ JobOutcome,
168
+ Worker,
169
+ WorkerOptions,
170
+ WorkerStats,
171
+ } from './worker';
172
+ export { createWorker, executeJob } from './worker';
@@ -0,0 +1,82 @@
1
+ import type { JobDriver, JobFilter, JobRecord, QueueStats } from './driver';
2
+ import type { Scheduler } from './scheduler';
3
+ import type { StepRecord } from './steps';
4
+ export interface QueueDepthReport {
5
+ readonly driver: string;
6
+ readonly queues: readonly QueueStats[];
7
+ readonly totals: {
8
+ readonly ready: number;
9
+ readonly delayed: number;
10
+ readonly running: number;
11
+ readonly suspended: number;
12
+ readonly dead: number;
13
+ };
14
+ /** Oldest claimable job across all queues, in ms. The autoscaling signal. */
15
+ readonly oldestReadyMs: number;
16
+ }
17
+ export declare function inspectQueues(driver: JobDriver): Promise<QueueDepthReport>;
18
+ export interface StepTrace {
19
+ readonly name: string;
20
+ readonly status: StepRecord['status'];
21
+ readonly startedAt: string;
22
+ readonly completedAt: string | null;
23
+ readonly wakeAt: string | null;
24
+ readonly durationMs: number | null;
25
+ readonly attempts: number;
26
+ readonly error: string | null;
27
+ }
28
+ export interface JobTrace {
29
+ readonly id: string;
30
+ readonly name: string;
31
+ readonly queue: string;
32
+ readonly state: JobRecord['state'];
33
+ readonly attempt: number;
34
+ readonly maxAttempts: number;
35
+ readonly idempotencyKey: string;
36
+ readonly runId: string;
37
+ readonly runAt: string;
38
+ readonly lastError: string | null;
39
+ readonly tenantId: string | null;
40
+ readonly steps: readonly StepTrace[];
41
+ /** Remaining retry delays in ms, jitter excluded. */
42
+ readonly retryDelaysMs: readonly number[];
43
+ }
44
+ export declare function inspectJob(driver: JobDriver, jobId: string): Promise<JobTrace | undefined>;
45
+ export declare function inspectJobList(driver: JobDriver, filter?: JobFilter): Promise<readonly JobRecord[]>;
46
+ export interface DeadLetterEntry {
47
+ readonly id: string;
48
+ readonly name: string;
49
+ readonly queue: string;
50
+ readonly attempt: number;
51
+ readonly lastError: string | null;
52
+ readonly failedAt: string;
53
+ readonly retryCommand: string;
54
+ }
55
+ export declare function inspectDeadLetters(driver: JobDriver, limit?: number): Promise<readonly DeadLetterEntry[]>;
56
+ /**
57
+ * Re-queue a job, optionally dropping one step's persisted result so it re-executes while
58
+ * everything before it replays from storage. This is why steps are stored, not just logged.
59
+ */
60
+ export declare function retryFromStep(driver: JobDriver, jobId: string, stepName?: string): Promise<JobTrace | undefined>;
61
+ export interface JobsManifest {
62
+ readonly jobs: readonly {
63
+ readonly name: string;
64
+ readonly queue: string;
65
+ readonly attempts: number;
66
+ readonly backoff: string;
67
+ readonly concurrency: number | null;
68
+ readonly timeoutMs: number | null;
69
+ readonly retryDelaysMs: readonly number[];
70
+ }[];
71
+ readonly tasks: readonly {
72
+ readonly name: string;
73
+ readonly cron: string;
74
+ readonly tz: string;
75
+ readonly catchUp: string;
76
+ readonly nextRun: string | null;
77
+ readonly enqueues: readonly string[];
78
+ }[];
79
+ }
80
+ /** Feeds `x.manifest.json` and the MCP `jobs.list` tool. Generated facts, never prose. */
81
+ export declare function inspectManifest(scheduler?: Scheduler): JobsManifest;
82
+ //# sourceMappingURL=inspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["inspect.ts"],"names":[],"mappings":"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"}
package/src/inspect.js ADDED
@@ -0,0 +1,113 @@
1
+ // Introspection for `/_x`, the CLI and MCP. Every function returns a plain JSON-serialisable
2
+ // object so `x jobs ... --json` and the MCP tool share one shape — an agent debugging a stuck
3
+ // queue reads exactly what the dashboard renders.
4
+ import { JobsNotImplementedError } from './errors';
5
+ import { registeredJobs } from './job';
6
+ import { retrySchedule } from './retry';
7
+ import { registeredTasks } from './scheduler';
8
+ export async function inspectQueues(driver) {
9
+ const queues = [...(await driver.stats())];
10
+ const totals = queues.reduce((acc, queue) => ({
11
+ ready: acc.ready + queue.ready,
12
+ delayed: acc.delayed + queue.delayed,
13
+ running: acc.running + queue.running,
14
+ suspended: acc.suspended + queue.suspended,
15
+ dead: acc.dead + queue.dead,
16
+ }), { ready: 0, delayed: 0, running: 0, suspended: 0, dead: 0 });
17
+ return {
18
+ driver: driver.name,
19
+ queues,
20
+ totals,
21
+ oldestReadyMs: queues.reduce((max, queue) => Math.max(max, queue.oldestReadyMs), 0),
22
+ };
23
+ }
24
+ const iso = (ms) => ms === undefined ? null : new Date(ms).toISOString();
25
+ function requireIntrospection(driver) {
26
+ if (driver.introspect === undefined) {
27
+ throw new JobsNotImplementedError({
28
+ feature: `introspection for the "${driver.name}" jobs driver`,
29
+ fix: 'use driver: "pg" (default) or "memory" — see docs/jobs/drivers.md',
30
+ });
31
+ }
32
+ return driver.introspect;
33
+ }
34
+ function toStepTrace(record) {
35
+ return {
36
+ name: record.name,
37
+ status: record.status,
38
+ startedAt: new Date(record.startedAt).toISOString(),
39
+ completedAt: iso(record.completedAt),
40
+ wakeAt: iso(record.wakeAt),
41
+ durationMs: record.completedAt === undefined ? null : record.completedAt - record.startedAt,
42
+ attempts: record.attempts,
43
+ error: record.error ?? null,
44
+ };
45
+ }
46
+ export async function inspectJob(driver, jobId) {
47
+ const record = await requireIntrospection(driver).job(jobId);
48
+ if (record === undefined)
49
+ return undefined;
50
+ const steps = await driver.steps.list(record.runId);
51
+ const handle = registeredJobs().find((candidate) => candidate.name === record.name);
52
+ return {
53
+ id: record.id,
54
+ name: record.name,
55
+ queue: record.queue,
56
+ state: record.state,
57
+ attempt: record.attempt,
58
+ maxAttempts: record.maxAttempts,
59
+ idempotencyKey: record.idempotencyKey,
60
+ runId: record.runId,
61
+ runAt: new Date(record.runAt).toISOString(),
62
+ lastError: record.lastError ?? null,
63
+ tenantId: record.tenantId ?? null,
64
+ steps: steps.map(toStepTrace),
65
+ retryDelaysMs: handle === undefined ? [] : [...retrySchedule(handle.retry)],
66
+ };
67
+ }
68
+ export function inspectJobList(driver, filter) {
69
+ return requireIntrospection(driver).list(filter);
70
+ }
71
+ export async function inspectDeadLetters(driver, limit = 100) {
72
+ const rows = await requireIntrospection(driver).deadLetters(limit);
73
+ return rows.map((record) => ({
74
+ id: record.id,
75
+ name: record.name,
76
+ queue: record.queue,
77
+ attempt: record.attempt,
78
+ lastError: record.lastError ?? null,
79
+ failedAt: new Date(record.updatedAt).toISOString(),
80
+ retryCommand: `x jobs retry ${record.id}`,
81
+ }));
82
+ }
83
+ /**
84
+ * Re-queue a job, optionally dropping one step's persisted result so it re-executes while
85
+ * everything before it replays from storage. This is why steps are stored, not just logged.
86
+ */
87
+ export async function retryFromStep(driver, jobId, stepName) {
88
+ await requireIntrospection(driver).requeue(jobId, stepName === undefined ? undefined : { fromStep: stepName });
89
+ return inspectJob(driver, jobId);
90
+ }
91
+ /** Feeds `x.manifest.json` and the MCP `jobs.list` tool. Generated facts, never prose. */
92
+ export function inspectManifest(scheduler) {
93
+ return {
94
+ jobs: registeredJobs().map((handle) => ({
95
+ name: handle.name,
96
+ queue: handle.queue,
97
+ attempts: handle.retry.attempts,
98
+ backoff: handle.retry.backoff ?? 'exponential',
99
+ concurrency: handle.concurrency ?? null,
100
+ timeoutMs: handle.timeoutMs ?? null,
101
+ retryDelaysMs: [...retrySchedule(handle.retry)],
102
+ })),
103
+ tasks: registeredTasks().map((handle) => ({
104
+ name: handle.name,
105
+ cron: handle.cron,
106
+ tz: handle.tz,
107
+ catchUp: handle.catchUp,
108
+ nextRun: scheduler === undefined ? null : scheduler.nextRunFor(handle).toISOString(),
109
+ enqueues: handle.entries().map(([job]) => job.name),
110
+ })),
111
+ };
112
+ }
113
+ //# sourceMappingURL=inspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect.js","sourceRoot":"","sources":["inspect.ts"],"names":[],"mappings":"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"}