@blokjs/trigger-worker 0.2.1 → 0.6.1

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 (50) hide show
  1. package/__tests__/integration/nats-adapter.real-nats.test.ts +116 -0
  2. package/__tests__/integration/pgboss-adapter.real-pg.test.ts +164 -0
  3. package/__tests__/integration/rabbitmq-adapter.real-rabbitmq.test.ts +179 -0
  4. package/__tests__/integration/sqs-adapter.real-sqs.test.ts +228 -0
  5. package/dist/WorkerTrigger.d.ts +40 -4
  6. package/dist/WorkerTrigger.js +272 -40
  7. package/dist/adapters/BullMQAdapter.d.ts +1 -1
  8. package/dist/adapters/BullMQAdapter.js +5 -42
  9. package/dist/adapters/InMemoryAdapter.d.ts +1 -1
  10. package/dist/adapters/InMemoryAdapter.js +13 -12
  11. package/dist/adapters/KafkaAdapter.d.ts +62 -0
  12. package/dist/adapters/KafkaAdapter.js +236 -0
  13. package/dist/adapters/NATSAdapter.d.ts +110 -0
  14. package/dist/adapters/NATSAdapter.js +394 -0
  15. package/dist/adapters/PgBossAdapter.d.ts +56 -0
  16. package/dist/adapters/PgBossAdapter.js +251 -0
  17. package/dist/adapters/RabbitMQAdapter.d.ts +51 -0
  18. package/dist/adapters/RabbitMQAdapter.js +241 -0
  19. package/dist/adapters/RedisStreamsAdapter.d.ts +64 -0
  20. package/dist/adapters/RedisStreamsAdapter.js +240 -0
  21. package/dist/adapters/SQSAdapter.d.ts +61 -0
  22. package/dist/adapters/SQSAdapter.js +269 -0
  23. package/dist/adapters/factory.d.ts +34 -0
  24. package/dist/adapters/factory.js +103 -0
  25. package/dist/index.d.ts +25 -7
  26. package/dist/index.js +31 -16
  27. package/package.json +27 -5
  28. package/src/WorkerTrigger.test.ts +44 -14
  29. package/src/WorkerTrigger.ts +299 -27
  30. package/src/adapters/InMemoryAdapter.ts +9 -5
  31. package/src/adapters/KafkaAdapter.ts +277 -0
  32. package/src/adapters/NATSAdapter.ts +454 -0
  33. package/src/adapters/PgBossAdapter.ts +293 -0
  34. package/src/adapters/RabbitMQAdapter.ts +285 -0
  35. package/src/adapters/RedisStreamsAdapter.ts +286 -0
  36. package/src/adapters/SQSAdapter.ts +306 -0
  37. package/src/adapters/factory.test.ts +89 -0
  38. package/src/adapters/factory.ts +111 -0
  39. package/src/adapters/new-adapters.test.ts +130 -0
  40. package/src/index.ts +31 -4
  41. package/template/.env.example +13 -0
  42. package/template/package.json +45 -0
  43. package/template/src/Nodes.ts +10 -0
  44. package/template/src/Workflows.ts +8 -0
  45. package/template/src/index.ts +41 -0
  46. package/template/src/runner/WorkerServer.ts +34 -0
  47. package/template/src/runner/types/Workflows.ts +7 -0
  48. package/template/src/workflows/jobs/process-job.ts +47 -0
  49. package/template/tsconfig.json +31 -0
  50. package/template/vitest.config.ts +39 -0
@@ -0,0 +1,394 @@
1
+ /**
2
+ * NATSAdapter - NATS JetStream worker adapter for WorkerTrigger
3
+ *
4
+ * Uses NATS JetStream for persistent background job processing with:
5
+ * - Pull-based consumers with configurable concurrency
6
+ * - Server-side retry config (max_deliver)
7
+ * - Ack wait for job timeouts
8
+ * - Priority via message headers
9
+ * - Delayed job scheduling
10
+ * - Queue statistics via consumer info
11
+ *
12
+ * Requires: npm install nats
13
+ *
14
+ * Environment variables:
15
+ * - NATS_SERVERS: Comma-separated NATS server URLs (default: localhost:4222)
16
+ * - NATS_TOKEN: Authentication token (optional)
17
+ * - NATS_USER: Username for auth (optional)
18
+ * - NATS_PASS: Password for auth (optional)
19
+ * - NATS_STREAM_NAME: JetStream stream name (default: blok-worker)
20
+ */
21
+ import { v4 as uuid } from "uuid";
22
+ /**
23
+ * Tier 2 polish — compute the consumer-side hold time for a NATS message
24
+ * with an `x-delay` header. NATS JetStream stores `x-delay` as opaque
25
+ * metadata; the broker does NOT defer delivery on it. The consumer is
26
+ * responsible for honouring the delay between the message's first-publish
27
+ * timestamp and `createdMs + delay`.
28
+ *
29
+ * Returns the milliseconds to wait. Clamps to >= 0; returns 0 when the
30
+ * delay has already elapsed (the message was queued for longer than the
31
+ * delay) or when no delay was set.
32
+ *
33
+ * Exported for unit testability — the consumer message handler in
34
+ * `NATSWorkerAdapter.process()` mocks the NATS client extensively, so
35
+ * isolating the math here keeps the surface easy to verify.
36
+ */
37
+ export function computeXDelayHoldMs(delay, createdMs, nowMs) {
38
+ if (!delay || delay <= 0)
39
+ return 0;
40
+ const dispatchAt = createdMs + delay;
41
+ return Math.max(0, dispatchAt - nowMs);
42
+ }
43
+ /**
44
+ * NATSWorkerAdapter - NATS JetStream implementation of WorkerAdapter
45
+ */
46
+ export class NATSWorkerAdapter {
47
+ provider = "nats";
48
+ // biome-ignore lint/suspicious/noExplicitAny: NATS types are dynamically imported (optional peer dependency)
49
+ nc = null;
50
+ // biome-ignore lint/suspicious/noExplicitAny: NATS types are dynamically imported
51
+ js = null;
52
+ // biome-ignore lint/suspicious/noExplicitAny: NATS types are dynamically imported
53
+ jsm = null;
54
+ connected = false;
55
+ config;
56
+ // biome-ignore lint/suspicious/noExplicitAny: NATS consumer instances
57
+ consumers = new Map();
58
+ // biome-ignore lint/suspicious/noExplicitAny: NATS consume iterators
59
+ consumeIterators = new Map();
60
+ constructor(config) {
61
+ this.config = {
62
+ servers: config?.servers || (process.env.NATS_SERVERS || "localhost:4222").split(","),
63
+ token: config?.token || process.env.NATS_TOKEN,
64
+ user: config?.user || process.env.NATS_USER,
65
+ pass: config?.pass || process.env.NATS_PASS,
66
+ streamName: config?.streamName || process.env.NATS_STREAM_NAME || "blok-worker",
67
+ };
68
+ }
69
+ /**
70
+ * Connect to NATS and initialize JetStream
71
+ */
72
+ async connect() {
73
+ if (this.connected)
74
+ return;
75
+ try {
76
+ const nats = await import("nats");
77
+ const connectOpts = {
78
+ servers: this.config.servers,
79
+ };
80
+ if (this.config.token)
81
+ connectOpts.token = this.config.token;
82
+ if (this.config.user)
83
+ connectOpts.user = this.config.user;
84
+ if (this.config.pass)
85
+ connectOpts.pass = this.config.pass;
86
+ this.nc = await nats.connect(connectOpts);
87
+ this.js = this.nc.jetstream();
88
+ this.jsm = await this.nc.jetstreamManager();
89
+ this.connected = true;
90
+ console.log(`[NATSWorkerAdapter] Connected to NATS: ${this.config.servers.join(", ")}`);
91
+ }
92
+ catch (error) {
93
+ throw new Error(`Failed to connect to NATS: ${error.message}. Make sure nats is installed: npm install nats`);
94
+ }
95
+ }
96
+ /**
97
+ * Disconnect from NATS
98
+ */
99
+ async disconnect() {
100
+ if (!this.connected)
101
+ return;
102
+ try {
103
+ // Stop all consume iterators
104
+ for (const [, iter] of this.consumeIterators) {
105
+ try {
106
+ iter.stop();
107
+ }
108
+ catch {
109
+ // Iterator may already be stopped
110
+ }
111
+ }
112
+ this.consumeIterators.clear();
113
+ this.consumers.clear();
114
+ await this.nc.drain();
115
+ this.connected = false;
116
+ console.log("[NATSWorkerAdapter] Disconnected from NATS");
117
+ }
118
+ catch (error) {
119
+ console.error(`[NATSWorkerAdapter] Disconnect error: ${error.message}`);
120
+ }
121
+ }
122
+ /**
123
+ * Start processing jobs from a queue
124
+ */
125
+ async process(config, handler) {
126
+ if (!this.connected) {
127
+ throw new Error("Not connected. Call connect() first.");
128
+ }
129
+ const nats = await import("nats");
130
+ const queue = config.queue;
131
+ const streamName = this.config.streamName || "blok-worker";
132
+ const subject = `worker.${queue}`;
133
+ const durableName = `blok-worker-${queue}`;
134
+ // Ensure stream exists with worker subjects
135
+ await this.ensureStream(streamName, [subject]);
136
+ // Create or update durable pull consumer with worker semantics
137
+ const ackWaitNs = ((config.timeout ?? 30000) + 5000) * 1_000_000; // timeout + 5s buffer, in nanoseconds
138
+ await this.jsm.consumers.add(streamName, {
139
+ durable_name: durableName,
140
+ ack_policy: nats.AckPolicy.Explicit,
141
+ max_deliver: (config.retries ?? 3) + 1, // +1 because first attempt counts
142
+ ack_wait: ackWaitNs,
143
+ filter_subjects: [subject],
144
+ });
145
+ // Get consumer handle
146
+ const consumer = await this.js.consumers.get(streamName, durableName);
147
+ this.consumers.set(queue, consumer);
148
+ // Start consuming
149
+ const iter = await consumer.consume();
150
+ this.consumeIterators.set(queue, iter);
151
+ // Process jobs in background
152
+ (async () => {
153
+ const semaphore = new Semaphore(config.concurrency ?? 1);
154
+ for await (const msg of iter) {
155
+ await semaphore.acquire();
156
+ // Process each job concurrently up to concurrency limit
157
+ (async () => {
158
+ try {
159
+ // Parse job data
160
+ let data;
161
+ try {
162
+ const codec = nats.JSONCodec();
163
+ data = codec.decode(msg.data);
164
+ }
165
+ catch {
166
+ try {
167
+ const sc = nats.StringCodec();
168
+ data = JSON.parse(sc.decode(msg.data));
169
+ }
170
+ catch {
171
+ data = msg.data;
172
+ }
173
+ }
174
+ // Extract headers
175
+ const headers = {};
176
+ if (msg.headers) {
177
+ for (const [key, values] of msg.headers) {
178
+ headers[key] = Array.isArray(values) ? values[0] : values;
179
+ }
180
+ }
181
+ // Extract job metadata from headers
182
+ const jobId = headers["x-job-id"] || msg.headers?.get("Nats-Msg-Id") || uuid();
183
+ const priority = Number.parseInt(headers["x-priority"] || "0", 10);
184
+ const delay = Number.parseInt(headers["x-delay"] || "0", 10);
185
+ const timeout = Number.parseInt(headers["x-timeout"] || "0", 10);
186
+ // Get redelivery count (attempts)
187
+ const info = msg.info;
188
+ const attempts = info.redeliveryCount ?? 0;
189
+ const maxRetries = config.retries ?? 3;
190
+ // Create WorkerJob
191
+ const workerJob = {
192
+ id: jobId,
193
+ data,
194
+ headers,
195
+ queue,
196
+ priority,
197
+ attempts,
198
+ maxRetries,
199
+ createdAt: new Date(info.timestampNanos ? Math.floor(Number(info.timestampNanos) / 1_000_000) : Date.now()),
200
+ delay: delay || undefined,
201
+ timeout: timeout || config.timeout || undefined,
202
+ raw: msg,
203
+ complete: async () => {
204
+ msg.ack();
205
+ },
206
+ fail: async (error, requeue) => {
207
+ if (requeue) {
208
+ // nak() tells the server to redeliver
209
+ msg.nak();
210
+ }
211
+ else {
212
+ // term() terminates delivery — no more retries
213
+ msg.term();
214
+ }
215
+ },
216
+ };
217
+ // Tier 2 polish — enforce `x-delay` header on the consumer side.
218
+ // NATS JetStream stores `x-delay` as opaque metadata; the broker
219
+ // does NOT defer delivery on it. We implement consumer-side
220
+ // holding here. createdMs is the message's first-publish timestamp;
221
+ // hold until createdMs + delay. Single-process semantics — for
222
+ // long deferrals, prefer trigger-level `delay` (DeferredRunScheduler).
223
+ const createdMs = info.timestampNanos ? Math.floor(Number(info.timestampNanos) / 1_000_000) : Date.now();
224
+ const waitMs = computeXDelayHoldMs(delay, createdMs, Date.now());
225
+ if (waitMs > 0) {
226
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
227
+ }
228
+ await handler(workerJob);
229
+ }
230
+ catch (error) {
231
+ console.error(`[NATSWorkerAdapter] Error processing job from ${queue}: ${error.message}`);
232
+ try {
233
+ msg.nak();
234
+ }
235
+ catch {
236
+ // Already acked/nacked
237
+ }
238
+ }
239
+ finally {
240
+ semaphore.release();
241
+ }
242
+ })();
243
+ }
244
+ })();
245
+ console.log(`[NATSWorkerAdapter] Processing queue: ${queue} (concurrency=${config.concurrency ?? 1}, retries=${config.retries ?? 3})`);
246
+ }
247
+ /**
248
+ * Add a job to a worker queue
249
+ */
250
+ async addJob(queue, data, opts) {
251
+ if (!this.connected) {
252
+ throw new Error("Not connected. Call connect() first.");
253
+ }
254
+ const nats = await import("nats");
255
+ const subject = `worker.${queue}`;
256
+ const streamName = this.config.streamName || "blok-worker";
257
+ // Ensure stream has this subject
258
+ await this.ensureStream(streamName, [subject]);
259
+ // Build headers with job metadata
260
+ const hdrs = nats.headers();
261
+ const jobId = opts?.jobId || uuid();
262
+ hdrs.set("x-job-id", jobId);
263
+ hdrs.set("Nats-Msg-Id", jobId); // Deduplication
264
+ if (opts?.priority)
265
+ hdrs.set("x-priority", String(opts.priority));
266
+ if (opts?.delay)
267
+ hdrs.set("x-delay", String(opts.delay));
268
+ if (opts?.timeout)
269
+ hdrs.set("x-timeout", String(opts.timeout));
270
+ // Encode and publish
271
+ const codec = nats.JSONCodec();
272
+ await this.js.publish(subject, codec.encode(data), { headers: hdrs });
273
+ return jobId;
274
+ }
275
+ /**
276
+ * Stop processing a specific queue
277
+ */
278
+ async stopProcessing(queue) {
279
+ const iter = this.consumeIterators.get(queue);
280
+ if (iter) {
281
+ try {
282
+ iter.stop();
283
+ }
284
+ catch {
285
+ // Already stopped
286
+ }
287
+ this.consumeIterators.delete(queue);
288
+ }
289
+ this.consumers.delete(queue);
290
+ console.log(`[NATSWorkerAdapter] Stopped processing queue: ${queue}`);
291
+ }
292
+ /**
293
+ * Check if connected
294
+ */
295
+ isConnected() {
296
+ return this.connected;
297
+ }
298
+ /**
299
+ * Health check
300
+ */
301
+ async healthCheck() {
302
+ if (!this.connected || !this.nc)
303
+ return false;
304
+ try {
305
+ const info = this.nc.info;
306
+ return info !== undefined;
307
+ }
308
+ catch {
309
+ return false;
310
+ }
311
+ }
312
+ /**
313
+ * Get queue statistics from JetStream consumer info
314
+ */
315
+ async getQueueStats(queue) {
316
+ if (!this.connected) {
317
+ return { waiting: 0, active: 0, completed: 0, failed: 0, delayed: 0 };
318
+ }
319
+ try {
320
+ const streamName = this.config.streamName || "blok-worker";
321
+ const durableName = `blok-worker-${queue}`;
322
+ const info = await this.jsm.consumers.info(streamName, durableName);
323
+ return {
324
+ waiting: info.num_pending ?? 0,
325
+ active: info.num_ack_pending ?? 0,
326
+ completed: info.delivered?.consumer_seq ?? 0,
327
+ failed: info.num_redelivered ?? 0,
328
+ delayed: 0, // NATS doesn't have a native delayed count
329
+ };
330
+ }
331
+ catch {
332
+ return { waiting: 0, active: 0, completed: 0, failed: 0, delayed: 0 };
333
+ }
334
+ }
335
+ /**
336
+ * Ensure a JetStream stream exists with the given subjects
337
+ */
338
+ async ensureStream(name, subjects) {
339
+ try {
340
+ const info = await this.jsm.streams.info(name);
341
+ // Merge new subjects with existing
342
+ const existingSubjects = info.config.subjects || [];
343
+ const allSubjects = [...new Set([...existingSubjects, ...subjects])];
344
+ if (allSubjects.length !== existingSubjects.length) {
345
+ await this.jsm.streams.update(name, {
346
+ ...info.config,
347
+ subjects: allSubjects,
348
+ });
349
+ }
350
+ }
351
+ catch {
352
+ // Stream doesn't exist, create it
353
+ await this.jsm.streams.add({
354
+ name,
355
+ subjects,
356
+ // biome-ignore lint/suspicious/noExplicitAny: nats JetStream retention policy enum
357
+ retention: "workqueue",
358
+ max_deliver: 4, // default: 3 retries + 1 initial attempt
359
+ // biome-ignore lint/suspicious/noExplicitAny: nats JetStream storage type enum
360
+ storage: "file",
361
+ });
362
+ }
363
+ }
364
+ }
365
+ /**
366
+ * Simple semaphore for concurrency control
367
+ */
368
+ class Semaphore {
369
+ permits;
370
+ waiting = [];
371
+ constructor(permits) {
372
+ this.permits = permits;
373
+ }
374
+ async acquire() {
375
+ if (this.permits > 0) {
376
+ this.permits--;
377
+ return;
378
+ }
379
+ return new Promise((resolve) => {
380
+ this.waiting.push(resolve);
381
+ });
382
+ }
383
+ release() {
384
+ const next = this.waiting.shift();
385
+ if (next) {
386
+ next();
387
+ }
388
+ else {
389
+ this.permits++;
390
+ }
391
+ }
392
+ }
393
+ export default NATSWorkerAdapter;
394
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTkFUU0FkYXB0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWRhcHRlcnMvTkFUU0FkYXB0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFHSCxPQUFPLEVBQUUsRUFBRSxJQUFJLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUdsQzs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxLQUFhLEVBQUUsU0FBaUIsRUFBRSxLQUFhO0lBQ2xGLElBQUksQ0FBQyxLQUFLLElBQUksS0FBSyxJQUFJLENBQUM7UUFBRSxPQUFPLENBQUMsQ0FBQztJQUNuQyxNQUFNLFVBQVUsR0FBRyxTQUFTLEdBQUcsS0FBSyxDQUFDO0lBQ3JDLE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsVUFBVSxHQUFHLEtBQUssQ0FBQyxDQUFDO0FBQ3hDLENBQUM7QUFrQkQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8saUJBQWlCO0lBQ3BCLFFBQVEsR0FBRyxNQUFlLENBQUM7SUFFcEMsNkdBQTZHO0lBQ3JHLEVBQUUsR0FBUSxJQUFJLENBQUM7SUFDdkIsa0ZBQWtGO0lBQzFFLEVBQUUsR0FBUSxJQUFJLENBQUM7SUFDdkIsa0ZBQWtGO0lBQzFFLEdBQUcsR0FBUSxJQUFJLENBQUM7SUFDaEIsU0FBUyxHQUFHLEtBQUssQ0FBQztJQUNsQixNQUFNLENBQW1CO0lBQ2pDLHNFQUFzRTtJQUM5RCxTQUFTLEdBQXFCLElBQUksR0FBRyxFQUFFLENBQUM7SUFDaEQscUVBQXFFO0lBQzdELGdCQUFnQixHQUFxQixJQUFJLEdBQUcsRUFBRSxDQUFDO0lBRXZELFlBQVksTUFBa0M7UUFDN0MsSUFBSSxDQUFDLE1BQU0sR0FBRztZQUNiLE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLElBQUksZ0JBQWdC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LENBQU8sQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUNwQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUM1QixDQUFDLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRCxPQUFPO1FBQ04sTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNsQyxJQUFJLElBQUksRUFBRSxDQUFDO1lBQ1YsSUFBSSxFQUFFLENBQUM7UUFDUixDQUFDO2FBQU0sQ0FBQztZQUNQLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNoQixDQUFDO0lBQ0YsQ0FBQztDQUNEO0FBRUQsZUFBZSxpQkFBaUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTkFUU0FkYXB0ZXIgLSBOQVRTIEpldFN0cmVhbSB3b3JrZXIgYWRhcHRlciBmb3IgV29ya2VyVHJpZ2dlclxuICpcbiAqIFVzZXMgTkFUUyBKZXRTdHJlYW0gZm9yIHBlcnNpc3RlbnQgYmFja2dyb3VuZCBqb2IgcHJvY2Vzc2luZyB3aXRoOlxuICogLSBQdWxsLWJhc2VkIGNvbnN1bWVycyB3aXRoIGNvbmZpZ3VyYWJsZSBjb25jdXJyZW5jeVxuICogLSBTZXJ2ZXItc2lkZSByZXRyeSBjb25maWcgKG1heF9kZWxpdmVyKVxuICogLSBBY2sgd2FpdCBmb3Igam9iIHRpbWVvdXRzXG4gKiAtIFByaW9yaXR5IHZpYSBtZXNzYWdlIGhlYWRlcnNcbiAqIC0gRGVsYXllZCBqb2Igc2NoZWR1bGluZ1xuICogLSBRdWV1ZSBzdGF0aXN0aWNzIHZpYSBjb25zdW1lciBpbmZvXG4gKlxuICogUmVxdWlyZXM6IG5wbSBpbnN0YWxsIG5hdHNcbiAqXG4gKiBFbnZpcm9ubWVudCB2YXJpYWJsZXM6XG4gKiAtIE5BVFNfU0VSVkVSUzogQ29tbWEtc2VwYXJhdGVkIE5BVFMgc2VydmVyIFVSTHMgKGRlZmF1bHQ6IGxvY2FsaG9zdDo0MjIyKVxuICogLSBOQVRTX1RPS0VOOiBBdXRoZW50aWNhdGlvbiB0b2tlbiAob3B0aW9uYWwpXG4gKiAtIE5BVFNfVVNFUjogVXNlcm5hbWUgZm9yIGF1dGggKG9wdGlvbmFsKVxuICogLSBOQVRTX1BBU1M6IFBhc3N3b3JkIGZvciBhdXRoIChvcHRpb25hbClcbiAqIC0gTkFUU19TVFJFQU1fTkFNRTogSmV0U3RyZWFtIHN0cmVhbSBuYW1lIChkZWZhdWx0OiBibG9rLXdvcmtlcilcbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFdvcmtlclRyaWdnZXJPcHRzIH0gZnJvbSBcIkBibG9ranMvaGVscGVyXCI7XG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSBcInV1aWRcIjtcbmltcG9ydCB0eXBlIHsgV29ya2VyQWRhcHRlciwgV29ya2VySm9iLCBXb3JrZXJRdWV1ZVN0YXRzIH0gZnJvbSBcIi4uL1dvcmtlclRyaWdnZXJcIjtcblxuLyoqXG4gKiBUaWVyIDIgcG9saXNoIOKAlCBjb21wdXRlIHRoZSBjb25zdW1lci1zaWRlIGhvbGQgdGltZSBmb3IgYSBOQVRTIG1lc3NhZ2VcbiAqIHdpdGggYW4gYHgtZGVsYXlgIGhlYWRlci4gTkFUUyBKZXRTdHJlYW0gc3RvcmVzIGB4LWRlbGF5YCBhcyBvcGFxdWVcbiAqIG1ldGFkYXRhOyB0aGUgYnJva2VyIGRvZXMgTk9UIGRlZmVyIGRlbGl2ZXJ5IG9uIGl0LiBUaGUgY29uc3VtZXIgaXNcbiAqIHJlc3BvbnNpYmxlIGZvciBob25vdXJpbmcgdGhlIGRlbGF5IGJldHdlZW4gdGhlIG1lc3NhZ2UncyBmaXJzdC1wdWJsaXNoXG4gKiB0aW1lc3RhbXAgYW5kIGBjcmVhdGVkTXMgKyBkZWxheWAuXG4gKlxuICogUmV0dXJucyB0aGUgbWlsbGlzZWNvbmRzIHRvIHdhaXQuIENsYW1wcyB0byA+PSAwOyByZXR1cm5zIDAgd2hlbiB0aGVcbiAqIGRlbGF5IGhhcyBhbHJlYWR5IGVsYXBzZWQgKHRoZSBtZXNzYWdlIHdhcyBxdWV1ZWQgZm9yIGxvbmdlciB0aGFuIHRoZVxuICogZGVsYXkpIG9yIHdoZW4gbm8gZGVsYXkgd2FzIHNldC5cbiAqXG4gKiBFeHBvcnRlZCBmb3IgdW5pdCB0ZXN0YWJpbGl0eSDigJQgdGhlIGNvbnN1bWVyIG1lc3NhZ2UgaGFuZGxlciBpblxuICogYE5BVFNXb3JrZXJBZGFwdGVyLnByb2Nlc3MoKWAgbW9ja3MgdGhlIE5BVFMgY2xpZW50IGV4dGVuc2l2ZWx5LCBzb1xuICogaXNvbGF0aW5nIHRoZSBtYXRoIGhlcmUga2VlcHMgdGhlIHN1cmZhY2UgZWFzeSB0byB2ZXJpZnkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlWERlbGF5SG9sZE1zKGRlbGF5OiBudW1iZXIsIGNyZWF0ZWRNczogbnVtYmVyLCBub3dNczogbnVtYmVyKTogbnVtYmVyIHtcblx0aWYgKCFkZWxheSB8fCBkZWxheSA8PSAwKSByZXR1cm4gMDtcblx0Y29uc3QgZGlzcGF0Y2hBdCA9IGNyZWF0ZWRNcyArIGRlbGF5O1xuXHRyZXR1cm4gTWF0aC5tYXgoMCwgZGlzcGF0Y2hBdCAtIG5vd01zKTtcbn1cblxuLyoqXG4gKiBOQVRTIHdvcmtlciBhZGFwdGVyIGNvbmZpZ3VyYXRpb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBOQVRTV29ya2VyQ29uZmlnIHtcblx0LyoqIE5BVFMgc2VydmVyIFVSTHMgKi9cblx0c2VydmVyczogc3RyaW5nW107XG5cdC8qKiBBdXRoZW50aWNhdGlvbiB0b2tlbiAqL1xuXHR0b2tlbj86IHN0cmluZztcblx0LyoqIFVzZXJuYW1lICovXG5cdHVzZXI/OiBzdHJpbmc7XG5cdC8qKiBQYXNzd29yZCAqL1xuXHRwYXNzPzogc3RyaW5nO1xuXHQvKiogSmV0U3RyZWFtIHN0cmVhbSBuYW1lIChkZWZhdWx0OiBcImJsb2std29ya2VyXCIpICovXG5cdHN0cmVhbU5hbWU/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogTkFUU1dvcmtlckFkYXB0ZXIgLSBOQVRTIEpldFN0cmVhbSBpbXBsZW1lbnRhdGlvbiBvZiBXb3JrZXJBZGFwdGVyXG4gKi9cbmV4cG9ydCBjbGFzcyBOQVRTV29ya2VyQWRhcHRlciBpbXBsZW1lbnRzIFdvcmtlckFkYXB0ZXIge1xuXHRyZWFkb25seSBwcm92aWRlciA9IFwibmF0c1wiIGFzIGNvbnN0O1xuXG5cdC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9FeHBsaWNpdEFueTogTkFUUyB0eXBlcyBhcmUgZHluYW1pY2FsbHkgaW1wb3J0ZWQgKG9wdGlvbmFsIHBlZXIgZGVwZW5kZW5jeSlcblx0cHJpdmF0ZSBuYzogYW55ID0gbnVsbDtcblx0Ly8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBOQVRTIHR5cGVzIGFyZSBkeW5hbWljYWxseSBpbXBvcnRlZFxuXHRwcml2YXRlIGpzOiBhbnkgPSBudWxsO1xuXHQvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IE5BVFMgdHlwZXMgYXJlIGR5bmFtaWNhbGx5IGltcG9ydGVkXG5cdHByaXZhdGUganNtOiBhbnkgPSBudWxsO1xuXHRwcml2YXRlIGNvbm5lY3RlZCA9IGZhbHNlO1xuXHRwcml2YXRlIGNvbmZpZzogTkFUU1dvcmtlckNvbmZpZztcblx0Ly8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBOQVRTIGNvbnN1bWVyIGluc3RhbmNlc1xuXHRwcml2YXRlIGNvbnN1bWVyczogTWFwPHN0cmluZywgYW55PiA9IG5ldyBNYXAoKTtcblx0Ly8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBOQVRTIGNvbnN1bWUgaXRlcmF0b3JzXG5cdHByaXZhdGUgY29uc3VtZUl0ZXJhdG9yczogTWFwPHN0cmluZywgYW55PiA9IG5ldyBNYXAoKTtcblxuXHRjb25zdHJ1Y3Rvcihjb25maWc/OiBQYXJ0aWFsPE5BVFNXb3JrZXJDb25maWc+KSB7XG5cdFx0dGhpcy5jb25maWcgPSB7XG5cdFx0XHRzZXJ2ZXJzOiBjb25maWc/LnNlcnZlcnMgfHwgKHByb2Nlc3MuZW52Lk5BVFNfU0VSVkVSUyB8fCBcImxvY2FsaG9zdDo0MjIyXCIpLnNwbGl0KFwiLFwiKSxcblx0XHRcdHRva2VuOiBjb25maWc/LnRva2VuIHx8IHByb2Nlc3MuZW52Lk5BVFNfVE9LRU4sXG5cdFx0XHR1c2VyOiBjb25maWc/LnVzZXIgfHwgcHJvY2Vzcy5lbnYuTkFUU19VU0VSLFxuXHRcdFx0cGFzczogY29uZmlnPy5wYXNzIHx8IHByb2Nlc3MuZW52Lk5BVFNfUEFTUyxcblx0XHRcdHN0cmVhbU5hbWU6IGNvbmZpZz8uc3RyZWFtTmFtZSB8fCBwcm9jZXNzLmVudi5OQVRTX1NUUkVBTV9OQU1FIHx8IFwiYmxvay13b3JrZXJcIixcblx0XHR9O1xuXHR9XG5cblx0LyoqXG5cdCAqIENvbm5lY3QgdG8gTkFUUyBhbmQgaW5pdGlhbGl6ZSBKZXRTdHJlYW1cblx0ICovXG5cdGFzeW5jIGNvbm5lY3QoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKHRoaXMuY29ubmVjdGVkKSByZXR1cm47XG5cblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgbmF0cyA9IGF3YWl0IGltcG9ydChcIm5hdHNcIik7XG5cblx0XHRcdGNvbnN0IGNvbm5lY3RPcHRzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHtcblx0XHRcdFx0c2VydmVyczogdGhpcy5jb25maWcuc2VydmVycyxcblx0XHRcdH07XG5cblx0XHRcdGlmICh0aGlzLmNvbmZpZy50b2tlbikgY29ubmVjdE9wdHMudG9rZW4gPSB0aGlzLmNvbmZpZy50b2tlbjtcblx0XHRcdGlmICh0aGlzLmNvbmZpZy51c2VyKSBjb25uZWN0T3B0cy51c2VyID0gdGhpcy5jb25maWcudXNlcjtcblx0XHRcdGlmICh0aGlzLmNvbmZpZy5wYXNzKSBjb25uZWN0T3B0cy5wYXNzID0gdGhpcy5jb25maWcucGFzcztcblxuXHRcdFx0dGhpcy5uYyA9IGF3YWl0IG5hdHMuY29ubmVjdChjb25uZWN0T3B0cyk7XG5cdFx0XHR0aGlzLmpzID0gdGhpcy5uYy5qZXRzdHJlYW0oKTtcblx0XHRcdHRoaXMuanNtID0gYXdhaXQgdGhpcy5uYy5qZXRzdHJlYW1NYW5hZ2VyKCk7XG5cblx0XHRcdHRoaXMuY29ubmVjdGVkID0gdHJ1ZTtcblx0XHRcdGNvbnNvbGUubG9nKGBbTkFUU1dvcmtlckFkYXB0ZXJdIENvbm5lY3RlZCB0byBOQVRTOiAke3RoaXMuY29uZmlnLnNlcnZlcnMuam9pbihcIiwgXCIpfWApO1xuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoXG5cdFx0XHRcdGBGYWlsZWQgdG8gY29ubmVjdCB0byBOQVRTOiAkeyhlcnJvciBhcyBFcnJvcikubWVzc2FnZX0uIE1ha2Ugc3VyZSBuYXRzIGlzIGluc3RhbGxlZDogbnBtIGluc3RhbGwgbmF0c2AsXG5cdFx0XHQpO1xuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBEaXNjb25uZWN0IGZyb20gTkFUU1xuXHQgKi9cblx0YXN5bmMgZGlzY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRpZiAoIXRoaXMuY29ubmVjdGVkKSByZXR1cm47XG5cblx0XHR0cnkge1xuXHRcdFx0Ly8gU3RvcCBhbGwgY29uc3VtZSBpdGVyYXRvcnNcblx0XHRcdGZvciAoY29uc3QgWywgaXRlcl0gb2YgdGhpcy5jb25zdW1lSXRlcmF0b3JzKSB7XG5cdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0aXRlci5zdG9wKCk7XG5cdFx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHRcdC8vIEl0ZXJhdG9yIG1heSBhbHJlYWR5IGJlIHN0b3BwZWRcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0dGhpcy5jb25zdW1lSXRlcmF0b3JzLmNsZWFyKCk7XG5cdFx0XHR0aGlzLmNvbnN1bWVycy5jbGVhcigpO1xuXG5cdFx0XHRhd2FpdCB0aGlzLm5jLmRyYWluKCk7XG5cdFx0XHR0aGlzLmNvbm5lY3RlZCA9IGZhbHNlO1xuXHRcdFx0Y29uc29sZS5sb2coXCJbTkFUU1dvcmtlckFkYXB0ZXJdIERpc2Nvbm5lY3RlZCBmcm9tIE5BVFNcIik7XG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdGNvbnNvbGUuZXJyb3IoYFtOQVRTV29ya2VyQWRhcHRlcl0gRGlzY29ubmVjdCBlcnJvcjogJHsoZXJyb3IgYXMgRXJyb3IpLm1lc3NhZ2V9YCk7XG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIFN0YXJ0IHByb2Nlc3Npbmcgam9icyBmcm9tIGEgcXVldWVcblx0ICovXG5cdGFzeW5jIHByb2Nlc3MoY29uZmlnOiBXb3JrZXJUcmlnZ2VyT3B0cywgaGFuZGxlcjogKGpvYjogV29ya2VySm9iKSA9PiBQcm9taXNlPHZvaWQ+KTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCkge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKFwiTm90IGNvbm5lY3RlZC4gQ2FsbCBjb25uZWN0KCkgZmlyc3QuXCIpO1xuXHRcdH1cblxuXHRcdGNvbnN0IG5hdHMgPSBhd2FpdCBpbXBvcnQoXCJuYXRzXCIpO1xuXHRcdGNvbnN0IHF1ZXVlID0gY29uZmlnLnF1ZXVlO1xuXHRcdGNvbnN0IHN0cmVhbU5hbWUgPSB0aGlzLmNvbmZpZy5zdHJlYW1OYW1lIHx8IFwiYmxvay13b3JrZXJcIjtcblx0XHRjb25zdCBzdWJqZWN0ID0gYHdvcmtlci4ke3F1ZXVlfWA7XG5cdFx0Y29uc3QgZHVyYWJsZU5hbWUgPSBgYmxvay13b3JrZXItJHtxdWV1ZX1gO1xuXG5cdFx0Ly8gRW5zdXJlIHN0cmVhbSBleGlzdHMgd2l0aCB3b3JrZXIgc3ViamVjdHNcblx0XHRhd2FpdCB0aGlzLmVuc3VyZVN0cmVhbShzdHJlYW1OYW1lLCBbc3ViamVjdF0pO1xuXG5cdFx0Ly8gQ3JlYXRlIG9yIHVwZGF0ZSBkdXJhYmxlIHB1bGwgY29uc3VtZXIgd2l0aCB3b3JrZXIgc2VtYW50aWNzXG5cdFx0Y29uc3QgYWNrV2FpdE5zID0gKChjb25maWcudGltZW91dCA/PyAzMDAwMCkgKyA1MDAwKSAqIDFfMDAwXzAwMDsgLy8gdGltZW91dCArIDVzIGJ1ZmZlciwgaW4gbmFub3NlY29uZHNcblx0XHRhd2FpdCB0aGlzLmpzbS5jb25zdW1lcnMuYWRkKHN0cmVhbU5hbWUsIHtcblx0XHRcdGR1cmFibGVfbmFtZTogZHVyYWJsZU5hbWUsXG5cdFx0XHRhY2tfcG9saWN5OiBuYXRzLkFja1BvbGljeS5FeHBsaWNpdCxcblx0XHRcdG1heF9kZWxpdmVyOiAoY29uZmlnLnJldHJpZXMgPz8gMykgKyAxLCAvLyArMSBiZWNhdXNlIGZpcnN0IGF0dGVtcHQgY291bnRzXG5cdFx0XHRhY2tfd2FpdDogYWNrV2FpdE5zLFxuXHRcdFx0ZmlsdGVyX3N1YmplY3RzOiBbc3ViamVjdF0sXG5cdFx0fSk7XG5cblx0XHQvLyBHZXQgY29uc3VtZXIgaGFuZGxlXG5cdFx0Y29uc3QgY29uc3VtZXIgPSBhd2FpdCB0aGlzLmpzLmNvbnN1bWVycy5nZXQoc3RyZWFtTmFtZSwgZHVyYWJsZU5hbWUpO1xuXHRcdHRoaXMuY29uc3VtZXJzLnNldChxdWV1ZSwgY29uc3VtZXIpO1xuXG5cdFx0Ly8gU3RhcnQgY29uc3VtaW5nXG5cdFx0Y29uc3QgaXRlciA9IGF3YWl0IGNvbnN1bWVyLmNvbnN1bWUoKTtcblx0XHR0aGlzLmNvbnN1bWVJdGVyYXRvcnMuc2V0KHF1ZXVlLCBpdGVyKTtcblxuXHRcdC8vIFByb2Nlc3Mgam9icyBpbiBiYWNrZ3JvdW5kXG5cdFx0KGFzeW5jICgpID0+IHtcblx0XHRcdGNvbnN0IHNlbWFwaG9yZSA9IG5ldyBTZW1hcGhvcmUoY29uZmlnLmNvbmN1cnJlbmN5ID8/IDEpO1xuXG5cdFx0XHRmb3IgYXdhaXQgKGNvbnN0IG1zZyBvZiBpdGVyKSB7XG5cdFx0XHRcdGF3YWl0IHNlbWFwaG9yZS5hY3F1aXJlKCk7XG5cblx0XHRcdFx0Ly8gUHJvY2VzcyBlYWNoIGpvYiBjb25jdXJyZW50bHkgdXAgdG8gY29uY3VycmVuY3kgbGltaXRcblx0XHRcdFx0KGFzeW5jICgpID0+IHtcblx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0Ly8gUGFyc2Ugam9iIGRhdGFcblx0XHRcdFx0XHRcdGxldCBkYXRhOiB1bmtub3duO1xuXHRcdFx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRcdFx0Y29uc3QgY29kZWMgPSBuYXRzLkpTT05Db2RlYygpO1xuXHRcdFx0XHRcdFx0XHRkYXRhID0gY29kZWMuZGVjb2RlKG1zZy5kYXRhKTtcblx0XHRcdFx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IHNjID0gbmF0cy5TdHJpbmdDb2RlYygpO1xuXHRcdFx0XHRcdFx0XHRcdGRhdGEgPSBKU09OLnBhcnNlKHNjLmRlY29kZShtc2cuZGF0YSkpO1xuXHRcdFx0XHRcdFx0XHR9IGNhdGNoIHtcblx0XHRcdFx0XHRcdFx0XHRkYXRhID0gbXNnLmRhdGE7XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0Ly8gRXh0cmFjdCBoZWFkZXJzXG5cdFx0XHRcdFx0XHRjb25zdCBoZWFkZXJzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge307XG5cdFx0XHRcdFx0XHRpZiAobXNnLmhlYWRlcnMpIHtcblx0XHRcdFx0XHRcdFx0Zm9yIChjb25zdCBba2V5LCB2YWx1ZXNdIG9mIG1zZy5oZWFkZXJzKSB7XG5cdFx0XHRcdFx0XHRcdFx0aGVhZGVyc1trZXldID0gQXJyYXkuaXNBcnJheSh2YWx1ZXMpID8gdmFsdWVzWzBdIDogdmFsdWVzO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdC8vIEV4dHJhY3Qgam9iIG1ldGFkYXRhIGZyb20gaGVhZGVyc1xuXHRcdFx0XHRcdFx0Y29uc3Qgam9iSWQgPSBoZWFkZXJzW1wieC1qb2ItaWRcIl0gfHwgbXNnLmhlYWRlcnM/LmdldChcIk5hdHMtTXNnLUlkXCIpIHx8IHV1aWQoKTtcblx0XHRcdFx0XHRcdGNvbnN0IHByaW9yaXR5ID0gTnVtYmVyLnBhcnNlSW50KGhlYWRlcnNbXCJ4LXByaW9yaXR5XCJdIHx8IFwiMFwiLCAxMCk7XG5cdFx0XHRcdFx0XHRjb25zdCBkZWxheSA9IE51bWJlci5wYXJzZUludChoZWFkZXJzW1wieC1kZWxheVwiXSB8fCBcIjBcIiwgMTApO1xuXHRcdFx0XHRcdFx0Y29uc3QgdGltZW91dCA9IE51bWJlci5wYXJzZUludChoZWFkZXJzW1wieC10aW1lb3V0XCJdIHx8IFwiMFwiLCAxMCk7XG5cblx0XHRcdFx0XHRcdC8vIEdldCByZWRlbGl2ZXJ5IGNvdW50IChhdHRlbXB0cylcblx0XHRcdFx0XHRcdGNvbnN0IGluZm8gPSBtc2cuaW5mbztcblx0XHRcdFx0XHRcdGNvbnN0IGF0dGVtcHRzID0gaW5mby5yZWRlbGl2ZXJ5Q291bnQgPz8gMDtcblx0XHRcdFx0XHRcdGNvbnN0IG1heFJldHJpZXMgPSBjb25maWcucmV0cmllcyA/PyAzO1xuXG5cdFx0XHRcdFx0XHQvLyBDcmVhdGUgV29ya2VySm9iXG5cdFx0XHRcdFx0XHRjb25zdCB3b3JrZXJKb2I6IFdvcmtlckpvYiA9IHtcblx0XHRcdFx0XHRcdFx0aWQ6IGpvYklkLFxuXHRcdFx0XHRcdFx0XHRkYXRhLFxuXHRcdFx0XHRcdFx0XHRoZWFkZXJzLFxuXHRcdFx0XHRcdFx0XHRxdWV1ZSxcblx0XHRcdFx0XHRcdFx0cHJpb3JpdHksXG5cdFx0XHRcdFx0XHRcdGF0dGVtcHRzLFxuXHRcdFx0XHRcdFx0XHRtYXhSZXRyaWVzLFxuXHRcdFx0XHRcdFx0XHRjcmVhdGVkQXQ6IG5ldyBEYXRlKFxuXHRcdFx0XHRcdFx0XHRcdGluZm8udGltZXN0YW1wTmFub3MgPyBNYXRoLmZsb29yKE51bWJlcihpbmZvLnRpbWVzdGFtcE5hbm9zKSAvIDFfMDAwXzAwMCkgOiBEYXRlLm5vdygpLFxuXHRcdFx0XHRcdFx0XHQpLFxuXHRcdFx0XHRcdFx0XHRkZWxheTogZGVsYXkgfHwgdW5kZWZpbmVkLFxuXHRcdFx0XHRcdFx0XHR0aW1lb3V0OiB0aW1lb3V0IHx8IGNvbmZpZy50aW1lb3V0IHx8IHVuZGVmaW5lZCxcblx0XHRcdFx0XHRcdFx0cmF3OiBtc2csXG5cdFx0XHRcdFx0XHRcdGNvbXBsZXRlOiBhc3luYyAoKSA9PiB7XG5cdFx0XHRcdFx0XHRcdFx0bXNnLmFjaygpO1xuXHRcdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdFx0XHRmYWlsOiBhc3luYyAoZXJyb3I6IEVycm9yLCByZXF1ZXVlPzogYm9vbGVhbikgPT4ge1xuXHRcdFx0XHRcdFx0XHRcdGlmIChyZXF1ZXVlKSB7XG5cdFx0XHRcdFx0XHRcdFx0XHQvLyBuYWsoKSB0ZWxscyB0aGUgc2VydmVyIHRvIHJlZGVsaXZlclxuXHRcdFx0XHRcdFx0XHRcdFx0bXNnLm5haygpO1xuXHRcdFx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdFx0XHQvLyB0ZXJtKCkgdGVybWluYXRlcyBkZWxpdmVyeSDigJQgbm8gbW9yZSByZXRyaWVzXG5cdFx0XHRcdFx0XHRcdFx0XHRtc2cudGVybSgpO1xuXHRcdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdH07XG5cblx0XHRcdFx0XHRcdC8vIFRpZXIgMiBwb2xpc2gg4oCUIGVuZm9yY2UgYHgtZGVsYXlgIGhlYWRlciBvbiB0aGUgY29uc3VtZXIgc2lkZS5cblx0XHRcdFx0XHRcdC8vIE5BVFMgSmV0U3RyZWFtIHN0b3JlcyBgeC1kZWxheWAgYXMgb3BhcXVlIG1ldGFkYXRhOyB0aGUgYnJva2VyXG5cdFx0XHRcdFx0XHQvLyBkb2VzIE5PVCBkZWZlciBkZWxpdmVyeSBvbiBpdC4gV2UgaW1wbGVtZW50IGNvbnN1bWVyLXNpZGVcblx0XHRcdFx0XHRcdC8vIGhvbGRpbmcgaGVyZS4gY3JlYXRlZE1zIGlzIHRoZSBtZXNzYWdlJ3MgZmlyc3QtcHVibGlzaCB0aW1lc3RhbXA7XG5cdFx0XHRcdFx0XHQvLyBob2xkIHVudGlsIGNyZWF0ZWRNcyArIGRlbGF5LiBTaW5nbGUtcHJvY2VzcyBzZW1hbnRpY3Mg4oCUIGZvclxuXHRcdFx0XHRcdFx0Ly8gbG9uZyBkZWZlcnJhbHMsIHByZWZlciB0cmlnZ2VyLWxldmVsIGBkZWxheWAgKERlZmVycmVkUnVuU2NoZWR1bGVyKS5cblx0XHRcdFx0XHRcdGNvbnN0IGNyZWF0ZWRNcyA9IGluZm8udGltZXN0YW1wTmFub3MgPyBNYXRoLmZsb29yKE51bWJlcihpbmZvLnRpbWVzdGFtcE5hbm9zKSAvIDFfMDAwXzAwMCkgOiBEYXRlLm5vdygpO1xuXHRcdFx0XHRcdFx0Y29uc3Qgd2FpdE1zID0gY29tcHV0ZVhEZWxheUhvbGRNcyhkZWxheSwgY3JlYXRlZE1zLCBEYXRlLm5vdygpKTtcblx0XHRcdFx0XHRcdGlmICh3YWl0TXMgPiAwKSB7XG5cdFx0XHRcdFx0XHRcdGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIHdhaXRNcykpO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRhd2FpdCBoYW5kbGVyKHdvcmtlckpvYik7XG5cdFx0XHRcdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoYFtOQVRTV29ya2VyQWRhcHRlcl0gRXJyb3IgcHJvY2Vzc2luZyBqb2IgZnJvbSAke3F1ZXVlfTogJHsoZXJyb3IgYXMgRXJyb3IpLm1lc3NhZ2V9YCk7XG5cdFx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0XHRtc2cubmFrKCk7XG5cdFx0XHRcdFx0XHR9IGNhdGNoIHtcblx0XHRcdFx0XHRcdFx0Ly8gQWxyZWFkeSBhY2tlZC9uYWNrZWRcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9IGZpbmFsbHkge1xuXHRcdFx0XHRcdFx0c2VtYXBob3JlLnJlbGVhc2UoKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0pKCk7XG5cdFx0XHR9XG5cdFx0fSkoKTtcblxuXHRcdGNvbnNvbGUubG9nKFxuXHRcdFx0YFtOQVRTV29ya2VyQWRhcHRlcl0gUHJvY2Vzc2luZyBxdWV1ZTogJHtxdWV1ZX0gKGNvbmN1cnJlbmN5PSR7Y29uZmlnLmNvbmN1cnJlbmN5ID8/IDF9LCByZXRyaWVzPSR7Y29uZmlnLnJldHJpZXMgPz8gM30pYCxcblx0XHQpO1xuXHR9XG5cblx0LyoqXG5cdCAqIEFkZCBhIGpvYiB0byBhIHdvcmtlciBxdWV1ZVxuXHQgKi9cblx0YXN5bmMgYWRkSm9iKFxuXHRcdHF1ZXVlOiBzdHJpbmcsXG5cdFx0ZGF0YTogdW5rbm93bixcblx0XHRvcHRzPzoge1xuXHRcdFx0cHJpb3JpdHk/OiBudW1iZXI7XG5cdFx0XHRkZWxheT86IG51bWJlcjtcblx0XHRcdHJldHJpZXM/OiBudW1iZXI7XG5cdFx0XHR0aW1lb3V0PzogbnVtYmVyO1xuXHRcdFx0am9iSWQ/OiBzdHJpbmc7XG5cdFx0fSxcblx0KTogUHJvbWlzZTxzdHJpbmc+IHtcblx0XHRpZiAoIXRoaXMuY29ubmVjdGVkKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoXCJOb3QgY29ubmVjdGVkLiBDYWxsIGNvbm5lY3QoKSBmaXJzdC5cIik7XG5cdFx0fVxuXG5cdFx0Y29uc3QgbmF0cyA9IGF3YWl0IGltcG9ydChcIm5hdHNcIik7XG5cdFx0Y29uc3Qgc3ViamVjdCA9IGB3b3JrZXIuJHtxdWV1ZX1gO1xuXHRcdGNvbnN0IHN0cmVhbU5hbWUgPSB0aGlzLmNvbmZpZy5zdHJlYW1OYW1lIHx8IFwiYmxvay13b3JrZXJcIjtcblxuXHRcdC8vIEVuc3VyZSBzdHJlYW0gaGFzIHRoaXMgc3ViamVjdFxuXHRcdGF3YWl0IHRoaXMuZW5zdXJlU3RyZWFtKHN0cmVhbU5hbWUsIFtzdWJqZWN0XSk7XG5cblx0XHQvLyBCdWlsZCBoZWFkZXJzIHdpdGggam9iIG1ldGFkYXRhXG5cdFx0Y29uc3QgaGRycyA9IG5hdHMuaGVhZGVycygpO1xuXHRcdGNvbnN0IGpvYklkID0gb3B0cz8uam9iSWQgfHwgdXVpZCgpO1xuXHRcdGhkcnMuc2V0KFwieC1qb2ItaWRcIiwgam9iSWQpO1xuXHRcdGhkcnMuc2V0KFwiTmF0cy1Nc2ctSWRcIiwgam9iSWQpOyAvLyBEZWR1cGxpY2F0aW9uXG5cdFx0aWYgKG9wdHM/LnByaW9yaXR5KSBoZHJzLnNldChcIngtcHJpb3JpdHlcIiwgU3RyaW5nKG9wdHMucHJpb3JpdHkpKTtcblx0XHRpZiAob3B0cz8uZGVsYXkpIGhkcnMuc2V0KFwieC1kZWxheVwiLCBTdHJpbmcob3B0cy5kZWxheSkpO1xuXHRcdGlmIChvcHRzPy50aW1lb3V0KSBoZHJzLnNldChcIngtdGltZW91dFwiLCBTdHJpbmcob3B0cy50aW1lb3V0KSk7XG5cblx0XHQvLyBFbmNvZGUgYW5kIHB1Ymxpc2hcblx0XHRjb25zdCBjb2RlYyA9IG5hdHMuSlNPTkNvZGVjKCk7XG5cdFx0YXdhaXQgdGhpcy5qcy5wdWJsaXNoKHN1YmplY3QsIGNvZGVjLmVuY29kZShkYXRhKSwgeyBoZWFkZXJzOiBoZHJzIH0pO1xuXG5cdFx0cmV0dXJuIGpvYklkO1xuXHR9XG5cblx0LyoqXG5cdCAqIFN0b3AgcHJvY2Vzc2luZyBhIHNwZWNpZmljIHF1ZXVlXG5cdCAqL1xuXHRhc3luYyBzdG9wUHJvY2Vzc2luZyhxdWV1ZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0Y29uc3QgaXRlciA9IHRoaXMuY29uc3VtZUl0ZXJhdG9ycy5nZXQocXVldWUpO1xuXHRcdGlmIChpdGVyKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRpdGVyLnN0b3AoKTtcblx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHQvLyBBbHJlYWR5IHN0b3BwZWRcblx0XHRcdH1cblx0XHRcdHRoaXMuY29uc3VtZUl0ZXJhdG9ycy5kZWxldGUocXVldWUpO1xuXHRcdH1cblx0XHR0aGlzLmNvbnN1bWVycy5kZWxldGUocXVldWUpO1xuXHRcdGNvbnNvbGUubG9nKGBbTkFUU1dvcmtlckFkYXB0ZXJdIFN0b3BwZWQgcHJvY2Vzc2luZyBxdWV1ZTogJHtxdWV1ZX1gKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDaGVjayBpZiBjb25uZWN0ZWRcblx0ICovXG5cdGlzQ29ubmVjdGVkKCk6IGJvb2xlYW4ge1xuXHRcdHJldHVybiB0aGlzLmNvbm5lY3RlZDtcblx0fVxuXG5cdC8qKlxuXHQgKiBIZWFsdGggY2hlY2tcblx0ICovXG5cdGFzeW5jIGhlYWx0aENoZWNrKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQgfHwgIXRoaXMubmMpIHJldHVybiBmYWxzZTtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgaW5mbyA9IHRoaXMubmMuaW5mbztcblx0XHRcdHJldHVybiBpbmZvICE9PSB1bmRlZmluZWQ7XG5cdFx0fSBjYXRjaCB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIEdldCBxdWV1ZSBzdGF0aXN0aWNzIGZyb20gSmV0U3RyZWFtIGNvbnN1bWVyIGluZm9cblx0ICovXG5cdGFzeW5jIGdldFF1ZXVlU3RhdHMocXVldWU6IHN0cmluZyk6IFByb21pc2U8V29ya2VyUXVldWVTdGF0cz4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQpIHtcblx0XHRcdHJldHVybiB7IHdhaXRpbmc6IDAsIGFjdGl2ZTogMCwgY29tcGxldGVkOiAwLCBmYWlsZWQ6IDAsIGRlbGF5ZWQ6IDAgfTtcblx0XHR9XG5cblx0XHR0cnkge1xuXHRcdFx0Y29uc3Qgc3RyZWFtTmFtZSA9IHRoaXMuY29uZmlnLnN0cmVhbU5hbWUgfHwgXCJibG9rLXdvcmtlclwiO1xuXHRcdFx0Y29uc3QgZHVyYWJsZU5hbWUgPSBgYmxvay13b3JrZXItJHtxdWV1ZX1gO1xuXG5cdFx0XHRjb25zdCBpbmZvID0gYXdhaXQgdGhpcy5qc20uY29uc3VtZXJzLmluZm8oc3RyZWFtTmFtZSwgZHVyYWJsZU5hbWUpO1xuXG5cdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHR3YWl0aW5nOiBpbmZvLm51bV9wZW5kaW5nID8/IDAsXG5cdFx0XHRcdGFjdGl2ZTogaW5mby5udW1fYWNrX3BlbmRpbmcgPz8gMCxcblx0XHRcdFx0Y29tcGxldGVkOiBpbmZvLmRlbGl2ZXJlZD8uY29uc3VtZXJfc2VxID8/IDAsXG5cdFx0XHRcdGZhaWxlZDogaW5mby5udW1fcmVkZWxpdmVyZWQgPz8gMCxcblx0XHRcdFx0ZGVsYXllZDogMCwgLy8gTkFUUyBkb2Vzbid0IGhhdmUgYSBuYXRpdmUgZGVsYXllZCBjb3VudFxuXHRcdFx0fTtcblx0XHR9IGNhdGNoIHtcblx0XHRcdHJldHVybiB7IHdhaXRpbmc6IDAsIGFjdGl2ZTogMCwgY29tcGxldGVkOiAwLCBmYWlsZWQ6IDAsIGRlbGF5ZWQ6IDAgfTtcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogRW5zdXJlIGEgSmV0U3RyZWFtIHN0cmVhbSBleGlzdHMgd2l0aCB0aGUgZ2l2ZW4gc3ViamVjdHNcblx0ICovXG5cdHByaXZhdGUgYXN5bmMgZW5zdXJlU3RyZWFtKG5hbWU6IHN0cmluZywgc3ViamVjdHM6IHN0cmluZ1tdKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0dHJ5IHtcblx0XHRcdGNvbnN0IGluZm8gPSBhd2FpdCB0aGlzLmpzbS5zdHJlYW1zLmluZm8obmFtZSk7XG5cblx0XHRcdC8vIE1lcmdlIG5ldyBzdWJqZWN0cyB3aXRoIGV4aXN0aW5nXG5cdFx0XHRjb25zdCBleGlzdGluZ1N1YmplY3RzID0gaW5mby5jb25maWcuc3ViamVjdHMgfHwgW107XG5cdFx0XHRjb25zdCBhbGxTdWJqZWN0cyA9IFsuLi5uZXcgU2V0KFsuLi5leGlzdGluZ1N1YmplY3RzLCAuLi5zdWJqZWN0c10pXTtcblxuXHRcdFx0aWYgKGFsbFN1YmplY3RzLmxlbmd0aCAhPT0gZXhpc3RpbmdTdWJqZWN0cy5sZW5ndGgpIHtcblx0XHRcdFx0YXdhaXQgdGhpcy5qc20uc3RyZWFtcy51cGRhdGUobmFtZSwge1xuXHRcdFx0XHRcdC4uLmluZm8uY29uZmlnLFxuXHRcdFx0XHRcdHN1YmplY3RzOiBhbGxTdWJqZWN0cyxcblx0XHRcdFx0fSk7XG5cdFx0XHR9XG5cdFx0fSBjYXRjaCB7XG5cdFx0XHQvLyBTdHJlYW0gZG9lc24ndCBleGlzdCwgY3JlYXRlIGl0XG5cdFx0XHRhd2FpdCB0aGlzLmpzbS5zdHJlYW1zLmFkZCh7XG5cdFx0XHRcdG5hbWUsXG5cdFx0XHRcdHN1YmplY3RzLFxuXHRcdFx0XHQvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IG5hdHMgSmV0U3RyZWFtIHJldGVudGlvbiBwb2xpY3kgZW51bVxuXHRcdFx0XHRyZXRlbnRpb246IFwid29ya3F1ZXVlXCIgYXMgYW55LFxuXHRcdFx0XHRtYXhfZGVsaXZlcjogNCwgLy8gZGVmYXVsdDogMyByZXRyaWVzICsgMSBpbml0aWFsIGF0dGVtcHRcblx0XHRcdFx0Ly8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBuYXRzIEpldFN0cmVhbSBzdG9yYWdlIHR5cGUgZW51bVxuXHRcdFx0XHRzdG9yYWdlOiBcImZpbGVcIiBhcyBhbnksXG5cdFx0XHR9KTtcblx0XHR9XG5cdH1cbn1cblxuLyoqXG4gKiBTaW1wbGUgc2VtYXBob3JlIGZvciBjb25jdXJyZW5jeSBjb250cm9sXG4gKi9cbmNsYXNzIFNlbWFwaG9yZSB7XG5cdHByaXZhdGUgcGVybWl0czogbnVtYmVyO1xuXHRwcml2YXRlIHdhaXRpbmc6IEFycmF5PCgpID0+IHZvaWQ+ID0gW107XG5cblx0Y29uc3RydWN0b3IocGVybWl0czogbnVtYmVyKSB7XG5cdFx0dGhpcy5wZXJtaXRzID0gcGVybWl0cztcblx0fVxuXG5cdGFzeW5jIGFjcXVpcmUoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKHRoaXMucGVybWl0cyA+IDApIHtcblx0XHRcdHRoaXMucGVybWl0cy0tO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRyZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHtcblx0XHRcdHRoaXMud2FpdGluZy5wdXNoKHJlc29sdmUpO1xuXHRcdH0pO1xuXHR9XG5cblx0cmVsZWFzZSgpOiB2b2lkIHtcblx0XHRjb25zdCBuZXh0ID0gdGhpcy53YWl0aW5nLnNoaWZ0KCk7XG5cdFx0aWYgKG5leHQpIHtcblx0XHRcdG5leHQoKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5wZXJtaXRzKys7XG5cdFx0fVxuXHR9XG59XG5cbmV4cG9ydCBkZWZhdWx0IE5BVFNXb3JrZXJBZGFwdGVyO1xuIl19
@@ -0,0 +1,56 @@
1
+ /**
2
+ * PgBossAdapter — v0.7 PR 5 — Worker adapter backed by `pg-boss`,
3
+ * which stores jobs in PostgreSQL. Operationally attractive when
4
+ * Postgres is already in the stack — no extra broker infra needed.
5
+ *
6
+ * Semantics:
7
+ * - `pg-boss` handles concurrency, retries, dead-letter, priority,
8
+ * delays, and exactly-once scheduling natively — the adapter
9
+ * forwards `config.concurrency`, `config.retries`, etc. to the
10
+ * `boss.work(queue, opts, handler)` and `boss.send(queue, data, opts)`
11
+ * calls directly.
12
+ * - **Single connection per process** — `pg-boss` manages its own
13
+ * pool; we instantiate one `PgBoss` instance per `PgBossAdapter`.
14
+ * - **Schema**: `pg-boss` creates its own schema (`pgboss` by
15
+ * default) on first start. Tables are migrated automatically.
16
+ *
17
+ * Requires `pg-boss` as a peer dependency:
18
+ *
19
+ * bun add pg-boss
20
+ *
21
+ * Environment variables:
22
+ * - `DATABASE_URL` — Postgres connection string (default
23
+ * `postgres://localhost:5432/blok`).
24
+ * - `PG_BOSS_SCHEMA` — schema name (default `"pgboss"`).
25
+ */
26
+ import type { WorkerTriggerOpts } from "@blokjs/helper";
27
+ import type { WorkerAdapter, WorkerJob, WorkerQueueStats } from "../WorkerTrigger";
28
+ export interface PgBossConfig {
29
+ connectionString: string;
30
+ schema?: string;
31
+ }
32
+ export declare class PgBossAdapter implements WorkerAdapter {
33
+ readonly provider: "pg-boss";
34
+ private readonly config;
35
+ private boss;
36
+ private workIds;
37
+ private connected;
38
+ private stats;
39
+ constructor(config?: Partial<PgBossConfig>);
40
+ connect(): Promise<void>;
41
+ disconnect(): Promise<void>;
42
+ process(config: WorkerTriggerOpts, handler: (job: WorkerJob) => Promise<void>): Promise<void>;
43
+ private runOneJob;
44
+ private ensureQueue;
45
+ addJob(queue: string, data: unknown, opts?: {
46
+ priority?: number;
47
+ delay?: number;
48
+ retries?: number;
49
+ timeout?: number;
50
+ jobId?: string;
51
+ }): Promise<string>;
52
+ stopProcessing(queue: string): Promise<void>;
53
+ isConnected(): boolean;
54
+ healthCheck(): Promise<boolean>;
55
+ getQueueStats(queue: string): Promise<WorkerQueueStats>;
56
+ }