Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/curly-taxis-try.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@electric-sql/pglite-socket': patch
---

allow extensions to be loaded via '-e/--extensions <list>' cmd line parameter'
1 change: 1 addition & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions packages/pglite-socket/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion packages/pglite-socket/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -60,6 +61,7 @@
"vitest": "^1.3.1"
},
"peerDependencies": {
"@electric-sql/pglite": "workspace:*"
"@electric-sql/pglite": "workspace:*",
"@electric-sql/pglite-postgis": "workspace:*"
}
}
70 changes: 69 additions & 1 deletion packages/pglite-socket/src/scripts/server.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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)
Expand All @@ -83,6 +91,7 @@ interface ServerConfig {
host: string
path?: string
debugLevel: DebugLevel
extensionNames?: string[]
runCommand?: string
includeDatabaseUrl: boolean
shutdownTimeout: number
Expand All @@ -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),
Expand All @@ -126,11 +139,66 @@ class PGLiteServerRunner {
}
}

private async importExtensions(): Promise<Extensions | undefined> {
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<void> {
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')
}
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.