bunqueue 2.8.12 → 2.8.13

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.
@@ -6,7 +6,7 @@ import { createPublicJob } from '../types';
6
6
  import { getSharedManager } from '../manager';
7
7
  import { UnrecoverableError } from '../errors';
8
8
  import { DelayedError } from '../errors';
9
- import { createProgressHandler, createLogHandler, createGetStateHandler, createGetChildrenValuesHandler, createGetFailedChildrenValuesHandler, createGetIgnoredChildrenFailuresHandler, createRemoveChildDependencyHandler, createRemoveUnprocessedChildrenHandler, createMoveToFailedHandler, createMoveToCompletedHandler, createRemoveHandler, createRetryHandler, createUpdateDataHandler, createPromoteHandler, createChangeDelayHandler, createChangePriorityHandler, createExtendLockHandler, createClearLogsHandler, createMoveToWaitHandler, createMoveToDelayedHandler, createMoveToWaitingChildrenHandler, createWaitUntilFinishedHandler, createDiscardHandler, createGetDependenciesHandler, createGetDependenciesCountHandler, createRemoveDeduplicationKeyHandler, } from './processorHandlers';
9
+ import { createProgressHandler, createLogHandler, createGetStateHandler, createGetChildrenValuesHandler, createGetFailedChildrenValuesHandler, createGetIgnoredChildrenFailuresHandler, createRemoveChildDependencyHandler, createRemoveUnprocessedChildrenHandler, createMoveToFailedHandler, createMoveToCompletedHandler, createRemoveHandler, createRetryHandler, createUpdateDataHandler, createPromoteHandler, createChangeDelayHandler, createChangePriorityHandler, createExtendLockHandler, createClearLogsHandler, createMoveToWaitHandler, createMoveToDelayedHandler, createMoveToWaitingChildrenHandler, createWaitUntilFinishedHandler, createDiscardHandler, createGetDependenciesHandler, createGetDependenciesCountHandler, createRemoveDeduplicationKeyHandler, computeStackLines, } from './processorHandlers';
10
10
  /**
11
11
  * Process a single job
12
12
  */
@@ -63,7 +63,7 @@ export async function processJob(internalJob, config) {
63
63
  try {
64
64
  const result = await processor(job);
65
65
  // Issue #82: If moveToFailed/moveToCompleted was called, skip auto-ACK
66
- if (handleManualMove(manualMove, job, config))
66
+ if (handleManualMove(manualMove, job, config, internalJob))
67
67
  return;
68
68
  // Normal path: auto-ACK
69
69
  try {
@@ -94,16 +94,23 @@ export async function processJob(internalJob, config) {
94
94
  }
95
95
  catch (error) {
96
96
  // Issue #82: If moveToFailed was already called, skip normal failure handling
97
- if (handleManualMove(manualMove, job, config))
97
+ if (handleManualMove(manualMove, job, config, internalJob))
98
98
  return;
99
99
  await handleJobFailure(internalJob, error, config, { job, jobIdStr, token });
100
100
  }
101
101
  }
102
102
  /** Issue #82: Handle explicit moveToFailed/moveToCompleted called inside processor */
103
- function handleManualMove(manualMove, job, config) {
103
+ function handleManualMove(manualMove, job, config, internalJob) {
104
104
  if (manualMove.result?.type === 'failed') {
105
105
  const err = manualMove.result.error ?? new Error('Job manually moved to failed');
106
106
  job.failedReason = err.message;
107
+ // Bug #74 follow-up: populate stacktrace on the local `failed` event for the
108
+ // explicit moveToFailed() path too, mirroring handleJobFailure. The stack is
109
+ // persisted server-side by createMoveToFailedHandler's FAIL/manager.fail call.
110
+ if (err.stack) {
111
+ const { stackLines } = computeStackLines(err);
112
+ job.stacktrace = stackLines.slice(0, internalJob.stackTraceLimit);
113
+ }
107
114
  config.onOutcome?.(false);
108
115
  config.emitter.emit('failed', job, err);
109
116
  return true;
@@ -160,13 +167,7 @@ async function handleJobFailure(internalJob, error, config, context) {
160
167
  // Bug #74: stack lines computed BEFORE the send so the server can persist
161
168
  // them. The wire copy is capped at 50 lines as a bandwidth guard; the
162
169
  // authoritative cap (job.stackTraceLimit) is applied server-side in failJob.
163
- const stackLines = err.stack
164
- ? err.stack
165
- .split('\n')
166
- .map((l) => l.trim())
167
- .filter(Boolean)
168
- : [];
169
- const wireStack = stackLines.length > 0 ? stackLines.slice(0, 50) : undefined;
170
+ const { stackLines, wireStack } = computeStackLines(err);
170
171
  try {
171
172
  if (embedded) {
172
173
  const manager = getSharedManager();
@@ -19,6 +19,17 @@ export declare function createGetFailedChildrenValuesHandler(embedded: boolean,
19
19
  export declare function createGetIgnoredChildrenFailuresHandler(embedded: boolean, tcp: TcpConnection | null): (id: string) => Promise<Record<string, string>>;
20
20
  export declare function createRemoveChildDependencyHandler(embedded: boolean, tcp: TcpConnection | null): (id: string) => Promise<boolean>;
21
21
  export declare function createRemoveUnprocessedChildrenHandler(embedded: boolean, tcp: TcpConnection | null): (id: string) => Promise<void>;
22
+ /**
23
+ * Bug #74: split an Error's stack into trimmed, non-empty lines.
24
+ * `wireStack` is the bandwidth-capped copy sent to the server (50 lines); the
25
+ * authoritative cap (job.stackTraceLimit) is applied server-side in failJob.
26
+ * Shared by the natural-throw path (handleJobFailure) and the explicit
27
+ * moveToFailed() path so both persist the stack identically.
28
+ */
29
+ export declare function computeStackLines(err: Error): {
30
+ stackLines: string[];
31
+ wireStack: string[] | undefined;
32
+ };
22
33
  /** Issue #82: Create moveToFailed handler for use inside processor */
23
34
  export declare function createMoveToFailedHandler(embedded: boolean, tcp: TcpConnection | null, internalJob: InternalJob, token: string | null | undefined, onCalled: (error: Error) => void): (id: string, error: Error, _lockToken?: string) => Promise<void>;
24
35
  /** Issue #82: Create moveToCompleted handler for use inside processor */
@@ -102,18 +102,40 @@ export function createRemoveUnprocessedChildrenHandler(embedded, tcp) {
102
102
  await tcp.send({ cmd: 'RemoveUnprocessedChildren', id });
103
103
  };
104
104
  }
105
+ /**
106
+ * Bug #74: split an Error's stack into trimmed, non-empty lines.
107
+ * `wireStack` is the bandwidth-capped copy sent to the server (50 lines); the
108
+ * authoritative cap (job.stackTraceLimit) is applied server-side in failJob.
109
+ * Shared by the natural-throw path (handleJobFailure) and the explicit
110
+ * moveToFailed() path so both persist the stack identically.
111
+ */
112
+ export function computeStackLines(err) {
113
+ const stackLines = err.stack
114
+ ? err.stack
115
+ .split('\n')
116
+ .map((l) => l.trim())
117
+ .filter(Boolean)
118
+ : [];
119
+ const wireStack = stackLines.length > 0 ? stackLines.slice(0, 50) : undefined;
120
+ return { stackLines, wireStack };
121
+ }
105
122
  /** Issue #82: Create moveToFailed handler for use inside processor */
106
123
  export function createMoveToFailedHandler(embedded, tcp, internalJob, token, onCalled) {
107
124
  return async (_id, error, _lockToken) => {
125
+ // Bug #74 follow-up: carry the stack on explicit moveToFailed() too. The
126
+ // natural-throw path already does; @arthurvanl's repro showed the manual
127
+ // path lost it. Compute before the send so the server can persist it.
128
+ const { wireStack } = computeStackLines(error);
108
129
  if (embedded) {
109
130
  const manager = getSharedManager();
110
- await manager.fail(internalJob.id, error.message, token ?? undefined);
131
+ await manager.fail(internalJob.id, error.message, token ?? undefined, undefined, wireStack);
111
132
  }
112
133
  else if (tcp) {
113
134
  await tcp.send({
114
135
  cmd: 'FAIL',
115
136
  id: internalJob.id,
116
137
  error: error.message,
138
+ ...(wireStack ? { stack: wireStack } : {}),
117
139
  ...(token ? { token } : {}),
118
140
  });
119
141
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunqueue",
3
- "version": "2.8.12",
3
+ "version": "2.8.13",
4
4
  "description": "High-performance job queue for Bun & AI agents. SQLite persistence, cron scheduling, priorities, retries, DLQ, webhooks, native MCP server. Zero external dependencies.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",