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