diff --git a/.changeset/curly-taxis-try.md b/.changeset/curly-taxis-try.md new file mode 100644 index 000000000..a6a89b542 --- /dev/null +++ b/.changeset/curly-taxis-try.md @@ -0,0 +1,5 @@ +--- +'@electric-sql/pglite-socket': patch +--- + +allow extensions to be loaded via '-e/--extensions ' cmd line parameter' diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 3a18acca4..a8fbaeae1 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -95,6 +95,7 @@ jobs: working-directory: ./packages/pglite needs: [build-all] steps: + - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 diff --git a/packages/pglite-socket/README.md b/packages/pglite-socket/README.md index f298db1f2..908285086 100644 --- a/packages/pglite-socket/README.md +++ b/packages/pglite-socket/README.md @@ -160,6 +160,7 @@ pglite-server --help - `-h, --host=HOST` - Host to bind to (default: 127.0.0.1) - `-u, --path=UNIX` - Unix socket to bind to (takes precedence over host:port) - `-v, --debug=LEVEL` - Debug level 0-5 (default: 0) +- `-e, --extensions=LIST` - Comma-separated list of extensions to load (e.g., vector,pgcrypto,postgis) - `-r, --run=COMMAND` - Command to run after server starts - `--include-database-url` - Include DATABASE_URL in subprocess environment - `--shutdown-timeout=MS` - Timeout for graceful subprocess shutdown in ms (default: 5000) diff --git a/packages/pglite-socket/package.json b/packages/pglite-socket/package.json index c5c6cf21e..eb9da0b88 100644 --- a/packages/pglite-socket/package.json +++ b/packages/pglite-socket/package.json @@ -52,6 +52,7 @@ "@arethetypeswrong/cli": "^0.18.1", "@electric-sql/pg-protocol": "workspace:*", "@electric-sql/pglite": "workspace:*", + "@electric-sql/pglite-postgis": "workspace:*", "@types/emscripten": "^1.41.1", "@types/node": "^20.16.11", "pg": "^8.14.0", @@ -60,6 +61,7 @@ "vitest": "^1.3.1" }, "peerDependencies": { - "@electric-sql/pglite": "workspace:*" + "@electric-sql/pglite": "workspace:*", + "@electric-sql/pglite-postgis": "workspace:*" } } diff --git a/packages/pglite-socket/src/scripts/server.ts b/packages/pglite-socket/src/scripts/server.ts index 552295625..be4465f86 100644 --- a/packages/pglite-socket/src/scripts/server.ts +++ b/packages/pglite-socket/src/scripts/server.ts @@ -1,6 +1,7 @@ #!/usr/bin/env node import { PGlite, DebugLevel } from '@electric-sql/pglite' +import type { Extension, Extensions } from '@electric-sql/pglite' import { PGLiteSocketServer } from '../index' import { parseArgs } from 'node:util' import { spawn, ChildProcess } from 'node:child_process' @@ -38,6 +39,12 @@ const args = parseArgs({ default: '0', help: 'Debug level (0-5)', }, + extensions: { + type: 'string', + short: 'e', + default: undefined, + help: 'Comma-separated list of extensions to load (e.g., vector,pgcrypto,postgis)', + }, run: { type: 'string', short: 'r', @@ -72,6 +79,7 @@ Options: -h, --host=HOST Host to bind to (default: 127.0.0.1) -u, --path=UNIX Unix socket to bind to (default: undefined). Takes precedence over host:port -v, --debug=LEVEL Debug level 0-5 (default: 0) + -e, --extensions=LIST Comma-separated list of extensions to load (e.g., vector,pgcrypto,postgis) -r, --run=COMMAND Command to run after server starts --include-database-url Include DATABASE_URL in subprocess environment --shutdown-timeout=MS Timeout for graceful subprocess shutdown in ms (default: 5000) @@ -83,6 +91,7 @@ interface ServerConfig { host: string path?: string debugLevel: DebugLevel + extensionNames?: string[] runCommand?: string includeDatabaseUrl: boolean shutdownTimeout: number @@ -99,12 +108,16 @@ class PGLiteServerRunner { } static parseConfig(): ServerConfig { + const extensionsArg = args.values.extensions as string | undefined return { dbPath: args.values.db as string, port: parseInt(args.values.port as string, 10), host: args.values.host as string, path: args.values.path as string, debugLevel: parseInt(args.values.debug as string, 10) as DebugLevel, + extensionNames: extensionsArg + ? extensionsArg.split(',').map((e) => e.trim()) + : undefined, runCommand: args.values.run as string, includeDatabaseUrl: args.values['include-database-url'] as boolean, shutdownTimeout: parseInt(args.values['shutdown-timeout'] as string, 10), @@ -126,11 +139,66 @@ class PGLiteServerRunner { } } + private async importExtensions(): Promise { + if (!this.config.extensionNames?.length) { + return undefined + } + + const extensions: Extensions = {} + + // Built-in extensions that are not in contrib + const builtInExtensions = [ + 'vector', + 'live', + 'pg_hashids', + 'pg_ivm', + 'pg_uuidv7', + 'pgtap', + ] + + for (const name of this.config.extensionNames) { + let ext: Extension | null = null + + try { + if (builtInExtensions.includes(name)) { + // Built-in extension (e.g., @electric-sql/pglite/vector) + const mod = await import(`@electric-sql/pglite/${name}`) + ext = mod[name] as Extension + } else { + // Try contrib first (e.g., @electric-sql/pglite/contrib/pgcrypto) + try { + const mod = await import(`@electric-sql/pglite/contrib/${name}`) + ext = mod[name] as Extension + } catch { + // Fall back to external package (e.g., @electric-sql/pglite-postgis) + const mod = await import(`@electric-sql/pglite-${name}`) + ext = mod[name] as Extension + } + } + + if (ext) { + extensions[name] = ext + console.log(`Imported extension: ${name}`) + } + } catch (error) { + console.error(`Failed to import extension '${name}':`, error) + throw new Error(`Failed to import extension '${name}'`) + } + } + + return Object.keys(extensions).length > 0 ? extensions : undefined + } + private async initializeDatabase(): Promise { console.log(`Initializing PGLite with database: ${this.config.dbPath}`) console.log(`Debug level: ${this.config.debugLevel}`) - this.db = new PGlite(this.config.dbPath, { debug: this.config.debugLevel }) + const extensions = await this.importExtensions() + + this.db = new PGlite(this.config.dbPath, { + debug: this.config.debugLevel, + extensions, + }) await this.db.waitReady console.log('PGlite database initialized') } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1693c8901..aceace19f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -340,6 +340,9 @@ importers: '@electric-sql/pglite': specifier: workspace:* version: link:../pglite + '@electric-sql/pglite-postgis': + specifier: workspace:* + version: link:../pglite-postgis '@types/emscripten': specifier: ^1.41.1 version: 1.41.1