@microfox/ai-worker 1.0.2 → 1.0.3

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.
@@ -1,3 +1,9 @@
1
+ import {
2
+ appendQueueJobStepInStore,
3
+ updateQueueJobStepInStore,
4
+ upsertInitialQueueJob
5
+ } from "./chunk-AOXGONGI.mjs";
6
+
1
7
  // src/handler.ts
2
8
  import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
3
9
 
@@ -328,318 +334,27 @@ async function upsertRedisJob(jobId, workerId, input, metadata) {
328
334
  }
329
335
  }
330
336
 
331
- // src/queueJobStore.ts
332
- import { Redis as UpstashRedis } from "@upstash/redis";
333
- import { MongoClient as MongoClient2 } from "mongodb";
334
- var mongoUri = process.env.DATABASE_MONGODB_URI || process.env.MONGODB_URI;
335
- var mongoDbName = process.env.DATABASE_MONGODB_DB || process.env.MONGODB_DB || "mediamake";
336
- var mongoQueueCollectionName = process.env.MONGODB_QUEUE_JOBS_COLLECTION || "queue_jobs";
337
- var mongoClientPromise = null;
338
- async function getMongoClient() {
339
- if (!mongoUri) {
340
- throw new Error(
341
- "MongoDB URI required for queue job store. Set DATABASE_MONGODB_URI or MONGODB_URI."
342
- );
343
- }
344
- if (!mongoClientPromise) {
345
- mongoClientPromise = new MongoClient2(mongoUri, {
346
- maxPoolSize: 10,
347
- minPoolSize: 0,
348
- serverSelectionTimeoutMS: 1e4
349
- }).connect();
350
- }
351
- return mongoClientPromise;
352
- }
353
- async function getMongoQueueCollection() {
354
- const client = await getMongoClient();
355
- return client.db(mongoDbName).collection(mongoQueueCollectionName);
356
- }
357
- var redisUrl2 = process.env.WORKER_UPSTASH_REDIS_REST_URL || process.env.UPSTASH_REDIS_REST_URL || process.env.UPSTASH_REDIS_URL;
358
- var redisToken2 = process.env.WORKER_UPSTASH_REDIS_REST_TOKEN || process.env.UPSTASH_REDIS_REST_TOKEN || process.env.UPSTASH_REDIS_TOKEN;
359
- var queueKeyPrefix = process.env.WORKER_UPSTASH_REDIS_QUEUE_PREFIX || process.env.UPSTASH_REDIS_QUEUE_PREFIX || "worker:queue-jobs:";
360
- var defaultTtlSeconds2 = 60 * 60 * 24 * 7;
361
- var queueJobTtlSeconds = typeof process.env.WORKER_QUEUE_JOBS_TTL_SECONDS === "string" ? parseInt(process.env.WORKER_QUEUE_JOBS_TTL_SECONDS, 10) || defaultTtlSeconds2 : typeof process.env.WORKER_JOBS_TTL_SECONDS === "string" ? parseInt(process.env.WORKER_JOBS_TTL_SECONDS, 10) || defaultTtlSeconds2 : defaultTtlSeconds2;
362
- var redisClient2 = null;
363
- function getRedis2() {
364
- if (!redisUrl2 || !redisToken2) {
365
- throw new Error(
366
- "Upstash Redis configuration missing for queue job store. Set WORKER_UPSTASH_REDIS_REST_URL and WORKER_UPSTASH_REDIS_REST_TOKEN (or UPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKEN)."
367
- );
368
- }
369
- if (!redisClient2) {
370
- redisClient2 = new UpstashRedis({
371
- url: redisUrl2,
372
- token: redisToken2
373
- });
374
- }
375
- return redisClient2;
376
- }
377
- function queueKey(id) {
378
- return `${queueKeyPrefix}${id}`;
379
- }
380
- function stepsFromHash(val) {
381
- if (Array.isArray(val)) return val;
382
- if (typeof val === "string") {
383
- try {
384
- const parsed = JSON.parse(val);
385
- return Array.isArray(parsed) ? parsed : [];
386
- } catch {
387
- return [];
388
- }
389
- }
390
- return [];
391
- }
392
- function metadataFromHash(val) {
393
- if (val && typeof val === "object" && !Array.isArray(val)) return val;
394
- if (typeof val === "string") {
395
- try {
396
- const parsed = JSON.parse(val);
397
- return parsed && typeof parsed === "object" ? parsed : {};
398
- } catch {
399
- return {};
400
- }
401
- }
402
- return {};
403
- }
404
- async function loadQueueJobRedis(queueJobId) {
405
- const redis = getRedis2();
406
- const key = queueKey(queueJobId);
407
- const data = await redis.hgetall(key);
408
- if (!data || typeof data !== "object" || Object.keys(data).length === 0) return null;
409
- const d = data;
410
- const record = {
411
- id: d.id === void 0 ? queueJobId : String(d.id),
412
- queueId: String(d.queueId ?? ""),
413
- status: String(d.status ?? "running"),
414
- steps: stepsFromHash(d.steps),
415
- metadata: metadataFromHash(d.metadata),
416
- createdAt: String(d.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()),
417
- updatedAt: String(d.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()),
418
- completedAt: d.completedAt != null ? String(d.completedAt) : void 0
419
- };
420
- return record;
421
- }
422
- async function saveQueueJobRedis(record) {
423
- const redis = getRedis2();
424
- const key = queueKey(record.id);
425
- const now = (/* @__PURE__ */ new Date()).toISOString();
426
- const toSet = {
427
- id: record.id,
428
- queueId: record.queueId,
429
- status: record.status,
430
- steps: JSON.stringify(record.steps || []),
431
- metadata: JSON.stringify(record.metadata || {}),
432
- createdAt: record.createdAt || now,
433
- updatedAt: record.updatedAt || now
434
- };
435
- if (record.completedAt) {
436
- toSet.completedAt = record.completedAt;
437
- }
438
- await redis.hset(key, toSet);
439
- if (queueJobTtlSeconds > 0) {
440
- await redis.expire(key, queueJobTtlSeconds);
441
- }
442
- }
443
- function getStoreType() {
444
- const t = (process.env.WORKER_DATABASE_TYPE || "upstash-redis").toLowerCase();
445
- return t === "mongodb" ? "mongodb" : "upstash-redis";
446
- }
447
- function preferMongo() {
448
- return getStoreType() === "mongodb" && Boolean(mongoUri?.trim());
449
- }
450
- function preferRedis() {
451
- return getStoreType() !== "mongodb" && Boolean((redisUrl2 || "").trim() && (redisToken2 || "").trim());
452
- }
453
- async function upsertInitialQueueJob(options) {
454
- const { queueJobId, queueId, firstWorkerId, firstWorkerJobId, metadata } = options;
455
- const now = (/* @__PURE__ */ new Date()).toISOString();
456
- if (preferMongo()) {
457
- const coll = await getMongoQueueCollection();
458
- const existing = await coll.findOne({ _id: queueJobId });
459
- if (existing) {
460
- const steps = existing.steps ?? [];
461
- if (steps.length === 0) {
462
- steps.push({
463
- workerId: firstWorkerId,
464
- workerJobId: firstWorkerJobId,
465
- status: "queued"
466
- });
467
- }
468
- await coll.updateOne(
469
- { _id: queueJobId },
470
- {
471
- $set: {
472
- steps,
473
- updatedAt: now
474
- }
475
- }
476
- );
477
- } else {
478
- const doc = {
479
- _id: queueJobId,
480
- id: queueJobId,
481
- queueId,
482
- status: "running",
483
- steps: [
484
- {
485
- workerId: firstWorkerId,
486
- workerJobId: firstWorkerJobId,
487
- status: "queued"
488
- }
489
- ],
490
- metadata: metadata ?? {},
491
- createdAt: now,
492
- updatedAt: now
493
- };
494
- await coll.updateOne(
495
- { _id: queueJobId },
496
- { $set: doc },
497
- { upsert: true }
498
- );
499
- }
500
- return;
501
- }
502
- if (preferRedis()) {
503
- const existing = await loadQueueJobRedis(queueJobId);
504
- if (existing) {
505
- if (!existing.steps || existing.steps.length === 0) {
506
- existing.steps = [
507
- {
508
- workerId: firstWorkerId,
509
- workerJobId: firstWorkerJobId,
510
- status: "queued"
511
- }
512
- ];
513
- }
514
- existing.updatedAt = now;
515
- await saveQueueJobRedis(existing);
516
- } else {
517
- const record = {
518
- id: queueJobId,
519
- queueId,
520
- status: "running",
521
- steps: [
522
- {
523
- workerId: firstWorkerId,
524
- workerJobId: firstWorkerJobId,
525
- status: "queued"
526
- }
527
- ],
528
- metadata: metadata ?? {},
529
- createdAt: now,
530
- updatedAt: now
531
- };
532
- await saveQueueJobRedis(record);
533
- }
534
- }
535
- }
536
- async function updateQueueJobStepInStore(options) {
537
- const { queueJobId, stepIndex, status, input, output, error } = options;
538
- const now = (/* @__PURE__ */ new Date()).toISOString();
539
- if (preferMongo()) {
540
- const coll = await getMongoQueueCollection();
541
- const existing = await coll.findOne({ _id: queueJobId });
542
- if (!existing) return;
543
- const step = existing.steps[stepIndex];
544
- if (!step) return;
545
- const mergedStep = {
546
- ...step,
547
- status,
548
- ...input !== void 0 && { input },
549
- ...output !== void 0 && { output },
550
- ...error !== void 0 && { error },
551
- startedAt: step.startedAt ?? (status === "running" ? now : step.startedAt),
552
- completedAt: step.completedAt ?? (status === "completed" || status === "failed" ? now : step.completedAt)
553
- };
554
- const setDoc = {
555
- steps: existing.steps,
556
- updatedAt: now
557
- };
558
- setDoc.steps[stepIndex] = mergedStep;
559
- if (status === "failed") {
560
- setDoc.status = "failed";
561
- if (!existing.completedAt) setDoc.completedAt = now;
562
- } else if (status === "completed" && stepIndex === existing.steps.length - 1) {
563
- setDoc.status = "completed";
564
- if (!existing.completedAt) setDoc.completedAt = now;
565
- }
566
- await coll.updateOne(
567
- { _id: queueJobId },
568
- {
569
- $set: setDoc
337
+ // src/handler.ts
338
+ var SQS_MAX_DELAY_SECONDS = 900;
339
+ function createWorkerLogger(jobId, workerId) {
340
+ const prefix = (level) => `[${level}] [${workerId}] [${jobId}]`;
341
+ return {
342
+ info(msg, data) {
343
+ console.log(prefix("INFO"), msg, data !== void 0 ? JSON.stringify(data) : "");
344
+ },
345
+ warn(msg, data) {
346
+ console.warn(prefix("WARN"), msg, data !== void 0 ? JSON.stringify(data) : "");
347
+ },
348
+ error(msg, data) {
349
+ console.error(prefix("ERROR"), msg, data !== void 0 ? JSON.stringify(data) : "");
350
+ },
351
+ debug(msg, data) {
352
+ if (process.env.DEBUG || process.env.WORKER_DEBUG) {
353
+ console.debug(prefix("DEBUG"), msg, data !== void 0 ? JSON.stringify(data) : "");
570
354
  }
571
- );
572
- return;
573
- }
574
- if (preferRedis()) {
575
- const existing = await loadQueueJobRedis(queueJobId);
576
- if (!existing) {
577
- return;
578
355
  }
579
- const steps = existing.steps || [];
580
- const step = steps[stepIndex];
581
- if (!step) {
582
- return;
583
- }
584
- step.status = status;
585
- if (input !== void 0) step.input = input;
586
- if (output !== void 0) step.output = output;
587
- if (error !== void 0) step.error = error;
588
- if (status === "running") {
589
- step.startedAt = step.startedAt ?? now;
590
- }
591
- if (status === "completed" || status === "failed") {
592
- step.completedAt = step.completedAt ?? now;
593
- }
594
- existing.steps = steps;
595
- existing.updatedAt = now;
596
- if (status === "failed") {
597
- existing.status = "failed";
598
- existing.completedAt = existing.completedAt ?? now;
599
- } else if (status === "completed" && stepIndex === steps.length - 1) {
600
- existing.status = "completed";
601
- existing.completedAt = existing.completedAt ?? now;
602
- }
603
- await saveQueueJobRedis(existing);
604
- }
605
- }
606
- async function appendQueueJobStepInStore(options) {
607
- const { queueJobId, workerId, workerJobId } = options;
608
- const now = (/* @__PURE__ */ new Date()).toISOString();
609
- if (preferMongo()) {
610
- const coll = await getMongoQueueCollection();
611
- await coll.updateOne(
612
- { _id: queueJobId },
613
- {
614
- $push: {
615
- steps: {
616
- workerId,
617
- workerJobId,
618
- status: "queued"
619
- }
620
- },
621
- $set: { updatedAt: now }
622
- }
623
- );
624
- return;
625
- }
626
- if (preferRedis()) {
627
- const existing = await loadQueueJobRedis(queueJobId);
628
- if (!existing) return;
629
- const steps = existing.steps || [];
630
- steps.push({
631
- workerId,
632
- workerJobId,
633
- status: "queued"
634
- });
635
- existing.steps = steps;
636
- existing.updatedAt = now;
637
- await saveQueueJobRedis(existing);
638
- }
356
+ };
639
357
  }
640
-
641
- // src/handler.ts
642
- var SQS_MAX_DELAY_SECONDS = 900;
643
358
  var WORKER_QUEUE_KEY = "__workerQueue";
644
359
  async function notifyQueueJobStep(queueJobId, action, params) {
645
360
  try {
@@ -672,14 +387,13 @@ async function notifyQueueJobStep(queueJobId, action, params) {
672
387
  output: params.output,
673
388
  error: params.error
674
389
  });
675
- if (process.env.DEBUG_WORKER_QUEUES === "1") {
676
- console.log("[Worker] Queue job step updated", {
677
- queueJobId,
678
- action,
679
- stepIndex: params.stepIndex,
680
- status
681
- });
682
- }
390
+ console.log("[Worker] Queue job step updated", {
391
+ queueId: params.queueId ?? queueJobId,
392
+ queueJobId,
393
+ stepIndex: params.stepIndex,
394
+ workerId: params.workerId,
395
+ status
396
+ });
683
397
  } catch (err) {
684
398
  console.warn("[Worker] Queue job update error:", {
685
399
  queueJobId,
@@ -696,20 +410,52 @@ function wrapHandlerForQueue(handler, queueRuntime) {
696
410
  return output;
697
411
  }
698
412
  const { id: queueId, stepIndex, initialInput, queueJobId } = queueContext;
413
+ const jobId = params.ctx?.jobId;
414
+ const workerId = params.ctx?.workerId ?? "";
699
415
  const next = queueRuntime.getNextStep(queueId, stepIndex);
700
- if (!next) {
701
- return output;
702
- }
703
- const childJobId = `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
704
- if (queueJobId) {
416
+ const childJobId = next ? `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}` : void 0;
417
+ if (next && queueJobId) {
705
418
  await notifyQueueJobStep(queueJobId, "append", {
706
419
  workerJobId: childJobId,
707
420
  workerId: next.workerId
708
421
  });
709
422
  }
423
+ if (queueJobId && typeof stepIndex === "number") {
424
+ await notifyQueueJobStep(queueJobId, "complete", {
425
+ queueId,
426
+ stepIndex,
427
+ workerJobId: jobId,
428
+ workerId,
429
+ output
430
+ });
431
+ }
432
+ if (!next) {
433
+ return output;
434
+ }
710
435
  let nextInput = output;
711
436
  if (next.mapInputFromPrev && typeof queueRuntime.invokeMapInput === "function") {
712
- nextInput = await queueRuntime.invokeMapInput(queueId, stepIndex + 1, output, initialInput);
437
+ let previousOutputs = [];
438
+ if (queueJobId && typeof queueRuntime.getQueueJob === "function") {
439
+ try {
440
+ const job = await queueRuntime.getQueueJob(queueJobId);
441
+ if (job?.steps) {
442
+ const fromStore = job.steps.slice(0, stepIndex).map((s, i) => ({ stepIndex: i, workerId: s.workerId, output: s.output }));
443
+ previousOutputs = fromStore.concat([
444
+ { stepIndex, workerId: params.ctx?.workerId ?? "", output }
445
+ ]);
446
+ }
447
+ } catch (e) {
448
+ if (process.env.AI_WORKER_QUEUES_DEBUG === "1") {
449
+ console.warn("[Worker] getQueueJob failed, mapping without previousOutputs:", e?.message ?? e);
450
+ }
451
+ }
452
+ }
453
+ nextInput = await queueRuntime.invokeMapInput(
454
+ queueId,
455
+ stepIndex + 1,
456
+ initialInput,
457
+ previousOutputs
458
+ );
713
459
  }
714
460
  const nextInputWithQueue = {
715
461
  ...nextInput !== null && typeof nextInput === "object" ? nextInput : { value: nextInput },
@@ -888,6 +634,7 @@ function createLambdaHandler(handler, outputSchema) {
888
634
  const handlerContext = {
889
635
  ...baseContext,
890
636
  ...jobStore ? { jobStore } : {},
637
+ logger: createWorkerLogger(jobId, workerId),
891
638
  dispatchWorker: createDispatchWorker(
892
639
  jobId,
893
640
  workerId,
@@ -898,9 +645,12 @@ function createLambdaHandler(handler, outputSchema) {
898
645
  if (jobStore) {
899
646
  try {
900
647
  await jobStore.update({ status: "running" });
648
+ const queueCtxForLog = input?.__workerQueue ?? metadata?.__workerQueue;
901
649
  console.log("[Worker] Job status updated to running:", {
902
650
  jobId,
903
- workerId
651
+ workerId,
652
+ ...queueCtxForLog?.id && { queueId: queueCtxForLog.id },
653
+ ...queueCtxForLog?.queueJobId && { queueJobId: queueCtxForLog.queueJobId }
904
654
  });
905
655
  } catch (error) {
906
656
  console.warn("[Worker] Failed to update status to running:", {
@@ -930,6 +680,7 @@ function createLambdaHandler(handler, outputSchema) {
930
680
  }
931
681
  }
932
682
  await notifyQueueJobStep(queueCtx.queueJobId, "start", {
683
+ queueId: queueCtx.id,
933
684
  stepIndex: queueCtx.stepIndex,
934
685
  workerJobId: jobId,
935
686
  workerId,
@@ -978,6 +729,7 @@ function createLambdaHandler(handler, outputSchema) {
978
729
  const queueCtxFail = input?.__workerQueue ?? metadata?.__workerQueue;
979
730
  if (queueCtxFail?.queueJobId && typeof queueCtxFail.stepIndex === "number") {
980
731
  await notifyQueueJobStep(queueCtxFail.queueJobId, "fail", {
732
+ queueId: queueCtxFail.id,
981
733
  stepIndex: queueCtxFail.stepIndex,
982
734
  workerJobId: jobId,
983
735
  workerId,
@@ -1007,15 +759,6 @@ function createLambdaHandler(handler, outputSchema) {
1007
759
  });
1008
760
  }
1009
761
  }
1010
- const queueCtxSuccess = input?.__workerQueue ?? metadata?.__workerQueue;
1011
- if (queueCtxSuccess?.queueJobId && typeof queueCtxSuccess.stepIndex === "number") {
1012
- await notifyQueueJobStep(queueCtxSuccess.queueJobId, "complete", {
1013
- stepIndex: queueCtxSuccess.stepIndex,
1014
- workerJobId: jobId,
1015
- workerId,
1016
- output
1017
- });
1018
- }
1019
762
  console.log("[Worker] Job completed:", {
1020
763
  jobId,
1021
764
  workerId,
@@ -1047,7 +790,8 @@ function createLambdaHandler(handler, outputSchema) {
1047
790
 
1048
791
  export {
1049
792
  SQS_MAX_DELAY_SECONDS,
793
+ createWorkerLogger,
1050
794
  wrapHandlerForQueue,
1051
795
  createLambdaHandler
1052
796
  };
1053
- //# sourceMappingURL=chunk-4WU5ZCHS.mjs.map
797
+ //# sourceMappingURL=chunk-7LQNS2SG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handler.ts","../src/mongoJobStore.ts","../src/redisJobStore.ts"],"sourcesContent":["/**\n * Generic Lambda handler wrapper for worker agents.\n * Handles SQS events, executes user handlers, and sends webhook callbacks.\n * Job store: MongoDB only. Never uses HTTP/origin URL for job updates.\n */\n\nimport type { SQSEvent, SQSRecord, Context as LambdaContext } from 'aws-lambda';\nimport type { ZodType } from 'zod';\nimport { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';\nimport {\n createMongoJobStore,\n upsertJob,\n isMongoJobStoreConfigured,\n getJobById as getMongoJobById,\n} from './mongoJobStore';\nimport {\n createRedisJobStore,\n upsertRedisJob,\n isRedisJobStoreConfigured,\n loadJob as loadRedisJob,\n} from './redisJobStore';\nimport {\n appendQueueJobStepInStore,\n updateQueueJobStepInStore,\n upsertInitialQueueJob,\n getQueueJob,\n} from './queueJobStore';\n\nexport interface JobStoreUpdate {\n status?: 'queued' | 'running' | 'completed' | 'failed';\n metadata?: Record<string, any>;\n progress?: number;\n progressMessage?: string;\n output?: any;\n error?: {\n message: string;\n stack?: string;\n name?: string;\n };\n}\n\nexport interface JobRecord {\n jobId: string;\n workerId: string;\n status: 'queued' | 'running' | 'completed' | 'failed';\n input: any;\n output?: any;\n error?: { message: string; stack?: string };\n metadata?: Record<string, any>;\n internalJobs?: Array<{ jobId: string; workerId: string }>;\n createdAt: string;\n updatedAt: string;\n completedAt?: string;\n}\n\nexport interface JobStore {\n /**\n * Update job in job store.\n * @param update - Update object with status, metadata, progress, output, or error\n */\n update(update: JobStoreUpdate): Promise<void>;\n /**\n * Get current job record from job store.\n * @returns Job record or null if not found\n */\n get(): Promise<JobRecord | null>;\n /**\n * Append an internal (child) job to the current job's internalJobs list.\n * Used when this worker dispatches another worker (fire-and-forget or await).\n */\n appendInternalJob?(entry: { jobId: string; workerId: string }): Promise<void>;\n /**\n * Get any job by jobId (e.g. to poll child job status when await: true).\n * @returns Job record or null if not found\n */\n getJob?(jobId: string): Promise<JobRecord | null>;\n}\n\n/** Max SQS delay in seconds (AWS limit). */\nexport const SQS_MAX_DELAY_SECONDS = 900;\n\n/** Options for ctx.dispatchWorker (worker-to-worker). */\nexport interface DispatchWorkerOptions {\n webhookUrl?: string;\n metadata?: Record<string, any>;\n /** Optional job ID for the child job (default: generated). */\n jobId?: string;\n /** If true, poll job store until child completes or fails; otherwise fire-and-forget. */\n await?: boolean;\n pollIntervalMs?: number;\n pollTimeoutMs?: number;\n /**\n * Delay before the child is invoked (fire-and-forget only; ignored when await is true).\n * Uses SQS DelaySeconds (0–900). In local mode, waits this many seconds before sending the trigger request.\n */\n delaySeconds?: number;\n}\n\n/**\n * Logger provided on ctx with prefixed levels: [INFO], [WARN], [ERROR], [DEBUG].\n * Each method accepts a message and optional data (logged as JSON).\n */\nexport interface WorkerLogger {\n info(message: string, data?: Record<string, unknown>): void;\n warn(message: string, data?: Record<string, unknown>): void;\n error(message: string, data?: Record<string, unknown>): void;\n debug(message: string, data?: Record<string, unknown>): void;\n}\n\nexport function createWorkerLogger(jobId: string, workerId: string): WorkerLogger {\n const prefix = (level: string) => `[${level}] [${workerId}] [${jobId}]`;\n return {\n info(msg: string, data?: Record<string, unknown>) {\n console.log(prefix('INFO'), msg, data !== undefined ? JSON.stringify(data) : '');\n },\n warn(msg: string, data?: Record<string, unknown>) {\n console.warn(prefix('WARN'), msg, data !== undefined ? JSON.stringify(data) : '');\n },\n error(msg: string, data?: Record<string, unknown>) {\n console.error(prefix('ERROR'), msg, data !== undefined ? JSON.stringify(data) : '');\n },\n debug(msg: string, data?: Record<string, unknown>) {\n if (process.env.DEBUG || process.env.WORKER_DEBUG) {\n console.debug(prefix('DEBUG'), msg, data !== undefined ? JSON.stringify(data) : '');\n }\n },\n };\n}\n\nexport interface WorkerHandlerParams<INPUT, OUTPUT> {\n input: INPUT;\n ctx: {\n jobId: string;\n workerId: string;\n requestId?: string;\n /**\n * Job store interface for updating and retrieving job state.\n * Uses MongoDB directly when configured; never HTTP/origin URL.\n */\n jobStore?: JobStore;\n /**\n * Logger with prefixed levels: ctx.logger.info(), .warn(), .error(), .debug().\n */\n logger: WorkerLogger;\n /**\n * Dispatch another worker (fire-and-forget or await). Uses WORKER_QUEUE_URL_<SANITIZED_ID> env.\n * Always provided by the runtime (Lambda and local).\n */\n dispatchWorker: (\n workerId: string,\n input: unknown,\n options?: DispatchWorkerOptions\n ) => Promise<{ jobId: string; messageId?: string; output?: unknown }>;\n [key: string]: any;\n };\n}\n\nexport type WorkerHandler<INPUT, OUTPUT> = (\n params: WorkerHandlerParams<INPUT, OUTPUT>\n) => Promise<OUTPUT>;\n\n/** Result of getNextStep for queue chaining. */\nexport interface QueueNextStep {\n workerId: string;\n delaySeconds?: number;\n mapInputFromPrev?: string;\n}\n\n/** One previous step's output (for mapInputFromPrev context). */\nexport interface QueueStepOutput {\n stepIndex: number;\n workerId: string;\n output: unknown;\n}\n\n/** Runtime helpers for queue-aware wrappers (provided by generated registry). */\nexport interface QueueRuntime {\n getNextStep(queueId: string, stepIndex: number): QueueNextStep | undefined;\n /** Optional: when provided, mapping can use outputs from any previous step. */\n getQueueJob?(queueJobId: string): Promise<{ steps: Array<{ workerId: string; output?: unknown }> } | null>;\n /** (initialInput, previousOutputs) – previousOutputs includes outputs for steps 0..stepIndex-1 and current step. */\n invokeMapInput?(\n queueId: string,\n stepIndex: number,\n initialInput: unknown,\n previousOutputs: QueueStepOutput[]\n ): Promise<unknown> | unknown;\n}\n\nconst WORKER_QUEUE_KEY = '__workerQueue';\nasync function notifyQueueJobStep(\n queueJobId: string,\n action: 'start' | 'complete' | 'fail' | 'append',\n params: {\n stepIndex?: number;\n workerJobId: string;\n workerId?: string;\n output?: unknown;\n error?: { message: string };\n input?: unknown;\n queueId?: string;\n }\n): Promise<void> {\n try {\n if (action === 'append') {\n if (!params.workerId || !params.workerJobId) return;\n await appendQueueJobStepInStore({\n queueJobId,\n workerId: params.workerId,\n workerJobId: params.workerJobId,\n });\n if (process.env.DEBUG_WORKER_QUEUES === '1') {\n console.log('[Worker] Queue job step appended', {\n queueJobId,\n workerId: params.workerId,\n workerJobId: params.workerJobId,\n });\n }\n return;\n }\n\n if (params.stepIndex === undefined) return;\n\n const status =\n action === 'start'\n ? 'running'\n : action === 'complete'\n ? 'completed'\n : action === 'fail'\n ? 'failed'\n : undefined;\n if (!status) return;\n\n await updateQueueJobStepInStore({\n queueJobId,\n stepIndex: params.stepIndex,\n workerId: params.workerId || '',\n workerJobId: params.workerJobId,\n status,\n input: params.input,\n output: params.output,\n error: params.error,\n });\n // Always log queue step updates so logs show which queue and step ran\n console.log('[Worker] Queue job step updated', {\n queueId: params.queueId ?? queueJobId,\n queueJobId,\n stepIndex: params.stepIndex,\n workerId: params.workerId,\n status,\n });\n } catch (err: any) {\n console.warn('[Worker] Queue job update error:', {\n queueJobId,\n action,\n error: err?.message ?? String(err),\n });\n }\n}\n\n/**\n * Wraps a user handler so that when the job has __workerQueue context (from\n * dispatchQueue or queue cron), it dispatches the next worker in the sequence\n * after the handler completes. Uses literal worker IDs so the CLI env injection\n * picks up WORKER_QUEUE_URL_* for next-step workers.\n */\nexport function wrapHandlerForQueue<INPUT, OUTPUT>(\n handler: WorkerHandler<INPUT, OUTPUT>,\n queueRuntime: QueueRuntime\n): WorkerHandler<INPUT & { __workerQueue?: { id: string; stepIndex: number; initialInput: unknown; queueJobId?: string } }, OUTPUT> {\n return async (params) => {\n const queueContext = (params.input as any)?.[WORKER_QUEUE_KEY];\n const output = await handler(params);\n\n if (!queueContext || typeof queueContext !== 'object' || !queueContext.id) {\n return output;\n }\n\n const { id: queueId, stepIndex, initialInput, queueJobId } = queueContext;\n const jobId = (params.ctx as any)?.jobId;\n const workerId = (params.ctx as any)?.workerId ?? '';\n\n const next = queueRuntime.getNextStep(queueId, stepIndex);\n const childJobId = next ? `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}` : undefined;\n if (next && queueJobId) {\n // Append next step first so updateQueueJobStepInStore(complete) sees steps.length > 1\n await notifyQueueJobStep(queueJobId, 'append', {\n workerJobId: childJobId!,\n workerId: next.workerId,\n });\n }\n\n // Notify current step complete (after append when there's next, so queue isn't marked completed yet)\n if (queueJobId && typeof stepIndex === 'number') {\n await notifyQueueJobStep(queueJobId, 'complete', {\n queueId,\n stepIndex,\n workerJobId: jobId,\n workerId,\n output,\n });\n }\n\n if (!next) {\n return output;\n }\n\n let nextInput: unknown = output;\n if (next.mapInputFromPrev && typeof queueRuntime.invokeMapInput === 'function') {\n let previousOutputs: QueueStepOutput[] = [];\n if (queueJobId && typeof queueRuntime.getQueueJob === 'function') {\n try {\n const job = await queueRuntime.getQueueJob(queueJobId);\n if (job?.steps) {\n const fromStore = job.steps\n .slice(0, stepIndex)\n .map((s, i) => ({ stepIndex: i, workerId: s.workerId, output: s.output }));\n previousOutputs = fromStore.concat([\n { stepIndex, workerId: (params.ctx as any)?.workerId ?? '', output },\n ]);\n }\n } catch (e: any) {\n if (process.env.AI_WORKER_QUEUES_DEBUG === '1') {\n console.warn('[Worker] getQueueJob failed, mapping without previousOutputs:', e?.message ?? e);\n }\n }\n }\n nextInput = await queueRuntime.invokeMapInput(\n queueId,\n stepIndex + 1,\n initialInput,\n previousOutputs\n );\n }\n\n const nextInputWithQueue = {\n ...(nextInput !== null && typeof nextInput === 'object' ? (nextInput as Record<string, unknown>) : { value: nextInput }),\n [WORKER_QUEUE_KEY]: {\n id: queueId,\n stepIndex: stepIndex + 1,\n initialInput,\n queueJobId,\n },\n };\n\n const debug = process.env.AI_WORKER_QUEUES_DEBUG === '1';\n if (debug) {\n console.log('[Worker] Queue chain dispatching next:', {\n queueId,\n fromStep: stepIndex,\n nextWorkerId: next.workerId,\n delaySeconds: next.delaySeconds,\n });\n }\n\n await params.ctx.dispatchWorker(next.workerId, nextInputWithQueue, {\n await: false,\n delaySeconds: next.delaySeconds,\n jobId: childJobId,\n });\n\n return output;\n };\n}\n\nexport interface SQSMessageBody {\n workerId: string;\n jobId: string;\n input: any;\n context: Record<string, any>;\n webhookUrl?: string;\n /** @deprecated Never use. Job updates use MongoDB only. */\n jobStoreUrl?: string;\n metadata?: Record<string, any>;\n timestamp: string;\n}\n\nexport interface WebhookPayload {\n jobId: string;\n workerId: string;\n status: 'success' | 'error';\n output?: any;\n error?: {\n message: string;\n stack?: string;\n name?: string;\n };\n metadata?: Record<string, any>;\n}\n\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\nconst DEFAULT_POLL_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes\n\nfunction sanitizeWorkerIdForEnv(workerId: string): string {\n return workerId.replace(/-/g, '_').toUpperCase();\n}\n\nfunction getQueueUrlForWorker(calleeWorkerId: string): string | undefined {\n const key = `WORKER_QUEUE_URL_${sanitizeWorkerIdForEnv(calleeWorkerId)}`;\n return process.env[key]?.trim() || undefined;\n}\n\n/**\n * Create dispatchWorker for use in handler context (Lambda).\n * Sends message to SQS, appends to parent internalJobs, optionally polls until child completes.\n */\nfunction createDispatchWorker(\n parentJobId: string,\n parentWorkerId: string,\n parentContext: Record<string, any>,\n jobStore: JobStore | undefined\n): (\n workerId: string,\n input: unknown,\n options?: DispatchWorkerOptions\n) => Promise<{ jobId: string; messageId?: string; output?: unknown }> {\n return async (\n calleeWorkerId: string,\n input: unknown,\n options?: DispatchWorkerOptions\n ): Promise<{ jobId: string; messageId?: string; output?: unknown }> => {\n const childJobId =\n options?.jobId ||\n `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n const metadata = options?.metadata ?? {};\n const serializedContext: Record<string, any> = {};\n if (parentContext.requestId) serializedContext.requestId = parentContext.requestId;\n\n const messageBody: SQSMessageBody = {\n workerId: calleeWorkerId,\n jobId: childJobId,\n input: input ?? {},\n context: serializedContext,\n webhookUrl: options?.webhookUrl,\n metadata,\n timestamp: new Date().toISOString(),\n };\n\n const queueUrl = getQueueUrlForWorker(calleeWorkerId);\n\n if (queueUrl) {\n const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-1';\n const sqs = new SQSClient({ region });\n // SQS message timer (per-message DelaySeconds): message stays invisible for N seconds.\n // Calling worker returns immediately; no computation during delay. See:\n // https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-delay-queues.html\n const delaySeconds =\n options?.await !== true && options?.delaySeconds != null\n ? Math.min(SQS_MAX_DELAY_SECONDS, Math.max(0, Math.floor(options.delaySeconds)))\n : undefined;\n const sendResult = await sqs.send(\n new SendMessageCommand({\n QueueUrl: queueUrl,\n MessageBody: JSON.stringify(messageBody),\n ...(delaySeconds !== undefined && delaySeconds > 0 ? { DelaySeconds: delaySeconds } : {}),\n })\n );\n const messageId = sendResult.MessageId ?? undefined;\n\n if (jobStore?.appendInternalJob) {\n await jobStore.appendInternalJob({ jobId: childJobId, workerId: calleeWorkerId });\n }\n\n if (options?.await && jobStore?.getJob) {\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const pollTimeoutMs = options.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;\n const deadline = Date.now() + pollTimeoutMs;\n while (Date.now() < deadline) {\n const child = await jobStore.getJob(childJobId);\n if (!child) {\n await new Promise((r) => setTimeout(r, pollIntervalMs));\n continue;\n }\n if (child.status === 'completed') {\n return { jobId: childJobId, messageId, output: child.output };\n }\n if (child.status === 'failed') {\n const err = child.error;\n throw new Error(\n err?.message ?? `Child worker ${calleeWorkerId} failed`\n );\n }\n await new Promise((r) => setTimeout(r, pollIntervalMs));\n }\n throw new Error(\n `Child worker ${calleeWorkerId} (${childJobId}) did not complete within ${pollTimeoutMs}ms`\n );\n }\n\n return { jobId: childJobId, messageId };\n }\n\n // Fallback: no queue URL (e.g. local dev). Caller (index.ts) should provide in-process dispatch.\n throw new Error(\n `WORKER_QUEUE_URL_${sanitizeWorkerIdForEnv(calleeWorkerId)} is not set. ` +\n 'Configure queue URL for worker-to-worker dispatch, or run in local mode.'\n );\n };\n}\n\n/**\n * Sends a webhook callback to the specified URL.\n */\nasync function sendWebhook(\n webhookUrl: string,\n payload: WebhookPayload\n): Promise<void> {\n try {\n const response = await fetch(webhookUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'User-Agent': 'ai-router-worker/1.0',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => '');\n console.error('[Worker] Webhook callback failed:', {\n url: webhookUrl,\n status: response.status,\n statusText: response.statusText,\n errorText,\n });\n // Don't throw - webhook failures shouldn't fail the Lambda\n } else {\n console.log('[Worker] Webhook callback successful:', {\n url: webhookUrl,\n status: response.status,\n });\n }\n } catch (error: any) {\n console.error('[Worker] Webhook callback error:', {\n url: webhookUrl,\n error: error?.message || String(error),\n stack: error?.stack,\n });\n // Don't throw - webhook failures shouldn't fail the Lambda\n }\n}\n\n/**\n * Creates a Lambda handler function that processes SQS events for workers.\n * Job store: MongoDB only. Never uses HTTP/origin URL for job updates.\n *\n * @param handler - The user's worker handler function\n * @param outputSchema - Optional Zod schema for output validation\n * @returns A Lambda handler function\n */\nexport function createLambdaHandler<INPUT, OUTPUT>(\n handler: WorkerHandler<INPUT, OUTPUT>,\n outputSchema?: ZodType<OUTPUT>\n): (event: SQSEvent, context: LambdaContext) => Promise<void> {\n return async (event: SQSEvent, lambdaContext: LambdaContext) => {\n const promises = event.Records.map(async (record: SQSRecord) => {\n let messageBody: SQSMessageBody | null = null;\n try {\n messageBody = JSON.parse(record.body) as SQSMessageBody;\n\n const { workerId, jobId, input, context, webhookUrl, metadata = {} } =\n messageBody;\n\n // Idempotency: skip if this job was already completed or failed (e.g. SQS redelivery or duplicate trigger).\n // Only the Lambda that processes a message creates/updates that job's key; parent workers only append to internalJobs and poll – they never write child job documents.\n const raw = (process.env.WORKER_DATABASE_TYPE || 'upstash-redis').toLowerCase();\n const jobStoreType: 'mongodb' | 'upstash-redis' =\n raw === 'mongodb' ? 'mongodb' : 'upstash-redis';\n if (jobStoreType === 'upstash-redis' && isRedisJobStoreConfigured()) {\n const existing = await loadRedisJob(jobId);\n if (existing && (existing.status === 'completed' || existing.status === 'failed')) {\n console.log('[Worker] Skipping already terminal job (idempotent):', {\n jobId,\n workerId,\n status: existing.status,\n });\n return;\n }\n } else if (jobStoreType === 'mongodb' || isMongoJobStoreConfigured()) {\n const existing = await getMongoJobById(jobId);\n if (existing && (existing.status === 'completed' || existing.status === 'failed')) {\n console.log('[Worker] Skipping already terminal job (idempotent):', {\n jobId,\n workerId,\n status: existing.status,\n });\n return;\n }\n }\n\n // Select job store and upsert this message's job only (never write child job documents from parent).\n let jobStore: JobStore | undefined;\n if (\n jobStoreType === 'upstash-redis' &&\n isRedisJobStoreConfigured()\n ) {\n await upsertRedisJob(jobId, workerId, input, metadata);\n jobStore = createRedisJobStore(workerId, jobId, input, metadata);\n } else if (\n jobStoreType === 'mongodb' ||\n isMongoJobStoreConfigured()\n ) {\n await upsertJob(jobId, workerId, input, metadata);\n jobStore = createMongoJobStore(workerId, jobId, input, metadata);\n }\n\n const baseContext = {\n jobId,\n workerId,\n requestId: context.requestId || lambdaContext.awsRequestId,\n ...context,\n };\n const handlerContext = {\n ...baseContext,\n ...(jobStore ? { jobStore } : {}),\n logger: createWorkerLogger(jobId, workerId),\n dispatchWorker: createDispatchWorker(\n jobId,\n workerId,\n baseContext,\n jobStore\n ),\n };\n\n if (jobStore) {\n try {\n await jobStore.update({ status: 'running' });\n const queueCtxForLog = (input as any)?.__workerQueue ?? metadata?.__workerQueue;\n console.log('[Worker] Job status updated to running:', {\n jobId,\n workerId,\n ...(queueCtxForLog?.id && { queueId: queueCtxForLog.id }),\n ...(queueCtxForLog?.queueJobId && { queueJobId: queueCtxForLog.queueJobId }),\n });\n } catch (error: any) {\n console.warn('[Worker] Failed to update status to running:', {\n jobId,\n workerId,\n error: error?.message || String(error),\n });\n }\n }\n\n const queueCtx = (input as any)?.__workerQueue ?? metadata?.__workerQueue;\n if (queueCtx?.queueJobId && typeof queueCtx.stepIndex === 'number') {\n // Ensure initial queue job exists (mainly for cron/queue-starter paths)\n if (queueCtx.stepIndex === 0) {\n try {\n await upsertInitialQueueJob({\n queueJobId: queueCtx.queueJobId,\n queueId: queueCtx.id,\n firstWorkerId: workerId,\n firstWorkerJobId: jobId,\n metadata,\n });\n } catch (e: any) {\n console.warn('[Worker] Failed to upsert initial queue job:', {\n queueJobId: queueCtx.queueJobId,\n queueId: queueCtx.id,\n error: e?.message ?? String(e),\n });\n }\n }\n await notifyQueueJobStep(queueCtx.queueJobId, 'start', {\n queueId: queueCtx.id,\n stepIndex: queueCtx.stepIndex,\n workerJobId: jobId,\n workerId,\n input,\n });\n }\n\n let output: OUTPUT;\n try {\n output = await handler({\n input: input as INPUT,\n ctx: handlerContext,\n });\n\n if (outputSchema) {\n output = outputSchema.parse(output);\n }\n } catch (error: any) {\n const errorPayload: WebhookPayload = {\n jobId,\n workerId,\n status: 'error',\n error: {\n message: error.message || 'Unknown error',\n stack: error.stack,\n name: error.name || 'Error',\n },\n metadata,\n };\n\n if (jobStore) {\n try {\n await jobStore.update({\n status: 'failed',\n error: errorPayload.error,\n });\n console.log('[Worker] Job status updated to failed:', {\n jobId,\n workerId,\n });\n } catch (updateError: any) {\n console.warn('[Worker] Failed to update job store on error:', {\n jobId,\n workerId,\n error: updateError?.message || String(updateError),\n });\n }\n }\n\n const queueCtxFail = (input as any)?.__workerQueue ?? metadata?.__workerQueue;\n if (queueCtxFail?.queueJobId && typeof queueCtxFail.stepIndex === 'number') {\n await notifyQueueJobStep(queueCtxFail.queueJobId, 'fail', {\n queueId: queueCtxFail.id,\n stepIndex: queueCtxFail.stepIndex,\n workerJobId: jobId,\n workerId,\n error: errorPayload.error,\n });\n }\n\n if (webhookUrl) {\n await sendWebhook(webhookUrl, errorPayload);\n }\n throw error;\n }\n\n if (jobStore) {\n try {\n await jobStore.update({\n status: 'completed',\n output,\n });\n console.log('[Worker] Job status updated to completed:', {\n jobId,\n workerId,\n });\n } catch (updateError: any) {\n console.warn('[Worker] Failed to update job store on success:', {\n jobId,\n workerId,\n error: updateError?.message || String(updateError),\n });\n }\n }\n\n // Queue step complete is notified from wrapHandlerForQueue (after append) so one DB update marks step + queue.\n\n console.log('[Worker] Job completed:', {\n jobId,\n workerId,\n output,\n });\n\n const successPayload: WebhookPayload = {\n jobId,\n workerId,\n status: 'success',\n output,\n metadata,\n };\n\n if (webhookUrl) {\n await sendWebhook(webhookUrl, successPayload);\n }\n } catch (error: any) {\n console.error('[Worker] Error processing SQS record:', {\n jobId: messageBody?.jobId ?? '(parse failed)',\n workerId: messageBody?.workerId ?? '(parse failed)',\n error: error?.message || String(error),\n stack: error?.stack,\n });\n throw error;\n }\n });\n\n await Promise.all(promises);\n };\n}\n","/**\n * MongoDB-backed job store for Lambda workers.\n * Updates jobs directly in MongoDB; never uses HTTP/origin URL.\n *\n * Env: MONGODB_WORKER_URI (or MONGODB_URI), MONGODB_WORKER_DB (or MONGODB_DB),\n * MONGODB_WORKER_JOBS_COLLECTION (default: worker_jobs).\n */\n\nimport { MongoClient, type Collection } from 'mongodb';\nimport type { JobStore, JobStoreUpdate } from './handler';\n\nconst uri = process.env.MONGODB_WORKER_URI || process.env.DATABASE_MONGODB_URI || process.env.MONGODB_URI;\nconst dbName =\n process.env.MONGODB_WORKER_DB ||\n process.env.MONGODB_DB ||\n 'worker';\nconst collectionName =\n process.env.MONGODB_WORKER_JOBS_COLLECTION || 'worker_jobs';\n\ntype InternalJobEntry = { jobId: string; workerId: string };\n\ntype Doc = {\n _id: string;\n jobId: string;\n workerId: string;\n status: 'queued' | 'running' | 'completed' | 'failed';\n input: any;\n output?: any;\n error?: { message: string; stack?: string; name?: string };\n metadata?: Record<string, any>;\n internalJobs?: InternalJobEntry[];\n createdAt: string;\n updatedAt: string;\n completedAt?: string;\n};\n\nlet clientPromise: Promise<MongoClient> | null = null;\n\nfunction getClient(): Promise<MongoClient> {\n if (!uri) {\n throw new Error(\n 'MongoDB URI required for job store. Set DATABASE_MONGODB_URI or MONGODB_URI.'\n );\n }\n if (!clientPromise) {\n clientPromise = new MongoClient(uri, {\n maxPoolSize: 10,\n minPoolSize: 0,\n serverSelectionTimeoutMS: 10_000,\n }).connect();\n }\n return clientPromise;\n}\n\nasync function getCollection(): Promise<Collection<Doc>> {\n const client = await getClient();\n return client.db(dbName).collection<Doc>(collectionName);\n}\n\n/**\n * Load a job by id (read-only). Used for idempotency check before processing.\n */\nexport async function getJobById(jobId: string): Promise<{\n jobId: string;\n workerId: string;\n status: 'queued' | 'running' | 'completed' | 'failed';\n input: any;\n output?: any;\n error?: { message: string; stack?: string };\n metadata?: Record<string, any>;\n internalJobs?: Array<{ jobId: string; workerId: string }>;\n createdAt: string;\n updatedAt: string;\n completedAt?: string;\n} | null> {\n try {\n const coll = await getCollection();\n const doc = await coll.findOne({ _id: jobId });\n if (!doc) return null;\n const { _id, ...r } = doc;\n return r as any;\n } catch (e: any) {\n console.error('[Worker] MongoDB getJobById failed:', {\n jobId,\n error: e?.message ?? String(e),\n });\n return null;\n }\n}\n\n/**\n * Create a JobStore that reads/writes directly to MongoDB.\n * Caller must ensure the job exists (upsert on first use).\n */\nexport function createMongoJobStore(\n workerId: string,\n jobId: string,\n input: any,\n metadata: Record<string, any>\n): JobStore {\n return {\n update: async (update: JobStoreUpdate): Promise<void> => {\n try {\n const coll = await getCollection();\n const now = new Date().toISOString();\n const existing = await coll.findOne({ _id: jobId });\n\n let metadataUpdate: Record<string, any> = { ...(existing?.metadata ?? {}) };\n if (update.metadata) {\n Object.assign(metadataUpdate, update.metadata);\n }\n if (update.progress !== undefined || update.progressMessage !== undefined) {\n metadataUpdate.progress = update.progress;\n metadataUpdate.progressMessage = update.progressMessage;\n }\n\n const set: Partial<Doc> = {\n updatedAt: now,\n metadata: metadataUpdate,\n };\n if (update.status !== undefined) {\n set.status = update.status;\n if (['completed', 'failed'].includes(update.status) && !existing?.completedAt) {\n set.completedAt = now;\n }\n }\n if (update.output !== undefined) set.output = update.output;\n if (update.error !== undefined) set.error = update.error;\n\n if (existing) {\n await coll.updateOne({ _id: jobId }, { $set: set });\n } else {\n const doc: Doc = {\n _id: jobId,\n jobId,\n workerId,\n status: (update.status as Doc['status']) ?? 'queued',\n input: input ?? {},\n output: update.output,\n error: update.error,\n metadata: metadataUpdate,\n createdAt: now,\n updatedAt: now,\n completedAt: set.completedAt,\n };\n if (doc.status === 'completed' || doc.status === 'failed') {\n doc.completedAt = doc.completedAt ?? now;\n }\n await coll.updateOne({ _id: jobId }, { $set: doc }, { upsert: true });\n }\n } catch (e: any) {\n console.error('[Worker] MongoDB job store update failed:', {\n jobId,\n workerId,\n error: e?.message ?? String(e),\n });\n }\n },\n get: async () => {\n try {\n const coll = await getCollection();\n const doc = await coll.findOne({ _id: jobId });\n if (!doc) return null;\n const { _id, ...r } = doc;\n return r as any;\n } catch (e: any) {\n console.error('[Worker] MongoDB job store get failed:', {\n jobId,\n workerId,\n error: e?.message ?? String(e),\n });\n return null;\n }\n },\n appendInternalJob: async (entry: { jobId: string; workerId: string }): Promise<void> => {\n try {\n const coll = await getCollection();\n await coll.updateOne(\n { _id: jobId },\n { $push: { internalJobs: entry } }\n );\n } catch (e: any) {\n console.error('[Worker] MongoDB job store appendInternalJob failed:', {\n jobId,\n workerId,\n error: e?.message ?? String(e),\n });\n }\n },\n getJob: async (otherJobId: string): Promise<{\n jobId: string;\n workerId: string;\n status: 'queued' | 'running' | 'completed' | 'failed';\n input: any;\n output?: any;\n error?: { message: string; stack?: string };\n metadata?: Record<string, any>;\n internalJobs?: Array<{ jobId: string; workerId: string }>;\n createdAt: string;\n updatedAt: string;\n completedAt?: string;\n } | null> => {\n try {\n const coll = await getCollection();\n const doc = await coll.findOne({ _id: otherJobId });\n if (!doc) return null;\n const { _id, ...r } = doc;\n return r as any;\n } catch (e: any) {\n console.error('[Worker] MongoDB job store getJob failed:', {\n otherJobId,\n error: e?.message ?? String(e),\n });\n return null;\n }\n },\n };\n}\n\n/**\n * Upsert initial job record in MongoDB (queued).\n * Call this when the Lambda starts processing a message.\n */\nexport async function upsertJob(\n jobId: string,\n workerId: string,\n input: any,\n metadata: Record<string, any>\n): Promise<void> {\n const coll = await getCollection();\n const now = new Date().toISOString();\n await coll.updateOne(\n { _id: jobId },\n {\n $set: {\n _id: jobId,\n jobId,\n workerId,\n status: 'queued',\n input: input ?? {},\n metadata: metadata ?? {},\n createdAt: now,\n updatedAt: now,\n },\n },\n { upsert: true }\n );\n}\n\nexport function isMongoJobStoreConfigured(): boolean {\n return Boolean(uri?.trim());\n}\n","import { Redis } from '@upstash/redis';\nimport type { JobStore, JobStoreUpdate, JobRecord } from './handler';\n\n// Canonical: WORKER_* first, then UPSTASH_* / REDIS_* / WORKFLOW_* fallbacks\nconst redisUrl =\n process.env.WORKER_UPSTASH_REDIS_REST_URL ||\n process.env.UPSTASH_REDIS_REST_URL ||\n process.env.UPSTASH_REDIS_URL;\nconst redisToken =\n process.env.WORKER_UPSTASH_REDIS_REST_TOKEN ||\n process.env.UPSTASH_REDIS_REST_TOKEN ||\n process.env.UPSTASH_REDIS_TOKEN;\nconst jobKeyPrefix =\n process.env.WORKER_UPSTASH_REDIS_JOBS_PREFIX ||\n process.env.UPSTASH_REDIS_KEY_PREFIX ||\n process.env.REDIS_WORKER_JOB_PREFIX ||\n 'worker:jobs:';\nconst defaultTtlSeconds = 60 * 60 * 24 * 7; // 7 days\nconst jobTtlSeconds =\n typeof process.env.WORKER_JOBS_TTL_SECONDS === 'string'\n ? parseInt(process.env.WORKER_JOBS_TTL_SECONDS, 10) || defaultTtlSeconds\n : typeof process.env.REDIS_WORKER_JOB_TTL_SECONDS === 'string'\n ? parseInt(process.env.REDIS_WORKER_JOB_TTL_SECONDS, 10) || defaultTtlSeconds\n : typeof process.env.WORKFLOW_JOBS_TTL_SECONDS === 'string'\n ? parseInt(process.env.WORKFLOW_JOBS_TTL_SECONDS, 10) || defaultTtlSeconds\n : defaultTtlSeconds;\n\nlet redisClient: Redis | null = null;\n\nfunction getRedis(): Redis {\n if (!redisUrl || !redisToken) {\n throw new Error(\n 'Upstash Redis configuration missing. Set WORKER_UPSTASH_REDIS_REST_URL and WORKER_UPSTASH_REDIS_REST_TOKEN (or UPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKEN).'\n );\n }\n if (!redisClient) {\n redisClient = new Redis({\n url: redisUrl,\n token: redisToken,\n });\n }\n return redisClient;\n}\n\nfunction jobKey(jobId: string): string {\n return `${jobKeyPrefix}${jobId}`;\n}\n\n/** Separate LIST key for internal job refs; each RPUSH is atomic so no race when appending multiple. */\nfunction internalListKey(jobId: string): string {\n return `${jobKeyPrefix}${jobId}:internal`;\n}\n\nexport function isRedisJobStoreConfigured(): boolean {\n return Boolean((redisUrl || '').trim() && (redisToken || '').trim());\n}\n\n/** Load a job by id (read-only). Used for idempotency check before processing. */\nexport async function loadJob(jobId: string): Promise<JobRecord | null> {\n const redis = getRedis();\n const key = jobKey(jobId);\n const data = await redis.hgetall<Record<string, string>>(key);\n if (!data || Object.keys(data).length === 0) return null;\n const parseJson = <T>(val?: string | null): T | undefined => {\n if (!val) return undefined;\n try {\n return JSON.parse(val) as T;\n } catch {\n return undefined;\n }\n };\n // Prefer atomic list key for internal jobs; fallback to hash field for old records\n const listKey = internalListKey(jobId);\n const listItems = await redis.lrange<string>(listKey, 0, -1);\n let internalJobs: Array<{ jobId: string; workerId: string }> | undefined;\n if (listItems && listItems.length > 0) {\n internalJobs = listItems.map((s) => {\n try {\n return JSON.parse(s) as { jobId: string; workerId: string };\n } catch {\n return null;\n }\n }).filter(Boolean) as Array<{ jobId: string; workerId: string }>;\n } else {\n internalJobs = parseJson<Array<{ jobId: string; workerId: string }>>(data.internalJobs);\n }\n const record: JobRecord = {\n jobId: data.jobId,\n workerId: data.workerId,\n status: (data.status as JobRecord['status']) || 'queued',\n input: parseJson<any>(data.input) ?? {},\n output: parseJson<any>(data.output),\n error: parseJson<any>(data.error),\n metadata: parseJson<Record<string, any>>(data.metadata) ?? {},\n internalJobs,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n completedAt: data.completedAt,\n };\n return record;\n}\n\nexport function createRedisJobStore(\n workerId: string,\n jobId: string,\n input: any,\n metadata: Record<string, any>\n): JobStore {\n return {\n update: async (update: JobStoreUpdate): Promise<void> => {\n const redis = getRedis();\n const key = jobKey(jobId);\n const now = new Date().toISOString();\n\n // Load existing to merge metadata/progress if needed\n const existing = await loadJob(jobId);\n const next: Partial<JobRecord> = {};\n\n // Start from existing metadata\n const mergedMeta: Record<string, any> = { ...(existing?.metadata ?? {}) };\n if (update.metadata) {\n Object.assign(mergedMeta, update.metadata);\n }\n if (update.progress !== undefined || update.progressMessage !== undefined) {\n mergedMeta.progress = update.progress;\n mergedMeta.progressMessage = update.progressMessage;\n }\n\n next.metadata = mergedMeta;\n if (update.status !== undefined) {\n next.status = update.error ? 'failed' : update.status;\n if ((update.status === 'completed' || update.status === 'failed') && !existing?.completedAt) {\n next.completedAt = now;\n }\n }\n if (update.output !== undefined) next.output = update.output;\n if (update.error !== undefined) next.error = update.error;\n\n const toSet: Record<string, string> = {};\n if (next.status) toSet['status'] = String(next.status);\n if (next.output !== undefined) toSet['output'] = JSON.stringify(next.output);\n if (next.error !== undefined) toSet['error'] = JSON.stringify(next.error);\n if (next.metadata !== undefined) toSet['metadata'] = JSON.stringify(next.metadata);\n if (next.completedAt) {\n toSet['completedAt'] = next.completedAt;\n }\n toSet['updatedAt'] = now;\n\n await redis.hset(key, toSet);\n if (jobTtlSeconds > 0) {\n await redis.expire(key, jobTtlSeconds);\n }\n },\n get: async () => {\n return loadJob(jobId);\n },\n appendInternalJob: async (entry) => {\n const redis = getRedis();\n const listKey = internalListKey(jobId);\n await redis.rpush(listKey, JSON.stringify(entry));\n const mainKey = jobKey(jobId);\n await redis.hset(mainKey, { updatedAt: new Date().toISOString() });\n if (jobTtlSeconds > 0) {\n await redis.expire(listKey, jobTtlSeconds);\n await redis.expire(mainKey, jobTtlSeconds);\n }\n },\n getJob: async (otherJobId: string) => {\n return loadJob(otherJobId);\n },\n };\n}\n\nexport async function upsertRedisJob(\n jobId: string,\n workerId: string,\n input: any,\n metadata: Record<string, any>\n): Promise<void> {\n const redis = getRedis();\n const key = jobKey(jobId);\n const now = new Date().toISOString();\n const doc: Partial<JobRecord> = {\n jobId,\n workerId,\n status: 'queued',\n input,\n metadata,\n createdAt: now,\n updatedAt: now,\n };\n const toSet: Record<string, string> = {\n jobId: jobId,\n workerId: workerId,\n status: doc.status!,\n input: JSON.stringify(doc.input ?? {}),\n metadata: JSON.stringify(doc.metadata ?? {}),\n createdAt: now,\n updatedAt: now,\n };\n await redis.hset(key, toSet);\n if (jobTtlSeconds > 0) {\n await redis.expire(key, jobTtlSeconds);\n }\n}\n\n"],"mappings":"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","names":[]}