@astrojs/db 0.6.1 → 0.6.2

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/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # @astrojs/db (experimental) 💿
2
+
3
+ This **[Astro integration][astro-integration]** enables the usage of [SQLite](https://www.sqlite.org/) in Astro Projects.
4
+
5
+ ## Documentation
6
+
7
+ Read the [`@astrojs/db` docs][docs]
8
+
9
+ ## Support
10
+
11
+ - Get help in the [Astro Discord][discord]. Post questions in our `#support` forum, or visit our dedicated `#dev` channel to discuss current development and more!
12
+
13
+ - Check our [Astro Integration Documentation][astro-integration] for more on integrations.
14
+
15
+ - Submit bug reports and feature requests as [GitHub issues][issues].
16
+
17
+ ## Contributing
18
+
19
+ This package is maintained by Astro's Core team. You're welcome to submit an issue or PR! These links will help you get started:
20
+
21
+ - [Contributor Manual][contributing]
22
+ - [Code of Conduct][coc]
23
+ - [Community Guide][community]
24
+
25
+ ## License
26
+
27
+ MIT
28
+
29
+ Copyright (c) 2023–present [Astro][astro]
30
+
31
+ [astro]: https://astro.build/
32
+ [docs]: https://docs.astro.build/en/guides/integrations-guide/db/
33
+ [contributing]: https://github.com/withastro/astro/blob/main/CONTRIBUTING.md
34
+ [coc]: https://github.com/withastro/.github/blob/main/CODE_OF_CONDUCT.md
35
+ [community]: https://github.com/withastro/.github/blob/main/COMMUNITY_GUIDE.md
36
+ [discord]: https://astro.build/chat/
37
+ [issues]: https://github.com/withastro/astro/issues
38
+ [astro-integration]: https://docs.astro.build/en/guides/integrations-guide/
@@ -1,9 +1,11 @@
1
+ import { red } from "kleur/colors";
1
2
  import { getManagedAppTokenOrExit } from "../../../tokens.js";
2
3
  import {} from "../../../types.js";
3
4
  import { getRemoteDatabaseUrl } from "../../../utils.js";
4
5
  import {
5
6
  createCurrentSnapshot,
6
7
  createEmptySnapshot,
8
+ formatDataLossMessage,
7
9
  getMigrationQueries,
8
10
  getProductionCurrentSnapshot
9
11
  } from "../../migration-queries.js";
@@ -17,7 +19,7 @@ async function cmd({
17
19
  const productionSnapshot = await getProductionCurrentSnapshot({ appToken: appToken.token });
18
20
  const currentSnapshot = createCurrentSnapshot(dbConfig);
19
21
  const isFromScratch = isForceReset || JSON.stringify(productionSnapshot) === "{}";
20
- const { queries: migrationQueries } = await getMigrationQueries({
22
+ const { queries: migrationQueries, confirmations } = await getMigrationQueries({
21
23
  oldSnapshot: isFromScratch ? createEmptySnapshot() : productionSnapshot,
22
24
  newSnapshot: currentSnapshot
23
25
  });
@@ -26,6 +28,12 @@ async function cmd({
26
28
  } else {
27
29
  console.log(`Database schema is out of date.`);
28
30
  }
31
+ if (isForceReset) {
32
+ console.log(`Force-pushing to the database. All existing data will be erased.`);
33
+ } else if (confirmations.length > 0) {
34
+ console.log("\n" + formatDataLossMessage(confirmations) + "\n");
35
+ throw new Error("Exiting.");
36
+ }
29
37
  if (isDryRun) {
30
38
  console.log("Statements:", JSON.stringify(migrationQueries, void 0, 2));
31
39
  } else {
@@ -2,6 +2,7 @@ import { getManagedAppTokenOrExit } from "../../../tokens.js";
2
2
  import {
3
3
  createCurrentSnapshot,
4
4
  createEmptySnapshot,
5
+ formatDataLossMessage,
5
6
  getMigrationQueries,
6
7
  getProductionCurrentSnapshot
7
8
  } from "../../migration-queries.js";
@@ -9,20 +10,36 @@ async function cmd({
9
10
  dbConfig,
10
11
  flags
11
12
  }) {
13
+ const isJson = flags.json;
12
14
  const appToken = await getManagedAppTokenOrExit(flags.token);
13
15
  const productionSnapshot = await getProductionCurrentSnapshot({ appToken: appToken.token });
14
16
  const currentSnapshot = createCurrentSnapshot(dbConfig);
15
- const { queries: migrationQueries } = await getMigrationQueries({
17
+ const { queries: migrationQueries, confirmations } = await getMigrationQueries({
16
18
  oldSnapshot: JSON.stringify(productionSnapshot) !== "{}" ? productionSnapshot : createEmptySnapshot(),
17
19
  newSnapshot: currentSnapshot
18
20
  });
21
+ const result = { exitCode: 0, message: "", code: "", data: void 0 };
19
22
  if (migrationQueries.length === 0) {
20
- console.log(`Database schema is up to date.`);
23
+ result.code = "MATCH";
24
+ result.message = `Database schema is up to date.`;
21
25
  } else {
22
- console.log(`Database schema is out of date.`);
23
- console.log(`Run 'astro db push' to push up your latest changes.`);
26
+ result.code = "NO_MATCH";
27
+ result.message = `Database schema is out of date.
28
+ Run 'astro db push' to push up your latest changes.`;
29
+ }
30
+ if (confirmations.length > 0) {
31
+ result.code = "DATA_LOSS";
32
+ result.exitCode = 1;
33
+ result.data = confirmations;
34
+ result.message = formatDataLossMessage(confirmations, !isJson);
35
+ }
36
+ if (isJson) {
37
+ console.log(JSON.stringify(result));
38
+ } else {
39
+ console.log(result.message);
24
40
  }
25
41
  await appToken.destroy();
42
+ process.exit(result.exitCode);
26
43
  }
27
44
  export {
28
45
  cmd
@@ -19,3 +19,4 @@ export declare function getProductionCurrentSnapshot({ appToken, }: {
19
19
  }): Promise<DBSnapshot>;
20
20
  export declare function createCurrentSnapshot({ tables }: DBConfig): DBSnapshot;
21
21
  export declare function createEmptySnapshot(): DBSnapshot;
22
+ export declare function formatDataLossMessage(confirmations: string[], isColor?: boolean): string;
@@ -2,6 +2,7 @@ import deepDiff from "deep-diff";
2
2
  import { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core";
3
3
  import * as color from "kleur/colors";
4
4
  import { customAlphabet } from "nanoid";
5
+ import stripAnsi from "strip-ansi";
5
6
  import { hasPrimaryKey } from "../../runtime/index.js";
6
7
  import {
7
8
  getCreateIndexQueries,
@@ -113,24 +114,13 @@ async function getCollectionChangeQueries({
113
114
  if (dataLossCheck.dataLoss) {
114
115
  const { reason, columnName } = dataLossCheck;
115
116
  const reasonMsgs = {
116
- "added-required": `New column ${color.bold(
117
+ "added-required": `You added new required column '${color.bold(
117
118
  collectionName + "." + columnName
118
- )} is required with no default value.
119
- This requires deleting existing data in the ${color.bold(
120
- collectionName
121
- )} collection.`,
122
- "added-unique": `New column ${color.bold(
119
+ )}' with no default value.
120
+ This cannot be executed on an existing table.`,
121
+ "updated-type": `Updating existing column ${color.bold(
123
122
  collectionName + "." + columnName
124
- )} is marked as unique.
125
- This requires deleting existing data in the ${color.bold(
126
- collectionName
127
- )} collection.`,
128
- "updated-type": `Updated column ${color.bold(
129
- collectionName + "." + columnName
130
- )} cannot convert data to new column data type.
131
- This requires deleting existing data in the ${color.bold(
132
- collectionName
133
- )} collection.`
123
+ )} to a new type that cannot be handled automatically.`
134
124
  };
135
125
  confirmations.push(reasonMsgs[reason]);
136
126
  }
@@ -260,9 +250,6 @@ function canRecreateTableWithoutDataLoss(added, updated) {
260
250
  if (!a.schema.optional && !hasDefault(a)) {
261
251
  return { dataLoss: true, columnName, reason: "added-required" };
262
252
  }
263
- if (!a.schema.optional && a.schema.unique) {
264
- return { dataLoss: true, columnName, reason: "added-unique" };
265
- }
266
253
  }
267
254
  for (const [columnName, u] of Object.entries(updated)) {
268
255
  if (u.old.type !== u.new.type && !canChangeTypeWithoutQuery(u.old, u.new)) {
@@ -353,9 +340,26 @@ function createCurrentSnapshot({ tables = {} }) {
353
340
  function createEmptySnapshot() {
354
341
  return { experimentalVersion: 1, schema: {} };
355
342
  }
343
+ function formatDataLossMessage(confirmations, isColor = true) {
344
+ const messages = [];
345
+ messages.push(color.red("\u2716 We found some schema changes that cannot be handled automatically:"));
346
+ messages.push(``);
347
+ messages.push(...confirmations.map((m, i) => color.red(` (${i + 1}) `) + m));
348
+ messages.push(``);
349
+ messages.push(`To resolve, revert these changes or update your schema, and re-run the command.`);
350
+ messages.push(
351
+ `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.`
352
+ );
353
+ let finalMessage = messages.join("\n");
354
+ if (!isColor) {
355
+ finalMessage = stripAnsi(finalMessage);
356
+ }
357
+ return finalMessage;
358
+ }
356
359
  export {
357
360
  createCurrentSnapshot,
358
361
  createEmptySnapshot,
362
+ formatDataLossMessage,
359
363
  getCollectionChangeQueries,
360
364
  getMigrationQueries,
361
365
  getProductionCurrentSnapshot
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/db",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -67,6 +67,7 @@
67
67
  "open": "^10.0.3",
68
68
  "ora": "^7.0.1",
69
69
  "prompts": "^2.4.2",
70
+ "strip-ansi": "^7.1.0",
70
71
  "yargs-parser": "^21.1.1",
71
72
  "zod": "^3.22.4"
72
73
  },
@@ -82,7 +83,7 @@
82
83
  "mocha": "^10.2.0",
83
84
  "typescript": "^5.2.2",
84
85
  "vite": "^5.1.4",
85
- "astro": "4.4.11",
86
+ "astro": "4.4.12",
86
87
  "astro-scripts": "0.0.14"
87
88
  },
88
89
  "scripts": {