@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.
- package/__tests__/integration/nats-adapter.real-nats.test.ts +116 -0
- package/__tests__/integration/pgboss-adapter.real-pg.test.ts +164 -0
- package/__tests__/integration/rabbitmq-adapter.real-rabbitmq.test.ts +179 -0
- package/__tests__/integration/sqs-adapter.real-sqs.test.ts +228 -0
- package/dist/WorkerTrigger.d.ts +40 -4
- package/dist/WorkerTrigger.js +272 -40
- package/dist/adapters/BullMQAdapter.d.ts +1 -1
- package/dist/adapters/BullMQAdapter.js +5 -42
- package/dist/adapters/InMemoryAdapter.d.ts +1 -1
- package/dist/adapters/InMemoryAdapter.js +13 -12
- package/dist/adapters/KafkaAdapter.d.ts +62 -0
- package/dist/adapters/KafkaAdapter.js +236 -0
- package/dist/adapters/NATSAdapter.d.ts +110 -0
- package/dist/adapters/NATSAdapter.js +394 -0
- package/dist/adapters/PgBossAdapter.d.ts +56 -0
- package/dist/adapters/PgBossAdapter.js +251 -0
- package/dist/adapters/RabbitMQAdapter.d.ts +51 -0
- package/dist/adapters/RabbitMQAdapter.js +241 -0
- package/dist/adapters/RedisStreamsAdapter.d.ts +64 -0
- package/dist/adapters/RedisStreamsAdapter.js +240 -0
- package/dist/adapters/SQSAdapter.d.ts +61 -0
- package/dist/adapters/SQSAdapter.js +269 -0
- package/dist/adapters/factory.d.ts +34 -0
- package/dist/adapters/factory.js +103 -0
- package/dist/index.d.ts +25 -7
- package/dist/index.js +31 -16
- package/package.json +27 -5
- package/src/WorkerTrigger.test.ts +44 -14
- package/src/WorkerTrigger.ts +299 -27
- package/src/adapters/InMemoryAdapter.ts +9 -5
- package/src/adapters/KafkaAdapter.ts +277 -0
- package/src/adapters/NATSAdapter.ts +454 -0
- package/src/adapters/PgBossAdapter.ts +293 -0
- package/src/adapters/RabbitMQAdapter.ts +285 -0
- package/src/adapters/RedisStreamsAdapter.ts +286 -0
- package/src/adapters/SQSAdapter.ts +306 -0
- package/src/adapters/factory.test.ts +89 -0
- package/src/adapters/factory.ts +111 -0
- package/src/adapters/new-adapters.test.ts +130 -0
- package/src/index.ts +31 -4
- package/template/.env.example +13 -0
- package/template/package.json +45 -0
- package/template/src/Nodes.ts +10 -0
- package/template/src/Workflows.ts +8 -0
- package/template/src/index.ts +41 -0
- package/template/src/runner/WorkerServer.ts +34 -0
- package/template/src/runner/types/Workflows.ts +7 -0
- package/template/src/workflows/jobs/process-job.ts +47 -0
- package/template/tsconfig.json +31 -0
- package/template/vitest.config.ts +39 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RedisStreamsAdapter — v0.7 PR 5 — Worker adapter backed by Redis
|
|
3
|
+
* Streams via `ioredis`. Consumes from a stream (the `queue` field)
|
|
4
|
+
* with a consumer group (`consumerGroup`); produces via `XADD`.
|
|
5
|
+
*
|
|
6
|
+
* Semantics:
|
|
7
|
+
* - **Consumer groups**: required. `consumerGroup` defaults to
|
|
8
|
+
* `"${queue}-group"`. The group is auto-created (`MKSTREAM` on the
|
|
9
|
+
* stream too) on first `process()` call.
|
|
10
|
+
* - **Consumer name**: per-process uuid, so multiple instances of
|
|
11
|
+
* the same worker process don't share pending entries.
|
|
12
|
+
* - **Retries**: pending entries are XACK'd on success / left
|
|
13
|
+
* unacked on failure (visible in `XPENDING`). A redrive loop
|
|
14
|
+
* reads pending entries older than `timeout` and re-delivers
|
|
15
|
+
* to the current consumer.
|
|
16
|
+
* - **Auto-claim**: skipped in v1 — operators should run XAUTOCLAIM
|
|
17
|
+
* periodically out of band.
|
|
18
|
+
*
|
|
19
|
+
* Requires `ioredis` as a peer dependency:
|
|
20
|
+
*
|
|
21
|
+
* bun add ioredis
|
|
22
|
+
*
|
|
23
|
+
* Environment variables:
|
|
24
|
+
* - `REDIS_HOST` (default `localhost`)
|
|
25
|
+
* - `REDIS_PORT` (default `6379`)
|
|
26
|
+
* - `REDIS_PASSWORD`
|
|
27
|
+
* - `REDIS_DB`
|
|
28
|
+
*/
|
|
29
|
+
import type { WorkerTriggerOpts } from "@blokjs/helper";
|
|
30
|
+
import type { WorkerAdapter, WorkerJob, WorkerQueueStats } from "../WorkerTrigger";
|
|
31
|
+
export interface RedisStreamsConfig {
|
|
32
|
+
host: string;
|
|
33
|
+
port: number;
|
|
34
|
+
password?: string;
|
|
35
|
+
db?: number;
|
|
36
|
+
blockMs: number;
|
|
37
|
+
count: number;
|
|
38
|
+
}
|
|
39
|
+
export declare class RedisStreamsAdapter implements WorkerAdapter {
|
|
40
|
+
readonly provider: "redis";
|
|
41
|
+
private readonly config;
|
|
42
|
+
private client;
|
|
43
|
+
private runners;
|
|
44
|
+
private connected;
|
|
45
|
+
private stats;
|
|
46
|
+
private consumerName;
|
|
47
|
+
constructor(config?: Partial<RedisStreamsConfig>);
|
|
48
|
+
connect(): Promise<void>;
|
|
49
|
+
disconnect(): Promise<void>;
|
|
50
|
+
process(config: WorkerTriggerOpts, handler: (job: WorkerJob) => Promise<void>): Promise<void>;
|
|
51
|
+
private runConsumerLoop;
|
|
52
|
+
private fieldsToObject;
|
|
53
|
+
addJob(queue: string, data: unknown, opts?: {
|
|
54
|
+
priority?: number;
|
|
55
|
+
delay?: number;
|
|
56
|
+
retries?: number;
|
|
57
|
+
timeout?: number;
|
|
58
|
+
jobId?: string;
|
|
59
|
+
}): Promise<string>;
|
|
60
|
+
stopProcessing(queue: string): Promise<void>;
|
|
61
|
+
isConnected(): boolean;
|
|
62
|
+
healthCheck(): Promise<boolean>;
|
|
63
|
+
getQueueStats(queue: string): Promise<WorkerQueueStats>;
|
|
64
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RedisStreamsAdapter — v0.7 PR 5 — Worker adapter backed by Redis
|
|
3
|
+
* Streams via `ioredis`. Consumes from a stream (the `queue` field)
|
|
4
|
+
* with a consumer group (`consumerGroup`); produces via `XADD`.
|
|
5
|
+
*
|
|
6
|
+
* Semantics:
|
|
7
|
+
* - **Consumer groups**: required. `consumerGroup` defaults to
|
|
8
|
+
* `"${queue}-group"`. The group is auto-created (`MKSTREAM` on the
|
|
9
|
+
* stream too) on first `process()` call.
|
|
10
|
+
* - **Consumer name**: per-process uuid, so multiple instances of
|
|
11
|
+
* the same worker process don't share pending entries.
|
|
12
|
+
* - **Retries**: pending entries are XACK'd on success / left
|
|
13
|
+
* unacked on failure (visible in `XPENDING`). A redrive loop
|
|
14
|
+
* reads pending entries older than `timeout` and re-delivers
|
|
15
|
+
* to the current consumer.
|
|
16
|
+
* - **Auto-claim**: skipped in v1 — operators should run XAUTOCLAIM
|
|
17
|
+
* periodically out of band.
|
|
18
|
+
*
|
|
19
|
+
* Requires `ioredis` as a peer dependency:
|
|
20
|
+
*
|
|
21
|
+
* bun add ioredis
|
|
22
|
+
*
|
|
23
|
+
* Environment variables:
|
|
24
|
+
* - `REDIS_HOST` (default `localhost`)
|
|
25
|
+
* - `REDIS_PORT` (default `6379`)
|
|
26
|
+
* - `REDIS_PASSWORD`
|
|
27
|
+
* - `REDIS_DB`
|
|
28
|
+
*/
|
|
29
|
+
import { v4 as uuid } from "uuid";
|
|
30
|
+
export class RedisStreamsAdapter {
|
|
31
|
+
provider = "redis";
|
|
32
|
+
config;
|
|
33
|
+
client = null;
|
|
34
|
+
runners = new Map();
|
|
35
|
+
connected = false;
|
|
36
|
+
stats = new Map();
|
|
37
|
+
consumerName = `blok-${uuid().slice(0, 8)}`;
|
|
38
|
+
constructor(config) {
|
|
39
|
+
this.config = {
|
|
40
|
+
host: config?.host ?? process.env.REDIS_HOST ?? "localhost",
|
|
41
|
+
port: config?.port ?? Number.parseInt(process.env.REDIS_PORT ?? "6379", 10),
|
|
42
|
+
password: config?.password ?? process.env.REDIS_PASSWORD,
|
|
43
|
+
db: config?.db ?? Number.parseInt(process.env.REDIS_DB ?? "0", 10),
|
|
44
|
+
blockMs: config?.blockMs ?? 5000,
|
|
45
|
+
count: config?.count ?? 10,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async connect() {
|
|
49
|
+
if (this.connected)
|
|
50
|
+
return;
|
|
51
|
+
try {
|
|
52
|
+
// biome-ignore lint/suspicious/noExplicitAny: ioredis is a runtime peer dep.
|
|
53
|
+
const ioredis = await import("ioredis");
|
|
54
|
+
const IORedis = ioredis.default ?? ioredis.Redis ?? ioredis;
|
|
55
|
+
this.client = new IORedis({
|
|
56
|
+
host: this.config.host,
|
|
57
|
+
port: this.config.port,
|
|
58
|
+
password: this.config.password,
|
|
59
|
+
db: this.config.db,
|
|
60
|
+
});
|
|
61
|
+
await this.client.ping();
|
|
62
|
+
this.connected = true;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
throw new Error(`[blok][redis] connect failed: ${err.message}. Install ioredis as a peer dependency: bun add ioredis`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async disconnect() {
|
|
69
|
+
if (!this.connected)
|
|
70
|
+
return;
|
|
71
|
+
for (const runner of this.runners.values())
|
|
72
|
+
runner.stop = true;
|
|
73
|
+
// Wait for in-flight loops to drain.
|
|
74
|
+
const drainDeadline = Date.now() + 2000;
|
|
75
|
+
while (Date.now() < drainDeadline) {
|
|
76
|
+
let active = 0;
|
|
77
|
+
for (const r of this.runners.values())
|
|
78
|
+
active += r.loops;
|
|
79
|
+
if (active === 0)
|
|
80
|
+
break;
|
|
81
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
82
|
+
}
|
|
83
|
+
this.runners.clear();
|
|
84
|
+
try {
|
|
85
|
+
await this.client?.quit();
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
/* ignore */
|
|
89
|
+
}
|
|
90
|
+
this.client = null;
|
|
91
|
+
this.connected = false;
|
|
92
|
+
}
|
|
93
|
+
async process(config, handler) {
|
|
94
|
+
if (!this.connected || !this.client)
|
|
95
|
+
throw new Error("[blok][redis] not connected. Call connect() first.");
|
|
96
|
+
const stream = config.queue;
|
|
97
|
+
const group = config.consumerGroup ?? `${stream}-group`;
|
|
98
|
+
// Create group + stream if missing. XGROUP CREATE with MKSTREAM
|
|
99
|
+
// errors with "BUSYGROUP" if the group already exists — swallow it.
|
|
100
|
+
try {
|
|
101
|
+
await this.client.xgroup("CREATE", stream, group, "$", "MKSTREAM");
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (!/BUSYGROUP/i.test(err.message))
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
const runner = { stop: false, loops: 0 };
|
|
108
|
+
this.runners.set(stream, runner);
|
|
109
|
+
this.stats.set(stream, { completed: 0, failed: 0, active: 0 });
|
|
110
|
+
const stats = this.stats.get(stream);
|
|
111
|
+
const concurrency = Math.max(1, config.concurrency ?? 1);
|
|
112
|
+
for (let i = 0; i < concurrency; i += 1) {
|
|
113
|
+
void this.runConsumerLoop(stream, group, config, handler, runner, stats);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async runConsumerLoop(stream, group, config, handler, runner, stats) {
|
|
117
|
+
if (!this.client)
|
|
118
|
+
return;
|
|
119
|
+
runner.loops += 1;
|
|
120
|
+
try {
|
|
121
|
+
while (!runner.stop) {
|
|
122
|
+
let entries = null;
|
|
123
|
+
try {
|
|
124
|
+
entries = await this.client.xreadgroup("GROUP", group, this.consumerName, "COUNT", String(this.config.count), "BLOCK", String(this.config.blockMs), "STREAMS", stream, ">");
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!entries)
|
|
131
|
+
continue;
|
|
132
|
+
for (const [, msgs] of entries) {
|
|
133
|
+
for (const [id, fields] of msgs) {
|
|
134
|
+
if (runner.stop)
|
|
135
|
+
break;
|
|
136
|
+
stats.active += 1;
|
|
137
|
+
try {
|
|
138
|
+
const payload = this.fieldsToObject(fields);
|
|
139
|
+
const dataString = typeof payload.data === "string" ? payload.data : "";
|
|
140
|
+
let data;
|
|
141
|
+
try {
|
|
142
|
+
data = dataString.length > 0 ? JSON.parse(dataString) : null;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
data = dataString;
|
|
146
|
+
}
|
|
147
|
+
const job = {
|
|
148
|
+
id,
|
|
149
|
+
data,
|
|
150
|
+
headers: {},
|
|
151
|
+
queue: stream,
|
|
152
|
+
priority: config.priority ?? 0,
|
|
153
|
+
attempts: 0,
|
|
154
|
+
maxRetries: config.retries ?? 0,
|
|
155
|
+
createdAt: new Date(Number.parseInt(id.split("-")[0] ?? String(Date.now()), 10)),
|
|
156
|
+
timeout: config.timeout,
|
|
157
|
+
raw: { id, fields },
|
|
158
|
+
complete: async () => {
|
|
159
|
+
await this.client?.xack(stream, group, id);
|
|
160
|
+
stats.completed += 1;
|
|
161
|
+
},
|
|
162
|
+
fail: async (_err) => {
|
|
163
|
+
stats.failed += 1;
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
await handler(job);
|
|
167
|
+
if (config.ack !== false)
|
|
168
|
+
await this.client?.xack(stream, group, id);
|
|
169
|
+
stats.completed += 1;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
stats.failed += 1;
|
|
173
|
+
// Pending entry left unacked — picked up by XAUTOCLAIM.
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
stats.active = Math.max(0, stats.active - 1);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
runner.loops -= 1;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
fieldsToObject(fields) {
|
|
187
|
+
const out = {};
|
|
188
|
+
for (let i = 0; i < fields.length; i += 2) {
|
|
189
|
+
out[fields[i]] = fields[i + 1];
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
async addJob(queue, data, opts) {
|
|
194
|
+
if (!this.connected || !this.client)
|
|
195
|
+
throw new Error("[blok][redis] not connected. Call connect() first.");
|
|
196
|
+
const payload = typeof data === "string" ? data : JSON.stringify(data);
|
|
197
|
+
const args = [];
|
|
198
|
+
if (opts?.jobId)
|
|
199
|
+
args.push("NOMKSTREAM");
|
|
200
|
+
const id = await this.client.xadd(queue, "*", "data", payload, "jobId", opts?.jobId ?? "");
|
|
201
|
+
return id;
|
|
202
|
+
}
|
|
203
|
+
async stopProcessing(queue) {
|
|
204
|
+
const runner = this.runners.get(queue);
|
|
205
|
+
if (runner)
|
|
206
|
+
runner.stop = true;
|
|
207
|
+
}
|
|
208
|
+
isConnected() {
|
|
209
|
+
return this.connected;
|
|
210
|
+
}
|
|
211
|
+
async healthCheck() {
|
|
212
|
+
if (!this.connected || !this.client)
|
|
213
|
+
return false;
|
|
214
|
+
try {
|
|
215
|
+
const pong = await this.client.ping();
|
|
216
|
+
return pong === "PONG";
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async getQueueStats(queue) {
|
|
223
|
+
const counters = this.stats.get(queue) ?? { completed: 0, failed: 0, active: 0 };
|
|
224
|
+
let waiting = 0;
|
|
225
|
+
try {
|
|
226
|
+
waiting = (await this.client?.xlen(queue)) ?? 0;
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
/* ignore */
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
waiting,
|
|
233
|
+
active: counters.active,
|
|
234
|
+
completed: counters.completed,
|
|
235
|
+
failed: counters.failed,
|
|
236
|
+
delayed: 0,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVkaXNTdHJlYW1zQWRhcHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hZGFwdGVycy9SZWRpc1N0cmVhbXNBZGFwdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyQkc7QUFHSCxPQUFPLEVBQUUsRUFBRSxJQUFJLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQWtDbEMsTUFBTSxPQUFPLG1CQUFtQjtJQUN0QixRQUFRLEdBQUcsT0FBZ0IsQ0FBQztJQUNwQixNQUFNLENBQXFCO0lBQ3BDLE1BQU0sR0FBdUIsSUFBSSxDQUFDO0lBQ2xDLE9BQU8sR0FBNkIsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUM5QyxTQUFTLEdBQUcsS0FBSyxDQUFDO0lBQ2xCLEtBQUssR0FBb0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNuRCxZQUFZLEdBQUcsUUFBUSxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFFcEQsWUFBWSxNQUFvQztRQUMvQyxJQUFJLENBQUMsTUFBTSxHQUFHO1lBQ2IsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLElBQUksV0FBVztZQUMzRCxJQUFJLEVBQUUsTUFBTSxFQUFFLElBQUksSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxJQUFJLE1BQU0sRUFBRSxFQUFFLENBQUM7WUFDM0UsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjO1lBQ3hELEVBQUUsRUFBRSxNQUFNLEVBQUUsRUFBRSxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRLElBQUksR0FBRyxFQUFFLEVBQUUsQ0FBQztZQUNsRSxPQUFPLEVBQUUs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QUFDO1FBQ2QsQ0FBQztJQUNGLENBQUM7SUFFRCxLQUFLLENBQUMsYUFBYSxDQUFDLEtBQWE7UUFDaEMsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDO1FBQ2pGLElBQUksT0FBTyxHQUFHLENBQUMsQ0FBQztRQUNoQixJQUFJLENBQUM7WUFDSixPQUFPLEdBQUcsQ0FBQyxNQUFNLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2pELENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUixZQUFZO1FBQ2IsQ0FBQztRQUNELE9BQU87WUFDTixPQUFPO1lBQ1AsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNO1lBQ3ZCLFNBQVMsRUFBRSxRQUFRLENBQUMsU0FBUztZQUM3QixNQUFNLEVBQUUsUUFBUSxDQUFDLE1BQU07WUFDdkIsT0FBTyxFQUFFLENBQUM7U0FDVixDQUFDO0lBQ0gsQ0FBQztDQUNEIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSZWRpc1N0cmVhbXNBZGFwdGVyIOKAlCB2MC43IFBSIDUg4oCUIFdvcmtlciBhZGFwdGVyIGJhY2tlZCBieSBSZWRpc1xuICogU3RyZWFtcyB2aWEgYGlvcmVkaXNgLiBDb25zdW1lcyBmcm9tIGEgc3RyZWFtICh0aGUgYHF1ZXVlYCBmaWVsZClcbiAqIHdpdGggYSBjb25zdW1lciBncm91cCAoYGNvbnN1bWVyR3JvdXBgKTsgcHJvZHVjZXMgdmlhIGBYQUREYC5cbiAqXG4gKiBTZW1hbnRpY3M6XG4gKiAgIC0gKipDb25zdW1lciBncm91cHMqKjogcmVxdWlyZWQuIGBjb25zdW1lckdyb3VwYCBkZWZhdWx0cyB0b1xuICogICAgIGBcIiR7cXVldWV9LWdyb3VwXCJgLiBUaGUgZ3JvdXAgaXMgYXV0by1jcmVhdGVkIChgTUtTVFJFQU1gIG9uIHRoZVxuICogICAgIHN0cmVhbSB0b28pIG9uIGZpcnN0IGBwcm9jZXNzKClgIGNhbGwuXG4gKiAgIC0gKipDb25zdW1lciBuYW1lKio6IHBlci1wcm9jZXNzIHV1aWQsIHNvIG11bHRpcGxlIGluc3RhbmNlcyBvZlxuICogICAgIHRoZSBzYW1lIHdvcmtlciBwcm9jZXNzIGRvbid0IHNoYXJlIHBlbmRpbmcgZW50cmllcy5cbiAqICAgLSAqKlJldHJpZXMqKjogcGVuZGluZyBlbnRyaWVzIGFyZSBYQUNLJ2Qgb24gc3VjY2VzcyAvIGxlZnRcbiAqICAgICB1bmFja2VkIG9uIGZhaWx1cmUgKHZpc2libGUgaW4gYFhQRU5ESU5HYCkuIEEgcmVkcml2ZSBsb29wXG4gKiAgICAgcmVhZHMgcGVuZGluZyBlbnRyaWVzIG9sZGVyIHRoYW4gYHRpbWVvdXRgIGFuZCByZS1kZWxpdmVyc1xuICogICAgIHRvIHRoZSBjdXJyZW50IGNvbnN1bWVyLlxuICogICAtICoqQXV0by1jbGFpbSoqOiBza2lwcGVkIGluIHYxIOKAlCBvcGVyYXRvcnMgc2hvdWxkIHJ1biBYQVVUT0NMQUlNXG4gKiAgICAgcGVyaW9kaWNhbGx5IG91dCBvZiBiYW5kLlxuICpcbiAqIFJlcXVpcmVzIGBpb3JlZGlzYCBhcyBhIHBlZXIgZGVwZW5kZW5jeTpcbiAqXG4gKiAgICAgYnVuIGFkZCBpb3JlZGlzXG4gKlxuICogRW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICogICAtIGBSRURJU19IT1NUYCAoZGVmYXVsdCBgbG9jYWxob3N0YClcbiAqICAgLSBgUkVESVNfUE9SVGAgKGRlZmF1bHQgYDYzNzlgKVxuICogICAtIGBSRURJU19QQVNTV09SRGBcbiAqICAgLSBgUkVESVNfREJgXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBXb3JrZXJUcmlnZ2VyT3B0cyB9IGZyb20gXCJAYmxva2pzL2hlbHBlclwiO1xuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gXCJ1dWlkXCI7XG5pbXBvcnQgdHlwZSB7IFdvcmtlckFkYXB0ZXIsIFdvcmtlckpvYiwgV29ya2VyUXVldWVTdGF0cyB9IGZyb20gXCIuLi9Xb3JrZXJUcmlnZ2VyXCI7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVkaXNTdHJlYW1zQ29uZmlnIHtcblx0aG9zdDogc3RyaW5nO1xuXHRwb3J0OiBudW1iZXI7XG5cdHBhc3N3b3JkPzogc3RyaW5nO1xuXHRkYj86IG51bWJlcjtcblx0YmxvY2tNczogbnVtYmVyO1xuXHRjb3VudDogbnVtYmVyO1xufVxuXG5pbnRlcmZhY2UgUmVkaXNDbGllbnQge1xuXHR4YWRkKHN0cmVhbTogc3RyaW5nLCAuLi5hcmdzOiBzdHJpbmdbXSk6IFByb21pc2U8c3RyaW5nPjtcblx0eHJlYWRncm91cCguLi5hcmdzOiBzdHJpbmdbXSk6IFByb21pc2U8QXJyYXk8W3N0cmluZywgQXJyYXk8W3N0cmluZywgc3RyaW5nW11dPl0+IHwgbnVsbD47XG5cdHhncm91cCguLi5hcmdzOiBzdHJpbmdbXSk6IFByb21pc2U8c3RyaW5nPjtcblx0eGFjayhzdHJlYW06IHN0cmluZywgZ3JvdXA6IHN0cmluZywgLi4uaWRzOiBzdHJpbmdbXSk6IFByb21pc2U8bnVtYmVyPjtcblx0eGxlbihzdHJlYW06IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPjtcblx0eHBlbmRpbmcoc3RyZWFtOiBzdHJpbmcsIGdyb3VwOiBzdHJpbmcpOiBQcm9taXNlPHVua25vd24+O1xuXHRwaW5nKCk6IFByb21pc2U8c3RyaW5nPjtcblx0cXVpdCgpOiBQcm9taXNlPHN0cmluZz47XG59XG5cbmludGVyZmFjZSBRdWV1ZVJ1bm5lciB7XG5cdHN0b3A6IGJvb2xlYW47XG5cdGxvb3BzOiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBRdWV1ZVN0YXRzQ291bnRlcnMge1xuXHRjb21wbGV0ZWQ6IG51bWJlcjtcblx0ZmFpbGVkOiBudW1iZXI7XG5cdGFjdGl2ZTogbnVtYmVyO1xufVxuXG5leHBvcnQgY2xhc3MgUmVkaXNTdHJlYW1zQWRhcHRlciBpbXBsZW1lbnRzIFdvcmtlckFkYXB0ZXIge1xuXHRyZWFkb25seSBwcm92aWRlciA9IFwicmVkaXNcIiBhcyBjb25zdDtcblx0cHJpdmF0ZSByZWFkb25seSBjb25maWc6IFJlZGlzU3RyZWFtc0NvbmZpZztcblx0cHJpdmF0ZSBjbGllbnQ6IFJlZGlzQ2xpZW50IHwgbnVsbCA9IG51bGw7XG5cdHByaXZhdGUgcnVubmVyczogTWFwPHN0cmluZywgUXVldWVSdW5uZXI+ID0gbmV3IE1hcCgpO1xuXHRwcml2YXRlIGNvbm5lY3RlZCA9IGZhbHNlO1xuXHRwcml2YXRlIHN0YXRzOiBNYXA8c3RyaW5nLCBRdWV1ZVN0YXRzQ291bnRlcnM+ID0gbmV3IE1hcCgpO1xuXHRwcml2YXRlIGNvbnN1bWVyTmFtZSA9IGBibG9rLSR7dXVpZCgpLnNsaWNlKDAsIDgpfWA7XG5cblx0Y29uc3RydWN0b3IoY29uZmlnPzogUGFydGlhbDxSZWRpc1N0cmVhbXNDb25maWc+KSB7XG5cdFx0dGhpcy5jb25maWcgPSB7XG5cdFx0XHRob3N0OiBjb25maWc/Lmhvc3QgPz8gcHJvY2Vzcy5lbnYuUkVESVNfSE9TVCA/PyBcImxvY2FsaG9zdFwiLFxuXHRcdFx0cG9ydDogY29uZmlnPy5wb3J0ID8/IE51bWJlci5wYXJzZUludChwcm9jZXNzLmVudi5SRURJU19QT1JUID8/IFwiNjM3OVwiLCAxMCksXG5cdFx0XHRwYXNzd29yZDogY29uZmlnPy5wYXNzd29yZCA/PyBwcm9jZXNzLmVudi5SRURJU19QQVNTV09SRCxcblx0XHRcdGRiOiBjb25maWc/LmRiID8/IE51bWJlci5wYXJzZUludChwcm9jZXNzLmVudi5SRURJU19EQiA/PyBcIjBcIiwgMTApLFxuXHRcdFx0YmxvY2tNczogY29uZmlnPy5ibG9ja01zID8/IDUwMDAsXG5cdFx0XHRjb3VudDogY29uZmlnPy5jb3VudCA/PyAxMCxcblx0XHR9O1xuXHR9XG5cblx0YXN5bmMgY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRpZiAodGhpcy5jb25uZWN0ZWQpIHJldHVybjtcblx0XHR0cnkge1xuXHRcdFx0Ly8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0V4cGxpY2l0QW55OiBpb3JlZGlzIGlzIGEgcnVudGltZSBwZWVyIGRlcC5cblx0XHRcdGNvbnN0IGlvcmVkaXM6IGFueSA9IGF3YWl0IGltcG9ydChcImlvcmVkaXNcIik7XG5cdFx0XHRjb25zdCBJT1JlZGlzID0gaW9yZWRpcy5kZWZhdWx0ID8/IGlvcmVkaXMuUmVkaXMgPz8gaW9yZWRpcztcblx0XHRcdHRoaXMuY2xpZW50ID0gbmV3IElPUmVkaXMoe1xuXHRcdFx0XHRob3N0OiB0aGlzLmNvbmZpZy5ob3N0LFxuXHRcdFx0XHRwb3J0OiB0aGlzLmNvbmZpZy5wb3J0LFxuXHRcdFx0XHRwYXNzd29yZDogdGhpcy5jb25maWcucGFzc3dvcmQsXG5cdFx0XHRcdGRiOiB0aGlzLmNvbmZpZy5kYixcblx0XHRcdH0pIGFzIFJlZGlzQ2xpZW50O1xuXHRcdFx0YXdhaXQgdGhpcy5jbGllbnQucGluZygpO1xuXHRcdFx0dGhpcy5jb25uZWN0ZWQgPSB0cnVlO1xuXHRcdH0gY2F0Y2ggKGVycikge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKFxuXHRcdFx0XHRgW2Jsb2tdW3JlZGlzXSBjb25uZWN0IGZhaWxlZDogJHsoZXJyIGFzIEVycm9yKS5tZXNzYWdlfS4gSW5zdGFsbCBpb3JlZGlzIGFzIGEgcGVlciBkZXBlbmRlbmN5OiBidW4gYWRkIGlvcmVkaXNgLFxuXHRcdFx0KTtcblx0XHR9XG5cdH1cblxuXHRhc3luYyBkaXNjb25uZWN0KCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQpIHJldHVybjtcblx0XHRmb3IgKGNvbnN0IHJ1bm5lciBvZiB0aGlzLnJ1bm5lcnMudmFsdWVzKCkpIHJ1bm5lci5zdG9wID0gdHJ1ZTtcblx0XHQvLyBXYWl0IGZvciBpbi1mbGlnaHQgbG9vcHMgdG8gZHJhaW4uXG5cdFx0Y29uc3QgZHJhaW5EZWFkbGluZSA9IERhdGUubm93KCkgKyAyMDAwO1xuXHRcdHdoaWxlIChEYXRlLm5vdygpIDwgZHJhaW5EZWFkbGluZSkge1xuXHRcdFx0bGV0IGFjdGl2ZSA9IDA7XG5cdFx0XHRmb3IgKGNvbnN0IHIgb2YgdGhpcy5ydW5uZXJzLnZhbHVlcygpKSBhY3RpdmUgKz0gci5sb29wcztcblx0XHRcdGlmIChhY3RpdmUgPT09IDApIGJyZWFrO1xuXHRcdFx0YXdhaXQgbmV3IFByb21pc2UoKHIpID0+IHNldFRpbWVvdXQociwgNTApKTtcblx0XHR9XG5cdFx0dGhpcy5ydW5uZXJzLmNsZWFyKCk7XG5cdFx0dHJ5IHtcblx0XHRcdGF3YWl0IHRoaXMuY2xpZW50Py5xdWl0KCk7XG5cdFx0fSBjYXRjaCB7XG5cdFx0XHQvKiBpZ25vcmUgKi9cblx0XHR9XG5cdFx0dGhpcy5jbGllbnQgPSBudWxsO1xuXHRcdHRoaXMuY29ubmVjdGVkID0gZmFsc2U7XG5cdH1cblxuXHRhc3luYyBwcm9jZXNzKGNvbmZpZzogV29ya2VyVHJpZ2dlck9wdHMsIGhhbmRsZXI6IChqb2I6IFdvcmtlckpvYikgPT4gUHJvbWlzZTx2b2lkPik6IFByb21pc2U8dm9pZD4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQgfHwgIXRoaXMuY2xpZW50KSB0aHJvdyBuZXcgRXJyb3IoXCJbYmxva11bcmVkaXNdIG5vdCBjb25uZWN0ZWQuIENhbGwgY29ubmVjdCgpIGZpcnN0LlwiKTtcblx0XHRjb25zdCBzdHJlYW0gPSBjb25maWcucXVldWU7XG5cdFx0Y29uc3QgZ3JvdXAgPSBjb25maWcuY29uc3VtZXJHcm91cCA/PyBgJHtzdHJlYW19LWdyb3VwYDtcblx0XHQvLyBDcmVhdGUgZ3JvdXAgKyBzdHJlYW0gaWYgbWlzc2luZy4gWEdST1VQIENSRUFURSB3aXRoIE1LU1RSRUFNXG5cdFx0Ly8gZXJyb3JzIHdpdGggXCJCVVNZR1JPVVBcIiBpZiB0aGUgZ3JvdXAgYWxyZWFkeSBleGlzdHMg4oCUIHN3YWxsb3cgaXQuXG5cdFx0dHJ5IHtcblx0XHRcdGF3YWl0IHRoaXMuY2xpZW50Lnhncm91cChcIkNSRUFURVwiLCBzdHJlYW0sIGdyb3VwLCBcIiRcIiwgXCJNS1NUUkVBTVwiKTtcblx0XHR9IGNhdGNoIChlcnIpIHtcblx0XHRcdGlmICghL0JVU1lHUk9VUC9pLnRlc3QoKGVyciBhcyBFcnJvcikubWVzc2FnZSkpIHRocm93IGVycjtcblx0XHR9XG5cblx0XHRjb25zdCBydW5uZXI6IFF1ZXVlUnVubmVyID0geyBzdG9wOiBmYWxzZSwgbG9vcHM6IDAgfTtcblx0XHR0aGlzLnJ1bm5lcnMuc2V0KHN0cmVhbSwgcnVubmVyKTtcblx0XHR0aGlzLnN0YXRzLnNldChzdHJlYW0sIHsgY29tcGxldGVkOiAwLCBmYWlsZWQ6IDAsIGFjdGl2ZTogMCB9KTtcblx0XHRjb25zdCBzdGF0cyA9IHRoaXMuc3RhdHMuZ2V0KHN0cmVhbSkgYXMgUXVldWVTdGF0c0NvdW50ZXJzO1xuXG5cdFx0Y29uc3QgY29uY3VycmVuY3kgPSBNYXRoLm1heCgxLCBjb25maWcuY29uY3VycmVuY3kgPz8gMSk7XG5cdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCBjb25jdXJyZW5jeTsgaSArPSAxKSB7XG5cdFx0XHR2b2lkIHRoaXMucnVuQ29uc3VtZXJMb29wKHN0cmVhbSwgZ3JvdXAsIGNvbmZpZywgaGFuZGxlciwgcnVubmVyLCBzdGF0cyk7XG5cdFx0fVxuXHR9XG5cblx0cHJpdmF0ZSBhc3luYyBydW5Db25zdW1lckxvb3AoXG5cdFx0c3RyZWFtOiBzdHJpbmcsXG5cdFx0Z3JvdXA6IHN0cmluZyxcblx0XHRjb25maWc6IFdvcmtlclRyaWdnZXJPcHRzLFxuXHRcdGhhbmRsZXI6IChqb2I6IFdvcmtlckpvYikgPT4gUHJvbWlzZTx2b2lkPixcblx0XHRydW5uZXI6IFF1ZXVlUnVubmVyLFxuXHRcdHN0YXRzOiBRdWV1ZVN0YXRzQ291bnRlcnMsXG5cdCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGlmICghdGhpcy5jbGllbnQpIHJldHVybjtcblx0XHRydW5uZXIubG9vcHMgKz0gMTtcblx0XHR0cnkge1xuXHRcdFx0d2hpbGUgKCFydW5uZXIuc3RvcCkge1xuXHRcdFx0XHRsZXQgZW50cmllczogQXJyYXk8W3N0cmluZywgQXJyYXk8W3N0cmluZywgc3RyaW5nW11dPl0+IHwgbnVsbCA9IG51bGw7XG5cdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0ZW50cmllcyA9IGF3YWl0IHRoaXMuY2xpZW50LnhyZWFkZ3JvdXAoXG5cdFx0XHRcdFx0XHRcIkdST1VQXCIsXG5cdFx0XHRcdFx0XHRncm91cCxcblx0XHRcdFx0XHRcdHRoaXMuY29uc3VtZXJOYW1lLFxuXHRcdFx0XHRcdFx0XCJDT1VOVFwiLFxuXHRcdFx0XHRcdFx0U3RyaW5nKHRoaXMuY29uZmlnLmNvdW50KSxcblx0XHRcdFx0XHRcdFwiQkxPQ0tcIixcblx0XHRcdFx0XHRcdFN0cmluZyh0aGlzLmNvbmZpZy5ibG9ja01zKSxcblx0XHRcdFx0XHRcdFwiU1RSRUFNU1wiLFxuXHRcdFx0XHRcdFx0c3RyZWFtLFxuXHRcdFx0XHRcdFx0XCI+XCIsXG5cdFx0XHRcdFx0KTtcblx0XHRcdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHRcdFx0YXdhaXQgbmV3IFByb21pc2UoKHIpID0+IHNldFRpbWVvdXQociwgMTAwMCkpO1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICghZW50cmllcykgY29udGludWU7XG5cdFx0XHRcdGZvciAoY29uc3QgWywgbXNnc10gb2YgZW50cmllcykge1xuXHRcdFx0XHRcdGZvciAoY29uc3QgW2lkLCBmaWVsZHNdIG9mIG1zZ3MpIHtcblx0XHRcdFx0XHRcdGlmIChydW5uZXIuc3RvcCkgYnJlYWs7XG5cdFx0XHRcdFx0XHRzdGF0cy5hY3RpdmUgKz0gMTtcblx0XHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRcdGNvbnN0IHBheWxvYWQgPSB0aGlzLmZpZWxkc1RvT2JqZWN0KGZpZWxkcyk7XG5cdFx0XHRcdFx0XHRcdGNvbnN0IGRhdGFTdHJpbmcgPSB0eXBlb2YgcGF5bG9hZC5kYXRhID09PSBcInN0cmluZ1wiID8gcGF5bG9hZC5kYXRhIDogXCJcIjtcblx0XHRcdFx0XHRcdFx0bGV0IGRhdGE6IHVua25vd247XG5cdFx0XHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRcdFx0ZGF0YSA9IGRhdGFTdHJpbmcubGVuZ3RoID4gMCA/IEpTT04ucGFyc2UoZGF0YVN0cmluZykgOiBudWxsO1xuXHRcdFx0XHRcdFx0XHR9IGNhdGNoIHtcblx0XHRcdFx0XHRcdFx0XHRkYXRhID0gZGF0YVN0cmluZztcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHRjb25zdCBqb2I6IFdvcmtlckpvYiA9IHtcblx0XHRcdFx0XHRcdFx0XHRpZCxcblx0XHRcdFx0XHRcdFx0XHRkYXRhLFxuXHRcdFx0XHRcdFx0XHRcdGhlYWRlcnM6IHt9LFxuXHRcdFx0XHRcdFx0XHRcdHF1ZXVlOiBzdHJlYW0sXG5cdFx0XHRcdFx0XHRcdFx0cHJpb3JpdHk6IGNvbmZpZy5wcmlvcml0eSA/PyAwLFxuXHRcdFx0XHRcdFx0XHRcdGF0dGVtcHRzOiAwLFxuXHRcdFx0XHRcdFx0XHRcdG1heFJldHJpZXM6IGNvbmZpZy5yZXRyaWVzID8/IDAsXG5cdFx0XHRcdFx0XHRcdFx0Y3JlYXRlZEF0OiBuZXcgRGF0ZShOdW1iZXIucGFyc2VJbnQoaWQuc3BsaXQoXCItXCIpWzBdID8/IFN0cmluZyhEYXRlLm5vdygpKSwgMTApKSxcblx0XHRcdFx0XHRcdFx0XHR0aW1lb3V0OiBjb25maWcudGltZW91dCxcblx0XHRcdFx0XHRcdFx0XHRyYXc6IHsgaWQsIGZpZWxkcyB9LFxuXHRcdFx0XHRcdFx0XHRcdGNvbXBsZXRlOiBhc3luYyAoKSA9PiB7XG5cdFx0XHRcdFx0XHRcdFx0XHRhd2FpdCB0aGlzLmNsaWVudD8ueGFjayhzdHJlYW0sIGdyb3VwLCBpZCk7XG5cdFx0XHRcdFx0XHRcdFx0XHRzdGF0cy5jb21wbGV0ZWQgKz0gMTtcblx0XHRcdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdFx0XHRcdGZhaWw6IGFzeW5jIChfZXJyOiBFcnJvcikgPT4ge1xuXHRcdFx0XHRcdFx0XHRcdFx0c3RhdHMuZmFpbGVkICs9IDE7XG5cdFx0XHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHRcdFx0fTtcblx0XHRcdFx0XHRcdFx0YXdhaXQgaGFuZGxlcihqb2IpO1xuXHRcdFx0XHRcdFx0XHRpZiAoY29uZmlnLmFjayAhPT0gZmFsc2UpIGF3YWl0IHRoaXMuY2xpZW50Py54YWNrKHN0cmVhbSwgZ3JvdXAsIGlkKTtcblx0XHRcdFx0XHRcdFx0c3RhdHMuY29tcGxldGVkICs9IDE7XG5cdFx0XHRcdFx0XHR9IGNhdGNoIHtcblx0XHRcdFx0XHRcdFx0c3RhdHMuZmFpbGVkICs9IDE7XG5cdFx0XHRcdFx0XHRcdC8vIFBlbmRpbmcgZW50cnkgbGVmdCB1bmFja2VkIOKAlCBwaWNrZWQgdXAgYnkgWEFVVE9DTEFJTS5cblx0XHRcdFx0XHRcdH0gZmluYWxseSB7XG5cdFx0XHRcdFx0XHRcdHN0YXRzLmFjdGl2ZSA9IE1hdGgubWF4KDAsIHN0YXRzLmFjdGl2ZSAtIDEpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0gZmluYWxseSB7XG5cdFx0XHRydW5uZXIubG9vcHMgLT0gMTtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIGZpZWxkc1RvT2JqZWN0KGZpZWxkczogc3RyaW5nW10pOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHtcblx0XHRjb25zdCBvdXQ6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblx0XHRmb3IgKGxldCBpID0gMDsgaSA8IGZpZWxkcy5sZW5ndGg7IGkgKz0gMikge1xuXHRcdFx0b3V0W2ZpZWxkc1tpXV0gPSBmaWVsZHNbaSArIDFdO1xuXHRcdH1cblx0XHRyZXR1cm4gb3V0O1xuXHR9XG5cblx0YXN5bmMgYWRkSm9iKFxuXHRcdHF1ZXVlOiBzdHJpbmcsXG5cdFx0ZGF0YTogdW5rbm93bixcblx0XHRvcHRzPzogeyBwcmlvcml0eT86IG51bWJlcjsgZGVsYXk/OiBudW1iZXI7IHJldHJpZXM/OiBudW1iZXI7IHRpbWVvdXQ/OiBudW1iZXI7IGpvYklkPzogc3RyaW5nIH0sXG5cdCk6IFByb21pc2U8c3RyaW5nPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCB8fCAhdGhpcy5jbGllbnQpIHRocm93IG5ldyBFcnJvcihcIltibG9rXVtyZWRpc10gbm90IGNvbm5lY3RlZC4gQ2FsbCBjb25uZWN0KCkgZmlyc3QuXCIpO1xuXHRcdGNvbnN0IHBheWxvYWQgPSB0eXBlb2YgZGF0YSA9PT0gXCJzdHJpbmdcIiA/IGRhdGEgOiBKU09OLnN0cmluZ2lmeShkYXRhKTtcblx0XHRjb25zdCBhcmdzOiBzdHJpbmdbXSA9IFtdO1xuXHRcdGlmIChvcHRzPy5qb2JJZCkgYXJncy5wdXNoKFwiTk9NS1NUUkVBTVwiKTtcblx0XHRjb25zdCBpZCA9IGF3YWl0IHRoaXMuY2xpZW50LnhhZGQocXVldWUsIFwiKlwiLCBcImRhdGFcIiwgcGF5bG9hZCwgXCJqb2JJZFwiLCBvcHRzPy5qb2JJZCA/PyBcIlwiKTtcblx0XHRyZXR1cm4gaWQ7XG5cdH1cblxuXHRhc3luYyBzdG9wUHJvY2Vzc2luZyhxdWV1ZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0Y29uc3QgcnVubmVyID0gdGhpcy5ydW5uZXJzLmdldChxdWV1ZSk7XG5cdFx0aWYgKHJ1bm5lcikgcnVubmVyLnN0b3AgPSB0cnVlO1xuXHR9XG5cblx0aXNDb25uZWN0ZWQoKTogYm9vbGVhbiB7XG5cdFx0cmV0dXJuIHRoaXMuY29ubmVjdGVkO1xuXHR9XG5cblx0YXN5bmMgaGVhbHRoQ2hlY2soKTogUHJvbWlzZTxib29sZWFuPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCB8fCAhdGhpcy5jbGllbnQpIHJldHVybiBmYWxzZTtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgcG9uZyA9IGF3YWl0IHRoaXMuY2xpZW50LnBpbmcoKTtcblx0XHRcdHJldHVybiBwb25nID09PSBcIlBPTkdcIjtcblx0XHR9IGNhdGNoIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdH1cblxuXHRhc3luYyBnZXRRdWV1ZVN0YXRzKHF1ZXVlOiBzdHJpbmcpOiBQcm9taXNlPFdvcmtlclF1ZXVlU3RhdHM+IHtcblx0XHRjb25zdCBjb3VudGVycyA9IHRoaXMuc3RhdHMuZ2V0KHF1ZXVlKSA/PyB7IGNvbXBsZXRlZDogMCwgZmFpbGVkOiAwLCBhY3RpdmU6IDAgfTtcblx0XHRsZXQgd2FpdGluZyA9IDA7XG5cdFx0dHJ5IHtcblx0XHRcdHdhaXRpbmcgPSAoYXdhaXQgdGhpcy5jbGllbnQ/LnhsZW4ocXVldWUpKSA/PyAwO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0LyogaWdub3JlICovXG5cdFx0fVxuXHRcdHJldHVybiB7XG5cdFx0XHR3YWl0aW5nLFxuXHRcdFx0YWN0aXZlOiBjb3VudGVycy5hY3RpdmUsXG5cdFx0XHRjb21wbGV0ZWQ6IGNvdW50ZXJzLmNvbXBsZXRlZCxcblx0XHRcdGZhaWxlZDogY291bnRlcnMuZmFpbGVkLFxuXHRcdFx0ZGVsYXllZDogMCxcblx0XHR9O1xuXHR9XG59XG4iXX0=
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQSAdapter — v0.7 PR 5 — Worker adapter backed by AWS SQS via
|
|
3
|
+
* `@aws-sdk/client-sqs`. Polls a queue URL (the `queue` field) with
|
|
4
|
+
* long-polling; processes messages with manual delete (ACK).
|
|
5
|
+
*
|
|
6
|
+
* Semantics:
|
|
7
|
+
* - **Long polling**: `WaitTimeSeconds=20` by default — minimises
|
|
8
|
+
* poll cost. `concurrency` controls how many parallel
|
|
9
|
+
* `ReceiveMessage` loops run.
|
|
10
|
+
* - **Visibility timeout**: configured via `timeout` (ms → s).
|
|
11
|
+
* Messages reappear after this if the worker doesn't delete them.
|
|
12
|
+
* - **Retries**: SQS handles retries automatically via redrive
|
|
13
|
+
* policy on the queue itself. The adapter doesn't simulate
|
|
14
|
+
* retries client-side — set `MaxReceiveCount` on the queue's
|
|
15
|
+
* redrive policy and a DLQ via `deadLetterQueue`.
|
|
16
|
+
* - **FIFO support**: when the queue URL ends with `.fifo`, the
|
|
17
|
+
* adapter passes `MessageGroupId` from `dedupId` or a default.
|
|
18
|
+
*
|
|
19
|
+
* Requires `@aws-sdk/client-sqs` as a peer dependency:
|
|
20
|
+
*
|
|
21
|
+
* bun add @aws-sdk/client-sqs
|
|
22
|
+
*
|
|
23
|
+
* Environment variables (standard AWS SDK):
|
|
24
|
+
* - `AWS_REGION` — default `us-east-1`.
|
|
25
|
+
* - `AWS_ACCESS_KEY_ID` — credentials (or use a profile).
|
|
26
|
+
* - `AWS_SECRET_ACCESS_KEY`
|
|
27
|
+
* - `SQS_ENDPOINT_URL` — for local SQS (LocalStack / ElasticMQ).
|
|
28
|
+
*/
|
|
29
|
+
import type { WorkerTriggerOpts } from "@blokjs/helper";
|
|
30
|
+
import type { WorkerAdapter, WorkerJob, WorkerQueueStats } from "../WorkerTrigger";
|
|
31
|
+
export interface SQSConfig {
|
|
32
|
+
region: string;
|
|
33
|
+
endpoint?: string;
|
|
34
|
+
waitTimeSeconds: number;
|
|
35
|
+
maxNumberOfMessages: number;
|
|
36
|
+
}
|
|
37
|
+
export declare class SQSAdapter implements WorkerAdapter {
|
|
38
|
+
readonly provider: "sqs";
|
|
39
|
+
private readonly config;
|
|
40
|
+
private client;
|
|
41
|
+
private commands;
|
|
42
|
+
private runners;
|
|
43
|
+
private connected;
|
|
44
|
+
private stats;
|
|
45
|
+
constructor(config?: Partial<SQSConfig>);
|
|
46
|
+
connect(): Promise<void>;
|
|
47
|
+
disconnect(): Promise<void>;
|
|
48
|
+
process(config: WorkerTriggerOpts, handler: (job: WorkerJob) => Promise<void>): Promise<void>;
|
|
49
|
+
private runConsumerLoop;
|
|
50
|
+
addJob(queue: string, data: unknown, opts?: {
|
|
51
|
+
priority?: number;
|
|
52
|
+
delay?: number;
|
|
53
|
+
retries?: number;
|
|
54
|
+
timeout?: number;
|
|
55
|
+
jobId?: string;
|
|
56
|
+
}): Promise<string>;
|
|
57
|
+
stopProcessing(queue: string): Promise<void>;
|
|
58
|
+
isConnected(): boolean;
|
|
59
|
+
healthCheck(): Promise<boolean>;
|
|
60
|
+
getQueueStats(queue: string): Promise<WorkerQueueStats>;
|
|
61
|
+
}
|