@plyaz/db 0.3.1 → 0.4.1
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/dist/cli/index.js +380 -25
- package/dist/cli/index.js.map +1 -1
- package/package.json +4 -3
- package/template/config/db.config.mjs.template +33 -0
- package/template/migrations/migration.sql.template +29 -0
- package/template/migrations/migration.ts.template +54 -0
- package/template/seeds/seed.csv.template +22 -0
- package/template/seeds/seed.sql.template +19 -0
- package/template/seeds/seed.ts.template +63 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plyaz/db",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Unified database abstraction layer with CLI for migrations and seeds",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [],
|
|
7
7
|
"packageManager": "pnpm@10.11.0",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"author": "Redeemer Pace",
|
|
34
34
|
"license": "ISC",
|
|
35
35
|
"files": [
|
|
36
|
-
"dist"
|
|
36
|
+
"dist",
|
|
37
|
+
"template"
|
|
37
38
|
],
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"@nestjs/common": "^11.1.3",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Configuration
|
|
3
|
+
*
|
|
4
|
+
* Used by the @plyaz/db CLI for migrations and seeds
|
|
5
|
+
*
|
|
6
|
+
* @type {import('@plyaz/db').DatabaseServiceConfig}
|
|
7
|
+
*/
|
|
8
|
+
export default {
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// ADAPTER CONFIGURATION
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
adapter: globalThis.process.env.DB_ADAPTER ?? '{{ADAPTER}}',
|
|
14
|
+
|
|
15
|
+
config: {
|
|
16
|
+
connectionString: globalThis.process.env.DATABASE_URL ?? '{{CONNECTION_STRING}}',
|
|
17
|
+
poolSize: 20,
|
|
18
|
+
|
|
19
|
+
// Custom ID columns (optional)
|
|
20
|
+
// tableIdColumns: {
|
|
21
|
+
// 'feature_flags': 'key',
|
|
22
|
+
// },
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// MIGRATIONS & SEEDS PATHS
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
migrationsPath: '{{MIGRATIONS_PATH}}',
|
|
30
|
+
seedsPath: '{{SEEDS_PATH}}',
|
|
31
|
+
migrationsTable: '{{MIGRATIONS_TABLE}}',
|
|
32
|
+
seedsTable: '{{SEEDS_TABLE}}',
|
|
33
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Migration: {{DESCRIPTION}}
|
|
3
|
+
-- Version: {{VERSION}}
|
|
4
|
+
-- Created: {{CREATED_DATE}}
|
|
5
|
+
-- Author: {{AUTHOR}}
|
|
6
|
+
-- Description: {{DESCRIPTION}}
|
|
7
|
+
-- ============================================================================
|
|
8
|
+
|
|
9
|
+
-- UP
|
|
10
|
+
-- Create {{TABLE_NAME}} table
|
|
11
|
+
CREATE TABLE IF NOT EXISTS {{SCHEMA}}.{{TABLE_NAME}} (
|
|
12
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
13
|
+
name VARCHAR(255) NOT NULL,
|
|
14
|
+
-- Add your columns here
|
|
15
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
16
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
17
|
+
deleted_at TIMESTAMPTZ DEFAULT NULL
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
-- Indexes
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_{{TABLE_NAME}}_created_at ON {{SCHEMA}}.{{TABLE_NAME}}(created_at);
|
|
22
|
+
|
|
23
|
+
-- Comments
|
|
24
|
+
COMMENT ON TABLE {{SCHEMA}}.{{TABLE_NAME}} IS '{{DESCRIPTION}}';
|
|
25
|
+
|
|
26
|
+
-- DOWN
|
|
27
|
+
-- Drop in reverse order
|
|
28
|
+
DROP INDEX IF EXISTS idx_{{TABLE_NAME}}_created_at;
|
|
29
|
+
DROP TABLE IF EXISTS {{SCHEMA}}.{{TABLE_NAME}};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Migration {{VERSION}}: {{DESCRIPTION}}
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Created: {{CREATED_DATE}}
|
|
5
|
+
// Author: {{AUTHOR}}
|
|
6
|
+
// Description: {{DESCRIPTION}}
|
|
7
|
+
// Tables: {{SCHEMA}}.{{TABLE_NAME}}
|
|
8
|
+
// Dependencies: [List migration dependencies if any]
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
import type { DatabaseAdapterType } from '@plyaz/types/db';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Run the migration (forward)
|
|
15
|
+
* Creates the {{TABLE_NAME}} table with standard audit columns
|
|
16
|
+
*/
|
|
17
|
+
export async function up(adapter: DatabaseAdapterType): Promise<void> {
|
|
18
|
+
// Create table
|
|
19
|
+
await adapter.query(`
|
|
20
|
+
CREATE TABLE IF NOT EXISTS {{SCHEMA}}.{{TABLE_NAME}} (
|
|
21
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
22
|
+
name VARCHAR(255) NOT NULL,
|
|
23
|
+
-- Add your columns here
|
|
24
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
25
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
26
|
+
deleted_at TIMESTAMPTZ DEFAULT NULL
|
|
27
|
+
)
|
|
28
|
+
`);
|
|
29
|
+
|
|
30
|
+
// Create indexes
|
|
31
|
+
await adapter.query(`
|
|
32
|
+
CREATE INDEX IF NOT EXISTS idx_{{TABLE_NAME}}_created_at
|
|
33
|
+
ON {{SCHEMA}}.{{TABLE_NAME}}(created_at)
|
|
34
|
+
`);
|
|
35
|
+
|
|
36
|
+
// Add table comment
|
|
37
|
+
await adapter.query(`
|
|
38
|
+
COMMENT ON TABLE {{SCHEMA}}.{{TABLE_NAME}} IS '{{DESCRIPTION}}'
|
|
39
|
+
`);
|
|
40
|
+
|
|
41
|
+
console.log('[Migration {{VERSION}}] {{DESCRIPTION}} - completed');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Rollback the migration (reverse)
|
|
46
|
+
* Drops the {{TABLE_NAME}} table and related objects
|
|
47
|
+
*/
|
|
48
|
+
export async function down(adapter: DatabaseAdapterType): Promise<void> {
|
|
49
|
+
// Drop in reverse order of creation
|
|
50
|
+
await adapter.query(`DROP INDEX IF EXISTS idx_{{TABLE_NAME}}_created_at`);
|
|
51
|
+
await adapter.query(`DROP TABLE IF EXISTS {{SCHEMA}}.{{TABLE_NAME}}`);
|
|
52
|
+
|
|
53
|
+
console.log('[Migration {{VERSION}}] {{DESCRIPTION}} - rolled back');
|
|
54
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Seed: {{DESCRIPTION}}
|
|
2
|
+
# Table: {{SCHEMA}}.{{TABLE_NAME}}
|
|
3
|
+
# Created: {{CREATED_DATE}}
|
|
4
|
+
#
|
|
5
|
+
# CSV Seed File - Edit columns and data below
|
|
6
|
+
# First row defines column names matching the table schema
|
|
7
|
+
# Subsequent rows contain the data to insert
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# plyaz-db seed run - Imports this CSV into the database
|
|
11
|
+
# plyaz-db seed reset - Removes seeded records
|
|
12
|
+
#
|
|
13
|
+
# Notes:
|
|
14
|
+
# - UUIDs should use format: 00000000-0000-0000-0000-000000000001
|
|
15
|
+
# - Dates use ISO format: 2025-01-01T00:00:00.000Z
|
|
16
|
+
# - Empty values: leave field empty between commas
|
|
17
|
+
# - Strings with commas: wrap in double quotes "value, with comma"
|
|
18
|
+
#
|
|
19
|
+
id,name,description,created_at
|
|
20
|
+
{{UUID_1}},Example Record 1,First seed record,{{CREATED_DATE}}T00:00:00.000Z
|
|
21
|
+
{{UUID_2}},Example Record 2,Second seed record,{{CREATED_DATE}}T00:00:00.000Z
|
|
22
|
+
{{UUID_3}},Example Record 3,Third seed record,{{CREATED_DATE}}T00:00:00.000Z
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- ============================================================================
|
|
2
|
+
-- Seed: {{DESCRIPTION}}
|
|
3
|
+
-- Order: {{ORDER}}
|
|
4
|
+
-- Created: {{CREATED_DATE}}
|
|
5
|
+
-- Author: {{AUTHOR}}
|
|
6
|
+
-- Description: {{DESCRIPTION}}
|
|
7
|
+
-- Table: {{SCHEMA}}.{{TABLE_NAME}}
|
|
8
|
+
-- ============================================================================
|
|
9
|
+
|
|
10
|
+
-- Insert seed data for {{TABLE_NAME}}
|
|
11
|
+
-- Uses ON CONFLICT for idempotent seeds
|
|
12
|
+
|
|
13
|
+
INSERT INTO {{SCHEMA}}.{{TABLE_NAME}} (id, name, created_at) VALUES
|
|
14
|
+
('{{UUID_1}}', 'Seed Record 1', NOW()),
|
|
15
|
+
('{{UUID_2}}', 'Seed Record 2', NOW()),
|
|
16
|
+
('{{UUID_3}}', 'Seed Record 3', NOW())
|
|
17
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
18
|
+
name = EXCLUDED.name,
|
|
19
|
+
updated_at = NOW();
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed: {{DESCRIPTION}}
|
|
3
|
+
* Order: {{ORDER}}
|
|
4
|
+
* Created: {{CREATED_DATE}}
|
|
5
|
+
* Author: {{AUTHOR}}
|
|
6
|
+
* Description: {{DESCRIPTION}}
|
|
7
|
+
* Table: {{SCHEMA}}.{{TABLE_NAME}}
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { DatabaseAdapterType } from '@plyaz/types/db';
|
|
11
|
+
|
|
12
|
+
// Define your seed data
|
|
13
|
+
const TABLE_NAME = '{{TABLE_NAME}}';
|
|
14
|
+
const SEED_DATA = [
|
|
15
|
+
{ name: 'Seed Record 1', value: 100 },
|
|
16
|
+
{ name: 'Seed Record 2', value: 200 },
|
|
17
|
+
{ name: 'Seed Record 3', value: 300 },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Run the seed (insert data into {{TABLE_NAME}})
|
|
22
|
+
*/
|
|
23
|
+
export async function run(adapter: DatabaseAdapterType): Promise<void> {
|
|
24
|
+
console.log('[Seed] {{DESCRIPTION}} - starting...');
|
|
25
|
+
|
|
26
|
+
for (const item of SEED_DATA) {
|
|
27
|
+
// Check if data already exists (idempotent)
|
|
28
|
+
const existing = await adapter.findMany(TABLE_NAME, {
|
|
29
|
+
filter: { field: 'name', operator: 'eq', value: item.name },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (existing.success && existing.value && existing.value.data.length > 0) {
|
|
33
|
+
console.log(`[Seed] ${item.name} already exists, skipping`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await adapter.create(TABLE_NAME, {
|
|
38
|
+
...item,
|
|
39
|
+
created_at: new Date().toISOString(),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(`[Seed] {{DESCRIPTION}} - created ${SEED_DATA.length} records`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Cleanup the seed data (for reset operations)
|
|
48
|
+
*/
|
|
49
|
+
export async function cleanup(adapter: DatabaseAdapterType): Promise<void> {
|
|
50
|
+
console.log('[Seed] {{DESCRIPTION}} - cleaning up...');
|
|
51
|
+
|
|
52
|
+
// Delete seeded records by identifiable pattern
|
|
53
|
+
if (typeof adapter.query === 'function') {
|
|
54
|
+
for (const item of SEED_DATA) {
|
|
55
|
+
await adapter.query(
|
|
56
|
+
`DELETE FROM {{SCHEMA}}.${TABLE_NAME} WHERE name = $1`,
|
|
57
|
+
[item.name]
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log('[Seed] {{DESCRIPTION}} - cleanup complete');
|
|
63
|
+
}
|