postgres-memory-server 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/PostgresMemoryServer.ts","../src/errors.ts","../src/presets.ts","../src/jest.ts"],"sourcesContent":["import { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { PostgreSqlContainer } from \"@testcontainers/postgresql\";\nimport { Client, type QueryResultRow } from \"pg\";\n\nimport { ServerStoppedError, SnapshotUnsupportedError } from \"./errors.js\";\nimport { buildInitStatements, normalizeOptions } from \"./presets.js\";\nimport type {\n PostgresConnectionOptions,\n PostgresMemoryServerOptions,\n QueryParams,\n QueryResponse,\n QueryText,\n} from \"./types.js\";\n\nexport type StartedPostgreSqlContainer = Awaited<\n ReturnType<PostgreSqlContainer[\"start\"]>\n>;\n\nexport class PostgresMemoryServer {\n private stopped = false;\n private readonly snapshotSupported: boolean;\n\n private constructor(\n private readonly container: StartedPostgreSqlContainer,\n private readonly options: ReturnType<typeof normalizeOptions>,\n ) {\n this.snapshotSupported = options.database !== \"postgres\";\n }\n\n static async create(\n options: PostgresMemoryServerOptions = {},\n ): Promise<PostgresMemoryServer> {\n const normalized = normalizeOptions(options);\n\n const container = await new PostgreSqlContainer(normalized.image)\n .withDatabase(normalized.database)\n .withUsername(normalized.username)\n .withPassword(normalized.password)\n .start();\n\n const server = new PostgresMemoryServer(container, normalized);\n\n const initStatements = buildInitStatements(normalized);\n if (initStatements.length > 0) {\n await server.runSql(initStatements);\n }\n\n return server;\n }\n\n static createPostgres(\n options: Omit<PostgresMemoryServerOptions, \"preset\"> = {},\n ): Promise<PostgresMemoryServer> {\n return PostgresMemoryServer.create({ ...options, preset: \"postgres\" });\n }\n\n static createParadeDb(\n options: Omit<PostgresMemoryServerOptions, \"preset\"> = {},\n ): Promise<PostgresMemoryServer> {\n return PostgresMemoryServer.create({ ...options, preset: \"paradedb\" });\n }\n\n getUri(): string {\n this.ensureRunning();\n return this.container.getConnectionUri();\n }\n\n getHost(): string {\n this.ensureRunning();\n return this.container.getHost();\n }\n\n getPort(): number {\n this.ensureRunning();\n return this.container.getPort();\n }\n\n getDatabase(): string {\n return this.options.database;\n }\n\n getUsername(): string {\n return this.options.username;\n }\n\n getPassword(): string {\n return this.options.password;\n }\n\n getImage(): string {\n return this.options.image;\n }\n\n getConnectionOptions(): PostgresConnectionOptions {\n return {\n host: this.getHost(),\n port: this.getPort(),\n database: this.getDatabase(),\n user: this.getUsername(),\n password: this.getPassword(),\n };\n }\n\n async query<Row extends QueryResultRow = QueryResultRow>(\n text: QueryText<Row>,\n params?: QueryParams,\n ): Promise<QueryResponse<Row>> {\n return this.withClient((client) => {\n if (typeof text === \"string\") {\n if (params === undefined) {\n return client.query<Row>(text);\n }\n\n return client.query<Row>(text, params);\n }\n\n return client.query<Row>(text);\n });\n }\n\n async withClient<T>(callback: (client: Client) => Promise<T>): Promise<T> {\n this.ensureRunning();\n\n const client = new Client({\n connectionString: this.getUri(),\n });\n\n await client.connect();\n try {\n return await callback(client);\n } finally {\n await client.end();\n }\n }\n\n async runSql(sql: string | string[]): Promise<void> {\n const statements = Array.isArray(sql) ? sql : [sql];\n\n await this.withClient(async (client) => {\n for (const statement of statements) {\n await client.query(statement);\n }\n });\n }\n\n async runSqlFile(filePath: string): Promise<void> {\n const sql = await fs.readFile(filePath, \"utf8\");\n await this.runSql(sql);\n }\n\n async runMigrationsDir(dirPath: string): Promise<string[]> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n const files = entries\n .filter(\n (entry) => entry.isFile() && entry.name.toLowerCase().endsWith(\".sql\"),\n )\n .map((entry) => entry.name)\n .sort((left, right) => left.localeCompare(right));\n\n for (const file of files) {\n await this.runSqlFile(path.join(dirPath, file));\n }\n\n return files;\n }\n\n async snapshot(): Promise<void> {\n this.ensureRunning();\n this.ensureSnapshotSupported();\n await this.container.snapshot();\n }\n\n async restore(): Promise<void> {\n this.ensureRunning();\n this.ensureSnapshotSupported();\n await this.container.restoreSnapshot();\n }\n\n async stop(): Promise<void> {\n if (this.stopped) {\n return;\n }\n\n this.stopped = true;\n await this.container.stop();\n }\n\n private ensureRunning(): void {\n if (this.stopped) {\n throw new ServerStoppedError();\n }\n }\n\n private ensureSnapshotSupported(): void {\n if (!this.snapshotSupported) {\n throw new SnapshotUnsupportedError(this.options.database);\n }\n }\n}\n","export class PostgresMemoryServerError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\nexport class SnapshotUnsupportedError extends PostgresMemoryServerError {\n constructor(database: string) {\n super(\n `Snapshots are not supported when the database name is \"${database}\". ` +\n `Use a non-system database name such as \"testdb\" before calling snapshot() or restore().`,\n );\n }\n}\n\nexport class ServerStoppedError extends PostgresMemoryServerError {\n constructor() {\n super(\"The PostgresMemoryServer has already been stopped.\");\n }\n}\n","import type {\n NormalizedPostgresMemoryServerOptions,\n PostgresMemoryServerOptions,\n PostgresMemoryServerPreset,\n} from \"./types.js\";\n\nexport const POSTGRES_IMAGE_REPOSITORY = \"postgres\";\nexport const PARADEDB_IMAGE_REPOSITORY = \"paradedb/paradedb\";\nexport const DEFAULT_POSTGRES_VERSION = \"17\";\nexport const DEFAULT_PARADEDB_VERSION = \"0.22.3-pg17\";\nexport const DEFAULT_POSTGRES_IMAGE = getImageForVersion(\n \"postgres\",\n DEFAULT_POSTGRES_VERSION,\n);\nexport const DEFAULT_PARADEDB_IMAGE = getImageForVersion(\n \"paradedb\",\n DEFAULT_PARADEDB_VERSION,\n);\n\nconst DEFAULT_DATABASE = \"testdb\";\nconst DEFAULT_USERNAME = \"testuser\";\nconst DEFAULT_PASSWORD = \"testpassword\";\n\nconst PARADEDB_DEFAULT_EXTENSIONS = [\"pg_search\", \"vector\"];\n\nexport function normalizeOptions(\n options: PostgresMemoryServerOptions = {},\n): NormalizedPostgresMemoryServerOptions {\n const preset = options.preset ?? \"postgres\";\n const version = options.version;\n const image = options.image ?? getImage(preset, version);\n const database = options.database ?? DEFAULT_DATABASE;\n const username = options.username ?? DEFAULT_USERNAME;\n const password = options.password ?? DEFAULT_PASSWORD;\n const extensions = options.extensions ?? getDefaultExtensions(preset);\n const initSql = options.initSql ?? [];\n\n return {\n preset,\n version,\n image,\n database,\n username,\n password,\n extensions,\n initSql,\n };\n}\n\nexport function getImageForVersion(\n preset: PostgresMemoryServerPreset,\n version: string,\n): string {\n const repository =\n preset === \"paradedb\"\n ? PARADEDB_IMAGE_REPOSITORY\n : POSTGRES_IMAGE_REPOSITORY;\n\n return `${repository}:${version}`;\n}\n\nexport function getDefaultImage(preset: PostgresMemoryServerPreset): string {\n return preset === \"paradedb\"\n ? DEFAULT_PARADEDB_IMAGE\n : DEFAULT_POSTGRES_IMAGE;\n}\n\nfunction getImage(\n preset: PostgresMemoryServerPreset,\n version?: string,\n): string {\n return version\n ? getImageForVersion(preset, version)\n : getDefaultImage(preset);\n}\n\nexport function getDefaultExtensions(\n preset: PostgresMemoryServerPreset,\n): string[] {\n return preset === \"paradedb\" ? [...PARADEDB_DEFAULT_EXTENSIONS] : [];\n}\n\nexport function buildInitStatements(\n options: NormalizedPostgresMemoryServerOptions,\n): string[] {\n const extensionStatements = options.extensions.map(\n (extension) =>\n `CREATE EXTENSION IF NOT EXISTS ${quoteIdentifier(extension)};`,\n );\n\n return [...extensionStatements, ...options.initSql];\n}\n\nfunction quoteIdentifier(name: string): string {\n if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n return name;\n }\n\n return `\"${name.replaceAll('\"', '\"\"')}\"`;\n}\n","import { promises as fs } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport process from \"node:process\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\nimport type { PostgresMemoryServerOptions } from \"./types.js\";\n\nconst CHILD_OPTIONS_ENV_VAR = \"POSTGRES_MEMORY_SERVER_CHILD_OPTIONS_B64\";\nconst CHILD_SETUP_TIMEOUT_MS = 120_000;\nconst CHILD_SHUTDOWN_TIMEOUT_MS = 30_000;\nconst POLL_INTERVAL_MS = 100;\n\nexport const DEFAULT_JEST_ENV_VAR_NAME = \"DATABASE_URL\";\nexport const DEFAULT_JEST_STATE_FILE = path.join(\n tmpdir(),\n `postgres-memory-server-jest-${createHash(\"sha256\")\n .update(process.cwd())\n .digest(\"hex\")\n .slice(0, 12)}.json`,\n);\n\ntype ChildPayload = {\n uri: string;\n host: string;\n port: number;\n database: string;\n username: string;\n password: string;\n image: string;\n};\n\ntype JestGlobalState = {\n pid: number;\n envVarName: string;\n payload: ChildPayload;\n};\n\nexport interface JestGlobalSetupOptions extends PostgresMemoryServerOptions {\n envVarName?: string;\n stateFilePath?: string;\n}\n\nexport interface JestGlobalTeardownOptions {\n stateFilePath?: string;\n}\n\nfunction getChildScript(childModuleUrl: string): string {\n return `\nimport process from \"node:process\";\nimport { PostgresMemoryServer } from ${JSON.stringify(childModuleUrl)};\n\nconst encodedOptions = process.env.${CHILD_OPTIONS_ENV_VAR};\nif (!encodedOptions) {\n throw new Error(\"Missing child setup options\");\n}\n\nconst options = JSON.parse(Buffer.from(encodedOptions, \"base64\").toString(\"utf8\"));\nconst server = await PostgresMemoryServer.create(options);\n\nconst payload = {\n uri: server.getUri(),\n host: server.getHost(),\n port: server.getPort(),\n database: server.getDatabase(),\n username: server.getUsername(),\n password: server.getPassword(),\n image: server.getImage(),\n};\n\nprocess.stdout.write(JSON.stringify(payload) + \"\\\\n\");\n\nconst stop = async () => {\n await server.stop();\n process.exit(0);\n};\n\nprocess.on(\"SIGINT\", () => {\n void stop();\n});\n\nprocess.on(\"SIGTERM\", () => {\n void stop();\n});\n\nawait new Promise(() => {});\n`;\n}\n\nexport function createJestGlobalSetup(\n options: JestGlobalSetupOptions = {},\n): () => Promise<void> {\n return async () => {\n const {\n envVarName = DEFAULT_JEST_ENV_VAR_NAME,\n stateFilePath,\n ...serverOptions\n } = options;\n const resolvedStateFilePath = resolveStateFilePath(stateFilePath);\n\n await fs.mkdir(path.dirname(resolvedStateFilePath), { recursive: true });\n\n const existingState = await readStateFile(resolvedStateFilePath);\n if (existingState) {\n await stopChildProcess(existingState.pid);\n }\n\n const { pid, payload } = await startChildProcess(serverOptions);\n\n applyConnectionEnvironment(envVarName, payload);\n\n const state: JestGlobalState = {\n pid,\n envVarName,\n payload,\n };\n\n await fs.writeFile(\n resolvedStateFilePath,\n JSON.stringify(state, null, 2),\n \"utf8\",\n );\n };\n}\n\nexport function createJestGlobalTeardown(\n options: JestGlobalTeardownOptions = {},\n): () => Promise<void> {\n return async () => {\n const resolvedStateFilePath = resolveStateFilePath(options.stateFilePath);\n const state = await readStateFile(resolvedStateFilePath);\n\n if (!state) {\n return;\n }\n\n await stopChildProcess(state.pid);\n await fs.rm(resolvedStateFilePath, { force: true });\n };\n}\n\nfunction resolveStateFilePath(stateFilePath?: string): string {\n return stateFilePath ? path.resolve(stateFilePath) : DEFAULT_JEST_STATE_FILE;\n}\n\nasync function readStateFile(\n filePath: string,\n): Promise<JestGlobalState | null> {\n try {\n const content = await fs.readFile(filePath, \"utf8\");\n return JSON.parse(content) as JestGlobalState;\n } catch (error) {\n if (isMissingFileError(error)) {\n return null;\n }\n\n throw error;\n }\n}\n\nfunction applyConnectionEnvironment(\n envVarName: string,\n payload: ChildPayload,\n): void {\n process.env[envVarName] = payload.uri;\n process.env.POSTGRES_MEMORY_SERVER_URI = payload.uri;\n process.env.POSTGRES_MEMORY_SERVER_HOST = payload.host;\n process.env.POSTGRES_MEMORY_SERVER_PORT = String(payload.port);\n process.env.POSTGRES_MEMORY_SERVER_DATABASE = payload.database;\n process.env.POSTGRES_MEMORY_SERVER_USERNAME = payload.username;\n process.env.POSTGRES_MEMORY_SERVER_PASSWORD = payload.password;\n process.env.POSTGRES_MEMORY_SERVER_IMAGE = payload.image;\n}\n\nasync function startChildProcess(\n options: PostgresMemoryServerOptions,\n): Promise<{ pid: number; payload: ChildPayload }> {\n const childModuleUrl = await resolveChildModuleUrl();\n\n return new Promise((resolve, reject) => {\n const child = spawn(\n process.execPath,\n [\"--input-type=module\", \"--eval\", getChildScript(childModuleUrl)],\n {\n env: {\n ...process.env,\n [CHILD_OPTIONS_ENV_VAR]: Buffer.from(\n JSON.stringify(options),\n \"utf8\",\n ).toString(\"base64\"),\n },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n },\n );\n\n if (child.pid === undefined) {\n reject(new Error(\"Failed to start postgres-memory-server child process\"));\n return;\n }\n\n const childPid = child.pid;\n\n let settled = false;\n let stdout = \"\";\n let stderr = \"\";\n\n const timeout = setTimeout(() => {\n if (settled) {\n return;\n }\n\n settled = true;\n void stopChildProcess(childPid).finally(() => {\n reject(\n new Error(\n `Timed out waiting for postgres-memory-server child process to become ready. ${stderr.trim()}`.trim(),\n ),\n );\n });\n }, CHILD_SETUP_TIMEOUT_MS);\n\n child.stdout.setEncoding(\"utf8\");\n child.stderr.setEncoding(\"utf8\");\n\n child.stdout.on(\"data\", (chunk: string) => {\n if (settled) {\n return;\n }\n\n stdout += chunk;\n const newlineIndex = stdout.indexOf(\"\\n\");\n if (newlineIndex === -1) {\n return;\n }\n\n const firstLine = stdout.slice(0, newlineIndex).trim();\n if (!firstLine) {\n return;\n }\n\n try {\n const payload = JSON.parse(firstLine) as ChildPayload;\n settled = true;\n clearTimeout(timeout);\n resolve({ pid: childPid, payload });\n } catch {\n // Wait for more output until the first line becomes valid JSON.\n }\n });\n\n child.stderr.on(\"data\", (chunk: string) => {\n stderr += chunk;\n });\n\n child.on(\"error\", (error) => {\n if (settled) {\n return;\n }\n\n settled = true;\n clearTimeout(timeout);\n reject(error);\n });\n\n child.on(\"exit\", (code, signal) => {\n if (settled) {\n return;\n }\n\n settled = true;\n clearTimeout(timeout);\n reject(\n new Error(\n `postgres-memory-server child process exited before reporting readiness (code: ${code ?? \"null\"}, signal: ${signal ?? \"null\"}). ${stderr.trim()}`.trim(),\n ),\n );\n });\n });\n}\n\nasync function resolveChildModuleUrl(): Promise<string> {\n const currentFilePath = fileURLToPath(import.meta.url);\n const currentDirectoryPath = path.dirname(currentFilePath);\n const distEntryPath = path.resolve(currentDirectoryPath, \"../dist/index.js\");\n\n try {\n await fs.access(distEntryPath);\n } catch (error) {\n if (isMissingFileError(error)) {\n throw new Error(\n `Missing built package entry at ${distEntryPath}. Run npm run build before using the Jest global setup helpers from the repository source checkout.`,\n );\n }\n\n throw error;\n }\n\n return pathToFileURL(distEntryPath).href;\n}\n\nasync function stopChildProcess(pid: number): Promise<void> {\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (error) {\n if (isMissingProcessError(error)) {\n return;\n }\n\n throw error;\n }\n\n const deadline = Date.now() + CHILD_SHUTDOWN_TIMEOUT_MS;\n\n while (Date.now() < deadline) {\n await sleep(POLL_INTERVAL_MS);\n\n try {\n process.kill(pid, 0);\n } catch (error) {\n if (isMissingProcessError(error)) {\n return;\n }\n\n throw error;\n }\n }\n\n throw new Error(\n `Timed out waiting for postgres-memory-server child process ${pid} to stop`,\n );\n}\n\nasync function sleep(durationMs: number): Promise<void> {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, durationMs);\n });\n}\n\nfunction isMissingFileError(error: unknown): boolean {\n return isNodeErrorWithCode(error, \"ENOENT\");\n}\n\nfunction isMissingProcessError(error: unknown): boolean {\n return isNodeErrorWithCode(error, \"ESRCH\");\n}\n\nfunction isNodeErrorWithCode(error: unknown, code: string): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n error.code === code\n );\n}\n"],"mappings":";AAAA,SAAS,YAAY,UAAU;AAC/B,OAAO,UAAU;AAEjB,SAAS,2BAA2B;AACpC,SAAS,cAAmC;;;ACJrC,IAAM,4BAAN,cAAwC,MAAM;AAAA,EACnD,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;AAEO,IAAM,2BAAN,cAAuC,0BAA0B;AAAA,EACtE,YAAY,UAAkB;AAC5B;AAAA,MACE,0DAA0D,QAAQ;AAAA,IAEpE;AAAA,EACF;AACF;AAEO,IAAM,qBAAN,cAAiC,0BAA0B;AAAA,EAChE,cAAc;AACZ,UAAM,oDAAoD;AAAA,EAC5D;AACF;;;ACdO,IAAM,4BAA4B;AAClC,IAAM,4BAA4B;AAClC,IAAM,2BAA2B;AACjC,IAAM,2BAA2B;AACjC,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AACF;AACO,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AACF;AAEA,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAEzB,IAAM,8BAA8B,CAAC,aAAa,QAAQ;AAEnD,SAAS,iBACd,UAAuC,CAAC,GACD;AACvC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,UAAU,QAAQ;AACxB,QAAM,QAAQ,QAAQ,SAAS,SAAS,QAAQ,OAAO;AACvD,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,aAAa,QAAQ,cAAc,qBAAqB,MAAM;AACpE,QAAM,UAAU,QAAQ,WAAW,CAAC;AAEpC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,mBACd,QACA,SACQ;AACR,QAAM,aACJ,WAAW,aACP,4BACA;AAEN,SAAO,GAAG,UAAU,IAAI,OAAO;AACjC;AAEO,SAAS,gBAAgB,QAA4C;AAC1E,SAAO,WAAW,aACd,yBACA;AACN;AAEA,SAAS,SACP,QACA,SACQ;AACR,SAAO,UACH,mBAAmB,QAAQ,OAAO,IAClC,gBAAgB,MAAM;AAC5B;AAEO,SAAS,qBACd,QACU;AACV,SAAO,WAAW,aAAa,CAAC,GAAG,2BAA2B,IAAI,CAAC;AACrE;AAEO,SAAS,oBACd,SACU;AACV,QAAM,sBAAsB,QAAQ,WAAW;AAAA,IAC7C,CAAC,cACC,kCAAkC,gBAAgB,SAAS,CAAC;AAAA,EAChE;AAEA,SAAO,CAAC,GAAG,qBAAqB,GAAG,QAAQ,OAAO;AACpD;AAEA,SAAS,gBAAgB,MAAsB;AAC7C,MAAI,2BAA2B,KAAK,IAAI,GAAG;AACzC,WAAO;AAAA,EACT;AAEA,SAAO,IAAI,KAAK,WAAW,KAAK,IAAI,CAAC;AACvC;;;AF/EO,IAAM,uBAAN,MAAM,sBAAqB;AAAA,EAIxB,YACW,WACA,SACjB;AAFiB;AACA;AAEjB,SAAK,oBAAoB,QAAQ,aAAa;AAAA,EAChD;AAAA,EARQ,UAAU;AAAA,EACD;AAAA,EASjB,aAAa,OACX,UAAuC,CAAC,GACT;AAC/B,UAAM,aAAa,iBAAiB,OAAO;AAE3C,UAAM,YAAY,MAAM,IAAI,oBAAoB,WAAW,KAAK,EAC7D,aAAa,WAAW,QAAQ,EAChC,aAAa,WAAW,QAAQ,EAChC,aAAa,WAAW,QAAQ,EAChC,MAAM;AAET,UAAM,SAAS,IAAI,sBAAqB,WAAW,UAAU;AAE7D,UAAM,iBAAiB,oBAAoB,UAAU;AACrD,QAAI,eAAe,SAAS,GAAG;AAC7B,YAAM,OAAO,OAAO,cAAc;AAAA,IACpC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,eACL,UAAuD,CAAC,GACzB;AAC/B,WAAO,sBAAqB,OAAO,EAAE,GAAG,SAAS,QAAQ,WAAW,CAAC;AAAA,EACvE;AAAA,EAEA,OAAO,eACL,UAAuD,CAAC,GACzB;AAC/B,WAAO,sBAAqB,OAAO,EAAE,GAAG,SAAS,QAAQ,WAAW,CAAC;AAAA,EACvE;AAAA,EAEA,SAAiB;AACf,SAAK,cAAc;AACnB,WAAO,KAAK,UAAU,iBAAiB;AAAA,EACzC;AAAA,EAEA,UAAkB;AAChB,SAAK,cAAc;AACnB,WAAO,KAAK,UAAU,QAAQ;AAAA,EAChC;AAAA,EAEA,UAAkB;AAChB,SAAK,cAAc;AACnB,WAAO,KAAK,UAAU,QAAQ;AAAA,EAChC;AAAA,EAEA,cAAsB;AACpB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,cAAsB;AACpB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,cAAsB;AACpB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,WAAmB;AACjB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,uBAAkD;AAChD,WAAO;AAAA,MACL,MAAM,KAAK,QAAQ;AAAA,MACnB,MAAM,KAAK,QAAQ;AAAA,MACnB,UAAU,KAAK,YAAY;AAAA,MAC3B,MAAM,KAAK,YAAY;AAAA,MACvB,UAAU,KAAK,YAAY;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,MAAM,MACJ,MACA,QAC6B;AAC7B,WAAO,KAAK,WAAW,CAAC,WAAW;AACjC,UAAI,OAAO,SAAS,UAAU;AAC5B,YAAI,WAAW,QAAW;AACxB,iBAAO,OAAO,MAAW,IAAI;AAAA,QAC/B;AAEA,eAAO,OAAO,MAAW,MAAM,MAAM;AAAA,MACvC;AAEA,aAAO,OAAO,MAAW,IAAI;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,WAAc,UAAsD;AACxE,SAAK,cAAc;AAEnB,UAAM,SAAS,IAAI,OAAO;AAAA,MACxB,kBAAkB,KAAK,OAAO;AAAA,IAChC,CAAC;AAED,UAAM,OAAO,QAAQ;AACrB,QAAI;AACF,aAAO,MAAM,SAAS,MAAM;AAAA,IAC9B,UAAE;AACA,YAAM,OAAO,IAAI;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAAuC;AAClD,UAAM,aAAa,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,GAAG;AAElD,UAAM,KAAK,WAAW,OAAO,WAAW;AACtC,iBAAW,aAAa,YAAY;AAClC,cAAM,OAAO,MAAM,SAAS;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,WAAW,UAAiC;AAChD,UAAM,MAAM,MAAM,GAAG,SAAS,UAAU,MAAM;AAC9C,UAAM,KAAK,OAAO,GAAG;AAAA,EACvB;AAAA,EAEA,MAAM,iBAAiB,SAAoC;AACzD,UAAM,UAAU,MAAM,GAAG,QAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AACjE,UAAM,QAAQ,QACX;AAAA,MACC,CAAC,UAAU,MAAM,OAAO,KAAK,MAAM,KAAK,YAAY,EAAE,SAAS,MAAM;AAAA,IACvE,EACC,IAAI,CAAC,UAAU,MAAM,IAAI,EACzB,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AAElD,eAAW,QAAQ,OAAO;AACxB,YAAM,KAAK,WAAW,KAAK,KAAK,SAAS,IAAI,CAAC;AAAA,IAChD;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAA0B;AAC9B,SAAK,cAAc;AACnB,SAAK,wBAAwB;AAC7B,UAAM,KAAK,UAAU,SAAS;AAAA,EAChC;AAAA,EAEA,MAAM,UAAyB;AAC7B,SAAK,cAAc;AACnB,SAAK,wBAAwB;AAC7B,UAAM,KAAK,UAAU,gBAAgB;AAAA,EACvC;AAAA,EAEA,MAAM,OAAsB;AAC1B,QAAI,KAAK,SAAS;AAChB;AAAA,IACF;AAEA,SAAK,UAAU;AACf,UAAM,KAAK,UAAU,KAAK;AAAA,EAC5B;AAAA,EAEQ,gBAAsB;AAC5B,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,mBAAmB;AAAA,IAC/B;AAAA,EACF;AAAA,EAEQ,0BAAgC;AACtC,QAAI,CAAC,KAAK,mBAAmB;AAC3B,YAAM,IAAI,yBAAyB,KAAK,QAAQ,QAAQ;AAAA,IAC1D;AAAA,EACF;AACF;;;AGxMA,SAAS,YAAYA,WAAU;AAC/B,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,OAAOC,WAAU;AACjB,SAAS,cAAc;AACvB,OAAO,aAAa;AACpB,SAAS,eAAe,qBAAqB;AAI7C,IAAM,wBAAwB;AAC9B,IAAM,yBAAyB;AAC/B,IAAM,4BAA4B;AAClC,IAAM,mBAAmB;AAElB,IAAM,4BAA4B;AAClC,IAAM,0BAA0BA,MAAK;AAAA,EAC1C,OAAO;AAAA,EACP,+BAA+B,WAAW,QAAQ,EAC/C,OAAO,QAAQ,IAAI,CAAC,EACpB,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE,CAAC;AACjB;AA2BA,SAAS,eAAe,gBAAgC;AACtD,SAAO;AAAA;AAAA,uCAE8B,KAAK,UAAU,cAAc,CAAC;AAAA;AAAA,qCAEhC,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmC1D;AAEO,SAAS,sBACd,UAAkC,CAAC,GACd;AACrB,SAAO,YAAY;AACjB,UAAM;AAAA,MACJ,aAAa;AAAA,MACb;AAAA,MACA,GAAG;AAAA,IACL,IAAI;AACJ,UAAM,wBAAwB,qBAAqB,aAAa;AAEhE,UAAMD,IAAG,MAAMC,MAAK,QAAQ,qBAAqB,GAAG,EAAE,WAAW,KAAK,CAAC;AAEvE,UAAM,gBAAgB,MAAM,cAAc,qBAAqB;AAC/D,QAAI,eAAe;AACjB,YAAM,iBAAiB,cAAc,GAAG;AAAA,IAC1C;AAEA,UAAM,EAAE,KAAK,QAAQ,IAAI,MAAM,kBAAkB,aAAa;AAE9D,+BAA2B,YAAY,OAAO;AAE9C,UAAM,QAAyB;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,UAAMD,IAAG;AAAA,MACP;AAAA,MACA,KAAK,UAAU,OAAO,MAAM,CAAC;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,yBACd,UAAqC,CAAC,GACjB;AACrB,SAAO,YAAY;AACjB,UAAM,wBAAwB,qBAAqB,QAAQ,aAAa;AACxE,UAAM,QAAQ,MAAM,cAAc,qBAAqB;AAEvD,QAAI,CAAC,OAAO;AACV;AAAA,IACF;AAEA,UAAM,iBAAiB,MAAM,GAAG;AAChC,UAAMA,IAAG,GAAG,uBAAuB,EAAE,OAAO,KAAK,CAAC;AAAA,EACpD;AACF;AAEA,SAAS,qBAAqB,eAAgC;AAC5D,SAAO,gBAAgBC,MAAK,QAAQ,aAAa,IAAI;AACvD;AAEA,eAAe,cACb,UACiC;AACjC,MAAI;AACF,UAAM,UAAU,MAAMD,IAAG,SAAS,UAAU,MAAM;AAClD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,SAAS,OAAO;AACd,QAAI,mBAAmB,KAAK,GAAG;AAC7B,aAAO;AAAA,IACT;AAEA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,2BACP,YACA,SACM;AACN,UAAQ,IAAI,UAAU,IAAI,QAAQ;AAClC,UAAQ,IAAI,6BAA6B,QAAQ;AACjD,UAAQ,IAAI,8BAA8B,QAAQ;AAClD,UAAQ,IAAI,8BAA8B,OAAO,QAAQ,IAAI;AAC7D,UAAQ,IAAI,kCAAkC,QAAQ;AACtD,UAAQ,IAAI,kCAAkC,QAAQ;AACtD,UAAQ,IAAI,kCAAkC,QAAQ;AACtD,UAAQ,IAAI,+BAA+B,QAAQ;AACrD;AAEA,eAAe,kBACb,SACiD;AACjD,QAAM,iBAAiB,MAAM,sBAAsB;AAEnD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQ;AAAA,MACZ,QAAQ;AAAA,MACR,CAAC,uBAAuB,UAAU,eAAe,cAAc,CAAC;AAAA,MAChE;AAAA,QACE,KAAK;AAAA,UACH,GAAG,QAAQ;AAAA,UACX,CAAC,qBAAqB,GAAG,OAAO;AAAA,YAC9B,KAAK,UAAU,OAAO;AAAA,YACtB;AAAA,UACF,EAAE,SAAS,QAAQ;AAAA,QACrB;AAAA,QACA,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,MAClC;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,QAAW;AAC3B,aAAO,IAAI,MAAM,sDAAsD,CAAC;AACxE;AAAA,IACF;AAEA,UAAM,WAAW,MAAM;AAEvB,QAAI,UAAU;AACd,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,UAAM,UAAU,WAAW,MAAM;AAC/B,UAAI,SAAS;AACX;AAAA,MACF;AAEA,gBAAU;AACV,WAAK,iBAAiB,QAAQ,EAAE,QAAQ,MAAM;AAC5C;AAAA,UACE,IAAI;AAAA,YACF,+EAA+E,OAAO,KAAK,CAAC,GAAG,KAAK;AAAA,UACtG;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,GAAG,sBAAsB;AAEzB,UAAM,OAAO,YAAY,MAAM;AAC/B,UAAM,OAAO,YAAY,MAAM;AAE/B,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,UAAI,SAAS;AACX;AAAA,MACF;AAEA,gBAAU;AACV,YAAM,eAAe,OAAO,QAAQ,IAAI;AACxC,UAAI,iBAAiB,IAAI;AACvB;AAAA,MACF;AAEA,YAAM,YAAY,OAAO,MAAM,GAAG,YAAY,EAAE,KAAK;AACrD,UAAI,CAAC,WAAW;AACd;AAAA,MACF;AAEA,UAAI;AACF,cAAM,UAAU,KAAK,MAAM,SAAS;AACpC,kBAAU;AACV,qBAAa,OAAO;AACpB,gBAAQ,EAAE,KAAK,UAAU,QAAQ,CAAC;AAAA,MACpC,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAED,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,gBAAU;AAAA,IACZ,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,UAAU;AAC3B,UAAI,SAAS;AACX;AAAA,MACF;AAEA,gBAAU;AACV,mBAAa,OAAO;AACpB,aAAO,KAAK;AAAA,IACd,CAAC;AAED,UAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AACjC,UAAI,SAAS;AACX;AAAA,MACF;AAEA,gBAAU;AACV,mBAAa,OAAO;AACpB;AAAA,QACE,IAAI;AAAA,UACF,iFAAiF,QAAQ,MAAM,aAAa,UAAU,MAAM,MAAM,OAAO,KAAK,CAAC,GAAG,KAAK;AAAA,QACzJ;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,wBAAyC;AACtD,QAAM,kBAAkB,cAAc,YAAY,GAAG;AACrD,QAAM,uBAAuBC,MAAK,QAAQ,eAAe;AACzD,QAAM,gBAAgBA,MAAK,QAAQ,sBAAsB,kBAAkB;AAE3E,MAAI;AACF,UAAMD,IAAG,OAAO,aAAa;AAAA,EAC/B,SAAS,OAAO;AACd,QAAI,mBAAmB,KAAK,GAAG;AAC7B,YAAM,IAAI;AAAA,QACR,kCAAkC,aAAa;AAAA,MACjD;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AAEA,SAAO,cAAc,aAAa,EAAE;AACtC;AAEA,eAAe,iBAAiB,KAA4B;AAC1D,MAAI;AACF,YAAQ,KAAK,KAAK,SAAS;AAAA,EAC7B,SAAS,OAAO;AACd,QAAI,sBAAsB,KAAK,GAAG;AAChC;AAAA,IACF;AAEA,UAAM;AAAA,EACR;AAEA,QAAM,WAAW,KAAK,IAAI,IAAI;AAE9B,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAM,MAAM,gBAAgB;AAE5B,QAAI;AACF,cAAQ,KAAK,KAAK,CAAC;AAAA,IACrB,SAAS,OAAO;AACd,UAAI,sBAAsB,KAAK,GAAG;AAChC;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR,8DAA8D,GAAG;AAAA,EACnE;AACF;AAEA,eAAe,MAAM,YAAmC;AACtD,QAAM,IAAI,QAAc,CAAC,YAAY;AACnC,eAAW,SAAS,UAAU;AAAA,EAChC,CAAC;AACH;AAEA,SAAS,mBAAmB,OAAyB;AACnD,SAAO,oBAAoB,OAAO,QAAQ;AAC5C;AAEA,SAAS,sBAAsB,OAAyB;AACtD,SAAO,oBAAoB,OAAO,OAAO;AAC3C;AAEA,SAAS,oBAAoB,OAAgB,MAAuB;AAClE,SACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,MAAM,SAAS;AAEnB;","names":["fs","path"]}
1
+ {"version":3,"sources":["../src/PostgresMemoryServer.ts","../src/errors.ts","../src/native.ts","../src/presets.ts","../src/jest.ts"],"sourcesContent":["import { promises as fs, rmSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport process from \"node:process\";\n\nimport EmbeddedPostgres from \"embedded-postgres\";\nimport { Client, type QueryResultRow } from \"pg\";\n\nimport {\n ExtensionInstallError,\n ServerStoppedError,\n SnapshotUnsupportedError,\n} from \"./errors.js\";\nimport {\n buildInitStatements,\n normalizeOptions,\n resolveParadeDBVersion,\n DEFAULT_POSTGRES_VERSION,\n} from \"./presets.js\";\nimport {\n getFreePort,\n getNativeDir,\n installParadeDBExtension,\n installPgVectorExtension,\n sweepOrphanedDataDirs,\n} from \"./native.js\";\nimport type {\n PostgresConnectionOptions,\n PostgresMemoryServerOptions,\n QueryParams,\n QueryResponse,\n QueryText,\n} from \"./types.js\";\n\n// Track all live instances so we can clean up their dataDirs on process exit.\nconst liveInstances = new Set<PostgresMemoryServer>();\nlet exitHandlersRegistered = false;\nlet orphanSweepDone = false;\n\nfunction registerExitHandlers(): void {\n if (exitHandlersRegistered) return;\n exitHandlersRegistered = true;\n\n const cleanup = () => {\n for (const instance of liveInstances) {\n try {\n instance._cleanupSync();\n } catch {\n // best-effort\n }\n }\n };\n\n process.once(\"exit\", cleanup);\n\n // For SIGINT/SIGTERM/uncaughtException we run cleanup then re-raise so\n // we don't override existing handlers' decisions about whether to exit.\n const signalCleanup = (signal: NodeJS.Signals) => {\n cleanup();\n // Remove our handler so a re-raise of the signal terminates normally.\n process.removeListener(signal, signalCleanup);\n process.kill(process.pid, signal);\n };\n process.on(\"SIGINT\", signalCleanup);\n process.on(\"SIGTERM\", signalCleanup);\n process.on(\"SIGHUP\", signalCleanup);\n}\n\nexport class PostgresMemoryServer {\n private stopped = false;\n private readonly snapshotSupported: boolean;\n private hasSnapshot = false;\n\n private constructor(\n private readonly pg: EmbeddedPostgres,\n private readonly port: number,\n private readonly dataDir: string,\n private readonly options: ReturnType<typeof normalizeOptions>,\n ) {\n this.snapshotSupported = options.database !== \"postgres\";\n }\n\n static async create(\n options: PostgresMemoryServerOptions = {},\n ): Promise<PostgresMemoryServer> {\n // One-time sweep of orphaned data dirs from prior crashed runs.\n if (!orphanSweepDone) {\n orphanSweepDone = true;\n await sweepOrphanedDataDirs().catch(() => {\n // best-effort cleanup\n });\n }\n\n const normalized = normalizeOptions(options);\n const port = await getFreePort();\n const dataDir = await fs.mkdtemp(\n path.join(os.tmpdir(), \"postgres-memory-server-\"),\n );\n\n let pg: EmbeddedPostgres | undefined;\n\n try {\n const postgresFlags: string[] = [];\n\n // Install ParadeDB extension if needed\n if (normalized.preset === \"paradedb\") {\n const nativeDir = getNativeDir();\n const extVersion = resolveParadeDBVersion(normalized.version);\n const pgMajor = DEFAULT_POSTGRES_VERSION;\n\n try {\n await installParadeDBExtension(nativeDir, extVersion, pgMajor);\n } catch (error) {\n throw new ExtensionInstallError(\n \"pg_search\",\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n\n if (normalized.extensions.includes(\"vector\")) {\n try {\n await installPgVectorExtension(nativeDir, pgMajor);\n } catch (error) {\n throw new ExtensionInstallError(\n \"vector\",\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n\n if (\n normalized.extensions.includes(\"pg_search\") ||\n normalized.extensions.length === 0\n ) {\n postgresFlags.push(\"-c\", \"shared_preload_libraries=pg_search\");\n }\n }\n\n pg = new EmbeddedPostgres({\n databaseDir: dataDir,\n port,\n user: normalized.username,\n password: normalized.password,\n persistent: false,\n postgresFlags,\n onLog: () => {},\n onError: () => {},\n });\n\n await pg.initialise();\n await pg.start();\n\n if (normalized.database !== \"postgres\") {\n await pg.createDatabase(normalized.database);\n }\n\n const server = new PostgresMemoryServer(pg, port, dataDir, normalized);\n liveInstances.add(server);\n registerExitHandlers();\n\n const initStatements = buildInitStatements(normalized);\n if (initStatements.length > 0) {\n await server.runSql(initStatements);\n }\n\n return server;\n } catch (error) {\n // Cleanup on failure: stop any partial postgres process and rm dataDir.\n if (pg) {\n try {\n await pg.stop();\n } catch {\n // best-effort\n }\n }\n await fs\n .rm(dataDir, { recursive: true, force: true })\n .catch(() => {});\n throw error;\n }\n }\n\n static createPostgres(\n options: Omit<PostgresMemoryServerOptions, \"preset\"> = {},\n ): Promise<PostgresMemoryServer> {\n return PostgresMemoryServer.create({ ...options, preset: \"postgres\" });\n }\n\n static createParadeDb(\n options: Omit<PostgresMemoryServerOptions, \"preset\"> = {},\n ): Promise<PostgresMemoryServer> {\n return PostgresMemoryServer.create({ ...options, preset: \"paradedb\" });\n }\n\n getUri(): string {\n this.ensureRunning();\n return `postgres://${this.options.username}:${this.options.password}@localhost:${this.port}/${this.options.database}`;\n }\n\n getHost(): string {\n this.ensureRunning();\n return \"localhost\";\n }\n\n getPort(): number {\n this.ensureRunning();\n return this.port;\n }\n\n getDatabase(): string {\n return this.options.database;\n }\n\n getUsername(): string {\n return this.options.username;\n }\n\n getPassword(): string {\n return this.options.password;\n }\n\n getImage(): string {\n return this.options.image;\n }\n\n /**\n * Returns the absolute path to the temporary PostgreSQL data directory\n * for this instance. Useful for debugging or backing up state. The\n * directory is automatically removed by `stop()`.\n */\n getDataDir(): string {\n return this.dataDir;\n }\n\n getConnectionOptions(): PostgresConnectionOptions {\n return {\n host: this.getHost(),\n port: this.getPort(),\n database: this.getDatabase(),\n user: this.getUsername(),\n password: this.getPassword(),\n };\n }\n\n async query<Row extends QueryResultRow = QueryResultRow>(\n text: QueryText<Row>,\n params?: QueryParams,\n ): Promise<QueryResponse<Row>> {\n return this.withClient((client) => {\n if (typeof text === \"string\") {\n if (params === undefined) {\n return client.query<Row>(text);\n }\n\n return client.query<Row>(text, params);\n }\n\n return client.query<Row>(text);\n });\n }\n\n async withClient<T>(callback: (client: Client) => Promise<T>): Promise<T> {\n this.ensureRunning();\n\n const client = new Client({\n connectionString: this.getUri(),\n });\n\n await client.connect();\n try {\n return await callback(client);\n } finally {\n await client.end();\n }\n }\n\n async runSql(sql: string | string[]): Promise<void> {\n const statements = Array.isArray(sql) ? sql : [sql];\n\n await this.withClient(async (client) => {\n for (const statement of statements) {\n await client.query(statement);\n }\n });\n }\n\n async runSqlFile(filePath: string): Promise<void> {\n const sql = await fs.readFile(filePath, \"utf8\");\n await this.runSql(sql);\n }\n\n async runMigrationsDir(dirPath: string): Promise<string[]> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n const files = entries\n .filter(\n (entry) => entry.isFile() && entry.name.toLowerCase().endsWith(\".sql\"),\n )\n .map((entry) => entry.name)\n .sort((left, right) => left.localeCompare(right));\n\n for (const file of files) {\n await this.runSqlFile(path.join(dirPath, file));\n }\n\n return files;\n }\n\n /**\n * Create a snapshot of the current database state.\n * Uses PostgreSQL template databases for fast, native snapshots.\n */\n async snapshot(): Promise<void> {\n this.ensureRunning();\n this.ensureSnapshotSupported();\n\n const snapshotDb = `${this.options.database}_snapshot`;\n\n await this.withAdminClient(async (client) => {\n // Terminate other connections to the database\n await client.query(\n `SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid != pg_backend_pid()`,\n [this.options.database],\n );\n\n // Drop existing snapshot if any\n if (this.hasSnapshot) {\n await client.query(`DROP DATABASE IF EXISTS \"${snapshotDb}\"`);\n }\n\n // Create snapshot as a template copy\n await client.query(\n `CREATE DATABASE \"${snapshotDb}\" TEMPLATE \"${this.options.database}\"`,\n );\n });\n\n this.hasSnapshot = true;\n }\n\n /**\n * Restore the database to the last snapshot.\n * Drops and recreates the database from the snapshot template.\n */\n async restore(): Promise<void> {\n this.ensureRunning();\n this.ensureSnapshotSupported();\n\n if (!this.hasSnapshot) {\n throw new Error(\n \"No snapshot exists. Call snapshot() before calling restore().\",\n );\n }\n\n const snapshotDb = `${this.options.database}_snapshot`;\n\n await this.withAdminClient(async (client) => {\n // Terminate all connections to the target database\n await client.query(\n `SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1 AND pid != pg_backend_pid()`,\n [this.options.database],\n );\n\n // Drop and recreate from snapshot\n await client.query(`DROP DATABASE \"${this.options.database}\"`);\n await client.query(\n `CREATE DATABASE \"${this.options.database}\" TEMPLATE \"${snapshotDb}\"`,\n );\n });\n }\n\n async stop(): Promise<void> {\n if (this.stopped) {\n return;\n }\n\n this.stopped = true;\n liveInstances.delete(this);\n\n try {\n await this.pg.stop();\n } catch {\n // Even if pg.stop() fails (e.g., process never started, already dead),\n // we still want to remove the data directory below.\n }\n\n // Defensive cleanup. embedded-postgres only deletes the data dir when\n // its `process` field is set; if start() failed before that, the dir\n // would be leaked. force: true makes this a no-op if already gone.\n await fs\n .rm(this.dataDir, { recursive: true, force: true })\n .catch(() => {});\n }\n\n /**\n * Synchronous cleanup for use in process exit handlers. Cannot await,\n * so we just remove the data directory and let the OS reap the postgres\n * child process. embedded-postgres registers its own exit hook to kill\n * the process; this method is a backup for the data directory only.\n *\n * @internal\n */\n _cleanupSync(): void {\n if (this.stopped) {\n return;\n }\n this.stopped = true;\n liveInstances.delete(this);\n try {\n rmSync(this.dataDir, { recursive: true, force: true });\n } catch {\n // best-effort\n }\n }\n\n /**\n * Connect to the \"postgres\" system database for admin operations\n * (snapshot, restore, etc.).\n */\n private async withAdminClient<T>(\n callback: (client: Client) => Promise<T>,\n ): Promise<T> {\n const client = new Client({\n host: \"localhost\",\n port: this.port,\n database: \"postgres\",\n user: this.options.username,\n password: this.options.password,\n });\n\n await client.connect();\n try {\n return await callback(client);\n } finally {\n await client.end();\n }\n }\n\n private ensureRunning(): void {\n if (this.stopped) {\n throw new ServerStoppedError();\n }\n }\n\n private ensureSnapshotSupported(): void {\n if (!this.snapshotSupported) {\n throw new SnapshotUnsupportedError(this.options.database);\n }\n }\n}\n","export class PostgresMemoryServerError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\nexport class SnapshotUnsupportedError extends PostgresMemoryServerError {\n constructor(database: string) {\n super(\n `Snapshots are not supported when the database name is \"${database}\". ` +\n `Use a non-system database name such as \"testdb\" before calling snapshot() or restore().`,\n );\n }\n}\n\nexport class ServerStoppedError extends PostgresMemoryServerError {\n constructor() {\n super(\"The PostgresMemoryServer has already been stopped.\");\n }\n}\n\nexport class ExtensionInstallError extends PostgresMemoryServerError {\n constructor(extensionName: string, cause?: Error) {\n super(\n `Failed to install the \"${extensionName}\" extension. ${cause?.message ?? \"\"}`.trim(),\n cause ? { cause } : undefined,\n );\n }\n}\n","import { promises as fs, readFileSync, existsSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { execFile as execFileCb } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport net from \"node:net\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nconst execFile = promisify(execFileCb);\n\n/**\n * Get a free TCP port by binding to port 0 and reading the assigned port.\n */\nexport async function getFreePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = net.createServer();\n server.listen(0, () => {\n const { port } = server.address() as net.AddressInfo;\n server.close(() => resolve(port));\n });\n server.on(\"error\", reject);\n });\n}\n\n/**\n * Get the PG major version from the installed embedded-postgres package.\n * The npm package version mirrors the PG version (e.g., 18.3.0-beta.16 = PG 18).\n */\nexport function getPgMajorVersion(): string {\n // Resolve the main entry of embedded-postgres, then walk up to find package.json\n const req = createRequire(import.meta.url);\n const mainEntry = req.resolve(\"embedded-postgres\");\n let dir = path.dirname(mainEntry);\n\n // Walk up until we find a package.json with name \"embedded-postgres\"\n while (dir !== path.dirname(dir)) {\n const candidate = path.join(dir, \"package.json\");\n try {\n const content = readFileSync(candidate, \"utf8\");\n const pkg = JSON.parse(content) as { name?: string; version?: string };\n if (pkg.name === \"embedded-postgres\" && pkg.version) {\n const major = pkg.version.split(\".\")[0];\n if (major) return major;\n }\n } catch {\n // continue walking up\n }\n dir = path.dirname(dir);\n }\n\n throw new Error(\n \"Could not determine embedded-postgres version. Ensure embedded-postgres is installed.\",\n );\n}\n\n/**\n * Get the native directory of the installed embedded-postgres platform package.\n * This directory contains bin/, lib/, and share/ subdirectories.\n */\nexport function getNativeDir(): string {\n const platform = os.platform();\n const arch = os.arch();\n\n const platformPkgNames: Record<string, Record<string, string>> = {\n darwin: {\n arm64: \"@embedded-postgres/darwin-arm64\",\n x64: \"@embedded-postgres/darwin-x64\",\n },\n linux: {\n x64: \"@embedded-postgres/linux-x64\",\n arm64: \"@embedded-postgres/linux-arm64\",\n },\n win32: {\n x64: \"@embedded-postgres/windows-x64\",\n },\n };\n\n const pkgName = platformPkgNames[platform]?.[arch];\n if (!pkgName) {\n throw new Error(`Unsupported platform: ${platform}-${arch}`);\n }\n\n const req = createRequire(import.meta.url);\n // Resolve the package's main entry, then find the native/ dir relative to it\n const mainEntry = req.resolve(pkgName);\n let dir = path.dirname(mainEntry);\n\n // Walk up to find the package root (containing native/)\n while (dir !== path.dirname(dir)) {\n const nativeDir = path.join(dir, \"native\");\n if (existsSync(nativeDir)) {\n return nativeDir;\n }\n dir = path.dirname(dir);\n }\n\n throw new Error(\n `Could not find native directory for ${pkgName}. Ensure embedded-postgres is installed correctly.`,\n );\n}\n\n/**\n * Get the cache directory for downloaded extension binaries.\n */\nexport function getCacheDir(): string {\n const xdgCache = process.env.XDG_CACHE_HOME;\n const base = xdgCache || path.join(os.homedir(), \".cache\");\n return path.join(base, \"postgres-memory-server\");\n}\n\n/**\n * Install the ParadeDB pg_search extension into the embedded-postgres native directory.\n * Downloads from GitHub releases if not already cached.\n */\nexport async function installParadeDBExtension(\n nativeDir: string,\n paradedbVersion: string,\n pgMajorVersion: string,\n): Promise<void> {\n // Check if already installed\n const libDir = path.join(nativeDir, \"lib\", \"postgresql\");\n const extDir = path.join(nativeDir, \"share\", \"postgresql\", \"extension\");\n\n const soName =\n os.platform() === \"darwin\" && parseInt(pgMajorVersion, 10) >= 16\n ? \"pg_search.dylib\"\n : \"pg_search.so\";\n\n try {\n await fs.access(path.join(libDir, soName));\n await fs.access(path.join(extDir, \"pg_search.control\"));\n return; // Already installed\n } catch {\n // Not installed, proceed\n }\n\n const cacheDir = getCacheDir();\n const platform = os.platform();\n const arch = os.arch();\n const cacheKey = `paradedb-${paradedbVersion}-pg${pgMajorVersion}-${platform}-${arch}`;\n const cachedDir = path.join(cacheDir, cacheKey);\n\n // Check cache\n let cached = false;\n try {\n await fs.access(path.join(cachedDir, \"lib\", soName));\n cached = true;\n } catch {\n // Not cached\n }\n\n if (!cached) {\n const url = buildDownloadUrl(\n paradedbVersion,\n pgMajorVersion,\n platform,\n arch,\n );\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"paradedb-\"));\n\n try {\n const filename = decodeURIComponent(url.split(\"/\").pop()!);\n const archivePath = path.join(tmpDir, filename);\n\n await downloadFile(url, archivePath);\n\n const extractedDir = path.join(tmpDir, \"extracted\");\n await fs.mkdir(extractedDir, { recursive: true });\n\n if (platform === \"darwin\") {\n await extractPkg(archivePath, extractedDir);\n } else {\n await extractDeb(archivePath, extractedDir);\n }\n\n // Cache the extracted extension files\n const cacheLibDir = path.join(cachedDir, \"lib\");\n const cacheExtDir = path.join(cachedDir, \"extension\");\n await fs.mkdir(cacheLibDir, { recursive: true });\n await fs.mkdir(cacheExtDir, { recursive: true });\n\n const soFiles = await findFiles(\n extractedDir,\n /pg_search\\.(so|dylib)$/,\n );\n for (const soFile of soFiles) {\n await copyFileWithPermissions(soFile, path.join(cacheLibDir, path.basename(soFile)));\n }\n\n const extFiles = await findFiles(\n extractedDir,\n /pg_search[^/]*(\\.control|\\.sql)$/,\n );\n for (const extFile of extFiles) {\n await copyFileWithPermissions(\n extFile,\n path.join(cacheExtDir, path.basename(extFile)),\n );\n }\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n }\n\n // Copy from cache to native dir\n await fs.mkdir(libDir, { recursive: true });\n await fs.mkdir(extDir, { recursive: true });\n\n const cacheLibDir = path.join(cachedDir, \"lib\");\n const cacheExtDir = path.join(cachedDir, \"extension\");\n\n for (const file of await fs.readdir(cacheLibDir)) {\n await copyFileWithPermissions(path.join(cacheLibDir, file), path.join(libDir, file));\n }\n\n for (const file of await fs.readdir(cacheExtDir)) {\n await copyFileWithPermissions(path.join(cacheExtDir, file), path.join(extDir, file));\n }\n}\n\nfunction buildDownloadUrl(\n version: string,\n pgMajorVersion: string,\n platform: string,\n arch: string,\n): string {\n const base = `https://github.com/paradedb/paradedb/releases/download/v${version}`;\n\n if (platform === \"darwin\") {\n if (arch !== \"arm64\") {\n throw new Error(\n \"ParadeDB only provides macOS binaries for arm64 (Apple Silicon). Intel Macs are not supported.\",\n );\n }\n const macosName = getMacOSCodename();\n return `${base}/pg_search%40${pgMajorVersion}--${version}.arm64_${macosName}.pkg`;\n }\n\n if (platform === \"linux\") {\n const debArch = arch === \"arm64\" ? \"arm64\" : \"amd64\";\n return `${base}/postgresql-${pgMajorVersion}-pg-search_${version}-1PARADEDB-bookworm_${debArch}.deb`;\n }\n\n throw new Error(\n `ParadeDB does not provide prebuilt binaries for ${platform}. Use the Docker-based preset instead.`,\n );\n}\n\nfunction getMacOSCodename(): string {\n const release = os.release();\n const majorVersion = parseInt(release.split(\".\")[0] ?? \"0\", 10);\n\n if (majorVersion >= 24) return \"sequoia\";\n if (majorVersion >= 23) return \"sonoma\";\n throw new Error(\n `ParadeDB requires macOS 14 (Sonoma) or later. Detected Darwin ${release}.`,\n );\n}\n\nasync function downloadFile(url: string, destPath: string): Promise<void> {\n const response = await fetch(url, { redirect: \"follow\" });\n\n if (!response.ok) {\n throw new Error(\n `Failed to download ParadeDB extension from ${url}: ${response.status} ${response.statusText}`,\n );\n }\n\n const buffer = Buffer.from(await response.arrayBuffer());\n await fs.writeFile(destPath, buffer);\n}\n\nasync function extractDeb(\n debPath: string,\n extractDir: string,\n): Promise<void> {\n await execFile(\"ar\", [\"x\", debPath], { cwd: extractDir });\n\n const files = await fs.readdir(extractDir);\n const dataTar = files.find((f) => f.startsWith(\"data.tar\"));\n\n if (!dataTar) {\n throw new Error(\n \"No data.tar.* found in .deb archive. The ParadeDB package format may have changed.\",\n );\n }\n\n const dataDir = path.join(extractDir, \"data\");\n await fs.mkdir(dataDir, { recursive: true });\n await execFile(\"tar\", [\n \"xf\",\n path.join(extractDir, dataTar),\n \"-C\",\n dataDir,\n ]);\n}\n\nasync function extractPkg(\n pkgPath: string,\n extractDir: string,\n): Promise<void> {\n const pkgDir = path.join(extractDir, \"pkg\");\n await execFile(\"pkgutil\", [\"--expand-full\", pkgPath, pkgDir]);\n}\n\n/**\n * Copy a file by reading and writing its contents, avoiding EACCES errors\n * when the source file is read-only (e.g., extracted from tar archives).\n */\nasync function copyFileWithPermissions(\n src: string,\n dest: string,\n): Promise<void> {\n const content = await fs.readFile(src);\n await fs.writeFile(dest, content, { mode: 0o755 });\n}\n\nasync function findFiles(dir: string, pattern: RegExp): Promise<string[]> {\n const results: string[] = [];\n\n async function walk(currentDir: string): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (pattern.test(entry.name)) {\n results.push(fullPath);\n }\n }\n }\n\n await walk(dir);\n return results;\n}\n\n/**\n * Install pgvector extension into the embedded-postgres native directory.\n * Downloads from Homebrew bottles (GHCR) which cover macOS + Linux.\n */\nexport async function installPgVectorExtension(\n nativeDir: string,\n pgMajorVersion: string,\n): Promise<void> {\n const libDir = path.join(nativeDir, \"lib\", \"postgresql\");\n const extDir = path.join(nativeDir, \"share\", \"postgresql\", \"extension\");\n\n const soName = os.platform() === \"darwin\" ? \"vector.dylib\" : \"vector.so\";\n\n // Check if already installed\n try {\n await fs.access(path.join(libDir, soName));\n await fs.access(path.join(extDir, \"vector.control\"));\n return;\n } catch {\n // Not installed\n }\n\n const platform = os.platform();\n const arch = os.arch();\n const cacheDir = getCacheDir();\n const cacheKey = `pgvector-pg${pgMajorVersion}-${platform}-${arch}`;\n const cachedDir = path.join(cacheDir, cacheKey);\n\n // Check cache\n let cached = false;\n try {\n await fs.access(path.join(cachedDir, \"lib\", soName));\n cached = true;\n } catch {\n // Not cached\n }\n\n if (!cached) {\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), \"pgvector-\"));\n\n try {\n // Fetch Homebrew formula metadata to get bottle URLs\n const formulaRes = await fetch(\n \"https://formulae.brew.sh/api/formula/pgvector.json\",\n );\n if (!formulaRes.ok) {\n throw new Error(\n `Failed to fetch pgvector formula: ${formulaRes.status}`,\n );\n }\n const formula = (await formulaRes.json()) as HomebrewFormula;\n\n const bottleTag = getHomebrewBottleTag(platform, arch);\n const fileInfo = formula.bottle.stable.files[bottleTag];\n if (!fileInfo) {\n throw new Error(\n `No pgvector Homebrew bottle for ${bottleTag}. ` +\n `Available: ${Object.keys(formula.bottle.stable.files).join(\", \")}`,\n );\n }\n\n // Get anonymous GHCR auth token\n const tokenRes = await fetch(\n \"https://ghcr.io/token?scope=repository:homebrew/core/pgvector:pull\",\n );\n if (!tokenRes.ok) {\n throw new Error(`Failed to get GHCR token: ${tokenRes.status}`);\n }\n const { token } = (await tokenRes.json()) as { token: string };\n\n // Download the bottle blob\n const blobUrl = `https://ghcr.io/v2/homebrew/core/pgvector/blobs/sha256:${fileInfo.sha256}`;\n const blobRes = await fetch(blobUrl, {\n headers: { Authorization: `Bearer ${token}` },\n redirect: \"follow\",\n });\n if (!blobRes.ok) {\n throw new Error(\n `Failed to download pgvector bottle: ${blobRes.status}`,\n );\n }\n\n const bottlePath = path.join(tmpDir, \"pgvector.tar.gz\");\n const buffer = Buffer.from(await blobRes.arrayBuffer());\n await fs.writeFile(bottlePath, buffer);\n\n // Extract the tarball\n const extractDir = path.join(tmpDir, \"extracted\");\n await fs.mkdir(extractDir, { recursive: true });\n await execFile(\"tar\", [\"xzf\", bottlePath, \"-C\", extractDir]);\n\n // Cache extracted extension files\n const cacheLibDir = path.join(cachedDir, \"lib\");\n const cacheExtDir = path.join(cachedDir, \"extension\");\n await fs.mkdir(cacheLibDir, { recursive: true });\n await fs.mkdir(cacheExtDir, { recursive: true });\n\n // Find vector.so/.dylib for the matching PG version\n const pgSubdir = `postgresql@${pgMajorVersion}`;\n let soFiles = await findFiles(\n extractDir,\n new RegExp(`${pgSubdir}.*vector\\\\.(so|dylib)$`),\n );\n if (soFiles.length === 0) {\n soFiles = await findFiles(extractDir, /vector\\.(so|dylib)$/);\n }\n for (const f of soFiles) {\n await copyFileWithPermissions(f, path.join(cacheLibDir, path.basename(f)));\n }\n\n // Find extension control and SQL files for matching PG version\n let extFiles = await findFiles(\n extractDir,\n new RegExp(`${pgSubdir}.*vector[^/]*(\\\\.control|\\\\.sql)$`),\n );\n if (extFiles.length === 0) {\n extFiles = await findFiles(extractDir, /vector[^/]*(\\.control|\\.sql)$/);\n }\n for (const f of extFiles) {\n await copyFileWithPermissions(f, path.join(cacheExtDir, path.basename(f)));\n }\n } finally {\n await fs.rm(tmpDir, { recursive: true, force: true });\n }\n }\n\n // Copy from cache to native dir\n await fs.mkdir(libDir, { recursive: true });\n await fs.mkdir(extDir, { recursive: true });\n\n const cacheLibDir = path.join(cachedDir, \"lib\");\n const cacheExtDir = path.join(cachedDir, \"extension\");\n\n for (const file of await fs.readdir(cacheLibDir)) {\n await copyFileWithPermissions(path.join(cacheLibDir, file), path.join(libDir, file));\n }\n\n for (const file of await fs.readdir(cacheExtDir)) {\n await copyFileWithPermissions(path.join(cacheExtDir, file), path.join(extDir, file));\n }\n}\n\nfunction getHomebrewBottleTag(platform: string, arch: string): string {\n if (platform === \"darwin\") {\n const release = os.release();\n const major = parseInt(release.split(\".\")[0] ?? \"0\", 10);\n const prefix = arch === \"arm64\" ? \"arm64_\" : \"\";\n if (major >= 25) return `${prefix}tahoe`;\n if (major >= 24) return `${prefix}sequoia`;\n if (major >= 23) return `${prefix}sonoma`;\n return `${prefix}ventura`;\n }\n if (platform === \"linux\") {\n return arch === \"arm64\" ? \"aarch64_linux\" : \"x86_64_linux\";\n }\n throw new Error(`No Homebrew bottles available for ${platform}-${arch}`);\n}\n\ninterface HomebrewFormula {\n bottle: {\n stable: {\n files: Record<string, { cellar: string; sha256: string }>;\n };\n };\n}\n\n/**\n * Minimum age (ms) a directory must have before the orphan sweep will\n * consider deleting it. This avoids racing with concurrent test processes\n * that have just created their data dir but have not yet started postgres\n * (so no postmaster.pid file exists yet).\n */\nconst ORPHAN_MIN_AGE_MS = 60_000;\n\n/**\n * Sweep $TMPDIR for orphaned `postgres-memory-server-*` data directories\n * left behind by previous processes that crashed or were hard-killed.\n *\n * A directory is considered orphaned if:\n * - it is at least `minAgeMs` old, AND\n * - it has no `postmaster.pid` file (init never finished), OR\n * - the PID inside `postmaster.pid` no longer maps to a live process.\n *\n * The age check protects concurrent test processes that are mid-init.\n * Live directories whose postmaster.pid points to a running process are\n * always left alone, regardless of age.\n */\nexport async function sweepOrphanedDataDirs(\n minAgeMs: number = ORPHAN_MIN_AGE_MS,\n): Promise<void> {\n const tmpDir = os.tmpdir();\n let entries: string[];\n try {\n entries = await fs.readdir(tmpDir);\n } catch {\n return;\n }\n\n const cutoff = Date.now() - minAgeMs;\n\n await Promise.all(\n entries\n .filter((name) => name.startsWith(\"postgres-memory-server-\"))\n .map(async (name) => {\n const fullPath = path.join(tmpDir, name);\n let stat: import(\"node:fs\").Stats;\n try {\n stat = await fs.stat(fullPath);\n if (!stat.isDirectory()) return;\n } catch {\n return;\n }\n\n const pidFile = path.join(fullPath, \"postmaster.pid\");\n let pid: number | null = null;\n let pidFileExists = false;\n try {\n const content = await fs.readFile(pidFile, \"utf8\");\n pidFileExists = true;\n const firstLine = content.split(\"\\n\")[0]?.trim();\n const parsed = firstLine ? parseInt(firstLine, 10) : NaN;\n if (!Number.isNaN(parsed) && parsed > 0) {\n pid = parsed;\n }\n } catch {\n // No pid file\n }\n\n if (pid !== null) {\n try {\n // Signal 0 just checks whether the process exists.\n process.kill(pid, 0);\n // Process is alive — leave it alone, regardless of age.\n return;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"EPERM\") {\n // Process exists but belongs to another user — leave it alone.\n return;\n }\n // ESRCH or anything else means the process is dead.\n }\n }\n\n // No live PID. Apply the age check before deleting to avoid\n // racing with a concurrent test that just mkdtemp'd this dir\n // and has not yet finished initdb.\n if (!pidFileExists && stat.mtimeMs > cutoff) {\n return;\n }\n\n await fs.rm(fullPath, { recursive: true, force: true }).catch(() => {});\n }),\n );\n}\n\n/**\n * Parse a ParadeDB version string like \"0.22.5\" or \"0.22.5-pg17\".\n * Returns the extension version and optional PG version suffix.\n */\nexport function parseParadeDBVersion(version: string): {\n extVersion: string;\n pgVersion?: string;\n} {\n const match = version.match(/^(\\d+\\.\\d+\\.\\d+)(?:-pg(\\d+))?$/);\n if (!match || !match[1]) {\n return { extVersion: version };\n }\n return {\n extVersion: match[1],\n pgVersion: match[2],\n };\n}\n","import { getPgMajorVersion, parseParadeDBVersion } from \"./native.js\";\nimport type {\n NormalizedPostgresMemoryServerOptions,\n PostgresMemoryServerOptions,\n PostgresMemoryServerPreset,\n} from \"./types.js\";\n\nexport const DEFAULT_PARADEDB_EXT_VERSION = \"0.22.5\";\nexport const DEFAULT_POSTGRES_VERSION = getPgMajorVersion();\nexport const DEFAULT_PARADEDB_VERSION = `${DEFAULT_PARADEDB_EXT_VERSION}-pg${DEFAULT_POSTGRES_VERSION}`;\n\n/** Descriptive label for the postgres preset. */\nexport const DEFAULT_POSTGRES_IMAGE = `postgres:${DEFAULT_POSTGRES_VERSION}`;\n\n/** Descriptive label for the paradedb preset. */\nexport const DEFAULT_PARADEDB_IMAGE = `paradedb:${DEFAULT_PARADEDB_VERSION}`;\n\n// Keep old constants for backward compatibility\nexport const POSTGRES_IMAGE_REPOSITORY = \"postgres\";\nexport const PARADEDB_IMAGE_REPOSITORY = \"paradedb\";\n\nconst DEFAULT_DATABASE = \"testdb\";\nconst DEFAULT_USERNAME = \"testuser\";\nconst DEFAULT_PASSWORD = \"testpassword\";\n\nconst PARADEDB_DEFAULT_EXTENSIONS = [\"pg_search\", \"vector\"];\n\nexport function normalizeOptions(\n options: PostgresMemoryServerOptions = {},\n): NormalizedPostgresMemoryServerOptions {\n const preset = options.preset ?? \"postgres\";\n const version = options.version;\n const image = getImageLabel(preset, version);\n const database = options.database ?? DEFAULT_DATABASE;\n const username = options.username ?? DEFAULT_USERNAME;\n const password = options.password ?? DEFAULT_PASSWORD;\n const extensions = options.extensions ?? getDefaultExtensions(preset);\n const initSql = options.initSql ?? [];\n\n return {\n preset,\n version,\n image,\n database,\n username,\n password,\n extensions,\n initSql,\n };\n}\n\nexport function getImageForVersion(\n preset: PostgresMemoryServerPreset,\n version: string,\n): string {\n const repository =\n preset === \"paradedb\" ? PARADEDB_IMAGE_REPOSITORY : POSTGRES_IMAGE_REPOSITORY;\n\n return `${repository}:${version}`;\n}\n\nexport function getDefaultImage(preset: PostgresMemoryServerPreset): string {\n return preset === \"paradedb\" ? DEFAULT_PARADEDB_IMAGE : DEFAULT_POSTGRES_IMAGE;\n}\n\nfunction getImageLabel(\n preset: PostgresMemoryServerPreset,\n version?: string,\n): string {\n if (version) {\n return getImageForVersion(preset, version);\n }\n return getDefaultImage(preset);\n}\n\nexport function getDefaultExtensions(\n preset: PostgresMemoryServerPreset,\n): string[] {\n return preset === \"paradedb\" ? [...PARADEDB_DEFAULT_EXTENSIONS] : [];\n}\n\nexport function buildInitStatements(\n options: NormalizedPostgresMemoryServerOptions,\n): string[] {\n const extensionStatements = options.extensions.map(\n (extension) =>\n `CREATE EXTENSION IF NOT EXISTS ${quoteIdentifier(extension)};`,\n );\n\n return [...extensionStatements, ...options.initSql];\n}\n\n/**\n * Resolve the ParadeDB extension version from the user-provided version string.\n * Validates that any PG version suffix matches the installed embedded-postgres.\n */\nexport function resolveParadeDBVersion(version?: string): string {\n if (!version) {\n return DEFAULT_PARADEDB_EXT_VERSION;\n }\n\n const parsed = parseParadeDBVersion(version);\n\n if (parsed.pgVersion) {\n const installedPg = DEFAULT_POSTGRES_VERSION;\n if (parsed.pgVersion !== installedPg) {\n throw new Error(\n `ParadeDB version \"${version}\" targets PostgreSQL ${parsed.pgVersion}, ` +\n `but embedded-postgres provides PostgreSQL ${installedPg}. ` +\n `Install embedded-postgres@${parsed.pgVersion}.x to match, ` +\n `or use version \"${parsed.extVersion}\" without the -pg suffix.`,\n );\n }\n }\n\n return parsed.extVersion;\n}\n\nfunction quoteIdentifier(name: string): string {\n if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n return name;\n }\n\n return `\"${name.replaceAll('\"', '\"\"')}\"`;\n}\n","import { promises as fs } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport process from \"node:process\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\nimport type { PostgresMemoryServerOptions } from \"./types.js\";\n\nconst CHILD_OPTIONS_ENV_VAR = \"POSTGRES_MEMORY_SERVER_CHILD_OPTIONS_B64\";\nconst CHILD_SETUP_TIMEOUT_MS = 120_000;\nconst CHILD_SHUTDOWN_TIMEOUT_MS = 30_000;\nconst POLL_INTERVAL_MS = 100;\n\nexport const DEFAULT_JEST_ENV_VAR_NAME = \"DATABASE_URL\";\nexport const DEFAULT_JEST_STATE_FILE = path.join(\n tmpdir(),\n `postgres-memory-server-jest-${createHash(\"sha256\")\n .update(process.cwd())\n .digest(\"hex\")\n .slice(0, 12)}.json`,\n);\n\ntype ChildPayload = {\n uri: string;\n host: string;\n port: number;\n database: string;\n username: string;\n password: string;\n image: string;\n};\n\ntype JestGlobalState = {\n pid: number;\n envVarName: string;\n payload: ChildPayload;\n};\n\nexport interface JestGlobalSetupOptions extends PostgresMemoryServerOptions {\n envVarName?: string;\n stateFilePath?: string;\n}\n\nexport interface JestGlobalTeardownOptions {\n stateFilePath?: string;\n}\n\nfunction getChildScript(childModuleUrl: string): string {\n return `\nimport process from \"node:process\";\nimport { PostgresMemoryServer } from ${JSON.stringify(childModuleUrl)};\n\nconst encodedOptions = process.env.${CHILD_OPTIONS_ENV_VAR};\nif (!encodedOptions) {\n throw new Error(\"Missing child setup options\");\n}\n\nconst options = JSON.parse(Buffer.from(encodedOptions, \"base64\").toString(\"utf8\"));\nconst server = await PostgresMemoryServer.create(options);\n\nconst payload = {\n uri: server.getUri(),\n host: server.getHost(),\n port: server.getPort(),\n database: server.getDatabase(),\n username: server.getUsername(),\n password: server.getPassword(),\n image: server.getImage(),\n};\n\nprocess.stdout.write(JSON.stringify(payload) + \"\\\\n\");\n\nconst stop = async () => {\n await server.stop();\n process.exit(0);\n};\n\nprocess.on(\"SIGINT\", () => {\n void stop();\n});\n\nprocess.on(\"SIGTERM\", () => {\n void stop();\n});\n\nawait new Promise(() => {});\n`;\n}\n\nexport function createJestGlobalSetup(\n options: JestGlobalSetupOptions = {},\n): () => Promise<void> {\n return async () => {\n const {\n envVarName = DEFAULT_JEST_ENV_VAR_NAME,\n stateFilePath,\n ...serverOptions\n } = options;\n const resolvedStateFilePath = resolveStateFilePath(stateFilePath);\n\n await fs.mkdir(path.dirname(resolvedStateFilePath), { recursive: true });\n\n const existingState = await readStateFile(resolvedStateFilePath);\n if (existingState) {\n await stopChildProcess(existingState.pid);\n }\n\n const { pid, payload } = await startChildProcess(serverOptions);\n\n applyConnectionEnvironment(envVarName, payload);\n\n const state: JestGlobalState = {\n pid,\n envVarName,\n payload,\n };\n\n await fs.writeFile(\n resolvedStateFilePath,\n JSON.stringify(state, null, 2),\n \"utf8\",\n );\n };\n}\n\nexport function createJestGlobalTeardown(\n options: JestGlobalTeardownOptions = {},\n): () => Promise<void> {\n return async () => {\n const resolvedStateFilePath = resolveStateFilePath(options.stateFilePath);\n const state = await readStateFile(resolvedStateFilePath);\n\n if (!state) {\n return;\n }\n\n await stopChildProcess(state.pid);\n await fs.rm(resolvedStateFilePath, { force: true });\n };\n}\n\nfunction resolveStateFilePath(stateFilePath?: string): string {\n return stateFilePath ? path.resolve(stateFilePath) : DEFAULT_JEST_STATE_FILE;\n}\n\nasync function readStateFile(\n filePath: string,\n): Promise<JestGlobalState | null> {\n try {\n const content = await fs.readFile(filePath, \"utf8\");\n return JSON.parse(content) as JestGlobalState;\n } catch (error) {\n if (isMissingFileError(error)) {\n return null;\n }\n\n throw error;\n }\n}\n\nfunction applyConnectionEnvironment(\n envVarName: string,\n payload: ChildPayload,\n): void {\n process.env[envVarName] = payload.uri;\n process.env.POSTGRES_MEMORY_SERVER_URI = payload.uri;\n process.env.POSTGRES_MEMORY_SERVER_HOST = payload.host;\n process.env.POSTGRES_MEMORY_SERVER_PORT = String(payload.port);\n process.env.POSTGRES_MEMORY_SERVER_DATABASE = payload.database;\n process.env.POSTGRES_MEMORY_SERVER_USERNAME = payload.username;\n process.env.POSTGRES_MEMORY_SERVER_PASSWORD = payload.password;\n process.env.POSTGRES_MEMORY_SERVER_IMAGE = payload.image;\n}\n\nasync function startChildProcess(\n options: PostgresMemoryServerOptions,\n): Promise<{ pid: number; payload: ChildPayload }> {\n const childModuleUrl = await resolveChildModuleUrl();\n\n return new Promise((resolve, reject) => {\n const child = spawn(\n process.execPath,\n [\"--input-type=module\", \"--eval\", getChildScript(childModuleUrl)],\n {\n env: {\n ...process.env,\n [CHILD_OPTIONS_ENV_VAR]: Buffer.from(\n JSON.stringify(options),\n \"utf8\",\n ).toString(\"base64\"),\n },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n },\n );\n\n if (child.pid === undefined) {\n reject(new Error(\"Failed to start postgres-memory-server child process\"));\n return;\n }\n\n const childPid = child.pid;\n\n let settled = false;\n let stdout = \"\";\n let stderr = \"\";\n\n const timeout = setTimeout(() => {\n if (settled) {\n return;\n }\n\n settled = true;\n void stopChildProcess(childPid).finally(() => {\n reject(\n new Error(\n `Timed out waiting for postgres-memory-server child process to become ready. ${stderr.trim()}`.trim(),\n ),\n );\n });\n }, CHILD_SETUP_TIMEOUT_MS);\n\n child.stdout.setEncoding(\"utf8\");\n child.stderr.setEncoding(\"utf8\");\n\n child.stdout.on(\"data\", (chunk: string) => {\n if (settled) {\n return;\n }\n\n stdout += chunk;\n const newlineIndex = stdout.indexOf(\"\\n\");\n if (newlineIndex === -1) {\n return;\n }\n\n const firstLine = stdout.slice(0, newlineIndex).trim();\n if (!firstLine) {\n return;\n }\n\n try {\n const payload = JSON.parse(firstLine) as ChildPayload;\n settled = true;\n clearTimeout(timeout);\n resolve({ pid: childPid, payload });\n } catch {\n // Wait for more output until the first line becomes valid JSON.\n }\n });\n\n child.stderr.on(\"data\", (chunk: string) => {\n stderr += chunk;\n });\n\n child.on(\"error\", (error) => {\n if (settled) {\n return;\n }\n\n settled = true;\n clearTimeout(timeout);\n reject(error);\n });\n\n child.on(\"exit\", (code, signal) => {\n if (settled) {\n return;\n }\n\n settled = true;\n clearTimeout(timeout);\n reject(\n new Error(\n `postgres-memory-server child process exited before reporting readiness (code: ${code ?? \"null\"}, signal: ${signal ?? \"null\"}). ${stderr.trim()}`.trim(),\n ),\n );\n });\n });\n}\n\nasync function resolveChildModuleUrl(): Promise<string> {\n const currentFilePath = fileURLToPath(import.meta.url);\n const currentDirectoryPath = path.dirname(currentFilePath);\n const distEntryPath = path.resolve(currentDirectoryPath, \"../dist/index.js\");\n\n try {\n await fs.access(distEntryPath);\n } catch (error) {\n if (isMissingFileError(error)) {\n throw new Error(\n `Missing built package entry at ${distEntryPath}. Run npm run build before using the Jest global setup helpers from the repository source checkout.`,\n );\n }\n\n throw error;\n }\n\n return pathToFileURL(distEntryPath).href;\n}\n\nasync function stopChildProcess(pid: number): Promise<void> {\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (error) {\n if (isMissingProcessError(error)) {\n return;\n }\n\n throw error;\n }\n\n const deadline = Date.now() + CHILD_SHUTDOWN_TIMEOUT_MS;\n\n while (Date.now() < deadline) {\n await sleep(POLL_INTERVAL_MS);\n\n try {\n process.kill(pid, 0);\n } catch (error) {\n if (isMissingProcessError(error)) {\n return;\n }\n\n throw error;\n }\n }\n\n throw new Error(\n `Timed out waiting for postgres-memory-server child process ${pid} to stop`,\n );\n}\n\nasync function sleep(durationMs: number): Promise<void> {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, durationMs);\n });\n}\n\nfunction isMissingFileError(error: unknown): boolean {\n return isNodeErrorWithCode(error, \"ENOENT\");\n}\n\nfunction isMissingProcessError(error: unknown): boolean {\n return isNodeErrorWithCode(error, \"ESRCH\");\n}\n\nfunction isNodeErrorWithCode(error: unknown, code: string): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n error.code === code\n );\n}\n"],"mappings":"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","names":["fs","os","path","process","cacheLibDir","cacheExtDir","process","fs","path","os","fs","path","process"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgres-memory-server",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "spin up an actual/real postgres server programmatically from within nodejs, for testing or mocking during development",
5
5
  "homepage": "https://github.com/amanthegreatone/postgres-memory-server#readme",
6
6
  "bugs": {
@@ -16,13 +16,14 @@
16
16
  "keywords": [
17
17
  "postgres",
18
18
  "postgresql",
19
- "testcontainers",
19
+ "embedded-postgres",
20
20
  "testing",
21
21
  "integration-testing",
22
22
  "paradedb",
23
23
  "pgvector",
24
24
  "database",
25
- "memory-server"
25
+ "memory-server",
26
+ "no-docker"
26
27
  ],
27
28
  "engines": {
28
29
  "node": ">=20"
@@ -52,7 +53,7 @@
52
53
  "test:watch": "vitest"
53
54
  },
54
55
  "dependencies": {
55
- "@testcontainers/postgresql": "^11.13.0",
56
+ "embedded-postgres": "^18.3.0-beta.16",
56
57
  "pg": "^8.20.0"
57
58
  },
58
59
  "devDependencies": {