@easy-cms/drizzle 0.5.0 → 0.7.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/dist/index.d.ts +25 -1
- package/dist/index.js +434 -226
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,10 @@ interface SqlRunner {
|
|
|
19
19
|
/** Runs the statements in one transaction: all or nothing. */
|
|
20
20
|
transaction(statements: readonly Statement[]): Promise<void>;
|
|
21
21
|
}
|
|
22
|
+
/** How to read a value out of a JSON element for comparing. */
|
|
23
|
+
type JsonValueType = 'text' | 'number' | 'integer' | 'boolean';
|
|
24
|
+
/** A value inside the JSON element being tested, e.g. `['heading', 'en']`. */
|
|
25
|
+
type JsonGetter = (path: readonly string[], type: JsonValueType) => SQL;
|
|
22
26
|
/** What differs between SQL dialects. */
|
|
23
27
|
interface Dialect {
|
|
24
28
|
readonly name: 'sqlite' | 'postgres';
|
|
@@ -39,6 +43,11 @@ interface Dialect {
|
|
|
39
43
|
json(name: string): ColumnBuilder;
|
|
40
44
|
/** Case-insensitive substring match; `pattern` is already escaped and wrapped in `%`. */
|
|
41
45
|
like(column: AnyColumn, pattern: string): SQL;
|
|
46
|
+
/**
|
|
47
|
+
* True when some element of a JSON array column matches `condition` (any element when it
|
|
48
|
+
* returns undefined). Path segments are field names, already checked against the config.
|
|
49
|
+
*/
|
|
50
|
+
someElement(column: AnyColumn, condition: (get: JsonGetter) => SQL | undefined): SQL;
|
|
42
51
|
/** Placeholder for the n-th parameter (1-based) in raw SQL. */
|
|
43
52
|
param(n: number): string;
|
|
44
53
|
/** CREATE TABLE IF NOT EXISTS for the migrations table. */
|
|
@@ -86,6 +95,9 @@ interface ChildModel {
|
|
|
86
95
|
readonly path: readonly string[];
|
|
87
96
|
readonly field: Field;
|
|
88
97
|
readonly table: TableModel;
|
|
98
|
+
/** Localized: rows carry `_locale`, and the data holds `{ [locale]: rows }`. */
|
|
99
|
+
readonly locales?: readonly string[];
|
|
100
|
+
readonly defaultLocale?: string;
|
|
89
101
|
}
|
|
90
102
|
interface TableModel {
|
|
91
103
|
readonly name: string;
|
|
@@ -108,6 +120,12 @@ interface SchemaModel {
|
|
|
108
120
|
readonly tables: Readonly<Record<string, AnyTable>>;
|
|
109
121
|
/** Changes whenever the generated schema changes. Used to detect missing migrations. */
|
|
110
122
|
readonly hash: string;
|
|
123
|
+
/** Locales of localized fields, recorded with snapshots so a new default locale keeps data. */
|
|
124
|
+
readonly localization: SchemaLocalization | null;
|
|
125
|
+
}
|
|
126
|
+
interface SchemaLocalization {
|
|
127
|
+
readonly defaultLocale: string;
|
|
128
|
+
readonly locales: readonly string[];
|
|
111
129
|
}
|
|
112
130
|
declare const snake: (name: string) => string;
|
|
113
131
|
/** Builds Drizzle tables for every collection and global in the config. */
|
|
@@ -136,6 +154,12 @@ declare class Migrator {
|
|
|
136
154
|
private snapshotOf;
|
|
137
155
|
/** SQL statements that turn `prev` into `cur`. Renames become drop + add unless interactive. */
|
|
138
156
|
private diff;
|
|
157
|
+
/**
|
|
158
|
+
* A new default locale changes which locale the plain column holds (`title` is the default
|
|
159
|
+
* locale's, `title__en` another's). Moves the values so every locale keeps its own.
|
|
160
|
+
* Snapshots from before 0.7 do not record the locales, so nothing can be moved for them.
|
|
161
|
+
*/
|
|
162
|
+
private localeMoves;
|
|
139
163
|
ensureTable(): Promise<void>;
|
|
140
164
|
private record;
|
|
141
165
|
private applied;
|
|
@@ -158,4 +182,4 @@ declare class Migrator {
|
|
|
158
182
|
private files;
|
|
159
183
|
}
|
|
160
184
|
|
|
161
|
-
export { type AnyColumn, type AnyTable, type ColumnBuilder, type Connection, DEFAULT_MIGRATION_DIR, DEFAULT_TABLE_PREFIX, type Dialect, type DrizzleAdapterOptions, type DrizzleDb, type IndexBuilder, Migrator, type SchemaModel, type Snapshot, type SqlRunner, type Statement, buildSchema, connectDatabase, snake, unionSnapshot };
|
|
185
|
+
export { type AnyColumn, type AnyTable, type ColumnBuilder, type Connection, DEFAULT_MIGRATION_DIR, DEFAULT_TABLE_PREFIX, type Dialect, type DrizzleAdapterOptions, type DrizzleDb, type IndexBuilder, type JsonGetter, type JsonValueType, Migrator, type SchemaModel, type Snapshot, type SqlRunner, type Statement, buildSchema, connectDatabase, snake, unionSnapshot };
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,12 @@ var columnPath = (column) => column.locale === void 0 ? column.path : [...column
|
|
|
26
26
|
function rowToData(model, row) {
|
|
27
27
|
const data = {};
|
|
28
28
|
for (const column of model.columns) setAt(data, columnPath(column), row[column.column] ?? null);
|
|
29
|
-
for (const child of model.children)
|
|
29
|
+
for (const child of model.children)
|
|
30
|
+
setAt(
|
|
31
|
+
data,
|
|
32
|
+
child.path,
|
|
33
|
+
child.locales ? Object.fromEntries(child.locales.map((locale) => [locale, []])) : []
|
|
34
|
+
);
|
|
30
35
|
return data;
|
|
31
36
|
}
|
|
32
37
|
function dataToRow(model, data) {
|
|
@@ -55,8 +60,12 @@ async function attachChildren(db, model, rows, docs) {
|
|
|
55
60
|
childRows.forEach((childRow, i) => {
|
|
56
61
|
const index = indexById.get(childRow._parent_id);
|
|
57
62
|
if (index === void 0) return;
|
|
58
|
-
const
|
|
59
|
-
|
|
63
|
+
const locale = child.locales ? String(childRow._locale) : void 0;
|
|
64
|
+
const path = locale === void 0 ? child.path : [...child.path, locale];
|
|
65
|
+
const list = getAt(docs[index], path);
|
|
66
|
+
if (!Array.isArray(list)) return;
|
|
67
|
+
const id = locale === void 0 ? childRow.id : String(childRow.id).replace(new RegExp(`:${locale}$`), "");
|
|
68
|
+
list.push(child.kind === "array" ? { id, ...childDocs?.[i] } : childRow.value);
|
|
60
69
|
});
|
|
61
70
|
}
|
|
62
71
|
}
|
|
@@ -77,20 +86,41 @@ function toDocument(row, data, drafts) {
|
|
|
77
86
|
async function insertChildren(db, model, parentId, data) {
|
|
78
87
|
for (const child of model.children) {
|
|
79
88
|
const value = getAt(data, child.path);
|
|
89
|
+
if (child.locales) {
|
|
90
|
+
if (!value || typeof value !== "object") continue;
|
|
91
|
+
for (const locale of child.locales) {
|
|
92
|
+
const items = value[locale];
|
|
93
|
+
if (Array.isArray(items) && items.length > 0)
|
|
94
|
+
await insertChild(db, child, parentId, items, locale);
|
|
95
|
+
}
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
80
98
|
if (!Array.isArray(value) || value.length === 0) continue;
|
|
81
99
|
await insertChild(db, child, parentId, value);
|
|
82
100
|
}
|
|
83
101
|
}
|
|
84
|
-
async function insertChild(db, child, parentId, items) {
|
|
102
|
+
async function insertChild(db, child, parentId, items, locale) {
|
|
103
|
+
const localeColumn = locale === void 0 ? {} : { _locale: locale };
|
|
85
104
|
if (child.kind === "values") {
|
|
86
|
-
const rows = items.map((value, i) => ({
|
|
105
|
+
const rows = items.map((value, i) => ({
|
|
106
|
+
_parent_id: parentId,
|
|
107
|
+
_order: i,
|
|
108
|
+
value,
|
|
109
|
+
...localeColumn
|
|
110
|
+
}));
|
|
87
111
|
for (const part of chunks(rows)) await db.insert(child.table.table).values(part);
|
|
88
112
|
return;
|
|
89
113
|
}
|
|
90
114
|
for (const [i, item] of items.entries()) {
|
|
91
115
|
const rowData = item;
|
|
92
|
-
const id = rowData.id
|
|
93
|
-
await db.insert(child.table.table).values({
|
|
116
|
+
const id = locale === void 0 ? rowData.id : `${rowData.id}:${locale}`;
|
|
117
|
+
await db.insert(child.table.table).values({
|
|
118
|
+
...dataToRow(child.table, rowData),
|
|
119
|
+
id,
|
|
120
|
+
_parent_id: parentId,
|
|
121
|
+
_order: i,
|
|
122
|
+
...localeColumn
|
|
123
|
+
});
|
|
94
124
|
await insertChildren(db, child.table, id, rowData);
|
|
95
125
|
}
|
|
96
126
|
}
|
|
@@ -138,6 +168,248 @@ async function deleteDocument(db, model, id) {
|
|
|
138
168
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
139
169
|
import { join } from "path";
|
|
140
170
|
import { SchemaError } from "@easy-cms/core";
|
|
171
|
+
|
|
172
|
+
// src/schema.ts
|
|
173
|
+
import { createHash } from "crypto";
|
|
174
|
+
import { ConfigError } from "@easy-cms/core";
|
|
175
|
+
function tableModels(schema) {
|
|
176
|
+
const all = [];
|
|
177
|
+
const walk = (model) => {
|
|
178
|
+
all.push(model);
|
|
179
|
+
for (const child of model.children) walk(child.table);
|
|
180
|
+
};
|
|
181
|
+
for (const collection of schema.collections.values()) walk(collection.root);
|
|
182
|
+
return all;
|
|
183
|
+
}
|
|
184
|
+
function migrationsTableName(prefix) {
|
|
185
|
+
return `${prefix}migrations`;
|
|
186
|
+
}
|
|
187
|
+
var snake = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").toLowerCase();
|
|
188
|
+
function columnKind(field) {
|
|
189
|
+
switch (field.type) {
|
|
190
|
+
case "number":
|
|
191
|
+
return "number";
|
|
192
|
+
case "boolean":
|
|
193
|
+
return "boolean";
|
|
194
|
+
case "json":
|
|
195
|
+
case "richText":
|
|
196
|
+
// Blocks are stored whole as JSON: rows of different kinds don't fit one child table.
|
|
197
|
+
case "blocks":
|
|
198
|
+
return "json";
|
|
199
|
+
case "upload":
|
|
200
|
+
case "relationship":
|
|
201
|
+
return "integer";
|
|
202
|
+
default:
|
|
203
|
+
return "text";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function buildSchema(config, prefix, dialect) {
|
|
207
|
+
const builder = {
|
|
208
|
+
dialect,
|
|
209
|
+
prefix,
|
|
210
|
+
tableNames: /* @__PURE__ */ new Set([migrationsTableName(prefix)]),
|
|
211
|
+
// The hash covers the logical schema only, so it stays stable across releases for the same config.
|
|
212
|
+
description: [],
|
|
213
|
+
localization: config.localization
|
|
214
|
+
};
|
|
215
|
+
const collections = /* @__PURE__ */ new Map();
|
|
216
|
+
const tables = {};
|
|
217
|
+
for (const collection of config.collections) {
|
|
218
|
+
const root = buildTable(
|
|
219
|
+
builder,
|
|
220
|
+
`${prefix}${snake(collection.slug)}`,
|
|
221
|
+
"root",
|
|
222
|
+
collection.fields,
|
|
223
|
+
{
|
|
224
|
+
drafts: collection.drafts === true
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
collections.set(collection.slug, { config: collection, root });
|
|
228
|
+
collectTables(root, tables);
|
|
229
|
+
}
|
|
230
|
+
const globalsName = claimName(builder, `${prefix}globals`);
|
|
231
|
+
const globals = dialect.table(
|
|
232
|
+
globalsName,
|
|
233
|
+
{
|
|
234
|
+
slug: dialect.text("slug").primaryKey(),
|
|
235
|
+
data: dialect.json("data").notNull(),
|
|
236
|
+
status: dialect.text("status"),
|
|
237
|
+
updated_at: dialect.text("updated_at").notNull()
|
|
238
|
+
},
|
|
239
|
+
() => []
|
|
240
|
+
);
|
|
241
|
+
tables[globalsName] = globals;
|
|
242
|
+
builder.description.push(["globals", globalsName]);
|
|
243
|
+
const hash = createHash("sha256").update(JSON.stringify(builder.description)).digest("hex").slice(0, 16);
|
|
244
|
+
const localization = config.localization ? { defaultLocale: config.localization.defaultLocale, locales: config.localization.locales } : null;
|
|
245
|
+
return { collections, globals, tables, hash, localization };
|
|
246
|
+
}
|
|
247
|
+
function collectTables(model, into) {
|
|
248
|
+
into[model.name] = model.table;
|
|
249
|
+
for (const child of model.children) collectTables(child.table, into);
|
|
250
|
+
}
|
|
251
|
+
function claimName(builder, name) {
|
|
252
|
+
if (builder.tableNames.has(name)) {
|
|
253
|
+
throw new ConfigError([
|
|
254
|
+
{
|
|
255
|
+
path: name,
|
|
256
|
+
message: `two tables would be named "${name}"`,
|
|
257
|
+
hint: "rename one of the collections or fields"
|
|
258
|
+
}
|
|
259
|
+
]);
|
|
260
|
+
}
|
|
261
|
+
builder.tableNames.add(name);
|
|
262
|
+
return name;
|
|
263
|
+
}
|
|
264
|
+
function makeColumn(dialect, name, kind) {
|
|
265
|
+
switch (kind) {
|
|
266
|
+
case "number":
|
|
267
|
+
return dialect.number(name);
|
|
268
|
+
case "integer":
|
|
269
|
+
return dialect.integer(name);
|
|
270
|
+
case "boolean":
|
|
271
|
+
return dialect.boolean(name);
|
|
272
|
+
case "json":
|
|
273
|
+
return dialect.json(name);
|
|
274
|
+
case "text":
|
|
275
|
+
return dialect.text(name);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function buildTable(builder, name, kind, fields, options) {
|
|
279
|
+
const { dialect } = builder;
|
|
280
|
+
claimName(builder, name);
|
|
281
|
+
const columns = {};
|
|
282
|
+
const described = [];
|
|
283
|
+
const columnModels = [];
|
|
284
|
+
const children = [];
|
|
285
|
+
const indexes = [];
|
|
286
|
+
const add = (column, builderColumn, desc2) => {
|
|
287
|
+
if (Object.hasOwn(columns, column)) {
|
|
288
|
+
throw new ConfigError([
|
|
289
|
+
{
|
|
290
|
+
path: `${name}.${column}`,
|
|
291
|
+
message: `two fields would use the column "${column}"`,
|
|
292
|
+
hint: "rename one of them"
|
|
293
|
+
}
|
|
294
|
+
]);
|
|
295
|
+
}
|
|
296
|
+
columns[column] = builderColumn;
|
|
297
|
+
described.push([column, desc2]);
|
|
298
|
+
};
|
|
299
|
+
if (kind === "root") {
|
|
300
|
+
add("id", dialect.serial("id"), "id:int");
|
|
301
|
+
add("created_at", dialect.text("created_at").notNull(), "text!");
|
|
302
|
+
add("updated_at", dialect.text("updated_at").notNull(), "text!");
|
|
303
|
+
if (options.drafts) add("status", dialect.text("status").notNull().default("draft"), "status");
|
|
304
|
+
indexes.push({ column: "created_at", unique: false });
|
|
305
|
+
} else {
|
|
306
|
+
const parentKind = options.parentIdKind ?? "integer";
|
|
307
|
+
add(
|
|
308
|
+
"id",
|
|
309
|
+
kind === "array" ? dialect.text("id").primaryKey() : dialect.serial("id"),
|
|
310
|
+
`id:${kind}`
|
|
311
|
+
);
|
|
312
|
+
add(
|
|
313
|
+
"_parent_id",
|
|
314
|
+
parentKind === "text" ? dialect.text("_parent_id").notNull() : dialect.integer("_parent_id").notNull(),
|
|
315
|
+
parentKind
|
|
316
|
+
);
|
|
317
|
+
add("_order", dialect.integer("_order").notNull(), "int!");
|
|
318
|
+
indexes.push({ column: "_parent_id", unique: false });
|
|
319
|
+
if (options.localized) {
|
|
320
|
+
add(
|
|
321
|
+
"_locale",
|
|
322
|
+
dialect.text("_locale").notNull().default(options.localized),
|
|
323
|
+
`locale:${options.localized}`
|
|
324
|
+
);
|
|
325
|
+
indexes.push({ column: "_locale", unique: false });
|
|
326
|
+
}
|
|
327
|
+
if (kind === "values" && options.valueField) {
|
|
328
|
+
const valueKind = columnKind(options.valueField);
|
|
329
|
+
add("value", makeColumn(dialect, "value", valueKind), valueKind);
|
|
330
|
+
indexes.push({ column: "value", unique: false });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const walk = (list, path, topLevel) => {
|
|
334
|
+
for (const field of list) {
|
|
335
|
+
const fieldPath = [...path, field.name];
|
|
336
|
+
const base = fieldPath.map(snake).join("_");
|
|
337
|
+
if (field.type === "group") {
|
|
338
|
+
walk(field.fields, fieldPath, false);
|
|
339
|
+
} else if (field.type === "array") {
|
|
340
|
+
const localization = field.localized ? builder.localization : null;
|
|
341
|
+
const child = buildTable(builder, `${name}__${base}`, "array", field.fields, {
|
|
342
|
+
parentIdKind: kind === "array" ? "text" : "integer",
|
|
343
|
+
...localization ? { localized: localization.defaultLocale } : {}
|
|
344
|
+
});
|
|
345
|
+
children.push({
|
|
346
|
+
kind: "array",
|
|
347
|
+
path: fieldPath,
|
|
348
|
+
field,
|
|
349
|
+
table: child,
|
|
350
|
+
...localeInfo(localization)
|
|
351
|
+
});
|
|
352
|
+
described.push([base, "array", child.name]);
|
|
353
|
+
} else if ((field.type === "select" || field.type === "relationship") && field.hasMany) {
|
|
354
|
+
const localization = field.localized ? builder.localization : null;
|
|
355
|
+
const child = buildTable(builder, `${name}__${base}`, "values", [], {
|
|
356
|
+
parentIdKind: kind === "array" ? "text" : "integer",
|
|
357
|
+
valueField: field,
|
|
358
|
+
...localization ? { localized: localization.defaultLocale } : {}
|
|
359
|
+
});
|
|
360
|
+
children.push({
|
|
361
|
+
kind: "values",
|
|
362
|
+
path: fieldPath,
|
|
363
|
+
field,
|
|
364
|
+
table: child,
|
|
365
|
+
...localeInfo(localization)
|
|
366
|
+
});
|
|
367
|
+
described.push([base, "values", child.name]);
|
|
368
|
+
} else {
|
|
369
|
+
const kindOfColumn = columnKind(field);
|
|
370
|
+
const localization = field.localized ? builder.localization : null;
|
|
371
|
+
const targets = localization ? localization.locales.map((locale) => ({
|
|
372
|
+
column: locale === localization.defaultLocale ? base : `${base}__${snake(locale)}`,
|
|
373
|
+
locale
|
|
374
|
+
})) : [{ column: base, locale: void 0 }];
|
|
375
|
+
for (const { column, locale } of targets) {
|
|
376
|
+
add(column, makeColumn(dialect, column, kindOfColumn), kindOfColumn);
|
|
377
|
+
columnModels.push(
|
|
378
|
+
locale === void 0 ? { path: fieldPath, field, column } : {
|
|
379
|
+
path: fieldPath,
|
|
380
|
+
field,
|
|
381
|
+
column,
|
|
382
|
+
locale,
|
|
383
|
+
defaultLocale: locale === localization?.defaultLocale
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
if (topLevel && kind === "root" && (field.unique || field.type === "slug")) {
|
|
387
|
+
indexes.push({ column, unique: true });
|
|
388
|
+
} else if (field.index || field.unique || field.type === "relationship" || field.type === "upload") {
|
|
389
|
+
indexes.push({ column, unique: false });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
walk(fields, [], true);
|
|
396
|
+
const table = dialect.table(
|
|
397
|
+
name,
|
|
398
|
+
columns,
|
|
399
|
+
(t) => indexes.map(({ column, unique }) => {
|
|
400
|
+
const col3 = t[column];
|
|
401
|
+
return unique ? dialect.uniqueIndex(`${name}_${column}_unique`).on(col3) : dialect.index(`${name}_${column}_idx`).on(col3);
|
|
402
|
+
})
|
|
403
|
+
);
|
|
404
|
+
builder.description.push([name, kind, described, indexes]);
|
|
405
|
+
const model = { name, kind, table, fields, columns: columnModels, children };
|
|
406
|
+
return options.valueField ? { ...model, valueField: options.valueField } : model;
|
|
407
|
+
}
|
|
408
|
+
function localeInfo(localization) {
|
|
409
|
+
return localization ? { locales: localization.locales, defaultLocale: localization.defaultLocale } : {};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/migrations.ts
|
|
141
413
|
var BREAKPOINT = "--> statement-breakpoint";
|
|
142
414
|
var TABLE_PARTS = [
|
|
143
415
|
"columns",
|
|
@@ -166,6 +438,17 @@ function unionSnapshot(prev, cur) {
|
|
|
166
438
|
}
|
|
167
439
|
return merged;
|
|
168
440
|
}
|
|
441
|
+
var META = "easyCms";
|
|
442
|
+
var metaOf = (snapshot) => snapshot[META];
|
|
443
|
+
function withoutMeta(snapshot) {
|
|
444
|
+
const { [META]: _, ...rest } = snapshot;
|
|
445
|
+
return rest;
|
|
446
|
+
}
|
|
447
|
+
function snapshotTable(snapshot, name) {
|
|
448
|
+
return Object.values(snapshot.tables ?? {}).find(
|
|
449
|
+
(t) => t.name === name
|
|
450
|
+
);
|
|
451
|
+
}
|
|
169
452
|
var DESTRUCTIVE = /\b(DROP TABLE|DROP COLUMN)\b|__new_/i;
|
|
170
453
|
var DEV_PUSH = "dev";
|
|
171
454
|
var Migrator = class {
|
|
@@ -179,15 +462,58 @@ var Migrator = class {
|
|
|
179
462
|
q(name) {
|
|
180
463
|
return this.options.dialect.name === "postgres" ? `"${name}"` : `\`${name}\``;
|
|
181
464
|
}
|
|
182
|
-
snapshotOf(schema) {
|
|
183
|
-
|
|
465
|
+
async snapshotOf(schema) {
|
|
466
|
+
const snapshot = await this.options.dialect.snapshot(schema ? schema.tables : {});
|
|
467
|
+
const meta = { localization: schema?.localization ?? null };
|
|
468
|
+
return { ...snapshot, [META]: meta };
|
|
184
469
|
}
|
|
185
470
|
/** SQL statements that turn `prev` into `cur`. Renames become drop + add unless interactive. */
|
|
186
471
|
async diff(prev, cur, interactive) {
|
|
187
472
|
const { dialect } = this.options;
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
473
|
+
const moves = this.localeMoves(prev, metaOf(prev), metaOf(cur));
|
|
474
|
+
const [from, to] = [withoutMeta(prev), withoutMeta(cur)];
|
|
475
|
+
if (interactive && moves.length === 0) return dialect.migration(from, to);
|
|
476
|
+
const union = unionSnapshot(from, to);
|
|
477
|
+
return [
|
|
478
|
+
...await dialect.migration(from, union),
|
|
479
|
+
...moves,
|
|
480
|
+
...await dialect.migration(union, to)
|
|
481
|
+
];
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* A new default locale changes which locale the plain column holds (`title` is the default
|
|
485
|
+
* locale's, `title__en` another's). Moves the values so every locale keeps its own.
|
|
486
|
+
* Snapshots from before 0.7 do not record the locales, so nothing can be moved for them.
|
|
487
|
+
*/
|
|
488
|
+
localeMoves(prev, before, after) {
|
|
489
|
+
const was = before?.localization;
|
|
490
|
+
const now = after?.localization;
|
|
491
|
+
if (!was || !now || was.defaultLocale === now.defaultLocale) return [];
|
|
492
|
+
const q = (name) => this.q(name);
|
|
493
|
+
const statements = [];
|
|
494
|
+
for (const model of tableModels(this.options.schema)) {
|
|
495
|
+
const old = snapshotTable(prev, model.name);
|
|
496
|
+
if (!old) continue;
|
|
497
|
+
const has = (column) => Object.hasOwn(old.columns ?? {}, column);
|
|
498
|
+
const keep = [];
|
|
499
|
+
const clear = [];
|
|
500
|
+
const fill = [];
|
|
501
|
+
for (const column of model.columns) {
|
|
502
|
+
if (!column.defaultLocale || !has(column.column)) continue;
|
|
503
|
+
const base = column.column;
|
|
504
|
+
const moved = model.columns.find(
|
|
505
|
+
(c) => c.path.join(".") === column.path.join(".") && c.locale === was.defaultLocale
|
|
506
|
+
);
|
|
507
|
+
if (moved && !has(moved.column)) keep.push(`${q(moved.column)} = ${q(base)}`);
|
|
508
|
+
clear.push(`${q(base)} = NULL`);
|
|
509
|
+
const incoming = `${base}__${snake(now.defaultLocale)}`;
|
|
510
|
+
if (has(incoming)) fill.push(`${q(base)} = ${q(incoming)}`);
|
|
511
|
+
}
|
|
512
|
+
for (const set of [keep, clear, fill]) {
|
|
513
|
+
if (set.length > 0) statements.push(`UPDATE ${q(model.name)} SET ${set.join(", ")}`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return statements;
|
|
191
517
|
}
|
|
192
518
|
async ensureTable() {
|
|
193
519
|
await this.runner.query(this.options.dialect.migrationsTableSQL(this.options.table));
|
|
@@ -387,7 +713,44 @@ var SYSTEM_COLUMNS = {
|
|
|
387
713
|
var col2 = (model, name) => model.table[name];
|
|
388
714
|
var ID_FIELD = { name: "id", type: "relationship", to: "" };
|
|
389
715
|
var LIKE_TYPES = /* @__PURE__ */ new Set(["text", "textarea", "email", "slug", "select"]);
|
|
390
|
-
|
|
716
|
+
var JSON_TYPES = {
|
|
717
|
+
number: "number",
|
|
718
|
+
boolean: "boolean",
|
|
719
|
+
relationship: "integer",
|
|
720
|
+
upload: "integer"
|
|
721
|
+
};
|
|
722
|
+
function resolveBlockValue(blocks, rest, shown, localization) {
|
|
723
|
+
if (rest.length === 1 && rest[0] === "blockType")
|
|
724
|
+
return { path: ["blockType"], type: "text", field: void 0 };
|
|
725
|
+
let fields = blocks.blocks.flatMap((block) => block.fields);
|
|
726
|
+
const path = [];
|
|
727
|
+
for (let i = 0; i < rest.length; i++) {
|
|
728
|
+
const segment = rest[i];
|
|
729
|
+
const field = fields.find((f) => f.name === segment);
|
|
730
|
+
const at = [shown, ...rest.slice(0, i + 1)].join(".");
|
|
731
|
+
if (!field) throw new QueryError(`Unknown field "${at}"`);
|
|
732
|
+
path.push(segment);
|
|
733
|
+
if (field.type === "group") {
|
|
734
|
+
fields = field.fields;
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
const hasMany = "hasMany" in field && field.hasMany === true;
|
|
738
|
+
if (hasMany || field.type === "array" || field.type === "blocks" || field.type === "json" || field.type === "richText") {
|
|
739
|
+
throw new QueryError(`Cannot query "${at}" inside blocks`);
|
|
740
|
+
}
|
|
741
|
+
let last = i;
|
|
742
|
+
if (field.localized && localization) {
|
|
743
|
+
const next = rest[i + 1];
|
|
744
|
+
const named = next !== void 0 && localization.locales.includes(next);
|
|
745
|
+
path.push(named ? next : localization.defaultLocale);
|
|
746
|
+
if (named) last = i + 1;
|
|
747
|
+
}
|
|
748
|
+
if (last < rest.length - 1) throw new QueryError(`Cannot query inside "${at}"`);
|
|
749
|
+
return { path, type: JSON_TYPES[field.type] ?? "text", field };
|
|
750
|
+
}
|
|
751
|
+
throw new QueryError(`"${[shown, ...rest].join(".")}" is a group; query one of its fields`);
|
|
752
|
+
}
|
|
753
|
+
function resolvePath(model, segments, drafts, localization = null) {
|
|
391
754
|
const [first] = segments;
|
|
392
755
|
if (first === void 0) throw new QueryError("empty field path");
|
|
393
756
|
if (segments.length === 1) {
|
|
@@ -413,19 +776,39 @@ function resolvePath(model, segments, drafts) {
|
|
|
413
776
|
continue;
|
|
414
777
|
}
|
|
415
778
|
const child = model.children.find((c) => c.path.join(".") === path.join("."));
|
|
779
|
+
if (child?.locales) {
|
|
780
|
+
const next = segments[i + 1];
|
|
781
|
+
const named = next !== void 0 && child.locales.includes(next);
|
|
782
|
+
return {
|
|
783
|
+
kind: "child",
|
|
784
|
+
child,
|
|
785
|
+
rest: segments.slice(i + (named ? 2 : 1)),
|
|
786
|
+
locale: named ? next : child.defaultLocale
|
|
787
|
+
};
|
|
788
|
+
}
|
|
416
789
|
if (child) return { kind: "child", child, rest: segments.slice(i + 1) };
|
|
417
|
-
|
|
790
|
+
let locale = field.localized ? segments[i + 1] : void 0;
|
|
791
|
+
if (field.type === "blocks" && locale !== void 0 && !localization?.locales.includes(locale))
|
|
792
|
+
locale = void 0;
|
|
418
793
|
const last = field.localized && locale !== void 0 ? i + 1 : i;
|
|
794
|
+
const same = (c) => c.path.join(".") === path.join(".");
|
|
795
|
+
const columnOf = () => field.localized ? model.columns.find(
|
|
796
|
+
(c) => same(c) && (locale === void 0 ? c.defaultLocale : c.locale === locale)
|
|
797
|
+
) : model.columns.find(same);
|
|
798
|
+
if (field.type === "blocks") {
|
|
799
|
+
const column2 = columnOf();
|
|
800
|
+
if (!column2) throw new QueryError(`Unknown locale "${locale}" for "${shown}"`);
|
|
801
|
+
const rest = segments.slice(last + 1);
|
|
802
|
+
const value = rest.length === 0 ? null : resolveBlockValue(field, rest, shown, localization);
|
|
803
|
+
return { kind: "blocks", column: col2(model, column2.column), value };
|
|
804
|
+
}
|
|
419
805
|
if (last < segments.length - 1) {
|
|
420
806
|
throw new QueryError(`Cannot query inside "${shown}" (${field.type} field)`);
|
|
421
807
|
}
|
|
422
808
|
if (field.type === "json" || field.type === "richText") {
|
|
423
809
|
throw new QueryError(`Cannot query "${shown}": ${field.type} fields are not queryable`);
|
|
424
810
|
}
|
|
425
|
-
const
|
|
426
|
-
const column = field.localized ? model.columns.find(
|
|
427
|
-
(c) => same(c) && (locale === void 0 ? c.defaultLocale : c.locale === locale)
|
|
428
|
-
) : model.columns.find(same);
|
|
811
|
+
const column = columnOf();
|
|
429
812
|
if (field.localized && !column)
|
|
430
813
|
throw new QueryError(`Unknown locale "${locale}" for "${shown}"`);
|
|
431
814
|
if (!column) throw new QueryError(`Unknown field "${shown}"`);
|
|
@@ -481,14 +864,16 @@ function operatorSQL(dialect, column, field, op, raw, path) {
|
|
|
481
864
|
}
|
|
482
865
|
}
|
|
483
866
|
var WhereBuilder = class {
|
|
484
|
-
constructor(db, dialect, drafts) {
|
|
867
|
+
constructor(db, dialect, drafts, localization = null) {
|
|
485
868
|
this.db = db;
|
|
486
869
|
this.dialect = dialect;
|
|
487
870
|
this.drafts = drafts;
|
|
871
|
+
this.localization = localization;
|
|
488
872
|
}
|
|
489
873
|
db;
|
|
490
874
|
dialect;
|
|
491
875
|
drafts;
|
|
876
|
+
localization;
|
|
492
877
|
build(model, where) {
|
|
493
878
|
if (where === void 0) return void 0;
|
|
494
879
|
if (typeof where !== "object" || where === null || Array.isArray(where)) {
|
|
@@ -513,19 +898,40 @@ var WhereBuilder = class {
|
|
|
513
898
|
}
|
|
514
899
|
const entries = Object.entries(operators).filter(([, v]) => v !== void 0);
|
|
515
900
|
if (entries.length === 0) throw new QueryError(`"${path}" has no operator`);
|
|
516
|
-
const target = resolvePath(
|
|
901
|
+
const target = resolvePath(
|
|
902
|
+
model,
|
|
903
|
+
path.split("."),
|
|
904
|
+
this.drafts && model.kind === "root",
|
|
905
|
+
this.localization
|
|
906
|
+
);
|
|
517
907
|
const parts = entries.map(([op, value]) => {
|
|
518
908
|
if (!OPERATORS.has(op)) throw new QueryError(`Unknown operator "${op}" on "${path}"`);
|
|
519
909
|
if (target.kind === "column")
|
|
520
910
|
return operatorSQL(this.dialect, target.column, target.field, op, value, path);
|
|
521
|
-
return this.
|
|
911
|
+
if (target.kind === "blocks") return this.blocks(target, op, value, path);
|
|
912
|
+
return this.child(model, target.child, target.rest, op, value, path, target.locale);
|
|
522
913
|
});
|
|
523
914
|
return parts.length === 1 ? parts[0] : and(...parts);
|
|
524
915
|
}
|
|
916
|
+
/** Conditions inside blocks test each block of the JSON column: some block must match. */
|
|
917
|
+
blocks(target, op, value, path) {
|
|
918
|
+
const { value: inner } = target;
|
|
919
|
+
if (!inner) {
|
|
920
|
+
if (op !== "exists")
|
|
921
|
+
throw new QueryError(`"${path}" is a blocks field; query one of its fields`);
|
|
922
|
+
const any = this.dialect.someElement(target.column, () => void 0);
|
|
923
|
+
return value === false ? not(any) : any;
|
|
924
|
+
}
|
|
925
|
+
return this.dialect.someElement(
|
|
926
|
+
target.column,
|
|
927
|
+
(get) => operatorSQL(this.dialect, get(inner.path, inner.type), inner.field, op, value, path)
|
|
928
|
+
);
|
|
929
|
+
}
|
|
525
930
|
/** Conditions on array rows or hasMany values become (NOT) EXISTS subqueries. */
|
|
526
|
-
child(parent, child, rest, op, value, path) {
|
|
931
|
+
child(parent, child, rest, op, value, path, locale) {
|
|
527
932
|
const table = child.table;
|
|
528
|
-
const
|
|
933
|
+
const parentLink = eq2(col2(table, "_parent_id"), col2(parent, "id"));
|
|
934
|
+
const link = locale === void 0 ? parentLink : and(parentLink, eq2(col2(table, "_locale"), locale));
|
|
529
935
|
const subquery = (condition) => exists(
|
|
530
936
|
this.db.select({ one: sql`1` }).from(table.table).where(condition ? and(link, condition) : link)
|
|
531
937
|
);
|
|
@@ -553,7 +959,7 @@ var WhereBuilder = class {
|
|
|
553
959
|
const order = sort.map((entry) => {
|
|
554
960
|
const descending = entry.startsWith("-");
|
|
555
961
|
const path = descending ? entry.slice(1) : entry;
|
|
556
|
-
const target = resolvePath(model, path.split("."), this.drafts);
|
|
962
|
+
const target = resolvePath(model, path.split("."), this.drafts, this.localization);
|
|
557
963
|
if (target.kind !== "column") throw new QueryError(`Cannot sort by "${path}"`);
|
|
558
964
|
return descending ? desc(target.column) : asc2(target.column);
|
|
559
965
|
});
|
|
@@ -562,207 +968,6 @@ var WhereBuilder = class {
|
|
|
562
968
|
}
|
|
563
969
|
};
|
|
564
970
|
|
|
565
|
-
// src/schema.ts
|
|
566
|
-
import { createHash } from "crypto";
|
|
567
|
-
import { ConfigError } from "@easy-cms/core";
|
|
568
|
-
function migrationsTableName(prefix) {
|
|
569
|
-
return `${prefix}migrations`;
|
|
570
|
-
}
|
|
571
|
-
var snake = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").toLowerCase();
|
|
572
|
-
function columnKind(field) {
|
|
573
|
-
switch (field.type) {
|
|
574
|
-
case "number":
|
|
575
|
-
return "number";
|
|
576
|
-
case "boolean":
|
|
577
|
-
return "boolean";
|
|
578
|
-
case "json":
|
|
579
|
-
case "richText":
|
|
580
|
-
return "json";
|
|
581
|
-
case "upload":
|
|
582
|
-
case "relationship":
|
|
583
|
-
return "integer";
|
|
584
|
-
default:
|
|
585
|
-
return "text";
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
function buildSchema(config, prefix, dialect) {
|
|
589
|
-
const builder = {
|
|
590
|
-
dialect,
|
|
591
|
-
prefix,
|
|
592
|
-
tableNames: /* @__PURE__ */ new Set([migrationsTableName(prefix)]),
|
|
593
|
-
// The hash covers the logical schema only, so it stays stable across releases for the same config.
|
|
594
|
-
description: [],
|
|
595
|
-
localization: config.localization
|
|
596
|
-
};
|
|
597
|
-
const collections = /* @__PURE__ */ new Map();
|
|
598
|
-
const tables = {};
|
|
599
|
-
for (const collection of config.collections) {
|
|
600
|
-
const root = buildTable(
|
|
601
|
-
builder,
|
|
602
|
-
`${prefix}${snake(collection.slug)}`,
|
|
603
|
-
"root",
|
|
604
|
-
collection.fields,
|
|
605
|
-
{
|
|
606
|
-
drafts: collection.drafts === true
|
|
607
|
-
}
|
|
608
|
-
);
|
|
609
|
-
collections.set(collection.slug, { config: collection, root });
|
|
610
|
-
collectTables(root, tables);
|
|
611
|
-
}
|
|
612
|
-
const globalsName = claimName(builder, `${prefix}globals`);
|
|
613
|
-
const globals = dialect.table(
|
|
614
|
-
globalsName,
|
|
615
|
-
{
|
|
616
|
-
slug: dialect.text("slug").primaryKey(),
|
|
617
|
-
data: dialect.json("data").notNull(),
|
|
618
|
-
status: dialect.text("status"),
|
|
619
|
-
updated_at: dialect.text("updated_at").notNull()
|
|
620
|
-
},
|
|
621
|
-
() => []
|
|
622
|
-
);
|
|
623
|
-
tables[globalsName] = globals;
|
|
624
|
-
builder.description.push(["globals", globalsName]);
|
|
625
|
-
const hash = createHash("sha256").update(JSON.stringify(builder.description)).digest("hex").slice(0, 16);
|
|
626
|
-
return { collections, globals, tables, hash };
|
|
627
|
-
}
|
|
628
|
-
function collectTables(model, into) {
|
|
629
|
-
into[model.name] = model.table;
|
|
630
|
-
for (const child of model.children) collectTables(child.table, into);
|
|
631
|
-
}
|
|
632
|
-
function claimName(builder, name) {
|
|
633
|
-
if (builder.tableNames.has(name)) {
|
|
634
|
-
throw new ConfigError([
|
|
635
|
-
{
|
|
636
|
-
path: name,
|
|
637
|
-
message: `two tables would be named "${name}"`,
|
|
638
|
-
hint: "rename one of the collections or fields"
|
|
639
|
-
}
|
|
640
|
-
]);
|
|
641
|
-
}
|
|
642
|
-
builder.tableNames.add(name);
|
|
643
|
-
return name;
|
|
644
|
-
}
|
|
645
|
-
function makeColumn(dialect, name, kind) {
|
|
646
|
-
switch (kind) {
|
|
647
|
-
case "number":
|
|
648
|
-
return dialect.number(name);
|
|
649
|
-
case "integer":
|
|
650
|
-
return dialect.integer(name);
|
|
651
|
-
case "boolean":
|
|
652
|
-
return dialect.boolean(name);
|
|
653
|
-
case "json":
|
|
654
|
-
return dialect.json(name);
|
|
655
|
-
case "text":
|
|
656
|
-
return dialect.text(name);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
function buildTable(builder, name, kind, fields, options) {
|
|
660
|
-
const { dialect } = builder;
|
|
661
|
-
claimName(builder, name);
|
|
662
|
-
const columns = {};
|
|
663
|
-
const described = [];
|
|
664
|
-
const columnModels = [];
|
|
665
|
-
const children = [];
|
|
666
|
-
const indexes = [];
|
|
667
|
-
const add = (column, builderColumn, desc2) => {
|
|
668
|
-
if (Object.hasOwn(columns, column)) {
|
|
669
|
-
throw new ConfigError([
|
|
670
|
-
{
|
|
671
|
-
path: `${name}.${column}`,
|
|
672
|
-
message: `two fields would use the column "${column}"`,
|
|
673
|
-
hint: "rename one of them"
|
|
674
|
-
}
|
|
675
|
-
]);
|
|
676
|
-
}
|
|
677
|
-
columns[column] = builderColumn;
|
|
678
|
-
described.push([column, desc2]);
|
|
679
|
-
};
|
|
680
|
-
if (kind === "root") {
|
|
681
|
-
add("id", dialect.serial("id"), "id:int");
|
|
682
|
-
add("created_at", dialect.text("created_at").notNull(), "text!");
|
|
683
|
-
add("updated_at", dialect.text("updated_at").notNull(), "text!");
|
|
684
|
-
if (options.drafts) add("status", dialect.text("status").notNull().default("draft"), "status");
|
|
685
|
-
indexes.push({ column: "created_at", unique: false });
|
|
686
|
-
} else {
|
|
687
|
-
const parentKind = options.parentIdKind ?? "integer";
|
|
688
|
-
add(
|
|
689
|
-
"id",
|
|
690
|
-
kind === "array" ? dialect.text("id").primaryKey() : dialect.serial("id"),
|
|
691
|
-
`id:${kind}`
|
|
692
|
-
);
|
|
693
|
-
add(
|
|
694
|
-
"_parent_id",
|
|
695
|
-
parentKind === "text" ? dialect.text("_parent_id").notNull() : dialect.integer("_parent_id").notNull(),
|
|
696
|
-
parentKind
|
|
697
|
-
);
|
|
698
|
-
add("_order", dialect.integer("_order").notNull(), "int!");
|
|
699
|
-
indexes.push({ column: "_parent_id", unique: false });
|
|
700
|
-
if (kind === "values" && options.valueField) {
|
|
701
|
-
const valueKind = columnKind(options.valueField);
|
|
702
|
-
add("value", makeColumn(dialect, "value", valueKind), valueKind);
|
|
703
|
-
indexes.push({ column: "value", unique: false });
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
const walk = (list, path, topLevel) => {
|
|
707
|
-
for (const field of list) {
|
|
708
|
-
const fieldPath = [...path, field.name];
|
|
709
|
-
const base = fieldPath.map(snake).join("_");
|
|
710
|
-
if (field.type === "group") {
|
|
711
|
-
walk(field.fields, fieldPath, false);
|
|
712
|
-
} else if (field.type === "array") {
|
|
713
|
-
const child = buildTable(builder, `${name}__${base}`, "array", field.fields, {
|
|
714
|
-
parentIdKind: kind === "array" ? "text" : "integer"
|
|
715
|
-
});
|
|
716
|
-
children.push({ kind: "array", path: fieldPath, field, table: child });
|
|
717
|
-
described.push([base, "array", child.name]);
|
|
718
|
-
} else if ((field.type === "select" || field.type === "relationship") && field.hasMany) {
|
|
719
|
-
const child = buildTable(builder, `${name}__${base}`, "values", [], {
|
|
720
|
-
parentIdKind: kind === "array" ? "text" : "integer",
|
|
721
|
-
valueField: field
|
|
722
|
-
});
|
|
723
|
-
children.push({ kind: "values", path: fieldPath, field, table: child });
|
|
724
|
-
described.push([base, "values", child.name]);
|
|
725
|
-
} else {
|
|
726
|
-
const kindOfColumn = columnKind(field);
|
|
727
|
-
const localization = field.localized ? builder.localization : null;
|
|
728
|
-
const targets = localization ? localization.locales.map((locale) => ({
|
|
729
|
-
column: locale === localization.defaultLocale ? base : `${base}__${snake(locale)}`,
|
|
730
|
-
locale
|
|
731
|
-
})) : [{ column: base, locale: void 0 }];
|
|
732
|
-
for (const { column, locale } of targets) {
|
|
733
|
-
add(column, makeColumn(dialect, column, kindOfColumn), kindOfColumn);
|
|
734
|
-
columnModels.push(
|
|
735
|
-
locale === void 0 ? { path: fieldPath, field, column } : {
|
|
736
|
-
path: fieldPath,
|
|
737
|
-
field,
|
|
738
|
-
column,
|
|
739
|
-
locale,
|
|
740
|
-
defaultLocale: locale === localization?.defaultLocale
|
|
741
|
-
}
|
|
742
|
-
);
|
|
743
|
-
if (topLevel && kind === "root" && (field.unique || field.type === "slug")) {
|
|
744
|
-
indexes.push({ column, unique: true });
|
|
745
|
-
} else if (field.index || field.unique || field.type === "relationship" || field.type === "upload") {
|
|
746
|
-
indexes.push({ column, unique: false });
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
};
|
|
752
|
-
walk(fields, [], true);
|
|
753
|
-
const table = dialect.table(
|
|
754
|
-
name,
|
|
755
|
-
columns,
|
|
756
|
-
(t) => indexes.map(({ column, unique }) => {
|
|
757
|
-
const col3 = t[column];
|
|
758
|
-
return unique ? dialect.uniqueIndex(`${name}_${column}_unique`).on(col3) : dialect.index(`${name}_${column}_idx`).on(col3);
|
|
759
|
-
})
|
|
760
|
-
);
|
|
761
|
-
builder.description.push([name, kind, described, indexes]);
|
|
762
|
-
const model = { name, kind, table, fields, columns: columnModels, children };
|
|
763
|
-
return options.valueField ? { ...model, valueField: options.valueField } : model;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
971
|
// src/database.ts
|
|
767
972
|
var DEFAULT_TABLE_PREFIX = "ecms_";
|
|
768
973
|
var DEFAULT_MIGRATION_DIR = "easy-cms/migrations";
|
|
@@ -795,7 +1000,10 @@ var DrizzleDatabase = class {
|
|
|
795
1000
|
this.migrator = migrator;
|
|
796
1001
|
this.db = connection.db;
|
|
797
1002
|
for (const [slug, model] of schema.collections) {
|
|
798
|
-
this.builders.set(
|
|
1003
|
+
this.builders.set(
|
|
1004
|
+
slug,
|
|
1005
|
+
new WhereBuilder(this.db, dialect, model.config.drafts === true, schema.localization)
|
|
1006
|
+
);
|
|
799
1007
|
}
|
|
800
1008
|
}
|
|
801
1009
|
connection;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easy-cms/drizzle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Shared Drizzle ORM layer for Easy CMS database adapters",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"drizzle-orm": "0.45.3"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@easy-cms/core": "^0.
|
|
26
|
+
"@easy-cms/core": "^0.7.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@easy-cms/core": "^0.
|
|
29
|
+
"@easy-cms/core": "^0.7.0"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|