@astrojs/db 0.1.2 → 0.1.3

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
@@ -17,6 +17,9 @@ import {
17
17
  import { z } from "zod";
18
18
  import { nanoid } from "nanoid";
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
  }
@@ -55,8 +58,10 @@ async function createLocalDatabaseClient({
55
58
  }
56
59
  async function setupDbTables({
57
60
  db,
61
+ data,
58
62
  collections,
59
- logger
63
+ logger,
64
+ mode
60
65
  }) {
61
66
  const setupQueries = [];
62
67
  for (const [name, collection] of Object.entries(collections)) {
@@ -67,6 +72,15 @@ async function setupDbTables({
67
72
  for (const q of setupQueries) {
68
73
  await db.run(q);
69
74
  }
75
+ if (data) {
76
+ const ormObjects = Object.fromEntries(
77
+ Object.entries(collections).map(([name, collection]) => {
78
+ const table = collectionToTable(name, collection, false);
79
+ return [name, table];
80
+ })
81
+ );
82
+ await data({ db, ...ormObjects, mode });
83
+ }
70
84
  for (const [name, collection] of Object.entries(collections)) {
71
85
  if (!isReadableCollection(collection) || !collection.data)
72
86
  continue;
@@ -86,7 +100,13 @@ ${e}`
86
100
  }
87
101
  function getCreateTableQuery(collectionName, collection) {
88
102
  let query = `CREATE TABLE ${sqlite.escapeName(collectionName)} (`;
89
- const colQueries = ['"id" text PRIMARY KEY'];
103
+ const colQueries = [];
104
+ const colHasPrimaryKey = Object.entries(collection.fields).find(
105
+ ([, field]) => hasPrimaryKey(field)
106
+ );
107
+ if (!colHasPrimaryKey) {
108
+ colQueries.push("_id INTEGER PRIMARY KEY");
109
+ }
90
110
  for (const [columnName, column] of Object.entries(collection.fields)) {
91
111
  const colQuery = `${sqlite.escapeName(columnName)} ${schemaTypeToSqlType(
92
112
  column.type
@@ -107,28 +127,37 @@ function schemaTypeToSqlType(type) {
107
127
  return "integer";
108
128
  }
109
129
  }
110
- function getModifiers(columnName, column) {
130
+ function getModifiers(fieldName, field) {
111
131
  let modifiers = "";
112
- if (!column.optional) {
132
+ if (hasPrimaryKey(field)) {
133
+ return " PRIMARY KEY";
134
+ }
135
+ if (!field.optional) {
113
136
  modifiers += " NOT NULL";
114
137
  }
115
- if (column.unique) {
138
+ if (field.unique) {
116
139
  modifiers += " UNIQUE";
117
140
  }
118
- if (hasDefault(column)) {
119
- modifiers += ` DEFAULT ${getDefaultValueSql(columnName, column)}`;
141
+ if (hasDefault(field)) {
142
+ modifiers += ` DEFAULT ${getDefaultValueSql(fieldName, field)}`;
120
143
  }
121
144
  return modifiers;
122
145
  }
123
146
  function hasDefault(field) {
124
- return field.default !== void 0;
147
+ if (field.default !== void 0) {
148
+ return true;
149
+ }
150
+ if (hasPrimaryKey(field) && field.type === "number") {
151
+ return true;
152
+ }
153
+ return false;
125
154
  }
126
155
  function getDefaultValueSql(columnName, column) {
127
156
  switch (column.type) {
128
157
  case "boolean":
129
158
  return column.default ? "TRUE" : "FALSE";
130
159
  case "number":
131
- return `${column.default}`;
160
+ return `${column.default || "AUTOINCREMENT"}`;
132
161
  case "text":
133
162
  return sqlite.escapeString(column.default);
134
163
  case "date":
@@ -138,7 +167,7 @@ function getDefaultValueSql(columnName, column) {
138
167
  try {
139
168
  stringified = JSON.stringify(column.default);
140
169
  } catch (e) {
141
- console.info(
170
+ console.log(
142
171
  `Invalid default value for column ${bold(
143
172
  columnName
144
173
  )}. Defaults must be valid JSON when using the \`json()\` type.`
@@ -149,9 +178,6 @@ function getDefaultValueSql(columnName, column) {
149
178
  }
150
179
  }
151
180
  }
152
- function generateId() {
153
- return nanoid(12);
154
- }
155
181
  const dateType = customType({
156
182
  dataType() {
157
183
  return "text";
@@ -174,14 +200,11 @@ const jsonType = customType({
174
200
  return JSON.parse(value);
175
201
  }
176
202
  });
177
- const initialColumns = {
178
- id: text("id").primaryKey().$default(() => generateId())
179
- };
180
203
  function collectionToTable(name, collection, isJsonSerializable = true) {
181
- const columns = {
182
- // Spread to avoid mutating `initialColumns`
183
- ...initialColumns
184
- };
204
+ const columns = {};
205
+ if (!Object.entries(collection.fields).some(([, field]) => hasPrimaryKey(field))) {
206
+ columns["_id"] = integer("_id").primaryKey();
207
+ }
185
208
  for (const [fieldName, field] of Object.entries(collection.fields)) {
186
209
  columns[fieldName] = columnMapper(fieldName, field, isJsonSerializable);
187
210
  }
@@ -195,12 +218,16 @@ function columnMapper(fieldName, field, isJsonSerializable) {
195
218
  c = text(fieldName);
196
219
  if (field.default !== void 0)
197
220
  c = c.default(field.default);
221
+ if (field.primaryKey === true)
222
+ c = c.primaryKey();
198
223
  break;
199
224
  }
200
225
  case "number": {
201
226
  c = integer(fieldName);
202
227
  if (field.default !== void 0)
203
228
  c = c.default(field.default);
229
+ if (field.primaryKey === true)
230
+ c = c.primaryKey({ autoIncrement: true });
204
231
  break;
205
232
  }
206
233
  case "boolean": {
@@ -246,5 +273,9 @@ export {
246
273
  createLocalDatabaseClient,
247
274
  createRemoteDatabaseClient,
248
275
  getCreateTableQuery,
276
+ getModifiers,
277
+ hasDefault,
278
+ hasPrimaryKey,
279
+ schemaTypeToSqlType,
249
280
  setupDbTables
250
281
  };
@@ -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
  };