@effectstream/db 0.100.10 → 0.100.12
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/package.json +6 -6
- package/scripts/pgtyped-update.ts +112 -81
- package/scripts/start-pglite.ts +47 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effectstream/db",
|
|
3
|
-
"version": "0.100.
|
|
3
|
+
"version": "0.100.12",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://effectstream.github.io/docs/",
|
|
6
6
|
"repository": {
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"./version": "./migrations/system-version.ts"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@effectstream/coroutine": "0.100.
|
|
25
|
-
"@effectstream/log": "0.100.
|
|
26
|
-
"@effectstream/runtime": "0.100.
|
|
27
|
-
"@effectstream/sync": "0.100.
|
|
28
|
-
"@effectstream/utils": "0.100.
|
|
24
|
+
"@effectstream/coroutine": "0.100.12",
|
|
25
|
+
"@effectstream/log": "0.100.12",
|
|
26
|
+
"@effectstream/runtime": "0.100.12",
|
|
27
|
+
"@effectstream/sync": "0.100.12",
|
|
28
|
+
"@effectstream/utils": "0.100.12",
|
|
29
29
|
"@electric-sql/pglite": "^0.3.14",
|
|
30
30
|
"@pgtyped/runtime": "2.4.2",
|
|
31
31
|
"@pgtyped/parser": "2.4.2",
|
|
@@ -1,119 +1,150 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Starts PGLite, applies system + user migrations, runs pgtyped codegen.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
wait: boolean;
|
|
17
|
-
}
|
|
10
|
+
import pg from "pg";
|
|
11
|
+
import { startPglite, type PgliteHandle } from "./start-pglite.ts";
|
|
12
|
+
import { waitForDb } from "./wait-for-db.ts";
|
|
13
|
+
import { applyMigrations } from "./apply-migrations.ts";
|
|
14
|
+
import { getConnection } from "../src/pg-connection.ts";
|
|
15
|
+
import { getMigrations } from "../migrations/system-version.ts";
|
|
18
16
|
|
|
19
17
|
function createPrefix(name: string): string {
|
|
20
|
-
const colors = {
|
|
21
|
-
"db:up": "\x1b[36m",
|
|
22
|
-
"
|
|
18
|
+
const colors: Record<string, string> = {
|
|
19
|
+
"db:up": "\x1b[36m",
|
|
20
|
+
"user-migrations": "\x1b[35m",
|
|
21
|
+
"pgtyped": "\x1b[33m",
|
|
23
22
|
};
|
|
24
23
|
const reset = "\x1b[0m";
|
|
25
|
-
const color = colors[name
|
|
24
|
+
const color = colors[name] || "\x1b[37m";
|
|
26
25
|
return `${color}[${name}]${reset} `;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
export interface UserMigration {
|
|
29
|
+
name: string;
|
|
30
|
+
sql: string;
|
|
31
|
+
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
export interface PgtypedUpdateOptions {
|
|
34
|
+
userMigrations?: UserMigration[];
|
|
35
|
+
pgtypedConfigPath?: string;
|
|
36
|
+
pgtypedBin?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function resolveUserMigrations(): Promise<UserMigration[]> {
|
|
40
|
+
const candidates = [
|
|
41
|
+
resolve(process.cwd(), "migration-order.ts"),
|
|
42
|
+
resolve(process.cwd(), "src/migration-order.ts"),
|
|
43
|
+
];
|
|
33
44
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
const migrationFile = candidates.find((p) => existsSync(p));
|
|
46
|
+
if (!migrationFile) return [];
|
|
47
|
+
|
|
48
|
+
const mod = await import(migrationFile);
|
|
49
|
+
return mod.migrationTable ?? [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function applyUserMigrations(migrations: UserMigration[]): Promise<void> {
|
|
53
|
+
const prefix = createPrefix("user-migrations");
|
|
54
|
+
|
|
55
|
+
if (migrations.length === 0) {
|
|
56
|
+
console.log(`${prefix}No user migrations to apply, skipping`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const client = new pg.Client({
|
|
61
|
+
host: "localhost",
|
|
62
|
+
port: 5432,
|
|
63
|
+
user: "postgres",
|
|
64
|
+
database: "postgres",
|
|
37
65
|
});
|
|
66
|
+
await client.connect();
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
for (const migration of migrations) {
|
|
70
|
+
console.log(`${prefix}Applying ${migration.name}`);
|
|
71
|
+
await client.query(migration.sql);
|
|
72
|
+
}
|
|
73
|
+
console.log(`${prefix}✅ Applied ${migrations.length} user migration(s)`);
|
|
74
|
+
} finally {
|
|
75
|
+
await client.end();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function applySystemMigrations(): Promise<void> {
|
|
80
|
+
const prefix = createPrefix("apply-migrations");
|
|
81
|
+
const db = getConnection();
|
|
82
|
+
const migrations = await getMigrations();
|
|
83
|
+
for (const migration of migrations) {
|
|
84
|
+
await applyMigrations(db as any, 0, migration.version, migration.sql, true);
|
|
85
|
+
}
|
|
86
|
+
console.log(`${prefix}✅ System migrations applied`);
|
|
87
|
+
await db.end();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function runPgtyped(pgtypedBin: string, pgtypedConfigPath: string): Promise<void> {
|
|
91
|
+
const prefix = createPrefix("pgtyped");
|
|
92
|
+
console.log(`${prefix}Starting...`);
|
|
93
|
+
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
const child = spawn("node", [pgtypedBin, "-c", pgtypedConfigPath], {
|
|
96
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
97
|
+
shell: false,
|
|
98
|
+
});
|
|
38
99
|
|
|
39
|
-
const stdoutPromise = new Promise<void>((resolve) => {
|
|
40
100
|
child.stdout?.on("data", (chunk: Buffer | string) => {
|
|
41
|
-
|
|
42
|
-
text.split("\n").forEach((line) => {
|
|
101
|
+
chunk.toString().split("\n").forEach((line) => {
|
|
43
102
|
if (line.trim()) console.log(`${prefix}${line}`);
|
|
44
103
|
});
|
|
45
104
|
});
|
|
46
|
-
child.stdout?.on("end", resolve);
|
|
47
|
-
if (!child.stdout) resolve();
|
|
48
|
-
});
|
|
49
105
|
|
|
50
|
-
const stderrPromise = new Promise<void>((resolve) => {
|
|
51
106
|
child.stderr?.on("data", (chunk: Buffer | string) => {
|
|
52
|
-
|
|
53
|
-
text.split("\n").forEach((line) => {
|
|
107
|
+
chunk.toString().split("\n").forEach((line) => {
|
|
54
108
|
if (line.trim()) console.error(`${prefix}${line}`);
|
|
55
109
|
});
|
|
56
110
|
});
|
|
57
|
-
child.stderr?.on("end", resolve);
|
|
58
|
-
if (!child.stderr) resolve();
|
|
59
|
-
});
|
|
60
111
|
|
|
61
|
-
|
|
62
|
-
|
|
112
|
+
child.on("close", (code) => {
|
|
113
|
+
if (code === 0) {
|
|
114
|
+
console.log(`${prefix}✅ Completed successfully`);
|
|
115
|
+
resolve();
|
|
116
|
+
} else {
|
|
117
|
+
reject(new Error(`pgtyped failed with exit code ${code}`));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
63
120
|
});
|
|
64
|
-
|
|
65
|
-
if (processInfo.wait) {
|
|
66
|
-
const [code] = await Promise.all([exitCode, stdoutPromise, stderrPromise]);
|
|
67
|
-
if (code === 0) {
|
|
68
|
-
console.log(`${prefix}✅ Completed successfully`);
|
|
69
|
-
} else {
|
|
70
|
-
console.error(`${prefix}❌ Failed with exit code ${code}`);
|
|
71
|
-
throw new Error(`Process ${processInfo.name} failed`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
121
|
}
|
|
75
122
|
|
|
76
|
-
async function main() {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
command: "bun",
|
|
81
|
-
args: ["run", join(__dirname, "start-pglite.ts")],
|
|
82
|
-
wait: false,
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
name: "db:wait",
|
|
86
|
-
command: "bun",
|
|
87
|
-
args: ["run", join(__dirname, "wait-for-db.ts")],
|
|
88
|
-
wait: true,
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: "apply-migrations",
|
|
92
|
-
command: "bun",
|
|
93
|
-
args: ["run", join(__dirname, "apply-migrations.ts")],
|
|
94
|
-
wait: true,
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: "pgtyped",
|
|
98
|
-
command: "npx",
|
|
99
|
-
args: ["pgtyped", "-c", "./pgtypedconfig.json"],
|
|
100
|
-
wait: true,
|
|
101
|
-
},
|
|
102
|
-
];
|
|
123
|
+
export async function main(options?: PgtypedUpdateOptions) {
|
|
124
|
+
const userMigrations = options?.userMigrations ?? await resolveUserMigrations();
|
|
125
|
+
const pgtypedConfigPath = options?.pgtypedConfigPath ?? "./pgtypedconfig.json";
|
|
126
|
+
const pgtypedBin = options?.pgtypedBin ?? resolve(process.cwd(), "node_modules/@paima/pgtyped-cli/lib/index.js");
|
|
103
127
|
|
|
104
|
-
console.log("🚀 Starting
|
|
128
|
+
console.log("🚀 Starting pgtyped-update...\n");
|
|
105
129
|
|
|
130
|
+
let pglite: PgliteHandle | undefined;
|
|
106
131
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
console.
|
|
113
|
-
|
|
132
|
+
pglite = await startPglite();
|
|
133
|
+
await waitForDb();
|
|
134
|
+
await applySystemMigrations();
|
|
135
|
+
await applyUserMigrations(userMigrations);
|
|
136
|
+
await runPgtyped(pgtypedBin, pgtypedConfigPath);
|
|
137
|
+
console.log("\n✅ All steps completed successfully");
|
|
138
|
+
} finally {
|
|
139
|
+
await pglite?.close();
|
|
114
140
|
}
|
|
115
141
|
}
|
|
116
142
|
|
|
117
143
|
if (import.meta.main) {
|
|
118
|
-
|
|
144
|
+
try {
|
|
145
|
+
await main();
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error("\n❌ One or more steps failed:", error);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
119
150
|
}
|
package/scripts/start-pglite.ts
CHANGED
|
@@ -7,35 +7,30 @@ import { fromNodeSocket } from "pg-gateway/node";
|
|
|
7
7
|
import { ENV } from "@effectstream/utils/node-env";
|
|
8
8
|
import { args, cwd } from "@effectstream/utils/runtime";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const portArgIndex = argv.indexOf(portArgName);
|
|
15
|
-
const portValue = portArgIndex !== -1 ? argv[portArgIndex + 1] : "5432";
|
|
16
|
-
const port = parseInt(portValue);
|
|
17
|
-
if (isNaN(port)) {
|
|
18
|
-
throw new Error(`Port argument ${portArgName} is not a number`);
|
|
10
|
+
export interface PgliteHandle {
|
|
11
|
+
server: net.Server;
|
|
12
|
+
db: PGlite;
|
|
13
|
+
close: () => Promise<void>;
|
|
19
14
|
}
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const
|
|
16
|
+
export async function startPglite(port = 5432): Promise<PgliteHandle> {
|
|
17
|
+
// Resolve pg_ivm extension path via import.meta.resolve (works with bun's hoisting)
|
|
18
|
+
const pgliteEntry = import.meta.resolve("@electric-sql/pglite");
|
|
19
|
+
const pgliteDir = pgliteEntry.replace(/\/dist\/[^/]+$/, "");
|
|
20
|
+
const pgIvmUrl = new URL(`${pgliteDir}/dist/pg_ivm.tar.gz`);
|
|
25
21
|
|
|
26
|
-
const db = new PGlite(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
const db = new PGlite(
|
|
23
|
+
ENV.PGLITE_DATA_DIR,
|
|
24
|
+
{
|
|
25
|
+
username: ENV.DB_USER,
|
|
26
|
+
database: ENV.DB_NAME,
|
|
27
|
+
extensions: {
|
|
28
|
+
pg_ivm: pgIvmUrl,
|
|
29
|
+
},
|
|
30
|
+
debug: (ENV.DEBUG_PGLITE as DebugLevel) || 0,
|
|
33
31
|
},
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
);
|
|
32
|
+
);
|
|
37
33
|
|
|
38
|
-
{
|
|
39
34
|
// TODO: consider switching to pglite-socket once it works
|
|
40
35
|
// https://discord.com/channels/933657521581858818/1371976702674075780/1371992712076595250
|
|
41
36
|
const server = net.createServer(async (socket) => {
|
|
@@ -65,7 +60,33 @@ const db = new PGlite(
|
|
|
65
60
|
});
|
|
66
61
|
});
|
|
67
62
|
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
await new Promise<void>((resolve) => {
|
|
64
|
+
server.listen(port, () => {
|
|
65
|
+
console.info(`database: server listening on port ${port}`);
|
|
66
|
+
resolve();
|
|
67
|
+
});
|
|
70
68
|
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
server,
|
|
72
|
+
db,
|
|
73
|
+
close: async () => {
|
|
74
|
+
server.close();
|
|
75
|
+
await db.close();
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (import.meta.main) {
|
|
81
|
+
// TODO PORT be a ENV variable
|
|
82
|
+
// Get port from arguments.
|
|
83
|
+
const portArgName = "--port";
|
|
84
|
+
const argv = args();
|
|
85
|
+
const portArgIndex = argv.indexOf(portArgName);
|
|
86
|
+
const portValue = portArgIndex !== -1 ? argv[portArgIndex + 1] : "5432";
|
|
87
|
+
const port = parseInt(portValue);
|
|
88
|
+
if (isNaN(port)) {
|
|
89
|
+
throw new Error(`Port argument ${portArgName} is not a number`);
|
|
90
|
+
}
|
|
91
|
+
await startPglite(port);
|
|
71
92
|
}
|