@workglow/supabase 0.2.35 → 0.2.37

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,7 +1,7 @@
1
1
  // src/job-queue/SupabaseQueueStorage.ts
2
- import { createServiceToken, deepEqual, makeFingerprint, uuid4 } from "@workglow/util";
2
+ import { JobStatus, validateLeaseMs } from "@workglow/job-queue";
3
3
  import { PollingSubscriptionManager } from "@workglow/storage";
4
- import { JobStatus } from "@workglow/job-queue";
4
+ import { createServiceToken, deepEqual, makeFingerprint, uuid4 } from "@workglow/util";
5
5
  var SUPABASE_QUEUE_STORAGE = createServiceToken("jobqueue.storage.supabase");
6
6
 
7
7
  class SupabaseQueueStorage {
@@ -81,7 +81,8 @@ class SupabaseQueueStorage {
81
81
  return value.replace(/'/g, "''");
82
82
  }
83
83
  async migrate() {
84
- const createTypeSql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus).map((v) => `'${v}'`).join(",")})`;
84
+ const enumValues = [...Object.values(JobStatus), "ABORTING"].filter((v, i, a) => a.indexOf(v) === i).map((v) => `'${v}'`).join(",");
85
+ const createTypeSql = `CREATE TYPE job_status AS ENUM (${enumValues})`;
85
86
  const { error: typeError } = await this.client.rpc("exec_sql", { query: createTypeSql });
86
87
  if (typeError && typeError.code !== "42710") {
87
88
  throw typeError;
@@ -99,10 +100,10 @@ class SupabaseQueueStorage {
99
100
  status job_status NOT NULL default 'PENDING',
100
101
  input jsonb NOT NULL,
101
102
  output jsonb,
102
- run_attempts integer default 0,
103
- max_retries integer default 20,
104
- run_after timestamp with time zone DEFAULT now(),
105
- last_ran_at timestamp with time zone,
103
+ attempts integer default 0,
104
+ max_attempts integer default 10,
105
+ visible_at timestamp with time zone DEFAULT now(),
106
+ last_attempted_at timestamp with time zone,
106
107
  created_at timestamp with time zone DEFAULT now(),
107
108
  deadline_at timestamp with time zone,
108
109
  completed_at timestamp with time zone,
@@ -111,7 +112,9 @@ class SupabaseQueueStorage {
111
112
  progress real DEFAULT 0,
112
113
  progress_message text DEFAULT '',
113
114
  progress_details jsonb,
114
- worker_id text
115
+ lease_owner text,
116
+ abort_requested_at timestamp with time zone,
117
+ lease_expires_at timestamp with time zone
115
118
  )`;
116
119
  const { error: tableError } = await this.client.rpc("exec_sql", { query: createTableSql });
117
120
  if (tableError) {
@@ -119,9 +122,24 @@ class SupabaseQueueStorage {
119
122
  throw tableError;
120
123
  }
121
124
  }
125
+ const alterSqls = [
126
+ `ALTER TABLE ${this.tableName} ADD COLUMN IF NOT EXISTS abort_requested_at timestamp with time zone`,
127
+ `ALTER TABLE ${this.tableName} ADD COLUMN IF NOT EXISTS lease_expires_at timestamp with time zone`,
128
+ `ALTER TABLE ${this.tableName} RENAME COLUMN run_after TO visible_at`,
129
+ `ALTER TABLE ${this.tableName} RENAME COLUMN last_ran_at TO last_attempted_at`,
130
+ `ALTER TABLE ${this.tableName} RENAME COLUMN run_attempts TO attempts`,
131
+ `ALTER TABLE ${this.tableName} RENAME COLUMN max_retries TO max_attempts`,
132
+ `ALTER TABLE ${this.tableName} RENAME COLUMN worker_id TO lease_owner`
133
+ ];
134
+ for (const sql of alterSqls) {
135
+ const { error } = await this.client.rpc("exec_sql", { query: sql });
136
+ if (error && error.code !== "42703") {
137
+ throw new Error(`Failed to rename column: ${error.message}`);
138
+ }
139
+ }
122
140
  const indexes = [
123
- `CREATE INDEX IF NOT EXISTS job_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}id, status, run_after)`,
124
- `CREATE INDEX IF NOT EXISTS job_queue_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}queue, status, run_after)`,
141
+ `CREATE INDEX IF NOT EXISTS job_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}id, status, visible_at)`,
142
+ `CREATE INDEX IF NOT EXISTS job_queue_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}queue, status, visible_at)`,
125
143
  `CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint, status)`
126
144
  ];
127
145
  for (const indexSql of indexes) {
@@ -141,17 +159,17 @@ class SupabaseQueueStorage {
141
159
  job.progress_message = "";
142
160
  job.progress_details = null;
143
161
  job.created_at = now;
144
- job.run_after = now;
162
+ job.visible_at = now;
145
163
  const prefixInsertValues = this.getPrefixInsertValues();
146
164
  const { data, error } = await this.client.from(this.tableName).insert({
147
165
  ...prefixInsertValues,
148
166
  queue: job.queue,
149
167
  fingerprint: job.fingerprint,
150
168
  input: job.input,
151
- run_after: job.run_after,
169
+ visible_at: job.visible_at,
152
170
  created_at: job.created_at,
153
171
  deadline_at: job.deadline_at,
154
- max_retries: job.max_retries,
172
+ max_attempts: job.max_attempts,
155
173
  job_run_id: job.job_run_id,
156
174
  progress: job.progress,
157
175
  progress_message: job.progress_message,
@@ -179,12 +197,14 @@ class SupabaseQueueStorage {
179
197
  num = Number(num) || 100;
180
198
  let query = this.client.from(this.tableName).select("*").eq("queue", this.queueName).eq("status", status);
181
199
  query = this.applyPrefixFilters(query);
182
- const { data, error } = await query.order("run_after", { ascending: true }).limit(num);
200
+ const { data, error } = await query.order("visible_at", { ascending: true }).limit(num);
183
201
  if (error)
184
202
  throw error;
185
203
  return data ?? [];
186
204
  }
187
- async next(workerId) {
205
+ async next(workerId, opts) {
206
+ const leaseMs = opts?.leaseMs ?? 30000;
207
+ validateLeaseMs(leaseMs, "leaseMs");
188
208
  const prefixConditions = this.buildPrefixWhereSql();
189
209
  const validatedQueueName = this.validateSqlValue(this.queueName, "queueName");
190
210
  const validatedWorkerId = this.validateSqlValue(workerId, "workerId");
@@ -192,15 +212,27 @@ class SupabaseQueueStorage {
192
212
  const escapedWorkerId = this.escapeSqlString(validatedWorkerId);
193
213
  const sql = `
194
214
  UPDATE ${this.tableName}
195
- SET status = '${JobStatus.PROCESSING}', last_ran_at = NOW() AT TIME ZONE 'UTC', worker_id = '${escapedWorkerId}'
215
+ SET status = '${JobStatus.PROCESSING}',
216
+ last_attempted_at = NOW() AT TIME ZONE 'UTC',
217
+ lease_owner = '${escapedWorkerId}',
218
+ lease_expires_at = NOW() AT TIME ZONE 'UTC' + (${Number(leaseMs)} * INTERVAL '1 millisecond'),
219
+ -- Lease-expiry reclaim consumes one attempt against max_attempts;
220
+ -- PENDING claims do not (the worker's validateJobState will FAIL
221
+ -- the job when attempts >= max_attempts at next-step time).
222
+ attempts = CASE WHEN status = '${JobStatus.PROCESSING}' THEN attempts + 1 ELSE attempts END,
223
+ -- Always clear stale abort_requested_at on (re)claim so a flag set
224
+ -- by an earlier worker doesn't immediately abort the new lease.
225
+ abort_requested_at = NULL
196
226
  WHERE id = (
197
227
  SELECT id
198
228
  FROM ${this.tableName}
199
229
  WHERE queue = '${escapedQueueName}'
200
- AND status = '${JobStatus.PENDING}'
230
+ AND (
231
+ (status = '${JobStatus.PENDING}' AND visible_at <= NOW() AT TIME ZONE 'UTC')
232
+ OR (status = '${JobStatus.PROCESSING}' AND (lease_expires_at IS NULL OR lease_expires_at < NOW() AT TIME ZONE 'UTC'))
233
+ )
201
234
  ${prefixConditions}
202
- AND run_after <= NOW() AT TIME ZONE 'UTC'
203
- ORDER BY run_after ASC
235
+ ORDER BY visible_at ASC
204
236
  FOR UPDATE SKIP LOCKED
205
237
  LIMIT 1
206
238
  )
@@ -213,6 +245,31 @@ class SupabaseQueueStorage {
213
245
  }
214
246
  return data[0];
215
247
  }
248
+ async extendLease(id, workerId, ms) {
249
+ validateLeaseMs(ms, "ms");
250
+ const validatedWorkerId = this.validateSqlValue(workerId, "workerId");
251
+ const escapedWorkerId = this.escapeSqlString(validatedWorkerId);
252
+ const numericId = Number(id);
253
+ if (!Number.isFinite(numericId)) {
254
+ throw new Error(`Invalid job id: ${id}`);
255
+ }
256
+ const prefixConditions = this.buildPrefixWhereSql();
257
+ const sql = `
258
+ UPDATE ${this.tableName}
259
+ SET lease_expires_at = NOW() AT TIME ZONE 'UTC' + (${Number(ms)} * INTERVAL '1 millisecond')
260
+ WHERE id = ${numericId}
261
+ AND queue = '${this.escapeSqlString(this.validateSqlValue(this.queueName, "queueName"))}'
262
+ AND lease_owner = '${escapedWorkerId}'
263
+ AND status = '${JobStatus.PROCESSING}'
264
+ ${prefixConditions}
265
+ RETURNING id`;
266
+ const { data, error } = await this.client.rpc("exec_sql", { query: sql });
267
+ if (error)
268
+ throw error;
269
+ if (!data || !Array.isArray(data) || data.length === 0) {
270
+ throw new Error(`extendLease failed: job ${String(id)} is not PROCESSING or lease is not owned by worker ${workerId}`);
271
+ }
272
+ }
216
273
  async size(status = JobStatus.PENDING) {
217
274
  let query = this.client.from(this.tableName).select("*", { count: "exact", head: true }).eq("queue", this.queueName).eq("status", status);
218
275
  query = this.applyPrefixFilters(query);
@@ -238,7 +295,7 @@ class SupabaseQueueStorage {
238
295
  progress_message: "",
239
296
  progress_details: null,
240
297
  completed_at: now,
241
- last_ran_at: now
298
+ last_attempted_at: now
242
299
  }).eq("id", jobDetails.id).eq("queue", this.queueName);
243
300
  query2 = this.applyPrefixFilters(query2);
244
301
  const { error: error2 } = await query2;
@@ -246,25 +303,25 @@ class SupabaseQueueStorage {
246
303
  throw error2;
247
304
  return;
248
305
  }
249
- let getQuery = this.client.from(this.tableName).select("run_attempts, max_retries").eq("id", jobDetails.id).eq("queue", this.queueName);
306
+ let getQuery = this.client.from(this.tableName).select("attempts, max_attempts").eq("id", jobDetails.id).eq("queue", this.queueName);
250
307
  getQuery = this.applyPrefixFilters(getQuery);
251
308
  const { data: current, error: getError } = await getQuery.single();
252
309
  if (getError)
253
310
  throw getError;
254
- const currentAttempts = current?.run_attempts ?? 0;
255
- const maxRetries = current?.max_retries ?? jobDetails.max_retries ?? 10;
311
+ const currentAttempts = current?.attempts ?? 0;
312
+ const maxAttempts = current?.max_attempts ?? jobDetails.max_attempts ?? 10;
256
313
  const nextAttempts = currentAttempts + 1;
257
314
  if (jobDetails.status === JobStatus.PENDING) {
258
- if (nextAttempts > maxRetries) {
315
+ if (nextAttempts >= maxAttempts) {
259
316
  let failQuery = this.client.from(this.tableName).update({
260
317
  status: JobStatus.FAILED,
261
- error: "Max retries reached",
262
- error_code: "MAX_RETRIES_REACHED",
318
+ error: "Max attempts reached",
319
+ error_code: "MAX_ATTEMPTS_REACHED",
263
320
  progress: 100,
264
321
  progress_message: "",
265
322
  progress_details: null,
266
323
  completed_at: now,
267
- last_ran_at: now
324
+ last_attempted_at: now
268
325
  }).eq("id", jobDetails.id).eq("queue", this.queueName);
269
326
  failQuery = this.applyPrefixFilters(failQuery);
270
327
  const { error: failError } = await failQuery;
@@ -276,12 +333,13 @@ class SupabaseQueueStorage {
276
333
  error: jobDetails.error ?? null,
277
334
  error_code: jobDetails.error_code ?? null,
278
335
  status: jobDetails.status,
279
- run_after: jobDetails.run_after,
336
+ visible_at: jobDetails.visible_at,
280
337
  progress: 0,
281
338
  progress_message: "",
282
339
  progress_details: null,
283
- run_attempts: nextAttempts,
284
- last_ran_at: now
340
+ attempts: nextAttempts,
341
+ last_attempted_at: now,
342
+ abort_requested_at: null
285
343
  }).eq("id", jobDetails.id).eq("queue", this.queueName);
286
344
  query2 = this.applyPrefixFilters(query2);
287
345
  const { error: error2 } = await query2;
@@ -298,9 +356,9 @@ class SupabaseQueueStorage {
298
356
  progress: 100,
299
357
  progress_message: "",
300
358
  progress_details: null,
301
- run_attempts: nextAttempts,
359
+ attempts: nextAttempts,
302
360
  completed_at: now,
303
- last_ran_at: now
361
+ last_attempted_at: now
304
362
  }).eq("id", jobDetails.id).eq("queue", this.queueName);
305
363
  query2 = this.applyPrefixFilters(query2);
306
364
  const { error: error2 } = await query2;
@@ -313,28 +371,60 @@ class SupabaseQueueStorage {
313
371
  output: jobDetails.output ?? null,
314
372
  error: jobDetails.error ?? null,
315
373
  error_code: jobDetails.error_code ?? null,
316
- run_after: jobDetails.run_after ?? null,
317
- run_attempts: nextAttempts,
318
- last_ran_at: now
374
+ visible_at: jobDetails.visible_at ?? null,
375
+ attempts: nextAttempts,
376
+ last_attempted_at: now
319
377
  }).eq("id", jobDetails.id).eq("queue", this.queueName);
320
378
  query = this.applyPrefixFilters(query);
321
379
  const { error } = await query;
322
380
  if (error)
323
381
  throw error;
324
382
  }
325
- async release(jobId) {
383
+ async releaseClaim(jobId) {
326
384
  let query = this.client.from(this.tableName).update({
327
385
  status: JobStatus.PENDING,
328
- worker_id: null,
386
+ lease_owner: null,
329
387
  progress: 0,
330
388
  progress_message: "",
331
- progress_details: null
389
+ progress_details: null,
390
+ abort_requested_at: null
332
391
  }).eq("id", jobId).eq("queue", this.queueName);
333
392
  query = this.applyPrefixFilters(query);
334
393
  const { error } = await query;
335
394
  if (error)
336
395
  throw error;
337
396
  }
397
+ async finalize(id, fields) {
398
+ const patch = {};
399
+ if ("output" in fields)
400
+ patch.output = fields.output ?? null;
401
+ if ("error" in fields)
402
+ patch.error = fields.error ?? null;
403
+ if ("error_code" in fields)
404
+ patch.error_code = fields.error_code ?? null;
405
+ if ("status" in fields)
406
+ patch.status = fields.status;
407
+ if ("completed_at" in fields)
408
+ patch.completed_at = fields.completed_at ?? null;
409
+ if ("abort_requested_at" in fields) {
410
+ patch.abort_requested_at = fields.abort_requested_at ?? null;
411
+ }
412
+ if ("lease_owner" in fields)
413
+ patch.lease_owner = fields.lease_owner ?? null;
414
+ if ("progress" in fields)
415
+ patch.progress = fields.progress ?? 0;
416
+ if ("progress_message" in fields)
417
+ patch.progress_message = fields.progress_message ?? "";
418
+ if ("progress_details" in fields)
419
+ patch.progress_details = fields.progress_details ?? null;
420
+ if (Object.keys(patch).length === 0)
421
+ return;
422
+ let query = this.client.from(this.tableName).update(patch).eq("id", id).eq("queue", this.queueName);
423
+ query = this.applyPrefixFilters(query);
424
+ const { error } = await query;
425
+ if (error)
426
+ throw error;
427
+ }
338
428
  async deleteAll() {
339
429
  let query = this.client.from(this.tableName).delete().eq("queue", this.queueName);
340
430
  query = this.applyPrefixFilters(query);
@@ -355,7 +445,28 @@ class SupabaseQueueStorage {
355
445
  return data?.output ?? null;
356
446
  }
357
447
  async abort(jobId) {
358
- let query = this.client.from(this.tableName).update({ status: JobStatus.ABORTING }).eq("id", jobId).eq("queue", this.queueName);
448
+ const now = new Date().toISOString();
449
+ {
450
+ let query = this.client.from(this.tableName).update({
451
+ status: JobStatus.FAILED,
452
+ abort_requested_at: now,
453
+ completed_at: now
454
+ }).eq("id", jobId).eq("queue", this.queueName).eq("status", JobStatus.PENDING);
455
+ query = this.applyPrefixFilters(query);
456
+ const { error } = await query;
457
+ if (error)
458
+ throw error;
459
+ }
460
+ {
461
+ let query = this.client.from(this.tableName).update({ abort_requested_at: now }).eq("id", jobId).eq("queue", this.queueName).eq("status", JobStatus.PROCESSING);
462
+ query = this.applyPrefixFilters(query);
463
+ const { error } = await query;
464
+ if (error)
465
+ throw error;
466
+ }
467
+ }
468
+ async saveStatus(jobId, status) {
469
+ let query = this.client.from(this.tableName).update({ status }).eq("id", jobId).eq("queue", this.queueName);
359
470
  query = this.applyPrefixFilters(query);
360
471
  const { error } = await query;
361
472
  if (error)
@@ -761,11 +872,220 @@ class SupabaseRateLimiterStorage {
761
872
  throw nextError;
762
873
  }
763
874
  }
875
+ // src/job-queue/SupabaseMessageQueue.ts
876
+ class SupabaseClaim {
877
+ core;
878
+ pending;
879
+ id;
880
+ body;
881
+ attempts;
882
+ workerId;
883
+ constructor(core, pending, id, body, attempts, workerId) {
884
+ this.core = core;
885
+ this.pending = pending;
886
+ this.id = id;
887
+ this.body = body;
888
+ this.attempts = attempts;
889
+ this.workerId = workerId;
890
+ }
891
+ async ack(result) {
892
+ const buf = this.pending.get(this.id);
893
+ this.pending.delete(this.id);
894
+ const current = await this.core.get(this.id) ?? this.body;
895
+ const output = result !== undefined ? result : buf?.output !== undefined ? buf.output : current.output ?? null;
896
+ await this.core.finalize(this.id, {
897
+ output,
898
+ error: null,
899
+ error_code: null,
900
+ status: "COMPLETED",
901
+ completed_at: current.completed_at ?? new Date().toISOString()
902
+ });
903
+ }
904
+ async retry(opts) {
905
+ this.pending.delete(this.id);
906
+ const delay = opts?.delaySeconds ?? 0;
907
+ const current = await this.core.get(this.id) ?? this.body;
908
+ await this.core.complete({
909
+ ...current,
910
+ status: "PENDING",
911
+ lease_owner: null,
912
+ lease_expires_at: null,
913
+ visible_at: new Date(Date.now() + delay * 1000).toISOString(),
914
+ progress: 0,
915
+ progress_message: "",
916
+ progress_details: null
917
+ });
918
+ }
919
+ async fail(opts) {
920
+ opts?.permanent;
921
+ const buf = this.pending.get(this.id);
922
+ this.pending.delete(this.id);
923
+ const current = await this.core.get(this.id) ?? this.body;
924
+ const error = opts?.error !== undefined ? opts.error : buf?.error !== undefined ? buf.error : current.error ?? null;
925
+ const errorCode = opts?.errorCode !== undefined ? opts.errorCode : buf?.errorCode !== undefined ? buf.errorCode : current.error_code ?? null;
926
+ const abortRequested = opts?.abortRequested !== undefined ? opts.abortRequested : buf?.abortRequested ?? false;
927
+ await this.core.finalize(this.id, {
928
+ error,
929
+ error_code: errorCode,
930
+ abort_requested_at: abortRequested ? current.abort_requested_at ?? new Date().toISOString() : current.abort_requested_at ?? null,
931
+ status: "FAILED",
932
+ completed_at: current.completed_at ?? new Date().toISOString()
933
+ });
934
+ }
935
+ async extendLease(ms) {
936
+ await this.core.extendLease(this.id, this.workerId, ms);
937
+ }
938
+ async disable() {
939
+ this.pending.delete(this.id);
940
+ const current = await this.core.get(this.id);
941
+ const completedAt = current?.completed_at ?? new Date().toISOString();
942
+ await this.core.finalize(this.id, {
943
+ status: "DISABLED",
944
+ completed_at: completedAt,
945
+ lease_owner: null,
946
+ progress: 0,
947
+ progress_message: "",
948
+ progress_details: null
949
+ });
950
+ }
951
+ }
952
+
953
+ class SupabaseMessageQueue {
954
+ scope;
955
+ core;
956
+ pending;
957
+ constructor(core, pending) {
958
+ this.core = core;
959
+ this.pending = pending;
960
+ this.scope = core.scope;
961
+ }
962
+ async send(body, opts) {
963
+ return this.core.add(applySendOptions(body, opts));
964
+ }
965
+ async sendBatch(bodies, opts) {
966
+ const ids = [];
967
+ for (const body of bodies) {
968
+ ids.push(await this.send(body, opts));
969
+ }
970
+ return ids;
971
+ }
972
+ async receive(opts) {
973
+ const max = Math.max(1, opts.max ?? 1);
974
+ const claims = [];
975
+ while (claims.length < max) {
976
+ const job = await this.core.next(opts.workerId, { leaseMs: opts.leaseMs });
977
+ if (!job)
978
+ break;
979
+ claims.push(new SupabaseClaim(this.core, this.pending, job.id, job, job.attempts ?? 0, opts.workerId));
980
+ }
981
+ return claims;
982
+ }
983
+ async releaseClaim(id) {
984
+ this.pending.delete(id);
985
+ await this.core.releaseClaim(id);
986
+ }
987
+ async migrate() {
988
+ await this.core.migrate();
989
+ }
990
+ getMigrations() {
991
+ return this.core.getMigrations();
992
+ }
993
+ subscribeToChanges(callback, options) {
994
+ return this.core.subscribeToChanges(callback, options);
995
+ }
996
+ }
997
+ function applySendOptions(body, opts) {
998
+ if (!opts)
999
+ return body;
1000
+ const out = { ...body };
1001
+ if (opts.delaySeconds != null) {
1002
+ out.visible_at = new Date(Date.now() + opts.delaySeconds * 1000).toISOString();
1003
+ }
1004
+ if (opts.timeoutSeconds != null) {
1005
+ out.deadline_at = new Date(Date.now() + opts.timeoutSeconds * 1000).toISOString();
1006
+ }
1007
+ if (opts.fingerprint != null)
1008
+ out.fingerprint = opts.fingerprint;
1009
+ if (opts.jobRunId != null)
1010
+ out.job_run_id = opts.jobRunId;
1011
+ if (opts.maxAttempts != null)
1012
+ out.max_attempts = opts.maxAttempts;
1013
+ return out;
1014
+ }
1015
+ // src/job-queue/SupabaseJobStore.ts
1016
+ class SupabaseJobStore {
1017
+ core;
1018
+ pending;
1019
+ constructor(core, pending) {
1020
+ this.core = core;
1021
+ this.pending = pending;
1022
+ }
1023
+ get(id) {
1024
+ return this.core.get(id);
1025
+ }
1026
+ async peek(status, num) {
1027
+ return this.core.peek(status, num);
1028
+ }
1029
+ size(status) {
1030
+ return this.core.size(status);
1031
+ }
1032
+ async getByRunId(runId) {
1033
+ return this.core.getByRunId(runId);
1034
+ }
1035
+ outputForInput(input) {
1036
+ return this.core.outputForInput(input);
1037
+ }
1038
+ async saveProgress(id, progress, message, details) {
1039
+ await this.core.saveProgress(id, progress, message, details);
1040
+ }
1041
+ async saveResult(id, output) {
1042
+ const buf = this.pending.get(id) ?? {};
1043
+ buf.output = output ?? null;
1044
+ this.pending.set(id, buf);
1045
+ }
1046
+ async saveError(id, error, errorCode, abortRequested) {
1047
+ const buf = this.pending.get(id) ?? {};
1048
+ buf.error = error;
1049
+ buf.errorCode = errorCode;
1050
+ buf.abortRequested = abortRequested;
1051
+ this.pending.set(id, buf);
1052
+ }
1053
+ async deleteByStatusAndAge(status, olderThanMs) {
1054
+ await this.core.deleteJobsByStatusAndAge(status, olderThanMs);
1055
+ }
1056
+ async delete(id) {
1057
+ this.pending.delete(id);
1058
+ await this.core.delete(id);
1059
+ }
1060
+ async deleteAll() {
1061
+ this.pending.clear();
1062
+ await this.core.deleteAll();
1063
+ }
1064
+ async abort(id) {
1065
+ await this.core.abort(id);
1066
+ }
1067
+ async saveStatus(id, status) {
1068
+ await this.core.saveStatus(id, status);
1069
+ }
1070
+ }
1071
+ // src/job-queue/createSupabaseQueue.ts
1072
+ function createSupabaseQueue(queueName, client, opts) {
1073
+ const core = new SupabaseQueueStorage(client, queueName, opts);
1074
+ const pending = new Map;
1075
+ return {
1076
+ messageQueue: new SupabaseMessageQueue(core, pending),
1077
+ jobStore: new SupabaseJobStore(core, pending),
1078
+ core
1079
+ };
1080
+ }
764
1081
  export {
1082
+ createSupabaseQueue,
765
1083
  SupabaseRateLimiterStorage,
766
1084
  SupabaseQueueStorage,
1085
+ SupabaseMessageQueue,
1086
+ SupabaseJobStore,
767
1087
  SUPABASE_RATE_LIMITER_STORAGE,
768
1088
  SUPABASE_QUEUE_STORAGE
769
1089
  };
770
1090
 
771
- //# debugId=E7022DCB352EE4D964756E2164756E21
1091
+ //# debugId=E36B21D32DCC1A8364756E2164756E21