@astrojs/db 0.0.0-edge-nested-20240223135627

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 (60) hide show
  1. package/LICENSE +59 -0
  2. package/config-augment.d.ts +4 -0
  3. package/dist/core/cli/commands/gen/index.d.ts +6 -0
  4. package/dist/core/cli/commands/gen/index.js +39 -0
  5. package/dist/core/cli/commands/link/index.d.ts +8 -0
  6. package/dist/core/cli/commands/link/index.js +78 -0
  7. package/dist/core/cli/commands/login/index.d.ts +6 -0
  8. package/dist/core/cli/commands/login/index.js +46 -0
  9. package/dist/core/cli/commands/logout/index.d.ts +6 -0
  10. package/dist/core/cli/commands/logout/index.js +9 -0
  11. package/dist/core/cli/commands/push/index.d.ts +6 -0
  12. package/dist/core/cli/commands/push/index.js +209 -0
  13. package/dist/core/cli/commands/shell/index.d.ts +6 -0
  14. package/dist/core/cli/commands/shell/index.js +15 -0
  15. package/dist/core/cli/commands/verify/index.d.ts +6 -0
  16. package/dist/core/cli/commands/verify/index.js +43 -0
  17. package/dist/core/cli/index.d.ts +6 -0
  18. package/dist/core/cli/index.js +68 -0
  19. package/dist/core/cli/migration-queries.d.ts +26 -0
  20. package/dist/core/cli/migration-queries.js +418 -0
  21. package/dist/core/cli/migrations.d.ts +34 -0
  22. package/dist/core/cli/migrations.js +129 -0
  23. package/dist/core/consts.d.ts +6 -0
  24. package/dist/core/consts.js +17 -0
  25. package/dist/core/errors.d.ts +7 -0
  26. package/dist/core/errors.js +54 -0
  27. package/dist/core/integration/error-map.d.ts +6 -0
  28. package/dist/core/integration/error-map.js +79 -0
  29. package/dist/core/integration/file-url.d.ts +2 -0
  30. package/dist/core/integration/file-url.js +84 -0
  31. package/dist/core/integration/index.d.ts +2 -0
  32. package/dist/core/integration/index.js +173 -0
  33. package/dist/core/integration/load-astro-config.d.ts +6 -0
  34. package/dist/core/integration/load-astro-config.js +79 -0
  35. package/dist/core/integration/typegen.d.ts +5 -0
  36. package/dist/core/integration/typegen.js +41 -0
  37. package/dist/core/integration/vite-plugin-db.d.ts +25 -0
  38. package/dist/core/integration/vite-plugin-db.js +73 -0
  39. package/dist/core/integration/vite-plugin-inject-env-ts.d.ts +11 -0
  40. package/dist/core/integration/vite-plugin-inject-env-ts.js +53 -0
  41. package/dist/core/queries.d.ts +78 -0
  42. package/dist/core/queries.js +218 -0
  43. package/dist/core/tokens.d.ts +11 -0
  44. package/dist/core/tokens.js +131 -0
  45. package/dist/core/types.d.ts +8059 -0
  46. package/dist/core/types.js +209 -0
  47. package/dist/core/utils.d.ts +5 -0
  48. package/dist/core/utils.js +18 -0
  49. package/dist/index.d.ts +5 -0
  50. package/dist/index.js +16 -0
  51. package/dist/runtime/db-client.d.ts +12 -0
  52. package/dist/runtime/db-client.js +96 -0
  53. package/dist/runtime/drizzle.d.ts +1 -0
  54. package/dist/runtime/drizzle.js +48 -0
  55. package/dist/runtime/index.d.ts +30 -0
  56. package/dist/runtime/index.js +124 -0
  57. package/dist/runtime/types.d.ts +69 -0
  58. package/dist/runtime/types.js +8 -0
  59. package/index.d.ts +3 -0
  60. package/package.json +81 -0
@@ -0,0 +1,78 @@
1
+ import type { SqliteRemoteDatabase } from 'drizzle-orm/sqlite-proxy';
2
+ import { type BooleanColumn, type DBTable, type DBTables, type DBColumn, type DateColumn, type ColumnType, type JsonColumn, type NumberColumn, type TextColumn } from '../core/types.js';
3
+ import type { AstroIntegrationLogger } from 'astro';
4
+ import type { DBUserConfig } from '../core/types.js';
5
+ export declare function recreateTables({ db, tables, }: {
6
+ db: SqliteRemoteDatabase;
7
+ tables: DBTables;
8
+ }): Promise<void>;
9
+ export declare function seedData({ db, data, logger, mode, }: {
10
+ db: SqliteRemoteDatabase;
11
+ data: DBUserConfig['data'];
12
+ logger?: AstroIntegrationLogger;
13
+ mode: 'dev' | 'build';
14
+ }): Promise<void>;
15
+ export declare function getCreateTableQuery(collectionName: string, collection: DBTable): string;
16
+ export declare function getCreateIndexQueries(collectionName: string, collection: Pick<DBTable, 'indexes'>): string[];
17
+ export declare function getCreateForeignKeyQueries(collectionName: string, collection: DBTable): string[];
18
+ export declare function schemaTypeToSqlType(type: ColumnType): 'text' | 'integer';
19
+ export declare function getModifiers(columnName: string, column: DBColumn): string;
20
+ export declare function getReferencesConfig(column: DBColumn): {
21
+ type: "number";
22
+ schema: ({
23
+ unique: boolean;
24
+ name?: string | undefined;
25
+ label?: string | undefined;
26
+ collection?: string | undefined;
27
+ } & {
28
+ optional: boolean;
29
+ primaryKey: false;
30
+ default?: number | import("../runtime/types.js").SerializedSQL | undefined;
31
+ } & {
32
+ references?: any | undefined;
33
+ }) | ({
34
+ unique: boolean;
35
+ name?: string | undefined;
36
+ label?: string | undefined;
37
+ collection?: string | undefined;
38
+ } & {
39
+ primaryKey: true;
40
+ optional?: false | undefined;
41
+ default?: undefined;
42
+ } & {
43
+ references?: any | undefined;
44
+ });
45
+ } | {
46
+ type: "text";
47
+ schema: ({
48
+ unique: boolean;
49
+ name?: string | undefined;
50
+ label?: string | undefined;
51
+ collection?: string | undefined;
52
+ default?: string | import("../runtime/types.js").SerializedSQL | undefined;
53
+ multiline?: boolean | undefined;
54
+ } & {
55
+ optional: boolean;
56
+ primaryKey: false;
57
+ } & {
58
+ references?: any | undefined;
59
+ }) | ({
60
+ unique: boolean;
61
+ name?: string | undefined;
62
+ label?: string | undefined;
63
+ collection?: string | undefined;
64
+ default?: string | import("../runtime/types.js").SerializedSQL | undefined;
65
+ multiline?: boolean | undefined;
66
+ } & {
67
+ primaryKey: true;
68
+ optional?: false | undefined;
69
+ } & {
70
+ references?: any | undefined;
71
+ });
72
+ } | undefined;
73
+ type WithDefaultDefined<T extends DBColumn> = T & {
74
+ schema: Required<Pick<T['schema'], 'default'>>;
75
+ };
76
+ type DBColumnWithDefault = WithDefaultDefined<TextColumn> | WithDefaultDefined<DateColumn> | WithDefaultDefined<NumberColumn> | WithDefaultDefined<BooleanColumn> | WithDefaultDefined<JsonColumn>;
77
+ export declare function hasDefault(column: DBColumn): column is DBColumnWithDefault;
78
+ export {};
@@ -0,0 +1,218 @@
1
+ import {
2
+ } from "../core/types.js";
3
+ import { bold } from "kleur/colors";
4
+ import { sql, getTableName } from "drizzle-orm";
5
+ import { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core";
6
+ import { hasPrimaryKey } from "../runtime/index.js";
7
+ import { isSerializedSQL } from "../runtime/types.js";
8
+ import { SEED_WRITABLE_IN_PROD_ERROR } from "./errors.js";
9
+ const sqlite = new SQLiteAsyncDialect();
10
+ async function recreateTables({
11
+ db,
12
+ tables
13
+ }) {
14
+ const setupQueries = [];
15
+ for (const [name, collection] of Object.entries(tables)) {
16
+ const dropQuery = sql.raw(`DROP TABLE IF EXISTS ${sqlite.escapeName(name)}`);
17
+ const createQuery = sql.raw(getCreateTableQuery(name, collection));
18
+ const indexQueries = getCreateIndexQueries(name, collection);
19
+ setupQueries.push(dropQuery, createQuery, ...indexQueries.map((s) => sql.raw(s)));
20
+ }
21
+ for (const q of setupQueries) {
22
+ await db.run(q);
23
+ }
24
+ }
25
+ async function seedData({
26
+ db,
27
+ data,
28
+ logger,
29
+ mode
30
+ }) {
31
+ try {
32
+ const dataFns = Array.isArray(data) ? data : [data];
33
+ for (const dataFn of dataFns) {
34
+ await dataFn({
35
+ seed: async ({ table, writable }, values) => {
36
+ if (writable && mode === "build" && process.env.ASTRO_DB_TEST_ENV !== "1") {
37
+ (logger ?? console).error(SEED_WRITABLE_IN_PROD_ERROR(getTableName(table)));
38
+ process.exit(1);
39
+ }
40
+ await db.insert(table).values(values);
41
+ },
42
+ seedReturning: async ({ table, writable }, values) => {
43
+ if (writable && mode === "build" && process.env.ASTRO_DB_TEST_ENV !== "1") {
44
+ (logger ?? console).error(SEED_WRITABLE_IN_PROD_ERROR(getTableName(table)));
45
+ process.exit(1);
46
+ }
47
+ let result = db.insert(table).values(values).returning();
48
+ if (!Array.isArray(values)) {
49
+ result = result.get();
50
+ }
51
+ return result;
52
+ },
53
+ db,
54
+ mode
55
+ });
56
+ }
57
+ } catch (error) {
58
+ (logger ?? console).error(
59
+ `Failed to seed data. Did you update to match recent schema changes?`
60
+ );
61
+ (logger ?? console).error(error);
62
+ }
63
+ }
64
+ function getCreateTableQuery(collectionName, collection) {
65
+ let query = `CREATE TABLE ${sqlite.escapeName(collectionName)} (`;
66
+ const colQueries = [];
67
+ const colHasPrimaryKey = Object.entries(collection.columns).find(
68
+ ([, column]) => hasPrimaryKey(column)
69
+ );
70
+ if (!colHasPrimaryKey) {
71
+ colQueries.push("_id INTEGER PRIMARY KEY");
72
+ }
73
+ for (const [columnName, column] of Object.entries(collection.columns)) {
74
+ const colQuery = `${sqlite.escapeName(columnName)} ${schemaTypeToSqlType(
75
+ column.type
76
+ )}${getModifiers(columnName, column)}`;
77
+ colQueries.push(colQuery);
78
+ }
79
+ colQueries.push(...getCreateForeignKeyQueries(collectionName, collection));
80
+ query += colQueries.join(", ") + ")";
81
+ return query;
82
+ }
83
+ function getCreateIndexQueries(collectionName, collection) {
84
+ let queries = [];
85
+ for (const [indexName, indexProps] of Object.entries(collection.indexes ?? {})) {
86
+ const onColNames = asArray(indexProps.on);
87
+ const onCols = onColNames.map((colName) => sqlite.escapeName(colName));
88
+ const unique = indexProps.unique ? "UNIQUE " : "";
89
+ const indexQuery = `CREATE ${unique}INDEX ${sqlite.escapeName(
90
+ indexName
91
+ )} ON ${sqlite.escapeName(collectionName)} (${onCols.join(", ")})`;
92
+ queries.push(indexQuery);
93
+ }
94
+ return queries;
95
+ }
96
+ function getCreateForeignKeyQueries(collectionName, collection) {
97
+ let queries = [];
98
+ for (const foreignKey of collection.foreignKeys ?? []) {
99
+ const columns = asArray(foreignKey.columns);
100
+ const references = asArray(foreignKey.references);
101
+ if (columns.length !== references.length) {
102
+ throw new Error(
103
+ `Foreign key on ${collectionName} is misconfigured. \`columns\` and \`references\` must be the same length.`
104
+ );
105
+ }
106
+ const referencedCollection = references[0]?.schema.collection;
107
+ if (!referencedCollection) {
108
+ throw new Error(
109
+ `Foreign key on ${collectionName} is misconfigured. \`references\` cannot be empty.`
110
+ );
111
+ }
112
+ const query = `FOREIGN KEY (${columns.map((f) => sqlite.escapeName(f)).join(", ")}) REFERENCES ${sqlite.escapeName(referencedCollection)}(${references.map((r) => sqlite.escapeName(r.schema.name)).join(", ")})`;
113
+ queries.push(query);
114
+ }
115
+ return queries;
116
+ }
117
+ function asArray(value) {
118
+ return Array.isArray(value) ? value : [value];
119
+ }
120
+ function schemaTypeToSqlType(type) {
121
+ switch (type) {
122
+ case "date":
123
+ case "text":
124
+ case "json":
125
+ return "text";
126
+ case "number":
127
+ case "boolean":
128
+ return "integer";
129
+ }
130
+ }
131
+ function getModifiers(columnName, column) {
132
+ let modifiers = "";
133
+ if (hasPrimaryKey(column)) {
134
+ return " PRIMARY KEY";
135
+ }
136
+ if (!column.schema.optional) {
137
+ modifiers += " NOT NULL";
138
+ }
139
+ if (column.schema.unique) {
140
+ modifiers += " UNIQUE";
141
+ }
142
+ if (hasDefault(column)) {
143
+ modifiers += ` DEFAULT ${getDefaultValueSql(columnName, column)}`;
144
+ }
145
+ const references = getReferencesConfig(column);
146
+ if (references) {
147
+ const { collection, name } = references.schema;
148
+ if (!collection || !name) {
149
+ throw new Error(
150
+ `Column ${collection}.${name} references a collection that does not exist. Did you apply the referenced collection to the \`tables\` object in your Astro config?`
151
+ );
152
+ }
153
+ modifiers += ` REFERENCES ${sqlite.escapeName(collection)} (${sqlite.escapeName(name)})`;
154
+ }
155
+ return modifiers;
156
+ }
157
+ function getReferencesConfig(column) {
158
+ const canHaveReferences = column.type === "number" || column.type === "text";
159
+ if (!canHaveReferences)
160
+ return void 0;
161
+ return column.schema.references;
162
+ }
163
+ function hasDefault(column) {
164
+ if (column.schema.default !== void 0) {
165
+ return true;
166
+ }
167
+ if (hasPrimaryKey(column) && column.type === "number") {
168
+ return true;
169
+ }
170
+ return false;
171
+ }
172
+ function toDefault(def) {
173
+ const type = typeof def;
174
+ if (type === "string") {
175
+ return sqlite.escapeString(def);
176
+ } else if (type === "boolean") {
177
+ return def ? "TRUE" : "FALSE";
178
+ } else {
179
+ return def + "";
180
+ }
181
+ }
182
+ function getDefaultValueSql(columnName, column) {
183
+ if (isSerializedSQL(column.schema.default)) {
184
+ return column.schema.default.sql;
185
+ }
186
+ switch (column.type) {
187
+ case "boolean":
188
+ case "number":
189
+ case "text":
190
+ case "date":
191
+ return toDefault(column.schema.default);
192
+ case "json": {
193
+ let stringified = "";
194
+ try {
195
+ stringified = JSON.stringify(column.schema.default);
196
+ } catch (e) {
197
+ console.log(
198
+ `Invalid default value for column ${bold(
199
+ columnName
200
+ )}. Defaults must be valid JSON when using the \`json()\` type.`
201
+ );
202
+ process.exit(0);
203
+ }
204
+ return sqlite.escapeString(stringified);
205
+ }
206
+ }
207
+ }
208
+ export {
209
+ getCreateForeignKeyQueries,
210
+ getCreateIndexQueries,
211
+ getCreateTableQuery,
212
+ getModifiers,
213
+ getReferencesConfig,
214
+ hasDefault,
215
+ recreateTables,
216
+ schemaTypeToSqlType,
217
+ seedData
218
+ };
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ export declare const SESSION_LOGIN_FILE: import("url").URL;
3
+ export declare const PROJECT_ID_FILE: import("url").URL;
4
+ export interface ManagedAppToken {
5
+ token: string;
6
+ renew(): Promise<void>;
7
+ destroy(): Promise<void>;
8
+ }
9
+ export declare function getProjectIdFromFile(): Promise<string | undefined>;
10
+ export declare function getSessionIdFromFile(): Promise<string | undefined>;
11
+ export declare function getManagedAppTokenOrExit(token?: string): Promise<ManagedAppToken>;
@@ -0,0 +1,131 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { getAstroStudioEnv, getAstroStudioUrl } from "./utils.js";
6
+ import { MISSING_PROJECT_ID_ERROR, MISSING_SESSION_ID_ERROR } from "./errors.js";
7
+ const SESSION_LOGIN_FILE = pathToFileURL(join(homedir(), ".astro", "session-token"));
8
+ const PROJECT_ID_FILE = pathToFileURL(join(process.cwd(), ".astro", "link"));
9
+ class ManagedLocalAppToken {
10
+ token;
11
+ constructor(token) {
12
+ this.token = token;
13
+ }
14
+ async renew() {
15
+ }
16
+ async destroy() {
17
+ }
18
+ }
19
+ class ManagedRemoteAppToken {
20
+ token;
21
+ session;
22
+ projectId;
23
+ ttl;
24
+ renewTimer;
25
+ static async create(sessionToken, projectId) {
26
+ const response = await fetch(new URL(`${getAstroStudioUrl()}/auth/cli/token-create`), {
27
+ method: "POST",
28
+ headers: new Headers({
29
+ Authorization: `Bearer ${sessionToken}`
30
+ }),
31
+ body: JSON.stringify({ projectId })
32
+ });
33
+ const { token: shortLivedAppToken, ttl } = await response.json();
34
+ return new ManagedRemoteAppToken({
35
+ token: shortLivedAppToken,
36
+ session: sessionToken,
37
+ projectId,
38
+ ttl
39
+ });
40
+ }
41
+ constructor(options) {
42
+ this.token = options.token;
43
+ this.session = options.session;
44
+ this.projectId = options.projectId;
45
+ this.ttl = options.ttl;
46
+ this.renewTimer = setTimeout(() => this.renew(), 1e3 * 60 * 5 / 2);
47
+ }
48
+ async fetch(url, body) {
49
+ return fetch(`${getAstroStudioUrl()}${url}`, {
50
+ method: "POST",
51
+ headers: {
52
+ Authorization: `Bearer ${this.session}`,
53
+ "Content-Type": "application/json"
54
+ },
55
+ body: JSON.stringify(body)
56
+ });
57
+ }
58
+ async renew() {
59
+ clearTimeout(this.renewTimer);
60
+ delete this.renewTimer;
61
+ try {
62
+ const response = await this.fetch("/auth/cli/token-renew", {
63
+ token: this.token,
64
+ projectId: this.projectId
65
+ });
66
+ if (response.status === 200) {
67
+ this.renewTimer = setTimeout(() => this.renew(), 1e3 * 60 * this.ttl / 2);
68
+ } else {
69
+ throw new Error(`Unexpected response: ${response.status} ${response.statusText}`);
70
+ }
71
+ } catch (error) {
72
+ const retryIn = 60 * this.ttl / 10;
73
+ console.error(`Failed to renew token. Retrying in ${retryIn} seconds.`, error?.message);
74
+ this.renewTimer = setTimeout(() => this.renew(), retryIn * 1e3);
75
+ }
76
+ }
77
+ async destroy() {
78
+ try {
79
+ const response = await this.fetch("/auth/cli/token-delete", {
80
+ token: this.token,
81
+ projectId: this.projectId
82
+ });
83
+ if (response.status !== 200) {
84
+ throw new Error(`Unexpected response: ${response.status} ${response.statusText}`);
85
+ }
86
+ } catch (error) {
87
+ console.error("Failed to delete token.", error?.message);
88
+ }
89
+ }
90
+ }
91
+ async function getProjectIdFromFile() {
92
+ try {
93
+ return await readFile(PROJECT_ID_FILE, "utf-8");
94
+ } catch (error) {
95
+ return void 0;
96
+ }
97
+ }
98
+ async function getSessionIdFromFile() {
99
+ try {
100
+ return await readFile(SESSION_LOGIN_FILE, "utf-8");
101
+ } catch (error) {
102
+ return void 0;
103
+ }
104
+ }
105
+ async function getManagedAppTokenOrExit(token) {
106
+ if (token) {
107
+ return new ManagedLocalAppToken(token);
108
+ }
109
+ const { ASTRO_STUDIO_APP_TOKEN } = getAstroStudioEnv();
110
+ if (ASTRO_STUDIO_APP_TOKEN) {
111
+ return new ManagedLocalAppToken(ASTRO_STUDIO_APP_TOKEN);
112
+ }
113
+ const sessionToken = await getSessionIdFromFile();
114
+ if (!sessionToken) {
115
+ console.error(MISSING_SESSION_ID_ERROR);
116
+ process.exit(1);
117
+ }
118
+ const projectId = await getProjectIdFromFile();
119
+ if (!sessionToken || !projectId) {
120
+ console.error(MISSING_PROJECT_ID_ERROR);
121
+ process.exit(1);
122
+ }
123
+ return ManagedRemoteAppToken.create(sessionToken, projectId);
124
+ }
125
+ export {
126
+ PROJECT_ID_FILE,
127
+ SESSION_LOGIN_FILE,
128
+ getManagedAppTokenOrExit,
129
+ getProjectIdFromFile,
130
+ getSessionIdFromFile
131
+ };