@pg-boss/dashboard 1.1.0 → 1.1.2

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 (39) hide show
  1. package/README.md +25 -173
  2. package/build/client/assets/MenuTrigger-CoRd-BqL.js +1 -0
  3. package/build/client/assets/{_index-Bcg_-XSd.js → _index-BTx7NRR8.js} +1 -1
  4. package/build/client/assets/{badge-Cd8v3tl3.js → badge-DBD4CzAv.js} +1 -1
  5. package/build/client/assets/{button-BaXUPm8v.js → button-Ba8FWhqs.js} +1 -1
  6. package/build/client/assets/{chevron-down-xu6Uceu-.js → chevron-down-CYa4FYmp.js} +1 -1
  7. package/build/client/assets/chunk-UVKPFVEO-YfSfkVr_.js +26 -0
  8. package/build/client/assets/{createLucideIcon-BXGwbdrh.js → createLucideIcon-Bt-yXILq.js} +1 -1
  9. package/build/client/assets/db-link-CU7IJqbq.js +1 -0
  10. package/build/client/assets/dialog-Dkx42Rkd.js +1 -0
  11. package/build/client/assets/{entry.client-COnaNoy-.js → entry.client-DXZUROTm.js} +4 -4
  12. package/build/client/assets/{error-card-DmoxS3Ao.js → error-card-D-uZRy_l.js} +1 -1
  13. package/build/client/assets/{filter-select-mMC79WOR.js → filter-select-Tk_CBvrn.js} +1 -1
  14. package/build/client/assets/{index-DhMkYPMa.js → index-DuhoQwUK.js} +1 -1
  15. package/build/client/assets/{jobs-DtmTCs8I.js → jobs-ouYjIGAi.js} +1 -1
  16. package/build/client/assets/manifest-72f6b3e9.js +1 -0
  17. package/build/client/assets/{pagination-NfhvsUbp.js → pagination-BItl293Q.js} +1 -1
  18. package/build/client/assets/{queues._index-Cw1B49mg.js → queues._index-_ID9kaow.js} +1 -1
  19. package/build/client/assets/{queues._name-D0cG_qDX.js → queues._name-DwdbQND-.js} +1 -1
  20. package/build/client/assets/{queues._name.jobs._jobId-uJ3dfM3J.js → queues._name.jobs._jobId-DrgG3VBb.js} +1 -1
  21. package/build/client/assets/{queues.create-BGXDhJ3m.js → queues.create-CmVW5UVm.js} +1 -1
  22. package/build/client/assets/root-Bpb4WtY4.css +1 -0
  23. package/build/client/assets/{root-NWrBrGvr.js → root-DDqOT0vL.js} +1 -1
  24. package/build/client/assets/{schedules-DzgBEayh.js → schedules-B6ixYsvz.js} +1 -1
  25. package/build/client/assets/{schedules._name._key-i42S9kw2.js → schedules._name._key-CXTVKoOc.js} +1 -1
  26. package/build/client/assets/{schedules.new-Dt78KptL.js → schedules.new-Dmasri9v.js} +1 -1
  27. package/build/client/assets/{send-0eWgiWNl.js → send-CqR34Z50.js} +1 -1
  28. package/build/client/assets/{table-CTo0I5HG.js → table-Au2E5mnP.js} +1 -1
  29. package/build/client/assets/useOpenInteractionType-mgYMZAa8.js +12 -0
  30. package/build/client/assets/{warnings-BhQM6lFV.js → warnings-Dj3qacgN.js} +1 -1
  31. package/build/server/assets/server-build.js +417 -79
  32. package/package.json +18 -18
  33. package/build/client/assets/MenuTrigger-SThQHnlb.js +0 -1
  34. package/build/client/assets/chunk-JZWAC4HX-DC8i-F7r.js +0 -26
  35. package/build/client/assets/db-link-CtPnIrIr.js +0 -1
  36. package/build/client/assets/dialog-Bl8T588f.js +0 -1
  37. package/build/client/assets/manifest-25954681.js +0 -1
  38. package/build/client/assets/root-DJRlbyb5.css +0 -1
  39. package/build/client/assets/useOpenInteractionType-C_L8nZ_l.js +0 -12
@@ -1428,6 +1428,7 @@ function createTableQueue(schema) {
1428
1428
  warning_queued int NOT NULL default 0,
1429
1429
  active_count int NOT NULL default 0,
1430
1430
  total_count int NOT NULL default 0,
1431
+ heartbeat_seconds int,
1431
1432
  singletons_active text[],
1432
1433
  monitor_on timestamp with time zone,
1433
1434
  maintain_on timestamp with time zone,
@@ -1610,11 +1611,13 @@ function createTableJob(schema) {
1610
1611
  keep_until timestamp with time zone NOT NULL default now() + interval '${QUEUE_DEFAULTS.retention_seconds}',
1611
1612
  output jsonb,
1612
1613
  dead_letter text,
1613
- policy text
1614
+ policy text,
1615
+ heartbeat_on timestamp with time zone,
1616
+ heartbeat_seconds int
1614
1617
  ) PARTITION BY LIST (name)
1615
1618
  `;
1616
1619
  }
1617
- const JOB_COLUMNS_MIN = 'id, name, data, expire_seconds as "expireInSeconds", group_id as "groupId", group_tier as "groupTier"';
1620
+ const JOB_COLUMNS_MIN = 'id, name, data, expire_seconds as "expireInSeconds", heartbeat_seconds as "heartbeatSeconds", group_id as "groupId", group_tier as "groupTier"';
1618
1621
  const JOB_COLUMNS_ALL = `${JOB_COLUMNS_MIN},
1619
1622
  policy,
1620
1623
  state,
@@ -1629,6 +1632,7 @@ const JOB_COLUMNS_ALL = `${JOB_COLUMNS_MIN},
1629
1632
  singleton_key as "singletonKey",
1630
1633
  singleton_on as "singletonOn",
1631
1634
  deletion_seconds as "deleteAfterSeconds",
1635
+ heartbeat_on as "heartbeatOn",
1632
1636
  created_on as "createdOn",
1633
1637
  completed_on as "completedOn",
1634
1638
  keep_until as "keepUntil",
@@ -1682,7 +1686,8 @@ function createQueueFunction(schema) {
1682
1686
  warning_queued,
1683
1687
  dead_letter,
1684
1688
  partition,
1685
- table_name
1689
+ table_name,
1690
+ heartbeat_seconds
1686
1691
  )
1687
1692
  VALUES (
1688
1693
  queue_name,
@@ -1697,7 +1702,8 @@ function createQueueFunction(schema) {
1697
1702
  COALESCE((options->>'warningQueueSize')::int, ${QUEUE_DEFAULTS.warning_queued}),
1698
1703
  options->>'deadLetter',
1699
1704
  COALESCE((options->>'partition')::bool, ${QUEUE_DEFAULTS.partition}),
1700
- tablename
1705
+ tablename,
1706
+ (options->>'heartbeatSeconds')::int
1701
1707
  )
1702
1708
  ON CONFLICT DO NOTHING
1703
1709
  RETURNING created_on
@@ -1854,6 +1860,9 @@ function updateQueue(schema, { deadLetter } = {}) {
1854
1860
  retention_seconds = COALESCE((o.data->>'retentionSeconds')::int, retention_seconds),
1855
1861
  deletion_seconds = COALESCE((o.data->>'deleteAfterSeconds')::int, deletion_seconds),
1856
1862
  warning_queued = COALESCE((o.data->>'warningQueueSize')::int, warning_queued),
1863
+ heartbeat_seconds = CASE WHEN o.data ? 'heartbeatSeconds'
1864
+ THEN (o.data->>'heartbeatSeconds')::int
1865
+ ELSE heartbeat_seconds END,
1857
1866
  ${deadLetter === void 0 ? "" : `dead_letter = CASE WHEN '${deadLetter}' IS DISTINCT FROM dead_letter THEN '${deadLetter}' ELSE dead_letter END,`}
1858
1867
  updated_on = now()
1859
1868
  FROM options o
@@ -1875,6 +1884,7 @@ function getQueues$1(schema, names) {
1875
1884
  q.retention_seconds as "retentionSeconds",
1876
1885
  q.deletion_seconds as "deleteAfterSeconds",
1877
1886
  q.partition,
1887
+ q.heartbeat_seconds as "heartbeatSeconds",
1878
1888
  q.dead_letter as "deadLetter",
1879
1889
  q.deferred_count as "deferredCount",
1880
1890
  q.warning_queued as "warningQueueSize",
@@ -1989,10 +1999,12 @@ function insertVersion(schema, version) {
1989
1999
  return `INSERT INTO ${schema}.version(version) VALUES ('${version}')`;
1990
2000
  }
1991
2001
  function buildFetchParams(options) {
1992
- const { ignoreSingletons, ignoreGroups, groupConcurrency } = options;
2002
+ const { ignoreSingletons, ignoreGroups, groupConcurrency, minPriority, maxPriority } = options;
1993
2003
  const hasIgnoreSingletons = ignoreSingletons != null && ignoreSingletons.length > 0;
1994
2004
  const hasIgnoreGroups = ignoreGroups != null && ignoreGroups.length > 0;
1995
2005
  const hasGroupConcurrency = groupConcurrency != null;
2006
+ const hasMinPriority = minPriority != null;
2007
+ const hasMaxPriority = maxPriority != null;
1996
2008
  const groupConcurrencyConfig = hasGroupConcurrency ? typeof groupConcurrency === "number" ? { default: groupConcurrency } : groupConcurrency : null;
1997
2009
  const hasTiers = groupConcurrencyConfig?.tiers && Object.keys(groupConcurrencyConfig.tiers).length > 0;
1998
2010
  const values = [];
@@ -2001,6 +2013,8 @@ function buildFetchParams(options) {
2001
2013
  let ignoreGroupsParam = "";
2002
2014
  let defaultGroupLimitParam = "";
2003
2015
  let tiersParam = "";
2016
+ let minPriorityParam = "";
2017
+ let maxPriorityParam = "";
2004
2018
  if (hasIgnoreSingletons) {
2005
2019
  paramIndex++;
2006
2020
  ignoreSingletonsParam = `$${paramIndex}::text[]`;
@@ -2021,14 +2035,26 @@ function buildFetchParams(options) {
2021
2035
  values.push(JSON.stringify(groupConcurrencyConfig.tiers));
2022
2036
  }
2023
2037
  }
2024
- return { values, ignoreSingletonsParam, ignoreGroupsParam, defaultGroupLimitParam, tiersParam };
2038
+ if (hasMinPriority) {
2039
+ paramIndex++;
2040
+ minPriorityParam = `$${paramIndex}::int`;
2041
+ values.push(minPriority);
2042
+ }
2043
+ if (hasMaxPriority) {
2044
+ paramIndex++;
2045
+ maxPriorityParam = `$${paramIndex}::int`;
2046
+ values.push(maxPriority);
2047
+ }
2048
+ return { values, ignoreSingletonsParam, ignoreGroupsParam, defaultGroupLimitParam, tiersParam, minPriorityParam, maxPriorityParam };
2025
2049
  }
2026
2050
  function fetchNextJob(options) {
2027
- const { schema, table, name, policy, limit, includeMetadata, priority = true, orderByCreatedOn = true, ignoreStartAfter = false, groupConcurrency } = options;
2051
+ const { schema, table, name, policy, limit, includeMetadata, priority = true, orderByCreatedOn = true, ignoreStartAfter = false, groupConcurrency, minPriority, maxPriority } = options;
2028
2052
  const singletonFetch = limit > 1 && (policy === QUEUE_POLICIES.singleton || policy === QUEUE_POLICIES.stately);
2029
2053
  const hasIgnoreSingletons = options.ignoreSingletons != null && options.ignoreSingletons.length > 0;
2030
2054
  const hasIgnoreGroups = options.ignoreGroups != null && options.ignoreGroups.length > 0;
2031
2055
  const hasGroupConcurrency = groupConcurrency != null;
2056
+ const hasMinPriority = minPriority != null;
2057
+ const hasMaxPriority = maxPriority != null;
2032
2058
  const hasTiers = hasGroupConcurrency && typeof groupConcurrency === "object" && groupConcurrency.tiers && Object.keys(groupConcurrency.tiers).length > 0;
2033
2059
  const params = buildFetchParams(options);
2034
2060
  const whereConditions = [
@@ -2036,7 +2062,9 @@ function fetchNextJob(options) {
2036
2062
  `state < '${JOB_STATES.active}'`,
2037
2063
  !ignoreStartAfter ? "start_after < now()" : "",
2038
2064
  hasIgnoreSingletons ? `singleton_key <> ALL(${params.ignoreSingletonsParam})` : "",
2039
- hasIgnoreGroups ? `(group_id IS NULL OR group_id <> ALL(${params.ignoreGroupsParam}))` : ""
2065
+ hasIgnoreGroups ? `(group_id IS NULL OR group_id <> ALL(${params.ignoreGroupsParam}))` : "",
2066
+ hasMinPriority ? `priority >= ${params.minPriorityParam}` : "",
2067
+ hasMaxPriority ? `priority <= ${params.maxPriorityParam}` : ""
2040
2068
  ].filter(Boolean).join(" AND ");
2041
2069
  const selectCols = [
2042
2070
  "id",
@@ -2091,6 +2119,7 @@ function fetchNextJob(options) {
2091
2119
  UPDATE ${schema}.${table} j SET
2092
2120
  state = '${JOB_STATES.active}',
2093
2121
  started_on = now(),
2122
+ heartbeat_on = now(),
2094
2123
  retry_count = CASE WHEN started_on IS NOT NULL THEN retry_count + 1 ELSE retry_count END
2095
2124
  FROM ${finalCte}
2096
2125
  WHERE name = '${name}' AND j.id = ${finalCte}.id
@@ -2172,7 +2201,8 @@ function insertJobs(schema, { table, name, returnId = true }) {
2172
2201
  retry_backoff,
2173
2202
  retry_delay_max,
2174
2203
  policy,
2175
- dead_letter
2204
+ dead_letter,
2205
+ heartbeat_seconds
2176
2206
  )
2177
2207
  SELECT
2178
2208
  COALESCE(id, gen_random_uuid()) as id,
@@ -2195,7 +2225,8 @@ function insertJobs(schema, { table, name, returnId = true }) {
2195
2225
  COALESCE("retryBackoff", q.retry_backoff, false) as retry_backoff,
2196
2226
  COALESCE("retryDelayMax", q.retry_delay_max) as retry_delay_max,
2197
2227
  q.policy,
2198
- COALESCE("deadLetter", q.dead_letter) as dead_letter
2228
+ COALESCE("deadLetter", q.dead_letter) as dead_letter,
2229
+ COALESCE("heartbeatSeconds", q.heartbeat_seconds) as heartbeat_seconds
2199
2230
  FROM (
2200
2231
  SELECT *,
2201
2232
  CASE
@@ -2219,7 +2250,8 @@ function insertJobs(schema, { table, name, returnId = true }) {
2219
2250
  "expireInSeconds" integer,
2220
2251
  "deleteAfterSeconds" integer,
2221
2252
  "retentionSeconds" integer,
2222
- "deadLetter" text
2253
+ "deadLetter" text,
2254
+ "heartbeatSeconds" integer
2223
2255
  )
2224
2256
  ) j
2225
2257
  JOIN ${schema}.queue q ON q.name = '${name}'
@@ -2240,6 +2272,27 @@ function failJobsByTimeout(schema, table, queues) {
2240
2272
  const output = `'{ "value": { "message": "job timed out" } }'::jsonb`;
2241
2273
  return locked(schema, failJobs(schema, table, where, output), table + "failJobsByTimeout");
2242
2274
  }
2275
+ function failJobsByHeartbeat(schema, table, queues) {
2276
+ const where = `state = '${JOB_STATES.active}'
2277
+ AND heartbeat_seconds IS NOT NULL
2278
+ AND (heartbeat_on + heartbeat_seconds * interval '1s') < now()
2279
+ AND name = ANY(${serializeArrayParam(queues)})`;
2280
+ const output = `'{ "value": { "message": "job heartbeat timeout" } }'::jsonb`;
2281
+ return locked(schema, failJobs(schema, table, where, output), table + "failJobsByHeartbeat");
2282
+ }
2283
+ function touchJobs(schema, table) {
2284
+ return `
2285
+ WITH results AS (
2286
+ UPDATE ${schema}.${table}
2287
+ SET heartbeat_on = now()
2288
+ WHERE name = $1
2289
+ AND id IN (SELECT UNNEST($2::uuid[]))
2290
+ AND state = '${JOB_STATES.active}'
2291
+ RETURNING 1
2292
+ )
2293
+ SELECT COUNT(*) FROM results
2294
+ `;
2295
+ }
2243
2296
  function failJobs(schema, table, where, output) {
2244
2297
  return `
2245
2298
  WITH deleted_jobs AS (
@@ -2272,7 +2325,9 @@ function failJobs(schema, table, where, output) {
2272
2325
  keep_until,
2273
2326
  policy,
2274
2327
  output,
2275
- dead_letter
2328
+ dead_letter,
2329
+ heartbeat_on,
2330
+ heartbeat_seconds
2276
2331
  )
2277
2332
  SELECT
2278
2333
  id,
@@ -2310,7 +2365,9 @@ function failJobs(schema, table, where, output) {
2310
2365
  keep_until,
2311
2366
  policy,
2312
2367
  ${output},
2313
- dead_letter
2368
+ dead_letter,
2369
+ NULL as heartbeat_on,
2370
+ heartbeat_seconds
2314
2371
  FROM deleted_jobs
2315
2372
  ON CONFLICT DO NOTHING
2316
2373
  RETURNING *
@@ -2340,7 +2397,9 @@ function failJobs(schema, table, where, output) {
2340
2397
  keep_until,
2341
2398
  policy,
2342
2399
  output,
2343
- dead_letter
2400
+ dead_letter,
2401
+ heartbeat_on,
2402
+ heartbeat_seconds
2344
2403
  )
2345
2404
  SELECT
2346
2405
  id,
@@ -2366,7 +2425,9 @@ function failJobs(schema, table, where, output) {
2366
2425
  keep_until,
2367
2426
  policy,
2368
2427
  ${output},
2369
- dead_letter
2428
+ dead_letter,
2429
+ NULL as heartbeat_on,
2430
+ heartbeat_seconds
2370
2431
  FROM deleted_jobs
2371
2432
  WHERE id NOT IN (SELECT id from retried_jobs)
2372
2433
  RETURNING *
@@ -2579,7 +2640,7 @@ const POLICY = {
2579
2640
  MAX_RETENTION_DAYS: 365
2580
2641
  };
2581
2642
  function assertObjectName(value, name = "Name") {
2582
- assert(/^[\w.-]+$/.test(value), `${name} can only contain alphanumeric characters, underscores, hyphens, or periods`);
2643
+ assert(/^[\w.\-/]+$/.test(value), `${name} can only contain alphanumeric characters, underscores, hyphens, periods, or forward slashes`);
2583
2644
  }
2584
2645
  function validateQueueArgs(config = {}) {
2585
2646
  assert(!("deadLetter" in config) || config.deadLetter === null || typeof config.deadLetter === "string", "deadLetter must be a string");
@@ -2590,6 +2651,7 @@ function validateQueueArgs(config = {}) {
2590
2651
  validateExpirationConfig(config);
2591
2652
  validateRetentionConfig(config);
2592
2653
  validateDeletionConfig(config);
2654
+ validateHeartbeatConfig(config);
2593
2655
  }
2594
2656
  function checkSendArgs(args) {
2595
2657
  let name, data, options;
@@ -2618,6 +2680,7 @@ function checkSendArgs(args) {
2618
2680
  validateRetentionConfig(options);
2619
2681
  validateDeletionConfig(options);
2620
2682
  validateGroupConfig(options);
2683
+ validateHeartbeatConfig(options);
2621
2684
  return { name, data, options };
2622
2685
  }
2623
2686
  function validateGroupConfig(config) {
@@ -2643,6 +2706,17 @@ function validateGroupConcurrencyValue(value, optionName) {
2643
2706
  }
2644
2707
  }
2645
2708
  }
2709
+ function validatePriorityRangeConfig(config) {
2710
+ if (config.minPriority !== void 0) {
2711
+ assert(Number.isInteger(config.minPriority), "minPriority must be an integer");
2712
+ }
2713
+ if (config.maxPriority !== void 0) {
2714
+ assert(Number.isInteger(config.maxPriority), "maxPriority must be an integer");
2715
+ }
2716
+ if (config.minPriority !== void 0 && config.maxPriority !== void 0) {
2717
+ assert(config.minPriority <= config.maxPriority, "minPriority must be <= maxPriority");
2718
+ }
2719
+ }
2646
2720
  function validateGroupConcurrencyConfig(config) {
2647
2721
  const hasGlobal = config.groupConcurrency != null;
2648
2722
  const hasLocal = config.localGroupConcurrency != null;
@@ -2696,7 +2770,9 @@ function checkWorkArgs(name, args) {
2696
2770
  assert(!("includeMetadata" in options) || typeof options.includeMetadata === "boolean", "includeMetadata must be a boolean");
2697
2771
  assert(!("priority" in options) || typeof options.priority === "boolean", "priority must be a boolean");
2698
2772
  assert(!("localConcurrency" in options) || Number.isInteger(options.localConcurrency) && options.localConcurrency >= 1, "localConcurrency must be an integer >= 1");
2773
+ validatePriorityRangeConfig(options);
2699
2774
  validateGroupConcurrencyConfig(options);
2775
+ validateHeartbeatRefreshConfig(options);
2700
2776
  return { options, callback };
2701
2777
  }
2702
2778
  function checkFetchArgs(name, options) {
@@ -2705,6 +2781,7 @@ function checkFetchArgs(name, options) {
2705
2781
  assert(!("includeMetadata" in options) || typeof options.includeMetadata === "boolean", "includeMetadata must be a boolean");
2706
2782
  assert(!("priority" in options) || typeof options.priority === "boolean", "priority must be a boolean");
2707
2783
  assert(!("ignoreStartAfter" in options) || typeof options.ignoreStartAfter === "boolean", "ignoreStartAfter must be a boolean");
2784
+ validatePriorityRangeConfig(options);
2708
2785
  }
2709
2786
  function getConfig(value) {
2710
2787
  assert(
@@ -2783,6 +2860,19 @@ function validateRetryConfig(config) {
2783
2860
  assert(!("retryDelayMax" in config) || config.retryDelayMax === null || config.retryBackoff === true, "retryDelayMax can only be set if retryBackoff is true");
2784
2861
  assert(!("retryDelayMax" in config) || config.retryDelayMax === null || Number.isInteger(config.retryDelayMax) && config.retryDelayMax >= 0, "retryDelayMax must be an integer >= 0");
2785
2862
  }
2863
+ function validateHeartbeatConfig(config) {
2864
+ assert(
2865
+ !("heartbeatSeconds" in config) || config.heartbeatSeconds === null || Number.isInteger(config.heartbeatSeconds) && config.heartbeatSeconds >= 10,
2866
+ "heartbeatSeconds must be an integer >= 10"
2867
+ );
2868
+ }
2869
+ function validateHeartbeatRefreshConfig(config) {
2870
+ if (!("heartbeatRefreshSeconds" in config) || config.heartbeatRefreshSeconds == null) return;
2871
+ assert(
2872
+ typeof config.heartbeatRefreshSeconds === "number" && config.heartbeatRefreshSeconds > 0,
2873
+ "heartbeatRefreshSeconds must be a number > 0"
2874
+ );
2875
+ }
2786
2876
  function applyPollingInterval(config) {
2787
2877
  assert(
2788
2878
  !("pollingIntervalSeconds" in config) || config.pollingIntervalSeconds >= POLICY.MIN_POLLING_INTERVAL_MS / 1e3,
@@ -3478,10 +3568,190 @@ function getAll(schema) {
3478
3568
  `DROP INDEX ${schema}.warning_i1`,
3479
3569
  `DROP TABLE ${schema}.warning`
3480
3570
  ]
3571
+ },
3572
+ {
3573
+ release: "12.12.0",
3574
+ version: 30,
3575
+ previous: 29,
3576
+ install: [
3577
+ `ALTER TABLE ${schema}.job ADD COLUMN heartbeat_on timestamp with time zone`,
3578
+ `ALTER TABLE ${schema}.job ADD COLUMN heartbeat_seconds int`,
3579
+ `ALTER TABLE ${schema}.queue ADD COLUMN heartbeat_seconds int`,
3580
+ `
3581
+ CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
3582
+ RETURNS VOID AS
3583
+ $$
3584
+ DECLARE
3585
+ tablename varchar := CASE WHEN options->>'partition' = 'true'
3586
+ THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
3587
+ ELSE 'job_common'
3588
+ END;
3589
+ queue_created_on timestamptz;
3590
+ BEGIN
3591
+
3592
+ WITH q as (
3593
+ INSERT INTO ${schema}.queue (
3594
+ name,
3595
+ policy,
3596
+ retry_limit,
3597
+ retry_delay,
3598
+ retry_backoff,
3599
+ retry_delay_max,
3600
+ expire_seconds,
3601
+ retention_seconds,
3602
+ deletion_seconds,
3603
+ warning_queued,
3604
+ dead_letter,
3605
+ partition,
3606
+ table_name,
3607
+ heartbeat_seconds
3608
+ )
3609
+ VALUES (
3610
+ queue_name,
3611
+ options->>'policy',
3612
+ COALESCE((options->>'retryLimit')::int, 2),
3613
+ COALESCE((options->>'retryDelay')::int, 0),
3614
+ COALESCE((options->>'retryBackoff')::bool, false),
3615
+ (options->>'retryDelayMax')::int,
3616
+ COALESCE((options->>'expireInSeconds')::int, 900),
3617
+ COALESCE((options->>'retentionSeconds')::int, 1209600),
3618
+ COALESCE((options->>'deleteAfterSeconds')::int, 604800),
3619
+ COALESCE((options->>'warningQueueSize')::int, 0),
3620
+ options->>'deadLetter',
3621
+ COALESCE((options->>'partition')::bool, false),
3622
+ tablename,
3623
+ (options->>'heartbeatSeconds')::int
3624
+ )
3625
+ ON CONFLICT DO NOTHING
3626
+ RETURNING created_on
3627
+ )
3628
+ SELECT created_on into queue_created_on from q;
3629
+
3630
+ IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
3631
+ RETURN;
3632
+ END IF;
3633
+
3634
+ EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
3635
+
3636
+ EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
3637
+ EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, tablename);
3638
+ EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, tablename);
3639
+
3640
+ EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
3641
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i4 ON ${schema}.job (name, singleton_on, COALESCE(singleton_key, '')) WHERE state <> 'cancelled' AND singleton_on IS NOT NULL$cmd$, tablename);
3642
+ EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i7 ON ${schema}.job (name, group_id) WHERE state = 'active' AND group_id IS NOT NULL$cmd$, tablename);
3643
+
3644
+ IF options->>'policy' = 'short' THEN
3645
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i1 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state = 'created' AND policy = 'short'$cmd$, tablename);
3646
+ ELSIF options->>'policy' = 'singleton' THEN
3647
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i2 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state = 'active' AND policy = 'singleton'$cmd$, tablename);
3648
+ ELSIF options->>'policy' = 'stately' THEN
3649
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i3 ON ${schema}.job (name, state, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'stately'$cmd$, tablename);
3650
+ ELSIF options->>'policy' = 'exclusive' THEN
3651
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i6 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'exclusive'$cmd$, tablename);
3652
+ ELSIF options->>'policy' = 'key_strict_fifo' THEN
3653
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i8 ON ${schema}.job (name, singleton_key) WHERE state IN ('active', 'retry', 'failed') AND policy = 'key_strict_fifo'$cmd$, tablename);
3654
+ EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT job_key_strict_fifo_singleton_key_check CHECK (NOT (policy = 'key_strict_fifo' AND singleton_key IS NULL))$cmd$, tablename);
3655
+ END IF;
3656
+
3657
+ EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
3658
+ EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
3659
+ END;
3660
+ $$
3661
+ LANGUAGE plpgsql;
3662
+ `
3663
+ ],
3664
+ uninstall: [
3665
+ // Restore previous version of create_queue function (without heartbeat_seconds)
3666
+ `
3667
+ CREATE OR REPLACE FUNCTION ${schema}.create_queue(queue_name text, options jsonb)
3668
+ RETURNS VOID AS
3669
+ $$
3670
+ DECLARE
3671
+ tablename varchar := CASE WHEN options->>'partition' = 'true'
3672
+ THEN 'j' || encode(sha224(queue_name::bytea), 'hex')
3673
+ ELSE 'job_common'
3674
+ END;
3675
+ queue_created_on timestamptz;
3676
+ BEGIN
3677
+
3678
+ WITH q as (
3679
+ INSERT INTO ${schema}.queue (
3680
+ name,
3681
+ policy,
3682
+ retry_limit,
3683
+ retry_delay,
3684
+ retry_backoff,
3685
+ retry_delay_max,
3686
+ expire_seconds,
3687
+ retention_seconds,
3688
+ deletion_seconds,
3689
+ warning_queued,
3690
+ dead_letter,
3691
+ partition,
3692
+ table_name
3693
+ )
3694
+ VALUES (
3695
+ queue_name,
3696
+ options->>'policy',
3697
+ COALESCE((options->>'retryLimit')::int, 2),
3698
+ COALESCE((options->>'retryDelay')::int, 0),
3699
+ COALESCE((options->>'retryBackoff')::bool, false),
3700
+ (options->>'retryDelayMax')::int,
3701
+ COALESCE((options->>'expireInSeconds')::int, 900),
3702
+ COALESCE((options->>'retentionSeconds')::int, 1209600),
3703
+ COALESCE((options->>'deleteAfterSeconds')::int, 604800),
3704
+ COALESCE((options->>'warningQueueSize')::int, 0),
3705
+ options->>'deadLetter',
3706
+ COALESCE((options->>'partition')::bool, false),
3707
+ tablename
3708
+ )
3709
+ ON CONFLICT DO NOTHING
3710
+ RETURNING created_on
3711
+ )
3712
+ SELECT created_on into queue_created_on from q;
3713
+
3714
+ IF queue_created_on IS NULL OR options->>'partition' IS DISTINCT FROM 'true' THEN
3715
+ RETURN;
3716
+ END IF;
3717
+
3718
+ EXECUTE format('CREATE TABLE ${schema}.%I (LIKE ${schema}.job INCLUDING DEFAULTS)', tablename);
3719
+
3720
+ EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD PRIMARY KEY (name, id)$cmd$, tablename);
3721
+ EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT q_fkey FOREIGN KEY (name) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, tablename);
3722
+ EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT dlq_fkey FOREIGN KEY (dead_letter) REFERENCES ${schema}.queue (name) ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED$cmd$, tablename);
3723
+
3724
+ EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i5 ON ${schema}.job (name, start_after) INCLUDE (priority, created_on, id) WHERE state < 'active'$cmd$, tablename);
3725
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i4 ON ${schema}.job (name, singleton_on, COALESCE(singleton_key, '')) WHERE state <> 'cancelled' AND singleton_on IS NOT NULL$cmd$, tablename);
3726
+ EXECUTE ${schema}.job_table_format($cmd$CREATE INDEX job_i7 ON ${schema}.job (name, group_id) WHERE state = 'active' AND group_id IS NOT NULL$cmd$, tablename);
3727
+
3728
+ IF options->>'policy' = 'short' THEN
3729
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i1 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state = 'created' AND policy = 'short'$cmd$, tablename);
3730
+ ELSIF options->>'policy' = 'singleton' THEN
3731
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i2 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state = 'active' AND policy = 'singleton'$cmd$, tablename);
3732
+ ELSIF options->>'policy' = 'stately' THEN
3733
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i3 ON ${schema}.job (name, state, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'stately'$cmd$, tablename);
3734
+ ELSIF options->>'policy' = 'exclusive' THEN
3735
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i6 ON ${schema}.job (name, COALESCE(singleton_key, '')) WHERE state <= 'active' AND policy = 'exclusive'$cmd$, tablename);
3736
+ ELSIF options->>'policy' = 'key_strict_fifo' THEN
3737
+ EXECUTE ${schema}.job_table_format($cmd$CREATE UNIQUE INDEX job_i8 ON ${schema}.job (name, singleton_key) WHERE state IN ('active', 'retry', 'failed') AND policy = 'key_strict_fifo'$cmd$, tablename);
3738
+ EXECUTE ${schema}.job_table_format($cmd$ALTER TABLE ${schema}.job ADD CONSTRAINT job_key_strict_fifo_singleton_key_check CHECK (NOT (policy = 'key_strict_fifo' AND singleton_key IS NULL))$cmd$, tablename);
3739
+ END IF;
3740
+
3741
+ EXECUTE format('ALTER TABLE ${schema}.%I ADD CONSTRAINT cjc CHECK (name=%L)', tablename, queue_name);
3742
+ EXECUTE format('ALTER TABLE ${schema}.job ATTACH PARTITION ${schema}.%I FOR VALUES IN (%L)', tablename, queue_name);
3743
+ END;
3744
+ $$
3745
+ LANGUAGE plpgsql;
3746
+ `,
3747
+ `ALTER TABLE ${schema}.queue DROP COLUMN heartbeat_seconds`,
3748
+ `ALTER TABLE ${schema}.job DROP COLUMN heartbeat_seconds`,
3749
+ `ALTER TABLE ${schema}.job DROP COLUMN heartbeat_on`
3750
+ ]
3481
3751
  }
3482
3752
  ];
3483
3753
  }
3484
- const pgboss = { "schema": 29 };
3754
+ const pgboss = { "schema": 30 };
3485
3755
  const packageJson = {
3486
3756
  pgboss
3487
3757
  };
@@ -3558,6 +3828,41 @@ class Contractor {
3558
3828
  await this.db.executeSql(commands);
3559
3829
  }
3560
3830
  }
3831
+ function unwrapSQLResult(result) {
3832
+ if (Array.isArray(result)) {
3833
+ return { rows: result.flatMap((i) => i.rows) };
3834
+ }
3835
+ return result;
3836
+ }
3837
+ function delay(ms, error, abortController) {
3838
+ const ac = abortController || new AbortController();
3839
+ const promise = new Promise((resolve, reject) => {
3840
+ setTimeout$1(ms, null, { signal: ac.signal }).then(() => {
3841
+ if (error) {
3842
+ reject(new Error(error));
3843
+ } else {
3844
+ resolve();
3845
+ }
3846
+ }).catch(resolve);
3847
+ });
3848
+ promise.abort = () => {
3849
+ if (!ac.signal.aborted) {
3850
+ ac.abort();
3851
+ }
3852
+ };
3853
+ return promise;
3854
+ }
3855
+ async function resolveWithinSeconds(promise, seconds, message, abortController) {
3856
+ const timeout = Math.max(1, seconds) * 1e3;
3857
+ const reject = delay(timeout, message, abortController);
3858
+ let result;
3859
+ try {
3860
+ result = await Promise.race([promise, reject]);
3861
+ } finally {
3862
+ reject.abort();
3863
+ }
3864
+ return result;
3865
+ }
3561
3866
  async function emitAndPersistWarning(ctx, type, message, data) {
3562
3867
  ctx.emitter.emit(ctx.warningEvent, { message, data });
3563
3868
  if (ctx.persistWarnings) {
@@ -3593,6 +3898,7 @@ class Timekeeper extends EventEmitter {
3593
3898
  cronMonitorInterval;
3594
3899
  skewMonitorInterval;
3595
3900
  timekeeping;
3901
+ _checkingSkew = false;
3596
3902
  clockSkew = 0;
3597
3903
  events = EVENTS;
3598
3904
  constructor(db, manager, config) {
@@ -3601,6 +3907,9 @@ class Timekeeper extends EventEmitter {
3601
3907
  this.config = config;
3602
3908
  this.manager = manager;
3603
3909
  }
3910
+ get checkingSkew() {
3911
+ return this._checkingSkew;
3912
+ }
3604
3913
  get warningContext() {
3605
3914
  return {
3606
3915
  emitter: this,
@@ -3638,13 +3947,20 @@ class Timekeeper extends EventEmitter {
3638
3947
  clearInterval(this.cronMonitorInterval);
3639
3948
  this.cronMonitorInterval = null;
3640
3949
  }
3950
+ while (this.timekeeping || this._checkingSkew) {
3951
+ await delay(10);
3952
+ }
3641
3953
  }
3642
3954
  async cacheClockSkew() {
3643
3955
  let skew = 0;
3956
+ this._checkingSkew = true;
3644
3957
  try {
3645
3958
  if (this.config.__test__force_clock_monitoring_error) {
3646
3959
  throw new Error(this.config.__test__force_clock_monitoring_error);
3647
3960
  }
3961
+ if (this.config.__test__delay_clock_skew_ms) {
3962
+ await delay(this.config.__test__delay_clock_skew_ms);
3963
+ }
3648
3964
  const { rows } = await this.db.executeSql(getTime());
3649
3965
  const local = Date.now();
3650
3966
  const dbTime = parseFloat(rows[0].time);
@@ -3662,6 +3978,7 @@ class Timekeeper extends EventEmitter {
3662
3978
  this.emit(this.events.error, err);
3663
3979
  } finally {
3664
3980
  this.clockSkew = skew;
3981
+ this._checkingSkew = false;
3665
3982
  }
3666
3983
  }
3667
3984
  async onCron() {
@@ -3731,41 +4048,6 @@ class Timekeeper extends EventEmitter {
3731
4048
  await this.db.executeSql(sql, [name, key]);
3732
4049
  }
3733
4050
  }
3734
- function unwrapSQLResult(result) {
3735
- if (Array.isArray(result)) {
3736
- return { rows: result.flatMap((i) => i.rows) };
3737
- }
3738
- return result;
3739
- }
3740
- function delay(ms, error, abortController) {
3741
- const ac = abortController || new AbortController();
3742
- const promise = new Promise((resolve, reject) => {
3743
- setTimeout$1(ms, null, { signal: ac.signal }).then(() => {
3744
- if (error) {
3745
- reject(new Error(error));
3746
- } else {
3747
- resolve();
3748
- }
3749
- }).catch(resolve);
3750
- });
3751
- promise.abort = () => {
3752
- if (!ac.signal.aborted) {
3753
- ac.abort();
3754
- }
3755
- };
3756
- return promise;
3757
- }
3758
- async function resolveWithinSeconds(promise, seconds, message, abortController) {
3759
- const timeout = Math.max(1, seconds) * 1e3;
3760
- const reject = delay(timeout, message, abortController);
3761
- let result;
3762
- try {
3763
- result = await Promise.race([promise, reject]);
3764
- } finally {
3765
- reject.abort();
3766
- }
3767
- return result;
3768
- }
3769
4051
  const WORKER_STATES = {
3770
4052
  created: "created",
3771
4053
  active: "active",
@@ -4098,9 +4380,10 @@ class Manager extends EventEmitter {
4098
4380
  }
4099
4381
  }
4100
4382
  }
4101
- async #processJobs(name, jobs2, callback, worker) {
4383
+ async #processJobs(name, jobs2, callback, worker, heartbeatRefreshSeconds) {
4102
4384
  const jobIds = jobs2.map((job) => job.id);
4103
4385
  const maxExpiration = jobs2.reduce((acc, i) => Math.max(acc, i.expireInSeconds), 0);
4386
+ const heartbeatSeconds = jobs2.reduce((acc, j) => Math.max(acc, j.heartbeatSeconds || 0), 0);
4104
4387
  const ac = new AbortController();
4105
4388
  jobs2.forEach((job) => {
4106
4389
  job.signal = ac.signal;
@@ -4108,6 +4391,18 @@ class Manager extends EventEmitter {
4108
4391
  if (worker) {
4109
4392
  worker.abortController = ac;
4110
4393
  }
4394
+ let heartbeatTimer = null;
4395
+ if (heartbeatSeconds > 0) {
4396
+ const refreshSeconds = heartbeatRefreshSeconds ?? heartbeatSeconds / 2;
4397
+ const intervalMs = refreshSeconds * 1e3;
4398
+ heartbeatTimer = setInterval(async () => {
4399
+ try {
4400
+ await this.touch(name, jobIds);
4401
+ } catch (err) {
4402
+ this.emit(events$3.error, err);
4403
+ }
4404
+ }, intervalMs);
4405
+ }
4111
4406
  try {
4112
4407
  const result = await resolveWithinSeconds(callback(jobs2), maxExpiration, `handler execution exceeded ${maxExpiration}s`, ac);
4113
4408
  await this.complete(name, jobIds, jobIds.length === 1 ? result : void 0);
@@ -4116,6 +4411,7 @@ class Manager extends EventEmitter {
4116
4411
  await this.fail(name, jobIds, err);
4117
4412
  this.#trackJobsFailed(name, jobs2, err);
4118
4413
  } finally {
4414
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
4119
4415
  if (worker) {
4120
4416
  worker.abortController = null;
4121
4417
  }
@@ -4182,7 +4478,10 @@ class Manager extends EventEmitter {
4182
4478
  localConcurrency = 1,
4183
4479
  localGroupConcurrency,
4184
4480
  groupConcurrency,
4185
- orderByCreatedOn = true
4481
+ orderByCreatedOn = true,
4482
+ heartbeatRefreshSeconds,
4483
+ minPriority,
4484
+ maxPriority
4186
4485
  } = options;
4187
4486
  if (localGroupConcurrency != null) {
4188
4487
  this.#storeLocalGroupConfig(name, localGroupConcurrency);
@@ -4191,7 +4490,7 @@ class Manager extends EventEmitter {
4191
4490
  const createWorker = (workerId) => {
4192
4491
  const fetch = () => {
4193
4492
  const ignoreGroups = localGroupConcurrency != null ? this.#getGroupsAtLocalCapacity(name) : void 0;
4194
- return this.fetch(name, { batchSize, includeMetadata, priority, orderByCreatedOn, groupConcurrency, ignoreGroups });
4493
+ return this.fetch(name, { batchSize, includeMetadata, priority, orderByCreatedOn, groupConcurrency, ignoreGroups, minPriority, maxPriority });
4195
4494
  };
4196
4495
  const onFetch = async (jobs2) => {
4197
4496
  if (!jobs2.length) return;
@@ -4200,7 +4499,7 @@ class Manager extends EventEmitter {
4200
4499
  this.#trackJobsActive(name, jobs2);
4201
4500
  const worker = this.workers.get(workerId);
4202
4501
  if (localGroupConcurrency == null) {
4203
- await this.#processJobs(name, jobs2, callback, worker);
4502
+ await this.#processJobs(name, jobs2, callback, worker, heartbeatRefreshSeconds);
4204
4503
  } else {
4205
4504
  const { allowed, excess, groupedJobs } = this.#trackLocalGroupStart(name, jobs2);
4206
4505
  if (excess.length > 0) {
@@ -4209,7 +4508,7 @@ class Manager extends EventEmitter {
4209
4508
  }
4210
4509
  if (allowed.length > 0) {
4211
4510
  try {
4212
- await this.#processJobs(name, allowed, callback, worker);
4511
+ await this.#processJobs(name, allowed, callback, worker, heartbeatRefreshSeconds);
4213
4512
  } finally {
4214
4513
  this.#trackLocalGroupEnd(name, groupedJobs);
4215
4514
  }
@@ -4346,6 +4645,7 @@ class Manager extends EventEmitter {
4346
4645
  retryDelay,
4347
4646
  retryBackoff,
4348
4647
  retryDelayMax,
4648
+ heartbeatSeconds,
4349
4649
  group,
4350
4650
  deadLetter = null
4351
4651
  } = options;
@@ -4368,6 +4668,7 @@ class Manager extends EventEmitter {
4368
4668
  retryDelay,
4369
4669
  retryBackoff,
4370
4670
  retryDelayMax,
4671
+ heartbeatSeconds,
4371
4672
  deadLetter
4372
4673
  };
4373
4674
  const db = wrapper || this.db;
@@ -4543,6 +4844,15 @@ class Manager extends EventEmitter {
4543
4844
  const result = await db.executeSql(sql, [name, ids]);
4544
4845
  return this.mapCommandResponse(ids, result);
4545
4846
  }
4847
+ async touch(name, id, options = {}) {
4848
+ assertQueueName(name);
4849
+ const db = this.assertDb(options);
4850
+ const ids = this.mapCompletionIdArg(id, "touch");
4851
+ const { table } = await this.getQueueCache(name);
4852
+ const sql = touchJobs(this.config.schema, table);
4853
+ const result = await db.executeSql(sql, [name, ids]);
4854
+ return this.mapCommandResponse(ids, result);
4855
+ }
4546
4856
  async createQueue(name, options = {}) {
4547
4857
  name = name || options.name;
4548
4858
  assertQueueName(name);
@@ -4729,6 +5039,9 @@ class Boss extends EventEmitter {
4729
5039
  WARNINGS.LARGE_QUEUE.size = config.warningQueueSize;
4730
5040
  }
4731
5041
  }
5042
+ get maintaining() {
5043
+ return !!this.#maintaining;
5044
+ }
4732
5045
  async start() {
4733
5046
  if (this.#stopped) {
4734
5047
  this.#stopping = false;
@@ -4744,6 +5057,9 @@ class Boss extends EventEmitter {
4744
5057
  this.#stopping = true;
4745
5058
  if (this.#superviseInterval) clearInterval(this.#superviseInterval);
4746
5059
  this.#stopped = true;
5060
+ while (this.#maintaining) {
5061
+ await delay(10);
5062
+ }
4747
5063
  }
4748
5064
  }
4749
5065
  get #warningContext() {
@@ -4756,21 +5072,10 @@ class Boss extends EventEmitter {
4756
5072
  errorEvent: events$2.error
4757
5073
  };
4758
5074
  }
4759
- async #executeSql(sql) {
4760
- const started = Date.now();
4761
- const result = unwrapSQLResult(await this.#db.executeSql(sql));
4762
- const elapsed = (Date.now() - started) / 1e3;
4763
- if (elapsed > WARNINGS.SLOW_QUERY.seconds || this.#config.__test__warn_slow_query) {
4764
- await emitAndPersistWarning(
4765
- this.#warningContext,
4766
- WARNING_TYPES.SLOW_QUERY,
4767
- WARNINGS.SLOW_QUERY.message,
4768
- { elapsed, sql }
4769
- );
4770
- }
4771
- return result;
4772
- }
4773
5075
  async #executeQuery(query2) {
5076
+ if (typeof query2 === "string") {
5077
+ query2 = { text: query2, values: [] };
5078
+ }
4774
5079
  const started = Date.now();
4775
5080
  const result = unwrapSQLResult(await this.#db.executeSql(query2.text, query2.values));
4776
5081
  const elapsed = (Date.now() - started) / 1e3;
@@ -4792,6 +5097,9 @@ class Boss extends EventEmitter {
4792
5097
  throw new Error(this.#config.__test__throw_maint);
4793
5098
  }
4794
5099
  this.#maintaining = true;
5100
+ if (this.#config.__test__delay_maint_ms) {
5101
+ await delay(this.#config.__test__delay_maint_ms);
5102
+ }
4795
5103
  const queues = await this.#manager.getQueues();
4796
5104
  !this.#stopped && await this.supervise(queues);
4797
5105
  !this.#stopped && await this.#maintainWarnings();
@@ -4806,7 +5114,7 @@ class Boss extends EventEmitter {
4806
5114
  return;
4807
5115
  }
4808
5116
  const sql = deleteOldWarnings(this.#config.schema, this.#config.warningRetentionDays);
4809
- await this.#executeSql(sql);
5117
+ await this.#executeQuery(sql);
4810
5118
  }
4811
5119
  async supervise(value) {
4812
5120
  let queues;
@@ -4821,6 +5129,9 @@ class Boss extends EventEmitter {
4821
5129
  acc[table].queues.push(q);
4822
5130
  return acc;
4823
5131
  }, {});
5132
+ const heartbeatQueueNames = new Set(
5133
+ queues.filter((q) => q.heartbeatSeconds != null).map((q) => q.name)
5134
+ );
4824
5135
  for (const queueGroup of Object.values(queueGroups)) {
4825
5136
  if (this.#stopping) return;
4826
5137
  const { table, queues: queues2 } = queueGroup;
@@ -4828,12 +5139,12 @@ class Boss extends EventEmitter {
4828
5139
  while (names.length) {
4829
5140
  if (this.#stopping) return;
4830
5141
  const chunk = names.splice(0, 100);
4831
- await this.#monitor(table, chunk);
5142
+ await this.#monitor(table, chunk, heartbeatQueueNames);
4832
5143
  await this.#maintain(table, chunk);
4833
5144
  }
4834
5145
  }
4835
5146
  }
4836
- async #monitor(table, names) {
5147
+ async #monitor(table, names, heartbeatQueueNames) {
4837
5148
  if (this.#stopping) return;
4838
5149
  const command = trySetQueueMonitorTime(
4839
5150
  this.#config.schema,
@@ -4845,7 +5156,7 @@ class Boss extends EventEmitter {
4845
5156
  if (rows.length) {
4846
5157
  const queues = rows.map((q) => q.name);
4847
5158
  const cacheStatsSql = cacheQueueStats(this.#config.schema, table, queues);
4848
- const { rows: rowsCacheStats } = await this.#executeSql(cacheStatsSql);
5159
+ const { rows: rowsCacheStats } = await this.#executeQuery(cacheStatsSql);
4849
5160
  if (this.#stopping) return;
4850
5161
  const warnings2 = rowsCacheStats.filter((i) => i.queuedCount > (i.warningQueueSize || WARNINGS.LARGE_QUEUE.size));
4851
5162
  for (const warning of warnings2) {
@@ -4857,7 +5168,13 @@ class Boss extends EventEmitter {
4857
5168
  );
4858
5169
  }
4859
5170
  const sql = failJobsByTimeout(this.#config.schema, table, queues);
4860
- await this.#executeSql(sql);
5171
+ await this.#executeQuery(sql);
5172
+ if (this.#stopping) return;
5173
+ const heartbeatQueues = queues.filter((q) => heartbeatQueueNames.has(q));
5174
+ if (heartbeatQueues.length) {
5175
+ const heartbeatSql = failJobsByHeartbeat(this.#config.schema, table, heartbeatQueues);
5176
+ await this.#executeQuery(heartbeatSql);
5177
+ }
4861
5178
  }
4862
5179
  }
4863
5180
  async #maintain(table, names) {
@@ -4872,7 +5189,7 @@ class Boss extends EventEmitter {
4872
5189
  if (rows.length) {
4873
5190
  const queues = rows.map((q) => q.name);
4874
5191
  const sql = deletion(this.#config.schema, table, queues);
4875
- await this.#executeSql(sql);
5192
+ await this.#executeQuery(sql);
4876
5193
  }
4877
5194
  }
4878
5195
  }
@@ -4894,6 +5211,9 @@ class Bam extends EventEmitter {
4894
5211
  this.#stopped = true;
4895
5212
  this.#working = false;
4896
5213
  }
5214
+ get working() {
5215
+ return this.#working;
5216
+ }
4897
5217
  async start() {
4898
5218
  if (!this.#stopped) return;
4899
5219
  this.#stopped = false;
@@ -4910,6 +5230,9 @@ class Bam extends EventEmitter {
4910
5230
  clearInterval(this.#pollInterval);
4911
5231
  this.#pollInterval = void 0;
4912
5232
  }
5233
+ while (this.#working) {
5234
+ await delay(10);
5235
+ }
4913
5236
  }
4914
5237
  async #onPoll() {
4915
5238
  if (this.#stopped || this.#working || !this.#config.migrate) return;
@@ -4918,6 +5241,9 @@ class Bam extends EventEmitter {
4918
5241
  if (this.#config.__test__throw_bam) {
4919
5242
  throw new Error(this.#config.__test__throw_bam);
4920
5243
  }
5244
+ if (this.#config.__test__delay_bam_ms) {
5245
+ await delay(this.#config.__test__delay_bam_ms);
5246
+ }
4921
5247
  const sql = trySetBamTime(
4922
5248
  this.#config.schema,
4923
5249
  this.#config.bamIntervalSeconds
@@ -5187,6 +5513,9 @@ class PgBoss extends EventEmitter {
5187
5513
  fail(name, id, data, options) {
5188
5514
  return this.#manager.fail(name, id, data, options);
5189
5515
  }
5516
+ touch(name, id, options) {
5517
+ return this.#manager.touch(name, id, options);
5518
+ }
5190
5519
  /**
5191
5520
  * @deprecated Use findJobs() instead
5192
5521
  */
@@ -5217,6 +5546,15 @@ class PgBoss extends EventEmitter {
5217
5546
  getQueueStats(name) {
5218
5547
  return this.#manager.getQueueStats(name);
5219
5548
  }
5549
+ isMaintaining() {
5550
+ return this.#boss.maintaining;
5551
+ }
5552
+ isBamWorking() {
5553
+ return this.#bam.working;
5554
+ }
5555
+ isCheckingSkew() {
5556
+ return this.#timekeeper.checkingSkew;
5557
+ }
5220
5558
  supervise(name) {
5221
5559
  return this.#boss.supervise(name);
5222
5560
  }
@@ -9559,10 +9897,10 @@ const route11 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
9559
9897
  default: warnings,
9560
9898
  loader
9561
9899
  }, Symbol.toStringTag, { value: "Module" }));
9562
- const serverManifest = { "entry": { "module": "/assets/entry.client-COnaNoy-.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/index-DhMkYPMa.js"], "css": [] }, "routes": { "root": { "id": "root", "parentId": void 0, "path": "", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/root-NWrBrGvr.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/index-DhMkYPMa.js", "/assets/db-link-CtPnIrIr.js", "/assets/createLucideIcon-BXGwbdrh.js", "/assets/MenuTrigger-SThQHnlb.js", "/assets/useOpenInteractionType-C_L8nZ_l.js"], "css": ["/assets/root-DJRlbyb5.css"], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/_index": { "id": "routes/_index", "parentId": "root", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/_index-Bcg_-XSd.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/badge-Cd8v3tl3.js", "/assets/table-CTo0I5HG.js", "/assets/error-card-DmoxS3Ao.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/jobs": { "id": "routes/jobs", "parentId": "root", "path": "jobs", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/jobs-DtmTCs8I.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/badge-Cd8v3tl3.js", "/assets/table-CTo0I5HG.js", "/assets/pagination-NfhvsUbp.js", "/assets/filter-select-mMC79WOR.js", "/assets/error-card-DmoxS3Ao.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/queues._index": { "id": "routes/queues._index", "parentId": "root", "path": "queues", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/queues._index-Cw1B49mg.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/badge-Cd8v3tl3.js", "/assets/filter-select-mMC79WOR.js", "/assets/table-CTo0I5HG.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/queues.create": { "id": "routes/queues.create", "parentId": "root", "path": "queues/create", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/queues.create-BGXDhJ3m.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/chevron-down-xu6Uceu-.js", "/assets/error-card-DmoxS3Ao.js", "/assets/createLucideIcon-BXGwbdrh.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/queues.$name": { "id": "routes/queues.$name", "parentId": "root", "path": "queues/:name", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/queues._name-D0cG_qDX.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/badge-Cd8v3tl3.js", "/assets/table-CTo0I5HG.js", "/assets/pagination-NfhvsUbp.js", "/assets/filter-select-mMC79WOR.js", "/assets/dialog-Bl8T588f.js", "/assets/error-card-DmoxS3Ao.js", "/assets/chevron-down-xu6Uceu-.js", "/assets/createLucideIcon-BXGwbdrh.js", "/assets/MenuTrigger-SThQHnlb.js", "/assets/useOpenInteractionType-C_L8nZ_l.js", "/assets/index-DhMkYPMa.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/queues.$name.jobs.$jobId": { "id": "routes/queues.$name.jobs.$jobId", "parentId": "root", "path": "queues/:name/jobs/:jobId", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/queues._name.jobs._jobId-uJ3dfM3J.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/badge-Cd8v3tl3.js", "/assets/dialog-Bl8T588f.js", "/assets/error-card-DmoxS3Ao.js", "/assets/createLucideIcon-BXGwbdrh.js", "/assets/useOpenInteractionType-C_L8nZ_l.js", "/assets/index-DhMkYPMa.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/schedules": { "id": "routes/schedules", "parentId": "root", "path": "schedules", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/schedules-DzgBEayh.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/table-CTo0I5HG.js", "/assets/pagination-NfhvsUbp.js", "/assets/error-card-DmoxS3Ao.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/schedules.$name.$key": { "id": "routes/schedules.$name.$key", "parentId": "root", "path": "schedules/:name/:key", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/schedules._name._key-i42S9kw2.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/dialog-Bl8T588f.js", "/assets/error-card-DmoxS3Ao.js", "/assets/createLucideIcon-BXGwbdrh.js", "/assets/useOpenInteractionType-C_L8nZ_l.js", "/assets/index-DhMkYPMa.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/schedules.new": { "id": "routes/schedules.new", "parentId": "root", "path": "schedules/new", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/schedules.new-Dt78KptL.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/error-card-DmoxS3Ao.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/send": { "id": "routes/send", "parentId": "root", "path": "send", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/send-0eWgiWNl.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/db-link-CtPnIrIr.js", "/assets/button-BaXUPm8v.js", "/assets/error-card-DmoxS3Ao.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/warnings": { "id": "routes/warnings", "parentId": "root", "path": "warnings", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasErrorBoundary": true, "module": "/assets/warnings-BhQM6lFV.js", "imports": ["/assets/chunk-JZWAC4HX-DC8i-F7r.js", "/assets/button-BaXUPm8v.js", "/assets/badge-Cd8v3tl3.js", "/assets/table-CTo0I5HG.js", "/assets/pagination-NfhvsUbp.js", "/assets/filter-select-mMC79WOR.js", "/assets/error-card-DmoxS3Ao.js", "/assets/db-link-CtPnIrIr.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 } }, "url": "/assets/manifest-25954681.js", "version": "25954681", "sri": void 0 };
9900
+ const serverManifest = { "entry": { "module": "/assets/entry.client-DXZUROTm.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/index-DuhoQwUK.js"], "css": [] }, "routes": { "root": { "id": "root", "parentId": void 0, "path": "", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/root-DDqOT0vL.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/index-DuhoQwUK.js", "/assets/db-link-CU7IJqbq.js", "/assets/createLucideIcon-Bt-yXILq.js", "/assets/MenuTrigger-CoRd-BqL.js", "/assets/useOpenInteractionType-mgYMZAa8.js"], "css": ["/assets/root-Bpb4WtY4.css"], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/_index": { "id": "routes/_index", "parentId": "root", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/_index-BTx7NRR8.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/badge-DBD4CzAv.js", "/assets/table-Au2E5mnP.js", "/assets/error-card-D-uZRy_l.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/jobs": { "id": "routes/jobs", "parentId": "root", "path": "jobs", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/jobs-ouYjIGAi.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/badge-DBD4CzAv.js", "/assets/table-Au2E5mnP.js", "/assets/pagination-BItl293Q.js", "/assets/filter-select-Tk_CBvrn.js", "/assets/error-card-D-uZRy_l.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/queues._index": { "id": "routes/queues._index", "parentId": "root", "path": "queues", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/queues._index-_ID9kaow.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/badge-DBD4CzAv.js", "/assets/filter-select-Tk_CBvrn.js", "/assets/table-Au2E5mnP.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/queues.create": { "id": "routes/queues.create", "parentId": "root", "path": "queues/create", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/queues.create-CmVW5UVm.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/chevron-down-CYa4FYmp.js", "/assets/error-card-D-uZRy_l.js", "/assets/createLucideIcon-Bt-yXILq.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/queues.$name": { "id": "routes/queues.$name", "parentId": "root", "path": "queues/:name", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/queues._name-DwdbQND-.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/badge-DBD4CzAv.js", "/assets/table-Au2E5mnP.js", "/assets/pagination-BItl293Q.js", "/assets/filter-select-Tk_CBvrn.js", "/assets/dialog-Dkx42Rkd.js", "/assets/error-card-D-uZRy_l.js", "/assets/chevron-down-CYa4FYmp.js", "/assets/createLucideIcon-Bt-yXILq.js", "/assets/MenuTrigger-CoRd-BqL.js", "/assets/useOpenInteractionType-mgYMZAa8.js", "/assets/index-DuhoQwUK.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/queues.$name.jobs.$jobId": { "id": "routes/queues.$name.jobs.$jobId", "parentId": "root", "path": "queues/:name/jobs/:jobId", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/queues._name.jobs._jobId-DrgG3VBb.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/badge-DBD4CzAv.js", "/assets/dialog-Dkx42Rkd.js", "/assets/error-card-D-uZRy_l.js", "/assets/createLucideIcon-Bt-yXILq.js", "/assets/useOpenInteractionType-mgYMZAa8.js", "/assets/index-DuhoQwUK.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/schedules": { "id": "routes/schedules", "parentId": "root", "path": "schedules", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/schedules-B6ixYsvz.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/table-Au2E5mnP.js", "/assets/pagination-BItl293Q.js", "/assets/error-card-D-uZRy_l.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/schedules.$name.$key": { "id": "routes/schedules.$name.$key", "parentId": "root", "path": "schedules/:name/:key", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/schedules._name._key-CXTVKoOc.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/dialog-Dkx42Rkd.js", "/assets/error-card-D-uZRy_l.js", "/assets/createLucideIcon-Bt-yXILq.js", "/assets/useOpenInteractionType-mgYMZAa8.js", "/assets/index-DuhoQwUK.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/schedules.new": { "id": "routes/schedules.new", "parentId": "root", "path": "schedules/new", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/schedules.new-Dmasri9v.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/error-card-D-uZRy_l.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/send": { "id": "routes/send", "parentId": "root", "path": "send", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/send-CqR34Z50.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/db-link-CU7IJqbq.js", "/assets/button-Ba8FWhqs.js", "/assets/error-card-D-uZRy_l.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 }, "routes/warnings": { "id": "routes/warnings", "parentId": "root", "path": "warnings", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasClientMiddleware": false, "hasDefaultExport": true, "hasErrorBoundary": true, "module": "/assets/warnings-Dj3qacgN.js", "imports": ["/assets/chunk-UVKPFVEO-YfSfkVr_.js", "/assets/button-Ba8FWhqs.js", "/assets/badge-DBD4CzAv.js", "/assets/table-Au2E5mnP.js", "/assets/pagination-BItl293Q.js", "/assets/filter-select-Tk_CBvrn.js", "/assets/error-card-D-uZRy_l.js", "/assets/db-link-CU7IJqbq.js"], "css": [], "clientActionModule": void 0, "clientLoaderModule": void 0, "clientMiddlewareModule": void 0, "hydrateFallbackModule": void 0 } }, "url": "/assets/manifest-72f6b3e9.js", "version": "72f6b3e9", "sri": void 0 };
9563
9901
  const assetsBuildDirectory = "build/client";
9564
9902
  const basename = "/";
9565
- const future = { "unstable_optimizeDeps": false, "unstable_subResourceIntegrity": false, "unstable_trailingSlashAwareDataRequests": false, "v8_middleware": false, "v8_splitRouteModules": false, "v8_viteEnvironmentApi": false };
9903
+ const future = { "unstable_optimizeDeps": false, "unstable_passThroughRequests": false, "unstable_subResourceIntegrity": false, "unstable_trailingSlashAwareDataRequests": false, "unstable_previewServerPrerendering": false, "v8_middleware": false, "v8_splitRouteModules": false, "v8_viteEnvironmentApi": false };
9566
9904
  const ssr = true;
9567
9905
  const isSpaMode = false;
9568
9906
  const prerender = [];