updebe 0.1.1 → 0.1.2

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.
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runInit = runInit;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function getCurrentDateStamp() {
10
+ const now = new Date();
11
+ const year = now.getFullYear();
12
+ const month = String(now.getMonth() + 1).padStart(2, "0");
13
+ const day = String(now.getDate()).padStart(2, "0");
14
+ return `${year}${month}${day}`;
15
+ }
16
+ function runInit(baseDir = process.cwd()) {
17
+ const updebeDir = node_path_1.default.join(baseDir, "updebe");
18
+ const migrationsDir = node_path_1.default.join(updebeDir, "Migrations");
19
+ const dateStamp = getCurrentDateStamp();
20
+ const migrationFilePath = node_path_1.default.join(migrationsDir, `${dateStamp}_init.ts`);
21
+ const configFilePath = node_path_1.default.join(updebeDir, "config.udb");
22
+ node_fs_1.default.mkdirSync(migrationsDir, { recursive: true });
23
+ if (!node_fs_1.default.existsSync(configFilePath)) {
24
+ node_fs_1.default.writeFileSync(configFilePath, "", "utf8");
25
+ }
26
+ if (!node_fs_1.default.existsSync(migrationFilePath)) {
27
+ node_fs_1.default.writeFileSync(migrationFilePath, "export const revision = {\n _id: \"20260827_init\",\n revised_id: \"\"\n};\n\nexport async function upgrade(): Promise<void> {\n // Write upgrade SQL here\n}\n\nexport async function downgrade(): Promise<void> {\n // Write downgrade SQL here\n}\n", "utf8");
28
+ }
29
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runUpgradeHead = runUpgradeHead;
4
+ const database_1 = require("../config/database");
5
+ const connection_1 = require("../db/connection");
6
+ const files_1 = require("../migrations/files");
7
+ async function runUpgradeHead() {
8
+ (0, files_1.loadMigrationDefinitions)();
9
+ const dbConfig = (0, database_1.resolveDatabaseConfig)();
10
+ await (0, connection_1.testDatabaseConnection)(dbConfig);
11
+ console.log("Hey! I'm working");
12
+ }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveDatabaseConfig = resolveDatabaseConfig;
7
+ const dotenv_1 = __importDefault(require("dotenv"));
8
+ dotenv_1.default.config();
9
+ const ANSI_GREEN = "\x1b[32m";
10
+ const ANSI_RED = "\x1b[31m";
11
+ const ANSI_RESET = "\x1b[0m";
12
+ function readEnv(name) {
13
+ const value = process.env[name];
14
+ if (!value) {
15
+ return undefined;
16
+ }
17
+ const trimmed = value.trim();
18
+ return trimmed.length > 0 ? trimmed : undefined;
19
+ }
20
+ function parsePort(rawPort) {
21
+ const port = Number(rawPort);
22
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
23
+ throw new Error("Invalid environment variable: PGDB_PORT must be an integer between 1 and 65535");
24
+ }
25
+ return port;
26
+ }
27
+ function resolveDatabaseConfig() {
28
+ try {
29
+ const schema = readEnv("PGDB_SCHEMA") ?? "public";
30
+ const databaseUrl = readEnv("DATABASE_URL");
31
+ if (databaseUrl) {
32
+ try {
33
+ new URL(databaseUrl);
34
+ console.log(`${ANSI_GREEN}Database config resolved from DATABASE_URL (schema: ${schema})${ANSI_RESET}`);
35
+ return { databaseUrl, schema };
36
+ }
37
+ catch {
38
+ throw new Error("Invalid environment variable: DATABASE_URL must be a valid URL");
39
+ }
40
+ }
41
+ const user = readEnv("PGDB_USER");
42
+ const host = readEnv("PGDB_HOST");
43
+ const database = readEnv("PGDB_DATABASE");
44
+ const password = readEnv("PGDB_PASSWORD");
45
+ const rawPort = readEnv("PGDB_PORT");
46
+ const missingVars = [
47
+ ["PGDB_USER", user],
48
+ ["PGDB_HOST", host],
49
+ ["PGDB_DATABASE", database],
50
+ ["PGDB_PASSWORD", password],
51
+ ["PGDB_PORT", rawPort]
52
+ ]
53
+ .filter(([, value]) => !value)
54
+ .map(([name]) => name);
55
+ if (missingVars.length > 0) {
56
+ throw new Error(`Missing required environment variables: ${missingVars.join(", ")}`);
57
+ }
58
+ const port = parsePort(rawPort);
59
+ const encodedUser = encodeURIComponent(user);
60
+ const encodedPassword = encodeURIComponent(password);
61
+ const encodedDatabase = encodeURIComponent(database);
62
+ const builtDatabaseUrl = `postgresql://${encodedUser}:${encodedPassword}@${host}:${port}/${encodedDatabase}`;
63
+ console.log(`${ANSI_GREEN}Database config resolved from PGDB_* variables (schema: ${schema})${ANSI_RESET}`);
64
+ return {
65
+ databaseUrl: builtDatabaseUrl,
66
+ schema
67
+ };
68
+ }
69
+ catch (error) {
70
+ const message = error instanceof Error ? error.message : "Unknown error";
71
+ console.error(`${ANSI_RED}Database config error: ${message}${ANSI_RESET}`);
72
+ throw error;
73
+ }
74
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.testDatabaseConnection = testDatabaseConnection;
4
+ const pg_1 = require("pg");
5
+ const ANSI_GREEN = "\x1b[32m";
6
+ const ANSI_RED = "\x1b[31m";
7
+ const ANSI_RESET = "\x1b[0m";
8
+ function formatSafeError(error) {
9
+ if (!(error instanceof Error)) {
10
+ return "Unknown database error";
11
+ }
12
+ // Avoid leaking credentials if a driver error includes connection details.
13
+ return error.message.replace(/:\/\/[^@\s]+@/g, "://***:***@");
14
+ }
15
+ async function testDatabaseConnection(config) {
16
+ const client = new pg_1.Client({ connectionString: config.databaseUrl });
17
+ try {
18
+ await client.connect();
19
+ await client.query("SELECT 1");
20
+ console.log(`${ANSI_GREEN}Database connected successfully (schema: ${config.schema})${ANSI_RESET}`);
21
+ }
22
+ catch (error) {
23
+ const message = formatSafeError(error);
24
+ console.error(`${ANSI_RED}Database connection error: ${message}${ANSI_RESET}`);
25
+ throw new Error("Database connection failed");
26
+ }
27
+ finally {
28
+ await client.end().catch(() => undefined);
29
+ }
30
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadMigrationDefinitions = loadMigrationDefinitions;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function isNonEmptyString(value) {
10
+ return typeof value === "string" && value.trim().length > 0;
11
+ }
12
+ function validateMigrationDefinition(migration, filePath) {
13
+ if (!migration || typeof migration !== "object") {
14
+ throw new Error(`${filePath}: migration must be an object`);
15
+ }
16
+ const revision = migration.revision;
17
+ if (!revision || typeof revision !== "object") {
18
+ throw new Error(`${filePath}: missing required key revision`);
19
+ }
20
+ const revisionData = revision;
21
+ if (!isNonEmptyString(revisionData._id)) {
22
+ throw new Error(`${filePath}: missing required key revision._id`);
23
+ }
24
+ if (!isNonEmptyString(revisionData.revised_id)) {
25
+ throw new Error(`${filePath}: missing required key revision.revised_id`);
26
+ }
27
+ }
28
+ function loadMigrationDefinitions(baseDir = process.cwd()) {
29
+ const migrationsDir = node_path_1.default.join(baseDir, "migrations");
30
+ if (!node_fs_1.default.existsSync(migrationsDir)) {
31
+ return [];
32
+ }
33
+ const fileNames = node_fs_1.default.readdirSync(migrationsDir)
34
+ .filter((fileName) => fileName.endsWith(".json"))
35
+ .sort();
36
+ return fileNames.map((fileName) => {
37
+ const relativePath = `migrations/${fileName}`;
38
+ const fullPath = node_path_1.default.join(migrationsDir, fileName);
39
+ let parsedMigration;
40
+ try {
41
+ const content = node_fs_1.default.readFileSync(fullPath, "utf8");
42
+ parsedMigration = JSON.parse(content);
43
+ }
44
+ catch {
45
+ throw new Error(`${relativePath}: invalid JSON`);
46
+ }
47
+ validateMigrationDefinition(parsedMigration, relativePath);
48
+ return parsedMigration;
49
+ });
50
+ }
package/dist/updebe.js CHANGED
@@ -1,7 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ const init_1 = require("./commands/init");
5
+ const upgradeHead_1 = require("./commands/upgradeHead");
4
6
  const command = process.argv[2];
7
+ if (command === "init") {
8
+ try {
9
+ (0, init_1.runInit)();
10
+ }
11
+ catch (error) {
12
+ const message = error instanceof Error ? error.message : "Unknown error";
13
+ console.error(message);
14
+ process.exitCode = 1;
15
+ }
16
+ }
5
17
  if (command === "upgrade:head") {
6
- console.log("Hey! I'm working");
18
+ (0, upgradeHead_1.runUpgradeHead)().catch((error) => {
19
+ const message = error instanceof Error ? error.message : "Unknown error";
20
+ console.error(message);
21
+ process.exitCode = 1;
22
+ });
7
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "updebe",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "Schema migration tool",
6
6
  "main": "dist/updebe.js",
@@ -16,8 +16,13 @@
16
16
  "preupdebe": "npm run build",
17
17
  "updebe": "node dist/updebe.js"
18
18
  },
19
+ "dependencies": {
20
+ "dotenv": "^16.6.1",
21
+ "pg": "^8.16.3"
22
+ },
19
23
  "devDependencies": {
20
24
  "@types/node": "^24.13.3",
25
+ "@types/pg": "^8.23.1",
21
26
  "typescript": "^7.0.2"
22
27
  }
23
28
  }