@metaobjectsdev/cli 0.11.4 → 0.11.5-rc.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.
@@ -1,158 +0,0 @@
1
- import {
2
- resolveTableName,
3
- MetaRoot,
4
- type MetaData,
5
- } from "@metaobjectsdev/metadata";
6
- import {
7
- isProjection,
8
- extractViewSpec,
9
- emitViewDdl,
10
- } from "@metaobjectsdev/codegen-ts";
11
- import {
12
- computeViewMigrations,
13
- viewSqlEquals,
14
- type ViewMigrationInput,
15
- type ViewMigrationsResult,
16
- } from "@metaobjectsdev/migrate-ts";
17
- import type { Dialect } from "./kysely.js";
18
-
19
- /** view-name → set of source-table names the view's SELECT depends on. */
20
- export type ProjectionViewDependencies = ReadonlyMap<string, ReadonlySet<string>>;
21
-
22
- export interface ProjectionMigrationsOpts {
23
- readonly metadata: MetaData;
24
- readonly dialect: Dialect;
25
- readonly allowBreaking?: boolean;
26
- /** Column naming strategy forwarded to extractViewSpec. Defaults to "snake_case". */
27
- readonly columnNamingStrategy?: "snake_case" | "literal" | "kebab-case";
28
- /**
29
- * Existing view SQL keyed by view name (from sqlite_master.sql or
30
- * pg_views.definition). When provided, projections whose emitted CREATE
31
- * SQL matches the existing definition (whitespace-normalized) are skipped
32
- * — no DROP+CREATE noise for unchanged views.
33
- */
34
- readonly existingViewSql?: ReadonlyMap<string, string>;
35
- }
36
-
37
- /**
38
- * Walk all projection entities in metadata, extract their ViewSpec, emit CREATE
39
- * VIEW DDL, and compute view migration SQL via computeViewMigrations.
40
- *
41
- * Currently treats every projection as a new view (no previous-shape tracking).
42
- * Future: introspect existing views from the live DB to do safe-append/replace
43
- * detection.
44
- */
45
- export function computeProjectionMigrations(
46
- opts: ProjectionMigrationsOpts,
47
- ): ViewMigrationsResult {
48
- // loadMemory now returns MetaRoot; guard here also covers callers that pass a
49
- // plain MetaData (e.g. test helpers or external callers with non-MetaRoot roots).
50
- if (!(opts.metadata instanceof MetaRoot)) {
51
- throw new Error("computeProjectionMigrations: opts.metadata must be a loaded MetaRoot.");
52
- }
53
- // D1 is SQLite at the SQL level; normalize before passing to downstream emitters.
54
- const dialect: "postgres" | "sqlite" = opts.dialect === "d1" ? "sqlite" : opts.dialect;
55
- const root = opts.metadata;
56
- const columnNamingStrategy = opts.columnNamingStrategy ?? "snake_case";
57
-
58
- // Collect all writable entities for table name resolution.
59
- const joinTables: Record<string, string> = {};
60
- for (const obj of root.objects()) {
61
- joinTables[obj.name] = resolveTableName(obj);
62
- }
63
-
64
- // Find projection entities.
65
- const projections = root.objects().filter(isProjection);
66
-
67
- if (projections.length === 0) {
68
- return { migrations: [], errors: [] };
69
- }
70
-
71
- const views: ViewMigrationInput[] = [];
72
- for (const projection of projections) {
73
- const spec = extractViewSpec(projection, root, { columnNamingStrategy });
74
-
75
- const baseTableName = joinTables[spec.joinTree.baseEntity];
76
- if (!baseTableName) {
77
- return {
78
- migrations: [],
79
- errors: [
80
- `Projection ${projection.name}: base entity "${spec.joinTree.baseEntity}" has no resolvable table name.`,
81
- ],
82
- };
83
- }
84
-
85
- const createSql = emitViewDdl(spec, {
86
- dialect,
87
- baseTableName,
88
- joinTables,
89
- });
90
-
91
- // Skip if the existing DB view's CREATE SQL matches what we'd emit.
92
- // Avoids the "every migration re-creates every view" noise when nothing
93
- // about the view's body actually changed.
94
- const existing = opts.existingViewSql?.get(spec.viewName);
95
- if (existing !== undefined && viewSqlEquals(existing, createSql)) {
96
- continue;
97
- }
98
-
99
- views.push({
100
- viewName: spec.viewName,
101
- // prevShape intentionally absent — treated as "safe-append" by
102
- // computeViewMigrations (source-aware-diff.ts line 32). On Postgres
103
- // this rewrites the emitted "CREATE VIEW" to "CREATE OR REPLACE VIEW",
104
- // so re-running migrate is idempotent.
105
- nextShape: {
106
- columns: spec.selectSpec.columns.map((c) => c.dbColAlias),
107
- },
108
- createSql,
109
- });
110
- }
111
-
112
- return computeViewMigrations({
113
- dialect,
114
- allowBreaking: opts.allowBreaking ?? false,
115
- views,
116
- });
117
- }
118
-
119
- /**
120
- * For each projection view, compute the set of source table names that the
121
- * view's SELECT depends on (base entity + all joined entities). Used by the
122
- * CLI to pre-drop only the views whose source tables are being recreated.
123
- */
124
- export function computeProjectionViewDependencies(
125
- opts: Pick<ProjectionMigrationsOpts, "metadata" | "columnNamingStrategy">,
126
- ): ProjectionViewDependencies {
127
- if (!(opts.metadata instanceof MetaRoot)) {
128
- throw new Error("computeProjectionViewDependencies: opts.metadata must be a loaded MetaRoot.");
129
- }
130
- const root = opts.metadata;
131
- const columnNamingStrategy = opts.columnNamingStrategy ?? "snake_case";
132
-
133
- const tableByEntity: Record<string, string> = {};
134
- for (const obj of root.objects()) {
135
- tableByEntity[obj.name] = resolveTableName(obj);
136
- }
137
-
138
- const result = new Map<string, Set<string>>();
139
- const projections = root.objects().filter(isProjection);
140
-
141
- for (const projection of projections) {
142
- const spec = extractViewSpec(projection, root, { columnNamingStrategy });
143
- const tables = new Set<string>();
144
- const baseTable = tableByEntity[spec.joinTree.baseEntity];
145
- if (baseTable) tables.add(baseTable);
146
- const walk = (joins: readonly typeof spec.joinTree.joins[number][]): void => {
147
- for (const j of joins) {
148
- const t = tableByEntity[j.targetEntity];
149
- if (t) tables.add(t);
150
- walk(j.children);
151
- }
152
- };
153
- walk(spec.joinTree.joins);
154
- result.set(spec.viewName, tables);
155
- }
156
-
157
- return result;
158
- }