@microfox/ai-worker 1.0.1 → 1.0.2

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 CHANGED
@@ -20,14 +20,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ SQS_MAX_DELAY_SECONDS: () => SQS_MAX_DELAY_SECONDS,
23
24
  clearWorkersConfigCache: () => clearWorkersConfigCache,
24
25
  createLambdaEntrypoint: () => createLambdaEntrypoint,
25
26
  createLambdaHandler: () => createLambdaHandler,
26
27
  createWorker: () => createWorker,
28
+ defineWorkerQueue: () => defineWorkerQueue,
27
29
  dispatch: () => dispatch,
28
30
  dispatchLocal: () => dispatchLocal,
31
+ dispatchQueue: () => dispatchQueue,
29
32
  getWorkersConfig: () => getWorkersConfig,
30
- resolveQueueUrl: () => resolveQueueUrl
33
+ getWorkersTriggerUrl: () => getWorkersTriggerUrl,
34
+ resolveQueueUrl: () => resolveQueueUrl,
35
+ wrapHandlerForQueue: () => wrapHandlerForQueue
31
36
  });
32
37
  module.exports = __toCommonJS(index_exports);
33
38
 
@@ -108,6 +113,110 @@ async function dispatch(workerId, input, inputSchema, options, ctx) {
108
113
  async function dispatchLocal(handler, input, ctx) {
109
114
  return handler({ input, ctx: ctx || {} });
110
115
  }
116
+ async function dispatchQueue(queueId, initialInput, options = {}, ctx) {
117
+ const registry = options.registry;
118
+ if (!registry?.getQueueById) {
119
+ throw new Error(
120
+ "dispatchQueue requires options.registry with getQueueById. Use getQueueRegistry() from your workflows registry (e.g. app/api/workflows/registry/workers) and pass { registry: await getQueueRegistry() }."
121
+ );
122
+ }
123
+ const { getQueueById, invokeMapInput } = registry;
124
+ const queue = getQueueById(queueId);
125
+ if (!queue) {
126
+ throw new Error(`Worker queue "${queueId}" not found in registry`);
127
+ }
128
+ if (!queue.steps || queue.steps.length === 0) {
129
+ throw new Error(`Worker queue "${queueId}" has no steps defined`);
130
+ }
131
+ const stepIndex = 0;
132
+ const firstStep = queue.steps[stepIndex];
133
+ const firstWorkerId = firstStep.workerId;
134
+ if (!firstWorkerId) {
135
+ throw new Error(
136
+ `Worker queue "${queueId}" has an invalid first step (missing workerId)`
137
+ );
138
+ }
139
+ let firstInput = initialInput;
140
+ if (firstStep.mapInputFromPrev && typeof invokeMapInput === "function") {
141
+ firstInput = await invokeMapInput(
142
+ queueId,
143
+ stepIndex,
144
+ void 0,
145
+ initialInput
146
+ );
147
+ }
148
+ const jobId = options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
149
+ const queueContext = {
150
+ id: queueId,
151
+ stepIndex,
152
+ initialInput,
153
+ queueJobId: jobId
154
+ };
155
+ if (options.onCreateQueueJob) {
156
+ try {
157
+ await options.onCreateQueueJob({
158
+ queueJobId: jobId,
159
+ queueId,
160
+ firstStep: { workerId: firstWorkerId, workerJobId: jobId },
161
+ metadata: options.metadata
162
+ });
163
+ } catch (err) {
164
+ console.warn("[dispatchQueue] onCreateQueueJob failed:", err?.message ?? err);
165
+ }
166
+ }
167
+ const normalizedFirstInput = firstInput !== null && typeof firstInput === "object" ? firstInput : { value: firstInput };
168
+ const inputWithQueue = {
169
+ ...normalizedFirstInput,
170
+ __workerQueue: queueContext
171
+ };
172
+ const metadataWithQueue = {
173
+ ...options.metadata || {},
174
+ __workerQueue: queueContext
175
+ };
176
+ const triggerUrl = getWorkersTriggerUrl();
177
+ const serializedContext = ctx ? serializeContext(ctx) : {};
178
+ const messageBody = {
179
+ workerId: firstWorkerId,
180
+ jobId,
181
+ input: inputWithQueue,
182
+ context: serializedContext,
183
+ webhookUrl: options.webhookUrl,
184
+ metadata: metadataWithQueue,
185
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
186
+ };
187
+ const headers = {
188
+ "Content-Type": "application/json"
189
+ };
190
+ const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
191
+ if (triggerKey) {
192
+ headers["x-workers-trigger-key"] = triggerKey;
193
+ }
194
+ const response = await fetch(triggerUrl, {
195
+ method: "POST",
196
+ headers,
197
+ body: JSON.stringify({
198
+ workerId: firstWorkerId,
199
+ body: messageBody
200
+ })
201
+ });
202
+ if (!response.ok) {
203
+ const text = await response.text().catch(() => "");
204
+ throw new Error(
205
+ `Failed to trigger queue "${queueId}" (worker "${firstWorkerId}"): ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`
206
+ );
207
+ }
208
+ const data = await response.json().catch(() => ({}));
209
+ const messageId = data?.messageId ? String(data.messageId) : `trigger-${jobId}`;
210
+ return {
211
+ queueId,
212
+ messageId,
213
+ status: "queued",
214
+ jobId
215
+ };
216
+ }
217
+
218
+ // src/handler.ts
219
+ var import_client_sqs = require("@aws-sdk/client-sqs");
111
220
 
112
221
  // src/mongoJobStore.ts
113
222
  var import_mongodb = require("mongodb");
@@ -134,6 +243,21 @@ async function getCollection() {
134
243
  const client = await getClient();
135
244
  return client.db(dbName).collection(collectionName);
136
245
  }
246
+ async function getJobById(jobId) {
247
+ try {
248
+ const coll = await getCollection();
249
+ const doc = await coll.findOne({ _id: jobId });
250
+ if (!doc) return null;
251
+ const { _id, ...r } = doc;
252
+ return r;
253
+ } catch (e) {
254
+ console.error("[Worker] MongoDB getJobById failed:", {
255
+ jobId,
256
+ error: e?.message ?? String(e)
257
+ });
258
+ return null;
259
+ }
260
+ }
137
261
  function createMongoJobStore(workerId, jobId, input, metadata) {
138
262
  return {
139
263
  update: async (update) => {
@@ -205,6 +329,36 @@ function createMongoJobStore(workerId, jobId, input, metadata) {
205
329
  });
206
330
  return null;
207
331
  }
332
+ },
333
+ appendInternalJob: async (entry) => {
334
+ try {
335
+ const coll = await getCollection();
336
+ await coll.updateOne(
337
+ { _id: jobId },
338
+ { $push: { internalJobs: entry } }
339
+ );
340
+ } catch (e) {
341
+ console.error("[Worker] MongoDB job store appendInternalJob failed:", {
342
+ jobId,
343
+ workerId,
344
+ error: e?.message ?? String(e)
345
+ });
346
+ }
347
+ },
348
+ getJob: async (otherJobId) => {
349
+ try {
350
+ const coll = await getCollection();
351
+ const doc = await coll.findOne({ _id: otherJobId });
352
+ if (!doc) return null;
353
+ const { _id, ...r } = doc;
354
+ return r;
355
+ } catch (e) {
356
+ console.error("[Worker] MongoDB job store getJob failed:", {
357
+ otherJobId,
358
+ error: e?.message ?? String(e)
359
+ });
360
+ return null;
361
+ }
208
362
  }
209
363
  };
210
364
  }
@@ -232,7 +386,646 @@ function isMongoJobStoreConfigured() {
232
386
  return Boolean(uri?.trim());
233
387
  }
234
388
 
389
+ // src/redisJobStore.ts
390
+ var import_redis = require("@upstash/redis");
391
+ var redisUrl = process.env.WORKER_UPSTASH_REDIS_REST_URL || process.env.UPSTASH_REDIS_REST_URL || process.env.UPSTASH_REDIS_URL;
392
+ var redisToken = process.env.WORKER_UPSTASH_REDIS_REST_TOKEN || process.env.UPSTASH_REDIS_REST_TOKEN || process.env.UPSTASH_REDIS_TOKEN;
393
+ var jobKeyPrefix = process.env.WORKER_UPSTASH_REDIS_JOBS_PREFIX || process.env.UPSTASH_REDIS_KEY_PREFIX || process.env.REDIS_WORKER_JOB_PREFIX || "worker:jobs:";
394
+ var defaultTtlSeconds = 60 * 60 * 24 * 7;
395
+ var jobTtlSeconds = typeof process.env.WORKER_JOBS_TTL_SECONDS === "string" ? parseInt(process.env.WORKER_JOBS_TTL_SECONDS, 10) || defaultTtlSeconds : typeof process.env.REDIS_WORKER_JOB_TTL_SECONDS === "string" ? parseInt(process.env.REDIS_WORKER_JOB_TTL_SECONDS, 10) || defaultTtlSeconds : typeof process.env.WORKFLOW_JOBS_TTL_SECONDS === "string" ? parseInt(process.env.WORKFLOW_JOBS_TTL_SECONDS, 10) || defaultTtlSeconds : defaultTtlSeconds;
396
+ var redisClient = null;
397
+ function getRedis() {
398
+ if (!redisUrl || !redisToken) {
399
+ throw new Error(
400
+ "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)."
401
+ );
402
+ }
403
+ if (!redisClient) {
404
+ redisClient = new import_redis.Redis({
405
+ url: redisUrl,
406
+ token: redisToken
407
+ });
408
+ }
409
+ return redisClient;
410
+ }
411
+ function jobKey(jobId) {
412
+ return `${jobKeyPrefix}${jobId}`;
413
+ }
414
+ function internalListKey(jobId) {
415
+ return `${jobKeyPrefix}${jobId}:internal`;
416
+ }
417
+ function isRedisJobStoreConfigured() {
418
+ return Boolean((redisUrl || "").trim() && (redisToken || "").trim());
419
+ }
420
+ async function loadJob(jobId) {
421
+ const redis = getRedis();
422
+ const key = jobKey(jobId);
423
+ const data = await redis.hgetall(key);
424
+ if (!data || Object.keys(data).length === 0) return null;
425
+ const parseJson = (val) => {
426
+ if (!val) return void 0;
427
+ try {
428
+ return JSON.parse(val);
429
+ } catch {
430
+ return void 0;
431
+ }
432
+ };
433
+ const listKey = internalListKey(jobId);
434
+ const listItems = await redis.lrange(listKey, 0, -1);
435
+ let internalJobs;
436
+ if (listItems && listItems.length > 0) {
437
+ internalJobs = listItems.map((s) => {
438
+ try {
439
+ return JSON.parse(s);
440
+ } catch {
441
+ return null;
442
+ }
443
+ }).filter(Boolean);
444
+ } else {
445
+ internalJobs = parseJson(data.internalJobs);
446
+ }
447
+ const record = {
448
+ jobId: data.jobId,
449
+ workerId: data.workerId,
450
+ status: data.status || "queued",
451
+ input: parseJson(data.input) ?? {},
452
+ output: parseJson(data.output),
453
+ error: parseJson(data.error),
454
+ metadata: parseJson(data.metadata) ?? {},
455
+ internalJobs,
456
+ createdAt: data.createdAt,
457
+ updatedAt: data.updatedAt,
458
+ completedAt: data.completedAt
459
+ };
460
+ return record;
461
+ }
462
+ function createRedisJobStore(workerId, jobId, input, metadata) {
463
+ return {
464
+ update: async (update) => {
465
+ const redis = getRedis();
466
+ const key = jobKey(jobId);
467
+ const now = (/* @__PURE__ */ new Date()).toISOString();
468
+ const existing = await loadJob(jobId);
469
+ const next = {};
470
+ const mergedMeta = { ...existing?.metadata ?? {} };
471
+ if (update.metadata) {
472
+ Object.assign(mergedMeta, update.metadata);
473
+ }
474
+ if (update.progress !== void 0 || update.progressMessage !== void 0) {
475
+ mergedMeta.progress = update.progress;
476
+ mergedMeta.progressMessage = update.progressMessage;
477
+ }
478
+ next.metadata = mergedMeta;
479
+ if (update.status !== void 0) {
480
+ next.status = update.error ? "failed" : update.status;
481
+ if ((update.status === "completed" || update.status === "failed") && !existing?.completedAt) {
482
+ next.completedAt = now;
483
+ }
484
+ }
485
+ if (update.output !== void 0) next.output = update.output;
486
+ if (update.error !== void 0) next.error = update.error;
487
+ const toSet = {};
488
+ if (next.status) toSet["status"] = String(next.status);
489
+ if (next.output !== void 0) toSet["output"] = JSON.stringify(next.output);
490
+ if (next.error !== void 0) toSet["error"] = JSON.stringify(next.error);
491
+ if (next.metadata !== void 0) toSet["metadata"] = JSON.stringify(next.metadata);
492
+ if (next.completedAt) {
493
+ toSet["completedAt"] = next.completedAt;
494
+ }
495
+ toSet["updatedAt"] = now;
496
+ await redis.hset(key, toSet);
497
+ if (jobTtlSeconds > 0) {
498
+ await redis.expire(key, jobTtlSeconds);
499
+ }
500
+ },
501
+ get: async () => {
502
+ return loadJob(jobId);
503
+ },
504
+ appendInternalJob: async (entry) => {
505
+ const redis = getRedis();
506
+ const listKey = internalListKey(jobId);
507
+ await redis.rpush(listKey, JSON.stringify(entry));
508
+ const mainKey = jobKey(jobId);
509
+ await redis.hset(mainKey, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
510
+ if (jobTtlSeconds > 0) {
511
+ await redis.expire(listKey, jobTtlSeconds);
512
+ await redis.expire(mainKey, jobTtlSeconds);
513
+ }
514
+ },
515
+ getJob: async (otherJobId) => {
516
+ return loadJob(otherJobId);
517
+ }
518
+ };
519
+ }
520
+ async function upsertRedisJob(jobId, workerId, input, metadata) {
521
+ const redis = getRedis();
522
+ const key = jobKey(jobId);
523
+ const now = (/* @__PURE__ */ new Date()).toISOString();
524
+ const doc = {
525
+ jobId,
526
+ workerId,
527
+ status: "queued",
528
+ input,
529
+ metadata,
530
+ createdAt: now,
531
+ updatedAt: now
532
+ };
533
+ const toSet = {
534
+ jobId,
535
+ workerId,
536
+ status: doc.status,
537
+ input: JSON.stringify(doc.input ?? {}),
538
+ metadata: JSON.stringify(doc.metadata ?? {}),
539
+ createdAt: now,
540
+ updatedAt: now
541
+ };
542
+ await redis.hset(key, toSet);
543
+ if (jobTtlSeconds > 0) {
544
+ await redis.expire(key, jobTtlSeconds);
545
+ }
546
+ }
547
+
548
+ // src/queueJobStore.ts
549
+ var import_redis2 = require("@upstash/redis");
550
+ var import_mongodb2 = require("mongodb");
551
+ var mongoUri = process.env.DATABASE_MONGODB_URI || process.env.MONGODB_URI;
552
+ var mongoDbName = process.env.DATABASE_MONGODB_DB || process.env.MONGODB_DB || "mediamake";
553
+ var mongoQueueCollectionName = process.env.MONGODB_QUEUE_JOBS_COLLECTION || "queue_jobs";
554
+ var mongoClientPromise = null;
555
+ async function getMongoClient() {
556
+ if (!mongoUri) {
557
+ throw new Error(
558
+ "MongoDB URI required for queue job store. Set DATABASE_MONGODB_URI or MONGODB_URI."
559
+ );
560
+ }
561
+ if (!mongoClientPromise) {
562
+ mongoClientPromise = new import_mongodb2.MongoClient(mongoUri, {
563
+ maxPoolSize: 10,
564
+ minPoolSize: 0,
565
+ serverSelectionTimeoutMS: 1e4
566
+ }).connect();
567
+ }
568
+ return mongoClientPromise;
569
+ }
570
+ async function getMongoQueueCollection() {
571
+ const client = await getMongoClient();
572
+ return client.db(mongoDbName).collection(mongoQueueCollectionName);
573
+ }
574
+ var redisUrl2 = process.env.WORKER_UPSTASH_REDIS_REST_URL || process.env.UPSTASH_REDIS_REST_URL || process.env.UPSTASH_REDIS_URL;
575
+ var redisToken2 = process.env.WORKER_UPSTASH_REDIS_REST_TOKEN || process.env.UPSTASH_REDIS_REST_TOKEN || process.env.UPSTASH_REDIS_TOKEN;
576
+ var queueKeyPrefix = process.env.WORKER_UPSTASH_REDIS_QUEUE_PREFIX || process.env.UPSTASH_REDIS_QUEUE_PREFIX || "worker:queue-jobs:";
577
+ var defaultTtlSeconds2 = 60 * 60 * 24 * 7;
578
+ 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;
579
+ var redisClient2 = null;
580
+ function getRedis2() {
581
+ if (!redisUrl2 || !redisToken2) {
582
+ throw new Error(
583
+ "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)."
584
+ );
585
+ }
586
+ if (!redisClient2) {
587
+ redisClient2 = new import_redis2.Redis({
588
+ url: redisUrl2,
589
+ token: redisToken2
590
+ });
591
+ }
592
+ return redisClient2;
593
+ }
594
+ function queueKey(id) {
595
+ return `${queueKeyPrefix}${id}`;
596
+ }
597
+ function stepsFromHash(val) {
598
+ if (Array.isArray(val)) return val;
599
+ if (typeof val === "string") {
600
+ try {
601
+ const parsed = JSON.parse(val);
602
+ return Array.isArray(parsed) ? parsed : [];
603
+ } catch {
604
+ return [];
605
+ }
606
+ }
607
+ return [];
608
+ }
609
+ function metadataFromHash(val) {
610
+ if (val && typeof val === "object" && !Array.isArray(val)) return val;
611
+ if (typeof val === "string") {
612
+ try {
613
+ const parsed = JSON.parse(val);
614
+ return parsed && typeof parsed === "object" ? parsed : {};
615
+ } catch {
616
+ return {};
617
+ }
618
+ }
619
+ return {};
620
+ }
621
+ async function loadQueueJobRedis(queueJobId) {
622
+ const redis = getRedis2();
623
+ const key = queueKey(queueJobId);
624
+ const data = await redis.hgetall(key);
625
+ if (!data || typeof data !== "object" || Object.keys(data).length === 0) return null;
626
+ const d = data;
627
+ const record = {
628
+ id: d.id === void 0 ? queueJobId : String(d.id),
629
+ queueId: String(d.queueId ?? ""),
630
+ status: String(d.status ?? "running"),
631
+ steps: stepsFromHash(d.steps),
632
+ metadata: metadataFromHash(d.metadata),
633
+ createdAt: String(d.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()),
634
+ updatedAt: String(d.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()),
635
+ completedAt: d.completedAt != null ? String(d.completedAt) : void 0
636
+ };
637
+ return record;
638
+ }
639
+ async function saveQueueJobRedis(record) {
640
+ const redis = getRedis2();
641
+ const key = queueKey(record.id);
642
+ const now = (/* @__PURE__ */ new Date()).toISOString();
643
+ const toSet = {
644
+ id: record.id,
645
+ queueId: record.queueId,
646
+ status: record.status,
647
+ steps: JSON.stringify(record.steps || []),
648
+ metadata: JSON.stringify(record.metadata || {}),
649
+ createdAt: record.createdAt || now,
650
+ updatedAt: record.updatedAt || now
651
+ };
652
+ if (record.completedAt) {
653
+ toSet.completedAt = record.completedAt;
654
+ }
655
+ await redis.hset(key, toSet);
656
+ if (queueJobTtlSeconds > 0) {
657
+ await redis.expire(key, queueJobTtlSeconds);
658
+ }
659
+ }
660
+ function getStoreType() {
661
+ const t = (process.env.WORKER_DATABASE_TYPE || "upstash-redis").toLowerCase();
662
+ return t === "mongodb" ? "mongodb" : "upstash-redis";
663
+ }
664
+ function preferMongo() {
665
+ return getStoreType() === "mongodb" && Boolean(mongoUri?.trim());
666
+ }
667
+ function preferRedis() {
668
+ return getStoreType() !== "mongodb" && Boolean((redisUrl2 || "").trim() && (redisToken2 || "").trim());
669
+ }
670
+ async function upsertInitialQueueJob(options) {
671
+ const { queueJobId, queueId, firstWorkerId, firstWorkerJobId, metadata } = options;
672
+ const now = (/* @__PURE__ */ new Date()).toISOString();
673
+ if (preferMongo()) {
674
+ const coll = await getMongoQueueCollection();
675
+ const existing = await coll.findOne({ _id: queueJobId });
676
+ if (existing) {
677
+ const steps = existing.steps ?? [];
678
+ if (steps.length === 0) {
679
+ steps.push({
680
+ workerId: firstWorkerId,
681
+ workerJobId: firstWorkerJobId,
682
+ status: "queued"
683
+ });
684
+ }
685
+ await coll.updateOne(
686
+ { _id: queueJobId },
687
+ {
688
+ $set: {
689
+ steps,
690
+ updatedAt: now
691
+ }
692
+ }
693
+ );
694
+ } else {
695
+ const doc = {
696
+ _id: queueJobId,
697
+ id: queueJobId,
698
+ queueId,
699
+ status: "running",
700
+ steps: [
701
+ {
702
+ workerId: firstWorkerId,
703
+ workerJobId: firstWorkerJobId,
704
+ status: "queued"
705
+ }
706
+ ],
707
+ metadata: metadata ?? {},
708
+ createdAt: now,
709
+ updatedAt: now
710
+ };
711
+ await coll.updateOne(
712
+ { _id: queueJobId },
713
+ { $set: doc },
714
+ { upsert: true }
715
+ );
716
+ }
717
+ return;
718
+ }
719
+ if (preferRedis()) {
720
+ const existing = await loadQueueJobRedis(queueJobId);
721
+ if (existing) {
722
+ if (!existing.steps || existing.steps.length === 0) {
723
+ existing.steps = [
724
+ {
725
+ workerId: firstWorkerId,
726
+ workerJobId: firstWorkerJobId,
727
+ status: "queued"
728
+ }
729
+ ];
730
+ }
731
+ existing.updatedAt = now;
732
+ await saveQueueJobRedis(existing);
733
+ } else {
734
+ const record = {
735
+ id: queueJobId,
736
+ queueId,
737
+ status: "running",
738
+ steps: [
739
+ {
740
+ workerId: firstWorkerId,
741
+ workerJobId: firstWorkerJobId,
742
+ status: "queued"
743
+ }
744
+ ],
745
+ metadata: metadata ?? {},
746
+ createdAt: now,
747
+ updatedAt: now
748
+ };
749
+ await saveQueueJobRedis(record);
750
+ }
751
+ }
752
+ }
753
+ async function updateQueueJobStepInStore(options) {
754
+ const { queueJobId, stepIndex, status, input, output, error } = options;
755
+ const now = (/* @__PURE__ */ new Date()).toISOString();
756
+ if (preferMongo()) {
757
+ const coll = await getMongoQueueCollection();
758
+ const existing = await coll.findOne({ _id: queueJobId });
759
+ if (!existing) return;
760
+ const step = existing.steps[stepIndex];
761
+ if (!step) return;
762
+ const mergedStep = {
763
+ ...step,
764
+ status,
765
+ ...input !== void 0 && { input },
766
+ ...output !== void 0 && { output },
767
+ ...error !== void 0 && { error },
768
+ startedAt: step.startedAt ?? (status === "running" ? now : step.startedAt),
769
+ completedAt: step.completedAt ?? (status === "completed" || status === "failed" ? now : step.completedAt)
770
+ };
771
+ const setDoc = {
772
+ steps: existing.steps,
773
+ updatedAt: now
774
+ };
775
+ setDoc.steps[stepIndex] = mergedStep;
776
+ if (status === "failed") {
777
+ setDoc.status = "failed";
778
+ if (!existing.completedAt) setDoc.completedAt = now;
779
+ } else if (status === "completed" && stepIndex === existing.steps.length - 1) {
780
+ setDoc.status = "completed";
781
+ if (!existing.completedAt) setDoc.completedAt = now;
782
+ }
783
+ await coll.updateOne(
784
+ { _id: queueJobId },
785
+ {
786
+ $set: setDoc
787
+ }
788
+ );
789
+ return;
790
+ }
791
+ if (preferRedis()) {
792
+ const existing = await loadQueueJobRedis(queueJobId);
793
+ if (!existing) {
794
+ return;
795
+ }
796
+ const steps = existing.steps || [];
797
+ const step = steps[stepIndex];
798
+ if (!step) {
799
+ return;
800
+ }
801
+ step.status = status;
802
+ if (input !== void 0) step.input = input;
803
+ if (output !== void 0) step.output = output;
804
+ if (error !== void 0) step.error = error;
805
+ if (status === "running") {
806
+ step.startedAt = step.startedAt ?? now;
807
+ }
808
+ if (status === "completed" || status === "failed") {
809
+ step.completedAt = step.completedAt ?? now;
810
+ }
811
+ existing.steps = steps;
812
+ existing.updatedAt = now;
813
+ if (status === "failed") {
814
+ existing.status = "failed";
815
+ existing.completedAt = existing.completedAt ?? now;
816
+ } else if (status === "completed" && stepIndex === steps.length - 1) {
817
+ existing.status = "completed";
818
+ existing.completedAt = existing.completedAt ?? now;
819
+ }
820
+ await saveQueueJobRedis(existing);
821
+ }
822
+ }
823
+ async function appendQueueJobStepInStore(options) {
824
+ const { queueJobId, workerId, workerJobId } = options;
825
+ const now = (/* @__PURE__ */ new Date()).toISOString();
826
+ if (preferMongo()) {
827
+ const coll = await getMongoQueueCollection();
828
+ await coll.updateOne(
829
+ { _id: queueJobId },
830
+ {
831
+ $push: {
832
+ steps: {
833
+ workerId,
834
+ workerJobId,
835
+ status: "queued"
836
+ }
837
+ },
838
+ $set: { updatedAt: now }
839
+ }
840
+ );
841
+ return;
842
+ }
843
+ if (preferRedis()) {
844
+ const existing = await loadQueueJobRedis(queueJobId);
845
+ if (!existing) return;
846
+ const steps = existing.steps || [];
847
+ steps.push({
848
+ workerId,
849
+ workerJobId,
850
+ status: "queued"
851
+ });
852
+ existing.steps = steps;
853
+ existing.updatedAt = now;
854
+ await saveQueueJobRedis(existing);
855
+ }
856
+ }
857
+
235
858
  // src/handler.ts
859
+ var SQS_MAX_DELAY_SECONDS = 900;
860
+ var WORKER_QUEUE_KEY = "__workerQueue";
861
+ async function notifyQueueJobStep(queueJobId, action, params) {
862
+ try {
863
+ if (action === "append") {
864
+ if (!params.workerId || !params.workerJobId) return;
865
+ await appendQueueJobStepInStore({
866
+ queueJobId,
867
+ workerId: params.workerId,
868
+ workerJobId: params.workerJobId
869
+ });
870
+ if (process.env.DEBUG_WORKER_QUEUES === "1") {
871
+ console.log("[Worker] Queue job step appended", {
872
+ queueJobId,
873
+ workerId: params.workerId,
874
+ workerJobId: params.workerJobId
875
+ });
876
+ }
877
+ return;
878
+ }
879
+ if (params.stepIndex === void 0) return;
880
+ const status = action === "start" ? "running" : action === "complete" ? "completed" : action === "fail" ? "failed" : void 0;
881
+ if (!status) return;
882
+ await updateQueueJobStepInStore({
883
+ queueJobId,
884
+ stepIndex: params.stepIndex,
885
+ workerId: params.workerId || "",
886
+ workerJobId: params.workerJobId,
887
+ status,
888
+ input: params.input,
889
+ output: params.output,
890
+ error: params.error
891
+ });
892
+ if (process.env.DEBUG_WORKER_QUEUES === "1") {
893
+ console.log("[Worker] Queue job step updated", {
894
+ queueJobId,
895
+ action,
896
+ stepIndex: params.stepIndex,
897
+ status
898
+ });
899
+ }
900
+ } catch (err) {
901
+ console.warn("[Worker] Queue job update error:", {
902
+ queueJobId,
903
+ action,
904
+ error: err?.message ?? String(err)
905
+ });
906
+ }
907
+ }
908
+ function wrapHandlerForQueue(handler, queueRuntime) {
909
+ return async (params) => {
910
+ const queueContext = params.input?.[WORKER_QUEUE_KEY];
911
+ const output = await handler(params);
912
+ if (!queueContext || typeof queueContext !== "object" || !queueContext.id) {
913
+ return output;
914
+ }
915
+ const { id: queueId, stepIndex, initialInput, queueJobId } = queueContext;
916
+ const next = queueRuntime.getNextStep(queueId, stepIndex);
917
+ if (!next) {
918
+ return output;
919
+ }
920
+ const childJobId = `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
921
+ if (queueJobId) {
922
+ await notifyQueueJobStep(queueJobId, "append", {
923
+ workerJobId: childJobId,
924
+ workerId: next.workerId
925
+ });
926
+ }
927
+ let nextInput = output;
928
+ if (next.mapInputFromPrev && typeof queueRuntime.invokeMapInput === "function") {
929
+ nextInput = await queueRuntime.invokeMapInput(queueId, stepIndex + 1, output, initialInput);
930
+ }
931
+ const nextInputWithQueue = {
932
+ ...nextInput !== null && typeof nextInput === "object" ? nextInput : { value: nextInput },
933
+ [WORKER_QUEUE_KEY]: {
934
+ id: queueId,
935
+ stepIndex: stepIndex + 1,
936
+ initialInput,
937
+ queueJobId
938
+ }
939
+ };
940
+ const debug = process.env.AI_WORKER_QUEUES_DEBUG === "1";
941
+ if (debug) {
942
+ console.log("[Worker] Queue chain dispatching next:", {
943
+ queueId,
944
+ fromStep: stepIndex,
945
+ nextWorkerId: next.workerId,
946
+ delaySeconds: next.delaySeconds
947
+ });
948
+ }
949
+ await params.ctx.dispatchWorker(next.workerId, nextInputWithQueue, {
950
+ await: false,
951
+ delaySeconds: next.delaySeconds,
952
+ jobId: childJobId
953
+ });
954
+ return output;
955
+ };
956
+ }
957
+ var DEFAULT_POLL_INTERVAL_MS = 2e3;
958
+ var DEFAULT_POLL_TIMEOUT_MS = 15 * 60 * 1e3;
959
+ function sanitizeWorkerIdForEnv(workerId) {
960
+ return workerId.replace(/-/g, "_").toUpperCase();
961
+ }
962
+ function getQueueUrlForWorker(calleeWorkerId) {
963
+ const key = `WORKER_QUEUE_URL_${sanitizeWorkerIdForEnv(calleeWorkerId)}`;
964
+ return process.env[key]?.trim() || void 0;
965
+ }
966
+ function createDispatchWorker(parentJobId, parentWorkerId, parentContext, jobStore) {
967
+ return async (calleeWorkerId, input, options) => {
968
+ const childJobId = options?.jobId || `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
969
+ const metadata = options?.metadata ?? {};
970
+ const serializedContext = {};
971
+ if (parentContext.requestId) serializedContext.requestId = parentContext.requestId;
972
+ const messageBody = {
973
+ workerId: calleeWorkerId,
974
+ jobId: childJobId,
975
+ input: input ?? {},
976
+ context: serializedContext,
977
+ webhookUrl: options?.webhookUrl,
978
+ metadata,
979
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
980
+ };
981
+ const queueUrl = getQueueUrlForWorker(calleeWorkerId);
982
+ if (queueUrl) {
983
+ const region = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || "us-east-1";
984
+ const sqs = new import_client_sqs.SQSClient({ region });
985
+ const delaySeconds = options?.await !== true && options?.delaySeconds != null ? Math.min(SQS_MAX_DELAY_SECONDS, Math.max(0, Math.floor(options.delaySeconds))) : void 0;
986
+ const sendResult = await sqs.send(
987
+ new import_client_sqs.SendMessageCommand({
988
+ QueueUrl: queueUrl,
989
+ MessageBody: JSON.stringify(messageBody),
990
+ ...delaySeconds !== void 0 && delaySeconds > 0 ? { DelaySeconds: delaySeconds } : {}
991
+ })
992
+ );
993
+ const messageId = sendResult.MessageId ?? void 0;
994
+ if (jobStore?.appendInternalJob) {
995
+ await jobStore.appendInternalJob({ jobId: childJobId, workerId: calleeWorkerId });
996
+ }
997
+ if (options?.await && jobStore?.getJob) {
998
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
999
+ const pollTimeoutMs = options.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
1000
+ const deadline = Date.now() + pollTimeoutMs;
1001
+ while (Date.now() < deadline) {
1002
+ const child = await jobStore.getJob(childJobId);
1003
+ if (!child) {
1004
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
1005
+ continue;
1006
+ }
1007
+ if (child.status === "completed") {
1008
+ return { jobId: childJobId, messageId, output: child.output };
1009
+ }
1010
+ if (child.status === "failed") {
1011
+ const err = child.error;
1012
+ throw new Error(
1013
+ err?.message ?? `Child worker ${calleeWorkerId} failed`
1014
+ );
1015
+ }
1016
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
1017
+ }
1018
+ throw new Error(
1019
+ `Child worker ${calleeWorkerId} (${childJobId}) did not complete within ${pollTimeoutMs}ms`
1020
+ );
1021
+ }
1022
+ return { jobId: childJobId, messageId };
1023
+ }
1024
+ throw new Error(
1025
+ `WORKER_QUEUE_URL_${sanitizeWorkerIdForEnv(calleeWorkerId)} is not set. Configure queue URL for worker-to-worker dispatch, or run in local mode.`
1026
+ );
1027
+ };
1028
+ }
236
1029
  async function sendWebhook(webhookUrl, payload) {
237
1030
  try {
238
1031
  const response = await fetch(webhookUrl, {
@@ -272,18 +1065,53 @@ function createLambdaHandler(handler, outputSchema) {
272
1065
  try {
273
1066
  messageBody = JSON.parse(record.body);
274
1067
  const { workerId, jobId, input, context, webhookUrl, metadata = {} } = messageBody;
1068
+ const raw = (process.env.WORKER_DATABASE_TYPE || "upstash-redis").toLowerCase();
1069
+ const jobStoreType = raw === "mongodb" ? "mongodb" : "upstash-redis";
1070
+ if (jobStoreType === "upstash-redis" && isRedisJobStoreConfigured()) {
1071
+ const existing = await loadJob(jobId);
1072
+ if (existing && (existing.status === "completed" || existing.status === "failed")) {
1073
+ console.log("[Worker] Skipping already terminal job (idempotent):", {
1074
+ jobId,
1075
+ workerId,
1076
+ status: existing.status
1077
+ });
1078
+ return;
1079
+ }
1080
+ } else if (jobStoreType === "mongodb" || isMongoJobStoreConfigured()) {
1081
+ const existing = await getJobById(jobId);
1082
+ if (existing && (existing.status === "completed" || existing.status === "failed")) {
1083
+ console.log("[Worker] Skipping already terminal job (idempotent):", {
1084
+ jobId,
1085
+ workerId,
1086
+ status: existing.status
1087
+ });
1088
+ return;
1089
+ }
1090
+ }
275
1091
  let jobStore;
276
- if (isMongoJobStoreConfigured()) {
1092
+ if (jobStoreType === "upstash-redis" && isRedisJobStoreConfigured()) {
1093
+ await upsertRedisJob(jobId, workerId, input, metadata);
1094
+ jobStore = createRedisJobStore(workerId, jobId, input, metadata);
1095
+ } else if (jobStoreType === "mongodb" || isMongoJobStoreConfigured()) {
277
1096
  await upsertJob(jobId, workerId, input, metadata);
278
1097
  jobStore = createMongoJobStore(workerId, jobId, input, metadata);
279
1098
  }
280
- const handlerContext = {
1099
+ const baseContext = {
281
1100
  jobId,
282
1101
  workerId,
283
1102
  requestId: context.requestId || lambdaContext.awsRequestId,
284
- ...jobStore ? { jobStore } : {},
285
1103
  ...context
286
1104
  };
1105
+ const handlerContext = {
1106
+ ...baseContext,
1107
+ ...jobStore ? { jobStore } : {},
1108
+ dispatchWorker: createDispatchWorker(
1109
+ jobId,
1110
+ workerId,
1111
+ baseContext,
1112
+ jobStore
1113
+ )
1114
+ };
287
1115
  if (jobStore) {
288
1116
  try {
289
1117
  await jobStore.update({ status: "running" });
@@ -299,6 +1127,32 @@ function createLambdaHandler(handler, outputSchema) {
299
1127
  });
300
1128
  }
301
1129
  }
1130
+ const queueCtx = input?.__workerQueue ?? metadata?.__workerQueue;
1131
+ if (queueCtx?.queueJobId && typeof queueCtx.stepIndex === "number") {
1132
+ if (queueCtx.stepIndex === 0) {
1133
+ try {
1134
+ await upsertInitialQueueJob({
1135
+ queueJobId: queueCtx.queueJobId,
1136
+ queueId: queueCtx.id,
1137
+ firstWorkerId: workerId,
1138
+ firstWorkerJobId: jobId,
1139
+ metadata
1140
+ });
1141
+ } catch (e) {
1142
+ console.warn("[Worker] Failed to upsert initial queue job:", {
1143
+ queueJobId: queueCtx.queueJobId,
1144
+ queueId: queueCtx.id,
1145
+ error: e?.message ?? String(e)
1146
+ });
1147
+ }
1148
+ }
1149
+ await notifyQueueJobStep(queueCtx.queueJobId, "start", {
1150
+ stepIndex: queueCtx.stepIndex,
1151
+ workerJobId: jobId,
1152
+ workerId,
1153
+ input
1154
+ });
1155
+ }
302
1156
  let output;
303
1157
  try {
304
1158
  output = await handler({
@@ -338,6 +1192,15 @@ function createLambdaHandler(handler, outputSchema) {
338
1192
  });
339
1193
  }
340
1194
  }
1195
+ const queueCtxFail = input?.__workerQueue ?? metadata?.__workerQueue;
1196
+ if (queueCtxFail?.queueJobId && typeof queueCtxFail.stepIndex === "number") {
1197
+ await notifyQueueJobStep(queueCtxFail.queueJobId, "fail", {
1198
+ stepIndex: queueCtxFail.stepIndex,
1199
+ workerJobId: jobId,
1200
+ workerId,
1201
+ error: errorPayload.error
1202
+ });
1203
+ }
341
1204
  if (webhookUrl) {
342
1205
  await sendWebhook(webhookUrl, errorPayload);
343
1206
  }
@@ -361,6 +1224,15 @@ function createLambdaHandler(handler, outputSchema) {
361
1224
  });
362
1225
  }
363
1226
  }
1227
+ const queueCtxSuccess = input?.__workerQueue ?? metadata?.__workerQueue;
1228
+ if (queueCtxSuccess?.queueJobId && typeof queueCtxSuccess.stepIndex === "number") {
1229
+ await notifyQueueJobStep(queueCtxSuccess.queueJobId, "complete", {
1230
+ stepIndex: queueCtxSuccess.stepIndex,
1231
+ workerJobId: jobId,
1232
+ workerId,
1233
+ output
1234
+ });
1235
+ }
364
1236
  console.log("[Worker] Job completed:", {
365
1237
  jobId,
366
1238
  workerId,
@@ -434,6 +1306,11 @@ function clearWorkersConfigCache() {
434
1306
  cacheExpiry = 0;
435
1307
  }
436
1308
 
1309
+ // src/queue.ts
1310
+ function defineWorkerQueue(config) {
1311
+ return config;
1312
+ }
1313
+
437
1314
  // src/index.ts
438
1315
  function createWorker(config) {
439
1316
  const { id, inputSchema, outputSchema, handler } = config;
@@ -550,6 +1427,42 @@ function createWorker(config) {
550
1427
  });
551
1428
  return null;
552
1429
  }
1430
+ },
1431
+ appendInternalJob: async (entry) => {
1432
+ try {
1433
+ const nextJsPath = "@/app/api/workflows/stores/jobStore";
1434
+ const explicitPath2 = process.env.WORKER_JOB_STORE_MODULE_PATH;
1435
+ for (const importPath of [nextJsPath, explicitPath2].filter(Boolean)) {
1436
+ try {
1437
+ const module2 = await import(importPath);
1438
+ if (typeof module2?.appendInternalJob === "function") {
1439
+ await module2.appendInternalJob(localJobId, entry);
1440
+ return;
1441
+ }
1442
+ } catch {
1443
+ }
1444
+ }
1445
+ } catch (error) {
1446
+ console.warn("[Worker] Failed to appendInternalJob (direct DB):", { localJobId, error: error?.message || String(error) });
1447
+ }
1448
+ },
1449
+ getJob: async (otherJobId) => {
1450
+ try {
1451
+ const nextJsPath = "@/app/api/workflows/stores/jobStore";
1452
+ const explicitPath2 = process.env.WORKER_JOB_STORE_MODULE_PATH;
1453
+ for (const importPath of [nextJsPath, explicitPath2].filter(Boolean)) {
1454
+ try {
1455
+ const module2 = await import(importPath);
1456
+ if (typeof module2?.getJob === "function") {
1457
+ return await module2.getJob(otherJobId);
1458
+ }
1459
+ } catch {
1460
+ }
1461
+ }
1462
+ } catch (error) {
1463
+ console.warn("[Worker] Failed to getJob (direct DB):", { otherJobId, error: error?.message || String(error) });
1464
+ }
1465
+ return null;
553
1466
  }
554
1467
  };
555
1468
  }
@@ -625,6 +1538,101 @@ function createWorker(config) {
625
1538
  };
626
1539
  };
627
1540
  const jobStore = createLocalJobStore(directJobStore, jobStoreUrl);
1541
+ const DEFAULT_POLL_INTERVAL_MS2 = 2e3;
1542
+ const DEFAULT_POLL_TIMEOUT_MS2 = 15 * 60 * 1e3;
1543
+ const createLocalDispatchWorker = (parentJobId, parentWorkerId, parentContext, store) => {
1544
+ return async (calleeWorkerId, input2, options2) => {
1545
+ const childJobId = options2?.jobId || `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
1546
+ const metadata = options2?.metadata ?? {};
1547
+ const serializedContext = {};
1548
+ if (parentContext.requestId) serializedContext.requestId = parentContext.requestId;
1549
+ const messageBody = {
1550
+ workerId: calleeWorkerId,
1551
+ jobId: childJobId,
1552
+ input: input2 ?? {},
1553
+ context: serializedContext,
1554
+ webhookUrl: options2?.webhookUrl,
1555
+ metadata,
1556
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1557
+ };
1558
+ let triggerUrl;
1559
+ try {
1560
+ triggerUrl = getWorkersTriggerUrl();
1561
+ } catch (e) {
1562
+ throw new Error(
1563
+ `Local dispatchWorker requires WORKER_BASE_URL (or similar) for worker "${calleeWorkerId}": ${e?.message ?? e}`
1564
+ );
1565
+ }
1566
+ const headers = { "Content-Type": "application/json" };
1567
+ const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
1568
+ if (triggerKey) headers["x-workers-trigger-key"] = triggerKey;
1569
+ if (options2?.await !== true && options2?.delaySeconds != null && options2.delaySeconds > 0) {
1570
+ const sec = Math.min(SQS_MAX_DELAY_SECONDS, Math.max(0, Math.floor(options2.delaySeconds)));
1571
+ const storeRef = store;
1572
+ setTimeout(() => {
1573
+ fetch(triggerUrl, {
1574
+ method: "POST",
1575
+ headers,
1576
+ body: JSON.stringify({ workerId: calleeWorkerId, body: messageBody })
1577
+ }).then(async (response2) => {
1578
+ if (!response2.ok) {
1579
+ const text = await response2.text().catch(() => "");
1580
+ console.error(
1581
+ `[Worker] Delayed trigger failed for "${calleeWorkerId}": ${response2.status} ${response2.statusText}${text ? ` - ${text}` : ""}`
1582
+ );
1583
+ return;
1584
+ }
1585
+ if (storeRef?.appendInternalJob) {
1586
+ await storeRef.appendInternalJob({ jobId: childJobId, workerId: calleeWorkerId });
1587
+ }
1588
+ }).catch((err) => {
1589
+ console.error("[Worker] Delayed trigger error:", { calleeWorkerId, jobId: childJobId, error: err?.message ?? err });
1590
+ });
1591
+ }, sec * 1e3);
1592
+ return { jobId: childJobId, messageId: void 0 };
1593
+ }
1594
+ const response = await fetch(triggerUrl, {
1595
+ method: "POST",
1596
+ headers,
1597
+ body: JSON.stringify({ workerId: calleeWorkerId, body: messageBody })
1598
+ });
1599
+ if (!response.ok) {
1600
+ const text = await response.text().catch(() => "");
1601
+ throw new Error(
1602
+ `Failed to trigger worker "${calleeWorkerId}": ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`
1603
+ );
1604
+ }
1605
+ const data = await response.json().catch(() => ({}));
1606
+ const messageId = data?.messageId ? String(data.messageId) : `trigger-${childJobId}`;
1607
+ if (store?.appendInternalJob) {
1608
+ await store.appendInternalJob({ jobId: childJobId, workerId: calleeWorkerId });
1609
+ }
1610
+ if (options2?.await && store?.getJob) {
1611
+ const pollIntervalMs = options2.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
1612
+ const pollTimeoutMs = options2.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS2;
1613
+ const deadline = Date.now() + pollTimeoutMs;
1614
+ while (Date.now() < deadline) {
1615
+ const child = await store.getJob(childJobId);
1616
+ if (!child) {
1617
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
1618
+ continue;
1619
+ }
1620
+ if (child.status === "completed") {
1621
+ return { jobId: childJobId, messageId, output: child.output };
1622
+ }
1623
+ if (child.status === "failed") {
1624
+ const err = child.error;
1625
+ throw new Error(err?.message ?? `Child worker ${calleeWorkerId} failed`);
1626
+ }
1627
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
1628
+ }
1629
+ throw new Error(
1630
+ `Child worker ${calleeWorkerId} (${childJobId}) did not complete within ${pollTimeoutMs}ms`
1631
+ );
1632
+ }
1633
+ return { jobId: childJobId, messageId };
1634
+ };
1635
+ };
628
1636
  if (directJobStore?.setJob) {
629
1637
  try {
630
1638
  await directJobStore.setJob(localJobId, {
@@ -642,10 +1650,16 @@ function createWorker(config) {
642
1650
  });
643
1651
  }
644
1652
  }
1653
+ const baseContext = { jobId: localJobId, workerId: id };
645
1654
  const handlerContext = {
646
- jobId: localJobId,
647
- workerId: id,
648
- ...jobStore ? { jobStore } : {}
1655
+ ...baseContext,
1656
+ ...jobStore ? { jobStore } : {},
1657
+ dispatchWorker: createLocalDispatchWorker(
1658
+ localJobId,
1659
+ id,
1660
+ baseContext,
1661
+ jobStore
1662
+ )
649
1663
  };
650
1664
  try {
651
1665
  if (jobStore) {
@@ -722,13 +1736,18 @@ function createLambdaEntrypoint(agent) {
722
1736
  }
723
1737
  // Annotate the CommonJS export names for ESM import in node:
724
1738
  0 && (module.exports = {
1739
+ SQS_MAX_DELAY_SECONDS,
725
1740
  clearWorkersConfigCache,
726
1741
  createLambdaEntrypoint,
727
1742
  createLambdaHandler,
728
1743
  createWorker,
1744
+ defineWorkerQueue,
729
1745
  dispatch,
730
1746
  dispatchLocal,
1747
+ dispatchQueue,
731
1748
  getWorkersConfig,
732
- resolveQueueUrl
1749
+ getWorkersTriggerUrl,
1750
+ resolveQueueUrl,
1751
+ wrapHandlerForQueue
733
1752
  });
734
1753
  //# sourceMappingURL=index.js.map