@workglow/supabase 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/job-queue/SupabaseJobStore.d.ts +2 -6
- package/dist/job-queue/SupabaseJobStore.d.ts.map +1 -1
- package/dist/job-queue/SupabaseMessageQueue.d.ts +1 -14
- package/dist/job-queue/SupabaseMessageQueue.d.ts.map +1 -1
- package/dist/job-queue/SupabaseQueueStorage.d.ts +12 -0
- package/dist/job-queue/SupabaseQueueStorage.d.ts.map +1 -1
- package/dist/job-queue/browser.js +35 -57
- package/dist/job-queue/browser.js.map +6 -6
- package/dist/job-queue/createSupabaseQueue.d.ts +0 -3
- package/dist/job-queue/createSupabaseQueue.d.ts.map +1 -1
- package/dist/job-queue/node.js +35 -57
- package/dist/job-queue/node.js.map +6 -6
- package/package.json +7 -7
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/job-queue/SupabaseQueueStorage.ts", "../../src/job-queue/SupabaseRateLimiterStorage.ts", "../../src/job-queue/SupabaseMessageQueue.ts", "../../src/job-queue/SupabaseJobStore.ts", "../../src/job-queue/createSupabaseQueue.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 type {\n IQueueStorage,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueChangeType,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"@workglow/job-queue\";\nimport { JobStatus, validateLeaseMs } from \"@workglow/job-queue\";\nimport { PollingSubscriptionManager } from \"@workglow/storage\";\nimport { createServiceToken, deepEqual, makeFingerprint, uuid4 } from \"@workglow/util\";\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 // ABORTING is included in the enum for backward compatibility with existing data,\n // but the application no longer writes that value.\n const enumValues = [...Object.values(JobStatus), \"ABORTING\"]\n .filter((v, i, a) => a.indexOf(v) === i)\n .map((v) => `'${v}'`)\n .join(\",\");\n const createTypeSql = `CREATE TYPE job_status AS ENUM (${enumValues})`;\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 attempts integer default 0,\n max_attempts integer default 10,\n visible_at timestamp with time zone DEFAULT now(),\n last_attempted_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 lease_owner text,\n abort_requested_at timestamp with time zone,\n lease_expires_at timestamp with time zone\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 // Add new columns to existing tables and rename old columns (idempotent)\n const alterSqls = [\n `ALTER TABLE ${this.tableName} ADD COLUMN IF NOT EXISTS abort_requested_at timestamp with time zone`,\n `ALTER TABLE ${this.tableName} ADD COLUMN IF NOT EXISTS lease_expires_at timestamp with time zone`,\n `ALTER TABLE ${this.tableName} RENAME COLUMN run_after TO visible_at`,\n `ALTER TABLE ${this.tableName} RENAME COLUMN last_ran_at TO last_attempted_at`,\n `ALTER TABLE ${this.tableName} RENAME COLUMN run_attempts TO attempts`,\n `ALTER TABLE ${this.tableName} RENAME COLUMN max_retries TO max_attempts`,\n `ALTER TABLE ${this.tableName} RENAME COLUMN worker_id TO lease_owner`,\n ];\n for (const sql of alterSqls) {\n const { error } = await this.client.rpc(\"exec_sql\", { query: sql });\n // 42703 = undefined_column: expected on re-run after a RENAME COLUMN has\n // already applied (the old column no longer exists). All other errors\n // (permission denied, wrong table name, etc.) are re-thrown.\n if (error && error.code !== \"42703\") {\n throw new Error(`Failed to rename column: ${error.message}`);\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, visible_at)`,\n `CREATE INDEX IF NOT EXISTS job_queue_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}queue, status, visible_at)`,\n `CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint, status)`,\n // H2: UNIQUE so concurrent inserts for the same active (queue,\n // fingerprint) race to the DB and resolve via 23505 unique_violation.\n // Supabase shares this DDL pattern with the Postgres provider.\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_${this.tableName}_fingerprint_active ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint) WHERE status IN ('PENDING','PROCESSING')`,\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 = 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.visible_at = 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 visible_at: job.visible_at,\n created_at: job.created_at,\n deadline_at: job.deadline_at,\n max_attempts: job.max_attempts,\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) {\n // H2: race-safety for fingerprint dedup. Supabase surfaces Postgres\n // unique_violation as `code === \"23505\"` on the PostgREST error shape.\n // When the partial UNIQUE index on (queue, fingerprint) WHERE status\n // IN ('PENDING','PROCESSING') fires, resolve the race by returning\n // the winner's id instead of bubbling the error up.\n const e = error as { code?: string; details?: string; message?: string };\n const isUniqueViolation = e?.code === \"23505\";\n const involvesFingerprint =\n (typeof e?.details === \"string\" && /fingerprint/i.test(e.details)) ||\n (typeof e?.message === \"string\" && /fingerprint/i.test(e.message));\n if (isUniqueViolation && involvesFingerprint && job.fingerprint) {\n const winner = await this.findActiveByFingerprint(job.fingerprint, this.queueName);\n if (winner?.id != null) {\n job.id = winner.id;\n return winner.id;\n }\n }\n throw error;\n }\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(\"visible_at\", { 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 * Claims PENDING jobs ready to run, and also reclaims PROCESSING jobs whose\n * lease has expired (crash recovery). Sets lease_expires_at on the claimed row.\n * @param workerId - Worker ID to associate with the job (required)\n * @param opts - Optional options including leaseMs (default 30000)\n * @returns The next job or undefined if no job is available\n */\n public async next(\n workerId: string,\n opts?: { leaseMs?: number }\n ): Promise<JobStorageFormat<Input, Output> | undefined> {\n const leaseMs = opts?.leaseMs ?? 30000;\n validateLeaseMs(leaseMs, \"leaseMs\");\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 const sql = `\n UPDATE ${this.tableName}\n SET status = '${JobStatus.PROCESSING}',\n last_attempted_at = NOW() AT TIME ZONE 'UTC',\n lease_owner = '${escapedWorkerId}',\n lease_expires_at = NOW() AT TIME ZONE 'UTC' + (${Number(leaseMs)} * INTERVAL '1 millisecond'),\n -- Lease-expiry reclaim consumes one attempt against max_attempts;\n -- PENDING claims do not (the worker's validateJobState will FAIL\n -- the job when attempts >= max_attempts at next-step time).\n attempts = CASE WHEN status = '${JobStatus.PROCESSING}' THEN attempts + 1 ELSE attempts END,\n -- Always clear stale abort_requested_at on (re)claim so a flag set\n -- by an earlier worker doesn't immediately abort the new lease.\n abort_requested_at = NULL\n WHERE id = (\n SELECT id\n FROM ${this.tableName}\n WHERE queue = '${escapedQueueName}'\n AND (\n (status = '${JobStatus.PENDING}' AND visible_at <= NOW() AT TIME ZONE 'UTC')\n OR (status = '${JobStatus.PROCESSING}' AND (lease_expires_at IS NULL OR lease_expires_at < NOW() AT TIME ZONE 'UTC'))\n )\n ${prefixConditions}\n ORDER BY visible_at 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 * Extend the lease on a currently PROCESSING job.\n * @param id - The ID of the job to extend the lease for\n * @param workerId - Worker ID that must match the current lease owner (lease_owner)\n * @param ms - Number of milliseconds to extend the lease by\n */\n public async extendLease(id: unknown, workerId: string, ms: number): Promise<void> {\n // Validate lease arg FIRST so callers get a consistent RangeError across\n // backends regardless of whether the id happens to be invalid too.\n validateLeaseMs(ms, \"ms\");\n const validatedWorkerId = this.validateSqlValue(workerId, \"workerId\");\n const escapedWorkerId = this.escapeSqlString(validatedWorkerId);\n const numericId = Number(id);\n if (!Number.isFinite(numericId)) {\n throw new Error(`Invalid job id: ${id}`);\n }\n\n const prefixConditions = this.buildPrefixWhereSql();\n\n const sql = `\n UPDATE ${this.tableName}\n SET lease_expires_at = NOW() AT TIME ZONE 'UTC' + (${Number(ms)} * INTERVAL '1 millisecond')\n WHERE id = ${numericId}\n AND queue = '${this.escapeSqlString(this.validateSqlValue(this.queueName, \"queueName\"))}'\n AND lease_owner = '${escapedWorkerId}'\n AND status = '${JobStatus.PROCESSING}'\n ${prefixConditions}\n RETURNING id`;\n\n const { data, error } = await this.client.rpc(\"exec_sql\", { query: sql });\n if (error) throw error;\n\n // exec_sql returns affected rows; if empty, the lease was lost\n if (!data || !Array.isArray(data) || data.length === 0) {\n throw new Error(\n `extendLease failed: job ${String(id)} is not PROCESSING or lease is not owned by worker ${workerId}`\n );\n }\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 attempts and updates visible_at.\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_attempted_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(\"attempts, max_attempts\")\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?.attempts as number | undefined) ?? 0;\n const maxAttempts =\n (current?.max_attempts as number | undefined) ?? jobDetails.max_attempts ?? 10;\n const nextAttempts = currentAttempts + 1;\n\n if (jobDetails.status === JobStatus.PENDING) {\n // Check if the next attempt would exceed max attempts\n if (nextAttempts >= maxAttempts) {\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 attempts reached\",\n error_code: \"MAX_ATTEMPTS_REACHED\",\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n last_attempted_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. Clear abort_requested_at so an abort that was\n // requested DURING the previous attempt does not immediately cancel\n // the retry.\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 visible_at: jobDetails.visible_at!,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n attempts: nextAttempts,\n last_attempted_at: now,\n abort_requested_at: null,\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 attempts: nextAttempts,\n completed_at: now,\n last_attempted_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 (e.g. PROCESSING) — 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 visible_at: jobDetails.visible_at ?? null,\n attempts: nextAttempts,\n last_attempted_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 releaseClaim(jobId: unknown): Promise<void> {\n // releaseClaim returns the row to PENDING without consuming an attempt.\n // Clear abort_requested_at so an abort that was requested mid-claim does\n // not survive the release and immediately cancel the next claim.\n let query = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.PENDING,\n lease_owner: null,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n abort_requested_at: 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 * Terminal write that does NOT bump `attempts`. See IQueueStorage.finalize\n * for the rationale (avoids double-counting on ack/fail because the lease\n * reclaim path already charged the attempt at next() time).\n */\n public async finalize(\n id: unknown,\n fields: {\n output?: Output | null;\n error?: string | null;\n error_code?: string | null;\n status?: JobStatus;\n completed_at?: string | null;\n abort_requested_at?: string | null;\n lease_owner?: string | null;\n progress?: number;\n progress_message?: string;\n progress_details?: Record<string, any> | null;\n visible_at?: string | null;\n }\n ): Promise<void> {\n // Partial update — Supabase's PostgREST `update()` only writes the\n // properties present on the object passed in.\n const patch: Record<string, unknown> = {};\n if (\"output\" in fields) patch.output = fields.output ?? null;\n if (\"error\" in fields) patch.error = fields.error ?? null;\n if (\"error_code\" in fields) patch.error_code = fields.error_code ?? null;\n if (\"status\" in fields) patch.status = fields.status;\n if (\"completed_at\" in fields) patch.completed_at = fields.completed_at ?? null;\n if (\"abort_requested_at\" in fields) {\n patch.abort_requested_at = fields.abort_requested_at ?? null;\n }\n if (\"lease_owner\" in fields) patch.lease_owner = fields.lease_owner ?? null;\n if (\"progress\" in fields) patch.progress = fields.progress ?? 0;\n if (\"progress_message\" in fields) patch.progress_message = fields.progress_message ?? \"\";\n if (\"progress_details\" in fields) patch.progress_details = fields.progress_details ?? null;\n if (\"visible_at\" in fields) patch.visible_at = fields.visible_at ?? null;\n if (Object.keys(patch).length === 0) return;\n let query = this.client\n .from(this.tableName)\n .update(patch)\n .eq(\"id\", id as never)\n .eq(\"queue\", this.queueName);\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.\n * - If PENDING: immediately mark as FAILED with abort_requested_at set.\n * - If PROCESSING: set abort_requested_at only (leave status as PROCESSING).\n * - Otherwise: no-op.\n */\n public async abort(jobId: unknown): Promise<void> {\n const now = new Date().toISOString();\n\n // Abort PENDING → FAILED immediately\n {\n let query = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.FAILED,\n abort_requested_at: now,\n completed_at: now,\n })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.PENDING);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n // Abort PROCESSING → set abort_requested_at only\n {\n let query = this.client\n .from(this.tableName)\n .update({ abort_requested_at: now })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.PROCESSING);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n }\n\n /** Force-overwrite status without touching attempts (used to persist DISABLED after lease release). */\n public async saveStatus(jobId: unknown, status: string): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({ status })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query as any) as any;\n const { error } = await (query as any);\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 * Finds the most recent active (PENDING or PROCESSING) job with the given fingerprint in this queue.\n * Uses the partial index idx_<table>_fingerprint_active for O(1) lookup.\n */\n public async findActiveByFingerprint(\n fingerprint: string,\n queueName: string\n ): Promise<JobStorageFormat<Input, Output> | undefined> {\n const prefixConditions = this.buildPrefixWhereSql();\n const validatedQueueName = this.validateSqlValue(queueName, \"queueName\");\n const escapedQueueName = this.escapeSqlString(validatedQueueName);\n const escapedFingerprint = this.escapeSqlString(fingerprint);\n\n const sql = `\n SELECT * FROM ${this.tableName}\n WHERE fingerprint = '${escapedFingerprint}' AND queue = '${escapedQueueName}'\n AND status IN ('PENDING','PROCESSING')${prefixConditions}\n ORDER BY created_at DESC\n LIMIT 1`;\n\n const { data, error } = await this.client.rpc(\"exec_sql\", { query: sql });\n if (error) throw error;\n if (!data || !Array.isArray(data) || data.length === 0) return undefined;\n return data[0] as JobStorageFormat<Input, Output>;\n }\n\n /**\n * Retrieves multiple jobs by their IDs in a single query.\n * Returns results in the same order as the input ids array, with undefined for missing ids.\n */\n public async getMany(\n ids: readonly unknown[]\n ): Promise<Array<JobStorageFormat<Input, Output> | undefined>> {\n if (ids.length === 0) return [];\n\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .in(\"id\", ids as any[])\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { data, error } = await query;\n if (error) throw error;\n\n const map = new Map<unknown, JobStorageFormat<Input, Output>>();\n for (const row of (data ?? []) as JobStorageFormat<Input, Output>[]) {\n map.set(row.id, row);\n }\n return ids.map((id) => map.get(id));\n }\n\n /**\n * Atomically writes output and sets status=COMPLETED.\n */\n public async completeWithResult(id: unknown, result: Output): Promise<void> {\n const now = new Date().toISOString();\n let query = this.client\n .from(this.tableName)\n .update({\n output: result ?? null,\n status: \"COMPLETED\" as JobStatus,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n })\n .eq(\"id\", id as never)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n /**\n * Atomically writes error fields and sets status=FAILED.\n * Preserves existing error/error_code when opts fields are absent.\n */\n public async failWithError(\n id: unknown,\n opts: {\n readonly error?: string | null;\n readonly errorCode?: string | null;\n readonly abortRequested?: boolean;\n }\n ): Promise<void> {\n // For Supabase's PostgREST API, COALESCE isn't directly available — we use\n // raw SQL via exec_sql to preserve existing values when opts fields are absent.\n const numericId = Number(id);\n if (!Number.isFinite(numericId)) {\n throw new Error(`Invalid job id: ${id}`);\n }\n const prefixConditions = this.buildPrefixWhereSql();\n const validatedQueueName = this.validateSqlValue(this.queueName, \"queueName\");\n const escapedQueueName = this.escapeSqlString(validatedQueueName);\n\n const errorLiteral =\n \"error\" in opts\n ? opts.error != null\n ? `'${this.escapeSqlString(opts.error)}'`\n : \"NULL\"\n : \"NULL\";\n const errorCodeLiteral =\n \"errorCode\" in opts\n ? opts.errorCode != null\n ? `'${this.escapeSqlString(opts.errorCode)}'`\n : \"NULL\"\n : \"NULL\";\n const abortClause =\n opts.abortRequested === true\n ? `CASE WHEN abort_requested_at IS NOT NULL THEN abort_requested_at ELSE NOW() AT TIME ZONE 'UTC' END`\n : `abort_requested_at`;\n\n const sql = `\n UPDATE ${this.tableName}\n SET error = COALESCE(${errorLiteral}, error),\n error_code = COALESCE(${errorCodeLiteral}, error_code),\n abort_requested_at = ${abortClause},\n status = 'FAILED',\n completed_at = COALESCE(completed_at, NOW() AT TIME ZONE 'UTC')\n WHERE id = ${numericId} AND queue = '${escapedQueueName}'${prefixConditions}`;\n\n const { error } = await this.client.rpc(\"exec_sql\", { query: sql });\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",
|
|
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 type {\n IQueueStorage,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueChangeType,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"@workglow/job-queue\";\nimport { JobStatus, validateLeaseMs } from \"@workglow/job-queue\";\nimport { PollingSubscriptionManager } from \"@workglow/storage\";\nimport { createServiceToken, deepEqual, makeFingerprint, uuid4 } from \"@workglow/util\";\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 // ABORTING is included in the enum for backward compatibility with existing data,\n // but the application no longer writes that value.\n const enumValues = [...Object.values(JobStatus), \"ABORTING\"]\n .filter((v, i, a) => a.indexOf(v) === i)\n .map((v) => `'${v}'`)\n .join(\",\");\n const createTypeSql = `CREATE TYPE job_status AS ENUM (${enumValues})`;\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 attempts integer default 0,\n max_attempts integer default 10,\n visible_at timestamp with time zone DEFAULT now(),\n last_attempted_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 lease_owner text,\n abort_requested_at timestamp with time zone,\n lease_expires_at timestamp with time zone\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, visible_at)`,\n `CREATE INDEX IF NOT EXISTS job_queue_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}queue, status, visible_at)`,\n `CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint, status)`,\n // H2: UNIQUE so concurrent inserts for the same active (queue,\n // fingerprint) race to the DB and resolve via 23505 unique_violation.\n // Supabase shares this DDL pattern with the Postgres provider.\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_${this.tableName}_fingerprint_active ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint) WHERE status IN ('PENDING','PROCESSING')`,\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 = 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.visible_at = 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 visible_at: job.visible_at,\n created_at: job.created_at,\n deadline_at: job.deadline_at,\n max_attempts: job.max_attempts,\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) {\n // H2: race-safety for fingerprint dedup. Supabase surfaces Postgres\n // unique_violation as `code === \"23505\"` on the PostgREST error shape.\n // When the partial UNIQUE index on (queue, fingerprint) WHERE status\n // IN ('PENDING','PROCESSING') fires, resolve the race by returning\n // the winner's id instead of bubbling the error up.\n const e = error as { code?: string; details?: string; message?: string };\n const isUniqueViolation = e?.code === \"23505\";\n const involvesFingerprint =\n (typeof e?.details === \"string\" && /fingerprint/i.test(e.details)) ||\n (typeof e?.message === \"string\" && /fingerprint/i.test(e.message));\n if (isUniqueViolation && involvesFingerprint && job.fingerprint) {\n const winner = await this.findActiveByFingerprint(job.fingerprint, this.queueName);\n if (winner?.id != null) {\n job.id = winner.id;\n return winner.id;\n }\n }\n throw error;\n }\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(\"visible_at\", { 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 * Claims PENDING jobs ready to run, and also reclaims PROCESSING jobs whose\n * lease has expired (crash recovery). Sets lease_expires_at on the claimed row.\n * @param workerId - Worker ID to associate with the job (required)\n * @param opts - Optional options including leaseMs (default 30000)\n * @returns The next job or undefined if no job is available\n */\n public async next(\n workerId: string,\n opts?: { leaseMs?: number }\n ): Promise<JobStorageFormat<Input, Output> | undefined> {\n const leaseMs = opts?.leaseMs ?? 30000;\n validateLeaseMs(leaseMs, \"leaseMs\");\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 const sql = `\n UPDATE ${this.tableName}\n SET status = '${JobStatus.PROCESSING}',\n last_attempted_at = NOW() AT TIME ZONE 'UTC',\n lease_owner = '${escapedWorkerId}',\n lease_expires_at = NOW() AT TIME ZONE 'UTC' + (${Number(leaseMs)} * INTERVAL '1 millisecond'),\n -- Lease-expiry reclaim consumes one attempt against max_attempts;\n -- PENDING claims do not (the worker's validateJobState will FAIL\n -- the job when attempts >= max_attempts at next-step time).\n attempts = CASE WHEN status = '${JobStatus.PROCESSING}' THEN attempts + 1 ELSE attempts END,\n -- Always clear stale abort_requested_at on (re)claim so a flag set\n -- by an earlier worker doesn't immediately abort the new lease.\n abort_requested_at = NULL\n WHERE id = (\n SELECT id\n FROM ${this.tableName}\n WHERE queue = '${escapedQueueName}'\n AND (\n (status = '${JobStatus.PENDING}' AND visible_at <= NOW() AT TIME ZONE 'UTC')\n OR (status = '${JobStatus.PROCESSING}' AND (lease_expires_at IS NULL OR lease_expires_at < NOW() AT TIME ZONE 'UTC'))\n )\n ${prefixConditions}\n ORDER BY visible_at 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 * Extend the lease on a currently PROCESSING job.\n * @param id - The ID of the job to extend the lease for\n * @param workerId - Worker ID that must match the current lease owner (lease_owner)\n * @param ms - Number of milliseconds to extend the lease by\n */\n public async extendLease(id: unknown, workerId: string, ms: number): Promise<void> {\n // Validate lease arg FIRST so callers get a consistent RangeError across\n // backends regardless of whether the id happens to be invalid too.\n validateLeaseMs(ms, \"ms\");\n const validatedWorkerId = this.validateSqlValue(workerId, \"workerId\");\n const escapedWorkerId = this.escapeSqlString(validatedWorkerId);\n const numericId = Number(id);\n if (!Number.isFinite(numericId)) {\n throw new Error(`Invalid job id: ${id}`);\n }\n\n const prefixConditions = this.buildPrefixWhereSql();\n\n const sql = `\n UPDATE ${this.tableName}\n SET lease_expires_at = NOW() AT TIME ZONE 'UTC' + (${Number(ms)} * INTERVAL '1 millisecond')\n WHERE id = ${numericId}\n AND queue = '${this.escapeSqlString(this.validateSqlValue(this.queueName, \"queueName\"))}'\n AND lease_owner = '${escapedWorkerId}'\n AND status = '${JobStatus.PROCESSING}'\n ${prefixConditions}\n RETURNING id`;\n\n const { data, error } = await this.client.rpc(\"exec_sql\", { query: sql });\n if (error) throw error;\n\n // exec_sql returns affected rows; if empty, the lease was lost\n if (!data || !Array.isArray(data) || data.length === 0) {\n throw new Error(\n `extendLease failed: job ${String(id)} is not PROCESSING or lease is not owned by worker ${workerId}`\n );\n }\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 attempts and updates visible_at.\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_attempted_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(\"attempts, max_attempts\")\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?.attempts as number | undefined) ?? 0;\n const maxAttempts =\n (current?.max_attempts as number | undefined) ?? jobDetails.max_attempts ?? 10;\n const nextAttempts = currentAttempts + 1;\n\n if (jobDetails.status === JobStatus.PENDING) {\n // Check if the next attempt would exceed max attempts\n if (nextAttempts >= maxAttempts) {\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 attempts reached\",\n error_code: \"MAX_ATTEMPTS_REACHED\",\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n last_attempted_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. Clear abort_requested_at so an abort that was\n // requested DURING the previous attempt does not immediately cancel\n // the retry.\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 visible_at: jobDetails.visible_at!,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n attempts: nextAttempts,\n last_attempted_at: now,\n abort_requested_at: null,\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 attempts: nextAttempts,\n completed_at: now,\n last_attempted_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 (e.g. PROCESSING) — 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 visible_at: jobDetails.visible_at ?? null,\n attempts: nextAttempts,\n last_attempted_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 releaseClaim(jobId: unknown): Promise<void> {\n // releaseClaim returns the row to PENDING without consuming an attempt.\n // Clear abort_requested_at so an abort that was requested mid-claim does\n // not survive the release and immediately cancel the next claim.\n let query = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.PENDING,\n lease_owner: null,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n abort_requested_at: 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 * Terminal write that does NOT bump `attempts`. See IQueueStorage.finalize\n * for the rationale (avoids double-counting on ack/fail because the lease\n * reclaim path already charged the attempt at next() time).\n */\n public async finalize(\n id: unknown,\n fields: {\n output?: Output | null;\n error?: string | null;\n error_code?: string | null;\n status?: JobStatus;\n completed_at?: string | null;\n abort_requested_at?: string | null;\n lease_owner?: string | null;\n progress?: number;\n progress_message?: string;\n progress_details?: Record<string, any> | null;\n visible_at?: string | null;\n }\n ): Promise<void> {\n // Partial update — Supabase's PostgREST `update()` only writes the\n // properties present on the object passed in.\n const patch: Record<string, unknown> = {};\n if (\"output\" in fields) patch.output = fields.output ?? null;\n if (\"error\" in fields) patch.error = fields.error ?? null;\n if (\"error_code\" in fields) patch.error_code = fields.error_code ?? null;\n if (\"status\" in fields) patch.status = fields.status;\n if (\"completed_at\" in fields) patch.completed_at = fields.completed_at ?? null;\n if (\"abort_requested_at\" in fields) {\n patch.abort_requested_at = fields.abort_requested_at ?? null;\n }\n if (\"lease_owner\" in fields) patch.lease_owner = fields.lease_owner ?? null;\n if (\"progress\" in fields) patch.progress = fields.progress ?? 0;\n if (\"progress_message\" in fields) patch.progress_message = fields.progress_message ?? \"\";\n if (\"progress_details\" in fields) patch.progress_details = fields.progress_details ?? null;\n if (\"visible_at\" in fields) patch.visible_at = fields.visible_at ?? null;\n if (Object.keys(patch).length === 0) return;\n let query = this.client\n .from(this.tableName)\n .update(patch)\n .eq(\"id\", id as never)\n .eq(\"queue\", this.queueName);\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.\n * - If PENDING: immediately mark as FAILED with abort_requested_at set.\n * - If PROCESSING: set abort_requested_at only (leave status as PROCESSING).\n * - Otherwise: no-op.\n */\n public async abort(jobId: unknown): Promise<void> {\n const now = new Date().toISOString();\n\n // Abort PENDING → FAILED immediately\n {\n let query = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.FAILED,\n abort_requested_at: now,\n completed_at: now,\n })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.PENDING);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n // Abort PROCESSING → set abort_requested_at only\n {\n let query = this.client\n .from(this.tableName)\n .update({ abort_requested_at: now })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.PROCESSING);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n }\n\n /** Force-overwrite status without touching attempts (used to persist DISABLED after lease release). */\n public async saveStatus(jobId: unknown, status: string): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({ status })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query as any) as any;\n const { error } = await (query as any);\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 * Finds the most recent active (PENDING or PROCESSING) job with the given fingerprint in this queue.\n * Uses the partial index idx_<table>_fingerprint_active for O(1) lookup.\n */\n public async findActiveByFingerprint(\n fingerprint: string,\n queueName: string\n ): Promise<JobStorageFormat<Input, Output> | undefined> {\n const prefixConditions = this.buildPrefixWhereSql();\n const validatedQueueName = this.validateSqlValue(queueName, \"queueName\");\n const escapedQueueName = this.escapeSqlString(validatedQueueName);\n const escapedFingerprint = this.escapeSqlString(fingerprint);\n\n const sql = `\n SELECT * FROM ${this.tableName}\n WHERE fingerprint = '${escapedFingerprint}' AND queue = '${escapedQueueName}'\n AND status IN ('PENDING','PROCESSING')${prefixConditions}\n ORDER BY created_at DESC\n LIMIT 1`;\n\n const { data, error } = await this.client.rpc(\"exec_sql\", { query: sql });\n if (error) throw error;\n if (!data || !Array.isArray(data) || data.length === 0) return undefined;\n return data[0] as JobStorageFormat<Input, Output>;\n }\n\n /**\n * Retrieves multiple jobs by their IDs in a single query.\n * Returns results in the same order as the input ids array, with undefined for missing ids.\n */\n public async getMany(\n ids: readonly unknown[]\n ): Promise<Array<JobStorageFormat<Input, Output> | undefined>> {\n if (ids.length === 0) return [];\n\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .in(\"id\", ids as any[])\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { data, error } = await query;\n if (error) throw error;\n\n const map = new Map<unknown, JobStorageFormat<Input, Output>>();\n for (const row of (data ?? []) as JobStorageFormat<Input, Output>[]) {\n map.set(row.id, row);\n }\n return ids.map((id) => map.get(id));\n }\n\n /**\n * Atomically writes output and sets status=COMPLETED.\n */\n public async completeWithResult(id: unknown, result: Output): Promise<void> {\n const now = new Date().toISOString();\n let query = this.client\n .from(this.tableName)\n .update({\n output: result ?? null,\n status: \"COMPLETED\" as JobStatus,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n })\n .eq(\"id\", id as never)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n /**\n * Atomically writes error fields and sets status=FAILED.\n * Preserves existing error/error_code when opts fields are absent.\n */\n public async failWithError(\n id: unknown,\n opts: {\n readonly error?: string | null;\n readonly errorCode?: string | null;\n readonly abortRequested?: boolean;\n }\n ): Promise<void> {\n // For Supabase's PostgREST API, COALESCE isn't directly available — we use\n // raw SQL via exec_sql to preserve existing values when opts fields are absent.\n const numericId = Number(id);\n if (!Number.isFinite(numericId)) {\n throw new Error(`Invalid job id: ${id}`);\n }\n const prefixConditions = this.buildPrefixWhereSql();\n const validatedQueueName = this.validateSqlValue(this.queueName, \"queueName\");\n const escapedQueueName = this.escapeSqlString(validatedQueueName);\n\n const errorLiteral =\n \"error\" in opts\n ? opts.error != null\n ? `'${this.escapeSqlString(opts.error)}'`\n : \"NULL\"\n : \"NULL\";\n const errorCodeLiteral =\n \"errorCode\" in opts\n ? opts.errorCode != null\n ? `'${this.escapeSqlString(opts.errorCode)}'`\n : \"NULL\"\n : \"NULL\";\n const abortClause =\n opts.abortRequested === true\n ? `CASE WHEN abort_requested_at IS NOT NULL THEN abort_requested_at ELSE NOW() AT TIME ZONE 'UTC' END`\n : `abort_requested_at`;\n\n const sql = `\n UPDATE ${this.tableName}\n SET error = COALESCE(${errorLiteral}, error),\n error_code = COALESCE(${errorCodeLiteral}, error_code),\n abort_requested_at = ${abortClause},\n status = 'FAILED',\n completed_at = COALESCE(completed_at, NOW() AT TIME ZONE 'UTC')\n WHERE id = ${numericId} AND queue = '${escapedQueueName}'${prefixConditions}`;\n\n const { error } = await this.client.rpc(\"exec_sql\", { query: sql });\n if (error) throw error;\n }\n\n /**\n * Atomically writes status=DISABLED, releases the lease, clears progress\n * fields, and stamps `completed_at` (preserving an existing value via\n * COALESCE). Does NOT write error/error_code — DISABLED is not an error\n * transition.\n *\n * Implemented via `exec_sql` so the COALESCE on completed_at runs inside\n * the same UPDATE — PostgREST `.update()` cannot reference existing column\n * values, which would otherwise force a read-then-write and open a race\n * window with concurrent updates. Mirrors the {@link failWithError} pattern.\n */\n public async markDisabled(id: unknown): Promise<void> {\n const numericId = Number(id);\n if (!Number.isFinite(numericId)) {\n throw new Error(`Invalid job id: ${id}`);\n }\n const prefixConditions = this.buildPrefixWhereSql();\n const validatedQueueName = this.validateSqlValue(this.queueName, \"queueName\");\n const escapedQueueName = this.escapeSqlString(validatedQueueName);\n\n const sql = `\n UPDATE ${this.tableName}\n SET status = 'DISABLED',\n completed_at = COALESCE(completed_at, NOW() AT TIME ZONE 'UTC'),\n lease_owner = NULL,\n progress = 0,\n progress_message = '',\n progress_details = NULL\n WHERE id = ${numericId} AND queue = '${escapedQueueName}'${prefixConditions}`;\n\n const { error } = await this.client.rpc(\"exec_sql\", { query: sql });\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
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 type {\n IRateLimiterStorage,\n PrefixColumn,\n RateLimiterStorageOptions,\n RateLimiterStorageScope,\n} from \"@workglow/job-queue\";\nimport { createServiceToken } from \"@workglow/util\";\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
|
-
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type {\n IClaim,\n IMessageQueue,\n JobStorageFormat,\n MessageId,\n QueueChangePayload,\n QueueStorageScope,\n QueueSubscribeOptions,\n SendOptions,\n} from \"@workglow/job-queue\";\nimport type { SupabaseQueueStorage } from \"./SupabaseQueueStorage\";\n\
|
|
8
|
-
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type {\n IJobStore,\n JobRecord,\n JobStatus,\n JobStorageFormat,\n MessageId,\n SendOptions,\n} from \"@workglow/job-queue\";\nimport type {
|
|
9
|
-
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { QueueStorageOptions } from \"@workglow/job-queue\";\nimport { SupabaseJobStore } from \"./SupabaseJobStore\";\nimport { SupabaseMessageQueue
|
|
7
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type {\n IClaim,\n IMessageQueue,\n JobStorageFormat,\n MessageId,\n QueueChangePayload,\n QueueStorageScope,\n QueueSubscribeOptions,\n SendOptions,\n} from \"@workglow/job-queue\";\nimport type { SupabaseQueueStorage } from \"./SupabaseQueueStorage\";\n\nclass SupabaseClaim<Input, Output> implements IClaim<JobStorageFormat<Input, Output>> {\n constructor(\n private readonly core: SupabaseQueueStorage<Input, Output>,\n public readonly id: MessageId,\n public readonly body: JobStorageFormat<Input, Output>,\n public readonly attempts: number,\n private readonly workerId: string\n ) {}\n\n async ack(result?: unknown): Promise<void> {\n const current = (await this.core.get(this.id)) ?? this.body;\n const output = result !== undefined ? result : (current.output ?? null);\n await this.core.finalize(this.id, {\n output: output as Output | null,\n error: null,\n error_code: null,\n status: \"COMPLETED\",\n completed_at: current.completed_at ?? new Date().toISOString(),\n });\n }\n\n async retry(opts?: { delaySeconds?: number }): Promise<void> {\n const delay = opts?.delaySeconds ?? 0;\n const current = (await this.core.get(this.id)) ?? this.body;\n await this.core.complete({\n ...current,\n status: \"PENDING\",\n lease_owner: null,\n lease_expires_at: null,\n visible_at: new Date(Date.now() + delay * 1000).toISOString(),\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n });\n }\n\n async fail(opts?: {\n error?: string | null;\n errorCode?: string | null;\n abortRequested?: boolean;\n permanent?: boolean;\n }): Promise<void> {\n void opts?.permanent;\n const current = (await this.core.get(this.id)) ?? this.body;\n const error = opts?.error !== undefined ? opts.error : (current.error ?? null);\n const errorCode = opts?.errorCode !== undefined ? opts.errorCode : (current.error_code ?? null);\n const abortRequested = opts?.abortRequested === true;\n await this.core.finalize(this.id, {\n error,\n error_code: errorCode,\n abort_requested_at: abortRequested\n ? (current.abort_requested_at ?? new Date().toISOString())\n : (current.abort_requested_at ?? null),\n status: \"FAILED\",\n completed_at: current.completed_at ?? new Date().toISOString(),\n });\n }\n\n async extendLease(ms: number): Promise<void> {\n await this.core.extendLease(this.id, this.workerId, ms);\n }\n\n async disable(): Promise<void> {\n const current = await this.core.get(this.id);\n const completedAt = current?.completed_at ?? new Date().toISOString();\n await this.core.finalize(this.id, {\n status: \"DISABLED\",\n completed_at: completedAt,\n lease_owner: null,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n });\n }\n}\n\nexport class SupabaseMessageQueue<Input, Output> implements IMessageQueue<\n JobStorageFormat<Input, Output>\n> {\n public readonly scope: QueueStorageScope;\n\n /** @internal — shared with the paired job store */\n public readonly core: SupabaseQueueStorage<Input, Output>;\n\n constructor(core: SupabaseQueueStorage<Input, Output>) {\n this.core = core;\n this.scope = core.scope;\n }\n\n async send(body: JobStorageFormat<Input, Output>, opts?: SendOptions): Promise<MessageId> {\n return this.core.add(applySendOptions(body, opts));\n }\n\n async sendBatch(\n bodies: readonly JobStorageFormat<Input, Output>[],\n opts?: SendOptions\n ): Promise<readonly MessageId[]> {\n const ids: MessageId[] = [];\n for (const body of bodies) {\n ids.push(await this.send(body, opts));\n }\n return ids;\n }\n\n async receive(opts: {\n workerId: string;\n leaseMs: number;\n max?: number;\n }): Promise<readonly IClaim<JobStorageFormat<Input, Output>>[]> {\n const max = Math.max(1, opts.max ?? 1);\n const claims: IClaim<JobStorageFormat<Input, Output>>[] = [];\n while (claims.length < max) {\n const job = await this.core.next(opts.workerId, { leaseMs: opts.leaseMs });\n if (!job) break;\n claims.push(\n new SupabaseClaim<Input, Output>(this.core, job.id, job, job.attempts ?? 0, opts.workerId)\n );\n }\n return claims;\n }\n\n async releaseClaim(id: MessageId): Promise<void> {\n await this.core.releaseClaim(id);\n }\n\n async migrate(): Promise<void> {\n await this.core.migrate();\n }\n\n getMigrations(): ReadonlyArray<unknown> {\n return this.core.getMigrations();\n }\n\n subscribeToChanges(\n callback: (change: QueueChangePayload<any, any>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n return this.core.subscribeToChanges(callback, options);\n }\n}\n\nfunction applySendOptions<Input, Output>(\n body: JobStorageFormat<Input, Output>,\n opts?: SendOptions\n): JobStorageFormat<Input, Output> {\n if (!opts) return body;\n const out: JobStorageFormat<Input, Output> = { ...body };\n if (opts.delaySeconds != null) {\n out.visible_at = new Date(Date.now() + opts.delaySeconds * 1000).toISOString();\n }\n if (opts.timeoutSeconds != null) {\n out.deadline_at = new Date(Date.now() + opts.timeoutSeconds * 1000).toISOString();\n }\n if (opts.fingerprint != null) out.fingerprint = opts.fingerprint;\n if (opts.jobRunId != null) out.job_run_id = opts.jobRunId;\n if (opts.maxAttempts != null) out.max_attempts = opts.maxAttempts;\n return out;\n}\n",
|
|
8
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type {\n IJobStore,\n JobRecord,\n JobStatus,\n JobStorageFormat,\n MessageId,\n SendOptions,\n} from \"@workglow/job-queue\";\nimport type { SupabaseQueueStorage } from \"./SupabaseQueueStorage\";\n\nexport class SupabaseJobStore<Input, Output> implements IJobStore<Input, Output> {\n /** @internal — shared with the paired message queue */\n public readonly core: SupabaseQueueStorage<Input, Output>;\n\n constructor(core: SupabaseQueueStorage<Input, Output>) {\n this.core = core;\n }\n\n get(id: MessageId): Promise<JobRecord<Input, Output> | undefined> {\n return this.core.get(id);\n }\n\n async peek(status?: JobStatus, num?: number): Promise<readonly JobRecord<Input, Output>[]> {\n return this.core.peek(status as any, num);\n }\n\n size(status?: JobStatus): Promise<number> {\n return this.core.size(status as any);\n }\n\n async getByRunId(runId: string): Promise<readonly JobRecord<Input, Output>[]> {\n return this.core.getByRunId(runId);\n }\n\n outputForInput(input: Input): Promise<Output | null> {\n return this.core.outputForInput(input);\n }\n\n async saveProgress(\n id: MessageId,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void> {\n await this.core.saveProgress(id, progress, message, details as Record<string, any>);\n }\n\n async deleteByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n await this.core.deleteJobsByStatusAndAge(status, olderThanMs);\n }\n\n async delete(id: MessageId): Promise<void> {\n await this.core.delete(id);\n }\n\n async deleteAll(): Promise<void> {\n await this.core.deleteAll();\n }\n\n async abort(id: MessageId): Promise<void> {\n await this.core.abort(id);\n }\n\n async saveStatus(id: MessageId, status: JobStatus): Promise<void> {\n await this.core.saveStatus(id, status);\n }\n\n async create(body: JobStorageFormat<Input, Output>, opts: SendOptions): Promise<MessageId> {\n const enriched = {\n ...body,\n fingerprint: opts.fingerprint ?? body.fingerprint,\n job_run_id: opts.jobRunId ?? body.job_run_id,\n max_attempts: opts.maxAttempts ?? body.max_attempts,\n deadline_at:\n opts.timeoutSeconds != null\n ? new Date(Date.now() + opts.timeoutSeconds * 1000).toISOString()\n : body.deadline_at,\n } as JobStorageFormat<Input, Output>;\n return this.core.add(enriched);\n }\n\n async findActiveByFingerprint(\n fingerprint: string,\n queueName: string\n ): Promise<JobRecord<Input, Output> | undefined> {\n return this.core.findActiveByFingerprint(fingerprint, queueName);\n }\n\n async getMany(\n ids: readonly MessageId[]\n ): Promise<readonly (JobRecord<Input, Output> | undefined)[]> {\n return this.core.getMany(ids);\n }\n\n async completeWithResult(id: MessageId, result: Output): Promise<void> {\n await this.core.completeWithResult(id, result);\n }\n\n async failWithError(\n id: MessageId,\n opts: {\n readonly error?: string | null;\n readonly errorCode?: string | null;\n readonly abortRequested?: boolean;\n }\n ): Promise<void> {\n await this.core.failWithError(id, opts);\n }\n\n async markDisabled(id: MessageId): Promise<void> {\n await this.core.markDisabled(id);\n }\n\n async markEnqueueDeferred(\n id: MessageId,\n opts: { readonly visible_at: Date; readonly errorCode: string }\n ): Promise<void> {\n await this.core.finalize(id, {\n visible_at: opts.visible_at.toISOString(),\n error_code: opts.errorCode,\n });\n }\n}\n",
|
|
9
|
+
"/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport type { QueueStorageOptions } from \"@workglow/job-queue\";\nimport { SupabaseJobStore } from \"./SupabaseJobStore\";\nimport { SupabaseMessageQueue } from \"./SupabaseMessageQueue\";\nimport { SupabaseQueueStorage } from \"./SupabaseQueueStorage\";\n\n/**\n * Factory for the paired Supabase message queue and job store. Both\n * facades share a single underlying {@link SupabaseQueueStorage} so writes\n * through one are observable through the other.\n */\nexport function createSupabaseQueue<Input, Output>(\n queueName: string,\n client: SupabaseClient,\n opts?: QueueStorageOptions\n): {\n messageQueue: SupabaseMessageQueue<Input, Output>;\n jobStore: SupabaseJobStore<Input, Output>;\n} {\n const core = new SupabaseQueueStorage<Input, Output>(client, queueName, opts);\n return {\n messageQueue: new SupabaseMessageQueue<Input, Output>(core),\n jobStore: new SupabaseJobStore<Input, Output>(core),\n };\n}\n"
|
|
10
10
|
],
|
|
11
|
-
"mappings": "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",
|
|
12
|
-
"debugId": "
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "84543FECAC08FA2964756E2164756E21",
|
|
13
13
|
"names": []
|
|
14
14
|
}
|
|
@@ -7,7 +7,6 @@ import type { SupabaseClient } from "@supabase/supabase-js";
|
|
|
7
7
|
import type { QueueStorageOptions } from "@workglow/job-queue";
|
|
8
8
|
import { SupabaseJobStore } from "./SupabaseJobStore";
|
|
9
9
|
import { SupabaseMessageQueue } from "./SupabaseMessageQueue";
|
|
10
|
-
import { SupabaseQueueStorage } from "./SupabaseQueueStorage";
|
|
11
10
|
/**
|
|
12
11
|
* Factory for the paired Supabase message queue and job store. Both
|
|
13
12
|
* facades share a single underlying {@link SupabaseQueueStorage} so writes
|
|
@@ -16,7 +15,5 @@ import { SupabaseQueueStorage } from "./SupabaseQueueStorage";
|
|
|
16
15
|
export declare function createSupabaseQueue<Input, Output>(queueName: string, client: SupabaseClient, opts?: QueueStorageOptions): {
|
|
17
16
|
messageQueue: SupabaseMessageQueue<Input, Output>;
|
|
18
17
|
jobStore: SupabaseJobStore<Input, Output>;
|
|
19
|
-
/** @internal — exposed for callers that still need the legacy storage object. */
|
|
20
|
-
core: SupabaseQueueStorage<Input, Output>;
|
|
21
18
|
};
|
|
22
19
|
//# sourceMappingURL=createSupabaseQueue.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSupabaseQueue.d.ts","sourceRoot":"","sources":["../../src/job-queue/createSupabaseQueue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"createSupabaseQueue.d.ts","sourceRoot":"","sources":["../../src/job-queue/createSupabaseQueue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG9D;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAC/C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,mBAAmB,GACzB;IACD,YAAY,EAAE,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAClD,QAAQ,EAAE,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;CAC3C,CAMA"}
|