@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,180 +1,61 @@
1
- import { createClient } from "@libsql/client";
2
- import { drizzle as drizzleProxy } from "drizzle-orm/sqlite-proxy";
3
- import { drizzle as drizzleLibsql } from "drizzle-orm/libsql";
4
- import { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core";
5
- import { red } from "kleur/colors";
6
- import prompts from "prompts";
7
- import { recreateTables, seedData } from "../../../queries.js";
8
1
  import { getManagedAppTokenOrExit } from "../../../tokens.js";
9
- import { tablesSchema } from "../../../types.js";
2
+ import {} from "../../../types.js";
10
3
  import { getRemoteDatabaseUrl } from "../../../utils.js";
11
- import { getMigrationQueries } from "../../migration-queries.js";
12
4
  import {
5
+ createCurrentSnapshot,
13
6
  createEmptySnapshot,
14
- getMigrations,
15
- getMigrationStatus,
16
- loadInitialSnapshot,
17
- loadMigration,
18
- MIGRATION_NEEDED,
19
- MIGRATIONS_NOT_INITIALIZED,
20
- MIGRATIONS_UP_TO_DATE
21
- } from "../../migrations.js";
22
- import { MISSING_SESSION_ID_ERROR } from "../../../errors.js";
23
- async function cmd({ config, flags }) {
7
+ getMigrationQueries,
8
+ getProductionCurrentSnapshot
9
+ } from "../../migration-queries.js";
10
+ async function cmd({
11
+ dbConfig,
12
+ flags
13
+ }) {
24
14
  const isDryRun = flags.dryRun;
15
+ const isForceReset = flags.forceReset;
25
16
  const appToken = await getManagedAppTokenOrExit(flags.token);
26
- const migration = await getMigrationStatus(config);
27
- if (migration.state === "no-migrations-found") {
28
- console.log(MIGRATIONS_NOT_INITIALIZED);
29
- process.exit(1);
30
- } else if (migration.state === "ahead") {
31
- console.log(MIGRATION_NEEDED);
32
- process.exit(1);
33
- }
34
- const allLocalMigrations = await getMigrations();
35
- let missingMigrations = [];
36
- try {
37
- const { data } = await prepareMigrateQuery({
38
- migrations: allLocalMigrations,
39
- appToken: appToken.token
40
- });
41
- missingMigrations = data;
42
- } catch (error) {
43
- if (error instanceof Error) {
44
- if (error.message.startsWith("{")) {
45
- const { error: { code } = { code: "" } } = JSON.parse(error.message);
46
- if (code === "TOKEN_UNAUTHORIZED") {
47
- console.error(MISSING_SESSION_ID_ERROR);
48
- }
49
- }
50
- }
51
- console.error(error);
52
- process.exit(1);
17
+ const productionSnapshot = await getProductionCurrentSnapshot({ appToken: appToken.token });
18
+ const currentSnapshot = createCurrentSnapshot(dbConfig);
19
+ const isFromScratch = isForceReset || JSON.stringify(productionSnapshot) === "{}";
20
+ const { queries: migrationQueries } = await getMigrationQueries({
21
+ oldSnapshot: isFromScratch ? createEmptySnapshot() : productionSnapshot,
22
+ newSnapshot: currentSnapshot
23
+ });
24
+ if (migrationQueries.length === 0) {
25
+ console.log("Database schema is up to date.");
26
+ } else {
27
+ console.log(`Database schema is out of date.`);
53
28
  }
54
- if (missingMigrations.length === 0) {
55
- console.log(MIGRATIONS_UP_TO_DATE);
29
+ if (isDryRun) {
30
+ console.log("Statements:", JSON.stringify(migrationQueries, void 0, 2));
56
31
  } else {
57
- console.log(`Pushing ${missingMigrations.length} migrations...`);
32
+ console.log(`Pushing database schema updates...`);
58
33
  await pushSchema({
59
- migrations: missingMigrations,
34
+ statements: migrationQueries,
60
35
  appToken: appToken.token,
61
36
  isDryRun,
62
- currentSnapshot: migration.currentSnapshot
37
+ currentSnapshot
63
38
  });
64
39
  }
65
- console.info("Pushing data...");
66
- await pushData({ config, appToken: appToken.token, isDryRun });
67
40
  await appToken.destroy();
68
41
  console.info("Push complete!");
69
42
  }
70
43
  async function pushSchema({
71
- migrations,
44
+ statements,
72
45
  appToken,
73
46
  isDryRun,
74
47
  currentSnapshot
75
48
  }) {
76
- const initialSnapshot = migrations.find((m) => m === "0000_snapshot.json");
77
- const filteredMigrations = migrations.filter((m) => m !== "0000_snapshot.json");
78
- const missingMigrationContents = await Promise.all(filteredMigrations.map(loadMigration));
79
- const initialMigrationBatch = initialSnapshot ? (await getMigrationQueries({
80
- oldSnapshot: createEmptySnapshot(),
81
- newSnapshot: await loadInitialSnapshot()
82
- })).queries : [];
83
- const confirmations = missingMigrationContents.reduce((acc, curr) => {
84
- return [...acc, ...curr.confirm || []];
85
- }, []);
86
- if (confirmations.length > 0) {
87
- const response = await prompts([
88
- ...confirmations.map((message, index) => ({
89
- type: "confirm",
90
- name: String(index),
91
- message: red("Warning: ") + message + "\nContinue?",
92
- initial: true
93
- }))
94
- ]);
95
- if (Object.values(response).length === 0 || Object.values(response).some((value) => value === false)) {
96
- process.exit(1);
97
- }
98
- }
99
- const queries = missingMigrationContents.reduce((acc, curr) => {
100
- return [...acc, ...curr.db];
101
- }, initialMigrationBatch);
102
- await runMigrateQuery({ queries, migrations, snapshot: currentSnapshot, appToken, isDryRun });
103
- }
104
- const sqlite = new SQLiteAsyncDialect();
105
- async function pushData({
106
- config,
107
- appToken,
108
- isDryRun
109
- }) {
110
- const queries = [];
111
- if (config.db?.data) {
112
- const libsqlClient = createClient({ url: ":memory:" });
113
- await recreateTables({
114
- db: drizzleLibsql(libsqlClient),
115
- tables: tablesSchema.parse(config.db.tables ?? {})
116
- });
117
- for (const [collectionName, { writable }] of Object.entries(config.db.tables ?? {})) {
118
- if (!writable) {
119
- queries.push({
120
- sql: `DELETE FROM ${sqlite.escapeName(collectionName)}`,
121
- args: []
122
- });
123
- }
124
- }
125
- const db = await drizzleProxy(async (sqlQuery, params, method) => {
126
- const stmt = { sql: sqlQuery, args: params };
127
- queries.push(stmt);
128
- const { rows } = await libsqlClient.execute(stmt);
129
- const rowValues = [];
130
- for (const row of rows) {
131
- if (row != null && typeof row === "object") {
132
- rowValues.push(Object.values(row));
133
- }
134
- }
135
- if (method === "get") {
136
- return { rows: rowValues[0] };
137
- }
138
- return { rows: rowValues };
139
- });
140
- await seedData({
141
- db,
142
- mode: "build",
143
- data: config.db.data
144
- });
145
- }
146
- const url = new URL("/db/query", getRemoteDatabaseUrl());
147
- if (isDryRun) {
148
- console.info("[DRY RUN] Batch data seed:", JSON.stringify(queries, null, 2));
149
- return new Response(null, { status: 200 });
150
- }
151
- return await fetch(url, {
152
- method: "POST",
153
- headers: new Headers({
154
- Authorization: `Bearer ${appToken}`
155
- }),
156
- body: JSON.stringify(queries)
157
- });
158
- }
159
- async function runMigrateQuery({
160
- queries: baseQueries,
161
- migrations,
162
- snapshot,
163
- appToken,
164
- isDryRun
165
- }) {
166
- const queries = ["pragma defer_foreign_keys=true;", ...baseQueries];
167
49
  const requestBody = {
168
- snapshot,
169
- migrations,
170
- sql: queries,
50
+ snapshot: currentSnapshot,
51
+ sql: statements,
171
52
  experimentalVersion: 1
172
53
  };
173
54
  if (isDryRun) {
174
55
  console.info("[DRY RUN] Batch query:", JSON.stringify(requestBody, null, 2));
175
56
  return new Response(null, { status: 200 });
176
57
  }
177
- const url = new URL("/migrations/run", getRemoteDatabaseUrl());
58
+ const url = new URL("/db/push", getRemoteDatabaseUrl());
178
59
  return await fetch(url, {
179
60
  method: "POST",
180
61
  headers: new Headers({
@@ -183,27 +64,6 @@ async function runMigrateQuery({
183
64
  body: JSON.stringify(requestBody)
184
65
  });
185
66
  }
186
- async function prepareMigrateQuery({
187
- migrations,
188
- appToken
189
- }) {
190
- const url = new URL("/migrations/prepare", getRemoteDatabaseUrl());
191
- const requestBody = {
192
- migrations,
193
- experimentalVersion: 1
194
- };
195
- const result = await fetch(url, {
196
- method: "POST",
197
- headers: new Headers({
198
- Authorization: `Bearer ${appToken}`
199
- }),
200
- body: JSON.stringify(requestBody)
201
- });
202
- if (result.status >= 400) {
203
- throw new Error(await result.text());
204
- }
205
- return await result.json();
206
- }
207
67
  export {
208
68
  cmd
209
69
  };
@@ -1,6 +1,8 @@
1
1
  import type { AstroConfig } from 'astro';
2
2
  import type { Arguments } from 'yargs-parser';
3
- export declare function cmd({ flags }: {
4
- config: AstroConfig;
3
+ import type { DBConfigInput } from '../../../types.js';
4
+ export declare function cmd({ flags, }: {
5
+ dbConfig: DBConfigInput;
6
+ astroConfig: AstroConfig;
5
7
  flags: Arguments;
6
8
  }): Promise<void>;
@@ -2,7 +2,9 @@ import { sql } from "drizzle-orm";
2
2
  import { createRemoteDatabaseClient } from "../../../../runtime/db-client.js";
3
3
  import { getManagedAppTokenOrExit } from "../../../tokens.js";
4
4
  import { getRemoteDatabaseUrl } from "../../../utils.js";
5
- async function cmd({ flags }) {
5
+ async function cmd({
6
+ flags
7
+ }) {
6
8
  const query = flags.query;
7
9
  const appToken = await getManagedAppTokenOrExit(flags.token);
8
10
  const db = createRemoteDatabaseClient(appToken.token, getRemoteDatabaseUrl());
@@ -1,6 +1,8 @@
1
1
  import type { AstroConfig } from 'astro';
2
2
  import type { Arguments } from 'yargs-parser';
3
- export declare function cmd({ config, flags }: {
4
- config: AstroConfig;
3
+ import type { DBConfig } from '../../../types.js';
4
+ export declare function cmd({ dbConfig, flags, }: {
5
+ astroConfig: AstroConfig;
6
+ dbConfig: DBConfig;
5
7
  flags: Arguments;
6
8
  }): Promise<void>;
@@ -1,42 +1,28 @@
1
+ import { getManagedAppTokenOrExit } from "../../../tokens.js";
1
2
  import {
2
- getMigrationStatus,
3
- MIGRATION_NEEDED,
4
- MIGRATIONS_NOT_INITIALIZED,
5
- MIGRATIONS_UP_TO_DATE
6
- } from "../../migrations.js";
7
- import { getMigrationQueries } from "../../migration-queries.js";
8
- async function cmd({ config, flags }) {
9
- const status = await getMigrationStatus(config);
10
- const { state } = status;
11
- if (flags.json) {
12
- if (state === "ahead") {
13
- const { queries: migrationQueries } = await getMigrationQueries({
14
- oldSnapshot: status.oldSnapshot,
15
- newSnapshot: status.newSnapshot
16
- });
17
- const newFileContent = {
18
- diff: status.diff,
19
- db: migrationQueries
20
- };
21
- status.newFileContent = JSON.stringify(newFileContent, null, 2);
22
- }
23
- console.log(JSON.stringify(status));
24
- process.exit(state === "up-to-date" ? 0 : 1);
25
- }
26
- switch (state) {
27
- case "no-migrations-found": {
28
- console.log(MIGRATIONS_NOT_INITIALIZED);
29
- process.exit(1);
30
- }
31
- case "ahead": {
32
- console.log(MIGRATION_NEEDED);
33
- process.exit(1);
34
- }
35
- case "up-to-date": {
36
- console.log(MIGRATIONS_UP_TO_DATE);
37
- return;
38
- }
3
+ createCurrentSnapshot,
4
+ createEmptySnapshot,
5
+ getMigrationQueries,
6
+ getProductionCurrentSnapshot
7
+ } from "../../migration-queries.js";
8
+ async function cmd({
9
+ dbConfig,
10
+ flags
11
+ }) {
12
+ const appToken = await getManagedAppTokenOrExit(flags.token);
13
+ const productionSnapshot = await getProductionCurrentSnapshot({ appToken: appToken.token });
14
+ const currentSnapshot = createCurrentSnapshot(dbConfig);
15
+ const { queries: migrationQueries } = await getMigrationQueries({
16
+ oldSnapshot: JSON.stringify(productionSnapshot) !== "{}" ? productionSnapshot : createEmptySnapshot(),
17
+ newSnapshot: currentSnapshot
18
+ });
19
+ if (migrationQueries.length === 0) {
20
+ console.log(`Database schema is up to date.`);
21
+ } else {
22
+ console.log(`Database schema is out of date.`);
23
+ console.log(`Run 'astro db push' to push up your latest changes.`);
39
24
  }
25
+ await appToken.destroy();
40
26
  }
41
27
  export {
42
28
  cmd
@@ -1,6 +1,6 @@
1
1
  import type { AstroConfig } from 'astro';
2
2
  import type { Arguments } from 'yargs-parser';
3
- export declare function cli({ flags, config }: {
3
+ export declare function cli({ flags, config: astroConfig, }: {
4
4
  flags: Arguments;
5
5
  config: AstroConfig;
6
6
  }): Promise<void>;
@@ -1,40 +1,49 @@
1
- import { STUDIO_CONFIG_MISSING_CLI_ERROR } from "../errors.js";
2
- async function cli({ flags, config }) {
1
+ import { loadDbConfigFile } from "../load-file.js";
2
+ import { dbConfigSchema } from "../types.js";
3
+ async function cli({
4
+ flags,
5
+ config: astroConfig
6
+ }) {
3
7
  const args = flags._;
4
8
  const command = args[2] === "db" ? args[3] : args[2];
5
- if (!config.db?.studio) {
6
- console.log(STUDIO_CONFIG_MISSING_CLI_ERROR);
7
- process.exit(1);
8
- }
9
+ const { mod } = await loadDbConfigFile(astroConfig.root);
10
+ const dbConfig = dbConfigSchema.parse(mod?.default ?? {});
9
11
  switch (command) {
10
12
  case "shell": {
11
13
  const { cmd } = await import("./commands/shell/index.js");
12
- return await cmd({ config, flags });
14
+ return await cmd({ astroConfig, dbConfig, flags });
15
+ }
16
+ case "gen": {
17
+ console.log('"astro db gen" is no longer needed! Visit the docs for more information.');
18
+ return;
13
19
  }
14
- case "gen":
15
20
  case "sync": {
16
- const { cmd } = await import("./commands/gen/index.js");
17
- return await cmd({ config, flags });
21
+ console.log('"astro db sync" is no longer needed! Visit the docs for more information.');
22
+ return;
18
23
  }
19
24
  case "push": {
20
25
  const { cmd } = await import("./commands/push/index.js");
21
- return await cmd({ config, flags });
26
+ return await cmd({ astroConfig, dbConfig, flags });
22
27
  }
23
28
  case "verify": {
24
29
  const { cmd } = await import("./commands/verify/index.js");
25
- return await cmd({ config, flags });
30
+ return await cmd({ astroConfig, dbConfig, flags });
31
+ }
32
+ case "execute": {
33
+ const { cmd } = await import("./commands/execute/index.js");
34
+ return await cmd({ astroConfig, dbConfig, flags });
26
35
  }
27
36
  case "login": {
28
37
  const { cmd } = await import("./commands/login/index.js");
29
- return await cmd({ config, flags });
38
+ return await cmd({ astroConfig, dbConfig, flags });
30
39
  }
31
40
  case "logout": {
32
41
  const { cmd } = await import("./commands/logout/index.js");
33
- return await cmd({ config, flags });
42
+ return await cmd();
34
43
  }
35
44
  case "link": {
36
45
  const { cmd } = await import("./commands/link/index.js");
37
- return await cmd({ config, flags });
46
+ return await cmd();
38
47
  }
39
48
  default: {
40
49
  if (command == null) {
@@ -59,8 +68,8 @@ astro logout End your authenticated session with Astro Studio
59
68
  astro link Link this directory to an Astro Studio project
60
69
 
61
70
  astro db gen Creates snapshot based on your schema
62
- astro db push Pushes migrations to Astro Studio
63
- astro db verify Verifies migrations have been pushed and errors if not`;
71
+ astro db push Pushes schema updates to Astro Studio
72
+ astro db verify Tests schema updates /w Astro Studio (good for CI)`;
64
73
  }
65
74
  }
66
75
  export {
@@ -1,26 +1,21 @@
1
- import { type DBTable, type DBSnapshot } from '../types.js';
2
- /** Dependency injected for unit testing */
3
- type AmbiguityResponses = {
4
- collectionRenames: Record<string, string>;
5
- columnRenames: {
6
- [collectionName: string]: Record<string, string>;
7
- };
8
- };
9
- export declare function getMigrationQueries({ oldSnapshot, newSnapshot, ambiguityResponses, }: {
1
+ import { type DBConfig, type DBSnapshot, type DBTable } from '../types.js';
2
+ export declare function getMigrationQueries({ oldSnapshot, newSnapshot, }: {
10
3
  oldSnapshot: DBSnapshot;
11
4
  newSnapshot: DBSnapshot;
12
- ambiguityResponses?: AmbiguityResponses;
13
5
  }): Promise<{
14
6
  queries: string[];
15
7
  confirmations: string[];
16
8
  }>;
17
- export declare function getCollectionChangeQueries({ collectionName, oldCollection, newCollection, ambiguityResponses, }: {
9
+ export declare function getCollectionChangeQueries({ collectionName, oldCollection, newCollection, }: {
18
10
  collectionName: string;
19
11
  oldCollection: DBTable;
20
12
  newCollection: DBTable;
21
- ambiguityResponses?: AmbiguityResponses;
22
13
  }): Promise<{
23
14
  queries: string[];
24
15
  confirmations: string[];
25
16
  }>;
26
- export {};
17
+ export declare function getProductionCurrentSnapshot({ appToken, }: {
18
+ appToken: string;
19
+ }): Promise<DBSnapshot>;
20
+ export declare function createCurrentSnapshot({ tables }: DBConfig): DBSnapshot;
21
+ export declare function createEmptySnapshot(): DBSnapshot;