@workglow/supabase 0.2.30 → 0.2.32

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/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # @workglow/supabase
2
+
3
+ Supabase backends for @workglow/storage and @workglow/job-queue.
4
+
5
+ ## Features
6
+
7
+ - Supabase implementation of `@workglow/storage` interfaces
8
+ - Supabase implementation of `@workglow/job-queue` interfaces
9
+ - Persistent storage for tasks, vectors, and queues
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @workglow/supabase
15
+ # or
16
+ bun add @workglow/supabase
17
+ # or
18
+ yarn add @workglow/supabase
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```typescript
24
+ import { SupabaseTabularStorage } from "@workglow/supabase/storage";
25
+ import { SupabaseQueueStorage } from "@workglow/supabase/job-queue";
26
+
27
+ const storage = new SupabaseTabularStorage(connectionConfig);
28
+ const queue = new SupabaseQueueStorage(connectionConfig);
29
+ ```
30
+
31
+ ## License
32
+
33
+ Apache 2.0 - See [LICENSE](../../LICENSE) for details.
@@ -61,7 +61,19 @@ export declare class SupabaseQueueStorage<Input, Output> implements IQueueStorag
61
61
  * Escapes a string value for use in SQL
62
62
  */
63
63
  private escapeSqlString;
64
- setupDatabase(): Promise<void>;
64
+ /**
65
+ * Schema setup for Supabase queues.
66
+ *
67
+ * Supabase exposes the SQL surface via a side-installed `exec_sql` RPC
68
+ * rather than the `pg.Pool` shape the {@link PostgresMigrationRunner} is
69
+ * built against, so this storage doesn't share the runner's bookkeeping
70
+ * table — it runs idempotent CREATE-IF-NOT-EXISTS statements directly via
71
+ * the RPC. {@link getMigrations} returns an empty array for the same
72
+ * reason.
73
+ */
74
+ migrate(): Promise<void>;
75
+ /** Supabase queue runs DDL via `exec_sql`, not via the migration runner. */
76
+ getMigrations(): ReadonlyArray<unknown>;
65
77
  /**
66
78
  * Adds a new job to the queue.
67
79
  * @param job - The job to add
@@ -1 +1 @@
1
- {"version":3,"file":"SupabaseQueueStorage.d.ts","sourceRoot":"","sources":["../../src/job-queue/SupabaseQueueStorage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG7E,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAElB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,sBAAsB,gEAElC,CAAC;AAEF;;;GAGG;AACH,qBAAa,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAE,YAAW,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC;IAepF,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM;IAdtC,SAAgB,KAAK,EAAG,SAAS,CAAU;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAC1C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IACrD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IAC3E,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IACrC,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,cAAc,CAIN;IAEhB,YACE,MAAM,EAAE,cAAc,EACH,SAAS,EAAE,MAAM,EACpC,OAAO,CAAC,EAAE,mBAAmB,EAY9B;IAED;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAElE;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,OAAO,CAAC,eAAe;IAIV,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA8D1C;IAED;;;;OAIG;IACU,GAAG,CAAC,GAAG,EAAE,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAsCvE;IAED;;;;OAIG;IACU,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CAiBlF;IAED;;;;;OAKG;IACU,IAAI,CACf,MAAM,GAAE,SAA6B,EACrC,GAAG,GAAE,MAAY,GAChB,OAAO,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,CAe5C;IAED;;;;;OAKG;IACU,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CAkCxF;IAED;;;;OAIG;IACU,IAAI,CAAC,MAAM,YAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAa7D;YAQa,UAAU;IAWxB;;;;;OAKG;IACU,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CA0HhF;IAED;;OAEG;IACU,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBlD;IAED;;OAEG;IACU,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAMtC;IAED;;;;OAIG;IACU,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBhE;IAED;;;;;OAKG;IACU,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhD;IAED;;;;OAIG;IACU,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAY3F;IAED;;OAEG;IACU,YAAY,CACvB,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CAef;IAED;;OAEG;IACU,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAWjD;IAED;;;;OAIG;IACU,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3F;IAED;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAiC3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;YA8Bd,oBAAoB;IAgBlC;;;;;;;OAOG;IACI,kBAAkB,CACvB,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAC7D,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,IAAI,CAEZ;IAED;;;;;;OAMG;IACH,SAAS,CAAC,8BAA8B,CACtC,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAC7D,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,GACvD,MAAM,IAAI,CA+CZ;IAED;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;;OAGG;IACH,OAAO,CAAC,sCAAsC;IA8C9C;;;;;;;;;OASG;IACH,SAAS,CAAC,6BAA6B,CACrC,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAC7D,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,IAAI,CAgBZ;CACF"}
1
+ {"version":3,"file":"SupabaseQueueStorage.d.ts","sourceRoot":"","sources":["../../src/job-queue/SupabaseQueueStorage.ts"],"names":[],"mappings":"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"}
@@ -39,7 +39,14 @@ export declare class SupabaseRateLimiterStorage implements IRateLimiterStorage {
39
39
  * Gets prefix values as an object for inserts.
40
40
  */
41
41
  private getPrefixInsertValues;
42
- setupDatabase(): Promise<void>;
42
+ /**
43
+ * Schema setup for Supabase rate-limiter. See
44
+ * {@link SupabaseQueueStorage.migrate} for why this storage doesn't share
45
+ * the {@link PostgresMigrationRunner} bookkeeping table.
46
+ */
47
+ migrate(): Promise<void>;
48
+ /** Supabase rate-limiter runs DDL via `exec_sql`, not via the migration runner. */
49
+ getMigrations(): ReadonlyArray<unknown>;
43
50
  /** Stable function name derived from table name (Postgres identifiers ≤63 chars). */
44
51
  private atomicReserveFunctionName;
45
52
  tryReserveExecution(queueName: string, maxExecutions: number, windowMs: number): Promise<unknown | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"SupabaseRateLimiterStorage.d.ts","sourceRoot":"","sources":["../../src/job-queue/SupabaseRateLimiterStorage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,6BAA6B,4DAEzC,CAAC;AAEF;;;GAGG;AACH,qBAAa,0BAA2B,YAAW,mBAAmB;IACpE,SAAgB,KAAK,EAAE,uBAAuB,CAAa;IAC3D,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAC1C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IACrD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IAC3E,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IAC9C,SAAS,CAAC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IAElD,YAAY,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,yBAAyB,EAc/D;IAED;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAQhB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAqG1C;IAED,qFAAqF;IACrF,OAAO,CAAC,yBAAyB;IAIpB,mBAAmB,CAC9B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA0BzB;IAEY,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9E;IAEY,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS7D;IAEY,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAa1F;IAEY,0BAA0B,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7B;IAEY,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiBhF;IAEY,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB3F;IAEY,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAanD;CACF"}
1
+ {"version":3,"file":"SupabaseRateLimiterStorage.d.ts","sourceRoot":"","sources":["../../src/job-queue/SupabaseRateLimiterStorage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,6BAA6B,4DAEzC,CAAC;AAEF;;;GAGG;AACH,qBAAa,0BAA2B,YAAW,mBAAmB;IACpE,SAAgB,KAAK,EAAE,uBAAuB,CAAa;IAC3D,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAC1C,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IACrD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IAC3E,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IAC9C,SAAS,CAAC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IAElD,YAAY,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,yBAAyB,EAc/D;IAED;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;;OAIG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAqGpC;IAED,mFAAmF;IAC5E,aAAa,IAAI,aAAa,CAAC,OAAO,CAAC,CAE7C;IAED,qFAAqF;IACrF,OAAO,CAAC,yBAAyB;IAIpB,mBAAmB,CAC9B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA0BzB;IAEY,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9E;IAEY,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS7D;IAEY,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAa1F;IAEY,0BAA0B,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAe7B;IAEY,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiBhF;IAEY,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB3F;IAEY,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAanD;CACF"}
@@ -80,7 +80,7 @@ class SupabaseQueueStorage {
80
80
  escapeSqlString(value) {
81
81
  return value.replace(/'/g, "''");
82
82
  }
83
- async setupDatabase() {
83
+ async migrate() {
84
84
  const createTypeSql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus).map((v) => `'${v}'`).join(",")})`;
85
85
  const { error: typeError } = await this.client.rpc("exec_sql", { query: createTypeSql });
86
86
  if (typeError && typeError.code !== "42710") {
@@ -128,6 +128,9 @@ class SupabaseQueueStorage {
128
128
  await this.client.rpc("exec_sql", { query: indexSql });
129
129
  }
130
130
  }
131
+ getMigrations() {
132
+ return [];
133
+ }
131
134
  async add(job) {
132
135
  const now = new Date().toISOString();
133
136
  job.queue = this.queueName;
@@ -579,7 +582,7 @@ class SupabaseRateLimiterStorage {
579
582
  }
580
583
  return values;
581
584
  }
582
- async setupDatabase() {
585
+ async migrate() {
583
586
  const prefixColumnsSql = this.buildPrefixColumnsSql();
584
587
  const prefixColumnNames = this.getPrefixColumnNames();
585
588
  const prefixIndexPrefix = prefixColumnNames.length > 0 ? prefixColumnNames.join(", ") + ", " : "";
@@ -656,6 +659,9 @@ class SupabaseRateLimiterStorage {
656
659
  throw fnError;
657
660
  }
658
661
  }
662
+ getMigrations() {
663
+ return [];
664
+ }
659
665
  atomicReserveFunctionName() {
660
666
  return `${this.executionTableName}_try_reserve`.slice(0, 63);
661
667
  }
@@ -762,4 +768,4 @@ export {
762
768
  SUPABASE_QUEUE_STORAGE
763
769
  };
764
770
 
765
- //# debugId=535B74F0E33B129364756E2164756E21
771
+ //# debugId=E7022DCB352EE4D964756E2164756E21
@@ -2,10 +2,10 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/job-queue/SupabaseQueueStorage.ts", "../../src/job-queue/SupabaseRateLimiterStorage.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { RealtimeChannel, SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken, deepEqual, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport { PollingSubscriptionManager } from \"@workglow/storage\";\nimport { JobStatus } from \"@workglow/job-queue\";\nimport type {\n IQueueStorage,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueChangeType,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"@workglow/job-queue\";\n\nexport const SUPABASE_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.supabase\"\n);\n\n/**\n * Supabase implementation of a job queue.\n * Provides storage and retrieval for job execution states using Supabase.\n */\nexport class SupabaseQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n public readonly scope = \"cluster\" as const;\n protected readonly client: SupabaseClient;\n protected readonly prefixes: readonly PrefixColumn[];\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n protected readonly tableName: string;\n private realtimeChannel: RealtimeChannel | null = null;\n private pollingManager: PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > | null = null;\n\n constructor(\n client: SupabaseClient,\n protected readonly queueName: string,\n options?: QueueStorageOptions\n ) {\n this.client = client as SupabaseClient;\n this.prefixes = options?.prefixes ?? [];\n this.prefixValues = options?.prefixValues ?? {};\n // Generate table name based on prefix configuration to avoid column conflicts\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.tableName = `job_queue_${prefixNames}`;\n } else {\n this.tableName = \"job_queue\";\n }\n }\n\n /**\n * Gets the SQL column type for a prefix column (Supabase supports UUID natively)\n */\n private getPrefixColumnType(type: PrefixColumn[\"type\"]): string {\n return type === \"uuid\" ? \"UUID\" : \"INTEGER\";\n }\n\n /**\n * Builds the prefix columns SQL for CREATE TABLE\n */\n private buildPrefixColumnsSql(): string {\n if (this.prefixes.length === 0) return \"\";\n return (\n this.prefixes\n .map((p) => `${p.name} ${this.getPrefixColumnType(p.type)} NOT NULL`)\n .join(\",\\n \") + \",\\n \"\n );\n }\n\n /**\n * Builds prefix column names for use in queries\n */\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n /**\n * Applies prefix filters to a Supabase query builder\n */\n private applyPrefixFilters<T>(query: T): T {\n let result = query as any;\n for (const prefix of this.prefixes) {\n result = result.eq(prefix.name, this.prefixValues[prefix.name]);\n }\n return result as T;\n }\n\n /**\n * Gets prefix values as an object for inserts\n */\n private getPrefixInsertValues(): Record<string, string | number> {\n const values: Record<string, string | number> = {};\n for (const prefix of this.prefixes) {\n values[prefix.name] = this.prefixValues[prefix.name];\n }\n return values;\n }\n\n /**\n * Builds WHERE clause conditions for prefix filtering with inline values (for raw SQL)\n * @returns SQL conditions string with values inlined\n */\n private buildPrefixWhereSql(): string {\n if (this.prefixes.length === 0) {\n return \"\";\n }\n const conditions = this.prefixes\n .map((p) => {\n const value = this.prefixValues[p.name];\n if (p.type === \"uuid\") {\n const validated = this.validateSqlValue(String(value), `prefix \"${p.name}\"`);\n return `${p.name} = '${this.escapeSqlString(validated)}'`;\n }\n const numValue = Number(value ?? 0);\n if (!Number.isFinite(numValue)) {\n throw new Error(`Invalid numeric prefix value for \"${p.name}\": ${value}`);\n }\n return `${p.name} = ${numValue}`;\n })\n .join(\" AND \");\n return \" AND \" + conditions;\n }\n\n /**\n * Regex for validating SQL literal-safe strings.\n * Used for quoted values (e.g. queue names/IDs) and only allows alphanumeric\n * characters, underscores, hyphens, colons, and periods.\n */\n private static readonly SAFE_SQL_VALUE_RE = /^[a-zA-Z0-9_\\-.:]+$/;\n\n /**\n * Validates that a string value is safe for use as a quoted SQL literal.\n * Throws an error if the value contains characters outside SAFE_SQL_VALUE_RE.\n */\n private validateSqlValue(value: string, context: string): string {\n if (!SupabaseQueueStorage.SAFE_SQL_VALUE_RE.test(value)) {\n throw new Error(\n `Unsafe value for ${context}: \"${value}\". Values must match /^[a-zA-Z0-9_\\\\-.:]+$/.`\n );\n }\n return value;\n }\n\n /**\n * Escapes a string value for use in SQL\n */\n private escapeSqlString(value: string): string {\n return value.replace(/'/g, \"''\");\n }\n\n public async setupDatabase(): Promise<void> {\n // Note: For Supabase, table creation should typically be done through migrations\n // This setup assumes the table already exists or uses exec_sql RPC function\n const createTypeSql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus)\n .map((v) => `'${v}'`)\n .join(\",\")})`;\n\n const { error: typeError } = await this.client.rpc(\"exec_sql\", { query: createTypeSql });\n // Ignore error if type already exists (code 42710)\n if (typeError && typeError.code !== \"42710\") {\n throw typeError;\n }\n\n const prefixColumnsSql = this.buildPrefixColumnsSql();\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixIndexPrefix =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const indexSuffix = prefixColumnNames.length > 0 ? \"_\" + prefixColumnNames.join(\"_\") : \"\";\n\n const createTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n id SERIAL NOT NULL,\n ${prefixColumnsSql}fingerprint text NOT NULL,\n queue text NOT NULL,\n job_run_id text NOT NULL,\n status job_status NOT NULL default 'PENDING',\n input jsonb NOT NULL,\n output jsonb,\n run_attempts integer default 0,\n max_retries integer default 20,\n run_after timestamp with time zone DEFAULT now(),\n last_ran_at timestamp with time zone,\n created_at timestamp with time zone DEFAULT now(),\n deadline_at timestamp with time zone,\n completed_at timestamp with time zone,\n error text,\n error_code text,\n progress real DEFAULT 0,\n progress_message text DEFAULT '',\n progress_details jsonb,\n worker_id text\n )`;\n\n const { error: tableError } = await this.client.rpc(\"exec_sql\", { query: createTableSql });\n if (tableError) {\n // Ignore error if table already exists (code 42P07)\n if (tableError.code !== \"42P07\") {\n throw tableError;\n }\n }\n\n // Create indexes with prefix columns prepended\n const indexes = [\n `CREATE INDEX IF NOT EXISTS job_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}id, status, run_after)`,\n `CREATE INDEX IF NOT EXISTS job_queue_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}queue, status, run_after)`,\n `CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint, status)`,\n ];\n\n for (const indexSql of indexes) {\n await this.client.rpc(\"exec_sql\", { query: indexSql });\n // Ignore index creation errors\n }\n }\n\n /**\n * Adds a new job to the queue.\n * @param job - The job to add\n * @returns The ID of the added job\n */\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const now = new Date().toISOString();\n job.queue = this.queueName;\n job.job_run_id = job.job_run_id ?? uuid4();\n job.fingerprint = await makeFingerprint(job.input);\n job.status = JobStatus.PENDING;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n job.created_at = now;\n job.run_after = now;\n\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { data, error } = await this.client\n .from(this.tableName)\n .insert({\n ...prefixInsertValues,\n queue: job.queue,\n fingerprint: job.fingerprint,\n input: job.input,\n run_after: job.run_after,\n created_at: job.created_at,\n deadline_at: job.deadline_at,\n max_retries: job.max_retries,\n job_run_id: job.job_run_id,\n progress: job.progress,\n progress_message: job.progress_message,\n progress_details: job.progress_details,\n })\n .select(\"id\")\n .single();\n\n if (error) throw error;\n if (!data) throw new Error(\"Failed to add to queue\");\n\n job.id = data.id;\n return job.id;\n }\n\n /**\n * Retrieves a job by its ID.\n * @param id - The ID of the job to retrieve\n * @returns The job if found, undefined otherwise\n */\n public async get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"id\", id)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return undefined; // Not found\n throw error;\n }\n\n return data as JobStorageFormat<Input, Output> | undefined;\n }\n\n /**\n * Retrieves a slice of jobs from the queue.\n * @param status - The status to filter by\n * @param num - Maximum number of jobs to return\n * @returns An array of jobs\n */\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<JobStorageFormat<Input, Output>[]> {\n num = Number(num) || 100;\n\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.order(\"run_after\", { ascending: true }).limit(num);\n\n if (error) throw error;\n return (data as JobStorageFormat<Input, Output>[]) ?? [];\n }\n\n /**\n * Retrieves the next available job that is ready to be processed.\n * Uses atomic UPDATE with subquery SELECT FOR UPDATE SKIP LOCKED to prevent race conditions.\n * @param workerId - Worker ID to associate with the job (required)\n * @returns The next job or undefined if no job is available\n */\n public async next(workerId: string): Promise<JobStorageFormat<Input, Output> | undefined> {\n const prefixConditions = this.buildPrefixWhereSql();\n const validatedQueueName = this.validateSqlValue(this.queueName, \"queueName\");\n const validatedWorkerId = this.validateSqlValue(workerId, \"workerId\");\n const escapedQueueName = this.escapeSqlString(validatedQueueName);\n const escapedWorkerId = this.escapeSqlString(validatedWorkerId);\n\n // Use the same atomic UPDATE...WHERE id = (SELECT...FOR UPDATE SKIP LOCKED) pattern as PostgresQueueStorage\n const sql = `\n UPDATE ${this.tableName}\n SET status = '${JobStatus.PROCESSING}', last_ran_at = NOW() AT TIME ZONE 'UTC', worker_id = '${escapedWorkerId}'\n WHERE id = (\n SELECT id\n FROM ${this.tableName}\n WHERE queue = '${escapedQueueName}'\n AND status = '${JobStatus.PENDING}'\n ${prefixConditions}\n AND run_after <= NOW() AT TIME ZONE 'UTC'\n ORDER BY run_after ASC\n FOR UPDATE SKIP LOCKED\n LIMIT 1\n )\n RETURNING *`;\n\n const { data, error } = await this.client.rpc(\"exec_sql\", { query: sql });\n\n if (error) throw error;\n\n // exec_sql returns result rows as an array\n if (!data || !Array.isArray(data) || data.length === 0) {\n return undefined;\n }\n\n return data[0] as JobStorageFormat<Input, Output>;\n }\n\n /**\n * Retrieves the number of jobs in the queue with a specific status.\n * @param status - The status of the jobs to count\n * @returns The count of jobs with the specified status\n */\n public async size(status = JobStatus.PENDING): Promise<number> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\", { count: \"exact\", head: true })\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status);\n\n query = this.applyPrefixFilters(query);\n\n const { count, error } = await query;\n\n if (error) throw error;\n return count ?? 0;\n }\n\n /**\n * Gets all jobs from the queue that match the current prefix values.\n * Used internally for polling-based subscriptions.\n *\n * @returns An array of jobs\n */\n private async getAllJobs(): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client.from(this.tableName).select(\"*\").eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query;\n\n if (error) throw error;\n return (data ?? []) as Array<JobStorageFormat<Input, Output>>;\n }\n\n /**\n * Marks a job as complete with its output or error.\n * Enhanced error handling:\n * - For a retryable error, increments run_attempts and updates run_after.\n * - Marks a job as FAILED immediately for permanent or generic errors.\n */\n public async complete(jobDetails: JobStorageFormat<Input, Output>): Promise<void> {\n const now = new Date().toISOString();\n\n // Handle disabled without changing attempts\n if (jobDetails.status === JobStatus.DISABLED) {\n let query = this.client\n .from(this.tableName)\n .update({\n status: jobDetails.status,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n // Read current attempts to compute next value deterministically\n let getQuery = this.client\n .from(this.tableName)\n .select(\"run_attempts, max_retries\")\n .eq(\"id\", jobDetails.id as number)\n .eq(\"queue\", this.queueName);\n getQuery = this.applyPrefixFilters(getQuery);\n const { data: current, error: getError } = await getQuery.single();\n if (getError) throw getError;\n const currentAttempts = (current?.run_attempts as number | undefined) ?? 0;\n const maxRetries = (current?.max_retries as number | undefined) ?? jobDetails.max_retries ?? 10;\n const nextAttempts = currentAttempts + 1;\n\n if (jobDetails.status === JobStatus.PENDING) {\n // Check if the next attempt would exceed max retries\n if (nextAttempts > maxRetries) {\n // Update to FAILED status instead of rescheduling\n let failQuery = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.FAILED,\n error: \"Max retries reached\",\n error_code: \"MAX_RETRIES_REACHED\",\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n failQuery = this.applyPrefixFilters(failQuery);\n const { error: failError } = await failQuery;\n if (failError) throw failError;\n return;\n }\n\n // Reschedule the job\n let query = this.client\n .from(this.tableName)\n .update({\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n status: jobDetails.status,\n run_after: jobDetails.run_after!,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n run_attempts: nextAttempts,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n if (jobDetails.status === JobStatus.COMPLETED || jobDetails.status === JobStatus.FAILED) {\n let query = this.client\n .from(this.tableName)\n .update({\n output: jobDetails.output ?? null,\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n status: jobDetails.status,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n run_attempts: nextAttempts,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n // Transitional states: PROCESSING/ABORTING etc - increment attempts like other stores\n let query = this.client\n .from(this.tableName)\n .update({\n status: jobDetails.status,\n output: jobDetails.output ?? null,\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n run_after: jobDetails.run_after ?? null,\n run_attempts: nextAttempts,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n /**\n * Releases a claimed job without consuming a retry attempt.\n */\n public async release(jobId: unknown): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.PENDING,\n worker_id: null,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n /**\n * Clears all jobs from the queue.\n */\n public async deleteAll(): Promise<void> {\n let query = this.client.from(this.tableName).delete().eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Looks up cached output for a given input\n * Uses input fingerprinting for efficient matching\n * @returns The cached output or null if not found\n */\n public async outputForInput(input: Input): Promise<Output | null> {\n const fingerprint = await makeFingerprint(input);\n\n let query = this.client\n .from(this.tableName)\n .select(\"output\")\n .eq(\"fingerprint\", fingerprint)\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.COMPLETED);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return null; // Not found\n throw error;\n }\n\n return data?.output ?? null;\n }\n\n /**\n * Aborts a job by setting its status to \"ABORTING\".\n * This method will signal the corresponding AbortController so that\n * the job's execute() method (if it supports an AbortSignal parameter)\n * can clean up and exit.\n */\n public async abort(jobId: unknown): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({ status: JobStatus.ABORTING })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Retrieves all jobs for a given job run ID.\n * @param job_run_id - The ID of the job run to retrieve\n * @returns An array of jobs\n */\n public async getByRunId(job_run_id: string): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"job_run_id\", job_run_id)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { data, error } = await query;\n\n if (error) throw error;\n return (data as Array<JobStorageFormat<Input, Output>>) ?? [];\n }\n\n /**\n * Implements the saveProgress method\n */\n public async saveProgress(\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, any>\n ): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({\n progress,\n progress_message: message,\n progress_details: details,\n })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Deletes a job by its ID\n */\n public async delete(jobId: unknown): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .delete()\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Delete jobs with a specific status older than a cutoff date\n * @param status - Status of jobs to delete\n * @param olderThanMs - Delete jobs completed more than this many milliseconds ago\n */\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n\n let query = this.client\n .from(this.tableName)\n .delete()\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status)\n .not(\"completed_at\", \"is\", null)\n .lte(\"completed_at\", cutoffDate);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Checks if a job from a realtime payload matches the specified prefix filter\n * @param job - The job record from the realtime payload\n * @param prefixFilter - The prefix filter to match against (undefined = use instance prefixes, {} = no filter)\n */\n private matchesPrefixFilter(\n job: Record<string, unknown> | undefined,\n prefixFilter?: Readonly<Record<string, string | number>>\n ): boolean {\n if (!job) return false;\n\n // Check queue name first\n if (job.queue !== this.queueName) {\n return false;\n }\n\n // If prefixFilter is explicitly an empty object, no prefix filtering\n if (prefixFilter && Object.keys(prefixFilter).length === 0) {\n return true;\n }\n\n // Use provided prefixFilter or fall back to instance's prefixValues\n const filterValues = prefixFilter ?? this.prefixValues;\n\n // If no filter values, match all\n if (Object.keys(filterValues).length === 0) {\n return true;\n }\n\n // Check each filter value\n for (const [key, value] of Object.entries(filterValues)) {\n if (job[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Checks if a prefix filter is custom (different from instance's prefixes).\n */\n private isCustomPrefixFilter(prefixFilter?: Readonly<Record<string, string | number>>): boolean {\n // No filter specified - use instance prefixes (not custom)\n if (prefixFilter === undefined) {\n return false;\n }\n // Empty filter - receive all (custom)\n if (Object.keys(prefixFilter).length === 0) {\n return true;\n }\n // Check if filter matches instance prefixes exactly\n const instanceKeys = Object.keys(this.prefixValues);\n const filterKeys = Object.keys(prefixFilter);\n if (instanceKeys.length !== filterKeys.length) {\n return true; // Different number of keys = custom\n }\n for (const key of instanceKeys) {\n if (this.prefixValues[key] !== prefixFilter[key]) {\n return true; // Different value = custom\n }\n }\n return false; // Matches instance prefixes exactly\n }\n\n /**\n * Gets all jobs from the queue with a custom prefix filter.\n * Used for subscriptions with custom prefix filters (filters at DB level).\n *\n * @param prefixFilter - The prefix values to filter by (empty object = all jobs)\n * @returns A promise that resolves to an array of jobs\n */\n private async getAllJobsWithFilter(\n prefixFilter: Readonly<Record<string, string | number>>\n ): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client.from(this.tableName).select(\"*\").eq(\"queue\", this.queueName);\n\n // Apply the custom prefix filter\n for (const [key, value] of Object.entries(prefixFilter)) {\n query = query.eq(key, value);\n }\n\n const { data, error } = await query;\n\n if (error) throw error;\n return (data ?? []) as Array<JobStorageFormat<Input, Output>>;\n }\n\n /**\n * Subscribes to changes in the queue.\n * Uses Supabase realtime by default.\n *\n * @param callback - Function called when a change occurs\n * @param options - Subscription options including prefix filter\n * @returns Unsubscribe function\n */\n public subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n return this.subscribeToChangesWithRealtime(callback, options?.prefixFilter);\n }\n\n /**\n * Subscribe using Supabase realtime (protected).\n *\n * @param callback - Function called when a change occurs\n * @param prefixFilter - Optional prefix filter (undefined = use instance prefixes, {} = no filter)\n * @returns Unsubscribe function\n */\n protected subscribeToChangesWithRealtime(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter?: Readonly<Record<string, string | number>>\n ): () => void {\n const channelName = `queue-${this.tableName}-${this.queueName}-${Date.now()}`;\n\n this.realtimeChannel = this.client\n .channel(channelName)\n .on(\n \"postgres_changes\",\n {\n event: \"*\",\n schema: \"public\",\n table: this.tableName,\n filter: `queue=eq.${this.queueName}`,\n },\n (payload) => {\n // Filter by prefix values\n const newJob = payload.new as Record<string, unknown> | undefined;\n const oldJob = payload.old as Record<string, unknown> | undefined;\n\n // Check if either old or new job matches the filter\n const newMatches = this.matchesPrefixFilter(newJob, prefixFilter);\n const oldMatches = this.matchesPrefixFilter(oldJob, prefixFilter);\n\n if (!newMatches && !oldMatches) {\n return;\n }\n\n callback({\n type: payload.eventType.toUpperCase() as QueueChangeType,\n old:\n oldJob && Object.keys(oldJob).length > 0\n ? (oldJob as JobStorageFormat<Input, Output>)\n : undefined,\n new:\n newJob && Object.keys(newJob).length > 0\n ? (newJob as JobStorageFormat<Input, Output>)\n : undefined,\n });\n }\n )\n .subscribe();\n\n return () => {\n if (this.realtimeChannel) {\n this.client.removeChannel(this.realtimeChannel);\n this.realtimeChannel = null;\n }\n };\n }\n\n /**\n * Gets or creates the shared polling subscription manager for normal subscriptions (fallback).\n * This ensures all normal subscriptions share a single polling loop per interval.\n */\n private getPollingManager(): PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > {\n if (!this.pollingManager) {\n this.pollingManager = new PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n >(\n async () => {\n // Fetch jobs with instance's prefix filter (efficient DB-level filtering)\n const jobs = await this.getAllJobs();\n return new Map(jobs.map((j) => [j.id, j]));\n },\n (a, b) => deepEqual(a, b),\n {\n insert: (item) => ({ type: \"INSERT\" as const, new: item }),\n update: (oldItem, newItem) => ({ type: \"UPDATE\" as const, old: oldItem, new: newItem }),\n delete: (item) => ({ type: \"DELETE\" as const, old: item }),\n }\n );\n }\n return this.pollingManager;\n }\n\n /**\n * Creates a dedicated polling subscription for custom prefix filters (fallback).\n * This runs separately from the normal polling manager with DB-level filtering.\n */\n private subscribeWithCustomPrefixFilterPolling(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter: Readonly<Record<string, string | number>>,\n intervalMs: number\n ): () => void {\n let lastKnownJobs = new Map<unknown, JobStorageFormat<Input, Output>>();\n let cancelled = false;\n\n const poll = async () => {\n if (cancelled) return;\n try {\n const currentJobs = await this.getAllJobsWithFilter(prefixFilter);\n if (cancelled) return;\n const currentMap = new Map(currentJobs.map((j) => [j.id, j]));\n\n // Detect changes\n for (const [id, job] of currentMap) {\n const old = lastKnownJobs.get(id);\n if (!old) {\n callback({ type: \"INSERT\", new: job });\n } else if (!deepEqual(old, job)) {\n callback({ type: \"UPDATE\", old, new: job });\n }\n }\n\n for (const [id, job] of lastKnownJobs) {\n if (!currentMap.has(id)) {\n callback({ type: \"DELETE\", old: job });\n }\n }\n\n lastKnownJobs = currentMap;\n } catch {\n // Ignore polling errors\n }\n };\n\n const intervalId = setInterval(poll, intervalMs);\n poll(); // Initial poll\n\n return () => {\n cancelled = true;\n clearInterval(intervalId);\n };\n }\n\n /**\n * Subscribe using polling (protected, available as fallback).\n *\n * Normal subscriptions (no custom prefix filter) share a single polling loop for efficiency.\n * Custom prefix filter subscriptions get their own dedicated polling loop with DB-level filtering.\n *\n * @param callback - Function called when a change occurs\n * @param options - Subscription options including interval and prefix filter\n * @returns Unsubscribe function\n */\n protected subscribeToChangesWithPolling(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n const intervalMs = options?.pollingIntervalMs ?? 1000;\n\n // Check if this is a custom prefix filter subscription\n if (this.isCustomPrefixFilter(options?.prefixFilter)) {\n // Custom prefix filter - use dedicated polling with DB-level filtering\n return this.subscribeWithCustomPrefixFilterPolling(\n callback,\n options!.prefixFilter!,\n intervalMs\n );\n }\n\n // Normal subscription - use shared polling manager (efficient)\n const manager = this.getPollingManager();\n return manager.subscribe(callback, { intervalMs });\n }\n}\n",
6
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken } from \"@workglow/util\";\nimport type { PrefixColumn } from \"@workglow/job-queue\";\nimport type {\n IRateLimiterStorage,\n RateLimiterStorageOptions,\n RateLimiterStorageScope,\n} from \"@workglow/job-queue\";\n\nexport const SUPABASE_RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\n \"ratelimiter.storage.supabase\"\n);\n\n/**\n * Supabase implementation of rate limiter storage.\n * Manages execution records and next available times for rate limiting.\n */\nexport class SupabaseRateLimiterStorage implements IRateLimiterStorage {\n public readonly scope: RateLimiterStorageScope = \"cluster\";\n protected readonly client: SupabaseClient;\n protected readonly prefixes: readonly PrefixColumn[];\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n protected readonly executionTableName: string;\n protected readonly nextAvailableTableName: string;\n\n constructor(client: unknown, options?: RateLimiterStorageOptions) {\n this.client = client as SupabaseClient;\n this.prefixes = options?.prefixes ?? [];\n this.prefixValues = options?.prefixValues ?? {};\n\n // Generate table names based on prefix configuration\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.executionTableName = `rate_limit_executions_${prefixNames}`;\n this.nextAvailableTableName = `rate_limit_next_available_${prefixNames}`;\n } else {\n this.executionTableName = \"rate_limit_executions\";\n this.nextAvailableTableName = \"rate_limit_next_available\";\n }\n }\n\n /**\n * Gets the SQL column type for a prefix column (Supabase supports UUID natively).\n */\n private getPrefixColumnType(type: PrefixColumn[\"type\"]): string {\n return type === \"uuid\" ? \"UUID\" : \"INTEGER\";\n }\n\n /**\n * Builds the prefix columns SQL for CREATE TABLE.\n */\n private buildPrefixColumnsSql(): string {\n if (this.prefixes.length === 0) return \"\";\n return (\n this.prefixes\n .map((p) => `${p.name} ${this.getPrefixColumnType(p.type)} NOT NULL`)\n .join(\",\\n \") + \",\\n \"\n );\n }\n\n /**\n * Builds prefix column names for use in queries.\n */\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n /**\n * Applies prefix filters to a Supabase query builder.\n */\n private applyPrefixFilters<T>(query: T): T {\n let result = query as any;\n for (const prefix of this.prefixes) {\n result = result.eq(prefix.name, this.prefixValues[prefix.name]);\n }\n return result as T;\n }\n\n /**\n * Gets prefix values as an object for inserts.\n */\n private getPrefixInsertValues(): Record<string, string | number> {\n const values: Record<string, string | number> = {};\n for (const prefix of this.prefixes) {\n values[prefix.name] = this.prefixValues[prefix.name];\n }\n return values;\n }\n\n public async setupDatabase(): Promise<void> {\n const prefixColumnsSql = this.buildPrefixColumnsSql();\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixIndexPrefix =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const indexSuffix = prefixColumnNames.length > 0 ? \"_\" + prefixColumnNames.join(\"_\") : \"\";\n\n // Create execution tracking table\n const createExecTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.executionTableName} (\n id SERIAL PRIMARY KEY,\n ${prefixColumnsSql}queue_name TEXT NOT NULL,\n executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()\n )\n `;\n\n const { error: execTableError } = await this.client.rpc(\"exec_sql\", {\n query: createExecTableSql,\n });\n if (execTableError && execTableError.code !== \"42P07\") {\n throw execTableError;\n }\n\n // Create index on execution table\n const createExecIndexSql = `\n CREATE INDEX IF NOT EXISTS rate_limit_exec_queue${indexSuffix}_idx \n ON ${this.executionTableName} (${prefixIndexPrefix}queue_name, executed_at)\n `;\n await this.client.rpc(\"exec_sql\", { query: createExecIndexSql });\n\n // Build primary key columns\n const primaryKeyColumns =\n prefixColumnNames.length > 0 ? `${prefixColumnNames.join(\", \")}, queue_name` : \"queue_name\";\n\n // Create next available table\n const createNextTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.nextAvailableTableName} (\n ${prefixColumnsSql}queue_name TEXT NOT NULL,\n next_available_at TIMESTAMP WITH TIME ZONE,\n PRIMARY KEY (${primaryKeyColumns})\n )\n `;\n\n const { error: nextTableError } = await this.client.rpc(\"exec_sql\", {\n query: createNextTableSql,\n });\n if (nextTableError && nextTableError.code !== \"42P07\") {\n throw nextTableError;\n }\n\n // Install the atomic reserve function. The function takes the queue name,\n // window-start cutoff, and max executions; returns true iff a row was\n // inserted. Advisory xact lock keyed on (table, prefixes, queue) ensures\n // concurrent acquirers serialize without contending on unrelated buckets.\n const fnName = this.atomicReserveFunctionName();\n const prefixSig = this.prefixes\n .map((p) => `${p.name} ${this.getPrefixColumnType(p.type)}`)\n .join(\", \");\n const prefixSigPrefix = prefixSig ? prefixSig + \", \" : \"\";\n const prefixWhere =\n this.prefixes.length > 0\n ? \" AND \" + this.prefixes.map((p) => `${p.name} = _${p.name}`).join(\" AND \")\n : \"\";\n const prefixInsertCols =\n this.prefixes.length > 0 ? this.prefixes.map((p) => p.name).join(\", \") + \", \" : \"\";\n const prefixInsertVals =\n this.prefixes.length > 0 ? this.prefixes.map((p) => `_${p.name}`).join(\", \") + \", \" : \"\";\n const lockKeyParts = [\n `'${this.executionTableName}'`,\n ...this.prefixes.map((p) => `_${p.name}::text`),\n `_queue_name::text`,\n ];\n const lockKeyExpr = `hashtextextended(${lockKeyParts.join(\" || '|' || \")}, 0)`;\n\n const createFnSql = `\n CREATE OR REPLACE FUNCTION ${fnName}(\n ${prefixSigPrefix}_queue_name TEXT, _window_start TIMESTAMPTZ, _max_exec INT\n ) RETURNS BIGINT AS $fn$\n DECLARE\n _count INT;\n _next TIMESTAMPTZ;\n _new_id BIGINT;\n BEGIN\n PERFORM pg_advisory_xact_lock(${lockKeyExpr});\n SELECT COUNT(*) INTO _count FROM ${this.executionTableName}\n WHERE queue_name = _queue_name AND executed_at > _window_start${prefixWhere};\n IF _count >= _max_exec THEN RETURN NULL; END IF;\n SELECT next_available_at INTO _next FROM ${this.nextAvailableTableName}\n WHERE queue_name = _queue_name${prefixWhere};\n IF _next IS NOT NULL AND _next > NOW() THEN RETURN NULL; END IF;\n INSERT INTO ${this.executionTableName} (${prefixInsertCols}queue_name)\n VALUES (${prefixInsertVals}_queue_name)\n RETURNING id INTO _new_id;\n RETURN _new_id;\n END;\n $fn$ LANGUAGE plpgsql;\n `;\n const { error: fnError } = await this.client.rpc(\"exec_sql\", { query: createFnSql });\n if (fnError) {\n throw fnError;\n }\n }\n\n /** Stable function name derived from table name (Postgres identifiers ≤63 chars). */\n private atomicReserveFunctionName(): string {\n return `${this.executionTableName}_try_reserve`.slice(0, 63);\n }\n\n public async tryReserveExecution(\n queueName: string,\n maxExecutions: number,\n windowMs: number\n ): Promise<unknown | null> {\n const args: Record<string, unknown> = {\n _queue_name: queueName,\n _window_start: new Date(Date.now() - windowMs).toISOString(),\n _max_exec: maxExecutions,\n };\n for (const p of this.prefixes) {\n args[`_${p.name}`] = this.prefixValues[p.name];\n }\n // The PL/pgSQL function returns the inserted id BIGINT on success and\n // NULL when capacity is reached or external backoff is active.\n const { data, error } = await this.client.rpc(this.atomicReserveFunctionName(), args);\n if (error) throw error;\n // The function now returns the inserted row id BIGINT (or NULL when\n // capacity is reached). Real Supabase RPC returns the scalar directly;\n // the pglite-backed mock executes `SELECT * FROM fn()` and returns a row\n // array — accept both shapes.\n if (data === null || data === undefined) return null;\n if (Array.isArray(data)) {\n if (data.length === 0) return null;\n const first = Object.values(data[0] as Record<string, unknown>)[0];\n return first ?? null;\n }\n // Supabase RPC returns BIGINTs as either numbers or strings depending on\n // size; both are valid as opaque tokens.\n return data;\n }\n\n public async releaseExecution(queueName: string, token: unknown): Promise<void> {\n if (token === null || token === undefined) return;\n // Delete by id — NEVER by recency. Two concurrent acquirers can hold\n // tokens for different rows; deleting \"the most recent\" would release the\n // other worker's slot.\n let del = this.client\n .from(this.executionTableName)\n .delete()\n .eq(\"id\", token as number | string)\n .eq(\"queue_name\", queueName);\n del = this.applyPrefixFilters(del);\n const { error: delError } = await del;\n if (delError) throw delError;\n }\n\n public async recordExecution(queueName: string): Promise<void> {\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { error } = await this.client.from(this.executionTableName).insert({\n ...prefixInsertValues,\n queue_name: queueName,\n });\n\n if (error) throw error;\n }\n\n public async getExecutionCount(queueName: string, windowStartTime: string): Promise<number> {\n let query = this.client\n .from(this.executionTableName)\n .select(\"*\", { count: \"exact\", head: true })\n .eq(\"queue_name\", queueName)\n .gt(\"executed_at\", windowStartTime);\n\n query = this.applyPrefixFilters(query);\n\n const { count, error } = await query;\n\n if (error) throw error;\n return count ?? 0;\n }\n\n public async getOldestExecutionAtOffset(\n queueName: string,\n offset: number\n ): Promise<string | undefined> {\n let query = this.client\n .from(this.executionTableName)\n .select(\"executed_at\")\n .eq(\"queue_name\", queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query\n .order(\"executed_at\", { ascending: true })\n .range(offset, offset);\n\n if (error) throw error;\n if (!data || data.length === 0) return undefined;\n return new Date(data[0].executed_at).toISOString();\n }\n\n public async getNextAvailableTime(queueName: string): Promise<string | undefined> {\n let query = this.client\n .from(this.nextAvailableTableName)\n .select(\"next_available_at\")\n .eq(\"queue_name\", queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return undefined; // Not found\n throw error;\n }\n\n if (!data?.next_available_at) return undefined;\n return new Date(data.next_available_at).toISOString();\n }\n\n public async setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void> {\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { error } = await this.client.from(this.nextAvailableTableName).upsert(\n {\n ...prefixInsertValues,\n queue_name: queueName,\n next_available_at: nextAvailableAt,\n },\n {\n onConflict:\n this.prefixes.length > 0\n ? `${this.getPrefixColumnNames().join(\",\")},queue_name`\n : \"queue_name\",\n }\n );\n\n if (error) throw error;\n }\n\n public async clear(queueName: string): Promise<void> {\n let execQuery = this.client.from(this.executionTableName).delete().eq(\"queue_name\", queueName);\n execQuery = this.applyPrefixFilters(execQuery);\n const { error: execError } = await execQuery;\n if (execError) throw execError;\n\n let nextQuery = this.client\n .from(this.nextAvailableTableName)\n .delete()\n .eq(\"queue_name\", queueName);\n nextQuery = this.applyPrefixFilters(nextQuery);\n const { error: nextError } = await nextQuery;\n if (nextError) throw nextError;\n }\n}\n"
5
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { RealtimeChannel, SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken, deepEqual, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport { PollingSubscriptionManager } from \"@workglow/storage\";\nimport { JobStatus } from \"@workglow/job-queue\";\nimport type {\n IQueueStorage,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueChangeType,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"@workglow/job-queue\";\n\nexport const SUPABASE_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.supabase\"\n);\n\n/**\n * Supabase implementation of a job queue.\n * Provides storage and retrieval for job execution states using Supabase.\n */\nexport class SupabaseQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n public readonly scope = \"cluster\" as const;\n protected readonly client: SupabaseClient;\n protected readonly prefixes: readonly PrefixColumn[];\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n protected readonly tableName: string;\n private realtimeChannel: RealtimeChannel | null = null;\n private pollingManager: PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > | null = null;\n\n constructor(\n client: SupabaseClient,\n protected readonly queueName: string,\n options?: QueueStorageOptions\n ) {\n this.client = client as SupabaseClient;\n this.prefixes = options?.prefixes ?? [];\n this.prefixValues = options?.prefixValues ?? {};\n // Generate table name based on prefix configuration to avoid column conflicts\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.tableName = `job_queue_${prefixNames}`;\n } else {\n this.tableName = \"job_queue\";\n }\n }\n\n /**\n * Gets the SQL column type for a prefix column (Supabase supports UUID natively)\n */\n private getPrefixColumnType(type: PrefixColumn[\"type\"]): string {\n return type === \"uuid\" ? \"UUID\" : \"INTEGER\";\n }\n\n /**\n * Builds the prefix columns SQL for CREATE TABLE\n */\n private buildPrefixColumnsSql(): string {\n if (this.prefixes.length === 0) return \"\";\n return (\n this.prefixes\n .map((p) => `${p.name} ${this.getPrefixColumnType(p.type)} NOT NULL`)\n .join(\",\\n \") + \",\\n \"\n );\n }\n\n /**\n * Builds prefix column names for use in queries\n */\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n /**\n * Applies prefix filters to a Supabase query builder\n */\n private applyPrefixFilters<T>(query: T): T {\n let result = query as any;\n for (const prefix of this.prefixes) {\n result = result.eq(prefix.name, this.prefixValues[prefix.name]);\n }\n return result as T;\n }\n\n /**\n * Gets prefix values as an object for inserts\n */\n private getPrefixInsertValues(): Record<string, string | number> {\n const values: Record<string, string | number> = {};\n for (const prefix of this.prefixes) {\n values[prefix.name] = this.prefixValues[prefix.name];\n }\n return values;\n }\n\n /**\n * Builds WHERE clause conditions for prefix filtering with inline values (for raw SQL)\n * @returns SQL conditions string with values inlined\n */\n private buildPrefixWhereSql(): string {\n if (this.prefixes.length === 0) {\n return \"\";\n }\n const conditions = this.prefixes\n .map((p) => {\n const value = this.prefixValues[p.name];\n if (p.type === \"uuid\") {\n const validated = this.validateSqlValue(String(value), `prefix \"${p.name}\"`);\n return `${p.name} = '${this.escapeSqlString(validated)}'`;\n }\n const numValue = Number(value ?? 0);\n if (!Number.isFinite(numValue)) {\n throw new Error(`Invalid numeric prefix value for \"${p.name}\": ${value}`);\n }\n return `${p.name} = ${numValue}`;\n })\n .join(\" AND \");\n return \" AND \" + conditions;\n }\n\n /**\n * Regex for validating SQL literal-safe strings.\n * Used for quoted values (e.g. queue names/IDs) and only allows alphanumeric\n * characters, underscores, hyphens, colons, and periods.\n */\n private static readonly SAFE_SQL_VALUE_RE = /^[a-zA-Z0-9_\\-.:]+$/;\n\n /**\n * Validates that a string value is safe for use as a quoted SQL literal.\n * Throws an error if the value contains characters outside SAFE_SQL_VALUE_RE.\n */\n private validateSqlValue(value: string, context: string): string {\n if (!SupabaseQueueStorage.SAFE_SQL_VALUE_RE.test(value)) {\n throw new Error(\n `Unsafe value for ${context}: \"${value}\". Values must match /^[a-zA-Z0-9_\\\\-.:]+$/.`\n );\n }\n return value;\n }\n\n /**\n * Escapes a string value for use in SQL\n */\n private escapeSqlString(value: string): string {\n return value.replace(/'/g, \"''\");\n }\n\n /**\n * Schema setup for Supabase queues.\n *\n * Supabase exposes the SQL surface via a side-installed `exec_sql` RPC\n * rather than the `pg.Pool` shape the {@link PostgresMigrationRunner} is\n * built against, so this storage doesn't share the runner's bookkeeping\n * table — it runs idempotent CREATE-IF-NOT-EXISTS statements directly via\n * the RPC. {@link getMigrations} returns an empty array for the same\n * reason.\n */\n public async migrate(): Promise<void> {\n // Note: For Supabase, table creation should typically be done through migrations\n // This setup assumes the table already exists or uses exec_sql RPC function\n const createTypeSql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus)\n .map((v) => `'${v}'`)\n .join(\",\")})`;\n\n const { error: typeError } = await this.client.rpc(\"exec_sql\", { query: createTypeSql });\n // Ignore error if type already exists (code 42710)\n if (typeError && typeError.code !== \"42710\") {\n throw typeError;\n }\n\n const prefixColumnsSql = this.buildPrefixColumnsSql();\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixIndexPrefix =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const indexSuffix = prefixColumnNames.length > 0 ? \"_\" + prefixColumnNames.join(\"_\") : \"\";\n\n const createTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n id SERIAL NOT NULL,\n ${prefixColumnsSql}fingerprint text NOT NULL,\n queue text NOT NULL,\n job_run_id text NOT NULL,\n status job_status NOT NULL default 'PENDING',\n input jsonb NOT NULL,\n output jsonb,\n run_attempts integer default 0,\n max_retries integer default 20,\n run_after timestamp with time zone DEFAULT now(),\n last_ran_at timestamp with time zone,\n created_at timestamp with time zone DEFAULT now(),\n deadline_at timestamp with time zone,\n completed_at timestamp with time zone,\n error text,\n error_code text,\n progress real DEFAULT 0,\n progress_message text DEFAULT '',\n progress_details jsonb,\n worker_id text\n )`;\n\n const { error: tableError } = await this.client.rpc(\"exec_sql\", { query: createTableSql });\n if (tableError) {\n // Ignore error if table already exists (code 42P07)\n if (tableError.code !== \"42P07\") {\n throw tableError;\n }\n }\n\n // Create indexes with prefix columns prepended\n const indexes = [\n `CREATE INDEX IF NOT EXISTS job_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}id, status, run_after)`,\n `CREATE INDEX IF NOT EXISTS job_queue_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}queue, status, run_after)`,\n `CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint, status)`,\n ];\n\n for (const indexSql of indexes) {\n await this.client.rpc(\"exec_sql\", { query: indexSql });\n // Ignore index creation errors\n }\n }\n\n /** Supabase queue runs DDL via `exec_sql`, not via the migration runner. */\n public getMigrations(): ReadonlyArray<unknown> {\n return [];\n }\n\n /**\n * Adds a new job to the queue.\n * @param job - The job to add\n * @returns The ID of the added job\n */\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const now = new Date().toISOString();\n job.queue = this.queueName;\n job.job_run_id = job.job_run_id ?? uuid4();\n job.fingerprint = await makeFingerprint(job.input);\n job.status = JobStatus.PENDING;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n job.created_at = now;\n job.run_after = now;\n\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { data, error } = await this.client\n .from(this.tableName)\n .insert({\n ...prefixInsertValues,\n queue: job.queue,\n fingerprint: job.fingerprint,\n input: job.input,\n run_after: job.run_after,\n created_at: job.created_at,\n deadline_at: job.deadline_at,\n max_retries: job.max_retries,\n job_run_id: job.job_run_id,\n progress: job.progress,\n progress_message: job.progress_message,\n progress_details: job.progress_details,\n })\n .select(\"id\")\n .single();\n\n if (error) throw error;\n if (!data) throw new Error(\"Failed to add to queue\");\n\n job.id = data.id;\n return job.id;\n }\n\n /**\n * Retrieves a job by its ID.\n * @param id - The ID of the job to retrieve\n * @returns The job if found, undefined otherwise\n */\n public async get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"id\", id)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return undefined; // Not found\n throw error;\n }\n\n return data as JobStorageFormat<Input, Output> | undefined;\n }\n\n /**\n * Retrieves a slice of jobs from the queue.\n * @param status - The status to filter by\n * @param num - Maximum number of jobs to return\n * @returns An array of jobs\n */\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<JobStorageFormat<Input, Output>[]> {\n num = Number(num) || 100;\n\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.order(\"run_after\", { ascending: true }).limit(num);\n\n if (error) throw error;\n return (data as JobStorageFormat<Input, Output>[]) ?? [];\n }\n\n /**\n * Retrieves the next available job that is ready to be processed.\n * Uses atomic UPDATE with subquery SELECT FOR UPDATE SKIP LOCKED to prevent race conditions.\n * @param workerId - Worker ID to associate with the job (required)\n * @returns The next job or undefined if no job is available\n */\n public async next(workerId: string): Promise<JobStorageFormat<Input, Output> | undefined> {\n const prefixConditions = this.buildPrefixWhereSql();\n const validatedQueueName = this.validateSqlValue(this.queueName, \"queueName\");\n const validatedWorkerId = this.validateSqlValue(workerId, \"workerId\");\n const escapedQueueName = this.escapeSqlString(validatedQueueName);\n const escapedWorkerId = this.escapeSqlString(validatedWorkerId);\n\n // Use the same atomic UPDATE...WHERE id = (SELECT...FOR UPDATE SKIP LOCKED) pattern as PostgresQueueStorage\n const sql = `\n UPDATE ${this.tableName}\n SET status = '${JobStatus.PROCESSING}', last_ran_at = NOW() AT TIME ZONE 'UTC', worker_id = '${escapedWorkerId}'\n WHERE id = (\n SELECT id\n FROM ${this.tableName}\n WHERE queue = '${escapedQueueName}'\n AND status = '${JobStatus.PENDING}'\n ${prefixConditions}\n AND run_after <= NOW() AT TIME ZONE 'UTC'\n ORDER BY run_after ASC\n FOR UPDATE SKIP LOCKED\n LIMIT 1\n )\n RETURNING *`;\n\n const { data, error } = await this.client.rpc(\"exec_sql\", { query: sql });\n\n if (error) throw error;\n\n // exec_sql returns result rows as an array\n if (!data || !Array.isArray(data) || data.length === 0) {\n return undefined;\n }\n\n return data[0] as JobStorageFormat<Input, Output>;\n }\n\n /**\n * Retrieves the number of jobs in the queue with a specific status.\n * @param status - The status of the jobs to count\n * @returns The count of jobs with the specified status\n */\n public async size(status = JobStatus.PENDING): Promise<number> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\", { count: \"exact\", head: true })\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status);\n\n query = this.applyPrefixFilters(query);\n\n const { count, error } = await query;\n\n if (error) throw error;\n return count ?? 0;\n }\n\n /**\n * Gets all jobs from the queue that match the current prefix values.\n * Used internally for polling-based subscriptions.\n *\n * @returns An array of jobs\n */\n private async getAllJobs(): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client.from(this.tableName).select(\"*\").eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query;\n\n if (error) throw error;\n return (data ?? []) as Array<JobStorageFormat<Input, Output>>;\n }\n\n /**\n * Marks a job as complete with its output or error.\n * Enhanced error handling:\n * - For a retryable error, increments run_attempts and updates run_after.\n * - Marks a job as FAILED immediately for permanent or generic errors.\n */\n public async complete(jobDetails: JobStorageFormat<Input, Output>): Promise<void> {\n const now = new Date().toISOString();\n\n // Handle disabled without changing attempts\n if (jobDetails.status === JobStatus.DISABLED) {\n let query = this.client\n .from(this.tableName)\n .update({\n status: jobDetails.status,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n // Read current attempts to compute next value deterministically\n let getQuery = this.client\n .from(this.tableName)\n .select(\"run_attempts, max_retries\")\n .eq(\"id\", jobDetails.id as number)\n .eq(\"queue\", this.queueName);\n getQuery = this.applyPrefixFilters(getQuery);\n const { data: current, error: getError } = await getQuery.single();\n if (getError) throw getError;\n const currentAttempts = (current?.run_attempts as number | undefined) ?? 0;\n const maxRetries = (current?.max_retries as number | undefined) ?? jobDetails.max_retries ?? 10;\n const nextAttempts = currentAttempts + 1;\n\n if (jobDetails.status === JobStatus.PENDING) {\n // Check if the next attempt would exceed max retries\n if (nextAttempts > maxRetries) {\n // Update to FAILED status instead of rescheduling\n let failQuery = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.FAILED,\n error: \"Max retries reached\",\n error_code: \"MAX_RETRIES_REACHED\",\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n failQuery = this.applyPrefixFilters(failQuery);\n const { error: failError } = await failQuery;\n if (failError) throw failError;\n return;\n }\n\n // Reschedule the job\n let query = this.client\n .from(this.tableName)\n .update({\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n status: jobDetails.status,\n run_after: jobDetails.run_after!,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n run_attempts: nextAttempts,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n if (jobDetails.status === JobStatus.COMPLETED || jobDetails.status === JobStatus.FAILED) {\n let query = this.client\n .from(this.tableName)\n .update({\n output: jobDetails.output ?? null,\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n status: jobDetails.status,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n run_attempts: nextAttempts,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n // Transitional states: PROCESSING/ABORTING etc - increment attempts like other stores\n let query = this.client\n .from(this.tableName)\n .update({\n status: jobDetails.status,\n output: jobDetails.output ?? null,\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n run_after: jobDetails.run_after ?? null,\n run_attempts: nextAttempts,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n /**\n * Releases a claimed job without consuming a retry attempt.\n */\n public async release(jobId: unknown): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.PENDING,\n worker_id: null,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n /**\n * Clears all jobs from the queue.\n */\n public async deleteAll(): Promise<void> {\n let query = this.client.from(this.tableName).delete().eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Looks up cached output for a given input\n * Uses input fingerprinting for efficient matching\n * @returns The cached output or null if not found\n */\n public async outputForInput(input: Input): Promise<Output | null> {\n const fingerprint = await makeFingerprint(input);\n\n let query = this.client\n .from(this.tableName)\n .select(\"output\")\n .eq(\"fingerprint\", fingerprint)\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.COMPLETED);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return null; // Not found\n throw error;\n }\n\n return data?.output ?? null;\n }\n\n /**\n * Aborts a job by setting its status to \"ABORTING\".\n * This method will signal the corresponding AbortController so that\n * the job's execute() method (if it supports an AbortSignal parameter)\n * can clean up and exit.\n */\n public async abort(jobId: unknown): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({ status: JobStatus.ABORTING })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Retrieves all jobs for a given job run ID.\n * @param job_run_id - The ID of the job run to retrieve\n * @returns An array of jobs\n */\n public async getByRunId(job_run_id: string): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"job_run_id\", job_run_id)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { data, error } = await query;\n\n if (error) throw error;\n return (data as Array<JobStorageFormat<Input, Output>>) ?? [];\n }\n\n /**\n * Implements the saveProgress method\n */\n public async saveProgress(\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, any>\n ): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({\n progress,\n progress_message: message,\n progress_details: details,\n })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Deletes a job by its ID\n */\n public async delete(jobId: unknown): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .delete()\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Delete jobs with a specific status older than a cutoff date\n * @param status - Status of jobs to delete\n * @param olderThanMs - Delete jobs completed more than this many milliseconds ago\n */\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n\n let query = this.client\n .from(this.tableName)\n .delete()\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status)\n .not(\"completed_at\", \"is\", null)\n .lte(\"completed_at\", cutoffDate);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Checks if a job from a realtime payload matches the specified prefix filter\n * @param job - The job record from the realtime payload\n * @param prefixFilter - The prefix filter to match against (undefined = use instance prefixes, {} = no filter)\n */\n private matchesPrefixFilter(\n job: Record<string, unknown> | undefined,\n prefixFilter?: Readonly<Record<string, string | number>>\n ): boolean {\n if (!job) return false;\n\n // Check queue name first\n if (job.queue !== this.queueName) {\n return false;\n }\n\n // If prefixFilter is explicitly an empty object, no prefix filtering\n if (prefixFilter && Object.keys(prefixFilter).length === 0) {\n return true;\n }\n\n // Use provided prefixFilter or fall back to instance's prefixValues\n const filterValues = prefixFilter ?? this.prefixValues;\n\n // If no filter values, match all\n if (Object.keys(filterValues).length === 0) {\n return true;\n }\n\n // Check each filter value\n for (const [key, value] of Object.entries(filterValues)) {\n if (job[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Checks if a prefix filter is custom (different from instance's prefixes).\n */\n private isCustomPrefixFilter(prefixFilter?: Readonly<Record<string, string | number>>): boolean {\n // No filter specified - use instance prefixes (not custom)\n if (prefixFilter === undefined) {\n return false;\n }\n // Empty filter - receive all (custom)\n if (Object.keys(prefixFilter).length === 0) {\n return true;\n }\n // Check if filter matches instance prefixes exactly\n const instanceKeys = Object.keys(this.prefixValues);\n const filterKeys = Object.keys(prefixFilter);\n if (instanceKeys.length !== filterKeys.length) {\n return true; // Different number of keys = custom\n }\n for (const key of instanceKeys) {\n if (this.prefixValues[key] !== prefixFilter[key]) {\n return true; // Different value = custom\n }\n }\n return false; // Matches instance prefixes exactly\n }\n\n /**\n * Gets all jobs from the queue with a custom prefix filter.\n * Used for subscriptions with custom prefix filters (filters at DB level).\n *\n * @param prefixFilter - The prefix values to filter by (empty object = all jobs)\n * @returns A promise that resolves to an array of jobs\n */\n private async getAllJobsWithFilter(\n prefixFilter: Readonly<Record<string, string | number>>\n ): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client.from(this.tableName).select(\"*\").eq(\"queue\", this.queueName);\n\n // Apply the custom prefix filter\n for (const [key, value] of Object.entries(prefixFilter)) {\n query = query.eq(key, value);\n }\n\n const { data, error } = await query;\n\n if (error) throw error;\n return (data ?? []) as Array<JobStorageFormat<Input, Output>>;\n }\n\n /**\n * Subscribes to changes in the queue.\n * Uses Supabase realtime by default.\n *\n * @param callback - Function called when a change occurs\n * @param options - Subscription options including prefix filter\n * @returns Unsubscribe function\n */\n public subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n return this.subscribeToChangesWithRealtime(callback, options?.prefixFilter);\n }\n\n /**\n * Subscribe using Supabase realtime (protected).\n *\n * @param callback - Function called when a change occurs\n * @param prefixFilter - Optional prefix filter (undefined = use instance prefixes, {} = no filter)\n * @returns Unsubscribe function\n */\n protected subscribeToChangesWithRealtime(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter?: Readonly<Record<string, string | number>>\n ): () => void {\n const channelName = `queue-${this.tableName}-${this.queueName}-${Date.now()}`;\n\n this.realtimeChannel = this.client\n .channel(channelName)\n .on(\n \"postgres_changes\",\n {\n event: \"*\",\n schema: \"public\",\n table: this.tableName,\n filter: `queue=eq.${this.queueName}`,\n },\n (payload) => {\n // Filter by prefix values\n const newJob = payload.new as Record<string, unknown> | undefined;\n const oldJob = payload.old as Record<string, unknown> | undefined;\n\n // Check if either old or new job matches the filter\n const newMatches = this.matchesPrefixFilter(newJob, prefixFilter);\n const oldMatches = this.matchesPrefixFilter(oldJob, prefixFilter);\n\n if (!newMatches && !oldMatches) {\n return;\n }\n\n callback({\n type: payload.eventType.toUpperCase() as QueueChangeType,\n old:\n oldJob && Object.keys(oldJob).length > 0\n ? (oldJob as JobStorageFormat<Input, Output>)\n : undefined,\n new:\n newJob && Object.keys(newJob).length > 0\n ? (newJob as JobStorageFormat<Input, Output>)\n : undefined,\n });\n }\n )\n .subscribe();\n\n return () => {\n if (this.realtimeChannel) {\n this.client.removeChannel(this.realtimeChannel);\n this.realtimeChannel = null;\n }\n };\n }\n\n /**\n * Gets or creates the shared polling subscription manager for normal subscriptions (fallback).\n * This ensures all normal subscriptions share a single polling loop per interval.\n */\n private getPollingManager(): PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > {\n if (!this.pollingManager) {\n this.pollingManager = new PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n >(\n async () => {\n // Fetch jobs with instance's prefix filter (efficient DB-level filtering)\n const jobs = await this.getAllJobs();\n return new Map(jobs.map((j) => [j.id, j]));\n },\n (a, b) => deepEqual(a, b),\n {\n insert: (item) => ({ type: \"INSERT\" as const, new: item }),\n update: (oldItem, newItem) => ({ type: \"UPDATE\" as const, old: oldItem, new: newItem }),\n delete: (item) => ({ type: \"DELETE\" as const, old: item }),\n }\n );\n }\n return this.pollingManager;\n }\n\n /**\n * Creates a dedicated polling subscription for custom prefix filters (fallback).\n * This runs separately from the normal polling manager with DB-level filtering.\n */\n private subscribeWithCustomPrefixFilterPolling(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter: Readonly<Record<string, string | number>>,\n intervalMs: number\n ): () => void {\n let lastKnownJobs = new Map<unknown, JobStorageFormat<Input, Output>>();\n let cancelled = false;\n\n const poll = async () => {\n if (cancelled) return;\n try {\n const currentJobs = await this.getAllJobsWithFilter(prefixFilter);\n if (cancelled) return;\n const currentMap = new Map(currentJobs.map((j) => [j.id, j]));\n\n // Detect changes\n for (const [id, job] of currentMap) {\n const old = lastKnownJobs.get(id);\n if (!old) {\n callback({ type: \"INSERT\", new: job });\n } else if (!deepEqual(old, job)) {\n callback({ type: \"UPDATE\", old, new: job });\n }\n }\n\n for (const [id, job] of lastKnownJobs) {\n if (!currentMap.has(id)) {\n callback({ type: \"DELETE\", old: job });\n }\n }\n\n lastKnownJobs = currentMap;\n } catch {\n // Ignore polling errors\n }\n };\n\n const intervalId = setInterval(poll, intervalMs);\n poll(); // Initial poll\n\n return () => {\n cancelled = true;\n clearInterval(intervalId);\n };\n }\n\n /**\n * Subscribe using polling (protected, available as fallback).\n *\n * Normal subscriptions (no custom prefix filter) share a single polling loop for efficiency.\n * Custom prefix filter subscriptions get their own dedicated polling loop with DB-level filtering.\n *\n * @param callback - Function called when a change occurs\n * @param options - Subscription options including interval and prefix filter\n * @returns Unsubscribe function\n */\n protected subscribeToChangesWithPolling(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n const intervalMs = options?.pollingIntervalMs ?? 1000;\n\n // Check if this is a custom prefix filter subscription\n if (this.isCustomPrefixFilter(options?.prefixFilter)) {\n // Custom prefix filter - use dedicated polling with DB-level filtering\n return this.subscribeWithCustomPrefixFilterPolling(\n callback,\n options!.prefixFilter!,\n intervalMs\n );\n }\n\n // Normal subscription - use shared polling manager (efficient)\n const manager = this.getPollingManager();\n return manager.subscribe(callback, { intervalMs });\n }\n}\n",
6
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken } from \"@workglow/util\";\nimport type { PrefixColumn } from \"@workglow/job-queue\";\nimport type {\n IRateLimiterStorage,\n RateLimiterStorageOptions,\n RateLimiterStorageScope,\n} from \"@workglow/job-queue\";\n\nexport const SUPABASE_RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\n \"ratelimiter.storage.supabase\"\n);\n\n/**\n * Supabase implementation of rate limiter storage.\n * Manages execution records and next available times for rate limiting.\n */\nexport class SupabaseRateLimiterStorage implements IRateLimiterStorage {\n public readonly scope: RateLimiterStorageScope = \"cluster\";\n protected readonly client: SupabaseClient;\n protected readonly prefixes: readonly PrefixColumn[];\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n protected readonly executionTableName: string;\n protected readonly nextAvailableTableName: string;\n\n constructor(client: unknown, options?: RateLimiterStorageOptions) {\n this.client = client as SupabaseClient;\n this.prefixes = options?.prefixes ?? [];\n this.prefixValues = options?.prefixValues ?? {};\n\n // Generate table names based on prefix configuration\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.executionTableName = `rate_limit_executions_${prefixNames}`;\n this.nextAvailableTableName = `rate_limit_next_available_${prefixNames}`;\n } else {\n this.executionTableName = \"rate_limit_executions\";\n this.nextAvailableTableName = \"rate_limit_next_available\";\n }\n }\n\n /**\n * Gets the SQL column type for a prefix column (Supabase supports UUID natively).\n */\n private getPrefixColumnType(type: PrefixColumn[\"type\"]): string {\n return type === \"uuid\" ? \"UUID\" : \"INTEGER\";\n }\n\n /**\n * Builds the prefix columns SQL for CREATE TABLE.\n */\n private buildPrefixColumnsSql(): string {\n if (this.prefixes.length === 0) return \"\";\n return (\n this.prefixes\n .map((p) => `${p.name} ${this.getPrefixColumnType(p.type)} NOT NULL`)\n .join(\",\\n \") + \",\\n \"\n );\n }\n\n /**\n * Builds prefix column names for use in queries.\n */\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n /**\n * Applies prefix filters to a Supabase query builder.\n */\n private applyPrefixFilters<T>(query: T): T {\n let result = query as any;\n for (const prefix of this.prefixes) {\n result = result.eq(prefix.name, this.prefixValues[prefix.name]);\n }\n return result as T;\n }\n\n /**\n * Gets prefix values as an object for inserts.\n */\n private getPrefixInsertValues(): Record<string, string | number> {\n const values: Record<string, string | number> = {};\n for (const prefix of this.prefixes) {\n values[prefix.name] = this.prefixValues[prefix.name];\n }\n return values;\n }\n\n /**\n * Schema setup for Supabase rate-limiter. See\n * {@link SupabaseQueueStorage.migrate} for why this storage doesn't share\n * the {@link PostgresMigrationRunner} bookkeeping table.\n */\n public async migrate(): Promise<void> {\n const prefixColumnsSql = this.buildPrefixColumnsSql();\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixIndexPrefix =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const indexSuffix = prefixColumnNames.length > 0 ? \"_\" + prefixColumnNames.join(\"_\") : \"\";\n\n // Create execution tracking table\n const createExecTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.executionTableName} (\n id SERIAL PRIMARY KEY,\n ${prefixColumnsSql}queue_name TEXT NOT NULL,\n executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()\n )\n `;\n\n const { error: execTableError } = await this.client.rpc(\"exec_sql\", {\n query: createExecTableSql,\n });\n if (execTableError && execTableError.code !== \"42P07\") {\n throw execTableError;\n }\n\n // Create index on execution table\n const createExecIndexSql = `\n CREATE INDEX IF NOT EXISTS rate_limit_exec_queue${indexSuffix}_idx \n ON ${this.executionTableName} (${prefixIndexPrefix}queue_name, executed_at)\n `;\n await this.client.rpc(\"exec_sql\", { query: createExecIndexSql });\n\n // Build primary key columns\n const primaryKeyColumns =\n prefixColumnNames.length > 0 ? `${prefixColumnNames.join(\", \")}, queue_name` : \"queue_name\";\n\n // Create next available table\n const createNextTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.nextAvailableTableName} (\n ${prefixColumnsSql}queue_name TEXT NOT NULL,\n next_available_at TIMESTAMP WITH TIME ZONE,\n PRIMARY KEY (${primaryKeyColumns})\n )\n `;\n\n const { error: nextTableError } = await this.client.rpc(\"exec_sql\", {\n query: createNextTableSql,\n });\n if (nextTableError && nextTableError.code !== \"42P07\") {\n throw nextTableError;\n }\n\n // Install the atomic reserve function. The function takes the queue name,\n // window-start cutoff, and max executions; returns true iff a row was\n // inserted. Advisory xact lock keyed on (table, prefixes, queue) ensures\n // concurrent acquirers serialize without contending on unrelated buckets.\n const fnName = this.atomicReserveFunctionName();\n const prefixSig = this.prefixes\n .map((p) => `${p.name} ${this.getPrefixColumnType(p.type)}`)\n .join(\", \");\n const prefixSigPrefix = prefixSig ? prefixSig + \", \" : \"\";\n const prefixWhere =\n this.prefixes.length > 0\n ? \" AND \" + this.prefixes.map((p) => `${p.name} = _${p.name}`).join(\" AND \")\n : \"\";\n const prefixInsertCols =\n this.prefixes.length > 0 ? this.prefixes.map((p) => p.name).join(\", \") + \", \" : \"\";\n const prefixInsertVals =\n this.prefixes.length > 0 ? this.prefixes.map((p) => `_${p.name}`).join(\", \") + \", \" : \"\";\n const lockKeyParts = [\n `'${this.executionTableName}'`,\n ...this.prefixes.map((p) => `_${p.name}::text`),\n `_queue_name::text`,\n ];\n const lockKeyExpr = `hashtextextended(${lockKeyParts.join(\" || '|' || \")}, 0)`;\n\n const createFnSql = `\n CREATE OR REPLACE FUNCTION ${fnName}(\n ${prefixSigPrefix}_queue_name TEXT, _window_start TIMESTAMPTZ, _max_exec INT\n ) RETURNS BIGINT AS $fn$\n DECLARE\n _count INT;\n _next TIMESTAMPTZ;\n _new_id BIGINT;\n BEGIN\n PERFORM pg_advisory_xact_lock(${lockKeyExpr});\n SELECT COUNT(*) INTO _count FROM ${this.executionTableName}\n WHERE queue_name = _queue_name AND executed_at > _window_start${prefixWhere};\n IF _count >= _max_exec THEN RETURN NULL; END IF;\n SELECT next_available_at INTO _next FROM ${this.nextAvailableTableName}\n WHERE queue_name = _queue_name${prefixWhere};\n IF _next IS NOT NULL AND _next > NOW() THEN RETURN NULL; END IF;\n INSERT INTO ${this.executionTableName} (${prefixInsertCols}queue_name)\n VALUES (${prefixInsertVals}_queue_name)\n RETURNING id INTO _new_id;\n RETURN _new_id;\n END;\n $fn$ LANGUAGE plpgsql;\n `;\n const { error: fnError } = await this.client.rpc(\"exec_sql\", { query: createFnSql });\n if (fnError) {\n throw fnError;\n }\n }\n\n /** Supabase rate-limiter runs DDL via `exec_sql`, not via the migration runner. */\n public getMigrations(): ReadonlyArray<unknown> {\n return [];\n }\n\n /** Stable function name derived from table name (Postgres identifiers ≤63 chars). */\n private atomicReserveFunctionName(): string {\n return `${this.executionTableName}_try_reserve`.slice(0, 63);\n }\n\n public async tryReserveExecution(\n queueName: string,\n maxExecutions: number,\n windowMs: number\n ): Promise<unknown | null> {\n const args: Record<string, unknown> = {\n _queue_name: queueName,\n _window_start: new Date(Date.now() - windowMs).toISOString(),\n _max_exec: maxExecutions,\n };\n for (const p of this.prefixes) {\n args[`_${p.name}`] = this.prefixValues[p.name];\n }\n // The PL/pgSQL function returns the inserted id BIGINT on success and\n // NULL when capacity is reached or external backoff is active.\n const { data, error } = await this.client.rpc(this.atomicReserveFunctionName(), args);\n if (error) throw error;\n // The function now returns the inserted row id BIGINT (or NULL when\n // capacity is reached). Real Supabase RPC returns the scalar directly;\n // the pglite-backed mock executes `SELECT * FROM fn()` and returns a row\n // array — accept both shapes.\n if (data === null || data === undefined) return null;\n if (Array.isArray(data)) {\n if (data.length === 0) return null;\n const first = Object.values(data[0] as Record<string, unknown>)[0];\n return first ?? null;\n }\n // Supabase RPC returns BIGINTs as either numbers or strings depending on\n // size; both are valid as opaque tokens.\n return data;\n }\n\n public async releaseExecution(queueName: string, token: unknown): Promise<void> {\n if (token === null || token === undefined) return;\n // Delete by id — NEVER by recency. Two concurrent acquirers can hold\n // tokens for different rows; deleting \"the most recent\" would release the\n // other worker's slot.\n let del = this.client\n .from(this.executionTableName)\n .delete()\n .eq(\"id\", token as number | string)\n .eq(\"queue_name\", queueName);\n del = this.applyPrefixFilters(del);\n const { error: delError } = await del;\n if (delError) throw delError;\n }\n\n public async recordExecution(queueName: string): Promise<void> {\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { error } = await this.client.from(this.executionTableName).insert({\n ...prefixInsertValues,\n queue_name: queueName,\n });\n\n if (error) throw error;\n }\n\n public async getExecutionCount(queueName: string, windowStartTime: string): Promise<number> {\n let query = this.client\n .from(this.executionTableName)\n .select(\"*\", { count: \"exact\", head: true })\n .eq(\"queue_name\", queueName)\n .gt(\"executed_at\", windowStartTime);\n\n query = this.applyPrefixFilters(query);\n\n const { count, error } = await query;\n\n if (error) throw error;\n return count ?? 0;\n }\n\n public async getOldestExecutionAtOffset(\n queueName: string,\n offset: number\n ): Promise<string | undefined> {\n let query = this.client\n .from(this.executionTableName)\n .select(\"executed_at\")\n .eq(\"queue_name\", queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query\n .order(\"executed_at\", { ascending: true })\n .range(offset, offset);\n\n if (error) throw error;\n if (!data || data.length === 0) return undefined;\n return new Date(data[0].executed_at).toISOString();\n }\n\n public async getNextAvailableTime(queueName: string): Promise<string | undefined> {\n let query = this.client\n .from(this.nextAvailableTableName)\n .select(\"next_available_at\")\n .eq(\"queue_name\", queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return undefined; // Not found\n throw error;\n }\n\n if (!data?.next_available_at) return undefined;\n return new Date(data.next_available_at).toISOString();\n }\n\n public async setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void> {\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { error } = await this.client.from(this.nextAvailableTableName).upsert(\n {\n ...prefixInsertValues,\n queue_name: queueName,\n next_available_at: nextAvailableAt,\n },\n {\n onConflict:\n this.prefixes.length > 0\n ? `${this.getPrefixColumnNames().join(\",\")},queue_name`\n : \"queue_name\",\n }\n );\n\n if (error) throw error;\n }\n\n public async clear(queueName: string): Promise<void> {\n let execQuery = this.client.from(this.executionTableName).delete().eq(\"queue_name\", queueName);\n execQuery = this.applyPrefixFilters(execQuery);\n const { error: execError } = await execQuery;\n if (execError) throw execError;\n\n let nextQuery = this.client\n .from(this.nextAvailableTableName)\n .delete()\n .eq(\"queue_name\", queueName);\n nextQuery = this.applyPrefixFilters(nextQuery);\n const { error: nextError } = await nextQuery;\n if (nextError) throw nextError;\n }\n}\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "535B74F0E33B129364756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "E7022DCB352EE4D964756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -80,7 +80,7 @@ class SupabaseQueueStorage {
80
80
  escapeSqlString(value) {
81
81
  return value.replace(/'/g, "''");
82
82
  }
83
- async setupDatabase() {
83
+ async migrate() {
84
84
  const createTypeSql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus).map((v) => `'${v}'`).join(",")})`;
85
85
  const { error: typeError } = await this.client.rpc("exec_sql", { query: createTypeSql });
86
86
  if (typeError && typeError.code !== "42710") {
@@ -128,6 +128,9 @@ class SupabaseQueueStorage {
128
128
  await this.client.rpc("exec_sql", { query: indexSql });
129
129
  }
130
130
  }
131
+ getMigrations() {
132
+ return [];
133
+ }
131
134
  async add(job) {
132
135
  const now = new Date().toISOString();
133
136
  job.queue = this.queueName;
@@ -579,7 +582,7 @@ class SupabaseRateLimiterStorage {
579
582
  }
580
583
  return values;
581
584
  }
582
- async setupDatabase() {
585
+ async migrate() {
583
586
  const prefixColumnsSql = this.buildPrefixColumnsSql();
584
587
  const prefixColumnNames = this.getPrefixColumnNames();
585
588
  const prefixIndexPrefix = prefixColumnNames.length > 0 ? prefixColumnNames.join(", ") + ", " : "";
@@ -656,6 +659,9 @@ class SupabaseRateLimiterStorage {
656
659
  throw fnError;
657
660
  }
658
661
  }
662
+ getMigrations() {
663
+ return [];
664
+ }
659
665
  atomicReserveFunctionName() {
660
666
  return `${this.executionTableName}_try_reserve`.slice(0, 63);
661
667
  }
@@ -762,4 +768,4 @@ export {
762
768
  SUPABASE_QUEUE_STORAGE
763
769
  };
764
770
 
765
- //# debugId=FFFCF982F7410DEF64756E2164756E21
771
+ //# debugId=5712D6F6F807D5B364756E2164756E21