strapi-relation-names 1.2.2 → 1.3.1

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.
Files changed (33) hide show
  1. package/README.md +17 -42
  2. package/dist/{admin/SettingsPage-BebR4ghK.js → _chunks/SettingsPage-BMwx-BuP.js} +122 -48
  3. package/dist/{admin/SettingsPage-DFZP5CZs.mjs → _chunks/SettingsPage-D8leItKF.mjs} +122 -48
  4. package/dist/{admin/index-CiS6cuI9.mjs → _chunks/index-BeLPoAiU.mjs} +1 -1
  5. package/dist/{admin/index-DfSAe961.js → _chunks/index-COuIED98.js} +1 -1
  6. package/dist/admin/index.js +2 -3
  7. package/dist/admin/index.mjs +1 -1
  8. package/dist/admin/src/index.d.ts +1 -1
  9. package/dist/admin/src/pages/SettingsPage.d.ts +1 -0
  10. package/dist/admin/src/utils/template.d.ts +1 -14
  11. package/dist/server/index.js +267 -129
  12. package/dist/server/index.mjs +266 -127
  13. package/dist/server/src/bootstrap.d.ts +1 -1
  14. package/dist/server/src/config/index.d.ts +2 -0
  15. package/dist/server/src/controllers/index.d.ts +1 -1
  16. package/dist/server/src/controllers/settings.d.ts +1 -1
  17. package/dist/server/src/destroy.d.ts +1 -1
  18. package/dist/server/src/index.d.ts +20 -11
  19. package/dist/server/src/register.d.ts +1 -1
  20. package/dist/server/src/services/index.d.ts +8 -7
  21. package/dist/server/src/services/relation-labels.d.ts +6 -9
  22. package/dist/server/src/services/settings.d.ts +2 -2
  23. package/dist/server/src/services/utils/relation-hydration.d.ts +7 -0
  24. package/dist/server/src/services/utils/relation-labels.d.ts +4 -22
  25. package/dist/server/src/services/utils/relation-runtime.d.ts +6 -0
  26. package/dist/server/src/services/utils/relation-values.d.ts +1 -1
  27. package/dist/shared/records.d.ts +1 -0
  28. package/dist/shared/template.d.ts +55 -0
  29. package/package.json +28 -28
  30. /package/dist/{admin → _chunks}/de-Bc81LkbY.mjs +0 -0
  31. /package/dist/{admin → _chunks}/de-CsBvOsWR.js +0 -0
  32. /package/dist/{admin → _chunks}/en-Bk-HIXNd.mjs +0 -0
  33. /package/dist/{admin → _chunks}/en-DT4g6X2H.js +0 -0
@@ -1,12 +1,11 @@
1
1
  "use strict";
2
- Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
2
  const bootstrap = ({ strapi }) => {
4
3
  };
5
4
  const destroy = ({ strapi }) => {
6
5
  };
7
- const PLUGIN_ID$1 = "strapi-relation-names";
6
+ const PLUGIN_ID = "strapi-relation-names";
8
7
  const register = ({ strapi }) => {
9
- const relationLabels2 = strapi.plugin(PLUGIN_ID$1).service("relation-labels");
8
+ const relationLabels2 = strapi.plugin(PLUGIN_ID).service("relation-labels");
10
9
  const controllers2 = strapi.get("controllers");
11
10
  controllers2.extend("plugin::content-manager.relations", (controller) => ({
12
11
  ...controller,
@@ -58,6 +57,7 @@ const register = ({ strapi }) => {
58
57
  await decorateConfiguration(ctx, ctx.params.uid);
59
58
  },
60
59
  async updateContentTypeConfiguration(ctx, next) {
60
+ ctx.request.body = relationLabels2.sanitizeConfigurationUpdate(ctx.request.body);
61
61
  await controller.updateContentTypeConfiguration(ctx, next);
62
62
  await decorateConfiguration(ctx, ctx.params.uid);
63
63
  }
@@ -74,12 +74,28 @@ const register = ({ strapi }) => {
74
74
  }
75
75
  }));
76
76
  };
77
+ const isOptionalString = (value) => value === void 0 || typeof value === "string";
77
78
  const validateConfig = (config2) => {
78
- if (!config2 || typeof config2 !== "object" || Array.isArray(config2) || !Array.isArray(config2.collections) || !config2.collections.every(
79
- (collection) => typeof collection === "string"
80
- )) {
79
+ if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) {
80
+ throw new Error("The collections config must be an array of strings");
81
+ }
82
+ const typedConfig = config2;
83
+ if (!Array.isArray(typedConfig.collections) || !typedConfig.collections.every((collection) => typeof collection === "string")) {
81
84
  throw new Error("The collections config must be an array of strings");
82
85
  }
86
+ if (!isOptionalString(typedConfig.locale)) {
87
+ throw new Error("The locale config must be a string");
88
+ }
89
+ if (!isOptionalString(typedConfig.timeZone)) {
90
+ throw new Error("The timeZone config must be a string");
91
+ }
92
+ if (typedConfig.timeZone) {
93
+ try {
94
+ new Intl.DateTimeFormat(void 0, { timeZone: typedConfig.timeZone });
95
+ } catch {
96
+ throw new Error("The timeZone config must be a valid IANA timezone");
97
+ }
98
+ }
83
99
  };
84
100
  const config = {
85
101
  default: {
@@ -90,7 +106,7 @@ const config = {
90
106
  const contentTypes = {};
91
107
  const settings$1 = ({ strapi }) => ({
92
108
  async find(ctx) {
93
- const data = await strapi.plugin("strapi-relation-names").service("settings").get();
109
+ const data = await strapi.plugin(PLUGIN_ID).service("settings").get();
94
110
  ctx.body = { data };
95
111
  },
96
112
  async update(ctx) {
@@ -98,7 +114,7 @@ const settings$1 = ({ strapi }) => ({
98
114
  if (!body || typeof body !== "object" || Array.isArray(body)) {
99
115
  return ctx.badRequest("Settings must be an object");
100
116
  }
101
- const data = await strapi.plugin("strapi-relation-names").service("settings").set(body);
117
+ const data = await strapi.plugin(PLUGIN_ID).service("settings").set(body);
102
118
  ctx.body = { data };
103
119
  }
104
120
  });
@@ -136,7 +152,8 @@ const routes = {
136
152
  "content-api": contentAPIRoutes,
137
153
  admin: adminAPIRoutes
138
154
  };
139
- const SCALAR_TYPES = /* @__PURE__ */ new Set([
155
+ const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
156
+ const FIELD_TYPES = /* @__PURE__ */ new Set([
140
157
  "string",
141
158
  "text",
142
159
  "email",
@@ -151,30 +168,81 @@ const SCALAR_TYPES = /* @__PURE__ */ new Set([
151
168
  "time",
152
169
  "boolean"
153
170
  ]);
154
- const SYSTEM_SCALAR_FIELDS = /* @__PURE__ */ new Set([
155
- "id",
156
- "documentId",
157
- "createdAt",
158
- "updatedAt",
159
- "publishedAt",
160
- "locale",
161
- "status"
162
- ]);
163
- const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
164
- const isRecord$1 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
171
+ const DATE_TYPES = /* @__PURE__ */ new Set(["date", "datetime"]);
172
+ const SYSTEM_SCALAR_TYPES = {
173
+ id: "integer",
174
+ documentId: "string",
175
+ createdAt: "datetime",
176
+ updatedAt: "datetime",
177
+ publishedAt: "datetime",
178
+ locale: "string",
179
+ status: "string"
180
+ };
181
+ const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
182
+ const PATH_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
183
+ const getNestedSchemaUid = (attribute) => {
184
+ if (attribute.type === "component") {
185
+ return attribute.component;
186
+ }
187
+ if (attribute.type === "relation") {
188
+ return attribute.targetModel ?? attribute.target;
189
+ }
190
+ return void 0;
191
+ };
192
+ const getDate = (value) => {
193
+ if (value instanceof Date) {
194
+ return Number.isNaN(value.getTime()) ? void 0 : value;
195
+ }
196
+ if (typeof value !== "string" && typeof value !== "number") {
197
+ return void 0;
198
+ }
199
+ const date = new Date(value);
200
+ return Number.isNaN(date.getTime()) ? void 0 : date;
201
+ };
202
+ const isDateOnly = (value) => typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
203
+ const formatDate = (value, options, context, preserveDateOnly) => {
204
+ const date = getDate(value);
205
+ if (!date) {
206
+ return "";
207
+ }
208
+ const timeZone = preserveDateOnly && isDateOnly(value) ? "UTC" : context.timeZone;
209
+ return new Intl.DateTimeFormat(context.locale, {
210
+ ...options,
211
+ ...timeZone ? { timeZone } : {}
212
+ }).format(date);
213
+ };
214
+ const TRANSFORMS = {
215
+ toLocalDate: {
216
+ accepts: (fieldType) => DATE_TYPES.has(fieldType ?? ""),
217
+ apply: (value, context) => formatDate(value, { dateStyle: "short" }, context, true)
218
+ },
219
+ toLocalDateTime: {
220
+ accepts: (fieldType) => fieldType === "datetime",
221
+ apply: (value, context) => formatDate(value, { dateStyle: "short", timeStyle: "short" }, context, false)
222
+ }
223
+ };
165
224
  const getPathValue = (value, path) => {
166
225
  return path.split(".").reduce((current, segment) => {
167
226
  if (Array.isArray(current)) {
168
- return current.map((item) => isRecord$1(item) ? item[segment] : void 0).filter((item) => item !== void 0);
227
+ return current.map((item) => isRecord(item) ? item[segment] : void 0).filter((item) => item !== void 0);
169
228
  }
170
- return isRecord$1(current) ? current[segment] : void 0;
229
+ return isRecord(current) ? current[segment] : void 0;
171
230
  }, value);
172
231
  };
173
- const parseTemplate = (template) => {
232
+ const parseExpression = (value) => {
233
+ const parts = value.split("|").map((part) => part.trim());
234
+ const [path, ...transforms] = parts;
235
+ if (!path || !PATH_PATTERN.test(path) || transforms.some((name) => !IDENTIFIER_PATTERN.test(name))) {
236
+ return null;
237
+ }
238
+ return { path, transforms };
239
+ };
240
+ const parseTemplateAst = (template) => {
174
241
  if (!template.trim()) {
175
242
  return null;
176
243
  }
177
- const placeholders = [];
244
+ const tokens = [];
245
+ const expressions = [];
178
246
  let cursor = 0;
179
247
  while (cursor < template.length) {
180
248
  const openingBrace = template.indexOf("{", cursor);
@@ -183,69 +251,98 @@ const parseTemplate = (template) => {
183
251
  return null;
184
252
  }
185
253
  if (openingBrace === -1) {
254
+ tokens.push({ type: "text", value: template.slice(cursor) });
186
255
  break;
187
256
  }
257
+ if (openingBrace > cursor) {
258
+ tokens.push({ type: "text", value: template.slice(cursor, openingBrace) });
259
+ }
188
260
  const end = template.indexOf("}", openingBrace + 1);
189
261
  if (end === -1) {
190
262
  return null;
191
263
  }
192
- const name = template.slice(openingBrace + 1, end);
193
- if (!PLACEHOLDER_PATTERN.test(name)) {
264
+ const expression = parseExpression(template.slice(openingBrace + 1, end));
265
+ if (!expression) {
194
266
  return null;
195
267
  }
196
- placeholders.push(name);
268
+ tokens.push({ type: "expression", expression });
269
+ expressions.push(expression);
197
270
  cursor = end + 1;
198
271
  }
199
- return placeholders.length > 0 ? placeholders : null;
272
+ return expressions.length > 0 ? { tokens, expressions } : null;
273
+ };
274
+ const parseTemplate = (template) => {
275
+ const ast = parseTemplateAst(template);
276
+ return ast?.expressions.map(({ path }) => path) ?? null;
277
+ };
278
+ const resolvePathType = (path, targetSchema, resolveSchema) => {
279
+ const segments = path.split(".");
280
+ let schema = targetSchema;
281
+ for (const [index2, segment] of segments.entries()) {
282
+ const isLastSegment = index2 === segments.length - 1;
283
+ if (index2 === 0 && isLastSegment && SYSTEM_SCALAR_TYPES[segment]) {
284
+ return SYSTEM_SCALAR_TYPES[segment];
285
+ }
286
+ const attribute = schema.attributes?.[segment];
287
+ if (!attribute || attribute.private === true) {
288
+ return void 0;
289
+ }
290
+ if (isLastSegment) {
291
+ return FIELD_TYPES.has(attribute.type ?? "") ? attribute.type : void 0;
292
+ }
293
+ const nestedSchemaUid = getNestedSchemaUid(attribute);
294
+ if (!nestedSchemaUid || !resolveSchema) {
295
+ return void 0;
296
+ }
297
+ const nestedSchema = resolveSchema(nestedSchemaUid);
298
+ if (!nestedSchema) {
299
+ return void 0;
300
+ }
301
+ schema = nestedSchema;
302
+ }
303
+ return void 0;
200
304
  };
201
305
  const compileTemplate = (template, targetSchema, resolveSchema) => {
202
- const placeholders = parseTemplate(template);
203
- if (!placeholders || !targetSchema) {
306
+ const ast = parseTemplateAst(template);
307
+ if (!ast || !targetSchema) {
204
308
  return null;
205
309
  }
206
- const valid = placeholders.every((path) => {
207
- const segments = path.split(".");
208
- let schema = targetSchema;
209
- for (const [index2, segment] of segments.entries()) {
210
- const attribute = schema.attributes?.[segment];
211
- const isLastSegment = index2 === segments.length - 1;
212
- if (index2 === 0 && isLastSegment && SYSTEM_SCALAR_FIELDS.has(segment)) {
213
- return true;
214
- }
215
- if (!attribute || attribute.private === true) {
216
- return false;
217
- }
218
- if (isLastSegment) {
219
- return SCALAR_TYPES.has(attribute.type ?? "");
220
- }
221
- const nestedSchemaUid = attribute.type === "component" ? attribute.component : attribute.type === "relation" ? attribute.target : void 0;
222
- if (!nestedSchemaUid || !resolveSchema) {
223
- return false;
224
- }
225
- schema = resolveSchema(nestedSchemaUid);
226
- if (!schema) {
227
- return false;
228
- }
229
- }
230
- return false;
310
+ const valid = ast.expressions.every(({ path, transforms }) => {
311
+ const fieldType = resolvePathType(path, targetSchema, resolveSchema);
312
+ return Boolean(fieldType && transforms.every((name) => TRANSFORMS[name]?.accepts(fieldType)));
231
313
  });
232
314
  if (!valid) {
233
315
  return null;
234
316
  }
235
317
  return {
318
+ ...ast,
236
319
  template,
237
- placeholders,
238
- displayField: placeholders[0].split(".").slice(-1)[0] ?? placeholders[0]
320
+ placeholders: ast.expressions.map(({ path }) => path),
321
+ displayField: ast.expressions[0].path.split(".").slice(-1)[0] ?? ast.expressions[0].path
239
322
  };
240
323
  };
241
- const renderTemplate = (compiled, values) => {
242
- const rendered = compiled.template.replace(
243
- /\{([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*)\}/g,
244
- (_match, name) => {
245
- const value = getPathValue(values, name);
246
- return value === null || value === void 0 ? "" : String(value);
247
- }
248
- );
324
+ const applyTransform = (value, name, context) => {
325
+ const transform = TRANSFORMS[name];
326
+ if (!transform) {
327
+ return "";
328
+ }
329
+ if (Array.isArray(value)) {
330
+ return value.map((item) => applyTransform(item, name, context));
331
+ }
332
+ return transform.apply(value, context);
333
+ };
334
+ const renderTemplate = (compiled, values, context = compiled.transformContext ?? {}) => {
335
+ const tokens = compiled.tokens ?? parseTemplateAst(compiled.template)?.tokens ?? [];
336
+ const rendered = tokens.map((token) => {
337
+ if (token.type === "text") {
338
+ return token.value;
339
+ }
340
+ const expressionValue = token.expression.transforms.reduce(
341
+ (value, name) => applyTransform(value, name, context),
342
+ getPathValue(values, token.expression.path)
343
+ );
344
+ return expressionValue === null || expressionValue === void 0 ? "" : String(expressionValue);
345
+ }).join("");
249
346
  return rendered.trim();
250
347
  };
251
348
  const getIdentity = (value, modelType) => modelType === "component" ? value.id : value.documentId ?? value.id;
@@ -283,60 +380,15 @@ const applyLabel = (value, hydrated, runtime) => {
283
380
  };
284
381
  const relationValues = (value) => {
285
382
  if (Array.isArray(value)) {
286
- return value.filter(isRecord$1);
383
+ return value.filter(isRecord);
287
384
  }
288
- return isRecord$1(value) ? [value] : [];
385
+ return isRecord(value) ? [value] : [];
289
386
  };
290
387
  const replaceRelationValue = (original, values, hydrated, runtime) => {
291
388
  const decorated = values.map((value) => applyLabel(value, hydrated.get(value) ?? value, runtime));
292
389
  return Array.isArray(original) ? decorated : decorated[0] ?? original;
293
390
  };
294
391
  const isCollectionEnabled = (collections, uid) => collections.length === 0 || collections.includes(uid);
295
- const getOriginalMainField = async (strapi, sourceSchema, targetSchema, targetField, userAbility) => {
296
- const serviceName = sourceSchema.modelType === "component" ? "components" : "content-types";
297
- const configuration = await strapi.plugin("content-manager").service(serviceName).findConfiguration(sourceSchema);
298
- const configuredMainField = configuration?.metadatas?.[targetField]?.edit?.mainField;
299
- if (typeof configuredMainField !== "string" || configuredMainField === "documentId" || configuredMainField === "id") {
300
- return configuredMainField === "documentId" ? "documentId" : "id";
301
- }
302
- const permissionChecker = strapi.plugin("content-manager").service("permission-checker").create({ userAbility, model: targetSchema.uid });
303
- const attribute = targetSchema.attributes?.[configuredMainField];
304
- if (!attribute || attribute.private === true || permissionChecker.cannot.read(null, configuredMainField)) {
305
- return "documentId";
306
- }
307
- return configuredMainField;
308
- };
309
- const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility) => {
310
- const sourceSchema = strapi.getModel(sourceUid);
311
- const attribute = sourceSchema?.attributes?.[targetField];
312
- if (!sourceSchema || !attribute || attribute.type !== "relation" || !attribute.target) {
313
- return null;
314
- }
315
- const template = settings2.relations[sourceUid]?.[targetField];
316
- const targetSchema = strapi.getModel(attribute.target);
317
- const compiled = typeof template === "string" ? compileTemplate(
318
- template,
319
- targetSchema,
320
- (uid) => strapi.getModel(uid)
321
- ) : null;
322
- if (!compiled || !targetSchema) {
323
- return null;
324
- }
325
- const originalMainField = await getOriginalMainField(
326
- strapi,
327
- sourceSchema,
328
- targetSchema,
329
- targetField,
330
- userAbility
331
- );
332
- return {
333
- ...compiled,
334
- displayField: compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? originalMainField : compiled.displayField,
335
- sourceUid,
336
- targetUid: targetSchema.uid,
337
- originalMainField
338
- };
339
- };
340
392
  const buildPopulate = (placeholders) => {
341
393
  const populate = {};
342
394
  for (const placeholder of placeholders) {
@@ -409,8 +461,61 @@ const hydrateValues = async (strapi, ctx, runtime, values) => {
409
461
  return new Map(values.map((value) => [value, value]));
410
462
  }
411
463
  };
464
+ const getOriginalMainField = async (strapi, sourceSchema, targetSchema, targetField, userAbility) => {
465
+ const serviceName = sourceSchema.modelType === "component" ? "components" : "content-types";
466
+ const configuration = await strapi.plugin("content-manager").service(serviceName).findConfiguration(sourceSchema);
467
+ const configuredMainField = configuration?.metadatas?.[targetField]?.edit?.mainField;
468
+ if (typeof configuredMainField !== "string" || configuredMainField === "documentId" || configuredMainField === "id") {
469
+ return configuredMainField === "documentId" ? "documentId" : "id";
470
+ }
471
+ const permissionChecker = strapi.plugin("content-manager").service("permission-checker").create({ userAbility, model: targetSchema.uid });
472
+ const attribute = targetSchema.attributes?.[configuredMainField];
473
+ if (!attribute || attribute.private === true || permissionChecker.cannot.read(null, configuredMainField)) {
474
+ return "documentId";
475
+ }
476
+ return configuredMainField;
477
+ };
478
+ const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility, transformContext) => {
479
+ const sourceSchema = strapi.getModel(sourceUid);
480
+ const attribute = sourceSchema?.attributes?.[targetField];
481
+ if (!sourceSchema || !attribute || attribute.type !== "relation" || !attribute.target) {
482
+ return null;
483
+ }
484
+ const template = settings2.relations[sourceUid]?.[targetField];
485
+ const targetSchema = strapi.getModel(attribute.target);
486
+ const compiled = typeof template === "string" ? compileTemplate(
487
+ template,
488
+ targetSchema,
489
+ (uid) => strapi.getModel(uid)
490
+ ) : null;
491
+ if (!compiled || !targetSchema) {
492
+ return null;
493
+ }
494
+ const originalMainField = await getOriginalMainField(
495
+ strapi,
496
+ sourceSchema,
497
+ targetSchema,
498
+ targetField,
499
+ userAbility
500
+ );
501
+ return {
502
+ ...compiled,
503
+ transformContext,
504
+ displayField: compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? originalMainField : compiled.displayField,
505
+ sourceUid,
506
+ targetUid: targetSchema.uid,
507
+ originalMainField
508
+ };
509
+ };
510
+ const getTransformContext = (strapi) => {
511
+ const config2 = strapi.config.get("plugin::strapi-relation-names");
512
+ return {
513
+ locale: typeof config2?.locale === "string" ? config2.locale : void 0,
514
+ timeZone: typeof config2?.timeZone === "string" ? config2.timeZone : void 0
515
+ };
516
+ };
412
517
  const relationLabels = ({ strapi }) => {
413
- const getSettings = () => strapi.plugin("strapi-relation-names").service("settings").get();
518
+ const getSettings = () => strapi.plugin(PLUGIN_ID).service("settings").get();
414
519
  const decorateRelationResults = async (ctx, sourceUid, targetField, results) => {
415
520
  const settings2 = await getSettings();
416
521
  if (!isCollectionEnabled(settings2.collections, sourceUid)) {
@@ -421,26 +526,27 @@ const relationLabels = ({ strapi }) => {
421
526
  settings2,
422
527
  sourceUid,
423
528
  targetField,
424
- ctx.state.userAbility
529
+ ctx.state.userAbility,
530
+ getTransformContext(strapi)
425
531
  );
426
532
  if (!runtime) {
427
533
  return results;
428
534
  }
429
- const values = results.filter(isRecord$1);
535
+ const values = results.filter(isRecord);
430
536
  const hydrated = await hydrateValues(strapi, ctx, runtime, values);
431
537
  return results.map(
432
- (result) => isRecord$1(result) ? applyLabel(result, hydrated.get(result) ?? result, runtime) : result
538
+ (result) => isRecord(result) ? applyLabel(result, hydrated.get(result) ?? result, runtime) : result
433
539
  );
434
540
  };
435
541
  const decorateConfiguration = async (configuration, sourceUid) => {
436
- if (!configuration || !isRecord$1(configuration.metadatas)) {
542
+ if (!configuration || !isRecord(configuration.metadatas)) {
437
543
  return configuration;
438
544
  }
439
545
  const settings2 = await getSettings();
440
546
  const metadatas = { ...configuration.metadatas };
441
547
  const relationNames = {};
548
+ const sourceSchema = strapi.getModel(sourceUid);
442
549
  for (const [fieldName, metadata] of Object.entries(metadatas)) {
443
- const sourceSchema = strapi.getModel(sourceUid);
444
550
  const attribute = sourceSchema?.attributes?.[fieldName];
445
551
  if (!attribute || attribute.type !== "relation" || !attribute.target) {
446
552
  continue;
@@ -451,10 +557,10 @@ const relationLabels = ({ strapi }) => {
451
557
  targetSchema,
452
558
  (uid) => strapi.getModel(uid)
453
559
  );
454
- if (!compiled || !isRecord$1(metadata)) {
560
+ if (!compiled || !isRecord(metadata)) {
455
561
  continue;
456
562
  }
457
- const editMetadata = isRecord$1(metadata.edit) ? metadata.edit : {};
563
+ const editMetadata = isRecord(metadata.edit) ? metadata.edit : {};
458
564
  const configuredMainField = typeof editMetadata.mainField === "string" ? editMetadata.mainField : "id";
459
565
  const isNestedTemplate = compiled.placeholders.some(
460
566
  (placeholder) => placeholder.includes(".")
@@ -468,7 +574,7 @@ const relationLabels = ({ strapi }) => {
468
574
  mainField: displayField
469
575
  },
470
576
  list: {
471
- ...isRecord$1(metadata.list) ? metadata.list : {},
577
+ ...isRecord(metadata.list) ? metadata.list : {},
472
578
  mainField: displayField
473
579
  }
474
580
  };
@@ -478,13 +584,13 @@ const relationLabels = ({ strapi }) => {
478
584
  ...configuration,
479
585
  metadatas,
480
586
  settings: {
481
- ...isRecord$1(configuration.settings) ? configuration.settings : {},
587
+ ...isRecord(configuration.settings) ? configuration.settings : {},
482
588
  relationNames
483
589
  }
484
590
  };
485
591
  };
486
592
  const decorateContentManagerConfiguration = async (data, sourceUid) => {
487
- if (!isRecord$1(data)) {
593
+ if (!isRecord(data)) {
488
594
  return data;
489
595
  }
490
596
  const settings2 = await getSettings();
@@ -498,7 +604,7 @@ const relationLabels = ({ strapi }) => {
498
604
  if (next.component) {
499
605
  next.component = await decorateConfiguration(next.component, sourceUid);
500
606
  }
501
- if (isRecord$1(next.components)) {
607
+ if (isRecord(next.components)) {
502
608
  const components = { ...next.components };
503
609
  for (const [uid, configuration] of Object.entries(components)) {
504
610
  components[uid] = await decorateConfiguration(configuration, uid);
@@ -507,6 +613,31 @@ const relationLabels = ({ strapi }) => {
507
613
  }
508
614
  return next;
509
615
  };
616
+ const sanitizeConfigurationUpdate = (configuration) => {
617
+ if (!isRecord(configuration) || !isRecord(configuration.metadatas)) {
618
+ return configuration;
619
+ }
620
+ const configurationSettings = isRecord(configuration.settings) ? configuration.settings : {};
621
+ const relationNames = isRecord(configurationSettings.relationNames) ? configurationSettings.relationNames : {};
622
+ const { relationNames: _relationNames, ...sanitizedSettings } = configurationSettings;
623
+ const metadatas = { ...configuration.metadatas };
624
+ for (const [fieldName, metadata] of Object.entries(metadatas)) {
625
+ const relationName = relationNames[fieldName];
626
+ if (!isRecord(relationName) || !isRecord(metadata)) {
627
+ continue;
628
+ }
629
+ const sanitizedMetadata = { ...metadata };
630
+ if (isRecord(metadata.list)) {
631
+ const { mainField: _mainField, ...sanitizedList } = metadata.list;
632
+ sanitizedMetadata.list = sanitizedList;
633
+ }
634
+ if (relationName.mainField === "label" && isRecord(metadata.edit) && metadata.edit.mainField === "label") {
635
+ sanitizedMetadata.edit = { ...metadata.edit, mainField: "id" };
636
+ }
637
+ metadatas[fieldName] = sanitizedMetadata;
638
+ }
639
+ return { ...configuration, settings: sanitizedSettings, metadatas };
640
+ };
510
641
  const decorateCollectionResults = async (ctx, sourceUid, results) => {
511
642
  const settings2 = await getSettings();
512
643
  if (!isCollectionEnabled(settings2.collections, sourceUid)) {
@@ -514,6 +645,7 @@ const relationLabels = ({ strapi }) => {
514
645
  }
515
646
  const runtimeCache = /* @__PURE__ */ new Map();
516
647
  const sourceSchemas = /* @__PURE__ */ new Map();
648
+ const transformContext = getTransformContext(strapi);
517
649
  const getSchema = (uid) => {
518
650
  if (!sourceSchemas.has(uid)) {
519
651
  sourceSchemas.set(uid, strapi.getModel(uid));
@@ -527,7 +659,7 @@ const relationLabels = ({ strapi }) => {
527
659
  }
528
660
  return;
529
661
  }
530
- if (!isRecord$1(value)) {
662
+ if (!isRecord(value)) {
531
663
  return;
532
664
  }
533
665
  const schema = getSchema(uid);
@@ -537,7 +669,14 @@ const relationLabels = ({ strapi }) => {
537
669
  if (!runtimeCache.has(cacheKey)) {
538
670
  runtimeCache.set(
539
671
  cacheKey,
540
- await getRuntime(strapi, settings2, uid, fieldName, ctx.state.userAbility)
672
+ await getRuntime(
673
+ strapi,
674
+ settings2,
675
+ uid,
676
+ fieldName,
677
+ ctx.state.userAbility,
678
+ transformContext
679
+ )
541
680
  );
542
681
  }
543
682
  const runtime = runtimeCache.get(cacheKey);
@@ -573,6 +712,7 @@ const relationLabels = ({ strapi }) => {
573
712
  return {
574
713
  decorateCollectionResults,
575
714
  decorateConfiguration: decorateContentManagerConfiguration,
715
+ sanitizeConfigurationUpdate,
576
716
  decorateRelationResults,
577
717
  compileTemplate,
578
718
  parseTemplate,
@@ -583,9 +723,7 @@ const EMPTY_SETTINGS = {
583
723
  collections: [],
584
724
  relations: {}
585
725
  };
586
- const PLUGIN_ID = "strapi-relation-names";
587
726
  const STORE_KEY = "settings";
588
- const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
589
727
  const normalizeSettings = (value, collections = []) => {
590
728
  if (!isRecord(value) || !isRecord(value.relations)) {
591
729
  return { collections, relations: {} };
@@ -647,4 +785,4 @@ const index = {
647
785
  policies,
648
786
  middlewares
649
787
  };
650
- exports.default = index;
788
+ module.exports = index;