@zukhruf/sqlite-queue 0.0.0-stage → 0.3.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +151 -2
  3. package/dist/activity.d.ts +10 -0
  4. package/dist/activity.d.ts.map +1 -0
  5. package/dist/activity.js +93 -0
  6. package/dist/activity.js.map +1 -0
  7. package/dist/attorney.d.ts +35 -0
  8. package/dist/attorney.d.ts.map +1 -0
  9. package/dist/attorney.js +367 -0
  10. package/dist/attorney.js.map +1 -0
  11. package/dist/claim-timer.d.ts +8 -0
  12. package/dist/claim-timer.d.ts.map +1 -0
  13. package/dist/claim-timer.js +80 -0
  14. package/dist/claim-timer.js.map +1 -0
  15. package/dist/completion-data.d.ts +6 -0
  16. package/dist/completion-data.d.ts.map +1 -0
  17. package/dist/completion-data.js +36 -0
  18. package/dist/completion-data.js.map +1 -0
  19. package/dist/database-error.d.ts +6 -0
  20. package/dist/database-error.d.ts.map +1 -0
  21. package/dist/database-error.js +12 -0
  22. package/dist/database-error.js.map +1 -0
  23. package/dist/database.d.ts +7 -0
  24. package/dist/database.d.ts.map +1 -0
  25. package/dist/database.js +28 -0
  26. package/dist/database.js.map +1 -0
  27. package/dist/in-flight.d.ts +7 -0
  28. package/dist/in-flight.d.ts.map +1 -0
  29. package/dist/in-flight.js +24 -0
  30. package/dist/in-flight.js.map +1 -0
  31. package/dist/index.d.ts +54 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +1069 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/job-fetcher.d.ts +16 -0
  36. package/dist/job-fetcher.d.ts.map +1 -0
  37. package/dist/job-fetcher.js +227 -0
  38. package/dist/job-fetcher.js.map +1 -0
  39. package/dist/job-queue.d.ts +27 -0
  40. package/dist/job-queue.d.ts.map +1 -0
  41. package/dist/job-queue.js +2 -0
  42. package/dist/job-queue.js.map +1 -0
  43. package/dist/job-rows.d.ts +49 -0
  44. package/dist/job-rows.d.ts.map +1 -0
  45. package/dist/job-rows.js +155 -0
  46. package/dist/job-rows.js.map +1 -0
  47. package/dist/job-settler.d.ts +24 -0
  48. package/dist/job-settler.d.ts.map +1 -0
  49. package/dist/job-settler.js +240 -0
  50. package/dist/job-settler.js.map +1 -0
  51. package/dist/jsonb.d.ts +7 -0
  52. package/dist/jsonb.d.ts.map +1 -0
  53. package/dist/jsonb.js +85 -0
  54. package/dist/jsonb.js.map +1 -0
  55. package/dist/local-groups.d.ts +16 -0
  56. package/dist/local-groups.d.ts.map +1 -0
  57. package/dist/local-groups.js +113 -0
  58. package/dist/local-groups.js.map +1 -0
  59. package/dist/naive-timestamp.d.ts +6 -0
  60. package/dist/naive-timestamp.d.ts.map +1 -0
  61. package/dist/naive-timestamp.js +21 -0
  62. package/dist/naive-timestamp.js.map +1 -0
  63. package/dist/queue-cache.d.ts +9 -0
  64. package/dist/queue-cache.d.ts.map +1 -0
  65. package/dist/queue-cache.js +27 -0
  66. package/dist/queue-cache.js.map +1 -0
  67. package/dist/queue-rows.d.ts +25 -0
  68. package/dist/queue-rows.d.ts.map +1 -0
  69. package/dist/queue-rows.js +44 -0
  70. package/dist/queue-rows.js.map +1 -0
  71. package/dist/recurrence.d.ts +11 -0
  72. package/dist/recurrence.d.ts.map +1 -0
  73. package/dist/recurrence.js +226 -0
  74. package/dist/recurrence.js.map +1 -0
  75. package/dist/rows.d.ts +10 -0
  76. package/dist/rows.d.ts.map +1 -0
  77. package/dist/rows.js +46 -0
  78. package/dist/rows.js.map +1 -0
  79. package/dist/schema.d.ts +3 -0
  80. package/dist/schema.d.ts.map +1 -0
  81. package/dist/schema.js +139 -0
  82. package/dist/schema.js.map +1 -0
  83. package/dist/sql-values.d.ts +9 -0
  84. package/dist/sql-values.d.ts.map +1 -0
  85. package/dist/sql-values.js +26 -0
  86. package/dist/sql-values.js.map +1 -0
  87. package/dist/start-after.d.ts +10 -0
  88. package/dist/start-after.d.ts.map +1 -0
  89. package/dist/start-after.js +125 -0
  90. package/dist/start-after.js.map +1 -0
  91. package/dist/supervisor.d.ts +16 -0
  92. package/dist/supervisor.d.ts.map +1 -0
  93. package/dist/supervisor.js +288 -0
  94. package/dist/supervisor.js.map +1 -0
  95. package/dist/throttle.d.ts +6 -0
  96. package/dist/throttle.d.ts.map +1 -0
  97. package/dist/throttle.js +35 -0
  98. package/dist/throttle.js.map +1 -0
  99. package/dist/timekeeper.d.ts +36 -0
  100. package/dist/timekeeper.d.ts.map +1 -0
  101. package/dist/timekeeper.js +305 -0
  102. package/dist/timekeeper.js.map +1 -0
  103. package/dist/timers.d.ts +9 -0
  104. package/dist/timers.d.ts.map +1 -0
  105. package/dist/timers.js +50 -0
  106. package/dist/timers.js.map +1 -0
  107. package/dist/uuid.d.ts +7 -0
  108. package/dist/uuid.d.ts.map +1 -0
  109. package/dist/uuid.js +36 -0
  110. package/dist/uuid.js.map +1 -0
  111. package/dist/worker.d.ts +35 -0
  112. package/dist/worker.d.ts.map +1 -0
  113. package/dist/worker.js +147 -0
  114. package/dist/worker.js.map +1 -0
  115. package/package.json +53 -4
@@ -0,0 +1,305 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { setImmediate } from 'node:timers/promises';
3
+ import { CronExpressionParser } from 'cron-parser';
4
+ import { scheduleKinds, scheduleMissedPolicies } from 'pg-boss';
5
+ import { transaction } from '@zukhruf/sqlite-transaction';
6
+ import { ClaimTimer } from "./claim-timer.js";
7
+ import { InFlight } from "./in-flight.js";
8
+ import { NaiveTimestamp } from "./naive-timestamp.js";
9
+ import { Recurrence } from "./recurrence.js";
10
+ import { Row } from "./rows.js";
11
+ import { SqlValues } from "./sql-values.js";
12
+ // pg-boss's Timekeeper (node_modules/pg-boss/dist/timekeeper.js) without the
13
+ // clock-skew loop (SQLite's clock is the database clock), invalid_schedule
14
+ // warnings, kind relabels and last_job_id: only schedule() writes a row, so
15
+ // no row is unreadable or mislabelled, and no method of the queue reads one
16
+ // back. A pass every cronMonitorIntervalSeconds claims the instance-wide
17
+ // cron stamp, files each schedule's due occurrences in __pgboss__send-it, one
18
+ // job per minute slot, and the send-it worker forwards each to its queue.
19
+ export class Timekeeper extends EventEmitter {
20
+ // timekeeper.js:12-14
21
+ static sendIt = '__pgboss__send-it';
22
+ // timekeeper.js:46: how long an occurrence stays due, and the width of the
23
+ // slot its job is filed in.
24
+ static #occurrenceWindowSeconds = 60;
25
+ // timekeeper.js:48
26
+ static #missedPolicies = Object.values(scheduleMissedPolicies);
27
+ #clock;
28
+ #activity;
29
+ #config;
30
+ #manager;
31
+ #phase = { kind: 'stopped' };
32
+ // pg-boss's _timekeeping: the pass in flight.
33
+ #pass = null;
34
+ constructor(clock, activity, config, manager) {
35
+ super();
36
+ this.#clock = clock;
37
+ this.#activity = activity;
38
+ this.#config = config;
39
+ this.#manager = manager;
40
+ }
41
+ // timekeeper.js:66-68: the name's underscores escaped, so the first
42
+ // unescaped underscore pair is the separator.
43
+ static #occurrenceKey(name, key) {
44
+ return `${name.replaceAll('_', '\\_')}__${key}`;
45
+ }
46
+ // timekeeper.js:76-79
47
+ static #throttleSlot(instant) {
48
+ const width = Timekeeper.#occurrenceWindowSeconds * 1000;
49
+ return new Date(Math.floor(instant.getTime() / width) * width)
50
+ .toISOString()
51
+ .replace('T', ' ')
52
+ .slice(0, 19);
53
+ }
54
+ // timekeeper.js:105-112: SQLite hands timestamps back as the text it
55
+ // stored.
56
+ static #toTime(value) {
57
+ if (value === null) {
58
+ return null;
59
+ }
60
+ const time = new Date(value).getTime();
61
+ return Number.isNaN(time) ? null : time;
62
+ }
63
+ // timekeeper.js:145-150
64
+ static #missedPolicy(options) {
65
+ const missed = typeof options === 'object' && options !== null && 'missed' in options
66
+ ? options.missed
67
+ : undefined;
68
+ return missed !== undefined && Timekeeper.#missedPolicies.includes(missed)
69
+ ? missed
70
+ : scheduleMissedPolicies.skip;
71
+ }
72
+ // timekeeper.js:241-262. The first pass runs on a real setImmediate, as
73
+ // pg-boss's does, then every cronMonitorIntervalSeconds on the clock.
74
+ async start(database) {
75
+ if (this.#phase.kind === 'keeping') {
76
+ return;
77
+ }
78
+ await this.#manager.createQueue(Timekeeper.sendIt);
79
+ await this.#manager.work(Timekeeper.sendIt, {
80
+ pollingIntervalSeconds: this.#config.cronWorkerIntervalSeconds,
81
+ batchSize: 50,
82
+ }, (jobs) => this.#onSendIt(jobs));
83
+ void setImmediate().then(() => this.#onCron());
84
+ this.#phase = {
85
+ kind: 'keeping',
86
+ database,
87
+ timer: new ClaimTimer(this.#clock, this.#config.cronMonitorIntervalSeconds, () => this.#onCron()),
88
+ };
89
+ }
90
+ // timekeeper.js:263-280, which polls its pass flag every 10 ms of real
91
+ // time.
92
+ async stop() {
93
+ const phase = this.#phase;
94
+ if (phase.kind !== 'keeping') {
95
+ return;
96
+ }
97
+ this.#phase = { kind: 'stopped' };
98
+ await this.#manager.offWork(Timekeeper.sendIt, { wait: true });
99
+ phase.timer.stop();
100
+ await this.#pass?.settled();
101
+ }
102
+ // timekeeper.js:335-384
103
+ async #onCron() {
104
+ if (this.#phase.kind !== 'keeping' || this.#pass) {
105
+ return;
106
+ }
107
+ const pass = this.#cronPass(this.#phase);
108
+ this.#pass = new InFlight(pass);
109
+ try {
110
+ await pass;
111
+ }
112
+ finally {
113
+ this.#pass = null;
114
+ }
115
+ }
116
+ async #cronPass(phase) {
117
+ try {
118
+ const { priorCronOn, elapsed, claimed } = await this.#activity.query(() => Timekeeper.#trySetCronTime(phase.database, this.#config.cronMonitorIntervalSeconds, new Date(this.#clock.now())));
119
+ if (claimed) {
120
+ phase.timer.anchor();
121
+ }
122
+ else {
123
+ phase.timer.anchor(this.#claimRetryDelay(elapsed));
124
+ }
125
+ if (this.#phase.kind === 'keeping' && claimed) {
126
+ await this.#cron(phase.database, priorCronOn);
127
+ }
128
+ }
129
+ catch (error) {
130
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
131
+ }
132
+ }
133
+ // timekeeper.js:406-411: until the claim it lost to comes due, plus a
134
+ // quarter of the interval capped at a second.
135
+ #claimRetryDelay(elapsed) {
136
+ const interval = this.#config.cronMonitorIntervalSeconds * 1000;
137
+ const age = Number(elapsed);
138
+ const remaining = Number.isFinite(age) && age > 0
139
+ ? Math.max(0, interval - age * 1000)
140
+ : interval;
141
+ return remaining + Math.min(1000, interval / 4);
142
+ }
143
+ // trySetCronTime and trySetTimestamp (plans.js:1078-1086, 1111-1118): the
144
+ // stamp it replaced and its age, and whether this pass moved it. A stamp
145
+ // never written counts as a week old.
146
+ static #trySetCronTime(db, seconds, now) {
147
+ return transaction(db, () => {
148
+ const row = Row.parse(db.prepare('SELECT cron_on FROM pgboss_version').get(), 'version');
149
+ const priorCronOn = Row.readNullableString(row, 'cron_on');
150
+ const prior = Timekeeper.#toTime(priorCronOn);
151
+ const elapsed = prior === null ? null : (now.getTime() - prior) / 1000;
152
+ const claimed = (now.getTime() - (prior ?? now.getTime() - 604_800_000)) / 1000 >=
153
+ seconds;
154
+ if (claimed) {
155
+ db.prepare('UPDATE pgboss_version SET cron_on = ?').run(SqlValues.asIso(now));
156
+ }
157
+ return { priorCronOn, elapsed, claimed };
158
+ }, { mode: 'immediate' });
159
+ }
160
+ // timekeeper.js:418-527, one instant for the whole pass.
161
+ async #cron(db, priorCronOn) {
162
+ const schedules = await this.#activity.query(() => Timekeeper.#getSchedules(db));
163
+ const scheduled = [];
164
+ const databaseTime = this.#clock.now();
165
+ const windowStart = databaseTime - Timekeeper.#occurrenceWindowSeconds * 1000;
166
+ const lastPass = Timekeeper.#toTime(priorCronOn);
167
+ for (const schedule of schedules) {
168
+ const { name, key, data, options, kind, cron, timezone } = schedule;
169
+ let due;
170
+ try {
171
+ due = Timekeeper.#readOccurrences(cron, kind, timezone, databaseTime);
172
+ }
173
+ catch {
174
+ // timekeeper.js:450-458: one row never decides the others'.
175
+ continue;
176
+ }
177
+ let missed = null;
178
+ try {
179
+ missed = Timekeeper.#missedOccurrence(schedule, lastPass, windowStart);
180
+ }
181
+ catch {
182
+ // timekeeper.js:464-474: the gap closes anyway, since the claim has
183
+ // moved.
184
+ }
185
+ // timekeeper.js:503-515: a set of slots, so a missed occurrence and a
186
+ // due one in the same minute send one job.
187
+ const slots = new Set();
188
+ if (missed !== null) {
189
+ slots.add(Timekeeper.#throttleSlot(missed));
190
+ }
191
+ for (const occurrence of due) {
192
+ slots.add(Timekeeper.#throttleSlot(occurrence));
193
+ }
194
+ for (const slot of slots) {
195
+ scheduled.push({
196
+ data: { name, key, data, options, slot },
197
+ singletonKey: Timekeeper.#occurrenceKey(name, key),
198
+ // The insert casts the slot to a zoneless timestamp
199
+ // (plans.js:2258).
200
+ singletonOn: NaiveTimestamp.fromUtc(new Date(`${slot}Z`)),
201
+ });
202
+ }
203
+ }
204
+ if (scheduled.length > 0 && this.#phase.kind === 'keeping') {
205
+ await this.#manager.insert(Timekeeper.sendIt, scheduled);
206
+ }
207
+ }
208
+ // getSchedules (plans.js:1276-1290), in name and key order.
209
+ static #getSchedules(db) {
210
+ return db
211
+ .prepare(`SELECT queue_name, key, kind, cron, COALESCE(timezone, 'UTC') AS timezone,
212
+ data, options, created_on
213
+ FROM pgboss_schedule
214
+ ORDER BY queue_name, key`)
215
+ .all()
216
+ .map((value) => {
217
+ const row = Row.parse(value, 'schedule');
218
+ const data = Row.readNullableString(row, 'data');
219
+ const options = Row.readNullableString(row, 'options');
220
+ return {
221
+ name: Row.readString(row, 'queue_name'),
222
+ key: Row.readString(row, 'key'),
223
+ kind: Row.readString(row, 'kind'),
224
+ cron: Row.readString(row, 'cron'),
225
+ timezone: Row.readString(row, 'timezone'),
226
+ data: data === null ? null : JSON.parse(data),
227
+ options: options === null ? null : JSON.parse(options),
228
+ createdOn: Row.readString(row, 'created_on'),
229
+ };
230
+ });
231
+ }
232
+ // timekeeper.js:591-601: under `once`, the latest occurrence in
233
+ // (max(lastPass, createdOn), windowStart], the gap no pass covered.
234
+ static #missedOccurrence(schedule, lastPass, windowStart) {
235
+ const { cron, kind, timezone, options, createdOn } = schedule;
236
+ if (Timekeeper.#missedPolicy(options) === scheduleMissedPolicies.skip ||
237
+ lastPass === null) {
238
+ return null;
239
+ }
240
+ const from = Math.max(lastPass, Timekeeper.#toTime(createdOn) ?? lastPass);
241
+ if (from >= windowStart) {
242
+ return null;
243
+ }
244
+ return Timekeeper.#latestOccurrenceBefore(cron, kind, timezone, new Date(from), new Date(windowStart));
245
+ }
246
+ // timekeeper.js:610-621. cron-parser's prev() answers strictly before its
247
+ // reference, so the reference is a millisecond past `until`.
248
+ static #latestOccurrenceBefore(expression, kind, tz, after, until) {
249
+ if (kind === scheduleKinds.rrule) {
250
+ return Recurrence.latestOccurrenceBefore(expression, after, until, tz);
251
+ }
252
+ const occurrence = CronExpressionParser.parse(expression, {
253
+ tz,
254
+ strict: false,
255
+ currentDate: new Date(until.getTime() + 1),
256
+ })
257
+ .prev()
258
+ .toDate();
259
+ return occurrence.getTime() > after.getTime() ? occurrence : null;
260
+ }
261
+ // timekeeper.js:639-653: every occurrence in the last minute, a cron
262
+ // expression's latest only.
263
+ static #readOccurrences(expression, kind, tz, databaseTime) {
264
+ const window = new Date(databaseTime - Timekeeper.#occurrenceWindowSeconds * 1000);
265
+ if (kind === scheduleKinds.rrule) {
266
+ return Recurrence.occurrencesInWindow(expression, window, new Date(databaseTime), tz);
267
+ }
268
+ const previous = CronExpressionParser.parse(expression, {
269
+ tz,
270
+ strict: false,
271
+ currentDate: new Date(databaseTime + 1),
272
+ })
273
+ .prev()
274
+ .toDate();
275
+ return previous.getTime() > window.getTime() ? [previous] : [];
276
+ }
277
+ // timekeeper.js:658-665: an error event nobody listens for must not fail
278
+ // the send-it job, which would replay every occurrence in its batch.
279
+ #reportSendItError(error) {
280
+ try {
281
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
282
+ }
283
+ catch {
284
+ // nothing left to report it to
285
+ }
286
+ }
287
+ // timekeeper.js:666-687: each occurrence's request goes to send() without
288
+ // its key and slot; a send that rejects is reported, and the send-it job
289
+ // completes either way.
290
+ async #onSendIt(jobs) {
291
+ const results = await Promise.allSettled(jobs.map(async ({ data }) => {
292
+ if (typeof data !== 'object' || data === null) {
293
+ throw new TypeError('send-it job data must be an object');
294
+ }
295
+ const request = Object.fromEntries(Object.entries(data).filter(([field]) => field !== 'key' && field !== 'slot'));
296
+ return await this.#manager.send(request);
297
+ }));
298
+ for (const result of results) {
299
+ if (result.status === 'rejected') {
300
+ this.#reportSendItError(result.reason);
301
+ }
302
+ }
303
+ }
304
+ }
305
+ //# sourceMappingURL=timekeeper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timekeeper.js","sourceRoot":"","sources":["../src/timekeeper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Clock } from 'pg-boss';
2
+ export type ClockDelay = Promise<void> & {
3
+ abort(): void;
4
+ };
5
+ export declare class Timers {
6
+ static delay(clock: Clock, ms: number, error?: string, abortController?: AbortController): ClockDelay;
7
+ static resolveWithinSeconds<T>(clock: Clock, promise: Promise<T>, seconds: number, message: string, abortController: AbortController): Promise<T | void>;
8
+ }
9
+ //# sourceMappingURL=timers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timers.d.ts","sourceRoot":"","sources":["../src/timers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGrC,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG;IAAE,KAAK,IAAI,IAAI,CAAA;CAAE,CAAC;AAI3D,qBAAa,MAAM;IAEjB,MAAM,CAAC,KAAK,CACV,KAAK,EAAE,KAAK,EACZ,EAAE,EAAE,MAAM,EACV,KAAK,CAAC,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,eAAe,GAChC,UAAU;WAiCA,oBAAoB,CAAC,CAAC,EACjC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CASrB"}
package/dist/timers.js ADDED
@@ -0,0 +1,50 @@
1
+ // pg-boss's waits, scheduled on the configured clock so a test clock fires
2
+ // them (node_modules/pg-boss/dist/tools.js:32-77).
3
+ export class Timers {
4
+ // clockDelay: rejects with `error` when the wait runs out, if given.
5
+ static delay(clock, ms, error, abortController) {
6
+ const controller = abortController ?? new AbortController();
7
+ const promise = new Promise((resolve, reject) => {
8
+ const handle = clock.setTimeout(() => {
9
+ controller.signal.removeEventListener('abort', onAbort);
10
+ if (error) {
11
+ reject(new Error(error));
12
+ }
13
+ else {
14
+ resolve();
15
+ }
16
+ }, ms);
17
+ const onAbort = () => {
18
+ clock.clearTimeout(handle);
19
+ resolve();
20
+ };
21
+ if (controller.signal.aborted) {
22
+ onAbort();
23
+ }
24
+ else {
25
+ controller.signal.addEventListener('abort', onAbort, { once: true });
26
+ }
27
+ });
28
+ return Object.assign(promise, {
29
+ abort: () => {
30
+ if (!controller.signal.aborted) {
31
+ controller.abort();
32
+ }
33
+ },
34
+ });
35
+ }
36
+ // Races `promise` against `seconds` (at least one), rejecting with
37
+ // `message` when they run out. Settling aborts the controller either way,
38
+ // which is how the deadline cancels itself.
39
+ static async resolveWithinSeconds(clock, promise, seconds, message, abortController) {
40
+ const timeout = Math.max(1, seconds) * 1000;
41
+ const deadline = Timers.delay(clock, timeout, message, abortController);
42
+ try {
43
+ return await Promise.race([promise, deadline]);
44
+ }
45
+ finally {
46
+ deadline.abort();
47
+ }
48
+ }
49
+ }
50
+ //# sourceMappingURL=timers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timers.js","sourceRoot":"","sources":["../src/timers.ts"],"names":[],"mappings":"AAKA,2EAA2E;AAC3E,mDAAmD;AACnD,MAAM,OAAO,MAAM;IACjB,qEAAqE;IACrE,MAAM,CAAC,KAAK,CACV,KAAY,EACZ,EAAU,EACV,KAAc,EACd,eAAiC;QAEjC,MAAM,UAAU,GAAG,eAAe,IAAI,IAAI,eAAe,EAAE,CAAC;QAC5D,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACpD,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE;gBACnC,UAAU,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACxD,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACN,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC,EAAE,EAAE,CAAC,CAAC;YACP,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;gBAC3B,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YACF,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC;YACZ,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACvE,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;YAC5B,KAAK,EAAE,GAAG,EAAE;gBACV,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBAC/B,UAAU,CAAC,KAAK,EAAE,CAAC;gBACrB,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,mEAAmE;IACnE,0EAA0E;IAC1E,4CAA4C;IAC5C,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAC/B,KAAY,EACZ,OAAmB,EACnB,OAAe,EACf,OAAe,EACf,eAAgC;QAEhC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;QAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC;QACxE,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QACjD,CAAC;gBAAS,CAAC;YACT,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;CACF"}
package/dist/uuid.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export declare class Uuid {
2
+ #private;
3
+ static canonical(text: string): string;
4
+ static fromPayload(value: unknown): string;
5
+ static parameter(value: unknown): string | null;
6
+ }
7
+ //# sourceMappingURL=uuid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAMA,qBAAa,IAAI;;IAIf,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAuBtC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAQ1C,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;CAGhD"}
package/dist/uuid.js ADDED
@@ -0,0 +1,36 @@
1
+ import { DatabaseError } from "./database-error.js";
2
+ // Postgres's uuid input, which pg-boss binds every job id through: 32 hex
3
+ // digits in either case, a hyphen allowed after any group of four but the
4
+ // last, optionally in braces, printed lower case as 8-4-4-4-12; any other
5
+ // text is 22P02 (https://www.postgresql.org/docs/current/datatype-uuid.html).
6
+ export class Uuid {
7
+ static #input = /^(?:\{([0-9a-f]{4}(?:-?[0-9a-f]{4}){7})\}|([0-9a-f]{4}(?:-?[0-9a-f]{4}){7}))$/i;
8
+ static canonical(text) {
9
+ const match = Uuid.#input.exec(text);
10
+ const digits = (match?.[1] ?? match?.[2])
11
+ ?.replaceAll('-', '')
12
+ .toLowerCase();
13
+ if (digits === undefined) {
14
+ throw new DatabaseError(`invalid input syntax for type uuid: "${text}"`, '22P02');
15
+ }
16
+ return [
17
+ digits.slice(0, 8),
18
+ digits.slice(8, 12),
19
+ digits.slice(12, 16),
20
+ digits.slice(16, 20),
21
+ digits.slice(20),
22
+ ].join('-');
23
+ }
24
+ // send()'s id, which pg-boss's insert reads out of its JSON payload
25
+ // (node_modules/pg-boss/dist/plans.js:2303): a string as it is, anything
26
+ // else as its JSON text.
27
+ static fromPayload(value) {
28
+ return Uuid.canonical(typeof value === 'string' ? value : JSON.stringify(value));
29
+ }
30
+ // findJobs()'s id, bound as a parameter (plans.js:3694): null is SQL NULL,
31
+ // which matches no job.
32
+ static parameter(value) {
33
+ return value === null ? null : Uuid.canonical(String(value));
34
+ }
35
+ }
36
+ //# sourceMappingURL=uuid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,8EAA8E;AAC9E,MAAM,OAAO,IAAI;IACf,MAAM,CAAU,MAAM,GACpB,gFAAgF,CAAC;IAEnF,MAAM,CAAC,SAAS,CAAC,IAAY;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YACvC,EAAE,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC;aACpB,WAAW,EAAE,CAAC;QACjB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,aAAa,CACrB,wCAAwC,IAAI,GAAG,EAC/C,OAAO,CACR,CAAC;QACJ,CAAC;QACD,OAAO;YACL,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;YAClB,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;YACnB,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;SACjB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACd,CAAC;IAED,oEAAoE;IACpE,yEAAyE;IACzE,yBAAyB;IACzB,MAAM,CAAC,WAAW,CAAC,KAAc;QAC/B,OAAO,IAAI,CAAC,SAAS,CACnB,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAC1D,CAAC;IACJ,CAAC;IAED,2EAA2E;IAC3E,wBAAwB;IACxB,MAAM,CAAC,SAAS,CAAC,KAAc;QAC7B,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { Clock, WipData, WorkOptions } from 'pg-boss';
2
+ export type WorkerOptions = WorkOptions & {
3
+ pollingInterval: number;
4
+ notifyPollingInterval: number;
5
+ };
6
+ export interface WorkerConfig<TJob> {
7
+ id: string;
8
+ workId: string;
9
+ name: string;
10
+ options: WorkerOptions;
11
+ clock: Clock;
12
+ resolveInterval: (lastFetchCount: number) => number;
13
+ fetch: () => Promise<TJob[]>;
14
+ onFetch: (jobs: TJob[]) => Promise<void>;
15
+ onError: (error: Error) => void;
16
+ }
17
+ export declare class Worker<TJob> {
18
+ #private;
19
+ readonly id: string;
20
+ readonly workId: string;
21
+ readonly name: string;
22
+ readonly options: WorkerOptions;
23
+ readonly createdOn: number;
24
+ jobs: TJob[];
25
+ abortController: AbortController | null;
26
+ constructor(config: WorkerConfig<TJob>);
27
+ get stopping(): boolean;
28
+ get stopped(): boolean;
29
+ start(): void;
30
+ notify(): void;
31
+ abort(): void;
32
+ stop(): Promise<void>;
33
+ toWipData(): WipData;
34
+ }
35
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAe,MAAM,SAAS,CAAC;AAQxE,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,MAAM,WAAW,YAAY,CAAC,IAAI;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,MAAM,CAAC;IACpD,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7B,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAKD,qBAAa,MAAM,CAAC,IAAI;;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,IAAI,EAAE,IAAI,EAAE,CAAM;IAElB,eAAe,EAAE,eAAe,GAAG,IAAI,CAAQ;gBAqBnC,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC;IAatC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAKD,KAAK,IAAI,IAAI;IAsDb,MAAM,IAAI,IAAI;IAMd,KAAK,IAAI,IAAI;IASP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAa3B,SAAS,IAAI,OAAO;CAiBrB"}
package/dist/worker.js ADDED
@@ -0,0 +1,147 @@
1
+ import { Latch } from '@zukhruf/async';
2
+ import { Timers } from "./timers.js";
3
+ // One polling loop of a work() call, which starts one per localConcurrency
4
+ // (node_modules/pg-boss/dist/worker.js:53-114): fetch at once, hand the jobs
5
+ // over, then wait out the interval on the clock unless notified meanwhile.
6
+ export class Worker {
7
+ id;
8
+ // The id work() returned, shared by every loop it started.
9
+ workId;
10
+ name;
11
+ options;
12
+ createdOn;
13
+ // The batch in flight, which a shutdown fails (manager.js:884-909).
14
+ jobs = [];
15
+ // The batch's controller while it runs, which a shutdown aborts.
16
+ abortController = null;
17
+ #clock;
18
+ #resolveInterval;
19
+ #fetch;
20
+ #onFetch;
21
+ #onError;
22
+ #state = 'created';
23
+ // pg-boss's #beenNotified, cleared as each round starts: notify() counts,
24
+ // and a round was notified when the count moved since it started.
25
+ #notifications = 0;
26
+ // Aborts the round's wait; a fresh one arms each wait.
27
+ #wake = new AbortController();
28
+ // How the loop ended, which stop() waits for.
29
+ #ran = new Latch();
30
+ #lastFetchedOn = null;
31
+ #lastJobStartedOn = null;
32
+ #lastJobEndedOn = null;
33
+ #lastJobDuration = null;
34
+ #lastError = null;
35
+ #lastErrorOn = null;
36
+ constructor(config) {
37
+ this.id = config.id;
38
+ this.workId = config.workId;
39
+ this.name = config.name;
40
+ this.options = config.options;
41
+ this.#clock = config.clock;
42
+ this.createdOn = config.clock.now();
43
+ this.#resolveInterval = config.resolveInterval;
44
+ this.#fetch = config.fetch;
45
+ this.#onFetch = config.onFetch;
46
+ this.#onError = config.onError;
47
+ }
48
+ get stopping() {
49
+ return this.#state === 'stopping';
50
+ }
51
+ get stopped() {
52
+ return this.#state === 'stopped';
53
+ }
54
+ // pg-boss keeps the loop's promise for stop() to await (worker.js:51, 113):
55
+ // a loop that rejects, for example on an error emit with no listener,
56
+ // rejects the stop that waits for it, and is unhandled when no stop waits.
57
+ start() {
58
+ void this.#run().then(() => this.#ran.open({ ok: true }), (error) => {
59
+ this.#ran.open({ ok: false, error });
60
+ if (!this.stopping) {
61
+ throw error;
62
+ }
63
+ });
64
+ }
65
+ async #run() {
66
+ // A stop() before start() leaves the loop at once.
67
+ if (this.#state === 'created') {
68
+ this.#state = 'active';
69
+ }
70
+ while (!this.stopping) {
71
+ const started = this.#clock.now();
72
+ // Stays 0 on an error, so a failed fetch backs off to the normal poll.
73
+ let fetchedCount = 0;
74
+ const notifications = this.#notifications;
75
+ try {
76
+ const jobs = await this.#fetch();
77
+ this.#lastFetchedOn = this.#clock.now();
78
+ fetchedCount = jobs.length;
79
+ this.jobs = jobs;
80
+ this.#lastJobStartedOn = this.#lastFetchedOn;
81
+ await this.#onFetch(jobs);
82
+ this.#lastJobEndedOn = this.#clock.now();
83
+ this.jobs = [];
84
+ }
85
+ catch (caught) {
86
+ this.#lastErrorOn = this.#clock.now();
87
+ const error = caught instanceof Error ? caught : new Error(String(caught));
88
+ this.#lastError = error;
89
+ error.message = `${error.message} (Queue: ${this.name}, Worker: ${this.id})`;
90
+ this.#onError(error);
91
+ }
92
+ const duration = this.#clock.now() - started;
93
+ this.#lastJobDuration = duration;
94
+ const remaining = this.#resolveInterval(fetchedCount) - duration;
95
+ if (!this.stopping &&
96
+ this.#notifications === notifications &&
97
+ remaining > 100) {
98
+ this.#wake = new AbortController();
99
+ await Timers.delay(this.#clock, remaining, undefined, this.#wake);
100
+ }
101
+ }
102
+ this.#state = 'stopped';
103
+ }
104
+ notify() {
105
+ this.#notifications += 1;
106
+ this.#wake.abort();
107
+ }
108
+ // worker.js:115-122
109
+ abort() {
110
+ if (!this.abortController) {
111
+ return;
112
+ }
113
+ if (!this.abortController.signal.aborted) {
114
+ this.abortController.abort();
115
+ }
116
+ }
117
+ async stop() {
118
+ if (this.stopped) {
119
+ return;
120
+ }
121
+ this.#state = 'stopping';
122
+ this.#wake.abort();
123
+ const ran = await this.#ran.wait();
124
+ if (!ran.ok) {
125
+ throw ran.error;
126
+ }
127
+ }
128
+ // worker.js:123-139
129
+ toWipData() {
130
+ return {
131
+ id: this.id,
132
+ workId: this.workId,
133
+ name: this.name,
134
+ options: this.options,
135
+ state: this.#state,
136
+ count: this.jobs.length,
137
+ createdOn: this.createdOn,
138
+ lastFetchedOn: this.#lastFetchedOn,
139
+ lastJobStartedOn: this.#lastJobStartedOn,
140
+ lastJobEndedOn: this.#lastJobEndedOn,
141
+ lastError: this.#lastError,
142
+ lastErrorOn: this.#lastErrorOn,
143
+ lastJobDuration: this.#lastJobDuration,
144
+ };
145
+ }
146
+ }
147
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}