@astrojs/db 0.4.1 → 0.6.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.
Files changed (57) hide show
  1. package/dist/core/cli/commands/execute/index.d.ts +8 -0
  2. package/dist/core/cli/commands/execute/index.js +32 -0
  3. package/dist/core/cli/commands/link/index.d.ts +20 -8
  4. package/dist/core/cli/commands/link/index.js +191 -31
  5. package/dist/core/cli/commands/login/index.d.ts +4 -2
  6. package/dist/core/cli/commands/login/index.js +31 -22
  7. package/dist/core/cli/commands/logout/index.d.ts +1 -6
  8. package/dist/core/cli/commands/logout/index.js +1 -1
  9. package/dist/core/cli/commands/push/index.d.ts +4 -2
  10. package/dist/core/cli/commands/push/index.js +30 -170
  11. package/dist/core/cli/commands/shell/index.d.ts +4 -2
  12. package/dist/core/cli/commands/shell/index.js +3 -1
  13. package/dist/core/cli/commands/verify/index.d.ts +4 -2
  14. package/dist/core/cli/commands/verify/index.js +23 -37
  15. package/dist/core/cli/index.d.ts +1 -1
  16. package/dist/core/cli/index.js +26 -17
  17. package/dist/core/cli/migration-queries.d.ts +8 -13
  18. package/dist/core/cli/migration-queries.js +65 -121
  19. package/dist/core/consts.d.ts +2 -0
  20. package/dist/core/consts.js +4 -0
  21. package/dist/core/errors.d.ts +9 -7
  22. package/dist/core/errors.js +44 -43
  23. package/dist/core/integration/file-url.js +2 -5
  24. package/dist/core/integration/index.js +56 -112
  25. package/dist/core/integration/typegen.js +3 -13
  26. package/dist/core/integration/vite-plugin-db.d.ts +9 -5
  27. package/dist/core/integration/vite-plugin-db.js +66 -23
  28. package/dist/core/integration/vite-plugin-inject-env-ts.d.ts +1 -1
  29. package/dist/core/load-file.d.ts +31 -0
  30. package/dist/core/load-file.js +98 -0
  31. package/dist/core/tokens.js +1 -1
  32. package/dist/core/types.d.ts +1148 -5306
  33. package/dist/core/types.js +19 -85
  34. package/dist/core/utils.d.ts +1 -0
  35. package/dist/core/utils.js +4 -0
  36. package/dist/index.d.ts +2 -3
  37. package/dist/index.js +3 -5
  38. package/dist/runtime/config.d.ts +147 -0
  39. package/dist/runtime/config.js +42 -0
  40. package/dist/runtime/db-client.d.ts +2 -9
  41. package/dist/runtime/db-client.js +8 -39
  42. package/dist/runtime/index.d.ts +5 -4
  43. package/dist/runtime/index.js +12 -10
  44. package/dist/{core → runtime}/queries.d.ts +20 -17
  45. package/dist/{core → runtime}/queries.js +67 -91
  46. package/dist/runtime/types.d.ts +3 -3
  47. package/dist/utils.d.ts +1 -0
  48. package/dist/utils.js +4 -0
  49. package/index.d.ts +5 -3
  50. package/package.json +20 -5
  51. package/config-augment.d.ts +0 -4
  52. package/dist/core/cli/commands/gen/index.d.ts +0 -6
  53. package/dist/core/cli/commands/gen/index.js +0 -39
  54. package/dist/core/cli/migrations.d.ts +0 -34
  55. package/dist/core/cli/migrations.js +0 -129
  56. package/dist/core/integration/load-astro-config.d.ts +0 -6
  57. package/dist/core/integration/load-astro-config.js +0 -79
@@ -1,48 +1,50 @@
1
- import * as color from "kleur/colors";
2
1
  import deepDiff from "deep-diff";
3
- import {
4
- columnSchema
5
- } from "../types.js";
6
2
  import { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core";
3
+ import * as color from "kleur/colors";
7
4
  import { customAlphabet } from "nanoid";
8
- import prompts from "prompts";
5
+ import { hasPrimaryKey } from "../../runtime/index.js";
9
6
  import {
10
7
  getCreateIndexQueries,
11
8
  getCreateTableQuery,
9
+ getDropTableIfExistsQuery,
12
10
  getModifiers,
13
11
  getReferencesConfig,
14
12
  hasDefault,
15
13
  schemaTypeToSqlType
16
- } from "../queries.js";
17
- import { hasPrimaryKey } from "../../runtime/index.js";
14
+ } from "../../runtime/queries.js";
18
15
  import { isSerializedSQL } from "../../runtime/types.js";
16
+ import { RENAME_COLUMN_ERROR, RENAME_TABLE_ERROR } from "../errors.js";
17
+ import {
18
+ columnSchema
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
- let added = getAddedCollections(oldSnapshot, newSnapshot);
29
- let dropped = getDroppedCollections(oldSnapshot, newSnapshot);
30
- if (!isEmpty(added) && !isEmpty(dropped)) {
31
- const resolved = await resolveCollectionRenames(added, dropped, ambiguityResponses);
32
- added = resolved.added;
33
- dropped = resolved.dropped;
34
- for (const { from, to } of resolved.renamed) {
35
- const renameQuery = `ALTER TABLE ${sqlite.escapeName(from)} RENAME TO ${sqlite.escapeName(
36
- to
37
- )}`;
38
- queries.push(renameQuery);
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(added)) {
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(dropped)) {
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
- let added = getAdded(oldCollection.columns, newCollection.columns);
73
- let dropped = getDropped(oldCollection.columns, newCollection.columns);
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
  };
@@ -1,6 +1,8 @@
1
1
  export declare const PACKAGE_NAME: any;
2
2
  export declare const RUNTIME_IMPORT: string;
3
3
  export declare const RUNTIME_DRIZZLE_IMPORT: string;
4
+ export declare const RUNTIME_CONFIG_IMPORT: string;
4
5
  export declare const DB_TYPES_FILE = "db-types.d.ts";
5
6
  export declare const VIRTUAL_MODULE_ID = "astro:db";
6
7
  export declare const DB_PATH = ".astro/content.db";
8
+ export declare const CONFIG_FILE_NAMES: string[];
@@ -4,13 +4,17 @@ const PACKAGE_NAME = JSON.parse(
4
4
  ).name;
5
5
  const RUNTIME_IMPORT = JSON.stringify(`${PACKAGE_NAME}/runtime`);
6
6
  const RUNTIME_DRIZZLE_IMPORT = JSON.stringify(`${PACKAGE_NAME}/runtime/drizzle`);
7
+ const RUNTIME_CONFIG_IMPORT = JSON.stringify(`${PACKAGE_NAME}/runtime/config`);
7
8
  const DB_TYPES_FILE = "db-types.d.ts";
8
9
  const VIRTUAL_MODULE_ID = "astro:db";
9
10
  const DB_PATH = ".astro/content.db";
11
+ const CONFIG_FILE_NAMES = ["config.ts", "config.js", "config.mts", "config.mjs"];
10
12
  export {
13
+ CONFIG_FILE_NAMES,
11
14
  DB_PATH,
12
15
  DB_TYPES_FILE,
13
16
  PACKAGE_NAME,
17
+ RUNTIME_CONFIG_IMPORT,
14
18
  RUNTIME_DRIZZLE_IMPORT,
15
19
  RUNTIME_IMPORT,
16
20
  VIRTUAL_MODULE_ID
@@ -1,9 +1,11 @@
1
1
  export declare const MISSING_SESSION_ID_ERROR: string;
2
2
  export declare const MISSING_PROJECT_ID_ERROR: string;
3
- export declare const STUDIO_CONFIG_MISSING_WRITABLE_TABLE_ERROR: (tableName: string) => string;
4
- export declare const UNSAFE_WRITABLE_WARNING: string;
5
- export declare const STUDIO_CONFIG_MISSING_CLI_ERROR: string;
6
- export declare const MIGRATIONS_NOT_INITIALIZED: string;
7
- export declare const SEED_WRITABLE_IN_PROD_ERROR: (tableName: string) => string;
8
- export declare const SEED_ERROR: (tableName: string, error: string) => string;
9
- export declare const SEED_EMPTY_ARRAY_ERROR: (tableName: string) => 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 SEED_ERROR: (error: string) => string;
8
+ export declare const REFERENCE_DNE_ERROR: (columnName: string) => string;
9
+ export declare const FOREIGN_KEY_DNE_ERROR: (tableName: string) => string;
10
+ export declare const FOREIGN_KEY_REFERENCES_LENGTH_ERROR: (tableName: string) => string;
11
+ export declare const FOREIGN_KEY_REFERENCES_EMPTY_ERROR: (tableName: string) => string;
@@ -1,4 +1,4 @@
1
- import { cyan, bold, red, green, yellow } from "kleur/colors";
1
+ import { bold, cyan, green, red, yellow } from "kleur/colors";
2
2
  const MISSING_SESSION_ID_ERROR = `${red("\u25B6 Login required!")}
3
3
 
4
4
  To authenticate with Astro Studio, run
@@ -9,56 +9,57 @@ 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 STUDIO_CONFIG_MISSING_WRITABLE_TABLE_ERROR = (tableName) => `${red(
13
- `\u25B6 Writable table ${bold(tableName)} requires Astro Studio or the ${yellow(
14
- "unsafeWritable"
15
- )} option.`
16
- )}
17
-
18
- Visit ${cyan("https://astro.build/studio")} to create your account
19
- and set ${green("studio: true")} in your astro.config.mjs file to enable Studio.
20
- `;
21
- const UNSAFE_WRITABLE_WARNING = `${yellow(
22
- "unsafeWritable"
23
- )} option is enabled and you are using writable tables.
24
- Redeploying your app may result in wiping away your database.
25
- I hope you know what you are doing.
12
+ const MISSING_EXECUTE_PATH_ERROR = `${red(
13
+ "\u25B6 No file path provided."
14
+ )} Provide a path by running ${cyan("astro db execute <path>")}
26
15
  `;
27
- const STUDIO_CONFIG_MISSING_CLI_ERROR = `${red("\u25B6 This command requires Astro Studio.")}
28
-
29
- Visit ${cyan("https://astro.build/studio")} to create your account
30
- and set ${green("studio: true")} in your astro.config.mjs file to enable Studio.
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)}
31
27
  `;
32
- const MIGRATIONS_NOT_INITIALIZED = `${yellow(
33
- "\u25B6 No migrations found!"
34
- )}
28
+ const SEED_ERROR = (error) => {
29
+ return `${red(`Error while seeding database:`)}
35
30
 
36
- To scaffold your migrations folder, run
37
- ${cyan("astro db sync")}
38
- `;
39
- const SEED_WRITABLE_IN_PROD_ERROR = (tableName) => {
40
- return `${red(
41
- `Writable tables should not be seeded in production with data().`
42
- )} You can seed ${bold(
31
+ ${error}`;
32
+ };
33
+ const REFERENCE_DNE_ERROR = (columnName) => {
34
+ return `Column ${bold(
35
+ columnName
36
+ )} references a table that does not exist. Did you apply the referenced table to the \`tables\` object in your db config?`;
37
+ };
38
+ const FOREIGN_KEY_DNE_ERROR = (tableName) => {
39
+ return `Table ${bold(
43
40
  tableName
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`;
41
+ )} references a table that does not exist. Did you apply the referenced table to the \`tables\` object in your db config?`;
45
42
  };
46
- const SEED_ERROR = (tableName, error) => {
47
- return `${red(`Error seeding table ${bold(tableName)}:`)}
48
-
49
- ${error}`;
43
+ const FOREIGN_KEY_REFERENCES_LENGTH_ERROR = (tableName) => {
44
+ return `Foreign key on ${bold(
45
+ tableName
46
+ )} is misconfigured. \`columns\` and \`references\` must be the same length.`;
50
47
  };
51
- const SEED_EMPTY_ARRAY_ERROR = (tableName) => {
52
- return SEED_ERROR(tableName, `Empty array was passed. seed() must receive at least one value.`);
48
+ const FOREIGN_KEY_REFERENCES_EMPTY_ERROR = (tableName) => {
49
+ return `Foreign key on ${bold(
50
+ tableName
51
+ )} is misconfigured. \`references\` array cannot be empty.`;
53
52
  };
54
53
  export {
55
- MIGRATIONS_NOT_INITIALIZED,
54
+ FILE_NOT_FOUND_ERROR,
55
+ FOREIGN_KEY_DNE_ERROR,
56
+ FOREIGN_KEY_REFERENCES_EMPTY_ERROR,
57
+ FOREIGN_KEY_REFERENCES_LENGTH_ERROR,
58
+ MISSING_EXECUTE_PATH_ERROR,
56
59
  MISSING_PROJECT_ID_ERROR,
57
60
  MISSING_SESSION_ID_ERROR,
58
- SEED_EMPTY_ARRAY_ERROR,
59
- SEED_ERROR,
60
- SEED_WRITABLE_IN_PROD_ERROR,
61
- STUDIO_CONFIG_MISSING_CLI_ERROR,
62
- STUDIO_CONFIG_MISSING_WRITABLE_TABLE_ERROR,
63
- UNSAFE_WRITABLE_WARNING
61
+ REFERENCE_DNE_ERROR,
62
+ RENAME_COLUMN_ERROR,
63
+ RENAME_TABLE_ERROR,
64
+ SEED_ERROR
64
65
  };
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
- import { pathToFileURL } from "node:url";
3
2
  import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
4
  async function copyFile(toDir, fromUrl, toUrl) {
5
5
  await fs.promises.mkdir(toDir, { recursive: true });
6
6
  await fs.promises.rename(fromUrl, toUrl);
@@ -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
- const assetFiles = await fs.promises.readdir(assetDir);
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) {
@@ -1,66 +1,27 @@
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
- };
41
- import { vitePluginDb } from "./vite-plugin-db.js";
42
- import { vitePluginInjectEnvTs } from "./vite-plugin-inject-env-ts.js";
43
- import { typegen } from "./typegen.js";
44
1
  import { existsSync } from "fs";
45
- import { mkdir, rm, writeFile } from "fs/promises";
46
- import { DB_PATH } from "../consts.js";
47
- import { createLocalDatabaseClient } from "../../runtime/db-client.js";
48
- import { astroConfigWithDbSchema } from "../types.js";
49
- import {} from "../utils.js";
50
- import { STUDIO_CONFIG_MISSING_WRITABLE_TABLE_ERROR, UNSAFE_WRITABLE_WARNING } from "../errors.js";
51
- import { errorMap } from "./error-map.js";
52
2
  import { dirname } from "path";
53
3
  import { fileURLToPath } from "url";
4
+ import { mkdir, rm, writeFile } from "fs/promises";
54
5
  import { blue, yellow } from "kleur/colors";
55
- import { fileURLIntegration } from "./file-url.js";
56
- import { recreateTables, seedData } from "../queries.js";
6
+ import { CONFIG_FILE_NAMES, DB_PATH } from "../consts.js";
7
+ import { loadDbConfigFile } from "../load-file.js";
57
8
  import { getManagedAppTokenOrExit } from "../tokens.js";
9
+ import { dbConfigSchema } from "../types.js";
10
+ import { getDbDirectoryUrl } from "../utils.js";
11
+ import { errorMap } from "./error-map.js";
12
+ import { fileURLIntegration } from "./file-url.js";
13
+ import { typegen } from "./typegen.js";
14
+ import { vitePluginDb } from "./vite-plugin-db.js";
15
+ import { vitePluginInjectEnvTs } from "./vite-plugin-inject-env-ts.js";
58
16
  function astroDBIntegration() {
59
- let connectedToRemote = false;
17
+ let connectToStudio = false;
18
+ let configFileDependencies = [];
19
+ let root;
60
20
  let appToken;
61
- let schemas = {
62
- tables() {
63
- throw new Error("tables not found");
21
+ let dbConfig;
22
+ let tables = {
23
+ get() {
24
+ throw new Error("[astro:db] INTERNAL Tables not loaded yet");
64
25
  }
65
26
  };
66
27
  let command;
@@ -69,24 +30,26 @@ function astroDBIntegration() {
69
30
  hooks: {
70
31
  "astro:config:setup": async ({ updateConfig, config, command: _command, logger }) => {
71
32
  command = _command;
72
- if (_command === "preview")
33
+ root = config.root;
34
+ if (command === "preview")
73
35
  return;
74
36
  let dbPlugin = void 0;
75
- const studio = config.db?.studio ?? false;
76
- if (studio && command === "build" && process.env.ASTRO_DB_TEST_ENV !== "1") {
37
+ connectToStudio = command === "build";
38
+ if (connectToStudio) {
77
39
  appToken = await getManagedAppTokenOrExit();
78
- connectedToRemote = true;
79
40
  dbPlugin = vitePluginDb({
80
- connectToStudio: true,
41
+ connectToStudio,
81
42
  appToken: appToken.token,
82
- schemas,
83
- root: config.root
43
+ tables,
44
+ root: config.root,
45
+ srcDir: config.srcDir
84
46
  });
85
47
  } else {
86
48
  dbPlugin = vitePluginDb({
87
49
  connectToStudio: false,
88
- schemas,
89
- root: config.root
50
+ tables,
51
+ root: config.root,
52
+ srcDir: config.srcDir
90
53
  });
91
54
  }
92
55
  updateConfig({
@@ -96,63 +59,44 @@ function astroDBIntegration() {
96
59
  }
97
60
  });
98
61
  },
99
- "astro:config:done": async ({ config, logger }) => {
100
- const configWithDb = astroConfigWithDbSchema.parse(config, { errorMap });
101
- const tables = configWithDb.db?.tables ?? {};
102
- schemas.tables = () => tables;
103
- const studio = configWithDb.db?.studio ?? false;
104
- const unsafeWritable = Boolean(configWithDb.db?.unsafeWritable);
105
- const foundWritableCollection = Object.entries(tables).find(([, c]) => c.writable);
106
- const writableAllowed = studio || unsafeWritable;
107
- if (!writableAllowed && foundWritableCollection) {
108
- logger.error(STUDIO_CONFIG_MISSING_WRITABLE_TABLE_ERROR(foundWritableCollection[0]));
109
- process.exit(1);
110
- } else if (unsafeWritable && foundWritableCollection) {
111
- logger.warn(UNSAFE_WRITABLE_WARNING);
112
- }
113
- if (!connectedToRemote) {
114
- var _stack = [];
115
- try {
116
- const dbUrl = new URL(DB_PATH, config.root);
117
- if (existsSync(dbUrl)) {
118
- await rm(dbUrl);
119
- }
120
- await mkdir(dirname(fileURLToPath(dbUrl)), { recursive: true });
121
- await writeFile(dbUrl, "");
122
- const db = __using(_stack, await createLocalDatabaseClient({
123
- tables,
124
- dbUrl: dbUrl.toString(),
125
- seeding: true
126
- }));
127
- await recreateTables({ db, tables });
128
- if (configWithDb.db?.data) {
129
- await seedData({
130
- db,
131
- data: configWithDb.db.data,
132
- logger,
133
- mode: command === "dev" ? "dev" : "build"
134
- });
135
- }
136
- logger.debug("Database setup complete.");
137
- } catch (_) {
138
- var _error = _, _hasError = true;
139
- } finally {
140
- __callDispose(_stack, _error, _hasError);
62
+ "astro:config:done": async ({ config }) => {
63
+ const { mod, dependencies } = await loadDbConfigFile(config.root);
64
+ configFileDependencies = dependencies;
65
+ dbConfig = dbConfigSchema.parse(mod?.default ?? {}, {
66
+ errorMap
67
+ });
68
+ tables.get = () => dbConfig.tables ?? {};
69
+ if (!connectToStudio && !process.env.TEST_IN_MEMORY_DB) {
70
+ const dbUrl = new URL(DB_PATH, config.root);
71
+ if (existsSync(dbUrl)) {
72
+ await rm(dbUrl);
141
73
  }
74
+ await mkdir(dirname(fileURLToPath(dbUrl)), { recursive: true });
75
+ await writeFile(dbUrl, "");
142
76
  }
143
- await typegen({ tables, root: config.root });
77
+ await typegen({ tables: tables.get() ?? {}, root: config.root });
144
78
  },
145
79
  "astro:server:start": async ({ logger }) => {
146
80
  setTimeout(() => {
147
81
  logger.info(
148
- connectedToRemote ? "Connected to remote database." : "New local database created."
82
+ connectToStudio ? "Connected to remote database." : "New local database created."
149
83
  );
150
84
  }, 100);
151
85
  },
86
+ "astro:server:setup": async ({ server }) => {
87
+ const filesToWatch = [
88
+ ...CONFIG_FILE_NAMES.map((c) => new URL(c, getDbDirectoryUrl(root))),
89
+ ...configFileDependencies.map((c) => new URL(c, root))
90
+ ];
91
+ server.watcher.on("all", (event, relativeEntry) => {
92
+ const entry = new URL(relativeEntry, root);
93
+ if (filesToWatch.some((f) => entry.href === f.href)) {
94
+ server.restart();
95
+ }
96
+ });
97
+ },
152
98
  "astro:build:start": async ({ logger }) => {
153
- logger.info(
154
- "database: " + (connectedToRemote ? yellow("remote") : blue("local database."))
155
- );
99
+ logger.info("database: " + (connectToStudio ? yellow("remote") : blue("local database.")));
156
100
  },
157
101
  "astro:build:done": async ({}) => {
158
102
  await appToken?.destroy();
@@ -18,21 +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(
24
- Object.fromEntries(
25
- Object.entries(collection.columns).map(([columnName, column]) => [
26
- columnName,
27
- {
28
- // Only select columns Drizzle needs for inference
29
- type: column.type,
30
- optional: column.schema.optional,
31
- default: column.schema.default
32
- }
33
- ])
34
- )
35
- )}
25
+ ${JSON.stringify(sanitizedColumns)}
36
26
  >;`;
37
27
  return tableType;
38
28
  }