@astrojs/db 0.3.5 → 0.4.0
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/link/index.js +7 -3
- package/dist/core/cli/commands/push/index.js +3 -3
- package/dist/core/cli/migration-queries.d.ts +4 -4
- package/dist/core/cli/migration-queries.js +73 -73
- package/dist/core/cli/migrations.js +15 -7
- package/dist/core/errors.js +7 -3
- package/dist/core/integration/file-url.js +2 -1
- package/dist/core/integration/index.js +106 -57
- package/dist/core/integration/typegen.d.ts +3 -3
- package/dist/core/integration/typegen.js +8 -8
- package/dist/core/integration/vite-plugin-db.d.ts +14 -9
- package/dist/core/integration/vite-plugin-db.js +15 -12
- package/dist/core/integration/vite-plugin-inject-env-ts.d.ts +4 -2
- package/dist/core/integration/vite-plugin-inject-env-ts.js +8 -4
- package/dist/core/queries.d.ts +12 -12
- package/dist/core/queries.js +49 -46
- package/dist/core/types.d.ts +232 -231
- package/dist/core/types.js +56 -55
- package/dist/core/utils.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/runtime/db-client.d.ts +9 -4
- package/dist/runtime/db-client.js +11 -7
- package/dist/runtime/index.d.ts +7 -7
- package/dist/runtime/index.js +27 -29
- package/dist/runtime/types.d.ts +7 -7
- package/package.json +3 -5
- package/components/Renderer.astro +0 -14
- package/components/astro-env.d.ts +0 -1
- package/components/index.ts +0 -2
- package/components/tsconfig.json +0 -7
- package/dist/cli/commands/push/index.d.ts +0 -6
- package/dist/cli/commands/shell/index.d.ts +0 -6
- package/dist/cli/commands/sync/index.d.ts +0 -6
- package/dist/cli/commands/verify/index.d.ts +0 -6
- package/dist/cli/index.d.ts +0 -6
- package/dist/cli/queries.d.ts +0 -18
- package/dist/cli/seed.d.ts +0 -6
- package/dist/cli/sync/admin.d.ts +0 -33
- package/dist/cli/sync/index.d.ts +0 -1
- package/dist/cli/sync/migrate.d.ts +0 -1
- package/dist/cli/sync/queries.d.ts +0 -19
- package/dist/cli/sync/remote-db.d.ts +0 -1
- package/dist/config.d.ts +0 -1374
- package/dist/consts.d.ts +0 -7
- package/dist/core/cli/commands/sync/index.d.ts +0 -6
- package/dist/error-map.d.ts +0 -6
- package/dist/errors.d.ts +0 -3
- package/dist/file-url-integration.d.ts +0 -2
- package/dist/integration.d.ts +0 -2
- package/dist/internal-drizzle.d.ts +0 -1
- package/dist/internal.d.ts +0 -47
- package/dist/load-astro-config.d.ts +0 -6
- package/dist/migrations.d.ts +0 -12
- package/dist/root.d.ts +0 -3
- package/dist/typegen.d.ts +0 -5
- package/dist/types.d.ts +0 -1604
- package/dist/utils-runtime.d.ts +0 -1
- package/dist/utils.d.ts +0 -59
- package/dist/vite-plugin-db.d.ts +0 -19
- package/dist/vite-plugin-inject-env-ts.d.ts +0 -9
|
@@ -27,12 +27,16 @@ async function cmd({ flags }) {
|
|
|
27
27
|
});
|
|
28
28
|
if (!response.ok) {
|
|
29
29
|
if (response.status === 401) {
|
|
30
|
-
console.error(
|
|
30
|
+
console.error(
|
|
31
|
+
`${bgRed("Unauthorized")}
|
|
31
32
|
|
|
32
33
|
Are you logged in?
|
|
33
|
-
Run ${cyan(
|
|
34
|
+
Run ${cyan(
|
|
35
|
+
"astro db login"
|
|
36
|
+
)} to authenticate and then try linking again.
|
|
34
37
|
|
|
35
|
-
`
|
|
38
|
+
`
|
|
39
|
+
);
|
|
36
40
|
process.exit(1);
|
|
37
41
|
}
|
|
38
42
|
console.error(`Failed to link project: ${response.status} ${response.statusText}`);
|
|
@@ -6,7 +6,7 @@ import { red } from "kleur/colors";
|
|
|
6
6
|
import prompts from "prompts";
|
|
7
7
|
import { recreateTables, seedData } from "../../../queries.js";
|
|
8
8
|
import { getManagedAppTokenOrExit } from "../../../tokens.js";
|
|
9
|
-
import {
|
|
9
|
+
import { tablesSchema } from "../../../types.js";
|
|
10
10
|
import { getRemoteDatabaseUrl } from "../../../utils.js";
|
|
11
11
|
import { getMigrationQueries } from "../../migration-queries.js";
|
|
12
12
|
import {
|
|
@@ -112,9 +112,9 @@ async function pushData({
|
|
|
112
112
|
const libsqlClient = createClient({ url: ":memory:" });
|
|
113
113
|
await recreateTables({
|
|
114
114
|
db: drizzleLibsql(libsqlClient),
|
|
115
|
-
|
|
115
|
+
tables: tablesSchema.parse(config.db.tables ?? {})
|
|
116
116
|
});
|
|
117
|
-
for (const [collectionName, { writable }] of Object.entries(config.db.
|
|
117
|
+
for (const [collectionName, { writable }] of Object.entries(config.db.tables ?? {})) {
|
|
118
118
|
if (!writable) {
|
|
119
119
|
queries.push({
|
|
120
120
|
sql: `DELETE FROM ${sqlite.escapeName(collectionName)}`,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type DBTable, type DBSnapshot } from '../types.js';
|
|
2
2
|
/** Dependency injected for unit testing */
|
|
3
3
|
type AmbiguityResponses = {
|
|
4
4
|
collectionRenames: Record<string, string>;
|
|
5
|
-
|
|
5
|
+
columnRenames: {
|
|
6
6
|
[collectionName: string]: Record<string, string>;
|
|
7
7
|
};
|
|
8
8
|
};
|
|
@@ -16,8 +16,8 @@ export declare function getMigrationQueries({ oldSnapshot, newSnapshot, ambiguit
|
|
|
16
16
|
}>;
|
|
17
17
|
export declare function getCollectionChangeQueries({ collectionName, oldCollection, newCollection, ambiguityResponses, }: {
|
|
18
18
|
collectionName: string;
|
|
19
|
-
oldCollection:
|
|
20
|
-
newCollection:
|
|
19
|
+
oldCollection: DBTable;
|
|
20
|
+
newCollection: DBTable;
|
|
21
21
|
ambiguityResponses?: AmbiguityResponses;
|
|
22
22
|
}): Promise<{
|
|
23
23
|
queries: string[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as color from "kleur/colors";
|
|
2
2
|
import deepDiff from "deep-diff";
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
columnSchema
|
|
5
5
|
} from "../types.js";
|
|
6
6
|
import { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core";
|
|
7
7
|
import { customAlphabet } from "nanoid";
|
|
@@ -68,9 +68,9 @@ async function getCollectionChangeQueries({
|
|
|
68
68
|
}) {
|
|
69
69
|
const queries = [];
|
|
70
70
|
const confirmations = [];
|
|
71
|
-
const updated =
|
|
72
|
-
let added = getAdded(oldCollection.
|
|
73
|
-
let dropped = getDropped(oldCollection.
|
|
71
|
+
const updated = getUpdatedColumns(oldCollection.columns, newCollection.columns);
|
|
72
|
+
let added = getAdded(oldCollection.columns, newCollection.columns);
|
|
73
|
+
let dropped = getDropped(oldCollection.columns, newCollection.columns);
|
|
74
74
|
const hasForeignKeyChanges = Boolean(
|
|
75
75
|
deepDiff(oldCollection.foreignKeys, newCollection.foreignKeys)
|
|
76
76
|
);
|
|
@@ -85,10 +85,10 @@ async function getCollectionChangeQueries({
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
if (!hasForeignKeyChanges && !isEmpty(added) && !isEmpty(dropped)) {
|
|
88
|
-
const resolved = await
|
|
88
|
+
const resolved = await resolveColumnRenames(collectionName, added, dropped, ambiguityResponses);
|
|
89
89
|
added = resolved.added;
|
|
90
90
|
dropped = resolved.dropped;
|
|
91
|
-
queries.push(...
|
|
91
|
+
queries.push(...getColumnRenameQueries(collectionName, resolved.renamed));
|
|
92
92
|
}
|
|
93
93
|
if (!hasForeignKeyChanges && isEmpty(updated) && Object.values(dropped).every(canAlterTableDropColumn) && Object.values(added).every(canAlterTableAddColumn)) {
|
|
94
94
|
queries.push(
|
|
@@ -103,33 +103,33 @@ async function getCollectionChangeQueries({
|
|
|
103
103
|
}
|
|
104
104
|
const dataLossCheck = canRecreateTableWithoutDataLoss(added, updated);
|
|
105
105
|
if (dataLossCheck.dataLoss) {
|
|
106
|
-
const { reason,
|
|
106
|
+
const { reason, columnName } = dataLossCheck;
|
|
107
107
|
const reasonMsgs = {
|
|
108
|
-
"added-required": `New
|
|
109
|
-
collectionName + "." +
|
|
108
|
+
"added-required": `New column ${color.bold(
|
|
109
|
+
collectionName + "." + columnName
|
|
110
110
|
)} is required with no default value.
|
|
111
111
|
This requires deleting existing data in the ${color.bold(
|
|
112
112
|
collectionName
|
|
113
113
|
)} collection.`,
|
|
114
|
-
"added-unique": `New
|
|
115
|
-
collectionName + "." +
|
|
114
|
+
"added-unique": `New column ${color.bold(
|
|
115
|
+
collectionName + "." + columnName
|
|
116
116
|
)} is marked as unique.
|
|
117
117
|
This requires deleting existing data in the ${color.bold(
|
|
118
118
|
collectionName
|
|
119
119
|
)} collection.`,
|
|
120
|
-
"updated-type": `Updated
|
|
121
|
-
collectionName + "." +
|
|
122
|
-
)} cannot convert data to new
|
|
120
|
+
"updated-type": `Updated column ${color.bold(
|
|
121
|
+
collectionName + "." + columnName
|
|
122
|
+
)} cannot convert data to new column data type.
|
|
123
123
|
This requires deleting existing data in the ${color.bold(
|
|
124
124
|
collectionName
|
|
125
125
|
)} collection.`
|
|
126
126
|
};
|
|
127
127
|
confirmations.push(reasonMsgs[reason]);
|
|
128
128
|
}
|
|
129
|
-
const primaryKeyExists = Object.entries(newCollection.
|
|
130
|
-
([,
|
|
129
|
+
const primaryKeyExists = Object.entries(newCollection.columns).find(
|
|
130
|
+
([, column]) => hasPrimaryKey(column)
|
|
131
131
|
);
|
|
132
|
-
const droppedPrimaryKey = Object.entries(dropped).find(([,
|
|
132
|
+
const droppedPrimaryKey = Object.entries(dropped).find(([, column]) => hasPrimaryKey(column));
|
|
133
133
|
const recreateTableQueries = getRecreateTableQueries({
|
|
134
134
|
collectionName,
|
|
135
135
|
newCollection,
|
|
@@ -158,20 +158,20 @@ function getChangeIndexQueries({
|
|
|
158
158
|
queries.push(...getCreateIndexQueries(collectionName, { indexes: added }));
|
|
159
159
|
return queries;
|
|
160
160
|
}
|
|
161
|
-
async function
|
|
161
|
+
async function resolveColumnRenames(collectionName, mightAdd, mightDrop, ambiguityResponses) {
|
|
162
162
|
const added = {};
|
|
163
163
|
const dropped = {};
|
|
164
164
|
const renamed = [];
|
|
165
|
-
for (const [
|
|
166
|
-
let
|
|
167
|
-
if (!
|
|
165
|
+
for (const [columnName, column] of Object.entries(mightAdd)) {
|
|
166
|
+
let oldColumnName = ambiguityResponses ? ambiguityResponses.columnRenames[collectionName]?.[columnName] ?? "__NEW__" : void 0;
|
|
167
|
+
if (!oldColumnName) {
|
|
168
168
|
const res = await prompts(
|
|
169
169
|
{
|
|
170
170
|
type: "select",
|
|
171
|
-
name: "
|
|
172
|
-
message: "New
|
|
171
|
+
name: "columnName",
|
|
172
|
+
message: "New column " + color.blue(color.bold(`${collectionName}.${columnName}`)) + " detected. Was this renamed from an existing column?",
|
|
173
173
|
choices: [
|
|
174
|
-
{ title: "New
|
|
174
|
+
{ title: "New column (not renamed from existing)", value: "__NEW__" },
|
|
175
175
|
...Object.keys(mightDrop).filter((key) => !(key in renamed)).map((key) => ({ title: key, value: key }))
|
|
176
176
|
]
|
|
177
177
|
},
|
|
@@ -181,17 +181,17 @@ async function resolveFieldRenames(collectionName, mightAdd, mightDrop, ambiguit
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
);
|
|
184
|
-
|
|
184
|
+
oldColumnName = res.columnName;
|
|
185
185
|
}
|
|
186
|
-
if (
|
|
187
|
-
added[
|
|
186
|
+
if (oldColumnName === "__NEW__") {
|
|
187
|
+
added[columnName] = column;
|
|
188
188
|
} else {
|
|
189
|
-
renamed.push({ from:
|
|
189
|
+
renamed.push({ from: oldColumnName, to: columnName });
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
-
for (const [
|
|
193
|
-
if (!renamed.find((r) => r.from ===
|
|
194
|
-
dropped[
|
|
192
|
+
for (const [droppedColumnName, droppedColumn] of Object.entries(mightDrop)) {
|
|
193
|
+
if (!renamed.find((r) => r.from === droppedColumnName)) {
|
|
194
|
+
dropped[droppedColumnName] = droppedColumn;
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
return { added, dropped, renamed };
|
|
@@ -250,7 +250,7 @@ function getDroppedCollections(oldCollections, newCollections) {
|
|
|
250
250
|
}
|
|
251
251
|
return dropped;
|
|
252
252
|
}
|
|
253
|
-
function
|
|
253
|
+
function getColumnRenameQueries(unescapedCollectionName, renamed) {
|
|
254
254
|
const queries = [];
|
|
255
255
|
const collectionName = sqlite.escapeName(unescapedCollectionName);
|
|
256
256
|
for (const { from, to } of renamed) {
|
|
@@ -264,18 +264,18 @@ function getFieldRenameQueries(unescapedCollectionName, renamed) {
|
|
|
264
264
|
function getAlterTableQueries(unescapedCollectionName, added, dropped) {
|
|
265
265
|
const queries = [];
|
|
266
266
|
const collectionName = sqlite.escapeName(unescapedCollectionName);
|
|
267
|
-
for (const [
|
|
268
|
-
const
|
|
269
|
-
const type = schemaTypeToSqlType(
|
|
270
|
-
const q = `ALTER TABLE ${collectionName} ADD COLUMN ${
|
|
271
|
-
|
|
272
|
-
|
|
267
|
+
for (const [unescColumnName, column] of Object.entries(added)) {
|
|
268
|
+
const columnName = sqlite.escapeName(unescColumnName);
|
|
269
|
+
const type = schemaTypeToSqlType(column.type);
|
|
270
|
+
const q = `ALTER TABLE ${collectionName} ADD COLUMN ${columnName} ${type}${getModifiers(
|
|
271
|
+
columnName,
|
|
272
|
+
column
|
|
273
273
|
)}`;
|
|
274
274
|
queries.push(q);
|
|
275
275
|
}
|
|
276
|
-
for (const
|
|
277
|
-
const
|
|
278
|
-
const q = `ALTER TABLE ${collectionName} DROP COLUMN ${
|
|
276
|
+
for (const unescColumnName of Object.keys(dropped)) {
|
|
277
|
+
const columnName = sqlite.escapeName(unescColumnName);
|
|
278
|
+
const q = `ALTER TABLE ${collectionName} DROP COLUMN ${columnName}`;
|
|
279
279
|
queries.push(q);
|
|
280
280
|
}
|
|
281
281
|
return queries;
|
|
@@ -296,7 +296,7 @@ function getRecreateTableQueries({
|
|
|
296
296
|
getCreateTableQuery(unescCollectionName, newCollection)
|
|
297
297
|
];
|
|
298
298
|
}
|
|
299
|
-
const newColumns = [...Object.keys(newCollection.
|
|
299
|
+
const newColumns = [...Object.keys(newCollection.columns)];
|
|
300
300
|
if (migrateHiddenPrimaryKey) {
|
|
301
301
|
newColumns.unshift("_id");
|
|
302
302
|
}
|
|
@@ -311,41 +311,41 @@ function getRecreateTableQueries({
|
|
|
311
311
|
function isEmpty(obj) {
|
|
312
312
|
return Object.keys(obj).length === 0;
|
|
313
313
|
}
|
|
314
|
-
function canAlterTableAddColumn(
|
|
315
|
-
if (
|
|
314
|
+
function canAlterTableAddColumn(column) {
|
|
315
|
+
if (column.schema.unique)
|
|
316
316
|
return false;
|
|
317
|
-
if (hasRuntimeDefault(
|
|
317
|
+
if (hasRuntimeDefault(column))
|
|
318
318
|
return false;
|
|
319
|
-
if (!
|
|
319
|
+
if (!column.schema.optional && !hasDefault(column))
|
|
320
320
|
return false;
|
|
321
|
-
if (hasPrimaryKey(
|
|
321
|
+
if (hasPrimaryKey(column))
|
|
322
322
|
return false;
|
|
323
|
-
if (getReferencesConfig(
|
|
323
|
+
if (getReferencesConfig(column))
|
|
324
324
|
return false;
|
|
325
325
|
return true;
|
|
326
326
|
}
|
|
327
|
-
function canAlterTableDropColumn(
|
|
328
|
-
if (
|
|
327
|
+
function canAlterTableDropColumn(column) {
|
|
328
|
+
if (column.schema.unique)
|
|
329
329
|
return false;
|
|
330
|
-
if (hasPrimaryKey(
|
|
330
|
+
if (hasPrimaryKey(column))
|
|
331
331
|
return false;
|
|
332
332
|
return true;
|
|
333
333
|
}
|
|
334
334
|
function canRecreateTableWithoutDataLoss(added, updated) {
|
|
335
|
-
for (const [
|
|
335
|
+
for (const [columnName, a] of Object.entries(added)) {
|
|
336
336
|
if (hasPrimaryKey(a) && a.type !== "number" && !hasDefault(a)) {
|
|
337
|
-
return { dataLoss: true,
|
|
337
|
+
return { dataLoss: true, columnName, reason: "added-required" };
|
|
338
338
|
}
|
|
339
339
|
if (!a.schema.optional && !hasDefault(a)) {
|
|
340
|
-
return { dataLoss: true,
|
|
340
|
+
return { dataLoss: true, columnName, reason: "added-required" };
|
|
341
341
|
}
|
|
342
342
|
if (!a.schema.optional && a.schema.unique) {
|
|
343
|
-
return { dataLoss: true,
|
|
343
|
+
return { dataLoss: true, columnName, reason: "added-unique" };
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
|
-
for (const [
|
|
346
|
+
for (const [columnName, u] of Object.entries(updated)) {
|
|
347
347
|
if (u.old.type !== u.new.type && !canChangeTypeWithoutQuery(u.old, u.new)) {
|
|
348
|
-
return { dataLoss: true,
|
|
348
|
+
return { dataLoss: true, columnName, reason: "updated-type" };
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
351
|
return { dataLoss: false };
|
|
@@ -377,24 +377,24 @@ function getUpdated(oldObj, newObj) {
|
|
|
377
377
|
}
|
|
378
378
|
return updated;
|
|
379
379
|
}
|
|
380
|
-
function
|
|
380
|
+
function getUpdatedColumns(oldColumns, newColumns) {
|
|
381
381
|
const updated = {};
|
|
382
|
-
for (const [key,
|
|
383
|
-
let
|
|
384
|
-
if (!
|
|
382
|
+
for (const [key, newColumn] of Object.entries(newColumns)) {
|
|
383
|
+
let oldColumn = oldColumns[key];
|
|
384
|
+
if (!oldColumn)
|
|
385
385
|
continue;
|
|
386
|
-
if (
|
|
387
|
-
const
|
|
388
|
-
type:
|
|
389
|
-
schema:
|
|
386
|
+
if (oldColumn.type !== newColumn.type && canChangeTypeWithoutQuery(oldColumn, newColumn)) {
|
|
387
|
+
const asNewColumn = columnSchema.safeParse({
|
|
388
|
+
type: newColumn.type,
|
|
389
|
+
schema: oldColumn.schema
|
|
390
390
|
});
|
|
391
|
-
if (
|
|
392
|
-
|
|
391
|
+
if (asNewColumn.success) {
|
|
392
|
+
oldColumn = asNewColumn.data;
|
|
393
393
|
}
|
|
394
394
|
}
|
|
395
|
-
const diff = deepDiff(
|
|
395
|
+
const diff = deepDiff(oldColumn, newColumn);
|
|
396
396
|
if (diff) {
|
|
397
|
-
updated[key] = { old:
|
|
397
|
+
updated[key] = { old: oldColumn, new: newColumn };
|
|
398
398
|
}
|
|
399
399
|
}
|
|
400
400
|
return updated;
|
|
@@ -404,13 +404,13 @@ const typeChangesWithoutQuery = [
|
|
|
404
404
|
{ from: "date", to: "text" },
|
|
405
405
|
{ from: "json", to: "text" }
|
|
406
406
|
];
|
|
407
|
-
function canChangeTypeWithoutQuery(
|
|
407
|
+
function canChangeTypeWithoutQuery(oldColumn, newColumn) {
|
|
408
408
|
return typeChangesWithoutQuery.some(
|
|
409
|
-
({ from, to }) =>
|
|
409
|
+
({ from, to }) => oldColumn.type === from && newColumn.type === to
|
|
410
410
|
);
|
|
411
411
|
}
|
|
412
|
-
function hasRuntimeDefault(
|
|
413
|
-
return !!(
|
|
412
|
+
function hasRuntimeDefault(column) {
|
|
413
|
+
return !!(column.schema.default && isSerializedSQL(column.schema.default));
|
|
414
414
|
}
|
|
415
415
|
export {
|
|
416
416
|
getCollectionChangeQueries,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import deepDiff from "deep-diff";
|
|
2
2
|
import { mkdir, readFile, readdir, writeFile } from "fs/promises";
|
|
3
|
-
import {
|
|
3
|
+
import { tablesSchema } from "../types.js";
|
|
4
4
|
import { cyan, green, yellow } from "kleur/colors";
|
|
5
5
|
const { applyChange, diff: generateDiff } = deepDiff;
|
|
6
6
|
async function getMigrationStatus(config) {
|
|
@@ -31,20 +31,28 @@ async function getMigrationStatus(config) {
|
|
|
31
31
|
currentSnapshot
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
const MIGRATIONS_CREATED = `${green(
|
|
34
|
+
const MIGRATIONS_CREATED = `${green(
|
|
35
|
+
"\u25A0 Migrations initialized!"
|
|
36
|
+
)}
|
|
35
37
|
|
|
36
38
|
To execute your migrations, run
|
|
37
39
|
${cyan("astro db push")}`;
|
|
38
|
-
const MIGRATIONS_UP_TO_DATE = `${green(
|
|
40
|
+
const MIGRATIONS_UP_TO_DATE = `${green(
|
|
41
|
+
"\u25A0 No migrations needed!"
|
|
42
|
+
)}
|
|
39
43
|
|
|
40
44
|
Your database is up to date.
|
|
41
45
|
`;
|
|
42
|
-
const MIGRATIONS_NOT_INITIALIZED = `${yellow(
|
|
46
|
+
const MIGRATIONS_NOT_INITIALIZED = `${yellow(
|
|
47
|
+
"\u25B6 No migrations found!"
|
|
48
|
+
)}
|
|
43
49
|
|
|
44
50
|
To scaffold your migrations folder, run
|
|
45
51
|
${cyan("astro db sync")}
|
|
46
52
|
`;
|
|
47
|
-
const MIGRATION_NEEDED = `${yellow(
|
|
53
|
+
const MIGRATION_NEEDED = `${yellow(
|
|
54
|
+
"\u25B6 Changes detected!"
|
|
55
|
+
)}
|
|
48
56
|
|
|
49
57
|
To create the necessary migration file, run
|
|
50
58
|
${cyan("astro db sync")}
|
|
@@ -98,8 +106,8 @@ async function initializeFromMigrations(allMigrationFiles) {
|
|
|
98
106
|
return prevSnapshot;
|
|
99
107
|
}
|
|
100
108
|
function createCurrentSnapshot(config) {
|
|
101
|
-
const
|
|
102
|
-
const schema = JSON.parse(JSON.stringify(
|
|
109
|
+
const tablesConfig = tablesSchema.parse(config.db?.tables ?? {});
|
|
110
|
+
const schema = JSON.parse(JSON.stringify(tablesConfig));
|
|
103
111
|
return { experimentalVersion: 1, schema };
|
|
104
112
|
}
|
|
105
113
|
function createEmptySnapshot() {
|
package/dist/core/errors.js
CHANGED
|
@@ -10,13 +10,17 @@ const MISSING_PROJECT_ID_ERROR = `${red("\u25B6 Directory not linked.")}
|
|
|
10
10
|
${cyan("astro db link")}
|
|
11
11
|
`;
|
|
12
12
|
const STUDIO_CONFIG_MISSING_WRITABLE_COLLECTIONS_ERROR = (collectionName) => `${red(
|
|
13
|
-
`\u25B6 Writable collection ${bold(collectionName)} requires Astro Studio or the ${yellow(
|
|
13
|
+
`\u25B6 Writable collection ${bold(collectionName)} requires Astro Studio or the ${yellow(
|
|
14
|
+
"unsafeWritable"
|
|
15
|
+
)} option.`
|
|
14
16
|
)}
|
|
15
17
|
|
|
16
18
|
Visit ${cyan("https://astro.build/studio")} to create your account
|
|
17
19
|
and set ${green("studio: true")} in your astro.config.mjs file to enable Studio.
|
|
18
20
|
`;
|
|
19
|
-
const UNSAFE_WRITABLE_WARNING = `${yellow(
|
|
21
|
+
const UNSAFE_WRITABLE_WARNING = `${yellow(
|
|
22
|
+
"unsafeWritable"
|
|
23
|
+
)} option is enabled and you are using writable tables.
|
|
20
24
|
Redeploying your app may result in wiping away your database.
|
|
21
25
|
I hope you know what you are doing.
|
|
22
26
|
`;
|
|
@@ -34,7 +38,7 @@ const MIGRATIONS_NOT_INITIALIZED = `${yellow(
|
|
|
34
38
|
`;
|
|
35
39
|
const SEED_WRITABLE_IN_PROD_ERROR = (collectionName) => {
|
|
36
40
|
return `${red(
|
|
37
|
-
`Writable
|
|
41
|
+
`Writable tables should not be seeded in production with data().`
|
|
38
42
|
)} You can seed ${bold(
|
|
39
43
|
collectionName
|
|
40
44
|
)} in development mode only using the "mode" flag. See the docs for more: https://www.notion.so/astroinc/astrojs-db-README-dcf6fa10de9a4f528be56cee96e8c054?pvs=4#278aed3fc37e4cec80240d1552ff6ac5`;
|
|
@@ -8,7 +8,7 @@ async function copyFile(toDir, fromUrl, toUrl) {
|
|
|
8
8
|
function fileURLIntegration() {
|
|
9
9
|
const fileNames = [];
|
|
10
10
|
function createVitePlugin(command) {
|
|
11
|
-
|
|
11
|
+
let referenceIds = [];
|
|
12
12
|
return {
|
|
13
13
|
name: "@astrojs/db/file-url",
|
|
14
14
|
enforce: "pre",
|
|
@@ -34,6 +34,7 @@ function fileURLIntegration() {
|
|
|
34
34
|
for (const referenceId of referenceIds) {
|
|
35
35
|
fileNames.push(this.getFileName(referenceId));
|
|
36
36
|
}
|
|
37
|
+
referenceIds = [];
|
|
37
38
|
}
|
|
38
39
|
};
|
|
39
40
|
}
|
|
@@ -1,3 +1,43 @@
|
|
|
1
|
+
var __knownSymbol = (name, symbol) => {
|
|
2
|
+
return (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
3
|
+
};
|
|
4
|
+
var __using = (stack, value, async) => {
|
|
5
|
+
if (value != null) {
|
|
6
|
+
if (typeof value !== "object" && typeof value !== "function")
|
|
7
|
+
throw TypeError("Object expected");
|
|
8
|
+
var dispose;
|
|
9
|
+
if (async)
|
|
10
|
+
dispose = value[__knownSymbol("asyncDispose")];
|
|
11
|
+
if (dispose === void 0)
|
|
12
|
+
dispose = value[__knownSymbol("dispose")];
|
|
13
|
+
if (typeof dispose !== "function")
|
|
14
|
+
throw TypeError("Object not disposable");
|
|
15
|
+
stack.push([async, dispose, value]);
|
|
16
|
+
} else if (async) {
|
|
17
|
+
stack.push([async]);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
var __callDispose = (stack, error, hasError) => {
|
|
22
|
+
var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
|
|
23
|
+
return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
|
|
24
|
+
};
|
|
25
|
+
var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
|
|
26
|
+
var next = (it) => {
|
|
27
|
+
while (it = stack.pop()) {
|
|
28
|
+
try {
|
|
29
|
+
var result = it[1] && it[1].call(it[2]);
|
|
30
|
+
if (it[0])
|
|
31
|
+
return Promise.resolve(result).then(next, (e) => (fail(e), next()));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
fail(e);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (hasError)
|
|
37
|
+
throw error;
|
|
38
|
+
};
|
|
39
|
+
return next();
|
|
40
|
+
};
|
|
1
41
|
import { vitePluginDb } from "./vite-plugin-db.js";
|
|
2
42
|
import { vitePluginInjectEnvTs } from "./vite-plugin-inject-env-ts.js";
|
|
3
43
|
import { typegen } from "./typegen.js";
|
|
@@ -7,7 +47,10 @@ import { DB_PATH } from "../consts.js";
|
|
|
7
47
|
import { createLocalDatabaseClient } from "../../runtime/db-client.js";
|
|
8
48
|
import { astroConfigWithDbSchema } from "../types.js";
|
|
9
49
|
import {} from "../utils.js";
|
|
10
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
STUDIO_CONFIG_MISSING_WRITABLE_COLLECTIONS_ERROR,
|
|
52
|
+
UNSAFE_WRITABLE_WARNING
|
|
53
|
+
} from "../errors.js";
|
|
11
54
|
import { errorMap } from "./error-map.js";
|
|
12
55
|
import { dirname } from "path";
|
|
13
56
|
import { fileURLToPath } from "url";
|
|
@@ -18,85 +61,91 @@ import { getManagedAppTokenOrExit } from "../tokens.js";
|
|
|
18
61
|
function astroDBIntegration() {
|
|
19
62
|
let connectedToRemote = false;
|
|
20
63
|
let appToken;
|
|
64
|
+
let schemas = {
|
|
65
|
+
tables() {
|
|
66
|
+
throw new Error("tables not found");
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
let command;
|
|
21
70
|
return {
|
|
22
71
|
name: "astro:db",
|
|
23
72
|
hooks: {
|
|
24
|
-
"astro:config:setup": async ({
|
|
25
|
-
|
|
73
|
+
"astro:config:setup": async ({ updateConfig, config, command: _command, logger }) => {
|
|
74
|
+
command = _command;
|
|
75
|
+
if (_command === "preview")
|
|
26
76
|
return;
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
const studio = configWithDb.db?.studio ?? false;
|
|
30
|
-
const unsafeWritable = Boolean(configWithDb.db?.unsafeWritable);
|
|
31
|
-
const foundWritableCollection = Object.entries(collections).find(([, c]) => c.writable);
|
|
32
|
-
const writableAllowed = studio || unsafeWritable;
|
|
33
|
-
if (!writableAllowed && foundWritableCollection) {
|
|
34
|
-
logger.error(
|
|
35
|
-
STUDIO_CONFIG_MISSING_WRITABLE_COLLECTIONS_ERROR(foundWritableCollection[0])
|
|
36
|
-
);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
} else if (unsafeWritable && foundWritableCollection) {
|
|
39
|
-
logger.warn(UNSAFE_WRITABLE_WARNING);
|
|
40
|
-
}
|
|
41
|
-
let dbPlugin;
|
|
77
|
+
let dbPlugin = void 0;
|
|
78
|
+
const studio = config.db?.studio ?? false;
|
|
42
79
|
if (studio && command === "build" && process.env.ASTRO_DB_TEST_ENV !== "1") {
|
|
43
80
|
appToken = await getManagedAppTokenOrExit();
|
|
44
81
|
connectedToRemote = true;
|
|
45
82
|
dbPlugin = vitePluginDb({
|
|
46
83
|
connectToStudio: true,
|
|
47
|
-
collections,
|
|
48
84
|
appToken: appToken.token,
|
|
85
|
+
schemas,
|
|
49
86
|
root: config.root
|
|
50
87
|
});
|
|
51
88
|
} else {
|
|
52
|
-
const dbUrl = new URL(DB_PATH, config.root);
|
|
53
|
-
if (existsSync(dbUrl)) {
|
|
54
|
-
await rm(dbUrl);
|
|
55
|
-
}
|
|
56
|
-
await mkdir(dirname(fileURLToPath(dbUrl)), { recursive: true });
|
|
57
|
-
await writeFile(dbUrl, "");
|
|
58
|
-
const db = await createLocalDatabaseClient({
|
|
59
|
-
collections,
|
|
60
|
-
dbUrl: dbUrl.toString(),
|
|
61
|
-
seeding: true
|
|
62
|
-
});
|
|
63
|
-
await recreateTables({ db, collections });
|
|
64
|
-
if (configWithDb.db?.data) {
|
|
65
|
-
await seedData({
|
|
66
|
-
db,
|
|
67
|
-
data: configWithDb.db.data,
|
|
68
|
-
logger,
|
|
69
|
-
mode: command === "dev" ? "dev" : "build"
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
logger.debug("Database setup complete.");
|
|
73
89
|
dbPlugin = vitePluginDb({
|
|
74
90
|
connectToStudio: false,
|
|
75
|
-
|
|
91
|
+
schemas,
|
|
76
92
|
root: config.root
|
|
77
93
|
});
|
|
78
94
|
}
|
|
79
95
|
updateConfig({
|
|
80
96
|
vite: {
|
|
81
97
|
assetsInclude: [DB_PATH],
|
|
82
|
-
plugins: [
|
|
83
|
-
dbPlugin,
|
|
84
|
-
vitePluginInjectEnvTs(config),
|
|
85
|
-
{
|
|
86
|
-
name: "my-plugin",
|
|
87
|
-
resolveId(id) {
|
|
88
|
-
if (id.endsWith("?server-path")) {
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
load(id) {
|
|
92
|
-
if (id.endsWith("?server-path")) {
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
]
|
|
98
|
+
plugins: [dbPlugin, vitePluginInjectEnvTs(config, logger)]
|
|
97
99
|
}
|
|
98
100
|
});
|
|
99
|
-
|
|
101
|
+
},
|
|
102
|
+
"astro:config:done": async ({ config, logger }) => {
|
|
103
|
+
const configWithDb = astroConfigWithDbSchema.parse(config, { errorMap });
|
|
104
|
+
const tables = configWithDb.db?.tables ?? {};
|
|
105
|
+
schemas.tables = () => tables;
|
|
106
|
+
const studio = configWithDb.db?.studio ?? false;
|
|
107
|
+
const unsafeWritable = Boolean(configWithDb.db?.unsafeWritable);
|
|
108
|
+
const foundWritableCollection = Object.entries(tables).find(([, c]) => c.writable);
|
|
109
|
+
const writableAllowed = studio || unsafeWritable;
|
|
110
|
+
if (!writableAllowed && foundWritableCollection) {
|
|
111
|
+
logger.error(
|
|
112
|
+
STUDIO_CONFIG_MISSING_WRITABLE_COLLECTIONS_ERROR(foundWritableCollection[0])
|
|
113
|
+
);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
} else if (unsafeWritable && foundWritableCollection) {
|
|
116
|
+
logger.warn(UNSAFE_WRITABLE_WARNING);
|
|
117
|
+
}
|
|
118
|
+
if (!connectedToRemote) {
|
|
119
|
+
var _stack = [];
|
|
120
|
+
try {
|
|
121
|
+
const dbUrl = new URL(DB_PATH, config.root);
|
|
122
|
+
if (existsSync(dbUrl)) {
|
|
123
|
+
await rm(dbUrl);
|
|
124
|
+
}
|
|
125
|
+
await mkdir(dirname(fileURLToPath(dbUrl)), { recursive: true });
|
|
126
|
+
await writeFile(dbUrl, "");
|
|
127
|
+
const db = __using(_stack, await createLocalDatabaseClient({
|
|
128
|
+
tables,
|
|
129
|
+
dbUrl: dbUrl.toString(),
|
|
130
|
+
seeding: true
|
|
131
|
+
}));
|
|
132
|
+
await recreateTables({ db, tables });
|
|
133
|
+
if (configWithDb.db?.data) {
|
|
134
|
+
await seedData({
|
|
135
|
+
db,
|
|
136
|
+
data: configWithDb.db.data,
|
|
137
|
+
logger,
|
|
138
|
+
mode: command === "dev" ? "dev" : "build"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
logger.debug("Database setup complete.");
|
|
142
|
+
} catch (_) {
|
|
143
|
+
var _error = _, _hasError = true;
|
|
144
|
+
} finally {
|
|
145
|
+
__callDispose(_stack, _error, _hasError);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
await typegen({ tables, root: config.root });
|
|
100
149
|
},
|
|
101
150
|
"astro:server:start": async ({ logger }) => {
|
|
102
151
|
setTimeout(() => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function typegen({
|
|
3
|
-
|
|
1
|
+
import type { DBTables } from '../types.js';
|
|
2
|
+
export declare function typegen({ tables, root }: {
|
|
3
|
+
tables: DBTables;
|
|
4
4
|
root: URL;
|
|
5
5
|
}): Promise<void>;
|