@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,251 @@
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 { v4 as uuid } from "uuid";
27
+ export class PgBossAdapter {
28
+ provider = "pg-boss";
29
+ config;
30
+ boss = null;
31
+ workIds = new Map();
32
+ connected = false;
33
+ stats = new Map();
34
+ constructor(config) {
35
+ this.config = {
36
+ connectionString: config?.connectionString ?? process.env.DATABASE_URL ?? "postgres://localhost:5432/blok",
37
+ schema: config?.schema ?? process.env.PG_BOSS_SCHEMA ?? "pgboss",
38
+ };
39
+ }
40
+ async connect() {
41
+ if (this.connected)
42
+ return;
43
+ try {
44
+ // Indirect specifier so tsc doesn't try to resolve types for
45
+ // the optional peer dependency at build time.
46
+ const moduleName = "pg-boss";
47
+ // biome-ignore lint/suspicious/noExplicitAny: pg-boss is a runtime peer dep.
48
+ const mod = await import(moduleName);
49
+ // biome-ignore lint/suspicious/noExplicitAny: pg-boss is a runtime peer dep.
50
+ const PgBoss = mod.default ?? mod;
51
+ this.boss = new PgBoss({
52
+ connectionString: this.config.connectionString,
53
+ schema: this.config.schema,
54
+ });
55
+ await this.boss.start();
56
+ this.connected = true;
57
+ }
58
+ catch (err) {
59
+ throw new Error(`[blok][pg-boss] connect failed: ${err.message}. Install pg-boss as a peer dependency: bun add pg-boss`);
60
+ }
61
+ }
62
+ async disconnect() {
63
+ if (!this.connected)
64
+ return;
65
+ try {
66
+ await this.boss?.stop({ graceful: true });
67
+ }
68
+ catch {
69
+ /* ignore */
70
+ }
71
+ this.workIds.clear();
72
+ this.boss = null;
73
+ this.connected = false;
74
+ }
75
+ async process(config, handler) {
76
+ if (!this.connected || !this.boss)
77
+ throw new Error("[blok][pg-boss] not connected. Call connect() first.");
78
+ this.stats.set(config.queue, { completed: 0, failed: 0, active: 0 });
79
+ const stats = this.stats.get(config.queue);
80
+ // pg-boss v10 requires explicit queue creation before send/work.
81
+ // The call is idempotent — succeeds whether the queue exists or
82
+ // not — so this is safe to repeat across re-entries.
83
+ await this.ensureQueue(config.queue);
84
+ // pg-boss v10's handler receives an ARRAY of jobs (batch); the
85
+ // `batchSize` option controls the max. We keep `batchSize: 1` so
86
+ // the iteration is a single-job loop (matches pre-v10 semantics
87
+ // the adapter was written against). `pollingIntervalSeconds`
88
+ // defaults to 2s which adds ~2s latency on the happy path —
89
+ // tighten it for low-latency workloads via the worker config.
90
+ const id = await this.boss.work(config.queue, {
91
+ batchSize: 1,
92
+ includeMetadata: true,
93
+ }, async (jobOrBatch) => {
94
+ const jobs = Array.isArray(jobOrBatch) ? jobOrBatch : [jobOrBatch];
95
+ for (const job of jobs) {
96
+ await this.runOneJob(config, handler, stats, job);
97
+ }
98
+ });
99
+ this.workIds.set(config.queue, id);
100
+ }
101
+ async runOneJob(config, handler, stats, job) {
102
+ stats.active += 1;
103
+ // Tracks whether stats have already been counted by the
104
+ // WorkerJob API (`complete` / `fail`). Without this, both
105
+ // the callback and the wrapper's try/catch credit the same
106
+ // outcome, double-counting `stats.completed` /
107
+ // `stats.failed`. pg-boss itself handles ack/retry/DLQ via
108
+ // the handler's return-vs-throw — these callbacks exist
109
+ // purely for stats parity with the other worker adapters.
110
+ let settled = false;
111
+ const workerJob = {
112
+ id: job.id,
113
+ data: job.data,
114
+ headers: {},
115
+ queue: config.queue,
116
+ priority: config.priority ?? 0,
117
+ attempts: 0,
118
+ maxRetries: config.retries ?? 0,
119
+ createdAt: new Date(),
120
+ timeout: config.timeout,
121
+ raw: job,
122
+ complete: async () => {
123
+ if (settled)
124
+ return;
125
+ stats.completed += 1;
126
+ settled = true;
127
+ },
128
+ fail: async (err) => {
129
+ if (settled)
130
+ return;
131
+ stats.failed += 1;
132
+ settled = true;
133
+ // Re-throw so pg-boss's `boss.work` sees the handler
134
+ // failure and schedules a retry / drops to DLQ per
135
+ // the queue config — the contract is "handler throws
136
+ // => job failed".
137
+ throw err;
138
+ },
139
+ };
140
+ try {
141
+ await handler(workerJob);
142
+ if (!settled) {
143
+ stats.completed += 1;
144
+ settled = true;
145
+ }
146
+ }
147
+ catch (err) {
148
+ if (!settled) {
149
+ stats.failed += 1;
150
+ settled = true;
151
+ }
152
+ throw err;
153
+ }
154
+ finally {
155
+ stats.active = Math.max(0, stats.active - 1);
156
+ }
157
+ }
158
+ async ensureQueue(queue) {
159
+ if (!this.boss)
160
+ return;
161
+ try {
162
+ await this.boss.createQueue(queue);
163
+ }
164
+ catch (err) {
165
+ // pg-boss v10's createQueue is idempotent but may throw on
166
+ // race conditions when multiple processes call it concurrently
167
+ // against a fresh schema — those errors are harmless once the
168
+ // queue exists. Swallow + let downstream send/work surface a
169
+ // real problem if the queue isn't usable.
170
+ const message = err.message ?? "";
171
+ if (!message.includes("already exists")) {
172
+ // Log the unexpected case but don't propagate — operators
173
+ // shouldn't see a queue-creation race blow up their
174
+ // trigger boot path.
175
+ console.warn(`[blok][pg-boss] ensureQueue(${queue}) warning:`, message);
176
+ }
177
+ }
178
+ }
179
+ async addJob(queue, data, opts) {
180
+ if (!this.connected || !this.boss)
181
+ throw new Error("[blok][pg-boss] not connected. Call connect() first.");
182
+ // pg-boss v10 requires the queue to exist before send. The
183
+ // call is idempotent — cheap to repeat per add.
184
+ await this.ensureQueue(queue);
185
+ // pg-boss v10's `attorney.checkSendArgs` rejects any explicitly
186
+ // undefined `priority` / `retryLimit` / `startAfter` /
187
+ // `expireInSeconds` / `singletonKey` with "X must be an integer".
188
+ // Build the options object with only the keys we actually want
189
+ // set — caught by the real-broker integration test in
190
+ // `__tests__/integration/pgboss-adapter.real-pg.test.ts`.
191
+ const sendOpts = {};
192
+ if (typeof opts?.priority === "number")
193
+ sendOpts.priority = opts.priority;
194
+ if (typeof opts?.delay === "number" && opts.delay > 0) {
195
+ sendOpts.startAfter = Math.ceil(opts.delay / 1000);
196
+ }
197
+ if (typeof opts?.retries === "number")
198
+ sendOpts.retryLimit = opts.retries;
199
+ if (typeof opts?.timeout === "number")
200
+ sendOpts.expireInSeconds = Math.ceil(opts.timeout / 1000);
201
+ if (typeof opts?.jobId === "string" && opts.jobId.length > 0)
202
+ sendOpts.singletonKey = opts.jobId;
203
+ const jobId = await this.boss.send(queue, data, sendOpts);
204
+ return jobId ?? opts?.jobId ?? uuid();
205
+ }
206
+ async stopProcessing(queue) {
207
+ if (!this.connected || !this.boss)
208
+ return;
209
+ try {
210
+ await this.boss.offWork(queue);
211
+ }
212
+ catch {
213
+ /* ignore */
214
+ }
215
+ this.workIds.delete(queue);
216
+ }
217
+ isConnected() {
218
+ return this.connected;
219
+ }
220
+ async healthCheck() {
221
+ if (!this.connected || !this.boss)
222
+ return false;
223
+ try {
224
+ await this.boss.getQueueSize("__health_check__");
225
+ return true;
226
+ }
227
+ catch {
228
+ // getQueueSize on a non-existent queue may throw — fall back to
229
+ // "connection is live" by checking the boss instance attribute.
230
+ return this.connected;
231
+ }
232
+ }
233
+ async getQueueStats(queue) {
234
+ const counters = this.stats.get(queue) ?? { completed: 0, failed: 0, active: 0 };
235
+ let waiting = 0;
236
+ try {
237
+ waiting = (await this.boss?.getQueueSize(queue)) ?? 0;
238
+ }
239
+ catch {
240
+ /* ignore */
241
+ }
242
+ return {
243
+ waiting,
244
+ active: counters.active,
245
+ completed: counters.completed,
246
+ failed: counters.failed,
247
+ delayed: 0,
248
+ };
249
+ }
250
+ }
251
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGdCb3NzQWRhcHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hZGFwdGVycy9QZ0Jvc3NBZGFwdGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F3Qkc7QUFHSCxPQUFPLEVBQUUsRUFBRSxJQUFJLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQWtDbEMsTUFBTSxPQUFPLGFBQWE7SUFDaEIsUUFBUSxHQUFHLFNBQWtCLENBQUM7SUFDdEIsTUFBTSxDQUFlO0lBQzlCLElBQUksR0FBMEIsSUFBSSxDQUFDO0lBQ25DLE9BQU8sR0FBd0IsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUN6QyxTQUFTLEdBQUcsS0FBSyxDQUFDO0lBQ2xCLEtBQUssR0FBb0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUUzRCxZQUFZLE1BQThCO1FBQ3pDLElBQUksQ0FBQyxNQUFNLEdBQUc7WUFDYixnQkFBZ0IsRUFBRSxNQUFNLEVBQUUsZ0JBQWdCLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLElBQUksZ0NBQWdDO1lBQzFHLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxJQUFJLFFBQVE7U0FDaEUsQ0FBQztJQUNILENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTztRQUNaLElBQUksSUFBSSxDQUFDLFNBQVM7WUFBRSxPQUFPO1FBQzNCLElBQUksQ0FBQztZQUNKLDZEQUE2RDtZQUM3RCw4Q0FBOEM7WUFDOUMsTUFBTSxVQUFVLEdBQUcsU0FBUyxDQUFDO1lBQzdCLDZFQUE2RTtZQUM3RSxNQUFNLEdBQUcsR0FBUSxNQUFNLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUMxQyw2RUFBNkU7WUFDN0UsTUFBTSxNQUFNLEdBQVEsR0FBRyxDQUFDLE9BQU8sSUFBSSxHQUFHLENBQUM7WUFDdkMsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJ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LE9BQU8sR0FBRyxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksRUFBRSxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDdkQsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNSLFlBQVk7UUFDYixDQUFDO1FBQ0QsT0FBTztZQUNOLE9BQU87WUFDUCxNQUFNLEVBQUUsUUFBUSxDQUFDLE1BQU07WUFDdkIsU0FBUyxFQUFFLFFBQVEsQ0FBQyxTQUFTO1lBQzdCLE1BQU0sRUFBRSxRQUFRLENBQUMsTUFBTTtZQUN2QixPQUFPLEVBQUUsQ0FBQztTQUNWLENBQUM7SUFDSCxDQUFDO0NBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBnQm9zc0FkYXB0ZXIg4oCUIHYwLjcgUFIgNSDigJQgV29ya2VyIGFkYXB0ZXIgYmFja2VkIGJ5IGBwZy1ib3NzYCxcbiAqIHdoaWNoIHN0b3JlcyBqb2JzIGluIFBvc3RncmVTUUwuIE9wZXJhdGlvbmFsbHkgYXR0cmFjdGl2ZSB3aGVuXG4gKiBQb3N0Z3JlcyBpcyBhbHJlYWR5IGluIHRoZSBzdGFjayDigJQgbm8gZXh0cmEgYnJva2VyIGluZnJhIG5lZWRlZC5cbiAqXG4gKiBTZW1hbnRpY3M6XG4gKiAgIC0gYHBnLWJvc3NgIGhhbmRsZXMgY29uY3VycmVuY3ksIHJldHJpZXMsIGRlYWQtbGV0dGVyLCBwcmlvcml0eSxcbiAqICAgICBkZWxheXMsIGFuZCBleGFjdGx5LW9uY2Ugc2NoZWR1bGluZyBuYXRpdmVseSDigJQgdGhlIGFkYXB0ZXJcbiAqICAgICBmb3J3YXJkcyBgY29uZmlnLmNvbmN1cnJlbmN5YCwgYGNvbmZpZy5yZXRyaWVzYCwgZXRjLiB0byB0aGVcbiAqICAgICBgYm9zcy53b3JrKHF1ZXVlLCBvcHRzLCBoYW5kbGVyKWAgYW5kIGBib3NzLnNlbmQocXVldWUsIGRhdGEsIG9wdHMpYFxuICogICAgIGNhbGxzIGRpcmVjdGx5LlxuICogICAtICoqU2luZ2xlIGNvbm5lY3Rpb24gcGVyIHByb2Nlc3MqKiDigJQgYHBnLWJvc3NgIG1hbmFnZXMgaXRzIG93blxuICogICAgIHBvb2w7IHdlIGluc3RhbnRpYXRlIG9uZSBgUGdCb3NzYCBpbnN0YW5jZSBwZXIgYFBnQm9zc0FkYXB0ZXJgLlxuICogICAtICoqU2NoZW1hKio6IGBwZy1ib3NzYCBjcmVhdGVzIGl0cyBvd24gc2NoZW1hIChgcGdib3NzYCBieVxuICogICAgIGRlZmF1bHQpIG9uIGZpcnN0IHN0YXJ0LiBUYWJsZXMgYXJlIG1pZ3JhdGVkIGF1dG9tYXRpY2FsbHkuXG4gKlxuICogUmVxdWlyZXMgYHBnLWJvc3NgIGFzIGEgcGVlciBkZXBlbmRlbmN5OlxuICpcbiAqICAgICBidW4gYWRkIHBnLWJvc3NcbiAqXG4gKiBFbnZpcm9ubWVudCB2YXJpYWJsZXM6XG4gKiAgIC0gYERBVEFCQVNFX1VSTGAg4oCUIFBvc3RncmVzIGNvbm5lY3Rpb24gc3RyaW5nIChkZWZhdWx0XG4gKiAgICAgYHBvc3RncmVzOi8vbG9jYWxob3N0OjU0MzIvYmxva2ApLlxuICogICAtIGBQR19CT1NTX1NDSEVNQWAg4oCUIHNjaGVtYSBuYW1lIChkZWZhdWx0IGBcInBnYm9zc1wiYCkuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBXb3JrZXJUcmlnZ2VyT3B0cyB9IGZyb20gXCJAYmxva2pzL2hlbHBlclwiO1xuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gXCJ1dWlkXCI7XG5pbXBvcnQgdHlwZSB7IFdvcmtlckFkYXB0ZXIsIFdvcmtlckpvYiwgV29ya2VyUXVldWVTdGF0cyB9IGZyb20gXCIuLi9Xb3JrZXJUcmlnZ2VyXCI7XG5cbmV4cG9ydCBpbnRlcmZhY2UgUGdCb3NzQ29uZmlnIHtcblx0Y29ubmVjdGlvblN0cmluZzogc3RyaW5nO1xuXHRzY2hlbWE/OiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBQZ0Jvc3NJbnN0YW5jZSB7XG5cdHN0YXJ0KCk6IFByb21pc2U8dW5rbm93bj47XG5cdHN0b3Aob3B0cz86IHsgZ3JhY2VmdWw/OiBib29sZWFuIH0pOiBQcm9taXNlPHZvaWQ+O1xuXHRjcmVhdGVRdWV1ZShxdWV1ZTogc3RyaW5nLCBvcHRzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBQcm9taXNlPHVua25vd24+O1xuXHRzZW5kKHF1ZXVlOiBzdHJpbmcsIGRhdGE6IHVua25vd24sIG9wdHM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IFByb21pc2U8c3RyaW5nIHwgbnVsbD47XG5cdHdvcmsoXG5cdFx0cXVldWU6IHN0cmluZyxcblx0XHRvcHRzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcblx0XHRoYW5kbGVyOiAoam9iOiBQZ0Jvc3NKb2IgfCBQZ0Jvc3NKb2JbXSkgPT4gUHJvbWlzZTx1bmtub3duPixcblx0KTogUHJvbWlzZTxzdHJpbmc+O1xuXHRvZmZXb3JrKHF1ZXVlOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+O1xuXHRnZXRRdWV1ZVNpemUocXVldWU6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPjtcbn1cblxuaW50ZXJmYWNlIFBnQm9zc0pvYiB7XG5cdGlkOiBzdHJpbmc7XG5cdGRhdGE6IHVua25vd247XG5cdG5hbWU6IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIFF1ZXVlU3RhdHNDb3VudGVycyB7XG5cdGNvbXBsZXRlZDogbnVtYmVyO1xuXHRmYWlsZWQ6IG51bWJlcjtcblx0YWN0aXZlOiBudW1iZXI7XG59XG5cbmV4cG9ydCBjbGFzcyBQZ0Jvc3NBZGFwdGVyIGltcGxlbWVudHMgV29ya2VyQWRhcHRlciB7XG5cdHJlYWRvbmx5IHByb3ZpZGVyID0gXCJwZy1ib3NzXCIgYXMgY29uc3Q7XG5cdHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBQZ0Jvc3NDb25maWc7XG5cdHByaXZhdGUgYm9zczogUGdCb3NzSW5zdGFuY2UgfCBudWxsID0gbnVsbDtcblx0cHJpdmF0ZSB3b3JrSWRzOiBNYXA8c3RyaW5nLCBzdHJpbmc+ID0gbmV3IE1hcCgpO1xuXHRwcml2YXRlIGNvbm5lY3RlZCA9IGZhbHNlO1xuXHRwcml2YXRlIHN0YXRzOiBNYXA8c3RyaW5nLCBRdWV1ZVN0YXRzQ291bnRlcnM+ID0gbmV3IE1hcCgpO1xuXG5cdGNvbnN0cnVjdG9yKGNvbmZpZz86IFBhcnRpYWw8UGdCb3NzQ29uZmlnPikge1xuXHRcdHRoaXMuY29uZmlnID0ge1xuXHRcdFx0Y29ubmVjdGlvblN0cmluZzogY29uZmlnPy5jb25uZWN0aW9uU3RyaW5nID8/IHByb2Nlc3MuZW52LkRBVEFCQVNFX1VSTCA/PyBcInBvc3RncmVzOi8vbG9jYWxob3N0OjU0MzIvYmxva1wiLFxuXHRcdFx0c2NoZW1hOiBjb25maWc/LnNjaGVtYSA/PyBwcm9jZXNzLmVudi5QR19CT1NTX1NDSEVNQSA/PyBcInBnYm9zc1wiLFxuXHRcdH07XG5cdH1cblxuXHRhc3luYyBjb25uZWN0KCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGlmICh0aGlzLmNvbm5lY3RlZCkgcmV0dXJuO1xuXHRcdHRyeSB7XG5cdFx0XHQvLyBJbmRpcmVjdCBzcGVjaWZpZXIgc28gdHNjIGRvZXNuJ3QgdHJ5IHRvIHJlc29sdmUgdHlwZXMgZm9yXG5cdFx0XHQvLyB0aGUgb3B0aW9uYWwgcGVlciBkZXBlbmRlbmN5IGF0IGJ1aWxkIHRpbWUuXG5cdFx0XHRjb25zdCBtb2R1bGVOYW1lID0gXCJwZy1ib3NzXCI7XG5cdFx0XHQvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IHBnLWJvc3MgaXMgYSBydW50aW1lIHBlZXIgZGVwLlxuXHRcdFx0Y29uc3QgbW9kOiBhbnkgPSBhd2FpdCBpbXBvcnQobW9kdWxlTmFtZSk7XG5cdFx0XHQvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IHBnLWJvc3MgaXMgYSBydW50aW1lIHBlZXIgZGVwLlxuXHRcdFx0Y29uc3QgUGdCb3NzOiBhbnkgPSBtb2QuZGVmYXVsdCA/PyBtb2Q7XG5cdFx0XHR0aGlzLmJvc3MgPSBuZXcgUGdCb3NzKHtcblx0XHRcdFx0Y29ubmVjdGlvblN0cmluZzogdGhpcy5jb25maWcuY29ubmVjdGlvblN0cmluZyxcblx0XHRcdFx0c2NoZW1hOiB0aGlzLmNvbmZpZy5zY2hlbWEsXG5cdFx0XHR9KSBhcyBQZ0Jvc3NJbnN0YW5jZTtcblx0XHRcdGF3YWl0IHRoaXMuYm9zcy5zdGFydCgpO1xuXHRcdFx0dGhpcy5jb25uZWN0ZWQgPSB0cnVlO1xuXHRcdH0gY2F0Y2ggKGVycikge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKFxuXHRcdFx0XHRgW2Jsb2tdW3BnLWJvc3NdIGNvbm5lY3QgZmFpbGVkOiAkeyhlcnIgYXMgRXJyb3IpLm1lc3NhZ2V9LiBJbnN0YWxsIHBnLWJvc3MgYXMgYSBwZWVyIGRlcGVuZGVuY3k6IGJ1biBhZGQgcGctYm9zc2AsXG5cdFx0XHQpO1xuXHRcdH1cblx0fVxuXG5cdGFzeW5jIGRpc2Nvbm5lY3QoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCkgcmV0dXJuO1xuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCB0aGlzLmJvc3M/LnN0b3AoeyBncmFjZWZ1bDogdHJ1ZSB9KTtcblx0XHR9IGNhdGNoIHtcblx0XHRcdC8qIGlnbm9yZSAqL1xuXHRcdH1cblx0XHR0aGlzLndvcmtJZHMuY2xlYXIoKTtcblx0XHR0aGlzLmJvc3MgPSBudWxsO1xuXHRcdHRoaXMuY29ubmVjdGVkID0gZmFsc2U7XG5cdH1cblxuXHRhc3luYyBwcm9jZXNzKGNvbmZpZzogV29ya2VyVHJpZ2dlck9wdHMsIGhhbmRsZXI6IChqb2I6IFdvcmtlckpvYikgPT4gUHJvbWlzZTx2b2lkPik6IFByb21pc2U8dm9pZD4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQgfHwgIXRoaXMuYm9zcykgdGhyb3cgbmV3IEVycm9yKFwiW2Jsb2tdW3BnLWJvc3NdIG5vdCBjb25uZWN0ZWQuIENhbGwgY29ubmVjdCgpIGZpcnN0LlwiKTtcblx0XHR0aGlzLnN0YXRzLnNldChjb25maWcucXVldWUsIHsgY29tcGxldGVkOiAwLCBmYWlsZWQ6IDAsIGFjdGl2ZTogMCB9KTtcblx0XHRjb25zdCBzdGF0cyA9IHRoaXMuc3RhdHMuZ2V0KGNvbmZpZy5xdWV1ZSkgYXMgUXVldWVTdGF0c0NvdW50ZXJzO1xuXG5cdFx0Ly8gcGctYm9zcyB2MTAgcmVxdWlyZXMgZXhwbGljaXQgcXVldWUgY3JlYXRpb24gYmVmb3JlIHNlbmQvd29yay5cblx0XHQvLyBUaGUgY2FsbCBpcyBpZGVtcG90ZW50IOKAlCBzdWNjZWVkcyB3aGV0aGVyIHRoZSBxdWV1ZSBleGlzdHMgb3Jcblx0XHQvLyBub3Qg4oCUIHNvIHRoaXMgaXMgc2FmZSB0byByZXBlYXQgYWNyb3NzIHJlLWVudHJpZXMuXG5cdFx0YXdhaXQgdGhpcy5lbnN1cmVRdWV1ZShjb25maWcucXVldWUpO1xuXG5cdFx0Ly8gcGctYm9zcyB2MTAncyBoYW5kbGVyIHJlY2VpdmVzIGFuIEFSUkFZIG9mIGpvYnMgKGJhdGNoKTsgdGhlXG5cdFx0Ly8gYGJhdGNoU2l6ZWAgb3B0aW9uIGNvbnRyb2xzIHRoZSBtYXguIFdlIGtlZXAgYGJhdGNoU2l6ZTogMWAgc29cblx0XHQvLyB0aGUgaXRlcmF0aW9uIGlzIGEgc2luZ2xlLWpvYiBsb29wIChtYXRjaGVzIHByZS12MTAgc2VtYW50aWNzXG5cdFx0Ly8gdGhlIGFkYXB0ZXIgd2FzIHdyaXR0ZW4gYWdhaW5zdCkuIGBwb2xsaW5nSW50ZXJ2YWxTZWNvbmRzYFxuXHRcdC8vIGRlZmF1bHRzIHRvIDJzIHdoaWNoIGFkZHMgfjJzIGxhdGVuY3kgb24gdGhlIGhhcHB5IHBhdGgg4oCUXG5cdFx0Ly8gdGlnaHRlbiBpdCBmb3IgbG93LWxhdGVuY3kgd29ya2xvYWRzIHZpYSB0aGUgd29ya2VyIGNvbmZpZy5cblx0XHRjb25zdCBpZCA9IGF3YWl0IHRoaXMuYm9zcy53b3JrKFxuXHRcdFx0Y29uZmlnLnF1ZXVlLFxuXHRcdFx0e1xuXHRcdFx0XHRiYXRjaFNpemU6IDEsXG5cdFx0XHRcdGluY2x1ZGVNZXRhZGF0YTogdHJ1ZSxcblx0XHRcdH0sXG5cdFx0XHRhc3luYyAoam9iT3JCYXRjaCkgPT4ge1xuXHRcdFx0XHRjb25zdCBqb2JzID0gQXJyYXkuaXNBcnJheShqb2JPckJhdGNoKSA/IGpvYk9yQmF0Y2ggOiBbam9iT3JCYXRjaF07XG5cdFx0XHRcdGZvciAoY29uc3Qgam9iIG9mIGpvYnMpIHtcblx0XHRcdFx0XHRhd2FpdCB0aGlzLnJ1bk9uZUpvYihjb25maWcsIGhhbmRsZXIsIHN0YXRzLCBqb2IpO1xuXHRcdFx0XHR9XG5cdFx0XHR9LFxuXHRcdCk7XG5cdFx0dGhpcy53b3JrSWRzLnNldChjb25maWcucXVldWUsIGlkKTtcblx0fVxuXG5cdHByaXZhdGUgYXN5bmMgcnVuT25lSm9iKFxuXHRcdGNvbmZpZzogV29ya2VyVHJpZ2dlck9wdHMsXG5cdFx0aGFuZGxlcjogKGpvYjogV29ya2VySm9iKSA9PiBQcm9taXNlPHZvaWQ+LFxuXHRcdHN0YXRzOiBRdWV1ZVN0YXRzQ291bnRlcnMsXG5cdFx0am9iOiBQZ0Jvc3NKb2IsXG5cdCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdHN0YXRzLmFjdGl2ZSArPSAxO1xuXHRcdC8vIFRyYWNrcyB3aGV0aGVyIHN0YXRzIGhhdmUgYWxyZWFkeSBiZWVuIGNvdW50ZWQgYnkgdGhlXG5cdFx0Ly8gV29ya2VySm9iIEFQSSAoYGNvbXBsZXRlYCAvIGBmYWlsYCkuIFdpdGhvdXQgdGhpcywgYm90aFxuXHRcdC8vIHRoZSBjYWxsYmFjayBhbmQgdGhlIHdyYXBwZXIncyB0cnkvY2F0Y2ggY3JlZGl0IHRoZSBzYW1lXG5cdFx0Ly8gb3V0Y29tZSwgZG91YmxlLWNvdW50aW5nIGBzdGF0cy5jb21wbGV0ZWRgIC9cblx0XHQvLyBgc3RhdHMuZmFpbGVkYC4gcGctYm9zcyBpdHNlbGYgaGFuZGxlcyBhY2svcmV0cnkvRExRIHZpYVxuXHRcdC8vIHRoZSBoYW5kbGVyJ3MgcmV0dXJuLXZzLXRocm93IOKAlCB0aGVzZSBjYWxsYmFja3MgZXhpc3Rcblx0XHQvLyBwdXJlbHkgZm9yIHN0YXRzIHBhcml0eSB3aXRoIHRoZSBvdGhlciB3b3JrZXIgYWRhcHRlcnMuXG5cdFx0bGV0IHNldHRsZWQgPSBmYWxzZTtcblx0XHRjb25zdCB3b3JrZXJKb2I6IFdvcmtlckpvYiA9IHtcblx0XHRcdGlkOiBqb2IuaWQsXG5cdFx0XHRkYXRhOiBqb2IuZGF0YSxcblx0XHRcdGhlYWRlcnM6IHt9LFxuXHRcdFx0cXVldWU6IGNvbmZpZy5xdWV1ZSxcblx0XHRcdHByaW9yaXR5OiBjb25maWcucHJpb3JpdHkgPz8gMCxcblx0XHRcdGF0dGVtcHRzOiAwLFxuXHRcdFx0bWF4UmV0cmllczogY29uZmlnLnJldHJpZXMgPz8gMCxcblx0XHRcdGNyZWF0ZWRBdDogbmV3IERhdGUoKSxcblx0XHRcdHRpbWVvdXQ6IGNvbmZpZy50aW1lb3V0LFxuXHRcdFx0cmF3OiBqb2IsXG5cdFx0XHRjb21wbGV0ZTogYXN5bmMgKCkgPT4ge1xuXHRcdFx0XHRpZiAoc2V0dGxlZCkgcmV0dXJuO1xuXHRcdFx0XHRzdGF0cy5jb21wbGV0ZWQgKz0gMTtcblx0XHRcdFx0c2V0dGxlZCA9IHRydWU7XG5cdFx0XHR9LFxuXHRcdFx0ZmFpbDogYXN5bmMgKGVycjogRXJyb3IpID0+IHtcblx0XHRcdFx0aWYgKHNldHRsZWQpIHJldHVybjtcblx0XHRcdFx0c3RhdHMuZmFpbGVkICs9IDE7XG5cdFx0XHRcdHNldHRsZWQgPSB0cnVlO1xuXHRcdFx0XHQvLyBSZS10aHJvdyBzbyBwZy1ib3NzJ3MgYGJvc3Mud29ya2Agc2VlcyB0aGUgaGFuZGxlclxuXHRcdFx0XHQvLyBmYWlsdXJlIGFuZCBzY2hlZHVsZXMgYSByZXRyeSAvIGRyb3BzIHRvIERMUSBwZXJcblx0XHRcdFx0Ly8gdGhlIHF1ZXVlIGNvbmZpZyDigJQgdGhlIGNvbnRyYWN0IGlzIFwiaGFuZGxlciB0aHJvd3Ncblx0XHRcdFx0Ly8gPT4gam9iIGZhaWxlZFwiLlxuXHRcdFx0XHR0aHJvdyBlcnI7XG5cdFx0XHR9LFxuXHRcdH07XG5cdFx0dHJ5IHtcblx0XHRcdGF3YWl0IGhhbmRsZXIod29ya2VySm9iKTtcblx0XHRcdGlmICghc2V0dGxlZCkge1xuXHRcdFx0XHRzdGF0cy5jb21wbGV0ZWQgKz0gMTtcblx0XHRcdFx0c2V0dGxlZCA9IHRydWU7XG5cdFx0XHR9XG5cdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHRpZiAoIXNldHRsZWQpIHtcblx0XHRcdFx0c3RhdHMuZmFpbGVkICs9IDE7XG5cdFx0XHRcdHNldHRsZWQgPSB0cnVlO1xuXHRcdFx0fVxuXHRcdFx0dGhyb3cgZXJyO1xuXHRcdH0gZmluYWxseSB7XG5cdFx0XHRzdGF0cy5hY3RpdmUgPSBNYXRoLm1heCgwLCBzdGF0cy5hY3RpdmUgLSAxKTtcblx0XHR9XG5cdH1cblxuXHRwcml2YXRlIGFzeW5jIGVuc3VyZVF1ZXVlKHF1ZXVlOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRpZiAoIXRoaXMuYm9zcykgcmV0dXJuO1xuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCB0aGlzLmJvc3MuY3JlYXRlUXVldWUocXVldWUpO1xuXHRcdH0gY2F0Y2ggKGVycikge1xuXHRcdFx0Ly8gcGctYm9zcyB2MTAncyBjcmVhdGVRdWV1ZSBpcyBpZGVtcG90ZW50IGJ1dCBtYXkgdGhyb3cgb25cblx0XHRcdC8vIHJhY2UgY29uZGl0aW9ucyB3aGVuIG11bHRpcGxlIHByb2Nlc3NlcyBjYWxsIGl0IGNvbmN1cnJlbnRseVxuXHRcdFx0Ly8gYWdhaW5zdCBhIGZyZXNoIHNjaGVtYSDigJQgdGhvc2UgZXJyb3JzIGFyZSBoYXJtbGVzcyBvbmNlIHRoZVxuXHRcdFx0Ly8gcXVldWUgZXhpc3RzLiBTd2FsbG93ICsgbGV0IGRvd25zdHJlYW0gc2VuZC93b3JrIHN1cmZhY2UgYVxuXHRcdFx0Ly8gcmVhbCBwcm9ibGVtIGlmIHRoZSBxdWV1ZSBpc24ndCB1c2FibGUuXG5cdFx0XHRjb25zdCBtZXNzYWdlID0gKGVyciBhcyBFcnJvcikubWVzc2FnZSA/PyBcIlwiO1xuXHRcdFx0aWYgKCFtZXNzYWdlLmluY2x1ZGVzKFwiYWxyZWFkeSBleGlzdHNcIikpIHtcblx0XHRcdFx0Ly8gTG9nIHRoZSB1bmV4cGVjdGVkIGNhc2UgYnV0IGRvbid0IHByb3BhZ2F0ZSDigJQgb3BlcmF0b3JzXG5cdFx0XHRcdC8vIHNob3VsZG4ndCBzZWUgYSBxdWV1ZS1jcmVhdGlvbiByYWNlIGJsb3cgdXAgdGhlaXJcblx0XHRcdFx0Ly8gdHJpZ2dlciBib290IHBhdGguXG5cdFx0XHRcdGNvbnNvbGUud2FybihgW2Jsb2tdW3BnLWJvc3NdIGVuc3VyZVF1ZXVlKCR7cXVldWV9KSB3YXJuaW5nOmAsIG1lc3NhZ2UpO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdGFzeW5jIGFkZEpvYihcblx0XHRxdWV1ZTogc3RyaW5nLFxuXHRcdGRhdGE6IHVua25vd24sXG5cdFx0b3B0cz86IHsgcHJpb3JpdHk/OiBudW1iZXI7IGRlbGF5PzogbnVtYmVyOyByZXRyaWVzPzogbnVtYmVyOyB0aW1lb3V0PzogbnVtYmVyOyBqb2JJZD86IHN0cmluZyB9LFxuXHQpOiBQcm9taXNlPHN0cmluZz4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQgfHwgIXRoaXMuYm9zcykgdGhyb3cgbmV3IEVycm9yKFwiW2Jsb2tdW3BnLWJvc3NdIG5vdCBjb25uZWN0ZWQuIENhbGwgY29ubmVjdCgpIGZpcnN0LlwiKTtcblx0XHQvLyBwZy1ib3NzIHYxMCByZXF1aXJlcyB0aGUgcXVldWUgdG8gZXhpc3QgYmVmb3JlIHNlbmQuIFRoZVxuXHRcdC8vIGNhbGwgaXMgaWRlbXBvdGVudCDigJQgY2hlYXAgdG8gcmVwZWF0IHBlciBhZGQuXG5cdFx0YXdhaXQgdGhpcy5lbnN1cmVRdWV1ZShxdWV1ZSk7XG5cdFx0Ly8gcGctYm9zcyB2MTAncyBgYXR0b3JuZXkuY2hlY2tTZW5kQXJnc2AgcmVqZWN0cyBhbnkgZXhwbGljaXRseVxuXHRcdC8vIHVuZGVmaW5lZCBgcHJpb3JpdHlgIC8gYHJldHJ5TGltaXRgIC8gYHN0YXJ0QWZ0ZXJgIC9cblx0XHQvLyBgZXhwaXJlSW5TZWNvbmRzYCAvIGBzaW5nbGV0b25LZXlgIHdpdGggXCJYIG11c3QgYmUgYW4gaW50ZWdlclwiLlxuXHRcdC8vIEJ1aWxkIHRoZSBvcHRpb25zIG9iamVjdCB3aXRoIG9ubHkgdGhlIGtleXMgd2UgYWN0dWFsbHkgd2FudFxuXHRcdC8vIHNldCDigJQgY2F1Z2h0IGJ5IHRoZSByZWFsLWJyb2tlciBpbnRlZ3JhdGlvbiB0ZXN0IGluXG5cdFx0Ly8gYF9fdGVzdHNfXy9pbnRlZ3JhdGlvbi9wZ2Jvc3MtYWRhcHRlci5yZWFsLXBnLnRlc3QudHNgLlxuXHRcdGNvbnN0IHNlbmRPcHRzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuXHRcdGlmICh0eXBlb2Ygb3B0cz8ucHJpb3JpdHkgPT09IFwibnVtYmVyXCIpIHNlbmRPcHRzLnByaW9yaXR5ID0gb3B0cy5wcmlvcml0eTtcblx0XHRpZiAodHlwZW9mIG9wdHM/LmRlbGF5ID09PSBcIm51bWJlclwiICYmIG9wdHMuZGVsYXkgPiAwKSB7XG5cdFx0XHRzZW5kT3B0cy5zdGFydEFmdGVyID0gTWF0aC5jZWlsKG9wdHMuZGVsYXkgLyAxMDAwKTtcblx0XHR9XG5cdFx0aWYgKHR5cGVvZiBvcHRzPy5yZXRyaWVzID09PSBcIm51bWJlclwiKSBzZW5kT3B0cy5yZXRyeUxpbWl0ID0gb3B0cy5yZXRyaWVzO1xuXHRcdGlmICh0eXBlb2Ygb3B0cz8udGltZW91dCA9PT0gXCJudW1iZXJcIikgc2VuZE9wdHMuZXhwaXJlSW5TZWNvbmRzID0gTWF0aC5jZWlsKG9wdHMudGltZW91dCAvIDEwMDApO1xuXHRcdGlmICh0eXBlb2Ygb3B0cz8uam9iSWQgPT09IFwic3RyaW5nXCIgJiYgb3B0cy5qb2JJZC5sZW5ndGggPiAwKSBzZW5kT3B0cy5zaW5nbGV0b25LZXkgPSBvcHRzLmpvYklkO1xuXG5cdFx0Y29uc3Qgam9iSWQgPSBhd2FpdCB0aGlzLmJvc3Muc2VuZChxdWV1ZSwgZGF0YSwgc2VuZE9wdHMpO1xuXHRcdHJldHVybiBqb2JJZCA/PyBvcHRzPy5qb2JJZCA/PyB1dWlkKCk7XG5cdH1cblxuXHRhc3luYyBzdG9wUHJvY2Vzc2luZyhxdWV1ZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCB8fCAhdGhpcy5ib3NzKSByZXR1cm47XG5cdFx0dHJ5IHtcblx0XHRcdGF3YWl0IHRoaXMuYm9zcy5vZmZXb3JrKHF1ZXVlKTtcblx0XHR9IGNhdGNoIHtcblx0XHRcdC8qIGlnbm9yZSAqL1xuXHRcdH1cblx0XHR0aGlzLndvcmtJZHMuZGVsZXRlKHF1ZXVlKTtcblx0fVxuXG5cdGlzQ29ubmVjdGVkKCk6IGJvb2xlYW4ge1xuXHRcdHJldHVybiB0aGlzLmNvbm5lY3RlZDtcblx0fVxuXG5cdGFzeW5jIGhlYWx0aENoZWNrKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQgfHwgIXRoaXMuYm9zcykgcmV0dXJuIGZhbHNlO1xuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCB0aGlzLmJvc3MuZ2V0UXVldWVTaXplKFwiX19oZWFsdGhfY2hlY2tfX1wiKTtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0Ly8gZ2V0UXVldWVTaXplIG9uIGEgbm9uLWV4aXN0ZW50IHF1ZXVlIG1heSB0aHJvdyDigJQgZmFsbCBiYWNrIHRvXG5cdFx0XHQvLyBcImNvbm5lY3Rpb24gaXMgbGl2ZVwiIGJ5IGNoZWNraW5nIHRoZSBib3NzIGluc3RhbmNlIGF0dHJpYnV0ZS5cblx0XHRcdHJldHVybiB0aGlzLmNvbm5lY3RlZDtcblx0XHR9XG5cdH1cblxuXHRhc3luYyBnZXRRdWV1ZVN0YXRzKHF1ZXVlOiBzdHJpbmcpOiBQcm9taXNlPFdvcmtlclF1ZXVlU3RhdHM+IHtcblx0XHRjb25zdCBjb3VudGVycyA9IHRoaXMuc3RhdHMuZ2V0KHF1ZXVlKSA/PyB7IGNvbXBsZXRlZDogMCwgZmFpbGVkOiAwLCBhY3RpdmU6IDAgfTtcblx0XHRsZXQgd2FpdGluZyA9IDA7XG5cdFx0dHJ5IHtcblx0XHRcdHdhaXRpbmcgPSAoYXdhaXQgdGhpcy5ib3NzPy5nZXRRdWV1ZVNpemUocXVldWUpKSA/PyAwO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0LyogaWdub3JlICovXG5cdFx0fVxuXHRcdHJldHVybiB7XG5cdFx0XHR3YWl0aW5nLFxuXHRcdFx0YWN0aXZlOiBjb3VudGVycy5hY3RpdmUsXG5cdFx0XHRjb21wbGV0ZWQ6IGNvdW50ZXJzLmNvbXBsZXRlZCxcblx0XHRcdGZhaWxlZDogY291bnRlcnMuZmFpbGVkLFxuXHRcdFx0ZGVsYXllZDogMCxcblx0XHR9O1xuXHR9XG59XG4iXX0=
@@ -0,0 +1,51 @@
1
+ /**
2
+ * RabbitMQAdapter — v0.7 PR 5 — Worker adapter backed by RabbitMQ via
3
+ * the `amqplib` driver. Direct-exchange / queue model — the `queue`
4
+ * field maps to an AMQP queue name; messages are consumed with
5
+ * manual ACK. The default exchange (`""`) is used for publishing.
6
+ *
7
+ * Features:
8
+ * - Concurrency via `prefetch(N)` per channel.
9
+ * - Retries via `nack` with requeue=true until `retries` is hit, then
10
+ * drop or DLQ-route based on `deadLetterQueue` config.
11
+ * - Priorities via the `x-max-priority` queue arg (AMQP standard).
12
+ * - Delayed delivery via the `x-delayed-message` plugin when
13
+ * available; falls back to immediate dispatch otherwise.
14
+ *
15
+ * Requires `amqplib` as a peer dependency:
16
+ *
17
+ * bun add amqplib
18
+ *
19
+ * Environment variables:
20
+ * - `AMQP_URL` — full AMQP connection string (default `amqp://localhost`).
21
+ * - `AMQP_VHOST` — virtual host (default `/`).
22
+ */
23
+ import type { WorkerTriggerOpts } from "@blokjs/helper";
24
+ import type { WorkerAdapter, WorkerJob, WorkerQueueStats } from "../WorkerTrigger";
25
+ export interface RabbitMQConfig {
26
+ url: string;
27
+ vhost?: string;
28
+ }
29
+ export declare class RabbitMQAdapter implements WorkerAdapter {
30
+ readonly provider: "rabbitmq";
31
+ private readonly config;
32
+ private conn;
33
+ private channels;
34
+ private connected;
35
+ private stats;
36
+ constructor(config?: Partial<RabbitMQConfig>);
37
+ connect(): Promise<void>;
38
+ disconnect(): Promise<void>;
39
+ process(config: WorkerTriggerOpts, handler: (job: WorkerJob) => Promise<void>): Promise<void>;
40
+ addJob(queue: string, data: unknown, opts?: {
41
+ priority?: number;
42
+ delay?: number;
43
+ retries?: number;
44
+ timeout?: number;
45
+ jobId?: string;
46
+ }): Promise<string>;
47
+ stopProcessing(queue: string): Promise<void>;
48
+ isConnected(): boolean;
49
+ healthCheck(): Promise<boolean>;
50
+ getQueueStats(queue: string): Promise<WorkerQueueStats>;
51
+ }
@@ -0,0 +1,241 @@
1
+ /**
2
+ * RabbitMQAdapter — v0.7 PR 5 — Worker adapter backed by RabbitMQ via
3
+ * the `amqplib` driver. Direct-exchange / queue model — the `queue`
4
+ * field maps to an AMQP queue name; messages are consumed with
5
+ * manual ACK. The default exchange (`""`) is used for publishing.
6
+ *
7
+ * Features:
8
+ * - Concurrency via `prefetch(N)` per channel.
9
+ * - Retries via `nack` with requeue=true until `retries` is hit, then
10
+ * drop or DLQ-route based on `deadLetterQueue` config.
11
+ * - Priorities via the `x-max-priority` queue arg (AMQP standard).
12
+ * - Delayed delivery via the `x-delayed-message` plugin when
13
+ * available; falls back to immediate dispatch otherwise.
14
+ *
15
+ * Requires `amqplib` as a peer dependency:
16
+ *
17
+ * bun add amqplib
18
+ *
19
+ * Environment variables:
20
+ * - `AMQP_URL` — full AMQP connection string (default `amqp://localhost`).
21
+ * - `AMQP_VHOST` — virtual host (default `/`).
22
+ */
23
+ import { v4 as uuid } from "uuid";
24
+ export class RabbitMQAdapter {
25
+ provider = "rabbitmq";
26
+ config;
27
+ conn = null;
28
+ channels = new Map();
29
+ connected = false;
30
+ stats = new Map();
31
+ constructor(config) {
32
+ this.config = {
33
+ url: config?.url ?? process.env.AMQP_URL ?? "amqp://localhost",
34
+ vhost: config?.vhost ?? process.env.AMQP_VHOST,
35
+ };
36
+ }
37
+ async connect() {
38
+ if (this.connected)
39
+ return;
40
+ try {
41
+ // biome-ignore lint/suspicious/noExplicitAny: amqplib's connect returns a loose ConnectionLike.
42
+ const amqp = await import("amqplib");
43
+ this.conn = (await amqp.connect(this.config.url, { vhost: this.config.vhost }));
44
+ this.connected = true;
45
+ }
46
+ catch (err) {
47
+ throw new Error(`[blok][rabbitmq] connect failed: ${err.message}. Install amqplib as a peer dependency: bun add amqplib`);
48
+ }
49
+ }
50
+ async disconnect() {
51
+ if (!this.connected)
52
+ return;
53
+ for (const [, entry] of this.channels) {
54
+ try {
55
+ if (entry.consumerTag)
56
+ await entry.channel.cancel(entry.consumerTag);
57
+ await entry.channel.close();
58
+ }
59
+ catch {
60
+ /* ignore */
61
+ }
62
+ }
63
+ this.channels.clear();
64
+ try {
65
+ await this.conn?.close();
66
+ }
67
+ catch {
68
+ /* ignore */
69
+ }
70
+ this.conn = null;
71
+ this.connected = false;
72
+ }
73
+ async process(config, handler) {
74
+ if (!this.connected || !this.conn)
75
+ throw new Error("[blok][rabbitmq] not connected. Call connect() first.");
76
+ const channel = await this.conn.createChannel();
77
+ await channel.prefetch(config.concurrency ?? 1);
78
+ const queueArgs = {};
79
+ if (config.deadLetterQueue) {
80
+ queueArgs["x-dead-letter-exchange"] = "";
81
+ queueArgs["x-dead-letter-routing-key"] = config.deadLetterQueue;
82
+ await channel.assertQueue(config.deadLetterQueue, { durable: true });
83
+ }
84
+ await channel.assertQueue(config.queue, { durable: true, arguments: queueArgs });
85
+ this.stats.set(config.queue, { completed: 0, failed: 0, active: 0 });
86
+ const stats = this.stats.get(config.queue);
87
+ const maxAttempts = (config.retries ?? 3) + 1;
88
+ const { consumerTag } = await channel.consume(config.queue, (msg) => {
89
+ if (!msg)
90
+ return;
91
+ void (async () => {
92
+ const payloadString = msg.content.toString("utf8");
93
+ let data;
94
+ try {
95
+ data = payloadString.length > 0 ? JSON.parse(payloadString) : null;
96
+ }
97
+ catch {
98
+ data = payloadString;
99
+ }
100
+ const headers = {};
101
+ for (const [k, v] of Object.entries(msg.properties.headers ?? {}))
102
+ headers[k] = String(v);
103
+ const attempts = Number.parseInt(String(msg.properties.headers?.["x-blok-attempt"] ?? 0), 10);
104
+ // Tracks whether the message has already been ack/nack'd via
105
+ // the WorkerJob API (`complete` / `fail`). The wrapping
106
+ // try/catch below also wants to ack on handler success and
107
+ // nack on handler throw — without this flag we'd
108
+ // double-ack the same delivery tag and Rabbit closes the
109
+ // channel with PRECONDITION_FAILED (caught by the
110
+ // real-broker integration test in
111
+ // `__tests__/integration/rabbitmq-adapter.real-rabbitmq.test.ts`).
112
+ let settled = false;
113
+ const job = {
114
+ id: msg.properties.messageId ?? `${config.queue}:${msg.fields.deliveryTag}`,
115
+ data,
116
+ headers,
117
+ queue: config.queue,
118
+ priority: msg.properties.priority ?? config.priority ?? 0,
119
+ attempts,
120
+ maxRetries: config.retries ?? 3,
121
+ createdAt: msg.properties.timestamp ? new Date(msg.properties.timestamp) : new Date(),
122
+ timeout: config.timeout,
123
+ raw: msg,
124
+ complete: async () => {
125
+ if (settled)
126
+ return;
127
+ channel.ack(msg);
128
+ stats.completed += 1;
129
+ settled = true;
130
+ },
131
+ fail: async (err, requeue) => {
132
+ if (settled)
133
+ return;
134
+ stats.failed += 1;
135
+ const exceeded = attempts + 1 >= maxAttempts;
136
+ channel.nack(msg, false, !exceeded && requeue !== false);
137
+ settled = true;
138
+ },
139
+ };
140
+ stats.active += 1;
141
+ try {
142
+ await handler(job);
143
+ if (!settled && config.ack !== false) {
144
+ channel.ack(msg);
145
+ stats.completed += 1;
146
+ settled = true;
147
+ }
148
+ }
149
+ catch {
150
+ if (!settled) {
151
+ stats.failed += 1;
152
+ const exceeded = attempts + 1 >= maxAttempts;
153
+ channel.nack(msg, false, !exceeded);
154
+ settled = true;
155
+ }
156
+ }
157
+ finally {
158
+ stats.active = Math.max(0, stats.active - 1);
159
+ }
160
+ })();
161
+ }, { noAck: config.ack === false });
162
+ this.channels.set(config.queue, { channel, consumerTag });
163
+ }
164
+ async addJob(queue, data, opts) {
165
+ if (!this.connected || !this.conn)
166
+ throw new Error("[blok][rabbitmq] not connected. Call connect() first.");
167
+ let channel = this.channels.get(queue)?.channel;
168
+ if (!channel) {
169
+ channel = await this.conn.createChannel();
170
+ await channel.assertQueue(queue, { durable: true });
171
+ }
172
+ const messageId = opts?.jobId ?? uuid();
173
+ const headers = {};
174
+ if (typeof opts?.delay === "number")
175
+ headers["x-delay"] = opts.delay;
176
+ const ok = channel.sendToQueue(queue, Buffer.from(typeof data === "string" ? data : JSON.stringify(data)), {
177
+ persistent: true,
178
+ messageId,
179
+ priority: opts?.priority,
180
+ timestamp: Date.now(),
181
+ headers,
182
+ });
183
+ if (!ok) {
184
+ // Channel is in flow-controlled state. The send is still
185
+ // accepted; the channel will emit a 'drain' event when ready.
186
+ // We don't currently surface backpressure to callers.
187
+ }
188
+ return messageId;
189
+ }
190
+ async stopProcessing(queue) {
191
+ const entry = this.channels.get(queue);
192
+ if (!entry)
193
+ return;
194
+ try {
195
+ if (entry.consumerTag)
196
+ await entry.channel.cancel(entry.consumerTag);
197
+ await entry.channel.close();
198
+ }
199
+ catch {
200
+ /* ignore */
201
+ }
202
+ this.channels.delete(queue);
203
+ }
204
+ isConnected() {
205
+ return this.connected;
206
+ }
207
+ async healthCheck() {
208
+ if (!this.connected || !this.conn)
209
+ return false;
210
+ try {
211
+ const channel = await this.conn.createChannel();
212
+ await channel.close();
213
+ return true;
214
+ }
215
+ catch {
216
+ return false;
217
+ }
218
+ }
219
+ async getQueueStats(queue) {
220
+ const counters = this.stats.get(queue) ?? { completed: 0, failed: 0, active: 0 };
221
+ let waiting = 0;
222
+ const entry = this.channels.get(queue);
223
+ if (entry) {
224
+ try {
225
+ const info = await entry.channel.checkQueue(queue);
226
+ waiting = info.messageCount;
227
+ }
228
+ catch {
229
+ /* ignore */
230
+ }
231
+ }
232
+ return {
233
+ waiting,
234
+ active: counters.active,
235
+ completed: counters.completed,
236
+ failed: counters.failed,
237
+ delayed: 0,
238
+ };
239
+ }
240
+ }
241
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmFiYml0TVFBZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FkYXB0ZXJzL1JhYmJpdE1RQWRhcHRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBcUJHO0FBR0gsT0FBTyxFQUFFLEVBQUUsSUFBSSxJQUFJLEVBQUUsTUFBTSxNQUFNLENBQUM7QUE0Q2xDLE1BQU0sT0FBTyxlQUFlO0lBQ2xCLFFBQVEsR0FBRyxVQUFtQixDQUFDO0lBQ3ZCLE1BQU0sQ0FBaUI7SUFDaEMsSUFBSSxHQUE0QixJQUFJLENBQUM7SUFDckMsUUFBUSxHQUFrRSxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQ3BGLFNBQVMsR0FBRyxLQUFLLENBQUM7SUFDbEIsS0FBSyxHQUFvQyxJQUFJLEdBQUcsRUFBRSxDQUFDO0lBRTNELFlBQVksTUFBZ0M7UUFDM0MsSUFBSSxDQUFDLE1BQU0sR0FBRztZQUNiLEdBQUcsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxJQUFJLGtCQUFrQjtZQUM5RCxLQUFLLEVBQUUsTUFBTSxFQUFFLEtBQUssSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVU7U0FDOUMsQ0FBQztJQUNILENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTztRQUNaLElBQUksSUFBSSxDQUFDLFNBQVM7WUFBRSxPQUFPO1FBQzNCLElBQUksQ0FBQztZQUNKLGdHQUFnRztZQUNoRyxNQUFNLElBQUksR0FBUSxNQUFNLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUMxQyxJQUFJLENBQUMsSUFBSSxHQUFHLENBQUMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBcUIsQ0FBQztZQUNwRyxJQUFJLENBQUMs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LENBQUMsS0FBSyxDQUFDLENBQUM7UUFDdkMsSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUNYLElBQUksQ0FBQztnQkFDSixNQUFNLElBQUksR0FBRyxNQUFNLEtBQUssQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNuRCxPQUFPLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQztZQUM3QixDQUFDO1lBQUMsTUFBTSxDQUFDO2dCQUNSLFlBQVk7WUFDYixDQUFDO1FBQ0YsQ0FBQztRQUNELE9BQU87WUFDTixPQUFPO1lBQ1AsTUFBTSxFQUFFLFFBQVEsQ0FBQyxNQUFNO1lBQ3ZCLFNBQVMsRUFBRSxRQUFRLENBQUMsU0FBUztZQUM3QixNQUFNLEVBQUUsUUFBUSxDQUFDLE1BQU07WUFDdkIsT0FBTyxFQUFFLENBQUM7U0FDVixDQUFDO0lBQ0gsQ0FBQztDQUNEIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSYWJiaXRNUUFkYXB0ZXIg4oCUIHYwLjcgUFIgNSDigJQgV29ya2VyIGFkYXB0ZXIgYmFja2VkIGJ5IFJhYmJpdE1RIHZpYVxuICogdGhlIGBhbXFwbGliYCBkcml2ZXIuIERpcmVjdC1leGNoYW5nZSAvIHF1ZXVlIG1vZGVsIOKAlCB0aGUgYHF1ZXVlYFxuICogZmllbGQgbWFwcyB0byBhbiBBTVFQIHF1ZXVlIG5hbWU7IG1lc3NhZ2VzIGFyZSBjb25zdW1lZCB3aXRoXG4gKiBtYW51YWwgQUNLLiBUaGUgZGVmYXVsdCBleGNoYW5nZSAoYFwiXCJgKSBpcyB1c2VkIGZvciBwdWJsaXNoaW5nLlxuICpcbiAqIEZlYXR1cmVzOlxuICogICAtIENvbmN1cnJlbmN5IHZpYSBgcHJlZmV0Y2goTilgIHBlciBjaGFubmVsLlxuICogICAtIFJldHJpZXMgdmlhIGBuYWNrYCB3aXRoIHJlcXVldWU9dHJ1ZSB1bnRpbCBgcmV0cmllc2AgaXMgaGl0LCB0aGVuXG4gKiAgICAgZHJvcCBvciBETFEtcm91dGUgYmFzZWQgb24gYGRlYWRMZXR0ZXJRdWV1ZWAgY29uZmlnLlxuICogICAtIFByaW9yaXRpZXMgdmlhIHRoZSBgeC1tYXgtcHJpb3JpdHlgIHF1ZXVlIGFyZyAoQU1RUCBzdGFuZGFyZCkuXG4gKiAgIC0gRGVsYXllZCBkZWxpdmVyeSB2aWEgdGhlIGB4LWRlbGF5ZWQtbWVzc2FnZWAgcGx1Z2luIHdoZW5cbiAqICAgICBhdmFpbGFibGU7IGZhbGxzIGJhY2sgdG8gaW1tZWRpYXRlIGRpc3BhdGNoIG90aGVyd2lzZS5cbiAqXG4gKiBSZXF1aXJlcyBgYW1xcGxpYmAgYXMgYSBwZWVyIGRlcGVuZGVuY3k6XG4gKlxuICogICAgIGJ1biBhZGQgYW1xcGxpYlxuICpcbiAqIEVudmlyb25tZW50IHZhcmlhYmxlczpcbiAqICAgLSBgQU1RUF9VUkxgIOKAlCBmdWxsIEFNUVAgY29ubmVjdGlvbiBzdHJpbmcgKGRlZmF1bHQgYGFtcXA6Ly9sb2NhbGhvc3RgKS5cbiAqICAgLSBgQU1RUF9WSE9TVGAg4oCUIHZpcnR1YWwgaG9zdCAoZGVmYXVsdCBgL2ApLlxuICovXG5cbmltcG9ydCB0eXBlIHsgV29ya2VyVHJpZ2dlck9wdHMgfSBmcm9tIFwiQGJsb2tqcy9oZWxwZXJcIjtcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tIFwidXVpZFwiO1xuaW1wb3J0IHR5cGUgeyBXb3JrZXJBZGFwdGVyLCBXb3JrZXJKb2IsIFdvcmtlclF1ZXVlU3RhdHMgfSBmcm9tIFwiLi4vV29ya2VyVHJpZ2dlclwiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFJhYmJpdE1RQ29uZmlnIHtcblx0dXJsOiBzdHJpbmc7XG5cdHZob3N0Pzogc3RyaW5nO1xufVxuXG5pbnRlcmZhY2UgUmFiYml0Q2hhbm5lbCB7XG5cdHByZWZldGNoKG46IG51bWJlcik6IFByb21pc2U8dm9pZD47XG5cdGFzc2VydFF1ZXVlKFxuXHRcdG5hbWU6IHN0cmluZyxcblx0XHRvcHRzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG5cdCk6IFByb21pc2U8eyBxdWV1ZTogc3RyaW5nOyBtZXNzYWdlQ291bnQ6IG51bWJlcjsgY29uc3VtZXJDb3VudDogbnVtYmVyIH0+O1xuXHRjaGVja1F1ZXVlKG5hbWU6IHN0cmluZyk6IFByb21pc2U8eyBxdWV1ZTogc3RyaW5nOyBtZXNzYWdlQ291bnQ6IG51bWJlcjsgY29uc3VtZXJDb3VudDogbnVtYmVyIH0+O1xuXHRjb25zdW1lKFxuXHRcdHF1ZXVlOiBzdHJpbmcsXG5cdFx0Y2I6IChcblx0XHRcdG1zZzoge1xuXHRcdFx0XHRjb250ZW50OiBCdWZmZXI7XG5cdFx0XHRcdGZpZWxkczogeyBkZWxpdmVyeVRhZzogbnVtYmVyOyByZWRlbGl2ZXJlZDogYm9vbGVhbiB9O1xuXHRcdFx0XHRwcm9wZXJ0aWVzOiB7IG1lc3NhZ2VJZD86IHN0cmluZzsgcHJpb3JpdHk/OiBudW1iZXI7IHRpbWVzdGFtcD86IG51bWJlcjsgaGVhZGVycz86IFJlY29yZDxzdHJpbmcsIHVua25vd24+IH07XG5cdFx0XHR9IHwgbnVsbCxcblx0XHQpID0+IHZvaWQsXG5cdFx0b3B0cz86IHsgbm9BY2s/OiBib29sZWFuOyBjb25zdW1lclRhZz86IHN0cmluZyB9LFxuXHQpOiBQcm9taXNlPHsgY29uc3VtZXJUYWc6IHN0cmluZyB9Pjtcblx0Y2FuY2VsKGNvbnN1bWVyVGFnOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+O1xuXHRhY2sobXNnOiB7IGZpZWxkczogeyBkZWxpdmVyeVRhZzogbnVtYmVyIH0gfSk6IHZvaWQ7XG5cdG5hY2sobXNnOiB7IGZpZWxkczogeyBkZWxpdmVyeVRhZzogbnVtYmVyIH0gfSwgYWxsVXBUbz86IGJvb2xlYW4sIHJlcXVldWU/OiBib29sZWFuKTogdm9pZDtcblx0c2VuZFRvUXVldWUocXVldWU6IHN0cmluZywgY29udGVudDogQnVmZmVyLCBvcHRzPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBib29sZWFuO1xuXHRjbG9zZSgpOiBQcm9taXNlPHZvaWQ+O1xufVxuXG5pbnRlcmZhY2UgUmFiYml0Q29ubmVjdGlvbiB7XG5cdGNyZWF0ZUNoYW5uZWwoKTogUHJvbWlzZTxSYWJiaXRDaGFubmVsPjtcblx0Y2xvc2UoKTogUHJvbWlzZTx2b2lkPjtcbn1cblxuaW50ZXJmYWNlIFF1ZXVlU3RhdHNDb3VudGVycyB7XG5cdGNvbXBsZXRlZDogbnVtYmVyO1xuXHRmYWlsZWQ6IG51bWJlcjtcblx0YWN0aXZlOiBudW1iZXI7XG59XG5cbmV4cG9ydCBjbGFzcyBSYWJiaXRNUUFkYXB0ZXIgaW1wbGVtZW50cyBXb3JrZXJBZGFwdGVyIHtcblx0cmVhZG9ubHkgcHJvdmlkZXIgPSBcInJhYmJpdG1xXCIgYXMgY29uc3Q7XG5cdHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBSYWJiaXRNUUNvbmZpZztcblx0cHJpdmF0ZSBjb25uOiBSYWJiaXRDb25uZWN0aW9uIHwgbnVsbCA9IG51bGw7XG5cdHByaXZhdGUgY2hhbm5lbHM6IE1hcDxzdHJpbmcsIHsgY2hhbm5lbDogUmFiYml0Q2hhbm5lbDsgY29uc3VtZXJUYWc/OiBzdHJpbmcgfT4gPSBuZXcgTWFwKCk7XG5cdHByaXZhdGUgY29ubmVjdGVkID0gZmFsc2U7XG5cdHByaXZhdGUgc3RhdHM6IE1hcDxzdHJpbmcsIFF1ZXVlU3RhdHNDb3VudGVycz4gPSBuZXcgTWFwKCk7XG5cblx0Y29uc3RydWN0b3IoY29uZmlnPzogUGFydGlhbDxSYWJiaXRNUUNvbmZpZz4pIHtcblx0XHR0aGlzLmNvbmZpZyA9IHtcblx0XHRcdHVybDogY29uZmlnPy51cmwgPz8gcHJvY2Vzcy5lbnYuQU1RUF9VUkwgPz8gXCJhbXFwOi8vbG9jYWxob3N0XCIsXG5cdFx0XHR2aG9zdDogY29uZmlnPy52aG9zdCA/PyBwcm9jZXNzLmVudi5BTVFQX1ZIT1NULFxuXHRcdH07XG5cdH1cblxuXHRhc3luYyBjb25uZWN0KCk6IFByb21pc2U8dm9pZD4ge1xuXHRcdGlmICh0aGlzLmNvbm5lY3RlZCkgcmV0dXJuO1xuXHRcdHRyeSB7XG5cdFx0XHQvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vRXhwbGljaXRBbnk6IGFtcXBsaWIncyBjb25uZWN0IHJldHVybnMgYSBsb29zZSBDb25uZWN0aW9uTGlrZS5cblx0XHRcdGNvbnN0IGFtcXA6IGFueSA9IGF3YWl0IGltcG9ydChcImFtcXBsaWJcIik7XG5cdFx0XHR0aGlzLmNvbm4gPSAoYXdhaXQgYW1xcC5jb25uZWN0KHRoaXMuY29uZmlnLnVybCwgeyB2aG9zdDogdGhpcy5jb25maWcudmhvc3QgfSkpIGFzIFJhYmJpdENvbm5lY3Rpb247XG5cdFx0XHR0aGlzLmNvbm5lY3RlZCA9IHRydWU7XG5cdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoXG5cdFx0XHRcdGBbYmxva11bcmFiYml0bXFdIGNvbm5lY3QgZmFpbGVkOiAkeyhlcnIgYXMgRXJyb3IpLm1lc3NhZ2V9LiBJbnN0YWxsIGFtcXBsaWIgYXMgYSBwZWVyIGRlcGVuZGVuY3k6IGJ1biBhZGQgYW1xcGxpYmAsXG5cdFx0XHQpO1xuXHRcdH1cblx0fVxuXG5cdGFzeW5jIGRpc2Nvbm5lY3QoKTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCkgcmV0dXJuO1xuXHRcdGZvciAoY29uc3QgWywgZW50cnldIG9mIHRoaXMuY2hhbm5lbHMpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGlmIChlbnRyeS5jb25zdW1lclRhZykgYXdhaXQgZW50cnkuY2hhbm5lbC5jYW5jZWwoZW50cnkuY29uc3VtZXJUYWcpO1xuXHRcdFx0XHRhd2FpdCBlbnRyeS5jaGFubmVsLmNsb3NlKCk7XG5cdFx0XHR9IGNhdGNoIHtcblx0XHRcdFx0LyogaWdub3JlICovXG5cdFx0XHR9XG5cdFx0fVxuXHRcdHRoaXMuY2hhbm5lbHMuY2xlYXIoKTtcblx0XHR0cnkge1xuXHRcdFx0YXdhaXQgdGhpcy5jb25uPy5jbG9zZSgpO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0LyogaWdub3JlICovXG5cdFx0fVxuXHRcdHRoaXMuY29ubiA9IG51bGw7XG5cdFx0dGhpcy5jb25uZWN0ZWQgPSBmYWxzZTtcblx0fVxuXG5cdGFzeW5jIHByb2Nlc3MoY29uZmlnOiBXb3JrZXJUcmlnZ2VyT3B0cywgaGFuZGxlcjogKGpvYjogV29ya2VySm9iKSA9PiBQcm9taXNlPHZvaWQ+KTogUHJvbWlzZTx2b2lkPiB7XG5cdFx0aWYgKCF0aGlzLmNvbm5lY3RlZCB8fCAhdGhpcy5jb25uKSB0aHJvdyBuZXcgRXJyb3IoXCJbYmxva11bcmFiYml0bXFdIG5vdCBjb25uZWN0ZWQuIENhbGwgY29ubmVjdCgpIGZpcnN0LlwiKTtcblx0XHRjb25zdCBjaGFubmVsID0gYXdhaXQgdGhpcy5jb25uLmNyZWF0ZUNoYW5uZWwoKTtcblx0XHRhd2FpdCBjaGFubmVsLnByZWZldGNoKGNvbmZpZy5jb25jdXJyZW5jeSA/PyAxKTtcblxuXHRcdGNvbnN0IHF1ZXVlQXJnczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcblx0XHRpZiAoY29uZmlnLmRlYWRMZXR0ZXJRdWV1ZSkge1xuXHRcdFx0cXVldWVBcmdzW1wieC1kZWFkLWxldHRlci1leGNoYW5nZVwiXSA9IFwiXCI7XG5cdFx0XHRxdWV1ZUFyZ3NbXCJ4LWRlYWQtbGV0dGVyLXJvdXRpbmcta2V5XCJdID0gY29uZmlnLmRlYWRMZXR0ZXJRdWV1ZTtcblx0XHRcdGF3YWl0IGNoYW5uZWwuYXNzZXJ0UXVldWUoY29uZmlnLmRlYWRMZXR0ZXJRdWV1ZSwgeyBkdXJhYmxlOiB0cnVlIH0pO1xuXHRcdH1cblx0XHRhd2FpdCBjaGFubmVsLmFzc2VydFF1ZXVlKGNvbmZpZy5xdWV1ZSwgeyBkdXJhYmxlOiB0cnVlLCBhcmd1bWVudHM6IHF1ZXVlQXJncyB9KTtcblxuXHRcdHRoaXMuc3RhdHMuc2V0KGNvbmZpZy5xdWV1ZSwgeyBjb21wbGV0ZWQ6IDAsIGZhaWxlZDogMCwgYWN0aXZlOiAwIH0pO1xuXHRcdGNvbnN0IHN0YXRzID0gdGhpcy5zdGF0cy5nZXQoY29uZmlnLnF1ZXVlKSBhcyBRdWV1ZVN0YXRzQ291bnRlcnM7XG5cdFx0Y29uc3QgbWF4QXR0ZW1wdHMgPSAoY29uZmlnLnJldHJpZXMgPz8gMykgKyAxO1xuXG5cdFx0Y29uc3QgeyBjb25zdW1lclRhZyB9ID0gYXdhaXQgY2hhbm5lbC5jb25zdW1lKFxuXHRcdFx0Y29uZmlnLnF1ZXVlLFxuXHRcdFx0KG1zZykgPT4ge1xuXHRcdFx0XHRpZiAoIW1zZykgcmV0dXJuO1xuXHRcdFx0XHR2b2lkIChhc3luYyAoKSA9PiB7XG5cdFx0XHRcdFx0Y29uc3QgcGF5bG9hZFN0cmluZyA9IG1zZy5jb250ZW50LnRvU3RyaW5nKFwidXRmOFwiKTtcblx0XHRcdFx0XHRsZXQgZGF0YTogdW5rbm93bjtcblx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0ZGF0YSA9IHBheWxvYWRTdHJpbmcubGVuZ3RoID4gMCA/IEpTT04ucGFyc2UocGF5bG9hZFN0cmluZykgOiBudWxsO1xuXHRcdFx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHRcdFx0ZGF0YSA9IHBheWxvYWRTdHJpbmc7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGNvbnN0IGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblx0XHRcdFx0XHRmb3IgKGNvbnN0IFtrLCB2XSBvZiBPYmplY3QuZW50cmllcyhtc2cucHJvcGVydGllcy5oZWFkZXJzID8/IHt9KSkgaGVhZGVyc1trXSA9IFN0cmluZyh2KTtcblx0XHRcdFx0XHRjb25zdCBhdHRlbXB0cyA9IE51bWJlci5wYXJzZUludChTdHJpbmcobXNnLnByb3BlcnRpZXMuaGVhZGVycz8uW1wieC1ibG9rLWF0dGVtcHRcIl0gPz8gMCksIDEwKTtcblx0XHRcdFx0XHQvLyBUcmFja3Mgd2hldGhlciB0aGUgbWVzc2FnZSBoYXMgYWxyZWFkeSBiZWVuIGFjay9uYWNrJ2QgdmlhXG5cdFx0XHRcdFx0Ly8gdGhlIFdvcmtlckpvYiBBUEkgKGBjb21wbGV0ZWAgLyBgZmFpbGApLiBUaGUgd3JhcHBpbmdcblx0XHRcdFx0XHQvLyB0cnkvY2F0Y2ggYmVsb3cgYWxzbyB3YW50cyB0byBhY2sgb24gaGFuZGxlciBzdWNjZXNzIGFuZFxuXHRcdFx0XHRcdC8vIG5hY2sgb24gaGFuZGxlciB0aHJvdyDigJQgd2l0aG91dCB0aGlzIGZsYWcgd2UnZFxuXHRcdFx0XHRcdC8vIGRvdWJsZS1hY2sgdGhlIHNhbWUgZGVsaXZlcnkgdGFnIGFuZCBSYWJiaXQgY2xvc2VzIHRoZVxuXHRcdFx0XHRcdC8vIGNoYW5uZWwgd2l0aCBQUkVDT05ESVRJT05fRkFJTEVEIChjYXVnaHQgYnkgdGhlXG5cdFx0XHRcdFx0Ly8gcmVhbC1icm9rZXIgaW50ZWdyYXRpb24gdGVzdCBpblxuXHRcdFx0XHRcdC8vIGBfX3Rlc3RzX18vaW50ZWdyYXRpb24vcmFiYml0bXEtYWRhcHRlci5yZWFsLXJhYmJpdG1xLnRlc3QudHNgKS5cblx0XHRcdFx0XHRsZXQgc2V0dGxlZCA9IGZhbHNlO1xuXHRcdFx0XHRcdGNvbnN0IGpvYjogV29ya2VySm9iID0ge1xuXHRcdFx0XHRcdFx0aWQ6IG1zZy5wcm9wZXJ0aWVzLm1lc3NhZ2VJZCA/PyBgJHtjb25maWcucXVldWV9OiR7bXNnLmZpZWxkcy5kZWxpdmVyeVRhZ31gLFxuXHRcdFx0XHRcdFx0ZGF0YSxcblx0XHRcdFx0XHRcdGhlYWRlcnMsXG5cdFx0XHRcdFx0XHRxdWV1ZTogY29uZmlnLnF1ZXVlLFxuXHRcdFx0XHRcdFx0cHJpb3JpdHk6IG1zZy5wcm9wZXJ0aWVzLnByaW9yaXR5ID8/IGNvbmZpZy5wcmlvcml0eSA/PyAwLFxuXHRcdFx0XHRcdFx0YXR0ZW1wdHMsXG5cdFx0XHRcdFx0XHRtYXhSZXRyaWVzOiBjb25maWcucmV0cmllcyA/PyAzLFxuXHRcdFx0XHRcdFx0Y3JlYXRlZEF0OiBtc2cucHJvcGVydGllcy50aW1lc3RhbXAgPyBuZXcgRGF0ZShtc2cucHJvcGVydGllcy50aW1lc3RhbXApIDogbmV3IERhdGUoKSxcblx0XHRcdFx0XHRcdHRpbWVvdXQ6IGNvbmZpZy50aW1lb3V0LFxuXHRcdFx0XHRcdFx0cmF3OiBtc2csXG5cdFx0XHRcdFx0XHRjb21wbGV0ZTogYXN5bmMgKCkgPT4ge1xuXHRcdFx0XHRcdFx0XHRpZiAoc2V0dGxlZCkgcmV0dXJuO1xuXHRcdFx0XHRcdFx0XHRjaGFubmVsLmFjayhtc2cpO1xuXHRcdFx0XHRcdFx0XHRzdGF0cy5jb21wbGV0ZWQgKz0gMTtcblx0XHRcdFx0XHRcdFx0c2V0dGxlZCA9IHRydWU7XG5cdFx0XHRcdFx0XHR9LFxuXHRcdFx0XHRcdFx0ZmFpbDogYXN5bmMgKGVycjogRXJyb3IsIHJlcXVldWU/OiBib29sZWFuKSA9PiB7XG5cdFx0XHRcdFx0XHRcdGlmIChzZXR0bGVkKSByZXR1cm47XG5cdFx0XHRcdFx0XHRcdHN0YXRzLmZhaWxlZCArPSAxO1xuXHRcdFx0XHRcdFx0XHRjb25zdCBleGNlZWRlZCA9IGF0dGVtcHRzICsgMSA+PSBtYXhBdHRlbXB0cztcblx0XHRcdFx0XHRcdFx0Y2hhbm5lbC5uYWNrKG1zZywgZmFsc2UsICFleGNlZWRlZCAmJiByZXF1ZXVlICE9PSBmYWxzZSk7XG5cdFx0XHRcdFx0XHRcdHNldHRsZWQgPSB0cnVlO1xuXHRcdFx0XHRcdFx0fSxcblx0XHRcdFx0XHR9O1xuXHRcdFx0XHRcdHN0YXRzLmFjdGl2ZSArPSAxO1xuXHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRhd2FpdCBoYW5kbGVyKGpvYik7XG5cdFx0XHRcdFx0XHRpZiAoIXNldHRsZWQgJiYgY29uZmlnLmFjayAhPT0gZmFsc2UpIHtcblx0XHRcdFx0XHRcdFx0Y2hhbm5lbC5hY2sobXNnKTtcblx0XHRcdFx0XHRcdFx0c3RhdHMuY29tcGxldGVkICs9IDE7XG5cdFx0XHRcdFx0XHRcdHNldHRsZWQgPSB0cnVlO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gY2F0Y2gge1xuXHRcdFx0XHRcdFx0aWYgKCFzZXR0bGVkKSB7XG5cdFx0XHRcdFx0XHRcdHN0YXRzLmZhaWxlZCArPSAxO1xuXHRcdFx0XHRcdFx0XHRjb25zdCBleGNlZWRlZCA9IGF0dGVtcHRzICsgMSA+PSBtYXhBdHRlbXB0cztcblx0XHRcdFx0XHRcdFx0Y2hhbm5lbC5uYWNrKG1zZywgZmFsc2UsICFleGNlZWRlZCk7XG5cdFx0XHRcdFx0XHRcdHNldHRsZWQgPSB0cnVlO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gZmluYWxseSB7XG5cdFx0XHRcdFx0XHRzdGF0cy5hY3RpdmUgPSBNYXRoLm1heCgwLCBzdGF0cy5hY3RpdmUgLSAxKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0pKCk7XG5cdFx0XHR9LFxuXHRcdFx0eyBub0FjazogY29uZmlnLmFjayA9PT0gZmFsc2UgfSxcblx0XHQpO1xuXHRcdHRoaXMuY2hhbm5lbHMuc2V0KGNvbmZpZy5xdWV1ZSwgeyBjaGFubmVsLCBjb25zdW1lclRhZyB9KTtcblx0fVxuXG5cdGFzeW5jIGFkZEpvYihcblx0XHRxdWV1ZTogc3RyaW5nLFxuXHRcdGRhdGE6IHVua25vd24sXG5cdFx0b3B0cz86IHsgcHJpb3JpdHk/OiBudW1iZXI7IGRlbGF5PzogbnVtYmVyOyByZXRyaWVzPzogbnVtYmVyOyB0aW1lb3V0PzogbnVtYmVyOyBqb2JJZD86IHN0cmluZyB9LFxuXHQpOiBQcm9taXNlPHN0cmluZz4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQgfHwgIXRoaXMuY29ubikgdGhyb3cgbmV3IEVycm9yKFwiW2Jsb2tdW3JhYmJpdG1xXSBub3QgY29ubmVjdGVkLiBDYWxsIGNvbm5lY3QoKSBmaXJzdC5cIik7XG5cdFx0bGV0IGNoYW5uZWwgPSB0aGlzLmNoYW5uZWxzLmdldChxdWV1ZSk/LmNoYW5uZWw7XG5cdFx0aWYgKCFjaGFubmVsKSB7XG5cdFx0XHRjaGFubmVsID0gYXdhaXQgdGhpcy5jb25uLmNyZWF0ZUNoYW5uZWwoKTtcblx0XHRcdGF3YWl0IGNoYW5uZWwuYXNzZXJ0UXVldWUocXVldWUsIHsgZHVyYWJsZTogdHJ1ZSB9KTtcblx0XHR9XG5cdFx0Y29uc3QgbWVzc2FnZUlkID0gb3B0cz8uam9iSWQgPz8gdXVpZCgpO1xuXHRcdGNvbnN0IGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG5cdFx0aWYgKHR5cGVvZiBvcHRzPy5kZWxheSA9PT0gXCJudW1iZXJcIikgaGVhZGVyc1tcIngtZGVsYXlcIl0gPSBvcHRzLmRlbGF5O1xuXHRcdGNvbnN0IG9rID0gY2hhbm5lbC5zZW5kVG9RdWV1ZShxdWV1ZSwgQnVmZmVyLmZyb20odHlwZW9mIGRhdGEgPT09IFwic3RyaW5nXCIgPyBkYXRhIDogSlNPTi5zdHJpbmdpZnkoZGF0YSkpLCB7XG5cdFx0XHRwZXJzaXN0ZW50OiB0cnVlLFxuXHRcdFx0bWVzc2FnZUlkLFxuXHRcdFx0cHJpb3JpdHk6IG9wdHM/LnByaW9yaXR5LFxuXHRcdFx0dGltZXN0YW1wOiBEYXRlLm5vdygpLFxuXHRcdFx0aGVhZGVycyxcblx0XHR9KTtcblx0XHRpZiAoIW9rKSB7XG5cdFx0XHQvLyBDaGFubmVsIGlzIGluIGZsb3ctY29udHJvbGxlZCBzdGF0ZS4gVGhlIHNlbmQgaXMgc3RpbGxcblx0XHRcdC8vIGFjY2VwdGVkOyB0aGUgY2hhbm5lbCB3aWxsIGVtaXQgYSAnZHJhaW4nIGV2ZW50IHdoZW4gcmVhZHkuXG5cdFx0XHQvLyBXZSBkb24ndCBjdXJyZW50bHkgc3VyZmFjZSBiYWNrcHJlc3N1cmUgdG8gY2FsbGVycy5cblx0XHR9XG5cdFx0cmV0dXJuIG1lc3NhZ2VJZDtcblx0fVxuXG5cdGFzeW5jIHN0b3BQcm9jZXNzaW5nKHF1ZXVlOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRjb25zdCBlbnRyeSA9IHRoaXMuY2hhbm5lbHMuZ2V0KHF1ZXVlKTtcblx0XHRpZiAoIWVudHJ5KSByZXR1cm47XG5cdFx0dHJ5IHtcblx0XHRcdGlmIChlbnRyeS5jb25zdW1lclRhZykgYXdhaXQgZW50cnkuY2hhbm5lbC5jYW5jZWwoZW50cnkuY29uc3VtZXJUYWcpO1xuXHRcdFx0YXdhaXQgZW50cnkuY2hhbm5lbC5jbG9zZSgpO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0LyogaWdub3JlICovXG5cdFx0fVxuXHRcdHRoaXMuY2hhbm5lbHMuZGVsZXRlKHF1ZXVlKTtcblx0fVxuXG5cdGlzQ29ubmVjdGVkKCk6IGJvb2xlYW4ge1xuXHRcdHJldHVybiB0aGlzLmNvbm5lY3RlZDtcblx0fVxuXG5cdGFzeW5jIGhlYWx0aENoZWNrKCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuXHRcdGlmICghdGhpcy5jb25uZWN0ZWQgfHwgIXRoaXMuY29ubikgcmV0dXJuIGZhbHNlO1xuXHRcdHRyeSB7XG5cdFx0XHRjb25zdCBjaGFubmVsID0gYXdhaXQgdGhpcy5jb25uLmNyZWF0ZUNoYW5uZWwoKTtcblx0XHRcdGF3YWl0IGNoYW5uZWwuY2xvc2UoKTtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH0gY2F0Y2gge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0fVxuXG5cdGFzeW5jIGdldFF1ZXVlU3RhdHMocXVldWU6IHN0cmluZyk6IFByb21pc2U8V29ya2VyUXVldWVTdGF0cz4ge1xuXHRcdGNvbnN0IGNvdW50ZXJzID0gdGhpcy5zdGF0cy5nZXQocXVldWUpID8/IHsgY29tcGxldGVkOiAwLCBmYWlsZWQ6IDAsIGFjdGl2ZTogMCB9O1xuXHRcdGxldCB3YWl0aW5nID0gMDtcblx0XHRjb25zdCBlbnRyeSA9IHRoaXMuY2hhbm5lbHMuZ2V0KHF1ZXVlKTtcblx0XHRpZiAoZW50cnkpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGNvbnN0IGluZm8gPSBhd2FpdCBlbnRyeS5jaGFubmVsLmNoZWNrUXVldWUocXVldWUpO1xuXHRcdFx0XHR3YWl0aW5nID0gaW5mby5tZXNzYWdlQ291bnQ7XG5cdFx0XHR9IGNhdGNoIHtcblx0XHRcdFx0LyogaWdub3JlICovXG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiB7XG5cdFx0XHR3YWl0aW5nLFxuXHRcdFx0YWN0aXZlOiBjb3VudGVycy5hY3RpdmUsXG5cdFx0XHRjb21wbGV0ZWQ6IGNvdW50ZXJzLmNvbXBsZXRlZCxcblx0XHRcdGZhaWxlZDogY291bnRlcnMuZmFpbGVkLFxuXHRcdFx0ZGVsYXllZDogMCxcblx0XHR9O1xuXHR9XG59XG4iXX0=