@easy-cms/drizzle 0.5.0 → 0.6.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 CHANGED
@@ -86,6 +86,9 @@ interface ChildModel {
86
86
  readonly path: readonly string[];
87
87
  readonly field: Field;
88
88
  readonly table: TableModel;
89
+ /** Localized: rows carry `_locale`, and the data holds `{ [locale]: rows }`. */
90
+ readonly locales?: readonly string[];
91
+ readonly defaultLocale?: string;
89
92
  }
90
93
  interface TableModel {
91
94
  readonly name: string;
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) setAt(data, child.path, []);
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 list = getAt(docs[index], child.path);
59
- list.push(child.kind === "array" ? { id: childRow.id, ...childDocs?.[i] } : childRow.value);
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) => ({ _parent_id: parentId, _order: i, value }));
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({ ...dataToRow(child.table, rowData), id, _parent_id: parentId, _order: i });
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
  }
@@ -413,13 +443,23 @@ function resolvePath(model, segments, drafts) {
413
443
  continue;
414
444
  }
415
445
  const child = model.children.find((c) => c.path.join(".") === path.join("."));
446
+ if (child?.locales) {
447
+ const next = segments[i + 1];
448
+ const named = next !== void 0 && child.locales.includes(next);
449
+ return {
450
+ kind: "child",
451
+ child,
452
+ rest: segments.slice(i + (named ? 2 : 1)),
453
+ locale: named ? next : child.defaultLocale
454
+ };
455
+ }
416
456
  if (child) return { kind: "child", child, rest: segments.slice(i + 1) };
417
457
  const locale = field.localized ? segments[i + 1] : void 0;
418
458
  const last = field.localized && locale !== void 0 ? i + 1 : i;
419
459
  if (last < segments.length - 1) {
420
460
  throw new QueryError(`Cannot query inside "${shown}" (${field.type} field)`);
421
461
  }
422
- if (field.type === "json" || field.type === "richText") {
462
+ if (field.type === "json" || field.type === "richText" || field.type === "blocks") {
423
463
  throw new QueryError(`Cannot query "${shown}": ${field.type} fields are not queryable`);
424
464
  }
425
465
  const same = (c) => c.path.join(".") === path.join(".");
@@ -518,14 +558,15 @@ var WhereBuilder = class {
518
558
  if (!OPERATORS.has(op)) throw new QueryError(`Unknown operator "${op}" on "${path}"`);
519
559
  if (target.kind === "column")
520
560
  return operatorSQL(this.dialect, target.column, target.field, op, value, path);
521
- return this.child(model, target.child, target.rest, op, value, path);
561
+ return this.child(model, target.child, target.rest, op, value, path, target.locale);
522
562
  });
523
563
  return parts.length === 1 ? parts[0] : and(...parts);
524
564
  }
525
565
  /** Conditions on array rows or hasMany values become (NOT) EXISTS subqueries. */
526
- child(parent, child, rest, op, value, path) {
566
+ child(parent, child, rest, op, value, path, locale) {
527
567
  const table = child.table;
528
- const link = eq2(col2(table, "_parent_id"), col2(parent, "id"));
568
+ const parentLink = eq2(col2(table, "_parent_id"), col2(parent, "id"));
569
+ const link = locale === void 0 ? parentLink : and(parentLink, eq2(col2(table, "_locale"), locale));
529
570
  const subquery = (condition) => exists(
530
571
  this.db.select({ one: sql`1` }).from(table.table).where(condition ? and(link, condition) : link)
531
572
  );
@@ -577,6 +618,8 @@ function columnKind(field) {
577
618
  return "boolean";
578
619
  case "json":
579
620
  case "richText":
621
+ // Blocks are stored whole as JSON: rows of different kinds don't fit one child table.
622
+ case "blocks":
580
623
  return "json";
581
624
  case "upload":
582
625
  case "relationship":
@@ -697,6 +740,14 @@ function buildTable(builder, name, kind, fields, options) {
697
740
  );
698
741
  add("_order", dialect.integer("_order").notNull(), "int!");
699
742
  indexes.push({ column: "_parent_id", unique: false });
743
+ if (options.localized) {
744
+ add(
745
+ "_locale",
746
+ dialect.text("_locale").notNull().default(options.localized),
747
+ `locale:${options.localized}`
748
+ );
749
+ indexes.push({ column: "_locale", unique: false });
750
+ }
700
751
  if (kind === "values" && options.valueField) {
701
752
  const valueKind = columnKind(options.valueField);
702
753
  add("value", makeColumn(dialect, "value", valueKind), valueKind);
@@ -710,17 +761,33 @@ function buildTable(builder, name, kind, fields, options) {
710
761
  if (field.type === "group") {
711
762
  walk(field.fields, fieldPath, false);
712
763
  } else if (field.type === "array") {
764
+ const localization = field.localized ? builder.localization : null;
713
765
  const child = buildTable(builder, `${name}__${base}`, "array", field.fields, {
714
- parentIdKind: kind === "array" ? "text" : "integer"
766
+ parentIdKind: kind === "array" ? "text" : "integer",
767
+ ...localization ? { localized: localization.defaultLocale } : {}
768
+ });
769
+ children.push({
770
+ kind: "array",
771
+ path: fieldPath,
772
+ field,
773
+ table: child,
774
+ ...localeInfo(localization)
715
775
  });
716
- children.push({ kind: "array", path: fieldPath, field, table: child });
717
776
  described.push([base, "array", child.name]);
718
777
  } else if ((field.type === "select" || field.type === "relationship") && field.hasMany) {
778
+ const localization = field.localized ? builder.localization : null;
719
779
  const child = buildTable(builder, `${name}__${base}`, "values", [], {
720
780
  parentIdKind: kind === "array" ? "text" : "integer",
721
- valueField: field
781
+ valueField: field,
782
+ ...localization ? { localized: localization.defaultLocale } : {}
783
+ });
784
+ children.push({
785
+ kind: "values",
786
+ path: fieldPath,
787
+ field,
788
+ table: child,
789
+ ...localeInfo(localization)
722
790
  });
723
- children.push({ kind: "values", path: fieldPath, field, table: child });
724
791
  described.push([base, "values", child.name]);
725
792
  } else {
726
793
  const kindOfColumn = columnKind(field);
@@ -762,6 +829,9 @@ function buildTable(builder, name, kind, fields, options) {
762
829
  const model = { name, kind, table, fields, columns: columnModels, children };
763
830
  return options.valueField ? { ...model, valueField: options.valueField } : model;
764
831
  }
832
+ function localeInfo(localization) {
833
+ return localization ? { locales: localization.locales, defaultLocale: localization.defaultLocale } : {};
834
+ }
765
835
 
766
836
  // src/database.ts
767
837
  var DEFAULT_TABLE_PREFIX = "ecms_";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easy-cms/drizzle",
3
- "version": "0.5.0",
3
+ "version": "0.6.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.5.0"
26
+ "@easy-cms/core": "^0.6.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@easy-cms/core": "^0.5.0"
29
+ "@easy-cms/core": "^0.6.0"
30
30
  },
31
31
  "publishConfig": {
32
32
  "access": "public"