bunqueue 2.8.53 → 2.8.54

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 (80) hide show
  1. package/dist/application/backgroundTasks.js +5 -1
  2. package/dist/application/cleanupTasks.js +1 -1
  3. package/dist/application/clientTracking.js +1 -1
  4. package/dist/application/contextFactory.js +1 -0
  5. package/dist/application/dlqManager.d.ts +7 -1
  6. package/dist/application/dlqManager.js +16 -5
  7. package/dist/application/lockManager.js +3 -3
  8. package/dist/application/operations/ack.js +2 -2
  9. package/dist/application/operations/ackHelpers.js +1 -1
  10. package/dist/application/operations/jobManagement.js +6 -4
  11. package/dist/application/operations/jobMoveOperations.js +3 -3
  12. package/dist/application/operations/jobStateTransitions.d.ts +1 -1
  13. package/dist/application/operations/jobStateTransitions.js +16 -2
  14. package/dist/application/queueManager.d.ts +22 -2
  15. package/dist/application/queueManager.js +48 -6
  16. package/dist/application/stallDetection.js +3 -3
  17. package/dist/client/flowJobDependencies.js +3 -1
  18. package/dist/client/flowJobMoveMethods.js +7 -2
  19. package/dist/client/jobConversion.d.ts +3 -3
  20. package/dist/client/jobConversion.js +10 -6
  21. package/dist/client/jobConversionHelpers.d.ts +1 -1
  22. package/dist/client/jobConversionHelpers.js +3 -6
  23. package/dist/client/jobConversionTypes.d.ts +4 -0
  24. package/dist/client/jobDeduplication.d.ts +6 -0
  25. package/dist/client/jobDeduplication.js +14 -0
  26. package/dist/client/queue/bullmqCompat.d.ts +18 -8
  27. package/dist/client/queue/bullmqCompat.js +39 -71
  28. package/dist/client/queue/deduplication.d.ts +2 -0
  29. package/dist/client/queue/deduplication.js +20 -11
  30. package/dist/client/queue/dlq.d.ts +11 -6
  31. package/dist/client/queue/dlq.js +47 -4
  32. package/dist/client/queue/dlqJobMethods.d.ts +9 -0
  33. package/dist/client/queue/dlqJobMethods.js +176 -0
  34. package/dist/client/queue/dlqOps.d.ts +2 -1
  35. package/dist/client/queue/dlqOps.js +2 -2
  36. package/dist/client/queue/jobMove.js +6 -1
  37. package/dist/client/queue/jobProxy.js +10 -5
  38. package/dist/client/queue/operations/add.js +17 -0
  39. package/dist/client/queue/operations/index.d.ts +1 -0
  40. package/dist/client/queue/operations/index.js +1 -0
  41. package/dist/client/queue/operations/management.d.ts +0 -4
  42. package/dist/client/queue/operations/management.js +33 -5
  43. package/dist/client/queue/operations/query.d.ts +1 -20
  44. package/dist/client/queue/operations/query.js +18 -53
  45. package/dist/client/queue/operations/queryStates.d.ts +13 -0
  46. package/dist/client/queue/operations/queryStates.js +32 -0
  47. package/dist/client/queue/operations/queryTcpPages.d.ts +14 -0
  48. package/dist/client/queue/operations/queryTcpPages.js +40 -0
  49. package/dist/client/queue/queue.d.ts +3 -0
  50. package/dist/client/queue/queue.js +23 -11
  51. package/dist/client/queue/rateLimit.d.ts +4 -4
  52. package/dist/client/queue/rateLimit.js +17 -8
  53. package/dist/client/queue/workers.d.ts +12 -0
  54. package/dist/client/queue/workers.js +20 -2
  55. package/dist/client/queueGroup.d.ts +11 -0
  56. package/dist/client/queueGroup.js +28 -1
  57. package/dist/client/sandboxed/worker.js +1 -1
  58. package/dist/client/worker/processor.js +1 -1
  59. package/dist/client/worker/processorHandlers.d.ts +1 -5
  60. package/dist/client/worker/processorHandlers.js +5 -7
  61. package/dist/client/worker/worker.d.ts +1 -1
  62. package/dist/client/worker/worker.js +37 -13
  63. package/dist/domain/queue/limiterManager.d.ts +11 -0
  64. package/dist/domain/queue/limiterManager.js +28 -0
  65. package/dist/domain/queue/shard.d.ts +10 -2
  66. package/dist/domain/queue/shard.js +23 -4
  67. package/dist/domain/queue/uniqueKeyManager.d.ts +3 -1
  68. package/dist/domain/queue/uniqueKeyManager.js +9 -1
  69. package/dist/domain/types/command.d.ts +34 -1
  70. package/dist/domain/types/queue.d.ts +5 -0
  71. package/dist/domain/types/queue.js +13 -0
  72. package/dist/infrastructure/persistence/sqlite.d.ts +4 -0
  73. package/dist/infrastructure/persistence/sqlite.js +16 -0
  74. package/dist/infrastructure/server/handlerRoutes.js +14 -1
  75. package/dist/infrastructure/server/handlers/dlq.d.ts +3 -0
  76. package/dist/infrastructure/server/handlers/dlq.js +18 -4
  77. package/dist/infrastructure/server/handlers/introspection.d.ts +19 -0
  78. package/dist/infrastructure/server/handlers/introspection.js +21 -0
  79. package/dist/infrastructure/server/handlers/query.js +1 -1
  80. package/package.json +1 -1
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import type { TcpConnectionPool } from '../tcpPool';
5
5
  import type { Job, JobDependencies, JobDependenciesCount, GetDependenciesOpts } from '../types';
6
- interface BullMQContext<T = unknown> {
6
+ interface CompatibilityContext<T = unknown> {
7
7
  name: string;
8
8
  embedded: boolean;
9
9
  tcp: TcpConnectionPool | null;
@@ -22,26 +22,36 @@ interface BullMQContext<T = unknown> {
22
22
  clearJobLogs: (id: string, keepLogs?: number) => Promise<void>;
23
23
  getJobDependencies: (id: string, opts?: GetDependenciesOpts) => Promise<JobDependencies>;
24
24
  getJobDependenciesCount: (id: string, opts?: GetDependenciesOpts) => Promise<JobDependenciesCount>;
25
+ getJobCountsAsync: () => Promise<{
26
+ prioritized: number;
27
+ 'waiting-children': number;
28
+ }>;
25
29
  getWaitingAsync: (start?: number, end?: number) => Promise<Job<T>[]>;
26
30
  getPrioritizedAsync: (start?: number, end?: number) => Promise<Job<T>[]>;
31
+ getJobsAsync: (options: {
32
+ state?: string | string[];
33
+ start?: number;
34
+ end?: number;
35
+ asc?: boolean;
36
+ }) => Promise<Job<T>[]>;
27
37
  }
28
38
  /** Get jobs in the "prioritized" state (priority > 0, BullMQ v5) */
29
- export declare function getPrioritized<T>(ctx: BullMQContext<T>, start?: number, end?: number): Promise<Job<T>[]>;
39
+ export declare function getPrioritized<T>(ctx: CompatibilityContext<T>, start?: number, end?: number): Promise<Job<T>[]>;
30
40
  /** Get count of prioritized jobs (priority > 0) */
31
- export declare function getPrioritizedCount<T>(ctx: BullMQContext<T>): Promise<number>;
41
+ export declare function getPrioritizedCount<T>(ctx: CompatibilityContext<T>): Promise<number>;
32
42
  /** Get jobs waiting for parent to complete */
33
- export declare function getWaitingChildren<T>(ctx: BullMQContext<T>, start?: number, end?: number): Promise<Job<T>[]>;
43
+ export declare function getWaitingChildren<T>(ctx: CompatibilityContext<T>, start?: number, end?: number): Promise<Job<T>[]>;
34
44
  /** Get count of jobs waiting for parent */
35
- export declare function getWaitingChildrenCount<T>(ctx: BullMQContext<T>): Promise<number>;
45
+ export declare function getWaitingChildrenCount<T>(ctx: CompatibilityContext<T>): Promise<number>;
36
46
  /** Get dependencies of a parent job */
37
- export declare function getDependencies(_ctx: BullMQContext, _parentId: string, _type?: 'processed' | 'unprocessed', _start?: number, _end?: number): Promise<{
47
+ export declare function getDependencies(ctx: CompatibilityContext, parentId: string, type?: 'processed' | 'unprocessed', start?: number, end?: number): Promise<{
38
48
  processed?: Record<string, unknown>;
39
49
  unprocessed?: string[];
40
50
  nextProcessedCursor?: number;
41
51
  nextUnprocessedCursor?: number;
42
52
  }>;
43
53
  /** Get job dependencies (for Job instance method) */
44
- export declare function getJobDependencies(ctx: BullMQContext, id: string, _opts?: GetDependenciesOpts): Promise<JobDependencies>;
54
+ export declare function getJobDependencies(ctx: CompatibilityContext, id: string, _opts?: GetDependenciesOpts): Promise<JobDependencies>;
45
55
  /** Get job dependencies count */
46
- export declare function getJobDependenciesCount(ctx: BullMQContext, id: string, _opts?: GetDependenciesOpts): Promise<JobDependenciesCount>;
56
+ export declare function getJobDependenciesCount(ctx: CompatibilityContext, id: string, _opts?: GetDependenciesOpts): Promise<JobDependenciesCount>;
47
57
  export {};
@@ -2,95 +2,63 @@
2
2
  /**
3
3
  * BullMQ v5 Compatibility Operations
4
4
  */
5
- import { getSharedManager } from '../manager';
6
- import { toPublicJob } from '../types';
7
- import { jobId } from '../../domain/types/job';
5
+ import { getFlowDependencies } from '../flowJobDependencies';
8
6
  /** Get jobs in the "prioritized" state (priority > 0, BullMQ v5) */
9
7
  export function getPrioritized(ctx, start = 0, end = -1) {
10
8
  return ctx.getPrioritizedAsync(start, end);
11
9
  }
12
10
  /** Get count of prioritized jobs (priority > 0) */
13
11
  export async function getPrioritizedCount(ctx) {
14
- const jobs = await ctx.getPrioritizedAsync(0, 1000);
15
- return jobs.length;
12
+ return (await ctx.getJobCountsAsync()).prioritized;
16
13
  }
17
14
  /** Get jobs waiting for parent to complete */
18
15
  export async function getWaitingChildren(ctx, start = 0, end = -1) {
19
- if (ctx.embedded) {
20
- const jobs = getSharedManager().getJobs(ctx.name, {
21
- state: 'delayed',
22
- start,
23
- end: end === -1 ? 1000 : end,
24
- });
25
- return jobs
26
- .filter((j) => {
27
- const data = j.data;
28
- return data?._waitingParent === true;
29
- })
30
- .map((job) => {
31
- const jobData = job.data;
32
- return toPublicJob({
33
- job,
34
- name: jobData?.name ?? 'default',
35
- getState: ctx.getJobState,
36
- remove: ctx.removeAsync,
37
- retry: ctx.retryJob,
38
- getChildrenValues: ctx.getChildrenValues,
39
- updateData: ctx.updateJobData,
40
- promote: ctx.promoteJob,
41
- changeDelay: ctx.changeJobDelay,
42
- changePriority: ctx.changeJobPriority,
43
- extendLock: ctx.extendJobLock,
44
- clearLogs: ctx.clearJobLogs,
45
- getDependencies: ctx.getJobDependencies,
46
- getDependenciesCount: ctx.getJobDependenciesCount,
47
- });
48
- });
49
- }
50
- return [];
16
+ return ctx.getJobsAsync({
17
+ state: 'waiting-children',
18
+ start,
19
+ end: end === -1 ? -1 : end + 1,
20
+ asc: true,
21
+ });
51
22
  }
52
23
  /** Get count of jobs waiting for parent */
53
24
  export async function getWaitingChildrenCount(ctx) {
54
- const children = await getWaitingChildren(ctx);
55
- return children.length;
25
+ return (await ctx.getJobCountsAsync())['waiting-children'];
56
26
  }
57
27
  /** Get dependencies of a parent job */
58
- export function getDependencies(_ctx, _parentId, _type, _start, _end) {
59
- return Promise.resolve({ processed: {}, unprocessed: [] });
28
+ export function getDependencies(ctx, parentId, type, start = 0, end = -1) {
29
+ return getFlowDependencies(parentId, ctx.name, ctx.embedded, ctx.tcp).then((dependencies) => {
30
+ const unprocessed = [...dependencies.unprocessed].sort();
31
+ const processedEntries = Object.entries(dependencies.processed).sort(([a], [b]) => a.localeCompare(b));
32
+ const stop = end < 0 ? undefined : end + 1;
33
+ return {
34
+ processed: type === 'unprocessed' ? {} : Object.fromEntries(processedEntries.slice(start, stop)),
35
+ unprocessed: type === 'processed' ? [] : unprocessed.slice(start, stop),
36
+ };
37
+ });
60
38
  }
61
39
  /** Get job dependencies (for Job instance method) */
62
40
  export async function getJobDependencies(ctx, id, _opts) {
63
- if (ctx.embedded) {
64
- const manager = getSharedManager();
65
- const job = await manager.getJob(jobId(id));
66
- if (!job)
67
- return { processed: {}, unprocessed: [] };
68
- const childIds = job.childrenIds;
69
- const processed = {};
70
- const unprocessed = [];
71
- for (const childId of childIds) {
72
- const result = manager.getResult(childId);
73
- if (result !== undefined) {
74
- const childJob = await manager.getJob(childId);
75
- const key = childJob ? `${childJob.queue}:${childId}` : String(childId);
76
- processed[key] = result;
77
- }
78
- else {
79
- unprocessed.push(String(childId));
80
- }
81
- }
82
- return { processed, unprocessed };
83
- }
84
- return { processed: {}, unprocessed: [] };
41
+ const dependencies = await getFlowDependencies(id, ctx.name, ctx.embedded, ctx.tcp);
42
+ const processedEntries = Object.entries(dependencies.processed).sort(([a], [b]) => a.localeCompare(b));
43
+ const unprocessed = [...dependencies.unprocessed].sort();
44
+ const processedCursor = _opts?.processed?.cursor ?? 0;
45
+ const processedCount = _opts?.processed?.count ?? processedEntries.length;
46
+ const unprocessedCursor = _opts?.unprocessed?.cursor ?? 0;
47
+ const unprocessedCount = _opts?.unprocessed?.count ?? unprocessed.length;
48
+ const processedEnd = Math.min(processedEntries.length, processedCursor + processedCount);
49
+ const unprocessedEnd = Math.min(unprocessed.length, unprocessedCursor + unprocessedCount);
50
+ return {
51
+ processed: Object.fromEntries(processedEntries.slice(processedCursor, processedEnd)),
52
+ unprocessed: unprocessed.slice(unprocessedCursor, unprocessedEnd),
53
+ nextProcessedCursor: processedEnd < processedEntries.length ? processedEnd : 0,
54
+ nextUnprocessedCursor: unprocessedEnd < unprocessed.length ? unprocessedEnd : 0,
55
+ };
85
56
  }
86
57
  /** Get job dependencies count */
87
58
  export async function getJobDependenciesCount(ctx, id, _opts) {
88
- if (ctx.embedded) {
89
- const deps = await getJobDependencies(ctx, id);
90
- return {
91
- processed: Object.keys(deps.processed).length,
92
- unprocessed: deps.unprocessed.length,
93
- };
94
- }
95
- return { processed: 0, unprocessed: 0 };
59
+ const deps = await getFlowDependencies(id, ctx.name, ctx.embedded, ctx.tcp);
60
+ return {
61
+ processed: Object.keys(deps.processed).length,
62
+ unprocessed: deps.unprocessed.length,
63
+ };
96
64
  }
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Deduplication Operations
3
3
  */
4
+ import type { TcpConnectionPool } from '../tcpPool';
4
5
  interface DeduplicationContext {
5
6
  name: string;
6
7
  embedded: boolean;
8
+ tcp: TcpConnectionPool | null;
7
9
  }
8
10
  /** Get the job ID associated with a deduplication key */
9
11
  export declare function getDeduplicationJobId(ctx: DeduplicationContext, deduplicationId: string): Promise<string | null>;
@@ -3,21 +3,30 @@
3
3
  */
4
4
  import { getSharedManager } from '../manager';
5
5
  /** Get the job ID associated with a deduplication key */
6
- export function getDeduplicationJobId(ctx, deduplicationId) {
6
+ export async function getDeduplicationJobId(ctx, deduplicationId) {
7
7
  if (ctx.embedded) {
8
- const job = getSharedManager().getJobByCustomId(deduplicationId);
9
- return Promise.resolve(job ? String(job.id) : null);
8
+ return getSharedManager().getDeduplicationJobId(ctx.name, deduplicationId);
10
9
  }
11
- return Promise.resolve(null);
10
+ if (!ctx.tcp)
11
+ return null;
12
+ const response = await ctx.tcp.send({
13
+ cmd: 'GetDeduplicationJobId',
14
+ queue: ctx.name,
15
+ deduplicationId,
16
+ });
17
+ return response.data.jobId;
12
18
  }
13
19
  /** Remove a deduplication key */
14
- export function removeDeduplicationKey(ctx, deduplicationId) {
20
+ export async function removeDeduplicationKey(ctx, deduplicationId) {
15
21
  if (ctx.embedded) {
16
- const job = getSharedManager().getJobByCustomId(deduplicationId);
17
- if (job) {
18
- return Promise.resolve(1);
19
- }
20
- return Promise.resolve(0);
22
+ return getSharedManager().removeDeduplicationKey(ctx.name, deduplicationId);
21
23
  }
22
- return Promise.resolve(0);
24
+ if (!ctx.tcp)
25
+ return 0;
26
+ const response = await ctx.tcp.send({
27
+ cmd: 'RemoveDeduplicationKey',
28
+ queue: ctx.name,
29
+ deduplicationId,
30
+ });
31
+ return response.data.count;
23
32
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { TcpConnectionPool } from '../tcpPool';
6
6
  import type { Job, DlqConfig, DlqEntry, DlqStats, DlqFilter } from '../types';
7
- import { type SimpleJobContext } from './jobProxy';
7
+ import { type DlqJobContext } from './dlqJobMethods';
8
8
  interface DlqContext {
9
9
  name: string;
10
10
  embedded: boolean;
@@ -19,16 +19,19 @@ export declare function getDlqConfig(ctx: DlqContext): DlqConfig;
19
19
  /** Get DLQ configuration (async, works in TCP mode) */
20
20
  export declare function getDlqConfigAsync(ctx: DlqContext): Promise<DlqConfig>;
21
21
  /** Get DLQ entries */
22
- export declare function getDlq<T>(ctx: DlqContext, filter?: DlqFilter): DlqEntry<T>[];
23
- type DlqQueryContext = DlqContext & Pick<SimpleJobContext, 'getJobState' | 'removeAsync' | 'retryJob' | 'getChildrenValues'>;
22
+ export declare function getDlq<T>(ctx: DlqQueryContext, filter?: DlqFilter): DlqEntry<T>[];
23
+ type DlqQueryContext = DlqJobContext;
24
+ /** Read full DLQ metadata from the selected broker runtime. */
25
+ export declare function getDlqAsync<T>(ctx: DlqQueryContext, filter?: DlqFilter): Promise<DlqEntry<T>[]>;
24
26
  /**
25
- * Get the dead jobs in the DLQ, working over TCP too (wire command `Dlq`).
26
- * Returns public Job objects; the wire does not carry DlqEntry metadata
27
- * (reason, enteredAt, attempts) — that stays embedded-only via getDlq().
27
+ * Get only the public jobs from the DLQ. Use getDlqAsync() when the full entry
28
+ * metadata is needed; both views work over TCP through the same command.
28
29
  */
29
30
  export declare function getDlqJobsAsync<T>(ctx: DlqQueryContext, count?: number): Promise<Job<T>[]>;
30
31
  /** Get DLQ stats */
31
32
  export declare function getDlqStats(ctx: DlqContext): DlqStats;
33
+ /** Read authoritative DLQ statistics from the selected broker runtime. */
34
+ export declare function getDlqStatsAsync(ctx: DlqContext): Promise<DlqStats>;
32
35
  /** Retry DLQ entries */
33
36
  export declare function retryDlq(ctx: DlqContext, id?: string): number;
34
37
  /**
@@ -39,6 +42,8 @@ export declare function retryDlq(ctx: DlqContext, id?: string): number;
39
42
  export declare function retryDlqAsync(ctx: DlqContext, id?: string): Promise<number>;
40
43
  /** Retry DLQ entries by filter */
41
44
  export declare function retryDlqByFilter(ctx: DlqContext, filter: DlqFilter): number;
45
+ /** Retry matching DLQ entries and return the applied count. */
46
+ export declare function retryDlqByFilterAsync(ctx: DlqContext, filter: DlqFilter): Promise<number>;
42
47
  /** Purge DLQ */
43
48
  export declare function purgeDlq(ctx: DlqContext): number;
44
49
  /**
@@ -6,6 +6,8 @@ import { getSharedManager } from '../manager';
6
6
  import { jobId } from '../../domain/types/job';
7
7
  import { createSimpleJob } from './jobProxy';
8
8
  import * as dlqOps from './dlqOps';
9
+ import { toDlqEntry } from '../jobConversion';
10
+ import { createDlqJobMethods } from './dlqJobMethods';
9
11
  /** Client-side cache for TCP mode */
10
12
  const tcpDlqConfigCache = new Map();
11
13
  /** Set DLQ configuration */
@@ -56,12 +58,31 @@ export async function getDlqConfigAsync(ctx) {
56
58
  export function getDlq(ctx, filter) {
57
59
  if (!ctx.embedded)
58
60
  return [];
59
- return dlqOps.getDlqEntries(ctx.name, filter);
61
+ const entries = getSharedManager().getDlqEntries(ctx.name, filter);
62
+ const methods = createDlqJobMethods(ctx);
63
+ return entries.map((entry) => toDlqEntry(entry, methods));
64
+ }
65
+ /** Read full DLQ metadata from the selected broker runtime. */
66
+ export async function getDlqAsync(ctx, filter) {
67
+ let entries;
68
+ if (ctx.embedded) {
69
+ entries = getSharedManager().getDlqEntries(ctx.name, filter);
70
+ }
71
+ else if (ctx.tcp) {
72
+ const response = await ctx.tcp.send({ cmd: 'Dlq', queue: ctx.name, filter });
73
+ if (!response.ok)
74
+ return [];
75
+ entries = (response.entries ?? []);
76
+ }
77
+ else {
78
+ return [];
79
+ }
80
+ const methods = createDlqJobMethods(ctx);
81
+ return entries.map((entry) => toDlqEntry(entry, methods));
60
82
  }
61
83
  /**
62
- * Get the dead jobs in the DLQ, working over TCP too (wire command `Dlq`).
63
- * Returns public Job objects; the wire does not carry DlqEntry metadata
64
- * (reason, enteredAt, attempts) — that stays embedded-only via getDlq().
84
+ * Get only the public jobs from the DLQ. Use getDlqAsync() when the full entry
85
+ * metadata is needed; both views work over TCP through the same command.
65
86
  */
66
87
  export async function getDlqJobsAsync(ctx, count) {
67
88
  let raw;
@@ -104,6 +125,17 @@ export function getDlqStats(ctx) {
104
125
  }
105
126
  return dlqOps.getDlqStatsEmbedded(ctx.name);
106
127
  }
128
+ /** Read authoritative DLQ statistics from the selected broker runtime. */
129
+ export async function getDlqStatsAsync(ctx) {
130
+ if (ctx.embedded)
131
+ return dlqOps.getDlqStatsEmbedded(ctx.name);
132
+ if (!ctx.tcp)
133
+ return getDlqStats(ctx);
134
+ const response = await ctx.tcp.send({ cmd: 'GetDlqStats', queue: ctx.name });
135
+ if (!response.ok)
136
+ return getDlqStats(ctx);
137
+ return response.data.stats;
138
+ }
107
139
  /** Retry DLQ entries */
108
140
  export function retryDlq(ctx, id) {
109
141
  if (ctx.embedded)
@@ -133,6 +165,17 @@ export function retryDlqByFilter(ctx, filter) {
133
165
  return 0;
134
166
  return dlqOps.retryDlqByFilterEmbedded(ctx.name, filter);
135
167
  }
168
+ /** Retry matching DLQ entries and return the applied count. */
169
+ export async function retryDlqByFilterAsync(ctx, filter) {
170
+ if (ctx.embedded)
171
+ return dlqOps.retryDlqByFilterEmbedded(ctx.name, filter);
172
+ if (!ctx.tcp)
173
+ return 0;
174
+ const response = await ctx.tcp.send({ cmd: 'RetryDlq', queue: ctx.name, filter });
175
+ if (!response.ok)
176
+ return 0;
177
+ return (response.count ?? 0);
178
+ }
136
179
  /** Purge DLQ */
137
180
  export function purgeDlq(ctx) {
138
181
  if (ctx.embedded)
@@ -0,0 +1,9 @@
1
+ import type { PublicJobMethodContext } from '../jobConversionTypes';
2
+ import type { TcpConnectionPool } from '../tcpPool';
3
+ import type { SimpleJobContext } from './jobProxy';
4
+ export interface DlqJobContext extends Pick<SimpleJobContext, 'getJobState' | 'removeAsync' | 'retryJob' | 'getChildrenValues'> {
5
+ name: string;
6
+ embedded: boolean;
7
+ tcp: TcpConnectionPool | null;
8
+ }
9
+ export declare function createDlqJobMethods(ctx: DlqJobContext): PublicJobMethodContext;
@@ -0,0 +1,176 @@
1
+ import { jobId } from '../../domain/types/job';
2
+ import { getFlowDependencies } from '../flowJobDependencies';
3
+ import { getSharedManager } from '../manager';
4
+ import { removeJobDeduplicationKey } from '../jobDeduplication';
5
+ import { buildFailCommand, failEmbeddedArgs } from './failWire';
6
+ function assertCommand(response, operation) {
7
+ if (response.ok === true)
8
+ return;
9
+ throw new Error(typeof response.error === 'string' ? response.error : `${operation} failed`);
10
+ }
11
+ export function createDlqJobMethods(ctx) {
12
+ const manager = getSharedManager();
13
+ const send = async (command, operation) => {
14
+ if (!ctx.tcp)
15
+ throw new Error(`${operation}: no connection`);
16
+ const response = await ctx.tcp.send(command);
17
+ assertCommand(response, operation);
18
+ return response;
19
+ };
20
+ const dependencies = (id) => getFlowDependencies(id, ctx.name, ctx.embedded, ctx.tcp);
21
+ return {
22
+ updateProgress: async (id, progress, message) => {
23
+ if (ctx.embedded) {
24
+ if (!(await manager.updateProgress(jobId(id), progress, message))) {
25
+ throw new Error(`Job ${id} is not active`);
26
+ }
27
+ }
28
+ else
29
+ await send({ cmd: 'Progress', id, progress, message }, 'Progress');
30
+ },
31
+ log: async (id, message) => {
32
+ if (ctx.embedded)
33
+ manager.addLog(jobId(id), message);
34
+ else
35
+ await send({ cmd: 'AddLog', id, message }, 'AddLog');
36
+ },
37
+ getState: ctx.getJobState,
38
+ remove: ctx.removeAsync,
39
+ retry: ctx.retryJob,
40
+ getChildrenValues: ctx.getChildrenValues,
41
+ updateData: async (id, data) => {
42
+ if (ctx.embedded)
43
+ await manager.updateJobData(jobId(id), data);
44
+ else
45
+ await send({ cmd: 'Update', id, data }, 'Update');
46
+ },
47
+ promote: async (id) => {
48
+ if (ctx.embedded)
49
+ await manager.promote(jobId(id));
50
+ else
51
+ await send({ cmd: 'Promote', id }, 'Promote');
52
+ },
53
+ changeDelay: async (id, delay) => {
54
+ if (ctx.embedded)
55
+ await manager.changeDelay(jobId(id), delay);
56
+ else
57
+ await send({ cmd: 'ChangeDelay', id, delay }, 'ChangeDelay');
58
+ },
59
+ changePriority: async (id, options) => {
60
+ if (ctx.embedded)
61
+ await manager.changePriority(jobId(id), options.priority, options.lifo);
62
+ else {
63
+ await send({ cmd: 'ChangePriority', id, priority: options.priority, lifo: options.lifo }, 'ChangePriority');
64
+ }
65
+ },
66
+ extendLock: async (id, token, duration) => {
67
+ if (ctx.embedded)
68
+ return (await manager.extendLock(jobId(id), token, duration)) ? duration : 0;
69
+ const response = await send({ cmd: 'ExtendLock', id, token, duration }, 'ExtendLock');
70
+ return response.ok === true ? duration : 0;
71
+ },
72
+ clearLogs: async (id, keepLogs) => {
73
+ if (ctx.embedded)
74
+ manager.clearLogs(jobId(id), keepLogs);
75
+ else
76
+ await send({ cmd: 'ClearLogs', id, keepLogs }, 'ClearLogs');
77
+ },
78
+ getDependencies: async (id) => dependencies(id),
79
+ getDependenciesCount: async (id) => {
80
+ const value = await dependencies(id);
81
+ return {
82
+ processed: Object.keys(value.processed).length,
83
+ unprocessed: value.unprocessed.length,
84
+ };
85
+ },
86
+ moveToCompleted: async (id, result, token) => {
87
+ if (ctx.embedded)
88
+ await manager.ack(jobId(id), result, token);
89
+ else
90
+ await send({ cmd: 'ACK', id, result, token }, 'ACK');
91
+ return null;
92
+ },
93
+ moveToFailed: async (id, error, token) => {
94
+ if (ctx.embedded)
95
+ await manager.fail(jobId(id), ...failEmbeddedArgs(error, token));
96
+ else
97
+ await send(buildFailCommand(id, error, token), 'FAIL');
98
+ },
99
+ moveToWait: async (id) => {
100
+ if (ctx.embedded) {
101
+ const state = await manager.getJobState(jobId(id));
102
+ if (state === 'failed')
103
+ return manager.retryDlq(ctx.name, jobId(id)) > 0;
104
+ if (state === 'active')
105
+ return manager.moveActiveToWait(jobId(id));
106
+ if (state === 'delayed')
107
+ return manager.promote(jobId(id));
108
+ return state === 'waiting' || state === 'prioritized';
109
+ }
110
+ const response = await send({ cmd: 'MoveToWait', id }, 'MoveToWait');
111
+ return response.ok === true;
112
+ },
113
+ moveToDelayed: async (id, timestamp) => {
114
+ const delay = Math.max(0, timestamp - Date.now());
115
+ if (ctx.embedded)
116
+ await manager.moveToDelayed(jobId(id), delay);
117
+ else
118
+ await send({ cmd: 'MoveToDelayed', id, delay }, 'MoveToDelayed');
119
+ },
120
+ moveToWaitingChildren: async (id) => {
121
+ if (ctx.embedded)
122
+ return manager.moveToWaitingChildren(jobId(id));
123
+ await send({ cmd: 'MoveToWaitingChildren', id }, 'MoveToWaitingChildren');
124
+ return true;
125
+ },
126
+ waitUntilFinished: async (id, _events, ttl) => {
127
+ const timeout = ttl ?? 30_000;
128
+ if (ctx.embedded) {
129
+ const current = await manager.getJob(jobId(id));
130
+ if (!current)
131
+ throw new Error(`Job ${id} not found`);
132
+ if (current.completedAt)
133
+ return manager.getResult(jobId(id));
134
+ if (!(await manager.waitForJobCompletion(jobId(id), timeout))) {
135
+ throw new Error(`Job ${id} timed out after ${timeout}ms`);
136
+ }
137
+ return manager.getResult(jobId(id));
138
+ }
139
+ const response = await send({ cmd: 'WaitJob', id, timeout }, 'WaitJob');
140
+ if (!response.completed)
141
+ throw new Error(`Job ${id} timed out after ${timeout}ms`);
142
+ return response.result;
143
+ },
144
+ discard: (id) => {
145
+ if (ctx.embedded)
146
+ void manager.discard(jobId(id));
147
+ else if (ctx.tcp)
148
+ void ctx.tcp.send({ cmd: 'Discard', id });
149
+ },
150
+ getFailedChildrenValues: async (id) => {
151
+ if (ctx.embedded)
152
+ return manager.getFailedChildrenValues(jobId(id));
153
+ const response = await send({ cmd: 'GetFailedChildrenValues', id }, 'GetFailedChildrenValues');
154
+ return response.values ?? {};
155
+ },
156
+ getIgnoredChildrenFailures: async (id) => {
157
+ if (ctx.embedded)
158
+ return manager.getIgnoredChildrenFailures(jobId(id));
159
+ const response = await send({ cmd: 'GetIgnoredChildrenFailures', id }, 'GetIgnoredChildrenFailures');
160
+ return response.values ?? {};
161
+ },
162
+ removeChildDependency: async (id) => {
163
+ if (ctx.embedded)
164
+ return manager.removeChildDependency(jobId(id));
165
+ const response = await send({ cmd: 'RemoveChildDependency', id }, 'RemoveChildDependency');
166
+ return response.removed ?? false;
167
+ },
168
+ removeDeduplicationKey: (id) => removeJobDeduplicationKey(id, ctx.embedded, ctx.tcp),
169
+ removeUnprocessedChildren: async (id) => {
170
+ if (ctx.embedded)
171
+ await manager.removeUnprocessedChildren(jobId(id));
172
+ else
173
+ await send({ cmd: 'RemoveUnprocessedChildren', id }, 'RemoveUnprocessedChildren');
174
+ },
175
+ };
176
+ }
@@ -3,12 +3,13 @@
3
3
  * Dead Letter Queue management for embedded mode
4
4
  */
5
5
  import type { DlqConfig, DlqEntry, DlqStats, DlqFilter, StallConfig } from '../types';
6
+ import type { PublicJobMethodContext } from '../jobConversionTypes';
6
7
  /** Set DLQ configuration (embedded only) */
7
8
  export declare function setDlqConfig(queue: string, config: Partial<DlqConfig>): void;
8
9
  /** Get DLQ configuration (embedded only) */
9
10
  export declare function getDlqConfigEmbedded(queue: string): DlqConfig;
10
11
  /** Get DLQ entries (embedded only) */
11
- export declare function getDlqEntries<T>(queue: string, filter?: DlqFilter): DlqEntry<T>[];
12
+ export declare function getDlqEntries<T>(queue: string, methods: PublicJobMethodContext, filter?: DlqFilter): DlqEntry<T>[];
12
13
  /** Get DLQ statistics (embedded only) */
13
14
  export declare function getDlqStatsEmbedded(queue: string): DlqStats;
14
15
  /** Retry DLQ jobs by filter (embedded only) */
@@ -18,11 +18,11 @@ export function getDlqConfigEmbedded(queue) {
18
18
  return dlqOps.getDlqConfig(queue, ctx);
19
19
  }
20
20
  /** Get DLQ entries (embedded only) */
21
- export function getDlqEntries(queue, filter) {
21
+ export function getDlqEntries(queue, methods, filter) {
22
22
  const manager = getSharedManager();
23
23
  const ctx = getDlqContext(manager);
24
24
  const entries = dlqOps.getDlqEntries(queue, ctx, toDomainFilter(filter));
25
- return entries.map((entry) => toDlqEntry(entry));
25
+ return entries.map((entry) => toDlqEntry(entry, methods));
26
26
  }
27
27
  /** Get DLQ statistics (embedded only) */
28
28
  export function getDlqStatsEmbedded(queue) {
@@ -104,7 +104,12 @@ export async function moveJobToWaitingChildren(ctx, id, _token, _opts) {
104
104
  const manager = getSharedManager();
105
105
  return manager.moveToWaitingChildren(jobId(id));
106
106
  }
107
- return false;
107
+ if (!ctx.tcp)
108
+ return false;
109
+ const response = await ctx.tcp.send({ cmd: 'MoveToWaitingChildren', id });
110
+ if (!response.ok)
111
+ return false;
112
+ return response.data?.moved ?? true;
108
113
  }
109
114
  /** Wait until job has finished */
110
115
  export async function waitJobUntilFinished(ctx, id, queueEvents, ttl) {
@@ -5,6 +5,7 @@
5
5
  import { getSharedManager } from '../manager';
6
6
  import { jobId } from '../../domain/types/job';
7
7
  import { buildFailCommand, failEmbeddedArgs } from './failWire';
8
+ import { removeJobDeduplicationKey } from '../jobDeduplication';
8
9
  function reflectFields(id, queueName, meta) {
9
10
  const opts = meta?.opts ?? {};
10
11
  const p = opts.parent;
@@ -142,8 +143,9 @@ export function createJobProxy(id, name, data, ctx, meta) {
142
143
  const delay = Math.max(0, timestamp - Date.now());
143
144
  await tcp.send({ cmd: 'MoveToDelayed', id, delay });
144
145
  },
145
- moveToWaitingChildren: () => {
146
- return Promise.reject(new Error('moveToWaitingChildren is not supported in TCP mode — no server command available'));
146
+ moveToWaitingChildren: async () => {
147
+ const response = await tcp.send({ cmd: 'MoveToWaitingChildren', id });
148
+ return response.ok === true;
147
149
  },
148
150
  waitUntilFinished: async (_queueEvents, ttl) => {
149
151
  const timeout = ttl ?? 30000;
@@ -169,7 +171,7 @@ export function createJobProxy(id, name, data, ctx, meta) {
169
171
  const res = await tcp.send({ cmd: 'RemoveChildDependency', id });
170
172
  return res.removed ?? false;
171
173
  },
172
- removeDeduplicationKey: () => Promise.reject(new Error('removeDeduplicationKey is not implemented — no server primitive available')),
174
+ removeDeduplicationKey: () => removeJobDeduplicationKey(id, false, tcp),
173
175
  removeUnprocessedChildren: async () => {
174
176
  await tcp.send({ cmd: 'RemoveUnprocessedChildren', id });
175
177
  },
@@ -398,7 +400,10 @@ export function createSimpleJob(id, name, data, timestamp, ctx) {
398
400
  if (embedded) {
399
401
  return await getSharedManager().moveToWaitingChildren(jobId(id));
400
402
  }
401
- throw new Error('moveToWaitingChildren is not supported in TCP mode — no server command available');
403
+ if (!tcp)
404
+ return false;
405
+ const response = await tcp.send({ cmd: 'MoveToWaitingChildren', id });
406
+ return response.ok === true;
402
407
  },
403
408
  waitUntilFinished: async (_qe, ttl) => {
404
409
  const timeout = ttl ?? 30000;
@@ -457,7 +462,7 @@ export function createSimpleJob(id, name, data, timestamp, ctx) {
457
462
  const res = await tcp.send({ cmd: 'RemoveChildDependency', id });
458
463
  return res.removed ?? false;
459
464
  },
460
- removeDeduplicationKey: () => Promise.reject(new Error('removeDeduplicationKey is not implemented — no server primitive available')),
465
+ removeDeduplicationKey: () => removeJobDeduplicationKey(id, embedded ?? false, tcp),
461
466
  removeUnprocessedChildren: async () => {
462
467
  if (embedded) {
463
468
  await getSharedManager().removeUnprocessedChildren(jobId(id));