@topogram/cli 0.3.34
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/ARCHITECTURE.md +67 -0
- package/CHANGELOG.md +240 -0
- package/README.md +223 -0
- package/package.json +51 -0
- package/src/adoption/index.js +3 -0
- package/src/adoption/plan.js +702 -0
- package/src/adoption/reporting.js +464 -0
- package/src/adoption/review-groups.js +313 -0
- package/src/agent-ops/query-builders.js +5012 -0
- package/src/archive/archive.js +141 -0
- package/src/archive/compact.js +26 -0
- package/src/archive/jsonl.js +70 -0
- package/src/archive/resolver-bridge.js +82 -0
- package/src/archive/schema.js +87 -0
- package/src/archive/unarchive.js +108 -0
- package/src/catalog.js +752 -0
- package/src/cli/catalog-alias.js +166 -0
- package/src/cli.js +9738 -0
- package/src/component-behavior.js +173 -0
- package/src/example-implementation.js +91 -0
- package/src/format.js +19 -0
- package/src/generator/adapters.d.ts +4 -0
- package/src/generator/adapters.js +325 -0
- package/src/generator/api.d.ts +1 -0
- package/src/generator/api.js +1196 -0
- package/src/generator/check.js +355 -0
- package/src/generator/component-conformance.js +767 -0
- package/src/generator/components.js +39 -0
- package/src/generator/context/bundle.js +291 -0
- package/src/generator/context/diff.js +256 -0
- package/src/generator/context/digest.js +182 -0
- package/src/generator/context/domain-coverage.js +94 -0
- package/src/generator/context/domain-page.js +137 -0
- package/src/generator/context/index.js +42 -0
- package/src/generator/context/report.js +121 -0
- package/src/generator/context/shared.js +1397 -0
- package/src/generator/context/slice.js +703 -0
- package/src/generator/context/task-mode.js +466 -0
- package/src/generator/docs.js +327 -0
- package/src/generator/index.js +161 -0
- package/src/generator/native/parity-bundle.js +311 -0
- package/src/generator/output.js +300 -0
- package/src/generator/registry.js +482 -0
- package/src/generator/runtime/app-bundle.js +456 -0
- package/src/generator/runtime/bundle-shared.js +166 -0
- package/src/generator/runtime/compile-check.js +163 -0
- package/src/generator/runtime/deployment.js +287 -0
- package/src/generator/runtime/environment.js +635 -0
- package/src/generator/runtime/index.js +32 -0
- package/src/generator/runtime/runtime-check.js +554 -0
- package/src/generator/runtime/shared.js +515 -0
- package/src/generator/runtime/smoke.js +219 -0
- package/src/generator/schema.js +204 -0
- package/src/generator/sdlc/board.js +66 -0
- package/src/generator/sdlc/doc-page.js +53 -0
- package/src/generator/sdlc/index.js +23 -0
- package/src/generator/sdlc/release-notes.js +62 -0
- package/src/generator/sdlc/traceability-matrix.js +65 -0
- package/src/generator/shared.js +29 -0
- package/src/generator/surfaces/contracts.js +146 -0
- package/src/generator/surfaces/databases/contract.js +40 -0
- package/src/generator/surfaces/databases/index.js +84 -0
- package/src/generator/surfaces/databases/lifecycle-shared.d.ts +1 -0
- package/src/generator/surfaces/databases/lifecycle-shared.js +612 -0
- package/src/generator/surfaces/databases/migration-plan.js +281 -0
- package/src/generator/surfaces/databases/postgres/capabilities.js +14 -0
- package/src/generator/surfaces/databases/postgres/drizzle.js +99 -0
- package/src/generator/surfaces/databases/postgres/index.js +9 -0
- package/src/generator/surfaces/databases/postgres/lifecycle.js +16 -0
- package/src/generator/surfaces/databases/postgres/prisma.js +159 -0
- package/src/generator/surfaces/databases/postgres/sql-migration.js +102 -0
- package/src/generator/surfaces/databases/postgres/sql-schema.js +34 -0
- package/src/generator/surfaces/databases/shared.d.ts +1 -0
- package/src/generator/surfaces/databases/shared.js +350 -0
- package/src/generator/surfaces/databases/snapshot.js +96 -0
- package/src/generator/surfaces/databases/sqlite/capabilities.js +14 -0
- package/src/generator/surfaces/databases/sqlite/index.js +8 -0
- package/src/generator/surfaces/databases/sqlite/lifecycle.js +16 -0
- package/src/generator/surfaces/databases/sqlite/prisma.js +143 -0
- package/src/generator/surfaces/databases/sqlite/sql-migration.js +65 -0
- package/src/generator/surfaces/databases/sqlite/sql-schema.js +27 -0
- package/src/generator/surfaces/index.js +25 -0
- package/src/generator/surfaces/native/swiftui-app.js +38 -0
- package/src/generator/surfaces/native/swiftui-templates/Package.swift.txt +20 -0
- package/src/generator/surfaces/native/swiftui-templates/README.generated.md +26 -0
- package/src/generator/surfaces/native/swiftui-templates/runtime/DynamicScreens.swift +682 -0
- package/src/generator/surfaces/native/swiftui-templates/runtime/TodoAPIClient.swift +156 -0
- package/src/generator/surfaces/native/swiftui-templates/runtime/TodoSwiftUIApp.swift +44 -0
- package/src/generator/surfaces/native/swiftui-templates/runtime/Visibility.swift +183 -0
- package/src/generator/surfaces/services/express.d.ts +1 -0
- package/src/generator/surfaces/services/express.js +766 -0
- package/src/generator/surfaces/services/hono.d.ts +1 -0
- package/src/generator/surfaces/services/hono.js +204 -0
- package/src/generator/surfaces/services/index.js +42 -0
- package/src/generator/surfaces/services/persistence-wiring.js +240 -0
- package/src/generator/surfaces/services/runtime-helpers.js +631 -0
- package/src/generator/surfaces/services/server-contract.js +80 -0
- package/src/generator/surfaces/services/stateless.d.ts +1 -0
- package/src/generator/surfaces/services/stateless.js +97 -0
- package/src/generator/surfaces/shared.js +64 -0
- package/src/generator/surfaces/web/api-client.js +1 -0
- package/src/generator/surfaces/web/forms.js +1 -0
- package/src/generator/surfaces/web/index.d.ts +2 -0
- package/src/generator/surfaces/web/index.js +53 -0
- package/src/generator/surfaces/web/react-components.js +248 -0
- package/src/generator/surfaces/web/react.js +538 -0
- package/src/generator/surfaces/web/routes.js +1 -0
- package/src/generator/surfaces/web/screens.js +1 -0
- package/src/generator/surfaces/web/shared.js +369 -0
- package/src/generator/surfaces/web/sveltekit-actions.js +28 -0
- package/src/generator/surfaces/web/sveltekit-components.js +234 -0
- package/src/generator/surfaces/web/sveltekit.js +426 -0
- package/src/generator/surfaces/web/ui-web-contract.js +65 -0
- package/src/generator/surfaces/web/vanilla.js +239 -0
- package/src/generator/verification.js +84 -0
- package/src/generator.js +1 -0
- package/src/import/core/context.js +52 -0
- package/src/import/core/contracts.js +23 -0
- package/src/import/core/registry.js +81 -0
- package/src/import/core/runner.js +646 -0
- package/src/import/core/shared.js +910 -0
- package/src/import/enrichers/auth-session.js +18 -0
- package/src/import/enrichers/django-rest.js +226 -0
- package/src/import/enrichers/doc-linking.js +20 -0
- package/src/import/enrichers/rails-controllers.js +246 -0
- package/src/import/enrichers/rails-models.js +130 -0
- package/src/import/enrichers/workflow-target-state.js +10 -0
- package/src/import/extractors/api/aspnet-core.js +304 -0
- package/src/import/extractors/api/django-routes.js +318 -0
- package/src/import/extractors/api/express.js +154 -0
- package/src/import/extractors/api/fastify.js +371 -0
- package/src/import/extractors/api/flutter-dio.js +135 -0
- package/src/import/extractors/api/generic-route-fallback.js +90 -0
- package/src/import/extractors/api/graphql-code-first.js +565 -0
- package/src/import/extractors/api/graphql-sdl.js +309 -0
- package/src/import/extractors/api/jaxrs.js +303 -0
- package/src/import/extractors/api/micronaut.js +213 -0
- package/src/import/extractors/api/next-route.js +50 -0
- package/src/import/extractors/api/next-server-action.js +51 -0
- package/src/import/extractors/api/nextauth.js +52 -0
- package/src/import/extractors/api/openapi-code.js +242 -0
- package/src/import/extractors/api/openapi.js +232 -0
- package/src/import/extractors/api/rails-routes.js +230 -0
- package/src/import/extractors/api/react-native-repository.js +128 -0
- package/src/import/extractors/api/retrofit.js +103 -0
- package/src/import/extractors/api/spring-web.js +372 -0
- package/src/import/extractors/api/swift-webapi.js +116 -0
- package/src/import/extractors/api/trpc.js +212 -0
- package/src/import/extractors/db/django-models.js +232 -0
- package/src/import/extractors/db/dotnet-models.js +93 -0
- package/src/import/extractors/db/drizzle.js +242 -0
- package/src/import/extractors/db/ef-core.js +221 -0
- package/src/import/extractors/db/flutter-entities.js +120 -0
- package/src/import/extractors/db/jpa.js +120 -0
- package/src/import/extractors/db/liquibase.js +180 -0
- package/src/import/extractors/db/mybatis-xml.js +145 -0
- package/src/import/extractors/db/prisma.js +185 -0
- package/src/import/extractors/db/rails-schema.js +175 -0
- package/src/import/extractors/db/react-native-entities.js +95 -0
- package/src/import/extractors/db/room.js +193 -0
- package/src/import/extractors/db/snapshot.js +112 -0
- package/src/import/extractors/db/sql.js +180 -0
- package/src/import/extractors/db/swiftdata.js +137 -0
- package/src/import/extractors/ui/android-compose.js +230 -0
- package/src/import/extractors/ui/backend-only.js +70 -0
- package/src/import/extractors/ui/blazor.js +227 -0
- package/src/import/extractors/ui/flutter-screens.js +152 -0
- package/src/import/extractors/ui/maui-xaml.js +135 -0
- package/src/import/extractors/ui/next-app-router.js +83 -0
- package/src/import/extractors/ui/next-pages-router.js +141 -0
- package/src/import/extractors/ui/razor-pages.js +181 -0
- package/src/import/extractors/ui/react-native-screens.js +166 -0
- package/src/import/extractors/ui/react-router.js +139 -0
- package/src/import/extractors/ui/sveltekit.js +123 -0
- package/src/import/extractors/ui/swiftui.js +193 -0
- package/src/import/extractors/ui/uikit.js +175 -0
- package/src/import/extractors/verification/generic.js +290 -0
- package/src/import/extractors/workflows/generic.js +137 -0
- package/src/import/index.js +7 -0
- package/src/import/provenance.js +158 -0
- package/src/new-project.js +2107 -0
- package/src/parser.js +439 -0
- package/src/policy/review-boundaries.js +165 -0
- package/src/project-config.js +535 -0
- package/src/proofs/backend-parity.js +19 -0
- package/src/proofs/contract-audit.js +220 -0
- package/src/proofs/ios-parity.js +7 -0
- package/src/proofs/issues-parity.js +10 -0
- package/src/proofs/web-parity.js +50 -0
- package/src/realization/api/build-api-realization.js +5 -0
- package/src/realization/api/index.js +1 -0
- package/src/realization/backend/build-backend-runtime-realization.js +82 -0
- package/src/realization/backend/index.d.ts +1 -0
- package/src/realization/backend/index.js +4 -0
- package/src/realization/db/build-db-realization.js +17 -0
- package/src/realization/db/index.js +3 -0
- package/src/realization/db/migration-plan.js +5 -0
- package/src/realization/db/snapshot.js +5 -0
- package/src/realization/ui/build-ui-shared-realization.js +305 -0
- package/src/realization/ui/build-web-realization.js +189 -0
- package/src/realization/ui/index.js +2 -0
- package/src/reconcile/docs.js +280 -0
- package/src/reconcile/index.js +3 -0
- package/src/reconcile/journeys.js +441 -0
- package/src/resolver/docs.js +1 -0
- package/src/resolver/enrich/acceptance-criterion.js +14 -0
- package/src/resolver/enrich/bug.js +12 -0
- package/src/resolver/enrich/component.js +2 -0
- package/src/resolver/enrich/index.js +1 -0
- package/src/resolver/enrich/pitch.js +18 -0
- package/src/resolver/enrich/requirement.js +20 -0
- package/src/resolver/enrich/task.js +16 -0
- package/src/resolver/expressions.js +1 -0
- package/src/resolver/index.js +2422 -0
- package/src/resolver/normalize.js +1 -0
- package/src/resolver.js +1 -0
- package/src/sdlc/adopt.js +65 -0
- package/src/sdlc/check.js +86 -0
- package/src/sdlc/dod/acceptance-criterion.js +22 -0
- package/src/sdlc/dod/bug.js +26 -0
- package/src/sdlc/dod/document.js +23 -0
- package/src/sdlc/dod/index.js +25 -0
- package/src/sdlc/dod/pitch.js +23 -0
- package/src/sdlc/dod/requirement.js +34 -0
- package/src/sdlc/dod/task.js +39 -0
- package/src/sdlc/explain.js +116 -0
- package/src/sdlc/history.js +80 -0
- package/src/sdlc/paths.js +11 -0
- package/src/sdlc/release.js +106 -0
- package/src/sdlc/scaffold.js +89 -0
- package/src/sdlc/status-filter.js +54 -0
- package/src/sdlc/transition.js +112 -0
- package/src/sdlc/transitions/acceptance-criterion.js +28 -0
- package/src/sdlc/transitions/bug.js +31 -0
- package/src/sdlc/transitions/document.js +29 -0
- package/src/sdlc/transitions/index.js +56 -0
- package/src/sdlc/transitions/pitch.js +34 -0
- package/src/sdlc/transitions/requirement.js +31 -0
- package/src/sdlc/transitions/task.js +34 -0
- package/src/template-trust.js +597 -0
- package/src/validator/expressions.js +1 -0
- package/src/validator/index.js +3424 -0
- package/src/validator/kinds.js +346 -0
- package/src/validator/per-kind/acceptance-criterion.js +91 -0
- package/src/validator/per-kind/bug.js +77 -0
- package/src/validator/per-kind/component.js +274 -0
- package/src/validator/per-kind/domain.js +205 -0
- package/src/validator/per-kind/pitch.js +101 -0
- package/src/validator/per-kind/requirement.js +75 -0
- package/src/validator/per-kind/task.js +96 -0
- package/src/validator/registry.js +1 -0
- package/src/validator/utils.js +12 -0
- package/src/validator.js +1 -0
- package/src/workflows.js +7597 -0
- package/src/workspace-docs.js +265 -0
- package/template-helpers/react.js +5 -0
- package/template-helpers/sveltekit.js +5 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateDbContractGraph } from "../contract.js";
|
|
2
|
+
import { normalizeDbSchemaSnapshot } from "../snapshot.js";
|
|
3
|
+
import {
|
|
4
|
+
enumStatementsForSnapshot,
|
|
5
|
+
indexGraphStatements,
|
|
6
|
+
renderCreateEnumType,
|
|
7
|
+
renderCreateTable,
|
|
8
|
+
renderIndexes
|
|
9
|
+
} from "../shared.js";
|
|
10
|
+
import { resolvePostgresCapabilities } from "./capabilities.js";
|
|
11
|
+
|
|
12
|
+
export function generatePostgresSqlSchema(graph, options = {}) {
|
|
13
|
+
resolvePostgresCapabilities(options.profileId);
|
|
14
|
+
const contract = generateDbContractGraph(graph, options);
|
|
15
|
+
const byId = indexGraphStatements(graph);
|
|
16
|
+
const snapshot = normalizeDbSchemaSnapshot(contract, byId);
|
|
17
|
+
const statements = [];
|
|
18
|
+
|
|
19
|
+
for (const enumStatement of enumStatementsForSnapshot(snapshot, byId)) {
|
|
20
|
+
statements.push(renderCreateEnumType(enumStatement));
|
|
21
|
+
statements.push("");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (const table of snapshot.tables) {
|
|
25
|
+
statements.push(renderCreateTable(table, "postgres", { byId }));
|
|
26
|
+
statements.push("");
|
|
27
|
+
for (const index of renderIndexes(table)) {
|
|
28
|
+
statements.push(index);
|
|
29
|
+
}
|
|
30
|
+
statements.push("");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return `${statements.join("\n").trimEnd()}\n`;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function getProjection(graph: any, projectionId?: any): any;
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
export const DB_TARGETS = new Set([
|
|
2
|
+
"db-contract-graph",
|
|
3
|
+
"db-contract-debug",
|
|
4
|
+
"db-schema-snapshot",
|
|
5
|
+
"db-migration-plan",
|
|
6
|
+
"db-lifecycle-plan",
|
|
7
|
+
"db-lifecycle-bundle",
|
|
8
|
+
"sql-schema",
|
|
9
|
+
"sql-migration",
|
|
10
|
+
"prisma-schema",
|
|
11
|
+
"drizzle-schema"
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
export function getDbFamily(options = {}) {
|
|
15
|
+
if (options.projectionId?.includes("sqlite")) {
|
|
16
|
+
return "sqlite";
|
|
17
|
+
}
|
|
18
|
+
return "postgres";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function indexStatements(graph) {
|
|
22
|
+
const byId = new Map();
|
|
23
|
+
for (const statement of graph.statements) {
|
|
24
|
+
byId.set(statement.id, statement);
|
|
25
|
+
}
|
|
26
|
+
return byId;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getProjection(graph, projectionId) {
|
|
30
|
+
const byId = indexStatements(graph);
|
|
31
|
+
const projection = byId.get(projectionId);
|
|
32
|
+
if (!projection || projection.kind !== "projection") {
|
|
33
|
+
throw new Error(`No projection found with id '${projectionId}'`);
|
|
34
|
+
}
|
|
35
|
+
return projection;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function indexGraphStatements(graph) {
|
|
39
|
+
return indexStatements(graph);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function dbProjectionCandidates(graph) {
|
|
43
|
+
return (graph.byKind.projection || []).filter(
|
|
44
|
+
(projection) =>
|
|
45
|
+
projection.platform?.startsWith("db_") ||
|
|
46
|
+
(projection.dbTables || []).length > 0 ||
|
|
47
|
+
(projection.dbColumns || []).length > 0 ||
|
|
48
|
+
(projection.dbRelations || []).length > 0
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function defaultTableName(entityId) {
|
|
53
|
+
const base = entityId.replace(/^entity_/, "");
|
|
54
|
+
return base.endsWith("s") ? base : `${base}s`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function generatorDefaultsMap(projection) {
|
|
58
|
+
const defaults = {};
|
|
59
|
+
for (const entry of projection.generatorDefaults || []) {
|
|
60
|
+
if (entry.key && entry.value != null) {
|
|
61
|
+
defaults[entry.key] = entry.value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return defaults;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function dbProfileForProjection(projection) {
|
|
68
|
+
const defaults = generatorDefaultsMap(projection);
|
|
69
|
+
return defaults.profile || (projection.platform === "db_sqlite" ? "sqlite_sql" : "postgres_sql");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function mergeDbKeys(entity, projection) {
|
|
73
|
+
const projectionKeys = (projection.dbKeys || []).filter((entry) => entry.entity?.id === entity.id);
|
|
74
|
+
const baseKeys = entity.keys || [];
|
|
75
|
+
return [...baseKeys, ...projectionKeys.map((entry) => ({ type: entry.keyType, fields: entry.fields, raw: entry.raw }))];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function mergeDbIndexes(entity, projection) {
|
|
79
|
+
const projectionIndexes = (projection.dbIndexes || []).filter((entry) => entry.entity?.id === entity.id);
|
|
80
|
+
const baseIndexes = entity.keys
|
|
81
|
+
.filter((entry) => entry.type === "index" || entry.type === "unique")
|
|
82
|
+
.map((entry) => ({ type: entry.type, fields: entry.fields, raw: entry.raw }));
|
|
83
|
+
return [...baseIndexes, ...projectionIndexes.map((entry) => ({ type: entry.indexType, fields: entry.fields, raw: entry.raw }))];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function dedupeFieldSets(entries) {
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
const output = [];
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const key = `${entry.type}:${entry.fields.join(",")}`;
|
|
91
|
+
if (seen.has(key)) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
seen.add(key);
|
|
95
|
+
output.push(entry);
|
|
96
|
+
}
|
|
97
|
+
return output;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function sortFieldSets(fieldSets = []) {
|
|
101
|
+
return [...fieldSets]
|
|
102
|
+
.map((fields) => [...fields])
|
|
103
|
+
.sort((a, b) => a.join("|").localeCompare(b.join("|")));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function findEnumStatement(byId, typeName) {
|
|
107
|
+
const candidate = byId.get(typeName);
|
|
108
|
+
return candidate?.kind === "enum" ? candidate : null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function toPascalCase(value) {
|
|
112
|
+
return value
|
|
113
|
+
.split(/[_\-\s]+/)
|
|
114
|
+
.filter(Boolean)
|
|
115
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
116
|
+
.join("");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function sqlTypeForField(fieldType, engine, byId = null) {
|
|
120
|
+
const enumStatement = byId ? findEnumStatement(byId, fieldType) : null;
|
|
121
|
+
if (enumStatement && engine === "postgres") {
|
|
122
|
+
return `"${toPascalCase(enumStatement.id.replace(/^enum_/, ""))}"`;
|
|
123
|
+
}
|
|
124
|
+
if (engine === "sqlite") {
|
|
125
|
+
switch (fieldType) {
|
|
126
|
+
case "integer":
|
|
127
|
+
case "boolean":
|
|
128
|
+
return "integer";
|
|
129
|
+
case "number":
|
|
130
|
+
return "real";
|
|
131
|
+
default:
|
|
132
|
+
return "text";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
switch (fieldType) {
|
|
137
|
+
case "uuid":
|
|
138
|
+
return "uuid";
|
|
139
|
+
case "integer":
|
|
140
|
+
return "integer";
|
|
141
|
+
case "number":
|
|
142
|
+
return "double precision";
|
|
143
|
+
case "boolean":
|
|
144
|
+
return "boolean";
|
|
145
|
+
case "datetime":
|
|
146
|
+
return "timestamptz";
|
|
147
|
+
default:
|
|
148
|
+
return "text";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function sqlDefaultLiteral(value, fieldType, engine) {
|
|
153
|
+
if (value == null) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
if (fieldType === "boolean") {
|
|
157
|
+
return engine === "sqlite" ? (value === "true" ? "1" : "0") : value.toUpperCase();
|
|
158
|
+
}
|
|
159
|
+
if (fieldType === "integer" || fieldType === "number") {
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
return `'${String(value).replace(/'/g, "''")}'`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function enumStatementsForSnapshot(snapshot, byId) {
|
|
166
|
+
if ((snapshot.enums || []).length > 0) {
|
|
167
|
+
return [...snapshot.enums].sort((a, b) => a.id.localeCompare(b.id));
|
|
168
|
+
}
|
|
169
|
+
const enums = new Map();
|
|
170
|
+
for (const table of snapshot.tables || []) {
|
|
171
|
+
for (const column of table.columns || []) {
|
|
172
|
+
const enumStatement = findEnumStatement(byId, column.fieldType);
|
|
173
|
+
if (enumStatement) {
|
|
174
|
+
enums.set(enumStatement.id, enumStatement);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return [...enums.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function renderCreateEnumType(enumStatement) {
|
|
182
|
+
const typeName = enumStatement.typeName || toPascalCase(enumStatement.id.replace(/^enum_/, ""));
|
|
183
|
+
const values = enumStatement.values.map((value) => `'${String(value).replace(/'/g, "''")}'`).join(", ");
|
|
184
|
+
return `DO $$ BEGIN\n CREATE TYPE "${typeName}" AS ENUM (${values});\nEXCEPTION\n WHEN duplicate_object THEN null;\nEND $$;`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function renderAddEnumValue(enumStatement, operation) {
|
|
188
|
+
const typeName = enumStatement.typeName || toPascalCase(enumStatement.id.replace(/^enum_/, ""));
|
|
189
|
+
const escapedValue = `'${String(operation.value).replace(/'/g, "''")}'`;
|
|
190
|
+
const position =
|
|
191
|
+
operation.before != null
|
|
192
|
+
? ` BEFORE '${String(operation.before).replace(/'/g, "''")}'`
|
|
193
|
+
: operation.after != null
|
|
194
|
+
? ` AFTER '${String(operation.after).replace(/'/g, "''")}'`
|
|
195
|
+
: "";
|
|
196
|
+
return `ALTER TYPE "${typeName}" ADD VALUE IF NOT EXISTS ${escapedValue}${position};`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function renderCreateTable(table, engine, options = {}) {
|
|
200
|
+
const includeRelations = options.includeRelations !== false;
|
|
201
|
+
const byId = options.byId || null;
|
|
202
|
+
const lines = [];
|
|
203
|
+
for (const column of table.columns) {
|
|
204
|
+
const parts = [`${column.name} ${sqlTypeForField(column.fieldType, engine, byId)}`];
|
|
205
|
+
if (column.requiredness === "required") {
|
|
206
|
+
parts.push("not null");
|
|
207
|
+
}
|
|
208
|
+
const defaultLiteral = sqlDefaultLiteral(column.defaultValue, column.fieldType, engine);
|
|
209
|
+
if (defaultLiteral != null) {
|
|
210
|
+
parts.push(`default ${defaultLiteral}`);
|
|
211
|
+
}
|
|
212
|
+
lines.push(` ${parts.join(" ")}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (table.primaryKey.length > 0) {
|
|
216
|
+
lines.push(` primary key (${table.primaryKey.join(", ")})`);
|
|
217
|
+
}
|
|
218
|
+
for (const fields of table.uniques) {
|
|
219
|
+
lines.push(` unique (${fields.join(", ")})`);
|
|
220
|
+
}
|
|
221
|
+
if (includeRelations) {
|
|
222
|
+
for (const relation of table.relations) {
|
|
223
|
+
if (relation.target?.id && relation.target?.field) {
|
|
224
|
+
const targetTable = defaultTableName(relation.target.id);
|
|
225
|
+
lines.push(` foreign key (${relation.field}) references ${targetTable}(${relation.target.field})${relation.onDelete ? ` on delete ${relation.onDelete.replace("_", " ")}` : ""}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return `create table ${table.table} (\n${lines.join(",\n")}\n);`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function renderIndexes(table) {
|
|
234
|
+
return table.indexes
|
|
235
|
+
.filter((entry) => entry.type === "index")
|
|
236
|
+
.map((entry) => {
|
|
237
|
+
const name = `${table.table}_${entry.fields.join("_")}_${entry.type}`;
|
|
238
|
+
return `create index ${name} on ${table.table} (${entry.fields.join(", ")});`;
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function renderAddColumn(table, column) {
|
|
243
|
+
const parts = [`alter table ${table} add column ${column.name} ${column.dbType}`];
|
|
244
|
+
if (column.required) {
|
|
245
|
+
parts.push("not null");
|
|
246
|
+
}
|
|
247
|
+
if (column.defaultSql != null) {
|
|
248
|
+
parts.push(`default ${column.defaultSql}`);
|
|
249
|
+
}
|
|
250
|
+
return `${parts.join(" ")};`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function buildDbProjectionContract(graph, projection) {
|
|
254
|
+
const byId = indexStatements(graph);
|
|
255
|
+
const realizedEntities = (projection.realizes || [])
|
|
256
|
+
.map((ref) => byId.get(ref.id))
|
|
257
|
+
.filter((statement) => statement?.kind === "entity");
|
|
258
|
+
|
|
259
|
+
const tableMappings = new Map((projection.dbTables || []).map((entry) => [entry.entity?.id, entry.table]));
|
|
260
|
+
const columnMappings = new Map((projection.dbColumns || []).map((entry) => [`${entry.entity?.id}:${entry.field}`, entry.column]));
|
|
261
|
+
const relationMappings = new Map();
|
|
262
|
+
for (const entry of projection.dbRelations || []) {
|
|
263
|
+
if (!relationMappings.has(entry.entity?.id)) {
|
|
264
|
+
relationMappings.set(entry.entity?.id, []);
|
|
265
|
+
}
|
|
266
|
+
relationMappings.get(entry.entity?.id).push(entry);
|
|
267
|
+
}
|
|
268
|
+
const lifecycleMappings = new Map();
|
|
269
|
+
for (const entry of projection.dbLifecycle || []) {
|
|
270
|
+
if (!lifecycleMappings.has(entry.entity?.id)) {
|
|
271
|
+
lifecycleMappings.set(entry.entity?.id, []);
|
|
272
|
+
}
|
|
273
|
+
lifecycleMappings.get(entry.entity?.id).push(entry);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
projection: {
|
|
278
|
+
id: projection.id,
|
|
279
|
+
name: projection.name || projection.id,
|
|
280
|
+
platform: projection.platform
|
|
281
|
+
},
|
|
282
|
+
profile: dbProfileForProjection(projection),
|
|
283
|
+
generatorDefaults: generatorDefaultsMap(projection),
|
|
284
|
+
tables: realizedEntities.map((entity) => {
|
|
285
|
+
const tableName = tableMappings.get(entity.id) || defaultTableName(entity.id);
|
|
286
|
+
const mergedKeys = mergeDbKeys(entity, projection);
|
|
287
|
+
const mergedIndexes = dedupeFieldSets(mergeDbIndexes(entity, projection));
|
|
288
|
+
const primaryKey = mergedKeys.find((entry) => entry.type === "primary")?.fields || [];
|
|
289
|
+
const uniques = mergedKeys
|
|
290
|
+
.filter((entry) => entry.type === "unique")
|
|
291
|
+
.map((entry) => entry.fields);
|
|
292
|
+
const indexes = mergedIndexes
|
|
293
|
+
.filter((entry) => entry.type === "index" || entry.type === "unique")
|
|
294
|
+
.map((entry) => ({
|
|
295
|
+
type: entry.type,
|
|
296
|
+
fields: entry.fields
|
|
297
|
+
}));
|
|
298
|
+
const relationEntries = relationMappings.get(entity.id) || entity.relations.map((relation) => ({
|
|
299
|
+
field: relation.sourceField,
|
|
300
|
+
target: relation.target,
|
|
301
|
+
onDelete: null
|
|
302
|
+
}));
|
|
303
|
+
const lifecycleEntries = lifecycleMappings.get(entity.id) || [];
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
type: "db_table_contract",
|
|
307
|
+
entity: {
|
|
308
|
+
id: entity.id,
|
|
309
|
+
name: entity.name || entity.id
|
|
310
|
+
},
|
|
311
|
+
table: tableName,
|
|
312
|
+
columns: entity.fields.map((field) => ({
|
|
313
|
+
name: columnMappings.get(`${entity.id}:${field.name}`) || field.name,
|
|
314
|
+
sourceField: field.name,
|
|
315
|
+
fieldType: field.fieldType,
|
|
316
|
+
requiredness: field.requiredness,
|
|
317
|
+
defaultValue: field.defaultValue
|
|
318
|
+
})),
|
|
319
|
+
primaryKey,
|
|
320
|
+
uniques,
|
|
321
|
+
indexes,
|
|
322
|
+
relations: relationEntries.map((entry) => ({
|
|
323
|
+
field: entry.field || entry.sourceField,
|
|
324
|
+
target: entry.target,
|
|
325
|
+
onDelete: entry.onDelete || null
|
|
326
|
+
})),
|
|
327
|
+
lifecycle: {
|
|
328
|
+
softDelete: (() => {
|
|
329
|
+
const entry = lifecycleEntries.find((item) => item.lifecycleType === "soft_delete");
|
|
330
|
+
return entry
|
|
331
|
+
? {
|
|
332
|
+
field: entry.field,
|
|
333
|
+
value: entry.value
|
|
334
|
+
}
|
|
335
|
+
: null;
|
|
336
|
+
})(),
|
|
337
|
+
timestamps: (() => {
|
|
338
|
+
const entry = lifecycleEntries.find((item) => item.lifecycleType === "timestamps");
|
|
339
|
+
return entry
|
|
340
|
+
? {
|
|
341
|
+
createdAt: entry.createdAt,
|
|
342
|
+
updatedAt: entry.updatedAt
|
|
343
|
+
}
|
|
344
|
+
: null;
|
|
345
|
+
})()
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
})
|
|
349
|
+
};
|
|
350
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildDbProjectionContract,
|
|
3
|
+
dbProjectionCandidates,
|
|
4
|
+
findEnumStatement,
|
|
5
|
+
getProjection,
|
|
6
|
+
indexGraphStatements,
|
|
7
|
+
sortFieldSets,
|
|
8
|
+
sqlDefaultLiteral,
|
|
9
|
+
sqlTypeForField,
|
|
10
|
+
toPascalCase
|
|
11
|
+
} from "./shared.js";
|
|
12
|
+
|
|
13
|
+
export function normalizeDbSchemaSnapshot(contract, byId = null) {
|
|
14
|
+
const engine = contract.projection.platform === "db_sqlite" ? "sqlite" : "postgres";
|
|
15
|
+
const tables = [...contract.tables]
|
|
16
|
+
.map((table) => ({
|
|
17
|
+
table: table.table,
|
|
18
|
+
entity: table.entity,
|
|
19
|
+
columns: [...table.columns]
|
|
20
|
+
.map((column) => ({
|
|
21
|
+
name: column.name,
|
|
22
|
+
sourceField: column.sourceField,
|
|
23
|
+
fieldType: column.fieldType,
|
|
24
|
+
dbType: sqlTypeForField(column.fieldType, engine, byId),
|
|
25
|
+
required: column.requiredness === "required",
|
|
26
|
+
requiredness: column.requiredness,
|
|
27
|
+
defaultValue: column.defaultValue ?? null,
|
|
28
|
+
defaultSql: sqlDefaultLiteral(column.defaultValue, column.fieldType, engine)
|
|
29
|
+
}))
|
|
30
|
+
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
31
|
+
primaryKey: [...table.primaryKey],
|
|
32
|
+
uniques: sortFieldSets(table.uniques || []),
|
|
33
|
+
indexes: [...(table.indexes || [])]
|
|
34
|
+
.map((entry) => ({
|
|
35
|
+
type: entry.type,
|
|
36
|
+
fields: [...entry.fields]
|
|
37
|
+
}))
|
|
38
|
+
.sort((a, b) => `${a.type}:${a.fields.join("|")}`.localeCompare(`${b.type}:${b.fields.join("|")}`)),
|
|
39
|
+
relations: [...(table.relations || [])]
|
|
40
|
+
.map((relation) => ({
|
|
41
|
+
field: relation.field,
|
|
42
|
+
target: relation.target,
|
|
43
|
+
onDelete: relation.onDelete || null
|
|
44
|
+
}))
|
|
45
|
+
.sort((a, b) => `${a.field}:${a.target?.id || ""}:${a.target?.field || ""}`.localeCompare(`${b.field}:${b.target?.id || ""}:${b.target?.field || ""}`)),
|
|
46
|
+
lifecycle: table.lifecycle
|
|
47
|
+
}))
|
|
48
|
+
.sort((a, b) => a.table.localeCompare(b.table));
|
|
49
|
+
const enums = byId
|
|
50
|
+
? [...new Map(
|
|
51
|
+
tables.flatMap((table) =>
|
|
52
|
+
table.columns.flatMap((column) => {
|
|
53
|
+
const enumStatement = findEnumStatement(byId, column.fieldType);
|
|
54
|
+
if (!enumStatement) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
return [[
|
|
58
|
+
enumStatement.id,
|
|
59
|
+
{
|
|
60
|
+
id: enumStatement.id,
|
|
61
|
+
typeName: toPascalCase(enumStatement.id.replace(/^enum_/, "")),
|
|
62
|
+
values: [...enumStatement.values]
|
|
63
|
+
}
|
|
64
|
+
]];
|
|
65
|
+
})
|
|
66
|
+
)
|
|
67
|
+
).values()].sort((a, b) => a.id.localeCompare(b.id))
|
|
68
|
+
: [];
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
type: "db_schema_snapshot",
|
|
72
|
+
projection: contract.projection,
|
|
73
|
+
profile: contract.profile,
|
|
74
|
+
generatorDefaults: contract.generatorDefaults,
|
|
75
|
+
engine,
|
|
76
|
+
enums,
|
|
77
|
+
tables
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function getTableFromSnapshot(snapshot, tableName) {
|
|
82
|
+
return (snapshot.tables || []).find((table) => table.table === tableName);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function generateDbSchemaSnapshot(graph, options = {}) {
|
|
86
|
+
const byId = indexGraphStatements(graph);
|
|
87
|
+
if (options.projectionId) {
|
|
88
|
+
return normalizeDbSchemaSnapshot(buildDbProjectionContract(graph, getProjection(graph, options.projectionId)), byId);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const output = {};
|
|
92
|
+
for (const projection of dbProjectionCandidates(graph)) {
|
|
93
|
+
output[projection.id] = normalizeDbSchemaSnapshot(buildDbProjectionContract(graph, projection), byId);
|
|
94
|
+
}
|
|
95
|
+
return output;
|
|
96
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const SQLITE_CAPABILITIES = {
|
|
2
|
+
default: {
|
|
3
|
+
family: "sqlite",
|
|
4
|
+
profiles: ["default"],
|
|
5
|
+
supportsEnums: false,
|
|
6
|
+
supportsPrisma: true,
|
|
7
|
+
supportsDrizzle: false,
|
|
8
|
+
supportsGeneratedBundles: true
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function resolveSqliteCapabilities(profileId = "default") {
|
|
13
|
+
return SQLITE_CAPABILITIES[profileId] || SQLITE_CAPABILITIES.default;
|
|
14
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { resolveSqliteCapabilities } from "./capabilities.js";
|
|
2
|
+
export { generateSqlitePrismaSchema } from "./prisma.js";
|
|
3
|
+
export { generateSqliteSqlSchema } from "./sql-schema.js";
|
|
4
|
+
export { generateSqliteSqlMigration } from "./sql-migration.js";
|
|
5
|
+
export {
|
|
6
|
+
generateSqliteDbLifecycleBundle,
|
|
7
|
+
generateSqliteDbLifecyclePlan
|
|
8
|
+
} from "./lifecycle.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateDbLifecycleBundleForProjection,
|
|
3
|
+
generateDbLifecyclePlanForProjection
|
|
4
|
+
} from "../lifecycle-shared.js";
|
|
5
|
+
import { getProjection } from "../shared.js";
|
|
6
|
+
import { resolveSqliteCapabilities } from "./capabilities.js";
|
|
7
|
+
|
|
8
|
+
export function generateSqliteDbLifecyclePlan(graph, options = {}) {
|
|
9
|
+
resolveSqliteCapabilities(options.profileId);
|
|
10
|
+
return generateDbLifecyclePlanForProjection(graph, getProjection(graph, options.projectionId), options);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function generateSqliteDbLifecycleBundle(graph, options = {}) {
|
|
14
|
+
resolveSqliteCapabilities(options.profileId);
|
|
15
|
+
return generateDbLifecycleBundleForProjection(graph, getProjection(graph, options.projectionId), options);
|
|
16
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildDbProjectionContract,
|
|
3
|
+
defaultTableName,
|
|
4
|
+
findEnumStatement,
|
|
5
|
+
getProjection,
|
|
6
|
+
indexGraphStatements,
|
|
7
|
+
toPascalCase
|
|
8
|
+
} from "../shared.js";
|
|
9
|
+
import { normalizeDbSchemaSnapshot } from "../snapshot.js";
|
|
10
|
+
import { resolveSqliteCapabilities } from "./capabilities.js";
|
|
11
|
+
|
|
12
|
+
function prismaScalarForColumn(column, byId) {
|
|
13
|
+
const enumStatement = findEnumStatement(byId, column.fieldType);
|
|
14
|
+
if (enumStatement) {
|
|
15
|
+
return { type: "String" };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
switch (column.fieldType) {
|
|
19
|
+
case "integer":
|
|
20
|
+
return { type: "Int" };
|
|
21
|
+
case "number":
|
|
22
|
+
return { type: "Float" };
|
|
23
|
+
case "boolean":
|
|
24
|
+
return { type: "Boolean" };
|
|
25
|
+
case "datetime":
|
|
26
|
+
return { type: "DateTime" };
|
|
27
|
+
default:
|
|
28
|
+
return { type: "String" };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function prismaDefaultForColumn(column) {
|
|
33
|
+
if (column.defaultValue == null) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (column.fieldType === "boolean") {
|
|
37
|
+
return `@default(${String(column.defaultValue) === "true" ? "true" : "false"})`;
|
|
38
|
+
}
|
|
39
|
+
if (column.fieldType === "integer" || column.fieldType === "number") {
|
|
40
|
+
return `@default(${column.defaultValue})`;
|
|
41
|
+
}
|
|
42
|
+
return `@default("${String(column.defaultValue).replace(/"/g, '\\"')}")`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function generateSqlitePrismaSchema(graph, options = {}) {
|
|
46
|
+
resolveSqliteCapabilities(options.profileId);
|
|
47
|
+
const projection = getProjection(graph, options.projectionId);
|
|
48
|
+
if (projection.platform !== "db_sqlite") {
|
|
49
|
+
throw new Error(`Prisma schema generation currently supports db_sqlite projections only, found '${projection.platform}'`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const byId = indexGraphStatements(graph);
|
|
53
|
+
const snapshot = normalizeDbSchemaSnapshot(buildDbProjectionContract(graph, projection));
|
|
54
|
+
|
|
55
|
+
const relationBackrefs = new Map();
|
|
56
|
+
for (const table of snapshot.tables) {
|
|
57
|
+
for (const relation of table.relations || []) {
|
|
58
|
+
const targetTable = defaultTableName(relation.target.id);
|
|
59
|
+
if (!relationBackrefs.has(targetTable)) {
|
|
60
|
+
relationBackrefs.set(targetTable, []);
|
|
61
|
+
}
|
|
62
|
+
relationBackrefs.get(targetTable).push({
|
|
63
|
+
fromTable: table.table,
|
|
64
|
+
fromModel: toPascalCase(table.entity.id.replace(/^entity_/, "")),
|
|
65
|
+
field: relation.field,
|
|
66
|
+
relationName: `${toPascalCase(table.entity.id.replace(/^entity_/, ""))}_${relation.field}_to_${toPascalCase(relation.target.id.replace(/^entity_/, ""))}`
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const lines = [];
|
|
72
|
+
lines.push('generator client {');
|
|
73
|
+
lines.push(' provider = "prisma-client-js"');
|
|
74
|
+
lines.push("}");
|
|
75
|
+
lines.push("");
|
|
76
|
+
lines.push("datasource db {");
|
|
77
|
+
lines.push(' provider = "sqlite"');
|
|
78
|
+
lines.push(' url = env("DATABASE_URL")');
|
|
79
|
+
lines.push("}");
|
|
80
|
+
lines.push("");
|
|
81
|
+
|
|
82
|
+
for (const table of snapshot.tables) {
|
|
83
|
+
const modelName = toPascalCase(table.entity.id.replace(/^entity_/, ""));
|
|
84
|
+
const pk = table.primaryKey || [];
|
|
85
|
+
const relationFields = new Map((table.relations || []).map((entry) => [entry.field, entry]));
|
|
86
|
+
|
|
87
|
+
lines.push(`model ${modelName} {`);
|
|
88
|
+
for (const column of table.columns) {
|
|
89
|
+
const scalar = prismaScalarForColumn(column, byId);
|
|
90
|
+
const optional = column.required ? "" : "?";
|
|
91
|
+
const attrs = [];
|
|
92
|
+
if (pk.length === 1 && pk[0] === column.name) {
|
|
93
|
+
attrs.push("@id");
|
|
94
|
+
}
|
|
95
|
+
const uniqueMatch = (table.uniques || []).find((fields) => fields.length === 1 && fields[0] === column.name);
|
|
96
|
+
if (uniqueMatch) {
|
|
97
|
+
attrs.push("@unique");
|
|
98
|
+
}
|
|
99
|
+
const defaultAttr = prismaDefaultForColumn(column);
|
|
100
|
+
if (defaultAttr) {
|
|
101
|
+
attrs.push(defaultAttr);
|
|
102
|
+
}
|
|
103
|
+
if (column.name !== column.sourceField) {
|
|
104
|
+
attrs.push(`@map("${column.name}")`);
|
|
105
|
+
}
|
|
106
|
+
lines.push(` ${column.sourceField} ${scalar.type}${optional}${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}`);
|
|
107
|
+
|
|
108
|
+
const relation = relationFields.get(column.name);
|
|
109
|
+
if (relation) {
|
|
110
|
+
const targetModel = toPascalCase(relation.target.id.replace(/^entity_/, ""));
|
|
111
|
+
const relationName = `${modelName}_${column.sourceField}_to_${targetModel}`;
|
|
112
|
+
const optionalRelation = column.required ? "" : "?";
|
|
113
|
+
lines.push(` ${column.sourceField.replace(/_id$/, "")} ${targetModel}${optionalRelation} @relation("${relationName}", fields: [${column.sourceField}], references: [${relation.target.field}])`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (const backref of relationBackrefs.get(table.table) || []) {
|
|
118
|
+
const relationFieldName = `${backref.fromTable}`;
|
|
119
|
+
lines.push(` ${relationFieldName} ${backref.fromModel}[] @relation("${backref.relationName}")`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (pk.length > 1) {
|
|
123
|
+
lines.push(` @@id([${pk.join(", ")}])`);
|
|
124
|
+
}
|
|
125
|
+
for (const fields of table.uniques || []) {
|
|
126
|
+
if (fields.length > 1) {
|
|
127
|
+
lines.push(` @@unique([${fields.join(", ")}])`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
for (const index of table.indexes || []) {
|
|
131
|
+
if (index.type === "index") {
|
|
132
|
+
lines.push(` @@index([${index.fields.join(", ")}])`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (table.table !== modelName) {
|
|
136
|
+
lines.push(` @@map("${table.table}")`);
|
|
137
|
+
}
|
|
138
|
+
lines.push("}");
|
|
139
|
+
lines.push("");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return `${lines.join("\n").trimEnd()}\n`;
|
|
143
|
+
}
|