@astrojs/db 0.0.0-10646-20240402132948
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/LICENSE +59 -0
- package/README.md +38 -0
- package/dist/_internal/core/integration/error-map.d.ts +6 -0
- package/dist/_internal/core/schemas.d.ts +4034 -0
- package/dist/_internal/core/types.d.ts +59 -0
- package/dist/_internal/core/utils.d.ts +20 -0
- package/dist/_internal/runtime/config.d.ts +154 -0
- package/dist/_internal/runtime/types.d.ts +69 -0
- package/dist/core/cli/commands/execute/index.d.ts +8 -0
- package/dist/core/cli/commands/execute/index.js +66 -0
- package/dist/core/cli/commands/link/index.d.ts +20 -0
- package/dist/core/cli/commands/link/index.js +252 -0
- package/dist/core/cli/commands/login/index.d.ts +8 -0
- package/dist/core/cli/commands/login/index.js +55 -0
- package/dist/core/cli/commands/logout/index.d.ts +1 -0
- package/dist/core/cli/commands/logout/index.js +9 -0
- package/dist/core/cli/commands/push/index.d.ts +8 -0
- package/dist/core/cli/commands/push/index.js +93 -0
- package/dist/core/cli/commands/shell/index.d.ts +8 -0
- package/dist/core/cli/commands/shell/index.js +33 -0
- package/dist/core/cli/commands/verify/index.d.ts +8 -0
- package/dist/core/cli/commands/verify/index.js +46 -0
- package/dist/core/cli/index.d.ts +6 -0
- package/dist/core/cli/index.js +76 -0
- package/dist/core/cli/migration-queries.d.ts +23 -0
- package/dist/core/cli/migration-queries.js +386 -0
- package/dist/core/cli/print-help.d.ts +11 -0
- package/dist/core/cli/print-help.js +55 -0
- package/dist/core/consts.d.ts +8 -0
- package/dist/core/consts.js +21 -0
- package/dist/core/errors.d.ts +10 -0
- package/dist/core/errors.js +56 -0
- package/dist/core/integration/error-map.d.ts +6 -0
- package/dist/core/integration/error-map.js +79 -0
- package/dist/core/integration/file-url.d.ts +2 -0
- package/dist/core/integration/file-url.js +81 -0
- package/dist/core/integration/index.d.ts +2 -0
- package/dist/core/integration/index.js +160 -0
- package/dist/core/integration/typegen.d.ts +7 -0
- package/dist/core/integration/typegen.js +33 -0
- package/dist/core/integration/vite-plugin-db.d.ts +39 -0
- package/dist/core/integration/vite-plugin-db.js +134 -0
- package/dist/core/integration/vite-plugin-inject-env-ts.d.ts +11 -0
- package/dist/core/integration/vite-plugin-inject-env-ts.js +53 -0
- package/dist/core/load-file.d.ts +253 -0
- package/dist/core/load-file.js +170 -0
- package/dist/core/schemas.d.ts +4034 -0
- package/dist/core/schemas.js +186 -0
- package/dist/core/tokens.d.ts +11 -0
- package/dist/core/tokens.js +181 -0
- package/dist/core/types.d.ts +59 -0
- package/dist/core/types.js +0 -0
- package/dist/core/utils.d.ts +20 -0
- package/dist/core/utils.js +32 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +8 -0
- package/dist/runtime/config.js +111 -0
- package/dist/runtime/db-client.d.ts +6 -0
- package/dist/runtime/db-client.js +148 -0
- package/dist/runtime/drizzle.d.ts +1 -0
- package/dist/runtime/drizzle.js +48 -0
- package/dist/runtime/errors.d.ts +5 -0
- package/dist/runtime/errors.js +31 -0
- package/dist/runtime/index.d.ts +26 -0
- package/dist/runtime/index.js +131 -0
- package/dist/runtime/queries.d.ts +71 -0
- package/dist/runtime/queries.js +169 -0
- package/dist/runtime/seed-local.d.ts +10 -0
- package/dist/runtime/seed-local.js +55 -0
- package/dist/runtime/types.d.ts +69 -0
- package/dist/runtime/types.js +8 -0
- package/dist/runtime/utils.d.ts +8 -0
- package/dist/runtime/utils.js +17 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +6 -0
- package/index.d.ts +3 -0
- package/package.json +95 -0
- package/virtual.d.ts +45 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import deepDiff from "deep-diff";
|
|
2
|
+
import { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core";
|
|
3
|
+
import * as color from "kleur/colors";
|
|
4
|
+
import { customAlphabet } from "nanoid";
|
|
5
|
+
import stripAnsi from "strip-ansi";
|
|
6
|
+
import { hasPrimaryKey } from "../../runtime/index.js";
|
|
7
|
+
import {
|
|
8
|
+
getCreateIndexQueries,
|
|
9
|
+
getCreateTableQuery,
|
|
10
|
+
getDropTableIfExistsQuery,
|
|
11
|
+
getModifiers,
|
|
12
|
+
getReferencesConfig,
|
|
13
|
+
hasDefault,
|
|
14
|
+
schemaTypeToSqlType
|
|
15
|
+
} from "../../runtime/queries.js";
|
|
16
|
+
import { isSerializedSQL } from "../../runtime/types.js";
|
|
17
|
+
import { safeFetch } from "../../runtime/utils.js";
|
|
18
|
+
import { MIGRATION_VERSION } from "../consts.js";
|
|
19
|
+
import { RENAME_COLUMN_ERROR, RENAME_TABLE_ERROR } from "../errors.js";
|
|
20
|
+
import { columnSchema } from "../schemas.js";
|
|
21
|
+
import {
|
|
22
|
+
} from "../types.js";
|
|
23
|
+
import { getRemoteDatabaseUrl } from "../utils.js";
|
|
24
|
+
const sqlite = new SQLiteAsyncDialect();
|
|
25
|
+
const genTempTableName = customAlphabet("abcdefghijklmnopqrstuvwxyz", 10);
|
|
26
|
+
async function getMigrationQueries({
|
|
27
|
+
oldSnapshot,
|
|
28
|
+
newSnapshot,
|
|
29
|
+
reset = false
|
|
30
|
+
}) {
|
|
31
|
+
const queries = [];
|
|
32
|
+
const confirmations = [];
|
|
33
|
+
if (reset) {
|
|
34
|
+
const currentSnapshot = oldSnapshot;
|
|
35
|
+
oldSnapshot = createEmptySnapshot();
|
|
36
|
+
queries.push(...getDropTableQueriesForSnapshot(currentSnapshot));
|
|
37
|
+
}
|
|
38
|
+
const addedTables = getAddedTables(oldSnapshot, newSnapshot);
|
|
39
|
+
const droppedTables = getDroppedTables(oldSnapshot, newSnapshot);
|
|
40
|
+
const notDeprecatedDroppedTables = Object.fromEntries(
|
|
41
|
+
Object.entries(droppedTables).filter(([, table]) => !table.deprecated)
|
|
42
|
+
);
|
|
43
|
+
if (!isEmpty(addedTables) && !isEmpty(notDeprecatedDroppedTables)) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
RENAME_TABLE_ERROR(Object.keys(addedTables)[0], Object.keys(notDeprecatedDroppedTables)[0])
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
for (const [tableName, table] of Object.entries(addedTables)) {
|
|
49
|
+
queries.push(getCreateTableQuery(tableName, table));
|
|
50
|
+
queries.push(...getCreateIndexQueries(tableName, table));
|
|
51
|
+
}
|
|
52
|
+
for (const [tableName] of Object.entries(droppedTables)) {
|
|
53
|
+
const dropQuery = `DROP TABLE ${sqlite.escapeName(tableName)}`;
|
|
54
|
+
queries.push(dropQuery);
|
|
55
|
+
}
|
|
56
|
+
for (const [tableName, newTable] of Object.entries(newSnapshot.schema)) {
|
|
57
|
+
const oldTable = oldSnapshot.schema[tableName];
|
|
58
|
+
if (!oldTable)
|
|
59
|
+
continue;
|
|
60
|
+
const addedColumns = getAdded(oldTable.columns, newTable.columns);
|
|
61
|
+
const droppedColumns = getDropped(oldTable.columns, newTable.columns);
|
|
62
|
+
const notDeprecatedDroppedColumns = Object.fromEntries(
|
|
63
|
+
Object.entries(droppedColumns).filter(([, col]) => !col.schema.deprecated)
|
|
64
|
+
);
|
|
65
|
+
if (!isEmpty(addedColumns) && !isEmpty(notDeprecatedDroppedColumns)) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
RENAME_COLUMN_ERROR(
|
|
68
|
+
`${tableName}.${Object.keys(addedColumns)[0]}`,
|
|
69
|
+
`${tableName}.${Object.keys(notDeprecatedDroppedColumns)[0]}`
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const result = await getTableChangeQueries({
|
|
74
|
+
tableName,
|
|
75
|
+
oldTable,
|
|
76
|
+
newTable
|
|
77
|
+
});
|
|
78
|
+
queries.push(...result.queries);
|
|
79
|
+
confirmations.push(...result.confirmations);
|
|
80
|
+
}
|
|
81
|
+
return { queries, confirmations };
|
|
82
|
+
}
|
|
83
|
+
async function getTableChangeQueries({
|
|
84
|
+
tableName,
|
|
85
|
+
oldTable,
|
|
86
|
+
newTable
|
|
87
|
+
}) {
|
|
88
|
+
const queries = [];
|
|
89
|
+
const confirmations = [];
|
|
90
|
+
const updated = getUpdatedColumns(oldTable.columns, newTable.columns);
|
|
91
|
+
const added = getAdded(oldTable.columns, newTable.columns);
|
|
92
|
+
const dropped = getDropped(oldTable.columns, newTable.columns);
|
|
93
|
+
const hasForeignKeyChanges = Boolean(deepDiff(oldTable.foreignKeys, newTable.foreignKeys));
|
|
94
|
+
if (!hasForeignKeyChanges && isEmpty(updated) && isEmpty(added) && isEmpty(dropped)) {
|
|
95
|
+
return {
|
|
96
|
+
queries: getChangeIndexQueries({
|
|
97
|
+
tableName,
|
|
98
|
+
oldIndexes: oldTable.indexes,
|
|
99
|
+
newIndexes: newTable.indexes
|
|
100
|
+
}),
|
|
101
|
+
confirmations
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (!hasForeignKeyChanges && isEmpty(updated) && Object.values(dropped).every(canAlterTableDropColumn) && Object.values(added).every(canAlterTableAddColumn)) {
|
|
105
|
+
queries.push(
|
|
106
|
+
...getAlterTableQueries(tableName, added, dropped),
|
|
107
|
+
...getChangeIndexQueries({
|
|
108
|
+
tableName,
|
|
109
|
+
oldIndexes: oldTable.indexes,
|
|
110
|
+
newIndexes: newTable.indexes
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
return { queries, confirmations };
|
|
114
|
+
}
|
|
115
|
+
const dataLossCheck = canRecreateTableWithoutDataLoss(added, updated);
|
|
116
|
+
if (dataLossCheck.dataLoss) {
|
|
117
|
+
const { reason, columnName } = dataLossCheck;
|
|
118
|
+
const reasonMsgs = {
|
|
119
|
+
"added-required": `You added new required column '${color.bold(
|
|
120
|
+
tableName + "." + columnName
|
|
121
|
+
)}' with no default value.
|
|
122
|
+
This cannot be executed on an existing table.`,
|
|
123
|
+
"updated-type": `Updating existing column ${color.bold(
|
|
124
|
+
tableName + "." + columnName
|
|
125
|
+
)} to a new type that cannot be handled automatically.`
|
|
126
|
+
};
|
|
127
|
+
confirmations.push(reasonMsgs[reason]);
|
|
128
|
+
}
|
|
129
|
+
const primaryKeyExists = Object.entries(newTable.columns).find(
|
|
130
|
+
([, column]) => hasPrimaryKey(column)
|
|
131
|
+
);
|
|
132
|
+
const droppedPrimaryKey = Object.entries(dropped).find(([, column]) => hasPrimaryKey(column));
|
|
133
|
+
const recreateTableQueries = getRecreateTableQueries({
|
|
134
|
+
tableName,
|
|
135
|
+
newTable,
|
|
136
|
+
added,
|
|
137
|
+
hasDataLoss: dataLossCheck.dataLoss,
|
|
138
|
+
migrateHiddenPrimaryKey: !primaryKeyExists && !droppedPrimaryKey
|
|
139
|
+
});
|
|
140
|
+
queries.push(...recreateTableQueries, ...getCreateIndexQueries(tableName, newTable));
|
|
141
|
+
return { queries, confirmations };
|
|
142
|
+
}
|
|
143
|
+
function getChangeIndexQueries({
|
|
144
|
+
tableName,
|
|
145
|
+
oldIndexes = {},
|
|
146
|
+
newIndexes = {}
|
|
147
|
+
}) {
|
|
148
|
+
const added = getAdded(oldIndexes, newIndexes);
|
|
149
|
+
const dropped = getDropped(oldIndexes, newIndexes);
|
|
150
|
+
const updated = getUpdated(oldIndexes, newIndexes);
|
|
151
|
+
Object.assign(dropped, updated);
|
|
152
|
+
Object.assign(added, updated);
|
|
153
|
+
const queries = [];
|
|
154
|
+
for (const indexName of Object.keys(dropped)) {
|
|
155
|
+
const dropQuery = `DROP INDEX ${sqlite.escapeName(indexName)}`;
|
|
156
|
+
queries.push(dropQuery);
|
|
157
|
+
}
|
|
158
|
+
queries.push(...getCreateIndexQueries(tableName, { indexes: added }));
|
|
159
|
+
return queries;
|
|
160
|
+
}
|
|
161
|
+
function getAddedTables(oldTables, newTables) {
|
|
162
|
+
const added = {};
|
|
163
|
+
for (const [key, newTable] of Object.entries(newTables.schema)) {
|
|
164
|
+
if (!(key in oldTables.schema))
|
|
165
|
+
added[key] = newTable;
|
|
166
|
+
}
|
|
167
|
+
return added;
|
|
168
|
+
}
|
|
169
|
+
function getDroppedTables(oldTables, newTables) {
|
|
170
|
+
const dropped = {};
|
|
171
|
+
for (const [key, oldTable] of Object.entries(oldTables.schema)) {
|
|
172
|
+
if (!(key in newTables.schema))
|
|
173
|
+
dropped[key] = oldTable;
|
|
174
|
+
}
|
|
175
|
+
return dropped;
|
|
176
|
+
}
|
|
177
|
+
function getAlterTableQueries(unescTableName, added, dropped) {
|
|
178
|
+
const queries = [];
|
|
179
|
+
const tableName = sqlite.escapeName(unescTableName);
|
|
180
|
+
for (const [unescColumnName, column] of Object.entries(added)) {
|
|
181
|
+
const columnName = sqlite.escapeName(unescColumnName);
|
|
182
|
+
const type = schemaTypeToSqlType(column.type);
|
|
183
|
+
const q = `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${type}${getModifiers(
|
|
184
|
+
columnName,
|
|
185
|
+
column
|
|
186
|
+
)}`;
|
|
187
|
+
queries.push(q);
|
|
188
|
+
}
|
|
189
|
+
for (const unescColumnName of Object.keys(dropped)) {
|
|
190
|
+
const columnName = sqlite.escapeName(unescColumnName);
|
|
191
|
+
const q = `ALTER TABLE ${tableName} DROP COLUMN ${columnName}`;
|
|
192
|
+
queries.push(q);
|
|
193
|
+
}
|
|
194
|
+
return queries;
|
|
195
|
+
}
|
|
196
|
+
function getRecreateTableQueries({
|
|
197
|
+
tableName: unescTableName,
|
|
198
|
+
newTable,
|
|
199
|
+
added,
|
|
200
|
+
hasDataLoss,
|
|
201
|
+
migrateHiddenPrimaryKey
|
|
202
|
+
}) {
|
|
203
|
+
const unescTempName = `${unescTableName}_${genTempTableName()}`;
|
|
204
|
+
const tempName = sqlite.escapeName(unescTempName);
|
|
205
|
+
const tableName = sqlite.escapeName(unescTableName);
|
|
206
|
+
if (hasDataLoss) {
|
|
207
|
+
return [`DROP TABLE ${tableName}`, getCreateTableQuery(unescTableName, newTable)];
|
|
208
|
+
}
|
|
209
|
+
const newColumns = [...Object.keys(newTable.columns)];
|
|
210
|
+
if (migrateHiddenPrimaryKey) {
|
|
211
|
+
newColumns.unshift("_id");
|
|
212
|
+
}
|
|
213
|
+
const escapedColumns = newColumns.filter((i) => !(i in added)).map((c) => sqlite.escapeName(c)).join(", ");
|
|
214
|
+
return [
|
|
215
|
+
getCreateTableQuery(unescTempName, newTable),
|
|
216
|
+
`INSERT INTO ${tempName} (${escapedColumns}) SELECT ${escapedColumns} FROM ${tableName}`,
|
|
217
|
+
`DROP TABLE ${tableName}`,
|
|
218
|
+
`ALTER TABLE ${tempName} RENAME TO ${tableName}`
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
function isEmpty(obj) {
|
|
222
|
+
return Object.keys(obj).length === 0;
|
|
223
|
+
}
|
|
224
|
+
function canAlterTableAddColumn(column) {
|
|
225
|
+
if (column.schema.unique)
|
|
226
|
+
return false;
|
|
227
|
+
if (hasRuntimeDefault(column))
|
|
228
|
+
return false;
|
|
229
|
+
if (!column.schema.optional && !hasDefault(column))
|
|
230
|
+
return false;
|
|
231
|
+
if (hasPrimaryKey(column))
|
|
232
|
+
return false;
|
|
233
|
+
if (getReferencesConfig(column))
|
|
234
|
+
return false;
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
function canAlterTableDropColumn(column) {
|
|
238
|
+
if (column.schema.unique)
|
|
239
|
+
return false;
|
|
240
|
+
if (hasPrimaryKey(column))
|
|
241
|
+
return false;
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
function canRecreateTableWithoutDataLoss(added, updated) {
|
|
245
|
+
for (const [columnName, a] of Object.entries(added)) {
|
|
246
|
+
if (hasPrimaryKey(a) && a.type !== "number" && !hasDefault(a)) {
|
|
247
|
+
return { dataLoss: true, columnName, reason: "added-required" };
|
|
248
|
+
}
|
|
249
|
+
if (!a.schema.optional && !hasDefault(a)) {
|
|
250
|
+
return { dataLoss: true, columnName, reason: "added-required" };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const [columnName, u] of Object.entries(updated)) {
|
|
254
|
+
if (u.old.type !== u.new.type && !canChangeTypeWithoutQuery(u.old, u.new)) {
|
|
255
|
+
return { dataLoss: true, columnName, reason: "updated-type" };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return { dataLoss: false };
|
|
259
|
+
}
|
|
260
|
+
function getAdded(oldObj, newObj) {
|
|
261
|
+
const added = {};
|
|
262
|
+
for (const [key, value] of Object.entries(newObj)) {
|
|
263
|
+
if (!(key in oldObj))
|
|
264
|
+
added[key] = value;
|
|
265
|
+
}
|
|
266
|
+
return added;
|
|
267
|
+
}
|
|
268
|
+
function getDropped(oldObj, newObj) {
|
|
269
|
+
const dropped = {};
|
|
270
|
+
for (const [key, value] of Object.entries(oldObj)) {
|
|
271
|
+
if (!(key in newObj))
|
|
272
|
+
dropped[key] = value;
|
|
273
|
+
}
|
|
274
|
+
return dropped;
|
|
275
|
+
}
|
|
276
|
+
function getUpdated(oldObj, newObj) {
|
|
277
|
+
const updated = {};
|
|
278
|
+
for (const [key, value] of Object.entries(newObj)) {
|
|
279
|
+
const oldValue = oldObj[key];
|
|
280
|
+
if (!oldValue)
|
|
281
|
+
continue;
|
|
282
|
+
if (deepDiff(oldValue, value))
|
|
283
|
+
updated[key] = value;
|
|
284
|
+
}
|
|
285
|
+
return updated;
|
|
286
|
+
}
|
|
287
|
+
function getUpdatedColumns(oldColumns, newColumns) {
|
|
288
|
+
const updated = {};
|
|
289
|
+
for (const [key, newColumn] of Object.entries(newColumns)) {
|
|
290
|
+
let oldColumn = oldColumns[key];
|
|
291
|
+
if (!oldColumn)
|
|
292
|
+
continue;
|
|
293
|
+
if (oldColumn.type !== newColumn.type && canChangeTypeWithoutQuery(oldColumn, newColumn)) {
|
|
294
|
+
const asNewColumn = columnSchema.safeParse({
|
|
295
|
+
type: newColumn.type,
|
|
296
|
+
schema: oldColumn.schema
|
|
297
|
+
});
|
|
298
|
+
if (asNewColumn.success) {
|
|
299
|
+
oldColumn = asNewColumn.data;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const diff = deepDiff(oldColumn, newColumn);
|
|
303
|
+
if (diff) {
|
|
304
|
+
updated[key] = { old: oldColumn, new: newColumn };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return updated;
|
|
308
|
+
}
|
|
309
|
+
const typeChangesWithoutQuery = [
|
|
310
|
+
{ from: "boolean", to: "number" },
|
|
311
|
+
{ from: "date", to: "text" },
|
|
312
|
+
{ from: "json", to: "text" }
|
|
313
|
+
];
|
|
314
|
+
function canChangeTypeWithoutQuery(oldColumn, newColumn) {
|
|
315
|
+
return typeChangesWithoutQuery.some(
|
|
316
|
+
({ from, to }) => oldColumn.type === from && newColumn.type === to
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
function hasRuntimeDefault(column) {
|
|
320
|
+
return !!(column.schema.default && isSerializedSQL(column.schema.default));
|
|
321
|
+
}
|
|
322
|
+
async function getProductionCurrentSnapshot({
|
|
323
|
+
appToken
|
|
324
|
+
}) {
|
|
325
|
+
const url = new URL("/db/schema", getRemoteDatabaseUrl());
|
|
326
|
+
const response = await safeFetch(
|
|
327
|
+
url,
|
|
328
|
+
{
|
|
329
|
+
method: "POST",
|
|
330
|
+
headers: new Headers({
|
|
331
|
+
Authorization: `Bearer ${appToken}`
|
|
332
|
+
})
|
|
333
|
+
},
|
|
334
|
+
async (res) => {
|
|
335
|
+
console.error(`${url.toString()} failed: ${res.status} ${res.statusText}`);
|
|
336
|
+
console.error(await res.text());
|
|
337
|
+
throw new Error(`/db/schema fetch failed: ${res.status} ${res.statusText}`);
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
const result = await response.json();
|
|
341
|
+
if (!result.success) {
|
|
342
|
+
console.error(`${url.toString()} unsuccessful`);
|
|
343
|
+
console.error(await response.text());
|
|
344
|
+
throw new Error(`/db/schema fetch unsuccessful`);
|
|
345
|
+
}
|
|
346
|
+
return result.data;
|
|
347
|
+
}
|
|
348
|
+
function getDropTableQueriesForSnapshot(snapshot) {
|
|
349
|
+
const queries = [];
|
|
350
|
+
for (const tableName of Object.keys(snapshot.schema)) {
|
|
351
|
+
const dropQuery = getDropTableIfExistsQuery(tableName);
|
|
352
|
+
queries.unshift(dropQuery);
|
|
353
|
+
}
|
|
354
|
+
return queries;
|
|
355
|
+
}
|
|
356
|
+
function createCurrentSnapshot({ tables = {} }) {
|
|
357
|
+
const schema = JSON.parse(JSON.stringify(tables));
|
|
358
|
+
return { version: MIGRATION_VERSION, schema };
|
|
359
|
+
}
|
|
360
|
+
function createEmptySnapshot() {
|
|
361
|
+
return { version: MIGRATION_VERSION, schema: {} };
|
|
362
|
+
}
|
|
363
|
+
function formatDataLossMessage(confirmations, isColor = true) {
|
|
364
|
+
const messages = [];
|
|
365
|
+
messages.push(color.red("\u2716 We found some schema changes that cannot be handled automatically:"));
|
|
366
|
+
messages.push(``);
|
|
367
|
+
messages.push(...confirmations.map((m, i) => color.red(` (${i + 1}) `) + m));
|
|
368
|
+
messages.push(``);
|
|
369
|
+
messages.push(`To resolve, revert these changes or update your schema, and re-run the command.`);
|
|
370
|
+
messages.push(
|
|
371
|
+
`You may also run 'astro db push --force-reset' to ignore all warnings and force-push your local database schema to production instead. All data will be lost and the database will be reset.`
|
|
372
|
+
);
|
|
373
|
+
let finalMessage = messages.join("\n");
|
|
374
|
+
if (!isColor) {
|
|
375
|
+
finalMessage = stripAnsi(finalMessage);
|
|
376
|
+
}
|
|
377
|
+
return finalMessage;
|
|
378
|
+
}
|
|
379
|
+
export {
|
|
380
|
+
createCurrentSnapshot,
|
|
381
|
+
createEmptySnapshot,
|
|
382
|
+
formatDataLossMessage,
|
|
383
|
+
getMigrationQueries,
|
|
384
|
+
getProductionCurrentSnapshot,
|
|
385
|
+
getTableChangeQueries
|
|
386
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uses implementation from Astro core
|
|
3
|
+
* @see https://github.com/withastro/astro/blob/main/packages/astro/src/core/messages.ts#L303
|
|
4
|
+
*/
|
|
5
|
+
export declare function printHelp({ commandName, headline, usage, tables, description, }: {
|
|
6
|
+
commandName: string;
|
|
7
|
+
headline?: string;
|
|
8
|
+
usage?: string;
|
|
9
|
+
tables?: Record<string, [command: string, help: string][]>;
|
|
10
|
+
description?: string;
|
|
11
|
+
}): void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { bgGreen, bgWhite, black, bold, dim, green } from "kleur/colors";
|
|
2
|
+
function printHelp({
|
|
3
|
+
commandName,
|
|
4
|
+
headline,
|
|
5
|
+
usage,
|
|
6
|
+
tables,
|
|
7
|
+
description
|
|
8
|
+
}) {
|
|
9
|
+
const linebreak = () => "";
|
|
10
|
+
const title = (label) => ` ${bgWhite(black(` ${label} `))}`;
|
|
11
|
+
const table = (rows, { padding }) => {
|
|
12
|
+
const split = process.stdout.columns < 60;
|
|
13
|
+
let raw = "";
|
|
14
|
+
for (const row of rows) {
|
|
15
|
+
if (split) {
|
|
16
|
+
raw += ` ${row[0]}
|
|
17
|
+
`;
|
|
18
|
+
} else {
|
|
19
|
+
raw += `${`${row[0]}`.padStart(padding)}`;
|
|
20
|
+
}
|
|
21
|
+
raw += " " + dim(row[1]) + "\n";
|
|
22
|
+
}
|
|
23
|
+
return raw.slice(0, -1);
|
|
24
|
+
};
|
|
25
|
+
let message = [];
|
|
26
|
+
if (headline) {
|
|
27
|
+
message.push(
|
|
28
|
+
linebreak(),
|
|
29
|
+
` ${bgGreen(black(` ${commandName} `))} ${green(
|
|
30
|
+
`v${"0.0.0-10646-20240402132948"}`
|
|
31
|
+
)} ${headline}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (usage) {
|
|
35
|
+
message.push(linebreak(), ` ${green(commandName)} ${bold(usage)}`);
|
|
36
|
+
}
|
|
37
|
+
if (tables) {
|
|
38
|
+
let calculateTablePadding2 = function(rows) {
|
|
39
|
+
return rows.reduce((val, [first]) => Math.max(val, first.length), 0) + 2;
|
|
40
|
+
};
|
|
41
|
+
var calculateTablePadding = calculateTablePadding2;
|
|
42
|
+
const tableEntries = Object.entries(tables);
|
|
43
|
+
const padding = Math.max(...tableEntries.map(([, rows]) => calculateTablePadding2(rows)));
|
|
44
|
+
for (const [tableTitle, tableRows] of tableEntries) {
|
|
45
|
+
message.push(linebreak(), title(tableTitle), table(tableRows, { padding }));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (description) {
|
|
49
|
+
message.push(linebreak(), `${description}`);
|
|
50
|
+
}
|
|
51
|
+
console.log(message.join("\n") + "\n");
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
printHelp
|
|
55
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const PACKAGE_NAME: any;
|
|
2
|
+
export declare const RUNTIME_IMPORT: string;
|
|
3
|
+
export declare const RUNTIME_CONFIG_IMPORT: string;
|
|
4
|
+
export declare const DB_TYPES_FILE = "db-types.d.ts";
|
|
5
|
+
export declare const VIRTUAL_MODULE_ID = "astro:db";
|
|
6
|
+
export declare const DB_PATH = ".astro/content.db";
|
|
7
|
+
export declare const CONFIG_FILE_NAMES: string[];
|
|
8
|
+
export declare const MIGRATION_VERSION = "2024-03-12";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
const PACKAGE_NAME = JSON.parse(
|
|
3
|
+
readFileSync(new URL("../../package.json", import.meta.url), "utf8")
|
|
4
|
+
).name;
|
|
5
|
+
const RUNTIME_IMPORT = JSON.stringify(`${PACKAGE_NAME}/runtime`);
|
|
6
|
+
const RUNTIME_CONFIG_IMPORT = JSON.stringify(`${PACKAGE_NAME}/dist/runtime/config.js`);
|
|
7
|
+
const DB_TYPES_FILE = "db-types.d.ts";
|
|
8
|
+
const VIRTUAL_MODULE_ID = "astro:db";
|
|
9
|
+
const DB_PATH = ".astro/content.db";
|
|
10
|
+
const CONFIG_FILE_NAMES = ["config.ts", "config.js", "config.mts", "config.mjs"];
|
|
11
|
+
const MIGRATION_VERSION = "2024-03-12";
|
|
12
|
+
export {
|
|
13
|
+
CONFIG_FILE_NAMES,
|
|
14
|
+
DB_PATH,
|
|
15
|
+
DB_TYPES_FILE,
|
|
16
|
+
MIGRATION_VERSION,
|
|
17
|
+
PACKAGE_NAME,
|
|
18
|
+
RUNTIME_CONFIG_IMPORT,
|
|
19
|
+
RUNTIME_IMPORT,
|
|
20
|
+
VIRTUAL_MODULE_ID
|
|
21
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const MISSING_SESSION_ID_ERROR: string;
|
|
2
|
+
export declare const MISSING_PROJECT_ID_ERROR: string;
|
|
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;
|
|
6
|
+
export declare const FILE_NOT_FOUND_ERROR: (path: string) => string;
|
|
7
|
+
export declare const SHELL_QUERY_MISSING_ERROR: string;
|
|
8
|
+
export declare const EXEC_ERROR: (error: string) => string;
|
|
9
|
+
export declare const EXEC_DEFAULT_EXPORT_ERROR: (fileName: string) => string;
|
|
10
|
+
export declare const INTEGRATION_TABLE_CONFLICT_ERROR: (integrationName: string, tableName: string, isUserConflict: boolean) => string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { bold, cyan, red } from "kleur/colors";
|
|
2
|
+
const MISSING_SESSION_ID_ERROR = `${red("\u25B6 Login required!")}
|
|
3
|
+
|
|
4
|
+
To authenticate with Astro Studio, run
|
|
5
|
+
${cyan("astro db login")}
|
|
6
|
+
`;
|
|
7
|
+
const MISSING_PROJECT_ID_ERROR = `${red("\u25B6 Directory not linked.")}
|
|
8
|
+
|
|
9
|
+
To link this directory to an Astro Studio project, run
|
|
10
|
+
${cyan("astro db link")}
|
|
11
|
+
`;
|
|
12
|
+
const MISSING_EXECUTE_PATH_ERROR = `${red(
|
|
13
|
+
"\u25B6 No file path provided."
|
|
14
|
+
)} Provide a path by running ${cyan("astro db execute <path>")}
|
|
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
|
+
};
|
|
26
|
+
const FILE_NOT_FOUND_ERROR = (path) => `${red("\u25B6 File not found:")} ${bold(path)}
|
|
27
|
+
`;
|
|
28
|
+
const SHELL_QUERY_MISSING_ERROR = `${red(
|
|
29
|
+
"\u25B6 Please provide a query to execute using the --query flag."
|
|
30
|
+
)}
|
|
31
|
+
`;
|
|
32
|
+
const EXEC_ERROR = (error) => {
|
|
33
|
+
return `${red(`Error while executing file:`)}
|
|
34
|
+
|
|
35
|
+
${error}`;
|
|
36
|
+
};
|
|
37
|
+
const EXEC_DEFAULT_EXPORT_ERROR = (fileName) => {
|
|
38
|
+
return EXEC_ERROR(`Missing default function export in ${bold(fileName)}`);
|
|
39
|
+
};
|
|
40
|
+
const INTEGRATION_TABLE_CONFLICT_ERROR = (integrationName, tableName, isUserConflict) => {
|
|
41
|
+
return red("\u25B6 Conflicting table name in integration " + bold(integrationName)) + isUserConflict ? `
|
|
42
|
+
A user-defined table named ${bold(tableName)} already exists` : `
|
|
43
|
+
Another integration already added a table named ${bold(tableName)}`;
|
|
44
|
+
};
|
|
45
|
+
export {
|
|
46
|
+
EXEC_DEFAULT_EXPORT_ERROR,
|
|
47
|
+
EXEC_ERROR,
|
|
48
|
+
FILE_NOT_FOUND_ERROR,
|
|
49
|
+
INTEGRATION_TABLE_CONFLICT_ERROR,
|
|
50
|
+
MISSING_EXECUTE_PATH_ERROR,
|
|
51
|
+
MISSING_PROJECT_ID_ERROR,
|
|
52
|
+
MISSING_SESSION_ID_ERROR,
|
|
53
|
+
RENAME_COLUMN_ERROR,
|
|
54
|
+
RENAME_TABLE_ERROR,
|
|
55
|
+
SHELL_QUERY_MISSING_ERROR
|
|
56
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const errorMap = (baseError, ctx) => {
|
|
2
|
+
const baseErrorPath = flattenErrorPath(baseError.path);
|
|
3
|
+
if (baseError.code === "invalid_union") {
|
|
4
|
+
const typeOrLiteralErrByPath = /* @__PURE__ */ new Map();
|
|
5
|
+
for (const unionError of baseError.unionErrors.flatMap((e) => e.errors)) {
|
|
6
|
+
if (unionError.code === "invalid_type" || unionError.code === "invalid_literal") {
|
|
7
|
+
const flattenedErrorPath = flattenErrorPath(unionError.path);
|
|
8
|
+
const typeOrLiteralErr = typeOrLiteralErrByPath.get(flattenedErrorPath);
|
|
9
|
+
if (typeOrLiteralErr) {
|
|
10
|
+
typeOrLiteralErr.expected.push(unionError.expected);
|
|
11
|
+
} else {
|
|
12
|
+
typeOrLiteralErrByPath.set(flattenedErrorPath, {
|
|
13
|
+
code: unionError.code,
|
|
14
|
+
received: unionError.received,
|
|
15
|
+
expected: [unionError.expected]
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const messages = [
|
|
21
|
+
prefix(
|
|
22
|
+
baseErrorPath,
|
|
23
|
+
typeOrLiteralErrByPath.size ? "Did not match union:" : "Did not match union."
|
|
24
|
+
)
|
|
25
|
+
];
|
|
26
|
+
return {
|
|
27
|
+
message: messages.concat(
|
|
28
|
+
[...typeOrLiteralErrByPath.entries()].filter(([, error]) => error.expected.length === baseError.unionErrors.length).map(
|
|
29
|
+
([key, error]) => (
|
|
30
|
+
// Avoid printing the key again if it's a base error
|
|
31
|
+
key === baseErrorPath ? `> ${getTypeOrLiteralMsg(error)}` : `> ${prefix(key, getTypeOrLiteralMsg(error))}`
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
).join("\n")
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (baseError.code === "invalid_literal" || baseError.code === "invalid_type") {
|
|
38
|
+
return {
|
|
39
|
+
message: prefix(
|
|
40
|
+
baseErrorPath,
|
|
41
|
+
getTypeOrLiteralMsg({
|
|
42
|
+
code: baseError.code,
|
|
43
|
+
received: baseError.received,
|
|
44
|
+
expected: [baseError.expected]
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
};
|
|
48
|
+
} else if (baseError.message) {
|
|
49
|
+
return { message: prefix(baseErrorPath, baseError.message) };
|
|
50
|
+
} else {
|
|
51
|
+
return { message: prefix(baseErrorPath, ctx.defaultError) };
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const getTypeOrLiteralMsg = (error) => {
|
|
55
|
+
if (error.received === "undefined")
|
|
56
|
+
return "Required";
|
|
57
|
+
const expectedDeduped = new Set(error.expected);
|
|
58
|
+
switch (error.code) {
|
|
59
|
+
case "invalid_type":
|
|
60
|
+
return `Expected type \`${unionExpectedVals(expectedDeduped)}\`, received ${JSON.stringify(
|
|
61
|
+
error.received
|
|
62
|
+
)}`;
|
|
63
|
+
case "invalid_literal":
|
|
64
|
+
return `Expected \`${unionExpectedVals(expectedDeduped)}\`, received ${JSON.stringify(
|
|
65
|
+
error.received
|
|
66
|
+
)}`;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const prefix = (key, msg) => key.length ? `**${key}**: ${msg}` : msg;
|
|
70
|
+
const unionExpectedVals = (expectedVals) => [...expectedVals].map((expectedVal, idx) => {
|
|
71
|
+
if (idx === 0)
|
|
72
|
+
return JSON.stringify(expectedVal);
|
|
73
|
+
const sep = " | ";
|
|
74
|
+
return `${sep}${JSON.stringify(expectedVal)}`;
|
|
75
|
+
}).join("");
|
|
76
|
+
const flattenErrorPath = (errorPath) => errorPath.join(".");
|
|
77
|
+
export {
|
|
78
|
+
errorMap
|
|
79
|
+
};
|