@astrojs/db 0.5.0 → 0.6.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/core/cli/commands/push/index.d.ts +1 -1
- package/dist/core/cli/commands/push/index.js +26 -114
- package/dist/core/cli/commands/verify/index.d.ts +1 -1
- package/dist/core/cli/commands/verify/index.js +19 -37
- package/dist/core/cli/index.js +8 -5
- package/dist/core/cli/migration-queries.d.ts +8 -13
- package/dist/core/cli/migration-queries.js +59 -115
- package/dist/core/errors.d.ts +2 -1
- package/dist/core/errors.js +12 -8
- package/dist/core/integration/file-url.js +1 -4
- package/dist/core/integration/typegen.js +3 -1
- package/dist/core/types.d.ts +316 -0
- package/dist/core/types.js +3 -1
- package/dist/core/utils.d.ts +0 -1
- package/dist/core/utils.js +0 -4
- package/dist/runtime/config.d.ts +9 -0
- package/dist/runtime/db-client.js +0 -1
- package/dist/runtime/queries.d.ts +5 -0
- package/dist/runtime/queries.js +4 -0
- package/package.json +3 -4
- package/dist/core/cli/commands/gen/index.d.ts +0 -8
- package/dist/core/cli/commands/gen/index.js +0 -47
- package/dist/core/cli/migrations.d.ts +0 -37
- package/dist/core/cli/migrations.js +0 -135
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AstroConfig } from 'astro';
|
|
2
2
|
import type { Arguments } from 'yargs-parser';
|
|
3
3
|
import { type DBConfig } from '../../../types.js';
|
|
4
|
-
export declare function cmd({
|
|
4
|
+
export declare function cmd({ dbConfig, flags, }: {
|
|
5
5
|
astroConfig: AstroConfig;
|
|
6
6
|
dbConfig: DBConfig;
|
|
7
7
|
flags: Arguments;
|
|
@@ -1,128 +1,61 @@
|
|
|
1
|
-
import { red } from "kleur/colors";
|
|
2
|
-
import prompts from "prompts";
|
|
3
|
-
import { MISSING_SESSION_ID_ERROR } from "../../../errors.js";
|
|
4
1
|
import { getManagedAppTokenOrExit } from "../../../tokens.js";
|
|
5
2
|
import {} from "../../../types.js";
|
|
6
|
-
import {
|
|
7
|
-
import { getMigrationQueries } from "../../migration-queries.js";
|
|
3
|
+
import { getRemoteDatabaseUrl } from "../../../utils.js";
|
|
8
4
|
import {
|
|
9
|
-
|
|
10
|
-
MIGRATIONS_NOT_INITIALIZED,
|
|
11
|
-
MIGRATIONS_UP_TO_DATE,
|
|
12
|
-
MIGRATION_NEEDED,
|
|
5
|
+
createCurrentSnapshot,
|
|
13
6
|
createEmptySnapshot,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
loadMigration
|
|
18
|
-
} from "../../migrations.js";
|
|
7
|
+
getMigrationQueries,
|
|
8
|
+
getProductionCurrentSnapshot
|
|
9
|
+
} from "../../migration-queries.js";
|
|
19
10
|
async function cmd({
|
|
20
|
-
astroConfig,
|
|
21
11
|
dbConfig,
|
|
22
12
|
flags
|
|
23
13
|
}) {
|
|
24
14
|
const isDryRun = flags.dryRun;
|
|
15
|
+
const isForceReset = flags.forceReset;
|
|
25
16
|
const appToken = await getManagedAppTokenOrExit(flags.token);
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const { data } = await prepareMigrateQuery({
|
|
39
|
-
migrations: allLocalMigrations,
|
|
40
|
-
appToken: appToken.token
|
|
41
|
-
});
|
|
42
|
-
missingMigrations = data;
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if (error instanceof Error) {
|
|
45
|
-
if (error.message.startsWith("{")) {
|
|
46
|
-
const { error: { code } = { code: "" } } = JSON.parse(error.message);
|
|
47
|
-
if (code === "TOKEN_UNAUTHORIZED") {
|
|
48
|
-
console.error(MISSING_SESSION_ID_ERROR);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
console.error(error);
|
|
53
|
-
process.exit(1);
|
|
17
|
+
const productionSnapshot = await getProductionCurrentSnapshot({ appToken: appToken.token });
|
|
18
|
+
const currentSnapshot = createCurrentSnapshot(dbConfig);
|
|
19
|
+
const isFromScratch = isForceReset || JSON.stringify(productionSnapshot) === "{}";
|
|
20
|
+
const { queries: migrationQueries } = await getMigrationQueries({
|
|
21
|
+
oldSnapshot: isFromScratch ? createEmptySnapshot() : productionSnapshot,
|
|
22
|
+
newSnapshot: currentSnapshot
|
|
23
|
+
});
|
|
24
|
+
if (migrationQueries.length === 0) {
|
|
25
|
+
console.log("Database schema is up to date.");
|
|
26
|
+
} else {
|
|
27
|
+
console.log(`Database schema is out of date.`);
|
|
54
28
|
}
|
|
55
|
-
if (
|
|
56
|
-
console.log(
|
|
29
|
+
if (isDryRun) {
|
|
30
|
+
console.log("Statements:", JSON.stringify(migrationQueries, void 0, 2));
|
|
57
31
|
} else {
|
|
58
|
-
console.log(`Pushing
|
|
32
|
+
console.log(`Pushing database schema updates...`);
|
|
59
33
|
await pushSchema({
|
|
60
|
-
|
|
61
|
-
migrationsDir,
|
|
34
|
+
statements: migrationQueries,
|
|
62
35
|
appToken: appToken.token,
|
|
63
36
|
isDryRun,
|
|
64
|
-
currentSnapshot
|
|
37
|
+
currentSnapshot
|
|
65
38
|
});
|
|
66
39
|
}
|
|
67
40
|
await appToken.destroy();
|
|
68
41
|
console.info("Push complete!");
|
|
69
42
|
}
|
|
70
43
|
async function pushSchema({
|
|
71
|
-
|
|
72
|
-
migrationsDir,
|
|
44
|
+
statements,
|
|
73
45
|
appToken,
|
|
74
46
|
isDryRun,
|
|
75
47
|
currentSnapshot
|
|
76
48
|
}) {
|
|
77
|
-
const initialSnapshot = migrations.find((m) => m === INITIAL_SNAPSHOT);
|
|
78
|
-
const filteredMigrations = migrations.filter((m) => m !== INITIAL_SNAPSHOT);
|
|
79
|
-
const missingMigrationContents = await Promise.all(
|
|
80
|
-
filteredMigrations.map((m) => loadMigration(m, migrationsDir))
|
|
81
|
-
);
|
|
82
|
-
const initialMigrationBatch = initialSnapshot ? (await getMigrationQueries({
|
|
83
|
-
oldSnapshot: createEmptySnapshot(),
|
|
84
|
-
newSnapshot: await loadInitialSnapshot(migrationsDir)
|
|
85
|
-
})).queries : [];
|
|
86
|
-
const confirmations = missingMigrationContents.reduce((acc, curr) => {
|
|
87
|
-
return [...acc, ...curr.confirm || []];
|
|
88
|
-
}, []);
|
|
89
|
-
if (confirmations.length > 0) {
|
|
90
|
-
const response = await prompts([
|
|
91
|
-
...confirmations.map((message, index) => ({
|
|
92
|
-
type: "confirm",
|
|
93
|
-
name: String(index),
|
|
94
|
-
message: red("Warning: ") + message + "\nContinue?",
|
|
95
|
-
initial: true
|
|
96
|
-
}))
|
|
97
|
-
]);
|
|
98
|
-
if (Object.values(response).length === 0 || Object.values(response).some((value) => value === false)) {
|
|
99
|
-
process.exit(1);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const queries = missingMigrationContents.reduce((acc, curr) => {
|
|
103
|
-
return [...acc, ...curr.db];
|
|
104
|
-
}, initialMigrationBatch);
|
|
105
|
-
await runMigrateQuery({ queries, migrations, snapshot: currentSnapshot, appToken, isDryRun });
|
|
106
|
-
}
|
|
107
|
-
async function runMigrateQuery({
|
|
108
|
-
queries: baseQueries,
|
|
109
|
-
migrations,
|
|
110
|
-
snapshot,
|
|
111
|
-
appToken,
|
|
112
|
-
isDryRun
|
|
113
|
-
}) {
|
|
114
|
-
const queries = ["pragma defer_foreign_keys=true;", ...baseQueries];
|
|
115
49
|
const requestBody = {
|
|
116
|
-
snapshot,
|
|
117
|
-
|
|
118
|
-
sql: queries,
|
|
50
|
+
snapshot: currentSnapshot,
|
|
51
|
+
sql: statements,
|
|
119
52
|
experimentalVersion: 1
|
|
120
53
|
};
|
|
121
54
|
if (isDryRun) {
|
|
122
55
|
console.info("[DRY RUN] Batch query:", JSON.stringify(requestBody, null, 2));
|
|
123
56
|
return new Response(null, { status: 200 });
|
|
124
57
|
}
|
|
125
|
-
const url = new URL("/
|
|
58
|
+
const url = new URL("/db/push", getRemoteDatabaseUrl());
|
|
126
59
|
return await fetch(url, {
|
|
127
60
|
method: "POST",
|
|
128
61
|
headers: new Headers({
|
|
@@ -131,27 +64,6 @@ async function runMigrateQuery({
|
|
|
131
64
|
body: JSON.stringify(requestBody)
|
|
132
65
|
});
|
|
133
66
|
}
|
|
134
|
-
async function prepareMigrateQuery({
|
|
135
|
-
migrations,
|
|
136
|
-
appToken
|
|
137
|
-
}) {
|
|
138
|
-
const url = new URL("/migrations/prepare", getRemoteDatabaseUrl());
|
|
139
|
-
const requestBody = {
|
|
140
|
-
migrations,
|
|
141
|
-
experimentalVersion: 1
|
|
142
|
-
};
|
|
143
|
-
const result = await fetch(url, {
|
|
144
|
-
method: "POST",
|
|
145
|
-
headers: new Headers({
|
|
146
|
-
Authorization: `Bearer ${appToken}`
|
|
147
|
-
}),
|
|
148
|
-
body: JSON.stringify(requestBody)
|
|
149
|
-
});
|
|
150
|
-
if (result.status >= 400) {
|
|
151
|
-
throw new Error(await result.text());
|
|
152
|
-
}
|
|
153
|
-
return await result.json();
|
|
154
|
-
}
|
|
155
67
|
export {
|
|
156
68
|
cmd
|
|
157
69
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AstroConfig } from 'astro';
|
|
2
2
|
import type { Arguments } from 'yargs-parser';
|
|
3
3
|
import type { DBConfig } from '../../../types.js';
|
|
4
|
-
export declare function cmd({
|
|
4
|
+
export declare function cmd({ dbConfig, flags, }: {
|
|
5
5
|
astroConfig: AstroConfig;
|
|
6
6
|
dbConfig: DBConfig;
|
|
7
7
|
flags: Arguments;
|
|
@@ -1,46 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getManagedAppTokenOrExit } from "../../../tokens.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "../../
|
|
3
|
+
createCurrentSnapshot,
|
|
4
|
+
createEmptySnapshot,
|
|
5
|
+
getMigrationQueries,
|
|
6
|
+
getProductionCurrentSnapshot
|
|
7
|
+
} from "../../migration-queries.js";
|
|
8
8
|
async function cmd({
|
|
9
|
-
astroConfig,
|
|
10
9
|
dbConfig,
|
|
11
10
|
flags
|
|
12
11
|
}) {
|
|
13
|
-
const
|
|
14
|
-
const {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
status.newFileContent = JSON.stringify(newFileContent, null, 2);
|
|
26
|
-
}
|
|
27
|
-
console.log(JSON.stringify(status));
|
|
28
|
-
process.exit(state === "up-to-date" ? 0 : 1);
|
|
29
|
-
}
|
|
30
|
-
switch (state) {
|
|
31
|
-
case "no-migrations-found": {
|
|
32
|
-
console.log(MIGRATIONS_NOT_INITIALIZED);
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
case "ahead": {
|
|
36
|
-
console.log(MIGRATION_NEEDED);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
case "up-to-date": {
|
|
40
|
-
console.log(MIGRATIONS_UP_TO_DATE);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
12
|
+
const appToken = await getManagedAppTokenOrExit(flags.token);
|
|
13
|
+
const productionSnapshot = await getProductionCurrentSnapshot({ appToken: appToken.token });
|
|
14
|
+
const currentSnapshot = createCurrentSnapshot(dbConfig);
|
|
15
|
+
const { queries: migrationQueries } = await getMigrationQueries({
|
|
16
|
+
oldSnapshot: JSON.stringify(productionSnapshot) !== "{}" ? productionSnapshot : createEmptySnapshot(),
|
|
17
|
+
newSnapshot: currentSnapshot
|
|
18
|
+
});
|
|
19
|
+
if (migrationQueries.length === 0) {
|
|
20
|
+
console.log(`Database schema is up to date.`);
|
|
21
|
+
} else {
|
|
22
|
+
console.log(`Database schema is out of date.`);
|
|
23
|
+
console.log(`Run 'astro db push' to push up your latest changes.`);
|
|
43
24
|
}
|
|
25
|
+
await appToken.destroy();
|
|
44
26
|
}
|
|
45
27
|
export {
|
|
46
28
|
cmd
|
package/dist/core/cli/index.js
CHANGED
|
@@ -13,10 +13,13 @@ async function cli({
|
|
|
13
13
|
const { cmd } = await import("./commands/shell/index.js");
|
|
14
14
|
return await cmd({ astroConfig, dbConfig, flags });
|
|
15
15
|
}
|
|
16
|
-
case "gen":
|
|
16
|
+
case "gen": {
|
|
17
|
+
console.log('"astro db gen" is no longer needed! Visit the docs for more information.');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
17
20
|
case "sync": {
|
|
18
|
-
|
|
19
|
-
return
|
|
21
|
+
console.log('"astro db sync" is no longer needed! Visit the docs for more information.');
|
|
22
|
+
return;
|
|
20
23
|
}
|
|
21
24
|
case "push": {
|
|
22
25
|
const { cmd } = await import("./commands/push/index.js");
|
|
@@ -65,8 +68,8 @@ astro logout End your authenticated session with Astro Studio
|
|
|
65
68
|
astro link Link this directory to an Astro Studio project
|
|
66
69
|
|
|
67
70
|
astro db gen Creates snapshot based on your schema
|
|
68
|
-
astro db push Pushes
|
|
69
|
-
astro db verify
|
|
71
|
+
astro db push Pushes schema updates to Astro Studio
|
|
72
|
+
astro db verify Tests schema updates /w Astro Studio (good for CI)`;
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
export {
|
|
@@ -1,26 +1,21 @@
|
|
|
1
|
-
import { type DBSnapshot, type DBTable } from '../types.js';
|
|
2
|
-
|
|
3
|
-
type AmbiguityResponses = {
|
|
4
|
-
collectionRenames: Record<string, string>;
|
|
5
|
-
columnRenames: {
|
|
6
|
-
[collectionName: string]: Record<string, string>;
|
|
7
|
-
};
|
|
8
|
-
};
|
|
9
|
-
export declare function getMigrationQueries({ oldSnapshot, newSnapshot, ambiguityResponses, }: {
|
|
1
|
+
import { type DBConfig, type DBSnapshot, type DBTable } from '../types.js';
|
|
2
|
+
export declare function getMigrationQueries({ oldSnapshot, newSnapshot, }: {
|
|
10
3
|
oldSnapshot: DBSnapshot;
|
|
11
4
|
newSnapshot: DBSnapshot;
|
|
12
|
-
ambiguityResponses?: AmbiguityResponses;
|
|
13
5
|
}): Promise<{
|
|
14
6
|
queries: string[];
|
|
15
7
|
confirmations: string[];
|
|
16
8
|
}>;
|
|
17
|
-
export declare function getCollectionChangeQueries({ collectionName, oldCollection, newCollection,
|
|
9
|
+
export declare function getCollectionChangeQueries({ collectionName, oldCollection, newCollection, }: {
|
|
18
10
|
collectionName: string;
|
|
19
11
|
oldCollection: DBTable;
|
|
20
12
|
newCollection: DBTable;
|
|
21
|
-
ambiguityResponses?: AmbiguityResponses;
|
|
22
13
|
}): Promise<{
|
|
23
14
|
queries: string[];
|
|
24
15
|
confirmations: string[];
|
|
25
16
|
}>;
|
|
26
|
-
export {}
|
|
17
|
+
export declare function getProductionCurrentSnapshot({ appToken, }: {
|
|
18
|
+
appToken: string;
|
|
19
|
+
}): Promise<DBSnapshot>;
|
|
20
|
+
export declare function createCurrentSnapshot({ tables }: DBConfig): DBSnapshot;
|
|
21
|
+
export declare function createEmptySnapshot(): DBSnapshot;
|
|
@@ -2,47 +2,49 @@ import deepDiff from "deep-diff";
|
|
|
2
2
|
import { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core";
|
|
3
3
|
import * as color from "kleur/colors";
|
|
4
4
|
import { customAlphabet } from "nanoid";
|
|
5
|
-
import prompts from "prompts";
|
|
6
5
|
import { hasPrimaryKey } from "../../runtime/index.js";
|
|
7
6
|
import {
|
|
8
7
|
getCreateIndexQueries,
|
|
9
8
|
getCreateTableQuery,
|
|
9
|
+
getDropTableIfExistsQuery,
|
|
10
10
|
getModifiers,
|
|
11
11
|
getReferencesConfig,
|
|
12
12
|
hasDefault,
|
|
13
13
|
schemaTypeToSqlType
|
|
14
14
|
} from "../../runtime/queries.js";
|
|
15
15
|
import { isSerializedSQL } from "../../runtime/types.js";
|
|
16
|
+
import { RENAME_COLUMN_ERROR, RENAME_TABLE_ERROR } from "../errors.js";
|
|
16
17
|
import {
|
|
17
18
|
columnSchema
|
|
18
19
|
} from "../types.js";
|
|
20
|
+
import { getRemoteDatabaseUrl } from "../utils.js";
|
|
19
21
|
const sqlite = new SQLiteAsyncDialect();
|
|
20
22
|
const genTempTableName = customAlphabet("abcdefghijklmnopqrstuvwxyz", 10);
|
|
21
23
|
async function getMigrationQueries({
|
|
22
24
|
oldSnapshot,
|
|
23
|
-
newSnapshot
|
|
24
|
-
ambiguityResponses
|
|
25
|
+
newSnapshot
|
|
25
26
|
}) {
|
|
26
27
|
const queries = [];
|
|
27
28
|
const confirmations = [];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
const addedCollections = getAddedCollections(oldSnapshot, newSnapshot);
|
|
30
|
+
const droppedTables = getDroppedCollections(oldSnapshot, newSnapshot);
|
|
31
|
+
const notDeprecatedDroppedTables = Object.fromEntries(
|
|
32
|
+
Object.entries(droppedTables).filter(([, table]) => !table.deprecated)
|
|
33
|
+
);
|
|
34
|
+
if (!isEmpty(addedCollections) && !isEmpty(notDeprecatedDroppedTables)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
RENAME_TABLE_ERROR(
|
|
37
|
+
Object.keys(addedCollections)[0],
|
|
38
|
+
Object.keys(notDeprecatedDroppedTables)[0]
|
|
39
|
+
)
|
|
40
|
+
);
|
|
40
41
|
}
|
|
41
|
-
for (const [collectionName, collection] of Object.entries(
|
|
42
|
+
for (const [collectionName, collection] of Object.entries(addedCollections)) {
|
|
43
|
+
queries.push(getDropTableIfExistsQuery(collectionName));
|
|
42
44
|
queries.push(getCreateTableQuery(collectionName, collection));
|
|
43
45
|
queries.push(...getCreateIndexQueries(collectionName, collection));
|
|
44
46
|
}
|
|
45
|
-
for (const [collectionName] of Object.entries(
|
|
47
|
+
for (const [collectionName] of Object.entries(droppedTables)) {
|
|
46
48
|
const dropQuery = `DROP TABLE ${sqlite.escapeName(collectionName)}`;
|
|
47
49
|
queries.push(dropQuery);
|
|
48
50
|
}
|
|
@@ -50,6 +52,19 @@ async function getMigrationQueries({
|
|
|
50
52
|
const oldCollection = oldSnapshot.schema[collectionName];
|
|
51
53
|
if (!oldCollection)
|
|
52
54
|
continue;
|
|
55
|
+
const addedColumns = getAdded(oldCollection.columns, newCollection.columns);
|
|
56
|
+
const droppedColumns = getDropped(oldCollection.columns, newCollection.columns);
|
|
57
|
+
const notDeprecatedDroppedColumns = Object.fromEntries(
|
|
58
|
+
Object.entries(droppedColumns).filter(([key, col]) => !col.schema.deprecated)
|
|
59
|
+
);
|
|
60
|
+
if (!isEmpty(addedColumns) && !isEmpty(notDeprecatedDroppedColumns)) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
RENAME_COLUMN_ERROR(
|
|
63
|
+
`${collectionName}.${Object.keys(addedColumns)[0]}`,
|
|
64
|
+
`${collectionName}.${Object.keys(notDeprecatedDroppedColumns)[0]}`
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
53
68
|
const result = await getCollectionChangeQueries({
|
|
54
69
|
collectionName,
|
|
55
70
|
oldCollection,
|
|
@@ -63,14 +78,13 @@ async function getMigrationQueries({
|
|
|
63
78
|
async function getCollectionChangeQueries({
|
|
64
79
|
collectionName,
|
|
65
80
|
oldCollection,
|
|
66
|
-
newCollection
|
|
67
|
-
ambiguityResponses
|
|
81
|
+
newCollection
|
|
68
82
|
}) {
|
|
69
83
|
const queries = [];
|
|
70
84
|
const confirmations = [];
|
|
71
85
|
const updated = getUpdatedColumns(oldCollection.columns, newCollection.columns);
|
|
72
|
-
|
|
73
|
-
|
|
86
|
+
const added = getAdded(oldCollection.columns, newCollection.columns);
|
|
87
|
+
const dropped = getDropped(oldCollection.columns, newCollection.columns);
|
|
74
88
|
const hasForeignKeyChanges = Boolean(
|
|
75
89
|
deepDiff(oldCollection.foreignKeys, newCollection.foreignKeys)
|
|
76
90
|
);
|
|
@@ -84,12 +98,6 @@ async function getCollectionChangeQueries({
|
|
|
84
98
|
confirmations
|
|
85
99
|
};
|
|
86
100
|
}
|
|
87
|
-
if (!hasForeignKeyChanges && !isEmpty(added) && !isEmpty(dropped)) {
|
|
88
|
-
const resolved = await resolveColumnRenames(collectionName, added, dropped, ambiguityResponses);
|
|
89
|
-
added = resolved.added;
|
|
90
|
-
dropped = resolved.dropped;
|
|
91
|
-
queries.push(...getColumnRenameQueries(collectionName, resolved.renamed));
|
|
92
|
-
}
|
|
93
101
|
if (!hasForeignKeyChanges && isEmpty(updated) && Object.values(dropped).every(canAlterTableDropColumn) && Object.values(added).every(canAlterTableAddColumn)) {
|
|
94
102
|
queries.push(
|
|
95
103
|
...getAlterTableQueries(collectionName, added, dropped),
|
|
@@ -158,82 +166,6 @@ function getChangeIndexQueries({
|
|
|
158
166
|
queries.push(...getCreateIndexQueries(collectionName, { indexes: added }));
|
|
159
167
|
return queries;
|
|
160
168
|
}
|
|
161
|
-
async function resolveColumnRenames(collectionName, mightAdd, mightDrop, ambiguityResponses) {
|
|
162
|
-
const added = {};
|
|
163
|
-
const dropped = {};
|
|
164
|
-
const renamed = [];
|
|
165
|
-
for (const [columnName, column] of Object.entries(mightAdd)) {
|
|
166
|
-
let oldColumnName = ambiguityResponses ? ambiguityResponses.columnRenames[collectionName]?.[columnName] ?? "__NEW__" : void 0;
|
|
167
|
-
if (!oldColumnName) {
|
|
168
|
-
const res = await prompts(
|
|
169
|
-
{
|
|
170
|
-
type: "select",
|
|
171
|
-
name: "columnName",
|
|
172
|
-
message: "New column " + color.blue(color.bold(`${collectionName}.${columnName}`)) + " detected. Was this renamed from an existing column?",
|
|
173
|
-
choices: [
|
|
174
|
-
{ title: "New column (not renamed from existing)", value: "__NEW__" },
|
|
175
|
-
...Object.keys(mightDrop).filter((key) => !(key in renamed)).map((key) => ({ title: key, value: key }))
|
|
176
|
-
]
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
onCancel: () => {
|
|
180
|
-
process.exit(1);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
);
|
|
184
|
-
oldColumnName = res.columnName;
|
|
185
|
-
}
|
|
186
|
-
if (oldColumnName === "__NEW__") {
|
|
187
|
-
added[columnName] = column;
|
|
188
|
-
} else {
|
|
189
|
-
renamed.push({ from: oldColumnName, to: columnName });
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
for (const [droppedColumnName, droppedColumn] of Object.entries(mightDrop)) {
|
|
193
|
-
if (!renamed.find((r) => r.from === droppedColumnName)) {
|
|
194
|
-
dropped[droppedColumnName] = droppedColumn;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return { added, dropped, renamed };
|
|
198
|
-
}
|
|
199
|
-
async function resolveCollectionRenames(mightAdd, mightDrop, ambiguityResponses) {
|
|
200
|
-
const added = {};
|
|
201
|
-
const dropped = {};
|
|
202
|
-
const renamed = [];
|
|
203
|
-
for (const [collectionName, collection] of Object.entries(mightAdd)) {
|
|
204
|
-
let oldCollectionName = ambiguityResponses ? ambiguityResponses.collectionRenames[collectionName] ?? "__NEW__" : void 0;
|
|
205
|
-
if (!oldCollectionName) {
|
|
206
|
-
const res = await prompts(
|
|
207
|
-
{
|
|
208
|
-
type: "select",
|
|
209
|
-
name: "collectionName",
|
|
210
|
-
message: "New collection " + color.blue(color.bold(collectionName)) + " detected. Was this renamed from an existing collection?",
|
|
211
|
-
choices: [
|
|
212
|
-
{ title: "New collection (not renamed from existing)", value: "__NEW__" },
|
|
213
|
-
...Object.keys(mightDrop).filter((key) => !(key in renamed)).map((key) => ({ title: key, value: key }))
|
|
214
|
-
]
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
onCancel: () => {
|
|
218
|
-
process.exit(1);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
);
|
|
222
|
-
oldCollectionName = res.collectionName;
|
|
223
|
-
}
|
|
224
|
-
if (oldCollectionName === "__NEW__") {
|
|
225
|
-
added[collectionName] = collection;
|
|
226
|
-
} else {
|
|
227
|
-
renamed.push({ from: oldCollectionName, to: collectionName });
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
for (const [droppedCollectionName, droppedCollection] of Object.entries(mightDrop)) {
|
|
231
|
-
if (!renamed.find((r) => r.from === droppedCollectionName)) {
|
|
232
|
-
dropped[droppedCollectionName] = droppedCollection;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return { added, dropped, renamed };
|
|
236
|
-
}
|
|
237
169
|
function getAddedCollections(oldCollections, newCollections) {
|
|
238
170
|
const added = {};
|
|
239
171
|
for (const [key, newCollection] of Object.entries(newCollections.schema)) {
|
|
@@ -250,17 +182,6 @@ function getDroppedCollections(oldCollections, newCollections) {
|
|
|
250
182
|
}
|
|
251
183
|
return dropped;
|
|
252
184
|
}
|
|
253
|
-
function getColumnRenameQueries(unescapedCollectionName, renamed) {
|
|
254
|
-
const queries = [];
|
|
255
|
-
const collectionName = sqlite.escapeName(unescapedCollectionName);
|
|
256
|
-
for (const { from, to } of renamed) {
|
|
257
|
-
const q = `ALTER TABLE ${collectionName} RENAME COLUMN ${sqlite.escapeName(
|
|
258
|
-
from
|
|
259
|
-
)} TO ${sqlite.escapeName(to)}`;
|
|
260
|
-
queries.push(q);
|
|
261
|
-
}
|
|
262
|
-
return queries;
|
|
263
|
-
}
|
|
264
185
|
function getAlterTableQueries(unescapedCollectionName, added, dropped) {
|
|
265
186
|
const queries = [];
|
|
266
187
|
const collectionName = sqlite.escapeName(unescapedCollectionName);
|
|
@@ -412,7 +333,30 @@ function canChangeTypeWithoutQuery(oldColumn, newColumn) {
|
|
|
412
333
|
function hasRuntimeDefault(column) {
|
|
413
334
|
return !!(column.schema.default && isSerializedSQL(column.schema.default));
|
|
414
335
|
}
|
|
336
|
+
async function getProductionCurrentSnapshot({
|
|
337
|
+
appToken
|
|
338
|
+
}) {
|
|
339
|
+
const url = new URL("/db/schema", getRemoteDatabaseUrl());
|
|
340
|
+
const response = await fetch(url, {
|
|
341
|
+
method: "POST",
|
|
342
|
+
headers: new Headers({
|
|
343
|
+
Authorization: `Bearer ${appToken}`
|
|
344
|
+
})
|
|
345
|
+
});
|
|
346
|
+
const result = await response.json();
|
|
347
|
+
return result.data;
|
|
348
|
+
}
|
|
349
|
+
function createCurrentSnapshot({ tables = {} }) {
|
|
350
|
+
const schema = JSON.parse(JSON.stringify(tables));
|
|
351
|
+
return { experimentalVersion: 1, schema };
|
|
352
|
+
}
|
|
353
|
+
function createEmptySnapshot() {
|
|
354
|
+
return { experimentalVersion: 1, schema: {} };
|
|
355
|
+
}
|
|
415
356
|
export {
|
|
357
|
+
createCurrentSnapshot,
|
|
358
|
+
createEmptySnapshot,
|
|
416
359
|
getCollectionChangeQueries,
|
|
417
|
-
getMigrationQueries
|
|
360
|
+
getMigrationQueries,
|
|
361
|
+
getProductionCurrentSnapshot
|
|
418
362
|
};
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export declare const MISSING_SESSION_ID_ERROR: string;
|
|
2
2
|
export declare const MISSING_PROJECT_ID_ERROR: string;
|
|
3
|
-
export declare const MIGRATIONS_NOT_INITIALIZED: string;
|
|
4
3
|
export declare const MISSING_EXECUTE_PATH_ERROR: string;
|
|
4
|
+
export declare const RENAME_TABLE_ERROR: (oldTable: string, newTable: string) => string;
|
|
5
|
+
export declare const RENAME_COLUMN_ERROR: (oldSelector: string, newSelector: string) => string;
|
|
5
6
|
export declare const FILE_NOT_FOUND_ERROR: (path: string) => string;
|
|
6
7
|
export declare const SEED_ERROR: (error: string) => string;
|
|
7
8
|
export declare const REFERENCE_DNE_ERROR: (columnName: string) => string;
|
package/dist/core/errors.js
CHANGED
|
@@ -9,17 +9,20 @@ const MISSING_PROJECT_ID_ERROR = `${red("\u25B6 Directory not linked.")}
|
|
|
9
9
|
To link this directory to an Astro Studio project, run
|
|
10
10
|
${cyan("astro db link")}
|
|
11
11
|
`;
|
|
12
|
-
const MIGRATIONS_NOT_INITIALIZED = `${yellow(
|
|
13
|
-
"\u25B6 No migrations found!"
|
|
14
|
-
)}
|
|
15
|
-
|
|
16
|
-
To scaffold your migrations folder, run
|
|
17
|
-
${cyan("astro db sync")}
|
|
18
|
-
`;
|
|
19
12
|
const MISSING_EXECUTE_PATH_ERROR = `${red(
|
|
20
13
|
"\u25B6 No file path provided."
|
|
21
14
|
)} Provide a path by running ${cyan("astro db execute <path>")}
|
|
22
15
|
`;
|
|
16
|
+
const RENAME_TABLE_ERROR = (oldTable, newTable) => {
|
|
17
|
+
return red("\u25B6 Potential table rename detected: " + oldTable + ", " + newTable) + `
|
|
18
|
+
You cannot add and remove tables in the same schema update batch.
|
|
19
|
+
To resolve, add a 'deprecated: true' flag to '${oldTable}' instead.`;
|
|
20
|
+
};
|
|
21
|
+
const RENAME_COLUMN_ERROR = (oldSelector, newSelector) => {
|
|
22
|
+
return red("\u25B6 Potential column rename detected: " + oldSelector + ", " + newSelector) + `
|
|
23
|
+
You cannot add and remove columns in the same table.
|
|
24
|
+
To resolve, add a 'deprecated: true' flag to '${oldSelector}' instead.`;
|
|
25
|
+
};
|
|
23
26
|
const FILE_NOT_FOUND_ERROR = (path) => `${red("\u25B6 File not found:")} ${bold(path)}
|
|
24
27
|
`;
|
|
25
28
|
const SEED_ERROR = (error) => {
|
|
@@ -52,10 +55,11 @@ export {
|
|
|
52
55
|
FOREIGN_KEY_DNE_ERROR,
|
|
53
56
|
FOREIGN_KEY_REFERENCES_EMPTY_ERROR,
|
|
54
57
|
FOREIGN_KEY_REFERENCES_LENGTH_ERROR,
|
|
55
|
-
MIGRATIONS_NOT_INITIALIZED,
|
|
56
58
|
MISSING_EXECUTE_PATH_ERROR,
|
|
57
59
|
MISSING_PROJECT_ID_ERROR,
|
|
58
60
|
MISSING_SESSION_ID_ERROR,
|
|
59
61
|
REFERENCE_DNE_ERROR,
|
|
62
|
+
RENAME_COLUMN_ERROR,
|
|
63
|
+
RENAME_TABLE_ERROR,
|
|
60
64
|
SEED_ERROR
|
|
61
65
|
};
|
|
@@ -61,10 +61,7 @@ function fileURLIntegration() {
|
|
|
61
61
|
}
|
|
62
62
|
await Promise.all(unlinks);
|
|
63
63
|
const assetDir = new URL(config.build.assets, config.outDir);
|
|
64
|
-
|
|
65
|
-
if (!assetFiles.length) {
|
|
66
|
-
await fs.promises.rmdir(assetDir);
|
|
67
|
-
}
|
|
64
|
+
await fs.promises.rmdir(assetDir).catch(() => []);
|
|
68
65
|
} else {
|
|
69
66
|
const moves = [];
|
|
70
67
|
for (const fileName of fileNames) {
|
|
@@ -18,9 +18,11 @@ ${Object.entries(tables).map(([name, collection]) => generateTableType(name, col
|
|
|
18
18
|
await writeFile(new URL(DB_TYPES_FILE, dotAstroDir), content);
|
|
19
19
|
}
|
|
20
20
|
function generateTableType(name, collection) {
|
|
21
|
+
const sanitizedColumnsList = Object.entries(collection.columns).filter(([key, val]) => !val.schema.deprecated);
|
|
22
|
+
const sanitizedColumns = Object.fromEntries(sanitizedColumnsList);
|
|
21
23
|
let tableType = ` export const ${name}: import(${RUNTIME_IMPORT}).Table<
|
|
22
24
|
${JSON.stringify(name)},
|
|
23
|
-
${JSON.stringify(
|
|
25
|
+
${JSON.stringify(sanitizedColumns)}
|
|
24
26
|
>;`;
|
|
25
27
|
return tableType;
|
|
26
28
|
}
|