@evanp/activitypub-bot 0.45.17 → 0.45.18

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
@@ -9,6 +9,13 @@ and this project adheres to
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.45.18] - 2026-05-12
13
+
14
+ ### Fixed
15
+
16
+ - Changed how AbortSignal is used in JobQueue to prevent
17
+ memory leak.
18
+
12
19
  ## [0.45.17] - 2026-05-11
13
20
 
14
21
  ### Changed
package/lib/jobqueue.js CHANGED
@@ -46,17 +46,25 @@ export class JobQueue {
46
46
  'dequeueing job'
47
47
  )
48
48
 
49
- while (!res) {
50
- res = await this.#claimNextJob(queueId, jobRunnerId)
51
- if (res) break
52
- delay = Math.min(delay * 2, MAX_DELAY)
53
- this.#logger.debug({ method: 'dequeue', delay }, 'sleeping')
54
- try {
55
- await sleep(delay, null, { signal: AbortSignal.any([this.#ac.signal, this.#wakeAc.signal]) })
56
- } catch (err) {
57
- if (this.#ac.signal.aborted) throw err
58
- delay = 50
49
+ let armed = this.#armCombined()
50
+ try {
51
+ while (!res) {
52
+ res = await this.#claimNextJob(queueId, jobRunnerId)
53
+ if (res) break
54
+ delay = Math.min(delay * 2, MAX_DELAY)
55
+ this.#logger.debug({ method: 'dequeue', delay }, 'sleeping')
56
+ try {
57
+ await sleep(delay, null, { signal: armed.signal })
58
+ } catch (err) {
59
+ if (this.#ac.signal.aborted) throw err
60
+ delay = 50
61
+ // Wake fired — tear down old listeners and arm a fresh combined signal.
62
+ this.#disarmCombined(armed)
63
+ armed = this.#armCombined()
64
+ }
59
65
  }
66
+ } finally {
67
+ this.#disarmCombined(armed)
60
68
  }
61
69
 
62
70
  const jobId = res.job_id
@@ -67,6 +75,24 @@ export class JobQueue {
67
75
  return { jobId, payload, attempts }
68
76
  }
69
77
 
78
+ // Build a combined abort signal manually, avoiding AbortSignal.any() which
79
+ // registers a FinalizationRegistry callback per call (Node's
80
+ // sourceSignalsCleanupRegistry). Under the dequeue loop's call rate, those
81
+ // registrations dominate heap growth.
82
+ #armCombined () {
83
+ const combinedAc = new AbortController()
84
+ const onAbort = () => combinedAc.abort()
85
+ const wakeAc = this.#wakeAc
86
+ this.#ac.signal.addEventListener('abort', onAbort, { once: true })
87
+ wakeAc.signal.addEventListener('abort', onAbort, { once: true })
88
+ return { signal: combinedAc.signal, onAbort, wakeAc }
89
+ }
90
+
91
+ #disarmCombined (armed) {
92
+ this.#ac.signal.removeEventListener('abort', armed.onAbort)
93
+ armed.wakeAc.signal.removeEventListener('abort', armed.onAbort)
94
+ }
95
+
70
96
  async complete (jobId, jobRunnerId) {
71
97
  await this.#connection.query(`
72
98
  DELETE FROM job
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evanp/activitypub-bot",
3
- "version": "0.45.17",
3
+ "version": "0.45.18",
4
4
  "description": "server-side ActivityPub bot framework",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",