nextjs-cms 0.9.40 → 0.9.41

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.
@@ -1 +1 @@
1
- {"version":3,"file":"update-sections.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/update-sections.ts"],"names":[],"mappings":"AAumFA,wBAAsB,cAAc,CAAC,SAAS,UAAQ,iBAoBrD"}
1
+ {"version":3,"file":"update-sections.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/update-sections.ts"],"names":[],"mappings":"AA4oFA,wBAAsB,cAAc,CAAC,SAAS,UAAQ,iBAoBrD"}
@@ -11,7 +11,7 @@ import { is } from '../../core/helpers/index.js';
11
11
  import { db } from '../../db/client.js';
12
12
  import { NextJsCmsTablesTable } from '../../db/schema.js';
13
13
  import { addTableKeys } from '../utils/add-table-keys.js';
14
- import { generateDrizzleSchema, generateLocalesTableSchema, resolveCaseStyleFns } from '../utils/schema-generator.js';
14
+ import { generateDrizzleSchema, generateLocalesTableSchema, getLocalizedLocalesConstraints, resolveCaseStyleFns, } from '../utils/schema-generator.js';
15
15
  /**
16
16
  * Returns all DB column names a field owns.
17
17
  * Most fields own a single column (field.name), but rarely some fields own two,
@@ -1535,6 +1535,23 @@ const main = async (spinnerObject) => {
1535
1535
  order: 0,
1536
1536
  });
1537
1537
  const localizedFieldInstances = s.fields.filter((f) => f.localized);
1538
+ /**
1539
+ * Propagate the section's own `unique` / `index` / `fulltext` constraints to the
1540
+ * `_locales` table, but only those whose columns are all localized (mirrors
1541
+ * `LocaleSubmit.checkUniqueConstraint`). Unique constraints get `locale` appended so
1542
+ * uniqueness is enforced per locale; indexes/fulltext keep their columns unchanged.
1543
+ */
1544
+ const localizedFieldNames = new Set(localizedFieldInstances.map((f) => f.name));
1545
+ const localizedConstraints = getLocalizedLocalesConstraints({
1546
+ localesTableName,
1547
+ localizedFieldNames,
1548
+ unique: s.db.unique,
1549
+ index: s.db.index,
1550
+ fulltext: s.db.fulltext,
1551
+ });
1552
+ const localizedUniqueColumns = localizedConstraints.unique;
1553
+ const localizedIndexColumns = localizedConstraints.index;
1554
+ const localizedFulltextColumns = localizedConstraints.fulltext;
1538
1555
  desiredTables.push({
1539
1556
  name: localesTableName,
1540
1557
  fields: [
@@ -1547,7 +1564,9 @@ const main = async (spinnerObject) => {
1547
1564
  sectionType: 'locales',
1548
1565
  identifier: idFieldConfig,
1549
1566
  primaryKey: [idFieldConfig],
1550
- unique: [{ columns: [parentIdFieldConfig, localeFieldConfig] }],
1567
+ unique: [{ columns: [parentIdFieldConfig, localeFieldConfig] }, ...localizedUniqueColumns],
1568
+ index: localizedIndexColumns.length > 0 ? localizedIndexColumns : undefined,
1569
+ fulltext: localizedFulltextColumns.length > 0 ? localizedFulltextColumns : undefined,
1551
1570
  });
1552
1571
  const localizedFieldConfigs = s.fieldConfigs.filter((f) => 'localized' in f && f.localized === true);
1553
1572
  const localesSchema = generateLocalesTableSchema({
@@ -1555,6 +1574,9 @@ const main = async (spinnerObject) => {
1555
1574
  parentIdentifier: s.db.identifier,
1556
1575
  localizedFields: localizedFieldConfigs,
1557
1576
  caseStyleFns,
1577
+ localizedUnique: localizedConstraints.unique,
1578
+ localizedIndex: localizedConstraints.index,
1579
+ localizedFulltext: localizedConstraints.fulltext,
1558
1580
  });
1559
1581
  drizzleTableSchemas.push(localesSchema.schema);
1560
1582
  localesSchema.drizzleImports.forEach((type) => drizzleImports.add(type));
@@ -1 +1 @@
1
- {"version":3,"file":"add-table-keys.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/add-table-keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAG7D,wBAAsB,YAAY,CAC9B,KAAK,EAAE;IACH,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,WAAW,EAAE,CAAA;IACzB,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,EAAE,CAAA;IACnD,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,EAAE,CAAA;IACpD,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,EAAE,CAAA;CACzD,EACD,YAAY,CAAC,EAAE;IACX,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;IACjD,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;IAChD,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;CACtD,mBAoPJ"}
1
+ {"version":3,"file":"add-table-keys.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/add-table-keys.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAE7D,wBAAsB,YAAY,CAC9B,KAAK,EAAE;IACH,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,WAAW,EAAE,CAAA;IACzB,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,EAAE,CAAA;IACnD,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,EAAE,CAAA;IACpD,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,EAAE,CAAA;KAAE,EAAE,CAAA;CACzD,EACD,YAAY,CAAC,EAAE;IACX,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;IACjD,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;IAChD,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAA;CACtD,mBA6SJ"}
@@ -88,7 +88,13 @@ export async function addTableKeys(table, existingKeys) {
88
88
  */
89
89
  if (!matchingUnique) {
90
90
  console.log(chalk.gray(` - Adding unique key with columns [${requestedUnique.columns.map((col) => col.name).join(', ')}] to '${table.name}'`));
91
- await db.execute(`ALTER TABLE \`${table.name}\` ADD UNIQUE ${requestedUnique.name ?? ''}(${uniqueColumns})`);
91
+ try {
92
+ await db.execute(`ALTER TABLE \`${table.name}\` ADD UNIQUE ${requestedUnique.name ?? ''}(${uniqueColumns})`);
93
+ }
94
+ catch (error) {
95
+ sqlErrors++;
96
+ console.error(chalk.red(` - Error adding unique key [${requestedUnique.columns.map((col) => col.name).join(', ')}] to '${table.name}':`, error));
97
+ }
92
98
  }
93
99
  }
94
100
  /**
@@ -100,7 +106,13 @@ export async function addTableKeys(table, existingKeys) {
100
106
  unique.columns.every((col) => existingUnique.columns.includes(col.name)));
101
107
  })) {
102
108
  console.log(chalk.gray(` - Dropping unique key with columns [${existingUnique.columns.join(', ')}] from '${table.name}'`));
103
- await db.execute(`ALTER TABLE \`${table.name}\` DROP INDEX \`${existingUnique.name}\``);
109
+ try {
110
+ await db.execute(`ALTER TABLE \`${table.name}\` DROP INDEX \`${existingUnique.name}\``);
111
+ }
112
+ catch (error) {
113
+ sqlErrors++;
114
+ console.error(chalk.red(` - Error dropping unique key '${existingUnique.name}' from '${table.name}':`, error));
115
+ }
104
116
  }
105
117
  }
106
118
  }
@@ -118,7 +130,13 @@ export async function addTableKeys(table, existingKeys) {
118
130
  if (!matchingIndex) {
119
131
  // Add index
120
132
  console.log(chalk.gray(` - Adding index with columns [${index.columns.map((col) => col.name).join(', ')}] to '${table.name}'`));
121
- await db.execute(`ALTER TABLE \`${table.name}\` ADD INDEX ${index.name ?? ''}(${indexColumns})`);
133
+ try {
134
+ await db.execute(`ALTER TABLE \`${table.name}\` ADD INDEX ${index.name ?? ''}(${indexColumns})`);
135
+ }
136
+ catch (error) {
137
+ sqlErrors++;
138
+ console.error(chalk.red(` - Error adding index [${index.columns.map((col) => col.name).join(', ')}] to '${table.name}':`, error));
139
+ }
122
140
  }
123
141
  }
124
142
  /**
@@ -130,7 +148,13 @@ export async function addTableKeys(table, existingKeys) {
130
148
  index.columns.every((col) => existingIndex.columns.includes(col.name)));
131
149
  })) {
132
150
  console.log(chalk.gray(` - Dropping index with columns [${existingIndex.columns.join(', ')}] from '${table.name}'`));
133
- await db.execute(`ALTER TABLE \`${table.name}\` DROP INDEX \`${existingIndex.name}\``);
151
+ try {
152
+ await db.execute(`ALTER TABLE \`${table.name}\` DROP INDEX \`${existingIndex.name}\``);
153
+ }
154
+ catch (error) {
155
+ sqlErrors++;
156
+ console.error(chalk.red(` - Error dropping index '${existingIndex.name}' from '${table.name}':`, error));
157
+ }
134
158
  }
135
159
  }
136
160
  }
@@ -151,7 +175,13 @@ export async function addTableKeys(table, existingKeys) {
151
175
  if (!matchingFulltext) {
152
176
  // Add fulltext index
153
177
  console.log(chalk.gray(` - Adding fulltext index with columns [${requestedFullTextIndex.columns.map((col) => col.name).join(', ')}] to '${table.name}'`));
154
- await db.execute(`ALTER TABLE \`${table.name}\` ADD FULLTEXT ${requestedFullTextIndex.name ?? ''}(${fulltextColumns})`);
178
+ try {
179
+ await db.execute(`ALTER TABLE \`${table.name}\` ADD FULLTEXT ${requestedFullTextIndex.name ?? ''}(${fulltextColumns})`);
180
+ }
181
+ catch (error) {
182
+ sqlErrors++;
183
+ console.error(chalk.red(` - Error adding fulltext index [${requestedFullTextIndex.columns.map((col) => col.name).join(', ')}] to '${table.name}':`, error));
184
+ }
155
185
  }
156
186
  }
157
187
  /**
@@ -163,7 +193,13 @@ export async function addTableKeys(table, existingKeys) {
163
193
  fulltext.columns.every((col) => existingFulltext.columns.includes(col.name)));
164
194
  })) {
165
195
  console.log(chalk.gray(` - Dropping fulltext index with columns [${existingFulltext.columns.join(', ')}] from '${table.name}'`));
166
- await db.execute(`ALTER TABLE \`${table.name}\` DROP INDEX \`${existingFulltext.name}\``);
196
+ try {
197
+ await db.execute(`ALTER TABLE \`${table.name}\` DROP INDEX \`${existingFulltext.name}\``);
198
+ }
199
+ catch (error) {
200
+ sqlErrors++;
201
+ console.error(chalk.red(` - Error dropping fulltext index '${existingFulltext.name}' from '${table.name}':`, error));
202
+ }
167
203
  }
168
204
  }
169
205
  }
@@ -41,6 +41,42 @@ export declare function generateDrizzleSchema(table: {
41
41
  schema: string;
42
42
  drizzleImports: string[];
43
43
  };
44
+ /** A constraint definition referencing columns by name (index / unique / fulltext). */
45
+ export type ConstraintDefinition = {
46
+ columns: {
47
+ name: string;
48
+ }[];
49
+ name?: string;
50
+ };
51
+ /**
52
+ * Computes the `unique` / `index` / `fulltext` constraints a section's `_locales`
53
+ * table should carry, derived from the section's own constraints.
54
+ *
55
+ * Only constraints whose columns are ALL localized fields apply — a constraint that
56
+ * touches a non-localized column belongs to the base table (that column does not
57
+ * exist in the `_locales` table). This mirrors `LocaleSubmit.checkUniqueConstraint`,
58
+ * which only enforces constraints whose columns are all localized.
59
+ *
60
+ * Translation rules:
61
+ * - unique → append `locale` so uniqueness is scoped per locale (matches the
62
+ * app-level check that adds `locale = <current locale>`).
63
+ * - index → columns unchanged.
64
+ * - fulltext → columns unchanged.
65
+ *
66
+ * Names are always `_locales`-scoped so they don't collide with the base table's
67
+ * constraint names in the generated schema.
68
+ */
69
+ export declare function getLocalizedLocalesConstraints(params: {
70
+ localesTableName: string;
71
+ localizedFieldNames: Set<string>;
72
+ unique?: ConstraintDefinition[];
73
+ index?: ConstraintDefinition[];
74
+ fulltext?: ConstraintDefinition[];
75
+ }): {
76
+ unique: ConstraintDefinition[];
77
+ index: ConstraintDefinition[];
78
+ fulltext: ConstraintDefinition[];
79
+ };
44
80
  /**
45
81
  * Generates a Drizzle schema for a `_locales` table that stores per-locale
46
82
  * translations of fields marked `localized: true`.
@@ -51,12 +87,17 @@ export declare function generateDrizzleSchema(table: {
51
87
  * - `locale` – locale code (e.g. "en", "ar")
52
88
  * - one column per localized field
53
89
  * - a unique constraint on `(parent_id, locale)`
90
+ * - any localized `unique` / `index` / `fulltext` constraints derived from the
91
+ * section (see `getLocalizedLocalesConstraints`)
54
92
  */
55
93
  export declare function generateLocalesTableSchema(params: {
56
94
  parentTableName: string;
57
95
  parentIdentifier: FieldConfig;
58
96
  localizedFields: FieldConfig[];
59
97
  caseStyleFns: CaseStyleFns;
98
+ localizedUnique?: ConstraintDefinition[];
99
+ localizedIndex?: ConstraintDefinition[];
100
+ localizedFulltext?: ConstraintDefinition[];
60
101
  }): {
61
102
  schema: string;
62
103
  drizzleImports: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"schema-generator.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/schema-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAoB7D,MAAM,WAAW,YAAY;IACzB,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACzC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAC7C;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;IAChD,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;CACpD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CA8BtE;AAED,wBAAgB,qBAAqB,CACjC,KAAK,EAAE;IACH,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,WAAW,CAAA;IACxB,mBAAmB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACxC,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACxD,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACzD,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC9D,EACD,YAAY,EAAE,YAAY,GAC3B;IACC,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B,CAgNA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAC/C,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,WAAW,CAAA;IAC7B,eAAe,EAAE,WAAW,EAAE,CAAA;IAC9B,YAAY,EAAE,YAAY,CAAA;CAC7B,GAAG;IACA,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B,CA+CA"}
1
+ {"version":3,"file":"schema-generator.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/schema-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAoB7D,MAAM,WAAW,YAAY;IACzB,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACzC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAC7C;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;IAChD,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;CACpD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CA8BtE;AAED,wBAAgB,qBAAqB,CACjC,KAAK,EAAE;IACH,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,WAAW,CAAA;IACxB,mBAAmB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACxC,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACxD,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACzD,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC9D,EACD,YAAY,EAAE,YAAY,GAC3B;IACC,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B,CAgNA;AAED,uFAAuF;AACvF,MAAM,MAAM,oBAAoB,GAAG;IAC/B,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACnD,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAChC,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAA;IAC/B,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAA;IAC9B,QAAQ,CAAC,EAAE,oBAAoB,EAAE,CAAA;CACpC,GAAG;IACA,MAAM,EAAE,oBAAoB,EAAE,CAAA;IAC9B,KAAK,EAAE,oBAAoB,EAAE,CAAA;IAC7B,QAAQ,EAAE,oBAAoB,EAAE,CAAA;CACnC,CAwBA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAC/C,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,WAAW,CAAA;IAC7B,eAAe,EAAE,WAAW,EAAE,CAAA;IAC9B,YAAY,EAAE,YAAY,CAAA;IAC1B,eAAe,CAAC,EAAE,oBAAoB,EAAE,CAAA;IACxC,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAA;IACvC,iBAAiB,CAAC,EAAE,oBAAoB,EAAE,CAAA;CAC7C,GAAG;IACA,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B,CAiDA"}
@@ -239,6 +239,42 @@ export function generateDrizzleSchema(table, caseStyleFns) {
239
239
  drizzleImports,
240
240
  };
241
241
  }
242
+ /**
243
+ * Computes the `unique` / `index` / `fulltext` constraints a section's `_locales`
244
+ * table should carry, derived from the section's own constraints.
245
+ *
246
+ * Only constraints whose columns are ALL localized fields apply — a constraint that
247
+ * touches a non-localized column belongs to the base table (that column does not
248
+ * exist in the `_locales` table). This mirrors `LocaleSubmit.checkUniqueConstraint`,
249
+ * which only enforces constraints whose columns are all localized.
250
+ *
251
+ * Translation rules:
252
+ * - unique → append `locale` so uniqueness is scoped per locale (matches the
253
+ * app-level check that adds `locale = <current locale>`).
254
+ * - index → columns unchanged.
255
+ * - fulltext → columns unchanged.
256
+ *
257
+ * Names are always `_locales`-scoped so they don't collide with the base table's
258
+ * constraint names in the generated schema.
259
+ */
260
+ export function getLocalizedLocalesConstraints(params) {
261
+ const { localesTableName, localizedFieldNames } = params;
262
+ const allLocalized = (constraint) => constraint.columns.every((column) => localizedFieldNames.has(column.name));
263
+ const columnSlug = (constraint) => constraint.columns.map((column) => column.name).join('_');
264
+ const unique = (params.unique ?? []).filter(allLocalized).map((constraint) => ({
265
+ columns: [...constraint.columns, { name: 'locale' }],
266
+ name: `${localesTableName}_${columnSlug(constraint)}_locale_unique`,
267
+ }));
268
+ const index = (params.index ?? []).filter(allLocalized).map((constraint) => ({
269
+ columns: constraint.columns,
270
+ name: `${localesTableName}_${columnSlug(constraint)}_index`,
271
+ }));
272
+ const fulltext = (params.fulltext ?? []).filter(allLocalized).map((constraint) => ({
273
+ columns: constraint.columns,
274
+ name: `${localesTableName}_${columnSlug(constraint)}_fulltext`,
275
+ }));
276
+ return { unique, index, fulltext };
277
+ }
242
278
  /**
243
279
  * Generates a Drizzle schema for a `_locales` table that stores per-locale
244
280
  * translations of fields marked `localized: true`.
@@ -249,6 +285,8 @@ export function generateDrizzleSchema(table, caseStyleFns) {
249
285
  * - `locale` – locale code (e.g. "en", "ar")
250
286
  * - one column per localized field
251
287
  * - a unique constraint on `(parent_id, locale)`
288
+ * - any localized `unique` / `index` / `fulltext` constraints derived from the
289
+ * section (see `getLocalizedLocalesConstraints`)
252
290
  */
253
291
  export function generateLocalesTableSchema(params) {
254
292
  const tableName = `${params.parentTableName}_locales`;
@@ -286,6 +324,8 @@ export function generateLocalesTableSchema(params) {
286
324
  name: tableName,
287
325
  fields: allFields,
288
326
  identifier: idField,
289
- unique: [{ columns: [{ name: 'parent_id' }, { name: 'locale' }] }],
327
+ unique: [{ columns: [{ name: 'parent_id' }, { name: 'locale' }] }, ...(params.localizedUnique ?? [])],
328
+ index: params.localizedIndex,
329
+ fulltext: params.localizedFulltext,
290
330
  }, params.caseStyleFns);
291
331
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextjs-cms",
3
- "version": "0.9.40",
3
+ "version": "0.9.41",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",