bunqueue 2.8.39 → 2.8.41
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/dist/application/backgroundTasks.js +3 -0
- package/dist/application/operations/jobManagement.js +7 -4
- package/dist/application/operations/jobStateTransitions.js +1 -0
- package/dist/application/operations/pull.d.ts +5 -26
- package/dist/application/operations/pull.js +68 -179
- package/dist/application/operations/pullStateTransition.d.ts +50 -0
- package/dist/application/operations/pullStateTransition.js +114 -0
- package/dist/application/queueManager.d.ts +4 -4
- package/dist/application/queueManager.js +26 -11
- package/dist/cli/client.js +6 -47
- package/dist/cli/commandRegistry.d.ts +27 -0
- package/dist/cli/commandRegistry.js +40 -0
- package/dist/cli/commandRouter.d.ts +3 -0
- package/dist/cli/commandRouter.js +44 -0
- package/dist/cli/commands/backup.js +1 -1
- package/dist/cli/commands/core.js +54 -2
- package/dist/cli/commands/doctor.d.ts +50 -5
- package/dist/cli/commands/doctor.js +125 -108
- package/dist/cli/commands/types.js +5 -1
- package/dist/cli/globalOptions.d.ts +16 -0
- package/dist/cli/globalOptions.js +192 -0
- package/dist/cli/help.d.ts +9 -0
- package/dist/cli/help.js +42 -21
- package/dist/cli/index.d.ts +1 -25
- package/dist/cli/index.js +84 -348
- package/dist/cli/localOutput.d.ts +22 -0
- package/dist/cli/localOutput.js +45 -0
- package/dist/client/queue/dlqOps.js +1 -3
- package/dist/client/tcp/client.js +10 -8
- package/dist/domain/queue/shard.d.ts +2 -2
- package/dist/domain/queue/shard.js +3 -3
- package/dist/domain/queue/waiterManager.d.ts +2 -2
- package/dist/domain/queue/waiterManager.js +26 -5
- package/dist/infrastructure/cloud/wsSender.js +4 -4
- package/dist/infrastructure/persistence/schema.d.ts +2 -2
- package/dist/infrastructure/persistence/schema.js +7 -2
- package/dist/infrastructure/persistence/sqlite.d.ts +5 -1
- package/dist/infrastructure/persistence/sqlite.js +10 -2
- package/dist/infrastructure/persistence/sqliteSerializer.js +1 -1
- package/dist/infrastructure/persistence/statements.d.ts +2 -1
- package/dist/infrastructure/persistence/statements.js +3 -2
- package/dist/infrastructure/server/handlers/core.js +4 -4
- package/dist/infrastructure/server/tcp.d.ts +1 -0
- package/dist/infrastructure/server/tcp.js +9 -5
- package/dist/infrastructure/server/types.d.ts +2 -0
- package/dist/shared/msgpack.d.ts +3 -0
- package/dist/shared/msgpack.js +70 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { DEFAULT_LOCK_TTL } from '../domain/types/job';
|
|
6
6
|
import { DEFAULT_STALL_CONFIG } from '../domain/types/stall';
|
|
7
|
+
import { DEFAULT_DLQ_CONFIG } from '../domain/types/dlq';
|
|
7
8
|
import { Shard } from '../domain/queue/shard';
|
|
8
9
|
import { SqliteStorage } from '../infrastructure/persistence/sqlite';
|
|
9
10
|
import { CronScheduler } from '../infrastructure/scheduler/cronScheduler';
|
|
@@ -257,21 +258,22 @@ export class QueueManager {
|
|
|
257
258
|
this.registerQueueName(queue);
|
|
258
259
|
return pushJobBatch(queue, inputs, this.contextFactory.getPushContext());
|
|
259
260
|
}
|
|
260
|
-
async pull(queue, timeoutMs = 0) {
|
|
261
|
-
return pullJob(queue, timeoutMs, this.contextFactory.getPullContext());
|
|
261
|
+
async pull(queue, timeoutMs = 0, signal) {
|
|
262
|
+
return pullJob(queue, timeoutMs, this.contextFactory.getPullContext(), signal);
|
|
262
263
|
}
|
|
263
|
-
async pullWithLock(queue, owner, timeoutMs = 0, lockTtl = DEFAULT_LOCK_TTL) {
|
|
264
|
-
const job = await pullJob(queue, timeoutMs, this.contextFactory.getPullContext());
|
|
264
|
+
async pullWithLock(queue, owner, timeoutMs = 0, lockTtl = DEFAULT_LOCK_TTL, signal) {
|
|
265
|
+
const job = await pullJob(queue, timeoutMs, this.contextFactory.getPullContext(), signal);
|
|
265
266
|
if (!job)
|
|
266
267
|
return { job: null, token: null };
|
|
267
268
|
const token = lockMgr.createLock(job.id, owner, this.contextFactory.getLockContext(), lockTtl);
|
|
268
269
|
return { job, token };
|
|
269
270
|
}
|
|
270
|
-
async pullBatch(queue, count, timeoutMs = 0) {
|
|
271
|
-
return pullJobBatch(queue, count, timeoutMs, this.contextFactory.getPullContext());
|
|
271
|
+
async pullBatch(queue, count, timeoutMs = 0, signal) {
|
|
272
|
+
return pullJobBatch(queue, count, timeoutMs, this.contextFactory.getPullContext(), signal);
|
|
272
273
|
}
|
|
273
|
-
|
|
274
|
-
|
|
274
|
+
// biome-ignore lint/complexity/useMaxParams: preserves the public API while adding cancellation
|
|
275
|
+
async pullBatchWithLock(queue, count, owner, timeoutMs = 0, lockTtl = DEFAULT_LOCK_TTL, signal) {
|
|
276
|
+
const jobs = await pullJobBatch(queue, count, timeoutMs, this.contextFactory.getPullContext(), signal);
|
|
275
277
|
const tokens = [];
|
|
276
278
|
for (const job of jobs) {
|
|
277
279
|
const token = lockMgr.createLock(job.id, owner, this.contextFactory.getLockContext(), lockTtl);
|
|
@@ -894,10 +896,16 @@ export class QueueManager {
|
|
|
894
896
|
return;
|
|
895
897
|
const state = this.shards[shardIndex(queue)].getState(queue);
|
|
896
898
|
const stallConfig = this.shards[shardIndex(queue)].getStallConfig(queue);
|
|
899
|
+
const dlqConfig = this.shards[shardIndex(queue)].getDlqConfig(queue);
|
|
897
900
|
const hasCustomStallConfig = stallConfig.enabled !== DEFAULT_STALL_CONFIG.enabled ||
|
|
898
901
|
stallConfig.stallInterval !== DEFAULT_STALL_CONFIG.stallInterval ||
|
|
899
902
|
stallConfig.maxStalls !== DEFAULT_STALL_CONFIG.maxStalls ||
|
|
900
903
|
stallConfig.gracePeriod !== DEFAULT_STALL_CONFIG.gracePeriod;
|
|
904
|
+
const hasCustomDlqConfig = dlqConfig.autoRetry !== DEFAULT_DLQ_CONFIG.autoRetry ||
|
|
905
|
+
dlqConfig.autoRetryInterval !== DEFAULT_DLQ_CONFIG.autoRetryInterval ||
|
|
906
|
+
dlqConfig.maxAutoRetries !== DEFAULT_DLQ_CONFIG.maxAutoRetries ||
|
|
907
|
+
dlqConfig.maxAge !== DEFAULT_DLQ_CONFIG.maxAge ||
|
|
908
|
+
dlqConfig.maxEntries !== DEFAULT_DLQ_CONFIG.maxEntries;
|
|
901
909
|
// When control-state returns fully to default (not paused, no limits), drop
|
|
902
910
|
// the row instead of persisting an all-default placeholder. Keeps the table
|
|
903
911
|
// free of noise rows for ephemeral queues that only ever call resume/clear*,
|
|
@@ -905,7 +913,8 @@ export class QueueManager {
|
|
|
905
913
|
if (!state.paused &&
|
|
906
914
|
state.rateLimit === null &&
|
|
907
915
|
state.concurrencyLimit === null &&
|
|
908
|
-
!hasCustomStallConfig
|
|
916
|
+
!hasCustomStallConfig &&
|
|
917
|
+
!hasCustomDlqConfig) {
|
|
909
918
|
this.storage.deleteQueueState(queue);
|
|
910
919
|
return;
|
|
911
920
|
}
|
|
@@ -916,6 +925,7 @@ export class QueueManager {
|
|
|
916
925
|
rateLimitDuration: state.rateLimitDuration,
|
|
917
926
|
rateLimitExpiresAt: state.rateLimitExpiresAt,
|
|
918
927
|
stallConfig,
|
|
928
|
+
dlqConfig,
|
|
919
929
|
});
|
|
920
930
|
}
|
|
921
931
|
/** Get rate limit and concurrency limit for a queue */
|
|
@@ -954,6 +964,7 @@ export class QueueManager {
|
|
|
954
964
|
}
|
|
955
965
|
setDlqConfig(queue, config) {
|
|
956
966
|
this.shards[shardIndex(queue)].setDlqConfig(queue, config);
|
|
967
|
+
this.persistQueueState(queue);
|
|
957
968
|
}
|
|
958
969
|
getDlqConfig(queue) {
|
|
959
970
|
return this.shards[shardIndex(queue)].getDlqConfig(queue);
|
|
@@ -1290,11 +1301,15 @@ export class QueueManager {
|
|
|
1290
1301
|
if (!childJob.parentId)
|
|
1291
1302
|
return;
|
|
1292
1303
|
if (childJob.continueParentOnFailure) {
|
|
1293
|
-
this.continueParentOnChildFailure(childJob, error).catch(() => {
|
|
1304
|
+
this.continueParentOnChildFailure(childJob, error).catch(() => {
|
|
1305
|
+
// Best-effort parent propagation; child failure is already durable.
|
|
1306
|
+
});
|
|
1294
1307
|
}
|
|
1295
1308
|
else {
|
|
1296
1309
|
// removeDependencyOnFailure or ignoreDependencyOnFailure
|
|
1297
|
-
this.removeChildFromParentDeps(childJob, error, childJob.ignoreDependencyOnFailure).catch(() => {
|
|
1310
|
+
this.removeChildFromParentDeps(childJob, error, childJob.ignoreDependencyOnFailure).catch(() => {
|
|
1311
|
+
// Best-effort parent propagation; child failure is already durable.
|
|
1312
|
+
});
|
|
1298
1313
|
}
|
|
1299
1314
|
}
|
|
1300
1315
|
/**
|
package/dist/cli/client.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* Connects to bunQ server and executes commands (msgpack binary protocol)
|
|
4
4
|
*/
|
|
5
5
|
import { formatOutput, formatError } from './output';
|
|
6
|
-
import { pack, unpack } from 'msgpackr';
|
|
7
6
|
import { FrameParser, FrameSizeError } from '../infrastructure/server/protocol';
|
|
8
7
|
import { CommandError } from './commands/types';
|
|
9
8
|
import { buildClientTls } from '../client/tcp/connection';
|
|
9
|
+
import { buildCommand } from './commandRouter';
|
|
10
|
+
import { decodeMessagePack, encodeMessagePack } from '../shared/msgpack';
|
|
10
11
|
/**
|
|
11
12
|
* Client-side timeout for a command: base 30s; ONLY the long-poll commands
|
|
12
13
|
* (PULL/WaitJob, where `timeout` means "how long the server may hold the
|
|
@@ -21,7 +22,7 @@ export function clientTimeoutFor(cmd) {
|
|
|
21
22
|
return Math.max(30000, cmdTimeout + 10000);
|
|
22
23
|
}
|
|
23
24
|
/** Send a command and wait for response */
|
|
24
|
-
|
|
25
|
+
function sendCommand(socket, command, timeoutMs = 30000) {
|
|
25
26
|
return new Promise((resolve, reject) => {
|
|
26
27
|
const timeoutId = setTimeout(() => {
|
|
27
28
|
socket.data.resolve = null;
|
|
@@ -36,11 +37,11 @@ async function sendCommand(socket, command, timeoutMs = 30000) {
|
|
|
36
37
|
clearTimeout(timeoutId);
|
|
37
38
|
reject(error);
|
|
38
39
|
};
|
|
39
|
-
socket.write(FrameParser.frame(
|
|
40
|
+
socket.write(FrameParser.frame(encodeMessagePack(command)));
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
43
|
/** Create TCP connection */
|
|
43
|
-
|
|
44
|
+
function connect(options) {
|
|
44
45
|
return new Promise((resolve, reject) => {
|
|
45
46
|
const socketData = {
|
|
46
47
|
frameParser: new FrameParser(),
|
|
@@ -73,7 +74,7 @@ async function connect(options) {
|
|
|
73
74
|
for (const frame of frames) {
|
|
74
75
|
if (socketData.resolve) {
|
|
75
76
|
try {
|
|
76
|
-
const response =
|
|
77
|
+
const response = decodeMessagePack(frame);
|
|
77
78
|
socketData.resolve(response);
|
|
78
79
|
socketData.resolve = null;
|
|
79
80
|
socketData.reject = null;
|
|
@@ -200,45 +201,3 @@ export async function executeCommand(command, args, options) {
|
|
|
200
201
|
connection?.close();
|
|
201
202
|
}
|
|
202
203
|
}
|
|
203
|
-
/** Build a command from CLI arguments */
|
|
204
|
-
async function buildCommand(command, args) {
|
|
205
|
-
// Import command builders
|
|
206
|
-
const { buildCoreCommand } = await import('./commands/core');
|
|
207
|
-
const { buildJobCommand } = await import('./commands/job');
|
|
208
|
-
const { buildQueueCommand } = await import('./commands/queue');
|
|
209
|
-
const { buildDlqCommand } = await import('./commands/dlq');
|
|
210
|
-
const { buildCronCommand } = await import('./commands/cron');
|
|
211
|
-
const { buildWorkerCommand } = await import('./commands/worker');
|
|
212
|
-
const { buildWebhookCommand } = await import('./commands/webhook');
|
|
213
|
-
const { buildRateLimitCommand } = await import('./commands/rateLimit');
|
|
214
|
-
const { buildMonitorCommand } = await import('./commands/monitor');
|
|
215
|
-
switch (command) {
|
|
216
|
-
case 'push':
|
|
217
|
-
case 'pull':
|
|
218
|
-
case 'ack':
|
|
219
|
-
case 'fail':
|
|
220
|
-
return buildCoreCommand(command, args);
|
|
221
|
-
case 'job':
|
|
222
|
-
return buildJobCommand(args);
|
|
223
|
-
case 'queue':
|
|
224
|
-
return buildQueueCommand(args);
|
|
225
|
-
case 'dlq':
|
|
226
|
-
return buildDlqCommand(args);
|
|
227
|
-
case 'cron':
|
|
228
|
-
return buildCronCommand(args);
|
|
229
|
-
case 'worker':
|
|
230
|
-
return buildWorkerCommand(args);
|
|
231
|
-
case 'webhook':
|
|
232
|
-
return buildWebhookCommand(args);
|
|
233
|
-
case 'rate-limit':
|
|
234
|
-
case 'concurrency':
|
|
235
|
-
return buildRateLimitCommand(command, args);
|
|
236
|
-
case 'stats':
|
|
237
|
-
case 'metrics':
|
|
238
|
-
case 'health':
|
|
239
|
-
case 'ping':
|
|
240
|
-
return buildMonitorCommand(command);
|
|
241
|
-
default:
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Canonical CLI command surface used by routing, help conformance, and tests. */
|
|
2
|
+
export declare const CLI_COMMAND_SURFACE: {
|
|
3
|
+
readonly push: readonly [];
|
|
4
|
+
readonly pull: readonly [];
|
|
5
|
+
readonly ack: readonly [];
|
|
6
|
+
readonly fail: readonly [];
|
|
7
|
+
readonly job: readonly ["get", "state", "result", "cancel", "progress", "update", "priority", "promote", "delay", "discard", "logs", "log", "wait"];
|
|
8
|
+
readonly queue: readonly ["list", "pause", "resume", "drain", "obliterate", "clean", "count", "jobs", "paused"];
|
|
9
|
+
readonly 'rate-limit': readonly ["set", "clear"];
|
|
10
|
+
readonly concurrency: readonly ["set", "clear"];
|
|
11
|
+
readonly dlq: readonly ["list", "retry", "purge"];
|
|
12
|
+
readonly cron: readonly ["list", "add", "delete"];
|
|
13
|
+
readonly worker: readonly ["list", "register", "unregister"];
|
|
14
|
+
readonly webhook: readonly ["list", "add", "remove"];
|
|
15
|
+
readonly stats: readonly [];
|
|
16
|
+
readonly metrics: readonly [];
|
|
17
|
+
readonly health: readonly [];
|
|
18
|
+
readonly ping: readonly [];
|
|
19
|
+
};
|
|
20
|
+
export type CliNetworkCommand = keyof typeof CLI_COMMAND_SURFACE;
|
|
21
|
+
/** Commands handled locally instead of being translated to a TCP command. */
|
|
22
|
+
export declare const CLI_LOCAL_COMMAND_SURFACE: {
|
|
23
|
+
readonly start: readonly [];
|
|
24
|
+
readonly version: readonly [];
|
|
25
|
+
readonly doctor: readonly [];
|
|
26
|
+
readonly backup: readonly ["now", "create", "list", "restore", "status"];
|
|
27
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Canonical CLI command surface used by routing, help conformance, and tests. */
|
|
2
|
+
export const CLI_COMMAND_SURFACE = {
|
|
3
|
+
push: [],
|
|
4
|
+
pull: [],
|
|
5
|
+
ack: [],
|
|
6
|
+
fail: [],
|
|
7
|
+
job: [
|
|
8
|
+
'get',
|
|
9
|
+
'state',
|
|
10
|
+
'result',
|
|
11
|
+
'cancel',
|
|
12
|
+
'progress',
|
|
13
|
+
'update',
|
|
14
|
+
'priority',
|
|
15
|
+
'promote',
|
|
16
|
+
'delay',
|
|
17
|
+
'discard',
|
|
18
|
+
'logs',
|
|
19
|
+
'log',
|
|
20
|
+
'wait',
|
|
21
|
+
],
|
|
22
|
+
queue: ['list', 'pause', 'resume', 'drain', 'obliterate', 'clean', 'count', 'jobs', 'paused'],
|
|
23
|
+
'rate-limit': ['set', 'clear'],
|
|
24
|
+
concurrency: ['set', 'clear'],
|
|
25
|
+
dlq: ['list', 'retry', 'purge'],
|
|
26
|
+
cron: ['list', 'add', 'delete'],
|
|
27
|
+
worker: ['list', 'register', 'unregister'],
|
|
28
|
+
webhook: ['list', 'add', 'remove'],
|
|
29
|
+
stats: [],
|
|
30
|
+
metrics: [],
|
|
31
|
+
health: [],
|
|
32
|
+
ping: [],
|
|
33
|
+
};
|
|
34
|
+
/** Commands handled locally instead of being translated to a TCP command. */
|
|
35
|
+
export const CLI_LOCAL_COMMAND_SURFACE = {
|
|
36
|
+
start: [],
|
|
37
|
+
version: [],
|
|
38
|
+
doctor: [],
|
|
39
|
+
backup: ['now', 'create', 'list', 'restore', 'status'],
|
|
40
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const CLI_NETWORK_COMMANDS: readonly ("push" | "queue" | "dlq" | "job" | "cron" | "worker" | "concurrency" | "ping" | "stats" | "metrics" | "pull" | "ack" | "fail" | "rate-limit" | "health" | "webhook")[];
|
|
2
|
+
/** Build the protocol command for a routed network CLI command. */
|
|
3
|
+
export declare function buildCommand(command: string, args: string[]): Promise<Record<string, unknown> | null>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CLI_COMMAND_SURFACE } from './commandRegistry';
|
|
2
|
+
export const CLI_NETWORK_COMMANDS = Object.freeze(Object.keys(CLI_COMMAND_SURFACE));
|
|
3
|
+
/** Build the protocol command for a routed network CLI command. */
|
|
4
|
+
export async function buildCommand(command, args) {
|
|
5
|
+
if (!CLI_NETWORK_COMMANDS.includes(command))
|
|
6
|
+
return null;
|
|
7
|
+
const { buildCoreCommand } = await import('./commands/core');
|
|
8
|
+
const { buildJobCommand } = await import('./commands/job');
|
|
9
|
+
const { buildQueueCommand } = await import('./commands/queue');
|
|
10
|
+
const { buildDlqCommand } = await import('./commands/dlq');
|
|
11
|
+
const { buildCronCommand } = await import('./commands/cron');
|
|
12
|
+
const { buildWorkerCommand } = await import('./commands/worker');
|
|
13
|
+
const { buildWebhookCommand } = await import('./commands/webhook');
|
|
14
|
+
const { buildRateLimitCommand } = await import('./commands/rateLimit');
|
|
15
|
+
const { buildMonitorCommand } = await import('./commands/monitor');
|
|
16
|
+
switch (command) {
|
|
17
|
+
case 'push':
|
|
18
|
+
case 'pull':
|
|
19
|
+
case 'ack':
|
|
20
|
+
case 'fail':
|
|
21
|
+
return buildCoreCommand(command, args);
|
|
22
|
+
case 'job':
|
|
23
|
+
return buildJobCommand(args);
|
|
24
|
+
case 'queue':
|
|
25
|
+
return buildQueueCommand(args);
|
|
26
|
+
case 'dlq':
|
|
27
|
+
return buildDlqCommand(args);
|
|
28
|
+
case 'cron':
|
|
29
|
+
return buildCronCommand(args);
|
|
30
|
+
case 'worker':
|
|
31
|
+
return buildWorkerCommand(args);
|
|
32
|
+
case 'webhook':
|
|
33
|
+
return buildWebhookCommand(args);
|
|
34
|
+
case 'rate-limit':
|
|
35
|
+
case 'concurrency':
|
|
36
|
+
return buildRateLimitCommand(command, args);
|
|
37
|
+
case 'stats':
|
|
38
|
+
case 'metrics':
|
|
39
|
+
case 'health':
|
|
40
|
+
case 'ping':
|
|
41
|
+
return buildMonitorCommand(command);
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
@@ -42,7 +42,7 @@ export async function executeBackupCommand(args) {
|
|
|
42
42
|
case 'status':
|
|
43
43
|
return executeBackupStatus(manager);
|
|
44
44
|
default:
|
|
45
|
-
throw new CommandError(`Unknown backup subcommand: ${subcommand}. Use: now, list, restore, status`);
|
|
45
|
+
throw new CommandError(`Unknown backup subcommand: ${subcommand}. Use: now, create, list, restore, status`);
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
async function executeBackupNow(manager) {
|
|
@@ -4,6 +4,56 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { parseArgs } from 'node:util';
|
|
6
6
|
import { CommandError, requireArg, parseJsonArg, parseNumberArg, parseBigIntArg } from './types';
|
|
7
|
+
const PUSH_VALUE_FLAGS = new Set([
|
|
8
|
+
'--priority',
|
|
9
|
+
'-P',
|
|
10
|
+
'--delay',
|
|
11
|
+
'-d',
|
|
12
|
+
'--max-attempts',
|
|
13
|
+
'--backoff',
|
|
14
|
+
'--ttl',
|
|
15
|
+
'--timeout',
|
|
16
|
+
'--unique-key',
|
|
17
|
+
'-u',
|
|
18
|
+
'--job-id',
|
|
19
|
+
'--depends-on',
|
|
20
|
+
'--tags',
|
|
21
|
+
'--group-id',
|
|
22
|
+
'-g',
|
|
23
|
+
]);
|
|
24
|
+
function protectNegativeJsonData(args) {
|
|
25
|
+
const protectedArgs = [...args];
|
|
26
|
+
const restore = new Map();
|
|
27
|
+
let positionals = 0;
|
|
28
|
+
for (let index = 0; index < args.length; index++) {
|
|
29
|
+
const arg = args[index];
|
|
30
|
+
if (arg === '--')
|
|
31
|
+
break;
|
|
32
|
+
if (PUSH_VALUE_FLAGS.has(arg)) {
|
|
33
|
+
index++;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (arg.startsWith('-')) {
|
|
37
|
+
if (positionals !== 1)
|
|
38
|
+
continue;
|
|
39
|
+
try {
|
|
40
|
+
if (typeof JSON.parse(arg) !== 'number')
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const sentinel = `bunqueue-negative-json-${index}`;
|
|
47
|
+
protectedArgs[index] = sentinel;
|
|
48
|
+
restore.set(sentinel, arg);
|
|
49
|
+
positionals++;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (!arg.startsWith('--') && !/^-[a-zA-Z]/.test(arg))
|
|
53
|
+
positionals++;
|
|
54
|
+
}
|
|
55
|
+
return { args: protectedArgs, restore };
|
|
56
|
+
}
|
|
7
57
|
/** Build a core command from CLI args */
|
|
8
58
|
export function buildCoreCommand(command, args) {
|
|
9
59
|
switch (command) {
|
|
@@ -21,8 +71,9 @@ export function buildCoreCommand(command, args) {
|
|
|
21
71
|
}
|
|
22
72
|
/** Build PUSH command */
|
|
23
73
|
function buildPush(args) {
|
|
74
|
+
const protectedInput = protectNegativeJsonData(args);
|
|
24
75
|
const { values, positionals } = parseArgs({
|
|
25
|
-
args,
|
|
76
|
+
args: protectedInput.args,
|
|
26
77
|
options: {
|
|
27
78
|
priority: { type: 'string', short: 'P' },
|
|
28
79
|
delay: { type: 'string', short: 'd' },
|
|
@@ -43,7 +94,8 @@ function buildPush(args) {
|
|
|
43
94
|
strict: false,
|
|
44
95
|
});
|
|
45
96
|
const queue = requireArg(positionals, 0, 'queue');
|
|
46
|
-
const
|
|
97
|
+
const protectedData = requireArg(positionals, 1, 'data');
|
|
98
|
+
const data = parseJsonArg(protectedInput.restore.get(protectedData) ?? protectedData, 'data');
|
|
47
99
|
const cmd = {
|
|
48
100
|
cmd: 'PUSH',
|
|
49
101
|
queue,
|
|
@@ -1,5 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export interface HealthData {
|
|
2
|
+
version?: string;
|
|
3
|
+
status?: string;
|
|
4
|
+
uptime?: number;
|
|
5
|
+
queues?: {
|
|
6
|
+
waiting?: number;
|
|
7
|
+
active?: number;
|
|
8
|
+
delayed?: number;
|
|
9
|
+
completed?: number;
|
|
10
|
+
dlq?: number;
|
|
11
|
+
};
|
|
12
|
+
connections?: {
|
|
13
|
+
tcp?: number;
|
|
14
|
+
ws?: number;
|
|
15
|
+
sse?: number;
|
|
16
|
+
};
|
|
17
|
+
memory?: {
|
|
18
|
+
heapUsed?: number;
|
|
19
|
+
heapTotal?: number;
|
|
20
|
+
rss?: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export type DoctorInput = {
|
|
24
|
+
kind: 'ok';
|
|
25
|
+
endpoint: string;
|
|
26
|
+
health: HealthData;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'http-error';
|
|
29
|
+
endpoint: string;
|
|
30
|
+
status: number;
|
|
31
|
+
} | {
|
|
32
|
+
kind: 'network-error';
|
|
33
|
+
endpoint: string;
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
export interface DoctorReport {
|
|
37
|
+
clientVersion: string;
|
|
38
|
+
endpoint: string;
|
|
39
|
+
reachable: boolean;
|
|
40
|
+
health?: HealthData;
|
|
41
|
+
error?: string;
|
|
42
|
+
issues: number;
|
|
43
|
+
fatal: boolean;
|
|
44
|
+
exitCode: 0 | 1;
|
|
45
|
+
}
|
|
46
|
+
export declare function fetchDoctorHealth(host: string, httpPort: number): Promise<DoctorInput>;
|
|
47
|
+
/** Evaluate diagnostics without performing I/O or reading clocks. */
|
|
48
|
+
export declare function evaluateDoctor(input: DoctorInput): DoctorReport;
|
|
49
|
+
export declare function formatDoctorText(report: DoctorReport): string;
|
|
50
|
+
export declare function runDoctor(host: string, httpPort: number): Promise<DoctorReport>;
|