@objectstack/service-queue 17.0.0-rc.3 → 17.0.0-rc.4

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/CHANGELOG.md CHANGED
@@ -1,13 +1,257 @@
1
1
  # @objectstack/service-queue
2
2
 
3
- ## 17.0.0-rc.3
4
-
5
- ### Patch Changes
6
-
3
+ ## 17.0.0-rc.4
4
+
5
+ ### Minor Changes
6
+
7
+ - e98fb14: fix(service-queue): `sys_job_queue` no longer grows forever — `completed` rows expire on a declared 7-day retention (#5179)
8
+
9
+ `DbQueueAdapter` marked a delivered message `status: 'completed'` and then
10
+ **nothing ever touched that row again**. `purge()` had zero production callers
11
+ (tests only), `purgeFailed()` is a manual dead-letter API, and the object
12
+ declared no lifecycle policy at all — so every queue delivery left a permanent
13
+ row, which since #5160 means one permanent row per queued email.
14
+
15
+ `sys_job_queue` now declares an ADR-0057 policy and the platform
16
+ `LifecycleService` enforces it on its existing hourly sweep:
17
+
18
+ ```ts
19
+ lifecycle: {
20
+ class: 'transient',
21
+ retention: { maxAge: '7d', onlyWhen: { status: 'completed' } },
22
+ }
23
+ ```
24
+
25
+ **Only `completed` rows are swept.** `pending` / `running` are live work, and
26
+ `failed` / `dlq` are the dead-letter queue — they exist to wait for a human, so
27
+ they are never deleted automatically at any age. `listFailed()` / `replay()` /
28
+ `purgeFailed()` remain the only way a dead letter leaves the table. This is
29
+ also why the policy is `retention` (age + row filter) rather than a `ttl` on
30
+ `completed_at`: TTL has no row filter, and `dlq` rows stamp `completed_at` too.
31
+
32
+ **No new configuration, and no new sweeper.** ADR-0057 §3.3 puts one reaper in
33
+ the platform rather than one per plugin — the same call the sibling
34
+ `sys_job_run` (30d) already makes. Any kernel with a data engine already runs
35
+ it, its per-sweep `[lifecycle] sweep: … ~N rows reaped` line now accounts for
36
+ this table too, and the window is overridable per environment through the
37
+ `lifecycle` settings namespace without touching code.
38
+
39
+ **The dedup window is now an enforced invariant, not a coincidence.** Publish
40
+ dedups against a terminal row by comparing its `created_at` to
41
+ `idempotencyWindowMs` (default 24h), and the reaper cuts off on that same
42
+ `created_at` axis — so retention (7d) ≥ dedup window is what keeps "duplicate
43
+ publishes inside the window are suppressed" true. `DbQueueAdapter` reads the
44
+ declared window (new export `completedRetentionWindowMs()`) and **throws at
45
+ construction** if `idempotencyWindowMs` is configured longer than it, instead of
46
+ silently degrading into duplicate deliveries days later. If you raise
47
+ `idempotencyWindowMs` past 7 days, raise the object's declared retention (or the
48
+ `lifecycle` settings override) to match — the error message names both numbers.
49
+
50
+ `class: 'transient'` is deliberate: `telemetry`/`event`/`audit` classes
51
+ relocate their table to the dedicated `telemetry` datasource wherever one is
52
+ registered (ADR-0057 §3.6), and moving a live work queue's storage would be a
53
+ migration, not a cleanup.
54
+
55
+ ### Patch Changes
56
+
57
+ - 7c2f7dd: fix(objectql,service-queue): a `lifecycle` settings override can no longer undercut a consumer's retention floor (#5195)
58
+
59
+ ADR-0057 P4 lets an operator override any object's retention window per
60
+ environment and per tenant through the `lifecycle` settings namespace. Until now
61
+ the only validation on that override was **does it parse** — and a retention
62
+ window is not only the operator's business: other code can depend on the rows
63
+ still being there.
64
+
65
+ `sys_job_queue` is the worked example. `DbQueueAdapter` deduplicates publishes by
66
+ comparing a terminal row's `created_at` against its idempotency window, so the
67
+ dedup check only means anything while that row still exists; #5179 made the
68
+ ordering an invariant by refusing, at construction, an idempotency window longer
69
+ than the object's **declared** retention. A settings override the constructor
70
+ cannot see walks straight around it:
71
+
72
+ ```jsonc
73
+ // lifecycle → retention_overrides
74
+ { "sys_job_queue": { "maxAge": "1h" } }
75
+ ```
76
+
77
+ completed rows are reaped an hour after they are written, publish keeps
78
+ deduplicating against 24h, and duplicate deliveries resume **with nothing in any
79
+ log**.
80
+
81
+ **New: retention floors.** A consumer may now declare, at runtime, the shortest
82
+ window its own contract survives:
83
+
84
+ ```ts
85
+ lifecycle.registerRetentionFloor("sys_job_queue", {
86
+ policy: "retention", // or 'ttl'
87
+ minWindowMs: 24 * 60 * 60 * 1000,
88
+ declaredBy: "com.objectstack.service.queue",
89
+ consequence: "…what silently breaks below it",
90
+ remedy: "…the settings change that makes an override legal",
91
+ });
92
+ ```
93
+
94
+ - An override below the floor — **global or tenant-scoped** — is **rejected**,
95
+ and the declared window keeps running. Not clamped to the floor: clamping
96
+ would enforce a third number written in neither the declaration nor the
97
+ settings, and that number would move whenever an unrelated package changed
98
+ its floor. Rejection has exactly one fallback, the declaration, which is
99
+ already how an unparseable override resolves.
100
+ - The rejection is `error`-level and carries both the consequence and the fix,
101
+ because what it prevents leaves the system looking entirely healthy. It is
102
+ also on the sweep report as `LifecycleSweepReport.floorViolations` — machine-
103
+ readable, every sweep.
104
+ - A **declared** window below a registered floor is reported the same way and
105
+ still enforced: refusing to reap would trade a broken consumer contract for
106
+ the unbounded table #5179 just closed.
107
+ - Objects with no registered floor are completely unaffected — P4 overrides
108
+ behave exactly as before.
109
+
110
+ Floors are runtime wiring, not spec surface (the same call ADR-0057's reap-guard
111
+ amendment makes), plus a reason of their own: the queue's floor **is**
112
+ `DbQueueAdapterOptions.idempotencyWindowMs`, a per-kernel construction option, so
113
+ a static key on the object's `lifecycle` block could only ever be a second copy
114
+ of it that drifts. No `packages/spec` change.
115
+
116
+ `QueueServicePlugin` registers `sys_job_queue`'s floor on `kernel:ready`,
117
+ carrying the window the adapter was actually constructed with — so a non-default
118
+ `db.idempotencyWindowMs` is covered too. The ordering is now enforced from both
119
+ sides: the constructor rejects a too-long `idempotencyWindowMs`, the floor
120
+ rejects a too-short `maxAge`.
121
+
122
+ New exports from `@objectstack/objectql`: `LifecycleRetentionFloor`,
123
+ `LifecycleFloorViolation`, plus `LifecycleService.registerRetentionFloor()`.
124
+ `LifecycleLoggerLike` gained an optional `error()` (absent ⇒ falls back to
125
+ `warn`), and `LifecycleSweepReport` gained `floorViolations`.
126
+
127
+ - Updated dependencies [9fe9c1d]
128
+ - Updated dependencies [d4e0809]
129
+ - Updated dependencies [f724f69]
130
+ - Updated dependencies [28ad90e]
131
+ - Updated dependencies [f8644c7]
132
+ - Updated dependencies [306ca50]
133
+ - Updated dependencies [978fed2]
134
+ - Updated dependencies [cfc293f]
135
+ - Updated dependencies [de70b42]
136
+ - Updated dependencies [fb3d99b]
137
+ - Updated dependencies [cdfbee2]
138
+ - Updated dependencies [29c6c9d]
139
+ - Updated dependencies [d21c001]
140
+ - Updated dependencies [f1cc3a3]
141
+ - Updated dependencies [ddc2527]
142
+ - Updated dependencies [553a47f]
143
+ - Updated dependencies [a3a884d]
144
+ - Updated dependencies [cfed092]
7
145
  - Updated dependencies [2e284b2]
8
- - @objectstack/spec@17.0.0-rc.3
9
- - @objectstack/core@17.0.0-rc.3
10
- - @objectstack/platform-objects@17.0.0-rc.3
146
+ - Updated dependencies [1b49eaf]
147
+ - Updated dependencies [0161c7f]
148
+ - Updated dependencies [e900015]
149
+ - Updated dependencies [b5bdf48]
150
+ - Updated dependencies [a019e52]
151
+ - Updated dependencies [64fc6d5]
152
+ - Updated dependencies [b746aa0]
153
+ - Updated dependencies [947d4f9]
154
+ - Updated dependencies [eaaf03c]
155
+ - Updated dependencies [d17df80]
156
+ - Updated dependencies [7d0e7b5]
157
+ - Updated dependencies [6513c17]
158
+ - Updated dependencies [c142ced]
159
+ - Updated dependencies [eda599e]
160
+ - Updated dependencies [c001422]
161
+ - Updated dependencies [77022a9]
162
+ - Updated dependencies [52760bf]
163
+ - Updated dependencies [5543020]
164
+ - Updated dependencies [880d343]
165
+ - Updated dependencies [6e82972]
166
+ - Updated dependencies [4615a18]
167
+ - Updated dependencies [7f62706]
168
+ - Updated dependencies [667fa44]
169
+ - Updated dependencies [37e38d1]
170
+ - Updated dependencies [1eb13a0]
171
+ - Updated dependencies [c52e608]
172
+ - Updated dependencies [4dfd002]
173
+ - Updated dependencies [77be690]
174
+ - Updated dependencies [811c30c]
175
+ - Updated dependencies [b49ccfd]
176
+ - Updated dependencies [85d95e7]
177
+ - Updated dependencies [168f60f]
178
+ - Updated dependencies [244ca86]
179
+ - Updated dependencies [546ab3c]
180
+ - Updated dependencies [0b51bb6]
181
+ - Updated dependencies [d9971d3]
182
+ - Updated dependencies [eb3e650]
183
+ - Updated dependencies [abeb375]
184
+ - Updated dependencies [ef4efa8]
185
+ - Updated dependencies [cbb6a5c]
186
+ - Updated dependencies [795b6e1]
187
+ - Updated dependencies [175d789]
188
+ - Updated dependencies [55dbbba]
189
+ - Updated dependencies [72c3c86]
190
+ - Updated dependencies [7f1a635]
191
+ - Updated dependencies [e98fb14]
192
+ - Updated dependencies [0f2fdcd]
193
+ - Updated dependencies [8ffa8b9]
194
+ - Updated dependencies [674ac99]
195
+ - Updated dependencies [1b9a53b]
196
+ - Updated dependencies [502564d]
197
+ - Updated dependencies [471839d]
198
+ - Updated dependencies [46365ab]
199
+ - Updated dependencies [b508244]
200
+ - Updated dependencies [594508e]
201
+ - Updated dependencies [1c625ca]
202
+ - Updated dependencies [71f205d]
203
+ - Updated dependencies [414395b]
204
+ - Updated dependencies [c5adfe1]
205
+ - Updated dependencies [26e1029]
206
+ - Updated dependencies [108ba8d]
207
+ - Updated dependencies [b4ad984]
208
+ - Updated dependencies [a9f32df]
209
+ - Updated dependencies [aeb9b27]
210
+ - Updated dependencies [7d27da0]
211
+ - Updated dependencies [089767f]
212
+ - Updated dependencies [e4c8b6c]
213
+ - Updated dependencies [acb10f6]
214
+ - Updated dependencies [1c3da1f]
215
+ - Updated dependencies [a34fd2e]
216
+ - Updated dependencies [889ae47]
217
+ - Updated dependencies [4f4c3fb]
218
+ - Updated dependencies [7adc841]
219
+ - Updated dependencies [4845f85]
220
+ - Updated dependencies [7b005b4]
221
+ - Updated dependencies [94f7b6a]
222
+ - Updated dependencies [5c94f83]
223
+ - Updated dependencies [73e576f]
224
+ - Updated dependencies [c5a5996]
225
+ - Updated dependencies [ae490ef]
226
+ - Updated dependencies [f61c8cf]
227
+ - Updated dependencies [e3ef52b]
228
+ - Updated dependencies [07f1822]
229
+ - Updated dependencies [04fab5e]
230
+ - Updated dependencies [efedd28]
231
+ - Updated dependencies [5278e11]
232
+ - Updated dependencies [23dba62]
233
+ - Updated dependencies [ba98e26]
234
+ - Updated dependencies [f104bab]
235
+ - Updated dependencies [fc5f536]
236
+ - Updated dependencies [f8cfbb4]
237
+ - Updated dependencies [c89d18c]
238
+ - Updated dependencies [aac90a5]
239
+ - Updated dependencies [1e6ab15]
240
+ - Updated dependencies [c87ef70]
241
+ - Updated dependencies [3cb0618]
242
+ - Updated dependencies [32a0874]
243
+ - Updated dependencies [7055c22]
244
+ - Updated dependencies [785a748]
245
+ - Updated dependencies [3af0354]
246
+ - Updated dependencies [866ff16]
247
+ - Updated dependencies [5a85e67]
248
+ - Updated dependencies [c183a12]
249
+ - Updated dependencies [8064b07]
250
+ - Updated dependencies [4a56dbd]
251
+ - Updated dependencies [06df4fa]
252
+ - @objectstack/spec@17.0.0-rc.4
253
+ - @objectstack/core@17.0.0-rc.4
254
+ - @objectstack/platform-objects@17.0.0-rc.4
11
255
 
12
256
  ## 17.0.0-rc.2
13
257
 
package/README.md CHANGED
@@ -70,6 +70,35 @@ new QueueServicePlugin({
70
70
  new QueueServicePlugin({ adapter: 'memory' });
71
71
  ```
72
72
 
73
+ ### Retention — how `sys_job_queue` stays bounded
74
+
75
+ Delivered messages are not kept forever. `sys_job_queue` declares an ADR-0057
76
+ lifecycle policy and the platform `LifecycleService` (shipped with
77
+ `@objectstack/objectql`, armed on every kernel that has data) enforces it — no
78
+ configuration, no extra scheduler:
79
+
80
+ | Row state | What happens |
81
+ |---|---|
82
+ | `completed` | deleted **7 days** after `created_at` |
83
+ | `pending` / `running` | never swept — live work |
84
+ | `failed` / `dlq` | never swept — the dead-letter queue waits for a human (`listFailed` / `replay` / `purgeFailed`) |
85
+
86
+ Two consequences worth knowing:
87
+
88
+ - **`idempotencyWindowMs` must not exceed the retention window.** Dedup against
89
+ a terminal message compares its `created_at` to that window, so a longer
90
+ setting would start accepting duplicates the moment the row was swept. The
91
+ `db` adapter throws at construction instead of degrading quietly.
92
+ - **The window is overridable per environment** through the `lifecycle`
93
+ settings namespace (`maxAge` per object), like every other ADR-0057 policy —
94
+ but **not below the idempotency window**. On startup this plugin registers a
95
+ *retention floor* with the `LifecycleService` carrying the window the adapter
96
+ was actually constructed with; a global or tenant-scoped override under it is
97
+ **rejected** at sweep time (the declared window keeps running) and logged at
98
+ `error` naming the consequence and the two settings that would make it legal.
99
+ So the ordering is enforced from both sides: the constructor rejects a
100
+ too-long `idempotencyWindowMs`, the floor rejects a too-short `maxAge`.
101
+
73
102
  ## Service API
74
103
 
75
104
  Implements `IQueueService` from `@objectstack/spec/contracts`:
package/dist/index.cjs CHANGED
@@ -22,12 +22,13 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  DbQueueAdapter: () => DbQueueAdapter,
24
24
  MemoryQueueAdapter: () => MemoryQueueAdapter,
25
- QueueServicePlugin: () => QueueServicePlugin
25
+ QueueServicePlugin: () => QueueServicePlugin,
26
+ completedRetentionWindowMs: () => completedRetentionWindowMs
26
27
  });
27
28
  module.exports = __toCommonJS(index_exports);
28
29
 
29
30
  // src/queue-service-plugin.ts
30
- var import_audit = require("@objectstack/platform-objects/audit");
31
+ var import_audit2 = require("@objectstack/platform-objects/audit");
31
32
 
32
33
  // src/memory-queue-adapter.ts
33
34
  var MemoryQueueAdapter = class {
@@ -83,6 +84,9 @@ var MemoryQueueAdapter = class {
83
84
  }
84
85
  };
85
86
 
87
+ // src/db-queue-adapter.ts
88
+ var import_audit = require("@objectstack/platform-objects/audit");
89
+
86
90
  // src/common.ts
87
91
  var SYSTEM_CTX = { isSystem: true, positions: [], permissions: [] };
88
92
  function uid(prefix) {
@@ -93,6 +97,21 @@ function uid(prefix) {
93
97
  function nowIso(clock) {
94
98
  return (clock?.now() ?? /* @__PURE__ */ new Date()).toISOString();
95
99
  }
100
+ var LIFECYCLE_UNIT_MS = {
101
+ h: 36e5,
102
+ d: 864e5,
103
+ w: 7 * 864e5,
104
+ y: 365 * 864e5
105
+ };
106
+ function lifecycleDurationMs(literal) {
107
+ const m = /^(\d+)(h|d|w|y)$/.exec(literal);
108
+ if (!m) {
109
+ throw new Error(
110
+ `[service-queue] invalid lifecycle duration literal '${literal}' \u2014 expected <n><unit> with unit h|d|w|y (e.g. '7d')`
111
+ );
112
+ }
113
+ return Number(m[1]) * LIFECYCLE_UNIT_MS[m[2]];
114
+ }
96
115
  function parseJson(raw, fallback) {
97
116
  if (raw == null) return fallback;
98
117
  if (typeof raw === "string") {
@@ -108,6 +127,15 @@ function parseJson(raw, fallback) {
108
127
 
109
128
  // src/db-queue-adapter.ts
110
129
  var QUEUE_TABLE = "sys_job_queue";
130
+ function completedRetentionWindowMs() {
131
+ const maxAge = import_audit.SysJobQueue.lifecycle?.retention?.maxAge;
132
+ if (!maxAge) {
133
+ throw new Error(
134
+ "[service-queue] sys_job_queue no longer declares lifecycle.retention \u2014 DbQueueAdapter dedups against terminal rows by `created_at` window and relies on that declared retention to keep them (ADR-0057, #5179). Restore the declaration in @objectstack/platform-objects rather than sweeping the table from here."
135
+ );
136
+ }
137
+ return lifecycleDurationMs(maxAge);
138
+ }
111
139
  var DbQueueAdapter = class {
112
140
  constructor(args) {
113
141
  this.handlers = /* @__PURE__ */ new Map();
@@ -125,6 +153,42 @@ var DbQueueAdapter = class {
125
153
  autoStart: o.autoStart ?? true,
126
154
  workerId: o.workerId ?? uid("worker")
127
155
  };
156
+ const retentionMs = completedRetentionWindowMs();
157
+ if (this.opts.idempotencyWindowMs > retentionMs) {
158
+ throw new Error(
159
+ `[service-queue] idempotencyWindowMs (${this.opts.idempotencyWindowMs}ms) exceeds the retention window sys_job_queue declares for completed rows (${retentionMs}ms, lifecycle.retention.maxAge \u2014 ADR-0057). Terminal-row dedup is evaluated by \`created_at\` against that same window, so the longer setting would silently accept duplicates once a row is reaped. Lower idempotencyWindowMs, or raise the declared retention (both windows are measured from \`created_at\`).`
160
+ );
161
+ }
162
+ }
163
+ /** The configured dedup window (ms) — the number the floor below is made of. */
164
+ get idempotencyWindowMs() {
165
+ return this.opts.idempotencyWindowMs;
166
+ }
167
+ /**
168
+ * [#5195] The retention floor `sys_job_queue` must satisfy for this adapter's
169
+ * dedup contract to mean anything, handed to `LifecycleService`
170
+ * (`registerRetentionFloor`) by `QueueServicePlugin`.
171
+ *
172
+ * The constructor check above only reads the object's **declaration**. ADR-0057
173
+ * P4 lets an operator override that window per environment/tenant through the
174
+ * `lifecycle` settings namespace, which the constructor cannot see: set
175
+ * `lifecycle.retention_overrides.sys_job_queue.maxAge = '1h'` and completed
176
+ * rows vanish an hour after they are written while publish keeps dedupping
177
+ * against a 24h window — duplicate deliveries resume, with nothing in any log.
178
+ * Registering the floor is what closes that door, and it carries the number
179
+ * this adapter was actually CONSTRUCTED with rather than a static copy of the
180
+ * default (a per-kernel option cannot live in the object's declaration).
181
+ */
182
+ retentionFloor() {
183
+ const ms = this.opts.idempotencyWindowMs;
184
+ const literal = `${Math.ceil(ms / 36e5)}h`;
185
+ return {
186
+ policy: "retention",
187
+ minWindowMs: ms,
188
+ declaredBy: "com.objectstack.service.queue",
189
+ consequence: `DbQueueAdapter dedups sys_job_queue publishes by comparing created_at against its ${ms}ms idempotency window, so a shorter retention deletes the very rows that check reads \u2014 duplicate deliveries resume silently, with nothing in any log.`,
190
+ remedy: `set lifecycle.retention_overrides.sys_job_queue.maxAge to '${literal}' or longer, or lower QueueServicePlugin's db.idempotencyWindowMs to the window you actually want (both are measured from created_at).`
191
+ };
128
192
  }
129
193
  // ── IQueueService ────────────────────────────────────────────────
130
194
  async publish(queue, data, options) {
@@ -456,7 +520,7 @@ var QueueServicePlugin = class {
456
520
  scope: "system",
457
521
  defaultDatasource: "cloud",
458
522
  namespace: "sys",
459
- objects: [import_audit.SysJobQueue]
523
+ objects: [import_audit2.SysJobQueue]
460
524
  });
461
525
  } catch (err) {
462
526
  ctx.logger.warn("QueueServicePlugin: manifest service unavailable; sys_job_queue not registered", err);
@@ -492,6 +556,7 @@ var QueueServicePlugin = class {
492
556
  logger: ctx.logger,
493
557
  options: this.options.db
494
558
  });
559
+ this.registerRetentionFloor(ctx, this.dbAdapter);
495
560
  try {
496
561
  ctx.replaceService?.("queue", this.dbAdapter);
497
562
  this.dbAdapter.start();
@@ -501,6 +566,37 @@ var QueueServicePlugin = class {
501
566
  }
502
567
  });
503
568
  }
569
+ /**
570
+ * [#5195] Register the adapter's retention floor with the platform
571
+ * LifecycleService. Best-effort: a kernel without a lifecycle service has no
572
+ * sweeper either, so there is no override for anything to bypass.
573
+ *
574
+ * The lookup is typed to {@link LifecycleFloorRegistrar} — the slot's
575
+ * contract as this package consumes it — rather than erased to `any`
576
+ * (#4127/#4251). The `typeof … === 'function'` probe stays because the method
577
+ * is genuinely optional (a lifecycle service predating floors), but it is now
578
+ * a check the compiler can see rather than one `any` was hiding.
579
+ */
580
+ registerRetentionFloor(ctx, adapter) {
581
+ let lifecycle;
582
+ try {
583
+ lifecycle = ctx.getService("lifecycle");
584
+ } catch {
585
+ lifecycle = void 0;
586
+ }
587
+ if (!lifecycle || typeof lifecycle.registerRetentionFloor !== "function") return;
588
+ try {
589
+ lifecycle.registerRetentionFloor(import_audit2.SysJobQueue.name, adapter.retentionFloor());
590
+ ctx.logger.info(
591
+ `QueueServicePlugin: registered a ${adapter.idempotencyWindowMs}ms retention floor on ${import_audit2.SysJobQueue.name} with the lifecycle service (settings overrides below it are rejected)`
592
+ );
593
+ } catch (err) {
594
+ ctx.logger.error(
595
+ "QueueServicePlugin: the lifecycle service rejected the sys_job_queue retention floor. A `lifecycle` settings override may now shorten sys_job_queue.retention below the idempotency window, in which case publish would silently re-accept duplicates (#5195). Fix the floor registration, or keep lifecycle.retention_overrides.sys_job_queue unset.",
596
+ err
597
+ );
598
+ }
599
+ }
504
600
  async destroy() {
505
601
  await this.dbAdapter?.stop();
506
602
  }
@@ -509,6 +605,7 @@ var QueueServicePlugin = class {
509
605
  0 && (module.exports = {
510
606
  DbQueueAdapter,
511
607
  MemoryQueueAdapter,
512
- QueueServicePlugin
608
+ QueueServicePlugin,
609
+ completedRetentionWindowMs
513
610
  });
514
611
  //# sourceMappingURL=index.cjs.map