@lssm/app.cli-database 0.0.0-canary-20251206160926
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 +52 -0
- package/dist/_virtual/rolldown_runtime.mjs +7 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +81 -0
- package/dist/commands/check.mjs +34 -0
- package/dist/commands/generate.mjs +9 -0
- package/dist/commands/import.mjs +95 -0
- package/dist/commands/migrate.mjs +20 -0
- package/dist/commands/schema-compose.mjs +76 -0
- package/dist/commands/schema-generate.mjs +51 -0
- package/dist/commands/seed.mjs +14 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +5 -0
- package/dist/lib/client.d.mts +7 -0
- package/dist/lib/client.mjs +17 -0
- package/dist/lib/config.d.mts +7 -0
- package/dist/lib/config.mjs +7 -0
- package/dist/lib/schema-config.d.mts +31 -0
- package/dist/lib/schema-config.mjs +22 -0
- package/dist/lib/types.d.mts +13 -0
- package/dist/libs/schema/dist/EnumType.mjs +4 -0
- package/dist/libs/schema/dist/FieldType.mjs +4 -0
- package/dist/libs/schema/dist/ScalarTypeEnum.mjs +5 -0
- package/dist/libs/schema/dist/SchemaModel.mjs +5 -0
- package/dist/libs/schema/dist/entity/defineEntity.mjs +3 -0
- package/dist/libs/schema/dist/entity/generator.mjs +90 -0
- package/dist/libs/schema/dist/entity/index.mjs +5 -0
- package/dist/libs/schema/dist/entity/types.mjs +3 -0
- package/dist/libs/schema/dist/index.mjs +9 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @lssm/app.cli-database
|
|
2
|
+
|
|
3
|
+
Prisma wrapper and CLI tools for managing database schemas, migrations, and seeding within the LSSM monorepo.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
To abstract and standardize Prisma usage across multiple services and verticals. It provides a unified CLI (`database`) to handle common database tasks like migrations (`dbs:migrate`), client generation (`dbs:generate`), and seeding (`dbs:seed`).
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @lssm/app.cli-database
|
|
13
|
+
# or
|
|
14
|
+
bun add @lssm/app.cli-database
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Key Concepts
|
|
18
|
+
|
|
19
|
+
- **Unified Config**: Centralizes Prisma configuration.
|
|
20
|
+
- **CLI Wrapper**: Wraps standard Prisma commands for consistent execution in the monorepo environment.
|
|
21
|
+
- **Seeding**: Standardized seeding entry point.
|
|
22
|
+
|
|
23
|
+
## Exports
|
|
24
|
+
|
|
25
|
+
- `cli`: The command-line interface.
|
|
26
|
+
- `prisma`: Re-exports `@prisma/client`.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### CLI Commands
|
|
31
|
+
|
|
32
|
+
Run via package scripts:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Generate Prisma Client
|
|
36
|
+
bun database generate
|
|
37
|
+
|
|
38
|
+
# Run migrations
|
|
39
|
+
bun database migrate:dev
|
|
40
|
+
|
|
41
|
+
# Seed database
|
|
42
|
+
bun database seed
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### In Code
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { PrismaClient } from '@lssm/app.cli-database';
|
|
49
|
+
|
|
50
|
+
const prisma = new PrismaClient();
|
|
51
|
+
const users = await prisma.user.findMany();
|
|
52
|
+
```
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runImport } from "./commands/import.mjs";
|
|
3
|
+
import { runCheck } from "./commands/check.mjs";
|
|
4
|
+
import { runGenerate } from "./commands/generate.mjs";
|
|
5
|
+
import { runMigrate } from "./commands/migrate.mjs";
|
|
6
|
+
import { runSeed } from "./commands/seed.mjs";
|
|
7
|
+
import { runSchemaGenerate } from "./commands/schema-generate.mjs";
|
|
8
|
+
import { runSchemaCompose } from "./commands/schema-compose.mjs";
|
|
9
|
+
import mri from "minimist";
|
|
10
|
+
|
|
11
|
+
//#region src/cli.ts
|
|
12
|
+
async function main() {
|
|
13
|
+
const argv = mri(process.argv.slice(2));
|
|
14
|
+
const [cmd] = argv._;
|
|
15
|
+
switch (cmd) {
|
|
16
|
+
case "import":
|
|
17
|
+
await runImport(argv);
|
|
18
|
+
break;
|
|
19
|
+
case "check":
|
|
20
|
+
await runCheck(argv);
|
|
21
|
+
break;
|
|
22
|
+
case "generate":
|
|
23
|
+
await runGenerate(argv);
|
|
24
|
+
break;
|
|
25
|
+
case "migrate:dev":
|
|
26
|
+
case "migrate:deploy":
|
|
27
|
+
case "migrate:status":
|
|
28
|
+
await runMigrate(cmd, argv);
|
|
29
|
+
break;
|
|
30
|
+
case "seed":
|
|
31
|
+
await runSeed(argv);
|
|
32
|
+
break;
|
|
33
|
+
case "schema:generate":
|
|
34
|
+
await runSchemaGenerate(argv);
|
|
35
|
+
break;
|
|
36
|
+
case "schema:compose":
|
|
37
|
+
await runSchemaCompose(argv);
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
printUsage();
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function printUsage() {
|
|
45
|
+
console.log(`
|
|
46
|
+
Usage: database <command> [options]
|
|
47
|
+
|
|
48
|
+
Commands:
|
|
49
|
+
import Import schema from external sources
|
|
50
|
+
check Check Prisma schema validity
|
|
51
|
+
generate Generate Prisma client
|
|
52
|
+
migrate:dev Run migrations in development
|
|
53
|
+
migrate:deploy Deploy migrations to production
|
|
54
|
+
migrate:status Check migration status
|
|
55
|
+
seed Seed the database
|
|
56
|
+
|
|
57
|
+
Schema Generation (spec-first):
|
|
58
|
+
schema:generate Generate Prisma schema from entity specs
|
|
59
|
+
--config <path> Schema config file (default: ./schema.config.ts)
|
|
60
|
+
--output <path> Output path (default: ./prisma/schema/generated.prisma)
|
|
61
|
+
--module <id> Generate only a specific module
|
|
62
|
+
|
|
63
|
+
schema:compose Compose multiple module schemas
|
|
64
|
+
--config <path> Schema config file (default: ./schema.config.ts)
|
|
65
|
+
--output <path> Output path (default: ./prisma/schema/composed.prisma)
|
|
66
|
+
--modules <list> Comma-separated module IDs to include
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
database schema:generate --config ./schema.config.ts
|
|
70
|
+
database schema:compose --modules "@lssm/lib.identity-rbac,@lssm/module.audit-trail"
|
|
71
|
+
database generate
|
|
72
|
+
database migrate:dev --name add_users_table
|
|
73
|
+
`);
|
|
74
|
+
}
|
|
75
|
+
main().catch((err) => {
|
|
76
|
+
console.error(err);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
|
|
5
|
+
//#region src/commands/check.ts
|
|
6
|
+
function sha256(content) {
|
|
7
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
8
|
+
}
|
|
9
|
+
async function runCheck(argv) {
|
|
10
|
+
const target = argv.target || process.cwd();
|
|
11
|
+
const lockPath = path.join(target, "prisma", "schema", "imported", "imported.lock.json");
|
|
12
|
+
if (!fs.existsSync(lockPath)) {
|
|
13
|
+
console.error("No imported.lock.json found. Run: database import");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const lock = JSON.parse(fs.readFileSync(lockPath, "utf8"));
|
|
17
|
+
let ok = true;
|
|
18
|
+
for (const e of lock.entries) {
|
|
19
|
+
if (!fs.existsSync(e.sourcePath)) {
|
|
20
|
+
console.error(`Missing source: ${e.sourcePath}`);
|
|
21
|
+
ok = false;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (sha256(fs.readFileSync(e.sourcePath, "utf8")) !== e.sha256) {
|
|
25
|
+
console.error(`Drift detected for ${e.moduleName} (${e.sourcePath})`);
|
|
26
|
+
ok = false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!ok) process.exit(2);
|
|
30
|
+
console.log("Imported schemas are in sync with lock.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { runCheck };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { __require } from "../_virtual/rolldown_runtime.mjs";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
//#region src/commands/import.ts
|
|
8
|
+
function sha256(content) {
|
|
9
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
function stripDatasourceAndGenerators(schema) {
|
|
12
|
+
return schema.replace(/datasource\s+\w+\s*\{[\s\S]*?\}/g, "").replace(/generator\s+\w+\s*\{[\s\S]*?\}/g, "").trim();
|
|
13
|
+
}
|
|
14
|
+
async function runImport(argv) {
|
|
15
|
+
const target = argv.target || process.cwd();
|
|
16
|
+
const modulesArg = argv.modules || "";
|
|
17
|
+
async function loadModulesFromMergerConfig() {
|
|
18
|
+
const candidates = [
|
|
19
|
+
path.join(target, "prisma-merger.config.ts"),
|
|
20
|
+
path.join(target, "prisma-merger.config.mts"),
|
|
21
|
+
path.join(target, "prisma-merger.config.js"),
|
|
22
|
+
path.join(target, "prisma-merger.config.mjs"),
|
|
23
|
+
path.join(target, "prisma-merger.config.cjs"),
|
|
24
|
+
path.join(target, "prisma-merger.config.json")
|
|
25
|
+
];
|
|
26
|
+
for (const file of candidates) {
|
|
27
|
+
if (!fs.existsSync(file)) continue;
|
|
28
|
+
if (file.endsWith(".json")) {
|
|
29
|
+
const json = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
30
|
+
if (Array.isArray(json.modules) && json.modules.length > 0) return json.modules;
|
|
31
|
+
} else if (file.endsWith(".ts") || file.endsWith(".mts")) {
|
|
32
|
+
const match = fs.readFileSync(file, "utf8").match(/modules\s*:\s*\[([\s\S]*?)\]/m);
|
|
33
|
+
if (Array.isArray(match) && typeof match[1] === "string") {
|
|
34
|
+
const arr = match[1] || "";
|
|
35
|
+
const mods = [];
|
|
36
|
+
const re = /['\"]([^'\"]+)['\"]/g;
|
|
37
|
+
let m;
|
|
38
|
+
while (m = re.exec(arr)) {
|
|
39
|
+
const val = m[1] ?? "";
|
|
40
|
+
if (val) mods.push(val);
|
|
41
|
+
}
|
|
42
|
+
if (mods.length > 0) return mods;
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
const mod = await import(pathToFileURL(file).href);
|
|
46
|
+
const cfg = mod.default || mod;
|
|
47
|
+
if (Array.isArray(cfg.modules) && cfg.modules.length > 0) return cfg.modules;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
let modules = modulesArg.split(",").map((s) => s.trim()).filter(Boolean);
|
|
53
|
+
if (modules.length === 0) {
|
|
54
|
+
const fromCfg = await loadModulesFromMergerConfig();
|
|
55
|
+
if (fromCfg) modules = fromCfg;
|
|
56
|
+
}
|
|
57
|
+
if (modules.length === 0) {
|
|
58
|
+
console.error("No modules specified. Provide --modules or define prisma-merger.config.ts { modules: [...] }");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const importedDir = path.join(target, "prisma", "schema", "imported");
|
|
62
|
+
fs.mkdirSync(importedDir, { recursive: true });
|
|
63
|
+
const entries = [];
|
|
64
|
+
for (const mod of modules) {
|
|
65
|
+
const pkgPath = __require.resolve(path.join(mod, "package.json"));
|
|
66
|
+
const modRoot = path.dirname(pkgPath);
|
|
67
|
+
const schemaPath = path.join(modRoot, "prisma", "schema.prisma");
|
|
68
|
+
if (!fs.existsSync(schemaPath)) {
|
|
69
|
+
console.error(`Module ${mod} missing prisma/schema.prisma`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const raw = fs.readFileSync(schemaPath, "utf8");
|
|
73
|
+
const stripped = stripDatasourceAndGenerators(raw);
|
|
74
|
+
const outDir = path.join(importedDir, path.basename(mod).replace(/^@/, "").replace(/\//g, "_"));
|
|
75
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
76
|
+
const outFile = path.join(outDir, "models.prisma");
|
|
77
|
+
fs.writeFileSync(outFile, stripped, "utf8");
|
|
78
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
79
|
+
entries.push({
|
|
80
|
+
moduleName: mod,
|
|
81
|
+
version: pkg.version || "0.0.0",
|
|
82
|
+
sourcePath: schemaPath,
|
|
83
|
+
sha256: sha256(raw)
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const lock = {
|
|
87
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
88
|
+
entries
|
|
89
|
+
};
|
|
90
|
+
fs.writeFileSync(path.join(importedDir, "imported.lock.json"), JSON.stringify(lock, null, 2));
|
|
91
|
+
console.log(`Imported ${entries.length} modules into ${importedDir}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { runImport };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
|
|
3
|
+
//#region src/commands/migrate.ts
|
|
4
|
+
async function runMigrate(cmd, argv) {
|
|
5
|
+
if (cmd === "migrate:dev") {
|
|
6
|
+
await execa("prisma", ["migrate", "dev"], { stdio: "inherit" });
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (cmd === "migrate:deploy") {
|
|
10
|
+
await execa("prisma", ["migrate", "deploy"], { stdio: "inherit" });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (cmd === "migrate:status") {
|
|
14
|
+
await execa("prisma", ["migrate", "status"], { stdio: "inherit" });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
export { runMigrate };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { loadSchemaConfig } from "../lib/schema-config.mjs";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region src/commands/schema-compose.ts
|
|
6
|
+
/**
|
|
7
|
+
* Compose multiple module schemas into a single Prisma schema.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* database schema:compose --config ./schema.config.ts
|
|
11
|
+
* database schema:compose --modules "@lssm/lib.identity-rbac,@lssm/module.audit-trail"
|
|
12
|
+
*/
|
|
13
|
+
async function runSchemaCompose(argv) {
|
|
14
|
+
console.log("🔗 Composing module schemas...\n");
|
|
15
|
+
const configPath = argv.config ?? "./schema.config.ts";
|
|
16
|
+
const config = await loadSchemaConfig(configPath);
|
|
17
|
+
if (!config) {
|
|
18
|
+
console.error(`❌ Could not load schema config from ${configPath}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
let modules = config.modules;
|
|
22
|
+
if (argv.modules) {
|
|
23
|
+
const moduleIds = argv.modules.split(",").map((m) => m.trim());
|
|
24
|
+
modules = config.modules.filter((m) => moduleIds.includes(m.moduleId));
|
|
25
|
+
}
|
|
26
|
+
if (modules.length === 0) {
|
|
27
|
+
console.error("❌ No modules found to compose");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
console.log(`📦 Composing ${modules.length} module(s):`);
|
|
31
|
+
modules.forEach((m) => {
|
|
32
|
+
console.log(` - ${m.moduleId}`);
|
|
33
|
+
m.entities.forEach((e) => {
|
|
34
|
+
console.log(` • ${e.name} (${Object.keys(e.fields).length} fields)`);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
console.log("");
|
|
38
|
+
const allEnums = /* @__PURE__ */ new Set();
|
|
39
|
+
modules.forEach((m) => {
|
|
40
|
+
m.enums?.forEach((e) => allEnums.add(e.name));
|
|
41
|
+
m.entities.forEach((entity) => {
|
|
42
|
+
entity.enums?.forEach((e) => allEnums.add(e.name));
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
if (allEnums.size > 0) {
|
|
46
|
+
console.log(`📋 Enums discovered: ${Array.from(allEnums).join(", ")}`);
|
|
47
|
+
console.log("");
|
|
48
|
+
}
|
|
49
|
+
const { composeModuleSchemas } = await import("../libs/schema/dist/index.mjs");
|
|
50
|
+
const prismaSchema = composeModuleSchemas(modules, {
|
|
51
|
+
provider: config.provider ?? "postgresql",
|
|
52
|
+
clientOutput: config.clientOutput,
|
|
53
|
+
includePothos: config.includePothos ?? true,
|
|
54
|
+
pothosOutput: config.pothosOutput
|
|
55
|
+
});
|
|
56
|
+
const outputPath = argv.output ?? config.outputPath ?? "./prisma/schema/composed.prisma";
|
|
57
|
+
const outputDir = path.dirname(outputPath);
|
|
58
|
+
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
|
|
59
|
+
fs.writeFileSync(outputPath, prismaSchema, "utf-8");
|
|
60
|
+
const entityCount = modules.reduce((acc, m) => acc + m.entities.length, 0);
|
|
61
|
+
const fieldCount = modules.reduce((acc, m) => acc + m.entities.reduce((eAcc, e) => eAcc + Object.keys(e.fields).length, 0), 0);
|
|
62
|
+
console.log("✅ Schema composition complete!");
|
|
63
|
+
console.log("");
|
|
64
|
+
console.log(` 📄 Output: ${outputPath}`);
|
|
65
|
+
console.log(` 📦 Modules: ${modules.length}`);
|
|
66
|
+
console.log(` 🏗️ Entities: ${entityCount}`);
|
|
67
|
+
console.log(` 📝 Fields: ${fieldCount}`);
|
|
68
|
+
console.log(` 📋 Enums: ${allEnums.size}`);
|
|
69
|
+
console.log("");
|
|
70
|
+
console.log("Next steps:");
|
|
71
|
+
console.log(" 1. Run: database generate");
|
|
72
|
+
console.log(" 2. Run: database migrate:dev --name <migration-name>");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
export { runSchemaCompose };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { loadSchemaConfig } from "../lib/schema-config.mjs";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region src/commands/schema-generate.ts
|
|
6
|
+
/**
|
|
7
|
+
* Generate Prisma schema from ContractSpec entity definitions.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* database schema:generate --config ./schema.config.ts --output ./prisma/schema
|
|
11
|
+
* database schema:generate --module @lssm/lib.identity-rbac
|
|
12
|
+
*/
|
|
13
|
+
async function runSchemaGenerate(argv) {
|
|
14
|
+
console.log("📝 Generating Prisma schema from entity specs...\n");
|
|
15
|
+
const configPath = argv.config ?? "./schema.config.ts";
|
|
16
|
+
const config = await loadSchemaConfig(configPath);
|
|
17
|
+
if (!config) {
|
|
18
|
+
console.error(`❌ Could not load schema config from ${configPath}`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const modules = argv.module ? config.modules.filter((m) => m.moduleId === argv.module) : config.modules;
|
|
22
|
+
if (modules.length === 0) {
|
|
23
|
+
console.error("❌ No modules found to generate");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
console.log(`📦 Processing ${modules.length} module(s):`);
|
|
27
|
+
modules.forEach((m) => {
|
|
28
|
+
console.log(` - ${m.moduleId} (${m.entities.length} entities)`);
|
|
29
|
+
});
|
|
30
|
+
console.log("");
|
|
31
|
+
const { composeModuleSchemas } = await import("../libs/schema/dist/index.mjs");
|
|
32
|
+
const prismaSchema = composeModuleSchemas(modules, {
|
|
33
|
+
provider: config.provider ?? "postgresql",
|
|
34
|
+
clientOutput: config.clientOutput,
|
|
35
|
+
includePothos: config.includePothos ?? true,
|
|
36
|
+
pothosOutput: config.pothosOutput
|
|
37
|
+
});
|
|
38
|
+
const outputPath = argv.output ?? config.outputPath ?? "./prisma/schema/generated.prisma";
|
|
39
|
+
const outputDir = path.dirname(outputPath);
|
|
40
|
+
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
|
|
41
|
+
fs.writeFileSync(outputPath, prismaSchema, "utf-8");
|
|
42
|
+
console.log(`✅ Generated Prisma schema: ${outputPath}`);
|
|
43
|
+
console.log("");
|
|
44
|
+
console.log("Next steps:");
|
|
45
|
+
console.log(" 1. Review the generated schema");
|
|
46
|
+
console.log(" 2. Run: database generate");
|
|
47
|
+
console.log(" 3. Run: database migrate:dev");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { runSchemaGenerate };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
|
|
3
|
+
//#region src/commands/seed.ts
|
|
4
|
+
async function runSeed(argv) {
|
|
5
|
+
try {
|
|
6
|
+
await execa("prisma", ["db", "seed"], { stdio: "inherit" });
|
|
7
|
+
} catch (e) {
|
|
8
|
+
console.warn("No prisma seed configured or seed failed.");
|
|
9
|
+
throw e;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { runSeed };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createPrismaClientFromEnv, getSingletonClient } from "./lib/client.mjs";
|
|
2
|
+
import { ImportLock, ImportLockEntry } from "./lib/types.mjs";
|
|
3
|
+
import { MergedPrismaConfig, defineMergedPrismaConfig } from "./lib/config.mjs";
|
|
4
|
+
import { ModuleSchemaContribution, SchemaConfig, defineSchemaConfig, loadSchemaConfig } from "./lib/schema-config.mjs";
|
|
5
|
+
export { ImportLock, ImportLockEntry, MergedPrismaConfig, ModuleSchemaContribution, SchemaConfig, createPrismaClientFromEnv, defineMergedPrismaConfig, defineSchemaConfig, getSingletonClient, loadSchemaConfig };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { defineSchemaConfig, loadSchemaConfig } from "./lib/schema-config.mjs";
|
|
2
|
+
import { createPrismaClientFromEnv, getSingletonClient } from "./lib/client.mjs";
|
|
3
|
+
import { defineMergedPrismaConfig } from "./lib/config.mjs";
|
|
4
|
+
|
|
5
|
+
export { createPrismaClientFromEnv, defineMergedPrismaConfig, defineSchemaConfig, getSingletonClient, loadSchemaConfig };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/client.d.ts
|
|
4
|
+
declare function createPrismaClientFromEnv(envVar: string, fallback?: string): PrismaClient;
|
|
5
|
+
declare function getSingletonClient(envVar?: string): PrismaClient;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { createPrismaClientFromEnv, getSingletonClient };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
|
+
import { PrismaPg } from "@prisma/adapter-pg";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/client.ts
|
|
5
|
+
const g = global;
|
|
6
|
+
function createPrismaClientFromEnv(envVar, fallback) {
|
|
7
|
+
const raw = process.env[envVar] || (fallback ? process.env[fallback] : void 0);
|
|
8
|
+
if (!raw) throw new Error(`Missing database URL env: ${envVar}${fallback ? ` (or ${fallback})` : ""}`);
|
|
9
|
+
return new PrismaClient({ adapter: new PrismaPg({ connectionString: `${raw}`.replaceAll("sslmode=require", "sslmode=disable") }) });
|
|
10
|
+
}
|
|
11
|
+
function getSingletonClient(envVar = "DATABASE_URL") {
|
|
12
|
+
if (!g.__lssm_db) g.__lssm_db = createPrismaClientFromEnv(envVar);
|
|
13
|
+
return g.__lssm_db;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { createPrismaClientFromEnv, getSingletonClient };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ModuleSchemaContribution } from "@lssm/lib.schema";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/schema-config.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for schema generation and composition.
|
|
7
|
+
*/
|
|
8
|
+
interface SchemaConfig {
|
|
9
|
+
/** Module schema contributions to include */
|
|
10
|
+
modules: ModuleSchemaContribution[];
|
|
11
|
+
/** Prisma datasource provider (default: 'postgresql') */
|
|
12
|
+
provider?: 'postgresql' | 'mysql' | 'sqlite' | 'mongodb' | 'sqlserver';
|
|
13
|
+
/** Output path for generated Prisma schema */
|
|
14
|
+
outputPath?: string;
|
|
15
|
+
/** Prisma client output directory */
|
|
16
|
+
clientOutput?: string;
|
|
17
|
+
/** Include Pothos generator (default: true) */
|
|
18
|
+
includePothos?: boolean;
|
|
19
|
+
/** Pothos output path */
|
|
20
|
+
pothosOutput?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Define a schema configuration.
|
|
24
|
+
*/
|
|
25
|
+
declare function defineSchemaConfig(config: SchemaConfig): SchemaConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Load schema configuration from a file.
|
|
28
|
+
*/
|
|
29
|
+
declare function loadSchemaConfig(configPath: string): Promise<SchemaConfig | null>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { type ModuleSchemaContribution, SchemaConfig, defineSchemaConfig, loadSchemaConfig };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/lib/schema-config.ts
|
|
2
|
+
/**
|
|
3
|
+
* Define a schema configuration.
|
|
4
|
+
*/
|
|
5
|
+
function defineSchemaConfig(config) {
|
|
6
|
+
return config;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Load schema configuration from a file.
|
|
10
|
+
*/
|
|
11
|
+
async function loadSchemaConfig(configPath) {
|
|
12
|
+
try {
|
|
13
|
+
const module = await (configPath.startsWith("/") ? import(configPath) : import(`${process.cwd()}/${configPath}`));
|
|
14
|
+
return module.default ?? module.config ?? module;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error(`Failed to load schema config from ${configPath}:`, error);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { defineSchemaConfig, loadSchemaConfig };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/lib/types.d.ts
|
|
2
|
+
interface ImportLockEntry {
|
|
3
|
+
moduleName: string;
|
|
4
|
+
version: string;
|
|
5
|
+
sourcePath: string;
|
|
6
|
+
sha256: string;
|
|
7
|
+
}
|
|
8
|
+
interface ImportLock {
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
entries: ImportLockEntry[];
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ImportLock, ImportLockEntry };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
//#region ../../libs/schema/dist/entity/generator.js
|
|
2
|
+
function e(e$1, r$1 = {}) {
|
|
3
|
+
let { provider: i$1 = `postgresql`, clientOutput: a$1 = `../../src/generated/prisma`, includePothos: o$1 = !0, pothosOutput: s$1 = `../../src/generated/pothos-types.ts` } = r$1, c$1 = [], l = /* @__PURE__ */ new Set();
|
|
4
|
+
e$1.forEach((e$2) => {
|
|
5
|
+
e$2.schema && l.add(e$2.schema);
|
|
6
|
+
});
|
|
7
|
+
let u = l.size > 0 ? Array.from(l) : [`public`];
|
|
8
|
+
c$1.push(`datasource db {`), c$1.push(` provider = "${i$1}"`), l.size > 0 && c$1.push(` schemas = [${u.map((e$2) => `"${e$2}"`).join(`, `)}]`), c$1.push(`}`), c$1.push(``), c$1.push(`generator client {`), c$1.push(` provider = "prisma-client"`), c$1.push(` output = "${a$1}"`), c$1.push(``), c$1.push(` engineType = "client"`), c$1.push(` runtime = "bun"`), c$1.push(` moduleFormat = "esm"`), c$1.push(` generatedFileExtension = "ts"`), c$1.push(` importFileExtension = "ts"`), c$1.push(`}`), c$1.push(``), o$1 && (c$1.push(`generator pothos {`), c$1.push(` provider = "prisma-pothos-types"`), c$1.push(` clientOutput = "./prisma"`), c$1.push(` output = "${s$1}"`), c$1.push(` generateDatamodel = true`), c$1.push(` documentation = false`), c$1.push(`}`), c$1.push(``));
|
|
9
|
+
let d = /* @__PURE__ */ new Map();
|
|
10
|
+
return e$1.forEach((e$2) => {
|
|
11
|
+
e$2.enums?.forEach((e$3) => {
|
|
12
|
+
d.has(e$3.name) || d.set(e$3.name, e$3);
|
|
13
|
+
}), Object.values(e$2.fields).forEach((t$1) => {
|
|
14
|
+
t$1.kind === `enum` && t$1.values && !d.has(t$1.enumName) && d.set(t$1.enumName, {
|
|
15
|
+
name: t$1.enumName,
|
|
16
|
+
values: t$1.values,
|
|
17
|
+
schema: e$2.schema
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}), d.forEach((e$2) => {
|
|
21
|
+
c$1.push(...t(e$2)), c$1.push(``);
|
|
22
|
+
}), e$1.forEach((e$2) => {
|
|
23
|
+
c$1.push(...n(e$2)), c$1.push(``);
|
|
24
|
+
}), c$1.join(`
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
function t(e$1) {
|
|
28
|
+
let t$1 = [];
|
|
29
|
+
return e$1.description && t$1.push(`/// ${e$1.description}`), t$1.push(`enum ${e$1.name} {`), e$1.values.forEach((e$2) => {
|
|
30
|
+
t$1.push(` ${e$2}`);
|
|
31
|
+
}), t$1.push(``), e$1.schema && t$1.push(` @@schema("${e$1.schema}")`), t$1.push(`}`), t$1;
|
|
32
|
+
}
|
|
33
|
+
function n(e$1) {
|
|
34
|
+
let t$1 = [];
|
|
35
|
+
return e$1.description && t$1.push(`/// ${e$1.description}`), t$1.push(`model ${e$1.name} {`), Object.entries(e$1.fields).forEach(([e$2, n$1]) => {
|
|
36
|
+
let i$1 = r(e$2, n$1);
|
|
37
|
+
n$1.description && t$1.push(` /// ${n$1.description}`), t$1.push(` ${i$1}`);
|
|
38
|
+
}), e$1.indexes && e$1.indexes.length > 0 && (t$1.push(``), e$1.indexes.forEach((e$2) => {
|
|
39
|
+
t$1.push(` ${s(e$2)}`);
|
|
40
|
+
})), e$1.map && t$1.push(` @@map("${e$1.map}")`), e$1.schema && t$1.push(` @@schema("${e$1.schema}")`), t$1.push(`}`), t$1;
|
|
41
|
+
}
|
|
42
|
+
function r(e$1, t$1) {
|
|
43
|
+
if (t$1.kind === `scalar`) return i(e$1, t$1);
|
|
44
|
+
if (t$1.kind === `enum`) return a(e$1, t$1);
|
|
45
|
+
if (t$1.kind === `relation`) return o(e$1, t$1);
|
|
46
|
+
throw Error(`Unknown field kind: ${t$1.kind}`);
|
|
47
|
+
}
|
|
48
|
+
function i(e$1, t$1) {
|
|
49
|
+
let n$1 = [e$1], r$1 = t$1.type;
|
|
50
|
+
t$1.isArray && (r$1 += `[]`), t$1.isOptional && (r$1 += `?`), n$1.push(r$1);
|
|
51
|
+
let i$1 = [];
|
|
52
|
+
return t$1.isId && i$1.push(`@id`), t$1.default !== void 0 && (typeof t$1.default == `string` && t$1.default.includes(`(`) || typeof t$1.default == `boolean` || typeof t$1.default == `number` ? i$1.push(`@default(${t$1.default})`) : i$1.push(`@default("${t$1.default}")`)), t$1.updatedAt && i$1.push(`@updatedAt`), t$1.isUnique && i$1.push(`@unique`), t$1.map && i$1.push(`@map("${t$1.map}")`), t$1.dbType && i$1.push(`@db.${t$1.dbType}`), i$1.length > 0 && n$1.push(i$1.join(` `)), n$1.join(` `);
|
|
53
|
+
}
|
|
54
|
+
function a(e$1, t$1) {
|
|
55
|
+
let n$1 = [e$1], r$1 = t$1.enumName;
|
|
56
|
+
t$1.isArray && (r$1 += `[]`), t$1.isOptional && (r$1 += `?`), n$1.push(r$1);
|
|
57
|
+
let i$1 = [];
|
|
58
|
+
return t$1.default !== void 0 && i$1.push(`@default(${t$1.default})`), t$1.isUnique && i$1.push(`@unique`), t$1.map && i$1.push(`@map("${t$1.map}")`), i$1.length > 0 && n$1.push(i$1.join(` `)), n$1.join(` `);
|
|
59
|
+
}
|
|
60
|
+
function o(e$1, t$1) {
|
|
61
|
+
let n$1 = [e$1], r$1 = t$1.target;
|
|
62
|
+
t$1.type === `hasMany` && (r$1 += `[]`), t$1.type === `hasOne` && (r$1 += `?`), n$1.push(r$1);
|
|
63
|
+
let i$1 = [];
|
|
64
|
+
return t$1.name && i$1.push(`name: "${t$1.name}"`), t$1.fields && t$1.fields.length > 0 && i$1.push(`fields: [${t$1.fields.join(`, `)}]`), t$1.references && t$1.references.length > 0 && i$1.push(`references: [${t$1.references.join(`, `)}]`), t$1.onDelete && i$1.push(`onDelete: ${t$1.onDelete}`), t$1.onUpdate && i$1.push(`onUpdate: ${t$1.onUpdate}`), i$1.length > 0 && n$1.push(`@relation(${i$1.join(`, `)})`), n$1.join(` `);
|
|
65
|
+
}
|
|
66
|
+
function s(e$1) {
|
|
67
|
+
let t$1 = e$1.fields.join(`, `), n$1 = [];
|
|
68
|
+
e$1.unique ? n$1.push(`@@unique([${t$1}]`) : n$1.push(`@@index([${t$1}]`);
|
|
69
|
+
let r$1 = [];
|
|
70
|
+
return e$1.name && r$1.push(`name: "${e$1.name}"`), e$1.type && r$1.push(`type: ${e$1.type}`), r$1.length > 0 && (n$1[0] += `, ${r$1.join(`, `)}`), n$1[0] += `)`, n$1.join(``);
|
|
71
|
+
}
|
|
72
|
+
function c(t$1, n$1 = {}) {
|
|
73
|
+
let r$1 = [], i$1 = /* @__PURE__ */ new Map();
|
|
74
|
+
return t$1.forEach((e$1) => {
|
|
75
|
+
e$1.entities.forEach((t$2) => {
|
|
76
|
+
r$1.push({
|
|
77
|
+
...t$2,
|
|
78
|
+
module: e$1.moduleId
|
|
79
|
+
});
|
|
80
|
+
}), e$1.enums?.forEach((e$2) => {
|
|
81
|
+
i$1.has(e$2.name) || i$1.set(e$2.name, e$2);
|
|
82
|
+
});
|
|
83
|
+
}), r$1.length > 0 && i$1.size > 0 && (r$1[0] = {
|
|
84
|
+
...r$1[0],
|
|
85
|
+
enums: [...r$1[0].enums ?? [], ...i$1.values()]
|
|
86
|
+
}), e(r$1, n$1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
//#endregion
|
|
90
|
+
export { c, e };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import "./EnumType.mjs";
|
|
2
|
+
import "./FieldType.mjs";
|
|
3
|
+
import "./ScalarTypeEnum.mjs";
|
|
4
|
+
import "./SchemaModel.mjs";
|
|
5
|
+
import "./entity/defineEntity.mjs";
|
|
6
|
+
import { c, e } from "./entity/generator.mjs";
|
|
7
|
+
import "./entity/index.mjs";
|
|
8
|
+
|
|
9
|
+
export { c as composeModuleSchemas };
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lssm/app.cli-database",
|
|
3
|
+
"version": "0.0.0-canary-20251206160926",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"database": "dist/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"clean": "rm -rf dist",
|
|
10
|
+
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
11
|
+
"build": "tsdown",
|
|
12
|
+
"dev": "bun run build --watch",
|
|
13
|
+
"lint": "bun run lint:fix",
|
|
14
|
+
"lint:fix": "eslint src --fix",
|
|
15
|
+
"lint:check": "eslint src",
|
|
16
|
+
"prisma:import": "bun database import",
|
|
17
|
+
"prisma:check": "bun database check",
|
|
18
|
+
"dbs:generate": "bun database generate",
|
|
19
|
+
"dbs:migrate": "bun database migrate:dev",
|
|
20
|
+
"dbs:deploy": "bun database migrate:deploy",
|
|
21
|
+
"dbs:status": "bun database migrate:status",
|
|
22
|
+
"dbs:seed": "bun database seed",
|
|
23
|
+
"schema:generate": "bun database schema:generate",
|
|
24
|
+
"schema:compose": "bun database schema:compose"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@lssm/lib.schema": "workspace:*",
|
|
28
|
+
"@prisma/adapter-pg": "^7.1.0",
|
|
29
|
+
"@prisma/client": "^7.1.0",
|
|
30
|
+
"prisma": "^7.1.0",
|
|
31
|
+
"minimist": "^1.2.8",
|
|
32
|
+
"execa": "^9.6.1"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@lssm/tool.tsdown": "workspace:*",
|
|
36
|
+
"@types/minimist": "^1.2.5",
|
|
37
|
+
"tsdown": "^0.17.0"
|
|
38
|
+
},
|
|
39
|
+
"main": "./dist/index.mjs",
|
|
40
|
+
"module": "./dist/index.mjs",
|
|
41
|
+
"types": "./dist/index.d.mts",
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md"
|
|
45
|
+
],
|
|
46
|
+
"exports": {
|
|
47
|
+
".": "./dist/index.mjs",
|
|
48
|
+
"./cli": "./dist/cli.mjs",
|
|
49
|
+
"./*": "./*"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
}
|
|
54
|
+
}
|