ravenflow 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/logger.ts","../src/app/queue-manager.ts","../src/processors/token-bucket-job.processor/constants.ts","../src/processors/constants.ts","../src/processors/simple-job.processor.ts","../src/types/job/common.ts","../src/types/job/simple.ts","../src/types/job/memory-etl.ts","../src/types/job/distributed-etl.ts","../src/types/job/stateful-etl.ts","../src/types/job/token-bucket.ts","../src/types/job/unions.ts","../src/types/config.ts","../src/rate-limiter.ts","../src/processors/memory-etl-job.processor.ts","../src/processors/sub-job.ts","../src/processors/etl-context.ts","../src/processors/distributed-etl-job.processor.ts","../src/mongo/connection.ts","../src/mongo/run.repo.ts","../src/mongo/token-bucket.repo.ts","../src/processors/stateful-etl-job.processor/constants.ts","../src/processors/stateful-etl-job.processor/heartbeat.ts","../src/processors/stateful-etl-job.processor/paginate.ts","../src/processors/stateful-etl-job.processor/process.ts","../src/processors/token-bucket-job.processor/compute-credit.ts","../src/processors/token-bucket-job.processor/index.ts","../src/app/job-registry.ts","../src/banner.ts","../src/app/worker-manager.ts","../src/app/scheduler.ts","../src/internal-jobs/stall-watcher/types.ts","../src/internal-jobs/stall-watcher/check-stalled-runs.ts","../src/internal-jobs/stall-watcher/resume-stalled-run.ts","../src/internal-jobs/stall-watcher/index.ts","../src/mongo/repositories/_base.ts","../src/mongo/repositories/workers.repo.ts","../src/mongo/repositories/_types.ts","../src/mongo/repositories/bucket-utils.ts","../src/mongo/mongo-error.ts","../src/mongo/repositories/types.ts","../src/mongo/repositories/job-stats.repo.ts","../src/monitoring/error-signature.ts","../src/monitoring/error-extraction.ts","../src/mongo/repositories/job-errors.repo.ts","../src/mongo/repositories/job-failures.repo.ts","../src/mongo/repositories/job-configs.repo.ts","../src/monitoring/worker-registry.ts","../src/monitoring/batch-writer.types.ts","../src/monitoring/batch-writer.ts","../src/monitoring/log-capture.ts","../src/monitoring/event-hooks.ts","../src/app/disabled-job.ts","../src/app/app.ts","../src/index.ts"],"names":["z","resolve","logger","collection","processJob","db","COLLECTION"],"mappings":"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","file":"index.mjs","sourcesContent":["import pino from \"pino\";\n\nexport type Logger = pino.Logger;\n\nexport interface LoggerOptions {\n level?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\";\n pretty?: boolean;\n}\n\nlet logger: Logger | null = null;\n\nexport function initLogger(options: LoggerOptions = {}): Logger {\n if (logger) return logger;\n\n const isDev = process.env.NODE_ENV !== \"production\";\n const usePretty = options.pretty ?? isDev;\n\n logger = pino({\n level: options.level || (isDev ? \"debug\" : \"info\"),\n transport: usePretty\n ? {\n target: \"pino-pretty\",\n options: {\n colorize: true,\n translateTime: \"HH:MM:ss\",\n ignore: \"pid,hostname\",\n },\n }\n : undefined,\n });\n\n return logger;\n}\n\nexport function getLogger(): Logger {\n if (!logger) {\n throw new Error(\"[ravenflow] Logger not initialized. Call initLogger() first.\");\n }\n return logger;\n}\n","import { ConnectionOptions, DefaultJobOptions, Queue, QueueEvents } from \"bullmq\";\nimport { RavenflowConfig, JobConfig, RetentionConfig } from \"../types\";\n\nexport type QueueStore = Record<string, { queue: Queue; events: QueueEvents }>;\n\nfunction buildDefaultJobOptions(\n retention?: RetentionConfig,\n): DefaultJobOptions | undefined {\n if (!retention) return undefined;\n\n const opts: DefaultJobOptions = {};\n\n if (retention.completed !== undefined) {\n opts.removeOnComplete = { count: retention.completed };\n }\n if (retention.failed !== undefined) {\n opts.removeOnFail = { count: retention.failed };\n }\n\n return Object.keys(opts).length > 0 ? opts : undefined;\n}\n\nexport function initQueues(\n config: RavenflowConfig,\n connection: ConnectionOptions,\n retention?: RetentionConfig,\n): QueueStore {\n const queueNames = new Set<string>([config.defaultQueue]);\n for (const job of Object.values<JobConfig>(config.jobs)) {\n if (job.queue) queueNames.add(job.queue);\n }\n\n const defaultJobOptions = buildDefaultJobOptions(retention);\n\n const queues: QueueStore = {};\n for (const name of queueNames) {\n queues[name] = {\n queue: new Queue(name, { connection, defaultJobOptions }),\n events: new QueueEvents(name, { connection }),\n };\n }\n return queues;\n}\n\n// Generic-erasure internals: `params` width is resolved at the typed proxy boundary\n// (`JobProxy<T>.send`); here the dispatcher accepts the erased shape. Typed config-walker\n// deferred per Story 1.8 AC #15.\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport async function sendJob(\n jobName: string,\n params: any,\n queues: QueueStore,\n queueName: string,\n) {\n const queueData = queues[queueName];\n if (!queueData) {\n throw new Error(`Queue ${queueName} not found`);\n }\n return await queueData.queue.add(jobName, params);\n}\n\nexport async function sendJobAndWait(\n jobName: string,\n params: any,\n queues: QueueStore,\n queueName: string,\n) {\n const queueData = queues[queueName];\n if (!queueData) {\n throw new Error(`Queue ${queueName} not found`);\n }\n const job = await queueData.queue.add(jobName, params);\n return await job.waitUntilFinished(queueData.events);\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\nexport async function closeQueues(queues: QueueStore) {\n await Promise.all(\n Object.values(queues).map(({ queue, events }) =>\n Promise.all([queue.close(), events.close()]),\n ),\n );\n}\n","import type { TokenBucketInterval } from \"@/types\";\n\nexport const TOKEN_BUCKET_CONSUME_SUFFIX = \":consume\";\n\nexport const INTERVAL_MS: Record<TokenBucketInterval, number> = {\n second: 1_000,\n minute: 60_000,\n hour: 3_600_000,\n day: 86_400_000,\n week: 604_800_000,\n};\n","export const ETL_EXTRACT_SUFFIX = \":extract\";\nexport const ETL_TRANSFORM_SUFFIX = \":transform\";\nexport const ETL_LOAD_SUFFIX = \":load\";\nexport { TOKEN_BUCKET_CONSUME_SUFFIX } from \"./token-bucket-job.processor/constants\";\n","import { Job } from \"bullmq\";\nimport { SimpleJobConfig } from \"../types\";\n\nexport async function processSimpleJob<TData, TReturn>(\n job: Job,\n jobConfig: SimpleJobConfig<TData, TReturn>\n): Promise<TReturn> {\n return jobConfig.handler(job, job.data);\n}\n","import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Job config base (shared across all job configs)\n// ---------------------------------------------------------------------------\n\nexport interface BaseJobConfig {\n queue?: string;\n cron?: string;\n attempts?: number;\n backoff?: { type: \"fixed\" | \"exponential\"; delay: number };\n}\n\n/** Job config with queue resolved (always has a queue name). */\nexport type ResolvedJobConfig<T extends BaseJobConfig> = T & { queue: string };\n\nexport const zRateLimitConfig = z.union([\n z.object({ perSecond: z.number().positive() }),\n z.object({ perMinute: z.number().positive() }),\n]);\n\nexport type RateLimitConfig = z.infer<typeof zRateLimitConfig>;\n\nexport interface MemoryEtlStepConfig {\n rateLimit?: RateLimitConfig;\n}\n\nexport interface StepConfig extends MemoryEtlStepConfig {\n attempts?: number;\n backoff?: { type: \"fixed\" | \"exponential\"; delay: number };\n queue?: string;\n}\n\nexport interface EtlExtractResult<TRaw, TCursor> {\n data: TRaw[];\n cursor: TCursor | null;\n hasMore: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Job entity base (shared across all DB-persisted jobs)\n// ---------------------------------------------------------------------------\n\nexport const zJobStatus = z.enum([\n \"pending\",\n \"in_progress\",\n \"completed\",\n \"failed\",\n \"stalled\",\n]);\nexport type JobStatus = z.infer<typeof zJobStatus>;\n\nexport const zBaseJob = z.object({\n _id: z.string(),\n type: z.string(),\n jobId: z.string(),\n jobName: z.string(),\n status: zJobStatus,\n params: z.unknown().nullable(),\n result: z.unknown().nullable(),\n error: z.string().nullable(),\n\n timestamps: z.object({\n createdAt: z.date(),\n updatedAt: z.date(),\n startedAt: z.date().nullable(),\n completedAt: z.date().nullable(),\n }),\n});\n\nexport type BaseJob = z.infer<typeof zBaseJob>;\n","import { z } from \"zod\";\nimport { Job } from \"bullmq\";\nimport { BaseJobConfig, zBaseJob } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface SimpleJobConfig<TData, TReturn> extends BaseJobConfig {\n type: \"simple\";\n handler: (job: Job, params: TData) => Promise<TReturn>;\n}\n\nexport function defineSimpleJob<TData, TReturn>(\n config: Omit<SimpleJobConfig<TData, TReturn>, \"type\">,\n): SimpleJobConfig<TData, TReturn> {\n return { ...config, type: \"simple\" };\n}\n\n// ---------------------------------------------------------------------------\n// Entity\n// ---------------------------------------------------------------------------\n\nexport const zSimpleJob = zBaseJob.extend({\n type: z.literal(\"simple\"),\n});\n\nexport type SimpleJob = z.infer<typeof zSimpleJob>;\n","import { z } from \"zod\";\nimport { BaseJobConfig, EtlExtractResult, MemoryEtlStepConfig, zBaseJob } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface MemoryEtlJobConfig<TRaw, TProcessed, TCursor = number>\n extends BaseJobConfig {\n type: \"memory-etl\";\n extract: (\n cursor: TCursor | null,\n ) => Promise<EtlExtractResult<TRaw, TCursor>>;\n transform: (data: TRaw) => Promise<TProcessed>;\n load: (data: TProcessed[]) => Promise<void>;\n steps?: {\n extract?: MemoryEtlStepConfig;\n transform?: MemoryEtlStepConfig;\n load?: MemoryEtlStepConfig;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entity\n// ---------------------------------------------------------------------------\n\nexport const zMemoryEtlJob = zBaseJob.extend({\n type: z.literal(\"memory-etl\"),\n\n progress: z.object({\n extractedCount: z.number().int().nonnegative(),\n transformedCount: z.number().int().nonnegative(),\n loadedCount: z.number().int().nonnegative(),\n }),\n});\n\nexport type MemoryEtlJob = z.infer<typeof zMemoryEtlJob>;\n\nexport function defineMemoryEtlJob<TRaw, TProcessed, TCursor = number>(\n config: Omit<MemoryEtlJobConfig<TRaw, TProcessed, TCursor>, \"type\">,\n): MemoryEtlJobConfig<TRaw, TProcessed, TCursor> {\n return { ...config, type: \"memory-etl\" };\n}\n","import { z } from \"zod\";\nimport { BaseJobConfig, EtlExtractResult, StepConfig, zBaseJob } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface DistributedEtlJobConfig<TRaw, TProcessed, TCursor = number>\n extends BaseJobConfig {\n type: \"distributed-etl\";\n extract: (cursor: TCursor | null) => Promise<EtlExtractResult<TRaw, TCursor>>;\n transform: (data: TRaw) => Promise<TProcessed>;\n load: (data: TProcessed[]) => Promise<void>;\n steps?: {\n extract?: StepConfig;\n transform?: StepConfig;\n load?: StepConfig;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entity\n// ---------------------------------------------------------------------------\n\nexport const zDistributedEtlJob = zBaseJob.extend({\n type: z.literal(\"distributed-etl\"),\n\n progress: z.object({\n extractedCount: z.number().int().nonnegative(),\n transformedCount: z.number().int().nonnegative(),\n loadedCount: z.number().int().nonnegative(),\n }),\n});\n\nexport type DistributedEtlJob = z.infer<typeof zDistributedEtlJob>;\n\nexport function defineDistributedEtlJob<TRaw, TProcessed, TCursor = number>(\n config: Omit<DistributedEtlJobConfig<TRaw, TProcessed, TCursor>, \"type\">,\n): DistributedEtlJobConfig<TRaw, TProcessed, TCursor> {\n return { ...config, type: \"distributed-etl\" };\n}\n","import { z } from \"zod\";\nimport { BaseJobConfig, EtlExtractResult, StepConfig, zBaseJob } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface StatefulEtlJobConfig<TRaw, TProcessed, TCursor = string>\n extends BaseJobConfig {\n type: \"stateful-etl\";\n\n extract: (\n cursor: TCursor | null,\n ) => Promise<EtlExtractResult<TRaw, TCursor>>;\n transform: (data: TRaw) => Promise<TProcessed>;\n load: (data: TProcessed[]) => Promise<void>;\n\n /**\n * When true, only one run of this job can be active at a time.\n * If a run is already pending or in_progress (with a live heartbeat), the new trigger is skipped.\n * Defaults to false — each trigger creates an independent run.\n */\n singletonRun?: boolean;\n stallTimeoutMs?: number;\n maxStallRetries?: number;\n steps?: {\n extract?: StepConfig;\n transform?: StepConfig;\n load?: StepConfig;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entity\n// ---------------------------------------------------------------------------\n\nexport const zStatefulEtlJob = zBaseJob.extend({\n type: z.literal(\"stateful-etl\"),\n\n stallTimeoutMs: z.number().int().positive(),\n maxStallRetries: z.number().int().nonnegative(),\n\n progress: z.object({\n transformedCount: z.number().int().nonnegative(),\n extractedCount: z.number().int().nonnegative(),\n loadedCount: z.number().int().nonnegative(),\n\n checkpoint: z.unknown().nullable(),\n lastHeartbeatAt: z.date().nullable(),\n stallRetries: z.number().int().nonnegative(),\n }),\n});\n\nexport type StatefulEtlJob = z.infer<typeof zStatefulEtlJob>;\n\nexport function defineStatefulEtlJob<TRaw, TProcessed, TCursor = string>(\n config: Omit<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>, \"type\">,\n): StatefulEtlJobConfig<TRaw, TProcessed, TCursor> {\n return { ...config, type: \"stateful-etl\" };\n}\n","import { z } from \"zod\";\nimport { BaseJobConfig } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport type TokenBucketInterval = \"second\" | \"minute\" | \"hour\" | \"day\" | \"week\";\n\nexport interface TokenBucketJobConfig extends BaseJobConfig {\n type: \"token-bucket\";\n cron: string;\n\n rate?: {\n amount: number;\n interval: TokenBucketInterval;\n };\n\n getBudget?: () => Promise<number> | number;\n maxCarryOver?: number;\n consumeTimeout?: number;\n\n consume: (tokens: number) => Promise<number | void> | number | void;\n}\n\n// ---------------------------------------------------------------------------\n// State entity (persisted in MongoDB — one document per job)\n// ---------------------------------------------------------------------------\n\nexport const zTokenBucketState = z.object({\n _id: z.string(),\n accumulatedCredit: z.number(),\n lastTickAt: z.date(),\n});\n\nexport type TokenBucketState = z.infer<typeof zTokenBucketState>;\n\nexport function defineTokenBucketJob(\n config: Omit<TokenBucketJobConfig, \"type\">,\n): TokenBucketJobConfig {\n return { ...config, type: \"token-bucket\" };\n}\n","import { z } from \"zod\";\nimport { SimpleJobConfig, zSimpleJob } from \"./simple\";\nimport { MemoryEtlJobConfig, zMemoryEtlJob } from \"./memory-etl\";\nimport { DistributedEtlJobConfig, zDistributedEtlJob } from \"./distributed-etl\";\nimport { StatefulEtlJobConfig, zStatefulEtlJob } from \"./stateful-etl\";\nimport { TokenBucketJobConfig } from \"./token-bucket\";\n\n// ---------------------------------------------------------------------------\n// Config union (discriminated on `type`)\n// ---------------------------------------------------------------------------\n\nexport type JobConfig =\n // `any` in type-parameter position: `JobConfig` is the discriminated-union of all\n // job definitions regardless of their data/return generics. A typed config-walker\n // is deferred per Story 1.8 AC #14 — flagged in deferred-work.md.\n /* eslint-disable @typescript-eslint/no-explicit-any */\n | SimpleJobConfig<any, any>\n | MemoryEtlJobConfig<any, any, any>\n | DistributedEtlJobConfig<any, any, any>\n | StatefulEtlJobConfig<any, any, any>\n | TokenBucketJobConfig;\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n// ---------------------------------------------------------------------------\n// Entity union (discriminated on `type`)\n// ---------------------------------------------------------------------------\n\nexport const zRavenflowJob = z.discriminatedUnion(\"type\", [\n zSimpleJob,\n zMemoryEtlJob,\n zDistributedEtlJob,\n zStatefulEtlJob,\n]);\n\nexport type RavenflowJob = z.infer<typeof zRavenflowJob>;\n","import { JobConfig } from \"./job\";\n\nexport interface RetentionConfig {\n /** Number of completed jobs to keep per queue. Set to 0 to remove immediately. */\n completed?: number;\n /** Number of failed jobs to keep per queue. Set to 0 to remove immediately. */\n failed?: number;\n}\n\nexport interface RavenflowConfig<TJobs extends string = string> {\n defaultQueue: string;\n jobs: Record<TJobs, JobConfig>;\n}\n\nexport type JobNames<T extends RavenflowConfig> =\n T extends RavenflowConfig<infer TJobs> ? TJobs : never;\n\nexport function defineConfig<const T extends RavenflowConfig>(config: T): T {\n return config;\n}\n","import { type RateLimitConfig, zRateLimitConfig } from \"./types\";\n\nexport class RateLimiter {\n private slots = new Map<string, number>(); // key → nextAvailableAt\n\n /**\n * Block until a slot is available. Combines getDelay + wait in a loop.\n */\n public async waitForSlot(\n key: string,\n config: RateLimitConfig,\n ): Promise<void> {\n let delay = this.getDelay(key, config);\n while (delay > 0) {\n await this.wait(delay);\n delay = this.getDelay(key, config);\n }\n }\n\n /**\n * Check if a slot is available. Returns 0 if allowed (and consumes the slot),\n * or the number of ms to wait before retrying.\n *\n * Slots are evenly spaced: perSecond: 10 → one slot every 100ms.\n */\n public getDelay(key: string, config: RateLimitConfig): number {\n const intervalMs = resolve(config);\n const now = Date.now();\n const nextAvailableAt = this.slots.get(key);\n\n if (nextAvailableAt === undefined || now >= nextAvailableAt) {\n this.slots.set(key, now + intervalMs);\n return 0;\n }\n\n return nextAvailableAt - now;\n }\n\n /**\n * Wait for the given number of ms.\n */\n public wait(ms: number): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\nfunction resolve(config: RateLimitConfig): number {\n zRateLimitConfig.parse(config);\n if (\"perSecond\" in config) return 1_000 / config.perSecond;\n return 60_000 / config.perMinute;\n}\n","import { Job } from \"bullmq\";\nimport { MemoryEtlJobConfig } from \"@/types\";\nimport { getLogger } from \"@/logger\";\nimport { RateLimiter } from \"@/rate-limiter\";\n\nexport async function processMemoryEtlJob<TRaw, TProcessed, TCursor>(\n job: Job,\n config: MemoryEtlJobConfig<TRaw, TProcessed, TCursor>,\n) {\n const logger = getLogger();\n const jobName = job.name;\n const counts = { extractedCount: 0, transformedCount: 0, loadedCount: 0 };\n const limiter = config.steps ? new RateLimiter() : null;\n\n let cursor: TCursor | null = null;\n let hasMore = true;\n\n while (hasMore) {\n await applyRateLimit(limiter, jobName, \"extract\", config);\n\n const page = await config.extract(cursor);\n hasMore = page.hasMore;\n cursor = page.cursor;\n\n if (page.data.length === 0) continue;\n\n counts.extractedCount += page.data.length;\n\n const processed = await transformParallel(\n page.data,\n config.transform,\n limiter,\n jobName,\n config,\n );\n counts.transformedCount += processed.length;\n\n await applyRateLimit(limiter, jobName, \"load\", config);\n await config.load(processed);\n counts.loadedCount += processed.length;\n\n logger.debug({ jobName, ...counts }, \"[memory-etl] batch done\");\n }\n\n logger.info({ jobName, ...counts }, \"[memory-etl] completed\");\n return counts;\n}\n\nasync function transformParallel<TRaw, TProcessed, TCursor>(\n batch: TRaw[],\n transform: (item: TRaw) => Promise<TProcessed>,\n limiter: RateLimiter | null,\n jobName: string,\n config: MemoryEtlJobConfig<TRaw, TProcessed, TCursor>,\n): Promise<TProcessed[]> {\n const pending: Promise<TProcessed>[] = [];\n\n for (const item of batch) {\n await applyRateLimit(limiter, jobName, \"transform\", config);\n pending.push(transform(item));\n }\n\n return Promise.all(pending);\n}\n\ntype StepName = \"extract\" | \"transform\" | \"load\";\n\nasync function applyRateLimit<TRaw, TProcessed, TCursor>(\n limiter: RateLimiter | null,\n jobName: string,\n step: StepName,\n config: MemoryEtlJobConfig<TRaw, TProcessed, TCursor>,\n): Promise<void> {\n const rlConfig = config.steps?.[step]?.rateLimit;\n if (!limiter || !rlConfig) return;\n await limiter.waitForSlot(`ravenflow:rl:${jobName}:${step}`, rlConfig);\n}\n","import { Job, Queue, QueueEvents } from \"bullmq\";\n\nexport interface SubJobOptions {\n attempts?: number;\n backoff?: { type: \"fixed\" | \"exponential\"; delay: number };\n}\n\n/**\n * Dispatch a single sub-job and wait for its result.\n */\nexport async function dispatchSubJob<TData, TReturn>(\n queue: Queue,\n queueEvents: QueueEvents,\n name: string,\n data: TData,\n options?: SubJobOptions,\n): Promise<TReturn> {\n const job = await queue.add(name, data, {\n attempts: options?.attempts,\n backoff: options?.backoff,\n });\n\n return (await job.waitUntilFinished(queueEvents)) as TReturn;\n}\n\n/**\n * Dispatch multiple sub-jobs in parallel and wait for all results.\n */\nexport async function dispatchSubJobs<TData, TReturn>(\n queue: Queue,\n queueEvents: QueueEvents,\n name: string,\n items: TData[],\n options?: SubJobOptions,\n): Promise<TReturn[]> {\n const jobs = await queue.addBulk(\n items.map((data) => ({\n name,\n data,\n opts: {\n attempts: options?.attempts,\n backoff: options?.backoff,\n },\n })),\n );\n\n return Promise.all(\n jobs.map((job) => job.waitUntilFinished(queueEvents) as Promise<TReturn>),\n );\n}\n\n/**\n * Dispatch multiple sub-jobs without waiting. Returns the BullMQ Job instances\n * so the caller can track or ignore them.\n */\nexport async function dispatchSubJobsFireAndForget<TData>(\n queue: Queue,\n name: string,\n items: TData[],\n options?: SubJobOptions,\n): Promise<Job[]> {\n return queue.addBulk(\n items.map((data) => ({\n name,\n data,\n opts: {\n attempts: options?.attempts,\n backoff: options?.backoff,\n },\n })),\n );\n}\n","import { Queue, QueueEvents } from \"bullmq\";\nimport { StepConfig } from \"@/types\";\nimport { getLogger } from \"@/logger\";\nimport { RateLimiter } from \"@/rate-limiter\";\nimport { dispatchSubJob, SubJobOptions } from \"./sub-job\";\nimport { ETL_EXTRACT_SUFFIX, ETL_TRANSFORM_SUFFIX, ETL_LOAD_SUFFIX } from \"./constants\";\n\nexport type StepName = \"extract\" | \"transform\" | \"load\";\n\ninterface StepContext {\n queue: Queue;\n queueEvents: QueueEvents;\n options: SubJobOptions;\n}\n\nexport interface ProgressCounts {\n extractedCount: number;\n transformedCount: number;\n loadedCount: number;\n}\n\ninterface EtlConfig {\n queue: string;\n attempts?: number;\n backoff?: { type: \"fixed\" | \"exponential\"; delay: number };\n steps?: {\n extract?: StepConfig;\n transform?: StepConfig;\n load?: StepConfig;\n };\n}\n\nexport class EtlContext<TRaw, TProcessed> {\n readonly jobName: string;\n readonly counts: ProgressCounts;\n readonly logger: ReturnType<typeof getLogger>;\n\n private readonly steps: Record<StepName, StepContext>;\n private readonly limiter: RateLimiter | null;\n private readonly stepsConfig: EtlConfig[\"steps\"];\n\n constructor(\n jobName: string,\n config: EtlConfig,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n initialCounts?: ProgressCounts,\n ) {\n this.jobName = jobName;\n this.logger = getLogger();\n this.stepsConfig = config.steps;\n this.limiter = config.steps ? new RateLimiter() : null;\n this.counts = initialCounts\n ? { ...initialCounts }\n : { extractedCount: 0, transformedCount: 0, loadedCount: 0 };\n\n const defaultQueue = queues[config.queue];\n this.steps = this.buildStepContexts(config, queues, defaultQueue);\n }\n\n // ---------------------------------------------------------------------------\n // Rate limiting\n // ---------------------------------------------------------------------------\n\n async applyRateLimit(step: StepName): Promise<void> {\n const config = this.stepsConfig?.[step]?.rateLimit;\n if (!this.limiter || !config) return;\n await this.limiter.waitForSlot(\n `ravenflow:rl:${this.jobName}:${step}`,\n config,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Sub-job dispatch primitives\n // ---------------------------------------------------------------------------\n\n async dispatchExtract<TCursor>(\n cursor: TCursor | null,\n ): Promise<{\n data: TRaw[];\n cursor: TCursor | null;\n hasMore: boolean;\n }> {\n await this.applyRateLimit(\"extract\");\n const { queue, queueEvents, options } = this.steps.extract;\n\n return dispatchSubJob(\n queue,\n queueEvents,\n `${this.jobName}${ETL_EXTRACT_SUFFIX}`,\n { cursor },\n options,\n );\n }\n\n async dispatchTransform(item: TRaw): Promise<TProcessed> {\n const { queue, queueEvents, options } = this.steps.transform;\n\n return dispatchSubJob<TRaw, TProcessed>(\n queue,\n queueEvents,\n `${this.jobName}${ETL_TRANSFORM_SUFFIX}`,\n item,\n options,\n );\n }\n\n async transformParallel(rawItems: TRaw[]): Promise<TProcessed[]> {\n const pending: Promise<TProcessed>[] = [];\n\n for (const item of rawItems) {\n await this.applyRateLimit(\"transform\");\n pending.push(this.dispatchTransform(item));\n }\n\n return Promise.all(pending);\n }\n\n async dispatchLoad(processedItems: TProcessed[]): Promise<void> {\n await this.applyRateLimit(\"load\");\n const { queue, queueEvents, options } = this.steps.load;\n\n await dispatchSubJob(\n queue,\n queueEvents,\n `${this.jobName}${ETL_LOAD_SUFFIX}`,\n { data: processedItems, jobName: this.jobName },\n options,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Step context setup\n // ---------------------------------------------------------------------------\n\n private buildStepContexts(\n config: EtlConfig,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n defaultQueue: { queue: Queue; events: QueueEvents },\n ): Record<StepName, StepContext> {\n const resolve = (step: StepConfig | undefined): StepContext => {\n const q = step?.queue ? queues[step.queue] : defaultQueue;\n if (!q) throw new Error(`Queue \"${step?.queue}\" not found`);\n return {\n queue: q.queue,\n queueEvents: q.events,\n options: {\n attempts: step?.attempts ?? config.attempts,\n backoff: step?.backoff ?? config.backoff,\n },\n };\n };\n\n return {\n extract: resolve(config.steps?.extract),\n transform: resolve(config.steps?.transform),\n load: resolve(config.steps?.load),\n };\n }\n}\n","import { Job, Queue, QueueEvents } from \"bullmq\";\nimport { DistributedEtlJobConfig, EtlExtractResult, ResolvedJobConfig } from \"@/types\";\nimport { EtlContext } from \"./etl-context\";\n\nexport async function processDistributedEtlJob<TRaw, TProcessed, TCursor>(\n job: Job,\n config: ResolvedJobConfig<DistributedEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n) {\n const ctx = new EtlContext<TRaw, TProcessed>(job.name, config, queues);\n\n let cursor: TCursor | null = null;\n let hasMore = true;\n\n while (hasMore) {\n const page: EtlExtractResult<TRaw, TCursor> = await ctx.dispatchExtract<TCursor>(cursor);\n hasMore = page.hasMore;\n cursor = page.cursor;\n\n if (page.data.length === 0) continue;\n\n ctx.counts.extractedCount += page.data.length;\n\n const processed = await ctx.transformParallel(page.data);\n ctx.counts.transformedCount += processed.length;\n\n await ctx.dispatchLoad(processed);\n ctx.counts.loadedCount += processed.length;\n\n ctx.logger.debug({ jobName: ctx.jobName, ...ctx.counts }, \"[distributed-etl] batch done\");\n }\n\n ctx.logger.info({ jobName: ctx.jobName, ...ctx.counts }, \"[distributed-etl] completed\");\n return ctx.counts;\n}\n","import { MongoClient, Db } from \"mongodb\";\n\nconst COLLECTION_PREFIX = \"ravenflow_\";\n\nlet client: MongoClient | null = null;\nlet db: Db | null = null;\n\nexport async function connectMongo(uri: string): Promise<Db> {\n if (db) return db;\n\n client = new MongoClient(uri);\n await client.connect();\n db = client.db();\n\n return db;\n}\n\nexport function getDb(): Db {\n if (!db) {\n throw new Error(\"[ravenflow] MongoDB not connected. Pass a mongo URI to createRavenflowApp().\");\n }\n return db;\n}\n\nexport function collectionName(name: string): string {\n return `${COLLECTION_PREFIX}${name}`;\n}\n\nexport async function closeMongo(): Promise<void> {\n if (client) {\n await client.close();\n client = null;\n db = null;\n }\n}\n","import { Collection, ObjectId } from \"mongodb\";\nimport { getDb, collectionName } from \"./connection\";\nimport type { StatefulEtlJob } from \"@/types\";\n\nconst RUNS_COLLECTION = \"runs\";\n\nfunction collection(): Collection<StatefulEtlJob> {\n return getDb().collection<StatefulEtlJob>(collectionName(RUNS_COLLECTION));\n}\n\nexport async function findActiveRun(jobName: string): Promise<StatefulEtlJob | null> {\n return await collection().findOne(\n { jobName, status: { $in: [\"pending\", \"in_progress\", \"stalled\"] } },\n { sort: { \"timestamps.createdAt\": -1 } },\n );\n}\n\nexport async function findById(runId: string): Promise<StatefulEtlJob | null> {\n return await collection().findOne({ _id: runId });\n}\n\nexport async function createRun(\n jobName: string,\n params: unknown,\n jobId: string,\n stallTimeoutMs: number,\n maxStallRetries: number,\n): Promise<string> {\n const now = new Date();\n const id = new ObjectId().toString();\n\n await collection().insertOne({\n _id: id,\n type: \"stateful-etl\",\n jobId,\n jobName,\n status: \"pending\",\n params: params ?? null,\n result: null,\n error: null,\n stallTimeoutMs,\n maxStallRetries,\n progress: {\n extractedCount: 0,\n transformedCount: 0,\n loadedCount: 0,\n checkpoint: null,\n lastHeartbeatAt: null,\n stallRetries: 0,\n },\n timestamps: {\n createdAt: now,\n updatedAt: now,\n startedAt: null,\n completedAt: null,\n },\n });\n\n return id;\n}\n\nexport async function markInProgress(runId: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n status: \"in_progress\",\n \"timestamps.startedAt\": now,\n \"timestamps.updatedAt\": now,\n \"progress.lastHeartbeatAt\": now,\n },\n },\n );\n}\n\nexport async function updateHeartbeat(runId: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n \"progress.lastHeartbeatAt\": now,\n \"timestamps.updatedAt\": now,\n },\n },\n );\n}\n\nexport async function updateCheckpoint(\n runId: string,\n checkpoint: unknown,\n counts: { extractedCount: number; transformedCount: number; loadedCount: number },\n): Promise<void> {\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n \"progress.checkpoint\": checkpoint,\n \"progress.extractedCount\": counts.extractedCount,\n \"progress.transformedCount\": counts.transformedCount,\n \"progress.loadedCount\": counts.loadedCount,\n \"timestamps.updatedAt\": new Date(),\n },\n },\n );\n}\n\nexport async function markCompleted(runId: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n status: \"completed\",\n \"timestamps.completedAt\": now,\n \"timestamps.updatedAt\": now,\n \"progress.lastHeartbeatAt\": null,\n },\n },\n );\n}\n\nexport async function markFailed(runId: string, error: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n status: \"failed\",\n error,\n \"timestamps.updatedAt\": now,\n \"progress.lastHeartbeatAt\": null,\n },\n },\n );\n}\n\nexport async function findStalled(stallTimeoutMs: number): Promise<StatefulEtlJob[]> {\n const threshold = new Date(Date.now() - stallTimeoutMs);\n\n return await collection()\n .find({\n status: \"in_progress\",\n \"progress.lastHeartbeatAt\": { $lt: threshold },\n })\n .toArray();\n}\n\nexport async function findStalledCandidates(): Promise<StatefulEtlJob[]> {\n const now = new Date();\n\n return await collection()\n .find({\n status: \"in_progress\",\n \"progress.lastHeartbeatAt\": { $ne: null },\n $expr: {\n $lt: [\n \"$progress.lastHeartbeatAt\",\n { $subtract: [now, \"$stallTimeoutMs\"] },\n ],\n },\n })\n .toArray();\n}\n\nexport async function claimStalledRun(runId: string): Promise<StatefulEtlJob | null> {\n const result = await collection().findOneAndUpdate(\n {\n _id: runId,\n status: \"in_progress\",\n },\n {\n $set: {\n status: \"stalled\",\n \"progress.lastHeartbeatAt\": null,\n \"timestamps.updatedAt\": new Date(),\n },\n $inc: {\n \"progress.stallRetries\": 1,\n },\n },\n { returnDocument: \"after\" },\n );\n\n return result ?? null;\n}\n\nexport async function markResuming(runId: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n status: \"in_progress\",\n \"progress.lastHeartbeatAt\": now,\n \"timestamps.updatedAt\": now,\n },\n },\n );\n}\n","import { Collection } from \"mongodb\";\nimport { getDb, collectionName } from \"./connection\";\nimport type { TokenBucketState } from \"@/types\";\n\nconst COLLECTION = \"token_bucket_state\";\n\nfunction collection(): Collection<TokenBucketState> {\n return getDb().collection<TokenBucketState>(collectionName(COLLECTION));\n}\n\nexport async function getOrCreateState(\n jobName: string,\n now: Date,\n): Promise<TokenBucketState> {\n const result = await collection().findOneAndUpdate(\n { _id: jobName },\n {\n $setOnInsert: {\n _id: jobName,\n accumulatedCredit: 0,\n lastTickAt: now,\n },\n },\n { upsert: true, returnDocument: \"after\" },\n );\n\n return result!;\n}\n\nexport async function updateState(\n jobName: string,\n accumulatedCredit: number,\n lastTickAt: Date,\n): Promise<void> {\n await collection().updateOne(\n { _id: jobName },\n {\n $set: {\n accumulatedCredit,\n lastTickAt,\n },\n },\n );\n}\n\nexport async function ensureIndexes(): Promise<void> {\n // _id index is created automatically by MongoDB — nothing extra needed\n}\n","export { ETL_EXTRACT_SUFFIX, ETL_TRANSFORM_SUFFIX, ETL_LOAD_SUFFIX } from \"../constants\";\n\nexport const DEFAULT_STALL_TIMEOUT_MS = 300_000;\nexport const HEARTBEAT_INTERVAL_MS = 30_000;\nexport const DEFAULT_MAX_STALL_RETRIES = 3;\nexport const DEFAULT_WATCHER_INTERVAL_MS = 60_000;\n","import { getLogger } from \"@/logger\";\nimport { runRepo } from \"@/mongo\";\nimport { HEARTBEAT_INTERVAL_MS } from \"./constants\";\n\nexport function startHeartbeat(\n runId: string,\n): ReturnType<typeof setInterval> {\n const logger = getLogger();\n\n const timer = setInterval(async () => {\n try {\n await runRepo.updateHeartbeat(runId);\n } catch (err) {\n logger.warn(\n { runId, error: err },\n \"[stateful-etl] heartbeat update failed\",\n );\n }\n logger.debug({ runId }, \"[stateful-etl] heartbeat\");\n }, HEARTBEAT_INTERVAL_MS);\n\n timer.unref();\n return timer;\n}\n","import { Queue, QueueEvents } from \"bullmq\";\nimport { StatefulEtlJobConfig, ResolvedJobConfig } from \"@/types\";\nimport { runRepo } from \"@/mongo\";\nimport { EtlContext, type ProgressCounts } from \"../etl-context\";\n\nexport { type ProgressCounts };\n\nexport async function paginateAndProcess<TRaw, TProcessed, TCursor>(\n jobName: string,\n runId: string,\n jobConfig: ResolvedJobConfig<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n initialCursor: TCursor | null,\n initialCounts: ProgressCounts,\n): Promise<void> {\n const ctx = new EtlContext<TRaw, TProcessed>(\n jobName,\n jobConfig,\n queues,\n initialCounts,\n );\n\n let cursor = initialCursor;\n let hasMore = true;\n\n while (hasMore) {\n const page = await ctx.dispatchExtract<TCursor>(cursor);\n hasMore = page.hasMore;\n cursor = page.cursor;\n\n if (page.data.length === 0) continue;\n\n const processed = await ctx.transformParallel(page.data);\n await ctx.dispatchLoad(processed);\n\n ctx.counts.extractedCount += page.data.length;\n ctx.counts.transformedCount += processed.length;\n ctx.counts.loadedCount += processed.length;\n\n await runRepo.updateCheckpoint(runId, cursor, ctx.counts);\n ctx.logger.debug({ jobName, ...ctx.counts }, \"[stateful-etl] batch done\");\n }\n}\n","import { Job, Queue, QueueEvents } from \"bullmq\";\nimport { StatefulEtlJobConfig, ResolvedJobConfig } from \"@/types\";\nimport { runRepo } from \"@/mongo\";\nimport { getLogger } from \"@/logger\";\nimport { startHeartbeat } from \"./heartbeat\";\nimport { paginateAndProcess } from \"./paginate\";\nimport type { ProgressCounts } from \"./paginate\";\nimport { DEFAULT_STALL_TIMEOUT_MS, DEFAULT_MAX_STALL_RETRIES } from \"./constants\";\n\ninterface ResumeData {\n __ravenflow_resume: true;\n runId: string;\n checkpoint: unknown;\n counts: ProgressCounts;\n}\n\nfunction isResumeData(data: unknown): data is ResumeData {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"__ravenflow_resume\" in data &&\n (data as ResumeData).__ravenflow_resume === true\n );\n}\n\nexport async function processStatefulEtlJob<TRaw, TProcessed, TCursor>(\n job: Job,\n jobConfig: ResolvedJobConfig<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n): Promise<void> {\n const logger = getLogger();\n const jobName = job.name;\n\n if (isResumeData(job.data)) {\n return resumeRun(job.data, jobName, jobConfig, queues, logger);\n }\n\n return freshRun(job, jobName, jobConfig, queues, logger);\n}\n\nasync function resumeRun<TRaw, TProcessed, TCursor>(\n data: ResumeData,\n jobName: string,\n jobConfig: ResolvedJobConfig<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n logger: ReturnType<typeof getLogger>,\n): Promise<void> {\n const { runId, checkpoint, counts } = data;\n logger.info({ jobName, runId }, \"[stateful-etl] resuming stalled run\");\n\n await runRepo.markResuming(runId);\n const heartbeatTimer = startHeartbeat(runId);\n\n try {\n await paginateAndProcess(\n jobName,\n runId,\n jobConfig,\n queues,\n checkpoint as TCursor | null,\n counts,\n );\n\n await runRepo.markCompleted(runId);\n logger.info({ jobName, runId }, \"[stateful-etl] resumed run completed\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n await runRepo.markFailed(runId, message);\n logger.error({ jobName, runId, error: message }, \"[stateful-etl] resumed run failed\");\n throw error;\n } finally {\n clearInterval(heartbeatTimer);\n }\n}\n\nasync function freshRun<TRaw, TProcessed, TCursor>(\n job: Job,\n jobName: string,\n jobConfig: ResolvedJobConfig<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n logger: ReturnType<typeof getLogger>,\n): Promise<void> {\n const params = job.data ?? null;\n\n if (jobConfig.singletonRun) {\n await ensureNotAlreadyRunning(jobName);\n }\n\n const stallTimeoutMs = jobConfig.stallTimeoutMs ?? DEFAULT_STALL_TIMEOUT_MS;\n const maxStallRetries = jobConfig.maxStallRetries ?? DEFAULT_MAX_STALL_RETRIES;\n const runId = await runRepo.createRun(jobName, params, job.id!, stallTimeoutMs, maxStallRetries);\n await runRepo.markInProgress(runId);\n\n const heartbeatTimer = startHeartbeat(runId);\n\n try {\n await paginateAndProcess(\n jobName,\n runId,\n jobConfig,\n queues,\n null,\n { extractedCount: 0, transformedCount: 0, loadedCount: 0 },\n );\n\n await runRepo.markCompleted(runId);\n logger.info({ jobName }, \"[stateful-etl] run completed\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n await runRepo.markFailed(runId, message);\n logger.error({ jobName, error: message }, \"[stateful-etl] run failed\");\n throw error;\n } finally {\n clearInterval(heartbeatTimer);\n }\n}\n\nasync function ensureNotAlreadyRunning(jobName: string): Promise<void> {\n const activeRun = await runRepo.findActiveRun(jobName);\n if (activeRun) {\n throw new Error(\n `[stateful-etl] job \"${jobName}\" already has an active run (status: ${activeRun.status})`,\n );\n }\n}\n","import type { TokenBucketInterval } from \"@/types\";\nimport { INTERVAL_MS } from \"./constants\";\n\nexport interface ComputeCreditInput {\n now: Date;\n rate?: { amount: number; interval: TokenBucketInterval };\n lastTickAt: Date;\n accumulatedCredit: number;\n maxCarryOver: number;\n budget?: number;\n}\n\nexport interface ComputeCreditResult {\n tokens: number;\n newCredit: number;\n}\n\nexport function computeCredit(input: ComputeCreditInput): ComputeCreditResult {\n const { now, rate, lastTickAt, accumulatedCredit, maxCarryOver, budget } = input;\n\n const elapsedMs = Math.max(0, now.getTime() - lastTickAt.getTime());\n\n const tickCredit = rate\n ? (rate.amount * elapsedMs) / INTERVAL_MS[rate.interval]\n : 0;\n\n const totalCredit = accumulatedCredit + tickCredit;\n\n const cappedCredit = rate\n ? Math.min(totalCredit, tickCredit * (1 + maxCarryOver / 100))\n : totalCredit;\n\n const availableCredit =\n budget !== undefined ? Math.min(cappedCredit, budget) : cappedCredit;\n\n const tokens = Math.floor(availableCredit);\n const newCredit = cappedCredit - tokens;\n\n return { tokens, newCredit };\n}\n","import type { Job } from \"bullmq\";\nimport type { TokenBucketJobConfig, ResolvedJobConfig } from \"@/types\";\nimport type { QueueStore } from \"@/app/queue-manager\";\nimport { getLogger } from \"@/logger\";\nimport { tokenBucketRepo } from \"@/mongo\";\nimport { computeCredit } from \"./compute-credit\";\nimport { TOKEN_BUCKET_CONSUME_SUFFIX } from \"./constants\";\n\nexport { TOKEN_BUCKET_CONSUME_SUFFIX } from \"./constants\";\n\nexport async function processTokenBucketJob(\n job: Job,\n config: ResolvedJobConfig<TokenBucketJobConfig>,\n queues: QueueStore,\n): Promise<void> {\n const logger = getLogger();\n const tickStartedAt = new Date();\n\n const state = await tokenBucketRepo.getOrCreateState(job.name, tickStartedAt);\n\n const budget = config.getBudget ? await config.getBudget() : undefined;\n\n const { tokens, newCredit } = computeCredit({\n now: tickStartedAt,\n rate: config.rate,\n lastTickAt: state.lastTickAt,\n accumulatedCredit: state.accumulatedCredit,\n maxCarryOver: config.maxCarryOver ?? 100,\n budget,\n });\n\n if (tokens === 0) {\n await tokenBucketRepo.updateState(job.name, newCredit, tickStartedAt);\n logger.debug({ jobName: job.name, newCredit }, \"[token-bucket] zero tokens, skipping consume\");\n return;\n }\n\n try {\n const queueData = queues[config.queue];\n const consumeJob = await queueData.queue.add(\n `${job.name}${TOKEN_BUCKET_CONSUME_SUFFIX}`,\n tokens,\n );\n\n // BullMQ's `JobsOptions` has no `timeout` field; the consume-side wait TTL is the\n // second arg of `waitUntilFinished(events, ttl)` per bullmq@5.63.1\n // node_modules/bullmq/dist/esm/classes/job.d.ts:436.\n const result = await consumeJob.waitUntilFinished(queueData.events, config.consumeTimeout);\n\n const consumed = typeof result === \"number\" ? result : tokens;\n const finalCredit = newCredit + (tokens - consumed);\n\n await tokenBucketRepo.updateState(job.name, finalCredit, tickStartedAt);\n logger.info({ jobName: job.name, tokens, consumed, finalCredit }, \"[token-bucket] tick complete\");\n } catch (error) {\n logger.warn({ jobName: job.name, error: String(error) }, \"[token-bucket] consume failed, skipping state update\");\n }\n}\n","import { Job } from \"bullmq\";\nimport { RavenflowConfig, JobConfig, JobNames, JobsProxy } from \"../types\";\nimport {\n processSimpleJob,\n processMemoryEtlJob,\n processDistributedEtlJob,\n processStatefulEtlJob,\n processTokenBucketJob,\n ETL_EXTRACT_SUFFIX,\n ETL_TRANSFORM_SUFFIX,\n ETL_LOAD_SUFFIX,\n TOKEN_BUCKET_CONSUME_SUFFIX,\n} from \"../processors\";\nimport { QueueStore, sendJob, sendJobAndWait } from \"./queue-manager\";\n\nexport function initJobs<TConfig extends RavenflowConfig>(\n config: TConfig,\n queues: QueueStore\n): {\n jobsProxy: JobsProxy<TConfig[\"jobs\"]>;\n jobConfigs: Record<JobNames<TConfig>, JobConfig>;\n} {\n // Generic-erasure proxy: per-job TData/TReturn get resolved via `JobsProxy<TConfig[\"jobs\"]>`\n // at the public API; here we hold the runtime dispatcher as loose `any` and cast once below.\n // Typed config-walker deferred per Story 1.8 AC #15.\n /* eslint-disable @typescript-eslint/no-explicit-any */\n const jobsProxy: Record<string, { send: (params: any) => any; sendAndWait: (params: any) => any }> = {};\n const jobConfigs: Record<string, JobConfig> = {};\n\n for (const [jobName, jobConfig] of Object.entries(config.jobs)) {\n jobConfigs[jobName] = jobConfig;\n\n const resolvedQueue = jobConfig.queue ?? config.defaultQueue;\n\n jobsProxy[jobName] = {\n send: async (params: any) => {\n return sendJob(jobName, params, queues, resolvedQueue);\n },\n sendAndWait: async (params: any) => {\n return sendJobAndWait(jobName, params, queues, resolvedQueue);\n },\n };\n }\n /* eslint-enable @typescript-eslint/no-explicit-any */\n\n return {\n jobsProxy: jobsProxy as JobsProxy<TConfig[\"jobs\"]>,\n jobConfigs: jobConfigs as Record<JobNames<TConfig>, JobConfig>,\n };\n}\n\nconst ALL_SUFFIXES = [\n ETL_EXTRACT_SUFFIX,\n ETL_TRANSFORM_SUFFIX,\n ETL_LOAD_SUFFIX,\n TOKEN_BUCKET_CONSUME_SUFFIX,\n] as const;\n\nexport function resolveJobName(\n name: string,\n jobConfigs: Record<string, JobConfig>\n): { baseName: string; suffix: string | null } {\n for (const suffix of ALL_SUFFIXES) {\n if (!name.endsWith(suffix)) continue;\n\n const baseName = name.slice(0, -suffix.length);\n if (jobConfigs[baseName]) {\n return { baseName, suffix };\n }\n }\n return { baseName: name, suffix: null };\n}\n\nexport async function processJob<TConfig extends RavenflowConfig>(\n job: Job,\n jobConfigs: Record<JobNames<TConfig>, JobConfig>,\n queues: QueueStore,\n defaultQueue: string,\n) {\n const { baseName, suffix } = resolveJobName(job.name, jobConfigs as Record<string, JobConfig>);\n const jobConfig = getJobConfig(baseName, jobConfigs);\n const resolvedQueue = jobConfig.queue ?? defaultQueue;\n\n // -- Sub-job routing --\n if (suffix !== null) {\n if (jobConfig.type === \"distributed-etl\") {\n if (suffix === ETL_EXTRACT_SUFFIX) {\n return await jobConfig.extract(job.data.cursor);\n }\n if (suffix === ETL_TRANSFORM_SUFFIX) {\n return await jobConfig.transform(job.data);\n }\n if (suffix === ETL_LOAD_SUFFIX) {\n return await jobConfig.load(job.data.data);\n }\n }\n\n if (jobConfig.type === \"stateful-etl\") {\n if (suffix === ETL_EXTRACT_SUFFIX) {\n return await jobConfig.extract(job.data.cursor);\n }\n if (suffix === ETL_TRANSFORM_SUFFIX) {\n return await jobConfig.transform(job.data);\n }\n if (suffix === ETL_LOAD_SUFFIX) {\n return await jobConfig.load(job.data.data);\n }\n }\n\n if (jobConfig.type === \"token-bucket\") {\n if (suffix === TOKEN_BUCKET_CONSUME_SUFFIX) {\n return await jobConfig.consume(job.data);\n }\n }\n\n throw new Error(`Unexpected sub-job suffix \"${suffix}\" for ${baseName} (type: ${jobConfig.type})`);\n }\n\n // -- Top-level job routing --\n if (jobConfig.type === \"simple\") {\n return await processSimpleJob(job, jobConfig);\n }\n\n if (jobConfig.type === \"memory-etl\") {\n return await processMemoryEtlJob(job, jobConfig);\n }\n\n if (jobConfig.type === \"distributed-etl\") {\n return await processDistributedEtlJob(job, { ...jobConfig, queue: resolvedQueue }, queues);\n }\n\n if (jobConfig.type === \"stateful-etl\") {\n return await processStatefulEtlJob(job, { ...jobConfig, queue: resolvedQueue }, queues);\n }\n\n if (jobConfig.type === \"token-bucket\") {\n return await processTokenBucketJob(job, { ...jobConfig, queue: resolvedQueue }, queues);\n }\n\n throw new Error(`Unknown job type for: ${job.name}`);\n}\n\nfunction getJobConfig<TConfig extends RavenflowConfig>(\n jobName: string,\n jobConfigs: Record<JobNames<TConfig>, JobConfig>\n): JobConfig {\n const config = jobConfigs[jobName as JobNames<TConfig>];\n if (!config) throw new Error(`Job handler not found: ${jobName}`);\n return config;\n}\n","import { JobConfig } from \"./types\";\n\nconst RAVENFLOW_ASCII = `\n ██████╗ █████╗ ██╗ ██╗███████╗███╗ ██╗███████╗██╗ ██████╗ ██╗ ██╗\n ██╔══██╗██╔══██╗██║ ██║██╔════╝████╗ ██║██╔════╝██║ ██╔═══██╗██║ ██║\n ██████╔╝███████║██║ ██║█████╗ ██╔██╗ ██║█████╗ ██║ ██║ ██║██║ █╗ ██║\n ██╔══██╗██╔══██║╚██╗ ██╔╝██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║ ██║██║███╗██║\n ██║ ██║██║ ██║ ╚████╔╝ ███████╗██║ ╚████║██║ ███████╗╚██████╔╝╚███╔███╔╝\n ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝\n`;\n\nconst RAVENFLOW_ASCII_ART = ` :==++==-:::..\n :--:::.*%%%%###+====--:::::.\n ===--:+@@%%%##%##*==-=%%%###*##:\n :+===-*@@%%%######*#*#%@@%\n =++==*@@%%%#########@@@@%.\n -*+==@@@%%%######*%@@@@%*\n **+#@@%%%#######%@@@@%%*=\n =**@@%%%%#######@@@@%%%**\n **@@%%%%#%####@@@@@%%#**+\n +#@%%%%%%#%##%@@@@@%%#***-\n %%%%%%#%#%##@@@@@%%%*+***\n .@%%%%%#%%%#%@@@@%%%%+=++*:\n =%%%%%#%%%##@@@@@%%%%===++=\n #%%%%%%%%##@@@@@%%%%*-=====\n =%%%%%%%%%#@@@@@%%%%%::-===\n =%%%#%%%#%@@@@@@@%%%=.:::-:\n +******#%%%%%%%#*=: . .\n`;\n\ninterface BannerConfig {\n queueName: string;\n concurrency: number;\n redis: {\n host: string;\n port: number;\n db: number;\n };\n mongo?: string | null;\n jobs: Record<string, JobConfig>;\n}\n\nfunction censorUri(uri: string): string {\n try {\n const url = new URL(uri);\n if (url.password) url.password = \"***\";\n return url.toString();\n } catch {\n return uri;\n }\n}\n\nexport function printWorkerBanner(config: BannerConfig): void {\n const { queueName, concurrency, redis, mongo, jobs } = config;\n\n console.log(\"\\n\\n\" + RAVENFLOW_ASCII_ART);\n console.log(\"\\n\" + RAVENFLOW_ASCII);\n console.log(\"═\".repeat(85));\n\n console.log(\"\\n[Config]\");\n console.log(` -> queue: ${queueName}`);\n console.log(` -> concurrency: ${concurrency}`);\n console.log(` -> redis: ${redis.host}:${redis.port}/${redis.db}`);\n if (mongo) {\n console.log(` -> mongo: ${censorUri(mongo)}`);\n }\n\n console.log(\"\\n[Registered Jobs]\");\n\n const regularJobs: string[] = [];\n const cronJobs: { name: string; cron: string }[] = [];\n\n for (const [jobName, jobConfig] of Object.entries(jobs)) {\n if (jobConfig.cron) {\n cronJobs.push({ name: jobName, cron: jobConfig.cron });\n } else {\n regularJobs.push(jobName);\n }\n }\n\n if (regularJobs.length > 0) {\n regularJobs.forEach((jobName) => {\n console.log(` . ${jobName}`);\n });\n }\n\n if (cronJobs.length > 0) {\n cronJobs.forEach(({ name, cron }) => {\n console.log(` . ${name} (↻ ${cron})`);\n });\n }\n\n console.log(\"\\n\" + \"═\".repeat(85));\n console.log(\"Ready to process jobs...\\n\");\n}\n","import {\n ConnectionOptions,\n Worker,\n Job,\n type WorkerOptions,\n} from \"bullmq\";\nimport { getLogger } from \"../logger\";\nimport { printWorkerBanner } from \"../banner\";\nimport { JobConfig } from \"../types\";\n\n/**\n * Inlined minute-count constants. Mirrors BullMQ's `MetricsTime` enum\n * (bullmq@5.63.1) but severs the import edge so consumers of this constant\n * don't pull BullMQ into their bundle. Values are pure math (minutes in\n * the named window) and therefore stable across BullMQ versions. Lives\n * here instead of `index.ts` to avoid an import cycle\n * (`index.ts` → `./app/index` → `./app/app` → `worker-manager`).\n */\nexport const METRICS_TIME = {\n ONE_MINUTE: 1,\n FIVE_MINUTES: 5,\n FIFTEEN_MINUTES: 15,\n THIRTY_MINUTES: 30,\n ONE_HOUR: 60,\n ONE_DAY: 1_440,\n ONE_WEEK: 10_080,\n TWO_WEEKS: 20_160,\n ONE_MONTH: 80_640,\n} as const;\n\n/**\n * Default BullMQ metrics configuration applied to every ravenflow-managed\n * Worker. Enables the per-minute data-point collection read by the\n * ravenflow-dashboard Jobs-per-Second indicator (Epic 4 Story 4.2).\n *\n * Without this default, BullMQ's `collectMetrics` Lua function is a no-op on\n * every `moveToFinished` — `queue.getMetrics('completed')` would return\n * `{ meta: { count: 0 }, data: [] }` indefinitely for Ravenflow-managed\n * queues (silent false-negative under real throughput).\n *\n * `ONE_WEEK = 10_080` minutes ≈ 7 days of minute-resolution history at a\n * Redis footprint of ~10 KB/queue/week. Callers may override via the\n * `workerOpts.metrics` parameter on `createWorker` (e.g. to disable\n * collection or pick a shorter window).\n *\n * Frozen so the exported reference cannot be mutated by a caller (shared\n * default would otherwise bleed globally across `createWorker` calls).\n */\nexport const DEFAULT_WORKER_METRICS: NonNullable<WorkerOptions[\"metrics\"]> = Object.freeze({\n maxDataPoints: METRICS_TIME.ONE_WEEK,\n});\n\n/**\n * Pass-through WorkerOptions accepted by `createWorker`. Excludes the three\n * fields ravenflow controls in-situ (`connection`, `autorun`, `concurrency`)\n * so callers cannot override them accidentally. Any other BullMQ option\n * (metrics, stalledInterval, lockDuration, ...) is forwarded to `new Worker`.\n */\nexport type RavenflowWorkerOpts = Omit<\n WorkerOptions,\n \"connection\" | \"autorun\" | \"concurrency\"\n>;\n\nexport function createWorker(\n queueName: string,\n connection: ConnectionOptions,\n concurrency: number,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- processJob return width is resolved per job config; typed config-walker deferred per Story 1.8 AC #15\n processJob: (job: Job, token?: string) => Promise<any>,\n workerOpts?: RavenflowWorkerOpts,\n): Worker {\n const worker = new Worker(queueName, processJob, {\n ...workerOpts,\n connection,\n autorun: false,\n concurrency,\n metrics: workerOpts?.metrics ?? DEFAULT_WORKER_METRICS,\n });\n return worker;\n}\n\nexport function setupWorkerEvents(\n worker: Worker,\n queueName: string,\n): void {\n const logger = getLogger();\n worker.on(\"active\", (job) => {\n logger.info(`[${queueName}] ${job.name} - active`);\n });\n\n worker.on(\"completed\", (job) => {\n logger.info(`[${queueName}] ${job.name} - completed`);\n });\n\n worker.on(\"failed\", (job, error) => {\n logger.error(`[${queueName}] ${job?.name} - failed: ${error.message}`);\n });\n}\n\nexport function printBanner(\n queueName: string,\n concurrency: number,\n connection: ConnectionOptions,\n jobConfigs: Record<string, JobConfig>,\n mongoUri?: string | null,\n): void {\n const redisInfo = {\n host:\n typeof connection === \"object\" && \"host\" in connection\n ? (connection.host as string)\n : \"localhost\",\n port:\n typeof connection === \"object\" && \"port\" in connection\n ? (connection.port as number)\n : 6379,\n db:\n typeof connection === \"object\" && \"db\" in connection\n ? (connection.db as number)\n : 0,\n };\n\n printWorkerBanner({\n queueName,\n concurrency,\n redis: redisInfo,\n mongo: mongoUri,\n jobs: jobConfigs,\n });\n}\n\nexport async function closeWorkers(workers: Worker[]): Promise<void> {\n await Promise.all(workers.map((w) => w.close()));\n}\n","import { Queue } from \"bullmq\";\nimport { JobConfig } from \"../types\";\nimport { getLogger } from \"../logger\";\n\nexport async function syncJobSchedulers(\n queue: Queue,\n jobConfigs: Record<string, JobConfig>,\n): Promise<void> {\n const logger = getLogger();\n logger.info(\"[Scheduled Jobs]\");\n\n const jobConfigsWithCron: Record<string, JobConfig> = {};\n for (const jobName in jobConfigs) {\n if (jobConfigs[jobName].cron) {\n jobConfigsWithCron[jobName] = jobConfigs[jobName];\n }\n }\n\n const registeredJobSchedulers = await queue.getJobSchedulers();\n const existingSchedulerKeys = new Set(registeredJobSchedulers.map((s) => s.key));\n\n const removed: string[] = [];\n for (const jobScheduler of registeredJobSchedulers) {\n if (!jobConfigsWithCron[jobScheduler.key]) {\n removed.push(jobScheduler.key);\n await queue.removeJobScheduler(jobScheduler.key);\n }\n }\n\n const newJobs: string[] = [];\n const updated: string[] = [];\n\n for (const jobName in jobConfigsWithCron) {\n const jobConfig = jobConfigsWithCron[jobName];\n if (!jobConfig?.cron) continue;\n\n if (existingSchedulerKeys.has(jobName)) {\n updated.push(jobName);\n } else {\n newJobs.push(jobName);\n }\n\n await queue.upsertJobScheduler(jobName, { pattern: jobConfig.cron });\n }\n\n for (const name of newJobs) {\n logger.info(` + ${name} → ${jobConfigsWithCron[name].cron}`);\n }\n for (const name of updated) {\n logger.info(` ↻ ${name} → ${jobConfigsWithCron[name].cron}`);\n }\n for (const name of removed) {\n logger.info(` - ${name}`);\n }\n if (newJobs.length === 0 && updated.length === 0 && removed.length === 0) {\n logger.info(\" (no scheduled jobs)\");\n }\n}\n","import type { StatefulEtlJobConfig } from \"../../types\";\n\nexport const STALL_CHECK = \"__stall_check\";\nexport const STALL_RESUME = \"__stall_resume\";\n\nexport interface StatefulEtlEntry {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic erasure at runtime; typed config-walker deferred per Story 1.8 AC #15\n config: StatefulEtlJobConfig<any, any, any>;\n queue: string;\n}\n\nexport interface StallResumePayload {\n runId: string;\n jobName: string;\n queueName: string;\n maxStallRetries: number;\n stallRetries: number;\n checkpoint: unknown;\n extractedCount: number;\n transformedCount: number;\n loadedCount: number;\n}\n","import { Queue } from \"bullmq\";\nimport { getLogger } from \"../../logger\";\nimport { runRepo } from \"../../mongo\";\nimport type { StatefulEtlEntry, StallResumePayload } from \"./types\";\nimport { STALL_RESUME } from \"./types\";\n\n/**\n * Step 1 — Scan MongoDB for stalled runs, verify against Redis,\n * atomically claim them, and dispatch resume sub-jobs.\n */\nexport async function checkStalledRuns(\n statefulEtlConfigs: Map<string, StatefulEtlEntry>,\n userQueues: Record<string, { queue: Queue }>,\n workerQueue: Queue,\n): Promise<void> {\n const logger = getLogger();\n const candidates = await runRepo.findStalledCandidates();\n\n for (const candidate of candidates) {\n const entry = statefulEtlConfigs.get(candidate.jobName);\n if (!entry) {\n logger.warn(\n { runId: candidate._id, jobName: candidate.jobName },\n \"[stall-watcher] unknown job, marking failed\",\n );\n await runRepo.markFailed(\n candidate._id,\n \"Job config not found during stall recovery\",\n );\n continue;\n }\n\n const queueData = userQueues[entry.queue];\n if (!queueData) {\n logger.error(\n { queueName: entry.queue, runId: candidate._id },\n \"[stall-watcher] queue not found\",\n );\n await runRepo.markFailed(\n candidate._id,\n `Queue \"${entry.queue}\" not found during stall recovery`,\n );\n continue;\n }\n\n if (await isJobActiveInRedis(queueData.queue, candidate.jobId)) {\n logger.debug(\n { runId: candidate._id, jobId: candidate.jobId },\n \"[stall-watcher] job still active in Redis, skipping\",\n );\n continue;\n }\n\n const claimed = await runRepo.claimStalledRun(candidate._id);\n if (!claimed) continue;\n\n await workerQueue.add(STALL_RESUME, {\n runId: claimed._id,\n jobName: claimed.jobName,\n queueName: entry.queue,\n maxStallRetries: claimed.maxStallRetries,\n stallRetries: claimed.progress.stallRetries,\n checkpoint: claimed.progress.checkpoint,\n extractedCount: claimed.progress.extractedCount,\n transformedCount: claimed.progress.transformedCount,\n loadedCount: claimed.progress.loadedCount,\n } satisfies StallResumePayload);\n }\n}\n\nasync function isJobActiveInRedis(\n queue: Queue,\n jobId: string,\n): Promise<boolean> {\n try {\n const state = await queue.getJobState(jobId);\n return state === \"active\";\n } catch {\n return false;\n }\n}\n","import { Queue } from \"bullmq\";\nimport { getLogger } from \"../../logger\";\nimport { runRepo } from \"../../mongo\";\nimport type { StallResumePayload } from \"./types\";\n\n/**\n * Step 2 — Handle a single stalled run: re-enqueue on the user's\n * queue with resume data, or mark failed if retries exhausted.\n */\nexport async function resumeStalledRun(\n data: StallResumePayload,\n userQueues: Record<string, { queue: Queue }>,\n): Promise<void> {\n const logger = getLogger();\n\n if (data.stallRetries > data.maxStallRetries) {\n logger.warn(\n {\n runId: data.runId,\n jobName: data.jobName,\n stallRetries: data.stallRetries,\n },\n \"[stall-watcher] max stall retries exceeded, marking failed\",\n );\n await runRepo.markFailed(\n data.runId,\n `Stall recovery exhausted after ${data.stallRetries} retries`,\n );\n return;\n }\n\n const queueData = userQueues[data.queueName];\n if (!queueData) {\n logger.error(\n { queueName: data.queueName, runId: data.runId },\n \"[stall-watcher] queue not found for resume\",\n );\n await runRepo.markFailed(\n data.runId,\n `Queue \"${data.queueName}\" not found`,\n );\n return;\n }\n\n logger.info(\n {\n runId: data.runId,\n jobName: data.jobName,\n stallRetries: data.stallRetries,\n checkpoint: data.checkpoint,\n },\n \"[stall-watcher] re-enqueuing stalled run\",\n );\n\n await queueData.queue.add(data.jobName, {\n __ravenflow_resume: true,\n runId: data.runId,\n checkpoint: data.checkpoint,\n counts: {\n extractedCount: data.extractedCount,\n transformedCount: data.transformedCount,\n loadedCount: data.loadedCount,\n },\n });\n}\n","import { Job, Queue } from \"bullmq\";\nimport { getLogger } from \"../../logger\";\nimport type { JobConfig, StatefulEtlJobConfig } from \"../../types\";\nimport { STALL_CHECK, STALL_RESUME } from \"./types\";\nimport type { StatefulEtlEntry, StallResumePayload } from \"./types\";\nimport { checkStalledRuns } from \"./check-stalled-runs\";\nimport { resumeStalledRun } from \"./resume-stalled-run\";\n\nexport class StallWatcher {\n private readonly workerQueue: Queue;\n private readonly statefulEtlConfigs: Map<string, StatefulEtlEntry>;\n private readonly userQueues: Record<string, { queue: Queue }>;\n\n constructor(\n jobConfigs: Record<string, JobConfig>,\n userQueues: Record<string, { queue: Queue }>,\n defaultQueue: string,\n workerQueue: Queue,\n ) {\n this.userQueues = userQueues;\n this.workerQueue = workerQueue;\n\n this.statefulEtlConfigs = new Map();\n for (const [jobName, config] of Object.entries(jobConfigs)) {\n if (config.type === \"stateful-etl\") {\n this.statefulEtlConfigs.set(jobName, {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic erasure at runtime; typed config-walker deferred per Story 1.8 AC #15\n config: config as StatefulEtlJobConfig<any, any, any>,\n queue: config.queue ?? defaultQueue,\n });\n }\n }\n }\n\n canProcess(jobName: string): boolean {\n return jobName === STALL_CHECK || jobName === STALL_RESUME;\n }\n\n async start(): Promise<void> {\n if (this.statefulEtlConfigs.size === 0) return;\n\n const logger = getLogger();\n logger.info(\n { jobs: [...this.statefulEtlConfigs.keys()] },\n \"[stall-watcher] starting\",\n );\n\n await this.workerQueue.upsertJobScheduler(STALL_CHECK, {\n pattern: \"* * * * *\",\n });\n }\n\n async stop(): Promise<void> {\n await this.workerQueue.removeJobScheduler(STALL_CHECK);\n }\n\n async process(job: Job): Promise<void> {\n switch (job.name) {\n case STALL_CHECK:\n return checkStalledRuns(\n this.statefulEtlConfigs,\n this.userQueues,\n this.workerQueue,\n );\n case STALL_RESUME:\n return resumeStalledRun(\n job.data as StallResumePayload,\n this.userQueues,\n );\n }\n }\n}\n","import type { Collection, Db, Document } from \"mongodb\";\nimport { collectionName } from \"../connection\";\nimport type { IndexDescriptor } from \"./types\";\n\nexport abstract class Repository<T extends Document> {\n public readonly collection: Collection<T>;\n\n constructor(db: Db, rawCollectionName: string) {\n this.collection = db.collection<T>(collectionName(rawCollectionName));\n }\n\n protected abstract indexes(): IndexDescriptor[];\n\n async init(): Promise<void> {\n for (const idx of this.indexes()) {\n await this.collection.createIndex(idx.keys, idx.options);\n }\n }\n}\n","import type { Db, Filter } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport type { UpdateSpec } from \"./_types\";\nimport { getLogger } from \"../../logger\";\nimport {\n IndexDescriptor,\n RegisterWorkerInput,\n UpsertWorkerQueueInput,\n WorkerDoc,\n WorkerQueueEntry,\n} from \"./types\";\n\nconst COLLECTION = \"workers\";\n\nexport class WorkersRepo extends Repository<WorkerDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { workerId: 1 },\n options: { unique: true, name: \"workerId_unique\" },\n },\n {\n keys: { lastHeartbeatAt: 1 },\n options: { name: \"lastHeartbeatAt_asc\" },\n },\n ];\n }\n\n async registerWorker(input: RegisterWorkerInput): Promise<void> {\n this.validateRegisterInput(input);\n const filter: Filter<WorkerDoc> = { workerId: input.workerId };\n const update: UpdateSpec<WorkerDoc> = {\n $currentDate: {\n lastHeartbeatAt: true,\n updatedAt: true,\n },\n $setOnInsert: {\n workerId: input.workerId,\n hostname: input.hostname,\n pid: input.pid,\n queues: input.queues,\n startedAt: input.startedAt,\n createdAt: new Date(),\n },\n };\n await this.collection.updateOne(filter, update, { upsert: true });\n }\n\n async updateHeartbeat(workerId: string): Promise<void> {\n if (typeof workerId !== \"string\" || workerId.length === 0) {\n throw new Error(\"WorkersRepo.updateHeartbeat: workerId must be a non-empty string\");\n }\n const filter: Filter<WorkerDoc> = { workerId };\n const update: UpdateSpec<WorkerDoc> = {\n $currentDate: { lastHeartbeatAt: true, updatedAt: true },\n };\n const result = await this.collection.updateOne(filter, update);\n if (result.matchedCount === 0) {\n getLogger().warn(\n { workerId },\n \"[workers-repo] heartbeat on missing worker record — ignoring\",\n );\n }\n }\n\n /**\n * Appends or replaces a queue entry on the worker's record.\n *\n * Single-writer invariant: this method is NOT atomic under concurrent writers to the\n * SAME `workerId`. WorkerRegistry is a process-local singleton, and `workerId` is\n * unique per process by contract (default: `<hostname>:<pid>:<uuid>`). Sharing a\n * stable `workerId` across processes violates AC #6 and will race last-writer-wins.\n */\n async addQueue(input: UpsertWorkerQueueInput): Promise<void> {\n if (typeof input.workerId !== \"string\" || input.workerId.length === 0) {\n throw new Error(\"WorkersRepo.addQueue: workerId must be a non-empty string\");\n }\n this.validateQueueEntry(input.queue, \"WorkersRepo.addQueue\");\n\n const existingFilter: Filter<WorkerDoc> = { workerId: input.workerId };\n const existing = await this.collection.findOne(existingFilter);\n\n if (existing === null) {\n throw new Error(\n `WorkersRepo.addQueue: worker not found for workerId=${input.workerId}`,\n );\n }\n\n const existingQueues = existing.queues ?? [];\n const matchIndex = existingQueues.findIndex((q) => q.name === input.queue.name);\n const newQueues: WorkerQueueEntry[] =\n matchIndex === -1\n ? [...existingQueues, input.queue]\n : existingQueues.map((q, i) => (i === matchIndex ? input.queue : q));\n\n const update: UpdateSpec<WorkerDoc> = {\n $set: { queues: newQueues },\n $currentDate: { updatedAt: true },\n };\n await this.collection.updateOne(existingFilter, update);\n }\n\n async deregisterWorker(workerId: string): Promise<void> {\n if (typeof workerId !== \"string\" || workerId.length === 0) {\n throw new Error(\"WorkersRepo.deregisterWorker: workerId must be a non-empty string\");\n }\n const filter: Filter<WorkerDoc> = { workerId };\n await this.collection.deleteOne(filter);\n }\n\n async findAlive(maxHeartbeatAgeMs: number): Promise<WorkerDoc[]> {\n this.validateMaxHeartbeatAgeMs(maxHeartbeatAgeMs, \"WorkersRepo.findAlive\");\n const threshold = new Date(Date.now() - maxHeartbeatAgeMs);\n const filter: Filter<WorkerDoc> = { lastHeartbeatAt: { $gte: threshold } };\n return await this.collection\n .find(filter)\n .sort({ lastHeartbeatAt: -1 })\n .toArray();\n }\n\n async cleanupStale(maxHeartbeatAgeMs: number): Promise<number> {\n this.validateMaxHeartbeatAgeMs(maxHeartbeatAgeMs, \"WorkersRepo.cleanupStale\");\n const threshold = new Date(Date.now() - maxHeartbeatAgeMs);\n const filter: Filter<WorkerDoc> = { lastHeartbeatAt: { $lt: threshold } };\n const result = await this.collection.deleteMany(filter);\n return result.deletedCount ?? 0;\n }\n\n private validateRegisterInput(input: RegisterWorkerInput): void {\n if (typeof input.workerId !== \"string\" || input.workerId.length === 0) {\n throw new Error(\"WorkersRepo.registerWorker: workerId must be a non-empty string\");\n }\n if (typeof input.hostname !== \"string\" || input.hostname.length === 0) {\n throw new Error(\"WorkersRepo.registerWorker: hostname must be a non-empty string\");\n }\n if (!Number.isInteger(input.pid) || input.pid < 0) {\n throw new Error(\"WorkersRepo.registerWorker: pid must be a non-negative integer\");\n }\n if (!Array.isArray(input.queues) || input.queues.length === 0) {\n throw new Error(\"WorkersRepo.registerWorker: queues must be a non-empty array\");\n }\n for (const q of input.queues) {\n this.validateQueueEntry(q, \"WorkersRepo.registerWorker\");\n }\n if (!(input.startedAt instanceof Date) || Number.isNaN(input.startedAt.getTime())) {\n throw new Error(\"WorkersRepo.registerWorker: startedAt must be a valid Date\");\n }\n }\n\n private validateQueueEntry(q: WorkerQueueEntry, ctx: string): void {\n if (q === null || typeof q !== \"object\") {\n throw new Error(`${ctx}: queue entry must be an object`);\n }\n if (typeof q.name !== \"string\" || q.name.length === 0) {\n throw new Error(`${ctx}: queue.name must be a non-empty string`);\n }\n if (!Number.isInteger(q.concurrency) || q.concurrency <= 0) {\n throw new Error(`${ctx}: queue.concurrency must be a positive integer`);\n }\n }\n\n private validateMaxHeartbeatAgeMs(value: number, ctx: string): void {\n const MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\n if (!Number.isInteger(value) || value <= 0 || value > MAX_AGE_MS) {\n throw new Error(\n `${ctx}: maxHeartbeatAgeMs must be a positive integer <= ${MAX_AGE_MS}`,\n );\n }\n }\n}\n","import type {\n AnyBulkWriteOperation,\n Document,\n Filter,\n OptionalUnlessRequiredId,\n UpdateFilter,\n} from \"mongodb\";\n\n/**\n * Call-site alias for the driver's `UpdateFilter<T>`. Declaring an update literal\n * with `const update: UpdateSpec<T> = { ... }` widens the literal against the\n * full driver type (not the narrower shape TS infers from the literal), so\n * partial `$setOnInsert` / `$set` documents assign without a post-hoc cast.\n *\n * Type-identical to `UpdateFilter<T>` — readability alias at the declaration\n * site, not a type relaxation.\n */\nexport type UpdateSpec<T extends Document> = UpdateFilter<T>;\n\n/** The shape `insertOne` expects. Re-export for call-site annotations. */\nexport type InsertSpec<T extends Document> = OptionalUnlessRequiredId<T>;\n\nexport interface BulkUpdateOp<T extends Document> {\n updateOne: {\n filter: Filter<T>;\n update: UpdateSpec<T>;\n upsert?: boolean;\n };\n}\n\nexport interface BulkInsertOp<T extends Document> {\n insertOne: {\n document: InsertSpec<T>;\n };\n}\n\n/**\n * Widen a `BulkUpdateOp<T>` to the driver's `AnyBulkWriteOperation<T>` union.\n * Single cast point for the widening — callers use this helper and never\n * write the cast themselves.\n */\nexport function asBulkUpdateOp<T extends Document>(\n op: BulkUpdateOp<T>,\n): AnyBulkWriteOperation<T> {\n return op as AnyBulkWriteOperation<T>;\n}\n\nexport function asBulkInsertOp<T extends Document>(\n op: BulkInsertOp<T>,\n): AnyBulkWriteOperation<T> {\n return op as AnyBulkWriteOperation<T>;\n}\n","import type { BucketGranularity } from \"./types\";\n\nexport function computeBucketStart(\n ts: Date,\n granularity: BucketGranularity,\n): Date {\n const result = new Date(ts);\n if (granularity === \"hour\") {\n result.setUTCMinutes(0, 0, 0);\n } else {\n result.setUTCHours(0, 0, 0, 0);\n }\n return result;\n}\n","export const MONGO_DUPLICATE_KEY_CODE = 11000;\n\nexport function isDuplicateKeyError(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) return false;\n return (err as { code?: unknown }).code === MONGO_DUPLICATE_KEY_CODE;\n}\n","import type { CreateIndexesOptions, IndexSpecification } from \"mongodb\";\n\nexport interface IndexDescriptor {\n keys: IndexSpecification;\n options?: CreateIndexesOptions;\n}\n\nexport type BucketGranularity = \"hour\" | \"day\";\n\nexport interface IncrementStatsInput {\n success: boolean;\n durationMs?: number;\n}\n\nexport interface JobStatsDoc {\n jobType: string;\n bucketStart: Date;\n granularity: BucketGranularity;\n count: number;\n success: number;\n failed: number;\n totalDurationMs: number;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/** NFR3 default: 90-day TTL for `job_stats`. Hardcoded for this story; configurable in a later story. */\nexport const JOB_STATS_TTL_SECONDS = 90 * 24 * 60 * 60;\n\nexport interface JobErrorDoc {\n signature: string;\n jobType: string;\n message: string;\n stack: string;\n count: number;\n firstSeenAt: Date;\n lastSeenAt: Date;\n createdAt: Date;\n updatedAt: Date;\n /** Normalized `Error.cause` chain beyond the top-level error. OMITTED when empty. */\n causeChain?: Array<{ message: string; name: string }>;\n}\n\nexport interface RecordOccurrenceInput {\n jobType: string;\n error: unknown;\n}\n\nexport interface TopErrorsOptions {\n limit: number;\n jobType?: string;\n /**\n * When set, restricts results to errors whose `lastSeenAt >= since`.\n * Dashboard health views pass the range start (e.g. 7 days ago, UTC midnight)\n * so \"top errors\" matches the displayed range; omitting returns all errors\n * back to the collection's 90-day TTL boundary.\n */\n since?: Date;\n}\n\nexport const JOB_ERRORS_MESSAGE_MAX_CHARS = 2000;\nexport const JOB_ERRORS_STACK_MAX_CHARS = 8000;\nexport const JOB_ERRORS_MAX_TOP_LIMIT = 1000;\n\nexport interface JobFailureDoc {\n jobId: string;\n jobType: string;\n input: string;\n errorMessage: string;\n errorStack: string;\n logs: string;\n failedAt: Date;\n createdAt: Date;\n /** Normalized `Error.cause` chain beyond the top-level error. OMITTED when empty. */\n causeChain?: Array<{ message: string; name: string }>;\n}\n\nexport interface RecordFailureInput {\n jobId: string;\n jobType: string;\n input: unknown;\n error: unknown;\n failedAt: Date;\n logs?: string;\n}\n\nexport interface ListFailuresOptions {\n jobType?: string;\n // `null` is accepted alongside `undefined` — callers crossing JSON boundaries routinely\n // serialize unset optional dates as `null`. The repo normalizes both to \"no filter applied\".\n fromDate?: Date | null;\n toDate?: Date | null;\n skip?: number;\n limit?: number;\n}\n\nexport const JOB_FAILURES_INPUT_MAX_CHARS = 100_000;\nexport const JOB_FAILURES_MESSAGE_MAX_CHARS = 2000;\nexport const JOB_FAILURES_STACK_MAX_CHARS = 8000;\nexport const JOB_FAILURES_LOGS_MAX_CHARS = 500_000;\nexport const JOB_FAILURES_MAX_LIST_LIMIT = 1000;\n\n/** NFR3 default: 7-day TTL for `job_failures`. Hardcoded for this story; configurable in a later story. */\nexport const JOB_FAILURES_TTL_SECONDS = 7 * 24 * 60 * 60;\n\nexport interface WorkerQueueEntry {\n name: string;\n concurrency: number;\n}\n\nexport interface WorkerDoc {\n workerId: string;\n hostname: string;\n pid: number;\n queues: WorkerQueueEntry[];\n startedAt: Date;\n lastHeartbeatAt: Date;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface RegisterWorkerInput {\n workerId: string;\n hostname: string;\n pid: number;\n queues: WorkerQueueEntry[];\n startedAt: Date;\n}\n\nexport interface UpsertWorkerQueueInput {\n workerId: string;\n queue: WorkerQueueEntry;\n}\n\nexport const WORKERS_DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;\nexport const WORKERS_MIN_HEARTBEAT_INTERVAL_MS = 1_000;\n\n/**\n * Read shape for the `job_configs` collection — what `JobConfigsRepo.getConfig`\n * returns (with persistence timestamps stripped, or synthesized for absent records).\n *\n * Distinct from `JobConfig` (in `src/types/job/unions.ts`), which is the discriminated\n * union of job-definition types (simple / memory-etl / distributed-etl / etc.) used\n * at app-registration time. `JobConfigEntry` is the runtime persistence shape for\n * dashboard-managed per-job-type settings (enabled flag + free-form metadata).\n */\nexport interface JobConfigEntry {\n jobType: string;\n enabled: boolean;\n metadata: Record<string, unknown>;\n}\n\n/**\n * Persisted shape for the `job_configs` collection — the read shape plus\n * MongoDB-managed timestamps. Returned by `JobConfigsRepo.listAll` (every row\n * there is, by definition, persisted, so the timestamps are always real).\n */\nexport interface JobConfigDoc extends JobConfigEntry {\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport const JOB_CONFIGS_DEFAULT_ENABLED = true;\n","import type { AnyBulkWriteOperation, Db, Filter } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport { asBulkUpdateOp, type UpdateSpec } from \"./_types\";\nimport { computeBucketStart } from \"./bucket-utils\";\nimport { isDuplicateKeyError } from \"../mongo-error\";\nimport { getLogger } from \"../../logger\";\nimport {\n IndexDescriptor,\n JobStatsDoc,\n BucketGranularity,\n IncrementStatsInput,\n JOB_STATS_TTL_SECONDS,\n} from \"./types\";\n\nconst COLLECTION = \"job_stats\";\n\nconst GRANULARITIES: readonly BucketGranularity[] = [\"hour\", \"day\"];\n\nexport const MAX_GET_STATS_RESULTS = 10_000;\n\nexport class JobStatsRepo extends Repository<JobStatsDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { jobType: 1, bucketStart: 1, granularity: 1 },\n options: { unique: true, name: \"jobType_bucketStart_granularity_unique\" },\n },\n // NOTE (TTL migration): MongoDB's `createIndex` is a no-op when an index with the same\n // key spec already exists — even if `expireAfterSeconds` differs. Changing\n // `JOB_STATS_TTL_SECONDS` will NOT propagate to a running deployment; apply a `collMod`\n // (or drop-and-recreate this index) manually at deploy time.\n {\n keys: { bucketStart: 1 },\n options: {\n expireAfterSeconds: JOB_STATS_TTL_SECONDS,\n name: \"bucketStart_ttl\",\n },\n },\n ];\n }\n\n /**\n * Builds the bulk write operations for a stats increment WITHOUT executing them.\n * Returns TWO ops — one for the hour bucket, one for the day bucket — matching\n * `incrementStats`'s direct-call shape bit-for-bit. Intended to be pushed through\n * a `BatchWriter` by the event-hooks layer (Story 1.7). Throws synchronously on\n * invalid `durationMs`; otherwise no side effects.\n *\n * NOTE: `now` is captured at builder invocation time, NOT at BatchWriter flush\n * time — `createdAt`, `updatedAt`, and `bucketStart` all reflect the event's\n * wall-clock, not the flush wall-clock. Aggregations are therefore correctly\n * attributed to the hour/day the event occurred, even if the batch flush is\n * delayed by the buffer timer.\n */\n toIncrementStatsOps(\n jobType: string,\n input: IncrementStatsInput,\n ): AnyBulkWriteOperation<JobStatsDoc>[] {\n this.validateDurationMs(input.durationMs, \"toIncrementStatsOps\");\n const now = new Date();\n return GRANULARITIES.map((g) => this.buildBucketOp(jobType, g, now, input));\n }\n\n async incrementStats(\n jobType: string,\n input: IncrementStatsInput,\n ): Promise<void> {\n this.validateDurationMs(input.durationMs, \"incrementStats\");\n\n const now = new Date();\n const results = await Promise.allSettled(\n GRANULARITIES.map((g) => this.upsertBucket(jobType, g, now, input)),\n );\n\n const failures = results\n .map((r, i) => ({ result: r, granularity: GRANULARITIES[i] }))\n .filter((x): x is { result: PromiseRejectedResult; granularity: BucketGranularity } =>\n x.result.status === \"rejected\",\n );\n\n if (failures.length === 0) return;\n\n const logger = getLogger();\n for (const { result, granularity } of failures) {\n logger.error(\n { jobType, granularity, err: result.reason },\n \"JobStatsRepo.incrementStats: bucket upsert failed\",\n );\n }\n\n if (failures.length === results.length) {\n throw failures[0].result.reason;\n }\n }\n\n async getStats(\n jobType: string,\n fromDate: Date,\n toDate: Date,\n granularity: BucketGranularity,\n ): Promise<JobStatsDoc[]> {\n if (fromDate > toDate) {\n throw new Error(\n `JobStatsRepo.getStats: fromDate (${fromDate.toISOString()}) must be <= toDate (${toDate.toISOString()})`,\n );\n }\n\n const filter: Filter<JobStatsDoc> = {\n jobType,\n granularity,\n bucketStart: { $gte: fromDate, $lte: toDate },\n };\n\n return await this.collection\n .find(filter)\n .sort({ bucketStart: 1 })\n .limit(MAX_GET_STATS_RESULTS)\n .toArray();\n }\n\n /**\n * Fetches every stats document in the given range + granularity, across ALL\n * jobTypes. Intended for dashboard views that need a cross-jobType overview\n * (e.g., \"7-day health per job type\") without a prior enumeration step.\n *\n * Result order: `bucketStart` ascending, then `jobType` ascending. Caller is\n * responsible for grouping by jobType client-side if desired.\n *\n * Applies the same `MAX_GET_STATS_RESULTS` cap as `getStats` — a single query\n * across all jobTypes × buckets × granularity is bounded, but the cap keeps\n * the method safe under accidental wide-range queries.\n */\n async getStatsAllJobTypes(\n fromDate: Date,\n toDate: Date,\n granularity: BucketGranularity,\n ): Promise<JobStatsDoc[]> {\n if (fromDate > toDate) {\n throw new Error(\n `JobStatsRepo.getStatsAllJobTypes: fromDate (${fromDate.toISOString()}) must be <= toDate (${toDate.toISOString()})`,\n );\n }\n\n const filter: Filter<JobStatsDoc> = {\n granularity,\n bucketStart: { $gte: fromDate, $lte: toDate },\n };\n\n return await this.collection\n .find(filter)\n .sort({ bucketStart: 1, jobType: 1 })\n .limit(MAX_GET_STATS_RESULTS)\n .toArray();\n }\n\n private async upsertBucket(\n jobType: string,\n granularity: BucketGranularity,\n now: Date,\n input: IncrementStatsInput,\n ): Promise<void> {\n const { filter, update } = this.buildBucketPayload(jobType, granularity, now, input);\n\n try {\n await this.collection.updateOne(filter, update, { upsert: true });\n } catch (err) {\n // E11000 can occur on upsert when two writers both miss the doc and race to insert.\n // Retry once: the doc now exists, so the second attempt hits the $inc path.\n if (isDuplicateKeyError(err)) {\n await this.collection.updateOne(filter, update, { upsert: true });\n return;\n }\n throw err;\n }\n }\n\n private buildBucketOp(\n jobType: string,\n granularity: BucketGranularity,\n now: Date,\n input: IncrementStatsInput,\n ): AnyBulkWriteOperation<JobStatsDoc> {\n const { filter, update } = this.buildBucketPayload(jobType, granularity, now, input);\n return asBulkUpdateOp<JobStatsDoc>({\n updateOne: { filter, update, upsert: true },\n });\n }\n\n private buildBucketPayload(\n jobType: string,\n granularity: BucketGranularity,\n now: Date,\n input: IncrementStatsInput,\n ): { filter: Filter<JobStatsDoc>; update: UpdateSpec<JobStatsDoc> } {\n const bucketStart = computeBucketStart(now, granularity);\n\n const filter: Filter<JobStatsDoc> = { jobType, bucketStart, granularity };\n const update: UpdateSpec<JobStatsDoc> = {\n $inc: {\n count: 1,\n success: input.success ? 1 : 0,\n failed: input.success ? 0 : 1,\n totalDurationMs: input.durationMs ?? 0,\n },\n $setOnInsert: {\n jobType,\n bucketStart,\n granularity,\n createdAt: now,\n },\n $set: {\n updatedAt: now,\n },\n };\n\n return { filter, update };\n }\n\n private validateDurationMs(\n durationMs: number | undefined,\n methodName: string,\n ): void {\n if (durationMs === undefined) return;\n if (!Number.isFinite(durationMs) || durationMs < 0) {\n throw new Error(\n `JobStatsRepo.${methodName}: durationMs must be a finite non-negative number (got ${durationMs})`,\n );\n }\n }\n}\n","import { createHash } from \"node:crypto\";\n\nexport const SIGNATURE_LENGTH = 16;\nexport const STACK_TOP_FRAMES = 5;\n\nconst ISO_TIMESTAMP_REGEX = /\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z?/g;\nconst UUID_REGEX = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst HEX_ADDRESS_REGEX = /\\b0x[0-9a-f]+\\b/gi;\nconst LONG_DIGIT_RUN_REGEX = /\\b\\d{5,}\\b/g;\n\nconst STACK_FRAME_PREFIX = \" at \";\nconst STACK_LINE_COL_SUFFIX_REGEX = /:\\d+:\\d+(\\)?)$/;\n\nfunction normalizeMessage(msg: string): string {\n return msg\n .replace(ISO_TIMESTAMP_REGEX, \"<ts>\")\n .replace(UUID_REGEX, \"<uuid>\")\n .replace(HEX_ADDRESS_REGEX, \"<hex>\")\n .replace(LONG_DIGIT_RUN_REGEX, \"<n>\");\n}\n\nfunction normalizeStack(stack: string): string {\n const frames: string[] = [];\n for (const rawLine of stack.split(\"\\n\")) {\n if (!rawLine.startsWith(STACK_FRAME_PREFIX)) continue;\n const slashed = rawLine.replace(/\\\\/g, \"/\");\n const stripped = slashed.replace(STACK_LINE_COL_SUFFIX_REGEX, \"$1\");\n frames.push(stripped);\n if (frames.length === STACK_TOP_FRAMES) break;\n }\n return frames.join(\"\\n\");\n}\n\nexport function errorSignature(err: unknown): string {\n let combined: string;\n if (err instanceof Error) {\n const name = err.name ?? \"Error\";\n const message = normalizeMessage(err.message ?? \"\");\n const stack = err.stack ? normalizeStack(err.stack) : \"\";\n combined = `${name}\\n${message}\\n${stack}`;\n } else {\n combined = normalizeMessage(String(err));\n }\n return createHash(\"sha256\").update(combined).digest(\"hex\").slice(0, SIGNATURE_LENGTH);\n}\n","import { getLogger } from \"../logger\";\n\nexport interface CauseChainEntry {\n message: string;\n name: string;\n}\n\nexport interface ExtractedError {\n /** Top-level message + \"\\nCaused by: \" + inner.message chain, truncated after joining. */\n message: string;\n /** Top-level stack trace only, truncated. Inner stacks are omitted to bound payload. */\n stack: string;\n /** Normalized chain beyond the top level. Empty array when the top Error has no `cause`. */\n causeChain: CauseChainEntry[];\n}\n\nexport interface ExtractMessageAndStackOptions {\n messageMaxChars: number;\n stackMaxChars: number;\n /** Depth cap to guard against cycles and pathological nesting. Default 5. */\n maxCauseDepth?: number;\n}\n\nconst DEFAULT_MAX_CAUSE_DEPTH = 5;\nconst NAME_MAX_CHARS = 64;\nconst CHAIN_SEPARATOR = \"\\nCaused by: \";\n\nfunction truncate(value: string, max: number): string {\n return value.length <= max ? value : value.slice(0, max);\n}\n\nfunction safeGet<T>(getter: () => T, fallback: T): T {\n try {\n return getter();\n } catch {\n return fallback;\n }\n}\n\nfunction isErrorLike(value: unknown): value is { message: unknown; name?: unknown } {\n if (typeof value !== \"object\" || value === null) return false;\n return safeGet(() => \"message\" in value, false);\n}\n\nfunction normalizeEntry(value: unknown, messageMaxChars: number): CauseChainEntry {\n const entryMax = Math.max(1, Math.floor(messageMaxChars / 2));\n if (isErrorLike(value)) {\n const rawMessage = safeGet<unknown>(() => (value as { message: unknown }).message, undefined);\n const rawName = safeGet<unknown>(() => (value as { name?: unknown }).name, undefined);\n const message = truncate(\n typeof rawMessage === \"string\" ? rawMessage : safeGet(() => String(rawMessage), \"\"),\n entryMax,\n );\n const name = truncate(typeof rawName === \"string\" ? rawName : \"Error\", NAME_MAX_CHARS);\n return { message, name };\n }\n return {\n message: truncate(safeGet(() => String(value), \"\"), entryMax),\n name: \"NonError\",\n };\n}\n\nfunction walkCauseChain(\n top: { cause?: unknown },\n maxDepth: number,\n messageMaxChars: number,\n): { chain: CauseChainEntry[]; truncated: boolean } {\n const chain: CauseChainEntry[] = [];\n const seen = new WeakSet<object>();\n if (typeof top === \"object\" && top !== null) seen.add(top);\n\n let current: unknown = safeGet<unknown>(() => top.cause, undefined);\n let truncated = false;\n\n while (current !== undefined && current !== null) {\n if (chain.length >= maxDepth) {\n truncated = true;\n break;\n }\n if (typeof current === \"object\" && current !== null) {\n if (seen.has(current)) {\n truncated = true;\n break;\n }\n seen.add(current);\n }\n\n chain.push(normalizeEntry(current, messageMaxChars));\n\n const hasCause =\n typeof current === \"object\" &&\n current !== null &&\n safeGet(() => \"cause\" in (current as object), false);\n if (hasCause) {\n current = safeGet(() => (current as { cause?: unknown }).cause, undefined);\n } else {\n break;\n }\n }\n\n return { chain, truncated };\n}\n\nexport function extractMessageAndStack(\n err: unknown,\n options: ExtractMessageAndStackOptions,\n): ExtractedError {\n const { messageMaxChars, stackMaxChars } = options;\n const rawMaxDepth = options.maxCauseDepth ?? DEFAULT_MAX_CAUSE_DEPTH;\n const maxCauseDepth = Number.isFinite(rawMaxDepth) && rawMaxDepth >= 1\n ? Math.floor(rawMaxDepth)\n : DEFAULT_MAX_CAUSE_DEPTH;\n\n if (!(err instanceof Error)) {\n return {\n message: truncate(safeGet(() => String(err), \"\"), messageMaxChars),\n stack: \"\",\n causeChain: [],\n };\n }\n\n const topMessage = safeGet<string>(() => {\n const m = err.message;\n return typeof m === \"string\" ? m : String(m ?? \"\");\n }, \"\");\n const topStack = safeGet<string>(() => {\n const s = err.stack;\n return typeof s === \"string\" ? s : String(s ?? \"\");\n }, \"\");\n\n const { chain, truncated } = walkCauseChain(\n err as { cause?: unknown },\n maxCauseDepth,\n messageMaxChars,\n );\n\n if (truncated) {\n getLogger().warn(\n { topMessage: truncate(topMessage, 120) },\n \"extractMessageAndStack: maxCauseDepth reached, truncating cause chain.\",\n );\n }\n\n let joinedMessage = topMessage;\n for (const entry of chain) {\n joinedMessage += `${CHAIN_SEPARATOR}${entry.message}`;\n }\n\n return {\n message: truncate(joinedMessage, messageMaxChars),\n stack: truncate(topStack, stackMaxChars),\n causeChain: chain,\n };\n}\n","import type { AnyBulkWriteOperation, Db, Filter } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport { asBulkUpdateOp, type UpdateSpec } from \"./_types\";\nimport { errorSignature } from \"../../monitoring/error-signature\";\nimport { extractMessageAndStack } from \"../../monitoring/error-extraction\";\nimport { isDuplicateKeyError } from \"../mongo-error\";\nimport {\n IndexDescriptor,\n JobErrorDoc,\n RecordOccurrenceInput,\n TopErrorsOptions,\n JOB_ERRORS_MESSAGE_MAX_CHARS,\n JOB_ERRORS_STACK_MAX_CHARS,\n JOB_ERRORS_MAX_TOP_LIMIT,\n} from \"./types\";\n\nconst COLLECTION = \"job_errors\";\n\nexport class JobErrorsRepo extends Repository<JobErrorDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { signature: 1 },\n options: { unique: true, name: \"signature_unique\" },\n },\n {\n keys: { lastSeenAt: -1 },\n options: { name: \"lastSeenAt_desc\" },\n },\n {\n keys: { jobType: 1, count: -1 },\n options: { name: \"jobType_count_desc\" },\n },\n {\n keys: { count: -1 },\n options: { name: \"count_desc\" },\n },\n ];\n }\n\n /**\n * Records one occurrence of an error, deduplicated by its stable signature.\n *\n * Invariant: `jobType` is set **only on insert** (`$setOnInsert`). Once a signature\n * is first observed under jobType A, subsequent occurrences under jobType B do NOT\n * overwrite the stored value. The signature is the identity of the error; a signature\n * appearing across multiple job types indicates shared infrastructure failure (e.g.\n * ECONNRESET from the shared DB) and is worth surfacing on the first-observed job.\n */\n async recordOccurrence(input: RecordOccurrenceInput): Promise<void> {\n const { filter, update } = this.buildRecordOccurrencePayload(input);\n\n try {\n await this.collection.updateOne(filter, update, { upsert: true });\n } catch (err) {\n if (isDuplicateKeyError(err)) {\n await this.collection.updateOne(filter, update, { upsert: true });\n return;\n }\n throw err;\n }\n }\n\n /**\n * Builds the bulk write operation for `recordOccurrence` WITHOUT executing it.\n * Returns a single upsert op matching the direct-call shape bit-for-bit.\n *\n * NOTE: `now` is captured at builder invocation time, NOT at BatchWriter flush\n * time — firstSeenAt / lastSeenAt reflect the event's wall-clock, not the\n * flush wall-clock. This preserves the semantic that these timestamps track\n * the error's occurrence, not the observability pipeline's delay.\n */\n toRecordOccurrenceOp(input: RecordOccurrenceInput): AnyBulkWriteOperation<JobErrorDoc> {\n const { filter, update } = this.buildRecordOccurrencePayload(input);\n return asBulkUpdateOp<JobErrorDoc>({\n updateOne: { filter, update, upsert: true },\n });\n }\n\n private buildRecordOccurrencePayload(\n input: RecordOccurrenceInput,\n ): { filter: Filter<JobErrorDoc>; update: UpdateSpec<JobErrorDoc> } {\n const signature = errorSignature(input.error);\n const { message, stack, causeChain } = extractMessageAndStack(input.error, {\n messageMaxChars: JOB_ERRORS_MESSAGE_MAX_CHARS,\n stackMaxChars: JOB_ERRORS_STACK_MAX_CHARS,\n });\n const now = new Date();\n\n const filter: Filter<JobErrorDoc> = { signature };\n const update: UpdateSpec<JobErrorDoc> = {\n $inc: { count: 1 },\n $setOnInsert: {\n signature,\n jobType: input.jobType,\n message,\n stack,\n firstSeenAt: now,\n createdAt: now,\n ...(causeChain.length > 0 ? { causeChain } : {}),\n },\n $set: {\n lastSeenAt: now,\n updatedAt: now,\n },\n };\n\n return { filter, update };\n }\n\n async topErrors(options: TopErrorsOptions): Promise<JobErrorDoc[]> {\n if (!(options.limit > 0) || options.limit > JOB_ERRORS_MAX_TOP_LIMIT) {\n throw new Error(\n `JobErrorsRepo.topErrors: limit must be in (0, ${JOB_ERRORS_MAX_TOP_LIMIT}]`,\n );\n }\n\n const filter: Filter<JobErrorDoc> = {\n ...(options.jobType !== undefined ? { jobType: options.jobType } : {}),\n ...(options.since !== undefined ? { lastSeenAt: { $gte: options.since } } : {}),\n };\n\n return await this.collection\n .find(filter)\n .sort({ count: -1 })\n .limit(options.limit)\n .toArray();\n }\n\n}\n","import type { AnyBulkWriteOperation, Db, Filter } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport { asBulkInsertOp, type BulkInsertOp } from \"./_types\";\nimport { extractMessageAndStack } from \"../../monitoring/error-extraction\";\nimport {\n IndexDescriptor,\n JobFailureDoc,\n RecordFailureInput,\n ListFailuresOptions,\n JOB_FAILURES_INPUT_MAX_CHARS,\n JOB_FAILURES_MESSAGE_MAX_CHARS,\n JOB_FAILURES_STACK_MAX_CHARS,\n JOB_FAILURES_LOGS_MAX_CHARS,\n JOB_FAILURES_MAX_LIST_LIMIT,\n JOB_FAILURES_TTL_SECONDS,\n} from \"./types\";\n\nconst COLLECTION = \"job_failures\";\n\nconst DEFAULT_LIST_LIMIT = 50;\n\nexport class JobFailuresRepo extends Repository<JobFailureDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { jobId: 1, failedAt: -1 },\n options: { name: \"jobId_failedAt_desc\" },\n },\n {\n keys: { jobType: 1, failedAt: -1 },\n options: { name: \"jobType_failedAt_desc\" },\n },\n // NOTE (TTL migration): MongoDB's `createIndex` is a no-op when an index with the same\n // key spec already exists — even if `expireAfterSeconds` differs. Changing\n // `JOB_FAILURES_TTL_SECONDS` will NOT propagate to a running deployment; apply a `collMod`\n // (or drop-and-recreate this index) manually at deploy time.\n {\n keys: { failedAt: 1 },\n options: {\n expireAfterSeconds: JOB_FAILURES_TTL_SECONDS,\n name: \"failedAt_ttl\",\n },\n },\n ];\n }\n\n async recordFailure(input: RecordFailureInput): Promise<void> {\n const doc = this.buildFailureDoc(input);\n await this.collection.insertOne(doc);\n }\n\n /**\n * Builds the bulk write operation for `recordFailure` WITHOUT executing it.\n * Returns a single insertOne op matching the direct-call document shape\n * bit-for-bit. Intended for the event-hooks layer (Story 1.7) to push through\n * a `BatchWriter`. Throws synchronously on invalid input using the same error\n * messages as `recordFailure`.\n */\n toRecordFailureOp(input: RecordFailureInput): AnyBulkWriteOperation<JobFailureDoc> {\n const doc = this.buildFailureDoc(input);\n const op: BulkInsertOp<JobFailureDoc> = { insertOne: { document: doc } };\n return asBulkInsertOp<JobFailureDoc>(op);\n }\n\n private buildFailureDoc(input: RecordFailureInput): JobFailureDoc {\n this.validateFailureInput(input);\n\n const serializedInput = this.serializeInputValue(input.input).slice(\n 0,\n JOB_FAILURES_INPUT_MAX_CHARS,\n );\n const { message, stack, causeChain } = extractMessageAndStack(input.error, {\n messageMaxChars: JOB_FAILURES_MESSAGE_MAX_CHARS,\n stackMaxChars: JOB_FAILURES_STACK_MAX_CHARS,\n });\n const logs = this.capLogs(input.logs);\n\n const doc: JobFailureDoc = {\n jobId: input.jobId,\n jobType: input.jobType,\n input: serializedInput,\n errorMessage: message,\n errorStack: stack,\n logs,\n failedAt: input.failedAt,\n createdAt: new Date(),\n };\n if (causeChain.length > 0) {\n doc.causeChain = causeChain;\n }\n return doc;\n }\n\n private validateFailureInput(input: RecordFailureInput): void {\n if (typeof input.jobId !== \"string\" || input.jobId.length === 0) {\n throw new Error(\"JobFailuresRepo.recordFailure: jobId must be a non-empty string\");\n }\n if (typeof input.jobType !== \"string\" || input.jobType.length === 0) {\n throw new Error(\"JobFailuresRepo.recordFailure: jobType must be a non-empty string\");\n }\n if (!(input.failedAt instanceof Date) || Number.isNaN(input.failedAt.getTime())) {\n throw new Error(\"JobFailuresRepo.recordFailure: failedAt must be a valid Date\");\n }\n }\n\n async listFailures(\n options: ListFailuresOptions,\n ): Promise<{ items: JobFailureDoc[]; total: number }> {\n if (options.limit !== undefined) {\n if (\n !Number.isInteger(options.limit) ||\n options.limit <= 0 ||\n options.limit > JOB_FAILURES_MAX_LIST_LIMIT\n ) {\n throw new Error(\n `JobFailuresRepo.listFailures: limit must be an integer in (0, ${JOB_FAILURES_MAX_LIST_LIMIT}]`,\n );\n }\n }\n if (options.skip !== undefined) {\n if (!Number.isInteger(options.skip) || options.skip < 0) {\n throw new Error(\n \"JobFailuresRepo.listFailures: skip must be a non-negative integer\",\n );\n }\n }\n\n // Normalize null → undefined; callers crossing JSON boundaries routinely send null for unset fields.\n const fromDate = options.fromDate ?? undefined;\n const toDate = options.toDate ?? undefined;\n if (fromDate !== undefined && (!(fromDate instanceof Date) || Number.isNaN(fromDate.getTime()))) {\n throw new Error(\"JobFailuresRepo.listFailures: fromDate must be a valid Date\");\n }\n if (toDate !== undefined && (!(toDate instanceof Date) || Number.isNaN(toDate.getTime()))) {\n throw new Error(\"JobFailuresRepo.listFailures: toDate must be a valid Date\");\n }\n if (fromDate !== undefined && toDate !== undefined && fromDate.getTime() > toDate.getTime()) {\n throw new Error(\"JobFailuresRepo.listFailures: fromDate must be <= toDate\");\n }\n\n const skip = options.skip ?? 0;\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n\n const filter: Filter<JobFailureDoc> = {};\n if (options.jobType !== undefined) {\n filter.jobType = options.jobType;\n }\n if (fromDate !== undefined || toDate !== undefined) {\n const range: { $gte?: Date; $lte?: Date } = {};\n if (fromDate !== undefined) range.$gte = fromDate;\n if (toDate !== undefined) range.$lte = toDate;\n filter.failedAt = range;\n }\n\n const [items, total] = await Promise.all([\n this.collection\n .find(filter)\n .sort({ failedAt: -1 })\n .skip(skip)\n .limit(limit)\n .toArray(),\n this.collection.countDocuments(filter),\n ]);\n\n return { items, total };\n }\n\n async getFailure(jobId: string): Promise<JobFailureDoc | null> {\n if (typeof jobId !== \"string\" || jobId.length === 0) {\n throw new Error(\"JobFailuresRepo.getFailure: jobId must be a non-empty string\");\n }\n const filter: Filter<JobFailureDoc> = { jobId };\n return await this.collection.findOne(filter, { sort: { failedAt: -1 } });\n }\n\n private serializeInputValue(input: unknown): string {\n if (input === undefined) return \"undefined\";\n try {\n // Custom replacer preserves Map and Set (JSON.stringify otherwise serializes both as \"{}\").\n // Symbols are still silently dropped per ECMA-262 — that behavior is intentional.\n const result = JSON.stringify(input, (_key, value) => {\n if (value instanceof Map) return { __type: \"Map\", entries: Array.from(value.entries()) };\n if (value instanceof Set) return { __type: \"Set\", values: Array.from(value.values()) };\n return value;\n });\n // JSON.stringify can return undefined for e.g. functions or raw symbols passed as root value.\n if (result === undefined) return \"undefined\";\n return result;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return `[input serialization failed: ${msg}]`;\n }\n }\n\n private capLogs(logs: string | undefined): string {\n if (logs === undefined) return \"\";\n if (logs.length <= JOB_FAILURES_LOGS_MAX_CHARS) return logs;\n\n const originalLen = logs.length;\n let tailLen = JOB_FAILURES_LOGS_MAX_CHARS;\n let marker = \"\";\n for (let i = 0; i < 3; i++) {\n marker = `[truncated — showing last ${tailLen} of ${originalLen} chars]\\n`;\n if (marker.length >= JOB_FAILURES_LOGS_MAX_CHARS) {\n return marker.slice(0, JOB_FAILURES_LOGS_MAX_CHARS);\n }\n tailLen = JOB_FAILURES_LOGS_MAX_CHARS - marker.length;\n }\n return marker + logs.slice(-tailLen);\n }\n}\n","import type { Db, Filter, AnyBulkWriteOperation } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport { asBulkUpdateOp, type UpdateSpec } from \"./_types\";\nimport {\n IndexDescriptor,\n JobConfigEntry,\n JobConfigDoc,\n JOB_CONFIGS_DEFAULT_ENABLED,\n} from \"./types\";\nimport type { JobConfig } from \"../../types\";\n\nconst COLLECTION = \"job_configs\";\n\nexport class JobConfigsRepo extends Repository<JobConfigDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { jobType: 1 },\n options: { unique: true, name: \"jobType_unique\" },\n },\n ];\n }\n\n async getConfig(jobType: string): Promise<JobConfigEntry> {\n this.validateJobType(jobType, \"JobConfigsRepo.getConfig\");\n const filter: Filter<JobConfigDoc> = { jobType };\n const doc = await this.collection.findOne(filter);\n\n if (!doc) {\n return {\n jobType,\n enabled: JOB_CONFIGS_DEFAULT_ENABLED,\n metadata: {},\n };\n }\n\n return {\n jobType: doc.jobType,\n enabled: doc.enabled,\n metadata: doc.metadata,\n };\n }\n\n async setEnabled(jobType: string, enabled: boolean): Promise<void> {\n this.validateJobType(jobType, \"JobConfigsRepo.setEnabled\");\n if (typeof enabled !== \"boolean\") {\n throw new Error(\"JobConfigsRepo.setEnabled: enabled must be a boolean\");\n }\n\n const filter: Filter<JobConfigDoc> = { jobType };\n const update: UpdateSpec<JobConfigDoc> = {\n $set: { enabled },\n $setOnInsert: {\n jobType,\n metadata: {},\n createdAt: new Date(),\n },\n $currentDate: { updatedAt: true },\n };\n const result = await this.collection.updateOne(filter, update, { upsert: true });\n\n if (result.matchedCount === 0 && (result.upsertedCount ?? 0) === 0) {\n throw new Error(\n `JobConfigsRepo.setEnabled: updateOne acknowledged no write for jobType=${jobType}`,\n );\n }\n }\n\n async listAll(): Promise<JobConfigDoc[]> {\n return await this.collection\n .find({})\n .sort({ jobType: 1 })\n .toArray();\n }\n\n async isEnabled(jobType: string): Promise<boolean> {\n this.validateJobType(jobType, \"JobConfigsRepo.isEnabled\");\n const filter: Filter<JobConfigDoc> = { jobType };\n const doc = await this.collection.findOne(filter, {\n projection: { enabled: 1, _id: 0 },\n });\n\n if (!doc) {\n return JOB_CONFIGS_DEFAULT_ENABLED;\n }\n return doc.enabled;\n }\n\n /**\n * Idempotent bulk upsert of every registered jobType + its metadata\n * projection (Story 5.1 D5.1-a). Called once per `RavenflowApp` instance\n * inside `initializeMonitoring()`.\n *\n * Semantics:\n * - `$setOnInsert { enabled: true, createdAt }` — the enabled flag is\n * set to the default ONLY on initial insert. An operator-toggled\n * `enabled: false` is NEVER overwritten by a subsequent boot.\n * - `$set { metadata }` — metadata is always refreshed from code config\n * (code is source of truth for non-user-writable fields).\n * - `$currentDate { updatedAt }` — timestamp bumps every call.\n *\n * Metadata projection (narrow + stable — see D5.1-h):\n * - `type`: jobConfig.type (always present in the discriminated union).\n * - `queue`: jobConfig.queue ?? null (null = inherits defaultQueue).\n * - `cron`: jobConfig.cron if present (token-bucket only today), else null.\n *\n * Additional type-specific fields (token-bucket's `rate`, distributed-etl's\n * `steps`) are NOT extracted — BSON can't serialize the function references\n * they contain, and the dashboard surface consumes only `type`/`queue`/`cron`\n * in V1. Future stories can widen this projection without a schema change.\n *\n * Failure mode: `bulkWrite` is the only I/O. Empty input short-circuits\n * because the driver rejects empty ops arrays.\n */\n async ensureEntries(\n jobs: Record<string, JobConfig>,\n ): Promise<void> {\n // Null / non-object input — return instead of throwing Object.entries(null)\n // TypeError (Story 5.1 code review P7). Preserves the \"non-fatal at boot\"\n // contract the caller (app.ts::initializeMonitoring) relies on.\n if (jobs === null || typeof jobs !== \"object\") return;\n\n const entries = Object.entries(jobs);\n if (entries.length === 0) return;\n\n // Validate-first pass (Story 5.1 code review P8). A bad jobType throws\n // BEFORE any bulk op is constructed so the error is localized and no\n // partial state is built up.\n for (const [jobType] of entries) {\n this.validateJobType(jobType, \"JobConfigsRepo.ensureEntries\");\n }\n\n const ops: Array<AnyBulkWriteOperation<JobConfigDoc>> = entries.map(\n ([jobType, cfg]) => {\n const cron =\n \"cron\" in cfg && typeof cfg.cron === \"string\" && cfg.cron.length > 0\n ? cfg.cron\n : null;\n const metadata: Record<string, unknown> = {\n type: cfg.type,\n queue: cfg.queue ?? null,\n cron,\n };\n const update: UpdateSpec<JobConfigDoc> = {\n $setOnInsert: {\n jobType,\n enabled: JOB_CONFIGS_DEFAULT_ENABLED,\n createdAt: new Date(),\n },\n $set: { metadata },\n $currentDate: { updatedAt: true },\n };\n return asBulkUpdateOp<JobConfigDoc>({\n updateOne: {\n filter: { jobType } as Filter<JobConfigDoc>,\n update,\n upsert: true,\n },\n });\n },\n );\n\n await this.collection.bulkWrite(ops, { ordered: false });\n }\n\n private validateJobType(jobType: string, ctx: string): void {\n if (typeof jobType !== \"string\" || jobType.length === 0) {\n throw new Error(`${ctx}: jobType must be a non-empty string`);\n }\n // Reject Mongo-reserved prefix/operator characters — `$` begins a\n // Mongo operator, `.` is a path separator. A jobType containing\n // either one could cross the filter/update-document boundary and\n // rewrite an unintended field (Story 5.1 code review P9).\n if (jobType.includes(\".\") || jobType.includes(\"$\")) {\n throw new Error(\n `${ctx}: jobType cannot contain '.' or '$' (Mongo-reserved characters)`,\n );\n }\n }\n}\n","import * as os from \"node:os\";\nimport * as crypto from \"node:crypto\";\nimport type { WorkersRepo } from \"../mongo/repositories/workers.repo\";\nimport type { WorkerQueueEntry } from \"../mongo/repositories/types\";\nimport {\n WORKERS_DEFAULT_HEARTBEAT_INTERVAL_MS,\n WORKERS_MIN_HEARTBEAT_INTERVAL_MS,\n} from \"../mongo/repositories/types\";\nimport { getLogger } from \"../logger\";\n\nexport interface WorkerRegistryOptions {\n heartbeatIntervalMs?: number;\n /** @internal Injected for testability; real callers should rely on the default. */\n hostnameProvider?: () => string;\n /** @internal Injected for testability; real callers should rely on the default. */\n pidProvider?: () => number;\n /** @internal Injected for testability; real callers should rely on the default. */\n workerIdProvider?: () => string;\n /** @internal Injected for testability; real callers should rely on the default. */\n clock?: () => Date;\n}\n\nexport class WorkerRegistry {\n private readonly repo: WorkersRepo;\n private readonly heartbeatIntervalMs: number;\n private readonly hostname: string;\n private readonly pid: number;\n private readonly _workerId: string;\n private readonly startedAt: Date;\n private readonly clock: () => Date;\n private timer: ReturnType<typeof setInterval> | null = null;\n private started = false;\n private registered = false;\n\n constructor(repo: WorkersRepo, options: WorkerRegistryOptions = {}) {\n WorkerRegistry.validateHeartbeatInterval(options.heartbeatIntervalMs);\n\n this.repo = repo;\n this.heartbeatIntervalMs =\n options.heartbeatIntervalMs ?? WORKERS_DEFAULT_HEARTBEAT_INTERVAL_MS;\n\n this.hostname = options.hostnameProvider ? options.hostnameProvider() : os.hostname();\n this.pid = options.pidProvider ? options.pidProvider() : process.pid;\n this._workerId = options.workerIdProvider\n ? options.workerIdProvider()\n : `${this.hostname}:${this.pid}:${crypto.randomUUID()}`;\n this.clock = options.clock ?? (() => new Date());\n this.startedAt = this.clock();\n }\n\n get workerId(): string {\n return this._workerId;\n }\n\n async registerQueue(queue: WorkerQueueEntry): Promise<void> {\n WorkerRegistry.validateQueueEntry(queue);\n\n if (!this.registered) {\n await this.repo.registerWorker({\n workerId: this._workerId,\n hostname: this.hostname,\n pid: this.pid,\n queues: [queue],\n startedAt: this.startedAt,\n });\n this.registered = true;\n return;\n }\n\n await this.repo.addQueue({\n workerId: this._workerId,\n queue,\n });\n }\n\n async start(): Promise<void> {\n if (this.started) return;\n if (!this.registered) {\n throw new Error(\n \"WorkerRegistry.start: call registerQueue at least once before start\",\n );\n }\n\n this.timer = setInterval(() => {\n void this.doHeartbeat();\n }, this.heartbeatIntervalMs);\n this.timer.unref();\n this.started = true;\n }\n\n async stop(): Promise<void> {\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.started = false;\n\n if (!this.registered) return;\n\n try {\n await this.repo.deregisterWorker(this._workerId);\n } catch (err) {\n getLogger().warn(\n { err, workerId: this._workerId },\n \"[worker-registry] deregister failed on stop\",\n );\n }\n this.registered = false;\n }\n\n private async doHeartbeat(): Promise<void> {\n try {\n await this.repo.updateHeartbeat(this._workerId);\n } catch (err) {\n getLogger().warn(\n { err, workerId: this._workerId },\n \"[worker-registry] heartbeat failed — will retry next tick\",\n );\n }\n }\n\n private static validateHeartbeatInterval(value: number | undefined | null): void {\n if (value == null) return;\n const MAX_HEARTBEAT_INTERVAL_MS = 24 * 60 * 60 * 1000;\n if (\n !Number.isInteger(value) ||\n value < WORKERS_MIN_HEARTBEAT_INTERVAL_MS ||\n value > MAX_HEARTBEAT_INTERVAL_MS\n ) {\n throw new Error(\n `[ravenflow] WorkerRegistry heartbeatIntervalMs must be an integer in [${WORKERS_MIN_HEARTBEAT_INTERVAL_MS}, ${MAX_HEARTBEAT_INTERVAL_MS}], got ${value}`,\n );\n }\n }\n\n private static validateQueueEntry(q: WorkerQueueEntry): void {\n if (q === null || typeof q !== \"object\") {\n throw new Error(\"WorkerRegistry.registerQueue: queue must be an object\");\n }\n if (typeof q.name !== \"string\" || q.name.length === 0) {\n throw new Error(\n \"WorkerRegistry.registerQueue: queue.name must be a non-empty string\",\n );\n }\n if (!Number.isInteger(q.concurrency) || q.concurrency <= 0) {\n throw new Error(\n \"WorkerRegistry.registerQueue: queue.concurrency must be a positive integer\",\n );\n }\n }\n}\n","export const DEFAULT_FLUSH_INTERVAL_MS = 30_000;\nexport const MIN_BUFFER_SIZE = 1000;\nexport const MAX_BUFFER_SIZE = 5000;\n\n/** Multiplier over `maxBufferSize` that caps memory growth under sustained flush failures. */\nexport const HARD_CAP_MULTIPLIER = 10;\n\n/** Backoff delays (ms) between drain retry attempts when the final flush fails. */\nexport const DRAIN_RETRY_BACKOFFS_MS = [0, 1000, 2000, 4000] as const;\n\nexport interface BatchWriterConfig {\n /** Max ops buffered before a size-triggered flush. Must be in [1000, 5000]. */\n maxBufferSize: number;\n /** Interval between time-triggered flushes in ms. Default 30000. Also used as `maxTimeMS` on bulkWrite. */\n flushIntervalMs?: number;\n}\n","import {\n MongoBulkWriteError,\n type AnyBulkWriteOperation,\n type Collection,\n type Document,\n} from \"mongodb\";\nimport { getLogger } from \"../logger\";\nimport {\n BatchWriterConfig,\n DEFAULT_FLUSH_INTERVAL_MS,\n DRAIN_RETRY_BACKOFFS_MS,\n HARD_CAP_MULTIPLIER,\n MAX_BUFFER_SIZE,\n MIN_BUFFER_SIZE,\n} from \"./batch-writer.types\";\n\nexport class BatchWriter<T extends Document = Document> {\n private readonly collection: Collection<T>;\n private readonly maxBufferSize: number;\n private readonly hardCapSize: number;\n private readonly flushIntervalMs: number;\n private buffer: AnyBulkWriteOperation<T>[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing: Promise<void> | null = null;\n private drained = false;\n\n constructor(collection: Collection<T>, config: BatchWriterConfig) {\n this.validateConfig(config);\n\n this.collection = collection;\n this.maxBufferSize = config.maxBufferSize;\n this.hardCapSize = config.maxBufferSize * HARD_CAP_MULTIPLIER;\n this.flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\n this.startTimer();\n }\n\n push(operation: AnyBulkWriteOperation<T>): void {\n if (this.drained) {\n getLogger().warn(\n \"[batch-writer] push called after drain — operation ignored\",\n );\n return;\n }\n\n this.buffer.push(operation);\n this.enforceHardCap();\n\n if (this.buffer.length >= this.maxBufferSize) {\n this.resetTimer();\n void this.flush();\n }\n }\n\n async drain(): Promise<void> {\n this.drained = true;\n this.stopTimer();\n\n if (this.flushing) await this.flushing;\n\n for (const backoffMs of DRAIN_RETRY_BACKOFFS_MS) {\n if (this.buffer.length === 0) return;\n if (backoffMs > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, backoffMs));\n }\n await this.flush();\n }\n\n if (this.buffer.length > 0) {\n getLogger().error(\n { unflushed: this.buffer.length },\n \"[batch-writer] drain exhausted retries — operations lost\",\n );\n }\n }\n\n private async flush(): Promise<void> {\n if (this.flushing) return this.flushing;\n if (this.buffer.length === 0) return;\n\n this.flushing = this.doFlush();\n try {\n await this.flushing;\n } finally {\n this.flushing = null;\n }\n }\n\n private async doFlush(): Promise<void> {\n while (this.buffer.length > 0) {\n const snapshot = this.buffer;\n this.buffer = [];\n\n try {\n await this.collection.bulkWrite(snapshot, {\n ordered: false,\n maxTimeMS: this.flushIntervalMs,\n });\n } catch (err: unknown) {\n // mongodb driver types `writeErrors` as `OneOrMore<WriteError>` —\n // runtime narrowing to `Array` is required before reading `.length`.\n // Fix surfaced by Story 3.2 code review D8: re-exporting the cap\n // constants requires a clean DTS build, which this narrowing unblocks\n // (the pre-existing TS2339 was in the \"4 preserved errors\" cohort).\n if (\n err instanceof MongoBulkWriteError &&\n Array.isArray(err.writeErrors) &&\n err.writeErrors.length > 0\n ) {\n getLogger().warn(\n {\n failed: err.writeErrors.length,\n total: snapshot.length,\n },\n \"[batch-writer] bulkWrite partial failure — dropping failed ops (likely persistent errors)\",\n );\n continue;\n }\n\n this.buffer = [...snapshot, ...this.buffer];\n this.enforceHardCap();\n getLogger().warn(\n { err, count: snapshot.length },\n \"[batch-writer] flush failed — will retry on next tick or threshold\",\n );\n return;\n }\n }\n }\n\n private enforceHardCap(): void {\n if (this.buffer.length <= this.hardCapSize) return;\n const dropCount = this.buffer.length - this.hardCapSize;\n this.buffer.splice(0, dropCount);\n getLogger().warn(\n { dropCount, hardCapSize: this.hardCapSize },\n \"[batch-writer] hard cap exceeded — dropped oldest operations\",\n );\n }\n\n private startTimer(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushIntervalMs);\n this.timer.unref();\n }\n\n private stopTimer(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n private resetTimer(): void {\n this.stopTimer();\n this.startTimer();\n }\n\n private validateConfig(config: BatchWriterConfig): void {\n if (\n !Number.isInteger(config.maxBufferSize) ||\n config.maxBufferSize < MIN_BUFFER_SIZE ||\n config.maxBufferSize > MAX_BUFFER_SIZE\n ) {\n throw new Error(\n `[ravenflow] BatchWriter maxBufferSize must be an integer in [${MIN_BUFFER_SIZE}, ${MAX_BUFFER_SIZE}], got ${config.maxBufferSize}`,\n );\n }\n\n if (config.flushIntervalMs !== undefined) {\n if (\n !Number.isInteger(config.flushIntervalMs) ||\n config.flushIntervalMs <= 0\n ) {\n throw new Error(\n `[ravenflow] BatchWriter flushIntervalMs must be a positive integer, got ${config.flushIntervalMs}`,\n );\n }\n }\n }\n}\n","import type { Queue } from \"bullmq\";\nimport { getLogger } from \"../logger\";\n\nexport async function captureJobLogs(\n queue: Queue,\n jobId: string,\n maxChars: number,\n): Promise<string> {\n if (typeof jobId !== \"string\" || jobId.length === 0) {\n throw new Error(\"captureJobLogs: jobId must be a non-empty string\");\n }\n if (!Number.isInteger(maxChars) || maxChars <= 0) {\n throw new Error(\n `captureJobLogs: maxChars must be a positive integer, got ${maxChars}`,\n );\n }\n\n let logs: string[];\n try {\n const result = await queue.getJobLogs(jobId, 0, -1, true);\n logs = result.logs;\n } catch (err) {\n getLogger().warn(\n { err, jobId },\n \"[log-capture] failed to fetch job logs — returning empty string\",\n );\n return \"\";\n }\n\n const joined = logs.join(\"\\n\");\n if (joined.length <= maxChars) return joined;\n\n const originalLen = joined.length;\n let tailLen = maxChars;\n let marker = \"\";\n for (let i = 0; i < 3; i++) {\n marker = `[truncated — showing last ${tailLen} of ${originalLen} chars]\\n`;\n if (marker.length >= maxChars) {\n return marker.slice(0, maxChars);\n }\n tailLen = maxChars - marker.length;\n }\n return marker + joined.slice(-tailLen);\n}\n","import type { Job, Queue, Worker } from \"bullmq\";\nimport { resolveJobName } from \"../app/job-registry\";\nimport type { JobConfig } from \"../types\";\nimport type { BatchWriter } from \"./batch-writer\";\nimport type { JobStatsRepo } from \"../mongo/repositories/job-stats.repo\";\nimport type { JobErrorsRepo } from \"../mongo/repositories/job-errors.repo\";\nimport type { JobFailuresRepo } from \"../mongo/repositories/job-failures.repo\";\nimport type {\n JobStatsDoc,\n JobErrorDoc,\n JobFailureDoc,\n} from \"../mongo/repositories/types\";\nimport { JOB_FAILURES_LOGS_MAX_CHARS } from \"../mongo/repositories/types\";\nimport { captureJobLogs } from \"./log-capture\";\nimport { getLogger } from \"../logger\";\n\nexport interface EventHooksOptions {\n queue: Queue;\n jobConfigs: Record<string, JobConfig>;\n jobStatsRepo: JobStatsRepo;\n jobErrorsRepo: JobErrorsRepo;\n jobFailuresRepo: JobFailuresRepo;\n jobStatsBatchWriter: BatchWriter<JobStatsDoc>;\n jobErrorsBatchWriter: BatchWriter<JobErrorDoc>;\n jobFailuresBatchWriter: BatchWriter<JobFailureDoc>;\n logsMaxChars?: number;\n}\n\nexport function attachEventHooks(worker: Worker, options: EventHooksOptions): void {\n worker.on(\"completed\", (job) => {\n void handleCompleted(job, options);\n });\n worker.on(\"failed\", (job, error) => {\n void handleFailed(job, error, options);\n });\n}\n\nasync function handleCompleted(job: Job, options: EventHooksOptions): Promise<void> {\n try {\n const jobType = resolveTopLevelJobType(job, options.jobConfigs);\n if (jobType === null) return;\n\n const durationMs = computeDurationMs(job);\n const ops = options.jobStatsRepo.toIncrementStatsOps(jobType, {\n success: true,\n durationMs,\n });\n for (const op of ops) {\n options.jobStatsBatchWriter.push(op);\n }\n } catch (err) {\n getLogger().warn(\n { err, jobId: job?.id, path: \"stats\" },\n \"[event-hooks] completed handler failed\",\n );\n }\n}\n\nasync function handleFailed(\n job: Job | undefined,\n error: Error,\n options: EventHooksOptions,\n): Promise<void> {\n if (job === undefined) {\n getLogger().warn(\n { message: error?.message },\n \"[event-hooks] failed event fired without a job — skipping\",\n );\n return;\n }\n if (job.id === undefined || job.id === null) {\n getLogger().warn(\n { jobName: job.name },\n \"[event-hooks] failed event fired without a job.id — skipping\",\n );\n return;\n }\n\n try {\n const jobType = resolveTopLevelJobType(job, options.jobConfigs);\n if (jobType === null) return;\n\n const durationMs = computeDurationMs(job);\n const failedAt =\n typeof job.finishedOn === \"number\" && Number.isFinite(job.finishedOn)\n ? new Date(job.finishedOn)\n : new Date();\n const jobId = String(job.id);\n const logsMaxChars = options.logsMaxChars ?? JOB_FAILURES_LOGS_MAX_CHARS;\n\n try {\n const ops = options.jobStatsRepo.toIncrementStatsOps(jobType, {\n success: false,\n durationMs,\n });\n for (const op of ops) {\n options.jobStatsBatchWriter.push(op);\n }\n } catch (err) {\n getLogger().warn(\n { err, jobId, path: \"stats\" },\n \"[event-hooks] failed handler: stats write failed\",\n );\n }\n\n try {\n const op = options.jobErrorsRepo.toRecordOccurrenceOp({\n jobType,\n error,\n });\n options.jobErrorsBatchWriter.push(op);\n } catch (err) {\n getLogger().warn(\n { err, jobId, path: \"errors\" },\n \"[event-hooks] failed handler: errors write failed\",\n );\n }\n\n let logs = \"\";\n try {\n logs = await captureJobLogs(options.queue, jobId, logsMaxChars);\n } catch (err) {\n getLogger().warn(\n { err, jobId, path: \"failures\" },\n \"[event-hooks] failed handler: log capture failed — recording failure with empty logs\",\n );\n }\n\n try {\n const op = options.jobFailuresRepo.toRecordFailureOp({\n jobId,\n jobType,\n input: job.data,\n error,\n failedAt,\n logs,\n });\n options.jobFailuresBatchWriter.push(op);\n } catch (err) {\n getLogger().warn(\n { err, jobId, path: \"failures\" },\n \"[event-hooks] failed handler: failures write failed\",\n );\n }\n } catch (err) {\n getLogger().warn(\n { err, jobId: job.id, path: \"unknown\" },\n \"[event-hooks] failed handler failed\",\n );\n }\n}\n\nfunction computeDurationMs(job: Job): number | undefined {\n const processedOn = job.processedOn;\n const finishedOn = job.finishedOn;\n if (typeof processedOn !== \"number\" || !Number.isFinite(processedOn)) return undefined;\n if (typeof finishedOn !== \"number\" || !Number.isFinite(finishedOn)) return undefined;\n const delta = finishedOn - processedOn;\n if (delta < 0) return undefined;\n return delta;\n}\n\nfunction resolveTopLevelJobType(\n job: Job,\n jobConfigs: Record<string, JobConfig>,\n): string | null {\n const { baseName, suffix } = resolveJobName(job.name, jobConfigs);\n if (suffix !== null) return null;\n if (!Object.prototype.hasOwnProperty.call(jobConfigs, baseName)) {\n getLogger().debug(\n { jobName: job.name, jobId: job.id },\n \"[event-hooks] skipping event for unknown jobType\",\n );\n return null;\n }\n return baseName;\n}\n","import { DelayedError, type Job } from \"bullmq\";\nimport type { JobConfig } from \"../types\";\nimport type { JobConfigsRepo } from \"../mongo/repositories/job-configs.repo\";\nimport { getLogger } from \"../logger\";\nimport { resolveJobName } from \"./job-registry\";\n\n/**\n * Delay (ms) applied to jobs whose jobType is `enabled: false`. After this\n * window BullMQ returns the job to the `waiting` set and a worker picks it\n * up again; the gate re-evaluates. When the jobType re-enables the next\n * attempt passes the check and dispatches normally.\n *\n * 60 seconds strikes the balance between \"pause takes effect quickly\" and\n * \"don't hammer Redis / Mongo with redefer churn\" (D5.1-b).\n */\nexport const DISABLED_JOB_DEFER_DELAY_MS = 60_000;\n\n/**\n * Non-fatal wrapper around `JobConfigsRepo.ensureEntries` called during\n * `RavenflowApp.initializeMonitoring`. A rejection (Mongo disconnect at\n * boot, write-concern downgrade, collection flip) is logged + swallowed;\n * the app continues without its `job_configs` bootstrap. The worker-side\n * `isEnabled` gate still works because `JobConfigsRepo.isEnabled` returns\n * `JOB_CONFIGS_DEFAULT_ENABLED=true` on missing docs — jobs enqueued\n * during this degraded state run as though uninitialized, which is the\n * pre-Story-5.1 behavior.\n *\n * Story 5.1 code review D5-P-a — extracted so the P11 live-reachable\n * degraded path is regression-anchored by a unit test rather than only\n * by visible-code inspection inside `initializeMonitoring`.\n */\nexport async function safeEnsureEntriesAtBoot(\n jobConfigsRepo: JobConfigsRepo,\n jobs: Record<string, JobConfig>,\n): Promise<void> {\n try {\n await jobConfigsRepo.ensureEntries(jobs);\n } catch (err) {\n getLogger().error(\n { err },\n \"[app] job_configs ensureEntries failed at boot — dashboard may show a stale job-type list\",\n );\n }\n}\n\n/**\n * Worker-side defer gate for disabled jobTypes (Story 5.1 D5.1-b).\n *\n * When the jobType's `enabled` flag is `false` in `job_configs`, this\n * helper moves the job to `delayed` with a +60s timestamp, increments\n * the cumulative `disabledDeferCount` on `job.progress`, and throws\n * `DelayedError` — the BullMQ-canonical signal for \"I moved this job to\n * delayed, don't mark completed/failed\" (verified against\n * node_modules/bullmq/dist/esm/classes/errors/delayed-error.d.ts:9\n * bullmq@5.63.1).\n *\n * When the jobType is enabled (or no row exists — default is `true`),\n * this is a no-op and the caller proceeds to normal dispatch.\n *\n * The `token` argument is the processor lambda's 2nd BullMQ argument\n * (verified against node_modules/bullmq/dist/esm/classes/job.d.ts:444\n * bullmq@5.63.1 — moveToDelayed(timestamp, token?) — passing undefined\n * is accepted but may fail under lock contention with renewLock).\n */\nexport async function deferIfDisabled(\n job: Job,\n jobConfigs: Record<string, JobConfig>,\n jobConfigsRepo: JobConfigsRepo,\n token: string | undefined,\n): Promise<void> {\n // Defensive input guards (Story 5.1 code review P2/P3). A lambda called\n // with malformed shape falls through to default dispatch rather than\n // crashing the worker — mirrors the null-guard pattern used throughout\n // app.ts for optional-infra surfaces.\n if (typeof job.name !== \"string\" || job.name.length === 0) return;\n if (jobConfigs === null || typeof jobConfigs !== \"object\") return;\n\n // BullMQ passes `token` as the 2nd processor argument in real dispatch;\n // undefined indicates a test rig or library misuse. Warn but proceed —\n // moveToDelayed may fail under lock contention without the token (P6).\n if (token === undefined) {\n getLogger().warn(\n { jobId: job.id, jobName: job.name },\n \"[app] deferIfDisabled invoked with undefined token — moveToDelayed may fail under lock contention\",\n );\n }\n\n const { baseName } = resolveJobName(job.name, jobConfigs);\n // resolveJobName can return empty when `job.name` matches only a suffix\n // pattern with no registered base — skip the gate + warn rather than\n // feeding \"\" to validateJobType which throws non-empty-string (P4).\n if (!baseName) {\n getLogger().warn(\n { jobId: job.id, jobName: job.name },\n \"[app] deferIfDisabled: resolveJobName returned empty baseName — skipping gate\",\n );\n return;\n }\n\n // Propagate isEnabled rejection — the spec (line 255) explicitly locks\n // \"disabled-check failure = job failure\" as the intentional choice.\n // Fail-open would silently run disabled jobs during a persistent Mongo\n // outage; BullMQ attempt-erosion on a transient blip is the accepted\n // trade. Job goes to failed + surfaces in /failures if persistent.\n const enabled = await jobConfigsRepo.isEnabled(baseName);\n if (enabled) return;\n\n // Cumulative counter — not reset on redefer. Structured progress (per\n // D5.1-i) preserves room for future observability fields without a\n // worker-side migration. BullMQ types `job.progress` as\n // `number | string | object` (verified against job.d.ts) so we narrow\n // defensively at the boundary (safe runtime shape check, not an\n // escape-hatch cast per P2).\n const prev = job.progress;\n const prevCount =\n typeof prev === \"object\" && prev !== null && !Array.isArray(prev)\n ? (prev as { disabledDeferCount?: unknown }).disabledDeferCount\n : undefined;\n const nextCount =\n typeof prevCount === \"number\" && Number.isFinite(prevCount) ? prevCount + 1 : 1;\n\n // moveToDelayed FIRST (Story 5.1 code review P1) — the counter never\n // bumps for a defer that didn't actually happen. If moveToDelayed\n // rejects (Redis blip, lock lost), the caller's worker sees a non-\n // DelayedError and the job goes to failed with eroded attempts — a\n // real infra failure, not a regression of the counter's semantics.\n // verified against node_modules/bullmq/dist/esm/classes/job.d.ts:444\n // (bullmq@5.63.1) — moveToDelayed(timestamp, token?).\n await job.moveToDelayed(Date.now() + DISABLED_JOB_DEFER_DELAY_MS, token);\n // Counter write is best-effort — updateProgress after a successful\n // moveToDelayed must not unwind the defer. Log the slip and continue.\n try {\n await job.updateProgress({ disabledDeferCount: nextCount });\n } catch (err) {\n getLogger().warn(\n { err, jobType: baseName, jobId: job.id },\n \"[app] deferIfDisabled: updateProgress failed post-defer — counter may lag by one\",\n );\n }\n\n getLogger().info(\n { jobType: baseName, jobId: job.id, disabledDeferCount: nextCount },\n \"[app] job deferred: disabled\",\n );\n\n // verified against node_modules/bullmq/dist/esm/classes/errors/delayed-error.d.ts:9\n // (bullmq@5.63.1) — DelayedError signals 'I moved this job to delayed,\n // don't mark completed/failed'.\n throw new DelayedError();\n}\n","import { ConnectionOptions as RedisConnectionOptions, Worker } from \"bullmq\";\nimport {\n RavenflowConfig,\n RetentionConfig,\n JobConfig,\n JobNames,\n JobsProxy,\n} from \"../types\";\nimport { initLogger, getLogger, LoggerOptions } from \"../logger\";\nimport { initQueues, closeQueues, QueueStore } from \"./queue-manager\";\nimport { initJobs, processJob } from \"./job-registry\";\nimport {\n createWorker,\n setupWorkerEvents,\n printBanner,\n closeWorkers,\n} from \"./worker-manager\";\nimport { syncJobSchedulers } from \"./scheduler\";\nimport { connectMongo, closeMongo, getDb, tokenBucketRepo } from \"../mongo\";\nimport { StallWatcher } from \"../internal-jobs\";\nimport { WorkersRepo } from \"../mongo/repositories/workers.repo\";\nimport { JobStatsRepo } from \"../mongo/repositories/job-stats.repo\";\nimport { JobErrorsRepo } from \"../mongo/repositories/job-errors.repo\";\nimport { JobFailuresRepo } from \"../mongo/repositories/job-failures.repo\";\nimport { JobConfigsRepo } from \"../mongo/repositories/job-configs.repo\";\nimport type {\n JobStatsDoc,\n JobErrorDoc,\n JobFailureDoc,\n} from \"../mongo/repositories/types\";\nimport { WorkerRegistry } from \"../monitoring/worker-registry\";\nimport { BatchWriter } from \"../monitoring/batch-writer\";\nimport type { BatchWriterConfig } from \"../monitoring/batch-writer.types\";\nimport { MIN_BUFFER_SIZE } from \"../monitoring/batch-writer.types\";\nimport { attachEventHooks } from \"../monitoring/event-hooks\";\nimport { deferIfDisabled, safeEnsureEntriesAtBoot } from \"./disabled-job\";\n\nexport interface MongoConnectionObject {\n host: string;\n port?: number;\n username?: string;\n password?: string;\n database?: string;\n}\n\nexport interface RavenflowAppOptions {\n redis: RedisConnectionOptions;\n mongo?: string | MongoConnectionObject;\n retention?: RetentionConfig;\n logger?: LoggerOptions;\n monitoring?: {\n enabled?: boolean;\n heartbeatIntervalMs?: number;\n batchWriter?: {\n maxBufferSize?: number;\n flushIntervalMs?: number;\n };\n };\n}\n\nconst DEFAULT_MONITORING_BATCH_BUFFER = MIN_BUFFER_SIZE;\n\nconst MONGO_REQUIRED_TYPES = new Set([\"stateful-etl\", \"token-bucket\"]);\n\nfunction resolveMongoUri(\n mongo: RavenflowAppOptions[\"mongo\"],\n): string | null {\n if (!mongo) return null;\n if (typeof mongo === \"string\") return mongo;\n\n const { host, port = 27017, username, password, database } = mongo;\n const auth = username && password ? `${encodeURIComponent(username)}:${encodeURIComponent(password)}@` : \"\";\n const db = database ? `/${database}` : \"\";\n return `mongodb://${auth}${host}:${port}${db}`;\n}\n\nfunction validateConfig(\n config: RavenflowConfig,\n mongoUri: string | null,\n): void {\n const errors: string[] = [];\n\n for (const [jobName, jobConfig] of Object.entries(config.jobs)) {\n const jc = jobConfig as JobConfig;\n\n // Mongo required for stateful-etl and token-bucket\n if (MONGO_REQUIRED_TYPES.has(jc.type) && !mongoUri) {\n errors.push(\n `\"${jobName}\" (${jc.type}) requires a mongo connection. Pass { mongo: \"mongodb://...\" } to createRavenflowApp().`,\n );\n }\n\n // Token-bucket requires cron\n if (jc.type === \"token-bucket\" && !jc.cron?.trim()) {\n errors.push(`\"${jobName}\" (token-bucket) requires a cron schedule.`);\n }\n\n // Token-bucket requires at least rate or getBudget\n if (jc.type === \"token-bucket\" && !jc.rate && !jc.getBudget) {\n errors.push(\n `\"${jobName}\" (token-bucket) requires at least one of \"rate\" or \"getBudget\".`,\n );\n }\n\n // Token-bucket rate validation\n if (jc.type === \"token-bucket\" && jc.rate) {\n const validIntervals = new Set([\"second\", \"minute\", \"hour\", \"day\", \"week\"]);\n if (!validIntervals.has(jc.rate.interval)) {\n errors.push(\n `\"${jobName}\" (token-bucket) has invalid rate interval \"${jc.rate.interval}\". Must be one of: second, minute, hour, day, week.`,\n );\n }\n if (jc.rate.amount <= 0) {\n errors.push(\n `\"${jobName}\" (token-bucket) rate.amount must be a positive number.`,\n );\n }\n }\n\n // Token-bucket optional numeric field validation\n if (jc.type === \"token-bucket\") {\n if (jc.maxCarryOver !== undefined && jc.maxCarryOver < 0) {\n errors.push(\n `\"${jobName}\" (token-bucket) maxCarryOver must be >= 0.`,\n );\n }\n if (jc.consumeTimeout !== undefined && jc.consumeTimeout <= 0) {\n errors.push(\n `\"${jobName}\" (token-bucket) consumeTimeout must be a positive number.`,\n );\n }\n }\n\n // Step queue references must exist in config\n if (\"steps\" in jc && jc.steps) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic-erasure cast for free-form step map; typed config-walker deferred per Story 1.8 AC #14\n for (const [stepName, stepConfig] of Object.entries(jc.steps as Record<string, any>)) {\n if (stepConfig?.queue && stepConfig.queue !== config.defaultQueue) {\n const knownQueues = new Set<string>([config.defaultQueue]);\n for (const j of Object.values(config.jobs)) {\n if ((j as JobConfig).queue) knownQueues.add((j as JobConfig).queue!);\n }\n if (!knownQueues.has(stepConfig.queue)) {\n errors.push(\n `\"${jobName}\" step \"${stepName}\" references queue \"${stepConfig.queue}\" which is not declared by any job or as defaultQueue.`,\n );\n }\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new Error(\n `[ravenflow] Config validation failed:\\n - ${errors.join(\"\\n - \")}`,\n );\n }\n}\n\nexport class RavenflowApp<TConfig extends RavenflowConfig = RavenflowConfig> {\n protected redisConnection: RedisConnectionOptions;\n protected defaultConcurrency: number = 5;\n protected config: TConfig;\n protected mongoUri: string | null;\n\n public jobs: JobsProxy<TConfig[\"jobs\"]>;\n protected jobConfigs: Record<JobNames<TConfig>, JobConfig>;\n protected queues: QueueStore;\n protected workers: Worker[] = [];\n protected stallWatcher: StallWatcher | null = null;\n protected workersRepo: WorkersRepo | null = null;\n protected workerRegistry: WorkerRegistry | null = null;\n protected jobStatsRepo: JobStatsRepo | null = null;\n protected jobErrorsRepo: JobErrorsRepo | null = null;\n protected jobFailuresRepo: JobFailuresRepo | null = null;\n protected jobConfigsRepo: JobConfigsRepo | null = null;\n protected jobStatsBatchWriter: BatchWriter<JobStatsDoc> | null = null;\n protected jobErrorsBatchWriter: BatchWriter<JobErrorDoc> | null = null;\n protected jobFailuresBatchWriter: BatchWriter<JobFailureDoc> | null = null;\n protected monitoringInitPromise: Promise<void> | null = null;\n protected readonly monitoringOptions: {\n enabled: boolean;\n heartbeatIntervalMs: number | undefined;\n batchWriter: BatchWriterConfig;\n };\n\n constructor(config: TConfig, options: RavenflowAppOptions) {\n this.redisConnection = options.redis;\n this.config = config;\n this.mongoUri = resolveMongoUri(options.mongo);\n\n initLogger(options.logger);\n\n validateConfig(config, this.mongoUri);\n\n this.queues = initQueues(config, options.redis, options.retention);\n\n const { jobsProxy, jobConfigs } = initJobs(config, this.queues);\n this.jobs = jobsProxy;\n this.jobConfigs = jobConfigs;\n\n this.monitoringOptions = {\n enabled:\n this.mongoUri !== null && options.monitoring?.enabled !== false,\n heartbeatIntervalMs: options.monitoring?.heartbeatIntervalMs,\n batchWriter: {\n maxBufferSize:\n options.monitoring?.batchWriter?.maxBufferSize ??\n DEFAULT_MONITORING_BATCH_BUFFER,\n flushIntervalMs: options.monitoring?.batchWriter?.flushIntervalMs,\n },\n };\n\n if (options.monitoring?.enabled === true && this.mongoUri === null) {\n getLogger().warn(\n {},\n \"[ravenflow] monitoring.enabled=true was requested but no mongo connection is configured — monitoring is disabled\",\n );\n }\n }\n\n public async startWorker(\n queueName: string,\n options?: { concurrency?: number },\n ) {\n if (this.mongoUri) {\n await connectMongo(this.mongoUri);\n await tokenBucketRepo.ensureIndexes();\n }\n\n const queueData = this.queues[queueName];\n if (!queueData) {\n throw new Error(`Queue ${queueName} not found in config.`);\n }\n\n const concurrency = options?.concurrency ?? this.defaultConcurrency;\n\n if (this.mongoUri && this.monitoringOptions.enabled) {\n if (!this.workerRegistry) {\n if (!this.monitoringInitPromise) {\n this.monitoringInitPromise = this.initializeMonitoring(queueName, concurrency);\n }\n try {\n await this.monitoringInitPromise;\n } catch {\n // Errors already logged inside initializeMonitoring.\n }\n }\n\n if (this.workerRegistry) {\n try {\n await this.workerRegistry.registerQueue({ name: queueName, concurrency });\n } catch (err) {\n getLogger().error(\n { err },\n \"[app] worker registry registerQueue failed — will retry on next startWorker call\",\n );\n }\n }\n }\n\n printBanner(\n queueName,\n concurrency,\n this.redisConnection,\n this.jobConfigs,\n this.mongoUri,\n );\n\n await syncJobSchedulers(queueData.queue, this.jobConfigs);\n\n const hasStatefulEtl = Object.values(this.jobConfigs).some(\n (c) => (c as JobConfig).type === \"stateful-etl\",\n );\n\n if (hasStatefulEtl && !this.stallWatcher) {\n this.stallWatcher = new StallWatcher(\n this.jobConfigs,\n this.queues,\n this.config.defaultQueue,\n queueData.queue,\n );\n }\n\n const worker = createWorker(\n queueName,\n this.redisConnection,\n concurrency,\n async (job, token) => {\n // D5.1-b — disabled-jobType defer gate (Story 5.1). When a jobType's\n // `enabled` flag is false, deferIfDisabled calls moveToDelayed(+60s)\n // and throws DelayedError so BullMQ requeues the job without\n // counting it against `attempts`. Skipped entirely when monitoring\n // is off (no jobConfigsRepo → pre-5.1 behavior, backward compat).\n // Snapshot the repo ref locally (Story 5.1 code review P11) so a\n // concurrent `close()` nulling `this.jobConfigsRepo` between the\n // check and the call cannot cause a null-deref.\n const configsRepo = this.jobConfigsRepo;\n if (configsRepo) {\n await deferIfDisabled(\n job,\n this.jobConfigs as Record<string, JobConfig>,\n configsRepo,\n token,\n );\n }\n if (this.stallWatcher?.canProcess(job.name)) {\n return this.stallWatcher.process(job);\n }\n return processJob(job, this.jobConfigs, this.queues, this.config.defaultQueue);\n },\n );\n setupWorkerEvents(worker, queueName);\n this.workers.push(worker);\n worker.run();\n\n if (\n this.monitoringOptions.enabled &&\n this.jobStatsRepo &&\n this.jobErrorsRepo &&\n this.jobFailuresRepo &&\n this.jobStatsBatchWriter &&\n this.jobErrorsBatchWriter &&\n this.jobFailuresBatchWriter\n ) {\n attachEventHooks(worker, {\n queue: queueData.queue,\n jobConfigs: this.jobConfigs as Record<string, JobConfig>,\n jobStatsRepo: this.jobStatsRepo,\n jobErrorsRepo: this.jobErrorsRepo,\n jobFailuresRepo: this.jobFailuresRepo,\n jobStatsBatchWriter: this.jobStatsBatchWriter,\n jobErrorsBatchWriter: this.jobErrorsBatchWriter,\n jobFailuresBatchWriter: this.jobFailuresBatchWriter,\n });\n }\n\n if (this.stallWatcher) {\n await this.stallWatcher.start();\n }\n }\n\n protected async initializeMonitoring(\n queueName: string,\n concurrency: number,\n ): Promise<void> {\n let registry: WorkerRegistry | null = null;\n let statsBatchWriter: BatchWriter<JobStatsDoc> | null = null;\n let errorsBatchWriter: BatchWriter<JobErrorDoc> | null = null;\n let failuresBatchWriter: BatchWriter<JobFailureDoc> | null = null;\n try {\n const db = getDb();\n const workersRepo = new WorkersRepo(db);\n const statsRepo = new JobStatsRepo(db);\n const errorsRepo = new JobErrorsRepo(db);\n const failuresRepo = new JobFailuresRepo(db);\n const configsRepo = new JobConfigsRepo(db);\n await Promise.all([\n workersRepo.init(),\n statsRepo.init(),\n errorsRepo.init(),\n failuresRepo.init(),\n configsRepo.init(),\n ]);\n\n // D5.1-a — idempotent upsert of registered job types + metadata.\n // Non-fatal per P11 audit: a transient Mongo blip here leaves the\n // dashboard with a stale list but the worker-side isEnabled gate\n // still works (falls back to default=true on missing docs), and\n // the next boot retries. Extracted into `safeEnsureEntriesAtBoot`\n // so the degraded path is regression-anchored by a unit test\n // (Story 5.1 code review D5-P-a).\n await safeEnsureEntriesAtBoot(\n configsRepo,\n this.config.jobs as Record<string, JobConfig>,\n );\n\n registry = new WorkerRegistry(workersRepo, {\n heartbeatIntervalMs: this.monitoringOptions.heartbeatIntervalMs,\n });\n await registry.registerQueue({ name: queueName, concurrency });\n await registry.start();\n\n statsBatchWriter = new BatchWriter<JobStatsDoc>(\n statsRepo.collection,\n this.monitoringOptions.batchWriter,\n );\n errorsBatchWriter = new BatchWriter<JobErrorDoc>(\n errorsRepo.collection,\n this.monitoringOptions.batchWriter,\n );\n failuresBatchWriter = new BatchWriter<JobFailureDoc>(\n failuresRepo.collection,\n this.monitoringOptions.batchWriter,\n );\n\n this.workersRepo = workersRepo;\n this.workerRegistry = registry;\n this.jobStatsRepo = statsRepo;\n this.jobErrorsRepo = errorsRepo;\n this.jobFailuresRepo = failuresRepo;\n this.jobConfigsRepo = configsRepo;\n this.jobStatsBatchWriter = statsBatchWriter;\n this.jobErrorsBatchWriter = errorsBatchWriter;\n this.jobFailuresBatchWriter = failuresBatchWriter;\n } catch (err) {\n getLogger().error(\n { err },\n \"[app] monitoring setup failed — continuing without observability writes\",\n );\n if (registry) {\n await registry.stop().catch(() => {});\n }\n const drainables = [statsBatchWriter, errorsBatchWriter, failuresBatchWriter].filter(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- heterogeneous BatchWriter union; typed narrowing deferred per Story 1.8 AC #14\n (bw): bw is BatchWriter<any> => bw !== null,\n );\n if (drainables.length > 0) {\n await Promise.allSettled(drainables.map((bw) => bw.drain()));\n }\n this.workerRegistry = null;\n this.workersRepo = null;\n this.jobStatsRepo = null;\n this.jobErrorsRepo = null;\n this.jobFailuresRepo = null;\n this.jobConfigsRepo = null;\n this.jobStatsBatchWriter = null;\n this.jobErrorsBatchWriter = null;\n this.jobFailuresBatchWriter = null;\n } finally {\n this.monitoringInitPromise = null;\n }\n }\n\n public async close(): Promise<void> {\n const logger = getLogger();\n logger.info(\"Shutting down...\");\n\n if (this.stallWatcher) {\n await this.stallWatcher.stop();\n this.stallWatcher = null;\n }\n\n if (this.workerRegistry) {\n await this.workerRegistry.stop();\n this.workerRegistry = null;\n this.workersRepo = null;\n }\n\n await closeWorkers(this.workers);\n\n const drainables = [\n this.jobStatsBatchWriter,\n this.jobErrorsBatchWriter,\n this.jobFailuresBatchWriter,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- heterogeneous BatchWriter union; typed narrowing deferred per Story 1.8 AC #14\n ].filter((bw): bw is BatchWriter<any> => bw !== null);\n if (drainables.length > 0) {\n await Promise.allSettled(drainables.map((bw) => bw.drain()));\n }\n this.jobStatsBatchWriter = null;\n this.jobErrorsBatchWriter = null;\n this.jobFailuresBatchWriter = null;\n this.jobStatsRepo = null;\n this.jobErrorsRepo = null;\n this.jobFailuresRepo = null;\n this.jobConfigsRepo = null;\n\n await closeQueues(this.queues);\n await closeMongo();\n logger.info(\"Shutdown complete.\");\n }\n}\n","import { RavenflowConfig } from \"./types\";\nimport { RavenflowApp, RavenflowAppOptions } from \"./app/index\";\n\nexport { RavenflowApp } from \"./app/index\";\nexport type { RavenflowAppOptions, MongoConnectionObject } from \"./app/index\";\nexport { initLogger, getLogger } from \"./logger\";\n\nexport type {\n // Config\n JobConfig,\n RavenflowConfig,\n RetentionConfig,\n\n // Job configs\n SimpleJobConfig,\n MemoryEtlJobConfig,\n DistributedEtlJobConfig,\n StatefulEtlJobConfig,\n TokenBucketJobConfig,\n TokenBucketInterval,\n\n // Step & rate limit configs\n StepConfig,\n MemoryEtlStepConfig,\n RateLimitConfig,\n\n // ETL types\n EtlExtractResult,\n EtlResult,\n\n // Job status\n JobStatus,\n} from \"./types\";\n\nexport {\n defineConfig,\n defineSimpleJob,\n defineMemoryEtlJob,\n defineDistributedEtlJob,\n defineStatefulEtlJob,\n defineTokenBucketJob,\n} from \"./types\";\n\nexport type { Logger, LoggerOptions } from \"./logger\";\n\nexport { BatchWriter } from \"./monitoring\";\nexport type { BatchWriterConfig } from \"./monitoring\";\nexport { errorSignature } from \"./monitoring\";\nexport { captureJobLogs } from \"./monitoring\";\nexport { WorkerRegistry } from \"./monitoring\";\nexport type { WorkerRegistryOptions } from \"./monitoring\";\nexport { attachEventHooks } from \"./monitoring\";\nexport type { EventHooksOptions } from \"./monitoring\";\n\nexport { DEFAULT_WORKER_METRICS, METRICS_TIME } from \"./app/worker-manager\";\nexport { DISABLED_JOB_DEFER_DELAY_MS, deferIfDisabled } from \"./app/disabled-job\";\n\nexport {\n JobStatsRepo,\n JobErrorsRepo,\n JobFailuresRepo,\n WorkersRepo,\n JobConfigsRepo,\n Repository,\n computeBucketStart,\n JOB_FAILURES_INPUT_MAX_CHARS,\n JOB_FAILURES_MESSAGE_MAX_CHARS,\n JOB_FAILURES_STACK_MAX_CHARS,\n JOB_FAILURES_LOGS_MAX_CHARS,\n} from \"./mongo/repositories\";\nexport type {\n IndexDescriptor,\n BucketGranularity,\n IncrementStatsInput,\n JobStatsDoc,\n JobErrorDoc,\n RecordOccurrenceInput,\n TopErrorsOptions,\n JobFailureDoc,\n RecordFailureInput,\n ListFailuresOptions,\n WorkerDoc,\n WorkerQueueEntry,\n RegisterWorkerInput,\n UpsertWorkerQueueInput,\n JobConfigEntry,\n JobConfigDoc,\n} from \"./mongo/repositories\";\n\nexport function createRavenflowApp<TConfig extends RavenflowConfig>(\n config: TConfig,\n options: RavenflowAppOptions,\n): RavenflowApp<TConfig> {\n return new RavenflowApp(config, options);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/logger.ts","../src/app/queue-manager.ts","../src/processors/token-bucket-job.processor/constants.ts","../src/processors/constants.ts","../src/processors/simple-job.processor.ts","../src/types/job/common.ts","../src/types/job/simple.ts","../src/types/job/memory-etl.ts","../src/types/job/distributed-etl.ts","../src/types/job/stateful-etl.ts","../src/types/job/token-bucket.ts","../src/types/job/unions.ts","../src/types/config.ts","../src/rate-limiter.ts","../src/processors/memory-etl-job.processor.ts","../src/processors/sub-job.ts","../src/processors/etl-context.ts","../src/processors/distributed-etl-job.processor.ts","../src/mongo/connection.ts","../src/mongo/run.repo.ts","../src/mongo/token-bucket.repo.ts","../src/processors/stateful-etl-job.processor/constants.ts","../src/processors/stateful-etl-job.processor/heartbeat.ts","../src/processors/stateful-etl-job.processor/paginate.ts","../src/processors/stateful-etl-job.processor/process.ts","../src/processors/token-bucket-job.processor/compute-credit.ts","../src/processors/token-bucket-job.processor/index.ts","../src/app/job-registry.ts","../src/banner.ts","../src/app/worker-manager.ts","../src/app/scheduler.ts","../src/internal-jobs/stall-watcher/types.ts","../src/internal-jobs/stall-watcher/check-stalled-runs.ts","../src/internal-jobs/stall-watcher/resume-stalled-run.ts","../src/internal-jobs/stall-watcher/index.ts","../src/mongo/repositories/_base.ts","../src/mongo/repositories/workers.repo.ts","../src/mongo/repositories/_types.ts","../src/mongo/repositories/bucket-utils.ts","../src/mongo/mongo-error.ts","../src/mongo/repositories/types.ts","../src/mongo/repositories/job-stats.repo.ts","../src/monitoring/error-signature.ts","../src/monitoring/error-extraction.ts","../src/mongo/repositories/job-errors.repo.ts","../src/mongo/repositories/job-failures.repo.ts","../src/mongo/repositories/job-configs.repo.ts","../src/monitoring/worker-registry.ts","../src/monitoring/batch-writer.types.ts","../src/monitoring/batch-writer.ts","../src/monitoring/log-capture.ts","../src/monitoring/event-hooks.ts","../src/app/disabled-job.ts","../src/app/app.ts","../src/index.ts"],"names":["z","resolve","logger","collection","processJob","db","COLLECTION"],"mappings":";;;;;;;;;;;;;AASA,IAAI,MAAA,GAAwB,IAAA;AAErB,SAAS,UAAA,CAAW,OAAA,GAAyB,EAAC,EAAW;AAC9D,EAAA,IAAI,QAAQ,OAAO,MAAA;AAEnB,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA;AACvC,EAAA,MAAM,SAAA,GAAY,QAAQ,MAAA,IAAU,KAAA;AAEpC,EAAA,MAAA,GAAS,IAAA,CAAK;AAAA,IACZ,KAAA,EAAO,OAAA,CAAQ,KAAA,KAAU,KAAA,GAAQ,OAAA,GAAU,MAAA,CAAA;AAAA,IAC3C,WAAW,SAAA,GACP;AAAA,MACE,MAAA,EAAQ,aAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,QAAA,EAAU,IAAA;AAAA,QACV,aAAA,EAAe,UAAA;AAAA,QACf,MAAA,EAAQ;AAAA;AACV,KACF,GACA;AAAA,GACL,CAAA;AAED,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,SAAA,GAAoB;AAClC,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,MAAM,8DAA8D,CAAA;AAAA,EAChF;AACA,EAAA,OAAO,MAAA;AACT;AClCA,SAAS,uBACP,SAAA,EAC+B;AAC/B,EAAA,IAAI,CAAC,WAAW,OAAO,MAAA;AAEvB,EAAA,MAAM,OAA0B,EAAC;AAEjC,EAAA,IAAI,SAAA,CAAU,cAAc,MAAA,EAAW;AACrC,IAAA,IAAA,CAAK,gBAAA,GAAmB,EAAE,KAAA,EAAO,SAAA,CAAU,SAAA,EAAU;AAAA,EACvD;AACA,EAAA,IAAI,SAAA,CAAU,WAAW,MAAA,EAAW;AAClC,IAAA,IAAA,CAAK,YAAA,GAAe,EAAE,KAAA,EAAO,SAAA,CAAU,MAAA,EAAO;AAAA,EAChD;AAEA,EAAA,OAAO,OAAO,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,IAAA,GAAO,MAAA;AAC/C;AAEO,SAAS,UAAA,CACd,MAAA,EACA,UAAA,EACA,SAAA,EACY;AACZ,EAAA,MAAM,6BAAa,IAAI,GAAA,CAAY,CAAC,MAAA,CAAO,YAAY,CAAC,CAAA;AACxD,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,MAAA,CAAkB,MAAA,CAAO,IAAI,CAAA,EAAG;AACvD,IAAA,IAAI,GAAA,CAAI,KAAA,EAAO,UAAA,CAAW,GAAA,CAAI,IAAI,KAAK,CAAA;AAAA,EACzC;AAEA,EAAA,MAAM,iBAAA,GAAoB,uBAAuB,SAAS,CAAA;AAE1D,EAAA,MAAM,SAAqB,EAAC;AAC5B,EAAA,KAAA,MAAW,QAAQ,UAAA,EAAY;AAC7B,IAAA,MAAA,CAAO,IAAI,CAAA,GAAI;AAAA,MACb,OAAO,IAAI,KAAA,CAAM,MAAM,EAAE,UAAA,EAAY,mBAAmB,CAAA;AAAA,MACxD,QAAQ,IAAI,WAAA,CAAY,IAAA,EAAM,EAAE,YAAY;AAAA,KAC9C;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAMA,eAAsB,OAAA,CACpB,OAAA,EACA,MAAA,EACA,MAAA,EACA,SAAA,EACA;AACA,EAAA,MAAM,SAAA,GAAY,OAAO,SAAS,CAAA;AAClC,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,SAAS,CAAA,UAAA,CAAY,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,MAAM,SAAA,CAAU,KAAA,CAAM,GAAA,CAAI,SAAS,MAAM,CAAA;AAClD;AAEA,eAAsB,cAAA,CACpB,OAAA,EACA,MAAA,EACA,MAAA,EACA,SAAA,EACA;AACA,EAAA,MAAM,SAAA,GAAY,OAAO,SAAS,CAAA;AAClC,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,SAAS,CAAA,UAAA,CAAY,CAAA;AAAA,EAChD;AACA,EAAA,MAAM,MAAM,MAAM,SAAA,CAAU,KAAA,CAAM,GAAA,CAAI,SAAS,MAAM,CAAA;AACrD,EAAA,OAAO,MAAM,GAAA,CAAI,iBAAA,CAAkB,SAAA,CAAU,MAAM,CAAA;AACrD;AAGA,eAAsB,YAAY,MAAA,EAAoB;AACpD,EAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,IACZ,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA,CAAE,GAAA;AAAA,MAAI,CAAC,EAAE,KAAA,EAAO,MAAA,OAClC,OAAA,CAAQ,GAAA,CAAI,CAAC,KAAA,CAAM,KAAA,EAAM,EAAG,MAAA,CAAO,KAAA,EAAO,CAAC;AAAA;AAC7C,GACF;AACF;;;AChFO,IAAM,2BAAA,GAA8B,UAAA;AAEpC,IAAM,WAAA,GAAmD;AAAA,EAC9D,MAAA,EAAQ,GAAA;AAAA,EACR,MAAA,EAAQ,GAAA;AAAA,EACR,IAAA,EAAM,IAAA;AAAA,EACN,GAAA,EAAK,KAAA;AAAA,EACL,IAAA,EAAM;AACR,CAAA;;;ACVO,IAAM,kBAAA,GAAqB,UAAA;AAC3B,IAAM,oBAAA,GAAuB,YAAA;AAC7B,IAAM,eAAA,GAAkB,OAAA;;;ACC/B,eAAsB,gBAAA,CACpB,KACA,SAAA,EACkB;AAClB,EAAA,OAAO,SAAA,CAAU,OAAA,CAAQ,GAAA,EAAK,GAAA,CAAI,IAAI,CAAA;AACxC;ACQO,IAAM,gBAAA,GAAmB,EAAE,KAAA,CAAM;AAAA,EACtC,CAAA,CAAE,OAAO,EAAE,SAAA,EAAW,EAAE,MAAA,EAAO,CAAE,QAAA,EAAS,EAAG,CAAA;AAAA,EAC7C,CAAA,CAAE,OAAO,EAAE,SAAA,EAAW,EAAE,MAAA,EAAO,CAAE,QAAA,EAAS,EAAG;AAC/C,CAAC,CAAA;AAwBM,IAAM,UAAA,GAAa,EAAE,IAAA,CAAK;AAAA,EAC/B,SAAA;AAAA,EACA,aAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAC,CAAA;AAGM,IAAM,QAAA,GAAW,EAAE,MAAA,CAAO;AAAA,EAC/B,GAAA,EAAK,EAAE,MAAA,EAAO;AAAA,EACd,IAAA,EAAM,EAAE,MAAA,EAAO;AAAA,EACf,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,EAChB,OAAA,EAAS,EAAE,MAAA,EAAO;AAAA,EAClB,MAAA,EAAQ,UAAA;AAAA,EACR,MAAA,EAAQ,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS;AAAA,EAC7B,MAAA,EAAQ,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS;AAAA,EAC7B,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAE3B,UAAA,EAAY,EAAE,MAAA,CAAO;AAAA,IACnB,SAAA,EAAW,EAAE,IAAA,EAAK;AAAA,IAClB,SAAA,EAAW,EAAE,IAAA,EAAK;AAAA,IAClB,SAAA,EAAW,CAAA,CAAE,IAAA,EAAK,CAAE,QAAA,EAAS;AAAA,IAC7B,WAAA,EAAa,CAAA,CAAE,IAAA,EAAK,CAAE,QAAA;AAAS,GAChC;AACH,CAAC,CAAA;ACvDM,SAAS,gBACd,MAAA,EACiC;AACjC,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,IAAA,EAAM,QAAA,EAAS;AACrC;AAMO,IAAM,UAAA,GAAa,SAAS,MAAA,CAAO;AAAA,EACxC,IAAA,EAAMA,CAAAA,CAAE,OAAA,CAAQ,QAAQ;AAC1B,CAAC,CAAA;ACCM,IAAM,aAAA,GAAgB,SAAS,MAAA,CAAO;AAAA,EAC3C,IAAA,EAAMA,CAAAA,CAAE,OAAA,CAAQ,YAAY,CAAA;AAAA,EAE5B,QAAA,EAAUA,EAAE,MAAA,CAAO;AAAA,IACjB,gBAAgBA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IAC7C,kBAAkBA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IAC/C,aAAaA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA;AAAY,GAC3C;AACH,CAAC,CAAA;AAIM,SAAS,mBACd,MAAA,EAC+C;AAC/C,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,IAAA,EAAM,YAAA,EAAa;AACzC;AClBO,IAAM,kBAAA,GAAqB,SAAS,MAAA,CAAO;AAAA,EAChD,IAAA,EAAMA,CAAAA,CAAE,OAAA,CAAQ,iBAAiB,CAAA;AAAA,EAEjC,QAAA,EAAUA,EAAE,MAAA,CAAO;AAAA,IACjB,gBAAgBA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IAC7C,kBAAkBA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IAC/C,aAAaA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA;AAAY,GAC3C;AACH,CAAC,CAAA;AAIM,SAAS,wBACd,MAAA,EACoD;AACpD,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,IAAA,EAAM,iBAAA,EAAkB;AAC9C;ACJO,IAAM,eAAA,GAAkB,SAAS,MAAA,CAAO;AAAA,EAC7C,IAAA,EAAMA,CAAAA,CAAE,OAAA,CAAQ,cAAc,CAAA;AAAA,EAE9B,gBAAgBA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,QAAA,EAAS;AAAA,EAC1C,iBAAiBA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,EAE9C,QAAA,EAAUA,EAAE,MAAA,CAAO;AAAA,IACjB,kBAAkBA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IAC/C,gBAAgBA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IAC7C,aAAaA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,IAE1C,UAAA,EAAYA,CAAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS;AAAA,IACjC,eAAA,EAAiBA,CAAAA,CAAE,IAAA,EAAK,CAAE,QAAA,EAAS;AAAA,IACnC,cAAcA,CAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA;AAAY,GAC5C;AACH,CAAC,CAAA;AAIM,SAAS,qBACd,MAAA,EACiD;AACjD,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,IAAA,EAAM,cAAA,EAAe;AAC3C;AC9BiCA,EAAE,MAAA,CAAO;AAAA,EACxC,GAAA,EAAKA,EAAE,MAAA,EAAO;AAAA,EACd,iBAAA,EAAmBA,EAAE,MAAA,EAAO;AAAA,EAC5B,UAAA,EAAYA,EAAE,IAAA;AAChB,CAAC;AAIM,SAAS,qBACd,MAAA,EACsB;AACtB,EAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,IAAA,EAAM,cAAA,EAAe;AAC3C;ACd6BA,CAAAA,CAAE,kBAAA,CAAmB,MAAA,EAAQ;AAAA,EACxD,UAAA;AAAA,EACA,aAAA;AAAA,EACA,kBAAA;AAAA,EACA;AACF,CAAC;;;ACfM,SAAS,aAA8C,MAAA,EAAc;AAC1E,EAAA,OAAO,MAAA;AACT;;;ACjBO,IAAM,cAAN,MAAkB;AAAA,EAAlB,WAAA,GAAA;AACL,IAAA,IAAA,CAAQ,KAAA,uBAAY,GAAA,EAAoB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxC,MAAa,WAAA,CACX,GAAA,EACA,MAAA,EACe;AACf,IAAA,IAAI,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,GAAA,EAAK,MAAM,CAAA;AACrC,IAAA,OAAO,QAAQ,CAAA,EAAG;AAChB,MAAA,MAAM,IAAA,CAAK,KAAK,KAAK,CAAA;AACrB,MAAA,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,GAAA,EAAK,MAAM,CAAA;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,QAAA,CAAS,KAAa,MAAA,EAAiC;AAC5D,IAAA,MAAM,UAAA,GAAa,QAAQ,MAAM,CAAA;AACjC,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAE1C,IAAA,IAAI,eAAA,KAAoB,MAAA,IAAa,GAAA,IAAO,eAAA,EAAiB;AAC3D,MAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,GAAA,GAAM,UAAU,CAAA;AACpC,MAAA,OAAO,CAAA;AAAA,IACT;AAEA,IAAA,OAAO,eAAA,GAAkB,GAAA;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKO,KAAK,EAAA,EAA2B;AACrC,IAAA,IAAI,EAAA,IAAM,CAAA,EAAG,OAAO,OAAA,CAAQ,OAAA,EAAQ;AACpC,IAAA,OAAO,IAAI,OAAA,CAAQ,CAACC,aAAY,UAAA,CAAWA,QAAAA,EAAS,EAAE,CAAC,CAAA;AAAA,EACzD;AACF,CAAA;AAEA,SAAS,QAAQ,MAAA,EAAiC;AAChD,EAAA,gBAAA,CAAiB,MAAM,MAAM,CAAA;AAC7B,EAAA,IAAI,WAAA,IAAe,MAAA,EAAQ,OAAO,GAAA,GAAQ,MAAA,CAAO,SAAA;AACjD,EAAA,OAAO,MAAS,MAAA,CAAO,SAAA;AACzB;;;AC9CA,eAAsB,mBAAA,CACpB,KACA,MAAA,EACA;AACA,EAAA,MAAMC,UAAS,SAAA,EAAU;AACzB,EAAA,MAAM,UAAU,GAAA,CAAI,IAAA;AACpB,EAAA,MAAM,SAAS,EAAE,cAAA,EAAgB,GAAG,gBAAA,EAAkB,CAAA,EAAG,aAAa,CAAA,EAAE;AACxE,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,KAAA,GAAQ,IAAI,aAAY,GAAI,IAAA;AAEnD,EAAA,IAAI,MAAA,GAAyB,IAAA;AAC7B,EAAA,IAAI,OAAA,GAAU,IAAA;AAEd,EAAA,OAAO,OAAA,EAAS;AACd,IAAA,MAAM,cAAA,CAAe,OAAA,EAAS,OAAA,EAAS,SAAA,EAAW,MAAM,CAAA;AAExD,IAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA;AACxC,IAAA,OAAA,GAAU,IAAA,CAAK,OAAA;AACf,IAAA,MAAA,GAAS,IAAA,CAAK,MAAA;AAEd,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AAE5B,IAAA,MAAA,CAAO,cAAA,IAAkB,KAAK,IAAA,CAAK,MAAA;AAEnC,IAAA,MAAM,YAAY,MAAM,iBAAA;AAAA,MACtB,IAAA,CAAK,IAAA;AAAA,MACL,MAAA,CAAO,SAAA;AAAA,MACP,OAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,MAAA,CAAO,oBAAoB,SAAA,CAAU,MAAA;AAErC,IAAA,MAAM,cAAA,CAAe,OAAA,EAAS,OAAA,EAAS,MAAA,EAAQ,MAAM,CAAA;AACrD,IAAA,MAAM,MAAA,CAAO,KAAK,SAAS,CAAA;AAC3B,IAAA,MAAA,CAAO,eAAe,SAAA,CAAU,MAAA;AAEhC,IAAAA,QAAO,KAAA,CAAM,EAAE,SAAS,GAAG,MAAA,IAAU,yBAAyB,CAAA;AAAA,EAChE;AAEA,EAAAA,QAAO,IAAA,CAAK,EAAE,SAAS,GAAG,MAAA,IAAU,wBAAwB,CAAA;AAC5D,EAAA,OAAO,MAAA;AACT;AAEA,eAAe,iBAAA,CACb,KAAA,EACA,SAAA,EACA,OAAA,EACA,SACA,MAAA,EACuB;AACvB,EAAA,MAAM,UAAiC,EAAC;AAExC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,cAAA,CAAe,OAAA,EAAS,OAAA,EAAS,WAAA,EAAa,MAAM,CAAA;AAC1D,IAAA,OAAA,CAAQ,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,EAC9B;AAEA,EAAA,OAAO,OAAA,CAAQ,IAAI,OAAO,CAAA;AAC5B;AAIA,eAAe,cAAA,CACb,OAAA,EACA,OAAA,EACA,IAAA,EACA,MAAA,EACe;AACf,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,KAAA,GAAQ,IAAI,CAAA,EAAG,SAAA;AACvC,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,QAAA,EAAU;AAC3B,EAAA,MAAM,QAAQ,WAAA,CAAY,CAAA,aAAA,EAAgB,OAAO,CAAA,CAAA,EAAI,IAAI,IAAI,QAAQ,CAAA;AACvE;;;AClEA,eAAsB,cAAA,CACpB,KAAA,EACA,WAAA,EACA,IAAA,EACA,MACA,OAAA,EACkB;AAClB,EAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,CAAI,MAAM,IAAA,EAAM;AAAA,IACtC,UAAU,OAAA,EAAS,QAAA;AAAA,IACnB,SAAS,OAAA,EAAS;AAAA,GACnB,CAAA;AAED,EAAA,OAAQ,MAAM,GAAA,CAAI,iBAAA,CAAkB,WAAW,CAAA;AACjD;;;ACSO,IAAM,aAAN,MAAmC;AAAA,EASxC,WAAA,CACE,OAAA,EACA,MAAA,EACA,MAAA,EACA,aAAA,EACA;AACA,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,IAAA,CAAK,SAAS,SAAA,EAAU;AACxB,IAAA,IAAA,CAAK,cAAc,MAAA,CAAO,KAAA;AAC1B,IAAA,IAAA,CAAK,OAAA,GAAU,MAAA,CAAO,KAAA,GAAQ,IAAI,aAAY,GAAI,IAAA;AAClD,IAAA,IAAA,CAAK,MAAA,GAAS,aAAA,GACV,EAAE,GAAG,aAAA,EAAc,GACnB,EAAE,cAAA,EAAgB,CAAA,EAAG,gBAAA,EAAkB,CAAA,EAAG,WAAA,EAAa,CAAA,EAAE;AAE7D,IAAA,MAAM,YAAA,GAAe,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AACxC,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA,CAAK,iBAAA,CAAkB,MAAA,EAAQ,QAAQ,YAAY,CAAA;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,IAAA,EAA+B;AAClD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,WAAA,GAAc,IAAI,CAAA,EAAG,SAAA;AACzC,IAAA,IAAI,CAAC,IAAA,CAAK,OAAA,IAAW,CAAC,MAAA,EAAQ;AAC9B,IAAA,MAAM,KAAK,OAAA,CAAQ,WAAA;AAAA,MACjB,CAAA,aAAA,EAAgB,IAAA,CAAK,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AAAA,MACpC;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBACJ,MAAA,EAKC;AACD,IAAA,MAAM,IAAA,CAAK,eAAe,SAAS,CAAA;AACnC,IAAA,MAAM,EAAE,KAAA,EAAO,WAAA,EAAa,OAAA,EAAQ,GAAI,KAAK,KAAA,CAAM,OAAA;AAEnD,IAAA,OAAO,cAAA;AAAA,MACL,KAAA;AAAA,MACA,WAAA;AAAA,MACA,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,EAAG,kBAAkB,CAAA,CAAA;AAAA,MACpC,EAAE,MAAA,EAAO;AAAA,MACT;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,kBAAkB,IAAA,EAAiC;AACvD,IAAA,MAAM,EAAE,KAAA,EAAO,WAAA,EAAa,OAAA,EAAQ,GAAI,KAAK,KAAA,CAAM,SAAA;AAEnD,IAAA,OAAO,cAAA;AAAA,MACL,KAAA;AAAA,MACA,WAAA;AAAA,MACA,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,EAAG,oBAAoB,CAAA,CAAA;AAAA,MACtC,IAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAAA,EAEA,MAAM,kBAAkB,QAAA,EAAyC;AAC/D,IAAA,MAAM,UAAiC,EAAC;AAExC,IAAA,KAAA,MAAW,QAAQ,QAAA,EAAU;AAC3B,MAAA,MAAM,IAAA,CAAK,eAAe,WAAW,CAAA;AACrC,MAAA,OAAA,CAAQ,IAAA,CAAK,IAAA,CAAK,iBAAA,CAAkB,IAAI,CAAC,CAAA;AAAA,IAC3C;AAEA,IAAA,OAAO,OAAA,CAAQ,IAAI,OAAO,CAAA;AAAA,EAC5B;AAAA,EAEA,MAAM,aAAa,cAAA,EAA6C;AAC9D,IAAA,MAAM,IAAA,CAAK,eAAe,MAAM,CAAA;AAChC,IAAA,MAAM,EAAE,KAAA,EAAO,WAAA,EAAa,OAAA,EAAQ,GAAI,KAAK,KAAA,CAAM,IAAA;AAEnD,IAAA,MAAM,cAAA;AAAA,MACJ,KAAA;AAAA,MACA,WAAA;AAAA,MACA,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,EAAG,eAAe,CAAA,CAAA;AAAA,MACjC,EAAE,IAAA,EAAM,cAAA,EAAgB,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA,MAC9C;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,iBAAA,CACN,MAAA,EACA,MAAA,EACA,YAAA,EAC+B;AAC/B,IAAA,MAAMD,QAAAA,GAAU,CAAC,IAAA,KAA8C;AAC7D,MAAA,MAAM,IAAI,IAAA,EAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,GAAI,YAAA;AAC7C,MAAA,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,CAAA,OAAA,EAAU,IAAA,EAAM,KAAK,CAAA,WAAA,CAAa,CAAA;AAC1D,MAAA,OAAO;AAAA,QACL,OAAO,CAAA,CAAE,KAAA;AAAA,QACT,aAAa,CAAA,CAAE,MAAA;AAAA,QACf,OAAA,EAAS;AAAA,UACP,QAAA,EAAU,IAAA,EAAM,QAAA,IAAY,MAAA,CAAO,QAAA;AAAA,UACnC,OAAA,EAAS,IAAA,EAAM,OAAA,IAAW,MAAA,CAAO;AAAA;AACnC,OACF;AAAA,IACF,CAAA;AAEA,IAAA,OAAO;AAAA,MACL,OAAA,EAASA,QAAAA,CAAQ,MAAA,CAAO,KAAA,EAAO,OAAO,CAAA;AAAA,MACtC,SAAA,EAAWA,QAAAA,CAAQ,MAAA,CAAO,KAAA,EAAO,SAAS,CAAA;AAAA,MAC1C,IAAA,EAAMA,QAAAA,CAAQ,MAAA,CAAO,KAAA,EAAO,IAAI;AAAA,KAClC;AAAA,EACF;AACF,CAAA;;;AC3JA,eAAsB,wBAAA,CACpB,GAAA,EACA,MAAA,EACA,MAAA,EACA;AACA,EAAA,MAAM,MAAM,IAAI,UAAA,CAA6B,GAAA,CAAI,IAAA,EAAM,QAAQ,MAAM,CAAA;AAErE,EAAA,IAAI,MAAA,GAAyB,IAAA;AAC7B,EAAA,IAAI,OAAA,GAAU,IAAA;AAEd,EAAA,OAAO,OAAA,EAAS;AACd,IAAA,MAAM,IAAA,GAAwC,MAAM,GAAA,CAAI,eAAA,CAAyB,MAAM,CAAA;AACvF,IAAA,OAAA,GAAU,IAAA,CAAK,OAAA;AACf,IAAA,MAAA,GAAS,IAAA,CAAK,MAAA;AAEd,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AAE5B,IAAA,GAAA,CAAI,MAAA,CAAO,cAAA,IAAkB,IAAA,CAAK,IAAA,CAAK,MAAA;AAEvC,IAAA,MAAM,SAAA,GAAY,MAAM,GAAA,CAAI,iBAAA,CAAkB,KAAK,IAAI,CAAA;AACvD,IAAA,GAAA,CAAI,MAAA,CAAO,oBAAoB,SAAA,CAAU,MAAA;AAEzC,IAAA,MAAM,GAAA,CAAI,aAAa,SAAS,CAAA;AAChC,IAAA,GAAA,CAAI,MAAA,CAAO,eAAe,SAAA,CAAU,MAAA;AAEpC,IAAA,GAAA,CAAI,MAAA,CAAO,KAAA,CAAM,EAAE,OAAA,EAAS,GAAA,CAAI,SAAS,GAAG,GAAA,CAAI,MAAA,EAAO,EAAG,8BAA8B,CAAA;AAAA,EAC1F;AAEA,EAAA,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK,EAAE,OAAA,EAAS,GAAA,CAAI,SAAS,GAAG,GAAA,CAAI,MAAA,EAAO,EAAG,6BAA6B,CAAA;AACtF,EAAA,OAAO,GAAA,CAAI,MAAA;AACb;AChCA,IAAM,iBAAA,GAAoB,YAAA;AAE1B,IAAI,MAAA,GAA6B,IAAA;AACjC,IAAI,EAAA,GAAgB,IAAA;AAEpB,eAAsB,aAAa,GAAA,EAA0B;AAC3D,EAAA,IAAI,IAAI,OAAO,EAAA;AAEf,EAAA,MAAA,GAAS,IAAI,YAAY,GAAG,CAAA;AAC5B,EAAA,MAAM,OAAO,OAAA,EAAQ;AACrB,EAAA,EAAA,GAAK,OAAO,EAAA,EAAG;AAEf,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,KAAA,GAAY;AAC1B,EAAA,IAAI,CAAC,EAAA,EAAI;AACP,IAAA,MAAM,IAAI,MAAM,8EAA8E,CAAA;AAAA,EAChG;AACA,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,eAAe,IAAA,EAAsB;AACnD,EAAA,OAAO,CAAA,EAAG,iBAAiB,CAAA,EAAG,IAAI,CAAA,CAAA;AACpC;AAEA,eAAsB,UAAA,GAA4B;AAChD,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,OAAO,KAAA,EAAM;AACnB,IAAA,MAAA,GAAS,IAAA;AACT,IAAA,EAAA,GAAK,IAAA;AAAA,EACP;AACF;;;AClCA,IAAA,gBAAA,GAAA,EAAA;AAAA,QAAA,CAAA,gBAAA,EAAA;AAAA,EAAA,eAAA,EAAA,MAAA,eAAA;AAAA,EAAA,SAAA,EAAA,MAAA,SAAA;AAAA,EAAA,aAAA,EAAA,MAAA,aAAA;AAAA,EAAA,QAAA,EAAA,MAAA,QAAA;AAAA,EAAA,WAAA,EAAA,MAAA,WAAA;AAAA,EAAA,qBAAA,EAAA,MAAA,qBAAA;AAAA,EAAA,aAAA,EAAA,MAAA,aAAA;AAAA,EAAA,UAAA,EAAA,MAAA,UAAA;AAAA,EAAA,cAAA,EAAA,MAAA,cAAA;AAAA,EAAA,YAAA,EAAA,MAAA,YAAA;AAAA,EAAA,gBAAA,EAAA,MAAA,gBAAA;AAAA,EAAA,eAAA,EAAA,MAAA;AAAA,CAAA,CAAA;AAIA,IAAM,eAAA,GAAkB,MAAA;AAExB,SAAS,UAAA,GAAyC;AAChD,EAAA,OAAO,KAAA,EAAM,CAAE,UAAA,CAA2B,cAAA,CAAe,eAAe,CAAC,CAAA;AAC3E;AAEA,eAAsB,cAAc,OAAA,EAAiD;AACnF,EAAA,OAAO,MAAM,YAAW,CAAE,OAAA;AAAA,IACxB,EAAE,OAAA,EAAS,MAAA,EAAQ,EAAE,GAAA,EAAK,CAAC,SAAA,EAAW,aAAA,EAAe,SAAS,CAAA,EAAE,EAAE;AAAA,IAClE,EAAE,IAAA,EAAM,EAAE,sBAAA,EAAwB,IAAG;AAAE,GACzC;AACF;AAEA,eAAsB,SAAS,KAAA,EAA+C;AAC5E,EAAA,OAAO,MAAM,UAAA,EAAW,CAAE,QAAQ,EAAE,GAAA,EAAK,OAAO,CAAA;AAClD;AAEA,eAAsB,SAAA,CACpB,OAAA,EACA,MAAA,EACA,KAAA,EACA,gBACA,eAAA,EACiB;AACjB,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,EAAA,MAAM,EAAA,GAAK,IAAI,QAAA,EAAS,CAAE,QAAA,EAAS;AAEnC,EAAA,MAAM,UAAA,GAAa,SAAA,CAAU;AAAA,IAC3B,GAAA,EAAK,EAAA;AAAA,IACL,IAAA,EAAM,cAAA;AAAA,IACN,KAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA,EAAQ,SAAA;AAAA,IACR,QAAQ,MAAA,IAAU,IAAA;AAAA,IAClB,MAAA,EAAQ,IAAA;AAAA,IACR,KAAA,EAAO,IAAA;AAAA,IACP,cAAA;AAAA,IACA,eAAA;AAAA,IACA,QAAA,EAAU;AAAA,MACR,cAAA,EAAgB,CAAA;AAAA,MAChB,gBAAA,EAAkB,CAAA;AAAA,MAClB,WAAA,EAAa,CAAA;AAAA,MACb,UAAA,EAAY,IAAA;AAAA,MACZ,eAAA,EAAiB,IAAA;AAAA,MACjB,YAAA,EAAc;AAAA,KAChB;AAAA,IACA,UAAA,EAAY;AAAA,MACV,SAAA,EAAW,GAAA;AAAA,MACX,SAAA,EAAW,GAAA;AAAA,MACX,SAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAa;AAAA;AACf,GACD,CAAA;AAED,EAAA,OAAO,EAAA;AACT;AAEA,eAAsB,eAAe,KAAA,EAA8B;AACjE,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AAErB,EAAA,MAAM,YAAW,CAAE,SAAA;AAAA,IACjB,EAAE,KAAK,KAAA,EAAM;AAAA,IACb;AAAA,MACE,IAAA,EAAM;AAAA,QACJ,MAAA,EAAQ,aAAA;AAAA,QACR,sBAAA,EAAwB,GAAA;AAAA,QACxB,sBAAA,EAAwB,GAAA;AAAA,QACxB,0BAAA,EAA4B;AAAA;AAC9B;AACF,GACF;AACF;AAEA,eAAsB,gBAAgB,KAAA,EAA8B;AAClE,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AAErB,EAAA,MAAM,YAAW,CAAE,SAAA;AAAA,IACjB,EAAE,KAAK,KAAA,EAAM;AAAA,IACb;AAAA,MACE,IAAA,EAAM;AAAA,QACJ,0BAAA,EAA4B,GAAA;AAAA,QAC5B,sBAAA,EAAwB;AAAA;AAC1B;AACF,GACF;AACF;AAEA,eAAsB,gBAAA,CACpB,KAAA,EACA,UAAA,EACA,MAAA,EACe;AACf,EAAA,MAAM,YAAW,CAAE,SAAA;AAAA,IACjB,EAAE,KAAK,KAAA,EAAM;AAAA,IACb;AAAA,MACE,IAAA,EAAM;AAAA,QACJ,qBAAA,EAAuB,UAAA;AAAA,QACvB,2BAA2B,MAAA,CAAO,cAAA;AAAA,QAClC,6BAA6B,MAAA,CAAO,gBAAA;AAAA,QACpC,wBAAwB,MAAA,CAAO,WAAA;AAAA,QAC/B,sBAAA,sBAA4B,IAAA;AAAK;AACnC;AACF,GACF;AACF;AAEA,eAAsB,cAAc,KAAA,EAA8B;AAChE,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AAErB,EAAA,MAAM,YAAW,CAAE,SAAA;AAAA,IACjB,EAAE,KAAK,KAAA,EAAM;AAAA,IACb;AAAA,MACE,IAAA,EAAM;AAAA,QACJ,MAAA,EAAQ,WAAA;AAAA,QACR,wBAAA,EAA0B,GAAA;AAAA,QAC1B,sBAAA,EAAwB,GAAA;AAAA,QACxB,0BAAA,EAA4B;AAAA;AAC9B;AACF,GACF;AACF;AAEA,eAAsB,UAAA,CAAW,OAAe,KAAA,EAA8B;AAC5E,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AAErB,EAAA,MAAM,YAAW,CAAE,SAAA;AAAA,IACjB,EAAE,KAAK,KAAA,EAAM;AAAA,IACb;AAAA,MACE,IAAA,EAAM;AAAA,QACJ,MAAA,EAAQ,QAAA;AAAA,QACR,KAAA;AAAA,QACA,sBAAA,EAAwB,GAAA;AAAA,QACxB,0BAAA,EAA4B;AAAA;AAC9B;AACF,GACF;AACF;AAEA,eAAsB,YAAY,cAAA,EAAmD;AACnF,EAAA,MAAM,YAAY,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,KAAQ,cAAc,CAAA;AAEtD,EAAA,OAAO,MAAM,UAAA,EAAW,CACrB,IAAA,CAAK;AAAA,IACJ,MAAA,EAAQ,aAAA;AAAA,IACR,0BAAA,EAA4B,EAAE,GAAA,EAAK,SAAA;AAAU,GAC9C,EACA,OAAA,EAAQ;AACb;AAEA,eAAsB,qBAAA,GAAmD;AACvE,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AAErB,EAAA,OAAO,MAAM,UAAA,EAAW,CACrB,IAAA,CAAK;AAAA,IACJ,MAAA,EAAQ,aAAA;AAAA,IACR,0BAAA,EAA4B,EAAE,GAAA,EAAK,IAAA,EAAK;AAAA,IACxC,KAAA,EAAO;AAAA,MACL,GAAA,EAAK;AAAA,QACH,2BAAA;AAAA,QACA,EAAE,SAAA,EAAW,CAAC,GAAA,EAAK,iBAAiB,CAAA;AAAE;AACxC;AACF,GACD,EACA,OAAA,EAAQ;AACb;AAEA,eAAsB,gBAAgB,KAAA,EAA+C;AACnF,EAAA,MAAM,MAAA,GAAS,MAAM,UAAA,EAAW,CAAE,gBAAA;AAAA,IAChC;AAAA,MACE,GAAA,EAAK,KAAA;AAAA,MACL,MAAA,EAAQ;AAAA,KACV;AAAA,IACA;AAAA,MACE,IAAA,EAAM;AAAA,QACJ,MAAA,EAAQ,SAAA;AAAA,QACR,0BAAA,EAA4B,IAAA;AAAA,QAC5B,sBAAA,sBAA4B,IAAA;AAAK,OACnC;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,uBAAA,EAAyB;AAAA;AAC3B,KACF;AAAA,IACA,EAAE,gBAAgB,OAAA;AAAQ,GAC5B;AAEA,EAAA,OAAO,MAAA,IAAU,IAAA;AACnB;AAEA,eAAsB,aAAa,KAAA,EAA8B;AAC/D,EAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AAErB,EAAA,MAAM,YAAW,CAAE,SAAA;AAAA,IACjB,EAAE,KAAK,KAAA,EAAM;AAAA,IACb;AAAA,MACE,IAAA,EAAM;AAAA,QACJ,MAAA,EAAQ,aAAA;AAAA,QACR,0BAAA,EAA4B,GAAA;AAAA,QAC5B,sBAAA,EAAwB;AAAA;AAC1B;AACF,GACF;AACF;;;AC7MA,IAAA,yBAAA,GAAA,EAAA;AAAA,QAAA,CAAA,yBAAA,EAAA;AAAA,EAAA,aAAA,EAAA,MAAA,aAAA;AAAA,EAAA,gBAAA,EAAA,MAAA,gBAAA;AAAA,EAAA,WAAA,EAAA,MAAA;AAAA,CAAA,CAAA;AAIA,IAAM,UAAA,GAAa,oBAAA;AAEnB,SAASE,WAAAA,GAA2C;AAClD,EAAA,OAAO,KAAA,EAAM,CAAE,UAAA,CAA6B,cAAA,CAAe,UAAU,CAAC,CAAA;AACxE;AAEA,eAAsB,gBAAA,CACpB,SACA,GAAA,EAC2B;AAC3B,EAAA,MAAM,MAAA,GAAS,MAAMA,WAAAA,EAAW,CAAE,gBAAA;AAAA,IAChC,EAAE,KAAK,OAAA,EAAQ;AAAA,IACf;AAAA,MACE,YAAA,EAAc;AAAA,QACZ,GAAA,EAAK,OAAA;AAAA,QACL,iBAAA,EAAmB,CAAA;AAAA,QACnB,UAAA,EAAY;AAAA;AACd,KACF;AAAA,IACA,EAAE,MAAA,EAAQ,IAAA,EAAM,cAAA,EAAgB,OAAA;AAAQ,GAC1C;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,eAAsB,WAAA,CACpB,OAAA,EACA,iBAAA,EACA,UAAA,EACe;AACf,EAAA,MAAMA,aAAW,CAAE,SAAA;AAAA,IACjB,EAAE,KAAK,OAAA,EAAQ;AAAA,IACf;AAAA,MACE,IAAA,EAAM;AAAA,QACJ,iBAAA;AAAA,QACA;AAAA;AACF;AACF,GACF;AACF;AAEA,eAAsB,aAAA,GAA+B;AAErD;;;AC7CO,IAAM,wBAAA,GAA2B,GAAA;AACjC,IAAM,qBAAA,GAAwB,GAAA;AAC9B,IAAM,yBAAA,GAA4B,CAAA;;;ACAlC,SAAS,eACd,KAAA,EACgC;AAChC,EAAA,MAAMD,UAAS,SAAA,EAAU;AAEzB,EAAA,MAAM,KAAA,GAAQ,YAAY,YAAY;AACpC,IAAA,IAAI;AACF,MAAA,MAAM,gBAAA,CAAQ,gBAAgB,KAAK,CAAA;AAAA,IACrC,SAAS,GAAA,EAAK;AACZ,MAAAA,OAAAA,CAAO,IAAA;AAAA,QACL,EAAE,KAAA,EAAO,KAAA,EAAO,GAAA,EAAI;AAAA,QACpB;AAAA,OACF;AAAA,IACF;AACA,IAAAA,OAAAA,CAAO,KAAA,CAAM,EAAE,KAAA,IAAS,0BAA0B,CAAA;AAAA,EACpD,GAAG,qBAAqB,CAAA;AAExB,EAAA,KAAA,CAAM,KAAA,EAAM;AACZ,EAAA,OAAO,KAAA;AACT;;;AChBA,eAAsB,mBACpB,OAAA,EACA,KAAA,EACA,SAAA,EACA,MAAA,EACA,eACA,aAAA,EACe;AACf,EAAA,MAAM,MAAM,IAAI,UAAA;AAAA,IACd,OAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,IAAI,MAAA,GAAS,aAAA;AACb,EAAA,IAAI,OAAA,GAAU,IAAA;AAEd,EAAA,OAAO,OAAA,EAAS;AACd,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,eAAA,CAAyB,MAAM,CAAA;AACtD,IAAA,OAAA,GAAU,IAAA,CAAK,OAAA;AACf,IAAA,MAAA,GAAS,IAAA,CAAK,MAAA;AAEd,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AAE5B,IAAA,MAAM,SAAA,GAAY,MAAM,GAAA,CAAI,iBAAA,CAAkB,KAAK,IAAI,CAAA;AACvD,IAAA,MAAM,GAAA,CAAI,aAAa,SAAS,CAAA;AAEhC,IAAA,GAAA,CAAI,MAAA,CAAO,cAAA,IAAkB,IAAA,CAAK,IAAA,CAAK,MAAA;AACvC,IAAA,GAAA,CAAI,MAAA,CAAO,oBAAoB,SAAA,CAAU,MAAA;AACzC,IAAA,GAAA,CAAI,MAAA,CAAO,eAAe,SAAA,CAAU,MAAA;AAEpC,IAAA,MAAM,gBAAA,CAAQ,gBAAA,CAAiB,KAAA,EAAO,MAAA,EAAQ,IAAI,MAAM,CAAA;AACxD,IAAA,GAAA,CAAI,MAAA,CAAO,MAAM,EAAE,OAAA,EAAS,GAAG,GAAA,CAAI,MAAA,IAAU,2BAA2B,CAAA;AAAA,EAC1E;AACF;;;AC1BA,SAAS,aAAa,IAAA,EAAmC;AACvD,EAAA,OACE,OAAO,SAAS,QAAA,IAChB,IAAA,KAAS,QACT,oBAAA,IAAwB,IAAA,IACvB,KAAoB,kBAAA,KAAuB,IAAA;AAEhD;AAEA,eAAsB,qBAAA,CACpB,GAAA,EACA,SAAA,EACA,MAAA,EACe;AACf,EAAA,MAAMA,UAAS,SAAA,EAAU;AACzB,EAAA,MAAM,UAAU,GAAA,CAAI,IAAA;AAEpB,EAAA,IAAI,YAAA,CAAa,GAAA,CAAI,IAAI,CAAA,EAAG;AAC1B,IAAA,OAAO,UAAU,GAAA,CAAI,IAAA,EAAM,OAAA,EAAS,SAAA,EAAW,QAAQA,OAAM,CAAA;AAAA,EAC/D;AAEA,EAAA,OAAO,QAAA,CAAS,GAAA,EAAK,OAAA,EAAS,SAAA,EAAW,QAAQA,OAAM,CAAA;AACzD;AAEA,eAAe,SAAA,CACb,IAAA,EACA,OAAA,EACA,SAAA,EACA,QACAA,OAAAA,EACe;AACf,EAAA,MAAM,EAAE,KAAA,EAAO,UAAA,EAAY,MAAA,EAAO,GAAI,IAAA;AACtC,EAAAA,QAAO,IAAA,CAAK,EAAE,OAAA,EAAS,KAAA,IAAS,qCAAqC,CAAA;AAErE,EAAA,MAAM,gBAAA,CAAQ,aAAa,KAAK,CAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,eAAe,KAAK,CAAA;AAE3C,EAAA,IAAI;AACF,IAAA,MAAM,kBAAA;AAAA,MACJ,OAAA;AAAA,MACA,KAAA;AAAA,MACA,SAAA;AAAA,MACA,MAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAM,gBAAA,CAAQ,cAAc,KAAK,CAAA;AACjC,IAAAA,QAAO,IAAA,CAAK,EAAE,OAAA,EAAS,KAAA,IAAS,sCAAsC,CAAA;AAAA,EACxE,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,UAAU,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AACrE,IAAA,MAAM,gBAAA,CAAQ,UAAA,CAAW,KAAA,EAAO,OAAO,CAAA;AACvC,IAAAA,OAAAA,CAAO,MAAM,EAAE,OAAA,EAAS,OAAO,KAAA,EAAO,OAAA,IAAW,mCAAmC,CAAA;AACpF,IAAA,MAAM,KAAA;AAAA,EACR,CAAA,SAAE;AACA,IAAA,aAAA,CAAc,cAAc,CAAA;AAAA,EAC9B;AACF;AAEA,eAAe,QAAA,CACb,GAAA,EACA,OAAA,EACA,SAAA,EACA,QACAA,OAAAA,EACe;AACf,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,IAAQ,IAAA;AAE3B,EAAA,IAAI,UAAU,YAAA,EAAc;AAC1B,IAAA,MAAM,wBAAwB,OAAO,CAAA;AAAA,EACvC;AAEA,EAAA,MAAM,cAAA,GAAiB,UAAU,cAAA,IAAkB,wBAAA;AACnD,EAAA,MAAM,eAAA,GAAkB,UAAU,eAAA,IAAmB,yBAAA;AACrD,EAAA,MAAM,KAAA,GAAQ,MAAM,gBAAA,CAAQ,SAAA,CAAU,SAAS,MAAA,EAAQ,GAAA,CAAI,EAAA,EAAK,cAAA,EAAgB,eAAe,CAAA;AAC/F,EAAA,MAAM,gBAAA,CAAQ,eAAe,KAAK,CAAA;AAElC,EAAA,MAAM,cAAA,GAAiB,eAAe,KAAK,CAAA;AAE3C,EAAA,IAAI;AACF,IAAA,MAAM,kBAAA;AAAA,MACJ,OAAA;AAAA,MACA,KAAA;AAAA,MACA,SAAA;AAAA,MACA,MAAA;AAAA,MACA,IAAA;AAAA,MACA,EAAE,cAAA,EAAgB,CAAA,EAAG,gBAAA,EAAkB,CAAA,EAAG,aAAa,CAAA;AAAE,KAC3D;AAEA,IAAA,MAAM,gBAAA,CAAQ,cAAc,KAAK,CAAA;AACjC,IAAAA,OAAAA,CAAO,IAAA,CAAK,EAAE,OAAA,IAAW,8BAA8B,CAAA;AAAA,EACzD,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,UAAU,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AACrE,IAAA,MAAM,gBAAA,CAAQ,UAAA,CAAW,KAAA,EAAO,OAAO,CAAA;AACvC,IAAAA,QAAO,KAAA,CAAM,EAAE,SAAS,KAAA,EAAO,OAAA,IAAW,2BAA2B,CAAA;AACrE,IAAA,MAAM,KAAA;AAAA,EACR,CAAA,SAAE;AACA,IAAA,aAAA,CAAc,cAAc,CAAA;AAAA,EAC9B;AACF;AAEA,eAAe,wBAAwB,OAAA,EAAgC;AACrE,EAAA,MAAM,SAAA,GAAY,MAAM,gBAAA,CAAQ,aAAA,CAAc,OAAO,CAAA;AACrD,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,oBAAA,EAAuB,OAAO,CAAA,qCAAA,EAAwC,SAAA,CAAU,MAAM,CAAA,CAAA;AAAA,KACxF;AAAA,EACF;AACF;;;AC3GO,SAAS,cAAc,KAAA,EAAgD;AAC5E,EAAA,MAAM,EAAE,GAAA,EAAK,IAAA,EAAM,YAAY,iBAAA,EAAmB,YAAA,EAAc,QAAO,GAAI,KAAA;AAE3E,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,CAAI,CAAA,EAAG,IAAI,OAAA,EAAQ,GAAI,UAAA,CAAW,OAAA,EAAS,CAAA;AAElE,EAAA,MAAM,UAAA,GAAa,OACd,IAAA,CAAK,MAAA,GAAS,YAAa,WAAA,CAAY,IAAA,CAAK,QAAQ,CAAA,GACrD,CAAA;AAEJ,EAAA,MAAM,cAAc,iBAAA,GAAoB,UAAA;AAExC,EAAA,MAAM,YAAA,GAAe,OACjB,IAAA,CAAK,GAAA,CAAI,aAAa,UAAA,IAAc,CAAA,GAAI,YAAA,GAAe,GAAA,CAAI,CAAA,GAC3D,WAAA;AAEJ,EAAA,MAAM,kBACJ,MAAA,KAAW,MAAA,GAAY,KAAK,GAAA,CAAI,YAAA,EAAc,MAAM,CAAA,GAAI,YAAA;AAE1D,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,eAAe,CAAA;AACzC,EAAA,MAAM,YAAY,YAAA,GAAe,MAAA;AAEjC,EAAA,OAAO,EAAE,QAAQ,SAAA,EAAU;AAC7B;;;AC7BA,eAAsB,qBAAA,CACpB,GAAA,EACA,MAAA,EACA,MAAA,EACe;AACf,EAAA,MAAMA,UAAS,SAAA,EAAU;AACzB,EAAA,MAAM,aAAA,uBAAoB,IAAA,EAAK;AAE/B,EAAA,MAAM,QAAQ,MAAM,yBAAA,CAAgB,gBAAA,CAAiB,GAAA,CAAI,MAAM,aAAa,CAAA;AAE5E,EAAA,MAAM,SAAS,MAAA,CAAO,SAAA,GAAY,MAAM,MAAA,CAAO,WAAU,GAAI,MAAA;AAE7D,EAAA,MAAM,EAAE,MAAA,EAAQ,SAAA,EAAU,GAAI,aAAA,CAAc;AAAA,IAC1C,GAAA,EAAK,aAAA;AAAA,IACL,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,YAAY,KAAA,CAAM,UAAA;AAAA,IAClB,mBAAmB,KAAA,CAAM,iBAAA;AAAA,IACzB,YAAA,EAAc,OAAO,YAAA,IAAgB,GAAA;AAAA,IACrC;AAAA,GACD,CAAA;AAED,EAAA,IAAI,WAAW,CAAA,EAAG;AAChB,IAAA,MAAM,yBAAA,CAAgB,WAAA,CAAY,GAAA,CAAI,IAAA,EAAM,WAAW,aAAa,CAAA;AACpE,IAAAA,OAAAA,CAAO,MAAM,EAAE,OAAA,EAAS,IAAI,IAAA,EAAM,SAAA,IAAa,8CAA8C,CAAA;AAC7F,IAAA;AAAA,EACF;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AACrC,IAAA,MAAM,UAAA,GAAa,MAAM,SAAA,CAAU,KAAA,CAAM,GAAA;AAAA,MACvC,CAAA,EAAG,GAAA,CAAI,IAAI,CAAA,EAAG,2BAA2B,CAAA,CAAA;AAAA,MACzC;AAAA,KACF;AAKA,IAAA,MAAM,SAAS,MAAM,UAAA,CAAW,kBAAkB,SAAA,CAAU,MAAA,EAAQ,OAAO,cAAc,CAAA;AAEzF,IAAA,MAAM,QAAA,GAAW,OAAO,MAAA,KAAW,QAAA,GAAW,MAAA,GAAS,MAAA;AACvD,IAAA,MAAM,WAAA,GAAc,aAAa,MAAA,GAAS,QAAA,CAAA;AAE1C,IAAA,MAAM,yBAAA,CAAgB,WAAA,CAAY,GAAA,CAAI,IAAA,EAAM,aAAa,aAAa,CAAA;AACtE,IAAAA,OAAAA,CAAO,IAAA,CAAK,EAAE,OAAA,EAAS,GAAA,CAAI,MAAM,MAAA,EAAQ,QAAA,EAAU,WAAA,EAAY,EAAG,8BAA8B,CAAA;AAAA,EAClG,SAAS,KAAA,EAAO;AACd,IAAAA,OAAAA,CAAO,IAAA,CAAK,EAAE,OAAA,EAAS,GAAA,CAAI,IAAA,EAAM,KAAA,EAAO,MAAA,CAAO,KAAK,CAAA,EAAE,EAAG,sDAAsD,CAAA;AAAA,EACjH;AACF;;;AC1CO,SAAS,QAAA,CACd,QACA,MAAA,EAIA;AAKA,EAAA,MAAM,YAA+F,EAAC;AACtG,EAAA,MAAM,aAAwC,EAAC;AAE/C,EAAA,KAAA,MAAW,CAAC,SAAS,SAAS,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA,EAAG;AAC9D,IAAA,UAAA,CAAW,OAAO,CAAA,GAAI,SAAA;AAEtB,IAAA,MAAM,aAAA,GAAgB,SAAA,CAAU,KAAA,IAAS,MAAA,CAAO,YAAA;AAEhD,IAAA,SAAA,CAAU,OAAO,CAAA,GAAI;AAAA,MACnB,IAAA,EAAM,OAAO,MAAA,KAAgB;AAC3B,QAAA,OAAO,OAAA,CAAQ,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,aAAa,CAAA;AAAA,MACvD,CAAA;AAAA,MACA,WAAA,EAAa,OAAO,MAAA,KAAgB;AAClC,QAAA,OAAO,cAAA,CAAe,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,aAAa,CAAA;AAAA,MAC9D;AAAA,KACF;AAAA,EACF;AAGA,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAEA,IAAM,YAAA,GAAe;AAAA,EACnB,kBAAA;AAAA,EACA,oBAAA;AAAA,EACA,eAAA;AAAA,EACA;AACF,CAAA;AAEO,SAAS,cAAA,CACd,MACA,UAAA,EAC6C;AAC7C,EAAA,KAAA,MAAW,UAAU,YAAA,EAAc;AACjC,IAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG;AAE5B,IAAA,MAAM,WAAW,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAC,OAAO,MAAM,CAAA;AAC7C,IAAA,IAAI,UAAA,CAAW,QAAQ,CAAA,EAAG;AACxB,MAAA,OAAO,EAAE,UAAU,MAAA,EAAO;AAAA,IAC5B;AAAA,EACF;AACA,EAAA,OAAO,EAAE,QAAA,EAAU,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAK;AACxC;AAEA,eAAsB,UAAA,CACpB,GAAA,EACA,UAAA,EACA,MAAA,EACA,YAAA,EACA;AACA,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,KAAW,cAAA,CAAe,GAAA,CAAI,MAAM,UAAuC,CAAA;AAC7F,EAAA,MAAM,SAAA,GAAY,YAAA,CAAa,QAAA,EAAU,UAAU,CAAA;AACnD,EAAA,MAAM,aAAA,GAAgB,UAAU,KAAA,IAAS,YAAA;AAGzC,EAAA,IAAI,WAAW,IAAA,EAAM;AACnB,IAAA,IAAI,SAAA,CAAU,SAAS,iBAAA,EAAmB;AACxC,MAAA,IAAI,WAAW,kBAAA,EAAoB;AACjC,QAAA,OAAO,MAAM,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA,MAChD;AACA,MAAA,IAAI,WAAW,oBAAA,EAAsB;AACnC,QAAA,OAAO,MAAM,SAAA,CAAU,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAAA,MAC3C;AACA,MAAA,IAAI,WAAW,eAAA,EAAiB;AAC9B,QAAA,OAAO,MAAM,SAAA,CAAU,IAAA,CAAK,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,MAC3C;AAAA,IACF;AAEA,IAAA,IAAI,SAAA,CAAU,SAAS,cAAA,EAAgB;AACrC,MAAA,IAAI,WAAW,kBAAA,EAAoB;AACjC,QAAA,OAAO,MAAM,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA,MAChD;AACA,MAAA,IAAI,WAAW,oBAAA,EAAsB;AACnC,QAAA,OAAO,MAAM,SAAA,CAAU,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAAA,MAC3C;AACA,MAAA,IAAI,WAAW,eAAA,EAAiB;AAC9B,QAAA,OAAO,MAAM,SAAA,CAAU,IAAA,CAAK,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,MAC3C;AAAA,IACF;AAEA,IAAA,IAAI,SAAA,CAAU,SAAS,cAAA,EAAgB;AACrC,MAAA,IAAI,WAAW,2BAAA,EAA6B;AAC1C,QAAA,OAAO,MAAM,SAAA,CAAU,OAAA,CAAQ,GAAA,CAAI,IAAI,CAAA;AAAA,MACzC;AAAA,IACF;AAEA,IAAA,MAAM,IAAI,MAAM,CAAA,2BAAA,EAA8B,MAAM,SAAS,QAAQ,CAAA,QAAA,EAAW,SAAA,CAAU,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACnG;AAGA,EAAA,IAAI,SAAA,CAAU,SAAS,QAAA,EAAU;AAC/B,IAAA,OAAO,MAAM,gBAAA,CAAiB,GAAA,EAAK,SAAS,CAAA;AAAA,EAC9C;AAEA,EAAA,IAAI,SAAA,CAAU,SAAS,YAAA,EAAc;AACnC,IAAA,OAAO,MAAM,mBAAA,CAAoB,GAAA,EAAK,SAAS,CAAA;AAAA,EACjD;AAEA,EAAA,IAAI,SAAA,CAAU,SAAS,iBAAA,EAAmB;AACxC,IAAA,OAAO,MAAM,yBAAyB,GAAA,EAAK,EAAE,GAAG,SAAA,EAAW,KAAA,EAAO,aAAA,EAAc,EAAG,MAAM,CAAA;AAAA,EAC3F;AAEA,EAAA,IAAI,SAAA,CAAU,SAAS,cAAA,EAAgB;AACrC,IAAA,OAAO,MAAM,sBAAsB,GAAA,EAAK,EAAE,GAAG,SAAA,EAAW,KAAA,EAAO,aAAA,EAAc,EAAG,MAAM,CAAA;AAAA,EACxF;AAEA,EAAA,IAAI,SAAA,CAAU,SAAS,cAAA,EAAgB;AACrC,IAAA,OAAO,MAAM,sBAAsB,GAAA,EAAK,EAAE,GAAG,SAAA,EAAW,KAAA,EAAO,aAAA,EAAc,EAAG,MAAM,CAAA;AAAA,EACxF;AAEA,EAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,GAAA,CAAI,IAAI,CAAA,CAAE,CAAA;AACrD;AAEA,SAAS,YAAA,CACP,SACA,UAAA,EACW;AACX,EAAA,MAAM,MAAA,GAAS,WAAW,OAA4B,CAAA;AACtD,EAAA,IAAI,CAAC,MAAA,EAAQ,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,OAAO,CAAA,CAAE,CAAA;AAChE,EAAA,OAAO,MAAA;AACT;;;ACnJA,IAAM,eAAA,GAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AASxB,IAAM,mBAAA,GAAsB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AA+B5B,SAAS,UAAU,GAAA,EAAqB;AACtC,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,GAAG,CAAA;AACvB,IAAA,IAAI,GAAA,CAAI,QAAA,EAAU,GAAA,CAAI,QAAA,GAAW,KAAA;AACjC,IAAA,OAAO,IAAI,QAAA,EAAS;AAAA,EACtB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,GAAA;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,MAAA,EAA4B;AAC5D,EAAA,MAAM,EAAE,SAAA,EAAW,WAAA,EAAa,KAAA,EAAO,KAAA,EAAO,MAAK,GAAI,MAAA;AAEvD,EAAA,OAAA,CAAQ,GAAA,CAAI,SAAS,mBAAmB,CAAA;AACxC,EAAA,OAAA,CAAQ,GAAA,CAAI,OAAO,eAAe,CAAA;AAClC,EAAA,OAAA,CAAQ,GAAA,CAAI,QAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAA;AAE1B,EAAA,OAAA,CAAQ,IAAI,YAAY,CAAA;AACxB,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,kBAAA,EAAqB,SAAS,CAAA,CAAE,CAAA;AAC5C,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,kBAAA,EAAqB,WAAW,CAAA,CAAE,CAAA;AAC9C,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,kBAAA,EAAqB,KAAA,CAAM,IAAI,CAAA,CAAA,EAAI,MAAM,IAAI,CAAA,CAAA,EAAI,KAAA,CAAM,EAAE,CAAA,CAAE,CAAA;AACvE,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,kBAAA,EAAqB,SAAA,CAAU,KAAK,CAAC,CAAA,CAAE,CAAA;AAAA,EACrD;AAEA,EAAA,OAAA,CAAQ,IAAI,qBAAqB,CAAA;AAEjC,EAAA,MAAM,cAAwB,EAAC;AAC/B,EAAA,MAAM,WAA6C,EAAC;AAEpD,EAAA,KAAA,MAAW,CAAC,OAAA,EAAS,SAAS,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AACvD,IAAA,IAAI,UAAU,IAAA,EAAM;AAClB,MAAA,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,SAAS,IAAA,EAAM,SAAA,CAAU,MAAM,CAAA;AAAA,IACvD,CAAA,MAAO;AACL,MAAA,WAAA,CAAY,KAAK,OAAO,CAAA;AAAA,IAC1B;AAAA,EACF;AAEA,EAAA,IAAI,WAAA,CAAY,SAAS,CAAA,EAAG;AAC1B,IAAA,WAAA,CAAY,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC/B,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,IAAA,EAAO,OAAO,CAAA,CAAE,CAAA;AAAA,IAC9B,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,IAAA,QAAA,CAAS,OAAA,CAAQ,CAAC,EAAE,IAAA,EAAM,MAAK,KAAM;AACnC,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,IAAA,EAAO,IAAI,CAAA,SAAA,EAAO,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,IACvC,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAA,CAAQ,GAAA,CAAI,IAAA,GAAO,QAAA,CAAI,MAAA,CAAO,EAAE,CAAC,CAAA;AACjC,EAAA,OAAA,CAAQ,IAAI,4BAA4B,CAAA;AAC1C;;;AC5EO,IAAM,YAAA,GAAe;AAAA,EAC1B,UAAA,EAAY,CAAA;AAAA,EACZ,YAAA,EAAc,CAAA;AAAA,EACd,eAAA,EAAiB,EAAA;AAAA,EACjB,cAAA,EAAgB,EAAA;AAAA,EAChB,QAAA,EAAU,EAAA;AAAA,EACV,OAAA,EAAS,IAAA;AAAA,EACT,QAAA,EAAU,KAAA;AAAA,EACV,SAAA,EAAW,KAAA;AAAA,EACX,SAAA,EAAW;AACb;AAoBO,IAAM,sBAAA,GAAgE,OAAO,MAAA,CAAO;AAAA,EACzF,eAAe,YAAA,CAAa;AAC9B,CAAC;AAaM,SAAS,YAAA,CACd,SAAA,EACA,UAAA,EACA,WAAA,EAEAE,aACA,UAAA,EACQ;AACR,EAAA,MAAM,MAAA,GAAS,IAAI,MAAA,CAAO,SAAA,EAAWA,WAAAA,EAAY;AAAA,IAC/C,GAAG,UAAA;AAAA,IACH,UAAA;AAAA,IACA,OAAA,EAAS,KAAA;AAAA,IACT,WAAA;AAAA,IACA,OAAA,EAAgC;AAAA,GACjC,CAAA;AACD,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,iBAAA,CACd,QACA,SAAA,EACM;AACN,EAAA,MAAMF,UAAS,SAAA,EAAU;AACzB,EAAA,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAC,GAAA,KAAQ;AAC3B,IAAAA,QAAO,IAAA,CAAK,CAAA,CAAA,EAAI,SAAS,CAAA,EAAA,EAAK,GAAA,CAAI,IAAI,CAAA,SAAA,CAAW,CAAA;AAAA,EACnD,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,EAAA,CAAG,WAAA,EAAa,CAAC,GAAA,KAAQ;AAC9B,IAAAA,QAAO,IAAA,CAAK,CAAA,CAAA,EAAI,SAAS,CAAA,EAAA,EAAK,GAAA,CAAI,IAAI,CAAA,YAAA,CAAc,CAAA;AAAA,EACtD,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAC,GAAA,EAAK,KAAA,KAAU;AAClC,IAAAA,OAAAA,CAAO,KAAA,CAAM,CAAA,CAAA,EAAI,SAAS,CAAA,EAAA,EAAK,KAAK,IAAI,CAAA,WAAA,EAAc,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,EACvE,CAAC,CAAA;AACH;AAEO,SAAS,WAAA,CACd,SAAA,EACA,WAAA,EACA,UAAA,EACA,YACA,QAAA,EACM;AACN,EAAA,MAAM,SAAA,GAAY;AAAA,IAChB,MACE,OAAO,UAAA,KAAe,YAAY,MAAA,IAAU,UAAA,GACvC,WAAW,IAAA,GACZ,WAAA;AAAA,IACN,MACE,OAAO,UAAA,KAAe,YAAY,MAAA,IAAU,UAAA,GACvC,WAAW,IAAA,GACZ,IAAA;AAAA,IACN,IACE,OAAO,UAAA,KAAe,YAAY,IAAA,IAAQ,UAAA,GACrC,WAAW,EAAA,GACZ;AAAA,GACR;AAEA,EAAA,iBAAA,CAAkB;AAAA,IAChB,SAAA;AAAA,IACA,WAAA;AAAA,IACA,KAAA,EAAO,SAAA;AAAA,IACP,KAAA,EAAO,QAAA;AAAA,IACP,IAAA,EAAM;AAAA,GACP,CAAA;AACH;AAEA,eAAsB,aAAa,OAAA,EAAkC;AACnE,EAAA,MAAM,OAAA,CAAQ,IAAI,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,KAAA,EAAO,CAAC,CAAA;AACjD;;;AChIA,eAAsB,iBAAA,CACpB,OACA,UAAA,EACe;AACf,EAAA,MAAMA,UAAS,SAAA,EAAU;AACzB,EAAAA,OAAAA,CAAO,KAAK,kBAAkB,CAAA;AAE9B,EAAA,MAAM,qBAAgD,EAAC;AACvD,EAAA,KAAA,MAAW,WAAW,UAAA,EAAY;AAChC,IAAA,IAAI,UAAA,CAAW,OAAO,CAAA,CAAE,IAAA,EAAM;AAC5B,MAAA,kBAAA,CAAmB,OAAO,CAAA,GAAI,UAAA,CAAW,OAAO,CAAA;AAAA,IAClD;AAAA,EACF;AAEA,EAAA,MAAM,uBAAA,GAA0B,MAAM,KAAA,CAAM,gBAAA,EAAiB;AAC7D,EAAA,MAAM,qBAAA,GAAwB,IAAI,GAAA,CAAI,uBAAA,CAAwB,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,CAAC,CAAA;AAE/E,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,KAAA,MAAW,gBAAgB,uBAAA,EAAyB;AAClD,IAAA,IAAI,CAAC,kBAAA,CAAmB,YAAA,CAAa,GAAG,CAAA,EAAG;AACzC,MAAA,OAAA,CAAQ,IAAA,CAAK,aAAa,GAAG,CAAA;AAC7B,MAAA,MAAM,KAAA,CAAM,kBAAA,CAAmB,YAAA,CAAa,GAAG,CAAA;AAAA,IACjD;AAAA,EACF;AAEA,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,KAAA,MAAW,WAAW,kBAAA,EAAoB;AACxC,IAAA,MAAM,SAAA,GAAY,mBAAmB,OAAO,CAAA;AAC5C,IAAA,IAAI,CAAC,WAAW,IAAA,EAAM;AAEtB,IAAA,IAAI,qBAAA,CAAsB,GAAA,CAAI,OAAO,CAAA,EAAG;AACtC,MAAA,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA,IACtB,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,KAAK,OAAO,CAAA;AAAA,IACtB;AAEA,IAAA,MAAM,MAAM,kBAAA,CAAmB,OAAA,EAAS,EAAE,OAAA,EAAS,SAAA,CAAU,MAAM,CAAA;AAAA,EACrE;AAEA,EAAA,KAAA,MAAW,QAAQ,OAAA,EAAS;AAC1B,IAAAA,OAAAA,CAAO,KAAK,CAAA,IAAA,EAAO,IAAI,WAAM,kBAAA,CAAmB,IAAI,CAAA,CAAE,IAAI,CAAA,CAAE,CAAA;AAAA,EAC9D;AACA,EAAA,KAAA,MAAW,QAAQ,OAAA,EAAS;AAC1B,IAAAA,OAAAA,CAAO,KAAK,CAAA,SAAA,EAAO,IAAI,WAAM,kBAAA,CAAmB,IAAI,CAAA,CAAE,IAAI,CAAA,CAAE,CAAA;AAAA,EAC9D;AACA,EAAA,KAAA,MAAW,QAAQ,OAAA,EAAS;AAC1B,IAAAA,OAAAA,CAAO,IAAA,CAAK,CAAA,IAAA,EAAO,IAAI,CAAA,CAAE,CAAA;AAAA,EAC3B;AACA,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,IAAK,OAAA,CAAQ,WAAW,CAAA,IAAK,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxE,IAAAA,OAAAA,CAAO,KAAK,uBAAuB,CAAA;AAAA,EACrC;AACF;;;ACvDO,IAAM,WAAA,GAAc,eAAA;AACpB,IAAM,YAAA,GAAe,gBAAA;;;ACO5B,eAAsB,gBAAA,CACpB,kBAAA,EACA,UAAA,EACA,WAAA,EACe;AACf,EAAA,MAAMA,UAAS,SAAA,EAAU;AACzB,EAAA,MAAM,UAAA,GAAa,MAAM,gBAAA,CAAQ,qBAAA,EAAsB;AAEvD,EAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAClC,IAAA,MAAM,KAAA,GAAQ,kBAAA,CAAmB,GAAA,CAAI,SAAA,CAAU,OAAO,CAAA;AACtD,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAAA,OAAAA,CAAO,IAAA;AAAA,QACL,EAAE,KAAA,EAAO,SAAA,CAAU,GAAA,EAAK,OAAA,EAAS,UAAU,OAAA,EAAQ;AAAA,QACnD;AAAA,OACF;AACA,MAAA,MAAM,gBAAA,CAAQ,UAAA;AAAA,QACZ,SAAA,CAAU,GAAA;AAAA,QACV;AAAA,OACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,KAAA,CAAM,KAAK,CAAA;AACxC,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAAA,OAAAA,CAAO,KAAA;AAAA,QACL,EAAE,SAAA,EAAW,KAAA,CAAM,KAAA,EAAO,KAAA,EAAO,UAAU,GAAA,EAAI;AAAA,QAC/C;AAAA,OACF;AACA,MAAA,MAAM,gBAAA,CAAQ,UAAA;AAAA,QACZ,SAAA,CAAU,GAAA;AAAA,QACV,CAAA,OAAA,EAAU,MAAM,KAAK,CAAA,iCAAA;AAAA,OACvB;AACA,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,MAAM,kBAAA,CAAmB,SAAA,CAAU,KAAA,EAAO,SAAA,CAAU,KAAK,CAAA,EAAG;AAC9D,MAAAA,OAAAA,CAAO,KAAA;AAAA,QACL,EAAE,KAAA,EAAO,SAAA,CAAU,GAAA,EAAK,KAAA,EAAO,UAAU,KAAA,EAAM;AAAA,QAC/C;AAAA,OACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,OAAA,GAAU,MAAM,gBAAA,CAAQ,eAAA,CAAgB,UAAU,GAAG,CAAA;AAC3D,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,MAAM,WAAA,CAAY,IAAI,YAAA,EAAc;AAAA,MAClC,OAAO,OAAA,CAAQ,GAAA;AAAA,MACf,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB,WAAW,KAAA,CAAM,KAAA;AAAA,MACjB,iBAAiB,OAAA,CAAQ,eAAA;AAAA,MACzB,YAAA,EAAc,QAAQ,QAAA,CAAS,YAAA;AAAA,MAC/B,UAAA,EAAY,QAAQ,QAAA,CAAS,UAAA;AAAA,MAC7B,cAAA,EAAgB,QAAQ,QAAA,CAAS,cAAA;AAAA,MACjC,gBAAA,EAAkB,QAAQ,QAAA,CAAS,gBAAA;AAAA,MACnC,WAAA,EAAa,QAAQ,QAAA,CAAS;AAAA,KACF,CAAA;AAAA,EAChC;AACF;AAEA,eAAe,kBAAA,CACb,OACA,KAAA,EACkB;AAClB,EAAA,IAAI;AACF,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,CAAM,WAAA,CAAY,KAAK,CAAA;AAC3C,IAAA,OAAO,KAAA,KAAU,QAAA;AAAA,EACnB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;;;ACvEA,eAAsB,gBAAA,CACpB,MACA,UAAA,EACe;AACf,EAAA,MAAMA,UAAS,SAAA,EAAU;AAEzB,EAAA,IAAI,IAAA,CAAK,YAAA,GAAe,IAAA,CAAK,eAAA,EAAiB;AAC5C,IAAAA,OAAAA,CAAO,IAAA;AAAA,MACL;AAAA,QACE,OAAO,IAAA,CAAK,KAAA;AAAA,QACZ,SAAS,IAAA,CAAK,OAAA;AAAA,QACd,cAAc,IAAA,CAAK;AAAA,OACrB;AAAA,MACA;AAAA,KACF;AACA,IAAA,MAAM,gBAAA,CAAQ,UAAA;AAAA,MACZ,IAAA,CAAK,KAAA;AAAA,MACL,CAAA,+BAAA,EAAkC,KAAK,YAAY,CAAA,QAAA;AAAA,KACrD;AACA,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,IAAA,CAAK,SAAS,CAAA;AAC3C,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAAA,OAAAA,CAAO,KAAA;AAAA,MACL,EAAE,SAAA,EAAW,IAAA,CAAK,SAAA,EAAW,KAAA,EAAO,KAAK,KAAA,EAAM;AAAA,MAC/C;AAAA,KACF;AACA,IAAA,MAAM,gBAAA,CAAQ,UAAA;AAAA,MACZ,IAAA,CAAK,KAAA;AAAA,MACL,CAAA,OAAA,EAAU,KAAK,SAAS,CAAA,WAAA;AAAA,KAC1B;AACA,IAAA;AAAA,EACF;AAEA,EAAAA,OAAAA,CAAO,IAAA;AAAA,IACL;AAAA,MACE,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,YAAY,IAAA,CAAK;AAAA,KACnB;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,SAAA,CAAU,KAAA,CAAM,GAAA,CAAI,IAAA,CAAK,OAAA,EAAS;AAAA,IACtC,kBAAA,EAAoB,IAAA;AAAA,IACpB,OAAO,IAAA,CAAK,KAAA;AAAA,IACZ,YAAY,IAAA,CAAK,UAAA;AAAA,IACjB,MAAA,EAAQ;AAAA,MACN,gBAAgB,IAAA,CAAK,cAAA;AAAA,MACrB,kBAAkB,IAAA,CAAK,gBAAA;AAAA,MACvB,aAAa,IAAA,CAAK;AAAA;AACpB,GACD,CAAA;AACH;;;ACxDO,IAAM,eAAN,MAAmB;AAAA,EAKxB,WAAA,CACE,UAAA,EACA,UAAA,EACA,YAAA,EACA,WAAA,EACA;AACA,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAClB,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AAEnB,IAAA,IAAA,CAAK,kBAAA,uBAAyB,GAAA,EAAI;AAClC,IAAA,KAAA,MAAW,CAAC,OAAA,EAAS,MAAM,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC1D,MAAA,IAAI,MAAA,CAAO,SAAS,cAAA,EAAgB;AAClC,QAAA,IAAA,CAAK,kBAAA,CAAmB,IAAI,OAAA,EAAS;AAAA;AAAA,UAEnC,MAAA;AAAA,UACA,KAAA,EAAO,OAAO,KAAA,IAAS;AAAA,SACxB,CAAA;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAAW,OAAA,EAA0B;AACnC,IAAA,OAAO,OAAA,KAAY,eAAe,OAAA,KAAY,YAAA;AAAA,EAChD;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI,IAAA,CAAK,kBAAA,CAAmB,IAAA,KAAS,CAAA,EAAG;AAExC,IAAA,MAAMA,UAAS,SAAA,EAAU;AACzB,IAAAA,OAAAA,CAAO,IAAA;AAAA,MACL,EAAE,MAAM,CAAC,GAAG,KAAK,kBAAA,CAAmB,IAAA,EAAM,CAAA,EAAE;AAAA,MAC5C;AAAA,KACF;AAEA,IAAA,MAAM,IAAA,CAAK,WAAA,CAAY,kBAAA,CAAmB,WAAA,EAAa;AAAA,MACrD,OAAA,EAAS;AAAA,KACV,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,IAAA,GAAsB;AAC1B,IAAA,MAAM,IAAA,CAAK,WAAA,CAAY,kBAAA,CAAmB,WAAW,CAAA;AAAA,EACvD;AAAA,EAEA,MAAM,QAAQ,GAAA,EAAyB;AACrC,IAAA,QAAQ,IAAI,IAAA;AAAM,MAChB,KAAK,WAAA;AACH,QAAA,OAAO,gBAAA;AAAA,UACL,IAAA,CAAK,kBAAA;AAAA,UACL,IAAA,CAAK,UAAA;AAAA,UACL,IAAA,CAAK;AAAA,SACP;AAAA,MACF,KAAK,YAAA;AACH,QAAA,OAAO,gBAAA;AAAA,UACL,GAAA,CAAI,IAAA;AAAA,UACJ,IAAA,CAAK;AAAA,SACP;AAAA;AACJ,EACF;AACF,CAAA;;;ACnEO,IAAe,aAAf,MAA8C;AAAA,EAGnD,WAAA,CAAYG,KAAQ,iBAAA,EAA2B;AAC7C,IAAA,IAAA,CAAK,UAAA,GAAaA,GAAAA,CAAG,UAAA,CAAc,cAAA,CAAe,iBAAiB,CAAC,CAAA;AAAA,EACtE;AAAA,EAIA,MAAM,IAAA,GAAsB;AAC1B,IAAA,KAAA,MAAW,GAAA,IAAO,IAAA,CAAK,OAAA,EAAQ,EAAG;AAChC,MAAA,MAAM,KAAK,UAAA,CAAW,WAAA,CAAY,GAAA,CAAI,IAAA,EAAM,IAAI,OAAO,CAAA;AAAA,IACzD;AAAA,EACF;AACF;;;ACNA,IAAMC,WAAAA,GAAa,SAAA;AAEZ,IAAM,WAAA,GAAN,cAA0B,UAAA,CAAsB;AAAA,EACrD,YAAYD,GAAAA,EAAQ;AAClB,IAAA,KAAA,CAAMA,KAAIC,WAAU,CAAA;AAAA,EACtB;AAAA,EAEU,OAAA,GAA6B;AACrC,IAAA,OAAO;AAAA,MACL;AAAA,QACE,IAAA,EAAM,EAAE,QAAA,EAAU,CAAA,EAAE;AAAA,QACpB,OAAA,EAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,MAAM,iBAAA;AAAkB,OACnD;AAAA,MACA;AAAA,QACE,IAAA,EAAM,EAAE,eAAA,EAAiB,CAAA,EAAE;AAAA,QAC3B,OAAA,EAAS,EAAE,IAAA,EAAM,qBAAA;AAAsB;AACzC,KACF;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,KAAA,EAA2C;AAC9D,IAAA,IAAA,CAAK,sBAAsB,KAAK,CAAA;AAChC,IAAA,MAAM,MAAA,GAA4B,EAAE,QAAA,EAAU,KAAA,CAAM,QAAA,EAAS;AAC7D,IAAA,MAAM,MAAA,GAAgC;AAAA,MACpC,YAAA,EAAc;AAAA,QACZ,eAAA,EAAiB,IAAA;AAAA,QACjB,SAAA,EAAW;AAAA,OACb;AAAA,MACA,YAAA,EAAc;AAAA,QACZ,UAAU,KAAA,CAAM,QAAA;AAAA,QAChB,UAAU,KAAA,CAAM,QAAA;AAAA,QAChB,KAAK,KAAA,CAAM,GAAA;AAAA,QACX,QAAQ,KAAA,CAAM,MAAA;AAAA,QACd,WAAW,KAAA,CAAM,SAAA;AAAA,QACjB,SAAA,sBAAe,IAAA;AAAK;AACtB,KACF;AACA,IAAA,MAAM,IAAA,CAAK,WAAW,SAAA,CAAU,MAAA,EAAQ,QAAQ,EAAE,MAAA,EAAQ,MAAM,CAAA;AAAA,EAClE;AAAA,EAEA,MAAM,gBAAgB,QAAA,EAAiC;AACrD,IAAA,IAAI,OAAO,QAAA,KAAa,QAAA,IAAY,QAAA,CAAS,WAAW,CAAA,EAAG;AACzD,MAAA,MAAM,IAAI,MAAM,kEAAkE,CAAA;AAAA,IACpF;AACA,IAAA,MAAM,MAAA,GAA4B,EAAE,QAAA,EAAS;AAC7C,IAAA,MAAM,MAAA,GAAgC;AAAA,MACpC,YAAA,EAAc,EAAE,eAAA,EAAiB,IAAA,EAAM,WAAW,IAAA;AAAK,KACzD;AACA,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,UAAA,CAAW,SAAA,CAAU,QAAQ,MAAM,CAAA;AAC7D,IAAA,IAAI,MAAA,CAAO,iBAAiB,CAAA,EAAG;AAC7B,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV,EAAE,QAAA,EAAS;AAAA,QACX;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,SAAS,KAAA,EAA8C;AAC3D,IAAA,IAAI,OAAO,KAAA,CAAM,QAAA,KAAa,YAAY,KAAA,CAAM,QAAA,CAAS,WAAW,CAAA,EAAG;AACrE,MAAA,MAAM,IAAI,MAAM,2DAA2D,CAAA;AAAA,IAC7E;AACA,IAAA,IAAA,CAAK,kBAAA,CAAmB,KAAA,CAAM,KAAA,EAAO,sBAAsB,CAAA;AAE3D,IAAA,MAAM,cAAA,GAAoC,EAAE,QAAA,EAAU,KAAA,CAAM,QAAA,EAAS;AACrE,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,UAAA,CAAW,QAAQ,cAAc,CAAA;AAE7D,IAAA,IAAI,aAAa,IAAA,EAAM;AACrB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,oDAAA,EAAuD,MAAM,QAAQ,CAAA;AAAA,OACvE;AAAA,IACF;AAEA,IAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,MAAA,IAAU,EAAC;AAC3C,IAAA,MAAM,UAAA,GAAa,eAAe,SAAA,CAAU,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA;AAC9E,IAAA,MAAM,YACJ,UAAA,KAAe,EAAA,GACX,CAAC,GAAG,cAAA,EAAgB,MAAM,KAAK,CAAA,GAC/B,cAAA,CAAe,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,KAAO,MAAM,UAAA,GAAa,KAAA,CAAM,QAAQ,CAAE,CAAA;AAEvE,IAAA,MAAM,MAAA,GAAgC;AAAA,MACpC,IAAA,EAAM,EAAE,MAAA,EAAQ,SAAA,EAAU;AAAA,MAC1B,YAAA,EAAc,EAAE,SAAA,EAAW,IAAA;AAAK,KAClC;AACA,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,SAAA,CAAU,cAAA,EAAgB,MAAM,CAAA;AAAA,EACxD;AAAA,EAEA,MAAM,iBAAiB,QAAA,EAAiC;AACtD,IAAA,IAAI,OAAO,QAAA,KAAa,QAAA,IAAY,QAAA,CAAS,WAAW,CAAA,EAAG;AACzD,MAAA,MAAM,IAAI,MAAM,mEAAmE,CAAA;AAAA,IACrF;AACA,IAAA,MAAM,MAAA,GAA4B,EAAE,QAAA,EAAS;AAC7C,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,SAAA,CAAU,MAAM,CAAA;AAAA,EACxC;AAAA,EAEA,MAAM,UAAU,iBAAA,EAAiD;AAC/D,IAAA,IAAA,CAAK,yBAAA,CAA0B,mBAAmB,uBAAuB,CAAA;AACzE,IAAA,MAAM,YAAY,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,KAAQ,iBAAiB,CAAA;AACzD,IAAA,MAAM,SAA4B,EAAE,eAAA,EAAiB,EAAE,IAAA,EAAM,WAAU,EAAE;AACzE,IAAA,OAAO,MAAM,IAAA,CAAK,UAAA,CACf,IAAA,CAAK,MAAM,CAAA,CACX,IAAA,CAAK,EAAE,eAAA,EAAiB,EAAA,EAAI,CAAA,CAC5B,OAAA,EAAQ;AAAA,EACb;AAAA,EAEA,MAAM,aAAa,iBAAA,EAA4C;AAC7D,IAAA,IAAA,CAAK,yBAAA,CAA0B,mBAAmB,0BAA0B,CAAA;AAC5E,IAAA,MAAM,YAAY,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,KAAQ,iBAAiB,CAAA;AACzD,IAAA,MAAM,SAA4B,EAAE,eAAA,EAAiB,EAAE,GAAA,EAAK,WAAU,EAAE;AACxE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,UAAA,CAAW,WAAW,MAAM,CAAA;AACtD,IAAA,OAAO,OAAO,YAAA,IAAgB,CAAA;AAAA,EAChC;AAAA,EAEQ,sBAAsB,KAAA,EAAkC;AAC9D,IAAA,IAAI,OAAO,KAAA,CAAM,QAAA,KAAa,YAAY,KAAA,CAAM,QAAA,CAAS,WAAW,CAAA,EAAG;AACrE,MAAA,MAAM,IAAI,MAAM,iEAAiE,CAAA;AAAA,IACnF;AACA,IAAA,IAAI,OAAO,KAAA,CAAM,QAAA,KAAa,YAAY,KAAA,CAAM,QAAA,CAAS,WAAW,CAAA,EAAG;AACrE,MAAA,MAAM,IAAI,MAAM,iEAAiE,CAAA;AAAA,IACnF;AACA,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,KAAA,CAAM,GAAG,CAAA,IAAK,KAAA,CAAM,MAAM,CAAA,EAAG;AACjD,MAAA,MAAM,IAAI,MAAM,gEAAgE,CAAA;AAAA,IAClF;AACA,IAAA,IAAI,CAAC,MAAM,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAA,IAAK,KAAA,CAAM,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG;AAC7D,MAAA,MAAM,IAAI,MAAM,8DAA8D,CAAA;AAAA,IAChF;AACA,IAAA,KAAA,MAAW,CAAA,IAAK,MAAM,MAAA,EAAQ;AAC5B,MAAA,IAAA,CAAK,kBAAA,CAAmB,GAAG,4BAA4B,CAAA;AAAA,IACzD;AACA,IAAA,IAAI,EAAE,KAAA,CAAM,SAAA,YAAqB,IAAA,CAAA,IAAS,MAAA,CAAO,MAAM,KAAA,CAAM,SAAA,CAAU,OAAA,EAAS,CAAA,EAAG;AACjF,MAAA,MAAM,IAAI,MAAM,4DAA4D,CAAA;AAAA,IAC9E;AAAA,EACF;AAAA,EAEQ,kBAAA,CAAmB,GAAqB,GAAA,EAAmB;AACjE,IAAA,IAAI,CAAA,KAAM,IAAA,IAAQ,OAAO,CAAA,KAAM,QAAA,EAAU;AACvC,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,+BAAA,CAAiC,CAAA;AAAA,IACzD;AACA,IAAA,IAAI,OAAO,CAAA,CAAE,IAAA,KAAS,YAAY,CAAA,CAAE,IAAA,CAAK,WAAW,CAAA,EAAG;AACrD,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,uCAAA,CAAyC,CAAA;AAAA,IACjE;AACA,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,CAAA,CAAE,WAAW,CAAA,IAAK,CAAA,CAAE,eAAe,CAAA,EAAG;AAC1D,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,8CAAA,CAAgD,CAAA;AAAA,IACxE;AAAA,EACF;AAAA,EAEQ,yBAAA,CAA0B,OAAe,GAAA,EAAmB;AAClE,IAAA,MAAM,UAAA,GAAa,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AACvC,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,KAAK,KAAK,KAAA,IAAS,CAAA,IAAK,QAAQ,UAAA,EAAY;AAChE,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,EAAG,GAAG,CAAA,kDAAA,EAAqD,UAAU,CAAA;AAAA,OACvE;AAAA,IACF;AAAA,EACF;AACF;;;ACpIO,SAAS,eACd,EAAA,EAC0B;AAC1B,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,eACd,EAAA,EAC0B;AAC1B,EAAA,OAAO,EAAA;AACT;;;ACjDO,SAAS,kBAAA,CACd,IACA,WAAA,EACM;AACN,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,EAAE,CAAA;AAC1B,EAAA,IAAI,gBAAgB,MAAA,EAAQ;AAC1B,IAAA,MAAA,CAAO,aAAA,CAAc,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAAA,EAC9B,CAAA,MAAO;AACL,IAAA,MAAA,CAAO,WAAA,CAAY,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAAA,EAC/B;AACA,EAAA,OAAO,MAAA;AACT;;;ACbO,IAAM,wBAAA,GAA2B,IAAA;AAEjC,SAAS,oBAAoB,GAAA,EAAuB;AACzD,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,MAAM,OAAO,KAAA;AACpD,EAAA,OAAQ,IAA2B,IAAA,KAAS,wBAAA;AAC9C;;;ACsBO,IAAM,qBAAA,GAAwB,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA;AAiC7C,IAAM,4BAAA,GAA+B,GAAA;AACrC,IAAM,0BAAA,GAA6B,GAAA;AACnC,IAAM,wBAAA,GAA2B,GAAA;AAkCjC,IAAM,4BAAA,GAA+B;AACrC,IAAM,8BAAA,GAAiC;AACvC,IAAM,4BAAA,GAA+B;AACrC,IAAM,2BAAA,GAA8B;AACpC,IAAM,2BAAA,GAA8B,GAAA;AAGpC,IAAM,wBAAA,GAA2B,CAAA,GAAI,EAAA,GAAK,EAAA,GAAK,EAAA;AA+B/C,IAAM,qCAAA,GAAwC,GAAA;AAC9C,IAAM,iCAAA,GAAoC,GAAA;AA2B1C,IAAM,2BAAA,GAA8B,IAAA;;;ACpJ3C,IAAMA,WAAAA,GAAa,WAAA;AAEnB,IAAM,aAAA,GAA8C,CAAC,MAAA,EAAQ,KAAK,CAAA;AAE3D,IAAM,qBAAA,GAAwB,GAAA;AAE9B,IAAM,YAAA,GAAN,cAA2B,UAAA,CAAwB;AAAA,EACxD,YAAYD,GAAAA,EAAQ;AAClB,IAAA,KAAA,CAAMA,KAAIC,WAAU,CAAA;AAAA,EACtB;AAAA,EAEU,OAAA,GAA6B;AACrC,IAAA,OAAO;AAAA,MACL;AAAA,QACE,MAAM,EAAE,OAAA,EAAS,GAAG,WAAA,EAAa,CAAA,EAAG,aAAa,CAAA,EAAE;AAAA,QACnD,OAAA,EAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,MAAM,wCAAA;AAAyC,OAC1E;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA;AAAA,QACE,IAAA,EAAM,EAAE,WAAA,EAAa,CAAA,EAAE;AAAA,QACvB,OAAA,EAAS;AAAA,UACP,kBAAA,EAAoB,qBAAA;AAAA,UACpB,IAAA,EAAM;AAAA;AACR;AACF,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,mBAAA,CACE,SACA,KAAA,EACsC;AACtC,IAAA,IAAA,CAAK,kBAAA,CAAmB,KAAA,CAAM,UAAA,EAAY,qBAAqB,CAAA;AAC/D,IAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,IAAA,OAAO,aAAA,CAAc,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,cAAc,OAAA,EAAS,CAAA,EAAG,GAAA,EAAK,KAAK,CAAC,CAAA;AAAA,EAC5E;AAAA,EAEA,MAAM,cAAA,CACJ,OAAA,EACA,KAAA,EACe;AACf,IAAA,IAAA,CAAK,kBAAA,CAAmB,KAAA,CAAM,UAAA,EAAY,gBAAgB,CAAA;AAE1D,IAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,UAAA;AAAA,MAC5B,aAAA,CAAc,GAAA,CAAI,CAAC,CAAA,KAAM,IAAA,CAAK,aAAa,OAAA,EAAS,CAAA,EAAG,GAAA,EAAK,KAAK,CAAC;AAAA,KACpE;AAEA,IAAA,MAAM,QAAA,GAAW,OAAA,CACd,GAAA,CAAI,CAAC,GAAG,CAAA,MAAO,EAAE,MAAA,EAAQ,CAAA,EAAG,WAAA,EAAa,aAAA,CAAc,CAAC,CAAA,GAAI,CAAA,CAC5D,MAAA;AAAA,MAAO,CAAC,CAAA,KACP,CAAA,CAAE,MAAA,CAAO,MAAA,KAAW;AAAA,KACtB;AAEF,IAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AAE3B,IAAA,MAAMJ,UAAS,SAAA,EAAU;AACzB,IAAA,KAAA,MAAW,EAAE,MAAA,EAAQ,WAAA,EAAY,IAAK,QAAA,EAAU;AAC9C,MAAAA,OAAAA,CAAO,KAAA;AAAA,QACL,EAAE,OAAA,EAAS,WAAA,EAAa,GAAA,EAAK,OAAO,MAAA,EAAO;AAAA,QAC3C;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,OAAA,CAAQ,MAAA,EAAQ;AACtC,MAAA,MAAM,QAAA,CAAS,CAAC,CAAA,CAAE,MAAA,CAAO,MAAA;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,MAAM,QAAA,CACJ,OAAA,EACA,QAAA,EACA,QACA,WAAA,EACwB;AACxB,IAAA,IAAI,WAAW,MAAA,EAAQ;AACrB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,oCAAoC,QAAA,CAAS,WAAA,EAAa,CAAA,qBAAA,EAAwB,MAAA,CAAO,aAAa,CAAA,CAAA;AAAA,OACxG;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAA8B;AAAA,MAClC,OAAA;AAAA,MACA,WAAA;AAAA,MACA,WAAA,EAAa,EAAE,IAAA,EAAM,QAAA,EAAU,MAAM,MAAA;AAAO,KAC9C;AAEA,IAAA,OAAO,MAAM,IAAA,CAAK,UAAA,CACf,IAAA,CAAK,MAAM,CAAA,CACX,IAAA,CAAK,EAAE,WAAA,EAAa,GAAG,CAAA,CACvB,KAAA,CAAM,qBAAqB,EAC3B,OAAA,EAAQ;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,mBAAA,CACJ,QAAA,EACA,MAAA,EACA,WAAA,EACwB;AACxB,IAAA,IAAI,WAAW,MAAA,EAAQ;AACrB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,+CAA+C,QAAA,CAAS,WAAA,EAAa,CAAA,qBAAA,EAAwB,MAAA,CAAO,aAAa,CAAA,CAAA;AAAA,OACnH;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAA8B;AAAA,MAClC,WAAA;AAAA,MACA,WAAA,EAAa,EAAE,IAAA,EAAM,QAAA,EAAU,MAAM,MAAA;AAAO,KAC9C;AAEA,IAAA,OAAO,MAAM,IAAA,CAAK,UAAA,CACf,IAAA,CAAK,MAAM,EACX,IAAA,CAAK,EAAE,WAAA,EAAa,CAAA,EAAG,SAAS,CAAA,EAAG,EACnC,KAAA,CAAM,qBAAqB,EAC3B,OAAA,EAAQ;AAAA,EACb;AAAA,EAEA,MAAc,YAAA,CACZ,OAAA,EACA,WAAA,EACA,KACA,KAAA,EACe;AACf,IAAA,MAAM,EAAE,QAAQ,MAAA,EAAO,GAAI,KAAK,kBAAA,CAAmB,OAAA,EAAS,WAAA,EAAa,GAAA,EAAK,KAAK,CAAA;AAEnF,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,WAAW,SAAA,CAAU,MAAA,EAAQ,QAAQ,EAAE,MAAA,EAAQ,MAAM,CAAA;AAAA,IAClE,SAAS,GAAA,EAAK;AAGZ,MAAA,IAAI,mBAAA,CAAoB,GAAG,CAAA,EAAG;AAC5B,QAAA,MAAM,IAAA,CAAK,WAAW,SAAA,CAAU,MAAA,EAAQ,QAAQ,EAAE,MAAA,EAAQ,MAAM,CAAA;AAChE,QAAA;AAAA,MACF;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,aAAA,CACN,OAAA,EACA,WAAA,EACA,GAAA,EACA,KAAA,EACoC;AACpC,IAAA,MAAM,EAAE,QAAQ,MAAA,EAAO,GAAI,KAAK,kBAAA,CAAmB,OAAA,EAAS,WAAA,EAAa,GAAA,EAAK,KAAK,CAAA;AACnF,IAAA,OAAO,cAAA,CAA4B;AAAA,MACjC,SAAA,EAAW,EAAE,MAAA,EAAQ,MAAA,EAAQ,QAAQ,IAAA;AAAK,KAC3C,CAAA;AAAA,EACH;AAAA,EAEQ,kBAAA,CACN,OAAA,EACA,WAAA,EACA,GAAA,EACA,KAAA,EACkE;AAClE,IAAA,MAAM,WAAA,GAAc,kBAAA,CAAmB,GAAA,EAAK,WAAW,CAAA;AAEvD,IAAA,MAAM,MAAA,GAA8B,EAAE,OAAA,EAAS,WAAA,EAAa,WAAA,EAAY;AACxE,IAAA,MAAM,MAAA,GAAkC;AAAA,MACtC,IAAA,EAAM;AAAA,QACJ,KAAA,EAAO,CAAA;AAAA,QACP,OAAA,EAAS,KAAA,CAAM,OAAA,GAAU,CAAA,GAAI,CAAA;AAAA,QAC7B,MAAA,EAAQ,KAAA,CAAM,OAAA,GAAU,CAAA,GAAI,CAAA;AAAA,QAC5B,eAAA,EAAiB,MAAM,UAAA,IAAc;AAAA,OACvC;AAAA,MACA,YAAA,EAAc;AAAA,QACZ,OAAA;AAAA,QACA,WAAA;AAAA,QACA,WAAA;AAAA,QACA,SAAA,EAAW;AAAA,OACb;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,SAAA,EAAW;AAAA;AACb,KACF;AAEA,IAAA,OAAO,EAAE,QAAQ,MAAA,EAAO;AAAA,EAC1B;AAAA,EAEQ,kBAAA,CACN,YACA,UAAA,EACM;AACN,IAAA,IAAI,eAAe,MAAA,EAAW;AAC9B,IAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,UAAU,CAAA,IAAK,aAAa,CAAA,EAAG;AAClD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,aAAA,EAAgB,UAAU,CAAA,uDAAA,EAA0D,UAAU,CAAA,CAAA;AAAA,OAChG;AAAA,IACF;AAAA,EACF;AACF;ACvOO,IAAM,gBAAA,GAAmB,EAAA;AACzB,IAAM,gBAAA,GAAmB,CAAA;AAEhC,IAAM,mBAAA,GAAsB,kDAAA;AAC5B,IAAM,UAAA,GAAa,oEAAA;AACnB,IAAM,iBAAA,GAAoB,mBAAA;AAC1B,IAAM,oBAAA,GAAuB,aAAA;AAE7B,IAAM,kBAAA,GAAqB,SAAA;AAC3B,IAAM,2BAAA,GAA8B,gBAAA;AAEpC,SAAS,iBAAiB,GAAA,EAAqB;AAC7C,EAAA,OAAO,GAAA,CACJ,OAAA,CAAQ,mBAAA,EAAqB,MAAM,EACnC,OAAA,CAAQ,UAAA,EAAY,QAAQ,CAAA,CAC5B,QAAQ,iBAAA,EAAmB,OAAO,CAAA,CAClC,OAAA,CAAQ,sBAAsB,KAAK,CAAA;AACxC;AAEA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,MAAM,SAAmB,EAAC;AAC1B,EAAA,KAAA,MAAW,OAAA,IAAW,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA,EAAG;AACvC,IAAA,IAAI,CAAC,OAAA,CAAQ,UAAA,CAAW,kBAAkB,CAAA,EAAG;AAC7C,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AAC1C,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,2BAAA,EAA6B,IAAI,CAAA;AAClE,IAAA,MAAA,CAAO,KAAK,QAAQ,CAAA;AACpB,IAAA,IAAI,MAAA,CAAO,WAAW,gBAAA,EAAkB;AAAA,EAC1C;AACA,EAAA,OAAO,MAAA,CAAO,KAAK,IAAI,CAAA;AACzB;AAEO,SAAS,eAAe,GAAA,EAAsB;AACnD,EAAA,IAAI,QAAA;AACJ,EAAA,IAAI,eAAe,KAAA,EAAO;AACxB,IAAA,MAAM,IAAA,GAAO,IAAI,IAAA,IAAQ,OAAA;AACzB,IAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,GAAA,CAAI,OAAA,IAAW,EAAE,CAAA;AAClD,IAAA,MAAM,QAAQ,GAAA,CAAI,KAAA,GAAQ,cAAA,CAAe,GAAA,CAAI,KAAK,CAAA,GAAI,EAAA;AACtD,IAAA,QAAA,GAAW,GAAG,IAAI;AAAA,EAAK,OAAO;AAAA,EAAK,KAAK,CAAA,CAAA;AAAA,EAC1C,CAAA,MAAO;AACL,IAAA,QAAA,GAAW,gBAAA,CAAiB,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,EACzC;AACA,EAAA,OAAO,UAAA,CAAW,QAAQ,CAAA,CAAE,MAAA,CAAO,QAAQ,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,gBAAgB,CAAA;AACtF;;;ACrBA,IAAM,uBAAA,GAA0B,CAAA;AAChC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,eAAA,GAAkB,eAAA;AAExB,SAAS,QAAA,CAAS,OAAe,GAAA,EAAqB;AACpD,EAAA,OAAO,MAAM,MAAA,IAAU,GAAA,GAAM,QAAQ,KAAA,CAAM,KAAA,CAAM,GAAG,GAAG,CAAA;AACzD;AAEA,SAAS,OAAA,CAAW,QAAiB,QAAA,EAAgB;AACnD,EAAA,IAAI;AACF,IAAA,OAAO,MAAA,EAAO;AAAA,EAChB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,QAAA;AAAA,EACT;AACF;AAEA,SAAS,YAAY,KAAA,EAA+D;AAClF,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,MAAM,OAAO,KAAA;AACxD,EAAA,OAAO,OAAA,CAAQ,MAAM,SAAA,IAAa,KAAA,EAAO,KAAK,CAAA;AAChD;AAEA,SAAS,cAAA,CAAe,OAAgB,eAAA,EAA0C;AAChF,EAAA,MAAM,QAAA,GAAW,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,eAAA,GAAkB,CAAC,CAAC,CAAA;AAC5D,EAAA,IAAI,WAAA,CAAY,KAAK,CAAA,EAAG;AACtB,IAAA,MAAM,UAAA,GAAa,OAAA,CAAiB,MAAO,KAAA,CAA+B,SAAS,MAAS,CAAA;AAC5F,IAAA,MAAM,OAAA,GAAU,OAAA,CAAiB,MAAO,KAAA,CAA6B,MAAM,MAAS,CAAA;AACpF,IAAA,MAAM,OAAA,GAAU,QAAA;AAAA,MACd,OAAO,eAAe,QAAA,GAAW,UAAA,GAAa,QAAQ,MAAM,MAAA,CAAO,UAAU,CAAA,EAAG,EAAE,CAAA;AAAA,MAClF;AAAA,KACF;AACA,IAAA,MAAM,OAAO,QAAA,CAAS,OAAO,YAAY,QAAA,GAAW,OAAA,GAAU,SAAS,cAAc,CAAA;AACrF,IAAA,OAAO,EAAE,SAAS,IAAA,EAAK;AAAA,EACzB;AACA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,SAAS,OAAA,CAAQ,MAAM,OAAO,KAAK,CAAA,EAAG,EAAE,CAAA,EAAG,QAAQ,CAAA;AAAA,IAC5D,IAAA,EAAM;AAAA,GACR;AACF;AAEA,SAAS,cAAA,CACP,GAAA,EACA,QAAA,EACA,eAAA,EACkD;AAClD,EAAA,MAAM,QAA2B,EAAC;AAClC,EAAA,MAAM,IAAA,uBAAW,OAAA,EAAgB;AACjC,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,QAAQ,IAAA,EAAM,IAAA,CAAK,IAAI,GAAG,CAAA;AAEzD,EAAA,IAAI,OAAA,GAAmB,OAAA,CAAiB,MAAM,GAAA,CAAI,OAAO,MAAS,CAAA;AAClE,EAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,EAAA,OAAO,OAAA,KAAY,MAAA,IAAa,OAAA,KAAY,IAAA,EAAM;AAChD,IAAA,IAAI,KAAA,CAAM,UAAU,QAAA,EAAU;AAC5B,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA;AAAA,IACF;AACA,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,OAAA,KAAY,IAAA,EAAM;AACnD,MAAA,IAAI,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA,EAAG;AACrB,QAAA,SAAA,GAAY,IAAA;AACZ,QAAA;AAAA,MACF;AACA,MAAA,IAAA,CAAK,IAAI,OAAO,CAAA;AAAA,IAClB;AAEA,IAAA,KAAA,CAAM,IAAA,CAAK,cAAA,CAAe,OAAA,EAAS,eAAe,CAAC,CAAA;AAEnD,IAAA,MAAM,QAAA,GACJ,OAAO,OAAA,KAAY,QAAA,IACnB,OAAA,KAAY,QACZ,OAAA,CAAQ,MAAM,OAAA,IAAY,OAAA,EAAoB,KAAK,CAAA;AACrD,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,OAAA,GAAU,OAAA,CAAQ,MAAO,OAAA,CAAgC,KAAA,EAAO,MAAS,CAAA;AAAA,IAC3E,CAAA,MAAO;AACL,MAAA;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,OAAO,SAAA,EAAU;AAC5B;AAEO,SAAS,sBAAA,CACd,KACA,OAAA,EACgB;AAChB,EAAA,MAAM,EAAE,eAAA,EAAiB,aAAA,EAAc,GAAI,OAAA;AAC3C,EAAA,MAAM,WAAA,GAAc,QAAQ,aAAA,IAAiB,uBAAA;AAC7C,EAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA,IAAK,eAAe,CAAA,GACjE,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA,GACtB,uBAAA;AAEJ,EAAA,IAAI,EAAE,eAAe,KAAA,CAAA,EAAQ;AAC3B,IAAA,OAAO;AAAA,MACL,OAAA,EAAS,SAAS,OAAA,CAAQ,MAAM,OAAO,GAAG,CAAA,EAAG,EAAE,CAAA,EAAG,eAAe,CAAA;AAAA,MACjE,KAAA,EAAO,EAAA;AAAA,MACP,YAAY;AAAC,KACf;AAAA,EACF;AAEA,EAAA,MAAM,UAAA,GAAa,QAAgB,MAAM;AACvC,IAAA,MAAM,IAAI,GAAA,CAAI,OAAA;AACd,IAAA,OAAO,OAAO,CAAA,KAAM,QAAA,GAAW,CAAA,GAAI,MAAA,CAAO,KAAK,EAAE,CAAA;AAAA,EACnD,GAAG,EAAE,CAAA;AACL,EAAA,MAAM,QAAA,GAAW,QAAgB,MAAM;AACrC,IAAA,MAAM,IAAI,GAAA,CAAI,KAAA;AACd,IAAA,OAAO,OAAO,CAAA,KAAM,QAAA,GAAW,CAAA,GAAI,MAAA,CAAO,KAAK,EAAE,CAAA;AAAA,EACnD,GAAG,EAAE,CAAA;AAEL,EAAA,MAAM,EAAE,KAAA,EAAO,SAAA,EAAU,GAAI,cAAA;AAAA,IAC3B,GAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,UAAA,EAAY,QAAA,CAAS,UAAA,EAAY,GAAG,CAAA,EAAE;AAAA,MACxC;AAAA,KACF;AAAA,EACF;AAEA,EAAA,IAAI,aAAA,GAAgB,UAAA;AACpB,EAAA,KAAA,MAAW,SAAS,KAAA,EAAO;AACzB,IAAA,aAAA,IAAiB,CAAA,EAAG,eAAe,CAAA,EAAG,KAAA,CAAM,OAAO,CAAA,CAAA;AAAA,EACrD;AAEA,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,QAAA,CAAS,aAAA,EAAe,eAAe,CAAA;AAAA,IAChD,KAAA,EAAO,QAAA,CAAS,QAAA,EAAU,aAAa,CAAA;AAAA,IACvC,UAAA,EAAY;AAAA,GACd;AACF;;;ACzIA,IAAMI,WAAAA,GAAa,YAAA;AAEZ,IAAM,aAAA,GAAN,cAA4B,UAAA,CAAwB;AAAA,EACzD,YAAYD,GAAAA,EAAQ;AAClB,IAAA,KAAA,CAAMA,KAAIC,WAAU,CAAA;AAAA,EACtB;AAAA,EAEU,OAAA,GAA6B;AACrC,IAAA,OAAO;AAAA,MACL;AAAA,QACE,IAAA,EAAM,EAAE,SAAA,EAAW,CAAA,EAAE;AAAA,QACrB,OAAA,EAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,MAAM,kBAAA;AAAmB,OACpD;AAAA,MACA;AAAA,QACE,IAAA,EAAM,EAAE,UAAA,EAAY,EAAA,EAAG;AAAA,QACvB,OAAA,EAAS,EAAE,IAAA,EAAM,iBAAA;AAAkB,OACrC;AAAA,MACA;AAAA,QACE,IAAA,EAAM,EAAE,OAAA,EAAS,CAAA,EAAG,OAAO,EAAA,EAAG;AAAA,QAC9B,OAAA,EAAS,EAAE,IAAA,EAAM,oBAAA;AAAqB,OACxC;AAAA,MACA;AAAA,QACE,IAAA,EAAM,EAAE,KAAA,EAAO,EAAA,EAAG;AAAA,QAClB,OAAA,EAAS,EAAE,IAAA,EAAM,YAAA;AAAa;AAChC,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,iBAAiB,KAAA,EAA6C;AAClE,IAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAO,GAAI,IAAA,CAAK,6BAA6B,KAAK,CAAA;AAElE,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,WAAW,SAAA,CAAU,MAAA,EAAQ,QAAQ,EAAE,MAAA,EAAQ,MAAM,CAAA;AAAA,IAClE,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,mBAAA,CAAoB,GAAG,CAAA,EAAG;AAC5B,QAAA,MAAM,IAAA,CAAK,WAAW,SAAA,CAAU,MAAA,EAAQ,QAAQ,EAAE,MAAA,EAAQ,MAAM,CAAA;AAChE,QAAA;AAAA,MACF;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,qBAAqB,KAAA,EAAkE;AACrF,IAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAO,GAAI,IAAA,CAAK,6BAA6B,KAAK,CAAA;AAClE,IAAA,OAAO,cAAA,CAA4B;AAAA,MACjC,SAAA,EAAW,EAAE,MAAA,EAAQ,MAAA,EAAQ,QAAQ,IAAA;AAAK,KAC3C,CAAA;AAAA,EACH;AAAA,EAEQ,6BACN,KAAA,EACkE;AAClE,IAAA,MAAM,SAAA,GAAY,cAAA,CAAe,KAAA,CAAM,KAAK,CAAA;AAC5C,IAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAO,YAAW,GAAI,sBAAA,CAAuB,MAAM,KAAA,EAAO;AAAA,MACzE,eAAA,EAAiB,4BAAA;AAAA,MACjB,aAAA,EAAe;AAAA,KAChB,CAAA;AACD,IAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AAErB,IAAA,MAAM,MAAA,GAA8B,EAAE,SAAA,EAAU;AAChD,IAAA,MAAM,MAAA,GAAkC;AAAA,MACtC,IAAA,EAAM,EAAE,KAAA,EAAO,CAAA,EAAE;AAAA,MACjB,YAAA,EAAc;AAAA,QACZ,SAAA;AAAA,QACA,SAAS,KAAA,CAAM,OAAA;AAAA,QACf,OAAA;AAAA,QACA,KAAA;AAAA,QACA,WAAA,EAAa,GAAA;AAAA,QACb,SAAA,EAAW,GAAA;AAAA,QACX,GAAI,UAAA,CAAW,MAAA,GAAS,IAAI,EAAE,UAAA,KAAe;AAAC,OAChD;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,UAAA,EAAY,GAAA;AAAA,QACZ,SAAA,EAAW;AAAA;AACb,KACF;AAEA,IAAA,OAAO,EAAE,QAAQ,MAAA,EAAO;AAAA,EAC1B;AAAA,EAEA,MAAM,UAAU,OAAA,EAAmD;AACjE,IAAA,IAAI,EAAE,OAAA,CAAQ,KAAA,GAAQ,CAAA,CAAA,IAAM,OAAA,CAAQ,QAAQ,wBAAA,EAA0B;AACpE,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,iDAAiD,wBAAwB,CAAA,CAAA;AAAA,OAC3E;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAA8B;AAAA,MAClC,GAAI,QAAQ,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,EAAS,OAAA,CAAQ,OAAA,EAAQ,GAAI,EAAC;AAAA,MACpE,GAAI,OAAA,CAAQ,KAAA,KAAU,MAAA,GAAY,EAAE,UAAA,EAAY,EAAE,IAAA,EAAM,OAAA,CAAQ,KAAA,EAAM,EAAE,GAAI;AAAC,KAC/E;AAEA,IAAA,OAAO,MAAM,IAAA,CAAK,UAAA,CACf,IAAA,CAAK,MAAM,EACX,IAAA,CAAK,EAAE,KAAA,EAAO,EAAA,EAAI,CAAA,CAClB,KAAA,CAAM,OAAA,CAAQ,KAAK,EACnB,OAAA,EAAQ;AAAA,EACb;AAEF;;;ACpHA,IAAMA,WAAAA,GAAa,cAAA;AAEnB,IAAM,kBAAA,GAAqB,EAAA;AAEpB,IAAM,eAAA,GAAN,cAA8B,UAAA,CAA0B;AAAA,EAC7D,YAAYD,GAAAA,EAAQ;AAClB,IAAA,KAAA,CAAMA,KAAIC,WAAU,CAAA;AAAA,EACtB;AAAA,EAEU,OAAA,GAA6B;AACrC,IAAA,OAAO;AAAA,MACL;AAAA,QACE,IAAA,EAAM,EAAE,KAAA,EAAO,CAAA,EAAG,UAAU,EAAA,EAAG;AAAA,QAC/B,OAAA,EAAS,EAAE,IAAA,EAAM,qBAAA;AAAsB,OACzC;AAAA,MACA;AAAA,QACE,IAAA,EAAM,EAAE,OAAA,EAAS,CAAA,EAAG,UAAU,EAAA,EAAG;AAAA,QACjC,OAAA,EAAS,EAAE,IAAA,EAAM,uBAAA;AAAwB,OAC3C;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA;AAAA,QACE,IAAA,EAAM,EAAE,QAAA,EAAU,CAAA,EAAE;AAAA,QACpB,OAAA,EAAS;AAAA,UACP,kBAAA,EAAoB,wBAAA;AAAA,UACpB,IAAA,EAAM;AAAA;AACR;AACF,KACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,KAAA,EAA0C;AAC5D,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA;AACtC,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,SAAA,CAAU,GAAG,CAAA;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,kBAAkB,KAAA,EAAiE;AACjF,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,eAAA,CAAgB,KAAK,CAAA;AACtC,IAAA,MAAM,KAAkC,EAAE,SAAA,EAAW,EAAE,QAAA,EAAU,KAAI,EAAE;AACvE,IAAA,OAAO,eAA8B,EAAE,CAAA;AAAA,EACzC;AAAA,EAEQ,gBAAgB,KAAA,EAA0C;AAChE,IAAA,IAAA,CAAK,qBAAqB,KAAK,CAAA;AAE/B,IAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,mBAAA,CAAoB,KAAA,CAAM,KAAK,CAAA,CAAE,KAAA;AAAA,MAC5D,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAO,YAAW,GAAI,sBAAA,CAAuB,MAAM,KAAA,EAAO;AAAA,MACzE,eAAA,EAAiB,8BAAA;AAAA,MACjB,aAAA,EAAe;AAAA,KAChB,CAAA;AACD,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,IAAI,CAAA;AAEpC,IAAA,MAAM,GAAA,GAAqB;AAAA,MACzB,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,KAAA,EAAO,eAAA;AAAA,MACP,YAAA,EAAc,OAAA;AAAA,MACd,UAAA,EAAY,KAAA;AAAA,MACZ,IAAA;AAAA,MACA,UAAU,KAAA,CAAM,QAAA;AAAA,MAChB,SAAA,sBAAe,IAAA;AAAK,KACtB;AACA,IAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,MAAA,GAAA,CAAI,UAAA,GAAa,UAAA;AAAA,IACnB;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AAAA,EAEQ,qBAAqB,KAAA,EAAiC;AAM5D,IAAA,IAAI,OAAO,MAAM,KAAA,KAAU,QAAA,IAAY,MAAM,KAAA,CAAM,IAAA,EAAK,CAAE,MAAA,KAAW,CAAA,EAAG;AACtE,MAAA,MAAM,IAAI,MAAM,iEAAiE,CAAA;AAAA,IACnF;AACA,IAAA,IAAI,OAAO,MAAM,OAAA,KAAY,QAAA,IAAY,MAAM,OAAA,CAAQ,IAAA,EAAK,CAAE,MAAA,KAAW,CAAA,EAAG;AAC1E,MAAA,MAAM,IAAI,MAAM,mEAAmE,CAAA;AAAA,IACrF;AACA,IAAA,IAAI,EAAE,KAAA,CAAM,QAAA,YAAoB,IAAA,CAAA,IAAS,MAAA,CAAO,MAAM,KAAA,CAAM,QAAA,CAAS,OAAA,EAAS,CAAA,EAAG;AAC/E,MAAA,MAAM,IAAI,MAAM,8DAA8D,CAAA;AAAA,IAChF;AAAA,EACF;AAAA,EAEA,MAAM,aACJ,OAAA,EACoD;AACpD,IAAA,IAAI,OAAA,CAAQ,UAAU,MAAA,EAAW;AAC/B,MAAA,IACE,CAAC,MAAA,CAAO,SAAA,CAAU,OAAA,CAAQ,KAAK,CAAA,IAC/B,OAAA,CAAQ,KAAA,IAAS,CAAA,IACjB,OAAA,CAAQ,KAAA,GAAQ,2BAAA,EAChB;AACA,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,iEAAiE,2BAA2B,CAAA,CAAA;AAAA,SAC9F;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAW;AAC9B,MAAA,IAAI,CAAC,OAAO,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA,IAAK,OAAA,CAAQ,OAAO,CAAA,EAAG;AACvD,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AAAA,IACF;AAGA,IAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,MAAA;AACrC,IAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,MAAA;AACjC,IAAA,IAAI,QAAA,KAAa,MAAA,KAAc,EAAE,QAAA,YAAoB,IAAA,CAAA,IAAS,OAAO,KAAA,CAAM,QAAA,CAAS,OAAA,EAAS,CAAA,CAAA,EAAI;AAC/F,MAAA,MAAM,IAAI,MAAM,6DAA6D,CAAA;AAAA,IAC/E;AACA,IAAA,IAAI,MAAA,KAAW,MAAA,KAAc,EAAE,MAAA,YAAkB,IAAA,CAAA,IAAS,OAAO,KAAA,CAAM,MAAA,CAAO,OAAA,EAAS,CAAA,CAAA,EAAI;AACzF,MAAA,MAAM,IAAI,MAAM,2DAA2D,CAAA;AAAA,IAC7E;AACA,IAAA,IAAI,QAAA,KAAa,UAAa,MAAA,KAAW,MAAA,IAAa,SAAS,OAAA,EAAQ,GAAI,MAAA,CAAO,OAAA,EAAQ,EAAG;AAC3F,MAAA,MAAM,IAAI,MAAM,0DAA0D,CAAA;AAAA,IAC5E;AAEA,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,IAAQ,CAAA;AAC7B,IAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,kBAAA;AAE/B,IAAA,MAAM,SAAgC,EAAC;AACvC,IAAA,IAAI,OAAA,CAAQ,YAAY,MAAA,EAAW;AACjC,MAAA,MAAA,CAAO,UAAU,OAAA,CAAQ,OAAA;AAAA,IAC3B;AACA,IAAA,IAAI,QAAA,KAAa,MAAA,IAAa,MAAA,KAAW,MAAA,EAAW;AAClD,MAAA,MAAM,QAAsC,EAAC;AAC7C,MAAA,IAAI,QAAA,KAAa,MAAA,EAAW,KAAA,CAAM,IAAA,GAAO,QAAA;AACzC,MAAA,IAAI,MAAA,KAAW,MAAA,EAAW,KAAA,CAAM,IAAA,GAAO,MAAA;AACvC,MAAA,MAAA,CAAO,QAAA,GAAW,KAAA;AAAA,IACpB;AAEA,IAAA,MAAM,CAAC,KAAA,EAAO,KAAK,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,MACvC,KAAK,UAAA,CACF,IAAA,CAAK,MAAM,CAAA,CACX,KAAK,EAAE,QAAA,EAAU,EAAA,EAAI,EACrB,IAAA,CAAK,IAAI,EACT,KAAA,CAAM,KAAK,EACX,OAAA,EAAQ;AAAA,MACX,IAAA,CAAK,UAAA,CAAW,cAAA,CAAe,MAAM;AAAA,KACtC,CAAA;AAED,IAAA,OAAO,EAAE,OAAO,KAAA,EAAM;AAAA,EACxB;AAAA,EAEA,MAAM,WAAW,KAAA,EAA8C;AAE7D,IAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,MAAM,IAAA,EAAK,CAAE,WAAW,CAAA,EAAG;AAC1D,MAAA,MAAM,IAAI,MAAM,8DAA8D,CAAA;AAAA,IAChF;AACA,IAAA,MAAM,MAAA,GAAgC,EAAE,KAAA,EAAM;AAC9C,IAAA,OAAO,MAAM,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,MAAA,EAAQ,EAAE,IAAA,EAAM,EAAE,QAAA,EAAU,EAAA,EAAG,EAAG,CAAA;AAAA,EACzE;AAAA,EAEQ,oBAAoB,KAAA,EAAwB;AAClD,IAAA,IAAI,KAAA,KAAU,QAAW,OAAO,WAAA;AAChC,IAAA,IAAI;AAGF,MAAA,MAAM,SAAS,IAAA,CAAK,SAAA,CAAU,KAAA,EAAO,CAAC,MAAM,KAAA,KAAU;AACpD,QAAA,IAAI,KAAA,YAAiB,GAAA,EAAK,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAA,EAAS,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,OAAA,EAAS,CAAA,EAAE;AACvF,QAAA,IAAI,KAAA,YAAiB,GAAA,EAAK,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAQ,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,CAAA,EAAE;AACrF,QAAA,OAAO,KAAA;AAAA,MACT,CAAC,CAAA;AAED,MAAA,IAAI,MAAA,KAAW,QAAW,OAAO,WAAA;AACjC,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,MAAM,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAC3D,MAAA,OAAO,gCAAgC,GAAG,CAAA,CAAA,CAAA;AAAA,IAC5C;AAAA,EACF;AAAA,EAEQ,QAAQ,IAAA,EAAkC;AAChD,IAAA,IAAI,IAAA,KAAS,QAAW,OAAO,EAAA;AAC/B,IAAA,IAAI,IAAA,CAAK,MAAA,IAAU,2BAAA,EAA6B,OAAO,IAAA;AAEvD,IAAA,MAAM,cAAc,IAAA,CAAK,MAAA;AACzB,IAAA,IAAI,OAAA,GAAU,2BAAA;AACd,IAAA,IAAI,MAAA,GAAS,EAAA;AACb,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,MAAA,MAAA,GAAS,CAAA,+BAAA,EAA6B,OAAO,CAAA,IAAA,EAAO,WAAW,CAAA;AAAA,CAAA;AAC/D,MAAA,IAAI,MAAA,CAAO,UAAU,2BAAA,EAA6B;AAChD,QAAA,OAAO,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,2BAA2B,CAAA;AAAA,MACpD;AACA,MAAA,OAAA,GAAU,8BAA8B,MAAA,CAAO,MAAA;AAAA,IACjD;AACA,IAAA,OAAO,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,CAAC,OAAO,CAAA;AAAA,EACrC;AACF;;;ACjNA,IAAMA,WAAAA,GAAa,aAAA;AAEZ,IAAM,cAAA,GAAN,cAA6B,UAAA,CAAyB;AAAA,EAC3D,YAAYD,GAAAA,EAAQ;AAClB,IAAA,KAAA,CAAMA,KAAIC,WAAU,CAAA;AAAA,EACtB;AAAA,EAEU,OAAA,GAA6B;AACrC,IAAA,OAAO;AAAA,MACL;AAAA,QACE,IAAA,EAAM,EAAE,OAAA,EAAS,CAAA,EAAE;AAAA,QACnB,OAAA,EAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,MAAM,gBAAA;AAAiB;AAClD,KACF;AAAA,EACF;AAAA,EAEA,MAAM,UAAU,OAAA,EAA0C;AACxD,IAAA,IAAA,CAAK,eAAA,CAAgB,SAAS,0BAA0B,CAAA;AACxD,IAAA,MAAM,MAAA,GAA+B,EAAE,OAAA,EAAQ;AAC/C,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,UAAA,CAAW,QAAQ,MAAM,CAAA;AAEhD,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,OAAO;AAAA,QACL,OAAA;AAAA,QACA,OAAA,EAAS,2BAAA;AAAA,QACT,UAAU;AAAC,OACb;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,SAAS,GAAA,CAAI,OAAA;AAAA,MACb,SAAS,GAAA,CAAI,OAAA;AAAA,MACb,UAAU,GAAA,CAAI;AAAA,KAChB;AAAA,EACF;AAAA,EAEA,MAAM,UAAA,CAAW,OAAA,EAAiB,OAAA,EAAiC;AACjE,IAAA,IAAA,CAAK,eAAA,CAAgB,SAAS,2BAA2B,CAAA;AACzD,IAAA,IAAI,OAAO,YAAY,SAAA,EAAW;AAChC,MAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,IACxE;AAEA,IAAA,MAAM,MAAA,GAA+B,EAAE,OAAA,EAAQ;AAC/C,IAAA,MAAM,MAAA,GAAmC;AAAA,MACvC,IAAA,EAAM,EAAE,OAAA,EAAQ;AAAA,MAChB,YAAA,EAAc;AAAA,QACZ,OAAA;AAAA,QACA,UAAU,EAAC;AAAA,QACX,SAAA,sBAAe,IAAA;AAAK,OACtB;AAAA,MACA,YAAA,EAAc,EAAE,SAAA,EAAW,IAAA;AAAK,KAClC;AACA,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,UAAA,CAAW,SAAA,CAAU,QAAQ,MAAA,EAAQ,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAE/E,IAAA,IAAI,OAAO,YAAA,KAAiB,CAAA,IAAA,CAAM,MAAA,CAAO,aAAA,IAAiB,OAAO,CAAA,EAAG;AAClE,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,0EAA0E,OAAO,CAAA;AAAA,OACnF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,OAAA,GAAmC;AACvC,IAAA,OAAO,MAAM,IAAA,CAAK,UAAA,CACf,IAAA,CAAK,EAAE,CAAA,CACP,IAAA,CAAK,EAAE,OAAA,EAAS,CAAA,EAAG,EACnB,OAAA,EAAQ;AAAA,EACb;AAAA,EAEA,MAAM,UAAU,OAAA,EAAmC;AACjD,IAAA,IAAA,CAAK,eAAA,CAAgB,SAAS,0BAA0B,CAAA;AACxD,IAAA,MAAM,MAAA,GAA+B,EAAE,OAAA,EAAQ;AAC/C,IAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,UAAA,CAAW,QAAQ,MAAA,EAAQ;AAAA,MAChD,UAAA,EAAY,EAAE,OAAA,EAAS,CAAA,EAAG,KAAK,CAAA;AAAE,KAClC,CAAA;AAED,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,OAAO,2BAAA;AAAA,IACT;AACA,IAAA,OAAO,GAAA,CAAI,OAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,MAAM,cACJ,IAAA,EACe;AAIf,IAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AAE/C,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA;AACnC,IAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AAK1B,IAAA,KAAA,MAAW,CAAC,OAAO,CAAA,IAAK,OAAA,EAAS;AAC/B,MAAA,IAAA,CAAK,eAAA,CAAgB,SAAS,8BAA8B,CAAA;AAAA,IAC9D;AAEA,IAAA,MAAM,MAAkD,OAAA,CAAQ,GAAA;AAAA,MAC9D,CAAC,CAAC,OAAA,EAAS,GAAG,CAAA,KAAM;AAClB,QAAA,MAAM,IAAA,GACJ,MAAA,IAAU,GAAA,IAAO,OAAO,GAAA,CAAI,IAAA,KAAS,QAAA,IAAY,GAAA,CAAI,IAAA,CAAK,MAAA,GAAS,CAAA,GAC/D,GAAA,CAAI,IAAA,GACJ,IAAA;AACN,QAAA,MAAM,QAAA,GAAoC;AAAA,UACxC,MAAM,GAAA,CAAI,IAAA;AAAA,UACV,KAAA,EAAO,IAAI,KAAA,IAAS,IAAA;AAAA,UACpB;AAAA,SACF;AACA,QAAA,MAAM,MAAA,GAAmC;AAAA,UACvC,YAAA,EAAc;AAAA,YACZ,OAAA;AAAA,YACA,OAAA,EAAS,2BAAA;AAAA,YACT,SAAA,sBAAe,IAAA;AAAK,WACtB;AAAA,UACA,IAAA,EAAM,EAAE,QAAA,EAAS;AAAA,UACjB,YAAA,EAAc,EAAE,SAAA,EAAW,IAAA;AAAK,SAClC;AACA,QAAA,OAAO,cAAA,CAA6B;AAAA,UAClC,SAAA,EAAW;AAAA,YACT,MAAA,EAAQ,EAAE,OAAA,EAAQ;AAAA,YAClB,MAAA;AAAA,YACA,MAAA,EAAQ;AAAA;AACV,SACD,CAAA;AAAA,MACH;AAAA,KACF;AAEA,IAAA,MAAM,KAAK,UAAA,CAAW,SAAA,CAAU,KAAK,EAAE,OAAA,EAAS,OAAO,CAAA;AAAA,EACzD;AAAA,EAEQ,eAAA,CAAgB,SAAiB,GAAA,EAAmB;AAI1D,IAAA,IAAI,OAAO,OAAA,KAAY,QAAA,IAAY,QAAQ,IAAA,EAAK,CAAE,WAAW,CAAA,EAAG;AAC9D,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,oCAAA,CAAsC,CAAA;AAAA,IAC9D;AAKA,IAAA,IAAI,QAAQ,QAAA,CAAS,GAAG,KAAK,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AAClD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,GAAG,GAAG,CAAA,+DAAA;AAAA,OACR;AAAA,IACF;AAAA,EACF;AACF;ACpKO,IAAM,cAAA,GAAN,MAAM,eAAA,CAAe;AAAA,EAY1B,WAAA,CAAY,IAAA,EAAmB,OAAA,GAAiC,EAAC,EAAG;AAJpE,IAAA,IAAA,CAAQ,KAAA,GAA+C,IAAA;AACvD,IAAA,IAAA,CAAQ,OAAA,GAAU,KAAA;AAClB,IAAA,IAAA,CAAQ,UAAA,GAAa,KAAA;AAGnB,IAAA,eAAA,CAAe,yBAAA,CAA0B,QAAQ,mBAAmB,CAAA;AAEpE,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,mBAAA,GACH,QAAQ,mBAAA,IAAuB,qCAAA;AAEjC,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,gBAAA,GAAmB,OAAA,CAAQ,gBAAA,KAAwB,EAAA,CAAA,QAAA,EAAS;AACpF,IAAA,IAAA,CAAK,MAAM,OAAA,CAAQ,WAAA,GAAc,OAAA,CAAQ,WAAA,KAAgB,OAAA,CAAQ,GAAA;AACjE,IAAA,IAAA,CAAK,SAAA,GAAY,OAAA,CAAQ,gBAAA,GACrB,OAAA,CAAQ,kBAAiB,GACzB,CAAA,EAAG,IAAA,CAAK,QAAQ,CAAA,CAAA,EAAI,IAAA,CAAK,GAAG,CAAA,CAAA,EAAW,mBAAY,CAAA,CAAA;AACvD,IAAA,IAAA,CAAK,KAAA,GAAQ,OAAA,CAAQ,KAAA,KAAU,0BAAU,IAAA,EAAK,CAAA;AAC9C,IAAA,IAAA,CAAK,SAAA,GAAY,KAAK,KAAA,EAAM;AAAA,EAC9B;AAAA,EAEA,IAAI,QAAA,GAAmB;AACrB,IAAA,OAAO,IAAA,CAAK,SAAA;AAAA,EACd;AAAA,EAEA,MAAM,cAAc,KAAA,EAAwC;AAC1D,IAAA,eAAA,CAAe,mBAAmB,KAAK,CAAA;AAEvC,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACpB,MAAA,MAAM,IAAA,CAAK,KAAK,cAAA,CAAe;AAAA,QAC7B,UAAU,IAAA,CAAK,SAAA;AAAA,QACf,UAAU,IAAA,CAAK,QAAA;AAAA,QACf,KAAK,IAAA,CAAK,GAAA;AAAA,QACV,MAAA,EAAQ,CAAC,KAAK,CAAA;AAAA,QACd,WAAW,IAAA,CAAK;AAAA,OACjB,CAAA;AACD,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,CAAK,KAAK,QAAA,CAAS;AAAA,MACvB,UAAU,IAAA,CAAK,SAAA;AAAA,MACf;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI,KAAK,OAAA,EAAS;AAClB,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACpB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,KAAA,GAAQ,YAAY,MAAM;AAC7B,MAAA,KAAK,KAAK,WAAA,EAAY;AAAA,IACxB,CAAA,EAAG,KAAK,mBAAmB,CAAA;AAC3B,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AACjB,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACjB;AAAA,EAEA,MAAM,IAAA,GAAsB;AAC1B,IAAA,IAAI,IAAA,CAAK,UAAU,IAAA,EAAM;AACvB,MAAA,aAAA,CAAc,KAAK,KAAK,CAAA;AACxB,MAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AAAA,IACf;AACA,IAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AAEf,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AAEtB,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,IAAA,CAAK,gBAAA,CAAiB,IAAA,CAAK,SAAS,CAAA;AAAA,IACjD,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV,EAAE,GAAA,EAAK,QAAA,EAAU,IAAA,CAAK,SAAA,EAAU;AAAA,QAChC;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAAA,EACpB;AAAA,EAEA,MAAc,WAAA,GAA6B;AACzC,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,SAAS,CAAA;AAAA,IAChD,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV,EAAE,GAAA,EAAK,QAAA,EAAU,IAAA,CAAK,SAAA,EAAU;AAAA,QAChC;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAe,0BAA0B,KAAA,EAAwC;AAC/E,IAAA,IAAI,SAAS,IAAA,EAAM;AACnB,IAAA,MAAM,yBAAA,GAA4B,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AACjD,IAAA,IACE,CAAC,OAAO,SAAA,CAAU,KAAK,KACvB,KAAA,GAAQ,iCAAA,IACR,QAAQ,yBAAA,EACR;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sEAAA,EAAyE,iCAAiC,CAAA,EAAA,EAAK,yBAAyB,UAAU,KAAK,CAAA;AAAA,OACzJ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAe,mBAAmB,CAAA,EAA2B;AAC3D,IAAA,IAAI,CAAA,KAAM,IAAA,IAAQ,OAAO,CAAA,KAAM,QAAA,EAAU;AACvC,MAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,IACzE;AACA,IAAA,IAAI,OAAO,CAAA,CAAE,IAAA,KAAS,YAAY,CAAA,CAAE,IAAA,CAAK,WAAW,CAAA,EAAG;AACrD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,CAAA,CAAE,WAAW,CAAA,IAAK,CAAA,CAAE,eAAe,CAAA,EAAG;AAC1D,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAAA,EACF;AACF;;;ACtJO,IAAM,yBAAA,GAA4B,GAAA;AAClC,IAAM,eAAA,GAAkB,GAAA;AACxB,IAAM,eAAA,GAAkB,GAAA;AAGxB,IAAM,mBAAA,GAAsB,EAAA;AAG5B,IAAM,uBAAA,GAA0B,CAAC,CAAA,EAAG,GAAA,EAAM,KAAM,GAAI,CAAA;;;ACQpD,IAAM,cAAN,MAAiD;AAAA,EAUtD,WAAA,CAAYH,aAA2B,MAAA,EAA2B;AALlE,IAAA,IAAA,CAAQ,SAAqC,EAAC;AAC9C,IAAA,IAAA,CAAQ,KAAA,GAA+C,IAAA;AACvD,IAAA,IAAA,CAAQ,QAAA,GAAiC,IAAA;AACzC,IAAA,IAAA,CAAQ,OAAA,GAAU,KAAA;AAGhB,IAAA,IAAA,CAAK,eAAe,MAAM,CAAA;AAE1B,IAAA,IAAA,CAAK,UAAA,GAAaA,WAAAA;AAClB,IAAA,IAAA,CAAK,gBAAgB,MAAA,CAAO,aAAA;AAC5B,IAAA,IAAA,CAAK,WAAA,GAAc,OAAO,aAAA,GAAgB,mBAAA;AAC1C,IAAA,IAAA,CAAK,eAAA,GAAkB,OAAO,eAAA,IAAmB,yBAAA;AAEjD,IAAA,IAAA,CAAK,UAAA,EAAW;AAAA,EAClB;AAAA,EAEA,KAAK,SAAA,EAA2C;AAC9C,IAAA,IAAI,KAAK,OAAA,EAAS;AAChB,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV;AAAA,OACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,SAAS,CAAA;AAC1B,IAAA,IAAA,CAAK,cAAA,EAAe;AAEpB,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,aAAA,EAAe;AAC5C,MAAA,IAAA,CAAK,UAAA,EAAW;AAChB,MAAA,KAAK,KAAK,KAAA,EAAM;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AACf,IAAA,IAAA,CAAK,SAAA,EAAU;AAEf,IAAA,IAAI,IAAA,CAAK,QAAA,EAAU,MAAM,IAAA,CAAK,QAAA;AAE9B,IAAA,KAAA,MAAW,aAAa,uBAAA,EAAyB;AAC/C,MAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG;AAC9B,MAAA,IAAI,YAAY,CAAA,EAAG;AACjB,QAAA,MAAM,IAAI,OAAA,CAAc,CAACF,aAAY,UAAA,CAAWA,QAAAA,EAAS,SAAS,CAAC,CAAA;AAAA,MACrE;AACA,MAAA,MAAM,KAAK,KAAA,EAAM;AAAA,IACnB;AAEA,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG;AAC1B,MAAA,SAAA,EAAU,CAAE,KAAA;AAAA,QACV,EAAE,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,MAAA,EAAO;AAAA,QAChC;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,KAAA,GAAuB;AACnC,IAAA,IAAI,IAAA,CAAK,QAAA,EAAU,OAAO,IAAA,CAAK,QAAA;AAC/B,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG;AAE9B,IAAA,IAAA,CAAK,QAAA,GAAW,KAAK,OAAA,EAAQ;AAC7B,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,QAAA;AAAA,IACb,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,MAAc,OAAA,GAAyB;AACrC,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG;AAC7B,MAAA,MAAM,WAAW,IAAA,CAAK,MAAA;AACtB,MAAA,IAAA,CAAK,SAAS,EAAC;AAEf,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,CAAK,UAAA,CAAW,SAAA,CAAU,QAAA,EAAU;AAAA,UACxC,OAAA,EAAS,KAAA;AAAA,UACT,WAAW,IAAA,CAAK;AAAA,SACjB,CAAA;AAAA,MACH,SAAS,GAAA,EAAc;AAMrB,QAAA,IACE,GAAA,YAAe,mBAAA,IACf,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,IAC7B,GAAA,CAAI,WAAA,CAAY,MAAA,GAAS,CAAA,EACzB;AACA,UAAA,SAAA,EAAU,CAAE,IAAA;AAAA,YACV;AAAA,cACE,MAAA,EAAQ,IAAI,WAAA,CAAY,MAAA;AAAA,cACxB,OAAO,QAAA,CAAS;AAAA,aAClB;AAAA,YACA;AAAA,WACF;AACA,UAAA;AAAA,QACF;AAEA,QAAA,IAAA,CAAK,SAAS,CAAC,GAAG,QAAA,EAAU,GAAG,KAAK,MAAM,CAAA;AAC1C,QAAA,IAAA,CAAK,cAAA,EAAe;AACpB,QAAA,SAAA,EAAU,CAAE,IAAA;AAAA,UACV,EAAE,GAAA,EAAK,KAAA,EAAO,QAAA,CAAS,MAAA,EAAO;AAAA,UAC9B;AAAA,SACF;AACA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAA,GAAuB;AAC7B,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,IAAU,IAAA,CAAK,WAAA,EAAa;AAC5C,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,IAAA,CAAK,WAAA;AAC5C,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,SAAS,CAAA;AAC/B,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,SAAA,EAAW,WAAA,EAAa,IAAA,CAAK,WAAA,EAAY;AAAA,MAC3C;AAAA,KACF;AAAA,EACF;AAAA,EAEQ,UAAA,GAAmB;AACzB,IAAA,IAAA,CAAK,KAAA,GAAQ,YAAY,MAAM;AAC7B,MAAA,KAAK,KAAK,KAAA,EAAM;AAAA,IAClB,CAAA,EAAG,KAAK,eAAe,CAAA;AACvB,IAAA,IAAA,CAAK,MAAM,KAAA,EAAM;AAAA,EACnB;AAAA,EAEQ,SAAA,GAAkB;AACxB,IAAA,IAAI,KAAK,KAAA,EAAO;AACd,MAAA,aAAA,CAAc,KAAK,KAAK,CAAA;AACxB,MAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AAAA,IACf;AAAA,EACF;AAAA,EAEQ,UAAA,GAAmB;AACzB,IAAA,IAAA,CAAK,SAAA,EAAU;AACf,IAAA,IAAA,CAAK,UAAA,EAAW;AAAA,EAClB;AAAA,EAEQ,eAAe,MAAA,EAAiC;AACtD,IAAA,IACE,CAAC,MAAA,CAAO,SAAA,CAAU,MAAA,CAAO,aAAa,CAAA,IACtC,MAAA,CAAO,aAAA,GAAgB,eAAA,IACvB,MAAA,CAAO,aAAA,GAAgB,eAAA,EACvB;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,gEAAgE,eAAe,CAAA,EAAA,EAAK,eAAe,CAAA,OAAA,EAAU,OAAO,aAAa,CAAA;AAAA,OACnI;AAAA,IACF;AAEA,IAAA,IAAI,MAAA,CAAO,oBAAoB,MAAA,EAAW;AACxC,MAAA,IACE,CAAC,OAAO,SAAA,CAAU,MAAA,CAAO,eAAe,CAAA,IACxC,MAAA,CAAO,mBAAmB,CAAA,EAC1B;AACA,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,wEAAA,EAA2E,OAAO,eAAe,CAAA;AAAA,SACnG;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AClLA,eAAsB,cAAA,CACpB,KAAA,EACA,KAAA,EACA,QAAA,EACiB;AACjB,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,WAAW,CAAA,EAAG;AACnD,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACpE;AACA,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,QAAQ,CAAA,IAAK,YAAY,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,4DAA4D,QAAQ,CAAA;AAAA,KACtE;AAAA,EACF;AAEA,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,MAAM,KAAA,CAAM,WAAW,KAAA,EAAO,CAAA,EAAG,IAAI,IAAI,CAAA;AACxD,IAAA,IAAA,GAAO,MAAA,CAAO,IAAA;AAAA,EAChB,SAAS,GAAA,EAAK;AACZ,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,KAAK,KAAA,EAAM;AAAA,MACb;AAAA,KACF;AACA,IAAA,OAAO,EAAA;AAAA,EACT;AAEA,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA;AAC7B,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,QAAA,EAAU,OAAO,MAAA;AAEtC,EAAA,MAAM,cAAc,MAAA,CAAO,MAAA;AAC3B,EAAA,IAAI,OAAA,GAAU,QAAA;AACd,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,IAAA,MAAA,GAAS,CAAA,+BAAA,EAA6B,OAAO,CAAA,IAAA,EAAO,WAAW,CAAA;AAAA,CAAA;AAC/D,IAAA,IAAI,MAAA,CAAO,UAAU,QAAA,EAAU;AAC7B,MAAA,OAAO,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,QAAQ,CAAA;AAAA,IACjC;AACA,IAAA,OAAA,GAAU,WAAW,MAAA,CAAO,MAAA;AAAA,EAC9B;AACA,EAAA,OAAO,MAAA,GAAS,MAAA,CAAO,KAAA,CAAM,CAAC,OAAO,CAAA;AACvC;;;ACfO,SAAS,gBAAA,CAAiB,QAAgB,OAAA,EAAkC;AACjF,EAAA,MAAA,CAAO,EAAA,CAAG,WAAA,EAAa,CAAC,GAAA,KAAQ;AAC9B,IAAA,KAAK,eAAA,CAAgB,KAAK,OAAO,CAAA;AAAA,EACnC,CAAC,CAAA;AACD,EAAA,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAC,GAAA,EAAK,KAAA,KAAU;AAClC,IAAA,KAAK,YAAA,CAAa,GAAA,EAAK,KAAA,EAAO,OAAO,CAAA;AAAA,EACvC,CAAC,CAAA;AACH;AAEA,eAAe,eAAA,CAAgB,KAAU,OAAA,EAA2C;AAClF,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,sBAAA,CAAuB,GAAA,EAAK,OAAA,CAAQ,UAAU,CAAA;AAC9D,IAAA,IAAI,YAAY,IAAA,EAAM;AAEtB,IAAA,MAAM,UAAA,GAAa,kBAAkB,GAAG,CAAA;AACxC,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,YAAA,CAAa,mBAAA,CAAoB,OAAA,EAAS;AAAA,MAC5D,OAAA,EAAS,IAAA;AAAA,MACT;AAAA,KACD,CAAA;AACD,IAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,MAAA,OAAA,CAAQ,mBAAA,CAAoB,KAAK,EAAE,CAAA;AAAA,IACrC;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,GAAA,EAAK,KAAA,EAAO,GAAA,EAAK,EAAA,EAAI,MAAM,OAAA,EAAQ;AAAA,MACrC;AAAA,KACF;AAAA,EACF;AACF;AAEA,eAAe,YAAA,CACb,GAAA,EACA,KAAA,EACA,OAAA,EACe;AACf,EAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAQ;AAAA,MAC1B;AAAA,KACF;AACA,IAAA;AAAA,EACF;AACA,EAAA,IAAI,GAAA,CAAI,EAAA,KAAO,MAAA,IAAa,GAAA,CAAI,OAAO,IAAA,EAAM;AAC3C,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,OAAA,EAAS,GAAA,CAAI,IAAA,EAAK;AAAA,MACpB;AAAA,KACF;AACA,IAAA;AAAA,EACF;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,sBAAA,CAAuB,GAAA,EAAK,OAAA,CAAQ,UAAU,CAAA;AAC9D,IAAA,IAAI,YAAY,IAAA,EAAM;AAEtB,IAAA,MAAM,UAAA,GAAa,kBAAkB,GAAG,CAAA;AACxC,IAAA,MAAM,WACJ,OAAO,GAAA,CAAI,UAAA,KAAe,QAAA,IAAY,OAAO,QAAA,CAAS,GAAA,CAAI,UAAU,CAAA,GAChE,IAAI,IAAA,CAAK,GAAA,CAAI,UAAU,CAAA,uBACnB,IAAA,EAAK;AACf,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,GAAA,CAAI,EAAE,CAAA;AAC3B,IAAA,MAAM,YAAA,GAAe,QAAQ,YAAA,IAAgB,2BAAA;AAE7C,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,YAAA,CAAa,mBAAA,CAAoB,OAAA,EAAS;AAAA,QAC5D,OAAA,EAAS,KAAA;AAAA,QACT;AAAA,OACD,CAAA;AACD,MAAA,KAAA,MAAW,MAAM,GAAA,EAAK;AACpB,QAAA,OAAA,CAAQ,mBAAA,CAAoB,KAAK,EAAE,CAAA;AAAA,MACrC;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV,EAAE,GAAA,EAAK,KAAA,EAAO,IAAA,EAAM,OAAA,EAAQ;AAAA,QAC5B;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,aAAA,CAAc,oBAAA,CAAqB;AAAA,QACpD,OAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,OAAA,CAAQ,oBAAA,CAAqB,KAAK,EAAE,CAAA;AAAA,IACtC,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV,EAAE,GAAA,EAAK,KAAA,EAAO,IAAA,EAAM,QAAA,EAAS;AAAA,QAC7B;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI,IAAA,GAAO,EAAA;AACX,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,MAAM,cAAA,CAAe,OAAA,CAAQ,KAAA,EAAO,OAAO,YAAY,CAAA;AAAA,IAChE,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV,EAAE,GAAA,EAAK,KAAA,EAAO,IAAA,EAAM,UAAA,EAAW;AAAA,QAC/B;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,eAAA,CAAgB,iBAAA,CAAkB;AAAA,QACnD,KAAA;AAAA,QACA,OAAA;AAAA,QACA,OAAO,GAAA,CAAI,IAAA;AAAA,QACX,KAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,OAAA,CAAQ,sBAAA,CAAuB,KAAK,EAAE,CAAA;AAAA,IACxC,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV,EAAE,GAAA,EAAK,KAAA,EAAO,IAAA,EAAM,UAAA,EAAW;AAAA,QAC/B;AAAA,OACF;AAAA,IACF;AAAA,EACF,SAAS,GAAA,EAAK;AACZ,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,GAAA,EAAK,KAAA,EAAO,GAAA,CAAI,EAAA,EAAI,MAAM,SAAA,EAAU;AAAA,MACtC;AAAA,KACF;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,GAAA,EAA8B;AACvD,EAAA,MAAM,cAAc,GAAA,CAAI,WAAA;AACxB,EAAA,MAAM,aAAa,GAAA,CAAI,UAAA;AACvB,EAAA,IAAI,OAAO,gBAAgB,QAAA,IAAY,CAAC,OAAO,QAAA,CAAS,WAAW,GAAG,OAAO,MAAA;AAC7E,EAAA,IAAI,OAAO,eAAe,QAAA,IAAY,CAAC,OAAO,QAAA,CAAS,UAAU,GAAG,OAAO,MAAA;AAC3E,EAAA,MAAM,QAAQ,UAAA,GAAa,WAAA;AAC3B,EAAA,IAAI,KAAA,GAAQ,GAAG,OAAO,MAAA;AACtB,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,sBAAA,CACP,KACA,UAAA,EACe;AACf,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,KAAW,cAAA,CAAe,GAAA,CAAI,MAAM,UAAU,CAAA;AAChE,EAAA,IAAI,MAAA,KAAW,MAAM,OAAO,IAAA;AAC5B,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,eAAe,IAAA,CAAK,UAAA,EAAY,QAAQ,CAAA,EAAG;AAC/D,IAAA,SAAA,EAAU,CAAE,KAAA;AAAA,MACV,EAAE,OAAA,EAAS,GAAA,CAAI,IAAA,EAAM,KAAA,EAAO,IAAI,EAAA,EAAG;AAAA,MACnC;AAAA,KACF;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,QAAA;AACT;ACjKO,IAAM,2BAAA,GAA8B;AAgB3C,eAAsB,uBAAA,CACpB,gBACA,IAAA,EACe;AACf,EAAA,IAAI;AACF,IAAA,MAAM,cAAA,CAAe,cAAc,IAAI,CAAA;AAAA,EACzC,SAAS,GAAA,EAAK;AACZ,IAAA,SAAA,EAAU,CAAE,KAAA;AAAA,MACV,EAAE,GAAA,EAAI;AAAA,MACN;AAAA,KACF;AAAA,EACF;AACF;AAqBA,eAAsB,eAAA,CACpB,GAAA,EACA,UAAA,EACA,cAAA,EACA,KAAA,EACe;AAKf,EAAA,IAAI,OAAO,GAAA,CAAI,IAAA,KAAS,YAAY,GAAA,CAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AAC3D,EAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,OAAO,UAAA,KAAe,QAAA,EAAU;AAK3D,EAAA,IAAI,UAAU,MAAA,EAAW;AACvB,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,KAAA,EAAO,GAAA,CAAI,EAAA,EAAI,OAAA,EAAS,IAAI,IAAA,EAAK;AAAA,MACnC;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,cAAA,CAAe,GAAA,CAAI,MAAM,UAAU,CAAA;AAIxD,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,KAAA,EAAO,GAAA,CAAI,EAAA,EAAI,OAAA,EAAS,IAAI,IAAA,EAAK;AAAA,MACnC;AAAA,KACF;AACA,IAAA;AAAA,EACF;AAOA,EAAA,MAAM,OAAA,GAAU,MAAM,cAAA,CAAe,SAAA,CAAU,QAAQ,CAAA;AACvD,EAAA,IAAI,OAAA,EAAS;AAQb,EAAA,MAAM,OAAO,GAAA,CAAI,QAAA;AACjB,EAAA,MAAM,SAAA,GACJ,OAAO,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,IAAQ,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,GAC3D,IAAA,CAA0C,kBAAA,GAC3C,MAAA;AACN,EAAA,MAAM,SAAA,GACJ,OAAO,SAAA,KAAc,QAAA,IAAY,OAAO,QAAA,CAAS,SAAS,CAAA,GAAI,SAAA,GAAY,CAAA,GAAI,CAAA;AAShF,EAAA,MAAM,IAAI,aAAA,CAAc,IAAA,CAAK,GAAA,EAAI,GAAI,6BAA6B,KAAK,CAAA;AAGvE,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,CAAI,cAAA,CAAe,EAAE,kBAAA,EAAoB,WAAW,CAAA;AAAA,EAC5D,SAAS,GAAA,EAAK;AACZ,IAAA,SAAA,EAAU,CAAE,IAAA;AAAA,MACV,EAAE,GAAA,EAAK,OAAA,EAAS,QAAA,EAAU,KAAA,EAAO,IAAI,EAAA,EAAG;AAAA,MACxC;AAAA,KACF;AAAA,EACF;AAEA,EAAA,SAAA,EAAU,CAAE,IAAA;AAAA,IACV,EAAE,OAAA,EAAS,QAAA,EAAU,OAAO,GAAA,CAAI,EAAA,EAAI,oBAAoB,SAAA,EAAU;AAAA,IAClE;AAAA,GACF;AAKA,EAAA,MAAM,IAAI,YAAA,EAAa;AACzB;;;ACzFA,IAAM,+BAAA,GAAkC,eAAA;AAExC,IAAM,uCAAuB,IAAI,GAAA,CAAI,CAAC,cAAA,EAAgB,cAAc,CAAC,CAAA;AAErE,SAAS,gBACP,KAAA,EACe;AACf,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,KAAA;AAEtC,EAAA,MAAM,EAAE,IAAA,EAAM,IAAA,GAAO,OAAO,QAAA,EAAU,QAAA,EAAU,UAAS,GAAI,KAAA;AAC7D,EAAA,MAAM,IAAA,GAAO,QAAA,IAAY,QAAA,GAAW,CAAA,EAAG,kBAAA,CAAmB,QAAQ,CAAC,CAAA,CAAA,EAAI,kBAAA,CAAmB,QAAQ,CAAC,CAAA,CAAA,CAAA,GAAM,EAAA;AACzG,EAAA,MAAMI,GAAAA,GAAK,QAAA,GAAW,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA,GAAK,EAAA;AACvC,EAAA,OAAO,aAAa,IAAI,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,IAAI,GAAGA,GAAE,CAAA,CAAA;AAC9C;AAEA,SAAS,cAAA,CACP,QACA,QAAA,EACM;AACN,EAAA,MAAM,SAAmB,EAAC;AAE1B,EAAA,KAAA,MAAW,CAAC,SAAS,SAAS,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA,EAAG;AAC9D,IAAA,MAAM,EAAA,GAAK,SAAA;AAGX,IAAA,IAAI,qBAAqB,GAAA,CAAI,EAAA,CAAG,IAAI,CAAA,IAAK,CAAC,QAAA,EAAU;AAClD,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,CAAA,CAAA,EAAI,OAAO,CAAA,GAAA,EAAM,EAAA,CAAG,IAAI,CAAA,uFAAA;AAAA,OAC1B;AAAA,IACF;AAGA,IAAA,IAAI,GAAG,IAAA,KAAS,cAAA,IAAkB,CAAC,EAAA,CAAG,IAAA,EAAM,MAAK,EAAG;AAClD,MAAA,MAAA,CAAO,IAAA,CAAK,CAAA,CAAA,EAAI,OAAO,CAAA,0CAAA,CAA4C,CAAA;AAAA,IACrE;AAGA,IAAA,IAAI,EAAA,CAAG,SAAS,cAAA,IAAkB,CAAC,GAAG,IAAA,IAAQ,CAAC,GAAG,SAAA,EAAW;AAC3D,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,IAAI,OAAO,CAAA,gEAAA;AAAA,OACb;AAAA,IACF;AAGA,IAAA,IAAI,EAAA,CAAG,IAAA,KAAS,cAAA,IAAkB,EAAA,CAAG,IAAA,EAAM;AACzC,MAAA,MAAM,cAAA,uBAAqB,GAAA,CAAI,CAAC,UAAU,QAAA,EAAU,MAAA,EAAQ,KAAA,EAAO,MAAM,CAAC,CAAA;AAC1E,MAAA,IAAI,CAAC,cAAA,CAAe,GAAA,CAAI,EAAA,CAAG,IAAA,CAAK,QAAQ,CAAA,EAAG;AACzC,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,CAAA,CAAA,EAAI,OAAO,CAAA,4CAAA,EAA+C,EAAA,CAAG,KAAK,QAAQ,CAAA,mDAAA;AAAA,SAC5E;AAAA,MACF;AACA,MAAA,IAAI,EAAA,CAAG,IAAA,CAAK,MAAA,IAAU,CAAA,EAAG;AACvB,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,IAAI,OAAO,CAAA,uDAAA;AAAA,SACb;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAI,EAAA,CAAG,SAAS,cAAA,EAAgB;AAC9B,MAAA,IAAI,EAAA,CAAG,YAAA,KAAiB,MAAA,IAAa,EAAA,CAAG,eAAe,CAAA,EAAG;AACxD,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,IAAI,OAAO,CAAA,2CAAA;AAAA,SACb;AAAA,MACF;AACA,MAAA,IAAI,EAAA,CAAG,cAAA,KAAmB,MAAA,IAAa,EAAA,CAAG,kBAAkB,CAAA,EAAG;AAC7D,QAAA,MAAA,CAAO,IAAA;AAAA,UACL,IAAI,OAAO,CAAA,0DAAA;AAAA,SACb;AAAA,MACF;AAAA,IACF;AAGA,IAAA,IAAI,OAAA,IAAW,EAAA,IAAM,EAAA,CAAG,KAAA,EAAO;AAE7B,MAAA,KAAA,MAAW,CAAC,UAAU,UAAU,CAAA,IAAK,OAAO,OAAA,CAAQ,EAAA,CAAG,KAA4B,CAAA,EAAG;AACpF,QAAA,IAAI,UAAA,EAAY,KAAA,IAAS,UAAA,CAAW,KAAA,KAAU,OAAO,YAAA,EAAc;AACjE,UAAA,MAAM,8BAAc,IAAI,GAAA,CAAY,CAAC,MAAA,CAAO,YAAY,CAAC,CAAA;AACzD,UAAA,KAAA,MAAW,CAAA,IAAK,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,IAAI,CAAA,EAAG;AAC1C,YAAA,IAAK,CAAA,CAAgB,KAAA,EAAO,WAAA,CAAY,GAAA,CAAK,EAAgB,KAAM,CAAA;AAAA,UACrE;AACA,UAAA,IAAI,CAAC,WAAA,CAAY,GAAA,CAAI,UAAA,CAAW,KAAK,CAAA,EAAG;AACtC,YAAA,MAAA,CAAO,IAAA;AAAA,cACL,IAAI,OAAO,CAAA,QAAA,EAAW,QAAQ,CAAA,oBAAA,EAAuB,WAAW,KAAK,CAAA,sDAAA;AAAA,aACvE;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA;AAAA,IAAA,EAA8C,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAC,CAAA;AAAA,KACrE;AAAA,EACF;AACF;AAEO,IAAM,eAAN,MAAsE;AAAA,EA2B3E,WAAA,CAAY,QAAiB,OAAA,EAA8B;AAzB3D,IAAA,IAAA,CAAU,kBAAA,GAA6B,CAAA;AAOvC,IAAA,IAAA,CAAU,UAAoB,EAAC;AAC/B,IAAA,IAAA,CAAU,YAAA,GAAoC,IAAA;AAC9C,IAAA,IAAA,CAAU,WAAA,GAAkC,IAAA;AAC5C,IAAA,IAAA,CAAU,cAAA,GAAwC,IAAA;AAClD,IAAA,IAAA,CAAU,YAAA,GAAoC,IAAA;AAC9C,IAAA,IAAA,CAAU,aAAA,GAAsC,IAAA;AAChD,IAAA,IAAA,CAAU,eAAA,GAA0C,IAAA;AACpD,IAAA,IAAA,CAAU,cAAA,GAAwC,IAAA;AAClD,IAAA,IAAA,CAAU,mBAAA,GAAuD,IAAA;AACjE,IAAA,IAAA,CAAU,oBAAA,GAAwD,IAAA;AAClE,IAAA,IAAA,CAAU,sBAAA,GAA4D,IAAA;AACtE,IAAA,IAAA,CAAU,qBAAA,GAA8C,IAAA;AAQtD,IAAA,IAAA,CAAK,kBAAkB,OAAA,CAAQ,KAAA;AAC/B,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,QAAA,GAAW,eAAA,CAAgB,OAAA,CAAQ,KAAK,CAAA;AAE7C,IAAA,UAAA,CAAW,QAAQ,MAAM,CAAA;AAEzB,IAAA,cAAA,CAAe,MAAA,EAAQ,KAAK,QAAQ,CAAA;AAEpC,IAAA,IAAA,CAAK,SAAS,UAAA,CAAW,MAAA,EAAQ,OAAA,CAAQ,KAAA,EAAO,QAAQ,SAAS,CAAA;AAEjE,IAAA,MAAM,EAAE,SAAA,EAAW,UAAA,KAAe,QAAA,CAAS,MAAA,EAAQ,KAAK,MAAM,CAAA;AAC9D,IAAA,IAAA,CAAK,IAAA,GAAO,SAAA;AACZ,IAAA,IAAA,CAAK,UAAA,GAAa,UAAA;AAElB,IAAA,IAAA,CAAK,iBAAA,GAAoB;AAAA,MACvB,SACE,IAAA,CAAK,QAAA,KAAa,IAAA,IAAQ,OAAA,CAAQ,YAAY,OAAA,KAAY,KAAA;AAAA,MAC5D,mBAAA,EAAqB,QAAQ,UAAA,EAAY,mBAAA;AAAA,MACzC,WAAA,EAAa;AAAA,QACX,aAAA,EACE,OAAA,CAAQ,UAAA,EAAY,WAAA,EAAa,aAAA,IACjC,+BAAA;AAAA,QACF,eAAA,EAAiB,OAAA,CAAQ,UAAA,EAAY,WAAA,EAAa;AAAA;AACpD,KACF;AAEA,IAAA,IAAI,QAAQ,UAAA,EAAY,OAAA,KAAY,IAAA,IAAQ,IAAA,CAAK,aAAa,IAAA,EAAM;AAClE,MAAA,SAAA,EAAU,CAAE,IAAA;AAAA,QACV,EAAC;AAAA,QACD;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAa,WAAA,CACX,SAAA,EACA,OAAA,EACA;AACA,IAAA,IAAI,KAAK,QAAA,EAAU;AACjB,MAAA,MAAM,YAAA,CAAa,KAAK,QAAQ,CAAA;AAChC,MAAA,MAAM,0BAAgB,aAAA,EAAc;AAAA,IACtC;AAEA,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,SAAS,CAAA;AACvC,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,SAAS,CAAA,qBAAA,CAAuB,CAAA;AAAA,IAC3D;AAEA,IAAA,MAAM,WAAA,GAAc,OAAA,EAAS,WAAA,IAAe,IAAA,CAAK,kBAAA;AAEjD,IAAA,IAAI,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,iBAAA,CAAkB,OAAA,EAAS;AACnD,MAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AACxB,QAAA,IAAI,CAAC,KAAK,qBAAA,EAAuB;AAC/B,UAAA,IAAA,CAAK,qBAAA,GAAwB,IAAA,CAAK,oBAAA,CAAqB,SAAA,EAAW,WAAW,CAAA;AAAA,QAC/E;AACA,QAAA,IAAI;AACF,UAAA,MAAM,IAAA,CAAK,qBAAA;AAAA,QACb,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAEA,MAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,QAAA,IAAI;AACF,UAAA,MAAM,KAAK,cAAA,CAAe,aAAA,CAAc,EAAE,IAAA,EAAM,SAAA,EAAW,aAAa,CAAA;AAAA,QAC1E,SAAS,GAAA,EAAK;AACZ,UAAA,SAAA,EAAU,CAAE,KAAA;AAAA,YACV,EAAE,GAAA,EAAI;AAAA,YACN;AAAA,WACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,IAAA,WAAA;AAAA,MACE,SAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAA,CAAK,eAAA;AAAA,MACL,IAAA,CAAK,UAAA;AAAA,MACL,IAAA,CAAK;AAAA,KACP;AAEA,IAAA,MAAM,iBAAA,CAAkB,SAAA,CAAU,KAAA,EAAO,IAAA,CAAK,UAAU,CAAA;AAExD,IAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,CAAE,IAAA;AAAA,MACpD,CAAC,CAAA,KAAO,CAAA,CAAgB,IAAA,KAAS;AAAA,KACnC;AAEA,IAAA,IAAI,cAAA,IAAkB,CAAC,IAAA,CAAK,YAAA,EAAc;AACxC,MAAA,IAAA,CAAK,eAAe,IAAI,YAAA;AAAA,QACtB,IAAA,CAAK,UAAA;AAAA,QACL,IAAA,CAAK,MAAA;AAAA,QACL,KAAK,MAAA,CAAO,YAAA;AAAA,QACZ,SAAA,CAAU;AAAA,OACZ;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAAS,YAAA;AAAA,MACb,SAAA;AAAA,MACA,IAAA,CAAK,eAAA;AAAA,MACL,WAAA;AAAA,MACA,OAAO,KAAK,KAAA,KAAU;AASpB,QAAA,MAAM,cAAc,IAAA,CAAK,cAAA;AACzB,QAAA,IAAI,WAAA,EAAa;AACf,UAAA,MAAM,eAAA;AAAA,YACJ,GAAA;AAAA,YACA,IAAA,CAAK,UAAA;AAAA,YACL,WAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF;AACA,QAAA,IAAI,IAAA,CAAK,YAAA,EAAc,UAAA,CAAW,GAAA,CAAI,IAAI,CAAA,EAAG;AAC3C,UAAA,OAAO,IAAA,CAAK,YAAA,CAAa,OAAA,CAAQ,GAAG,CAAA;AAAA,QACtC;AACA,QAAA,OAAO,UAAA,CAAW,KAAK,IAAA,CAAK,UAAA,EAAY,KAAK,MAAA,EAAQ,IAAA,CAAK,OAAO,YAAY,CAAA;AAAA,MAC/E;AAAA,KACF;AACA,IAAA,iBAAA,CAAkB,QAAQ,SAAS,CAAA;AACnC,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,MAAM,CAAA;AACxB,IAAA,MAAA,CAAO,GAAA,EAAI;AAEX,IAAA,IACE,IAAA,CAAK,iBAAA,CAAkB,OAAA,IACvB,IAAA,CAAK,gBACL,IAAA,CAAK,aAAA,IACL,IAAA,CAAK,eAAA,IACL,IAAA,CAAK,mBAAA,IACL,IAAA,CAAK,oBAAA,IACL,KAAK,sBAAA,EACL;AACA,MAAA,gBAAA,CAAiB,MAAA,EAAQ;AAAA,QACvB,OAAO,SAAA,CAAU,KAAA;AAAA,QACjB,YAAY,IAAA,CAAK,UAAA;AAAA,QACjB,cAAc,IAAA,CAAK,YAAA;AAAA,QACnB,eAAe,IAAA,CAAK,aAAA;AAAA,QACpB,iBAAiB,IAAA,CAAK,eAAA;AAAA,QACtB,qBAAqB,IAAA,CAAK,mBAAA;AAAA,QAC1B,sBAAsB,IAAA,CAAK,oBAAA;AAAA,QAC3B,wBAAwB,IAAA,CAAK;AAAA,OAC9B,CAAA;AAAA,IACH;AAEA,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,MAAM,IAAA,CAAK,aAAa,KAAA,EAAM;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,MAAgB,oBAAA,CACd,SAAA,EACA,WAAA,EACe;AACf,IAAA,IAAI,QAAA,GAAkC,IAAA;AACtC,IAAA,IAAI,gBAAA,GAAoD,IAAA;AACxD,IAAA,IAAI,iBAAA,GAAqD,IAAA;AACzD,IAAA,IAAI,mBAAA,GAAyD,IAAA;AAC7D,IAAA,IAAI;AACF,MAAA,MAAMA,MAAK,KAAA,EAAM;AACjB,MAAA,MAAM,WAAA,GAAc,IAAI,WAAA,CAAYA,GAAE,CAAA;AACtC,MAAA,MAAM,SAAA,GAAY,IAAI,YAAA,CAAaA,GAAE,CAAA;AACrC,MAAA,MAAM,UAAA,GAAa,IAAI,aAAA,CAAcA,GAAE,CAAA;AACvC,MAAA,MAAM,YAAA,GAAe,IAAI,eAAA,CAAgBA,GAAE,CAAA;AAC3C,MAAA,MAAM,WAAA,GAAc,IAAI,cAAA,CAAeA,GAAE,CAAA;AACzC,MAAA,MAAM,QAAQ,GAAA,CAAI;AAAA,QAChB,YAAY,IAAA,EAAK;AAAA,QACjB,UAAU,IAAA,EAAK;AAAA,QACf,WAAW,IAAA,EAAK;AAAA,QAChB,aAAa,IAAA,EAAK;AAAA,QAClB,YAAY,IAAA;AAAK,OAClB,CAAA;AASD,MAAA,MAAM,uBAAA;AAAA,QACJ,WAAA;AAAA,QACA,KAAK,MAAA,CAAO;AAAA,OACd;AAEA,MAAA,QAAA,GAAW,IAAI,eAAe,WAAA,EAAa;AAAA,QACzC,mBAAA,EAAqB,KAAK,iBAAA,CAAkB;AAAA,OAC7C,CAAA;AACD,MAAA,MAAM,SAAS,aAAA,CAAc,EAAE,IAAA,EAAM,SAAA,EAAW,aAAa,CAAA;AAC7D,MAAA,MAAM,SAAS,KAAA,EAAM;AAErB,MAAA,gBAAA,GAAmB,IAAI,WAAA;AAAA,QACrB,SAAA,CAAU,UAAA;AAAA,QACV,KAAK,iBAAA,CAAkB;AAAA,OACzB;AACA,MAAA,iBAAA,GAAoB,IAAI,WAAA;AAAA,QACtB,UAAA,CAAW,UAAA;AAAA,QACX,KAAK,iBAAA,CAAkB;AAAA,OACzB;AACA,MAAA,mBAAA,GAAsB,IAAI,WAAA;AAAA,QACxB,YAAA,CAAa,UAAA;AAAA,QACb,KAAK,iBAAA,CAAkB;AAAA,OACzB;AAEA,MAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AACnB,MAAA,IAAA,CAAK,cAAA,GAAiB,QAAA;AACtB,MAAA,IAAA,CAAK,YAAA,GAAe,SAAA;AACpB,MAAA,IAAA,CAAK,aAAA,GAAgB,UAAA;AACrB,MAAA,IAAA,CAAK,eAAA,GAAkB,YAAA;AACvB,MAAA,IAAA,CAAK,cAAA,GAAiB,WAAA;AACtB,MAAA,IAAA,CAAK,mBAAA,GAAsB,gBAAA;AAC3B,MAAA,IAAA,CAAK,oBAAA,GAAuB,iBAAA;AAC5B,MAAA,IAAA,CAAK,sBAAA,GAAyB,mBAAA;AAAA,IAChC,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,EAAU,CAAE,KAAA;AAAA,QACV,EAAE,GAAA,EAAI;AAAA,QACN;AAAA,OACF;AACA,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,MAAM,QAAA,CAAS,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAAA,MACtC;AACA,MAAA,MAAM,UAAA,GAAa,CAAC,gBAAA,EAAkB,iBAAA,EAAmB,mBAAmB,CAAA,CAAE,MAAA;AAAA;AAAA,QAE5E,CAAC,OAA+B,EAAA,KAAO;AAAA,OACzC;AACA,MAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,QAAA,MAAM,OAAA,CAAQ,WAAW,UAAA,CAAW,GAAA,CAAI,CAAC,EAAA,KAAO,EAAA,CAAG,KAAA,EAAO,CAAC,CAAA;AAAA,MAC7D;AACA,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,MAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,MAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,MAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AACrB,MAAA,IAAA,CAAK,eAAA,GAAkB,IAAA;AACvB,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,MAAA,IAAA,CAAK,mBAAA,GAAsB,IAAA;AAC3B,MAAA,IAAA,CAAK,oBAAA,GAAuB,IAAA;AAC5B,MAAA,IAAA,CAAK,sBAAA,GAAyB,IAAA;AAAA,IAChC,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,qBAAA,GAAwB,IAAA;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,MAAa,KAAA,GAAuB;AAClC,IAAA,MAAMH,UAAS,SAAA,EAAU;AACzB,IAAAA,OAAAA,CAAO,KAAK,kBAAkB,CAAA;AAE9B,IAAA,IAAI,KAAK,YAAA,EAAc;AACrB,MAAA,MAAM,IAAA,CAAK,aAAa,IAAA,EAAK;AAC7B,MAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAAA,IACtB;AAEA,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,MAAM,IAAA,CAAK,eAAe,IAAA,EAAK;AAC/B,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,MAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AAAA,IACrB;AAEA,IAAA,MAAM,YAAA,CAAa,KAAK,OAAO,CAAA;AAE/B,IAAA,MAAM,UAAA,GAAa;AAAA,MACjB,IAAA,CAAK,mBAAA;AAAA,MACL,IAAA,CAAK,oBAAA;AAAA,MACL,IAAA,CAAK;AAAA;AAAA,KAEP,CAAE,MAAA,CAAO,CAAC,EAAA,KAA+B,OAAO,IAAI,CAAA;AACpD,IAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,MAAA,MAAM,OAAA,CAAQ,WAAW,UAAA,CAAW,GAAA,CAAI,CAAC,EAAA,KAAO,EAAA,CAAG,KAAA,EAAO,CAAC,CAAA;AAAA,IAC7D;AACA,IAAA,IAAA,CAAK,mBAAA,GAAsB,IAAA;AAC3B,IAAA,IAAA,CAAK,oBAAA,GAAuB,IAAA;AAC5B,IAAA,IAAA,CAAK,sBAAA,GAAyB,IAAA;AAC9B,IAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,IAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AACrB,IAAA,IAAA,CAAK,eAAA,GAAkB,IAAA;AACvB,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AAEtB,IAAA,MAAM,WAAA,CAAY,KAAK,MAAM,CAAA;AAC7B,IAAA,MAAM,UAAA,EAAW;AACjB,IAAAA,OAAAA,CAAO,KAAK,oBAAoB,CAAA;AAAA,EAClC;AACF;;;AC/XO,SAAS,kBAAA,CACd,QACA,OAAA,EACuB;AACvB,EAAA,OAAO,IAAI,YAAA,CAAa,MAAA,EAAQ,OAAO,CAAA;AACzC","file":"index.mjs","sourcesContent":["import pino from \"pino\";\n\nexport type Logger = pino.Logger;\n\nexport interface LoggerOptions {\n level?: \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\" | \"fatal\";\n pretty?: boolean;\n}\n\nlet logger: Logger | null = null;\n\nexport function initLogger(options: LoggerOptions = {}): Logger {\n if (logger) return logger;\n\n const isDev = process.env.NODE_ENV !== \"production\";\n const usePretty = options.pretty ?? isDev;\n\n logger = pino({\n level: options.level || (isDev ? \"debug\" : \"info\"),\n transport: usePretty\n ? {\n target: \"pino-pretty\",\n options: {\n colorize: true,\n translateTime: \"HH:MM:ss\",\n ignore: \"pid,hostname\",\n },\n }\n : undefined,\n });\n\n return logger;\n}\n\nexport function getLogger(): Logger {\n if (!logger) {\n throw new Error(\"[ravenflow] Logger not initialized. Call initLogger() first.\");\n }\n return logger;\n}\n","import { ConnectionOptions, DefaultJobOptions, Queue, QueueEvents } from \"bullmq\";\nimport { RavenflowConfig, JobConfig, RetentionConfig } from \"../types\";\n\nexport type QueueStore = Record<string, { queue: Queue; events: QueueEvents }>;\n\nfunction buildDefaultJobOptions(\n retention?: RetentionConfig,\n): DefaultJobOptions | undefined {\n if (!retention) return undefined;\n\n const opts: DefaultJobOptions = {};\n\n if (retention.completed !== undefined) {\n opts.removeOnComplete = { count: retention.completed };\n }\n if (retention.failed !== undefined) {\n opts.removeOnFail = { count: retention.failed };\n }\n\n return Object.keys(opts).length > 0 ? opts : undefined;\n}\n\nexport function initQueues(\n config: RavenflowConfig,\n connection: ConnectionOptions,\n retention?: RetentionConfig,\n): QueueStore {\n const queueNames = new Set<string>([config.defaultQueue]);\n for (const job of Object.values<JobConfig>(config.jobs)) {\n if (job.queue) queueNames.add(job.queue);\n }\n\n const defaultJobOptions = buildDefaultJobOptions(retention);\n\n const queues: QueueStore = {};\n for (const name of queueNames) {\n queues[name] = {\n queue: new Queue(name, { connection, defaultJobOptions }),\n events: new QueueEvents(name, { connection }),\n };\n }\n return queues;\n}\n\n// Generic-erasure internals: `params` width is resolved at the typed proxy boundary\n// (`JobProxy<T>.send`); here the dispatcher accepts the erased shape. Typed config-walker\n// deferred per Story 1.8 AC #15.\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport async function sendJob(\n jobName: string,\n params: any,\n queues: QueueStore,\n queueName: string,\n) {\n const queueData = queues[queueName];\n if (!queueData) {\n throw new Error(`Queue ${queueName} not found`);\n }\n return await queueData.queue.add(jobName, params);\n}\n\nexport async function sendJobAndWait(\n jobName: string,\n params: any,\n queues: QueueStore,\n queueName: string,\n) {\n const queueData = queues[queueName];\n if (!queueData) {\n throw new Error(`Queue ${queueName} not found`);\n }\n const job = await queueData.queue.add(jobName, params);\n return await job.waitUntilFinished(queueData.events);\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\nexport async function closeQueues(queues: QueueStore) {\n await Promise.all(\n Object.values(queues).map(({ queue, events }) =>\n Promise.all([queue.close(), events.close()]),\n ),\n );\n}\n","import type { TokenBucketInterval } from \"@/types\";\n\nexport const TOKEN_BUCKET_CONSUME_SUFFIX = \":consume\";\n\nexport const INTERVAL_MS: Record<TokenBucketInterval, number> = {\n second: 1_000,\n minute: 60_000,\n hour: 3_600_000,\n day: 86_400_000,\n week: 604_800_000,\n};\n","export const ETL_EXTRACT_SUFFIX = \":extract\";\nexport const ETL_TRANSFORM_SUFFIX = \":transform\";\nexport const ETL_LOAD_SUFFIX = \":load\";\nexport { TOKEN_BUCKET_CONSUME_SUFFIX } from \"./token-bucket-job.processor/constants\";\n","import { Job } from \"bullmq\";\nimport { SimpleJobConfig } from \"../types\";\n\nexport async function processSimpleJob<TData, TReturn>(\n job: Job,\n jobConfig: SimpleJobConfig<TData, TReturn>\n): Promise<TReturn> {\n return jobConfig.handler(job, job.data);\n}\n","import { z } from \"zod\";\n\n// ---------------------------------------------------------------------------\n// Job config base (shared across all job configs)\n// ---------------------------------------------------------------------------\n\nexport interface BaseJobConfig {\n queue?: string;\n cron?: string;\n attempts?: number;\n backoff?: { type: \"fixed\" | \"exponential\"; delay: number };\n}\n\n/** Job config with queue resolved (always has a queue name). */\nexport type ResolvedJobConfig<T extends BaseJobConfig> = T & { queue: string };\n\nexport const zRateLimitConfig = z.union([\n z.object({ perSecond: z.number().positive() }),\n z.object({ perMinute: z.number().positive() }),\n]);\n\nexport type RateLimitConfig = z.infer<typeof zRateLimitConfig>;\n\nexport interface MemoryEtlStepConfig {\n rateLimit?: RateLimitConfig;\n}\n\nexport interface StepConfig extends MemoryEtlStepConfig {\n attempts?: number;\n backoff?: { type: \"fixed\" | \"exponential\"; delay: number };\n queue?: string;\n}\n\nexport interface EtlExtractResult<TRaw, TCursor> {\n data: TRaw[];\n cursor: TCursor | null;\n hasMore: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Job entity base (shared across all DB-persisted jobs)\n// ---------------------------------------------------------------------------\n\nexport const zJobStatus = z.enum([\n \"pending\",\n \"in_progress\",\n \"completed\",\n \"failed\",\n \"stalled\",\n]);\nexport type JobStatus = z.infer<typeof zJobStatus>;\n\nexport const zBaseJob = z.object({\n _id: z.string(),\n type: z.string(),\n jobId: z.string(),\n jobName: z.string(),\n status: zJobStatus,\n params: z.unknown().nullable(),\n result: z.unknown().nullable(),\n error: z.string().nullable(),\n\n timestamps: z.object({\n createdAt: z.date(),\n updatedAt: z.date(),\n startedAt: z.date().nullable(),\n completedAt: z.date().nullable(),\n }),\n});\n\nexport type BaseJob = z.infer<typeof zBaseJob>;\n","import { z } from \"zod\";\nimport { Job } from \"bullmq\";\nimport { BaseJobConfig, zBaseJob } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface SimpleJobConfig<TData, TReturn> extends BaseJobConfig {\n type: \"simple\";\n handler: (job: Job, params: TData) => Promise<TReturn>;\n}\n\nexport function defineSimpleJob<TData, TReturn>(\n config: Omit<SimpleJobConfig<TData, TReturn>, \"type\">,\n): SimpleJobConfig<TData, TReturn> {\n return { ...config, type: \"simple\" };\n}\n\n// ---------------------------------------------------------------------------\n// Entity\n// ---------------------------------------------------------------------------\n\nexport const zSimpleJob = zBaseJob.extend({\n type: z.literal(\"simple\"),\n});\n\nexport type SimpleJob = z.infer<typeof zSimpleJob>;\n","import { z } from \"zod\";\nimport { BaseJobConfig, EtlExtractResult, MemoryEtlStepConfig, zBaseJob } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface MemoryEtlJobConfig<TRaw, TProcessed, TCursor = number>\n extends BaseJobConfig {\n type: \"memory-etl\";\n extract: (\n cursor: TCursor | null,\n ) => Promise<EtlExtractResult<TRaw, TCursor>>;\n transform: (data: TRaw) => Promise<TProcessed>;\n load: (data: TProcessed[]) => Promise<void>;\n steps?: {\n extract?: MemoryEtlStepConfig;\n transform?: MemoryEtlStepConfig;\n load?: MemoryEtlStepConfig;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entity\n// ---------------------------------------------------------------------------\n\nexport const zMemoryEtlJob = zBaseJob.extend({\n type: z.literal(\"memory-etl\"),\n\n progress: z.object({\n extractedCount: z.number().int().nonnegative(),\n transformedCount: z.number().int().nonnegative(),\n loadedCount: z.number().int().nonnegative(),\n }),\n});\n\nexport type MemoryEtlJob = z.infer<typeof zMemoryEtlJob>;\n\nexport function defineMemoryEtlJob<TRaw, TProcessed, TCursor = number>(\n config: Omit<MemoryEtlJobConfig<TRaw, TProcessed, TCursor>, \"type\">,\n): MemoryEtlJobConfig<TRaw, TProcessed, TCursor> {\n return { ...config, type: \"memory-etl\" };\n}\n","import { z } from \"zod\";\nimport { BaseJobConfig, EtlExtractResult, StepConfig, zBaseJob } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface DistributedEtlJobConfig<TRaw, TProcessed, TCursor = number>\n extends BaseJobConfig {\n type: \"distributed-etl\";\n extract: (cursor: TCursor | null) => Promise<EtlExtractResult<TRaw, TCursor>>;\n transform: (data: TRaw) => Promise<TProcessed>;\n load: (data: TProcessed[]) => Promise<void>;\n steps?: {\n extract?: StepConfig;\n transform?: StepConfig;\n load?: StepConfig;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entity\n// ---------------------------------------------------------------------------\n\nexport const zDistributedEtlJob = zBaseJob.extend({\n type: z.literal(\"distributed-etl\"),\n\n progress: z.object({\n extractedCount: z.number().int().nonnegative(),\n transformedCount: z.number().int().nonnegative(),\n loadedCount: z.number().int().nonnegative(),\n }),\n});\n\nexport type DistributedEtlJob = z.infer<typeof zDistributedEtlJob>;\n\nexport function defineDistributedEtlJob<TRaw, TProcessed, TCursor = number>(\n config: Omit<DistributedEtlJobConfig<TRaw, TProcessed, TCursor>, \"type\">,\n): DistributedEtlJobConfig<TRaw, TProcessed, TCursor> {\n return { ...config, type: \"distributed-etl\" };\n}\n","import { z } from \"zod\";\nimport { BaseJobConfig, EtlExtractResult, StepConfig, zBaseJob } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport interface StatefulEtlJobConfig<TRaw, TProcessed, TCursor = string>\n extends BaseJobConfig {\n type: \"stateful-etl\";\n\n extract: (\n cursor: TCursor | null,\n ) => Promise<EtlExtractResult<TRaw, TCursor>>;\n transform: (data: TRaw) => Promise<TProcessed>;\n load: (data: TProcessed[]) => Promise<void>;\n\n /**\n * When true, only one run of this job can be active at a time.\n * If a run is already pending or in_progress (with a live heartbeat), the new trigger is skipped.\n * Defaults to false — each trigger creates an independent run.\n */\n singletonRun?: boolean;\n stallTimeoutMs?: number;\n maxStallRetries?: number;\n steps?: {\n extract?: StepConfig;\n transform?: StepConfig;\n load?: StepConfig;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Entity\n// ---------------------------------------------------------------------------\n\nexport const zStatefulEtlJob = zBaseJob.extend({\n type: z.literal(\"stateful-etl\"),\n\n stallTimeoutMs: z.number().int().positive(),\n maxStallRetries: z.number().int().nonnegative(),\n\n progress: z.object({\n transformedCount: z.number().int().nonnegative(),\n extractedCount: z.number().int().nonnegative(),\n loadedCount: z.number().int().nonnegative(),\n\n checkpoint: z.unknown().nullable(),\n lastHeartbeatAt: z.date().nullable(),\n stallRetries: z.number().int().nonnegative(),\n }),\n});\n\nexport type StatefulEtlJob = z.infer<typeof zStatefulEtlJob>;\n\nexport function defineStatefulEtlJob<TRaw, TProcessed, TCursor = string>(\n config: Omit<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>, \"type\">,\n): StatefulEtlJobConfig<TRaw, TProcessed, TCursor> {\n return { ...config, type: \"stateful-etl\" };\n}\n","import { z } from \"zod\";\nimport { BaseJobConfig } from \"./common\";\n\n// ---------------------------------------------------------------------------\n// Config\n// ---------------------------------------------------------------------------\n\nexport type TokenBucketInterval = \"second\" | \"minute\" | \"hour\" | \"day\" | \"week\";\n\nexport interface TokenBucketJobConfig extends BaseJobConfig {\n type: \"token-bucket\";\n cron: string;\n\n rate?: {\n amount: number;\n interval: TokenBucketInterval;\n };\n\n getBudget?: () => Promise<number> | number;\n maxCarryOver?: number;\n consumeTimeout?: number;\n\n consume: (tokens: number) => Promise<number | void> | number | void;\n}\n\n// ---------------------------------------------------------------------------\n// State entity (persisted in MongoDB — one document per job)\n// ---------------------------------------------------------------------------\n\nexport const zTokenBucketState = z.object({\n _id: z.string(),\n accumulatedCredit: z.number(),\n lastTickAt: z.date(),\n});\n\nexport type TokenBucketState = z.infer<typeof zTokenBucketState>;\n\nexport function defineTokenBucketJob(\n config: Omit<TokenBucketJobConfig, \"type\">,\n): TokenBucketJobConfig {\n return { ...config, type: \"token-bucket\" };\n}\n","import { z } from \"zod\";\nimport { SimpleJobConfig, zSimpleJob } from \"./simple\";\nimport { MemoryEtlJobConfig, zMemoryEtlJob } from \"./memory-etl\";\nimport { DistributedEtlJobConfig, zDistributedEtlJob } from \"./distributed-etl\";\nimport { StatefulEtlJobConfig, zStatefulEtlJob } from \"./stateful-etl\";\nimport { TokenBucketJobConfig } from \"./token-bucket\";\n\n// ---------------------------------------------------------------------------\n// Config union (discriminated on `type`)\n// ---------------------------------------------------------------------------\n\nexport type JobConfig =\n // `any` in type-parameter position: `JobConfig` is the discriminated-union of all\n // job definitions regardless of their data/return generics. A typed config-walker\n // is deferred per Story 1.8 AC #14 — flagged in deferred-work.md.\n /* eslint-disable @typescript-eslint/no-explicit-any */\n | SimpleJobConfig<any, any>\n | MemoryEtlJobConfig<any, any, any>\n | DistributedEtlJobConfig<any, any, any>\n | StatefulEtlJobConfig<any, any, any>\n | TokenBucketJobConfig;\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n// ---------------------------------------------------------------------------\n// Entity union (discriminated on `type`)\n// ---------------------------------------------------------------------------\n\nexport const zRavenflowJob = z.discriminatedUnion(\"type\", [\n zSimpleJob,\n zMemoryEtlJob,\n zDistributedEtlJob,\n zStatefulEtlJob,\n]);\n\nexport type RavenflowJob = z.infer<typeof zRavenflowJob>;\n","import { JobConfig } from \"./job\";\n\nexport interface RetentionConfig {\n /** Number of completed jobs to keep per queue. Set to 0 to remove immediately. */\n completed?: number;\n /** Number of failed jobs to keep per queue. Set to 0 to remove immediately. */\n failed?: number;\n}\n\nexport interface RavenflowConfig<TJobs extends string = string> {\n defaultQueue: string;\n jobs: Record<TJobs, JobConfig>;\n}\n\nexport type JobNames<T extends RavenflowConfig> =\n T extends RavenflowConfig<infer TJobs> ? TJobs : never;\n\nexport function defineConfig<const T extends RavenflowConfig>(config: T): T {\n return config;\n}\n","import { type RateLimitConfig, zRateLimitConfig } from \"./types\";\n\nexport class RateLimiter {\n private slots = new Map<string, number>(); // key → nextAvailableAt\n\n /**\n * Block until a slot is available. Combines getDelay + wait in a loop.\n */\n public async waitForSlot(\n key: string,\n config: RateLimitConfig,\n ): Promise<void> {\n let delay = this.getDelay(key, config);\n while (delay > 0) {\n await this.wait(delay);\n delay = this.getDelay(key, config);\n }\n }\n\n /**\n * Check if a slot is available. Returns 0 if allowed (and consumes the slot),\n * or the number of ms to wait before retrying.\n *\n * Slots are evenly spaced: perSecond: 10 → one slot every 100ms.\n */\n public getDelay(key: string, config: RateLimitConfig): number {\n const intervalMs = resolve(config);\n const now = Date.now();\n const nextAvailableAt = this.slots.get(key);\n\n if (nextAvailableAt === undefined || now >= nextAvailableAt) {\n this.slots.set(key, now + intervalMs);\n return 0;\n }\n\n return nextAvailableAt - now;\n }\n\n /**\n * Wait for the given number of ms.\n */\n public wait(ms: number): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\nfunction resolve(config: RateLimitConfig): number {\n zRateLimitConfig.parse(config);\n if (\"perSecond\" in config) return 1_000 / config.perSecond;\n return 60_000 / config.perMinute;\n}\n","import { Job } from \"bullmq\";\nimport { MemoryEtlJobConfig } from \"@/types\";\nimport { getLogger } from \"@/logger\";\nimport { RateLimiter } from \"@/rate-limiter\";\n\nexport async function processMemoryEtlJob<TRaw, TProcessed, TCursor>(\n job: Job,\n config: MemoryEtlJobConfig<TRaw, TProcessed, TCursor>,\n) {\n const logger = getLogger();\n const jobName = job.name;\n const counts = { extractedCount: 0, transformedCount: 0, loadedCount: 0 };\n const limiter = config.steps ? new RateLimiter() : null;\n\n let cursor: TCursor | null = null;\n let hasMore = true;\n\n while (hasMore) {\n await applyRateLimit(limiter, jobName, \"extract\", config);\n\n const page = await config.extract(cursor);\n hasMore = page.hasMore;\n cursor = page.cursor;\n\n if (page.data.length === 0) continue;\n\n counts.extractedCount += page.data.length;\n\n const processed = await transformParallel(\n page.data,\n config.transform,\n limiter,\n jobName,\n config,\n );\n counts.transformedCount += processed.length;\n\n await applyRateLimit(limiter, jobName, \"load\", config);\n await config.load(processed);\n counts.loadedCount += processed.length;\n\n logger.debug({ jobName, ...counts }, \"[memory-etl] batch done\");\n }\n\n logger.info({ jobName, ...counts }, \"[memory-etl] completed\");\n return counts;\n}\n\nasync function transformParallel<TRaw, TProcessed, TCursor>(\n batch: TRaw[],\n transform: (item: TRaw) => Promise<TProcessed>,\n limiter: RateLimiter | null,\n jobName: string,\n config: MemoryEtlJobConfig<TRaw, TProcessed, TCursor>,\n): Promise<TProcessed[]> {\n const pending: Promise<TProcessed>[] = [];\n\n for (const item of batch) {\n await applyRateLimit(limiter, jobName, \"transform\", config);\n pending.push(transform(item));\n }\n\n return Promise.all(pending);\n}\n\ntype StepName = \"extract\" | \"transform\" | \"load\";\n\nasync function applyRateLimit<TRaw, TProcessed, TCursor>(\n limiter: RateLimiter | null,\n jobName: string,\n step: StepName,\n config: MemoryEtlJobConfig<TRaw, TProcessed, TCursor>,\n): Promise<void> {\n const rlConfig = config.steps?.[step]?.rateLimit;\n if (!limiter || !rlConfig) return;\n await limiter.waitForSlot(`ravenflow:rl:${jobName}:${step}`, rlConfig);\n}\n","import { Job, Queue, QueueEvents } from \"bullmq\";\n\nexport interface SubJobOptions {\n attempts?: number;\n backoff?: { type: \"fixed\" | \"exponential\"; delay: number };\n}\n\n/**\n * Dispatch a single sub-job and wait for its result.\n */\nexport async function dispatchSubJob<TData, TReturn>(\n queue: Queue,\n queueEvents: QueueEvents,\n name: string,\n data: TData,\n options?: SubJobOptions,\n): Promise<TReturn> {\n const job = await queue.add(name, data, {\n attempts: options?.attempts,\n backoff: options?.backoff,\n });\n\n return (await job.waitUntilFinished(queueEvents)) as TReturn;\n}\n\n/**\n * Dispatch multiple sub-jobs in parallel and wait for all results.\n */\nexport async function dispatchSubJobs<TData, TReturn>(\n queue: Queue,\n queueEvents: QueueEvents,\n name: string,\n items: TData[],\n options?: SubJobOptions,\n): Promise<TReturn[]> {\n const jobs = await queue.addBulk(\n items.map((data) => ({\n name,\n data,\n opts: {\n attempts: options?.attempts,\n backoff: options?.backoff,\n },\n })),\n );\n\n return Promise.all(\n jobs.map((job) => job.waitUntilFinished(queueEvents) as Promise<TReturn>),\n );\n}\n\n/**\n * Dispatch multiple sub-jobs without waiting. Returns the BullMQ Job instances\n * so the caller can track or ignore them.\n */\nexport async function dispatchSubJobsFireAndForget<TData>(\n queue: Queue,\n name: string,\n items: TData[],\n options?: SubJobOptions,\n): Promise<Job[]> {\n return queue.addBulk(\n items.map((data) => ({\n name,\n data,\n opts: {\n attempts: options?.attempts,\n backoff: options?.backoff,\n },\n })),\n );\n}\n","import { Queue, QueueEvents } from \"bullmq\";\nimport { StepConfig } from \"@/types\";\nimport { getLogger } from \"@/logger\";\nimport { RateLimiter } from \"@/rate-limiter\";\nimport { dispatchSubJob, SubJobOptions } from \"./sub-job\";\nimport { ETL_EXTRACT_SUFFIX, ETL_TRANSFORM_SUFFIX, ETL_LOAD_SUFFIX } from \"./constants\";\n\nexport type StepName = \"extract\" | \"transform\" | \"load\";\n\ninterface StepContext {\n queue: Queue;\n queueEvents: QueueEvents;\n options: SubJobOptions;\n}\n\nexport interface ProgressCounts {\n extractedCount: number;\n transformedCount: number;\n loadedCount: number;\n}\n\ninterface EtlConfig {\n queue: string;\n attempts?: number;\n backoff?: { type: \"fixed\" | \"exponential\"; delay: number };\n steps?: {\n extract?: StepConfig;\n transform?: StepConfig;\n load?: StepConfig;\n };\n}\n\nexport class EtlContext<TRaw, TProcessed> {\n readonly jobName: string;\n readonly counts: ProgressCounts;\n readonly logger: ReturnType<typeof getLogger>;\n\n private readonly steps: Record<StepName, StepContext>;\n private readonly limiter: RateLimiter | null;\n private readonly stepsConfig: EtlConfig[\"steps\"];\n\n constructor(\n jobName: string,\n config: EtlConfig,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n initialCounts?: ProgressCounts,\n ) {\n this.jobName = jobName;\n this.logger = getLogger();\n this.stepsConfig = config.steps;\n this.limiter = config.steps ? new RateLimiter() : null;\n this.counts = initialCounts\n ? { ...initialCounts }\n : { extractedCount: 0, transformedCount: 0, loadedCount: 0 };\n\n const defaultQueue = queues[config.queue];\n this.steps = this.buildStepContexts(config, queues, defaultQueue);\n }\n\n // ---------------------------------------------------------------------------\n // Rate limiting\n // ---------------------------------------------------------------------------\n\n async applyRateLimit(step: StepName): Promise<void> {\n const config = this.stepsConfig?.[step]?.rateLimit;\n if (!this.limiter || !config) return;\n await this.limiter.waitForSlot(\n `ravenflow:rl:${this.jobName}:${step}`,\n config,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Sub-job dispatch primitives\n // ---------------------------------------------------------------------------\n\n async dispatchExtract<TCursor>(\n cursor: TCursor | null,\n ): Promise<{\n data: TRaw[];\n cursor: TCursor | null;\n hasMore: boolean;\n }> {\n await this.applyRateLimit(\"extract\");\n const { queue, queueEvents, options } = this.steps.extract;\n\n return dispatchSubJob(\n queue,\n queueEvents,\n `${this.jobName}${ETL_EXTRACT_SUFFIX}`,\n { cursor },\n options,\n );\n }\n\n async dispatchTransform(item: TRaw): Promise<TProcessed> {\n const { queue, queueEvents, options } = this.steps.transform;\n\n return dispatchSubJob<TRaw, TProcessed>(\n queue,\n queueEvents,\n `${this.jobName}${ETL_TRANSFORM_SUFFIX}`,\n item,\n options,\n );\n }\n\n async transformParallel(rawItems: TRaw[]): Promise<TProcessed[]> {\n const pending: Promise<TProcessed>[] = [];\n\n for (const item of rawItems) {\n await this.applyRateLimit(\"transform\");\n pending.push(this.dispatchTransform(item));\n }\n\n return Promise.all(pending);\n }\n\n async dispatchLoad(processedItems: TProcessed[]): Promise<void> {\n await this.applyRateLimit(\"load\");\n const { queue, queueEvents, options } = this.steps.load;\n\n await dispatchSubJob(\n queue,\n queueEvents,\n `${this.jobName}${ETL_LOAD_SUFFIX}`,\n { data: processedItems, jobName: this.jobName },\n options,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Step context setup\n // ---------------------------------------------------------------------------\n\n private buildStepContexts(\n config: EtlConfig,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n defaultQueue: { queue: Queue; events: QueueEvents },\n ): Record<StepName, StepContext> {\n const resolve = (step: StepConfig | undefined): StepContext => {\n const q = step?.queue ? queues[step.queue] : defaultQueue;\n if (!q) throw new Error(`Queue \"${step?.queue}\" not found`);\n return {\n queue: q.queue,\n queueEvents: q.events,\n options: {\n attempts: step?.attempts ?? config.attempts,\n backoff: step?.backoff ?? config.backoff,\n },\n };\n };\n\n return {\n extract: resolve(config.steps?.extract),\n transform: resolve(config.steps?.transform),\n load: resolve(config.steps?.load),\n };\n }\n}\n","import { Job, Queue, QueueEvents } from \"bullmq\";\nimport { DistributedEtlJobConfig, EtlExtractResult, ResolvedJobConfig } from \"@/types\";\nimport { EtlContext } from \"./etl-context\";\n\nexport async function processDistributedEtlJob<TRaw, TProcessed, TCursor>(\n job: Job,\n config: ResolvedJobConfig<DistributedEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n) {\n const ctx = new EtlContext<TRaw, TProcessed>(job.name, config, queues);\n\n let cursor: TCursor | null = null;\n let hasMore = true;\n\n while (hasMore) {\n const page: EtlExtractResult<TRaw, TCursor> = await ctx.dispatchExtract<TCursor>(cursor);\n hasMore = page.hasMore;\n cursor = page.cursor;\n\n if (page.data.length === 0) continue;\n\n ctx.counts.extractedCount += page.data.length;\n\n const processed = await ctx.transformParallel(page.data);\n ctx.counts.transformedCount += processed.length;\n\n await ctx.dispatchLoad(processed);\n ctx.counts.loadedCount += processed.length;\n\n ctx.logger.debug({ jobName: ctx.jobName, ...ctx.counts }, \"[distributed-etl] batch done\");\n }\n\n ctx.logger.info({ jobName: ctx.jobName, ...ctx.counts }, \"[distributed-etl] completed\");\n return ctx.counts;\n}\n","import { MongoClient, Db } from \"mongodb\";\n\nconst COLLECTION_PREFIX = \"ravenflow_\";\n\nlet client: MongoClient | null = null;\nlet db: Db | null = null;\n\nexport async function connectMongo(uri: string): Promise<Db> {\n if (db) return db;\n\n client = new MongoClient(uri);\n await client.connect();\n db = client.db();\n\n return db;\n}\n\nexport function getDb(): Db {\n if (!db) {\n throw new Error(\"[ravenflow] MongoDB not connected. Pass a mongo URI to createRavenflowApp().\");\n }\n return db;\n}\n\nexport function collectionName(name: string): string {\n return `${COLLECTION_PREFIX}${name}`;\n}\n\nexport async function closeMongo(): Promise<void> {\n if (client) {\n await client.close();\n client = null;\n db = null;\n }\n}\n","import { Collection, ObjectId } from \"mongodb\";\nimport { getDb, collectionName } from \"./connection\";\nimport type { StatefulEtlJob } from \"@/types\";\n\nconst RUNS_COLLECTION = \"runs\";\n\nfunction collection(): Collection<StatefulEtlJob> {\n return getDb().collection<StatefulEtlJob>(collectionName(RUNS_COLLECTION));\n}\n\nexport async function findActiveRun(jobName: string): Promise<StatefulEtlJob | null> {\n return await collection().findOne(\n { jobName, status: { $in: [\"pending\", \"in_progress\", \"stalled\"] } },\n { sort: { \"timestamps.createdAt\": -1 } },\n );\n}\n\nexport async function findById(runId: string): Promise<StatefulEtlJob | null> {\n return await collection().findOne({ _id: runId });\n}\n\nexport async function createRun(\n jobName: string,\n params: unknown,\n jobId: string,\n stallTimeoutMs: number,\n maxStallRetries: number,\n): Promise<string> {\n const now = new Date();\n const id = new ObjectId().toString();\n\n await collection().insertOne({\n _id: id,\n type: \"stateful-etl\",\n jobId,\n jobName,\n status: \"pending\",\n params: params ?? null,\n result: null,\n error: null,\n stallTimeoutMs,\n maxStallRetries,\n progress: {\n extractedCount: 0,\n transformedCount: 0,\n loadedCount: 0,\n checkpoint: null,\n lastHeartbeatAt: null,\n stallRetries: 0,\n },\n timestamps: {\n createdAt: now,\n updatedAt: now,\n startedAt: null,\n completedAt: null,\n },\n });\n\n return id;\n}\n\nexport async function markInProgress(runId: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n status: \"in_progress\",\n \"timestamps.startedAt\": now,\n \"timestamps.updatedAt\": now,\n \"progress.lastHeartbeatAt\": now,\n },\n },\n );\n}\n\nexport async function updateHeartbeat(runId: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n \"progress.lastHeartbeatAt\": now,\n \"timestamps.updatedAt\": now,\n },\n },\n );\n}\n\nexport async function updateCheckpoint(\n runId: string,\n checkpoint: unknown,\n counts: { extractedCount: number; transformedCount: number; loadedCount: number },\n): Promise<void> {\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n \"progress.checkpoint\": checkpoint,\n \"progress.extractedCount\": counts.extractedCount,\n \"progress.transformedCount\": counts.transformedCount,\n \"progress.loadedCount\": counts.loadedCount,\n \"timestamps.updatedAt\": new Date(),\n },\n },\n );\n}\n\nexport async function markCompleted(runId: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n status: \"completed\",\n \"timestamps.completedAt\": now,\n \"timestamps.updatedAt\": now,\n \"progress.lastHeartbeatAt\": null,\n },\n },\n );\n}\n\nexport async function markFailed(runId: string, error: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n status: \"failed\",\n error,\n \"timestamps.updatedAt\": now,\n \"progress.lastHeartbeatAt\": null,\n },\n },\n );\n}\n\nexport async function findStalled(stallTimeoutMs: number): Promise<StatefulEtlJob[]> {\n const threshold = new Date(Date.now() - stallTimeoutMs);\n\n return await collection()\n .find({\n status: \"in_progress\",\n \"progress.lastHeartbeatAt\": { $lt: threshold },\n })\n .toArray();\n}\n\nexport async function findStalledCandidates(): Promise<StatefulEtlJob[]> {\n const now = new Date();\n\n return await collection()\n .find({\n status: \"in_progress\",\n \"progress.lastHeartbeatAt\": { $ne: null },\n $expr: {\n $lt: [\n \"$progress.lastHeartbeatAt\",\n { $subtract: [now, \"$stallTimeoutMs\"] },\n ],\n },\n })\n .toArray();\n}\n\nexport async function claimStalledRun(runId: string): Promise<StatefulEtlJob | null> {\n const result = await collection().findOneAndUpdate(\n {\n _id: runId,\n status: \"in_progress\",\n },\n {\n $set: {\n status: \"stalled\",\n \"progress.lastHeartbeatAt\": null,\n \"timestamps.updatedAt\": new Date(),\n },\n $inc: {\n \"progress.stallRetries\": 1,\n },\n },\n { returnDocument: \"after\" },\n );\n\n return result ?? null;\n}\n\nexport async function markResuming(runId: string): Promise<void> {\n const now = new Date();\n\n await collection().updateOne(\n { _id: runId },\n {\n $set: {\n status: \"in_progress\",\n \"progress.lastHeartbeatAt\": now,\n \"timestamps.updatedAt\": now,\n },\n },\n );\n}\n","import { Collection } from \"mongodb\";\nimport { getDb, collectionName } from \"./connection\";\nimport type { TokenBucketState } from \"@/types\";\n\nconst COLLECTION = \"token_bucket_state\";\n\nfunction collection(): Collection<TokenBucketState> {\n return getDb().collection<TokenBucketState>(collectionName(COLLECTION));\n}\n\nexport async function getOrCreateState(\n jobName: string,\n now: Date,\n): Promise<TokenBucketState> {\n const result = await collection().findOneAndUpdate(\n { _id: jobName },\n {\n $setOnInsert: {\n _id: jobName,\n accumulatedCredit: 0,\n lastTickAt: now,\n },\n },\n { upsert: true, returnDocument: \"after\" },\n );\n\n return result!;\n}\n\nexport async function updateState(\n jobName: string,\n accumulatedCredit: number,\n lastTickAt: Date,\n): Promise<void> {\n await collection().updateOne(\n { _id: jobName },\n {\n $set: {\n accumulatedCredit,\n lastTickAt,\n },\n },\n );\n}\n\nexport async function ensureIndexes(): Promise<void> {\n // _id index is created automatically by MongoDB — nothing extra needed\n}\n","export { ETL_EXTRACT_SUFFIX, ETL_TRANSFORM_SUFFIX, ETL_LOAD_SUFFIX } from \"../constants\";\n\nexport const DEFAULT_STALL_TIMEOUT_MS = 300_000;\nexport const HEARTBEAT_INTERVAL_MS = 30_000;\nexport const DEFAULT_MAX_STALL_RETRIES = 3;\nexport const DEFAULT_WATCHER_INTERVAL_MS = 60_000;\n","import { getLogger } from \"@/logger\";\nimport { runRepo } from \"@/mongo\";\nimport { HEARTBEAT_INTERVAL_MS } from \"./constants\";\n\nexport function startHeartbeat(\n runId: string,\n): ReturnType<typeof setInterval> {\n const logger = getLogger();\n\n const timer = setInterval(async () => {\n try {\n await runRepo.updateHeartbeat(runId);\n } catch (err) {\n logger.warn(\n { runId, error: err },\n \"[stateful-etl] heartbeat update failed\",\n );\n }\n logger.debug({ runId }, \"[stateful-etl] heartbeat\");\n }, HEARTBEAT_INTERVAL_MS);\n\n timer.unref();\n return timer;\n}\n","import { Queue, QueueEvents } from \"bullmq\";\nimport { StatefulEtlJobConfig, ResolvedJobConfig } from \"@/types\";\nimport { runRepo } from \"@/mongo\";\nimport { EtlContext, type ProgressCounts } from \"../etl-context\";\n\nexport { type ProgressCounts };\n\nexport async function paginateAndProcess<TRaw, TProcessed, TCursor>(\n jobName: string,\n runId: string,\n jobConfig: ResolvedJobConfig<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n initialCursor: TCursor | null,\n initialCounts: ProgressCounts,\n): Promise<void> {\n const ctx = new EtlContext<TRaw, TProcessed>(\n jobName,\n jobConfig,\n queues,\n initialCounts,\n );\n\n let cursor = initialCursor;\n let hasMore = true;\n\n while (hasMore) {\n const page = await ctx.dispatchExtract<TCursor>(cursor);\n hasMore = page.hasMore;\n cursor = page.cursor;\n\n if (page.data.length === 0) continue;\n\n const processed = await ctx.transformParallel(page.data);\n await ctx.dispatchLoad(processed);\n\n ctx.counts.extractedCount += page.data.length;\n ctx.counts.transformedCount += processed.length;\n ctx.counts.loadedCount += processed.length;\n\n await runRepo.updateCheckpoint(runId, cursor, ctx.counts);\n ctx.logger.debug({ jobName, ...ctx.counts }, \"[stateful-etl] batch done\");\n }\n}\n","import { Job, Queue, QueueEvents } from \"bullmq\";\nimport { StatefulEtlJobConfig, ResolvedJobConfig } from \"@/types\";\nimport { runRepo } from \"@/mongo\";\nimport { getLogger } from \"@/logger\";\nimport { startHeartbeat } from \"./heartbeat\";\nimport { paginateAndProcess } from \"./paginate\";\nimport type { ProgressCounts } from \"./paginate\";\nimport { DEFAULT_STALL_TIMEOUT_MS, DEFAULT_MAX_STALL_RETRIES } from \"./constants\";\n\ninterface ResumeData {\n __ravenflow_resume: true;\n runId: string;\n checkpoint: unknown;\n counts: ProgressCounts;\n}\n\nfunction isResumeData(data: unknown): data is ResumeData {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"__ravenflow_resume\" in data &&\n (data as ResumeData).__ravenflow_resume === true\n );\n}\n\nexport async function processStatefulEtlJob<TRaw, TProcessed, TCursor>(\n job: Job,\n jobConfig: ResolvedJobConfig<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n): Promise<void> {\n const logger = getLogger();\n const jobName = job.name;\n\n if (isResumeData(job.data)) {\n return resumeRun(job.data, jobName, jobConfig, queues, logger);\n }\n\n return freshRun(job, jobName, jobConfig, queues, logger);\n}\n\nasync function resumeRun<TRaw, TProcessed, TCursor>(\n data: ResumeData,\n jobName: string,\n jobConfig: ResolvedJobConfig<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n logger: ReturnType<typeof getLogger>,\n): Promise<void> {\n const { runId, checkpoint, counts } = data;\n logger.info({ jobName, runId }, \"[stateful-etl] resuming stalled run\");\n\n await runRepo.markResuming(runId);\n const heartbeatTimer = startHeartbeat(runId);\n\n try {\n await paginateAndProcess(\n jobName,\n runId,\n jobConfig,\n queues,\n checkpoint as TCursor | null,\n counts,\n );\n\n await runRepo.markCompleted(runId);\n logger.info({ jobName, runId }, \"[stateful-etl] resumed run completed\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n await runRepo.markFailed(runId, message);\n logger.error({ jobName, runId, error: message }, \"[stateful-etl] resumed run failed\");\n throw error;\n } finally {\n clearInterval(heartbeatTimer);\n }\n}\n\nasync function freshRun<TRaw, TProcessed, TCursor>(\n job: Job,\n jobName: string,\n jobConfig: ResolvedJobConfig<StatefulEtlJobConfig<TRaw, TProcessed, TCursor>>,\n queues: Record<string, { queue: Queue; events: QueueEvents }>,\n logger: ReturnType<typeof getLogger>,\n): Promise<void> {\n const params = job.data ?? null;\n\n if (jobConfig.singletonRun) {\n await ensureNotAlreadyRunning(jobName);\n }\n\n const stallTimeoutMs = jobConfig.stallTimeoutMs ?? DEFAULT_STALL_TIMEOUT_MS;\n const maxStallRetries = jobConfig.maxStallRetries ?? DEFAULT_MAX_STALL_RETRIES;\n const runId = await runRepo.createRun(jobName, params, job.id!, stallTimeoutMs, maxStallRetries);\n await runRepo.markInProgress(runId);\n\n const heartbeatTimer = startHeartbeat(runId);\n\n try {\n await paginateAndProcess(\n jobName,\n runId,\n jobConfig,\n queues,\n null,\n { extractedCount: 0, transformedCount: 0, loadedCount: 0 },\n );\n\n await runRepo.markCompleted(runId);\n logger.info({ jobName }, \"[stateful-etl] run completed\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n await runRepo.markFailed(runId, message);\n logger.error({ jobName, error: message }, \"[stateful-etl] run failed\");\n throw error;\n } finally {\n clearInterval(heartbeatTimer);\n }\n}\n\nasync function ensureNotAlreadyRunning(jobName: string): Promise<void> {\n const activeRun = await runRepo.findActiveRun(jobName);\n if (activeRun) {\n throw new Error(\n `[stateful-etl] job \"${jobName}\" already has an active run (status: ${activeRun.status})`,\n );\n }\n}\n","import type { TokenBucketInterval } from \"@/types\";\nimport { INTERVAL_MS } from \"./constants\";\n\nexport interface ComputeCreditInput {\n now: Date;\n rate?: { amount: number; interval: TokenBucketInterval };\n lastTickAt: Date;\n accumulatedCredit: number;\n maxCarryOver: number;\n budget?: number;\n}\n\nexport interface ComputeCreditResult {\n tokens: number;\n newCredit: number;\n}\n\nexport function computeCredit(input: ComputeCreditInput): ComputeCreditResult {\n const { now, rate, lastTickAt, accumulatedCredit, maxCarryOver, budget } = input;\n\n const elapsedMs = Math.max(0, now.getTime() - lastTickAt.getTime());\n\n const tickCredit = rate\n ? (rate.amount * elapsedMs) / INTERVAL_MS[rate.interval]\n : 0;\n\n const totalCredit = accumulatedCredit + tickCredit;\n\n const cappedCredit = rate\n ? Math.min(totalCredit, tickCredit * (1 + maxCarryOver / 100))\n : totalCredit;\n\n const availableCredit =\n budget !== undefined ? Math.min(cappedCredit, budget) : cappedCredit;\n\n const tokens = Math.floor(availableCredit);\n const newCredit = cappedCredit - tokens;\n\n return { tokens, newCredit };\n}\n","import type { Job } from \"bullmq\";\nimport type { TokenBucketJobConfig, ResolvedJobConfig } from \"@/types\";\nimport type { QueueStore } from \"@/app/queue-manager\";\nimport { getLogger } from \"@/logger\";\nimport { tokenBucketRepo } from \"@/mongo\";\nimport { computeCredit } from \"./compute-credit\";\nimport { TOKEN_BUCKET_CONSUME_SUFFIX } from \"./constants\";\n\nexport { TOKEN_BUCKET_CONSUME_SUFFIX } from \"./constants\";\n\nexport async function processTokenBucketJob(\n job: Job,\n config: ResolvedJobConfig<TokenBucketJobConfig>,\n queues: QueueStore,\n): Promise<void> {\n const logger = getLogger();\n const tickStartedAt = new Date();\n\n const state = await tokenBucketRepo.getOrCreateState(job.name, tickStartedAt);\n\n const budget = config.getBudget ? await config.getBudget() : undefined;\n\n const { tokens, newCredit } = computeCredit({\n now: tickStartedAt,\n rate: config.rate,\n lastTickAt: state.lastTickAt,\n accumulatedCredit: state.accumulatedCredit,\n maxCarryOver: config.maxCarryOver ?? 100,\n budget,\n });\n\n if (tokens === 0) {\n await tokenBucketRepo.updateState(job.name, newCredit, tickStartedAt);\n logger.debug({ jobName: job.name, newCredit }, \"[token-bucket] zero tokens, skipping consume\");\n return;\n }\n\n try {\n const queueData = queues[config.queue];\n const consumeJob = await queueData.queue.add(\n `${job.name}${TOKEN_BUCKET_CONSUME_SUFFIX}`,\n tokens,\n );\n\n // BullMQ's `JobsOptions` has no `timeout` field; the consume-side wait TTL is the\n // second arg of `waitUntilFinished(events, ttl)` per bullmq@5.63.1\n // node_modules/bullmq/dist/esm/classes/job.d.ts:436.\n const result = await consumeJob.waitUntilFinished(queueData.events, config.consumeTimeout);\n\n const consumed = typeof result === \"number\" ? result : tokens;\n const finalCredit = newCredit + (tokens - consumed);\n\n await tokenBucketRepo.updateState(job.name, finalCredit, tickStartedAt);\n logger.info({ jobName: job.name, tokens, consumed, finalCredit }, \"[token-bucket] tick complete\");\n } catch (error) {\n logger.warn({ jobName: job.name, error: String(error) }, \"[token-bucket] consume failed, skipping state update\");\n }\n}\n","import { Job } from \"bullmq\";\nimport { RavenflowConfig, JobConfig, JobNames, JobsProxy } from \"../types\";\nimport {\n processSimpleJob,\n processMemoryEtlJob,\n processDistributedEtlJob,\n processStatefulEtlJob,\n processTokenBucketJob,\n ETL_EXTRACT_SUFFIX,\n ETL_TRANSFORM_SUFFIX,\n ETL_LOAD_SUFFIX,\n TOKEN_BUCKET_CONSUME_SUFFIX,\n} from \"../processors\";\nimport { QueueStore, sendJob, sendJobAndWait } from \"./queue-manager\";\n\nexport function initJobs<TConfig extends RavenflowConfig>(\n config: TConfig,\n queues: QueueStore\n): {\n jobsProxy: JobsProxy<TConfig[\"jobs\"]>;\n jobConfigs: Record<JobNames<TConfig>, JobConfig>;\n} {\n // Generic-erasure proxy: per-job TData/TReturn get resolved via `JobsProxy<TConfig[\"jobs\"]>`\n // at the public API; here we hold the runtime dispatcher as loose `any` and cast once below.\n // Typed config-walker deferred per Story 1.8 AC #15.\n /* eslint-disable @typescript-eslint/no-explicit-any */\n const jobsProxy: Record<string, { send: (params: any) => any; sendAndWait: (params: any) => any }> = {};\n const jobConfigs: Record<string, JobConfig> = {};\n\n for (const [jobName, jobConfig] of Object.entries(config.jobs)) {\n jobConfigs[jobName] = jobConfig;\n\n const resolvedQueue = jobConfig.queue ?? config.defaultQueue;\n\n jobsProxy[jobName] = {\n send: async (params: any) => {\n return sendJob(jobName, params, queues, resolvedQueue);\n },\n sendAndWait: async (params: any) => {\n return sendJobAndWait(jobName, params, queues, resolvedQueue);\n },\n };\n }\n /* eslint-enable @typescript-eslint/no-explicit-any */\n\n return {\n jobsProxy: jobsProxy as JobsProxy<TConfig[\"jobs\"]>,\n jobConfigs: jobConfigs as Record<JobNames<TConfig>, JobConfig>,\n };\n}\n\nconst ALL_SUFFIXES = [\n ETL_EXTRACT_SUFFIX,\n ETL_TRANSFORM_SUFFIX,\n ETL_LOAD_SUFFIX,\n TOKEN_BUCKET_CONSUME_SUFFIX,\n] as const;\n\nexport function resolveJobName(\n name: string,\n jobConfigs: Record<string, JobConfig>\n): { baseName: string; suffix: string | null } {\n for (const suffix of ALL_SUFFIXES) {\n if (!name.endsWith(suffix)) continue;\n\n const baseName = name.slice(0, -suffix.length);\n if (jobConfigs[baseName]) {\n return { baseName, suffix };\n }\n }\n return { baseName: name, suffix: null };\n}\n\nexport async function processJob<TConfig extends RavenflowConfig>(\n job: Job,\n jobConfigs: Record<JobNames<TConfig>, JobConfig>,\n queues: QueueStore,\n defaultQueue: string,\n) {\n const { baseName, suffix } = resolveJobName(job.name, jobConfigs as Record<string, JobConfig>);\n const jobConfig = getJobConfig(baseName, jobConfigs);\n const resolvedQueue = jobConfig.queue ?? defaultQueue;\n\n // -- Sub-job routing --\n if (suffix !== null) {\n if (jobConfig.type === \"distributed-etl\") {\n if (suffix === ETL_EXTRACT_SUFFIX) {\n return await jobConfig.extract(job.data.cursor);\n }\n if (suffix === ETL_TRANSFORM_SUFFIX) {\n return await jobConfig.transform(job.data);\n }\n if (suffix === ETL_LOAD_SUFFIX) {\n return await jobConfig.load(job.data.data);\n }\n }\n\n if (jobConfig.type === \"stateful-etl\") {\n if (suffix === ETL_EXTRACT_SUFFIX) {\n return await jobConfig.extract(job.data.cursor);\n }\n if (suffix === ETL_TRANSFORM_SUFFIX) {\n return await jobConfig.transform(job.data);\n }\n if (suffix === ETL_LOAD_SUFFIX) {\n return await jobConfig.load(job.data.data);\n }\n }\n\n if (jobConfig.type === \"token-bucket\") {\n if (suffix === TOKEN_BUCKET_CONSUME_SUFFIX) {\n return await jobConfig.consume(job.data);\n }\n }\n\n throw new Error(`Unexpected sub-job suffix \"${suffix}\" for ${baseName} (type: ${jobConfig.type})`);\n }\n\n // -- Top-level job routing --\n if (jobConfig.type === \"simple\") {\n return await processSimpleJob(job, jobConfig);\n }\n\n if (jobConfig.type === \"memory-etl\") {\n return await processMemoryEtlJob(job, jobConfig);\n }\n\n if (jobConfig.type === \"distributed-etl\") {\n return await processDistributedEtlJob(job, { ...jobConfig, queue: resolvedQueue }, queues);\n }\n\n if (jobConfig.type === \"stateful-etl\") {\n return await processStatefulEtlJob(job, { ...jobConfig, queue: resolvedQueue }, queues);\n }\n\n if (jobConfig.type === \"token-bucket\") {\n return await processTokenBucketJob(job, { ...jobConfig, queue: resolvedQueue }, queues);\n }\n\n throw new Error(`Unknown job type for: ${job.name}`);\n}\n\nfunction getJobConfig<TConfig extends RavenflowConfig>(\n jobName: string,\n jobConfigs: Record<JobNames<TConfig>, JobConfig>\n): JobConfig {\n const config = jobConfigs[jobName as JobNames<TConfig>];\n if (!config) throw new Error(`Job handler not found: ${jobName}`);\n return config;\n}\n","import { JobConfig } from \"./types\";\n\nconst RAVENFLOW_ASCII = `\n ██████╗ █████╗ ██╗ ██╗███████╗███╗ ██╗███████╗██╗ ██████╗ ██╗ ██╗\n ██╔══██╗██╔══██╗██║ ██║██╔════╝████╗ ██║██╔════╝██║ ██╔═══██╗██║ ██║\n ██████╔╝███████║██║ ██║█████╗ ██╔██╗ ██║█████╗ ██║ ██║ ██║██║ █╗ ██║\n ██╔══██╗██╔══██║╚██╗ ██╔╝██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║ ██║██║███╗██║\n ██║ ██║██║ ██║ ╚████╔╝ ███████╗██║ ╚████║██║ ███████╗╚██████╔╝╚███╔███╔╝\n ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝\n`;\n\nconst RAVENFLOW_ASCII_ART = ` :==++==-:::..\n :--:::.*%%%%###+====--:::::.\n ===--:+@@%%%##%##*==-=%%%###*##:\n :+===-*@@%%%######*#*#%@@%\n =++==*@@%%%#########@@@@%.\n -*+==@@@%%%######*%@@@@%*\n **+#@@%%%#######%@@@@%%*=\n =**@@%%%%#######@@@@%%%**\n **@@%%%%#%####@@@@@%%#**+\n +#@%%%%%%#%##%@@@@@%%#***-\n %%%%%%#%#%##@@@@@%%%*+***\n .@%%%%%#%%%#%@@@@%%%%+=++*:\n =%%%%%#%%%##@@@@@%%%%===++=\n #%%%%%%%%##@@@@@%%%%*-=====\n =%%%%%%%%%#@@@@@%%%%%::-===\n =%%%#%%%#%@@@@@@@%%%=.:::-:\n +******#%%%%%%%#*=: . .\n`;\n\ninterface BannerConfig {\n queueName: string;\n concurrency: number;\n redis: {\n host: string;\n port: number;\n db: number;\n };\n mongo?: string | null;\n jobs: Record<string, JobConfig>;\n}\n\nfunction censorUri(uri: string): string {\n try {\n const url = new URL(uri);\n if (url.password) url.password = \"***\";\n return url.toString();\n } catch {\n return uri;\n }\n}\n\nexport function printWorkerBanner(config: BannerConfig): void {\n const { queueName, concurrency, redis, mongo, jobs } = config;\n\n console.log(\"\\n\\n\" + RAVENFLOW_ASCII_ART);\n console.log(\"\\n\" + RAVENFLOW_ASCII);\n console.log(\"═\".repeat(85));\n\n console.log(\"\\n[Config]\");\n console.log(` -> queue: ${queueName}`);\n console.log(` -> concurrency: ${concurrency}`);\n console.log(` -> redis: ${redis.host}:${redis.port}/${redis.db}`);\n if (mongo) {\n console.log(` -> mongo: ${censorUri(mongo)}`);\n }\n\n console.log(\"\\n[Registered Jobs]\");\n\n const regularJobs: string[] = [];\n const cronJobs: { name: string; cron: string }[] = [];\n\n for (const [jobName, jobConfig] of Object.entries(jobs)) {\n if (jobConfig.cron) {\n cronJobs.push({ name: jobName, cron: jobConfig.cron });\n } else {\n regularJobs.push(jobName);\n }\n }\n\n if (regularJobs.length > 0) {\n regularJobs.forEach((jobName) => {\n console.log(` . ${jobName}`);\n });\n }\n\n if (cronJobs.length > 0) {\n cronJobs.forEach(({ name, cron }) => {\n console.log(` . ${name} (↻ ${cron})`);\n });\n }\n\n console.log(\"\\n\" + \"═\".repeat(85));\n console.log(\"Ready to process jobs...\\n\");\n}\n","import {\n ConnectionOptions,\n Worker,\n Job,\n type WorkerOptions,\n} from \"bullmq\";\nimport { getLogger } from \"../logger\";\nimport { printWorkerBanner } from \"../banner\";\nimport { JobConfig } from \"../types\";\n\n/**\n * Inlined minute-count constants. Mirrors BullMQ's `MetricsTime` enum\n * (bullmq@5.63.1) but severs the import edge so consumers of this constant\n * don't pull BullMQ into their bundle. Values are pure math (minutes in\n * the named window) and therefore stable across BullMQ versions. Lives\n * here instead of `index.ts` to avoid an import cycle\n * (`index.ts` → `./app/index` → `./app/app` → `worker-manager`).\n */\nexport const METRICS_TIME = {\n ONE_MINUTE: 1,\n FIVE_MINUTES: 5,\n FIFTEEN_MINUTES: 15,\n THIRTY_MINUTES: 30,\n ONE_HOUR: 60,\n ONE_DAY: 1_440,\n ONE_WEEK: 10_080,\n TWO_WEEKS: 20_160,\n ONE_MONTH: 80_640,\n} as const;\n\n/**\n * Default BullMQ metrics configuration applied to every ravenflow-managed\n * Worker. Enables the per-minute data-point collection read by the\n * ravenflow-dashboard Jobs-per-Second indicator (Epic 4 Story 4.2).\n *\n * Without this default, BullMQ's `collectMetrics` Lua function is a no-op on\n * every `moveToFinished` — `queue.getMetrics('completed')` would return\n * `{ meta: { count: 0 }, data: [] }` indefinitely for Ravenflow-managed\n * queues (silent false-negative under real throughput).\n *\n * `ONE_WEEK = 10_080` minutes ≈ 7 days of minute-resolution history at a\n * Redis footprint of ~10 KB/queue/week. Callers may override via the\n * `workerOpts.metrics` parameter on `createWorker` (e.g. to disable\n * collection or pick a shorter window).\n *\n * Frozen so the exported reference cannot be mutated by a caller (shared\n * default would otherwise bleed globally across `createWorker` calls).\n */\nexport const DEFAULT_WORKER_METRICS: NonNullable<WorkerOptions[\"metrics\"]> = Object.freeze({\n maxDataPoints: METRICS_TIME.ONE_WEEK,\n});\n\n/**\n * Pass-through WorkerOptions accepted by `createWorker`. Excludes the three\n * fields ravenflow controls in-situ (`connection`, `autorun`, `concurrency`)\n * so callers cannot override them accidentally. Any other BullMQ option\n * (metrics, stalledInterval, lockDuration, ...) is forwarded to `new Worker`.\n */\nexport type RavenflowWorkerOpts = Omit<\n WorkerOptions,\n \"connection\" | \"autorun\" | \"concurrency\"\n>;\n\nexport function createWorker(\n queueName: string,\n connection: ConnectionOptions,\n concurrency: number,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- processJob return width is resolved per job config; typed config-walker deferred per Story 1.8 AC #15\n processJob: (job: Job, token?: string) => Promise<any>,\n workerOpts?: RavenflowWorkerOpts,\n): Worker {\n const worker = new Worker(queueName, processJob, {\n ...workerOpts,\n connection,\n autorun: false,\n concurrency,\n metrics: workerOpts?.metrics ?? DEFAULT_WORKER_METRICS,\n });\n return worker;\n}\n\nexport function setupWorkerEvents(\n worker: Worker,\n queueName: string,\n): void {\n const logger = getLogger();\n worker.on(\"active\", (job) => {\n logger.info(`[${queueName}] ${job.name} - active`);\n });\n\n worker.on(\"completed\", (job) => {\n logger.info(`[${queueName}] ${job.name} - completed`);\n });\n\n worker.on(\"failed\", (job, error) => {\n logger.error(`[${queueName}] ${job?.name} - failed: ${error.message}`);\n });\n}\n\nexport function printBanner(\n queueName: string,\n concurrency: number,\n connection: ConnectionOptions,\n jobConfigs: Record<string, JobConfig>,\n mongoUri?: string | null,\n): void {\n const redisInfo = {\n host:\n typeof connection === \"object\" && \"host\" in connection\n ? (connection.host as string)\n : \"localhost\",\n port:\n typeof connection === \"object\" && \"port\" in connection\n ? (connection.port as number)\n : 6379,\n db:\n typeof connection === \"object\" && \"db\" in connection\n ? (connection.db as number)\n : 0,\n };\n\n printWorkerBanner({\n queueName,\n concurrency,\n redis: redisInfo,\n mongo: mongoUri,\n jobs: jobConfigs,\n });\n}\n\nexport async function closeWorkers(workers: Worker[]): Promise<void> {\n await Promise.all(workers.map((w) => w.close()));\n}\n","import { Queue } from \"bullmq\";\nimport { JobConfig } from \"../types\";\nimport { getLogger } from \"../logger\";\n\nexport async function syncJobSchedulers(\n queue: Queue,\n jobConfigs: Record<string, JobConfig>,\n): Promise<void> {\n const logger = getLogger();\n logger.info(\"[Scheduled Jobs]\");\n\n const jobConfigsWithCron: Record<string, JobConfig> = {};\n for (const jobName in jobConfigs) {\n if (jobConfigs[jobName].cron) {\n jobConfigsWithCron[jobName] = jobConfigs[jobName];\n }\n }\n\n const registeredJobSchedulers = await queue.getJobSchedulers();\n const existingSchedulerKeys = new Set(registeredJobSchedulers.map((s) => s.key));\n\n const removed: string[] = [];\n for (const jobScheduler of registeredJobSchedulers) {\n if (!jobConfigsWithCron[jobScheduler.key]) {\n removed.push(jobScheduler.key);\n await queue.removeJobScheduler(jobScheduler.key);\n }\n }\n\n const newJobs: string[] = [];\n const updated: string[] = [];\n\n for (const jobName in jobConfigsWithCron) {\n const jobConfig = jobConfigsWithCron[jobName];\n if (!jobConfig?.cron) continue;\n\n if (existingSchedulerKeys.has(jobName)) {\n updated.push(jobName);\n } else {\n newJobs.push(jobName);\n }\n\n await queue.upsertJobScheduler(jobName, { pattern: jobConfig.cron });\n }\n\n for (const name of newJobs) {\n logger.info(` + ${name} → ${jobConfigsWithCron[name].cron}`);\n }\n for (const name of updated) {\n logger.info(` ↻ ${name} → ${jobConfigsWithCron[name].cron}`);\n }\n for (const name of removed) {\n logger.info(` - ${name}`);\n }\n if (newJobs.length === 0 && updated.length === 0 && removed.length === 0) {\n logger.info(\" (no scheduled jobs)\");\n }\n}\n","import type { StatefulEtlJobConfig } from \"../../types\";\n\nexport const STALL_CHECK = \"__stall_check\";\nexport const STALL_RESUME = \"__stall_resume\";\n\nexport interface StatefulEtlEntry {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic erasure at runtime; typed config-walker deferred per Story 1.8 AC #15\n config: StatefulEtlJobConfig<any, any, any>;\n queue: string;\n}\n\nexport interface StallResumePayload {\n runId: string;\n jobName: string;\n queueName: string;\n maxStallRetries: number;\n stallRetries: number;\n checkpoint: unknown;\n extractedCount: number;\n transformedCount: number;\n loadedCount: number;\n}\n","import { Queue } from \"bullmq\";\nimport { getLogger } from \"../../logger\";\nimport { runRepo } from \"../../mongo\";\nimport type { StatefulEtlEntry, StallResumePayload } from \"./types\";\nimport { STALL_RESUME } from \"./types\";\n\n/**\n * Step 1 — Scan MongoDB for stalled runs, verify against Redis,\n * atomically claim them, and dispatch resume sub-jobs.\n */\nexport async function checkStalledRuns(\n statefulEtlConfigs: Map<string, StatefulEtlEntry>,\n userQueues: Record<string, { queue: Queue }>,\n workerQueue: Queue,\n): Promise<void> {\n const logger = getLogger();\n const candidates = await runRepo.findStalledCandidates();\n\n for (const candidate of candidates) {\n const entry = statefulEtlConfigs.get(candidate.jobName);\n if (!entry) {\n logger.warn(\n { runId: candidate._id, jobName: candidate.jobName },\n \"[stall-watcher] unknown job, marking failed\",\n );\n await runRepo.markFailed(\n candidate._id,\n \"Job config not found during stall recovery\",\n );\n continue;\n }\n\n const queueData = userQueues[entry.queue];\n if (!queueData) {\n logger.error(\n { queueName: entry.queue, runId: candidate._id },\n \"[stall-watcher] queue not found\",\n );\n await runRepo.markFailed(\n candidate._id,\n `Queue \"${entry.queue}\" not found during stall recovery`,\n );\n continue;\n }\n\n if (await isJobActiveInRedis(queueData.queue, candidate.jobId)) {\n logger.debug(\n { runId: candidate._id, jobId: candidate.jobId },\n \"[stall-watcher] job still active in Redis, skipping\",\n );\n continue;\n }\n\n const claimed = await runRepo.claimStalledRun(candidate._id);\n if (!claimed) continue;\n\n await workerQueue.add(STALL_RESUME, {\n runId: claimed._id,\n jobName: claimed.jobName,\n queueName: entry.queue,\n maxStallRetries: claimed.maxStallRetries,\n stallRetries: claimed.progress.stallRetries,\n checkpoint: claimed.progress.checkpoint,\n extractedCount: claimed.progress.extractedCount,\n transformedCount: claimed.progress.transformedCount,\n loadedCount: claimed.progress.loadedCount,\n } satisfies StallResumePayload);\n }\n}\n\nasync function isJobActiveInRedis(\n queue: Queue,\n jobId: string,\n): Promise<boolean> {\n try {\n const state = await queue.getJobState(jobId);\n return state === \"active\";\n } catch {\n return false;\n }\n}\n","import { Queue } from \"bullmq\";\nimport { getLogger } from \"../../logger\";\nimport { runRepo } from \"../../mongo\";\nimport type { StallResumePayload } from \"./types\";\n\n/**\n * Step 2 — Handle a single stalled run: re-enqueue on the user's\n * queue with resume data, or mark failed if retries exhausted.\n */\nexport async function resumeStalledRun(\n data: StallResumePayload,\n userQueues: Record<string, { queue: Queue }>,\n): Promise<void> {\n const logger = getLogger();\n\n if (data.stallRetries > data.maxStallRetries) {\n logger.warn(\n {\n runId: data.runId,\n jobName: data.jobName,\n stallRetries: data.stallRetries,\n },\n \"[stall-watcher] max stall retries exceeded, marking failed\",\n );\n await runRepo.markFailed(\n data.runId,\n `Stall recovery exhausted after ${data.stallRetries} retries`,\n );\n return;\n }\n\n const queueData = userQueues[data.queueName];\n if (!queueData) {\n logger.error(\n { queueName: data.queueName, runId: data.runId },\n \"[stall-watcher] queue not found for resume\",\n );\n await runRepo.markFailed(\n data.runId,\n `Queue \"${data.queueName}\" not found`,\n );\n return;\n }\n\n logger.info(\n {\n runId: data.runId,\n jobName: data.jobName,\n stallRetries: data.stallRetries,\n checkpoint: data.checkpoint,\n },\n \"[stall-watcher] re-enqueuing stalled run\",\n );\n\n await queueData.queue.add(data.jobName, {\n __ravenflow_resume: true,\n runId: data.runId,\n checkpoint: data.checkpoint,\n counts: {\n extractedCount: data.extractedCount,\n transformedCount: data.transformedCount,\n loadedCount: data.loadedCount,\n },\n });\n}\n","import { Job, Queue } from \"bullmq\";\nimport { getLogger } from \"../../logger\";\nimport type { JobConfig, StatefulEtlJobConfig } from \"../../types\";\nimport { STALL_CHECK, STALL_RESUME } from \"./types\";\nimport type { StatefulEtlEntry, StallResumePayload } from \"./types\";\nimport { checkStalledRuns } from \"./check-stalled-runs\";\nimport { resumeStalledRun } from \"./resume-stalled-run\";\n\nexport class StallWatcher {\n private readonly workerQueue: Queue;\n private readonly statefulEtlConfigs: Map<string, StatefulEtlEntry>;\n private readonly userQueues: Record<string, { queue: Queue }>;\n\n constructor(\n jobConfigs: Record<string, JobConfig>,\n userQueues: Record<string, { queue: Queue }>,\n defaultQueue: string,\n workerQueue: Queue,\n ) {\n this.userQueues = userQueues;\n this.workerQueue = workerQueue;\n\n this.statefulEtlConfigs = new Map();\n for (const [jobName, config] of Object.entries(jobConfigs)) {\n if (config.type === \"stateful-etl\") {\n this.statefulEtlConfigs.set(jobName, {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic erasure at runtime; typed config-walker deferred per Story 1.8 AC #15\n config: config as StatefulEtlJobConfig<any, any, any>,\n queue: config.queue ?? defaultQueue,\n });\n }\n }\n }\n\n canProcess(jobName: string): boolean {\n return jobName === STALL_CHECK || jobName === STALL_RESUME;\n }\n\n async start(): Promise<void> {\n if (this.statefulEtlConfigs.size === 0) return;\n\n const logger = getLogger();\n logger.info(\n { jobs: [...this.statefulEtlConfigs.keys()] },\n \"[stall-watcher] starting\",\n );\n\n await this.workerQueue.upsertJobScheduler(STALL_CHECK, {\n pattern: \"* * * * *\",\n });\n }\n\n async stop(): Promise<void> {\n await this.workerQueue.removeJobScheduler(STALL_CHECK);\n }\n\n async process(job: Job): Promise<void> {\n switch (job.name) {\n case STALL_CHECK:\n return checkStalledRuns(\n this.statefulEtlConfigs,\n this.userQueues,\n this.workerQueue,\n );\n case STALL_RESUME:\n return resumeStalledRun(\n job.data as StallResumePayload,\n this.userQueues,\n );\n }\n }\n}\n","import type { Collection, Db, Document } from \"mongodb\";\nimport { collectionName } from \"../connection\";\nimport type { IndexDescriptor } from \"./types\";\n\nexport abstract class Repository<T extends Document> {\n public readonly collection: Collection<T>;\n\n constructor(db: Db, rawCollectionName: string) {\n this.collection = db.collection<T>(collectionName(rawCollectionName));\n }\n\n protected abstract indexes(): IndexDescriptor[];\n\n async init(): Promise<void> {\n for (const idx of this.indexes()) {\n await this.collection.createIndex(idx.keys, idx.options);\n }\n }\n}\n","import type { Db, Filter } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport type { UpdateSpec } from \"./_types\";\nimport { getLogger } from \"../../logger\";\nimport {\n IndexDescriptor,\n RegisterWorkerInput,\n UpsertWorkerQueueInput,\n WorkerDoc,\n WorkerQueueEntry,\n} from \"./types\";\n\nconst COLLECTION = \"workers\";\n\nexport class WorkersRepo extends Repository<WorkerDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { workerId: 1 },\n options: { unique: true, name: \"workerId_unique\" },\n },\n {\n keys: { lastHeartbeatAt: 1 },\n options: { name: \"lastHeartbeatAt_asc\" },\n },\n ];\n }\n\n async registerWorker(input: RegisterWorkerInput): Promise<void> {\n this.validateRegisterInput(input);\n const filter: Filter<WorkerDoc> = { workerId: input.workerId };\n const update: UpdateSpec<WorkerDoc> = {\n $currentDate: {\n lastHeartbeatAt: true,\n updatedAt: true,\n },\n $setOnInsert: {\n workerId: input.workerId,\n hostname: input.hostname,\n pid: input.pid,\n queues: input.queues,\n startedAt: input.startedAt,\n createdAt: new Date(),\n },\n };\n await this.collection.updateOne(filter, update, { upsert: true });\n }\n\n async updateHeartbeat(workerId: string): Promise<void> {\n if (typeof workerId !== \"string\" || workerId.length === 0) {\n throw new Error(\"WorkersRepo.updateHeartbeat: workerId must be a non-empty string\");\n }\n const filter: Filter<WorkerDoc> = { workerId };\n const update: UpdateSpec<WorkerDoc> = {\n $currentDate: { lastHeartbeatAt: true, updatedAt: true },\n };\n const result = await this.collection.updateOne(filter, update);\n if (result.matchedCount === 0) {\n getLogger().warn(\n { workerId },\n \"[workers-repo] heartbeat on missing worker record — ignoring\",\n );\n }\n }\n\n /**\n * Appends or replaces a queue entry on the worker's record.\n *\n * Single-writer invariant: this method is NOT atomic under concurrent writers to the\n * SAME `workerId`. WorkerRegistry is a process-local singleton, and `workerId` is\n * unique per process by contract (default: `<hostname>:<pid>:<uuid>`). Sharing a\n * stable `workerId` across processes violates AC #6 and will race last-writer-wins.\n */\n async addQueue(input: UpsertWorkerQueueInput): Promise<void> {\n if (typeof input.workerId !== \"string\" || input.workerId.length === 0) {\n throw new Error(\"WorkersRepo.addQueue: workerId must be a non-empty string\");\n }\n this.validateQueueEntry(input.queue, \"WorkersRepo.addQueue\");\n\n const existingFilter: Filter<WorkerDoc> = { workerId: input.workerId };\n const existing = await this.collection.findOne(existingFilter);\n\n if (existing === null) {\n throw new Error(\n `WorkersRepo.addQueue: worker not found for workerId=${input.workerId}`,\n );\n }\n\n const existingQueues = existing.queues ?? [];\n const matchIndex = existingQueues.findIndex((q) => q.name === input.queue.name);\n const newQueues: WorkerQueueEntry[] =\n matchIndex === -1\n ? [...existingQueues, input.queue]\n : existingQueues.map((q, i) => (i === matchIndex ? input.queue : q));\n\n const update: UpdateSpec<WorkerDoc> = {\n $set: { queues: newQueues },\n $currentDate: { updatedAt: true },\n };\n await this.collection.updateOne(existingFilter, update);\n }\n\n async deregisterWorker(workerId: string): Promise<void> {\n if (typeof workerId !== \"string\" || workerId.length === 0) {\n throw new Error(\"WorkersRepo.deregisterWorker: workerId must be a non-empty string\");\n }\n const filter: Filter<WorkerDoc> = { workerId };\n await this.collection.deleteOne(filter);\n }\n\n async findAlive(maxHeartbeatAgeMs: number): Promise<WorkerDoc[]> {\n this.validateMaxHeartbeatAgeMs(maxHeartbeatAgeMs, \"WorkersRepo.findAlive\");\n const threshold = new Date(Date.now() - maxHeartbeatAgeMs);\n const filter: Filter<WorkerDoc> = { lastHeartbeatAt: { $gte: threshold } };\n return await this.collection\n .find(filter)\n .sort({ lastHeartbeatAt: -1 })\n .toArray();\n }\n\n async cleanupStale(maxHeartbeatAgeMs: number): Promise<number> {\n this.validateMaxHeartbeatAgeMs(maxHeartbeatAgeMs, \"WorkersRepo.cleanupStale\");\n const threshold = new Date(Date.now() - maxHeartbeatAgeMs);\n const filter: Filter<WorkerDoc> = { lastHeartbeatAt: { $lt: threshold } };\n const result = await this.collection.deleteMany(filter);\n return result.deletedCount ?? 0;\n }\n\n private validateRegisterInput(input: RegisterWorkerInput): void {\n if (typeof input.workerId !== \"string\" || input.workerId.length === 0) {\n throw new Error(\"WorkersRepo.registerWorker: workerId must be a non-empty string\");\n }\n if (typeof input.hostname !== \"string\" || input.hostname.length === 0) {\n throw new Error(\"WorkersRepo.registerWorker: hostname must be a non-empty string\");\n }\n if (!Number.isInteger(input.pid) || input.pid < 0) {\n throw new Error(\"WorkersRepo.registerWorker: pid must be a non-negative integer\");\n }\n if (!Array.isArray(input.queues) || input.queues.length === 0) {\n throw new Error(\"WorkersRepo.registerWorker: queues must be a non-empty array\");\n }\n for (const q of input.queues) {\n this.validateQueueEntry(q, \"WorkersRepo.registerWorker\");\n }\n if (!(input.startedAt instanceof Date) || Number.isNaN(input.startedAt.getTime())) {\n throw new Error(\"WorkersRepo.registerWorker: startedAt must be a valid Date\");\n }\n }\n\n private validateQueueEntry(q: WorkerQueueEntry, ctx: string): void {\n if (q === null || typeof q !== \"object\") {\n throw new Error(`${ctx}: queue entry must be an object`);\n }\n if (typeof q.name !== \"string\" || q.name.length === 0) {\n throw new Error(`${ctx}: queue.name must be a non-empty string`);\n }\n if (!Number.isInteger(q.concurrency) || q.concurrency <= 0) {\n throw new Error(`${ctx}: queue.concurrency must be a positive integer`);\n }\n }\n\n private validateMaxHeartbeatAgeMs(value: number, ctx: string): void {\n const MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\n if (!Number.isInteger(value) || value <= 0 || value > MAX_AGE_MS) {\n throw new Error(\n `${ctx}: maxHeartbeatAgeMs must be a positive integer <= ${MAX_AGE_MS}`,\n );\n }\n }\n}\n","import type {\n AnyBulkWriteOperation,\n Document,\n Filter,\n OptionalUnlessRequiredId,\n UpdateFilter,\n} from \"mongodb\";\n\n/**\n * Call-site alias for the driver's `UpdateFilter<T>`. Declaring an update literal\n * with `const update: UpdateSpec<T> = { ... }` widens the literal against the\n * full driver type (not the narrower shape TS infers from the literal), so\n * partial `$setOnInsert` / `$set` documents assign without a post-hoc cast.\n *\n * Type-identical to `UpdateFilter<T>` — readability alias at the declaration\n * site, not a type relaxation.\n */\nexport type UpdateSpec<T extends Document> = UpdateFilter<T>;\n\n/** The shape `insertOne` expects. Re-export for call-site annotations. */\nexport type InsertSpec<T extends Document> = OptionalUnlessRequiredId<T>;\n\nexport interface BulkUpdateOp<T extends Document> {\n updateOne: {\n filter: Filter<T>;\n update: UpdateSpec<T>;\n upsert?: boolean;\n };\n}\n\nexport interface BulkInsertOp<T extends Document> {\n insertOne: {\n document: InsertSpec<T>;\n };\n}\n\n/**\n * Widen a `BulkUpdateOp<T>` to the driver's `AnyBulkWriteOperation<T>` union.\n * Single cast point for the widening — callers use this helper and never\n * write the cast themselves.\n */\nexport function asBulkUpdateOp<T extends Document>(\n op: BulkUpdateOp<T>,\n): AnyBulkWriteOperation<T> {\n return op as AnyBulkWriteOperation<T>;\n}\n\nexport function asBulkInsertOp<T extends Document>(\n op: BulkInsertOp<T>,\n): AnyBulkWriteOperation<T> {\n return op as AnyBulkWriteOperation<T>;\n}\n","import type { BucketGranularity } from \"./types\";\n\nexport function computeBucketStart(\n ts: Date,\n granularity: BucketGranularity,\n): Date {\n const result = new Date(ts);\n if (granularity === \"hour\") {\n result.setUTCMinutes(0, 0, 0);\n } else {\n result.setUTCHours(0, 0, 0, 0);\n }\n return result;\n}\n","export const MONGO_DUPLICATE_KEY_CODE = 11000;\n\nexport function isDuplicateKeyError(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) return false;\n return (err as { code?: unknown }).code === MONGO_DUPLICATE_KEY_CODE;\n}\n","import type { CreateIndexesOptions, IndexSpecification } from \"mongodb\";\n\nexport interface IndexDescriptor {\n keys: IndexSpecification;\n options?: CreateIndexesOptions;\n}\n\nexport type BucketGranularity = \"hour\" | \"day\";\n\nexport interface IncrementStatsInput {\n success: boolean;\n durationMs?: number;\n}\n\nexport interface JobStatsDoc {\n jobType: string;\n bucketStart: Date;\n granularity: BucketGranularity;\n count: number;\n success: number;\n failed: number;\n totalDurationMs: number;\n createdAt: Date;\n updatedAt: Date;\n}\n\n/** NFR3 default: 90-day TTL for `job_stats`. Hardcoded for this story; configurable in a later story. */\nexport const JOB_STATS_TTL_SECONDS = 90 * 24 * 60 * 60;\n\nexport interface JobErrorDoc {\n signature: string;\n jobType: string;\n message: string;\n stack: string;\n count: number;\n firstSeenAt: Date;\n lastSeenAt: Date;\n createdAt: Date;\n updatedAt: Date;\n /** Normalized `Error.cause` chain beyond the top-level error. OMITTED when empty. */\n causeChain?: Array<{ message: string; name: string }>;\n}\n\nexport interface RecordOccurrenceInput {\n jobType: string;\n error: unknown;\n}\n\nexport interface TopErrorsOptions {\n limit: number;\n jobType?: string;\n /**\n * When set, restricts results to errors whose `lastSeenAt >= since`.\n * Dashboard health views pass the range start (e.g. 7 days ago, UTC midnight)\n * so \"top errors\" matches the displayed range; omitting returns all errors\n * back to the collection's 90-day TTL boundary.\n */\n since?: Date;\n}\n\nexport const JOB_ERRORS_MESSAGE_MAX_CHARS = 2000;\nexport const JOB_ERRORS_STACK_MAX_CHARS = 8000;\nexport const JOB_ERRORS_MAX_TOP_LIMIT = 1000;\n\nexport interface JobFailureDoc {\n jobId: string;\n jobType: string;\n input: string;\n errorMessage: string;\n errorStack: string;\n logs: string;\n failedAt: Date;\n createdAt: Date;\n /** Normalized `Error.cause` chain beyond the top-level error. OMITTED when empty. */\n causeChain?: Array<{ message: string; name: string }>;\n}\n\nexport interface RecordFailureInput {\n jobId: string;\n jobType: string;\n input: unknown;\n error: unknown;\n failedAt: Date;\n logs?: string;\n}\n\nexport interface ListFailuresOptions {\n jobType?: string;\n // `null` is accepted alongside `undefined` — callers crossing JSON boundaries routinely\n // serialize unset optional dates as `null`. The repo normalizes both to \"no filter applied\".\n fromDate?: Date | null;\n toDate?: Date | null;\n skip?: number;\n limit?: number;\n}\n\nexport const JOB_FAILURES_INPUT_MAX_CHARS = 100_000;\nexport const JOB_FAILURES_MESSAGE_MAX_CHARS = 2000;\nexport const JOB_FAILURES_STACK_MAX_CHARS = 8000;\nexport const JOB_FAILURES_LOGS_MAX_CHARS = 500_000;\nexport const JOB_FAILURES_MAX_LIST_LIMIT = 1000;\n\n/** NFR3 default: 7-day TTL for `job_failures`. Hardcoded for this story; configurable in a later story. */\nexport const JOB_FAILURES_TTL_SECONDS = 7 * 24 * 60 * 60;\n\nexport interface WorkerQueueEntry {\n name: string;\n concurrency: number;\n}\n\nexport interface WorkerDoc {\n workerId: string;\n hostname: string;\n pid: number;\n queues: WorkerQueueEntry[];\n startedAt: Date;\n lastHeartbeatAt: Date;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface RegisterWorkerInput {\n workerId: string;\n hostname: string;\n pid: number;\n queues: WorkerQueueEntry[];\n startedAt: Date;\n}\n\nexport interface UpsertWorkerQueueInput {\n workerId: string;\n queue: WorkerQueueEntry;\n}\n\nexport const WORKERS_DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;\nexport const WORKERS_MIN_HEARTBEAT_INTERVAL_MS = 1_000;\n\n/**\n * Read shape for the `job_configs` collection — what `JobConfigsRepo.getConfig`\n * returns (with persistence timestamps stripped, or synthesized for absent records).\n *\n * Distinct from `JobConfig` (in `src/types/job/unions.ts`), which is the discriminated\n * union of job-definition types (simple / memory-etl / distributed-etl / etc.) used\n * at app-registration time. `JobConfigEntry` is the runtime persistence shape for\n * dashboard-managed per-job-type settings (enabled flag + free-form metadata).\n */\nexport interface JobConfigEntry {\n jobType: string;\n enabled: boolean;\n metadata: Record<string, unknown>;\n}\n\n/**\n * Persisted shape for the `job_configs` collection — the read shape plus\n * MongoDB-managed timestamps. Returned by `JobConfigsRepo.listAll` (every row\n * there is, by definition, persisted, so the timestamps are always real).\n */\nexport interface JobConfigDoc extends JobConfigEntry {\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport const JOB_CONFIGS_DEFAULT_ENABLED = true;\n","import type { AnyBulkWriteOperation, Db, Filter } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport { asBulkUpdateOp, type UpdateSpec } from \"./_types\";\nimport { computeBucketStart } from \"./bucket-utils\";\nimport { isDuplicateKeyError } from \"../mongo-error\";\nimport { getLogger } from \"../../logger\";\nimport {\n IndexDescriptor,\n JobStatsDoc,\n BucketGranularity,\n IncrementStatsInput,\n JOB_STATS_TTL_SECONDS,\n} from \"./types\";\n\nconst COLLECTION = \"job_stats\";\n\nconst GRANULARITIES: readonly BucketGranularity[] = [\"hour\", \"day\"];\n\nexport const MAX_GET_STATS_RESULTS = 10_000;\n\nexport class JobStatsRepo extends Repository<JobStatsDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { jobType: 1, bucketStart: 1, granularity: 1 },\n options: { unique: true, name: \"jobType_bucketStart_granularity_unique\" },\n },\n // NOTE (TTL migration): MongoDB's `createIndex` is a no-op when an index with the same\n // key spec already exists — even if `expireAfterSeconds` differs. Changing\n // `JOB_STATS_TTL_SECONDS` will NOT propagate to a running deployment; apply a `collMod`\n // (or drop-and-recreate this index) manually at deploy time.\n {\n keys: { bucketStart: 1 },\n options: {\n expireAfterSeconds: JOB_STATS_TTL_SECONDS,\n name: \"bucketStart_ttl\",\n },\n },\n ];\n }\n\n /**\n * Builds the bulk write operations for a stats increment WITHOUT executing them.\n * Returns TWO ops — one for the hour bucket, one for the day bucket — matching\n * `incrementStats`'s direct-call shape bit-for-bit. Intended to be pushed through\n * a `BatchWriter` by the event-hooks layer (Story 1.7). Throws synchronously on\n * invalid `durationMs`; otherwise no side effects.\n *\n * NOTE: `now` is captured at builder invocation time, NOT at BatchWriter flush\n * time — `createdAt`, `updatedAt`, and `bucketStart` all reflect the event's\n * wall-clock, not the flush wall-clock. Aggregations are therefore correctly\n * attributed to the hour/day the event occurred, even if the batch flush is\n * delayed by the buffer timer.\n */\n toIncrementStatsOps(\n jobType: string,\n input: IncrementStatsInput,\n ): AnyBulkWriteOperation<JobStatsDoc>[] {\n this.validateDurationMs(input.durationMs, \"toIncrementStatsOps\");\n const now = new Date();\n return GRANULARITIES.map((g) => this.buildBucketOp(jobType, g, now, input));\n }\n\n async incrementStats(\n jobType: string,\n input: IncrementStatsInput,\n ): Promise<void> {\n this.validateDurationMs(input.durationMs, \"incrementStats\");\n\n const now = new Date();\n const results = await Promise.allSettled(\n GRANULARITIES.map((g) => this.upsertBucket(jobType, g, now, input)),\n );\n\n const failures = results\n .map((r, i) => ({ result: r, granularity: GRANULARITIES[i] }))\n .filter((x): x is { result: PromiseRejectedResult; granularity: BucketGranularity } =>\n x.result.status === \"rejected\",\n );\n\n if (failures.length === 0) return;\n\n const logger = getLogger();\n for (const { result, granularity } of failures) {\n logger.error(\n { jobType, granularity, err: result.reason },\n \"JobStatsRepo.incrementStats: bucket upsert failed\",\n );\n }\n\n if (failures.length === results.length) {\n throw failures[0].result.reason;\n }\n }\n\n async getStats(\n jobType: string,\n fromDate: Date,\n toDate: Date,\n granularity: BucketGranularity,\n ): Promise<JobStatsDoc[]> {\n if (fromDate > toDate) {\n throw new Error(\n `JobStatsRepo.getStats: fromDate (${fromDate.toISOString()}) must be <= toDate (${toDate.toISOString()})`,\n );\n }\n\n const filter: Filter<JobStatsDoc> = {\n jobType,\n granularity,\n bucketStart: { $gte: fromDate, $lte: toDate },\n };\n\n return await this.collection\n .find(filter)\n .sort({ bucketStart: 1 })\n .limit(MAX_GET_STATS_RESULTS)\n .toArray();\n }\n\n /**\n * Fetches every stats document in the given range + granularity, across ALL\n * jobTypes. Intended for dashboard views that need a cross-jobType overview\n * (e.g., \"7-day health per job type\") without a prior enumeration step.\n *\n * Result order: `bucketStart` ascending, then `jobType` ascending. Caller is\n * responsible for grouping by jobType client-side if desired.\n *\n * Applies the same `MAX_GET_STATS_RESULTS` cap as `getStats` — a single query\n * across all jobTypes × buckets × granularity is bounded, but the cap keeps\n * the method safe under accidental wide-range queries.\n */\n async getStatsAllJobTypes(\n fromDate: Date,\n toDate: Date,\n granularity: BucketGranularity,\n ): Promise<JobStatsDoc[]> {\n if (fromDate > toDate) {\n throw new Error(\n `JobStatsRepo.getStatsAllJobTypes: fromDate (${fromDate.toISOString()}) must be <= toDate (${toDate.toISOString()})`,\n );\n }\n\n const filter: Filter<JobStatsDoc> = {\n granularity,\n bucketStart: { $gte: fromDate, $lte: toDate },\n };\n\n return await this.collection\n .find(filter)\n .sort({ bucketStart: 1, jobType: 1 })\n .limit(MAX_GET_STATS_RESULTS)\n .toArray();\n }\n\n private async upsertBucket(\n jobType: string,\n granularity: BucketGranularity,\n now: Date,\n input: IncrementStatsInput,\n ): Promise<void> {\n const { filter, update } = this.buildBucketPayload(jobType, granularity, now, input);\n\n try {\n await this.collection.updateOne(filter, update, { upsert: true });\n } catch (err) {\n // E11000 can occur on upsert when two writers both miss the doc and race to insert.\n // Retry once: the doc now exists, so the second attempt hits the $inc path.\n if (isDuplicateKeyError(err)) {\n await this.collection.updateOne(filter, update, { upsert: true });\n return;\n }\n throw err;\n }\n }\n\n private buildBucketOp(\n jobType: string,\n granularity: BucketGranularity,\n now: Date,\n input: IncrementStatsInput,\n ): AnyBulkWriteOperation<JobStatsDoc> {\n const { filter, update } = this.buildBucketPayload(jobType, granularity, now, input);\n return asBulkUpdateOp<JobStatsDoc>({\n updateOne: { filter, update, upsert: true },\n });\n }\n\n private buildBucketPayload(\n jobType: string,\n granularity: BucketGranularity,\n now: Date,\n input: IncrementStatsInput,\n ): { filter: Filter<JobStatsDoc>; update: UpdateSpec<JobStatsDoc> } {\n const bucketStart = computeBucketStart(now, granularity);\n\n const filter: Filter<JobStatsDoc> = { jobType, bucketStart, granularity };\n const update: UpdateSpec<JobStatsDoc> = {\n $inc: {\n count: 1,\n success: input.success ? 1 : 0,\n failed: input.success ? 0 : 1,\n totalDurationMs: input.durationMs ?? 0,\n },\n $setOnInsert: {\n jobType,\n bucketStart,\n granularity,\n createdAt: now,\n },\n $set: {\n updatedAt: now,\n },\n };\n\n return { filter, update };\n }\n\n private validateDurationMs(\n durationMs: number | undefined,\n methodName: string,\n ): void {\n if (durationMs === undefined) return;\n if (!Number.isFinite(durationMs) || durationMs < 0) {\n throw new Error(\n `JobStatsRepo.${methodName}: durationMs must be a finite non-negative number (got ${durationMs})`,\n );\n }\n }\n}\n","import { createHash } from \"node:crypto\";\n\nexport const SIGNATURE_LENGTH = 16;\nexport const STACK_TOP_FRAMES = 5;\n\nconst ISO_TIMESTAMP_REGEX = /\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z?/g;\nconst UUID_REGEX = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst HEX_ADDRESS_REGEX = /\\b0x[0-9a-f]+\\b/gi;\nconst LONG_DIGIT_RUN_REGEX = /\\b\\d{5,}\\b/g;\n\nconst STACK_FRAME_PREFIX = \" at \";\nconst STACK_LINE_COL_SUFFIX_REGEX = /:\\d+:\\d+(\\)?)$/;\n\nfunction normalizeMessage(msg: string): string {\n return msg\n .replace(ISO_TIMESTAMP_REGEX, \"<ts>\")\n .replace(UUID_REGEX, \"<uuid>\")\n .replace(HEX_ADDRESS_REGEX, \"<hex>\")\n .replace(LONG_DIGIT_RUN_REGEX, \"<n>\");\n}\n\nfunction normalizeStack(stack: string): string {\n const frames: string[] = [];\n for (const rawLine of stack.split(\"\\n\")) {\n if (!rawLine.startsWith(STACK_FRAME_PREFIX)) continue;\n const slashed = rawLine.replace(/\\\\/g, \"/\");\n const stripped = slashed.replace(STACK_LINE_COL_SUFFIX_REGEX, \"$1\");\n frames.push(stripped);\n if (frames.length === STACK_TOP_FRAMES) break;\n }\n return frames.join(\"\\n\");\n}\n\nexport function errorSignature(err: unknown): string {\n let combined: string;\n if (err instanceof Error) {\n const name = err.name ?? \"Error\";\n const message = normalizeMessage(err.message ?? \"\");\n const stack = err.stack ? normalizeStack(err.stack) : \"\";\n combined = `${name}\\n${message}\\n${stack}`;\n } else {\n combined = normalizeMessage(String(err));\n }\n return createHash(\"sha256\").update(combined).digest(\"hex\").slice(0, SIGNATURE_LENGTH);\n}\n","import { getLogger } from \"../logger\";\n\nexport interface CauseChainEntry {\n message: string;\n name: string;\n}\n\nexport interface ExtractedError {\n /** Top-level message + \"\\nCaused by: \" + inner.message chain, truncated after joining. */\n message: string;\n /** Top-level stack trace only, truncated. Inner stacks are omitted to bound payload. */\n stack: string;\n /** Normalized chain beyond the top level. Empty array when the top Error has no `cause`. */\n causeChain: CauseChainEntry[];\n}\n\nexport interface ExtractMessageAndStackOptions {\n messageMaxChars: number;\n stackMaxChars: number;\n /** Depth cap to guard against cycles and pathological nesting. Default 5. */\n maxCauseDepth?: number;\n}\n\nconst DEFAULT_MAX_CAUSE_DEPTH = 5;\nconst NAME_MAX_CHARS = 64;\nconst CHAIN_SEPARATOR = \"\\nCaused by: \";\n\nfunction truncate(value: string, max: number): string {\n return value.length <= max ? value : value.slice(0, max);\n}\n\nfunction safeGet<T>(getter: () => T, fallback: T): T {\n try {\n return getter();\n } catch {\n return fallback;\n }\n}\n\nfunction isErrorLike(value: unknown): value is { message: unknown; name?: unknown } {\n if (typeof value !== \"object\" || value === null) return false;\n return safeGet(() => \"message\" in value, false);\n}\n\nfunction normalizeEntry(value: unknown, messageMaxChars: number): CauseChainEntry {\n const entryMax = Math.max(1, Math.floor(messageMaxChars / 2));\n if (isErrorLike(value)) {\n const rawMessage = safeGet<unknown>(() => (value as { message: unknown }).message, undefined);\n const rawName = safeGet<unknown>(() => (value as { name?: unknown }).name, undefined);\n const message = truncate(\n typeof rawMessage === \"string\" ? rawMessage : safeGet(() => String(rawMessage), \"\"),\n entryMax,\n );\n const name = truncate(typeof rawName === \"string\" ? rawName : \"Error\", NAME_MAX_CHARS);\n return { message, name };\n }\n return {\n message: truncate(safeGet(() => String(value), \"\"), entryMax),\n name: \"NonError\",\n };\n}\n\nfunction walkCauseChain(\n top: { cause?: unknown },\n maxDepth: number,\n messageMaxChars: number,\n): { chain: CauseChainEntry[]; truncated: boolean } {\n const chain: CauseChainEntry[] = [];\n const seen = new WeakSet<object>();\n if (typeof top === \"object\" && top !== null) seen.add(top);\n\n let current: unknown = safeGet<unknown>(() => top.cause, undefined);\n let truncated = false;\n\n while (current !== undefined && current !== null) {\n if (chain.length >= maxDepth) {\n truncated = true;\n break;\n }\n if (typeof current === \"object\" && current !== null) {\n if (seen.has(current)) {\n truncated = true;\n break;\n }\n seen.add(current);\n }\n\n chain.push(normalizeEntry(current, messageMaxChars));\n\n const hasCause =\n typeof current === \"object\" &&\n current !== null &&\n safeGet(() => \"cause\" in (current as object), false);\n if (hasCause) {\n current = safeGet(() => (current as { cause?: unknown }).cause, undefined);\n } else {\n break;\n }\n }\n\n return { chain, truncated };\n}\n\nexport function extractMessageAndStack(\n err: unknown,\n options: ExtractMessageAndStackOptions,\n): ExtractedError {\n const { messageMaxChars, stackMaxChars } = options;\n const rawMaxDepth = options.maxCauseDepth ?? DEFAULT_MAX_CAUSE_DEPTH;\n const maxCauseDepth = Number.isFinite(rawMaxDepth) && rawMaxDepth >= 1\n ? Math.floor(rawMaxDepth)\n : DEFAULT_MAX_CAUSE_DEPTH;\n\n if (!(err instanceof Error)) {\n return {\n message: truncate(safeGet(() => String(err), \"\"), messageMaxChars),\n stack: \"\",\n causeChain: [],\n };\n }\n\n const topMessage = safeGet<string>(() => {\n const m = err.message;\n return typeof m === \"string\" ? m : String(m ?? \"\");\n }, \"\");\n const topStack = safeGet<string>(() => {\n const s = err.stack;\n return typeof s === \"string\" ? s : String(s ?? \"\");\n }, \"\");\n\n const { chain, truncated } = walkCauseChain(\n err as { cause?: unknown },\n maxCauseDepth,\n messageMaxChars,\n );\n\n if (truncated) {\n getLogger().warn(\n { topMessage: truncate(topMessage, 120) },\n \"extractMessageAndStack: maxCauseDepth reached, truncating cause chain.\",\n );\n }\n\n let joinedMessage = topMessage;\n for (const entry of chain) {\n joinedMessage += `${CHAIN_SEPARATOR}${entry.message}`;\n }\n\n return {\n message: truncate(joinedMessage, messageMaxChars),\n stack: truncate(topStack, stackMaxChars),\n causeChain: chain,\n };\n}\n","import type { AnyBulkWriteOperation, Db, Filter } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport { asBulkUpdateOp, type UpdateSpec } from \"./_types\";\nimport { errorSignature } from \"../../monitoring/error-signature\";\nimport { extractMessageAndStack } from \"../../monitoring/error-extraction\";\nimport { isDuplicateKeyError } from \"../mongo-error\";\nimport {\n IndexDescriptor,\n JobErrorDoc,\n RecordOccurrenceInput,\n TopErrorsOptions,\n JOB_ERRORS_MESSAGE_MAX_CHARS,\n JOB_ERRORS_STACK_MAX_CHARS,\n JOB_ERRORS_MAX_TOP_LIMIT,\n} from \"./types\";\n\nconst COLLECTION = \"job_errors\";\n\nexport class JobErrorsRepo extends Repository<JobErrorDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { signature: 1 },\n options: { unique: true, name: \"signature_unique\" },\n },\n {\n keys: { lastSeenAt: -1 },\n options: { name: \"lastSeenAt_desc\" },\n },\n {\n keys: { jobType: 1, count: -1 },\n options: { name: \"jobType_count_desc\" },\n },\n {\n keys: { count: -1 },\n options: { name: \"count_desc\" },\n },\n ];\n }\n\n /**\n * Records one occurrence of an error, deduplicated by its stable signature.\n *\n * Invariant: `jobType` is set **only on insert** (`$setOnInsert`). Once a signature\n * is first observed under jobType A, subsequent occurrences under jobType B do NOT\n * overwrite the stored value. The signature is the identity of the error; a signature\n * appearing across multiple job types indicates shared infrastructure failure (e.g.\n * ECONNRESET from the shared DB) and is worth surfacing on the first-observed job.\n */\n async recordOccurrence(input: RecordOccurrenceInput): Promise<void> {\n const { filter, update } = this.buildRecordOccurrencePayload(input);\n\n try {\n await this.collection.updateOne(filter, update, { upsert: true });\n } catch (err) {\n if (isDuplicateKeyError(err)) {\n await this.collection.updateOne(filter, update, { upsert: true });\n return;\n }\n throw err;\n }\n }\n\n /**\n * Builds the bulk write operation for `recordOccurrence` WITHOUT executing it.\n * Returns a single upsert op matching the direct-call shape bit-for-bit.\n *\n * NOTE: `now` is captured at builder invocation time, NOT at BatchWriter flush\n * time — firstSeenAt / lastSeenAt reflect the event's wall-clock, not the\n * flush wall-clock. This preserves the semantic that these timestamps track\n * the error's occurrence, not the observability pipeline's delay.\n */\n toRecordOccurrenceOp(input: RecordOccurrenceInput): AnyBulkWriteOperation<JobErrorDoc> {\n const { filter, update } = this.buildRecordOccurrencePayload(input);\n return asBulkUpdateOp<JobErrorDoc>({\n updateOne: { filter, update, upsert: true },\n });\n }\n\n private buildRecordOccurrencePayload(\n input: RecordOccurrenceInput,\n ): { filter: Filter<JobErrorDoc>; update: UpdateSpec<JobErrorDoc> } {\n const signature = errorSignature(input.error);\n const { message, stack, causeChain } = extractMessageAndStack(input.error, {\n messageMaxChars: JOB_ERRORS_MESSAGE_MAX_CHARS,\n stackMaxChars: JOB_ERRORS_STACK_MAX_CHARS,\n });\n const now = new Date();\n\n const filter: Filter<JobErrorDoc> = { signature };\n const update: UpdateSpec<JobErrorDoc> = {\n $inc: { count: 1 },\n $setOnInsert: {\n signature,\n jobType: input.jobType,\n message,\n stack,\n firstSeenAt: now,\n createdAt: now,\n ...(causeChain.length > 0 ? { causeChain } : {}),\n },\n $set: {\n lastSeenAt: now,\n updatedAt: now,\n },\n };\n\n return { filter, update };\n }\n\n async topErrors(options: TopErrorsOptions): Promise<JobErrorDoc[]> {\n if (!(options.limit > 0) || options.limit > JOB_ERRORS_MAX_TOP_LIMIT) {\n throw new Error(\n `JobErrorsRepo.topErrors: limit must be in (0, ${JOB_ERRORS_MAX_TOP_LIMIT}]`,\n );\n }\n\n const filter: Filter<JobErrorDoc> = {\n ...(options.jobType !== undefined ? { jobType: options.jobType } : {}),\n ...(options.since !== undefined ? { lastSeenAt: { $gte: options.since } } : {}),\n };\n\n return await this.collection\n .find(filter)\n .sort({ count: -1 })\n .limit(options.limit)\n .toArray();\n }\n\n}\n","import type { AnyBulkWriteOperation, Db, Filter } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport { asBulkInsertOp, type BulkInsertOp } from \"./_types\";\nimport { extractMessageAndStack } from \"../../monitoring/error-extraction\";\nimport {\n IndexDescriptor,\n JobFailureDoc,\n RecordFailureInput,\n ListFailuresOptions,\n JOB_FAILURES_INPUT_MAX_CHARS,\n JOB_FAILURES_MESSAGE_MAX_CHARS,\n JOB_FAILURES_STACK_MAX_CHARS,\n JOB_FAILURES_LOGS_MAX_CHARS,\n JOB_FAILURES_MAX_LIST_LIMIT,\n JOB_FAILURES_TTL_SECONDS,\n} from \"./types\";\n\nconst COLLECTION = \"job_failures\";\n\nconst DEFAULT_LIST_LIMIT = 50;\n\nexport class JobFailuresRepo extends Repository<JobFailureDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { jobId: 1, failedAt: -1 },\n options: { name: \"jobId_failedAt_desc\" },\n },\n {\n keys: { jobType: 1, failedAt: -1 },\n options: { name: \"jobType_failedAt_desc\" },\n },\n // NOTE (TTL migration): MongoDB's `createIndex` is a no-op when an index with the same\n // key spec already exists — even if `expireAfterSeconds` differs. Changing\n // `JOB_FAILURES_TTL_SECONDS` will NOT propagate to a running deployment; apply a `collMod`\n // (or drop-and-recreate this index) manually at deploy time.\n {\n keys: { failedAt: 1 },\n options: {\n expireAfterSeconds: JOB_FAILURES_TTL_SECONDS,\n name: \"failedAt_ttl\",\n },\n },\n ];\n }\n\n async recordFailure(input: RecordFailureInput): Promise<void> {\n const doc = this.buildFailureDoc(input);\n await this.collection.insertOne(doc);\n }\n\n /**\n * Builds the bulk write operation for `recordFailure` WITHOUT executing it.\n * Returns a single insertOne op matching the direct-call document shape\n * bit-for-bit. Intended for the event-hooks layer (Story 1.7) to push through\n * a `BatchWriter`. Throws synchronously on invalid input using the same error\n * messages as `recordFailure`.\n */\n toRecordFailureOp(input: RecordFailureInput): AnyBulkWriteOperation<JobFailureDoc> {\n const doc = this.buildFailureDoc(input);\n const op: BulkInsertOp<JobFailureDoc> = { insertOne: { document: doc } };\n return asBulkInsertOp<JobFailureDoc>(op);\n }\n\n private buildFailureDoc(input: RecordFailureInput): JobFailureDoc {\n this.validateFailureInput(input);\n\n const serializedInput = this.serializeInputValue(input.input).slice(\n 0,\n JOB_FAILURES_INPUT_MAX_CHARS,\n );\n const { message, stack, causeChain } = extractMessageAndStack(input.error, {\n messageMaxChars: JOB_FAILURES_MESSAGE_MAX_CHARS,\n stackMaxChars: JOB_FAILURES_STACK_MAX_CHARS,\n });\n const logs = this.capLogs(input.logs);\n\n const doc: JobFailureDoc = {\n jobId: input.jobId,\n jobType: input.jobType,\n input: serializedInput,\n errorMessage: message,\n errorStack: stack,\n logs,\n failedAt: input.failedAt,\n createdAt: new Date(),\n };\n if (causeChain.length > 0) {\n doc.causeChain = causeChain;\n }\n return doc;\n }\n\n private validateFailureInput(input: RecordFailureInput): void {\n // Story 8.0 F1 (closes DF-1.7 + DF-CR1.4-second-pass + DF-CR1.6):\n // tighten \"non-empty string\" to also reject whitespace-only strings\n // (`\" \"` previously passed `length !== 0` but is semantic junk).\n // The validator's documented contract has always been \"non-empty\";\n // existing valid inputs are unaffected, only a new invalid class is rejected.\n if (typeof input.jobId !== \"string\" || input.jobId.trim().length === 0) {\n throw new Error(\"JobFailuresRepo.recordFailure: jobId must be a non-empty string\");\n }\n if (typeof input.jobType !== \"string\" || input.jobType.trim().length === 0) {\n throw new Error(\"JobFailuresRepo.recordFailure: jobType must be a non-empty string\");\n }\n if (!(input.failedAt instanceof Date) || Number.isNaN(input.failedAt.getTime())) {\n throw new Error(\"JobFailuresRepo.recordFailure: failedAt must be a valid Date\");\n }\n }\n\n async listFailures(\n options: ListFailuresOptions,\n ): Promise<{ items: JobFailureDoc[]; total: number }> {\n if (options.limit !== undefined) {\n if (\n !Number.isInteger(options.limit) ||\n options.limit <= 0 ||\n options.limit > JOB_FAILURES_MAX_LIST_LIMIT\n ) {\n throw new Error(\n `JobFailuresRepo.listFailures: limit must be an integer in (0, ${JOB_FAILURES_MAX_LIST_LIMIT}]`,\n );\n }\n }\n if (options.skip !== undefined) {\n if (!Number.isInteger(options.skip) || options.skip < 0) {\n throw new Error(\n \"JobFailuresRepo.listFailures: skip must be a non-negative integer\",\n );\n }\n }\n\n // Normalize null → undefined; callers crossing JSON boundaries routinely send null for unset fields.\n const fromDate = options.fromDate ?? undefined;\n const toDate = options.toDate ?? undefined;\n if (fromDate !== undefined && (!(fromDate instanceof Date) || Number.isNaN(fromDate.getTime()))) {\n throw new Error(\"JobFailuresRepo.listFailures: fromDate must be a valid Date\");\n }\n if (toDate !== undefined && (!(toDate instanceof Date) || Number.isNaN(toDate.getTime()))) {\n throw new Error(\"JobFailuresRepo.listFailures: toDate must be a valid Date\");\n }\n if (fromDate !== undefined && toDate !== undefined && fromDate.getTime() > toDate.getTime()) {\n throw new Error(\"JobFailuresRepo.listFailures: fromDate must be <= toDate\");\n }\n\n const skip = options.skip ?? 0;\n const limit = options.limit ?? DEFAULT_LIST_LIMIT;\n\n const filter: Filter<JobFailureDoc> = {};\n if (options.jobType !== undefined) {\n filter.jobType = options.jobType;\n }\n if (fromDate !== undefined || toDate !== undefined) {\n const range: { $gte?: Date; $lte?: Date } = {};\n if (fromDate !== undefined) range.$gte = fromDate;\n if (toDate !== undefined) range.$lte = toDate;\n filter.failedAt = range;\n }\n\n const [items, total] = await Promise.all([\n this.collection\n .find(filter)\n .sort({ failedAt: -1 })\n .skip(skip)\n .limit(limit)\n .toArray(),\n this.collection.countDocuments(filter),\n ]);\n\n return { items, total };\n }\n\n async getFailure(jobId: string): Promise<JobFailureDoc | null> {\n // Story 8.0 F1 — symmetric with validateFailureInput above.\n if (typeof jobId !== \"string\" || jobId.trim().length === 0) {\n throw new Error(\"JobFailuresRepo.getFailure: jobId must be a non-empty string\");\n }\n const filter: Filter<JobFailureDoc> = { jobId };\n return await this.collection.findOne(filter, { sort: { failedAt: -1 } });\n }\n\n private serializeInputValue(input: unknown): string {\n if (input === undefined) return \"undefined\";\n try {\n // Custom replacer preserves Map and Set (JSON.stringify otherwise serializes both as \"{}\").\n // Symbols are still silently dropped per ECMA-262 — that behavior is intentional.\n const result = JSON.stringify(input, (_key, value) => {\n if (value instanceof Map) return { __type: \"Map\", entries: Array.from(value.entries()) };\n if (value instanceof Set) return { __type: \"Set\", values: Array.from(value.values()) };\n return value;\n });\n // JSON.stringify can return undefined for e.g. functions or raw symbols passed as root value.\n if (result === undefined) return \"undefined\";\n return result;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return `[input serialization failed: ${msg}]`;\n }\n }\n\n private capLogs(logs: string | undefined): string {\n if (logs === undefined) return \"\";\n if (logs.length <= JOB_FAILURES_LOGS_MAX_CHARS) return logs;\n\n const originalLen = logs.length;\n let tailLen = JOB_FAILURES_LOGS_MAX_CHARS;\n let marker = \"\";\n for (let i = 0; i < 3; i++) {\n marker = `[truncated — showing last ${tailLen} of ${originalLen} chars]\\n`;\n if (marker.length >= JOB_FAILURES_LOGS_MAX_CHARS) {\n return marker.slice(0, JOB_FAILURES_LOGS_MAX_CHARS);\n }\n tailLen = JOB_FAILURES_LOGS_MAX_CHARS - marker.length;\n }\n return marker + logs.slice(-tailLen);\n }\n}\n","import type { Db, Filter, AnyBulkWriteOperation } from \"mongodb\";\nimport { Repository } from \"./_base\";\nimport { asBulkUpdateOp, type UpdateSpec } from \"./_types\";\nimport {\n IndexDescriptor,\n JobConfigEntry,\n JobConfigDoc,\n JOB_CONFIGS_DEFAULT_ENABLED,\n} from \"./types\";\nimport type { JobConfig } from \"../../types\";\n\nconst COLLECTION = \"job_configs\";\n\nexport class JobConfigsRepo extends Repository<JobConfigDoc> {\n constructor(db: Db) {\n super(db, COLLECTION);\n }\n\n protected indexes(): IndexDescriptor[] {\n return [\n {\n keys: { jobType: 1 },\n options: { unique: true, name: \"jobType_unique\" },\n },\n ];\n }\n\n async getConfig(jobType: string): Promise<JobConfigEntry> {\n this.validateJobType(jobType, \"JobConfigsRepo.getConfig\");\n const filter: Filter<JobConfigDoc> = { jobType };\n const doc = await this.collection.findOne(filter);\n\n if (!doc) {\n return {\n jobType,\n enabled: JOB_CONFIGS_DEFAULT_ENABLED,\n metadata: {},\n };\n }\n\n return {\n jobType: doc.jobType,\n enabled: doc.enabled,\n metadata: doc.metadata,\n };\n }\n\n async setEnabled(jobType: string, enabled: boolean): Promise<void> {\n this.validateJobType(jobType, \"JobConfigsRepo.setEnabled\");\n if (typeof enabled !== \"boolean\") {\n throw new Error(\"JobConfigsRepo.setEnabled: enabled must be a boolean\");\n }\n\n const filter: Filter<JobConfigDoc> = { jobType };\n const update: UpdateSpec<JobConfigDoc> = {\n $set: { enabled },\n $setOnInsert: {\n jobType,\n metadata: {},\n createdAt: new Date(),\n },\n $currentDate: { updatedAt: true },\n };\n const result = await this.collection.updateOne(filter, update, { upsert: true });\n\n if (result.matchedCount === 0 && (result.upsertedCount ?? 0) === 0) {\n throw new Error(\n `JobConfigsRepo.setEnabled: updateOne acknowledged no write for jobType=${jobType}`,\n );\n }\n }\n\n async listAll(): Promise<JobConfigDoc[]> {\n return await this.collection\n .find({})\n .sort({ jobType: 1 })\n .toArray();\n }\n\n async isEnabled(jobType: string): Promise<boolean> {\n this.validateJobType(jobType, \"JobConfigsRepo.isEnabled\");\n const filter: Filter<JobConfigDoc> = { jobType };\n const doc = await this.collection.findOne(filter, {\n projection: { enabled: 1, _id: 0 },\n });\n\n if (!doc) {\n return JOB_CONFIGS_DEFAULT_ENABLED;\n }\n return doc.enabled;\n }\n\n /**\n * Idempotent bulk upsert of every registered jobType + its metadata\n * projection (Story 5.1 D5.1-a). Called once per `RavenflowApp` instance\n * inside `initializeMonitoring()`.\n *\n * Semantics:\n * - `$setOnInsert { enabled: true, createdAt }` — the enabled flag is\n * set to the default ONLY on initial insert. An operator-toggled\n * `enabled: false` is NEVER overwritten by a subsequent boot.\n * - `$set { metadata }` — metadata is always refreshed from code config\n * (code is source of truth for non-user-writable fields).\n * - `$currentDate { updatedAt }` — timestamp bumps every call.\n *\n * Metadata projection (narrow + stable — see D5.1-h):\n * - `type`: jobConfig.type (always present in the discriminated union).\n * - `queue`: jobConfig.queue ?? null (null = inherits defaultQueue).\n * - `cron`: jobConfig.cron if present (token-bucket only today), else null.\n *\n * Additional type-specific fields (token-bucket's `rate`, distributed-etl's\n * `steps`) are NOT extracted — BSON can't serialize the function references\n * they contain, and the dashboard surface consumes only `type`/`queue`/`cron`\n * in V1. Future stories can widen this projection without a schema change.\n *\n * Failure mode: `bulkWrite` is the only I/O. Empty input short-circuits\n * because the driver rejects empty ops arrays.\n */\n async ensureEntries(\n jobs: Record<string, JobConfig>,\n ): Promise<void> {\n // Null / non-object input — return instead of throwing Object.entries(null)\n // TypeError (Story 5.1 code review P7). Preserves the \"non-fatal at boot\"\n // contract the caller (app.ts::initializeMonitoring) relies on.\n if (jobs === null || typeof jobs !== \"object\") return;\n\n const entries = Object.entries(jobs);\n if (entries.length === 0) return;\n\n // Validate-first pass (Story 5.1 code review P8). A bad jobType throws\n // BEFORE any bulk op is constructed so the error is localized and no\n // partial state is built up.\n for (const [jobType] of entries) {\n this.validateJobType(jobType, \"JobConfigsRepo.ensureEntries\");\n }\n\n const ops: Array<AnyBulkWriteOperation<JobConfigDoc>> = entries.map(\n ([jobType, cfg]) => {\n const cron =\n \"cron\" in cfg && typeof cfg.cron === \"string\" && cfg.cron.length > 0\n ? cfg.cron\n : null;\n const metadata: Record<string, unknown> = {\n type: cfg.type,\n queue: cfg.queue ?? null,\n cron,\n };\n const update: UpdateSpec<JobConfigDoc> = {\n $setOnInsert: {\n jobType,\n enabled: JOB_CONFIGS_DEFAULT_ENABLED,\n createdAt: new Date(),\n },\n $set: { metadata },\n $currentDate: { updatedAt: true },\n };\n return asBulkUpdateOp<JobConfigDoc>({\n updateOne: {\n filter: { jobType } as Filter<JobConfigDoc>,\n update,\n upsert: true,\n },\n });\n },\n );\n\n await this.collection.bulkWrite(ops, { ordered: false });\n }\n\n private validateJobType(jobType: string, ctx: string): void {\n // Story 8.0 F1 (closes DF-CR1.6 whitespace-only) — `\" \"` previously\n // passed `length !== 0` but is semantic junk. Tighten to `.trim().length`.\n // Existing valid inputs unaffected; new invalid class rejected.\n if (typeof jobType !== \"string\" || jobType.trim().length === 0) {\n throw new Error(`${ctx}: jobType must be a non-empty string`);\n }\n // Reject Mongo-reserved prefix/operator characters — `$` begins a\n // Mongo operator, `.` is a path separator. A jobType containing\n // either one could cross the filter/update-document boundary and\n // rewrite an unintended field (Story 5.1 code review P9).\n if (jobType.includes(\".\") || jobType.includes(\"$\")) {\n throw new Error(\n `${ctx}: jobType cannot contain '.' or '$' (Mongo-reserved characters)`,\n );\n }\n }\n}\n","import * as os from \"node:os\";\nimport * as crypto from \"node:crypto\";\nimport type { WorkersRepo } from \"../mongo/repositories/workers.repo\";\nimport type { WorkerQueueEntry } from \"../mongo/repositories/types\";\nimport {\n WORKERS_DEFAULT_HEARTBEAT_INTERVAL_MS,\n WORKERS_MIN_HEARTBEAT_INTERVAL_MS,\n} from \"../mongo/repositories/types\";\nimport { getLogger } from \"../logger\";\n\nexport interface WorkerRegistryOptions {\n heartbeatIntervalMs?: number;\n /** @internal Injected for testability; real callers should rely on the default. */\n hostnameProvider?: () => string;\n /** @internal Injected for testability; real callers should rely on the default. */\n pidProvider?: () => number;\n /** @internal Injected for testability; real callers should rely on the default. */\n workerIdProvider?: () => string;\n /** @internal Injected for testability; real callers should rely on the default. */\n clock?: () => Date;\n}\n\nexport class WorkerRegistry {\n private readonly repo: WorkersRepo;\n private readonly heartbeatIntervalMs: number;\n private readonly hostname: string;\n private readonly pid: number;\n private readonly _workerId: string;\n private readonly startedAt: Date;\n private readonly clock: () => Date;\n private timer: ReturnType<typeof setInterval> | null = null;\n private started = false;\n private registered = false;\n\n constructor(repo: WorkersRepo, options: WorkerRegistryOptions = {}) {\n WorkerRegistry.validateHeartbeatInterval(options.heartbeatIntervalMs);\n\n this.repo = repo;\n this.heartbeatIntervalMs =\n options.heartbeatIntervalMs ?? WORKERS_DEFAULT_HEARTBEAT_INTERVAL_MS;\n\n this.hostname = options.hostnameProvider ? options.hostnameProvider() : os.hostname();\n this.pid = options.pidProvider ? options.pidProvider() : process.pid;\n this._workerId = options.workerIdProvider\n ? options.workerIdProvider()\n : `${this.hostname}:${this.pid}:${crypto.randomUUID()}`;\n this.clock = options.clock ?? (() => new Date());\n this.startedAt = this.clock();\n }\n\n get workerId(): string {\n return this._workerId;\n }\n\n async registerQueue(queue: WorkerQueueEntry): Promise<void> {\n WorkerRegistry.validateQueueEntry(queue);\n\n if (!this.registered) {\n await this.repo.registerWorker({\n workerId: this._workerId,\n hostname: this.hostname,\n pid: this.pid,\n queues: [queue],\n startedAt: this.startedAt,\n });\n this.registered = true;\n return;\n }\n\n await this.repo.addQueue({\n workerId: this._workerId,\n queue,\n });\n }\n\n async start(): Promise<void> {\n if (this.started) return;\n if (!this.registered) {\n throw new Error(\n \"WorkerRegistry.start: call registerQueue at least once before start\",\n );\n }\n\n this.timer = setInterval(() => {\n void this.doHeartbeat();\n }, this.heartbeatIntervalMs);\n this.timer.unref();\n this.started = true;\n }\n\n async stop(): Promise<void> {\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.started = false;\n\n if (!this.registered) return;\n\n try {\n await this.repo.deregisterWorker(this._workerId);\n } catch (err) {\n getLogger().warn(\n { err, workerId: this._workerId },\n \"[worker-registry] deregister failed on stop\",\n );\n }\n this.registered = false;\n }\n\n private async doHeartbeat(): Promise<void> {\n try {\n await this.repo.updateHeartbeat(this._workerId);\n } catch (err) {\n getLogger().warn(\n { err, workerId: this._workerId },\n \"[worker-registry] heartbeat failed — will retry next tick\",\n );\n }\n }\n\n private static validateHeartbeatInterval(value: number | undefined | null): void {\n if (value == null) return;\n const MAX_HEARTBEAT_INTERVAL_MS = 24 * 60 * 60 * 1000;\n if (\n !Number.isInteger(value) ||\n value < WORKERS_MIN_HEARTBEAT_INTERVAL_MS ||\n value > MAX_HEARTBEAT_INTERVAL_MS\n ) {\n throw new Error(\n `[ravenflow] WorkerRegistry heartbeatIntervalMs must be an integer in [${WORKERS_MIN_HEARTBEAT_INTERVAL_MS}, ${MAX_HEARTBEAT_INTERVAL_MS}], got ${value}`,\n );\n }\n }\n\n private static validateQueueEntry(q: WorkerQueueEntry): void {\n if (q === null || typeof q !== \"object\") {\n throw new Error(\"WorkerRegistry.registerQueue: queue must be an object\");\n }\n if (typeof q.name !== \"string\" || q.name.length === 0) {\n throw new Error(\n \"WorkerRegistry.registerQueue: queue.name must be a non-empty string\",\n );\n }\n if (!Number.isInteger(q.concurrency) || q.concurrency <= 0) {\n throw new Error(\n \"WorkerRegistry.registerQueue: queue.concurrency must be a positive integer\",\n );\n }\n }\n}\n","export const DEFAULT_FLUSH_INTERVAL_MS = 30_000;\nexport const MIN_BUFFER_SIZE = 1000;\nexport const MAX_BUFFER_SIZE = 5000;\n\n/** Multiplier over `maxBufferSize` that caps memory growth under sustained flush failures. */\nexport const HARD_CAP_MULTIPLIER = 10;\n\n/** Backoff delays (ms) between drain retry attempts when the final flush fails. */\nexport const DRAIN_RETRY_BACKOFFS_MS = [0, 1000, 2000, 4000] as const;\n\nexport interface BatchWriterConfig {\n /** Max ops buffered before a size-triggered flush. Must be in [1000, 5000]. */\n maxBufferSize: number;\n /** Interval between time-triggered flushes in ms. Default 30000. Also used as `maxTimeMS` on bulkWrite. */\n flushIntervalMs?: number;\n}\n","import {\n MongoBulkWriteError,\n type AnyBulkWriteOperation,\n type Collection,\n type Document,\n} from \"mongodb\";\nimport { getLogger } from \"../logger\";\nimport {\n BatchWriterConfig,\n DEFAULT_FLUSH_INTERVAL_MS,\n DRAIN_RETRY_BACKOFFS_MS,\n HARD_CAP_MULTIPLIER,\n MAX_BUFFER_SIZE,\n MIN_BUFFER_SIZE,\n} from \"./batch-writer.types\";\n\nexport class BatchWriter<T extends Document = Document> {\n private readonly collection: Collection<T>;\n private readonly maxBufferSize: number;\n private readonly hardCapSize: number;\n private readonly flushIntervalMs: number;\n private buffer: AnyBulkWriteOperation<T>[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private flushing: Promise<void> | null = null;\n private drained = false;\n\n constructor(collection: Collection<T>, config: BatchWriterConfig) {\n this.validateConfig(config);\n\n this.collection = collection;\n this.maxBufferSize = config.maxBufferSize;\n this.hardCapSize = config.maxBufferSize * HARD_CAP_MULTIPLIER;\n this.flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\n this.startTimer();\n }\n\n push(operation: AnyBulkWriteOperation<T>): void {\n if (this.drained) {\n getLogger().warn(\n \"[batch-writer] push called after drain — operation ignored\",\n );\n return;\n }\n\n this.buffer.push(operation);\n this.enforceHardCap();\n\n if (this.buffer.length >= this.maxBufferSize) {\n this.resetTimer();\n void this.flush();\n }\n }\n\n async drain(): Promise<void> {\n this.drained = true;\n this.stopTimer();\n\n if (this.flushing) await this.flushing;\n\n for (const backoffMs of DRAIN_RETRY_BACKOFFS_MS) {\n if (this.buffer.length === 0) return;\n if (backoffMs > 0) {\n await new Promise<void>((resolve) => setTimeout(resolve, backoffMs));\n }\n await this.flush();\n }\n\n if (this.buffer.length > 0) {\n getLogger().error(\n { unflushed: this.buffer.length },\n \"[batch-writer] drain exhausted retries — operations lost\",\n );\n }\n }\n\n private async flush(): Promise<void> {\n if (this.flushing) return this.flushing;\n if (this.buffer.length === 0) return;\n\n this.flushing = this.doFlush();\n try {\n await this.flushing;\n } finally {\n this.flushing = null;\n }\n }\n\n private async doFlush(): Promise<void> {\n while (this.buffer.length > 0) {\n const snapshot = this.buffer;\n this.buffer = [];\n\n try {\n await this.collection.bulkWrite(snapshot, {\n ordered: false,\n maxTimeMS: this.flushIntervalMs,\n });\n } catch (err: unknown) {\n // mongodb driver types `writeErrors` as `OneOrMore<WriteError>` —\n // runtime narrowing to `Array` is required before reading `.length`.\n // Fix surfaced by Story 3.2 code review D8: re-exporting the cap\n // constants requires a clean DTS build, which this narrowing unblocks\n // (the pre-existing TS2339 was in the \"4 preserved errors\" cohort).\n if (\n err instanceof MongoBulkWriteError &&\n Array.isArray(err.writeErrors) &&\n err.writeErrors.length > 0\n ) {\n getLogger().warn(\n {\n failed: err.writeErrors.length,\n total: snapshot.length,\n },\n \"[batch-writer] bulkWrite partial failure — dropping failed ops (likely persistent errors)\",\n );\n continue;\n }\n\n this.buffer = [...snapshot, ...this.buffer];\n this.enforceHardCap();\n getLogger().warn(\n { err, count: snapshot.length },\n \"[batch-writer] flush failed — will retry on next tick or threshold\",\n );\n return;\n }\n }\n }\n\n private enforceHardCap(): void {\n if (this.buffer.length <= this.hardCapSize) return;\n const dropCount = this.buffer.length - this.hardCapSize;\n this.buffer.splice(0, dropCount);\n getLogger().warn(\n { dropCount, hardCapSize: this.hardCapSize },\n \"[batch-writer] hard cap exceeded — dropped oldest operations\",\n );\n }\n\n private startTimer(): void {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushIntervalMs);\n this.timer.unref();\n }\n\n private stopTimer(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n private resetTimer(): void {\n this.stopTimer();\n this.startTimer();\n }\n\n private validateConfig(config: BatchWriterConfig): void {\n if (\n !Number.isInteger(config.maxBufferSize) ||\n config.maxBufferSize < MIN_BUFFER_SIZE ||\n config.maxBufferSize > MAX_BUFFER_SIZE\n ) {\n throw new Error(\n `[ravenflow] BatchWriter maxBufferSize must be an integer in [${MIN_BUFFER_SIZE}, ${MAX_BUFFER_SIZE}], got ${config.maxBufferSize}`,\n );\n }\n\n if (config.flushIntervalMs !== undefined) {\n if (\n !Number.isInteger(config.flushIntervalMs) ||\n config.flushIntervalMs <= 0\n ) {\n throw new Error(\n `[ravenflow] BatchWriter flushIntervalMs must be a positive integer, got ${config.flushIntervalMs}`,\n );\n }\n }\n }\n}\n","import type { Queue } from \"bullmq\";\nimport { getLogger } from \"../logger\";\n\nexport async function captureJobLogs(\n queue: Queue,\n jobId: string,\n maxChars: number,\n): Promise<string> {\n if (typeof jobId !== \"string\" || jobId.length === 0) {\n throw new Error(\"captureJobLogs: jobId must be a non-empty string\");\n }\n if (!Number.isInteger(maxChars) || maxChars <= 0) {\n throw new Error(\n `captureJobLogs: maxChars must be a positive integer, got ${maxChars}`,\n );\n }\n\n let logs: string[];\n try {\n const result = await queue.getJobLogs(jobId, 0, -1, true);\n logs = result.logs;\n } catch (err) {\n getLogger().warn(\n { err, jobId },\n \"[log-capture] failed to fetch job logs — returning empty string\",\n );\n return \"\";\n }\n\n const joined = logs.join(\"\\n\");\n if (joined.length <= maxChars) return joined;\n\n const originalLen = joined.length;\n let tailLen = maxChars;\n let marker = \"\";\n for (let i = 0; i < 3; i++) {\n marker = `[truncated — showing last ${tailLen} of ${originalLen} chars]\\n`;\n if (marker.length >= maxChars) {\n return marker.slice(0, maxChars);\n }\n tailLen = maxChars - marker.length;\n }\n return marker + joined.slice(-tailLen);\n}\n","import type { Job, Queue, Worker } from \"bullmq\";\nimport { resolveJobName } from \"../app/job-registry\";\nimport type { JobConfig } from \"../types\";\nimport type { BatchWriter } from \"./batch-writer\";\nimport type { JobStatsRepo } from \"../mongo/repositories/job-stats.repo\";\nimport type { JobErrorsRepo } from \"../mongo/repositories/job-errors.repo\";\nimport type { JobFailuresRepo } from \"../mongo/repositories/job-failures.repo\";\nimport type {\n JobStatsDoc,\n JobErrorDoc,\n JobFailureDoc,\n} from \"../mongo/repositories/types\";\nimport { JOB_FAILURES_LOGS_MAX_CHARS } from \"../mongo/repositories/types\";\nimport { captureJobLogs } from \"./log-capture\";\nimport { getLogger } from \"../logger\";\n\nexport interface EventHooksOptions {\n queue: Queue;\n jobConfigs: Record<string, JobConfig>;\n jobStatsRepo: JobStatsRepo;\n jobErrorsRepo: JobErrorsRepo;\n jobFailuresRepo: JobFailuresRepo;\n jobStatsBatchWriter: BatchWriter<JobStatsDoc>;\n jobErrorsBatchWriter: BatchWriter<JobErrorDoc>;\n jobFailuresBatchWriter: BatchWriter<JobFailureDoc>;\n logsMaxChars?: number;\n}\n\nexport function attachEventHooks(worker: Worker, options: EventHooksOptions): void {\n worker.on(\"completed\", (job) => {\n void handleCompleted(job, options);\n });\n worker.on(\"failed\", (job, error) => {\n void handleFailed(job, error, options);\n });\n}\n\nasync function handleCompleted(job: Job, options: EventHooksOptions): Promise<void> {\n try {\n const jobType = resolveTopLevelJobType(job, options.jobConfigs);\n if (jobType === null) return;\n\n const durationMs = computeDurationMs(job);\n const ops = options.jobStatsRepo.toIncrementStatsOps(jobType, {\n success: true,\n durationMs,\n });\n for (const op of ops) {\n options.jobStatsBatchWriter.push(op);\n }\n } catch (err) {\n getLogger().warn(\n { err, jobId: job?.id, path: \"stats\" },\n \"[event-hooks] completed handler failed\",\n );\n }\n}\n\nasync function handleFailed(\n job: Job | undefined,\n error: Error,\n options: EventHooksOptions,\n): Promise<void> {\n if (job === undefined) {\n getLogger().warn(\n { message: error?.message },\n \"[event-hooks] failed event fired without a job — skipping\",\n );\n return;\n }\n if (job.id === undefined || job.id === null) {\n getLogger().warn(\n { jobName: job.name },\n \"[event-hooks] failed event fired without a job.id — skipping\",\n );\n return;\n }\n\n try {\n const jobType = resolveTopLevelJobType(job, options.jobConfigs);\n if (jobType === null) return;\n\n const durationMs = computeDurationMs(job);\n const failedAt =\n typeof job.finishedOn === \"number\" && Number.isFinite(job.finishedOn)\n ? new Date(job.finishedOn)\n : new Date();\n const jobId = String(job.id);\n const logsMaxChars = options.logsMaxChars ?? JOB_FAILURES_LOGS_MAX_CHARS;\n\n try {\n const ops = options.jobStatsRepo.toIncrementStatsOps(jobType, {\n success: false,\n durationMs,\n });\n for (const op of ops) {\n options.jobStatsBatchWriter.push(op);\n }\n } catch (err) {\n getLogger().warn(\n { err, jobId, path: \"stats\" },\n \"[event-hooks] failed handler: stats write failed\",\n );\n }\n\n try {\n const op = options.jobErrorsRepo.toRecordOccurrenceOp({\n jobType,\n error,\n });\n options.jobErrorsBatchWriter.push(op);\n } catch (err) {\n getLogger().warn(\n { err, jobId, path: \"errors\" },\n \"[event-hooks] failed handler: errors write failed\",\n );\n }\n\n let logs = \"\";\n try {\n logs = await captureJobLogs(options.queue, jobId, logsMaxChars);\n } catch (err) {\n getLogger().warn(\n { err, jobId, path: \"failures\" },\n \"[event-hooks] failed handler: log capture failed — recording failure with empty logs\",\n );\n }\n\n try {\n const op = options.jobFailuresRepo.toRecordFailureOp({\n jobId,\n jobType,\n input: job.data,\n error,\n failedAt,\n logs,\n });\n options.jobFailuresBatchWriter.push(op);\n } catch (err) {\n getLogger().warn(\n { err, jobId, path: \"failures\" },\n \"[event-hooks] failed handler: failures write failed\",\n );\n }\n } catch (err) {\n getLogger().warn(\n { err, jobId: job.id, path: \"unknown\" },\n \"[event-hooks] failed handler failed\",\n );\n }\n}\n\nfunction computeDurationMs(job: Job): number | undefined {\n const processedOn = job.processedOn;\n const finishedOn = job.finishedOn;\n if (typeof processedOn !== \"number\" || !Number.isFinite(processedOn)) return undefined;\n if (typeof finishedOn !== \"number\" || !Number.isFinite(finishedOn)) return undefined;\n const delta = finishedOn - processedOn;\n if (delta < 0) return undefined;\n return delta;\n}\n\nfunction resolveTopLevelJobType(\n job: Job,\n jobConfigs: Record<string, JobConfig>,\n): string | null {\n const { baseName, suffix } = resolveJobName(job.name, jobConfigs);\n if (suffix !== null) return null;\n if (!Object.prototype.hasOwnProperty.call(jobConfigs, baseName)) {\n getLogger().debug(\n { jobName: job.name, jobId: job.id },\n \"[event-hooks] skipping event for unknown jobType\",\n );\n return null;\n }\n return baseName;\n}\n","import { DelayedError, type Job } from \"bullmq\";\nimport type { JobConfig } from \"../types\";\nimport type { JobConfigsRepo } from \"../mongo/repositories/job-configs.repo\";\nimport { getLogger } from \"../logger\";\nimport { resolveJobName } from \"./job-registry\";\n\n/**\n * Delay (ms) applied to jobs whose jobType is `enabled: false`. After this\n * window BullMQ returns the job to the `waiting` set and a worker picks it\n * up again; the gate re-evaluates. When the jobType re-enables the next\n * attempt passes the check and dispatches normally.\n *\n * 60 seconds strikes the balance between \"pause takes effect quickly\" and\n * \"don't hammer Redis / Mongo with redefer churn\" (D5.1-b).\n */\nexport const DISABLED_JOB_DEFER_DELAY_MS = 60_000;\n\n/**\n * Non-fatal wrapper around `JobConfigsRepo.ensureEntries` called during\n * `RavenflowApp.initializeMonitoring`. A rejection (Mongo disconnect at\n * boot, write-concern downgrade, collection flip) is logged + swallowed;\n * the app continues without its `job_configs` bootstrap. The worker-side\n * `isEnabled` gate still works because `JobConfigsRepo.isEnabled` returns\n * `JOB_CONFIGS_DEFAULT_ENABLED=true` on missing docs — jobs enqueued\n * during this degraded state run as though uninitialized, which is the\n * pre-Story-5.1 behavior.\n *\n * Story 5.1 code review D5-P-a — extracted so the P11 live-reachable\n * degraded path is regression-anchored by a unit test rather than only\n * by visible-code inspection inside `initializeMonitoring`.\n */\nexport async function safeEnsureEntriesAtBoot(\n jobConfigsRepo: JobConfigsRepo,\n jobs: Record<string, JobConfig>,\n): Promise<void> {\n try {\n await jobConfigsRepo.ensureEntries(jobs);\n } catch (err) {\n getLogger().error(\n { err },\n \"[app] job_configs ensureEntries failed at boot — dashboard may show a stale job-type list\",\n );\n }\n}\n\n/**\n * Worker-side defer gate for disabled jobTypes (Story 5.1 D5.1-b).\n *\n * When the jobType's `enabled` flag is `false` in `job_configs`, this\n * helper moves the job to `delayed` with a +60s timestamp, increments\n * the cumulative `disabledDeferCount` on `job.progress`, and throws\n * `DelayedError` — the BullMQ-canonical signal for \"I moved this job to\n * delayed, don't mark completed/failed\" (verified against\n * node_modules/bullmq/dist/esm/classes/errors/delayed-error.d.ts:9\n * bullmq@5.63.1).\n *\n * When the jobType is enabled (or no row exists — default is `true`),\n * this is a no-op and the caller proceeds to normal dispatch.\n *\n * The `token` argument is the processor lambda's 2nd BullMQ argument\n * (verified against node_modules/bullmq/dist/esm/classes/job.d.ts:444\n * bullmq@5.63.1 — moveToDelayed(timestamp, token?) — passing undefined\n * is accepted but may fail under lock contention with renewLock).\n */\nexport async function deferIfDisabled(\n job: Job,\n jobConfigs: Record<string, JobConfig>,\n jobConfigsRepo: JobConfigsRepo,\n token: string | undefined,\n): Promise<void> {\n // Defensive input guards (Story 5.1 code review P2/P3). A lambda called\n // with malformed shape falls through to default dispatch rather than\n // crashing the worker — mirrors the null-guard pattern used throughout\n // app.ts for optional-infra surfaces.\n if (typeof job.name !== \"string\" || job.name.length === 0) return;\n if (jobConfigs === null || typeof jobConfigs !== \"object\") return;\n\n // BullMQ passes `token` as the 2nd processor argument in real dispatch;\n // undefined indicates a test rig or library misuse. Warn but proceed —\n // moveToDelayed may fail under lock contention without the token (P6).\n if (token === undefined) {\n getLogger().warn(\n { jobId: job.id, jobName: job.name },\n \"[app] deferIfDisabled invoked with undefined token — moveToDelayed may fail under lock contention\",\n );\n }\n\n const { baseName } = resolveJobName(job.name, jobConfigs);\n // resolveJobName can return empty when `job.name` matches only a suffix\n // pattern with no registered base — skip the gate + warn rather than\n // feeding \"\" to validateJobType which throws non-empty-string (P4).\n if (!baseName) {\n getLogger().warn(\n { jobId: job.id, jobName: job.name },\n \"[app] deferIfDisabled: resolveJobName returned empty baseName — skipping gate\",\n );\n return;\n }\n\n // Propagate isEnabled rejection — the spec (line 255) explicitly locks\n // \"disabled-check failure = job failure\" as the intentional choice.\n // Fail-open would silently run disabled jobs during a persistent Mongo\n // outage; BullMQ attempt-erosion on a transient blip is the accepted\n // trade. Job goes to failed + surfaces in /failures if persistent.\n const enabled = await jobConfigsRepo.isEnabled(baseName);\n if (enabled) return;\n\n // Cumulative counter — not reset on redefer. Structured progress (per\n // D5.1-i) preserves room for future observability fields without a\n // worker-side migration. BullMQ types `job.progress` as\n // `number | string | object` (verified against job.d.ts) so we narrow\n // defensively at the boundary (safe runtime shape check, not an\n // escape-hatch cast per P2).\n const prev = job.progress;\n const prevCount =\n typeof prev === \"object\" && prev !== null && !Array.isArray(prev)\n ? (prev as { disabledDeferCount?: unknown }).disabledDeferCount\n : undefined;\n const nextCount =\n typeof prevCount === \"number\" && Number.isFinite(prevCount) ? prevCount + 1 : 1;\n\n // moveToDelayed FIRST (Story 5.1 code review P1) — the counter never\n // bumps for a defer that didn't actually happen. If moveToDelayed\n // rejects (Redis blip, lock lost), the caller's worker sees a non-\n // DelayedError and the job goes to failed with eroded attempts — a\n // real infra failure, not a regression of the counter's semantics.\n // verified against node_modules/bullmq/dist/esm/classes/job.d.ts:444\n // (bullmq@5.63.1) — moveToDelayed(timestamp, token?).\n await job.moveToDelayed(Date.now() + DISABLED_JOB_DEFER_DELAY_MS, token);\n // Counter write is best-effort — updateProgress after a successful\n // moveToDelayed must not unwind the defer. Log the slip and continue.\n try {\n await job.updateProgress({ disabledDeferCount: nextCount });\n } catch (err) {\n getLogger().warn(\n { err, jobType: baseName, jobId: job.id },\n \"[app] deferIfDisabled: updateProgress failed post-defer — counter may lag by one\",\n );\n }\n\n getLogger().info(\n { jobType: baseName, jobId: job.id, disabledDeferCount: nextCount },\n \"[app] job deferred: disabled\",\n );\n\n // verified against node_modules/bullmq/dist/esm/classes/errors/delayed-error.d.ts:9\n // (bullmq@5.63.1) — DelayedError signals 'I moved this job to delayed,\n // don't mark completed/failed'.\n throw new DelayedError();\n}\n","import { ConnectionOptions as RedisConnectionOptions, Worker } from \"bullmq\";\nimport {\n RavenflowConfig,\n RetentionConfig,\n JobConfig,\n JobNames,\n JobsProxy,\n} from \"../types\";\nimport { initLogger, getLogger, LoggerOptions } from \"../logger\";\nimport { initQueues, closeQueues, QueueStore } from \"./queue-manager\";\nimport { initJobs, processJob } from \"./job-registry\";\nimport {\n createWorker,\n setupWorkerEvents,\n printBanner,\n closeWorkers,\n} from \"./worker-manager\";\nimport { syncJobSchedulers } from \"./scheduler\";\nimport { connectMongo, closeMongo, getDb, tokenBucketRepo } from \"../mongo\";\nimport { StallWatcher } from \"../internal-jobs\";\nimport { WorkersRepo } from \"../mongo/repositories/workers.repo\";\nimport { JobStatsRepo } from \"../mongo/repositories/job-stats.repo\";\nimport { JobErrorsRepo } from \"../mongo/repositories/job-errors.repo\";\nimport { JobFailuresRepo } from \"../mongo/repositories/job-failures.repo\";\nimport { JobConfigsRepo } from \"../mongo/repositories/job-configs.repo\";\nimport type {\n JobStatsDoc,\n JobErrorDoc,\n JobFailureDoc,\n} from \"../mongo/repositories/types\";\nimport { WorkerRegistry } from \"../monitoring/worker-registry\";\nimport { BatchWriter } from \"../monitoring/batch-writer\";\nimport type { BatchWriterConfig } from \"../monitoring/batch-writer.types\";\nimport { MIN_BUFFER_SIZE } from \"../monitoring/batch-writer.types\";\nimport { attachEventHooks } from \"../monitoring/event-hooks\";\nimport { deferIfDisabled, safeEnsureEntriesAtBoot } from \"./disabled-job\";\n\nexport interface MongoConnectionObject {\n host: string;\n port?: number;\n username?: string;\n password?: string;\n database?: string;\n}\n\nexport interface RavenflowAppOptions {\n redis: RedisConnectionOptions;\n mongo?: string | MongoConnectionObject;\n retention?: RetentionConfig;\n logger?: LoggerOptions;\n monitoring?: {\n enabled?: boolean;\n heartbeatIntervalMs?: number;\n batchWriter?: {\n maxBufferSize?: number;\n flushIntervalMs?: number;\n };\n };\n}\n\nconst DEFAULT_MONITORING_BATCH_BUFFER = MIN_BUFFER_SIZE;\n\nconst MONGO_REQUIRED_TYPES = new Set([\"stateful-etl\", \"token-bucket\"]);\n\nfunction resolveMongoUri(\n mongo: RavenflowAppOptions[\"mongo\"],\n): string | null {\n if (!mongo) return null;\n if (typeof mongo === \"string\") return mongo;\n\n const { host, port = 27017, username, password, database } = mongo;\n const auth = username && password ? `${encodeURIComponent(username)}:${encodeURIComponent(password)}@` : \"\";\n const db = database ? `/${database}` : \"\";\n return `mongodb://${auth}${host}:${port}${db}`;\n}\n\nfunction validateConfig(\n config: RavenflowConfig,\n mongoUri: string | null,\n): void {\n const errors: string[] = [];\n\n for (const [jobName, jobConfig] of Object.entries(config.jobs)) {\n const jc = jobConfig as JobConfig;\n\n // Mongo required for stateful-etl and token-bucket\n if (MONGO_REQUIRED_TYPES.has(jc.type) && !mongoUri) {\n errors.push(\n `\"${jobName}\" (${jc.type}) requires a mongo connection. Pass { mongo: \"mongodb://...\" } to createRavenflowApp().`,\n );\n }\n\n // Token-bucket requires cron\n if (jc.type === \"token-bucket\" && !jc.cron?.trim()) {\n errors.push(`\"${jobName}\" (token-bucket) requires a cron schedule.`);\n }\n\n // Token-bucket requires at least rate or getBudget\n if (jc.type === \"token-bucket\" && !jc.rate && !jc.getBudget) {\n errors.push(\n `\"${jobName}\" (token-bucket) requires at least one of \"rate\" or \"getBudget\".`,\n );\n }\n\n // Token-bucket rate validation\n if (jc.type === \"token-bucket\" && jc.rate) {\n const validIntervals = new Set([\"second\", \"minute\", \"hour\", \"day\", \"week\"]);\n if (!validIntervals.has(jc.rate.interval)) {\n errors.push(\n `\"${jobName}\" (token-bucket) has invalid rate interval \"${jc.rate.interval}\". Must be one of: second, minute, hour, day, week.`,\n );\n }\n if (jc.rate.amount <= 0) {\n errors.push(\n `\"${jobName}\" (token-bucket) rate.amount must be a positive number.`,\n );\n }\n }\n\n // Token-bucket optional numeric field validation\n if (jc.type === \"token-bucket\") {\n if (jc.maxCarryOver !== undefined && jc.maxCarryOver < 0) {\n errors.push(\n `\"${jobName}\" (token-bucket) maxCarryOver must be >= 0.`,\n );\n }\n if (jc.consumeTimeout !== undefined && jc.consumeTimeout <= 0) {\n errors.push(\n `\"${jobName}\" (token-bucket) consumeTimeout must be a positive number.`,\n );\n }\n }\n\n // Step queue references must exist in config\n if (\"steps\" in jc && jc.steps) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic-erasure cast for free-form step map; typed config-walker deferred per Story 1.8 AC #14\n for (const [stepName, stepConfig] of Object.entries(jc.steps as Record<string, any>)) {\n if (stepConfig?.queue && stepConfig.queue !== config.defaultQueue) {\n const knownQueues = new Set<string>([config.defaultQueue]);\n for (const j of Object.values(config.jobs)) {\n if ((j as JobConfig).queue) knownQueues.add((j as JobConfig).queue!);\n }\n if (!knownQueues.has(stepConfig.queue)) {\n errors.push(\n `\"${jobName}\" step \"${stepName}\" references queue \"${stepConfig.queue}\" which is not declared by any job or as defaultQueue.`,\n );\n }\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new Error(\n `[ravenflow] Config validation failed:\\n - ${errors.join(\"\\n - \")}`,\n );\n }\n}\n\nexport class RavenflowApp<TConfig extends RavenflowConfig = RavenflowConfig> {\n protected redisConnection: RedisConnectionOptions;\n protected defaultConcurrency: number = 5;\n protected config: TConfig;\n protected mongoUri: string | null;\n\n public jobs: JobsProxy<TConfig[\"jobs\"]>;\n protected jobConfigs: Record<JobNames<TConfig>, JobConfig>;\n protected queues: QueueStore;\n protected workers: Worker[] = [];\n protected stallWatcher: StallWatcher | null = null;\n protected workersRepo: WorkersRepo | null = null;\n protected workerRegistry: WorkerRegistry | null = null;\n protected jobStatsRepo: JobStatsRepo | null = null;\n protected jobErrorsRepo: JobErrorsRepo | null = null;\n protected jobFailuresRepo: JobFailuresRepo | null = null;\n protected jobConfigsRepo: JobConfigsRepo | null = null;\n protected jobStatsBatchWriter: BatchWriter<JobStatsDoc> | null = null;\n protected jobErrorsBatchWriter: BatchWriter<JobErrorDoc> | null = null;\n protected jobFailuresBatchWriter: BatchWriter<JobFailureDoc> | null = null;\n protected monitoringInitPromise: Promise<void> | null = null;\n protected readonly monitoringOptions: {\n enabled: boolean;\n heartbeatIntervalMs: number | undefined;\n batchWriter: BatchWriterConfig;\n };\n\n constructor(config: TConfig, options: RavenflowAppOptions) {\n this.redisConnection = options.redis;\n this.config = config;\n this.mongoUri = resolveMongoUri(options.mongo);\n\n initLogger(options.logger);\n\n validateConfig(config, this.mongoUri);\n\n this.queues = initQueues(config, options.redis, options.retention);\n\n const { jobsProxy, jobConfigs } = initJobs(config, this.queues);\n this.jobs = jobsProxy;\n this.jobConfigs = jobConfigs;\n\n this.monitoringOptions = {\n enabled:\n this.mongoUri !== null && options.monitoring?.enabled !== false,\n heartbeatIntervalMs: options.monitoring?.heartbeatIntervalMs,\n batchWriter: {\n maxBufferSize:\n options.monitoring?.batchWriter?.maxBufferSize ??\n DEFAULT_MONITORING_BATCH_BUFFER,\n flushIntervalMs: options.monitoring?.batchWriter?.flushIntervalMs,\n },\n };\n\n if (options.monitoring?.enabled === true && this.mongoUri === null) {\n getLogger().warn(\n {},\n \"[ravenflow] monitoring.enabled=true was requested but no mongo connection is configured — monitoring is disabled\",\n );\n }\n }\n\n public async startWorker(\n queueName: string,\n options?: { concurrency?: number },\n ) {\n if (this.mongoUri) {\n await connectMongo(this.mongoUri);\n await tokenBucketRepo.ensureIndexes();\n }\n\n const queueData = this.queues[queueName];\n if (!queueData) {\n throw new Error(`Queue ${queueName} not found in config.`);\n }\n\n const concurrency = options?.concurrency ?? this.defaultConcurrency;\n\n if (this.mongoUri && this.monitoringOptions.enabled) {\n if (!this.workerRegistry) {\n if (!this.monitoringInitPromise) {\n this.monitoringInitPromise = this.initializeMonitoring(queueName, concurrency);\n }\n try {\n await this.monitoringInitPromise;\n } catch {\n // Errors already logged inside initializeMonitoring.\n }\n }\n\n if (this.workerRegistry) {\n try {\n await this.workerRegistry.registerQueue({ name: queueName, concurrency });\n } catch (err) {\n getLogger().error(\n { err },\n \"[app] worker registry registerQueue failed — will retry on next startWorker call\",\n );\n }\n }\n }\n\n printBanner(\n queueName,\n concurrency,\n this.redisConnection,\n this.jobConfigs,\n this.mongoUri,\n );\n\n await syncJobSchedulers(queueData.queue, this.jobConfigs);\n\n const hasStatefulEtl = Object.values(this.jobConfigs).some(\n (c) => (c as JobConfig).type === \"stateful-etl\",\n );\n\n if (hasStatefulEtl && !this.stallWatcher) {\n this.stallWatcher = new StallWatcher(\n this.jobConfigs,\n this.queues,\n this.config.defaultQueue,\n queueData.queue,\n );\n }\n\n const worker = createWorker(\n queueName,\n this.redisConnection,\n concurrency,\n async (job, token) => {\n // D5.1-b — disabled-jobType defer gate (Story 5.1). When a jobType's\n // `enabled` flag is false, deferIfDisabled calls moveToDelayed(+60s)\n // and throws DelayedError so BullMQ requeues the job without\n // counting it against `attempts`. Skipped entirely when monitoring\n // is off (no jobConfigsRepo → pre-5.1 behavior, backward compat).\n // Snapshot the repo ref locally (Story 5.1 code review P11) so a\n // concurrent `close()` nulling `this.jobConfigsRepo` between the\n // check and the call cannot cause a null-deref.\n const configsRepo = this.jobConfigsRepo;\n if (configsRepo) {\n await deferIfDisabled(\n job,\n this.jobConfigs as Record<string, JobConfig>,\n configsRepo,\n token,\n );\n }\n if (this.stallWatcher?.canProcess(job.name)) {\n return this.stallWatcher.process(job);\n }\n return processJob(job, this.jobConfigs, this.queues, this.config.defaultQueue);\n },\n );\n setupWorkerEvents(worker, queueName);\n this.workers.push(worker);\n worker.run();\n\n if (\n this.monitoringOptions.enabled &&\n this.jobStatsRepo &&\n this.jobErrorsRepo &&\n this.jobFailuresRepo &&\n this.jobStatsBatchWriter &&\n this.jobErrorsBatchWriter &&\n this.jobFailuresBatchWriter\n ) {\n attachEventHooks(worker, {\n queue: queueData.queue,\n jobConfigs: this.jobConfigs as Record<string, JobConfig>,\n jobStatsRepo: this.jobStatsRepo,\n jobErrorsRepo: this.jobErrorsRepo,\n jobFailuresRepo: this.jobFailuresRepo,\n jobStatsBatchWriter: this.jobStatsBatchWriter,\n jobErrorsBatchWriter: this.jobErrorsBatchWriter,\n jobFailuresBatchWriter: this.jobFailuresBatchWriter,\n });\n }\n\n if (this.stallWatcher) {\n await this.stallWatcher.start();\n }\n }\n\n protected async initializeMonitoring(\n queueName: string,\n concurrency: number,\n ): Promise<void> {\n let registry: WorkerRegistry | null = null;\n let statsBatchWriter: BatchWriter<JobStatsDoc> | null = null;\n let errorsBatchWriter: BatchWriter<JobErrorDoc> | null = null;\n let failuresBatchWriter: BatchWriter<JobFailureDoc> | null = null;\n try {\n const db = getDb();\n const workersRepo = new WorkersRepo(db);\n const statsRepo = new JobStatsRepo(db);\n const errorsRepo = new JobErrorsRepo(db);\n const failuresRepo = new JobFailuresRepo(db);\n const configsRepo = new JobConfigsRepo(db);\n await Promise.all([\n workersRepo.init(),\n statsRepo.init(),\n errorsRepo.init(),\n failuresRepo.init(),\n configsRepo.init(),\n ]);\n\n // D5.1-a — idempotent upsert of registered job types + metadata.\n // Non-fatal per P11 audit: a transient Mongo blip here leaves the\n // dashboard with a stale list but the worker-side isEnabled gate\n // still works (falls back to default=true on missing docs), and\n // the next boot retries. Extracted into `safeEnsureEntriesAtBoot`\n // so the degraded path is regression-anchored by a unit test\n // (Story 5.1 code review D5-P-a).\n await safeEnsureEntriesAtBoot(\n configsRepo,\n this.config.jobs as Record<string, JobConfig>,\n );\n\n registry = new WorkerRegistry(workersRepo, {\n heartbeatIntervalMs: this.monitoringOptions.heartbeatIntervalMs,\n });\n await registry.registerQueue({ name: queueName, concurrency });\n await registry.start();\n\n statsBatchWriter = new BatchWriter<JobStatsDoc>(\n statsRepo.collection,\n this.monitoringOptions.batchWriter,\n );\n errorsBatchWriter = new BatchWriter<JobErrorDoc>(\n errorsRepo.collection,\n this.monitoringOptions.batchWriter,\n );\n failuresBatchWriter = new BatchWriter<JobFailureDoc>(\n failuresRepo.collection,\n this.monitoringOptions.batchWriter,\n );\n\n this.workersRepo = workersRepo;\n this.workerRegistry = registry;\n this.jobStatsRepo = statsRepo;\n this.jobErrorsRepo = errorsRepo;\n this.jobFailuresRepo = failuresRepo;\n this.jobConfigsRepo = configsRepo;\n this.jobStatsBatchWriter = statsBatchWriter;\n this.jobErrorsBatchWriter = errorsBatchWriter;\n this.jobFailuresBatchWriter = failuresBatchWriter;\n } catch (err) {\n getLogger().error(\n { err },\n \"[app] monitoring setup failed — continuing without observability writes\",\n );\n if (registry) {\n await registry.stop().catch(() => {});\n }\n const drainables = [statsBatchWriter, errorsBatchWriter, failuresBatchWriter].filter(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- heterogeneous BatchWriter union; typed narrowing deferred per Story 1.8 AC #14\n (bw): bw is BatchWriter<any> => bw !== null,\n );\n if (drainables.length > 0) {\n await Promise.allSettled(drainables.map((bw) => bw.drain()));\n }\n this.workerRegistry = null;\n this.workersRepo = null;\n this.jobStatsRepo = null;\n this.jobErrorsRepo = null;\n this.jobFailuresRepo = null;\n this.jobConfigsRepo = null;\n this.jobStatsBatchWriter = null;\n this.jobErrorsBatchWriter = null;\n this.jobFailuresBatchWriter = null;\n } finally {\n this.monitoringInitPromise = null;\n }\n }\n\n public async close(): Promise<void> {\n const logger = getLogger();\n logger.info(\"Shutting down...\");\n\n if (this.stallWatcher) {\n await this.stallWatcher.stop();\n this.stallWatcher = null;\n }\n\n if (this.workerRegistry) {\n await this.workerRegistry.stop();\n this.workerRegistry = null;\n this.workersRepo = null;\n }\n\n await closeWorkers(this.workers);\n\n const drainables = [\n this.jobStatsBatchWriter,\n this.jobErrorsBatchWriter,\n this.jobFailuresBatchWriter,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- heterogeneous BatchWriter union; typed narrowing deferred per Story 1.8 AC #14\n ].filter((bw): bw is BatchWriter<any> => bw !== null);\n if (drainables.length > 0) {\n await Promise.allSettled(drainables.map((bw) => bw.drain()));\n }\n this.jobStatsBatchWriter = null;\n this.jobErrorsBatchWriter = null;\n this.jobFailuresBatchWriter = null;\n this.jobStatsRepo = null;\n this.jobErrorsRepo = null;\n this.jobFailuresRepo = null;\n this.jobConfigsRepo = null;\n\n await closeQueues(this.queues);\n await closeMongo();\n logger.info(\"Shutdown complete.\");\n }\n}\n","import { RavenflowConfig } from \"./types\";\nimport { RavenflowApp, RavenflowAppOptions } from \"./app/index\";\n\nexport { RavenflowApp } from \"./app/index\";\nexport type { RavenflowAppOptions, MongoConnectionObject } from \"./app/index\";\nexport { initLogger, getLogger } from \"./logger\";\n\nexport type {\n // Config\n JobConfig,\n RavenflowConfig,\n RetentionConfig,\n\n // Job configs\n SimpleJobConfig,\n MemoryEtlJobConfig,\n DistributedEtlJobConfig,\n StatefulEtlJobConfig,\n TokenBucketJobConfig,\n TokenBucketInterval,\n\n // Step & rate limit configs\n StepConfig,\n MemoryEtlStepConfig,\n RateLimitConfig,\n\n // ETL types\n EtlExtractResult,\n EtlResult,\n\n // Job status\n JobStatus,\n} from \"./types\";\n\nexport {\n defineConfig,\n defineSimpleJob,\n defineMemoryEtlJob,\n defineDistributedEtlJob,\n defineStatefulEtlJob,\n defineTokenBucketJob,\n} from \"./types\";\n\nexport type { Logger, LoggerOptions } from \"./logger\";\n\nexport { BatchWriter } from \"./monitoring\";\nexport type { BatchWriterConfig } from \"./monitoring\";\nexport { errorSignature } from \"./monitoring\";\nexport { captureJobLogs } from \"./monitoring\";\nexport { WorkerRegistry } from \"./monitoring\";\nexport type { WorkerRegistryOptions } from \"./monitoring\";\nexport { attachEventHooks } from \"./monitoring\";\nexport type { EventHooksOptions } from \"./monitoring\";\n\nexport { DEFAULT_WORKER_METRICS, METRICS_TIME } from \"./app/worker-manager\";\nexport { DISABLED_JOB_DEFER_DELAY_MS, deferIfDisabled } from \"./app/disabled-job\";\n\nexport {\n JobStatsRepo,\n JobErrorsRepo,\n JobFailuresRepo,\n WorkersRepo,\n JobConfigsRepo,\n Repository,\n computeBucketStart,\n JOB_FAILURES_INPUT_MAX_CHARS,\n JOB_FAILURES_MESSAGE_MAX_CHARS,\n JOB_FAILURES_STACK_MAX_CHARS,\n JOB_FAILURES_LOGS_MAX_CHARS,\n} from \"./mongo/repositories\";\nexport type {\n IndexDescriptor,\n BucketGranularity,\n IncrementStatsInput,\n JobStatsDoc,\n JobErrorDoc,\n RecordOccurrenceInput,\n TopErrorsOptions,\n JobFailureDoc,\n RecordFailureInput,\n ListFailuresOptions,\n WorkerDoc,\n WorkerQueueEntry,\n RegisterWorkerInput,\n UpsertWorkerQueueInput,\n JobConfigEntry,\n JobConfigDoc,\n} from \"./mongo/repositories\";\n\nexport function createRavenflowApp<TConfig extends RavenflowConfig>(\n config: TConfig,\n options: RavenflowAppOptions,\n): RavenflowApp<TConfig> {\n return new RavenflowApp(config, options);\n}\n"]}
|