@astrojs/db 0.1.2 → 0.1.4

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/internal.js CHANGED
@@ -15,8 +15,11 @@ import {
15
15
  text
16
16
  } from "drizzle-orm/sqlite-core";
17
17
  import { z } from "zod";
18
- import { nanoid } from "nanoid";
18
+ import { createRemoteDatabaseClient } from "./utils-runtime.js";
19
19
  const sqlite = new SQLiteAsyncDialect();
20
+ function hasPrimaryKey(field) {
21
+ return "primaryKey" in field && !!field.primaryKey;
22
+ }
20
23
  function isReadableCollection(collection) {
21
24
  return !collection.writable;
22
25
  }
@@ -27,7 +30,6 @@ function checkIfModificationIsAllowed(collections, Table) {
27
30
  throw new Error(`The [${tableName}] collection is read-only.`);
28
31
  }
29
32
  }
30
- import { createRemoteDatabaseClient } from "./utils.js";
31
33
  async function createLocalDatabaseClient({
32
34
  collections,
33
35
  dbUrl,
@@ -55,8 +57,10 @@ async function createLocalDatabaseClient({
55
57
  }
56
58
  async function setupDbTables({
57
59
  db,
60
+ data,
58
61
  collections,
59
- logger
62
+ logger,
63
+ mode
60
64
  }) {
61
65
  const setupQueries = [];
62
66
  for (const [name, collection] of Object.entries(collections)) {
@@ -67,6 +71,15 @@ async function setupDbTables({
67
71
  for (const q of setupQueries) {
68
72
  await db.run(q);
69
73
  }
74
+ if (data) {
75
+ const ormObjects = Object.fromEntries(
76
+ Object.entries(collections).map(([name, collection]) => {
77
+ const table = collectionToTable(name, collection, false);
78
+ return [name, table];
79
+ })
80
+ );
81
+ await data({ db, ...ormObjects, mode });
82
+ }
70
83
  for (const [name, collection] of Object.entries(collections)) {
71
84
  if (!isReadableCollection(collection) || !collection.data)
72
85
  continue;
@@ -86,7 +99,13 @@ ${e}`
86
99
  }
87
100
  function getCreateTableQuery(collectionName, collection) {
88
101
  let query = `CREATE TABLE ${sqlite.escapeName(collectionName)} (`;
89
- const colQueries = ['"id" text PRIMARY KEY'];
102
+ const colQueries = [];
103
+ const colHasPrimaryKey = Object.entries(collection.fields).find(
104
+ ([, field]) => hasPrimaryKey(field)
105
+ );
106
+ if (!colHasPrimaryKey) {
107
+ colQueries.push("_id INTEGER PRIMARY KEY");
108
+ }
90
109
  for (const [columnName, column] of Object.entries(collection.fields)) {
91
110
  const colQuery = `${sqlite.escapeName(columnName)} ${schemaTypeToSqlType(
92
111
  column.type
@@ -107,28 +126,37 @@ function schemaTypeToSqlType(type) {
107
126
  return "integer";
108
127
  }
109
128
  }
110
- function getModifiers(columnName, column) {
129
+ function getModifiers(fieldName, field) {
111
130
  let modifiers = "";
112
- if (!column.optional) {
131
+ if (hasPrimaryKey(field)) {
132
+ return " PRIMARY KEY";
133
+ }
134
+ if (!field.optional) {
113
135
  modifiers += " NOT NULL";
114
136
  }
115
- if (column.unique) {
137
+ if (field.unique) {
116
138
  modifiers += " UNIQUE";
117
139
  }
118
- if (hasDefault(column)) {
119
- modifiers += ` DEFAULT ${getDefaultValueSql(columnName, column)}`;
140
+ if (hasDefault(field)) {
141
+ modifiers += ` DEFAULT ${getDefaultValueSql(fieldName, field)}`;
120
142
  }
121
143
  return modifiers;
122
144
  }
123
145
  function hasDefault(field) {
124
- return field.default !== void 0;
146
+ if (field.default !== void 0) {
147
+ return true;
148
+ }
149
+ if (hasPrimaryKey(field) && field.type === "number") {
150
+ return true;
151
+ }
152
+ return false;
125
153
  }
126
154
  function getDefaultValueSql(columnName, column) {
127
155
  switch (column.type) {
128
156
  case "boolean":
129
157
  return column.default ? "TRUE" : "FALSE";
130
158
  case "number":
131
- return `${column.default}`;
159
+ return `${column.default || "AUTOINCREMENT"}`;
132
160
  case "text":
133
161
  return sqlite.escapeString(column.default);
134
162
  case "date":
@@ -138,7 +166,7 @@ function getDefaultValueSql(columnName, column) {
138
166
  try {
139
167
  stringified = JSON.stringify(column.default);
140
168
  } catch (e) {
141
- console.info(
169
+ console.log(
142
170
  `Invalid default value for column ${bold(
143
171
  columnName
144
172
  )}. Defaults must be valid JSON when using the \`json()\` type.`
@@ -149,9 +177,6 @@ function getDefaultValueSql(columnName, column) {
149
177
  }
150
178
  }
151
179
  }
152
- function generateId() {
153
- return nanoid(12);
154
- }
155
180
  const dateType = customType({
156
181
  dataType() {
157
182
  return "text";
@@ -174,14 +199,11 @@ const jsonType = customType({
174
199
  return JSON.parse(value);
175
200
  }
176
201
  });
177
- const initialColumns = {
178
- id: text("id").primaryKey().$default(() => generateId())
179
- };
180
202
  function collectionToTable(name, collection, isJsonSerializable = true) {
181
- const columns = {
182
- // Spread to avoid mutating `initialColumns`
183
- ...initialColumns
184
- };
203
+ const columns = {};
204
+ if (!Object.entries(collection.fields).some(([, field]) => hasPrimaryKey(field))) {
205
+ columns["_id"] = integer("_id").primaryKey();
206
+ }
185
207
  for (const [fieldName, field] of Object.entries(collection.fields)) {
186
208
  columns[fieldName] = columnMapper(fieldName, field, isJsonSerializable);
187
209
  }
@@ -195,12 +217,16 @@ function columnMapper(fieldName, field, isJsonSerializable) {
195
217
  c = text(fieldName);
196
218
  if (field.default !== void 0)
197
219
  c = c.default(field.default);
220
+ if (field.primaryKey === true)
221
+ c = c.primaryKey();
198
222
  break;
199
223
  }
200
224
  case "number": {
201
225
  c = integer(fieldName);
202
226
  if (field.default !== void 0)
203
227
  c = c.default(field.default);
228
+ if (field.primaryKey === true)
229
+ c = c.primaryKey({ autoIncrement: true });
204
230
  break;
205
231
  }
206
232
  case "boolean": {
@@ -246,5 +272,9 @@ export {
246
272
  createLocalDatabaseClient,
247
273
  createRemoteDatabaseClient,
248
274
  getCreateTableQuery,
275
+ getModifiers,
276
+ hasDefault,
277
+ hasPrimaryKey,
278
+ schemaTypeToSqlType,
249
279
  setupDbTables
250
280
  };
@@ -1,9 +1,12 @@
1
- import type { DBCollections } from './types.js';
1
+ import type { DBSnapshot } from './types.js';
2
+ import type { AstroConfig } from 'astro';
2
3
  export declare function getMigrations(): Promise<string[]>;
3
4
  export declare function loadMigration(migration: string): Promise<{
4
5
  diff: any[];
5
6
  db: string[];
6
7
  }>;
7
- export declare function loadInitialSnapshot(): Promise<DBCollections>;
8
- export declare function initializeMigrationsDirectory(currentSnapshot: unknown): Promise<void>;
9
- export declare function initializeFromMigrations(allMigrationFiles: string[]): Promise<DBCollections>;
8
+ export declare function loadInitialSnapshot(): Promise<DBSnapshot>;
9
+ export declare function initializeMigrationsDirectory(currentSnapshot: DBSnapshot): Promise<void>;
10
+ export declare function initializeFromMigrations(allMigrationFiles: string[]): Promise<DBSnapshot>;
11
+ export declare function createCurrentSnapshot(config: AstroConfig): DBSnapshot;
12
+ export declare function createEmptySnapshot(): DBSnapshot;
@@ -14,7 +14,14 @@ async function loadMigration(migration) {
14
14
  return JSON.parse(await readFile(`./migrations/${migration}`, "utf-8"));
15
15
  }
16
16
  async function loadInitialSnapshot() {
17
- return JSON.parse(await readFile("./migrations/0000_snapshot.json", "utf-8"));
17
+ const snapshot = JSON.parse(await readFile("./migrations/0000_snapshot.json", "utf-8"));
18
+ if (snapshot.experimentalVersion === 1) {
19
+ return snapshot;
20
+ }
21
+ if (!snapshot.schema) {
22
+ return { experimentalVersion: 1, schema: snapshot };
23
+ }
24
+ throw new Error("Invalid snapshot format");
18
25
  }
19
26
  async function initializeMigrationsDirectory(currentSnapshot) {
20
27
  await mkdir("./migrations", { recursive: true });
@@ -32,7 +39,16 @@ async function initializeFromMigrations(allMigrationFiles) {
32
39
  }
33
40
  return prevSnapshot;
34
41
  }
42
+ function createCurrentSnapshot(config) {
43
+ const schema = JSON.parse(JSON.stringify(config.db?.collections ?? {}));
44
+ return { experimentalVersion: 1, schema };
45
+ }
46
+ function createEmptySnapshot() {
47
+ return { experimentalVersion: 1, schema: {} };
48
+ }
35
49
  export {
50
+ createCurrentSnapshot,
51
+ createEmptySnapshot,
36
52
  getMigrations,
37
53
  initializeFromMigrations,
38
54
  initializeMigrationsDirectory,
package/dist/typegen.js CHANGED
@@ -17,41 +17,24 @@ ${Object.entries(collections).map(([name, collection]) => generateTableType(name
17
17
  await writeFile(new URL(DB_TYPES_FILE, dotAstroDir), content);
18
18
  }
19
19
  function generateTableType(name, collection) {
20
- let tableType = ` export const ${name}: import(${INTERNAL_MOD_IMPORT}).AstroTable<{
21
- name: ${JSON.stringify(name)};
22
- columns: {
23
- id: import(${INTERNAL_MOD_IMPORT}).AstroId<{
24
- tableName: ${JSON.stringify(name)};
25
- }>;`;
26
- for (const [fieldName, field] of Object.entries(collection.fields)) {
27
- const drizzleInterface = schemaTypeToDrizzleInterface(field.type);
28
- tableType += `
29
- ${fieldName}: import(${INTERNAL_MOD_IMPORT}).${drizzleInterface}<{
30
- tableName: ${JSON.stringify(name)};
31
- name: ${JSON.stringify(fieldName)};
32
- notNull: ${field.optional ? "false" : "true"};
33
- hasDefault: ${typeof field.default !== "undefined" ? "true" : "false"};
34
- }>;`;
35
- }
36
- tableType += `
37
- };
38
- }>;`;
20
+ let tableType = ` export const ${name}: import(${INTERNAL_MOD_IMPORT}).Table<
21
+ ${JSON.stringify(name)},
22
+ ${JSON.stringify(
23
+ Object.fromEntries(
24
+ Object.entries(collection.fields).map(([fieldName, field]) => [
25
+ fieldName,
26
+ {
27
+ // Only select fields Drizzle needs for inference
28
+ type: field.type,
29
+ optional: field.optional,
30
+ default: field.default
31
+ }
32
+ ])
33
+ )
34
+ )}
35
+ >;`;
39
36
  return tableType;
40
37
  }
41
- function schemaTypeToDrizzleInterface(type) {
42
- switch (type) {
43
- case "text":
44
- return "AstroText";
45
- case "number":
46
- return "AstroNumber";
47
- case "boolean":
48
- return "AstroBoolean";
49
- case "date":
50
- return "AstroDate";
51
- case "json":
52
- return "AstroJson";
53
- }
54
- }
55
38
  export {
56
39
  typegen
57
40
  };