@igniter-js/jobs 0.1.13 → 0.1.15

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 (40) hide show
  1. package/AGENTS.md +119 -243
  2. package/README.md +352 -158
  3. package/dist/{adapter-CXZxomI9.d.mts → adapter-DDyMVche.d.mts} +125 -20
  4. package/dist/{adapter-CXZxomI9.d.ts → adapter-DDyMVche.d.ts} +125 -20
  5. package/dist/adapters/bun.d.mts +101 -0
  6. package/dist/adapters/bun.d.ts +101 -0
  7. package/dist/adapters/bun.js +1048 -0
  8. package/dist/adapters/bun.js.map +1 -0
  9. package/dist/adapters/bun.mjs +1046 -0
  10. package/dist/adapters/bun.mjs.map +1 -0
  11. package/dist/adapters/{memory.adapter.d.ts → mock.d.mts} +7 -3
  12. package/dist/adapters/{memory.adapter.d.mts → mock.d.ts} +7 -3
  13. package/dist/adapters/{memory.adapter.js → mock.js} +122 -25
  14. package/dist/adapters/mock.js.map +1 -0
  15. package/dist/adapters/{memory.adapter.mjs → mock.mjs} +122 -25
  16. package/dist/adapters/mock.mjs.map +1 -0
  17. package/dist/adapters/{bullmq.adapter.d.mts → node.d.mts} +8 -3
  18. package/dist/adapters/{bullmq.adapter.d.ts → node.d.ts} +8 -3
  19. package/dist/adapters/{bullmq.adapter.js → node.js} +194 -40
  20. package/dist/adapters/node.js.map +1 -0
  21. package/dist/adapters/{bullmq.adapter.mjs → node.mjs} +194 -40
  22. package/dist/adapters/node.mjs.map +1 -0
  23. package/dist/index.d.mts +41 -38
  24. package/dist/index.d.ts +41 -38
  25. package/dist/index.js +145 -1856
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +146 -1854
  28. package/dist/index.mjs.map +1 -1
  29. package/package.json +29 -41
  30. package/CHANGELOG.md +0 -13
  31. package/dist/adapters/bullmq.adapter.js.map +0 -1
  32. package/dist/adapters/bullmq.adapter.mjs.map +0 -1
  33. package/dist/adapters/index.d.mts +0 -143
  34. package/dist/adapters/index.d.ts +0 -143
  35. package/dist/adapters/index.js +0 -1891
  36. package/dist/adapters/index.js.map +0 -1
  37. package/dist/adapters/index.mjs +0 -1887
  38. package/dist/adapters/index.mjs.map +0 -1
  39. package/dist/adapters/memory.adapter.js.map +0 -1
  40. package/dist/adapters/memory.adapter.mjs.map +0 -1
@@ -30,6 +30,11 @@ var _IgniterJobsPrefix = class _IgniterJobsPrefix {
30
30
  if (!params.scope) return base;
31
31
  return `${base}:scope:${params.scope.type}:${params.scope.id}`;
32
32
  }
33
+ static buildJobStreamChannel(params) {
34
+ const base = `${_IgniterJobsPrefix.BASE_PREFIX}:stream:${params.environment}:${params.service}:${params.queue}:${params.jobId}`;
35
+ if (!params.scope) return base;
36
+ return `${base}:scope:${params.scope.type}:${params.scope.id}`;
37
+ }
33
38
  };
34
39
  _IgniterJobsPrefix.BASE_PREFIX = "igniter:jobs";
35
40
  var IgniterJobsPrefix = _IgniterJobsPrefix;
@@ -171,37 +176,68 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
171
176
  });
172
177
  }
173
178
  async getJob(jobId, queue) {
174
- const result = await this.core().job.get(jobId, queue ? this.toCoreQueueName(queue) : void 0);
179
+ const result = await this.core().job.get(
180
+ jobId,
181
+ queue ? this.toCoreQueueName(queue) : void 0
182
+ );
175
183
  return result ? this.mapJob(result, queue) : null;
176
184
  }
177
185
  async getJobState(jobId, queue) {
178
- const state = await this.core().job.getState(jobId, queue ? this.toCoreQueueName(queue) : void 0);
186
+ const state = await this.core().job.getState(
187
+ jobId,
188
+ queue ? this.toCoreQueueName(queue) : void 0
189
+ );
179
190
  return state;
180
191
  }
181
192
  async getJobLogs(jobId, queue) {
182
- const logs = await this.core().job.getLogs(jobId, queue ? this.toCoreQueueName(queue) : void 0);
193
+ const logs = await this.core().job.getLogs(
194
+ jobId,
195
+ queue ? this.toCoreQueueName(queue) : void 0
196
+ );
183
197
  return logs;
184
198
  }
185
199
  async getJobProgress(jobId, queue) {
186
- return this.core().job.getProgress(jobId, queue ? this.toCoreQueueName(queue) : void 0);
200
+ return this.core().job.getProgress(
201
+ jobId,
202
+ queue ? this.toCoreQueueName(queue) : void 0
203
+ );
187
204
  }
188
205
  async retryJob(jobId, queue) {
189
- await this.core().job.retry(jobId, queue ? this.toCoreQueueName(queue) : void 0);
206
+ await this.core().job.retry(
207
+ jobId,
208
+ queue ? this.toCoreQueueName(queue) : void 0
209
+ );
190
210
  }
191
211
  async removeJob(jobId, queue) {
192
- await this.core().job.remove(jobId, queue ? this.toCoreQueueName(queue) : void 0);
212
+ await this.core().job.remove(
213
+ jobId,
214
+ queue ? this.toCoreQueueName(queue) : void 0
215
+ );
193
216
  }
194
217
  async promoteJob(jobId, queue) {
195
- await this.core().job.promote(jobId, queue ? this.toCoreQueueName(queue) : void 0);
218
+ await this.core().job.promote(
219
+ jobId,
220
+ queue ? this.toCoreQueueName(queue) : void 0
221
+ );
196
222
  }
197
223
  async moveJobToFailed(jobId, reason, queue) {
198
- await this.core().job.moveToFailed(jobId, reason, queue ? this.toCoreQueueName(queue) : void 0);
224
+ await this.core().job.moveToFailed(
225
+ jobId,
226
+ reason,
227
+ queue ? this.toCoreQueueName(queue) : void 0
228
+ );
199
229
  }
200
230
  async retryManyJobs(jobIds, queue) {
201
- await this.core().job.retryMany(jobIds, queue ? this.toCoreQueueName(queue) : void 0);
231
+ await this.core().job.retryMany(
232
+ jobIds,
233
+ queue ? this.toCoreQueueName(queue) : void 0
234
+ );
202
235
  }
203
236
  async removeManyJobs(jobIds, queue) {
204
- await this.core().job.removeMany(jobIds, queue ? this.toCoreQueueName(queue) : void 0);
237
+ await this.core().job.removeMany(
238
+ jobIds,
239
+ queue ? this.toCoreQueueName(queue) : void 0
240
+ );
205
241
  }
206
242
  async getQueueInfo(queue) {
207
243
  const info = await this.core().queues.get(this.toCoreQueueName(queue));
@@ -209,7 +245,9 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
209
245
  return this.mapQueueInfo(info);
210
246
  }
211
247
  async getQueueJobCounts(queue) {
212
- const counts = await this.core().queues.getJobCounts(this.toCoreQueueName(queue));
248
+ const counts = await this.core().queues.getJobCounts(
249
+ this.toCoreQueueName(queue)
250
+ );
213
251
  return counts;
214
252
  }
215
253
  async listQueues() {
@@ -226,27 +264,23 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
226
264
  return this.core().queues.drain(this.toCoreQueueName(queue));
227
265
  }
228
266
  async cleanQueue(queue, options) {
229
- return this.core().queues.clean(this.toCoreQueueName(queue), options);
267
+ return this.core().queues.clean(
268
+ this.toCoreQueueName(queue),
269
+ options
270
+ );
230
271
  }
231
272
  async obliterateQueue(queue, options) {
232
273
  await this.core().queues.obliterate(this.toCoreQueueName(queue), options);
233
274
  }
234
275
  async retryAllInQueue(queue) {
235
- const jobs = await this.core().queues.getJobs(this.toCoreQueueName(queue), { status: ["failed"], limit: 1e3 });
236
- await Promise.all(jobs.map((j) => this.core().job.retry(j.id, this.toCoreQueueName(queue))));
237
- return jobs.length;
238
- }
239
- async pauseJobType(queue, jobName) {
240
- throw new IgniterJobsError({
241
- code: "JOBS_QUEUE_OPERATION_FAILED",
242
- message: "BullMQ backend does not support pausing a single job type; pause the queue or adjust worker filters."
243
- });
244
- }
245
- async resumeJobType(queue, jobName) {
246
- throw new IgniterJobsError({
247
- code: "JOBS_QUEUE_OPERATION_FAILED",
248
- message: "BullMQ backend does not support resuming a single job type; resume the queue or adjust worker filters."
276
+ const jobs = await this.core().queues.getJobs(this.toCoreQueueName(queue), {
277
+ status: ["failed"],
278
+ limit: 1e3
249
279
  });
280
+ await Promise.all(
281
+ jobs.map((j) => this.core().job.retry(j.id, this.toCoreQueueName(queue)))
282
+ );
283
+ return jobs.length;
250
284
  }
251
285
  async searchJobs(filter) {
252
286
  const queue = filter?.queue;
@@ -254,13 +288,19 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
254
288
  const limit = filter?.limit ?? 100;
255
289
  const offset = filter?.offset ?? 0;
256
290
  if (queue) {
257
- const jobs = await this.core().queues.getJobs(this.toCoreQueueName(queue), { status, limit, offset });
291
+ const jobs = await this.core().queues.getJobs(
292
+ this.toCoreQueueName(queue),
293
+ { status, limit, offset }
294
+ );
258
295
  return jobs.map((j) => this.mapJob(j, queue));
259
296
  }
260
297
  const queues = await this.listQueues();
261
298
  const results = [];
262
299
  for (const q of queues) {
263
- const jobs = await this.core().queues.getJobs(this.toCoreQueueName(q.name), { status, limit, offset });
300
+ const jobs = await this.core().queues.getJobs(
301
+ this.toCoreQueueName(q.name),
302
+ { status, limit, offset }
303
+ );
264
304
  results.push(...jobs.map((j) => this.mapJob(j, q.name)));
265
305
  if (results.length >= limit) break;
266
306
  }
@@ -270,7 +310,9 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
270
310
  const all = await this.listQueues();
271
311
  const name = filter?.name;
272
312
  const isPaused = filter?.isPaused;
273
- return all.filter((q) => name ? q.name.includes(name) : true).filter((q) => typeof isPaused === "boolean" ? q.isPaused === isPaused : true);
313
+ return all.filter((q) => name ? q.name.includes(name) : true).filter(
314
+ (q) => typeof isPaused === "boolean" ? q.isPaused === isPaused : true
315
+ );
274
316
  }
275
317
  async searchWorkers(filter) {
276
318
  const queue = filter?.queue;
@@ -281,11 +323,15 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
281
323
  const coreQueue = this.toCoreQueueName(queue);
282
324
  const queues = w.config?.queues ?? [w.queueName];
283
325
  return Array.isArray(queues) ? queues.includes(coreQueue) : false;
284
- }).filter((w) => typeof isRunning === "boolean" ? isRunning ? w.isRunning() : !w.isRunning() : true).map((w) => this.mapWorker(w));
326
+ }).filter(
327
+ (w) => typeof isRunning === "boolean" ? isRunning ? w.isRunning() : !w.isRunning() : true
328
+ ).map((w) => this.mapWorker(w));
285
329
  }
286
330
  async createWorker(config) {
287
331
  await this.executor();
288
- const queuesSource = config.queues?.length ? config.queues : Array.from(/* @__PURE__ */ new Set([...this.jobsByQueue.keys(), ...this.cronsByQueue.keys()]));
332
+ const queuesSource = config.queues?.length ? config.queues : Array.from(
333
+ /* @__PURE__ */ new Set([...this.jobsByQueue.keys(), ...this.cronsByQueue.keys()])
334
+ );
289
335
  const queues = queuesSource.map((q) => this.toCoreQueueName(q));
290
336
  const coreConfig = {
291
337
  queues,
@@ -301,7 +347,8 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
301
347
  }
302
348
  getWorkers() {
303
349
  const out = /* @__PURE__ */ new Map();
304
- for (const [id, handle] of this.core().getWorkers()) out.set(id, this.mapWorker(handle));
350
+ for (const [id, handle] of this.core().getWorkers())
351
+ out.set(id, this.mapWorker(handle));
305
352
  return out;
306
353
  }
307
354
  async publishEvent(channel, payload) {
@@ -325,6 +372,58 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
325
372
  }
326
373
  };
327
374
  }
375
+ async writeJobStreamEvent(params) {
376
+ const id = String(
377
+ await this.publisher.incr(
378
+ this.getJobStreamSequenceKey(params.queue, params.jobId)
379
+ )
380
+ );
381
+ const event = {
382
+ ...params.event,
383
+ id
384
+ };
385
+ if (params.persistence?.enabled) {
386
+ const listKey = this.getJobStreamListKey(params.queue, params.jobId);
387
+ await this.publisher.rpush(listKey, JSON.stringify(event));
388
+ const maxEvents = params.persistence.maxEvents;
389
+ if (typeof maxEvents === "number" && maxEvents > 0) {
390
+ await this.publisher.ltrim(listKey, -maxEvents, -1);
391
+ }
392
+ }
393
+ await this.publisher.publish(
394
+ this.getJobStreamChannel(params.queue, params.jobId),
395
+ JSON.stringify(event)
396
+ );
397
+ return id;
398
+ }
399
+ async readJobStream(params) {
400
+ const rows = await this.publisher.lrange(
401
+ this.getJobStreamListKey(params.queue, params.jobId),
402
+ 0,
403
+ -1
404
+ );
405
+ const after = params.after ? Number(params.after) : void 0;
406
+ const limit = params.limit ?? 100;
407
+ const parsed = rows.map((row) => this.parseJobStreamRow(row)).filter(
408
+ (event) => Boolean(event)
409
+ ).filter(
410
+ (event) => typeof after === "number" ? Number(event.id) > after : true
411
+ );
412
+ const items = parsed.slice(0, limit);
413
+ return {
414
+ items,
415
+ nextCursor: items.at(-1)?.id,
416
+ hasMore: parsed.length > items.length
417
+ };
418
+ }
419
+ async subscribeJobStream(params) {
420
+ const channel = this.getJobStreamChannel(params.queue, params.jobId);
421
+ return this.subscribeEvent(channel, async (payload) => {
422
+ await params.handler(
423
+ payload
424
+ );
425
+ });
426
+ }
328
427
  async shutdown() {
329
428
  await this.subscriber.quit();
330
429
  }
@@ -340,7 +439,10 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
340
439
  if (!this.executorDirty && this.coreExecutor) return this.coreExecutor;
341
440
  const routers = {};
342
441
  const flattened = {};
343
- const allQueues = /* @__PURE__ */ new Set([...this.jobsByQueue.keys(), ...this.cronsByQueue.keys()]);
442
+ const allQueues = /* @__PURE__ */ new Set([
443
+ ...this.jobsByQueue.keys(),
444
+ ...this.cronsByQueue.keys()
445
+ ]);
344
446
  for (const queueName of allQueues) {
345
447
  const coreJobs = {};
346
448
  const jobs = this.jobsByQueue.get(queueName);
@@ -348,14 +450,24 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
348
450
  for (const [jobName, def] of jobs.entries()) {
349
451
  const queue = def.queue ? `${queueName}.${def.queue}` : queueName;
350
452
  const fullQueue = IgniterJobsPrefix.buildQueueName(queue);
351
- coreJobs[jobName] = this.toCoreJobDefinition(queueName, jobName, def, fullQueue);
453
+ coreJobs[jobName] = this.toCoreJobDefinition(
454
+ queueName,
455
+ jobName,
456
+ def,
457
+ fullQueue
458
+ );
352
459
  }
353
460
  }
354
461
  const crons = this.cronsByQueue.get(queueName);
355
462
  if (crons) {
356
463
  for (const [cronName, def] of crons.entries()) {
357
464
  const fullQueue = IgniterJobsPrefix.buildQueueName(queueName);
358
- coreJobs[cronName] = this.toCoreCronJobDefinition(queueName, cronName, def, fullQueue);
465
+ coreJobs[cronName] = this.toCoreCronJobDefinition(
466
+ queueName,
467
+ cronName,
468
+ def,
469
+ fullQueue
470
+ );
359
471
  }
360
472
  }
361
473
  if (Object.keys(coreJobs).length === 0) continue;
@@ -375,6 +487,26 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
375
487
  toCoreQueueName(queueName) {
376
488
  return IgniterJobsPrefix.buildQueueName(queueName);
377
489
  }
490
+ getJobStreamChannel(queue, jobId) {
491
+ return `${IgniterJobsPrefix.BASE_PREFIX}:stream:${queue}:${jobId}:live`;
492
+ }
493
+ getJobStreamListKey(queue, jobId) {
494
+ return `${IgniterJobsPrefix.BASE_PREFIX}:stream:${queue}:${jobId}:events`;
495
+ }
496
+ getJobStreamSequenceKey(queue, jobId) {
497
+ return `${IgniterJobsPrefix.BASE_PREFIX}:stream:${queue}:${jobId}:seq`;
498
+ }
499
+ parseJobStreamRow(row) {
500
+ try {
501
+ const parsed = JSON.parse(row);
502
+ return {
503
+ ...parsed,
504
+ timestamp: new Date(parsed.timestamp)
505
+ };
506
+ } catch {
507
+ return null;
508
+ }
509
+ }
378
510
  mapQueueInfo(info) {
379
511
  return {
380
512
  name: this.fromCoreQueueName(info.name),
@@ -397,7 +529,7 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
397
529
  input: job.payload,
398
530
  result: job.result,
399
531
  error: job.error,
400
- progress: 0,
532
+ progress: typeof job.progress === "number" ? job.progress : 0,
401
533
  attemptsMade: job.attemptsMade ?? 0,
402
534
  priority: job.priority ?? 0,
403
535
  createdAt: job.createdAt,
@@ -408,7 +540,9 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
408
540
  };
409
541
  }
410
542
  mapWorker(handle) {
411
- const queues = handle.config?.queues ?? [handle.queueName];
543
+ const queues = handle.config?.queues ?? [
544
+ handle.queueName
545
+ ];
412
546
  return {
413
547
  id: handle.id,
414
548
  queues: queues.map((q) => this.fromCoreQueueName(q)),
@@ -423,6 +557,25 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
423
557
  }
424
558
  toCoreJobDefinition(queueName, jobName, def, fullQueueName) {
425
559
  const handler = async (ctx) => {
560
+ const updateProgress = typeof ctx.job?.updateProgress === "function" ? async (progress, message) => {
561
+ await ctx.job.updateProgress(progress);
562
+ await def.onProgress?.({
563
+ input: ctx.input,
564
+ context: ctx.context,
565
+ job: {
566
+ id: ctx.job.id,
567
+ name: jobName,
568
+ queue: queueName,
569
+ attemptsMade: ctx.job.attemptsMade,
570
+ createdAt: ctx.job.createdAt,
571
+ metadata: ctx.job.metadata,
572
+ updateProgress
573
+ },
574
+ scope: ctx.job.metadata?.__igniter_jobs_scope,
575
+ progress,
576
+ message
577
+ });
578
+ } : void 0;
426
579
  return def.handler({
427
580
  input: ctx.input,
428
581
  context: ctx.context,
@@ -432,7 +585,8 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
432
585
  queue: queueName,
433
586
  attemptsMade: ctx.job.attemptsMade,
434
587
  createdAt: ctx.job.createdAt,
435
- metadata: ctx.job.metadata
588
+ metadata: ctx.job.metadata,
589
+ updateProgress
436
590
  },
437
591
  scope: ctx.job.metadata?.__igniter_jobs_scope
438
592
  });
@@ -496,5 +650,5 @@ var IgniterJobsBullMQAdapter = class _IgniterJobsBullMQAdapter {
496
650
  };
497
651
 
498
652
  exports.IgniterJobsBullMQAdapter = IgniterJobsBullMQAdapter;
499
- //# sourceMappingURL=bullmq.adapter.js.map
500
- //# sourceMappingURL=bullmq.adapter.js.map
653
+ //# sourceMappingURL=node.js.map
654
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/prefix.ts","../../src/errors/jobs.error.ts","../../src/adapters/bullmq.adapter.ts"],"names":["IgniterError","options","createBullMQAdapter"],"mappings":";;;;;;;;AAGO,IAAM,kBAAA,GAAN,MAAM,kBAAA,CAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY7B,OAAc,eAAe,KAAA,EAAuB;AAClD,IAAA,OAAO,CAAA,EAAG,kBAAA,CAAkB,WAAW,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAc,mBAAmB,MAAA,EAItB;AACT,IAAA,MAAM,IAAA,GAAO,GAAG,kBAAA,CAAkB,WAAW,WAAW,MAAA,CAAO,WAAW,CAAA,CAAA,EAAI,MAAA,CAAO,OAAO,CAAA,CAAA;AAC5F,IAAA,IAAI,CAAC,MAAA,CAAO,KAAA,EAAO,OAAO,IAAA;AAC1B,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,OAAA,EAAU,MAAA,CAAO,MAAM,IAAI,CAAA,CAAA,EAAI,MAAA,CAAO,KAAA,CAAM,EAAE,CAAA,CAAA;AAAA,EAC9D;AAAA,EAEA,OAAc,sBAAsB,MAAA,EAMzB;AACT,IAAA,MAAM,IAAA,GAAO,CAAA,EAAG,kBAAA,CAAkB,WAAW,WAAW,MAAA,CAAO,WAAW,CAAA,CAAA,EAAI,MAAA,CAAO,OAAO,CAAA,CAAA,EAAI,MAAA,CAAO,KAAK,CAAA,CAAA,EAAI,OAAO,KAAK,CAAA,CAAA;AAC5H,IAAA,IAAI,CAAC,MAAA,CAAO,KAAA,EAAO,OAAO,IAAA;AAC1B,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,OAAA,EAAU,MAAA,CAAO,MAAM,IAAI,CAAA,CAAA,EAAI,MAAA,CAAO,KAAA,CAAM,EAAE,CAAA,CAAA;AAAA,EAC9D;AACF,CAAA;AA3Ca,kBAAA,CACY,WAAA,GAAc,cAAA;AADhC,IAAM,iBAAA,GAAN,kBAAA;AC6DA,IAAM,gBAAA,GAAN,cAA+BA,mBAAA,CAAa;AAAA,EACjD,YAAY,OAAA,EAAkC;AAC5C,IAAA,KAAA,CAAM,OAAO,CAAA;AAAA,EACf;AACF,CAAA;;;ACtBA,SAAS,YAAY,MAAA,EAAmD;AACtE,EAAA,IAAI,CAAC,QAAQ,OAAO,MAAA;AACpB,EAAA,OAAO,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAO,CAAA,YAAa,OAAO,CAAA,GAAI,IAAI,IAAA,CAAK,CAAC,CAAE,CAAA;AAChE;AAQO,IAAM,wBAAA,GAAN,MAAM,yBAAA,CAAuD;AAAA,EAuB1D,YAAY,OAAA,EAA0C;AAjB9D,IAAA,IAAA,CAAiB,WAAA,uBAAkB,GAAA,EAAyC;AAE5E,IAAA,IAAA,CAAQ,WAAA,GAAkC,IAAA;AAC1C,IAAA,IAAA,CAAQ,YAAA,GAA0C,IAAA;AAClD,IAAA,IAAA,CAAQ,aAAA,GAAgB,IAAA;AAExB,IAAA,IAAA,CAAiB,WAAA,uBAAkB,GAAA,EAGjC;AACF,IAAA,IAAA,CAAiB,YAAA,uBAAmB,GAAA,EAGlC;AAKA,IAAA,IAAA,CAAK,QAAQ,OAAA,CAAQ,KAAA;AACrB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK,KAAA;AACtB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA,CAAK,KAAA,CAAM,SAAA,EAAU;AACvC,IAAA,IAAA,CAAK,MAAA,GAAS,EAAE,KAAA,EAAO,IAAA,CAAK,KAAA,EAAM;AAElC,IAAA,IAAA,CAAK,UAAA,CAAW,EAAA,CAAG,SAAA,EAAW,CAAC,SAAiB,OAAA,KAAoB;AAClE,MAAA,MAAM,GAAA,GAAM,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,OAAO,CAAA;AACxC,MAAA,IAAI,CAAC,GAAA,IAAO,GAAA,CAAI,IAAA,KAAS,CAAA,EAAG;AAC5B,MAAA,IAAI,OAAA,GAAe,OAAA;AACnB,MAAA,IAAI;AACF,QAAA,OAAA,GAAU,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,MAC9B,CAAA,CAAA,MAAQ;AAAA,MAER;AACA,MAAA,KAAA,MAAW,OAAA,IAAW,GAAA,EAAK,OAAA,CAAQ,OAAO,CAAA;AAAA,IAC5C,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,MAAA,GAAS;AAAA,MACZ,IAAA,EAAM,YAAY,IAAA,CAAK,UAAA,EAAW;AAAA,MAClC,GAAA,EAAK,OAAO,IAAA,KAAS,IAAA,CAAK,aAAa,IAAI,CAAA;AAAA,MAC3C,YAAA,EAAc,OAAO,IAAA,KAAS,IAAA,CAAK,kBAAkB,IAAI,CAAA;AAAA,MACzD,OAAA,EAAS,OAAO,IAAA,EAAM,MAAA,KAAW;AAC/B,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,eAAA,CAAgB,IAAI,CAAA;AACtC,QAAA,OAAO,KAAK,IAAA,EAAK,CAAE,MAAA,CAAO,OAAA,CAAQ,MAAM,MAAa,CAAA;AAAA,MACvD,CAAA;AAAA,MACA,KAAA,EAAO,OAAO,IAAA,KAAS,IAAA,CAAK,WAAW,IAAI,CAAA;AAAA,MAC3C,MAAA,EAAQ,OAAO,IAAA,KAAS,IAAA,CAAK,YAAY,IAAI,CAAA;AAAA,MAC7C,QAAA,EAAU,OAAO,IAAA,KAAS;AACxB,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,eAAA,CAAgB,IAAI,CAAA;AACtC,QAAA,OAAO,IAAA,CAAK,IAAA,EAAK,CAAE,MAAA,CAAO,SAAS,IAAI,CAAA;AAAA,MACzC,CAAA;AAAA,MACA,KAAA,EAAO,OAAO,IAAA,KAAS,IAAA,CAAK,WAAW,IAAI,CAAA;AAAA,MAC3C,OAAO,OAAO,IAAA,EAAMC,aAAY,IAAA,CAAK,UAAA,CAAW,MAAMA,QAAO,CAAA;AAAA,MAC7D,YAAY,OAAO,IAAA,EAAMA,aAAY,IAAA,CAAK,eAAA,CAAgB,MAAMA,QAAO;AAAA,KACzE;AAAA,EACF;AAAA,EAEA,OAAc,OACZ,OAAA,EACoB;AACpB,IAAA,OAAO,IAAI,0BAAyB,OAAO,CAAA;AAAA,EAC7C;AAAA,EAEO,WAAA,CACL,SAAA,EACA,OAAA,EACA,UAAA,EACM;AACN,IAAA,MAAM,MAAM,IAAA,CAAK,WAAA,CAAY,IAAI,SAAS,CAAA,wBAAS,GAAA,EAAI;AACvD,IAAA,IAAI,GAAA,CAAI,GAAA,CAAI,OAAO,CAAA,EAAG;AACpB,MAAA,MAAM,IAAI,gBAAA,CAAiB;AAAA,QACzB,IAAA,EAAM,oBAAA;AAAA,QACN,OAAA,EAAS,CAAA,KAAA,EAAQ,OAAO,CAAA,kCAAA,EAAqC,SAAS,CAAA,EAAA;AAAA,OACvE,CAAA;AAAA,IACH;AACA,IAAA,GAAA,CAAI,GAAA,CAAI,SAAS,UAAU,CAAA;AAC3B,IAAA,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,SAAA,EAAW,GAAG,CAAA;AACnC,IAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AAAA,EACvB;AAAA,EAEO,YAAA,CACL,SAAA,EACA,QAAA,EACA,UAAA,EACM;AACN,IAAA,MAAM,MAAM,IAAA,CAAK,YAAA,CAAa,IAAI,SAAS,CAAA,wBAAS,GAAA,EAAI;AACxD,IAAA,IAAI,GAAA,CAAI,GAAA,CAAI,QAAQ,CAAA,EAAG;AACrB,MAAA,MAAM,IAAI,gBAAA,CAAiB;AAAA,QACzB,IAAA,EAAM,mBAAA;AAAA,QACN,OAAA,EAAS,CAAA,MAAA,EAAS,QAAQ,CAAA,kCAAA,EAAqC,SAAS,CAAA,EAAA;AAAA,OACzE,CAAA;AAAA,IACH;AACA,IAAA,GAAA,CAAI,GAAA,CAAI,UAAU,UAAU,CAAA;AAC5B,IAAA,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,SAAA,EAAW,GAAG,CAAA;AACpC,IAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AAAA,EACvB;AAAA,EAEA,MAAa,SACX,MAAA,EACiB;AACjB,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,EAAS;AACrC,IAAA,MAAM,SAAA,GAAa,QAAA,CAAiB,MAAA,CAAO,KAAK,CAAA;AAChD,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,MAAM,IAAI,gBAAA,CAAiB;AAAA,QACzB,IAAA,EAAM,sBAAA;AAAA,QACN,OAAA,EAAS,CAAA,OAAA,EAAU,MAAA,CAAO,KAAK,CAAA,mCAAA;AAAA,OAChC,CAAA;AAAA,IACH;AACA,IAAA,OAAO,UAAU,OAAA,CAAQ;AAAA,MACvB,MAAM,MAAA,CAAO,OAAA;AAAA,MACb,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,kBAAkB,MAAA,CAAO,gBAAA;AAAA,MACzB,cAAc,MAAA,CAAO,YAAA;AAAA,MACrB,SAAS,MAAA,CAAO;AAAA,KACjB,CAAA;AAAA,EACH;AAAA,EAEA,MAAa,SACX,MAAA,EACiB;AACjB,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,EAAS;AACrC,IAAA,MAAM,SAAA,GAAa,QAAA,CAAiB,MAAA,CAAO,KAAK,CAAA;AAChD,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,MAAM,IAAI,gBAAA,CAAiB;AAAA,QACzB,IAAA,EAAM,sBAAA;AAAA,QACN,OAAA,EAAS,CAAA,OAAA,EAAU,MAAA,CAAO,KAAK,CAAA,mCAAA;AAAA,OAChC,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,QAAA,GAAoC;AAAA,MACxC,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,kBAAkB,MAAA,CAAO,gBAAA;AAAA,MACzB,cAAc,MAAA,CAAO,YAAA;AAAA,MACrB,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,IAAI,MAAA,CAAO,EAAA;AAAA,MACX,QACE,MAAA,CAAO,IAAA,IACP,MAAA,CAAO,KAAA,IACP,OAAO,aAAA,IACP,MAAA,CAAO,YAAA,IACP,MAAA,CAAO,qBACP,MAAA,CAAO,aAAA,IACP,MAAA,CAAO,YAAA,IACP,OAAO,SAAA,GACH;AAAA,QACE,MAAM,MAAA,CAAO,IAAA;AAAA,QACb,OAAO,MAAA,CAAO,KAAA;AAAA,QACd,OAAO,MAAA,CAAO,aAAA;AAAA,QACd,cAAc,MAAA,CAAO,YAAA;AAAA,QACrB,mBAAmB,MAAA,CAAO,iBAAA;AAAA,QAC1B,eAAe,MAAA,CAAO,aAAA;AAAA,QACtB,cAAc,MAAA,CAAO,YAAA;AAAA,QACrB,SAAA,EAAW,WAAA,CAAY,MAAA,CAAO,SAAS;AAAA,OACzC,GACA;AAAA,KACR;AAEA,IAAA,OAAO,UAAU,QAAA,CAAS;AAAA,MACxB,MAAM,MAAA,CAAO,OAAA;AAAA,MACb,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,GAAG;AAAA,KACJ,CAAA;AAAA,EACH;AAAA,EAEA,MAAa,MAAA,CACX,KAAA,EACA,KAAA,EACwC;AACxC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,GAAO,GAAA,CAAI,GAAA;AAAA,MACnC,KAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AACA,IAAA,OAAO,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,KAAK,CAAA,GAAI,IAAA;AAAA,EAC/C;AAAA,EAEA,MAAa,WAAA,CACX,KAAA,EACA,KAAA,EACkC;AAClC,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,IAAA,GAAO,GAAA,CAAI,QAAA;AAAA,MAClC,KAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AACA,IAAA,OAAO,KAAA;AAAA,EACT;AAAA,EAEA,MAAa,UAAA,CACX,KAAA,EACA,KAAA,EAC8B;AAC9B,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,GAAO,GAAA,CAAI,OAAA;AAAA,MACjC,KAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAa,cAAA,CAAe,KAAA,EAAe,KAAA,EAAiC;AAC1E,IAAA,OAAO,IAAA,CAAK,IAAA,EAAK,CAAE,GAAA,CAAI,WAAA;AAAA,MACrB,KAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAa,QAAA,CAAS,KAAA,EAAe,KAAA,EAA+B;AAClE,IAAA,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,GAAA,CAAI,KAAA;AAAA,MACpB,KAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAa,SAAA,CAAU,KAAA,EAAe,KAAA,EAA+B;AACnE,IAAA,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,GAAA,CAAI,MAAA;AAAA,MACpB,KAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAa,UAAA,CAAW,KAAA,EAAe,KAAA,EAA+B;AACpE,IAAA,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,GAAA,CAAI,OAAA;AAAA,MACpB,KAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAa,eAAA,CACX,KAAA,EACA,MAAA,EACA,KAAA,EACe;AACf,IAAA,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,GAAA,CAAI,YAAA;AAAA,MACpB,KAAA;AAAA,MACA,MAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAa,aAAA,CAAc,MAAA,EAAkB,KAAA,EAA+B;AAC1E,IAAA,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,GAAA,CAAI,SAAA;AAAA,MACpB,MAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAa,cAAA,CAAe,MAAA,EAAkB,KAAA,EAA+B;AAC3E,IAAA,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,GAAA,CAAI,UAAA;AAAA,MACpB,MAAA;AAAA,MACA,KAAA,GAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,GAAI;AAAA,KACxC;AAAA,EACF;AAAA,EAEA,MAAa,aACX,KAAA,EACsC;AACtC,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,OAAO,GAAA,CAAI,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAC,CAAA;AACrE,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,IAAA,OAAO,IAAA,CAAK,aAAa,IAAI,CAAA;AAAA,EAC/B;AAAA,EAEA,MAAa,kBAAkB,KAAA,EAA6B;AAC1D,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,IAAA,GAAO,MAAA,CAAO,YAAA;AAAA,MACtC,IAAA,CAAK,gBAAgB,KAAK;AAAA,KAC5B;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,MAAa,UAAA,GAA8C;AACzD,IAAA,MAAM,OAAO,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,OAAO,IAAA,EAAK;AAC3C,IAAA,OAAO,KAAK,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,YAAA,CAAa,CAAC,CAAC,CAAA;AAAA,EAC7C;AAAA,EAEA,MAAa,WAAW,KAAA,EAA8B;AACpD,IAAA,MAAM,IAAA,CAAK,MAAK,CAAE,MAAA,CAAO,MAAM,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAC,CAAA;AAAA,EAC5D;AAAA,EAEA,MAAa,YAAY,KAAA,EAA8B;AACrD,IAAA,MAAM,IAAA,CAAK,MAAK,CAAE,MAAA,CAAO,OAAO,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAC,CAAA;AAAA,EAC7D;AAAA,EAEA,MAAa,WAAW,KAAA,EAAgC;AACtD,IAAA,OAAO,IAAA,CAAK,MAAK,CAAE,MAAA,CAAO,MAAM,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAC,CAAA;AAAA,EAC7D;AAAA,EAEA,MAAa,UAAA,CACX,KAAA,EACA,OAAA,EACiB;AACjB,IAAA,OAAO,IAAA,CAAK,IAAA,EAAK,CAAE,MAAA,CAAO,KAAA;AAAA,MACxB,IAAA,CAAK,gBAAgB,KAAK,CAAA;AAAA,MAC1B;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAa,eAAA,CACX,KAAA,EACA,OAAA,EACe;AACf,IAAA,MAAM,IAAA,CAAK,MAAK,CAAE,MAAA,CAAO,WAAW,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,EAAG,OAAO,CAAA;AAAA,EAC1E;AAAA,EAEA,MAAa,gBAAgB,KAAA,EAAgC;AAC3D,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,OAAO,OAAA,CAAQ,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA,EAAG;AAAA,MACzE,MAAA,EAAQ,CAAC,QAAQ,CAAA;AAAA,MACjB,KAAA,EAAO;AAAA,KACD,CAAA;AACR,IAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,MACZ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,KAAK,IAAA,EAAK,CAAE,GAAA,CAAI,KAAA,CAAM,EAAE,EAAA,EAAI,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAC,CAAC;AAAA,KAC1E;AACA,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,MAAa,WAAW,MAAA,EAAgD;AAEtE,IAAA,MAAM,QAAQ,MAAA,EAAQ,KAAA;AACtB,IAAA,MAAM,SAAS,MAAA,EAAQ,MAAA;AACvB,IAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,GAAA;AAC/B,IAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,CAAA;AAEjC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,GAAO,MAAA,CAAO,OAAA;AAAA,QACpC,IAAA,CAAK,gBAAgB,KAAK,CAAA;AAAA,QAC1B,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAA;AAAO,OAC1B;AACA,MAAA,OAAO,IAAA,CAAK,IAAI,CAAC,CAAA,KAAM,KAAK,MAAA,CAAO,CAAA,EAAU,KAAK,CAAC,CAAA;AAAA,IACrD;AAEA,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,UAAA,EAAW;AACrC,IAAA,MAAM,UAAoC,EAAC;AAC3C,IAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,GAAO,MAAA,CAAO,OAAA;AAAA,QACpC,IAAA,CAAK,eAAA,CAAgB,CAAA,CAAE,IAAI,CAAA;AAAA,QAC3B,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAA;AAAO,OAC1B;AACA,MAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,MAAA,CAAO,CAAA,EAAU,CAAA,CAAE,IAAI,CAAC,CAAC,CAAA;AAC9D,MAAA,IAAI,OAAA,CAAQ,UAAU,KAAA,EAAO;AAAA,IAC/B;AACA,IAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA;AAAA,EAC/B;AAAA,EAEA,MAAa,aAAa,MAAA,EAA8C;AACtE,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,UAAA,EAAW;AAClC,IAAA,MAAM,OAAO,MAAA,EAAQ,IAAA;AACrB,IAAA,MAAM,WAAW,MAAA,EAAQ,QAAA;AACzB,IAAA,OAAO,GAAA,CACJ,MAAA,CAAO,CAAC,CAAA,KAAO,IAAA,GAAO,CAAA,CAAE,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA,GAAI,IAAK,CAAA,CACnD,MAAA;AAAA,MAAO,CAAC,CAAA,KACP,OAAO,aAAa,SAAA,GAAY,CAAA,CAAE,aAAa,QAAA,GAAW;AAAA,KAC5D;AAAA,EACJ;AAAA,EAEA,MAAa,cAAc,MAAA,EAAiD;AAC1E,IAAA,MAAM,QAAQ,MAAA,EAAQ,KAAA;AACtB,IAAA,MAAM,YAAY,MAAA,EAAQ,SAAA;AAE1B,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,IAAA,CAAK,MAAK,CAAE,UAAA,EAAW,CAAE,MAAA,EAAQ,CAAA;AACxD,IAAA,OAAO,GAAA,CACJ,MAAA,CAAO,CAAC,CAAA,KAAM;AACb,MAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA;AAC5C,MAAA,MAAM,SAAU,CAAA,CAAU,MAAA,EAAQ,MAAA,IAAU,CAAE,EAAU,SAAS,CAAA;AACjE,MAAA,OAAO,MAAM,OAAA,CAAQ,MAAM,IAAI,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA,GAAI,KAAA;AAAA,IAC9D,CAAC,CAAA,CACA,MAAA;AAAA,MAAO,CAAC,CAAA,KACP,OAAO,SAAA,KAAc,SAAA,GACjB,SAAA,GACE,CAAA,CAAE,SAAA,EAAU,GACZ,CAAC,CAAA,CAAE,SAAA,EAAU,GACf;AAAA,MAEL,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,SAAA,CAAU,CAAC,CAAC,CAAA;AAAA,EACjC;AAAA,EAEA,MAAa,aACX,MAAA,EACkC;AAElC,IAAA,MAAM,KAAK,QAAA,EAAS;AAEpB,IAAA,MAAM,eAAe,MAAA,CAAO,MAAA,EAAQ,MAAA,GAChC,MAAA,CAAO,SACP,KAAA,CAAM,IAAA;AAAA,sBACJ,IAAI,GAAA,CAAI,CAAC,GAAG,IAAA,CAAK,WAAA,CAAY,IAAA,EAAK,EAAG,GAAG,IAAA,CAAK,YAAA,CAAa,IAAA,EAAM,CAAC;AAAA,KACnE;AACJ,IAAA,MAAM,MAAA,GAAS,aAAa,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,eAAA,CAAgB,CAAC,CAAC,CAAA;AAC9D,IAAA,MAAM,UAAA,GAA8B;AAAA,MAClC,MAAA;AAAA,MACA,WAAA,EAAa,OAAO,WAAA,IAAe,CAAA;AAAA,MACnC,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,QAAA,EAAU,OAAO,QAAA,EAAU,QAAA;AAAA,MAC3B,SAAA,EAAW,OAAO,QAAA,EAAU,SAAA;AAAA,MAC5B,SAAA,EAAW,OAAO,QAAA,EAAU,SAAA;AAAA,MAC5B,MAAA,EAAQ,OAAO,QAAA,EAAU;AAAA,KAC3B;AACA,IAAA,MAAM,SAAS,MAAO,IAAA,CAAK,IAAA,EAAK,CAAU,OAAO,UAAU,CAAA;AAC3D,IAAA,OAAO,IAAA,CAAK,UAAU,MAAM,CAAA;AAAA,EAC9B;AAAA,EAEO,UAAA,GAAmD;AACxD,IAAA,MAAM,GAAA,uBAAU,GAAA,EAAqC;AACrD,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,MAAM,KAAK,IAAA,CAAK,IAAA,GAAO,UAAA,EAAW;AAChD,MAAA,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA;AACpC,IAAA,OAAO,GAAA;AAAA,EACT;AAAA,EAEA,MAAa,YAAA,CAAa,OAAA,EAAiB,OAAA,EAAiC;AAC1E,IAAA,MAAM,KAAK,SAAA,CAAU,OAAA,CAAQ,SAAS,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA,EAC/D;AAAA,EAEA,MAAa,cAAA,CACX,OAAA,EACA,OAAA,EAC8B;AAC9B,IAAA,MAAM,MACJ,IAAA,CAAK,WAAA,CAAY,IAAI,OAAO,CAAA,wBAAS,GAAA,EAA4B;AACnE,IAAA,MAAM,OAAA,GAAU,CAAC,OAAA,KAAiB,KAAK,QAAQ,OAAc,CAAA;AAC7D,IAAA,GAAA,CAAI,IAAI,OAAO,CAAA;AACf,IAAA,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,OAAA,EAAS,GAAG,CAAA;AAEjC,IAAA,IAAI,GAAA,CAAI,SAAS,CAAA,EAAG;AAClB,MAAA,MAAM,IAAA,CAAK,UAAA,CAAW,SAAA,CAAU,OAAO,CAAA;AAAA,IACzC;AAEA,IAAA,OAAO,YAAY;AACjB,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,OAAO,CAAA;AAC5C,MAAA,IAAI,CAAC,OAAA,EAAS;AACd,MAAA,OAAA,CAAQ,OAAO,OAAO,CAAA;AACtB,MAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,QAAA,IAAA,CAAK,WAAA,CAAY,OAAO,OAAO,CAAA;AAC/B,QAAA,MAAM,IAAA,CAAK,UAAA,CAAW,WAAA,CAAY,OAAO,CAAA;AAAA,MAC3C;AAAA,IACF,CAAA;AAAA,EACF;AAAA,EAEA,MAAa,oBACX,MAAA,EACiB;AACjB,IAAA,MAAM,EAAA,GAAK,MAAA;AAAA,MACT,MAAM,KAAK,SAAA,CAAU,IAAA;AAAA,QACnB,IAAA,CAAK,uBAAA,CAAwB,MAAA,CAAO,KAAA,EAAO,OAAO,KAAK;AAAA;AACzD,KACF;AACA,IAAA,MAAM,KAAA,GAAoD;AAAA,MACxD,GAAG,MAAA,CAAO,KAAA;AAAA,MACV;AAAA,KACF;AAEA,IAAA,IAAI,MAAA,CAAO,aAAa,OAAA,EAAS;AAC/B,MAAA,MAAM,UAAU,IAAA,CAAK,mBAAA,CAAoB,MAAA,CAAO,KAAA,EAAO,OAAO,KAAK,CAAA;AACnE,MAAA,MAAM,KAAK,SAAA,CAAU,KAAA,CAAM,SAAS,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AACzD,MAAA,MAAM,SAAA,GAAY,OAAO,WAAA,CAAY,SAAA;AACrC,MAAA,IAAI,OAAO,SAAA,KAAc,QAAA,IAAY,SAAA,GAAY,CAAA,EAAG;AAClD,QAAA,MAAM,KAAK,SAAA,CAAU,KAAA,CAAM,OAAA,EAAS,CAAC,WAAW,EAAE,CAAA;AAAA,MACpD;AAAA,IACF;AAEA,IAAA,MAAM,KAAK,SAAA,CAAU,OAAA;AAAA,MACnB,IAAA,CAAK,mBAAA,CAAoB,MAAA,CAAO,KAAA,EAAO,OAAO,KAAK,CAAA;AAAA,MACnD,IAAA,CAAK,UAAU,KAAK;AAAA,KACtB;AAEA,IAAA,OAAO,EAAA;AAAA,EACT;AAAA,EAEA,MAAa,cACX,MAAA,EAGA;AACA,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,SAAA,CAAU,MAAA;AAAA,MAChC,IAAA,CAAK,mBAAA,CAAoB,MAAA,CAAO,KAAA,EAAO,OAAO,KAAK,CAAA;AAAA,MACnD,CAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAM,QAAQ,MAAA,CAAO,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,GAAI,MAAA;AACpD,IAAA,MAAM,KAAA,GAAQ,OAAO,KAAA,IAAS,GAAA;AAC9B,IAAA,MAAM,MAAA,GAAS,KACZ,GAAA,CAAI,CAAC,QAAQ,IAAA,CAAK,iBAAA,CAAkB,GAAG,CAAC,CAAA,CACxC,MAAA;AAAA,MAAO,CAAC,KAAA,KACP,OAAA,CAAQ,KAAK;AAAA,KACf,CACC,MAAA;AAAA,MAAO,CAAC,UACP,OAAO,KAAA,KAAU,WAAW,MAAA,CAAO,KAAA,CAAM,EAAE,CAAA,GAAI,KAAA,GAAQ;AAAA,KACzD;AAEF,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA;AACnC,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,UAAA,EAAY,KAAA,CAAM,EAAA,CAAG,EAAE,CAAA,EAAG,EAAA;AAAA,MAC1B,OAAA,EAAS,MAAA,CAAO,MAAA,GAAS,KAAA,CAAM;AAAA,KACjC;AAAA,EACF;AAAA,EAEA,MAAa,mBACX,MAAA,EAC8B;AAC9B,IAAA,MAAM,UAAU,IAAA,CAAK,mBAAA,CAAoB,MAAA,CAAO,KAAA,EAAO,OAAO,KAAK,CAAA;AACnE,IAAA,OAAO,IAAA,CAAK,cAAA,CAAe,OAAA,EAAS,OAAO,OAAA,KAAY;AACrD,MAAA,MAAM,MAAA,CAAO,OAAA;AAAA,QACX;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAa,QAAA,GAA0B;AACrC,IAAA,MAAM,IAAA,CAAK,WAAW,IAAA,EAAK;AAAA,EAG7B;AAAA,EAEQ,IAAA,GAAoB;AAC1B,IAAA,IAAI,CAAC,KAAK,WAAA,EAAa;AAErB,MAAA,IAAA,CAAK,cAAcC,iCAAA,CAAoB;AAAA,QACrC,KAAA,EAAO,EAAE,MAAA,EAAQ,IAAA,CAAK,KAAA;AAAM,OAC7B,CAAA;AAAA,IACH;AACA,IAAA,OAAO,IAAA,CAAK,WAAA;AAAA,EACd;AAAA,EAEA,MAAc,QAAA,GAAwC;AACpD,IAAA,IAAI,CAAC,IAAA,CAAK,aAAA,IAAiB,IAAA,CAAK,YAAA,SAAqB,IAAA,CAAK,YAAA;AAE1D,IAAA,MAAM,UAA2C,EAAC;AAClD,IAAA,MAAM,YAA8D,EAAC;AAErE,IAAA,MAAM,SAAA,uBAAgB,GAAA,CAAY;AAAA,MAChC,GAAG,IAAA,CAAK,WAAA,CAAY,IAAA,EAAK;AAAA,MACzB,GAAG,IAAA,CAAK,YAAA,CAAa,IAAA;AAAK,KAC3B,CAAA;AAED,IAAA,KAAA,MAAW,aAAa,SAAA,EAAW;AACjC,MAAA,MAAM,WAA6D,EAAC;AAEpE,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,SAAS,CAAA;AAC3C,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,KAAA,MAAW,CAAC,OAAA,EAAS,GAAG,CAAA,IAAK,IAAA,CAAK,SAAQ,EAAG;AAC3C,UAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,GAAQ,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,GAAA,CAAI,KAAK,CAAA,CAAA,GAAK,SAAA;AACxD,UAAA,MAAM,SAAA,GAAY,iBAAA,CAAkB,cAAA,CAAe,KAAK,CAAA;AACxD,UAAA,QAAA,CAAS,OAAO,IAAI,IAAA,CAAK,mBAAA;AAAA,YACvB,SAAA;AAAA,YACA,OAAA;AAAA,YACA,GAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF;AAAA,MACF;AAEA,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,SAAS,CAAA;AAC7C,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,KAAA,MAAW,CAAC,QAAA,EAAU,GAAG,CAAA,IAAK,KAAA,CAAM,SAAQ,EAAG;AAC7C,UAAA,MAAM,SAAA,GAAY,iBAAA,CAAkB,cAAA,CAAe,SAAS,CAAA;AAC5D,UAAA,QAAA,CAAS,QAAQ,IAAI,IAAA,CAAK,uBAAA;AAAA,YACxB,SAAA;AAAA,YACA,QAAA;AAAA,YACA,GAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF;AAAA,MACF;AAEA,MAAA,IAAI,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,CAAE,WAAW,CAAA,EAAG;AAExC,MAAA,OAAA,CAAQ,SAAS,CAAA,GAAI,IAAA,CAAK,IAAA,GAAO,MAAA,CAAO;AAAA,QACtC,IAAA,EAAM,QAAA;AAAA,QACN,SAAA,EAAW;AAAA,OACZ,CAAA;AAED,MAAA,KAAA,MAAW,CAAC,OAAA,EAAS,GAAG,KAAK,MAAA,CAAO,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACrD,QAAA,SAAA,CAAU,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,OAAO,EAAE,CAAA,GAAI,GAAA;AAAA,MACzC;AAAA,IACF;AAGA,IAAA,MAAM,IAAA,CAAK,IAAA,EAAK,CAAE,YAAA,CAAa,SAAgB,CAAA;AAE/C,IAAA,IAAA,CAAK,YAAA,GAAe,IAAA,CAAK,IAAA,EAAK,CAAE,MAAM,OAAc,CAAA;AACpD,IAAA,IAAA,CAAK,aAAA,GAAgB,KAAA;AACrB,IAAA,OAAO,IAAA,CAAK,YAAA;AAAA,EACd;AAAA,EAEQ,gBAAgB,SAAA,EAA2B;AACjD,IAAA,OAAO,iBAAA,CAAkB,eAAe,SAAS,CAAA;AAAA,EACnD;AAAA,EAEQ,mBAAA,CAAoB,OAAe,KAAA,EAAuB;AAChE,IAAA,OAAO,GAAG,iBAAA,CAAkB,WAAW,CAAA,QAAA,EAAW,KAAK,IAAI,KAAK,CAAA,KAAA,CAAA;AAAA,EAClE;AAAA,EAEQ,mBAAA,CAAoB,OAAe,KAAA,EAAuB;AAChE,IAAA,OAAO,GAAG,iBAAA,CAAkB,WAAW,CAAA,QAAA,EAAW,KAAK,IAAI,KAAK,CAAA,OAAA,CAAA;AAAA,EAClE;AAAA,EAEQ,uBAAA,CAAwB,OAAe,KAAA,EAAuB;AACpE,IAAA,OAAO,GAAG,iBAAA,CAAkB,WAAW,CAAA,QAAA,EAAW,KAAK,IAAI,KAAK,CAAA,IAAA,CAAA;AAAA,EAClE;AAAA,EAEQ,kBACN,GAAA,EACmD;AACnD,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAI7B,MAAA,OAAO;AAAA,QACL,GAAG,MAAA;AAAA,QACH,SAAA,EAAW,IAAI,IAAA,CAAK,MAAA,CAAO,SAAS;AAAA,OACtC;AAAA,IACF,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,aAAa,IAAA,EAAiC;AACpD,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,IAAA,CAAK,iBAAA,CAAkB,IAAA,CAAK,IAAI,CAAA;AAAA,MACtC,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,WAAW,IAAA,CAAK;AAAA,KAClB;AAAA,EACF;AAAA,EAEQ,kBAAkB,IAAA,EAAsB;AAC9C,IAAA,MAAM,MAAA,GAAS,CAAA,EAAG,iBAAA,CAAkB,WAAW,CAAA,CAAA,CAAA;AAC/C,IAAA,OAAO,IAAA,CAAK,WAAW,MAAM,CAAA,GAAI,KAAK,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA,GAAI,IAAA;AAAA,EAC/D;AAAA,EAEQ,MAAA,CAAO,KAAU,KAAA,EAAwC;AAC/D,IAAA,MAAM,CAAA,GACJ,SACA,IAAA,CAAK,iBAAA,CAAkB,IAAI,QAAA,EAAU,KAAA,IAAS,GAAA,CAAI,SAAA,IAAa,EAAE,CAAA;AACnE,IAAA,MAAM,KAAA,GAAS,IAAI,QAAA,EAAkB,oBAAA;AACrC,IAAA,OAAO;AAAA,MACL,IAAI,GAAA,CAAI,EAAA;AAAA,MACR,MAAM,GAAA,CAAI,IAAA;AAAA,MACV,KAAA,EAAO,CAAA;AAAA,MACP,QAAQ,GAAA,CAAI,MAAA;AAAA,MACZ,OAAO,GAAA,CAAI,OAAA;AAAA,MACX,QAAQ,GAAA,CAAI,MAAA;AAAA,MACZ,OAAO,GAAA,CAAI,KAAA;AAAA,MACX,UAAU,OAAO,GAAA,CAAI,QAAA,KAAa,QAAA,GAAW,IAAI,QAAA,GAAW,CAAA;AAAA,MAC5D,YAAA,EAAc,IAAI,YAAA,IAAgB,CAAA;AAAA,MAClC,QAAA,EAAU,IAAI,QAAA,IAAY,CAAA;AAAA,MAC1B,WAAW,GAAA,CAAI,SAAA;AAAA,MACf,WAAW,GAAA,CAAI,WAAA;AAAA,MACf,aAAa,GAAA,CAAI,WAAA;AAAA,MACjB,UAAU,GAAA,CAAI,QAAA;AAAA,MACd;AAAA,KACF;AAAA,EACF;AAAA,EAEQ,UAAU,MAAA,EAAsC;AACtD,IAAA,MAAM,MAAA,GAAU,MAAA,CAAe,MAAA,EAAQ,MAAA,IAAU;AAAA,MAC9C,MAAA,CAAe;AAAA,KAClB;AACA,IAAA,OAAO;AAAA,MACL,IAAI,MAAA,CAAO,EAAA;AAAA,MACX,MAAA,EAAS,OAAoB,GAAA,CAAI,CAAC,MAAM,IAAA,CAAK,iBAAA,CAAkB,CAAC,CAAC,CAAA;AAAA,MACjE,KAAA,EAAO,MAAM,MAAA,CAAO,KAAA,EAAM;AAAA,MAC1B,MAAA,EAAQ,MAAM,MAAA,CAAO,MAAA,EAAO;AAAA,MAC5B,KAAA,EAAO,MAAM,MAAA,CAAO,KAAA,EAAM;AAAA,MAC1B,SAAA,EAAW,MAAM,MAAA,CAAO,SAAA,EAAU;AAAA,MAClC,QAAA,EAAU,MAAM,MAAA,CAAO,QAAA,EAAS;AAAA,MAChC,QAAA,EAAU,MAAM,MAAA,CAAO,QAAA,EAAS;AAAA,MAChC,UAAA,EAAY,YAAY,MAAA,CAAO,UAAA;AAAW,KAC5C;AAAA,EACF;AAAA,EAEQ,mBAAA,CACN,SAAA,EACA,OAAA,EACA,GAAA,EACA,aAAA,EACkC;AAClC,IAAA,MAAM,OAAA,GAAU,OAAO,GAAA,KAA2C;AAChE,MAAA,MAAM,cAAA,GACJ,OAAQ,GAAA,CAAI,GAAA,EAAa,mBAAmB,UAAA,GACxC,OAAO,UAAkB,OAAA,KAAqB;AAC5C,QAAA,MAAO,GAAA,CAAI,GAAA,CAAY,cAAA,CAAe,QAAQ,CAAA;AAC9C,QAAA,MAAM,IAAI,UAAA,GAAa;AAAA,UACrB,OAAO,GAAA,CAAI,KAAA;AAAA,UACX,SAAS,GAAA,CAAI,OAAA;AAAA,UACb,GAAA,EAAK;AAAA,YACH,EAAA,EAAI,IAAI,GAAA,CAAI,EAAA;AAAA,YACZ,IAAA,EAAM,OAAA;AAAA,YACN,KAAA,EAAO,SAAA;AAAA,YACP,YAAA,EAAc,IAAI,GAAA,CAAI,YAAA;AAAA,YACtB,SAAA,EAAY,IAAI,GAAA,CAAY,SAAA;AAAA,YAC5B,QAAA,EAAU,IAAI,GAAA,CAAI,QAAA;AAAA,YAClB;AAAA,WACF;AAAA,UACA,KAAA,EAAQ,GAAA,CAAI,GAAA,CAAI,QAAA,EAAkB,oBAAA;AAAA,UAClC,QAAA;AAAA,UACA;AAAA,SACM,CAAA;AAAA,MACV,CAAA,GACA,MAAA;AAEN,MAAA,OAAO,IAAI,OAAA,CAAQ;AAAA,QACjB,OAAO,GAAA,CAAI,KAAA;AAAA,QACX,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,GAAA,EAAK;AAAA,UACH,EAAA,EAAI,IAAI,GAAA,CAAI,EAAA;AAAA,UACZ,IAAA,EAAM,OAAA;AAAA,UACN,KAAA,EAAO,SAAA;AAAA,UACP,YAAA,EAAc,IAAI,GAAA,CAAI,YAAA;AAAA,UACtB,SAAA,EAAY,IAAI,GAAA,CAAY,SAAA;AAAA,UAC5B,QAAA,EAAU,IAAI,GAAA,CAAI,QAAA;AAAA,UAClB;AAAA,SACF;AAAA,QACA,KAAA,EAAQ,GAAA,CAAI,GAAA,CAAI,QAAA,EAAkB;AAAA,OAC5B,CAAA;AAAA,IACV,CAAA;AAEA,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,OAAA;AAAA,MACN,OAAO,GAAA,CAAI,KAAA;AAAA,MACX,OAAA;AAAA,MACA,KAAA,EAAO,EAAE,IAAA,EAAM,aAAA,EAAc;AAAA,MAC7B,UAAU,GAAA,CAAI,QAAA;AAAA,MACd,UAAU,GAAA,CAAI,QAAA;AAAA,MACd,OAAO,GAAA,CAAI,KAAA;AAAA,MACX,kBAAkB,GAAA,CAAI,gBAAA;AAAA,MACtB,cAAc,GAAA,CAAI,YAAA;AAAA,MAClB,UAAU,GAAA,CAAI,QAAA;AAAA,MACd,SAAS,GAAA,CAAI,OAAA;AAAA,MACb,SAAS,GAAA,CAAI,OAAA;AAAA,MACb,WAAW,GAAA,CAAI,SAAA;AAAA,MACf,WAAW,GAAA,CAAI,SAAA;AAAA,MACf,YAAY,GAAA,CAAI;AAAA,KAClB;AAAA,EACF;AAAA,EAEQ,uBAAA,CACN,SAAA,EACA,QAAA,EACA,GAAA,EACA,aAAA,EACkC;AAClC,IAAA,MAAM,OAAA,GAAU,OAAO,GAAA,KAA2C;AAChE,MAAA,OAAO,IAAI,OAAA,CAAQ;AAAA,QACjB,SAAS,GAAA,CAAI,OAAA;AAAA,QACb,GAAA,EAAK;AAAA,UACH,EAAA,EAAI,IAAI,GAAA,CAAI,EAAA;AAAA,UACZ,IAAA,EAAM,QAAA;AAAA,UACN,KAAA,EAAO,SAAA;AAAA,UACP,YAAA,EAAc,IAAI,GAAA,CAAI,YAAA;AAAA,UACtB,SAAA,EAAY,IAAI,GAAA,CAAY,SAAA;AAAA,UAC5B,QAAA,EAAU,IAAI,GAAA,CAAI;AAAA,SACpB;AAAA,QACA,KAAA,EAAQ,GAAA,CAAI,GAAA,CAAI,QAAA,EAAkB;AAAA,OAC5B,CAAA;AAAA,IACV,CAAA;AAEA,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,QAAA;AAAA,MACN,OAAA;AAAA,MACA,KAAA,EAAO,EAAE,IAAA,EAAM,aAAA,EAAc;AAAA,MAC7B,MAAA,EAAQ;AAAA,QACN,MAAM,GAAA,CAAI,IAAA;AAAA,QACV,IAAI,GAAA,CAAI,EAAA;AAAA,QACR,OAAO,GAAA,CAAI,aAAA;AAAA,QACX,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,SAAS,GAAA,CAAI;AAAA,OACf;AAAA,MACA,QAAA,EACE,GAAA,CAAI,iBAAA,IACJ,GAAA,CAAI,gBACJ,GAAA,CAAI,aAAA,IACJ,GAAA,CAAI,YAAA,IACJ,GAAA,CAAI,SAAA,IACH,GAAA,CAAI,SAAA,IAAa,IAAI,OAAA,GAClB;AAAA,QACE,kBAAA,EAAoB;AAAA,UAClB,mBAAmB,GAAA,CAAI,iBAAA;AAAA,UACvB,cAAc,GAAA,CAAI,YAAA;AAAA,UAClB,eAAe,GAAA,CAAI,aAAA;AAAA,UACnB,SAAA,EAAW,WAAA,CAAY,GAAA,CAAI,SAAS,CAAA;AAAA,UACpC,cAAc,GAAA,CAAI,YAAA;AAAA,UAClB,OAAA,EACE,GAAA,CAAI,SAAA,IAAa,GAAA,CAAI,OAAA,GACjB,CAAC,GAAA,CAAI,SAAA,EAAW,GAAA,CAAI,OAAO,CAAA,GAC3B;AAAA;AACR,OACF,GACA;AAAA,KACR;AAAA,EACF;AACF","file":"node.js","sourcesContent":["/**\n * Helpers for building consistent key/queue prefixes.\n */\nexport class IgniterJobsPrefix {\n public static readonly BASE_PREFIX = \"igniter:jobs\";\n\n /**\n * Builds a normalized queue name using the global prefix and queue id.\n *\n * @example\n * ```typescript\n * const name = IgniterJobsPrefix.buildQueueName('email')\n * // -> igniter:jobs:email\n * ```\n */\n public static buildQueueName(queue: string): string {\n return `${IgniterJobsPrefix.BASE_PREFIX}:${queue}`;\n }\n\n /**\n * Builds the event channel used for pub/sub.\n *\n * Unscoped events are published to a global channel per service/environment.\n * Scoped events are also published to an additional channel for that scope.\n */\n public static buildEventsChannel(params: {\n service: string;\n environment: string;\n scope?: { type: string; id: string | number };\n }): string {\n const base = `${IgniterJobsPrefix.BASE_PREFIX}:events:${params.environment}:${params.service}`;\n if (!params.scope) return base;\n return `${base}:scope:${params.scope.type}:${params.scope.id}`;\n }\n\n public static buildJobStreamChannel(params: {\n service: string;\n environment: string;\n queue: string;\n jobId: string;\n scope?: { type: string; id: string | number };\n }): string {\n const base = `${IgniterJobsPrefix.BASE_PREFIX}:stream:${params.environment}:${params.service}:${params.queue}:${params.jobId}`;\n if (!params.scope) return base;\n return `${base}:scope:${params.scope.type}:${params.scope.id}`;\n }\n}\n","import { IgniterError } from \"@igniter-js/common\";\n\n/**\n * Canonical error codes for `@igniter-js/jobs`.\n */\nexport const IGNITER_JOBS_ERROR_CODES = {\n JOBS_ADAPTER_REQUIRED: 'JOBS_ADAPTER_REQUIRED',\n JOBS_SERVICE_REQUIRED: 'JOBS_SERVICE_REQUIRED',\n JOBS_CONTEXT_REQUIRED: 'JOBS_CONTEXT_REQUIRED',\n JOBS_CONFIGURATION_INVALID: 'JOBS_CONFIGURATION_INVALID',\n JOBS_QUEUE_NOT_FOUND: 'JOBS_QUEUE_NOT_FOUND',\n JOBS_QUEUE_DUPLICATE: 'JOBS_QUEUE_DUPLICATE',\n JOBS_QUEUE_OPERATION_FAILED: 'JOBS_QUEUE_OPERATION_FAILED',\n JOBS_INVALID_DEFINITION: 'JOBS_INVALID_DEFINITION',\n JOBS_HANDLER_REQUIRED: 'JOBS_HANDLER_REQUIRED',\n JOBS_DUPLICATE_JOB: 'JOBS_DUPLICATE_JOB',\n JOBS_NOT_FOUND: 'JOBS_NOT_FOUND',\n JOBS_NOT_REGISTERED: 'JOBS_NOT_REGISTERED',\n JOBS_EXECUTION_FAILED: 'JOBS_EXECUTION_FAILED',\n JOBS_TIMEOUT: 'JOBS_TIMEOUT',\n JOBS_CONTEXT_FACTORY_FAILED: 'JOBS_CONTEXT_FACTORY_FAILED',\n JOBS_VALIDATION_FAILED: 'JOBS_VALIDATION_FAILED',\n JOBS_INVALID_INPUT: 'JOBS_INVALID_INPUT',\n JOBS_INVALID_CRON: 'JOBS_INVALID_CRON',\n JOBS_INVALID_SCHEDULE: 'JOBS_INVALID_SCHEDULE',\n JOBS_SCOPE_ALREADY_DEFINED: 'JOBS_SCOPE_ALREADY_DEFINED',\n JOBS_WORKER_FAILED: 'JOBS_WORKER_FAILED',\n JOBS_ADAPTER_ERROR: 'JOBS_ADAPTER_ERROR',\n JOBS_ADAPTER_CONNECTION_FAILED: 'JOBS_ADAPTER_CONNECTION_FAILED',\n JOBS_SUBSCRIBE_FAILED: 'JOBS_SUBSCRIBE_FAILED',\n} as const\n\nexport type IgniterJobsErrorCode = keyof typeof IGNITER_JOBS_ERROR_CODES\n\nexport interface IgniterJobsErrorOptions {\n /** Error code scoped to Igniter Jobs. */\n code: IgniterJobsErrorCode\n /** Human-readable message. */\n message: string\n /** HTTP-like status code hint (default: 500). */\n statusCode?: number\n /** Optional structured details for debugging and clients. */\n details?: unknown\n /** Optional metadata for logs and tracing. */\n metadata?: Record<string, unknown>\n /** Optional causer tag used by some Igniter tooling. */\n causer?: string\n /** Underlying cause for debugging (optional). */\n cause?: Error\n /** Optional logger passthrough to align with other Igniter errors. */\n logger?: any\n}\n\n/**\n * Typed error class for the Jobs package.\n *\n * @example\n * ```typescript\n * throw new IgniterJobsError({\n * code: 'JOBS_INVALID_INPUT',\n * message: 'Input payload failed validation',\n * })\n * ```\n */\nexport class IgniterJobsError extends IgniterError {\n constructor(options: IgniterJobsErrorOptions) {\n super(options)\n }\n}\n","/**\n * @fileoverview BullMQ adapter for @igniter-js/jobs (wraps @igniter-js/adapter-bullmq)\n * @module @igniter-js/jobs/adapters/bullmq\n */\n\nimport type { Redis } from \"ioredis\";\nimport type {\n AdvancedScheduleOptions,\n IgniterJobQueueAdapter,\n JobsRouter,\n JobDefinition as CoreJobDefinition,\n JobExecutionContext as CoreJobExecutionContext,\n JobWorkerConfig,\n} from \"@igniter-js/core\";\nimport { createBullMQAdapter } from \"@igniter-js/adapter-bullmq\";\nimport type {\n IgniterJobsAdapterJobStreamReadParams,\n IgniterJobsAdapterJobStreamSubscribeParams,\n IgniterJobsAdapterJobStreamWriteParams,\n IgniterCronDefinition,\n IgniterJobDefinition,\n IgniterJobSearchResult,\n IgniterJobStatus,\n IgniterJobsAdapter,\n IgniterJobsAdapterDispatchParams,\n IgniterJobsAdapterScheduleParams,\n IgniterJobsBullMQAdapterOptions,\n IgniterJobsEventHandler,\n IgniterJobsJobLog,\n IgniterJobsQueueCleanOptions,\n IgniterJobsQueueInfo,\n IgniterJobsQueueManager,\n IgniterJobsWorkerBuilderConfig,\n IgniterJobsWorkerHandle,\n IgniterJobsWorkerMetrics,\n} from \"../types\";\nimport { IgniterJobsPrefix } from \"../utils/prefix\";\nimport { IgniterJobsError } from \"../errors\";\nimport type {\n IgniterJobsJobStreamEvent,\n IgniterJobsJobStreamReadResult,\n} from \"../types/stream\";\n\ntype CoreAdapter = IgniterJobQueueAdapter<any>;\ntype CoreMergedExecutor = ReturnType<CoreAdapter[\"merge\"]>;\n\nfunction toDateArray(values?: Array<string | Date>): Date[] | undefined {\n if (!values) return undefined;\n return values.map((v) => (v instanceof Date ? v : new Date(v)));\n}\n\n/**\n * BullMQ adapter facade.\n *\n * It reuses the mature BullMQ integration from `@igniter-js/adapter-bullmq` to keep\n * feature parity (workers, queue/job management, advanced scheduling, hooks).\n */\nexport class IgniterJobsBullMQAdapter implements IgniterJobsAdapter {\n public readonly client: unknown;\n\n private readonly redis: Redis;\n private readonly publisher: Redis;\n private readonly subscriber: Redis;\n private readonly subscribers = new Map<string, Set<(payload: any) => void>>();\n\n private coreAdapter: CoreAdapter | null = null;\n private coreExecutor: CoreMergedExecutor | null = null;\n private executorDirty = true;\n\n private readonly jobsByQueue = new Map<\n string,\n Map<string, IgniterJobDefinition<any, any, any>>\n >();\n private readonly cronsByQueue = new Map<\n string,\n Map<string, IgniterCronDefinition<any, any>>\n >();\n\n public readonly queues: IgniterJobsQueueManager;\n\n private constructor(options: IgniterJobsBullMQAdapterOptions) {\n this.redis = options.redis;\n this.publisher = this.redis;\n this.subscriber = this.redis.duplicate();\n this.client = { redis: this.redis };\n\n this.subscriber.on(\"message\", (channel: string, message: string) => {\n const set = this.subscribers.get(channel);\n if (!set || set.size === 0) return;\n let payload: any = message;\n try {\n payload = JSON.parse(message);\n } catch {\n // ignore, treat as raw\n }\n for (const handler of set) handler(payload);\n });\n\n this.queues = {\n list: async () => this.listQueues(),\n get: async (name) => this.getQueueInfo(name),\n getJobCounts: async (name) => this.getQueueJobCounts(name),\n getJobs: async (name, filter) => {\n const full = this.toCoreQueueName(name);\n return this.core().queues.getJobs(full, filter as any) as any;\n },\n pause: async (name) => this.pauseQueue(name),\n resume: async (name) => this.resumeQueue(name),\n isPaused: async (name) => {\n const full = this.toCoreQueueName(name);\n return this.core().queues.isPaused(full);\n },\n drain: async (name) => this.drainQueue(name),\n clean: async (name, options) => this.cleanQueue(name, options),\n obliterate: async (name, options) => this.obliterateQueue(name, options),\n };\n }\n\n public static create(\n options: IgniterJobsBullMQAdapterOptions,\n ): IgniterJobsAdapter {\n return new IgniterJobsBullMQAdapter(options);\n }\n\n public registerJob(\n queueName: string,\n jobName: string,\n definition: IgniterJobDefinition<any, any, any>,\n ): void {\n const map = this.jobsByQueue.get(queueName) ?? new Map();\n if (map.has(jobName)) {\n throw new IgniterJobsError({\n code: \"JOBS_DUPLICATE_JOB\",\n message: `Job \"${jobName}\" is already registered in queue \"${queueName}\".`,\n });\n }\n map.set(jobName, definition);\n this.jobsByQueue.set(queueName, map);\n this.executorDirty = true;\n }\n\n public registerCron(\n queueName: string,\n cronName: string,\n definition: IgniterCronDefinition<any, any>,\n ): void {\n const map = this.cronsByQueue.get(queueName) ?? new Map();\n if (map.has(cronName)) {\n throw new IgniterJobsError({\n code: \"JOBS_INVALID_CRON\",\n message: `Cron \"${cronName}\" is already registered in queue \"${queueName}\".`,\n });\n }\n map.set(cronName, definition);\n this.cronsByQueue.set(queueName, map);\n this.executorDirty = true;\n }\n\n public async dispatch(\n params: IgniterJobsAdapterDispatchParams,\n ): Promise<string> {\n const executor = await this.executor();\n const namespace = (executor as any)[params.queue];\n if (!namespace) {\n throw new IgniterJobsError({\n code: \"JOBS_QUEUE_NOT_FOUND\",\n message: `Queue \"${params.queue}\" is not registered in the adapter.`,\n });\n }\n return namespace.enqueue({\n task: params.jobName,\n input: params.input,\n jobId: params.jobId,\n delay: params.delay,\n priority: params.priority,\n attempts: params.attempts,\n metadata: params.metadata as any,\n removeOnComplete: params.removeOnComplete as any,\n removeOnFail: params.removeOnFail as any,\n limiter: params.limiter as any,\n });\n }\n\n public async schedule(\n params: IgniterJobsAdapterScheduleParams,\n ): Promise<string> {\n const executor = await this.executor();\n const namespace = (executor as any)[params.queue];\n if (!namespace) {\n throw new IgniterJobsError({\n code: \"JOBS_QUEUE_NOT_FOUND\",\n message: `Queue \"${params.queue}\" is not registered in the adapter.`,\n });\n }\n\n const schedule: AdvancedScheduleOptions = {\n jobId: params.jobId,\n delay: params.delay,\n priority: params.priority,\n attempts: params.attempts,\n metadata: params.metadata as any,\n removeOnComplete: params.removeOnComplete as any,\n removeOnFail: params.removeOnFail as any,\n limiter: params.limiter as any,\n at: params.at,\n repeat:\n params.cron ||\n params.every ||\n params.maxExecutions ||\n params.skipWeekends ||\n params.onlyBusinessHours ||\n params.businessHours ||\n params.onlyWeekdays ||\n params.skipDates\n ? {\n cron: params.cron,\n every: params.every,\n times: params.maxExecutions,\n skipWeekends: params.skipWeekends,\n onlyBusinessHours: params.onlyBusinessHours,\n businessHours: params.businessHours,\n onlyWeekdays: params.onlyWeekdays,\n skipDates: toDateArray(params.skipDates),\n }\n : undefined,\n };\n\n return namespace.schedule({\n task: params.jobName,\n input: params.input,\n ...schedule,\n });\n }\n\n public async getJob(\n jobId: string,\n queue?: string,\n ): Promise<IgniterJobSearchResult | null> {\n const result = await this.core().job.get(\n jobId,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n return result ? this.mapJob(result, queue) : null;\n }\n\n public async getJobState(\n jobId: string,\n queue?: string,\n ): Promise<IgniterJobStatus | null> {\n const state = await this.core().job.getState(\n jobId,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n return state as any;\n }\n\n public async getJobLogs(\n jobId: string,\n queue?: string,\n ): Promise<IgniterJobsJobLog[]> {\n const logs = await this.core().job.getLogs(\n jobId,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n return logs as any;\n }\n\n public async getJobProgress(jobId: string, queue?: string): Promise<number> {\n return this.core().job.getProgress(\n jobId,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n }\n\n public async retryJob(jobId: string, queue?: string): Promise<void> {\n await this.core().job.retry(\n jobId,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n }\n\n public async removeJob(jobId: string, queue?: string): Promise<void> {\n await this.core().job.remove(\n jobId,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n }\n\n public async promoteJob(jobId: string, queue?: string): Promise<void> {\n await this.core().job.promote(\n jobId,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n }\n\n public async moveJobToFailed(\n jobId: string,\n reason: string,\n queue?: string,\n ): Promise<void> {\n await this.core().job.moveToFailed(\n jobId,\n reason,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n }\n\n public async retryManyJobs(jobIds: string[], queue?: string): Promise<void> {\n await this.core().job.retryMany(\n jobIds,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n }\n\n public async removeManyJobs(jobIds: string[], queue?: string): Promise<void> {\n await this.core().job.removeMany(\n jobIds,\n queue ? this.toCoreQueueName(queue) : undefined,\n );\n }\n\n public async getQueueInfo(\n queue: string,\n ): Promise<IgniterJobsQueueInfo | null> {\n const info = await this.core().queues.get(this.toCoreQueueName(queue));\n if (!info) return null;\n return this.mapQueueInfo(info);\n }\n\n public async getQueueJobCounts(queue: string): Promise<any> {\n const counts = await this.core().queues.getJobCounts(\n this.toCoreQueueName(queue),\n );\n return counts as any;\n }\n\n public async listQueues(): Promise<IgniterJobsQueueInfo[]> {\n const list = await this.core().queues.list();\n return list.map((q) => this.mapQueueInfo(q));\n }\n\n public async pauseQueue(queue: string): Promise<void> {\n await this.core().queues.pause(this.toCoreQueueName(queue));\n }\n\n public async resumeQueue(queue: string): Promise<void> {\n await this.core().queues.resume(this.toCoreQueueName(queue));\n }\n\n public async drainQueue(queue: string): Promise<number> {\n return this.core().queues.drain(this.toCoreQueueName(queue));\n }\n\n public async cleanQueue(\n queue: string,\n options: IgniterJobsQueueCleanOptions,\n ): Promise<number> {\n return this.core().queues.clean(\n this.toCoreQueueName(queue),\n options as any,\n );\n }\n\n public async obliterateQueue(\n queue: string,\n options?: { force?: boolean },\n ): Promise<void> {\n await this.core().queues.obliterate(this.toCoreQueueName(queue), options);\n }\n\n public async retryAllInQueue(queue: string): Promise<number> {\n const jobs = await this.core().queues.getJobs(this.toCoreQueueName(queue), {\n status: [\"failed\"],\n limit: 1000,\n } as any);\n await Promise.all(\n jobs.map((j) => this.core().job.retry(j.id, this.toCoreQueueName(queue))),\n );\n return jobs.length;\n }\n\n public async searchJobs(filter: any): Promise<IgniterJobSearchResult[]> {\n // Minimal implementation: list jobs from a specific queue when provided, otherwise aggregate known queues.\n const queue = filter?.queue as string | undefined;\n const status = filter?.status as IgniterJobStatus[] | undefined;\n const limit = filter?.limit ?? 100;\n const offset = filter?.offset ?? 0;\n\n if (queue) {\n const jobs = await this.core().queues.getJobs(\n this.toCoreQueueName(queue),\n { status, limit, offset } as any,\n );\n return jobs.map((j) => this.mapJob(j as any, queue));\n }\n\n const queues = await this.listQueues();\n const results: IgniterJobSearchResult[] = [];\n for (const q of queues) {\n const jobs = await this.core().queues.getJobs(\n this.toCoreQueueName(q.name),\n { status, limit, offset } as any,\n );\n results.push(...jobs.map((j) => this.mapJob(j as any, q.name)));\n if (results.length >= limit) break;\n }\n return results.slice(0, limit);\n }\n\n public async searchQueues(filter: any): Promise<IgniterJobsQueueInfo[]> {\n const all = await this.listQueues();\n const name = filter?.name as string | undefined;\n const isPaused = filter?.isPaused as boolean | undefined;\n return all\n .filter((q) => (name ? q.name.includes(name) : true))\n .filter((q) =>\n typeof isPaused === \"boolean\" ? q.isPaused === isPaused : true,\n );\n }\n\n public async searchWorkers(filter: any): Promise<IgniterJobsWorkerHandle[]> {\n const queue = filter?.queue as string | undefined;\n const isRunning = filter?.isRunning as boolean | undefined;\n\n const all = Array.from(this.core().getWorkers().values());\n return all\n .filter((w) => {\n if (!queue) return true;\n const coreQueue = this.toCoreQueueName(queue);\n const queues = (w as any).config?.queues ?? [(w as any).queueName];\n return Array.isArray(queues) ? queues.includes(coreQueue) : false;\n })\n .filter((w) =>\n typeof isRunning === \"boolean\"\n ? isRunning\n ? w.isRunning()\n : !w.isRunning()\n : true,\n )\n .map((w) => this.mapWorker(w));\n }\n\n public async createWorker(\n config: IgniterJobsWorkerBuilderConfig,\n ): Promise<IgniterJobsWorkerHandle> {\n // Ensure jobs/crons are registered in the underlying BullMQ adapter before starting workers.\n await this.executor();\n\n const queuesSource = config.queues?.length\n ? config.queues\n : Array.from(\n new Set([...this.jobsByQueue.keys(), ...this.cronsByQueue.keys()]),\n );\n const queues = queuesSource.map((q) => this.toCoreQueueName(q));\n const coreConfig: JobWorkerConfig = {\n queues,\n concurrency: config.concurrency ?? 1,\n limiter: config.limiter as any,\n onActive: config.handlers?.onActive as any,\n onSuccess: config.handlers?.onSuccess as any,\n onFailure: config.handlers?.onFailure as any,\n onIdle: config.handlers?.onIdle as any,\n };\n const handle = await (this.core() as any).worker(coreConfig);\n return this.mapWorker(handle);\n }\n\n public getWorkers(): Map<string, IgniterJobsWorkerHandle> {\n const out = new Map<string, IgniterJobsWorkerHandle>();\n for (const [id, handle] of this.core().getWorkers())\n out.set(id, this.mapWorker(handle));\n return out;\n }\n\n public async publishEvent(channel: string, payload: unknown): Promise<void> {\n await this.publisher.publish(channel, JSON.stringify(payload));\n }\n\n public async subscribeEvent(\n channel: string,\n handler: IgniterJobsEventHandler,\n ): Promise<() => Promise<void>> {\n const set =\n this.subscribers.get(channel) ?? new Set<(payload: any) => void>();\n const wrapped = (payload: any) => void handler(payload as any);\n set.add(wrapped);\n this.subscribers.set(channel, set);\n\n if (set.size === 1) {\n await this.subscriber.subscribe(channel);\n }\n\n return async () => {\n const current = this.subscribers.get(channel);\n if (!current) return;\n current.delete(wrapped);\n if (current.size === 0) {\n this.subscribers.delete(channel);\n await this.subscriber.unsubscribe(channel);\n }\n };\n }\n\n public async writeJobStreamEvent(\n params: IgniterJobsAdapterJobStreamWriteParams,\n ): Promise<string> {\n const id = String(\n await this.publisher.incr(\n this.getJobStreamSequenceKey(params.queue, params.jobId),\n ),\n );\n const event: IgniterJobsJobStreamEvent<string, unknown> = {\n ...params.event,\n id,\n };\n\n if (params.persistence?.enabled) {\n const listKey = this.getJobStreamListKey(params.queue, params.jobId);\n await this.publisher.rpush(listKey, JSON.stringify(event));\n const maxEvents = params.persistence.maxEvents;\n if (typeof maxEvents === \"number\" && maxEvents > 0) {\n await this.publisher.ltrim(listKey, -maxEvents, -1);\n }\n }\n\n await this.publisher.publish(\n this.getJobStreamChannel(params.queue, params.jobId),\n JSON.stringify(event),\n );\n\n return id;\n }\n\n public async readJobStream(\n params: IgniterJobsAdapterJobStreamReadParams,\n ): Promise<\n IgniterJobsJobStreamReadResult<IgniterJobsJobStreamEvent<string, unknown>>\n > {\n const rows = await this.publisher.lrange(\n this.getJobStreamListKey(params.queue, params.jobId),\n 0,\n -1,\n );\n\n const after = params.after ? Number(params.after) : undefined;\n const limit = params.limit ?? 100;\n const parsed = rows\n .map((row) => this.parseJobStreamRow(row))\n .filter((event): event is IgniterJobsJobStreamEvent<string, unknown> =>\n Boolean(event),\n )\n .filter((event) =>\n typeof after === \"number\" ? Number(event.id) > after : true,\n );\n\n const items = parsed.slice(0, limit);\n return {\n items,\n nextCursor: items.at(-1)?.id,\n hasMore: parsed.length > items.length,\n };\n }\n\n public async subscribeJobStream(\n params: IgniterJobsAdapterJobStreamSubscribeParams,\n ): Promise<() => Promise<void>> {\n const channel = this.getJobStreamChannel(params.queue, params.jobId);\n return this.subscribeEvent(channel, async (payload) => {\n await params.handler(\n payload as IgniterJobsJobStreamEvent<string, unknown>,\n );\n });\n }\n\n public async shutdown(): Promise<void> {\n await this.subscriber.quit();\n // BullMQ adapter does not expose global shutdown on the core adapter in a single method;\n // queue/worker cleanup is handled by worker close and queue obliterate.\n }\n\n private core(): CoreAdapter {\n if (!this.coreAdapter) {\n // We only need the Redis connection. The wrapped job handlers can create real context.\n this.coreAdapter = createBullMQAdapter({\n store: { client: this.redis } as any,\n }) as unknown as CoreAdapter;\n }\n return this.coreAdapter;\n }\n\n private async executor(): Promise<CoreMergedExecutor> {\n if (!this.executorDirty && this.coreExecutor) return this.coreExecutor;\n\n const routers: Record<string, JobsRouter<any>> = {};\n const flattened: Record<string, CoreJobDefinition<any, any, any>> = {};\n\n const allQueues = new Set<string>([\n ...this.jobsByQueue.keys(),\n ...this.cronsByQueue.keys(),\n ]);\n\n for (const queueName of allQueues) {\n const coreJobs: Record<string, CoreJobDefinition<any, any, any>> = {};\n\n const jobs = this.jobsByQueue.get(queueName);\n if (jobs) {\n for (const [jobName, def] of jobs.entries()) {\n const queue = def.queue ? `${queueName}.${def.queue}` : queueName;\n const fullQueue = IgniterJobsPrefix.buildQueueName(queue);\n coreJobs[jobName] = this.toCoreJobDefinition(\n queueName,\n jobName,\n def,\n fullQueue,\n );\n }\n }\n\n const crons = this.cronsByQueue.get(queueName);\n if (crons) {\n for (const [cronName, def] of crons.entries()) {\n const fullQueue = IgniterJobsPrefix.buildQueueName(queueName);\n coreJobs[cronName] = this.toCoreCronJobDefinition(\n queueName,\n cronName,\n def,\n fullQueue,\n );\n }\n }\n\n if (Object.keys(coreJobs).length === 0) continue;\n\n routers[queueName] = this.core().router({\n jobs: coreJobs as any,\n namespace: queueName,\n });\n\n for (const [jobName, def] of Object.entries(coreJobs)) {\n flattened[`${queueName}.${jobName}`] = def;\n }\n }\n\n // Register jobs in bulk so management APIs and workers can resolve handlers.\n await this.core().bulkRegister(flattened as any);\n\n this.coreExecutor = this.core().merge(routers as any) as any;\n this.executorDirty = false;\n return this.coreExecutor as CoreMergedExecutor;\n }\n\n private toCoreQueueName(queueName: string): string {\n return IgniterJobsPrefix.buildQueueName(queueName);\n }\n\n private getJobStreamChannel(queue: string, jobId: string): string {\n return `${IgniterJobsPrefix.BASE_PREFIX}:stream:${queue}:${jobId}:live`;\n }\n\n private getJobStreamListKey(queue: string, jobId: string): string {\n return `${IgniterJobsPrefix.BASE_PREFIX}:stream:${queue}:${jobId}:events`;\n }\n\n private getJobStreamSequenceKey(queue: string, jobId: string): string {\n return `${IgniterJobsPrefix.BASE_PREFIX}:stream:${queue}:${jobId}:seq`;\n }\n\n private parseJobStreamRow(\n row: string,\n ): IgniterJobsJobStreamEvent<string, unknown> | null {\n try {\n const parsed = JSON.parse(row) as IgniterJobsJobStreamEvent<\n string,\n unknown\n >;\n return {\n ...parsed,\n timestamp: new Date(parsed.timestamp),\n };\n } catch {\n return null;\n }\n }\n\n private mapQueueInfo(info: any): IgniterJobsQueueInfo {\n return {\n name: this.fromCoreQueueName(info.name),\n isPaused: info.isPaused,\n jobCounts: info.jobCounts,\n };\n }\n\n private fromCoreQueueName(full: string): string {\n const prefix = `${IgniterJobsPrefix.BASE_PREFIX}:`;\n return full.startsWith(prefix) ? full.slice(prefix.length) : full;\n }\n\n private mapJob(job: any, queue?: string): IgniterJobSearchResult {\n const q =\n queue ??\n this.fromCoreQueueName(job.metadata?.queue ?? job.queueName ?? \"\");\n const scope = (job.metadata as any)?.__igniter_jobs_scope;\n return {\n id: job.id,\n name: job.name,\n queue: q,\n status: job.status,\n input: job.payload,\n result: job.result,\n error: job.error,\n progress: typeof job.progress === \"number\" ? job.progress : 0,\n attemptsMade: job.attemptsMade ?? 0,\n priority: job.priority ?? 0,\n createdAt: job.createdAt,\n startedAt: job.processedAt,\n completedAt: job.completedAt,\n metadata: job.metadata,\n scope,\n };\n }\n\n private mapWorker(handle: any): IgniterJobsWorkerHandle {\n const queues = (handle as any).config?.queues ?? [\n (handle as any).queueName,\n ];\n return {\n id: handle.id,\n queues: (queues as string[]).map((q) => this.fromCoreQueueName(q)),\n pause: () => handle.pause(),\n resume: () => handle.resume(),\n close: () => handle.close(),\n isRunning: () => handle.isRunning(),\n isPaused: () => handle.isPaused(),\n isClosed: () => handle.isClosed(),\n getMetrics: async () => handle.getMetrics() as IgniterJobsWorkerMetrics,\n };\n }\n\n private toCoreJobDefinition(\n queueName: string,\n jobName: string,\n def: IgniterJobDefinition<any, any, any>,\n fullQueueName: string,\n ): CoreJobDefinition<any, any, any> {\n const handler = async (ctx: CoreJobExecutionContext<any, any>) => {\n const updateProgress =\n typeof (ctx.job as any)?.updateProgress === \"function\"\n ? async (progress: number, message?: string) => {\n await (ctx.job as any).updateProgress(progress);\n await def.onProgress?.({\n input: ctx.input as any,\n context: ctx.context as any,\n job: {\n id: ctx.job.id,\n name: jobName,\n queue: queueName,\n attemptsMade: ctx.job.attemptsMade,\n createdAt: (ctx.job as any).createdAt,\n metadata: ctx.job.metadata,\n updateProgress,\n },\n scope: (ctx.job.metadata as any)?.__igniter_jobs_scope,\n progress,\n message,\n } as any);\n }\n : undefined;\n\n return def.handler({\n input: ctx.input as any,\n context: ctx.context as any,\n job: {\n id: ctx.job.id,\n name: jobName,\n queue: queueName,\n attemptsMade: ctx.job.attemptsMade,\n createdAt: (ctx.job as any).createdAt,\n metadata: ctx.job.metadata,\n updateProgress,\n },\n scope: (ctx.job.metadata as any)?.__igniter_jobs_scope,\n } as any);\n };\n\n return {\n name: jobName,\n input: def.input as any,\n handler,\n queue: { name: fullQueueName },\n attempts: def.attempts,\n priority: def.priority,\n delay: def.delay,\n removeOnComplete: def.removeOnComplete as any,\n removeOnFail: def.removeOnFail as any,\n metadata: def.metadata as any,\n limiter: def.limiter as any,\n onStart: def.onStart as any,\n onSuccess: def.onSuccess as any,\n onFailure: def.onFailure as any,\n onProgress: def.onProgress as any,\n } as any;\n }\n\n private toCoreCronJobDefinition(\n queueName: string,\n cronName: string,\n def: IgniterCronDefinition<any, any>,\n fullQueueName: string,\n ): CoreJobDefinition<any, any, any> {\n const handler = async (ctx: CoreJobExecutionContext<any, any>) => {\n return def.handler({\n context: ctx.context as any,\n job: {\n id: ctx.job.id,\n name: cronName,\n queue: queueName,\n attemptsMade: ctx.job.attemptsMade,\n createdAt: (ctx.job as any).createdAt,\n metadata: ctx.job.metadata,\n },\n scope: (ctx.job.metadata as any)?.__igniter_jobs_scope,\n } as any);\n };\n\n return {\n name: cronName,\n handler,\n queue: { name: fullQueueName },\n repeat: {\n cron: def.cron,\n tz: def.tz,\n limit: def.maxExecutions,\n startDate: def.startDate,\n endDate: def.endDate,\n },\n metadata:\n def.onlyBusinessHours ||\n def.skipWeekends ||\n def.businessHours ||\n def.onlyWeekdays ||\n def.skipDates ||\n (def.startDate && def.endDate)\n ? {\n advancedScheduling: {\n onlyBusinessHours: def.onlyBusinessHours,\n skipWeekends: def.skipWeekends,\n businessHours: def.businessHours,\n skipDates: toDateArray(def.skipDates),\n onlyWeekdays: def.onlyWeekdays,\n between:\n def.startDate && def.endDate\n ? [def.startDate, def.endDate]\n : undefined,\n },\n }\n : undefined,\n } as any;\n }\n}\n"]}