orez 0.0.50 → 0.0.52
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/README.md +23 -13
- package/dist/cli.js +30 -19
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +87 -33
- package/dist/index.js.map +1 -1
- package/dist/pglite-manager.d.ts.map +1 -1
- package/dist/pglite-manager.js +29 -11
- package/dist/pglite-manager.js.map +1 -1
- package/dist/vite-plugin.d.ts +5 -3
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +1 -1
- package/dist/vite-plugin.js.map +1 -1
- package/package.json +4 -3
- package/src/cli.ts +43 -31
- package/src/config.ts +10 -2
- package/src/index.ts +100 -33
- package/src/pglite-manager.ts +32 -12
- package/src/replication/pgoutput-encoder.test.ts +18 -10
- package/src/vite-plugin.ts +8 -3
package/dist/pglite-manager.js
CHANGED
|
@@ -4,22 +4,40 @@ import { PGlite } from '@electric-sql/pglite';
|
|
|
4
4
|
import { pg_trgm } from '@electric-sql/pglite/contrib/pg_trgm';
|
|
5
5
|
import { vector } from '@electric-sql/pglite/vector';
|
|
6
6
|
import { log } from './log.js';
|
|
7
|
+
// detect pglite corruption errors (wasm abort during init)
|
|
8
|
+
function isCorruptionError(err) {
|
|
9
|
+
const msg = String(err);
|
|
10
|
+
return msg.includes('Aborted()') || msg.includes('_pg_initdb');
|
|
11
|
+
}
|
|
7
12
|
// create a single pglite instance with given dataDir suffix
|
|
8
|
-
async function createInstance(config, name, withExtensions) {
|
|
13
|
+
async function createInstance(config, name, withExtensions, isRetry = false) {
|
|
9
14
|
const dataPath = resolve(config.dataDir, `pgdata-${name}`);
|
|
10
15
|
mkdirSync(dataPath, { recursive: true });
|
|
11
16
|
log.debug.pglite(`creating ${name} instance at ${dataPath}`);
|
|
12
17
|
const { dataDir: _d, debug: _dbg, ...userOpts } = config.pgliteOptions;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
try {
|
|
19
|
+
const db = new PGlite({
|
|
20
|
+
dataDir: dataPath,
|
|
21
|
+
debug: config.logLevel === 'debug' ? 1 : 0,
|
|
22
|
+
relaxedDurability: true,
|
|
23
|
+
...userOpts,
|
|
24
|
+
extensions: withExtensions ? userOpts.extensions || { vector, pg_trgm } : {},
|
|
25
|
+
});
|
|
26
|
+
await db.waitReady;
|
|
27
|
+
log.debug.pglite(`${name} ready`);
|
|
28
|
+
return db;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
if (isCorruptionError(err) && !isRetry) {
|
|
32
|
+
// corrupted data directory - backup and retry with fresh init
|
|
33
|
+
const backupPath = `${dataPath}.corrupt.${Date.now()}`;
|
|
34
|
+
log.pglite(`corrupted data detected in ${name}, backing up to ${backupPath}`);
|
|
35
|
+
renameSync(dataPath, backupPath);
|
|
36
|
+
log.pglite(`retrying ${name} with fresh database`);
|
|
37
|
+
return createInstance(config, name, withExtensions, true);
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
23
41
|
}
|
|
24
42
|
/**
|
|
25
43
|
* create separate pglite instances for each "database".
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pglite-manager.js","sourceRoot":"","sources":["../src/pglite-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACtF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAA;AAEpD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAU9B,4DAA4D;AAC5D,KAAK,UAAU,cAAc,CAC3B,MAAsB,EACtB,IAAY,EACZ,cAAuB;
|
|
1
|
+
{"version":3,"file":"pglite-manager.js","sourceRoot":"","sources":["../src/pglite-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/vite-plugin.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import type { ZeroLiteConfig } from './config.js';
|
|
1
|
+
import type { Hook, ZeroLiteConfig } from './config.js';
|
|
2
2
|
import type { Plugin } from 'vite';
|
|
3
|
-
export interface OrezPluginOptions extends Partial<ZeroLiteConfig
|
|
3
|
+
export interface OrezPluginOptions extends Partial<Omit<ZeroLiteConfig, 'onDbReady' | 'onHealthy'>> {
|
|
4
4
|
s3?: boolean;
|
|
5
5
|
s3Port?: number;
|
|
6
|
+
onDbReady?: Hook;
|
|
7
|
+
onHealthy?: Hook;
|
|
6
8
|
}
|
|
7
|
-
export
|
|
9
|
+
export declare function orezPlugin(options?: OrezPluginOptions): Plugin;
|
|
8
10
|
//# sourceMappingURL=vite-plugin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAElC,MAAM,WAAW,iBAAkB,SAAQ,OAAO,CAChD,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,WAAW,CAAC,CAChD;IACC,EAAE,CAAC,EAAE,OAAO,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,SAAS,CAAC,EAAE,IAAI,CAAA;CACjB;AAED,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CA4B9D"}
|
package/dist/vite-plugin.js
CHANGED
package/dist/vite-plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAgB1C,MAAM,UAAU,UAAU,CAAC,OAA2B;IACpD,IAAI,IAAI,GAAiC,IAAI,CAAA;IAC7C,IAAI,QAAQ,GAAkB,IAAI,CAAA;IAElC,OAAO;QACL,IAAI,EAAE,MAAM;QAEZ,KAAK,CAAC,eAAe,CAAC,MAAM;YAC1B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAA;YAC3C,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;YAElB,IAAI,OAAO,EAAE,EAAE,EAAE,CAAC;gBAChB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;gBACtD,QAAQ,GAAG,MAAM,YAAY,CAAC;oBAC5B,IAAI,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;oBAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO;iBAC/B,CAAC,CAAA;YACJ,CAAC;YAED,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;gBACxC,QAAQ,EAAE,KAAK,EAAE,CAAA;gBACjB,IAAI,IAAI,EAAE,CAAC;oBACT,MAAM,IAAI,EAAE,CAAA;oBACZ,IAAI,GAAG,IAAI,CAAA;gBACb,CAAC;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orez",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.52",
|
|
4
4
|
"description": "PGlite-powered zero-sync development backend. No Docker required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "tsc",
|
|
32
32
|
"dev": "tsc --watch",
|
|
33
|
-
"test": "vitest run --exclude src/integration/",
|
|
33
|
+
"test": "vitest run --exclude 'src/integration/' --exclude 'src/wasm-sqlite.test.ts'",
|
|
34
34
|
"test:integration": "vitest run src/integration/",
|
|
35
|
+
"test:wasm": "vitest run src/wasm-sqlite.test.ts",
|
|
35
36
|
"test:all": "vitest run",
|
|
36
37
|
"test:watch": "vitest",
|
|
37
38
|
"lint": "oxlint --import-plugin --ignore-pattern sqlite-wasm/",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"@electric-sql/pglite": "^0.2.17",
|
|
49
50
|
"@electric-sql/pglite-tools": "0.2.4",
|
|
50
51
|
"@rocicorp/zero": ">=0.1.0",
|
|
51
|
-
"bedrock-sqlite": "0.0.
|
|
52
|
+
"bedrock-sqlite": "0.0.45",
|
|
52
53
|
"citty": "^0.2.0",
|
|
53
54
|
"pg-gateway": "0.3.0-beta.4",
|
|
54
55
|
"pgsql-parser": "^17.9.11",
|
package/src/cli.ts
CHANGED
|
@@ -757,6 +757,16 @@ const main = defineCommand({
|
|
|
757
757
|
description: 'command to run once all services are healthy',
|
|
758
758
|
default: '',
|
|
759
759
|
},
|
|
760
|
+
admin: {
|
|
761
|
+
type: 'boolean',
|
|
762
|
+
description: 'start admin dashboard',
|
|
763
|
+
default: false,
|
|
764
|
+
},
|
|
765
|
+
'admin-port': {
|
|
766
|
+
type: 'string',
|
|
767
|
+
description: 'admin dashboard port',
|
|
768
|
+
default: '6477',
|
|
769
|
+
},
|
|
760
770
|
},
|
|
761
771
|
subCommands: {
|
|
762
772
|
s3: s3Command,
|
|
@@ -764,19 +774,23 @@ const main = defineCommand({
|
|
|
764
774
|
pg_restore: pgRestoreCommand,
|
|
765
775
|
},
|
|
766
776
|
async run({ args }) {
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
777
|
+
const adminPort = args.admin ? Number(args['admin-port']) : 0
|
|
778
|
+
const { config, stop, zeroEnv, logStore, restartZero, resetZero } =
|
|
779
|
+
await startZeroLite({
|
|
780
|
+
pgPort: Number(args['pg-port']),
|
|
781
|
+
zeroPort: Number(args['zero-port']),
|
|
782
|
+
adminPort,
|
|
783
|
+
dataDir: args['data-dir'],
|
|
784
|
+
migrationsDir: args.migrations,
|
|
785
|
+
seedFile: args.seed,
|
|
786
|
+
pgUser: args['pg-user'],
|
|
787
|
+
pgPassword: args['pg-password'],
|
|
788
|
+
skipZeroCache: args['skip-zero-cache'],
|
|
789
|
+
disableWasmSqlite: args['disable-wasm-sqlite'],
|
|
790
|
+
logLevel: (args['log-level'] as 'error' | 'warn' | 'info' | 'debug') || undefined,
|
|
791
|
+
onDbReady: args['on-db-ready'] || undefined,
|
|
792
|
+
onHealthy: args['on-healthy'] || undefined,
|
|
793
|
+
})
|
|
780
794
|
|
|
781
795
|
let s3Server: import('node:http').Server | null = null
|
|
782
796
|
if (args.s3) {
|
|
@@ -787,6 +801,20 @@ const main = defineCommand({
|
|
|
787
801
|
})
|
|
788
802
|
}
|
|
789
803
|
|
|
804
|
+
let adminServer: import('node:http').Server | null = null
|
|
805
|
+
if (args.admin && logStore && zeroEnv) {
|
|
806
|
+
const { startAdminServer } = await import('./admin/server.js')
|
|
807
|
+
adminServer = await startAdminServer({
|
|
808
|
+
port: config.adminPort,
|
|
809
|
+
logStore,
|
|
810
|
+
config,
|
|
811
|
+
zeroEnv,
|
|
812
|
+
actions: { restartZero, resetZero },
|
|
813
|
+
startTime: Date.now(),
|
|
814
|
+
})
|
|
815
|
+
log.orez(`admin: http://localhost:${config.adminPort}`)
|
|
816
|
+
}
|
|
817
|
+
|
|
790
818
|
log.orez('ready')
|
|
791
819
|
log.orez(
|
|
792
820
|
`pg: postgresql://${config.pgUser}:${config.pgPassword}@127.0.0.1:${config.pgPort}/postgres`
|
|
@@ -795,30 +823,14 @@ const main = defineCommand({
|
|
|
795
823
|
log.zero(`http://localhost:${config.zeroPort}`)
|
|
796
824
|
}
|
|
797
825
|
|
|
798
|
-
if (args['on-healthy']) {
|
|
799
|
-
log.orez(`running on-healthy: ${args['on-healthy']}`)
|
|
800
|
-
const child = spawn(args['on-healthy'], {
|
|
801
|
-
shell: true,
|
|
802
|
-
stdio: 'inherit',
|
|
803
|
-
env: {
|
|
804
|
-
...process.env,
|
|
805
|
-
OREZ_PG_PORT: String(config.pgPort),
|
|
806
|
-
OREZ_ZERO_PORT: String(config.zeroPort),
|
|
807
|
-
},
|
|
808
|
-
})
|
|
809
|
-
child.on('exit', (code) => {
|
|
810
|
-
if (code !== 0 && code !== null) {
|
|
811
|
-
log.orez(`on-healthy command exited with code ${code}`)
|
|
812
|
-
}
|
|
813
|
-
})
|
|
814
|
-
}
|
|
815
|
-
|
|
816
826
|
process.on('SIGINT', async () => {
|
|
827
|
+
adminServer?.close()
|
|
817
828
|
s3Server?.close()
|
|
818
829
|
await stop()
|
|
819
830
|
process.exit(0)
|
|
820
831
|
})
|
|
821
832
|
process.on('SIGTERM', async () => {
|
|
833
|
+
adminServer?.close()
|
|
822
834
|
s3Server?.close()
|
|
823
835
|
await stop()
|
|
824
836
|
process.exit(0)
|
package/src/config.ts
CHANGED
|
@@ -2,10 +2,14 @@ import type { PGliteOptions } from '@electric-sql/pglite'
|
|
|
2
2
|
|
|
3
3
|
export type LogLevel = 'error' | 'warn' | 'info' | 'debug'
|
|
4
4
|
|
|
5
|
+
// lifecycle hooks - can be shell command string (CLI) or callback (programmatic)
|
|
6
|
+
export type Hook = string | (() => void | Promise<void>)
|
|
7
|
+
|
|
5
8
|
export interface ZeroLiteConfig {
|
|
6
9
|
dataDir: string
|
|
7
10
|
pgPort: number
|
|
8
11
|
zeroPort: number
|
|
12
|
+
adminPort: number
|
|
9
13
|
pgUser: string
|
|
10
14
|
pgPassword: string
|
|
11
15
|
migrationsDir: string
|
|
@@ -14,7 +18,9 @@ export interface ZeroLiteConfig {
|
|
|
14
18
|
disableWasmSqlite: boolean
|
|
15
19
|
logLevel: LogLevel
|
|
16
20
|
pgliteOptions: Partial<PGliteOptions>
|
|
17
|
-
|
|
21
|
+
// lifecycle hooks
|
|
22
|
+
onDbReady?: Hook // after db+proxy ready, before zero-cache
|
|
23
|
+
onHealthy?: Hook // after all services ready
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
export function getConfig(overrides: Partial<ZeroLiteConfig> = {}): ZeroLiteConfig {
|
|
@@ -22,6 +28,7 @@ export function getConfig(overrides: Partial<ZeroLiteConfig> = {}): ZeroLiteConf
|
|
|
22
28
|
dataDir: overrides.dataDir || '.orez',
|
|
23
29
|
pgPort: overrides.pgPort || 6434,
|
|
24
30
|
zeroPort: overrides.zeroPort || 5849,
|
|
31
|
+
adminPort: overrides.adminPort || 0,
|
|
25
32
|
pgUser: overrides.pgUser || 'user',
|
|
26
33
|
pgPassword: overrides.pgPassword || 'password',
|
|
27
34
|
migrationsDir: overrides.migrationsDir || '',
|
|
@@ -30,7 +37,8 @@ export function getConfig(overrides: Partial<ZeroLiteConfig> = {}): ZeroLiteConf
|
|
|
30
37
|
disableWasmSqlite: overrides.disableWasmSqlite ?? false,
|
|
31
38
|
logLevel: overrides.logLevel || 'warn',
|
|
32
39
|
pgliteOptions: overrides.pgliteOptions || {},
|
|
33
|
-
onDbReady: overrides.onDbReady
|
|
40
|
+
onDbReady: overrides.onDbReady,
|
|
41
|
+
onHealthy: overrides.onHealthy,
|
|
34
42
|
}
|
|
35
43
|
}
|
|
36
44
|
|
package/src/index.ts
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
import { spawn, type ChildProcess } from 'node:child_process'
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
11
11
|
import { createRequire } from 'node:module'
|
|
12
|
-
import {
|
|
12
|
+
import { resolve } from 'node:path'
|
|
13
13
|
|
|
14
|
+
import { createLogStore, type LogStore } from './admin/log-store.js'
|
|
14
15
|
import { getConfig, getConnectionString } from './config.js'
|
|
15
16
|
import { log, port, setLogLevel } from './log.js'
|
|
16
17
|
import { startPgProxy } from './pg-proxy.js'
|
|
@@ -22,7 +23,42 @@ import type { ZeroLiteConfig } from './config.js'
|
|
|
22
23
|
import type { PGlite } from '@electric-sql/pglite'
|
|
23
24
|
|
|
24
25
|
export { getConfig, getConnectionString } from './config.js'
|
|
25
|
-
export type { LogLevel, ZeroLiteConfig } from './config.js'
|
|
26
|
+
export type { Hook, LogLevel, ZeroLiteConfig } from './config.js'
|
|
27
|
+
|
|
28
|
+
// helper to run a hook (string command or callback function)
|
|
29
|
+
async function runHook(
|
|
30
|
+
hook: string | (() => void | Promise<void>) | undefined,
|
|
31
|
+
name: string,
|
|
32
|
+
env: Record<string, string>
|
|
33
|
+
): Promise<void> {
|
|
34
|
+
if (!hook) return
|
|
35
|
+
|
|
36
|
+
if (typeof hook === 'function') {
|
|
37
|
+
log.debug.orez(`running ${name} callback`)
|
|
38
|
+
await hook()
|
|
39
|
+
log.orez(`${name} done`)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// string command
|
|
44
|
+
log.debug.orez(`running ${name}: ${hook}`)
|
|
45
|
+
await new Promise<void>((resolve, reject) => {
|
|
46
|
+
const child = spawn(hook, {
|
|
47
|
+
shell: true,
|
|
48
|
+
stdio: 'inherit',
|
|
49
|
+
env: { ...process.env, ...env },
|
|
50
|
+
})
|
|
51
|
+
child.on('exit', (code) => {
|
|
52
|
+
if (code === 0) {
|
|
53
|
+
log.orez(`${name} done`)
|
|
54
|
+
resolve()
|
|
55
|
+
} else {
|
|
56
|
+
reject(new Error(`${name} exited with code ${code}`))
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
child.on('error', reject)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
26
62
|
|
|
27
63
|
// resolve a package entry — import.meta.resolve doesn't work in vitest
|
|
28
64
|
function resolvePackage(pkg: string): string {
|
|
@@ -46,12 +82,20 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
46
82
|
const zeroPort = config.skipZeroCache
|
|
47
83
|
? config.zeroPort
|
|
48
84
|
: await findPort(config.zeroPort)
|
|
85
|
+
const adminPort = config.adminPort > 0 ? await findPort(config.adminPort) : 0
|
|
49
86
|
if (pgPort !== config.pgPort)
|
|
50
87
|
log.debug.orez(`port ${config.pgPort} in use, using ${pgPort}`)
|
|
51
88
|
if (!config.skipZeroCache && zeroPort !== config.zeroPort)
|
|
52
89
|
log.debug.orez(`port ${config.zeroPort} in use, using ${zeroPort}`)
|
|
90
|
+
if (adminPort > 0 && adminPort !== config.adminPort)
|
|
91
|
+
log.debug.orez(`port ${config.adminPort} in use, using ${adminPort}`)
|
|
53
92
|
config.pgPort = pgPort
|
|
54
93
|
config.zeroPort = zeroPort
|
|
94
|
+
config.adminPort = adminPort
|
|
95
|
+
|
|
96
|
+
// create log store for admin dashboard
|
|
97
|
+
const logStore: LogStore | undefined =
|
|
98
|
+
adminPort > 0 ? createLogStore(config.dataDir) : undefined
|
|
55
99
|
|
|
56
100
|
log.debug.orez(`data dir: ${resolve(config.dataDir)}`)
|
|
57
101
|
|
|
@@ -80,34 +124,17 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
80
124
|
// seed data if needed
|
|
81
125
|
await seedIfNeeded(db, config)
|
|
82
126
|
|
|
83
|
-
// run on-db-ready
|
|
127
|
+
// run on-db-ready hook (e.g. migrations) before zero-cache starts
|
|
84
128
|
if (config.onDbReady) {
|
|
85
|
-
log.debug.orez(`running on-db-ready: ${config.onDbReady}`)
|
|
86
129
|
const upstreamUrl = getConnectionString(config, 'postgres')
|
|
87
130
|
const cvrUrl = getConnectionString(config, 'zero_cvr')
|
|
88
131
|
const cdbUrl = getConnectionString(config, 'zero_cdb')
|
|
89
|
-
await
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
ZERO_UPSTREAM_DB: upstreamUrl,
|
|
96
|
-
ZERO_CVR_DB: cvrUrl,
|
|
97
|
-
ZERO_CHANGE_DB: cdbUrl,
|
|
98
|
-
DATABASE_URL: upstreamUrl,
|
|
99
|
-
OREZ_PG_PORT: String(config.pgPort),
|
|
100
|
-
},
|
|
101
|
-
})
|
|
102
|
-
child.on('exit', (code) => {
|
|
103
|
-
if (code === 0) {
|
|
104
|
-
log.orez('on-db-ready done')
|
|
105
|
-
resolve()
|
|
106
|
-
} else {
|
|
107
|
-
reject(new Error(`on-db-ready exited with code ${code}`))
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
child.on('error', reject)
|
|
132
|
+
await runHook(config.onDbReady, 'on-db-ready', {
|
|
133
|
+
ZERO_UPSTREAM_DB: upstreamUrl,
|
|
134
|
+
ZERO_CVR_DB: cvrUrl,
|
|
135
|
+
ZERO_CHANGE_DB: cdbUrl,
|
|
136
|
+
DATABASE_URL: upstreamUrl,
|
|
137
|
+
OREZ_PG_PORT: String(config.pgPort),
|
|
111
138
|
})
|
|
112
139
|
|
|
113
140
|
// re-install change tracking on tables created by on-db-ready
|
|
@@ -120,19 +147,28 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
120
147
|
|
|
121
148
|
// start zero-cache
|
|
122
149
|
let zeroCacheProcess: ChildProcess | null = null
|
|
150
|
+
let zeroEnv: Record<string, string> = {}
|
|
123
151
|
if (!config.skipZeroCache) {
|
|
124
|
-
|
|
152
|
+
const result = await startZeroCache(config, logStore)
|
|
153
|
+
zeroCacheProcess = result.process
|
|
154
|
+
zeroEnv = result.env
|
|
125
155
|
await waitForZeroCache(config)
|
|
126
156
|
log.zero(`ready ${port(config.zeroPort, 'magenta')}`)
|
|
127
157
|
} else {
|
|
128
158
|
log.orez('skip zero-cache')
|
|
129
159
|
}
|
|
130
160
|
|
|
131
|
-
|
|
132
|
-
|
|
161
|
+
// run on-healthy hook after all services are ready
|
|
162
|
+
if (config.onHealthy) {
|
|
163
|
+
await runHook(config.onHealthy, 'on-healthy', {
|
|
164
|
+
OREZ_PG_PORT: String(config.pgPort),
|
|
165
|
+
OREZ_ZERO_PORT: String(config.zeroPort),
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const killZeroCache = async () => {
|
|
133
170
|
if (zeroCacheProcess && !zeroCacheProcess.killed) {
|
|
134
171
|
zeroCacheProcess.kill('SIGTERM')
|
|
135
|
-
// wait up to 3s for graceful exit, then force kill
|
|
136
172
|
await new Promise<void>((r) => {
|
|
137
173
|
const timeout = setTimeout(() => {
|
|
138
174
|
if (zeroCacheProcess && !zeroCacheProcess.killed) {
|
|
@@ -146,6 +182,20 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
146
182
|
})
|
|
147
183
|
})
|
|
148
184
|
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const restartZeroCache = async (cleanup = false) => {
|
|
188
|
+
await killZeroCache()
|
|
189
|
+
if (cleanup) cleanupStaleReplica(config)
|
|
190
|
+
const result = await startZeroCache(config, logStore)
|
|
191
|
+
zeroCacheProcess = result.process
|
|
192
|
+
zeroEnv = result.env
|
|
193
|
+
await waitForZeroCache(config)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const stop = async () => {
|
|
197
|
+
log.debug.orez('shutting down')
|
|
198
|
+
await killZeroCache()
|
|
149
199
|
pgServer.close()
|
|
150
200
|
await Promise.all([
|
|
151
201
|
instances.postgres.close(),
|
|
@@ -155,7 +205,18 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
155
205
|
log.debug.orez('stopped')
|
|
156
206
|
}
|
|
157
207
|
|
|
158
|
-
return {
|
|
208
|
+
return {
|
|
209
|
+
config,
|
|
210
|
+
stop,
|
|
211
|
+
db,
|
|
212
|
+
instances,
|
|
213
|
+
pgPort: config.pgPort,
|
|
214
|
+
zeroPort: config.zeroPort,
|
|
215
|
+
logStore,
|
|
216
|
+
zeroEnv,
|
|
217
|
+
restartZero: config.skipZeroCache ? undefined : () => restartZeroCache(false),
|
|
218
|
+
resetZero: config.skipZeroCache ? undefined : () => restartZeroCache(true),
|
|
219
|
+
}
|
|
159
220
|
}
|
|
160
221
|
|
|
161
222
|
function cleanupStaleReplica(config: ZeroLiteConfig): void {
|
|
@@ -272,7 +333,10 @@ module.exports.SqliteError = SqliteError;
|
|
|
272
333
|
return resolve(tmp, 'orez-sqlite', 'node_modules')
|
|
273
334
|
}
|
|
274
335
|
|
|
275
|
-
async function startZeroCache(
|
|
336
|
+
async function startZeroCache(
|
|
337
|
+
config: ZeroLiteConfig,
|
|
338
|
+
logStore?: LogStore
|
|
339
|
+
): Promise<{ process: ChildProcess; env: Record<string, string> }> {
|
|
276
340
|
// resolve @rocicorp/zero entry for finding zero-cache modules
|
|
277
341
|
const zeroEntry = resolvePackage('@rocicorp/zero')
|
|
278
342
|
|
|
@@ -342,6 +406,7 @@ async function startZeroCache(config: ZeroLiteConfig): Promise<ChildProcess> {
|
|
|
342
406
|
const lines = data.toString().trim().split('\n')
|
|
343
407
|
for (const line of lines) {
|
|
344
408
|
log.debug.zero(line)
|
|
409
|
+
logStore?.push('zero', 'info', line)
|
|
345
410
|
}
|
|
346
411
|
})
|
|
347
412
|
|
|
@@ -349,16 +414,18 @@ async function startZeroCache(config: ZeroLiteConfig): Promise<ChildProcess> {
|
|
|
349
414
|
const lines = data.toString().trim().split('\n')
|
|
350
415
|
for (const line of lines) {
|
|
351
416
|
log.debug.zero(line)
|
|
417
|
+
logStore?.push('zero', 'error', line)
|
|
352
418
|
}
|
|
353
419
|
})
|
|
354
420
|
|
|
355
421
|
child.on('exit', (code) => {
|
|
356
422
|
if (code !== 0 && code !== null) {
|
|
357
423
|
log.zero(`exited with code ${code}`)
|
|
424
|
+
logStore?.push('zero', 'error', `exited with code ${code}`)
|
|
358
425
|
}
|
|
359
426
|
})
|
|
360
427
|
|
|
361
|
-
return child
|
|
428
|
+
return { process: child, env }
|
|
362
429
|
}
|
|
363
430
|
|
|
364
431
|
async function waitForZeroCache(
|
package/src/pglite-manager.ts
CHANGED
|
@@ -15,11 +15,18 @@ export interface PGliteInstances {
|
|
|
15
15
|
cdb: PGlite
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// detect pglite corruption errors (wasm abort during init)
|
|
19
|
+
function isCorruptionError(err: unknown): boolean {
|
|
20
|
+
const msg = String(err)
|
|
21
|
+
return msg.includes('Aborted()') || msg.includes('_pg_initdb')
|
|
22
|
+
}
|
|
23
|
+
|
|
18
24
|
// create a single pglite instance with given dataDir suffix
|
|
19
25
|
async function createInstance(
|
|
20
26
|
config: ZeroLiteConfig,
|
|
21
27
|
name: string,
|
|
22
|
-
withExtensions: boolean
|
|
28
|
+
withExtensions: boolean,
|
|
29
|
+
isRetry = false
|
|
23
30
|
): Promise<PGlite> {
|
|
24
31
|
const dataPath = resolve(config.dataDir, `pgdata-${name}`)
|
|
25
32
|
mkdirSync(dataPath, { recursive: true })
|
|
@@ -30,17 +37,30 @@ async function createInstance(
|
|
|
30
37
|
debug: _dbg,
|
|
31
38
|
...userOpts
|
|
32
39
|
} = config.pgliteOptions as Record<string, any>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const db = new PGlite({
|
|
43
|
+
dataDir: dataPath,
|
|
44
|
+
debug: config.logLevel === 'debug' ? 1 : 0,
|
|
45
|
+
relaxedDurability: true,
|
|
46
|
+
...userOpts,
|
|
47
|
+
extensions: withExtensions ? userOpts.extensions || { vector, pg_trgm } : {},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
await db.waitReady
|
|
51
|
+
log.debug.pglite(`${name} ready`)
|
|
52
|
+
return db
|
|
53
|
+
} catch (err) {
|
|
54
|
+
if (isCorruptionError(err) && !isRetry) {
|
|
55
|
+
// corrupted data directory - backup and retry with fresh init
|
|
56
|
+
const backupPath = `${dataPath}.corrupt.${Date.now()}`
|
|
57
|
+
log.pglite(`corrupted data detected in ${name}, backing up to ${backupPath}`)
|
|
58
|
+
renameSync(dataPath, backupPath)
|
|
59
|
+
log.pglite(`retrying ${name} with fresh database`)
|
|
60
|
+
return createInstance(config, name, withExtensions, true)
|
|
61
|
+
}
|
|
62
|
+
throw err
|
|
63
|
+
}
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
/**
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
|
|
1
3
|
import { describe, it, expect } from 'vitest'
|
|
2
4
|
|
|
3
5
|
import {
|
|
@@ -367,11 +369,13 @@ describe('pgoutput-encoder', () => {
|
|
|
367
369
|
// roundtrip tests: encode with orez → parse with zero-cache's parser
|
|
368
370
|
// this validates the fundamental contract between orez and zero-cache
|
|
369
371
|
describe('roundtrip: orez encoder → zero-cache parser', () => {
|
|
370
|
-
//
|
|
372
|
+
// relative path bypasses package.json exports restriction
|
|
371
373
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
374
|
+
const parserPath = join(
|
|
375
|
+
import.meta.dirname,
|
|
376
|
+
'../../node_modules/@rocicorp/zero/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js'
|
|
377
|
+
)
|
|
378
|
+
const { PgoutputParser } = require(parserPath)
|
|
375
379
|
|
|
376
380
|
// mock type parsers: unknown OIDs default to String (identity for text)
|
|
377
381
|
const typeParsers = { getTypeParser: () => String }
|
|
@@ -571,13 +575,17 @@ describe('pgoutput-encoder', () => {
|
|
|
571
575
|
|
|
572
576
|
// verify lexi version ordering is preserved
|
|
573
577
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
578
|
+
const lexiPath = join(
|
|
579
|
+
import.meta.dirname,
|
|
580
|
+
'../../node_modules/@rocicorp/zero/out/zero-cache/src/types/lexi-version.js'
|
|
581
|
+
)
|
|
582
|
+
const { versionToLexi } = require(lexiPath)
|
|
577
583
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
584
|
+
const lsnPath = join(
|
|
585
|
+
import.meta.dirname,
|
|
586
|
+
'../../node_modules/@rocicorp/zero/out/zero-cache/src/services/change-source/pg/lsn.js'
|
|
587
|
+
)
|
|
588
|
+
const { toBigInt: lsnToBigInt } = require(lsnPath)
|
|
581
589
|
|
|
582
590
|
const slotHex = `00000000/${slotLsn.toString(16).padStart(8, '0')}`.toUpperCase()
|
|
583
591
|
const beginHex = `00000000/${beginLsn.toString(16).padStart(8, '0')}`.toUpperCase()
|
package/src/vite-plugin.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { startZeroLite } from './index.js'
|
|
2
2
|
|
|
3
|
-
import type { ZeroLiteConfig } from './config.js'
|
|
3
|
+
import type { Hook, ZeroLiteConfig } from './config.js'
|
|
4
4
|
import type { Server } from 'node:http'
|
|
5
5
|
import type { Plugin } from 'vite'
|
|
6
6
|
|
|
7
|
-
export interface OrezPluginOptions extends Partial<
|
|
7
|
+
export interface OrezPluginOptions extends Partial<
|
|
8
|
+
Omit<ZeroLiteConfig, 'onDbReady' | 'onHealthy'>
|
|
9
|
+
> {
|
|
8
10
|
s3?: boolean
|
|
9
11
|
s3Port?: number
|
|
12
|
+
// lifecycle hooks - callback functions (preferred for vite) or shell commands
|
|
13
|
+
onDbReady?: Hook
|
|
14
|
+
onHealthy?: Hook
|
|
10
15
|
}
|
|
11
16
|
|
|
12
|
-
export
|
|
17
|
+
export function orezPlugin(options?: OrezPluginOptions): Plugin {
|
|
13
18
|
let stop: (() => Promise<void>) | null = null
|
|
14
19
|
let s3Server: Server | null = null
|
|
15
20
|
|