@trigger.dev/redis-worker 0.0.0-prerelease-20250430152137 → 0.0.0-prerelease-20250702122422

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/index.cjs CHANGED
@@ -1,11 +1,21 @@
1
1
  'use strict';
2
2
 
3
+ var process2 = require('process');
4
+ var os = require('os');
5
+ var tty = require('tty');
3
6
  var logger$1 = require('@trigger.dev/core/logger');
4
7
  var crypto = require('crypto');
5
8
  require('@trigger.dev/core/v3/utils/flattenAttributes');
6
9
  var v3 = require('@trigger.dev/core/v3');
7
10
  var serverOnly = require('@trigger.dev/core/v3/serverOnly');
8
- var promClient = require('prom-client');
11
+ var zod = require('zod');
12
+ var cronParser = require('cron-parser');
13
+
14
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
+
16
+ var process2__default = /*#__PURE__*/_interopDefault(process2);
17
+ var os__default = /*#__PURE__*/_interopDefault(os);
18
+ var tty__default = /*#__PURE__*/_interopDefault(tty);
9
19
 
10
20
  var __create = Object.create;
11
21
  var __defProp = Object.defineProperty;
@@ -19,9 +29,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
19
29
  if (typeof require !== "undefined") return require.apply(this, arguments);
20
30
  throw Error('Dynamic require of "' + x + '" is not supported');
21
31
  });
32
+ var __esm = (fn, res) => function __init() {
33
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
34
+ };
22
35
  var __commonJS = (cb, mod) => function __require2() {
23
36
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
24
37
  };
38
+ var __export = (target, all) => {
39
+ for (var name in all)
40
+ __defProp(target, name, { get: all[name], enumerable: true });
41
+ };
25
42
  var __copyProps = (to, from, except, desc) => {
26
43
  if (from && typeof from === "object" || typeof from === "function") {
27
44
  for (let key of __getOwnPropNames(from))
@@ -38,6 +55,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
38
55
  __defProp(target, "default", { value: mod, enumerable: true }) ,
39
56
  mod
40
57
  ));
58
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
41
59
 
42
60
  // ../../node_modules/.pnpm/@ioredis+commands@1.2.0/node_modules/@ioredis/commands/built/commands.json
43
61
  var require_commands = __commonJS({
@@ -2513,13 +2531,13 @@ var require_built = __commonJS({
2513
2531
  return Boolean(commands_json_1.default[commandName]);
2514
2532
  }
2515
2533
  exports.exists = exists;
2516
- function hasFlag(commandName, flag) {
2534
+ function hasFlag2(commandName, flag) {
2517
2535
  if (!flags[commandName]) {
2518
2536
  throw new Error("Unknown command " + commandName);
2519
2537
  }
2520
2538
  return Boolean(flags[commandName][flag]);
2521
2539
  }
2522
- exports.hasFlag = hasFlag;
2540
+ exports.hasFlag = hasFlag2;
2523
2541
  function getKeyIndexes(commandName, args, options) {
2524
2542
  const command = commands_json_1.default[commandName];
2525
2543
  if (!command) {
@@ -3567,10 +3585,10 @@ var require_ms = __commonJS({
3567
3585
  }
3568
3586
  });
3569
3587
 
3570
- // ../../node_modules/.pnpm/debug@4.3.7/node_modules/debug/src/common.js
3588
+ // ../../node_modules/.pnpm/debug@4.3.7_supports-color@10.0.0/node_modules/debug/src/common.js
3571
3589
  var require_common = __commonJS({
3572
- "../../node_modules/.pnpm/debug@4.3.7/node_modules/debug/src/common.js"(exports, module) {
3573
- function setup(env) {
3590
+ "../../node_modules/.pnpm/debug@4.3.7_supports-color@10.0.0/node_modules/debug/src/common.js"(exports, module) {
3591
+ function setup(env2) {
3574
3592
  createDebug.debug = createDebug;
3575
3593
  createDebug.default = createDebug;
3576
3594
  createDebug.coerce = coerce;
@@ -3579,8 +3597,8 @@ var require_common = __commonJS({
3579
3597
  createDebug.enabled = enabled;
3580
3598
  createDebug.humanize = require_ms();
3581
3599
  createDebug.destroy = destroy;
3582
- Object.keys(env).forEach((key) => {
3583
- createDebug[key] = env[key];
3600
+ Object.keys(env2).forEach((key) => {
3601
+ createDebug[key] = env2[key];
3584
3602
  });
3585
3603
  createDebug.names = [];
3586
3604
  createDebug.skips = [];
@@ -3730,9 +3748,9 @@ var require_common = __commonJS({
3730
3748
  }
3731
3749
  });
3732
3750
 
3733
- // ../../node_modules/.pnpm/debug@4.3.7/node_modules/debug/src/browser.js
3751
+ // ../../node_modules/.pnpm/debug@4.3.7_supports-color@10.0.0/node_modules/debug/src/browser.js
3734
3752
  var require_browser = __commonJS({
3735
- "../../node_modules/.pnpm/debug@4.3.7/node_modules/debug/src/browser.js"(exports, module) {
3753
+ "../../node_modules/.pnpm/debug@4.3.7_supports-color@10.0.0/node_modules/debug/src/browser.js"(exports, module) {
3736
3754
  exports.formatArgs = formatArgs;
3737
3755
  exports.save = save;
3738
3756
  exports.load = load;
@@ -3900,117 +3918,150 @@ var require_browser = __commonJS({
3900
3918
  }
3901
3919
  });
3902
3920
 
3903
- // ../../node_modules/.pnpm/has-flag@3.0.0/node_modules/has-flag/index.js
3904
- var require_has_flag = __commonJS({
3905
- "../../node_modules/.pnpm/has-flag@3.0.0/node_modules/has-flag/index.js"(exports, module) {
3906
- module.exports = (flag, argv) => {
3907
- argv = argv || process.argv;
3908
- const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
3909
- const pos = argv.indexOf(prefix + flag);
3910
- const terminatorPos = argv.indexOf("--");
3911
- return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos);
3912
- };
3913
- }
3921
+ // ../../node_modules/.pnpm/supports-color@10.0.0/node_modules/supports-color/index.js
3922
+ var supports_color_exports = {};
3923
+ __export(supports_color_exports, {
3924
+ createSupportsColor: () => createSupportsColor,
3925
+ default: () => supports_color_default
3914
3926
  });
3915
-
3916
- // ../../node_modules/.pnpm/supports-color@5.5.0/node_modules/supports-color/index.js
3917
- var require_supports_color = __commonJS({
3918
- "../../node_modules/.pnpm/supports-color@5.5.0/node_modules/supports-color/index.js"(exports, module) {
3919
- var os = __require("os");
3920
- var hasFlag = require_has_flag();
3921
- var env = process.env;
3922
- var forceColor;
3923
- if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false")) {
3924
- forceColor = false;
3925
- } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
3926
- forceColor = true;
3927
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2__default.default.argv) {
3928
+ const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
3929
+ const position = argv.indexOf(prefix + flag);
3930
+ const terminatorPosition = argv.indexOf("--");
3931
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
3932
+ }
3933
+ function envForceColor() {
3934
+ if (!("FORCE_COLOR" in env)) {
3935
+ return;
3936
+ }
3937
+ if (env.FORCE_COLOR === "true") {
3938
+ return 1;
3939
+ }
3940
+ if (env.FORCE_COLOR === "false") {
3941
+ return 0;
3942
+ }
3943
+ if (env.FORCE_COLOR.length === 0) {
3944
+ return 1;
3945
+ }
3946
+ const level = Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
3947
+ if (![0, 1, 2, 3].includes(level)) {
3948
+ return;
3949
+ }
3950
+ return level;
3951
+ }
3952
+ function translateLevel(level) {
3953
+ if (level === 0) {
3954
+ return false;
3955
+ }
3956
+ return {
3957
+ level,
3958
+ hasBasic: true,
3959
+ has256: level >= 2,
3960
+ has16m: level >= 3
3961
+ };
3962
+ }
3963
+ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
3964
+ const noFlagForceColor = envForceColor();
3965
+ if (noFlagForceColor !== void 0) {
3966
+ flagForceColor = noFlagForceColor;
3967
+ }
3968
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
3969
+ if (forceColor === 0) {
3970
+ return 0;
3971
+ }
3972
+ if (sniffFlags) {
3973
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
3974
+ return 3;
3927
3975
  }
3928
- if ("FORCE_COLOR" in env) {
3929
- forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0;
3976
+ if (hasFlag("color=256")) {
3977
+ return 2;
3930
3978
  }
3931
- function translateLevel(level) {
3932
- if (level === 0) {
3933
- return false;
3934
- }
3935
- return {
3936
- level,
3937
- hasBasic: true,
3938
- has256: level >= 2,
3939
- has16m: level >= 3
3940
- };
3979
+ }
3980
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) {
3981
+ return 1;
3982
+ }
3983
+ if (haveStream && !streamIsTTY && forceColor === void 0) {
3984
+ return 0;
3985
+ }
3986
+ const min = forceColor || 0;
3987
+ if (env.TERM === "dumb") {
3988
+ return min;
3989
+ }
3990
+ if (process2__default.default.platform === "win32") {
3991
+ const osRelease = os__default.default.release().split(".");
3992
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
3993
+ return Number(osRelease[2]) >= 14931 ? 3 : 2;
3941
3994
  }
3942
- function supportsColor(stream) {
3943
- if (forceColor === false) {
3944
- return 0;
3945
- }
3946
- if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
3947
- return 3;
3948
- }
3949
- if (hasFlag("color=256")) {
3950
- return 2;
3951
- }
3952
- if (stream && !stream.isTTY && forceColor !== true) {
3953
- return 0;
3954
- }
3955
- const min = forceColor ? 1 : 0;
3956
- if (process.platform === "win32") {
3957
- const osRelease = os.release().split(".");
3958
- if (Number(process.versions.node.split(".")[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
3959
- return Number(osRelease[2]) >= 14931 ? 3 : 2;
3960
- }
3961
- return 1;
3962
- }
3963
- if ("CI" in env) {
3964
- if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
3965
- return 1;
3966
- }
3967
- return min;
3968
- }
3969
- if ("TEAMCITY_VERSION" in env) {
3970
- return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
3971
- }
3972
- if (env.COLORTERM === "truecolor") {
3973
- return 3;
3974
- }
3975
- if ("TERM_PROGRAM" in env) {
3976
- const version = parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
3977
- switch (env.TERM_PROGRAM) {
3978
- case "iTerm.app":
3979
- return version >= 3 ? 3 : 2;
3980
- case "Apple_Terminal":
3981
- return 2;
3982
- }
3995
+ return 1;
3996
+ }
3997
+ if ("CI" in env) {
3998
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) {
3999
+ return 3;
4000
+ }
4001
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
4002
+ return 1;
4003
+ }
4004
+ return min;
4005
+ }
4006
+ if ("TEAMCITY_VERSION" in env) {
4007
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
4008
+ }
4009
+ if (env.COLORTERM === "truecolor") {
4010
+ return 3;
4011
+ }
4012
+ if (env.TERM === "xterm-kitty") {
4013
+ return 3;
4014
+ }
4015
+ if ("TERM_PROGRAM" in env) {
4016
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
4017
+ switch (env.TERM_PROGRAM) {
4018
+ case "iTerm.app": {
4019
+ return version >= 3 ? 3 : 2;
3983
4020
  }
3984
- if (/-256(color)?$/i.test(env.TERM)) {
4021
+ case "Apple_Terminal": {
3985
4022
  return 2;
3986
4023
  }
3987
- if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
3988
- return 1;
3989
- }
3990
- if ("COLORTERM" in env) {
3991
- return 1;
3992
- }
3993
- if (env.TERM === "dumb") {
3994
- return min;
3995
- }
3996
- return min;
3997
4024
  }
3998
- function getSupportLevel(stream) {
3999
- const level = supportsColor(stream);
4000
- return translateLevel(level);
4025
+ }
4026
+ if (/-256(color)?$/i.test(env.TERM)) {
4027
+ return 2;
4028
+ }
4029
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
4030
+ return 1;
4031
+ }
4032
+ if ("COLORTERM" in env) {
4033
+ return 1;
4034
+ }
4035
+ return min;
4036
+ }
4037
+ function createSupportsColor(stream, options = {}) {
4038
+ const level = _supportsColor(stream, {
4039
+ streamIsTTY: stream && stream.isTTY,
4040
+ ...options
4041
+ });
4042
+ return translateLevel(level);
4043
+ }
4044
+ var env, flagForceColor, supportsColor, supports_color_default;
4045
+ var init_supports_color = __esm({
4046
+ "../../node_modules/.pnpm/supports-color@10.0.0/node_modules/supports-color/index.js"() {
4047
+ ({ env } = process2__default.default);
4048
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
4049
+ flagForceColor = 0;
4050
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
4051
+ flagForceColor = 1;
4001
4052
  }
4002
- module.exports = {
4003
- supportsColor: getSupportLevel,
4004
- stdout: getSupportLevel(process.stdout),
4005
- stderr: getSupportLevel(process.stderr)
4053
+ supportsColor = {
4054
+ stdout: createSupportsColor({ isTTY: tty__default.default.isatty(1) }),
4055
+ stderr: createSupportsColor({ isTTY: tty__default.default.isatty(2) })
4006
4056
  };
4057
+ supports_color_default = supportsColor;
4007
4058
  }
4008
4059
  });
4009
4060
 
4010
- // ../../node_modules/.pnpm/debug@4.3.7/node_modules/debug/src/node.js
4061
+ // ../../node_modules/.pnpm/debug@4.3.7_supports-color@10.0.0/node_modules/debug/src/node.js
4011
4062
  var require_node = __commonJS({
4012
- "../../node_modules/.pnpm/debug@4.3.7/node_modules/debug/src/node.js"(exports, module) {
4013
- var tty = __require("tty");
4063
+ "../../node_modules/.pnpm/debug@4.3.7_supports-color@10.0.0/node_modules/debug/src/node.js"(exports, module) {
4064
+ var tty2 = __require("tty");
4014
4065
  var util = __require("util");
4015
4066
  exports.init = init;
4016
4067
  exports.log = log;
@@ -4025,8 +4076,8 @@ var require_node = __commonJS({
4025
4076
  );
4026
4077
  exports.colors = [6, 2, 3, 4, 5, 1];
4027
4078
  try {
4028
- const supportsColor = require_supports_color();
4029
- if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
4079
+ const supportsColor2 = (init_supports_color(), __toCommonJS(supports_color_exports));
4080
+ if (supportsColor2 && (supportsColor2.stderr || supportsColor2).level >= 2) {
4030
4081
  exports.colors = [
4031
4082
  20,
4032
4083
  21,
@@ -4128,7 +4179,7 @@ var require_node = __commonJS({
4128
4179
  return obj;
4129
4180
  }, {});
4130
4181
  function useColors() {
4131
- return "colors" in exports.inspectOpts ? Boolean(exports.inspectOpts.colors) : tty.isatty(process.stderr.fd);
4182
+ return "colors" in exports.inspectOpts ? Boolean(exports.inspectOpts.colors) : tty2.isatty(process.stderr.fd);
4132
4183
  }
4133
4184
  function formatArgs(args) {
4134
4185
  const { namespace: name, useColors: useColors2 } = this;
@@ -4181,9 +4232,9 @@ var require_node = __commonJS({
4181
4232
  }
4182
4233
  });
4183
4234
 
4184
- // ../../node_modules/.pnpm/debug@4.3.7/node_modules/debug/src/index.js
4235
+ // ../../node_modules/.pnpm/debug@4.3.7_supports-color@10.0.0/node_modules/debug/src/index.js
4185
4236
  var require_src = __commonJS({
4186
- "../../node_modules/.pnpm/debug@4.3.7/node_modules/debug/src/index.js"(exports, module) {
4237
+ "../../node_modules/.pnpm/debug@4.3.7_supports-color@10.0.0/node_modules/debug/src/index.js"(exports, module) {
4187
4238
  if (typeof process === "undefined" || process.type === "renderer" || process.browser === true || process.__nwjs) {
4188
4239
  module.exports = require_browser();
4189
4240
  } else {
@@ -8693,7 +8744,7 @@ var require_Redis = __commonJS({
8693
8744
  var lodash_1 = require_lodash3();
8694
8745
  var Deque = require_denque();
8695
8746
  var debug = (0, utils_1.Debug)("redis");
8696
- var Redis3 = class _Redis extends Commander_1.default {
8747
+ var Redis4 = class _Redis extends Commander_1.default {
8697
8748
  constructor(arg1, arg2, arg3) {
8698
8749
  super();
8699
8750
  this.status = "wait";
@@ -9256,12 +9307,12 @@ var require_Redis = __commonJS({
9256
9307
  }).catch(lodash_1.noop);
9257
9308
  }
9258
9309
  };
9259
- Redis3.Cluster = cluster_1.default;
9260
- Redis3.Command = Command_1.default;
9261
- Redis3.defaultOptions = RedisOptions_1.DEFAULT_REDIS_OPTIONS;
9262
- (0, applyMixin_1.default)(Redis3, events_1.EventEmitter);
9263
- (0, transaction_1.addTransactionSupport)(Redis3.prototype);
9264
- exports.default = Redis3;
9310
+ Redis4.Cluster = cluster_1.default;
9311
+ Redis4.Command = Command_1.default;
9312
+ Redis4.defaultOptions = RedisOptions_1.DEFAULT_REDIS_OPTIONS;
9313
+ (0, applyMixin_1.default)(Redis4, events_1.EventEmitter);
9314
+ (0, transaction_1.addTransactionSupport)(Redis4.prototype);
9315
+ exports.default = Redis4;
9265
9316
  }
9266
9317
  });
9267
9318
 
@@ -9459,6 +9510,39 @@ var SimpleQueue = class {
9459
9510
  throw e;
9460
9511
  }
9461
9512
  }
9513
+ async enqueueOnce({
9514
+ id,
9515
+ job,
9516
+ item,
9517
+ attempt,
9518
+ availableAt,
9519
+ visibilityTimeoutMs
9520
+ }) {
9521
+ if (!id) {
9522
+ throw new Error("enqueueOnce requires an id");
9523
+ }
9524
+ try {
9525
+ const score = availableAt ? availableAt.getTime() : Date.now();
9526
+ const deduplicationKey = nanoid();
9527
+ const serializedItem = JSON.stringify({
9528
+ job,
9529
+ item,
9530
+ visibilityTimeoutMs,
9531
+ attempt,
9532
+ deduplicationKey
9533
+ });
9534
+ const result = await this.redis.enqueueItemOnce(`queue`, `items`, id, score, serializedItem);
9535
+ return result === 1;
9536
+ } catch (e) {
9537
+ this.logger.error(`SimpleQueue ${this.name}.enqueueOnce(): error enqueuing`, {
9538
+ queue: this.name,
9539
+ error: e,
9540
+ id,
9541
+ item
9542
+ });
9543
+ throw e;
9544
+ }
9545
+ }
9462
9546
  async dequeue(count = 1) {
9463
9547
  const now = Date.now();
9464
9548
  try {
@@ -9481,7 +9565,8 @@ var SimpleQueue = class {
9481
9565
  id,
9482
9566
  item: parsedItem,
9483
9567
  job: parsedItem.job,
9484
- timestamp
9568
+ timestamp,
9569
+ availableJobs: Object.keys(this.schema)
9485
9570
  });
9486
9571
  continue;
9487
9572
  }
@@ -9562,8 +9647,30 @@ var SimpleQueue = class {
9562
9647
  throw e;
9563
9648
  }
9564
9649
  }
9650
+ async getJob(id) {
9651
+ const result = await this.redis.getJob(`queue`, `items`, id);
9652
+ if (!result) {
9653
+ return null;
9654
+ }
9655
+ const [_, score, serializedItem] = result;
9656
+ const item = JSON.parse(serializedItem);
9657
+ return {
9658
+ id,
9659
+ job: item.job,
9660
+ item: item.item,
9661
+ visibilityTimeoutMs: item.visibilityTimeoutMs,
9662
+ attempt: item.attempt ?? 0,
9663
+ timestamp: new Date(Number(score)),
9664
+ deduplicationKey: item.deduplicationKey ?? void 0
9665
+ };
9666
+ }
9565
9667
  async moveToDeadLetterQueue(id, errorMessage) {
9566
9668
  try {
9669
+ this.logger.debug(`SimpleQueue ${this.name}.moveToDeadLetterQueue(): moving item to DLQ`, {
9670
+ queue: this.name,
9671
+ id,
9672
+ errorMessage
9673
+ });
9567
9674
  const result = await this.redis.moveToDeadLetterQueue(
9568
9675
  `queue`,
9569
9676
  `items`,
@@ -9684,6 +9791,25 @@ var SimpleQueue = class {
9684
9791
  return dequeued
9685
9792
  `
9686
9793
  });
9794
+ this.redis.defineCommand("getJob", {
9795
+ numberOfKeys: 2,
9796
+ lua: `
9797
+ local queue = KEYS[1]
9798
+ local items = KEYS[2]
9799
+ local jobId = ARGV[1]
9800
+
9801
+ local serializedItem = redis.call('HGET', items, jobId)
9802
+
9803
+ if serializedItem == false then
9804
+ return nil
9805
+ end
9806
+
9807
+ -- get the score from the queue sorted set
9808
+ local score = redis.call('ZSCORE', queue, jobId)
9809
+
9810
+ return { jobId, score, serializedItem }
9811
+ `
9812
+ });
9687
9813
  this.redis.defineCommand("ackItem", {
9688
9814
  numberOfKeys: 2,
9689
9815
  lua: `
@@ -9773,6 +9899,25 @@ var SimpleQueue = class {
9773
9899
  return 1
9774
9900
  `
9775
9901
  });
9902
+ this.redis.defineCommand("enqueueItemOnce", {
9903
+ numberOfKeys: 2,
9904
+ lua: `
9905
+ local queue = KEYS[1]
9906
+ local items = KEYS[2]
9907
+ local id = ARGV[1]
9908
+ local score = ARGV[2]
9909
+ local serializedItem = ARGV[3]
9910
+
9911
+ -- Only add if not exists
9912
+ local added = redis.call('HSETNX', items, id, serializedItem)
9913
+ if added == 1 then
9914
+ redis.call('ZADD', queue, 'NX', score, id)
9915
+ return 1
9916
+ else
9917
+ return 0
9918
+ end
9919
+ `
9920
+ });
9776
9921
  }
9777
9922
  };
9778
9923
 
@@ -10129,6 +10274,173 @@ var BaseContext = (
10129
10274
  );
10130
10275
  var ROOT_CONTEXT = new BaseContext();
10131
10276
 
10277
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/metrics/NoopMeter.js
10278
+ var __extends = /* @__PURE__ */ function() {
10279
+ var extendStatics = function(d, b) {
10280
+ extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) {
10281
+ d2.__proto__ = b2;
10282
+ } || function(d2, b2) {
10283
+ for (var p in b2) if (Object.prototype.hasOwnProperty.call(b2, p)) d2[p] = b2[p];
10284
+ };
10285
+ return extendStatics(d, b);
10286
+ };
10287
+ return function(d, b) {
10288
+ if (typeof b !== "function" && b !== null)
10289
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
10290
+ extendStatics(d, b);
10291
+ function __() {
10292
+ this.constructor = d;
10293
+ }
10294
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
10295
+ };
10296
+ }();
10297
+ var NoopMeter = (
10298
+ /** @class */
10299
+ function() {
10300
+ function NoopMeter2() {
10301
+ }
10302
+ NoopMeter2.prototype.createGauge = function(_name, _options) {
10303
+ return NOOP_GAUGE_METRIC;
10304
+ };
10305
+ NoopMeter2.prototype.createHistogram = function(_name, _options) {
10306
+ return NOOP_HISTOGRAM_METRIC;
10307
+ };
10308
+ NoopMeter2.prototype.createCounter = function(_name, _options) {
10309
+ return NOOP_COUNTER_METRIC;
10310
+ };
10311
+ NoopMeter2.prototype.createUpDownCounter = function(_name, _options) {
10312
+ return NOOP_UP_DOWN_COUNTER_METRIC;
10313
+ };
10314
+ NoopMeter2.prototype.createObservableGauge = function(_name, _options) {
10315
+ return NOOP_OBSERVABLE_GAUGE_METRIC;
10316
+ };
10317
+ NoopMeter2.prototype.createObservableCounter = function(_name, _options) {
10318
+ return NOOP_OBSERVABLE_COUNTER_METRIC;
10319
+ };
10320
+ NoopMeter2.prototype.createObservableUpDownCounter = function(_name, _options) {
10321
+ return NOOP_OBSERVABLE_UP_DOWN_COUNTER_METRIC;
10322
+ };
10323
+ NoopMeter2.prototype.addBatchObservableCallback = function(_callback, _observables) {
10324
+ };
10325
+ NoopMeter2.prototype.removeBatchObservableCallback = function(_callback) {
10326
+ };
10327
+ return NoopMeter2;
10328
+ }()
10329
+ );
10330
+ var NoopMetric = (
10331
+ /** @class */
10332
+ /* @__PURE__ */ function() {
10333
+ function NoopMetric2() {
10334
+ }
10335
+ return NoopMetric2;
10336
+ }()
10337
+ );
10338
+ var NoopCounterMetric = (
10339
+ /** @class */
10340
+ function(_super) {
10341
+ __extends(NoopCounterMetric2, _super);
10342
+ function NoopCounterMetric2() {
10343
+ return _super !== null && _super.apply(this, arguments) || this;
10344
+ }
10345
+ NoopCounterMetric2.prototype.add = function(_value, _attributes) {
10346
+ };
10347
+ return NoopCounterMetric2;
10348
+ }(NoopMetric)
10349
+ );
10350
+ var NoopUpDownCounterMetric = (
10351
+ /** @class */
10352
+ function(_super) {
10353
+ __extends(NoopUpDownCounterMetric2, _super);
10354
+ function NoopUpDownCounterMetric2() {
10355
+ return _super !== null && _super.apply(this, arguments) || this;
10356
+ }
10357
+ NoopUpDownCounterMetric2.prototype.add = function(_value, _attributes) {
10358
+ };
10359
+ return NoopUpDownCounterMetric2;
10360
+ }(NoopMetric)
10361
+ );
10362
+ var NoopGaugeMetric = (
10363
+ /** @class */
10364
+ function(_super) {
10365
+ __extends(NoopGaugeMetric2, _super);
10366
+ function NoopGaugeMetric2() {
10367
+ return _super !== null && _super.apply(this, arguments) || this;
10368
+ }
10369
+ NoopGaugeMetric2.prototype.record = function(_value, _attributes) {
10370
+ };
10371
+ return NoopGaugeMetric2;
10372
+ }(NoopMetric)
10373
+ );
10374
+ var NoopHistogramMetric = (
10375
+ /** @class */
10376
+ function(_super) {
10377
+ __extends(NoopHistogramMetric2, _super);
10378
+ function NoopHistogramMetric2() {
10379
+ return _super !== null && _super.apply(this, arguments) || this;
10380
+ }
10381
+ NoopHistogramMetric2.prototype.record = function(_value, _attributes) {
10382
+ };
10383
+ return NoopHistogramMetric2;
10384
+ }(NoopMetric)
10385
+ );
10386
+ var NoopObservableMetric = (
10387
+ /** @class */
10388
+ function() {
10389
+ function NoopObservableMetric2() {
10390
+ }
10391
+ NoopObservableMetric2.prototype.addCallback = function(_callback) {
10392
+ };
10393
+ NoopObservableMetric2.prototype.removeCallback = function(_callback) {
10394
+ };
10395
+ return NoopObservableMetric2;
10396
+ }()
10397
+ );
10398
+ var NoopObservableCounterMetric = (
10399
+ /** @class */
10400
+ function(_super) {
10401
+ __extends(NoopObservableCounterMetric2, _super);
10402
+ function NoopObservableCounterMetric2() {
10403
+ return _super !== null && _super.apply(this, arguments) || this;
10404
+ }
10405
+ return NoopObservableCounterMetric2;
10406
+ }(NoopObservableMetric)
10407
+ );
10408
+ var NoopObservableGaugeMetric = (
10409
+ /** @class */
10410
+ function(_super) {
10411
+ __extends(NoopObservableGaugeMetric2, _super);
10412
+ function NoopObservableGaugeMetric2() {
10413
+ return _super !== null && _super.apply(this, arguments) || this;
10414
+ }
10415
+ return NoopObservableGaugeMetric2;
10416
+ }(NoopObservableMetric)
10417
+ );
10418
+ var NoopObservableUpDownCounterMetric = (
10419
+ /** @class */
10420
+ function(_super) {
10421
+ __extends(NoopObservableUpDownCounterMetric2, _super);
10422
+ function NoopObservableUpDownCounterMetric2() {
10423
+ return _super !== null && _super.apply(this, arguments) || this;
10424
+ }
10425
+ return NoopObservableUpDownCounterMetric2;
10426
+ }(NoopObservableMetric)
10427
+ );
10428
+ var NOOP_METER = new NoopMeter();
10429
+ var NOOP_COUNTER_METRIC = new NoopCounterMetric();
10430
+ var NOOP_GAUGE_METRIC = new NoopGaugeMetric();
10431
+ var NOOP_HISTOGRAM_METRIC = new NoopHistogramMetric();
10432
+ var NOOP_UP_DOWN_COUNTER_METRIC = new NoopUpDownCounterMetric();
10433
+ var NOOP_OBSERVABLE_COUNTER_METRIC = new NoopObservableCounterMetric();
10434
+ var NOOP_OBSERVABLE_GAUGE_METRIC = new NoopObservableGaugeMetric();
10435
+ var NOOP_OBSERVABLE_UP_DOWN_COUNTER_METRIC = new NoopObservableUpDownCounterMetric();
10436
+
10437
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/metrics/Metric.js
10438
+ var ValueType;
10439
+ (function(ValueType2) {
10440
+ ValueType2[ValueType2["INT"] = 0] = "INT";
10441
+ ValueType2[ValueType2["DOUBLE"] = 1] = "DOUBLE";
10442
+ })(ValueType || (ValueType = {}));
10443
+
10132
10444
  // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js
10133
10445
  var __read3 = function(o, n) {
10134
10446
  var m = typeof Symbol === "function" && o[Symbol.iterator];
@@ -10491,8 +10803,54 @@ var SpanStatusCode;
10491
10803
  SpanStatusCode2[SpanStatusCode2["ERROR"] = 2] = "ERROR";
10492
10804
  })(SpanStatusCode || (SpanStatusCode = {}));
10493
10805
 
10806
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/metrics/NoopMeterProvider.js
10807
+ var NoopMeterProvider = (
10808
+ /** @class */
10809
+ function() {
10810
+ function NoopMeterProvider2() {
10811
+ }
10812
+ NoopMeterProvider2.prototype.getMeter = function(_name, _version, _options) {
10813
+ return NOOP_METER;
10814
+ };
10815
+ return NoopMeterProvider2;
10816
+ }()
10817
+ );
10818
+ var NOOP_METER_PROVIDER = new NoopMeterProvider();
10819
+
10820
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/api/metrics.js
10821
+ var API_NAME3 = "metrics";
10822
+ var MetricsAPI = (
10823
+ /** @class */
10824
+ function() {
10825
+ function MetricsAPI2() {
10826
+ }
10827
+ MetricsAPI2.getInstance = function() {
10828
+ if (!this._instance) {
10829
+ this._instance = new MetricsAPI2();
10830
+ }
10831
+ return this._instance;
10832
+ };
10833
+ MetricsAPI2.prototype.setGlobalMeterProvider = function(provider) {
10834
+ return registerGlobal(API_NAME3, provider, DiagAPI.instance());
10835
+ };
10836
+ MetricsAPI2.prototype.getMeterProvider = function() {
10837
+ return getGlobal(API_NAME3) || NOOP_METER_PROVIDER;
10838
+ };
10839
+ MetricsAPI2.prototype.getMeter = function(name, version, options) {
10840
+ return this.getMeterProvider().getMeter(name, version, options);
10841
+ };
10842
+ MetricsAPI2.prototype.disable = function() {
10843
+ unregisterGlobal(API_NAME3, DiagAPI.instance());
10844
+ };
10845
+ return MetricsAPI2;
10846
+ }()
10847
+ );
10848
+
10849
+ // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/metrics-api.js
10850
+ var metrics = MetricsAPI.getInstance();
10851
+
10494
10852
  // ../../node_modules/.pnpm/@opentelemetry+api@1.9.0/node_modules/@opentelemetry/api/build/esm/api/trace.js
10495
- var API_NAME3 = "trace";
10853
+ var API_NAME4 = "trace";
10496
10854
  var TraceAPI = (
10497
10855
  /** @class */
10498
10856
  function() {
@@ -10514,20 +10872,20 @@ var TraceAPI = (
10514
10872
  return this._instance;
10515
10873
  };
10516
10874
  TraceAPI2.prototype.setGlobalTracerProvider = function(provider) {
10517
- var success = registerGlobal(API_NAME3, this._proxyTracerProvider, DiagAPI.instance());
10875
+ var success = registerGlobal(API_NAME4, this._proxyTracerProvider, DiagAPI.instance());
10518
10876
  if (success) {
10519
10877
  this._proxyTracerProvider.setDelegate(provider);
10520
10878
  }
10521
10879
  return success;
10522
10880
  };
10523
10881
  TraceAPI2.prototype.getTracerProvider = function() {
10524
- return getGlobal(API_NAME3) || this._proxyTracerProvider;
10882
+ return getGlobal(API_NAME4) || this._proxyTracerProvider;
10525
10883
  };
10526
10884
  TraceAPI2.prototype.getTracer = function(name, version) {
10527
10885
  return this.getTracerProvider().getTracer(name, version);
10528
10886
  };
10529
10887
  TraceAPI2.prototype.disable = function() {
10530
- unregisterGlobal(API_NAME3, DiagAPI.instance());
10888
+ unregisterGlobal(API_NAME4, DiagAPI.instance());
10531
10889
  this._proxyTracerProvider = new ProxyTracerProvider();
10532
10890
  };
10533
10891
  return TraceAPI2;
@@ -10693,6 +11051,11 @@ function validateConcurrency(concurrency) {
10693
11051
  throw new TypeError("Expected `concurrency` to be a number from 1 and up");
10694
11052
  }
10695
11053
  }
11054
+ var CronSchema = zod.z.object({
11055
+ cron: zod.z.string(),
11056
+ lastTimestamp: zod.z.number().optional(),
11057
+ timestamp: zod.z.number()
11058
+ });
10696
11059
  var defaultRetrySettings = {
10697
11060
  maxAttempts: 12,
10698
11061
  factor: 2,
@@ -10707,6 +11070,7 @@ var Worker = class _Worker {
10707
11070
  this.options = options;
10708
11071
  this.logger = options.logger ?? new logger$1.Logger("Worker", "debug");
10709
11072
  this.tracer = options.tracer ?? trace.getTracer(options.name);
11073
+ this.meter = options.meter ?? metrics.getMeter(options.name);
10710
11074
  this.shutdownTimeoutMs = options.shutdownTimeoutMs ?? 6e4;
10711
11075
  const schema = Object.fromEntries(
10712
11076
  Object.entries(this.options.catalog).map(([key, value]) => [key, value.schema])
@@ -10721,56 +11085,47 @@ var Worker = class _Worker {
10721
11085
  const { workers = 1, tasksPerWorker = 1, limit = 10 } = options.concurrency ?? {};
10722
11086
  this.concurrency = { workers, tasksPerWorker, limit };
10723
11087
  this.limiter = pLimit(this.concurrency.limit);
10724
- this.metrics.register = options.metrics?.register;
10725
- if (!this.metrics.register) {
10726
- return;
10727
- }
10728
- this.metrics.enqueueDuration = new promClient.Histogram({
10729
- name: "redis_worker_enqueue_duration_seconds",
10730
- help: "The duration of enqueue operations",
10731
- labelNames: ["worker_name", "job_type", "has_available_at"],
10732
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10733
- registers: [this.metrics.register]
10734
- });
10735
- this.metrics.dequeueDuration = new promClient.Histogram({
10736
- name: "redis_worker_dequeue_duration_seconds",
10737
- help: "The duration of dequeue operations",
10738
- labelNames: ["worker_name", "worker_id", "task_count"],
10739
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10740
- registers: [this.metrics.register]
10741
- });
10742
- this.metrics.jobDuration = new promClient.Histogram({
10743
- name: "redis_worker_job_duration_seconds",
10744
- help: "The duration of job operations",
10745
- labelNames: ["worker_name", "worker_id", "batch_size", "job_type", "attempt"],
10746
- // use different buckets here as jobs can take a while to run
10747
- buckets: [0.1, 0.25, 0.5, 1, 2.5, 5, 10, 20, 30, 45, 60],
10748
- registers: [this.metrics.register]
10749
- });
10750
- this.metrics.ackDuration = new promClient.Histogram({
10751
- name: "redis_worker_ack_duration_seconds",
10752
- help: "The duration of ack operations",
10753
- labelNames: ["worker_name"],
10754
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10755
- registers: [this.metrics.register]
10756
- });
10757
- this.metrics.redriveDuration = new promClient.Histogram({
10758
- name: "redis_worker_redrive_duration_seconds",
10759
- help: "The duration of redrive operations",
10760
- labelNames: ["worker_name"],
10761
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10762
- registers: [this.metrics.register]
10763
- });
10764
- this.metrics.rescheduleDuration = new promClient.Histogram({
10765
- name: "redis_worker_reschedule_duration_seconds",
10766
- help: "The duration of reschedule operations",
10767
- labelNames: ["worker_name"],
10768
- buckets: [1e-3, 5e-3, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
10769
- registers: [this.metrics.register]
11088
+ const masterQueueObservableGauge = this.meter.createObservableGauge("redis_worker.queue.size", {
11089
+ description: "The number of items in the queue",
11090
+ unit: "items",
11091
+ valueType: ValueType.INT
10770
11092
  });
11093
+ masterQueueObservableGauge.addCallback(this.#updateQueueSizeMetric.bind(this));
11094
+ const deadLetterQueueObservableGauge = this.meter.createObservableGauge(
11095
+ "redis_worker.queue.dead_letter_size",
11096
+ {
11097
+ description: "The number of items in the dead letter queue",
11098
+ unit: "items",
11099
+ valueType: ValueType.INT
11100
+ }
11101
+ );
11102
+ deadLetterQueueObservableGauge.addCallback(this.#updateDeadLetterQueueSizeMetric.bind(this));
11103
+ const concurrencyLimitActiveObservableGauge = this.meter.createObservableGauge(
11104
+ "redis_worker.concurrency.active",
11105
+ {
11106
+ description: "The number of active workers",
11107
+ unit: "workers",
11108
+ valueType: ValueType.INT
11109
+ }
11110
+ );
11111
+ concurrencyLimitActiveObservableGauge.addCallback(
11112
+ this.#updateConcurrencyLimitActiveMetric.bind(this)
11113
+ );
11114
+ const concurrencyLimitPendingObservableGauge = this.meter.createObservableGauge(
11115
+ "redis_worker.concurrency.pending",
11116
+ {
11117
+ description: "The number of pending workers",
11118
+ unit: "workers",
11119
+ valueType: ValueType.INT
11120
+ }
11121
+ );
11122
+ concurrencyLimitPendingObservableGauge.addCallback(
11123
+ this.#updateConcurrencyLimitPendingMetric.bind(this)
11124
+ );
10771
11125
  }
10772
11126
  subscriber;
10773
11127
  tracer;
11128
+ meter;
10774
11129
  metrics = {};
10775
11130
  queue;
10776
11131
  jobs;
@@ -10781,10 +11136,37 @@ var Worker = class _Worker {
10781
11136
  shutdownTimeoutMs;
10782
11137
  // The p-limit limiter to control overall concurrency.
10783
11138
  limiter;
11139
+ async #updateQueueSizeMetric(observableResult) {
11140
+ const queueSize = await this.queue.size();
11141
+ observableResult.observe(queueSize, {
11142
+ worker_name: this.options.name
11143
+ });
11144
+ }
11145
+ async #updateDeadLetterQueueSizeMetric(observableResult) {
11146
+ const deadLetterQueueSize = await this.queue.sizeOfDeadLetterQueue();
11147
+ observableResult.observe(deadLetterQueueSize, {
11148
+ worker_name: this.options.name
11149
+ });
11150
+ }
11151
+ async #updateConcurrencyLimitActiveMetric(observableResult) {
11152
+ observableResult.observe(this.limiter.activeCount, {
11153
+ worker_name: this.options.name
11154
+ });
11155
+ }
11156
+ async #updateConcurrencyLimitPendingMetric(observableResult) {
11157
+ observableResult.observe(this.limiter.pendingCount, {
11158
+ worker_name: this.options.name
11159
+ });
11160
+ }
10784
11161
  start() {
10785
11162
  const { workers, tasksPerWorker } = this.concurrency;
11163
+ this.logger.info("Starting worker", {
11164
+ workers,
11165
+ tasksPerWorker,
11166
+ concurrency: this.concurrency
11167
+ });
10786
11168
  for (let i = 0; i < workers; i++) {
10787
- this.workerLoops.push(this.runWorkerLoop(`worker-${nanoid(12)}`, tasksPerWorker));
11169
+ this.workerLoops.push(this.runWorkerLoop(`worker-${nanoid(12)}`, tasksPerWorker, i, workers));
10788
11170
  }
10789
11171
  this.setupShutdownHandlers();
10790
11172
  this.subscriber = createRedisClient(this.options.redisOptions, {
@@ -10796,6 +11178,7 @@ var Worker = class _Worker {
10796
11178
  }
10797
11179
  });
10798
11180
  this.setupSubscriber();
11181
+ this.setupCron();
10799
11182
  return this;
10800
11183
  }
10801
11184
  /**
@@ -10848,6 +11231,56 @@ var Worker = class _Worker {
10848
11231
  }
10849
11232
  );
10850
11233
  }
11234
+ /**
11235
+ * Enqueues a job for processing once. If the job is already in the queue, it will be ignored.
11236
+ * @param options - The enqueue options.
11237
+ * @param options.id - Required unique identifier for the job.
11238
+ * @param options.job - The job type from the worker catalog.
11239
+ * @param options.payload - The job payload that matches the schema defined in the catalog.
11240
+ * @param options.visibilityTimeoutMs - Optional visibility timeout in milliseconds. Defaults to value from catalog.
11241
+ * @param options.availableAt - Optional date when the job should become available for processing. Defaults to now.
11242
+ * @returns A promise that resolves when the job is enqueued.
11243
+ */
11244
+ enqueueOnce({
11245
+ id,
11246
+ job,
11247
+ payload,
11248
+ visibilityTimeoutMs,
11249
+ availableAt
11250
+ }) {
11251
+ return startSpan(
11252
+ this.tracer,
11253
+ "enqueueOnce",
11254
+ async (span) => {
11255
+ const timeout = visibilityTimeoutMs ?? this.options.catalog[job]?.visibilityTimeoutMs;
11256
+ if (!timeout) {
11257
+ throw new Error(`No visibility timeout found for job ${String(job)} with id ${id}`);
11258
+ }
11259
+ span.setAttribute("job_visibility_timeout_ms", timeout);
11260
+ return this.withHistogram(
11261
+ this.metrics.enqueueDuration,
11262
+ this.queue.enqueueOnce({
11263
+ id,
11264
+ job,
11265
+ item: payload,
11266
+ visibilityTimeoutMs: timeout,
11267
+ availableAt
11268
+ }),
11269
+ {
11270
+ job_type: String(job),
11271
+ has_available_at: availableAt ? "true" : "false"
11272
+ }
11273
+ );
11274
+ },
11275
+ {
11276
+ kind: SpanKind.PRODUCER,
11277
+ attributes: {
11278
+ job_type: String(job),
11279
+ job_id: id
11280
+ }
11281
+ }
11282
+ );
11283
+ }
10851
11284
  /**
10852
11285
  * Reschedules an existing job to a new available date.
10853
11286
  * If the job isn't in the queue, it will be ignored.
@@ -10884,15 +11317,37 @@ var Worker = class _Worker {
10884
11317
  }
10885
11318
  );
10886
11319
  }
11320
+ async getJob(id) {
11321
+ return this.queue.getJob(id);
11322
+ }
10887
11323
  /**
10888
11324
  * The main loop that each worker runs. It repeatedly polls for items,
10889
11325
  * processes them, and then waits before the next iteration.
10890
11326
  */
10891
- async runWorkerLoop(workerId, taskCount) {
11327
+ async runWorkerLoop(workerId, taskCount, workerIndex, totalWorkers) {
10892
11328
  const pollIntervalMs = this.options.pollIntervalMs ?? 1e3;
10893
11329
  const immediatePollIntervalMs = this.options.immediatePollIntervalMs ?? 100;
11330
+ const delayBetweenWorkers = this.options.pollIntervalMs ?? 1e3;
11331
+ const delay = delayBetweenWorkers * (totalWorkers - workerIndex);
11332
+ await _Worker.delay(delay);
11333
+ this.logger.info("Starting worker loop", {
11334
+ workerIndex,
11335
+ totalWorkers,
11336
+ delay,
11337
+ workerId,
11338
+ taskCount,
11339
+ pollIntervalMs,
11340
+ immediatePollIntervalMs,
11341
+ concurrencyOptions: this.concurrency
11342
+ });
10894
11343
  while (!this.isShuttingDown) {
10895
11344
  if (this.limiter.activeCount + this.limiter.pendingCount >= this.concurrency.limit) {
11345
+ this.logger.debug("Worker at capacity, waiting", {
11346
+ workerId,
11347
+ concurrencyOptions: this.concurrency,
11348
+ activeCount: this.limiter.activeCount,
11349
+ pendingCount: this.limiter.pendingCount
11350
+ });
10896
11351
  await _Worker.delay(pollIntervalMs);
10897
11352
  continue;
10898
11353
  }
@@ -10906,9 +11361,22 @@ var Worker = class _Worker {
10906
11361
  }
10907
11362
  );
10908
11363
  if (items.length === 0) {
11364
+ this.logger.debug("No items to dequeue", {
11365
+ workerId,
11366
+ concurrencyOptions: this.concurrency,
11367
+ activeCount: this.limiter.activeCount,
11368
+ pendingCount: this.limiter.pendingCount
11369
+ });
10909
11370
  await _Worker.delay(pollIntervalMs);
10910
11371
  continue;
10911
11372
  }
11373
+ this.logger.debug("Dequeued items", {
11374
+ workerId,
11375
+ itemCount: items.length,
11376
+ concurrencyOptions: this.concurrency,
11377
+ activeCount: this.limiter.activeCount,
11378
+ pendingCount: this.limiter.pendingCount
11379
+ });
10912
11380
  for (const item of items) {
10913
11381
  this.limiter(() => this.processItem(item, items.length, workerId)).catch(
10914
11382
  (err) => {
@@ -10923,6 +11391,7 @@ var Worker = class _Worker {
10923
11391
  }
10924
11392
  await _Worker.delay(immediatePollIntervalMs);
10925
11393
  }
11394
+ this.logger.info("Worker loop finished", { workerId });
10926
11395
  }
10927
11396
  /**
10928
11397
  * Processes a single item.
@@ -10934,6 +11403,10 @@ var Worker = class _Worker {
10934
11403
  this.logger.error(`No handler found for job type: ${job}`);
10935
11404
  return;
10936
11405
  }
11406
+ if (!catalogItem) {
11407
+ this.logger.error(`No catalog item found for job type: ${job}`);
11408
+ return;
11409
+ }
10937
11410
  await startSpan(
10938
11411
  this.tracer,
10939
11412
  "processItem",
@@ -10949,6 +11422,9 @@ var Worker = class _Worker {
10949
11422
  }
10950
11423
  );
10951
11424
  await this.queue.ack(id, deduplicationKey);
11425
+ if (catalogItem.cron) {
11426
+ await this.rescheduleCronJob(job, catalogItem, item);
11427
+ }
10952
11428
  },
10953
11429
  {
10954
11430
  kind: SpanKind.CONSUMER,
@@ -10995,6 +11471,9 @@ var Worker = class _Worker {
10995
11471
  errorMessage
10996
11472
  });
10997
11473
  await this.queue.moveToDeadLetterQueue(id, errorMessage);
11474
+ if (catalogItem.cron) {
11475
+ await this.rescheduleCronJob(job, catalogItem, item);
11476
+ }
10998
11477
  return;
10999
11478
  }
11000
11479
  const retryDate = new Date(Date.now() + retryDelay);
@@ -11032,20 +11511,108 @@ var Worker = class _Worker {
11032
11511
  });
11033
11512
  }
11034
11513
  async withHistogram(histogram, promise, labels) {
11035
- if (!histogram || !this.metrics.register) {
11514
+ if (!histogram) {
11036
11515
  return promise;
11037
11516
  }
11038
- const end = histogram.startTimer({ worker_name: this.options.name, ...labels });
11517
+ const start = Date.now();
11039
11518
  try {
11040
11519
  return await promise;
11041
11520
  } finally {
11042
- end();
11521
+ const duration = (Date.now() - start) / 1e3;
11522
+ histogram.record(duration, { worker_name: this.options.name, ...labels });
11043
11523
  }
11044
11524
  }
11045
11525
  // A simple helper to delay for a given number of milliseconds.
11046
11526
  static delay(ms) {
11047
11527
  return new Promise((resolve) => setTimeout(resolve, ms));
11048
11528
  }
11529
+ setupCron() {
11530
+ const cronJobs = Object.entries(this.options.catalog).filter(([_, value]) => value.cron);
11531
+ if (cronJobs.length === 0) {
11532
+ return;
11533
+ }
11534
+ this.logger.info("Setting up cron jobs", {
11535
+ cronJobs: cronJobs.map(([job, value]) => ({
11536
+ job,
11537
+ cron: value.cron,
11538
+ jitterInMs: value.jitterInMs
11539
+ }))
11540
+ });
11541
+ const enqueuePromises = cronJobs.map(
11542
+ ([job, value]) => this.enqueueCronJob(value.cron, job, value.jitterInMs)
11543
+ );
11544
+ Promise.allSettled(enqueuePromises).then((results) => {
11545
+ results.forEach((result) => {
11546
+ if (result.status === "fulfilled") {
11547
+ this.logger.info("Enqueued cron job", { result: result.value });
11548
+ } else {
11549
+ this.logger.error("Failed to enqueue cron job", { reason: result.reason });
11550
+ }
11551
+ });
11552
+ });
11553
+ }
11554
+ async enqueueCronJob(cron, job, jitter, lastTimestamp) {
11555
+ const scheduledAt = this.calculateNextScheduledAt(cron, lastTimestamp);
11556
+ const identifier = [job, this.timestampIdentifier(scheduledAt)].join(":");
11557
+ const appliedJitter = typeof jitter === "number" ? Math.random() * jitter - jitter / 2 : 0;
11558
+ const availableAt = new Date(scheduledAt.getTime() + appliedJitter);
11559
+ const enqueued = await this.enqueueOnce({
11560
+ id: identifier,
11561
+ job,
11562
+ payload: {
11563
+ timestamp: scheduledAt.getTime(),
11564
+ lastTimestamp: lastTimestamp?.getTime(),
11565
+ cron
11566
+ },
11567
+ availableAt
11568
+ });
11569
+ this.logger.info("Enqueued cron job", {
11570
+ identifier,
11571
+ cron,
11572
+ job,
11573
+ scheduledAt,
11574
+ enqueued,
11575
+ availableAt,
11576
+ appliedJitter,
11577
+ jitter
11578
+ });
11579
+ return {
11580
+ identifier,
11581
+ cron,
11582
+ job,
11583
+ scheduledAt,
11584
+ enqueued
11585
+ };
11586
+ }
11587
+ async rescheduleCronJob(job, catalogItem, item) {
11588
+ if (!catalogItem.cron) {
11589
+ return;
11590
+ }
11591
+ return this.enqueueCronJob(
11592
+ catalogItem.cron,
11593
+ job,
11594
+ catalogItem.jitterInMs,
11595
+ new Date(item.timestamp)
11596
+ );
11597
+ }
11598
+ calculateNextScheduledAt(cron, lastTimestamp) {
11599
+ const scheduledAt = cronParser.parseExpression(cron, {
11600
+ currentDate: lastTimestamp
11601
+ }).next().toDate();
11602
+ if (scheduledAt < /* @__PURE__ */ new Date()) {
11603
+ return this.calculateNextScheduledAt(cron);
11604
+ }
11605
+ return scheduledAt;
11606
+ }
11607
+ timestampIdentifier(timestamp) {
11608
+ const year = timestamp.getUTCFullYear();
11609
+ const month = timestamp.getUTCMonth();
11610
+ const day = timestamp.getUTCDate();
11611
+ const hour = timestamp.getUTCHours();
11612
+ const minute = timestamp.getUTCMinutes();
11613
+ const second = timestamp.getUTCSeconds();
11614
+ return `${year}-${month}-${day}-${hour}-${minute}-${second}`;
11615
+ }
11049
11616
  setupSubscriber() {
11050
11617
  const channel = `${this.options.name}:redrive`;
11051
11618
  this.subscriber?.subscribe(channel, (err) => {
@@ -11102,6 +11669,7 @@ var Worker = class _Worker {
11102
11669
  }
11103
11670
  };
11104
11671
 
11672
+ exports.CronSchema = CronSchema;
11105
11673
  exports.SimpleQueue = SimpleQueue;
11106
11674
  exports.Worker = Worker;
11107
11675
  //# sourceMappingURL=index.cjs.map