nextjs-cms 0.9.21 → 0.9.22
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/api/index.d.ts +3 -3
- package/dist/api/lib/serverActions.d.ts +3 -3
- package/dist/api/root.d.ts +6 -6
- package/dist/api/routers/navigation.d.ts +3 -3
- package/dist/cli/lib/update-sections.d.ts.map +1 -1
- package/dist/cli/lib/update-sections.js +72 -33
- package/dist/core/db/table-checker/MysqlTable.d.ts.map +1 -1
- package/dist/core/db/table-checker/MysqlTable.js +3 -1
- package/dist/core/fields/date-range.d.ts +4 -4
- package/dist/core/sections/category.d.ts +44 -44
- package/dist/core/sections/hasItems.d.ts +44 -44
- package/dist/core/sections/section.d.ts +23 -23
- package/dist/core/sections/simple.d.ts +6 -6
- package/dist/translations/base/en.d.ts +4 -0
- package/dist/translations/base/en.d.ts.map +1 -1
- package/dist/translations/base/en.js +4 -0
- package/dist/translations/client.d.ts +52 -4
- package/dist/translations/client.d.ts.map +1 -1
- package/dist/translations/server.d.ts +52 -4
- package/dist/translations/server.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-sections.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/update-sections.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update-sections.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/update-sections.ts"],"names":[],"mappings":"AAm3EA,wBAAsB,cAAc,CAAC,SAAS,UAAQ,iBAoBrD"}
|
|
@@ -383,6 +383,40 @@ function buildLocaleFieldConfig() {
|
|
|
383
383
|
order: 0,
|
|
384
384
|
});
|
|
385
385
|
}
|
|
386
|
+
async function createEditorPhotosTable(localized) {
|
|
387
|
+
await db.execute(sql `
|
|
388
|
+
CREATE TABLE IF NOT EXISTS \`editor_photos\` (
|
|
389
|
+
\`photo\` VARCHAR(100) NOT NULL PRIMARY KEY,
|
|
390
|
+
\`section\` VARCHAR(100) NOT NULL,
|
|
391
|
+
\`item_id\` VARCHAR(50) NOT NULL,
|
|
392
|
+
\`field\` VARCHAR(100) NOT NULL,
|
|
393
|
+
\`meta\` LONGTEXT,
|
|
394
|
+
${localized ? sql `\`locale\` VARCHAR(10) NOT NULL,` : sql ``}
|
|
395
|
+
\`linked\` BOOLEAN DEFAULT false,
|
|
396
|
+
\`created_at\` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
397
|
+
)
|
|
398
|
+
`);
|
|
399
|
+
}
|
|
400
|
+
async function ensureEditorPhotosLocaleColumn(defaultLocaleCode, knownColumns) {
|
|
401
|
+
let columns = knownColumns ?? (await MysqlTableChecker.getColumns('editor_photos').catch(() => []));
|
|
402
|
+
if (columns.includes('locale')) {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
if (columns.length === 0) {
|
|
406
|
+
await createEditorPhotosTable(true);
|
|
407
|
+
columns = await MysqlTableChecker.getColumns('editor_photos').catch(() => []);
|
|
408
|
+
if (columns.includes('locale')) {
|
|
409
|
+
log.info(chalk.gray(` - Created 'editor_photos' table with 'locale' column.`));
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
throw new Error(`Failed to create 'editor_photos' table with a 'locale' column.`);
|
|
413
|
+
}
|
|
414
|
+
await db.execute(sql `ALTER TABLE \`editor_photos\` ADD COLUMN \`locale\` VARCHAR(10) DEFAULT NULL`);
|
|
415
|
+
await db.execute(sql `UPDATE \`editor_photos\` SET \`locale\` = ${defaultLocaleCode} WHERE \`locale\` IS NULL`);
|
|
416
|
+
await db.execute(sql `ALTER TABLE \`editor_photos\` MODIFY COLUMN \`locale\` VARCHAR(10) NOT NULL`);
|
|
417
|
+
log.info(chalk.gray(` - Added 'locale' column to 'editor_photos' and backfilled existing rows with '${defaultLocaleCode}'.`));
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
386
420
|
async function ensureTableRegistryEntry(tableName, sectionName) {
|
|
387
421
|
await db
|
|
388
422
|
.insert(NextJsCmsTablesTable)
|
|
@@ -987,46 +1021,54 @@ const main = async (s) => {
|
|
|
987
1021
|
*/
|
|
988
1022
|
const RESERVED_TABLE_SUFFIX = '_locales';
|
|
989
1023
|
const RESERVED_COLUMN_NAME = 'locale';
|
|
990
|
-
const
|
|
1024
|
+
const configurationErrors = [];
|
|
1025
|
+
const builtSections = sections.map((_s) => {
|
|
1026
|
+
const section = _s.build();
|
|
1027
|
+
section.buildFields();
|
|
1028
|
+
return section;
|
|
1029
|
+
});
|
|
1030
|
+
const managedSectionTables = new Map(builtSections.map((section) => [section.db.table, section.name]));
|
|
991
1031
|
/**
|
|
992
1032
|
* Insert the sections into the database
|
|
993
1033
|
*/
|
|
994
|
-
for (const
|
|
995
|
-
const s = _s.build();
|
|
996
|
-
s.buildFields();
|
|
1034
|
+
for (const s of builtSections) {
|
|
997
1035
|
localesTableAssetMetadata.set(s.localesTableName, {
|
|
998
1036
|
sectionName: s.name,
|
|
999
1037
|
fields: getLocalizedAssetFields(s.fieldConfigs),
|
|
1000
1038
|
});
|
|
1001
1039
|
if (s.db.table.endsWith(RESERVED_TABLE_SUFFIX)) {
|
|
1002
|
-
|
|
1040
|
+
configurationErrors.push(`Section '${s.name}': table name '${s.db.table}' ends with reserved suffix '${RESERVED_TABLE_SUFFIX}'.`);
|
|
1003
1041
|
}
|
|
1004
1042
|
for (const field of s.fields) {
|
|
1005
1043
|
if (field.name === RESERVED_COLUMN_NAME) {
|
|
1006
|
-
|
|
1044
|
+
configurationErrors.push(`Section '${s.name}', field '${field.name}': field name '${RESERVED_COLUMN_NAME}' is reserved.`);
|
|
1007
1045
|
}
|
|
1008
1046
|
if (field.destinationDb) {
|
|
1009
1047
|
if (field.destinationDb.table.endsWith(RESERVED_TABLE_SUFFIX)) {
|
|
1010
|
-
|
|
1048
|
+
configurationErrors.push(`Section '${s.name}', field '${field.name}': destinationDb.table '${field.destinationDb.table}' ends with reserved suffix '${RESERVED_TABLE_SUFFIX}'.`);
|
|
1011
1049
|
}
|
|
1012
1050
|
if (field.destinationDb.itemIdentifier === RESERVED_COLUMN_NAME ||
|
|
1013
1051
|
field.destinationDb.selectIdentifier === RESERVED_COLUMN_NAME) {
|
|
1014
|
-
|
|
1052
|
+
configurationErrors.push(`Section '${s.name}', field '${field.name}': destinationDb identifier column name '${RESERVED_COLUMN_NAME}' is reserved.`);
|
|
1015
1053
|
}
|
|
1016
1054
|
}
|
|
1017
1055
|
if (isExternalSelectDbField(field)) {
|
|
1056
|
+
const ownerSectionName = managedSectionTables.get(field.db.table);
|
|
1057
|
+
if (ownerSectionName) {
|
|
1058
|
+
configurationErrors.push(`Section '${s.name}', field '${field.name}': db.table '${field.db.table}' belongs to managed section '${ownerSectionName}'. Use the 'section' prop instead of 'db' so update-sections does not treat the section table as an external lookup table.`);
|
|
1059
|
+
}
|
|
1018
1060
|
if (field.db.table.endsWith(RESERVED_TABLE_SUFFIX)) {
|
|
1019
|
-
|
|
1061
|
+
configurationErrors.push(`Section '${s.name}', field '${field.name}': db.table '${field.db.table}' ends with reserved suffix '${RESERVED_TABLE_SUFFIX}'.`);
|
|
1020
1062
|
}
|
|
1021
1063
|
const reservedColumns = [field.db.identifier, field.db.label, field.db.orderBy].filter((column) => column === RESERVED_COLUMN_NAME);
|
|
1022
1064
|
if (reservedColumns.length > 0) {
|
|
1023
|
-
|
|
1065
|
+
configurationErrors.push(`Section '${s.name}', field '${field.name}': db column name '${RESERVED_COLUMN_NAME}' is reserved.`);
|
|
1024
1066
|
}
|
|
1025
1067
|
}
|
|
1026
1068
|
}
|
|
1027
1069
|
const galleryForValidation = await s.getGallery();
|
|
1028
1070
|
if (galleryForValidation?.db.tableName && galleryForValidation.db.tableName.endsWith(RESERVED_TABLE_SUFFIX)) {
|
|
1029
|
-
|
|
1071
|
+
configurationErrors.push(`Section '${s.name}': gallery table '${galleryForValidation.db.tableName}' ends with reserved suffix '${RESERVED_TABLE_SUFFIX}'.`);
|
|
1030
1072
|
}
|
|
1031
1073
|
/**
|
|
1032
1074
|
* Generate the Drizzle schema for the table
|
|
@@ -1335,13 +1377,13 @@ const main = async (s) => {
|
|
|
1335
1377
|
}
|
|
1336
1378
|
}
|
|
1337
1379
|
/**
|
|
1338
|
-
* Reject the run before
|
|
1380
|
+
* Reject the run before schema writes if any section configuration is unsafe.
|
|
1339
1381
|
*/
|
|
1340
|
-
if (
|
|
1341
|
-
s.stop(`update-sections aborted: ${
|
|
1342
|
-
|
|
1343
|
-
`\n\nThe suffix '${RESERVED_TABLE_SUFFIX}' and the column name '${RESERVED_COLUMN_NAME}' are reserved for the localization system. Please
|
|
1344
|
-
throw new Error('update-sections:
|
|
1382
|
+
if (configurationErrors.length > 0) {
|
|
1383
|
+
s.stop(`update-sections aborted: ${configurationErrors.length} configuration conflict(s) detected.\n` +
|
|
1384
|
+
configurationErrors.map((e) => ` - ${e}`).join('\n') +
|
|
1385
|
+
`\n\nThe suffix '${RESERVED_TABLE_SUFFIX}' and the column name '${RESERVED_COLUMN_NAME}' are reserved for the localization system. Section-managed tables must be referenced with the 'section' prop. Please update cms.config.ts and re-run.`);
|
|
1386
|
+
throw new Error('update-sections: configuration conflicts detected');
|
|
1345
1387
|
}
|
|
1346
1388
|
/**
|
|
1347
1389
|
* Write schema file if schema generation is enabled
|
|
@@ -1423,6 +1465,7 @@ const main = async (s) => {
|
|
|
1423
1465
|
if (localizationCurrentlyEnabled && cmsConfig.localization) {
|
|
1424
1466
|
const configLocalizationState = buildStoredEnabledLocalizationState(cmsConfig.localization);
|
|
1425
1467
|
enableTargetState = configLocalizationState;
|
|
1468
|
+
const storedPendingDisableLocalizationState = storedEnabledLocalizationState?.transition?.type === 'disable' ? storedEnabledLocalizationState : null;
|
|
1426
1469
|
if (storedEnabledLocalizationState &&
|
|
1427
1470
|
storedEnabledLocalizationState.defaultLocale !== configLocalizationState.defaultLocale) {
|
|
1428
1471
|
s.stop(`Aborting. Changing localization defaultLocale is not supported (stored='${storedEnabledLocalizationState.defaultLocale}', config='${configLocalizationState.defaultLocale}'). Revert cms.config.ts and re-run update-sections.`);
|
|
@@ -1451,18 +1494,23 @@ const main = async (s) => {
|
|
|
1451
1494
|
}
|
|
1452
1495
|
}
|
|
1453
1496
|
const needsEditorPhotosMigration = editorPhotosExists && !editorPhotosHasLocale;
|
|
1454
|
-
const shouldRunEnableTransition = storedPendingEnableLocalizationState ||
|
|
1497
|
+
const shouldRunEnableTransition = storedPendingEnableLocalizationState ||
|
|
1498
|
+
!storedEnabledLocalizationState ||
|
|
1499
|
+
storedPendingDisableLocalizationState;
|
|
1455
1500
|
if (shouldRunEnableTransition) {
|
|
1456
1501
|
const affectedList = [...(needsEditorPhotosMigration ? ['editor_photos'] : []), ...affectedLocalizedTables];
|
|
1457
1502
|
const affectedListText = affectedList.length > 0
|
|
1458
1503
|
? affectedList.map((n) => ` - ${n}`).join('\n')
|
|
1459
1504
|
: ` - No existing tables need a locale backfill; update-sections will verify localized tables after sync.`;
|
|
1460
1505
|
const isEnableRetry = storedPendingEnableLocalizationState?.transition.status === 'pending';
|
|
1506
|
+
const isDisableRecovery = storedPendingDisableLocalizationState?.transition?.status === 'pending';
|
|
1461
1507
|
s.stop();
|
|
1462
1508
|
const confirm = await select({
|
|
1463
|
-
message: (
|
|
1464
|
-
? `A previous localization
|
|
1465
|
-
:
|
|
1509
|
+
message: (isDisableRecovery
|
|
1510
|
+
? `A previous localization disable attempt did not complete, but cms.config.ts has localization enabled again. update-sections will repair localization artifacts and clear the pending disable state after verification.\n\n`
|
|
1511
|
+
: isEnableRetry
|
|
1512
|
+
? `A previous localization enable attempt did not complete. update-sections will retry setup now.\n\n`
|
|
1513
|
+
: `It looks like you enabled localization. Your existing data in these tables will be marked as defaultLocale '${configLocalizationState.defaultLocale}':\n`) +
|
|
1466
1514
|
affectedListText +
|
|
1467
1515
|
`\n\n${chalk.redBright('WARNING:')} Existing data in these tables MUST already be in '${configLocalizationState.defaultLocale}'. ` +
|
|
1468
1516
|
`Changing defaultLocale after this point is not supported. If your data is not in '${configLocalizationState.defaultLocale}', ` +
|
|
@@ -1483,22 +1531,12 @@ const main = async (s) => {
|
|
|
1483
1531
|
type: 'enable',
|
|
1484
1532
|
status: 'pending',
|
|
1485
1533
|
startedAt: storedPendingEnableLocalizationState?.transition.startedAt ?? now,
|
|
1486
|
-
...(isEnableRetry ? { lastAttemptAt: now } : {}),
|
|
1534
|
+
...(isEnableRetry || isDisableRecovery ? { lastAttemptAt: now } : {}),
|
|
1487
1535
|
});
|
|
1488
1536
|
await writeStoredLocalizationState(enablePendingState);
|
|
1489
|
-
if (needsEditorPhotosMigration) {
|
|
1490
|
-
try {
|
|
1491
|
-
await db.execute(sql `ALTER TABLE \`editor_photos\` ADD COLUMN \`locale\` VARCHAR(10) DEFAULT NULL`);
|
|
1492
|
-
await db.execute(sql `UPDATE \`editor_photos\` SET \`locale\` = ${configLocalizationState.defaultLocale} WHERE \`locale\` IS NULL`);
|
|
1493
|
-
await db.execute(sql `ALTER TABLE \`editor_photos\` MODIFY COLUMN \`locale\` VARCHAR(10) NOT NULL`);
|
|
1494
|
-
log.info(chalk.gray(` - Added 'locale' column to 'editor_photos' and backfilled existing rows with '${configLocalizationState.defaultLocale}'.`));
|
|
1495
|
-
}
|
|
1496
|
-
catch (error) {
|
|
1497
|
-
console.error(chalk.red(` - Error migrating 'editor_photos' for localization enable:`, error));
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
1537
|
s.start();
|
|
1501
1538
|
}
|
|
1539
|
+
await ensureEditorPhotosLocaleColumn(configLocalizationState.defaultLocale, editorPhotosColumns);
|
|
1502
1540
|
}
|
|
1503
1541
|
else if (!localizationCurrentlyEnabled && storedEnabledLocalizationState) {
|
|
1504
1542
|
/**
|
|
@@ -1534,6 +1572,7 @@ const main = async (s) => {
|
|
|
1534
1572
|
` 3. Drop '_locales' tables entirely\n\n` +
|
|
1535
1573
|
`Affected tables:\n` +
|
|
1536
1574
|
affected.map((t) => ` - ${t.name} (${t.type})`).join('\n') +
|
|
1575
|
+
`\n\n${chalk.redBright('WARNING: This action is irreversible and will permanently delete data and affected tables. It is recommended to make a backup of your data before proceeding.')}` +
|
|
1537
1576
|
`\n\nProceed?`,
|
|
1538
1577
|
options: [
|
|
1539
1578
|
{ value: 'yes', label: `Yes, that's correct` },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MysqlTable.d.ts","sourceRoot":"","sources":["../../../../src/core/db/table-checker/MysqlTable.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAG7C;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;WACpC,iBAAiB;WAgBjB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"MysqlTable.d.ts","sourceRoot":"","sources":["../../../../src/core/db/table-checker/MysqlTable.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAG7C;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;WACpC,iBAAiB;WAgBjB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;WAoBhD,eAAe,CAAC,KAAK,EAAE,MAAM;;;kBAIR,MAAM;qBAAW,MAAM,EAAE;;;kBAC1B,MAAM;qBAAW,MAAM,EAAE;;;;kBACtB,MAAM;qBAAW,MAAM,EAAE;;;WA2DhD,yBAAyB,CAAC,KAAK,EAAE,MAAM;;mBAU7B,MAAM;kBACP,MAAM;kBACN,MAAM;iBACP,MAAM;qBACF,MAAM;mBACR,MAAM;;;CAahC"}
|
|
@@ -26,7 +26,9 @@ export class MysqlTableChecker extends DbTableChecker {
|
|
|
26
26
|
SELECT COLUMN_NAME
|
|
27
27
|
FROM information_schema.COLUMNS c
|
|
28
28
|
inner join information_schema.TABLES t ON t.TABLE_NAME = c.TABLE_NAME
|
|
29
|
-
WHERE t.
|
|
29
|
+
WHERE t.TABLE_SCHEMA = DATABASE()
|
|
30
|
+
AND c.TABLE_SCHEMA = DATABASE()
|
|
31
|
+
AND t.TABLE_NAME = ${tableName}`;
|
|
30
32
|
const _cols = [];
|
|
31
33
|
const _res = await db.execute(statement);
|
|
32
34
|
// @ts-ignore
|
|
@@ -70,13 +70,13 @@ declare const optionsSchema: z.ZodObject<{
|
|
|
70
70
|
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
71
71
|
defaultStartValue: z.ZodOptional<z.ZodDate>;
|
|
72
72
|
defaultEndValue: z.ZodOptional<z.ZodDate>;
|
|
73
|
-
order: z.ZodOptional<z.ZodNumber>;
|
|
74
|
-
localized: z.ZodOptional<z.ZodBoolean>;
|
|
75
73
|
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
76
74
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
77
75
|
defaultValue: z.ZodOptional<z.ZodAny>;
|
|
76
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
78
77
|
conditionalRules: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").ConditionalRule, import("../types/index.js").ConditionalRule>>>;
|
|
79
78
|
adminGenerated: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodLiteral<false>, z.ZodLiteral<"readonly">]>>;
|
|
79
|
+
localized: z.ZodOptional<z.ZodBoolean>;
|
|
80
80
|
}, z.core.$strict>;
|
|
81
81
|
declare const dateRangeFieldConfigSchema: z.ZodObject<{
|
|
82
82
|
/**
|
|
@@ -97,13 +97,13 @@ declare const dateRangeFieldConfigSchema: z.ZodObject<{
|
|
|
97
97
|
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
98
98
|
defaultStartValue: z.ZodOptional<z.ZodDate>;
|
|
99
99
|
defaultEndValue: z.ZodOptional<z.ZodDate>;
|
|
100
|
-
order: z.ZodOptional<z.ZodNumber>;
|
|
101
|
-
localized: z.ZodOptional<z.ZodBoolean>;
|
|
102
100
|
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
103
101
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
104
102
|
defaultValue: z.ZodOptional<z.ZodAny>;
|
|
103
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
105
104
|
conditionalRules: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").ConditionalRule, import("../types/index.js").ConditionalRule>>>;
|
|
106
105
|
adminGenerated: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodLiteral<false>, z.ZodLiteral<"readonly">]>>;
|
|
106
|
+
localized: z.ZodOptional<z.ZodBoolean>;
|
|
107
107
|
}, z.core.$strict>;
|
|
108
108
|
export type DateRangeFieldConfig = z.infer<typeof dateRangeFieldConfigSchema>;
|
|
109
109
|
/**
|