@workglow/postgres 0.2.28

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.
Files changed (41) hide show
  1. package/dist/job-queue/PostgresQueueStorage.d.ts +155 -0
  2. package/dist/job-queue/PostgresQueueStorage.d.ts.map +1 -0
  3. package/dist/job-queue/PostgresRateLimiterStorage.d.ts +57 -0
  4. package/dist/job-queue/PostgresRateLimiterStorage.d.ts.map +1 -0
  5. package/dist/job-queue/browser.d.ts +7 -0
  6. package/dist/job-queue/browser.d.ts.map +1 -0
  7. package/dist/job-queue/browser.js +732 -0
  8. package/dist/job-queue/browser.js.map +11 -0
  9. package/dist/job-queue/bun.d.ts +7 -0
  10. package/dist/job-queue/bun.d.ts.map +1 -0
  11. package/dist/job-queue/common.d.ts +8 -0
  12. package/dist/job-queue/common.d.ts.map +1 -0
  13. package/dist/job-queue/node.d.ts +7 -0
  14. package/dist/job-queue/node.d.ts.map +1 -0
  15. package/dist/job-queue/node.js +732 -0
  16. package/dist/job-queue/node.js.map +11 -0
  17. package/dist/storage/PostgresKvStorage.d.ts +27 -0
  18. package/dist/storage/PostgresKvStorage.d.ts.map +1 -0
  19. package/dist/storage/PostgresTabularStorage.d.ts +194 -0
  20. package/dist/storage/PostgresTabularStorage.d.ts.map +1 -0
  21. package/dist/storage/PostgresVectorStorage.d.ts +39 -0
  22. package/dist/storage/PostgresVectorStorage.d.ts.map +1 -0
  23. package/dist/storage/_postgres/browser.d.ts +32 -0
  24. package/dist/storage/_postgres/browser.d.ts.map +1 -0
  25. package/dist/storage/_postgres/node-bun.d.ts +26 -0
  26. package/dist/storage/_postgres/node-bun.d.ts.map +1 -0
  27. package/dist/storage/_postgres/pglite-pool.d.ts +21 -0
  28. package/dist/storage/_postgres/pglite-pool.d.ts.map +1 -0
  29. package/dist/storage/browser.d.ts +10 -0
  30. package/dist/storage/browser.d.ts.map +1 -0
  31. package/dist/storage/browser.js +951 -0
  32. package/dist/storage/browser.js.map +14 -0
  33. package/dist/storage/bun.d.ts +7 -0
  34. package/dist/storage/bun.d.ts.map +1 -0
  35. package/dist/storage/common.d.ts +10 -0
  36. package/dist/storage/common.d.ts.map +1 -0
  37. package/dist/storage/node.d.ts +7 -0
  38. package/dist/storage/node.d.ts.map +1 -0
  39. package/dist/storage/node.js +842 -0
  40. package/dist/storage/node.js.map +13 -0
  41. package/package.json +78 -0
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/job-queue/PostgresQueueStorage.ts", "../../src/job-queue/PostgresRateLimiterStorage.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createHash } from \"node:crypto\";\nimport type { Pool } from \"@workglow/postgres/storage\";\nimport { createServiceToken, getLogger, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport { JobStatus } from \"@workglow/job-queue\";\nimport type {\n IQueueStorage,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"@workglow/job-queue\";\n\nexport const POSTGRES_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.postgres\"\n);\n\n/** Regex for safe SQL identifiers: starts with a letter, then alphanumeric/underscores */\nconst SAFE_IDENTIFIER = /^[a-zA-Z][a-zA-Z0-9_]*$/;\n\n/**\n * Subset of pg.PoolClient that {@link PostgresQueueStorage.subscribeToChanges}\n * needs. Typed locally so we don't require `pg` types at the storage layer.\n */\ninterface ListenClient {\n query: (sql: string) => Promise<unknown>;\n release: () => void;\n removeAllListeners?: (event: string) => void;\n on: (event: string, listener: (...args: any[]) => void) => void;\n}\n\n/**\n * PostgreSQL implementation of a job queue.\n * Provides storage and retrieval for job execution states using PostgreSQL.\n */\nexport class PostgresQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n public readonly scope = \"cluster\" as const;\n /** The prefix column definitions */\n protected readonly prefixes: readonly PrefixColumn[];\n /** The prefix values for filtering */\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n /** The table name for the job queue */\n protected readonly tableName: string;\n\n constructor(\n protected readonly db: Pool,\n protected readonly queueName: string,\n options?: QueueStorageOptions\n ) {\n this.prefixes = options?.prefixes ?? [];\n this.prefixValues = options?.prefixValues ?? {};\n\n // Validate prefix column names to prevent SQL injection in DDL statements\n for (const prefix of this.prefixes) {\n if (!SAFE_IDENTIFIER.test(prefix.name)) {\n throw new Error(\n `Prefix column name must start with a letter and contain only letters, digits, and underscores, got: ${prefix.name}`\n );\n }\n }\n\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\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 * Builds WHERE clause conditions for prefix filtering\n * @param startParam - The starting parameter number for parameterized queries\n * @returns Object with conditions string and parameter values\n */\n private buildPrefixWhereClause(startParam: number): {\n conditions: string;\n params: Array<string | number>;\n } {\n if (this.prefixes.length === 0) {\n return { conditions: \"\", params: [] };\n }\n const conditions = this.prefixes.map((p, i) => `${p.name} = $${startParam + i}`).join(\" AND \");\n const params = this.prefixes.map((p) => this.prefixValues[p.name]);\n return { conditions: \" AND \" + conditions, params };\n }\n\n /**\n * Gets prefix values as an array in column order\n */\n private getPrefixParamValues(): Array<string | number> {\n return this.prefixes.map((p) => this.prefixValues[p.name]);\n }\n\n public async setupDatabase(): Promise<void> {\n let sql: string;\n try {\n const enumValues = Object.values(JobStatus);\n for (const v of enumValues) {\n if (!SAFE_IDENTIFIER.test(v)) {\n throw new Error(`Invalid JobStatus enum value: ${v}`);\n }\n }\n sql = `CREATE TYPE job_status AS ENUM (${enumValues.map((v) => `'${v}'`).join(\",\")})`;\n await this.db.query(sql);\n } catch (e: any) {\n // Ignore error if type already exists (code 42710)\n if (e.code !== \"42710\") throw e;\n }\n\n const prefixColumnsSql = this.buildPrefixColumnsSql();\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixIndexPrefix =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n\n sql = `\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n id SERIAL NOT NULL,\n ${prefixColumnsSql}fingerprint text NOT NULL,\n queue text NOT NULL,\n job_run_id text NOT NULL,\n status job_status NOT NULL default 'PENDING',\n input jsonb NOT NULL,\n output jsonb,\n run_attempts integer default 0,\n max_retries integer default 20,\n run_after timestamp with time zone DEFAULT now(),\n last_ran_at timestamp with time zone,\n created_at timestamp with time zone DEFAULT now(),\n deadline_at timestamp with time zone,\n completed_at timestamp with time zone,\n error text,\n error_code text,\n progress real DEFAULT 0,\n progress_message text DEFAULT '',\n progress_details jsonb,\n worker_id text\n )`;\n\n await this.db.query(sql);\n\n // Create indexes with prefix columns prepended\n const indexSuffix = prefixColumnNames.length > 0 ? \"_\" + prefixColumnNames.join(\"_\") : \"\";\n\n sql = `\n CREATE INDEX IF NOT EXISTS job_fetcher${indexSuffix}_idx \n ON ${this.tableName} (${prefixIndexPrefix}id, status, run_after)`;\n await this.db.query(sql);\n\n sql = `\n CREATE INDEX IF NOT EXISTS job_queue_fetcher${indexSuffix}_idx \n ON ${this.tableName} (${prefixIndexPrefix}queue, status, run_after)`;\n await this.db.query(sql);\n\n sql = `\n CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx\n ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint, status)`;\n await this.db.query(sql);\n\n // Install LISTEN/NOTIFY plumbing so subscribers can wake on INSERT/UPDATE\n // without polling. The channel name is derived from md5(table || queue) so\n // (a) it fits Postgres's 63-char identifier limit even with long queue\n // names, and (b) different queues on the same table don't share a channel.\n //\n // Best-effort: in-process Postgres-compatible engines like PGLite may not\n // implement pg_notify or plpgsql. Skip trigger installation in that case\n // — subscribeToChanges will throw synchronously for those engines anyway,\n // so callers fall back to polling.\n const fnName = `${this.tableName}_notify`;\n const trgName = `${this.tableName}_notify_trg`;\n try {\n await this.db.query(`\n CREATE OR REPLACE FUNCTION ${fnName}() RETURNS trigger AS $fn$\n DECLARE\n channel TEXT := 'wglw_q_' || md5('${this.tableName}' || COALESCE(NEW.queue, OLD.queue));\n payload TEXT;\n BEGIN\n payload := json_build_object(\n 'op', TG_OP,\n 'id', COALESCE(NEW.id, OLD.id),\n 'queue', COALESCE(NEW.queue, OLD.queue),\n 'status', COALESCE(NEW.status::text, OLD.status::text)\n )::text;\n PERFORM pg_notify(channel, payload);\n RETURN NULL;\n END;\n $fn$ LANGUAGE plpgsql;\n `);\n await this.db.query(`DROP TRIGGER IF EXISTS ${trgName} ON ${this.tableName}`);\n await this.db.query(`\n CREATE TRIGGER ${trgName}\n AFTER INSERT OR UPDATE ON ${this.tableName}\n FOR EACH ROW EXECUTE FUNCTION ${fnName}();\n `);\n } catch {\n // best-effort — the engine doesn't support LISTEN/NOTIFY; subscribers\n // will get a synchronous throw and fall back to polling.\n }\n }\n\n /**\n * Channel name for this storage's LISTEN/NOTIFY. Mirrors the trigger's\n * computation so subscriber and notifier agree.\n */\n private notifyChannelName(): string {\n // md5() returns 32 hex chars; combined with the 7-char prefix this is well\n // under Postgres's 63-byte identifier limit.\n const tableAndQueue = `${this.tableName}${this.queueName}`;\n const hash = createHash(\"md5\").update(tableAndQueue).digest(\"hex\");\n return `wglw_q_${hash}`;\n }\n\n /**\n * Adds a new job to the queue.\n * @param job - The job to add\n * @returns The ID of the added job\n */\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const now = new Date().toISOString();\n job.queue = this.queueName;\n job.job_run_id = job.job_run_id ?? uuid4();\n job.fingerprint = await makeFingerprint(job.input);\n job.status = JobStatus.PENDING;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n job.created_at = now;\n job.run_after = now;\n\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixColumnsInsert =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const prefixParamValues = this.getPrefixParamValues();\n const prefixParamPlaceholders =\n prefixColumnNames.length > 0\n ? prefixColumnNames.map((_, i) => `$${i + 1}`).join(\",\") + \",\"\n : \"\";\n const baseParamStart = prefixColumnNames.length + 1;\n\n const sql = `\n INSERT INTO ${this.tableName}(\n ${prefixColumnsInsert}queue, \n fingerprint, \n input, \n run_after,\n created_at,\n deadline_at,\n max_retries, \n job_run_id, \n progress, \n progress_message, \n progress_details\n )\n VALUES \n (${prefixParamPlaceholders}$${baseParamStart},$${baseParamStart + 1},$${baseParamStart + 2},$${baseParamStart + 3},$${baseParamStart + 4},$${baseParamStart + 5},$${baseParamStart + 6},$${baseParamStart + 7},$${baseParamStart + 8},$${baseParamStart + 9},$${baseParamStart + 10})\n RETURNING id`;\n const params = [\n ...prefixParamValues,\n job.queue,\n job.fingerprint,\n JSON.stringify(job.input),\n job.run_after,\n job.created_at,\n job.deadline_at,\n job.max_retries,\n job.job_run_id,\n job.progress,\n job.progress_message,\n job.progress_details ? JSON.stringify(job.progress_details) : null,\n ];\n const result = await this.db.query(sql, params);\n\n if (!result) throw new Error(\"Failed to add to queue\");\n job.id = result.rows[0].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 const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n const result = await this.db.query(\n `SELECT *\n FROM ${this.tableName}\n WHERE id = $1 AND queue = $2${prefixConditions}\n FOR UPDATE SKIP LOCKED\n LIMIT 1`,\n [id, this.queueName, ...prefixParams]\n );\n\n if (!result || result.rows.length === 0) return undefined;\n return result.rows[0];\n }\n\n /**\n * Retrieves a slice of jobs from the queue.\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<Array<JobStorageFormat<Input, Output>>> {\n num = Number(num) || 100; // TS does not validate, so ensure it is a number\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(4);\n const result = await this.db.query<\n JobStorageFormat<Input, Output>,\n Array<string | number | JobStatus>\n >(\n `\n SELECT *\n FROM ${this.tableName}\n WHERE queue = $1\n AND status = $2${prefixConditions}\n ORDER BY run_after ASC\n LIMIT $3\n FOR UPDATE SKIP LOCKED`,\n [this.queueName, status, num, ...prefixParams]\n );\n if (!result) return [];\n return result.rows;\n }\n\n /**\n * Retrieves the next available job that is ready to be processed.\n * @param workerId - Worker ID to associate with the job (required)\n * @returns The next job or undefined if no job is available\n */\n public async next(workerId: string): Promise<JobStorageFormat<Input, Output> | undefined> {\n // Parameters: $1=status, $2=queue, $3=status, $4=worker_id, $5+=prefix params\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(5);\n const result = await this.db.query<\n JobStorageFormat<Input, Output>,\n Array<string | number | JobStatus | null>\n >(\n `\n UPDATE ${this.tableName} \n SET status = $1, last_ran_at = NOW() AT TIME ZONE 'UTC', worker_id = $4\n WHERE id = (\n SELECT id \n FROM ${this.tableName} \n WHERE queue = $2 \n AND status = $3\n ${prefixConditions}\n AND run_after <= NOW() AT TIME ZONE 'UTC'\n ORDER BY run_after ASC \n FOR UPDATE SKIP LOCKED \n LIMIT 1\n )\n RETURNING *`,\n [JobStatus.PROCESSING, this.queueName, JobStatus.PENDING, workerId, ...prefixParams]\n );\n\n return result?.rows?.[0] ?? undefined;\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 const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n const result = await this.db.query<{ count: string }, Array<string | number | JobStatus>>(\n `\n SELECT COUNT(*) as count\n FROM ${this.tableName}\n WHERE queue = $1\n AND status = $2${prefixConditions}`,\n [this.queueName, status, ...prefixParams]\n );\n if (!result) return 0;\n return parseInt(result.rows[0].count, 10);\n }\n\n /**\n * Marks a job as complete with its output or error.\n * Enhanced error handling:\n * - For a retryable error, increments run_attempts and updates run_after.\n * - Marks a job as FAILED immediately for permanent or generic errors.\n */\n public async complete(jobDetails: JobStorageFormat<Input, Output>): Promise<void> {\n const prefixParams = this.getPrefixParamValues();\n\n if (jobDetails.status === JobStatus.DISABLED) {\n const { conditions: prefixConditions } = this.buildPrefixWhereClause(4);\n await this.db.query(\n `UPDATE ${this.tableName} \n SET \n status = $1, \n progress = 100,\n progress_message = '',\n progress_details = NULL,\n completed_at = NOW() AT TIME ZONE 'UTC'\n WHERE id = $2 AND queue = $3${prefixConditions}`,\n [jobDetails.status, jobDetails.id, this.queueName, ...prefixParams]\n );\n } else if (jobDetails.status === JobStatus.PENDING) {\n const { conditions: prefixConditions } = this.buildPrefixWhereClause(7);\n await this.db.query(\n `UPDATE ${this.tableName} \n SET \n error = $1, \n error_code = $2,\n status = $3, \n run_after = $4, \n progress = 0,\n progress_message = '',\n progress_details = NULL,\n run_attempts = run_attempts + 1, \n last_ran_at = NOW() AT TIME ZONE 'UTC'\n WHERE id = $5 AND queue = $6${prefixConditions}`,\n [\n jobDetails.error,\n jobDetails.error_code,\n jobDetails.status,\n jobDetails.run_after,\n jobDetails.id,\n this.queueName,\n ...prefixParams,\n ]\n );\n } else {\n const { conditions: prefixConditions } = this.buildPrefixWhereClause(7);\n await this.db.query(\n `\n UPDATE ${this.tableName} \n SET \n output = $1, \n error = $2, \n error_code = $3,\n status = $4, \n progress = 100,\n progress_message = '',\n progress_details = NULL,\n run_attempts = run_attempts + 1, \n completed_at = NOW() AT TIME ZONE 'UTC',\n last_ran_at = NOW() AT TIME ZONE 'UTC'\n WHERE id = $5 AND queue = $6${prefixConditions}`,\n [\n jobDetails.output ? JSON.stringify(jobDetails.output) : null,\n jobDetails.error ?? null,\n jobDetails.error_code ?? null,\n jobDetails.status,\n jobDetails.id,\n this.queueName,\n ...prefixParams,\n ]\n );\n }\n }\n\n /**\n * Clears all jobs from the queue.\n */\n public async deleteAll(): Promise<void> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(2);\n await this.db.query(\n `\n DELETE FROM ${this.tableName}\n WHERE queue = $1${prefixConditions}`,\n [this.queueName, ...prefixParams]\n );\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 const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n const result = await this.db.query(\n `\n SELECT output\n FROM ${this.tableName}\n WHERE fingerprint = $1 AND queue = $2 AND status = 'COMPLETED'${prefixConditions}`,\n [fingerprint, this.queueName, ...prefixParams]\n );\n if (!result || result.rows.length === 0) return null;\n return result.rows[0].output;\n }\n\n /**\n * Aborts a job by setting its status to \"ABORTING\".\n * This method will signal the corresponding AbortController so that\n * the job's execute() method (if it supports an AbortSignal parameter)\n * can clean up and exit.\n */\n public async abort(jobId: unknown): Promise<void> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n await this.db.query(\n `\n UPDATE ${this.tableName} \n SET status = 'ABORTING' \n WHERE id = $1 AND queue = $2${prefixConditions}`,\n [jobId, this.queueName, ...prefixParams]\n );\n }\n\n /**\n * Releases a claimed job back to PENDING without incrementing run_attempts.\n * @param jobId - The id of the claimed job to release.\n */\n public async release(jobId: unknown): Promise<void> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n await this.db.query(\n `\n UPDATE ${this.tableName}\n SET status = 'PENDING',\n worker_id = NULL,\n progress = 0,\n progress_message = '',\n progress_details = NULL\n WHERE id = $1 AND queue = $2${prefixConditions}`,\n [jobId, this.queueName, ...prefixParams]\n );\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 const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n const result = await this.db.query(\n `\n SELECT * FROM ${this.tableName} WHERE job_run_id = $1 AND queue = $2${prefixConditions}`,\n [job_run_id, this.queueName, ...prefixParams]\n );\n if (!result) return [];\n return result.rows;\n }\n\n /**\n * Implements the abstract saveProgress method from JobQueue\n */\n public async saveProgress(\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, any>\n ): Promise<void> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(6);\n await this.db.query(\n `\n UPDATE ${this.tableName} \n SET progress = $1,\n progress_message = $2,\n progress_details = $3\n WHERE id = $4 AND queue = $5${prefixConditions}`,\n [\n progress,\n message,\n details ? JSON.stringify(details) : null,\n jobId,\n this.queueName,\n ...prefixParams,\n ]\n );\n }\n\n /**\n * Deletes a job by its ID\n */\n public async delete(jobId: unknown): Promise<void> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n await this.db.query(\n `DELETE FROM ${this.tableName} WHERE id = $1 AND queue = $2${prefixConditions}`,\n [jobId, this.queueName, ...prefixParams]\n );\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 const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(4);\n await this.db.query(\n `DELETE FROM ${this.tableName} \n WHERE queue = $1 \n AND status = $2 \n AND completed_at IS NOT NULL \n AND completed_at <= $3${prefixConditions}`,\n [this.queueName, status, cutoffDate, ...prefixParams]\n );\n }\n\n /**\n * Subscribe to INSERT/UPDATE notifications via PostgreSQL LISTEN/NOTIFY.\n * Replaces 100ms-poll fallback for cluster Postgres deployments — workers\n * wake within network-latency of an actual change rather than on a timer.\n *\n * Acquires a dedicated client from the pool (LISTEN occupies a connection\n * for its lifetime). On unsubscribe, runs UNLISTEN and releases the client.\n * On connection loss, attempts to reconnect with bounded backoff and\n * re-LISTEN. After every successful (re)connect — including the initial\n * one — emits a synthetic `{ type: \"RESYNC\" }` event so subscribers can\n * re-poll state and pick up any rows inserted during the disconnect window\n * (or between subscribe and the first NOTIFY landing).\n *\n * Throws synchronously when the underlying pool lacks `connect()`\n * (single-connection wrappers like PGLite) so the caller's try/catch can\n * fall back to polling. JobQueueServer.start does this and logs at debug.\n *\n * `options.prefixFilter` follows {@link QueueSubscribeOptions.prefixFilter}:\n * `undefined` means \"use the storage instance's configured prefixValues\",\n * `{}` means \"receive all changes regardless of prefix\".\n */\n public subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n type PoolWithConnect = { connect: () => Promise<ListenClient> };\n const poolMaybe = this.db as unknown as Partial<PoolWithConnect>;\n if (typeof poolMaybe.connect !== \"function\") {\n // Detect synchronously so callers can catch and fall back to polling\n // without leaving a dangling unhandled promise rejection behind.\n throw new Error(\n \"PostgresQueueStorage.subscribeToChanges requires a pg.Pool (got a single-connection wrapper)\"\n );\n }\n const pool = poolMaybe as PoolWithConnect;\n\n const channel = this.notifyChannelName();\n // undefined -> default to instance prefixValues; {} -> no filtering;\n // partial -> filter by the provided subset (matches QueueSubscribeOptions docs).\n const effectivePrefixFilter: Readonly<Record<string, string | number>> | null =\n options?.prefixFilter === undefined\n ? this.prefixes.length > 0\n ? this.prefixValues\n : null\n : Object.keys(options.prefixFilter).length === 0\n ? null\n : options.prefixFilter;\n\n let unsubscribed = false;\n let activeClient: ListenClient | null = null;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let backoffMs = 250;\n\n const dispatch = (change: QueueChangePayload<Input, Output>): void => {\n try {\n callback(change);\n } catch (err) {\n // Never let user callbacks tear down the listener loop.\n getLogger().debug(\"PostgresQueueStorage subscribe callback threw\", {\n channel,\n changeType: change.type,\n error: err,\n });\n }\n };\n\n const matchesPrefix = (\n change: QueueChangePayload<Input, Output>,\n filter: Readonly<Record<string, string | number>>\n ): boolean => {\n const row = (change.new ?? change.old) as Record<string, unknown> | undefined;\n if (!row) return false;\n for (const [k, v] of Object.entries(filter)) {\n if (row[k] !== v) return false;\n }\n return true;\n };\n\n // Hydrate the row referenced by a notification. Postgres NOTIFY is capped\n // at 8 KB so we ship only {id, queue, status} in the payload and fetch the\n // full row here — keeps subscriber payloads consistent with other backends\n // (e.g. Supabase realtime, IndexedDb) and lets prefixFilter inspect the\n // prefix columns the row actually has.\n const hydrate = async (id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> => {\n try {\n return await this.get(id);\n } catch {\n return undefined;\n }\n };\n\n const handleNotification = (msg: { channel: string; payload?: string }): void => {\n if (msg.channel !== channel || !msg.payload) return;\n let parsed: { op: string; id: number; queue: string; status: string };\n try {\n parsed = JSON.parse(msg.payload);\n } catch {\n return;\n }\n void (async () => {\n const op = parsed.op;\n const fallback = {\n id: parsed.id,\n queue: parsed.queue,\n status: parsed.status,\n } as unknown as JobStorageFormat<Input, Output>;\n const fullRow = op === \"DELETE\" ? undefined : await hydrate(parsed.id);\n const change: QueueChangePayload<Input, Output> = {\n type: op === \"INSERT\" ? \"INSERT\" : op === \"DELETE\" ? \"DELETE\" : \"UPDATE\",\n new: op === \"DELETE\" ? undefined : (fullRow ?? fallback),\n old: op === \"DELETE\" ? fallback : undefined,\n };\n if (effectivePrefixFilter && !matchesPrefix(change, effectivePrefixFilter)) return;\n dispatch(change);\n })();\n };\n\n const connect = async (): Promise<void> => {\n if (unsubscribed) return;\n try {\n const client = await pool.connect();\n if (unsubscribed) {\n client.release();\n return;\n }\n activeClient = client;\n client.on(\"notification\", handleNotification);\n client.on(\"error\", () => scheduleReconnect());\n await client.query(`LISTEN ${channel}`);\n backoffMs = 250; // reset on successful (re)connect\n // Synthetic resync: any rows inserted between subscribe and LISTEN\n // landing (or during a reconnect window) have no NOTIFY backing.\n // Fire a no-payload event so subscribers can re-poll state.\n dispatch({ type: \"RESYNC\" });\n } catch {\n scheduleReconnect();\n }\n };\n\n const scheduleReconnect = (): void => {\n if (unsubscribed || reconnectTimer) return;\n const c = activeClient;\n activeClient = null;\n try {\n c?.removeAllListeners?.(\"notification\");\n c?.removeAllListeners?.(\"error\");\n c?.release();\n } catch {\n // best-effort\n }\n const delay = Math.min(backoffMs, 30_000);\n backoffMs = Math.min(backoffMs * 2, 30_000);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n void connect();\n }, delay);\n };\n\n void connect();\n\n return () => {\n unsubscribed = true;\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n const c = activeClient;\n activeClient = null;\n if (c) {\n // Best-effort UNLISTEN; ignore errors (connection may already be dead).\n c.query(`UNLISTEN ${channel}`)\n .catch(() => {})\n .finally(() => {\n try {\n c.removeAllListeners?.(\"notification\");\n c.removeAllListeners?.(\"error\");\n c.release();\n } catch {\n // best-effort\n }\n });\n }\n };\n }\n}\n",
6
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\nimport type { Pool } from \"@workglow/postgres/storage\";\nimport type { PrefixColumn } from \"@workglow/job-queue\";\nimport type {\n IRateLimiterStorage,\n RateLimiterStorageOptions,\n RateLimiterStorageScope,\n} from \"@workglow/job-queue\";\n\nexport const POSTGRES_RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\n \"ratelimiter.storage.postgres\"\n);\n\n/**\n * PostgreSQL implementation of rate limiter storage.\n * Manages execution records and next available times for rate limiting.\n */\nexport class PostgresRateLimiterStorage implements IRateLimiterStorage {\n public readonly scope: RateLimiterStorageScope = \"cluster\";\n /** The prefix column definitions */\n protected readonly prefixes: readonly PrefixColumn[];\n /** The prefix values for filtering */\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n /** The table name for execution tracking */\n protected readonly executionTableName: string;\n /** The table name for next available times */\n protected readonly nextAvailableTableName: string;\n\n constructor(\n protected readonly db: Pool,\n options?: RateLimiterStorageOptions\n ) {\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.\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 * Builds WHERE clause conditions for prefix filtering.\n * @param startParam - The starting parameter number for parameterized queries\n */\n private buildPrefixWhereClause(startParam: number): {\n conditions: string;\n params: Array<string | number>;\n } {\n if (this.prefixes.length === 0) {\n return { conditions: \"\", params: [] };\n }\n const conditions = this.prefixes.map((p, i) => `${p.name} = $${startParam + i}`).join(\" AND \");\n const params = this.prefixes.map((p) => this.prefixValues[p.name]);\n return { conditions: \" AND \" + conditions, params };\n }\n\n /**\n * Gets prefix values as an array in column order.\n */\n private getPrefixParamValues(): Array<string | number> {\n return this.prefixes.map((p) => this.prefixValues[p.name]);\n }\n\n public async setupDatabase(): Promise<void> {\n const prefixColumnsSql = this.buildPrefixColumnsSql();\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixIndexPrefix =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const indexSuffix = prefixColumnNames.length > 0 ? \"_\" + prefixColumnNames.join(\"_\") : \"\";\n\n await this.db.query(`\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 await this.db.query(`\n CREATE INDEX IF NOT EXISTS rate_limit_exec_queue${indexSuffix}_idx \n ON ${this.executionTableName} (${prefixIndexPrefix}queue_name, executed_at)\n `);\n\n // For the next_available table, we need a composite primary key with prefixes\n const primaryKeyColumns =\n prefixColumnNames.length > 0 ? `${prefixColumnNames.join(\", \")}, queue_name` : \"queue_name\";\n\n await this.db.query(`\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\n public async tryReserveExecution(\n queueName: string,\n maxExecutions: number,\n windowMs: number\n ): Promise<unknown | null> {\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixParamValues = this.getPrefixParamValues();\n const prefixCount = prefixColumnNames.length;\n\n // Parameter layout:\n // $1..$N prefix values (used in lock-key, count, next-available, and INSERT)\n // $(N+1) queueName\n // $(N+2) windowStart timestamp\n // $(N+3) maxExecutions\n const queueParam = `$${prefixCount + 1}`;\n const windowStartParam = `$${prefixCount + 2}`;\n\n // For the advisory lock we hash table-name + prefix values + queue-name into a bigint.\n // Use hashtextextended which returns int8 (advisory_xact_lock takes bigint).\n const lockKeyParts: string[] = [`'${this.executionTableName}'`];\n for (let i = 0; i < prefixCount; i++) {\n lockKeyParts.push(`$${i + 1}::text`);\n }\n lockKeyParts.push(`${queueParam}::text`);\n const lockKeyExpr = `hashtextextended(${lockKeyParts.join(\" || '|' || \")}, 0)`;\n\n const prefixWhere =\n prefixCount > 0\n ? \" AND \" + prefixColumnNames.map((p, i) => `${p} = $${i + 1}`).join(\" AND \")\n : \"\";\n\n const prefixInsertCols = prefixCount > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const prefixInsertPlaceholders =\n prefixCount > 0 ? prefixColumnNames.map((_, i) => `$${i + 1}`).join(\", \") + \", \" : \"\";\n\n const windowStart = new Date(Date.now() - windowMs).toISOString();\n\n // Use a dedicated client when the pool supports connect() (real pg.Pool)\n // — the advisory xact lock + transaction MUST run on the same connection\n // for atomicity. Without connect(), only known single-connection wrappers\n // (PGLitePool, raw PGlite) are safe, since they implicitly serialize all\n // queries through one underlying connection. Anything else (e.g. a custom\n // multi-connection adapter without connect()) would dispatch the lock and\n // the INSERT to different sessions, defeating the lock entirely.\n const supportsConnect =\n typeof (this.db as unknown as { connect?: unknown }).connect === \"function\";\n if (!supportsConnect) {\n // Without connect() we run BEGIN/advisory_lock/INSERT/COMMIT directly on\n // `db.query`. That's only safe if `db` is a single-connection wrapper\n // (every query goes through the same underlying session). Recognize:\n // - PGLitePool — our own wrapper class; constructor name preserved.\n // - PGlite — third-party, ships minified so `constructor.name`\n // can be obfuscated (observed: \"q\"). Detect via duck-typing on\n // methods PGlite uniquely exposes (`waitReady` Promise + `exec`).\n // Any other no-connect() pool would dispatch the lock and the INSERT\n // to potentially different sessions, breaking atomicity.\n const dbAny = this.db as unknown as {\n waitReady?: unknown;\n exec?: unknown;\n constructor?: { name?: string };\n };\n const ctorName = dbAny.constructor?.name;\n const looksLikePGlite = typeof dbAny.exec === \"function\" && dbAny.waitReady !== undefined;\n const looksLikePGLitePool = ctorName === \"PGLitePool\";\n if (!looksLikePGlite && !looksLikePGLitePool) {\n throw new Error(\n `PostgresRateLimiterStorage.tryReserveExecution requires a pg.Pool with connect() or a known single-connection wrapper (PGLitePool, PGlite); got ${ctorName ?? typeof this.db}. A multi-connection pool without connect() would dispatch the advisory lock and the INSERT to different sessions, breaking atomicity.`\n );\n }\n }\n const conn = supportsConnect\n ? await (\n this.db as unknown as {\n connect: () => Promise<{\n query: Pool[\"query\"];\n release: () => void;\n }>;\n }\n ).connect()\n : { query: this.db.query.bind(this.db), release: () => {} };\n\n try {\n await conn.query(\"BEGIN\");\n try {\n await conn.query(`SELECT pg_advisory_xact_lock(${lockKeyExpr})`, [\n ...prefixParamValues,\n queueName,\n ]);\n\n const countResult = await conn.query(\n `\n SELECT COUNT(*)::int AS n\n FROM ${this.executionTableName}\n WHERE queue_name = ${queueParam} AND executed_at > ${windowStartParam}${prefixWhere}\n `,\n [...prefixParamValues, queueName, windowStart]\n );\n const n: number = countResult.rows[0]?.n ?? 0;\n if (n >= maxExecutions) {\n await conn.query(\"COMMIT\");\n return null;\n }\n\n const naResult = await conn.query(\n `\n SELECT next_available_at\n FROM ${this.nextAvailableTableName}\n WHERE queue_name = ${queueParam}${prefixWhere}\n `,\n [...prefixParamValues, queueName]\n );\n const nextAvailableAt: Date | null = naResult.rows[0]?.next_available_at ?? null;\n if (nextAvailableAt && new Date(nextAvailableAt).getTime() > Date.now()) {\n await conn.query(\"COMMIT\");\n return null;\n }\n\n const insertResult = await conn.query(\n `\n INSERT INTO ${this.executionTableName} (${prefixInsertCols}queue_name)\n VALUES (${prefixInsertPlaceholders}${queueParam})\n RETURNING id\n `,\n [...prefixParamValues, queueName]\n );\n await conn.query(\"COMMIT\");\n return insertResult.rows[0]?.id ?? null;\n } catch (err) {\n try {\n await conn.query(\"ROLLBACK\");\n } catch {\n // best-effort\n }\n throw err;\n }\n } finally {\n conn.release();\n }\n }\n\n public async releaseExecution(queueName: string, token: unknown): Promise<void> {\n if (token === null || token === undefined) return;\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\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 await this.db.query(\n `DELETE FROM ${this.executionTableName} WHERE id = $1 AND queue_name = $2${prefixConditions}`,\n [token as string | number, queueName, ...prefixParams]\n );\n }\n\n public async recordExecution(queueName: string): Promise<void> {\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixColumnsInsert =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const prefixParamValues = this.getPrefixParamValues();\n const prefixParamPlaceholders =\n prefixColumnNames.length > 0\n ? prefixColumnNames.map((_, i) => `$${i + 1}`).join(\", \") + \", \"\n : \"\";\n const queueParamNum = prefixColumnNames.length + 1;\n\n await this.db.query(\n `\n INSERT INTO ${this.executionTableName} (${prefixColumnsInsert}queue_name)\n VALUES (${prefixParamPlaceholders}$${queueParamNum})\n `,\n [...prefixParamValues, queueName]\n );\n }\n\n public async getExecutionCount(queueName: string, windowStartTime: string): Promise<number> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n\n const result = await this.db.query(\n `\n SELECT COUNT(*) AS count\n FROM ${this.executionTableName}\n WHERE queue_name = $1 AND executed_at > $2${prefixConditions}\n `,\n [queueName, windowStartTime, ...prefixParams]\n );\n\n return parseInt(result.rows[0]?.count ?? \"0\", 10);\n }\n\n public async getOldestExecutionAtOffset(\n queueName: string,\n offset: number\n ): Promise<string | undefined> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(3);\n\n const result = await this.db.query(\n `\n SELECT executed_at\n FROM ${this.executionTableName}\n WHERE queue_name = $1${prefixConditions}\n ORDER BY executed_at ASC\n LIMIT 1 OFFSET $2\n `,\n [queueName, offset, ...prefixParams]\n );\n\n const executedAt = result.rows[0]?.executed_at;\n if (!executedAt) return undefined;\n return new Date(executedAt).toISOString();\n }\n\n public async getNextAvailableTime(queueName: string): Promise<string | undefined> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(2);\n\n const result = await this.db.query(\n `\n SELECT next_available_at\n FROM ${this.nextAvailableTableName}\n WHERE queue_name = $1${prefixConditions}\n `,\n [queueName, ...prefixParams]\n );\n\n const nextAvailableAt = result.rows[0]?.next_available_at;\n if (!nextAvailableAt) return undefined;\n return new Date(nextAvailableAt).toISOString();\n }\n\n public async setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void> {\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixColumnsInsert =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const prefixParamValues = this.getPrefixParamValues();\n const prefixParamPlaceholders =\n prefixColumnNames.length > 0\n ? prefixColumnNames.map((_, i) => `$${i + 1}`).join(\", \") + \", \"\n : \"\";\n const baseParamStart = prefixColumnNames.length + 1;\n\n // Build the conflict columns for upsert\n const conflictColumns =\n prefixColumnNames.length > 0 ? `${prefixColumnNames.join(\", \")}, queue_name` : \"queue_name\";\n\n await this.db.query(\n `\n INSERT INTO ${this.nextAvailableTableName} (${prefixColumnsInsert}queue_name, next_available_at)\n VALUES (${prefixParamPlaceholders}$${baseParamStart}, $${baseParamStart + 1})\n ON CONFLICT (${conflictColumns})\n DO UPDATE SET next_available_at = EXCLUDED.next_available_at\n `,\n [...prefixParamValues, queueName, nextAvailableAt]\n );\n }\n\n public async clear(queueName: string): Promise<void> {\n const { conditions: prefixConditions, params: prefixParams } = this.buildPrefixWhereClause(2);\n\n await this.db.query(\n `DELETE FROM ${this.executionTableName} WHERE queue_name = $1${prefixConditions}`,\n [queueName, ...prefixParams]\n );\n await this.db.query(\n `DELETE FROM ${this.nextAvailableTableName} WHERE queue_name = $1${prefixConditions}`,\n [queueName, ...prefixParams]\n );\n }\n}\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "561DECA09B89A19664756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { JsonSchema } from "@workglow/util/schema";
7
+ import { PostgresTabularStorage } from "./PostgresTabularStorage";
8
+ import { DefaultKeyValueKey, DefaultKeyValueSchema, IKvStorage, KvViaTabularStorage } from "@workglow/storage";
9
+ export declare const POSTGRES_KV_REPOSITORY: import("@workglow/util").ServiceToken<IKvStorage<string, any, any>>;
10
+ /**
11
+ * A key-value repository implementation that uses PostgreSQL for persistent storage.
12
+ * Leverages a tabular repository abstraction for PostgreSQL operations.
13
+ *
14
+ * @template Key - The type of the primary key
15
+ * @template Value - The type of the value being stored
16
+ * @template Combined - Combined type of Key & Value
17
+ */
18
+ export declare class PostgresKvStorage extends KvViaTabularStorage {
19
+ db: any;
20
+ dbName: string;
21
+ tabularRepository: PostgresTabularStorage<typeof DefaultKeyValueSchema, typeof DefaultKeyValueKey>;
22
+ /**
23
+ * Creates a new KvStorage instance
24
+ */
25
+ constructor(db: any, dbName: string, keySchema?: JsonSchema, valueSchema?: JsonSchema);
26
+ }
27
+ //# sourceMappingURL=PostgresKvStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresKvStorage.d.ts","sourceRoot":"","sources":["../../src/storage/PostgresKvStorage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,UAAU,EACV,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,sBAAsB,qEAElC,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,iBAAkB,SAAQ,mBAAmB;IAU/C,EAAE,EAAE,GAAG;IACP,MAAM,EAAE,MAAM;IAVhB,iBAAiB,EAAE,sBAAsB,CAC9C,OAAO,qBAAqB,EAC5B,OAAO,kBAAkB,CAC1B,CAAC;IAEF;;OAEG;IACH,YACS,EAAE,EAAE,GAAG,EACP,MAAM,EAAE,MAAM,EACrB,SAAS,GAAE,UAA+B,EAC1C,WAAW,GAAE,UAAe,EAS7B;CACF"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { Pool } from "@workglow/postgres/storage";
7
+ import { DataPortSchemaObject, FromSchema, JsonSchema, TypedArraySchemaOptions } from "@workglow/util/schema";
8
+ import { BaseSqlTabularStorage, ClientProvidedKeysOption, AnyTabularStorage, AutoGeneratedKeys, CoveringIndexQueryOptions, DeleteSearchCriteria, InsertEntity, QueryOptions, SearchCriteria, SimplifyPrimaryKey, TabularChangePayload, TabularSubscribeOptions, ValueOptionType } from "@workglow/storage";
9
+ export declare const POSTGRES_TABULAR_REPOSITORY: import("@workglow/util").ServiceToken<AnyTabularStorage>;
10
+ /**
11
+ * A PostgreSQL-based tabular repository implementation that extends BaseSqlTabularStorage.
12
+ * This class provides persistent storage for data in a PostgreSQL database,
13
+ * making it suitable for multi-user scenarios.
14
+ *
15
+ * @template Schema - The schema definition for the entity
16
+ * @template PrimaryKeyNames - Array of property names that form the primary key
17
+ */
18
+ export declare class PostgresTabularStorage<Schema extends DataPortSchemaObject, PrimaryKeyNames extends ReadonlyArray<keyof Schema["properties"]>, Entity = FromSchema<Schema, TypedArraySchemaOptions>, PrimaryKey = SimplifyPrimaryKey<Entity, PrimaryKeyNames>, Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>, InsertType extends InsertEntity<Entity, AutoGeneratedKeys<Schema>> = InsertEntity<Entity, AutoGeneratedKeys<Schema>>> extends BaseSqlTabularStorage<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value, InsertType> {
19
+ protected db: Pool;
20
+ /**
21
+ * Creates a new PostgresTabularStorage instance.
22
+ *
23
+ * @param db - PostgreSQL db
24
+ * @param table - Name of the table to store data (defaults to "tabular_store")
25
+ * @param schema - Schema defining the structure of the entity
26
+ * @param primaryKeyNames - Array of property names that form the primary key
27
+ * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,
28
+ * while each array creates a compound index with columns in the specified order.
29
+ * @param clientProvidedKeys - How to handle client-provided values for auto-generated keys
30
+ */
31
+ constructor(db: Pool, table: string | undefined, schema: Schema, primaryKeyNames: PrimaryKeyNames, indexes?: readonly (keyof NoInfer<Entity> | readonly (keyof NoInfer<Entity>)[])[], clientProvidedKeys?: ClientProvidedKeysOption);
32
+ /**
33
+ * Initializes the database table with the required schema.
34
+ * Creates the table if it doesn't exist with primary key and value columns.
35
+ * Must be called before using any other methods.
36
+ */
37
+ setupDatabase(): Promise<void>;
38
+ protected isVectorFormat(format?: string): boolean;
39
+ protected getVectorDimensions(typeDef: JsonSchema): number | undefined;
40
+ /**
41
+ * Maps TypeScript/JavaScript types to corresponding PostgreSQL data types.
42
+ * Uses additional schema information like minimum/maximum values, nullable status,
43
+ * and string lengths to create more optimized column types.
44
+ *
45
+ * @param typeDef - The TypeScript/JavaScript type to map
46
+ * @returns The corresponding PostgreSQL data type
47
+ */
48
+ protected mapTypeToSQL(typeDef: JsonSchema): string;
49
+ /**
50
+ * Generates the SQL column definitions for primary key fields with constraints
51
+ * Handles auto-generated keys using SERIAL for integers and UUID DEFAULT for strings
52
+ * @returns SQL string containing primary key column definitions
53
+ */
54
+ protected constructPrimaryKeyColumns($delimiter?: string): string;
55
+ /**
56
+ * Generates the SQL column definitions for value fields with constraints
57
+ * @returns SQL string containing value column definitions
58
+ */
59
+ protected constructValueColumns($delimiter?: string): string;
60
+ /**
61
+ * Convert JavaScript values to PostgreSQL values, including TypedArray to vector string
62
+ */
63
+ protected jsToSqlValue(column: string, value: Entity[keyof Entity]): ValueOptionType;
64
+ /**
65
+ * Convert PostgreSQL values to JS values. Ensures numeric strings become numbers where schema says number.
66
+ */
67
+ protected sqlToJsValue(column: string, value: ValueOptionType): Entity[keyof Entity];
68
+ /**
69
+ * Determines if a field should be treated as unsigned based on schema properties
70
+ * @param typeDef - The schema type definition
71
+ * @returns true if the field should be treated as unsigned
72
+ */
73
+ protected shouldBeUnsigned(typeDef: JsonSchema): boolean;
74
+ /**
75
+ * Gets information about vector columns in the schema
76
+ * @returns Array of objects with column name and dimension
77
+ */
78
+ protected getVectorColumns(): Array<{
79
+ column: string;
80
+ dimension: number;
81
+ }>;
82
+ /**
83
+ * Creates vector-specific indexes (HNSW for pgvector)
84
+ * Called after table creation if vector columns exist
85
+ */
86
+ protected createVectorIndexes(): Promise<void>;
87
+ /**
88
+ * Stores or updates a row in the database.
89
+ * Uses UPSERT (INSERT ... ON CONFLICT DO UPDATE) for atomic operations.
90
+ *
91
+ * @param entity - The entity to store (may be missing auto-generated keys)
92
+ * @returns The entity with any server-generated fields updated
93
+ * @emits "put" event with the updated entity when successful
94
+ */
95
+ put(entity: InsertType): Promise<Entity>;
96
+ /**
97
+ * Stores multiple rows in the database in a bulk operation.
98
+ * Uses individual put calls to ensure auto-generated keys are handled correctly.
99
+ *
100
+ * @param entities - Array of entities to store (may be missing auto-generated keys)
101
+ * @returns Array of entities with any server-generated fields updated
102
+ * @emits "put" event for each entity stored
103
+ */
104
+ putBulk(entities: InsertType[]): Promise<Entity[]>;
105
+ /**
106
+ * Retrieves a value from the database by its primary key.
107
+ *
108
+ * @param key - The primary key object to look up
109
+ * @returns The stored value or undefined if not found
110
+ * @emits "get" event with the key when successful
111
+ */
112
+ get(key: PrimaryKey): Promise<Entity | undefined>;
113
+ /**
114
+ * Deletes a row from the database.
115
+ *
116
+ * @param key - The primary key object to delete
117
+ * @emits "delete" event with the key when successful
118
+ */
119
+ delete(value: PrimaryKey | Entity): Promise<void>;
120
+ /**
121
+ * Retrieves all entries from the database table, with optional ordering, offset, and limit.
122
+ * @param options - Optional ordering, limit, and offset options
123
+ * @returns Promise resolving to an array of entries or undefined if not found
124
+ */
125
+ getAll(options?: QueryOptions<Entity>): Promise<Entity[] | undefined>;
126
+ /**
127
+ * Deletes all rows from the database table.
128
+ * @emits "clearall" event when successful
129
+ */
130
+ deleteAll(): Promise<void>;
131
+ /**
132
+ * Returns the total number of rows in the database.
133
+ *
134
+ * @returns Promise resolving to the count of stored items
135
+ */
136
+ size(): Promise<number>;
137
+ /**
138
+ * Counts rows matching the specified search criteria.
139
+ */
140
+ count(criteria?: SearchCriteria<Entity>): Promise<number>;
141
+ /**
142
+ * Fetches a page of records from the repository.
143
+ * @param offset - Number of records to skip
144
+ * @param limit - Maximum number of records to return
145
+ * @returns Array of entities or undefined if no records found
146
+ */
147
+ getBulk(offset: number, limit: number): Promise<Entity[] | undefined>;
148
+ /**
149
+ * Builds WHERE clause conditions from delete search criteria.
150
+ * @param criteria - The search criteria object
151
+ * @returns Object with whereClause string and params array
152
+ */
153
+ protected buildDeleteSearchWhere(criteria: DeleteSearchCriteria<Entity>): {
154
+ whereClause: string;
155
+ params: ValueOptionType[];
156
+ };
157
+ /**
158
+ * Deletes all entries matching the specified search criteria.
159
+ * Supports multiple columns with optional comparison operators.
160
+ *
161
+ * @param criteria - Object with column names as keys and values or SearchConditions
162
+ */
163
+ deleteSearch(criteria: DeleteSearchCriteria<Entity>): Promise<void>;
164
+ /**
165
+ * Queries entries matching the specified search criteria with optional ordering, limit, and offset.
166
+ *
167
+ * @param criteria - Object with column names as keys and values or SearchConditions
168
+ * @param options - Optional ordering, limit, and offset options
169
+ * @returns Array of matching entities or undefined if no matches found
170
+ */
171
+ query(criteria: SearchCriteria<Entity>, options?: QueryOptions<Entity>): Promise<Entity[] | undefined>;
172
+ /**
173
+ * Queries entries matching the specified search criteria, returning only the selected columns.
174
+ * Requires a covering index that satisfies the criteria, orderBy, and select columns.
175
+ *
176
+ * @param criteria - Object with column names as keys and values or SearchConditions
177
+ * @param options - Required select columns plus optional orderBy, limit, and offset
178
+ * @returns Array of partial entities containing only the selected columns
179
+ * @throws {CoveringIndexMissingError} when no registered index covers the query
180
+ */
181
+ queryIndex<K extends keyof Entity & string>(criteria: SearchCriteria<Entity>, options: CoveringIndexQueryOptions<Entity, K>): Promise<Pick<Entity, K>[]>;
182
+ /**
183
+ * Subscribes to changes in the repository.
184
+ * NOT IMPLEMENTED for PostgreSQL storage.
185
+ *
186
+ * @throws Error always - subscribeToChanges is not supported for PostgreSQL storage
187
+ */
188
+ subscribeToChanges(callback: (change: TabularChangePayload<Entity>) => void, options?: TabularSubscribeOptions): () => void;
189
+ /**
190
+ * Destroys the repository and frees up resources.
191
+ */
192
+ destroy(): void;
193
+ }
194
+ //# sourceMappingURL=PostgresTabularStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresTabularStorage.d.ts","sourceRoot":"","sources":["../../src/storage/PostgresTabularStorage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { Pool } from "@workglow/postgres/storage";
7
+ import type { DataPortSchemaObject, FromSchema, TypedArray, TypedArrayConstructor, TypedArraySchemaOptions } from "@workglow/util/schema";
8
+ import { PostgresTabularStorage } from "./PostgresTabularStorage";
9
+ import type { HybridSearchOptions, IVectorStorage, VectorSearchOptions } from "@workglow/storage";
10
+ export declare class PostgresVectorStorage<Schema extends DataPortSchemaObject, PrimaryKeyNames extends ReadonlyArray<keyof Schema["properties"]>, Metadata extends Record<string, unknown> = Record<string, unknown>, Entity = FromSchema<Schema, TypedArraySchemaOptions>> extends PostgresTabularStorage<Schema, PrimaryKeyNames, Entity> implements IVectorStorage<Metadata, Schema, Entity, PrimaryKeyNames> {
11
+ private vectorDimensions;
12
+ private readonly vectorCtor;
13
+ private vectorPropertyName;
14
+ private metadataPropertyName;
15
+ /**
16
+ * Creates a new PostgreSQL vector repository
17
+ * @param db - PostgreSQL connection pool
18
+ * @param table - The name of the table to use for storage
19
+ * @param schema - The schema definition for the entity
20
+ * @param primaryKeyNames - Array of property names that form the primary key
21
+ * @param indexes - Array of columns or column arrays to make searchable
22
+ * @param dimensions - The number of dimensions of the vector
23
+ * @param vectorCtor - TypedArray constructor used when hydrating vectors from SQL text (e.g. {@link Float32Array})
24
+ */
25
+ constructor(db: Pool, table: string, schema: Schema, primaryKeyNames: PrimaryKeyNames, indexes: readonly (keyof NoInfer<Entity> | readonly (keyof NoInfer<Entity>)[])[] | undefined, dimensions: number, vectorCtor?: TypedArrayConstructor);
26
+ getVectorDimensions(): number;
27
+ similaritySearch(query: TypedArray, options?: VectorSearchOptions<Metadata>): Promise<Array<Entity & {
28
+ score: number;
29
+ }>>;
30
+ hybridSearch(query: TypedArray, options: HybridSearchOptions<Metadata>): Promise<(Entity & {
31
+ score: number;
32
+ })[]>;
33
+ private searchFallback;
34
+ private hybridSearchFallback;
35
+ private getPrimaryKeyWhereClause;
36
+ private getPrimaryKeyValues;
37
+ private matchesFilter;
38
+ }
39
+ //# sourceMappingURL=PostgresVectorStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresVectorStorage.d.ts","sourceRoot":"","sources":["../../src/storage/PostgresVectorStorage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,KAAK,EACV,oBAAoB,EACpB,UAAU,EACV,UAAU,EACV,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAoBlG,qBAAa,qBAAqB,CAChC,MAAM,SAAS,oBAAoB,EACnC,eAAe,SAAS,aAAa,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC,EACjE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClE,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAEpD,SAAQ,sBAAsB,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAC9D,YAAW,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC;IAEpE,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;IACnD,OAAO,CAAC,kBAAkB,CAAe;IACzC,OAAO,CAAC,oBAAoB,CAA2B;IAEvD;;;;;;;;;OASG;IACH,YACE,EAAE,EAAE,IAAI,EACR,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,SAAS,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,YAAK,EACrF,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,qBAAoC,EAcjD;IAEe,mBAAmB,IAAI,MAAM,CAE5C;IAEY,gBAAgB,CAC3B,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,mBAAmB,CAAC,QAAQ,CAAM,GAC1C,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAwE5C;IAEK,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,CAAC,QAAQ,CAAC;eA1EzC,MAAM;UA8JxC;YAKa,cAAc;YA+Bd,oBAAoB;IA8ClC,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,aAAa;CAQtB"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { Pool, PoolConfig } from "pg";
7
+ export type { Pool, PoolConfig };
8
+ /**
9
+ * Loads `@electric-sql/pglite` (browser WASM Postgres). Idempotent; same implementation as
10
+ * {@link Postgres.init} (mirrors {@link Sqlite.init} from `@workglow/sqlite/storage`).
11
+ */
12
+ export declare function loadPostgres(): Promise<void>;
13
+ /**
14
+ * Minimal `pg`-shaped module: `Pool` is a constructor compatible with `new Pool(config)` on Node.
15
+ * Call {@link Postgres.init} / {@link loadPostgres} first.
16
+ */
17
+ export declare function getPostgres(): typeof import("pg");
18
+ /**
19
+ * Creates a PGlite-backed pool whose {@link Pool.query} matches `pg` (string + params or {@link import("pg").QueryConfig}).
20
+ *
21
+ * `PoolConfig` mapping: default data dir is `memory://`. Use `connectionString` `memory://` or `idb://…`,
22
+ * or `dataDir` / `pglite.dataDir`, or `pglite: { … }` for full {@link import("@electric-sql/pglite").PGliteOptions}.
23
+ */
24
+ export declare function createPool(config?: PoolConfig): Promise<Pool>;
25
+ /** Same entry shape as the Node/Bun entry of this package. {@link Postgres.init} matches {@link Sqlite.init}. */
26
+ export declare const Postgres: {
27
+ readonly init: typeof loadPostgres;
28
+ readonly load: typeof loadPostgres;
29
+ readonly module: typeof import("pg");
30
+ readonly createPool: typeof createPool;
31
+ };
32
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/storage/_postgres/browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAI3C,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAKjC;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAuBlD;AAWD;;;GAGG;AACH,wBAAgB,WAAW,IAAI,cAAc,IAAI,CAAC,CAKjD;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnE;AAED,iHAAiH;AACjH,eAAO,MAAM,QAAQ;;;qBAGL,cAAc,IAAI,CAAC;;CAIzB,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { Pool, PoolConfig } from "pg";
7
+ export type { Pool, PoolConfig };
8
+ /**
9
+ * Dynamically loads the `pg` package (Node.js and Bun). Idempotent; same implementation as
10
+ * {@link Postgres.init} (mirrors {@link Sqlite.init} from `@workglow/sqlite/storage`).
11
+ *
12
+ * Call before using {@link getPostgres} or {@link createPool}.
13
+ */
14
+ export declare function loadPostgres(): Promise<void>;
15
+ /** Resolved `pg` module after {@link Postgres.init} / {@link loadPostgres}. */
16
+ export declare function getPostgres(): typeof import("pg");
17
+ /** Creates a connection pool after loading `pg`. */
18
+ export declare function createPool(config: PoolConfig): Promise<Pool>;
19
+ /** Namespaced helpers (lazy `pg` load). {@link Postgres.init} matches {@link Sqlite.init}. */
20
+ export declare const Postgres: {
21
+ readonly init: typeof loadPostgres;
22
+ readonly load: typeof loadPostgres;
23
+ readonly module: typeof import("pg");
24
+ readonly createPool: typeof createPool;
25
+ };
26
+ //# sourceMappingURL=node-bun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-bun.d.ts","sourceRoot":"","sources":["../../../src/storage/_postgres/node-bun.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAE3C,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAIjC;;;;;GAKG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAWlD;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,IAAI,cAAc,IAAI,CAAC,CAOjD;AAED,oDAAoD;AACpD,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlE;AAED,8FAA8F;AAC9F,eAAO,MAAM,QAAQ;;;qBAGL,cAAc,IAAI,CAAC;;CAIzB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { PoolConfig, QueryConfig, QueryResult, QueryResultRow, Submittable } from "pg";
7
+ /**
8
+ * Wraps a {@link PGliteInterface} so {@link import("pg").Pool#query} call sites can stay the same
9
+ * as with `pg` (string + optional params, or {@link QueryConfig} with `text` / `values`).
10
+ */
11
+ export declare class PGLitePool {
12
+ #private;
13
+ constructor(PGliteCtor: typeof import("@electric-sql/pglite").PGlite, config?: PoolConfig);
14
+ /** Resolve when WASM / FS initialization has finished. */
15
+ waitUntilReady(): Promise<void>;
16
+ query<T extends Submittable>(queryStream: T): T;
17
+ query<R extends QueryResultRow = any, I extends any[] = any[]>(queryConfig: QueryConfig<I>): Promise<QueryResult<R>>;
18
+ query<R extends QueryResultRow = any, I extends any[] = any[]>(queryTextOrConfig: string | QueryConfig<I>, values?: import("pg").QueryConfigValues<I>): Promise<QueryResult<R>>;
19
+ end(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=pglite-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pglite-pool.d.ts","sourceRoot":"","sources":["../../../src/storage/_postgres/pglite-pool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAyD5F;;;GAGG;AACH,qBAAa,UAAU;;IAGrB,YAAY,UAAU,EAAE,cAAc,sBAAsB,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,EAIxF;IAED,0DAA0D;IACpD,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAEpC;IAED,KAAK,CAAC,CAAC,SAAS,WAAW,EAAE,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;IAChD,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAC3D,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,CAAC,CAAC,SAAS,cAAc,GAAG,GAAG,EAAE,CAAC,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAC3D,iBAAiB,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,EAC1C,MAAM,CAAC,EAAE,OAAO,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IA8BrB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAEzB;CACF"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export * from "./_postgres/browser";
7
+ export * from "./PostgresKvStorage";
8
+ export * from "./PostgresTabularStorage";
9
+ export * from "./PostgresVectorStorage";
10
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/storage/browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC"}