@astrojs/db 0.1.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.
- package/LICENSE +59 -0
- package/components/Renderer.astro +14 -0
- package/components/astro-env.d.ts +1 -0
- package/components/index.ts +2 -0
- package/components/tsconfig.json +7 -0
- package/config-augment.d.ts +4 -0
- package/dist/cli/commands/push/index.d.ts +6 -0
- package/dist/cli/commands/push/index.js +144 -0
- package/dist/cli/commands/sync/index.d.ts +6 -0
- package/dist/cli/commands/sync/index.js +45 -0
- package/dist/cli/commands/verify/index.d.ts +6 -0
- package/dist/cli/commands/verify/index.js +25 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +24 -0
- package/dist/cli/queries.d.ts +19 -0
- package/dist/cli/queries.js +453 -0
- package/dist/cli/seed.d.ts +6 -0
- package/dist/cli/sync/admin.d.ts +33 -0
- package/dist/cli/sync/index.d.ts +1 -0
- package/dist/cli/sync/index.js +0 -0
- package/dist/cli/sync/migrate.d.ts +1 -0
- package/dist/cli/sync/migrate.js +0 -0
- package/dist/cli/sync/queries.d.ts +19 -0
- package/dist/cli/sync/remote-db.d.ts +1 -0
- package/dist/config.d.ts +1149 -0
- package/dist/config.js +53 -0
- package/dist/consts.d.ts +6 -0
- package/dist/consts.js +19 -0
- package/dist/error-map.d.ts +6 -0
- package/dist/error-map.js +79 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +15 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +10 -0
- package/dist/integration.d.ts +2 -0
- package/dist/integration.js +68 -0
- package/dist/internal-drizzle.d.ts +1 -0
- package/dist/internal-drizzle.js +48 -0
- package/dist/internal.d.ts +50 -0
- package/dist/internal.js +250 -0
- package/dist/load-astro-config.d.ts +6 -0
- package/dist/load-astro-config.js +79 -0
- package/dist/migrations.d.ts +9 -0
- package/dist/migrations.js +41 -0
- package/dist/typegen.d.ts +5 -0
- package/dist/typegen.js +57 -0
- package/dist/types.d.ts +1367 -0
- package/dist/types.js +58 -0
- package/dist/utils.d.ts +59 -0
- package/dist/utils.js +84 -0
- package/dist/vite-plugin-db.d.ts +19 -0
- package/dist/vite-plugin-db.js +66 -0
- package/dist/vite-plugin-inject-env-ts.d.ts +9 -0
- package/dist/vite-plugin-inject-env-ts.js +49 -0
- package/index.d.ts +3 -0
- package/package.json +72 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import * as color from "kleur/colors";
|
|
2
|
+
import { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core";
|
|
3
|
+
import { customAlphabet } from "nanoid";
|
|
4
|
+
import prompts from "prompts";
|
|
5
|
+
const sqlite = new SQLiteAsyncDialect();
|
|
6
|
+
const genTempTableName = customAlphabet("abcdefghijklmnopqrstuvwxyz", 10);
|
|
7
|
+
async function getMigrationQueries({
|
|
8
|
+
oldCollections,
|
|
9
|
+
newCollections,
|
|
10
|
+
promptResponses
|
|
11
|
+
}) {
|
|
12
|
+
const queries = [];
|
|
13
|
+
let added = getAddedCollections(oldCollections, newCollections);
|
|
14
|
+
let dropped = getDroppedCollections(oldCollections, newCollections);
|
|
15
|
+
if (!isEmpty(added) && !isEmpty(dropped)) {
|
|
16
|
+
const resolved = await resolveCollectionRenames(
|
|
17
|
+
added,
|
|
18
|
+
dropped,
|
|
19
|
+
promptResponses?.collectionRenames
|
|
20
|
+
);
|
|
21
|
+
added = resolved.added;
|
|
22
|
+
dropped = resolved.dropped;
|
|
23
|
+
for (const { from, to } of resolved.renamed) {
|
|
24
|
+
const renameQuery = `ALTER TABLE ${sqlite.escapeName(from)} RENAME TO ${sqlite.escapeName(
|
|
25
|
+
to
|
|
26
|
+
)}`;
|
|
27
|
+
queries.push(renameQuery);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
for (const [collectionName, collection] of Object.entries(added)) {
|
|
31
|
+
queries.push(getCreateTableQuery(collectionName, collection));
|
|
32
|
+
}
|
|
33
|
+
for (const [collectionName] of Object.entries(dropped)) {
|
|
34
|
+
const dropQuery = `DROP TABLE ${sqlite.escapeName(collectionName)}`;
|
|
35
|
+
queries.push(dropQuery);
|
|
36
|
+
}
|
|
37
|
+
for (const [collectionName, newCollection] of Object.entries(newCollections)) {
|
|
38
|
+
const oldCollection = oldCollections[collectionName];
|
|
39
|
+
if (!oldCollection)
|
|
40
|
+
continue;
|
|
41
|
+
const collectionChangeQueries = await getCollectionChangeQueries({
|
|
42
|
+
collectionName,
|
|
43
|
+
oldCollection,
|
|
44
|
+
newCollection,
|
|
45
|
+
promptResponses
|
|
46
|
+
});
|
|
47
|
+
queries.push(...collectionChangeQueries);
|
|
48
|
+
}
|
|
49
|
+
return queries;
|
|
50
|
+
}
|
|
51
|
+
async function getCollectionChangeQueries({
|
|
52
|
+
collectionName,
|
|
53
|
+
oldCollection,
|
|
54
|
+
newCollection,
|
|
55
|
+
promptResponses
|
|
56
|
+
}) {
|
|
57
|
+
const queries = [];
|
|
58
|
+
const updated = getUpdatedFields(oldCollection.fields, newCollection.fields);
|
|
59
|
+
let added = getAddedFields(oldCollection.fields, newCollection.fields);
|
|
60
|
+
let dropped = getDroppedFields(oldCollection.fields, newCollection.fields);
|
|
61
|
+
if (isEmpty(updated) && isEmpty(added) && isEmpty(dropped)) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
if (!isEmpty(added) && !isEmpty(dropped)) {
|
|
65
|
+
const resolved = await resolveFieldRenames(
|
|
66
|
+
collectionName,
|
|
67
|
+
added,
|
|
68
|
+
dropped,
|
|
69
|
+
promptResponses?.fieldRenames
|
|
70
|
+
);
|
|
71
|
+
added = resolved.added;
|
|
72
|
+
dropped = resolved.dropped;
|
|
73
|
+
queries.push(...getFieldRenameQueries(collectionName, resolved.renamed));
|
|
74
|
+
}
|
|
75
|
+
if (isEmpty(updated) && Object.values(dropped).every(canAlterTableDropColumn) && Object.values(added).every(canAlterTableAddColumn)) {
|
|
76
|
+
queries.push(...getAlterTableQueries(collectionName, added, dropped));
|
|
77
|
+
return queries;
|
|
78
|
+
}
|
|
79
|
+
const dataLossCheck = canRecreateTableWithoutDataLoss(added, updated);
|
|
80
|
+
if (dataLossCheck.dataLoss) {
|
|
81
|
+
let allowDataLoss = promptResponses?.allowDataLoss;
|
|
82
|
+
const nameMsg = `Type the collection name ${color.blue(
|
|
83
|
+
collectionName
|
|
84
|
+
)} to confirm you want to delete all data:`;
|
|
85
|
+
const { reason, fieldName } = dataLossCheck;
|
|
86
|
+
const reasonMsgs = {
|
|
87
|
+
"added-required": `Adding required ${color.blue(
|
|
88
|
+
color.bold(collectionName)
|
|
89
|
+
)} field ${color.blue(color.bold(fieldName))}. ${color.red(
|
|
90
|
+
"This will delete all existing data in the collection!"
|
|
91
|
+
)} We recommend setting a default value to avoid data loss.`,
|
|
92
|
+
"updated-required": `Changing ${color.blue(color.bold(collectionName))} field ${color.blue(
|
|
93
|
+
color.bold(fieldName)
|
|
94
|
+
)} to required. ${color.red("This will delete all existing data in the collection!")}`,
|
|
95
|
+
"updated-unique": `Changing ${color.blue(color.bold(collectionName))} field ${color.blue(
|
|
96
|
+
color.bold(fieldName)
|
|
97
|
+
)} to unique. ${color.red("This will delete all existing data in the collection!")}`,
|
|
98
|
+
"updated-type": `Changing the type of ${color.blue(
|
|
99
|
+
color.bold(collectionName)
|
|
100
|
+
)} field ${color.blue(color.bold(fieldName))}. ${color.red(
|
|
101
|
+
"This will delete all existing data in the collection!"
|
|
102
|
+
)}`
|
|
103
|
+
};
|
|
104
|
+
if (allowDataLoss === void 0) {
|
|
105
|
+
const res = await prompts({
|
|
106
|
+
type: "text",
|
|
107
|
+
name: "allowDataLoss",
|
|
108
|
+
message: `${reasonMsgs[reason]} ${nameMsg}`,
|
|
109
|
+
validate: (name) => name === collectionName || "Incorrect collection name"
|
|
110
|
+
});
|
|
111
|
+
if (typeof res.allowDataLoss !== "string")
|
|
112
|
+
process.exit(0);
|
|
113
|
+
allowDataLoss = !!res.allowDataLoss;
|
|
114
|
+
}
|
|
115
|
+
if (!allowDataLoss) {
|
|
116
|
+
console.log("Exiting without changes \u{1F44B}");
|
|
117
|
+
process.exit(0);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const recreateTableQueries = getRecreateTableQueries({
|
|
121
|
+
unescCollectionName: collectionName,
|
|
122
|
+
newCollection,
|
|
123
|
+
added,
|
|
124
|
+
hasDataLoss: dataLossCheck.dataLoss
|
|
125
|
+
});
|
|
126
|
+
queries.push(...recreateTableQueries);
|
|
127
|
+
return queries;
|
|
128
|
+
}
|
|
129
|
+
async function resolveFieldRenames(collectionName, mightAdd, mightDrop, renamePromptResponses) {
|
|
130
|
+
const added = {};
|
|
131
|
+
const dropped = {};
|
|
132
|
+
const renamed = [];
|
|
133
|
+
for (const [fieldName, field] of Object.entries(mightAdd)) {
|
|
134
|
+
const promptResponse = renamePromptResponses?.[fieldName];
|
|
135
|
+
if (promptResponse === false) {
|
|
136
|
+
added[fieldName] = field;
|
|
137
|
+
continue;
|
|
138
|
+
} else if (promptResponse) {
|
|
139
|
+
renamed.push({ from: promptResponse, to: fieldName });
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const res = await prompts({
|
|
143
|
+
type: "toggle",
|
|
144
|
+
name: "isRename",
|
|
145
|
+
message: `Is the field ${color.blue(color.bold(fieldName))} in collection ${color.blue(
|
|
146
|
+
color.bold(collectionName)
|
|
147
|
+
)} a new field, or renaming an existing field?`,
|
|
148
|
+
initial: false,
|
|
149
|
+
active: "Rename",
|
|
150
|
+
inactive: "New field"
|
|
151
|
+
});
|
|
152
|
+
if (typeof res.isRename !== "boolean")
|
|
153
|
+
process.exit(0);
|
|
154
|
+
if (!res.isRename) {
|
|
155
|
+
added[fieldName] = field;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const choices = Object.keys(mightDrop).filter((key) => !(key in renamed)).map((key) => ({ title: key, value: key }));
|
|
159
|
+
const { oldFieldName } = await prompts({
|
|
160
|
+
type: "select",
|
|
161
|
+
name: "oldFieldName",
|
|
162
|
+
message: `Which field in ${color.blue(
|
|
163
|
+
color.bold(collectionName)
|
|
164
|
+
)} should be renamed to ${color.blue(color.bold(fieldName))}?`,
|
|
165
|
+
choices
|
|
166
|
+
});
|
|
167
|
+
if (typeof oldFieldName !== "string")
|
|
168
|
+
process.exit(0);
|
|
169
|
+
renamed.push({ from: oldFieldName, to: fieldName });
|
|
170
|
+
for (const [droppedFieldName, droppedField] of Object.entries(mightDrop)) {
|
|
171
|
+
if (!renamed.find((r) => r.from === droppedFieldName))
|
|
172
|
+
dropped[droppedFieldName] = droppedField;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { added, dropped, renamed };
|
|
176
|
+
}
|
|
177
|
+
async function resolveCollectionRenames(mightAdd, mightDrop, renamePromptResponses) {
|
|
178
|
+
const added = {};
|
|
179
|
+
const dropped = {};
|
|
180
|
+
const renamed = [];
|
|
181
|
+
for (const [collectionName, collection] of Object.entries(mightAdd)) {
|
|
182
|
+
const promptResponse = renamePromptResponses?.[collectionName];
|
|
183
|
+
if (promptResponse === false) {
|
|
184
|
+
added[collectionName] = collection;
|
|
185
|
+
continue;
|
|
186
|
+
} else if (promptResponse) {
|
|
187
|
+
renamed.push({ from: promptResponse, to: collectionName });
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const res = await prompts({
|
|
191
|
+
type: "toggle",
|
|
192
|
+
name: "isRename",
|
|
193
|
+
message: `Is the collection ${color.blue(
|
|
194
|
+
color.bold(collectionName)
|
|
195
|
+
)} a new collection, or renaming an existing collection?`,
|
|
196
|
+
initial: false,
|
|
197
|
+
active: "Rename",
|
|
198
|
+
inactive: "New collection"
|
|
199
|
+
});
|
|
200
|
+
if (typeof res.isRename !== "boolean")
|
|
201
|
+
process.exit(0);
|
|
202
|
+
if (!res.isRename) {
|
|
203
|
+
added[collectionName] = collection;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const choices = Object.keys(mightDrop).filter((key) => !(key in renamed)).map((key) => ({ title: key, value: key }));
|
|
207
|
+
const { oldCollectionName } = await prompts({
|
|
208
|
+
type: "select",
|
|
209
|
+
name: "oldCollectionName",
|
|
210
|
+
message: `Which collection should be renamed to ${color.blue(color.bold(collectionName))}?`,
|
|
211
|
+
choices
|
|
212
|
+
});
|
|
213
|
+
if (typeof oldCollectionName !== "string")
|
|
214
|
+
process.exit(0);
|
|
215
|
+
renamed.push({ from: oldCollectionName, to: collectionName });
|
|
216
|
+
for (const [droppedCollectionName, droppedCollection] of Object.entries(mightDrop)) {
|
|
217
|
+
if (!renamed.find((r) => r.from === droppedCollectionName))
|
|
218
|
+
dropped[droppedCollectionName] = droppedCollection;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return { added, dropped, renamed };
|
|
222
|
+
}
|
|
223
|
+
function getAddedCollections(oldCollections, newCollections) {
|
|
224
|
+
const added = {};
|
|
225
|
+
for (const [key, newCollection] of Object.entries(newCollections)) {
|
|
226
|
+
if (!(key in oldCollections))
|
|
227
|
+
added[key] = newCollection;
|
|
228
|
+
}
|
|
229
|
+
return added;
|
|
230
|
+
}
|
|
231
|
+
function getDroppedCollections(oldCollections, newCollections) {
|
|
232
|
+
const dropped = {};
|
|
233
|
+
for (const [key, oldCollection] of Object.entries(oldCollections)) {
|
|
234
|
+
if (!(key in newCollections))
|
|
235
|
+
dropped[key] = oldCollection;
|
|
236
|
+
}
|
|
237
|
+
return dropped;
|
|
238
|
+
}
|
|
239
|
+
function getFieldRenameQueries(unescCollectionName, renamed) {
|
|
240
|
+
const queries = [];
|
|
241
|
+
const collectionName = sqlite.escapeName(unescCollectionName);
|
|
242
|
+
for (const { from, to } of renamed) {
|
|
243
|
+
const q = `ALTER TABLE ${collectionName} RENAME COLUMN ${sqlite.escapeName(
|
|
244
|
+
from
|
|
245
|
+
)} TO ${sqlite.escapeName(to)}`;
|
|
246
|
+
queries.push(q);
|
|
247
|
+
}
|
|
248
|
+
return queries;
|
|
249
|
+
}
|
|
250
|
+
function getAlterTableQueries(unescCollectionName, added, dropped) {
|
|
251
|
+
const queries = [];
|
|
252
|
+
const collectionName = sqlite.escapeName(unescCollectionName);
|
|
253
|
+
for (const [unescFieldName, field] of Object.entries(added)) {
|
|
254
|
+
const fieldName = sqlite.escapeName(unescFieldName);
|
|
255
|
+
const type = schemaTypeToSqlType(field.type);
|
|
256
|
+
const q = `ALTER TABLE ${collectionName} ADD COLUMN ${fieldName} ${type}${getModifiers(
|
|
257
|
+
fieldName,
|
|
258
|
+
field
|
|
259
|
+
)}`;
|
|
260
|
+
queries.push(q);
|
|
261
|
+
}
|
|
262
|
+
for (const unescFieldName of Object.keys(dropped)) {
|
|
263
|
+
const fieldName = sqlite.escapeName(unescFieldName);
|
|
264
|
+
const q = `ALTER TABLE ${collectionName} DROP COLUMN ${fieldName}`;
|
|
265
|
+
queries.push(q);
|
|
266
|
+
}
|
|
267
|
+
return queries;
|
|
268
|
+
}
|
|
269
|
+
function getRecreateTableQueries({
|
|
270
|
+
unescCollectionName,
|
|
271
|
+
newCollection,
|
|
272
|
+
added,
|
|
273
|
+
hasDataLoss
|
|
274
|
+
}) {
|
|
275
|
+
const unescTempName = `${unescCollectionName}_${genTempTableName()}`;
|
|
276
|
+
const tempName = sqlite.escapeName(unescTempName);
|
|
277
|
+
const collectionName = sqlite.escapeName(unescCollectionName);
|
|
278
|
+
const queries = [getCreateTableQuery(unescTempName, newCollection)];
|
|
279
|
+
if (!hasDataLoss) {
|
|
280
|
+
const newColumns = ["id", ...Object.keys(newCollection.fields)];
|
|
281
|
+
const originalColumns = newColumns.filter((i) => !(i in added));
|
|
282
|
+
const escapedColumns = originalColumns.map((c) => sqlite.escapeName(c)).join(", ");
|
|
283
|
+
queries.push(
|
|
284
|
+
`INSERT INTO ${tempName} (${escapedColumns}) SELECT ${escapedColumns} FROM ${collectionName}`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
queries.push(`DROP TABLE ${collectionName}`);
|
|
288
|
+
queries.push(`ALTER TABLE ${tempName} RENAME TO ${collectionName}`);
|
|
289
|
+
return queries;
|
|
290
|
+
}
|
|
291
|
+
function getCreateTableQuery(collectionName, collection) {
|
|
292
|
+
let query = `CREATE TABLE ${sqlite.escapeName(collectionName)} (`;
|
|
293
|
+
const colQueries = ['"id" text PRIMARY KEY'];
|
|
294
|
+
for (const [columnName, column] of Object.entries(collection.fields)) {
|
|
295
|
+
const colQuery = `${sqlite.escapeName(columnName)} ${schemaTypeToSqlType(
|
|
296
|
+
column.type
|
|
297
|
+
)}${getModifiers(columnName, column)}`;
|
|
298
|
+
colQueries.push(colQuery);
|
|
299
|
+
}
|
|
300
|
+
query += colQueries.join(", ") + ")";
|
|
301
|
+
return query;
|
|
302
|
+
}
|
|
303
|
+
function getModifiers(columnName, column) {
|
|
304
|
+
let modifiers = "";
|
|
305
|
+
if (!column.optional) {
|
|
306
|
+
modifiers += " NOT NULL";
|
|
307
|
+
}
|
|
308
|
+
if (column.unique) {
|
|
309
|
+
modifiers += " UNIQUE";
|
|
310
|
+
}
|
|
311
|
+
if (hasDefault(column)) {
|
|
312
|
+
modifiers += ` DEFAULT ${getDefaultValueSql(columnName, column)}`;
|
|
313
|
+
}
|
|
314
|
+
return modifiers;
|
|
315
|
+
}
|
|
316
|
+
function schemaTypeToSqlType(type) {
|
|
317
|
+
switch (type) {
|
|
318
|
+
case "date":
|
|
319
|
+
case "text":
|
|
320
|
+
case "json":
|
|
321
|
+
return "text";
|
|
322
|
+
case "number":
|
|
323
|
+
case "boolean":
|
|
324
|
+
return "integer";
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function isEmpty(obj) {
|
|
328
|
+
return Object.keys(obj).length === 0;
|
|
329
|
+
}
|
|
330
|
+
function canAlterTableAddColumn(column) {
|
|
331
|
+
if (column.unique)
|
|
332
|
+
return false;
|
|
333
|
+
if (hasRuntimeDefault(column))
|
|
334
|
+
return false;
|
|
335
|
+
if (!column.optional && !hasDefault(column))
|
|
336
|
+
return false;
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
function canAlterTableDropColumn(column) {
|
|
340
|
+
if (column.unique)
|
|
341
|
+
return false;
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
function canRecreateTableWithoutDataLoss(added, updated) {
|
|
345
|
+
for (const [fieldName, a] of Object.entries(added)) {
|
|
346
|
+
if (!a.optional && !hasDefault(a))
|
|
347
|
+
return { dataLoss: true, fieldName, reason: "added-required" };
|
|
348
|
+
}
|
|
349
|
+
for (const [fieldName, u] of Object.entries(updated)) {
|
|
350
|
+
if (u.old.optional && !u.new.optional)
|
|
351
|
+
return { dataLoss: true, fieldName, reason: "updated-required" };
|
|
352
|
+
if (!u.old.unique && u.new.unique)
|
|
353
|
+
return { dataLoss: true, fieldName, reason: "updated-unique" };
|
|
354
|
+
if (u.old.type !== u.new.type && !canChangeTypeWithoutQuery(u.old, u.new))
|
|
355
|
+
return { dataLoss: true, fieldName, reason: "updated-type" };
|
|
356
|
+
}
|
|
357
|
+
return { dataLoss: false };
|
|
358
|
+
}
|
|
359
|
+
function getAddedFields(oldFields, newFields) {
|
|
360
|
+
const added = {};
|
|
361
|
+
for (const [key, newField] of Object.entries(newFields)) {
|
|
362
|
+
if (!(key in oldFields))
|
|
363
|
+
added[key] = newField;
|
|
364
|
+
}
|
|
365
|
+
return added;
|
|
366
|
+
}
|
|
367
|
+
function getDroppedFields(oldFields, newFields) {
|
|
368
|
+
const dropped = {};
|
|
369
|
+
for (const [key, oldField] of Object.entries(oldFields)) {
|
|
370
|
+
if (!(key in newFields))
|
|
371
|
+
dropped[key] = oldField;
|
|
372
|
+
}
|
|
373
|
+
return dropped;
|
|
374
|
+
}
|
|
375
|
+
function getUpdatedFields(oldFields, newFields) {
|
|
376
|
+
const updated = {};
|
|
377
|
+
for (const [key, newField] of Object.entries(newFields)) {
|
|
378
|
+
const oldField = oldFields[key];
|
|
379
|
+
if (!oldField)
|
|
380
|
+
continue;
|
|
381
|
+
if (objShallowEqual(oldField, newField))
|
|
382
|
+
continue;
|
|
383
|
+
const oldFieldSqlType = { ...oldField, type: schemaTypeToSqlType(oldField.type) };
|
|
384
|
+
const newFieldSqlType = { ...newField, type: schemaTypeToSqlType(newField.type) };
|
|
385
|
+
const isSafeTypeUpdate = objShallowEqual(oldFieldSqlType, newFieldSqlType) && canChangeTypeWithoutQuery(oldField, newField);
|
|
386
|
+
if (isSafeTypeUpdate)
|
|
387
|
+
continue;
|
|
388
|
+
updated[key] = { old: oldField, new: newField };
|
|
389
|
+
}
|
|
390
|
+
return updated;
|
|
391
|
+
}
|
|
392
|
+
const typeChangesWithoutQuery = [
|
|
393
|
+
{ from: "boolean", to: "number" },
|
|
394
|
+
{ from: "date", to: "text" },
|
|
395
|
+
{ from: "json", to: "text" }
|
|
396
|
+
// TODO: decide on these. They *could* work with SQLite CAST
|
|
397
|
+
// { from: 'boolean', to: 'text' },
|
|
398
|
+
// { from: 'boolean', to: 'json' },
|
|
399
|
+
// { from: 'number', to: 'text' },
|
|
400
|
+
// { from: 'number', to: 'json' },
|
|
401
|
+
];
|
|
402
|
+
function canChangeTypeWithoutQuery(oldField, newField) {
|
|
403
|
+
return typeChangesWithoutQuery.some(
|
|
404
|
+
({ from, to }) => oldField.type === from && newField.type === to
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
function hasDefault(field) {
|
|
408
|
+
return field.default !== void 0;
|
|
409
|
+
}
|
|
410
|
+
function hasRuntimeDefault(field) {
|
|
411
|
+
return field.type === "date" && field.default === "now";
|
|
412
|
+
}
|
|
413
|
+
function getDefaultValueSql(columnName, column) {
|
|
414
|
+
switch (column.type) {
|
|
415
|
+
case "boolean":
|
|
416
|
+
return column.default ? "TRUE" : "FALSE";
|
|
417
|
+
case "number":
|
|
418
|
+
return `${column.default}`;
|
|
419
|
+
case "text":
|
|
420
|
+
return sqlite.escapeString(column.default);
|
|
421
|
+
case "date":
|
|
422
|
+
return column.default === "now" ? "CURRENT_TIMESTAMP" : sqlite.escapeString(column.default);
|
|
423
|
+
case "json": {
|
|
424
|
+
let stringified = "";
|
|
425
|
+
try {
|
|
426
|
+
stringified = JSON.stringify(column.default);
|
|
427
|
+
} catch (e) {
|
|
428
|
+
console.log(
|
|
429
|
+
`Invalid default value for column ${color.bold(
|
|
430
|
+
columnName
|
|
431
|
+
)}. Defaults must be valid JSON when using the \`json()\` type.`
|
|
432
|
+
);
|
|
433
|
+
process.exit(0);
|
|
434
|
+
}
|
|
435
|
+
return sqlite.escapeString(stringified);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function objShallowEqual(a, b) {
|
|
440
|
+
if (Object.keys(a).length !== Object.keys(b).length)
|
|
441
|
+
return false;
|
|
442
|
+
for (const [key, value] of Object.entries(a)) {
|
|
443
|
+
if (JSON.stringify(b[key]) !== JSON.stringify(value)) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
export {
|
|
450
|
+
getCollectionChangeQueries,
|
|
451
|
+
getCreateTableQuery,
|
|
452
|
+
getMigrationQueries
|
|
453
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const STUDIO_ADMIN_TABLE = "ReservedAstroStudioAdmin";
|
|
2
|
+
export declare const STUDIO_ADMIN_TABLE_ROW_ID = "admin";
|
|
3
|
+
export declare const adminTable: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
4
|
+
name: "ReservedAstroStudioAdmin";
|
|
5
|
+
schema: undefined;
|
|
6
|
+
columns: {
|
|
7
|
+
id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
8
|
+
name: "id";
|
|
9
|
+
tableName: "ReservedAstroStudioAdmin";
|
|
10
|
+
dataType: "string";
|
|
11
|
+
columnType: "SQLiteText";
|
|
12
|
+
data: string;
|
|
13
|
+
driverParam: string;
|
|
14
|
+
notNull: true;
|
|
15
|
+
hasDefault: false;
|
|
16
|
+
enumValues: [string, ...string[]];
|
|
17
|
+
baseColumn: never;
|
|
18
|
+
}, object>;
|
|
19
|
+
collections: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
20
|
+
name: "collections";
|
|
21
|
+
tableName: "ReservedAstroStudioAdmin";
|
|
22
|
+
dataType: "string";
|
|
23
|
+
columnType: "SQLiteText";
|
|
24
|
+
data: string;
|
|
25
|
+
driverParam: string;
|
|
26
|
+
notNull: true;
|
|
27
|
+
hasDefault: false;
|
|
28
|
+
enumValues: [string, ...string[]];
|
|
29
|
+
baseColumn: never;
|
|
30
|
+
}, object>;
|
|
31
|
+
};
|
|
32
|
+
dialect: "sqlite";
|
|
33
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DBCollection, DBCollections } from '../../types.js';
|
|
2
|
+
interface PromptResponses {
|
|
3
|
+
allowDataLoss: boolean;
|
|
4
|
+
fieldRenames: Record<string, string | false>;
|
|
5
|
+
collectionRenames: Record<string, string | false>;
|
|
6
|
+
}
|
|
7
|
+
export declare function getMigrationQueries({ oldCollections, newCollections, promptResponses, }: {
|
|
8
|
+
oldCollections: DBCollections;
|
|
9
|
+
newCollections: DBCollections;
|
|
10
|
+
promptResponses?: PromptResponses;
|
|
11
|
+
}): Promise<string[]>;
|
|
12
|
+
export declare function getCollectionChangeQueries({ collectionName, oldCollection, newCollection, promptResponses, }: {
|
|
13
|
+
collectionName: string;
|
|
14
|
+
oldCollection: DBCollection;
|
|
15
|
+
newCollection: DBCollection;
|
|
16
|
+
promptResponses?: PromptResponses;
|
|
17
|
+
}): Promise<string[]>;
|
|
18
|
+
export declare function getCreateTableQuery(collectionName: string, collection: DBCollection): string;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createDb(appToken: string): any;
|