@strapi/core 5.51.0 → 5.51.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.
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var name = "@strapi/core";
6
- var version = "5.51.0";
6
+ var version = "5.51.1";
7
7
  var description = "Core of Strapi";
8
8
  var homepage = "https://strapi.io";
9
9
  var bugs = {
@@ -65,15 +65,15 @@ var dependencies = {
65
65
  "@koa/router": "12.0.2",
66
66
  "@modelcontextprotocol/sdk": "1.29.0",
67
67
  "@paralleldrive/cuid2": "2.2.2",
68
- "@strapi/admin": "5.51.0",
69
- "@strapi/database": "5.51.0",
70
- "@strapi/generators": "5.51.0",
71
- "@strapi/logger": "5.51.0",
72
- "@strapi/openapi": "5.51.0",
73
- "@strapi/permissions": "5.51.0",
74
- "@strapi/types": "5.51.0",
75
- "@strapi/typescript-utils": "5.51.0",
76
- "@strapi/utils": "5.51.0",
68
+ "@strapi/admin": "5.51.1",
69
+ "@strapi/database": "5.51.1",
70
+ "@strapi/generators": "5.51.1",
71
+ "@strapi/logger": "5.51.1",
72
+ "@strapi/openapi": "5.51.1",
73
+ "@strapi/permissions": "5.51.1",
74
+ "@strapi/types": "5.51.1",
75
+ "@strapi/typescript-utils": "5.51.1",
76
+ "@strapi/utils": "5.51.1",
77
77
  "@vercel/stega": "0.1.2",
78
78
  bcryptjs: "2.4.3",
79
79
  boxen: "5.1.2",
@@ -137,12 +137,12 @@ var devDependencies = {
137
137
  "@types/mime-types": "2.1.1",
138
138
  "@types/node": "20.19.41",
139
139
  "@types/node-schedule": "2.1.7",
140
- "eslint-config-custom": "5.51.0",
140
+ "eslint-config-custom": "5.51.1",
141
141
  jest: "29.6.0",
142
142
  supertest: "7.2.2",
143
- tsconfig: "5.51.0",
143
+ tsconfig: "5.51.1",
144
144
  vitest: "catalog:",
145
- "vitest-config": "5.51.0"
145
+ "vitest-config": "5.51.1"
146
146
  };
147
147
  var engines = {
148
148
  node: ">=20.0.0 <=26.x.x",
@@ -1,5 +1,5 @@
1
1
  var name = "@strapi/core";
2
- var version = "5.51.0";
2
+ var version = "5.51.1";
3
3
  var description = "Core of Strapi";
4
4
  var homepage = "https://strapi.io";
5
5
  var bugs = {
@@ -61,15 +61,15 @@ var dependencies = {
61
61
  "@koa/router": "12.0.2",
62
62
  "@modelcontextprotocol/sdk": "1.29.0",
63
63
  "@paralleldrive/cuid2": "2.2.2",
64
- "@strapi/admin": "5.51.0",
65
- "@strapi/database": "5.51.0",
66
- "@strapi/generators": "5.51.0",
67
- "@strapi/logger": "5.51.0",
68
- "@strapi/openapi": "5.51.0",
69
- "@strapi/permissions": "5.51.0",
70
- "@strapi/types": "5.51.0",
71
- "@strapi/typescript-utils": "5.51.0",
72
- "@strapi/utils": "5.51.0",
64
+ "@strapi/admin": "5.51.1",
65
+ "@strapi/database": "5.51.1",
66
+ "@strapi/generators": "5.51.1",
67
+ "@strapi/logger": "5.51.1",
68
+ "@strapi/openapi": "5.51.1",
69
+ "@strapi/permissions": "5.51.1",
70
+ "@strapi/types": "5.51.1",
71
+ "@strapi/typescript-utils": "5.51.1",
72
+ "@strapi/utils": "5.51.1",
73
73
  "@vercel/stega": "0.1.2",
74
74
  bcryptjs: "2.4.3",
75
75
  boxen: "5.1.2",
@@ -133,12 +133,12 @@ var devDependencies = {
133
133
  "@types/mime-types": "2.1.1",
134
134
  "@types/node": "20.19.41",
135
135
  "@types/node-schedule": "2.1.7",
136
- "eslint-config-custom": "5.51.0",
136
+ "eslint-config-custom": "5.51.1",
137
137
  jest: "29.6.0",
138
138
  supertest: "7.2.2",
139
- tsconfig: "5.51.0",
139
+ tsconfig: "5.51.1",
140
140
  vitest: "catalog:",
141
- "vitest-config": "5.51.0"
141
+ "vitest-config": "5.51.1"
142
142
  };
143
143
  var engines = {
144
144
  node: ">=20.0.0 <=26.x.x",
@@ -1 +1 @@
1
- {"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../../src/services/document-service/entries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAYlD,QAAA,MAAM,oBAAoB,GACxB,KAAK,GAAG,CAAC,WAAW,EACpB,iBAAiB,OAAO,CAAC,eAAe,CAAC,eAAe;sBAId,GAAG;iBAuEd,MAAM,UAAgB,GAAG;4BAUd,GAAG,WAAiB,GAAG;qBA4B9B,GAAG,WAAiB,GAAG;0BAqBlB,GAAG,WAAiB,GAAG;CA4BhE,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
1
+ {"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../../src/services/document-service/entries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAkClD,QAAA,MAAM,oBAAoB,GACxB,KAAK,GAAG,CAAC,WAAW,EACpB,iBAAiB,OAAO,CAAC,eAAe,CAAC,eAAe;sBAId,GAAG;iBAwEd,MAAM,UAAgB,GAAG;4BAUd,GAAG,WAAiB,GAAG;qBA6B9B,GAAG,WAAiB,GAAG;0BAqBlB,GAAG,WAAiB,GAAG;CA4BhE,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -9,6 +9,18 @@ var params = require('./params.js');
9
9
  var index = require('./attributes/index.js');
10
10
  var data = require('./transform/data.js');
11
11
 
12
+ /**
13
+ * Reads and validates the `api.documents.strictRelations` flag.
14
+ * false/undefined => legacy behaviour (relational required constraints not enforced),
15
+ * true => enforce required media and relations on non-draft writes.
16
+ * Mirrors the validation of `api.documents.strictParams` (see repository.ts).
17
+ */ const isStrictRelationsEnabled = ()=>{
18
+ const rawStrictRelations = strapi.config.get('api.documents.strictRelations', undefined);
19
+ if (rawStrictRelations !== undefined && rawStrictRelations !== false && rawStrictRelations !== true) {
20
+ throw new strapiUtils.errors.ValidationError(`Invalid config.api.documents.strictRelations value: "${rawStrictRelations}". Expected boolean (true or false).`);
21
+ }
22
+ return rawStrictRelations === true;
23
+ };
12
24
  const createEntriesService = (uid, entityValidator)=>{
13
25
  const contentType = strapi.contentType(uid);
14
26
  async function createEntry(params$1 = {}) {
@@ -63,7 +75,8 @@ const createEntriesService = (uid, entityValidator)=>{
63
75
  const validData = await entityValidator.validateEntityCreation(contentType, data, {
64
76
  // Note: publishedAt value will always be set when DP is disabled
65
77
  isDraft: !params$1?.data?.publishedAt,
66
- locale: params$1?.locale
78
+ locale: params$1?.locale,
79
+ strictRelations: isStrictRelationsEnabled()
67
80
  });
68
81
  // Component handling
69
82
  const componentData = await components.createComponents(uid, validData);
@@ -95,7 +108,8 @@ const createEntriesService = (uid, entityValidator)=>{
95
108
  const query$1 = query.transformParamsToQuery(uid, params.pickSelectionParams(restParams)); // select / populate
96
109
  const validData = await entityValidator.validateEntityUpdate(contentType, data, {
97
110
  isDraft: !params$1?.data?.publishedAt,
98
- locale: params$1?.locale
111
+ locale: params$1?.locale,
112
+ strictRelations: isStrictRelationsEnabled()
99
113
  }, entryToUpdate);
100
114
  // Component handling
101
115
  const componentData = await components.updateComponents(uid, entryToUpdate, validData);
@@ -1 +1 @@
1
- {"version":3,"file":"entries.js","sources":["../../../src/services/document-service/entries.ts"],"sourcesContent":["import type { UID, Modules } from '@strapi/types';\nimport { async, errors } from '@strapi/utils';\nimport { assoc, omit } from 'lodash/fp';\n\nimport * as components from './components';\n\nimport { transformParamsDocumentId } from './transform/id-transform';\nimport { transformParamsToQuery } from './transform/query';\nimport { pickSelectionParams } from './params';\nimport { applyTransforms } from './attributes';\nimport { clearTransformDataRequestCache, transformData } from './transform/data';\n\nconst createEntriesService = (\n uid: UID.ContentType,\n entityValidator: Modules.EntityValidator.EntityValidator\n) => {\n const contentType = strapi.contentType(uid);\n\n async function createEntry(params = {} as any) {\n const { data, ...restParams } = await transformParamsDocumentId(uid, params);\n const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate\n\n // Validation\n if (!data) {\n throw new Error('Create requires data attribute');\n }\n\n // Check for uniqueness based on documentId and locale (if localized)\n if (data.documentId) {\n const i18nService = strapi.plugin('i18n')?.service('content-types');\n const isLocalized = i18nService?.isLocalizedContentType(contentType) ?? false;\n const hasDraftAndPublish = contentType.options?.draftAndPublish === true;\n\n const whereClause: Record<string, unknown> = { documentId: data.documentId };\n\n if (isLocalized) {\n whereClause.locale = data.locale;\n }\n\n let publishedStateDescription = '';\n\n if (hasDraftAndPublish) {\n if (data.publishedAt) {\n // Current entry is published, check for existing published entry\n whereClause.publishedAt = { $notNull: true };\n publishedStateDescription = 'published';\n } else {\n // Current entry is a draft, check for existing draft entry\n whereClause.publishedAt = { $null: true };\n publishedStateDescription = 'draft';\n }\n }\n\n const existingEntry = await strapi.db.query(uid).findOne({\n select: ['id'],\n where: whereClause,\n });\n\n if (existingEntry) {\n let errorMsg = `A ${publishedStateDescription} entry with documentId \"${data.documentId}\"`;\n if (isLocalized && data.locale) {\n errorMsg += ` and locale \"${data.locale}\"`;\n }\n errorMsg += ` already exists for UID \"${uid}\". This combination must be unique.`;\n throw new errors.ApplicationError(errorMsg);\n }\n }\n\n const validData = await entityValidator.validateEntityCreation(contentType, data, {\n // Note: publishedAt value will always be set when DP is disabled\n isDraft: !params?.data?.publishedAt,\n locale: params?.locale,\n });\n\n // Component handling\n const componentData = await components.createComponents(uid, validData);\n const dataWithComponents = components.assignComponentData(\n contentType,\n componentData,\n validData\n );\n\n const entryData = applyTransforms(contentType, dataWithComponents);\n\n const doc = await strapi.db.query(uid).create({ ...query, data: entryData });\n\n return doc;\n }\n\n async function deleteEntry(id: number, query = {} as any) {\n const componentsToDelete = await components.getComponents(uid, { id });\n\n const deletedEntry = await strapi.db.query(uid).delete({ ...query, where: { id } });\n\n await components.deleteComponents(uid, componentsToDelete as any, { loadComponents: false });\n\n return deletedEntry;\n }\n\n async function updateEntry(entryToUpdate: any, params = {} as any) {\n const { data, ...restParams } = await transformParamsDocumentId(uid, params);\n const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate\n\n const validData = await entityValidator.validateEntityUpdate(\n contentType,\n data,\n {\n isDraft: !params?.data?.publishedAt, // Always update the draft version\n locale: params?.locale,\n },\n entryToUpdate\n );\n // Component handling\n const componentData = await components.updateComponents(uid, entryToUpdate, validData as any);\n const dataWithComponents = components.assignComponentData(\n contentType,\n componentData,\n validData\n );\n\n const entryData = applyTransforms(contentType, dataWithComponents);\n\n return strapi.db\n .query(uid)\n .update({ ...query, where: { id: entryToUpdate.id }, data: entryData });\n }\n\n async function publishEntry(entry: any, params = {} as any) {\n clearTransformDataRequestCache();\n\n return async.pipe(\n omit('id'),\n assoc('publishedAt', new Date()),\n (draft) => {\n const opts = {\n uid,\n locale: draft.locale,\n status: 'published',\n allowMissingId: true,\n useRequestCache: false,\n };\n return transformData(draft, opts);\n },\n // Create the published entry\n (draft) => createEntry({ ...params, data: draft, locale: draft.locale, status: 'published' })\n )(entry);\n }\n\n async function discardDraftEntry(entry: any, params = {} as any) {\n clearTransformDataRequestCache();\n\n return async.pipe(\n omit('id'),\n assoc('publishedAt', null),\n (entry) => {\n const opts = {\n uid,\n locale: entry.locale,\n status: 'draft',\n allowMissingId: true,\n useRequestCache: false,\n };\n return transformData(entry, opts);\n },\n // Create the draft entry\n (data) => createEntry({ ...params, locale: data.locale, data, status: 'draft' })\n )(entry);\n }\n\n return {\n create: createEntry,\n delete: deleteEntry,\n update: updateEntry,\n publish: publishEntry,\n discardDraft: discardDraftEntry,\n };\n};\n\nexport { createEntriesService };\n"],"names":["createEntriesService","uid","entityValidator","contentType","strapi","createEntry","params","data","restParams","transformParamsDocumentId","query","transformParamsToQuery","pickSelectionParams","Error","documentId","i18nService","plugin","service","isLocalized","isLocalizedContentType","hasDraftAndPublish","options","draftAndPublish","whereClause","locale","publishedStateDescription","publishedAt","$notNull","$null","existingEntry","db","findOne","select","where","errorMsg","errors","ApplicationError","validData","validateEntityCreation","isDraft","componentData","components","dataWithComponents","entryData","applyTransforms","doc","create","deleteEntry","id","componentsToDelete","deletedEntry","delete","loadComponents","updateEntry","entryToUpdate","validateEntityUpdate","update","publishEntry","entry","clearTransformDataRequestCache","async","pipe","omit","assoc","Date","draft","opts","status","allowMissingId","useRequestCache","transformData","discardDraftEntry","publish","discardDraft"],"mappings":"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"}
1
+ {"version":3,"file":"entries.js","sources":["../../../src/services/document-service/entries.ts"],"sourcesContent":["import type { UID, Modules } from '@strapi/types';\nimport { async, errors } from '@strapi/utils';\nimport { assoc, omit } from 'lodash/fp';\n\nimport * as components from './components';\n\nimport { transformParamsDocumentId } from './transform/id-transform';\nimport { transformParamsToQuery } from './transform/query';\nimport { pickSelectionParams } from './params';\nimport { applyTransforms } from './attributes';\nimport { clearTransformDataRequestCache, transformData } from './transform/data';\n\n/**\n * Reads and validates the `api.documents.strictRelations` flag.\n * false/undefined => legacy behaviour (relational required constraints not enforced),\n * true => enforce required media and relations on non-draft writes.\n * Mirrors the validation of `api.documents.strictParams` (see repository.ts).\n */\nconst isStrictRelationsEnabled = (): boolean => {\n const rawStrictRelations: unknown = strapi.config.get('api.documents.strictRelations', undefined);\n\n if (\n rawStrictRelations !== undefined &&\n rawStrictRelations !== false &&\n rawStrictRelations !== true\n ) {\n throw new errors.ValidationError(\n `Invalid config.api.documents.strictRelations value: \"${rawStrictRelations}\". Expected boolean (true or false).`\n );\n }\n\n return rawStrictRelations === true;\n};\n\nconst createEntriesService = (\n uid: UID.ContentType,\n entityValidator: Modules.EntityValidator.EntityValidator\n) => {\n const contentType = strapi.contentType(uid);\n\n async function createEntry(params = {} as any) {\n const { data, ...restParams } = await transformParamsDocumentId(uid, params);\n const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate\n\n // Validation\n if (!data) {\n throw new Error('Create requires data attribute');\n }\n\n // Check for uniqueness based on documentId and locale (if localized)\n if (data.documentId) {\n const i18nService = strapi.plugin('i18n')?.service('content-types');\n const isLocalized = i18nService?.isLocalizedContentType(contentType) ?? false;\n const hasDraftAndPublish = contentType.options?.draftAndPublish === true;\n\n const whereClause: Record<string, unknown> = { documentId: data.documentId };\n\n if (isLocalized) {\n whereClause.locale = data.locale;\n }\n\n let publishedStateDescription = '';\n\n if (hasDraftAndPublish) {\n if (data.publishedAt) {\n // Current entry is published, check for existing published entry\n whereClause.publishedAt = { $notNull: true };\n publishedStateDescription = 'published';\n } else {\n // Current entry is a draft, check for existing draft entry\n whereClause.publishedAt = { $null: true };\n publishedStateDescription = 'draft';\n }\n }\n\n const existingEntry = await strapi.db.query(uid).findOne({\n select: ['id'],\n where: whereClause,\n });\n\n if (existingEntry) {\n let errorMsg = `A ${publishedStateDescription} entry with documentId \"${data.documentId}\"`;\n if (isLocalized && data.locale) {\n errorMsg += ` and locale \"${data.locale}\"`;\n }\n errorMsg += ` already exists for UID \"${uid}\". This combination must be unique.`;\n throw new errors.ApplicationError(errorMsg);\n }\n }\n\n const validData = await entityValidator.validateEntityCreation(contentType, data, {\n // Note: publishedAt value will always be set when DP is disabled\n isDraft: !params?.data?.publishedAt,\n locale: params?.locale,\n strictRelations: isStrictRelationsEnabled(),\n });\n\n // Component handling\n const componentData = await components.createComponents(uid, validData);\n const dataWithComponents = components.assignComponentData(\n contentType,\n componentData,\n validData\n );\n\n const entryData = applyTransforms(contentType, dataWithComponents);\n\n const doc = await strapi.db.query(uid).create({ ...query, data: entryData });\n\n return doc;\n }\n\n async function deleteEntry(id: number, query = {} as any) {\n const componentsToDelete = await components.getComponents(uid, { id });\n\n const deletedEntry = await strapi.db.query(uid).delete({ ...query, where: { id } });\n\n await components.deleteComponents(uid, componentsToDelete as any, { loadComponents: false });\n\n return deletedEntry;\n }\n\n async function updateEntry(entryToUpdate: any, params = {} as any) {\n const { data, ...restParams } = await transformParamsDocumentId(uid, params);\n const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate\n\n const validData = await entityValidator.validateEntityUpdate(\n contentType,\n data,\n {\n isDraft: !params?.data?.publishedAt, // Always update the draft version\n locale: params?.locale,\n strictRelations: isStrictRelationsEnabled(),\n },\n entryToUpdate\n );\n // Component handling\n const componentData = await components.updateComponents(uid, entryToUpdate, validData as any);\n const dataWithComponents = components.assignComponentData(\n contentType,\n componentData,\n validData\n );\n\n const entryData = applyTransforms(contentType, dataWithComponents);\n\n return strapi.db\n .query(uid)\n .update({ ...query, where: { id: entryToUpdate.id }, data: entryData });\n }\n\n async function publishEntry(entry: any, params = {} as any) {\n clearTransformDataRequestCache();\n\n return async.pipe(\n omit('id'),\n assoc('publishedAt', new Date()),\n (draft) => {\n const opts = {\n uid,\n locale: draft.locale,\n status: 'published',\n allowMissingId: true,\n useRequestCache: false,\n };\n return transformData(draft, opts);\n },\n // Create the published entry\n (draft) => createEntry({ ...params, data: draft, locale: draft.locale, status: 'published' })\n )(entry);\n }\n\n async function discardDraftEntry(entry: any, params = {} as any) {\n clearTransformDataRequestCache();\n\n return async.pipe(\n omit('id'),\n assoc('publishedAt', null),\n (entry) => {\n const opts = {\n uid,\n locale: entry.locale,\n status: 'draft',\n allowMissingId: true,\n useRequestCache: false,\n };\n return transformData(entry, opts);\n },\n // Create the draft entry\n (data) => createEntry({ ...params, locale: data.locale, data, status: 'draft' })\n )(entry);\n }\n\n return {\n create: createEntry,\n delete: deleteEntry,\n update: updateEntry,\n publish: publishEntry,\n discardDraft: discardDraftEntry,\n };\n};\n\nexport { createEntriesService };\n"],"names":["isStrictRelationsEnabled","rawStrictRelations","strapi","config","get","undefined","errors","ValidationError","createEntriesService","uid","entityValidator","contentType","createEntry","params","data","restParams","transformParamsDocumentId","query","transformParamsToQuery","pickSelectionParams","Error","documentId","i18nService","plugin","service","isLocalized","isLocalizedContentType","hasDraftAndPublish","options","draftAndPublish","whereClause","locale","publishedStateDescription","publishedAt","$notNull","$null","existingEntry","db","findOne","select","where","errorMsg","ApplicationError","validData","validateEntityCreation","isDraft","strictRelations","componentData","components","dataWithComponents","entryData","applyTransforms","doc","create","deleteEntry","id","componentsToDelete","deletedEntry","delete","loadComponents","updateEntry","entryToUpdate","validateEntityUpdate","update","publishEntry","entry","clearTransformDataRequestCache","async","pipe","omit","assoc","Date","draft","opts","status","allowMissingId","useRequestCache","transformData","discardDraftEntry","publish","discardDraft"],"mappings":"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"}
@@ -7,6 +7,18 @@ import { pickSelectionParams } from './params.mjs';
7
7
  import { applyTransforms } from './attributes/index.mjs';
8
8
  import { clearTransformDataRequestCache, transformData } from './transform/data.mjs';
9
9
 
10
+ /**
11
+ * Reads and validates the `api.documents.strictRelations` flag.
12
+ * false/undefined => legacy behaviour (relational required constraints not enforced),
13
+ * true => enforce required media and relations on non-draft writes.
14
+ * Mirrors the validation of `api.documents.strictParams` (see repository.ts).
15
+ */ const isStrictRelationsEnabled = ()=>{
16
+ const rawStrictRelations = strapi.config.get('api.documents.strictRelations', undefined);
17
+ if (rawStrictRelations !== undefined && rawStrictRelations !== false && rawStrictRelations !== true) {
18
+ throw new errors.ValidationError(`Invalid config.api.documents.strictRelations value: "${rawStrictRelations}". Expected boolean (true or false).`);
19
+ }
20
+ return rawStrictRelations === true;
21
+ };
10
22
  const createEntriesService = (uid, entityValidator)=>{
11
23
  const contentType = strapi.contentType(uid);
12
24
  async function createEntry(params = {}) {
@@ -61,7 +73,8 @@ const createEntriesService = (uid, entityValidator)=>{
61
73
  const validData = await entityValidator.validateEntityCreation(contentType, data, {
62
74
  // Note: publishedAt value will always be set when DP is disabled
63
75
  isDraft: !params?.data?.publishedAt,
64
- locale: params?.locale
76
+ locale: params?.locale,
77
+ strictRelations: isStrictRelationsEnabled()
65
78
  });
66
79
  // Component handling
67
80
  const componentData = await createComponents(uid, validData);
@@ -93,7 +106,8 @@ const createEntriesService = (uid, entityValidator)=>{
93
106
  const query = transformParamsToQuery(uid, pickSelectionParams(restParams)); // select / populate
94
107
  const validData = await entityValidator.validateEntityUpdate(contentType, data, {
95
108
  isDraft: !params?.data?.publishedAt,
96
- locale: params?.locale
109
+ locale: params?.locale,
110
+ strictRelations: isStrictRelationsEnabled()
97
111
  }, entryToUpdate);
98
112
  // Component handling
99
113
  const componentData = await updateComponents(uid, entryToUpdate, validData);
@@ -1 +1 @@
1
- {"version":3,"file":"entries.mjs","sources":["../../../src/services/document-service/entries.ts"],"sourcesContent":["import type { UID, Modules } from '@strapi/types';\nimport { async, errors } from '@strapi/utils';\nimport { assoc, omit } from 'lodash/fp';\n\nimport * as components from './components';\n\nimport { transformParamsDocumentId } from './transform/id-transform';\nimport { transformParamsToQuery } from './transform/query';\nimport { pickSelectionParams } from './params';\nimport { applyTransforms } from './attributes';\nimport { clearTransformDataRequestCache, transformData } from './transform/data';\n\nconst createEntriesService = (\n uid: UID.ContentType,\n entityValidator: Modules.EntityValidator.EntityValidator\n) => {\n const contentType = strapi.contentType(uid);\n\n async function createEntry(params = {} as any) {\n const { data, ...restParams } = await transformParamsDocumentId(uid, params);\n const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate\n\n // Validation\n if (!data) {\n throw new Error('Create requires data attribute');\n }\n\n // Check for uniqueness based on documentId and locale (if localized)\n if (data.documentId) {\n const i18nService = strapi.plugin('i18n')?.service('content-types');\n const isLocalized = i18nService?.isLocalizedContentType(contentType) ?? false;\n const hasDraftAndPublish = contentType.options?.draftAndPublish === true;\n\n const whereClause: Record<string, unknown> = { documentId: data.documentId };\n\n if (isLocalized) {\n whereClause.locale = data.locale;\n }\n\n let publishedStateDescription = '';\n\n if (hasDraftAndPublish) {\n if (data.publishedAt) {\n // Current entry is published, check for existing published entry\n whereClause.publishedAt = { $notNull: true };\n publishedStateDescription = 'published';\n } else {\n // Current entry is a draft, check for existing draft entry\n whereClause.publishedAt = { $null: true };\n publishedStateDescription = 'draft';\n }\n }\n\n const existingEntry = await strapi.db.query(uid).findOne({\n select: ['id'],\n where: whereClause,\n });\n\n if (existingEntry) {\n let errorMsg = `A ${publishedStateDescription} entry with documentId \"${data.documentId}\"`;\n if (isLocalized && data.locale) {\n errorMsg += ` and locale \"${data.locale}\"`;\n }\n errorMsg += ` already exists for UID \"${uid}\". This combination must be unique.`;\n throw new errors.ApplicationError(errorMsg);\n }\n }\n\n const validData = await entityValidator.validateEntityCreation(contentType, data, {\n // Note: publishedAt value will always be set when DP is disabled\n isDraft: !params?.data?.publishedAt,\n locale: params?.locale,\n });\n\n // Component handling\n const componentData = await components.createComponents(uid, validData);\n const dataWithComponents = components.assignComponentData(\n contentType,\n componentData,\n validData\n );\n\n const entryData = applyTransforms(contentType, dataWithComponents);\n\n const doc = await strapi.db.query(uid).create({ ...query, data: entryData });\n\n return doc;\n }\n\n async function deleteEntry(id: number, query = {} as any) {\n const componentsToDelete = await components.getComponents(uid, { id });\n\n const deletedEntry = await strapi.db.query(uid).delete({ ...query, where: { id } });\n\n await components.deleteComponents(uid, componentsToDelete as any, { loadComponents: false });\n\n return deletedEntry;\n }\n\n async function updateEntry(entryToUpdate: any, params = {} as any) {\n const { data, ...restParams } = await transformParamsDocumentId(uid, params);\n const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate\n\n const validData = await entityValidator.validateEntityUpdate(\n contentType,\n data,\n {\n isDraft: !params?.data?.publishedAt, // Always update the draft version\n locale: params?.locale,\n },\n entryToUpdate\n );\n // Component handling\n const componentData = await components.updateComponents(uid, entryToUpdate, validData as any);\n const dataWithComponents = components.assignComponentData(\n contentType,\n componentData,\n validData\n );\n\n const entryData = applyTransforms(contentType, dataWithComponents);\n\n return strapi.db\n .query(uid)\n .update({ ...query, where: { id: entryToUpdate.id }, data: entryData });\n }\n\n async function publishEntry(entry: any, params = {} as any) {\n clearTransformDataRequestCache();\n\n return async.pipe(\n omit('id'),\n assoc('publishedAt', new Date()),\n (draft) => {\n const opts = {\n uid,\n locale: draft.locale,\n status: 'published',\n allowMissingId: true,\n useRequestCache: false,\n };\n return transformData(draft, opts);\n },\n // Create the published entry\n (draft) => createEntry({ ...params, data: draft, locale: draft.locale, status: 'published' })\n )(entry);\n }\n\n async function discardDraftEntry(entry: any, params = {} as any) {\n clearTransformDataRequestCache();\n\n return async.pipe(\n omit('id'),\n assoc('publishedAt', null),\n (entry) => {\n const opts = {\n uid,\n locale: entry.locale,\n status: 'draft',\n allowMissingId: true,\n useRequestCache: false,\n };\n return transformData(entry, opts);\n },\n // Create the draft entry\n (data) => createEntry({ ...params, locale: data.locale, data, status: 'draft' })\n )(entry);\n }\n\n return {\n create: createEntry,\n delete: deleteEntry,\n update: updateEntry,\n publish: publishEntry,\n discardDraft: discardDraftEntry,\n };\n};\n\nexport { createEntriesService };\n"],"names":["createEntriesService","uid","entityValidator","contentType","strapi","createEntry","params","data","restParams","transformParamsDocumentId","query","transformParamsToQuery","pickSelectionParams","Error","documentId","i18nService","plugin","service","isLocalized","isLocalizedContentType","hasDraftAndPublish","options","draftAndPublish","whereClause","locale","publishedStateDescription","publishedAt","$notNull","$null","existingEntry","db","findOne","select","where","errorMsg","errors","ApplicationError","validData","validateEntityCreation","isDraft","componentData","components","dataWithComponents","entryData","applyTransforms","doc","create","deleteEntry","id","componentsToDelete","deletedEntry","delete","loadComponents","updateEntry","entryToUpdate","validateEntityUpdate","update","publishEntry","entry","clearTransformDataRequestCache","async","pipe","omit","assoc","Date","draft","opts","status","allowMissingId","useRequestCache","transformData","discardDraftEntry","publish","discardDraft"],"mappings":"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"}
1
+ {"version":3,"file":"entries.mjs","sources":["../../../src/services/document-service/entries.ts"],"sourcesContent":["import type { UID, Modules } from '@strapi/types';\nimport { async, errors } from '@strapi/utils';\nimport { assoc, omit } from 'lodash/fp';\n\nimport * as components from './components';\n\nimport { transformParamsDocumentId } from './transform/id-transform';\nimport { transformParamsToQuery } from './transform/query';\nimport { pickSelectionParams } from './params';\nimport { applyTransforms } from './attributes';\nimport { clearTransformDataRequestCache, transformData } from './transform/data';\n\n/**\n * Reads and validates the `api.documents.strictRelations` flag.\n * false/undefined => legacy behaviour (relational required constraints not enforced),\n * true => enforce required media and relations on non-draft writes.\n * Mirrors the validation of `api.documents.strictParams` (see repository.ts).\n */\nconst isStrictRelationsEnabled = (): boolean => {\n const rawStrictRelations: unknown = strapi.config.get('api.documents.strictRelations', undefined);\n\n if (\n rawStrictRelations !== undefined &&\n rawStrictRelations !== false &&\n rawStrictRelations !== true\n ) {\n throw new errors.ValidationError(\n `Invalid config.api.documents.strictRelations value: \"${rawStrictRelations}\". Expected boolean (true or false).`\n );\n }\n\n return rawStrictRelations === true;\n};\n\nconst createEntriesService = (\n uid: UID.ContentType,\n entityValidator: Modules.EntityValidator.EntityValidator\n) => {\n const contentType = strapi.contentType(uid);\n\n async function createEntry(params = {} as any) {\n const { data, ...restParams } = await transformParamsDocumentId(uid, params);\n const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate\n\n // Validation\n if (!data) {\n throw new Error('Create requires data attribute');\n }\n\n // Check for uniqueness based on documentId and locale (if localized)\n if (data.documentId) {\n const i18nService = strapi.plugin('i18n')?.service('content-types');\n const isLocalized = i18nService?.isLocalizedContentType(contentType) ?? false;\n const hasDraftAndPublish = contentType.options?.draftAndPublish === true;\n\n const whereClause: Record<string, unknown> = { documentId: data.documentId };\n\n if (isLocalized) {\n whereClause.locale = data.locale;\n }\n\n let publishedStateDescription = '';\n\n if (hasDraftAndPublish) {\n if (data.publishedAt) {\n // Current entry is published, check for existing published entry\n whereClause.publishedAt = { $notNull: true };\n publishedStateDescription = 'published';\n } else {\n // Current entry is a draft, check for existing draft entry\n whereClause.publishedAt = { $null: true };\n publishedStateDescription = 'draft';\n }\n }\n\n const existingEntry = await strapi.db.query(uid).findOne({\n select: ['id'],\n where: whereClause,\n });\n\n if (existingEntry) {\n let errorMsg = `A ${publishedStateDescription} entry with documentId \"${data.documentId}\"`;\n if (isLocalized && data.locale) {\n errorMsg += ` and locale \"${data.locale}\"`;\n }\n errorMsg += ` already exists for UID \"${uid}\". This combination must be unique.`;\n throw new errors.ApplicationError(errorMsg);\n }\n }\n\n const validData = await entityValidator.validateEntityCreation(contentType, data, {\n // Note: publishedAt value will always be set when DP is disabled\n isDraft: !params?.data?.publishedAt,\n locale: params?.locale,\n strictRelations: isStrictRelationsEnabled(),\n });\n\n // Component handling\n const componentData = await components.createComponents(uid, validData);\n const dataWithComponents = components.assignComponentData(\n contentType,\n componentData,\n validData\n );\n\n const entryData = applyTransforms(contentType, dataWithComponents);\n\n const doc = await strapi.db.query(uid).create({ ...query, data: entryData });\n\n return doc;\n }\n\n async function deleteEntry(id: number, query = {} as any) {\n const componentsToDelete = await components.getComponents(uid, { id });\n\n const deletedEntry = await strapi.db.query(uid).delete({ ...query, where: { id } });\n\n await components.deleteComponents(uid, componentsToDelete as any, { loadComponents: false });\n\n return deletedEntry;\n }\n\n async function updateEntry(entryToUpdate: any, params = {} as any) {\n const { data, ...restParams } = await transformParamsDocumentId(uid, params);\n const query = transformParamsToQuery(uid, pickSelectionParams(restParams) as any); // select / populate\n\n const validData = await entityValidator.validateEntityUpdate(\n contentType,\n data,\n {\n isDraft: !params?.data?.publishedAt, // Always update the draft version\n locale: params?.locale,\n strictRelations: isStrictRelationsEnabled(),\n },\n entryToUpdate\n );\n // Component handling\n const componentData = await components.updateComponents(uid, entryToUpdate, validData as any);\n const dataWithComponents = components.assignComponentData(\n contentType,\n componentData,\n validData\n );\n\n const entryData = applyTransforms(contentType, dataWithComponents);\n\n return strapi.db\n .query(uid)\n .update({ ...query, where: { id: entryToUpdate.id }, data: entryData });\n }\n\n async function publishEntry(entry: any, params = {} as any) {\n clearTransformDataRequestCache();\n\n return async.pipe(\n omit('id'),\n assoc('publishedAt', new Date()),\n (draft) => {\n const opts = {\n uid,\n locale: draft.locale,\n status: 'published',\n allowMissingId: true,\n useRequestCache: false,\n };\n return transformData(draft, opts);\n },\n // Create the published entry\n (draft) => createEntry({ ...params, data: draft, locale: draft.locale, status: 'published' })\n )(entry);\n }\n\n async function discardDraftEntry(entry: any, params = {} as any) {\n clearTransformDataRequestCache();\n\n return async.pipe(\n omit('id'),\n assoc('publishedAt', null),\n (entry) => {\n const opts = {\n uid,\n locale: entry.locale,\n status: 'draft',\n allowMissingId: true,\n useRequestCache: false,\n };\n return transformData(entry, opts);\n },\n // Create the draft entry\n (data) => createEntry({ ...params, locale: data.locale, data, status: 'draft' })\n )(entry);\n }\n\n return {\n create: createEntry,\n delete: deleteEntry,\n update: updateEntry,\n publish: publishEntry,\n discardDraft: discardDraftEntry,\n };\n};\n\nexport { createEntriesService };\n"],"names":["isStrictRelationsEnabled","rawStrictRelations","strapi","config","get","undefined","errors","ValidationError","createEntriesService","uid","entityValidator","contentType","createEntry","params","data","restParams","transformParamsDocumentId","query","transformParamsToQuery","pickSelectionParams","Error","documentId","i18nService","plugin","service","isLocalized","isLocalizedContentType","hasDraftAndPublish","options","draftAndPublish","whereClause","locale","publishedStateDescription","publishedAt","$notNull","$null","existingEntry","db","findOne","select","where","errorMsg","ApplicationError","validData","validateEntityCreation","isDraft","strictRelations","componentData","components","dataWithComponents","entryData","applyTransforms","doc","create","deleteEntry","id","componentsToDelete","deletedEntry","delete","loadComponents","updateEntry","entryToUpdate","validateEntityUpdate","update","publishEntry","entry","clearTransformDataRequestCache","async","pipe","omit","assoc","Date","draft","opts","status","allowMissingId","useRequestCache","transformData","discardDraftEntry","publish","discardDraft"],"mappings":"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"}
@@ -16,6 +16,7 @@ export type ComponentContext = {
16
16
  interface ValidatorContext {
17
17
  isDraft?: boolean;
18
18
  locale?: string | null;
19
+ strictRelations?: boolean;
19
20
  }
20
21
  declare const entityValidator: Modules.EntityValidator.EntityValidator;
21
22
  export default entityValidator;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/entity-validator/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAO,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAalE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE;QAEb,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;QAErB,EAAE,CAAC,EAAE,MAAM,CAAC;QAGZ,OAAO,CAAC,EAAE,gBAAgB,CAAC;KAC5B,CAAC;IAEF,eAAe,EAAE,MAAM,EAAE,CAAC;IAG1B,cAAc,EAAE,OAAO,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;IACjD,sBAAsB,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;CAC/E,CAAC;AAWF,UAAU,gBAAgB;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAoiBD,QAAA,MAAM,eAAe,EAAE,OAAO,CAAC,eAAe,CAAC,eAG9C,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/entity-validator/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAO,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAclE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE;QAEb,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;QAErB,EAAE,CAAC,EAAE,MAAM,CAAC;QAGZ,OAAO,CAAC,EAAE,gBAAgB,CAAC;KAC5B,CAAC;IAEF,eAAe,EAAE,MAAM,EAAE,CAAC;IAG1B,cAAc,EAAE,OAAO,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;IACjD,sBAAsB,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;CAC/E,CAAC;AAWF,UAAU,gBAAgB;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA0tBD,QAAA,MAAM,eAAe,EAAE,OAAO,CAAC,eAAe,CAAC,eAG9C,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -13,6 +13,7 @@ var strapiUtils__default = /*#__PURE__*/_interopDefault(strapiUtils);
13
13
 
14
14
  const { yup, validateYupSchema } = strapiUtils__default.default;
15
15
  const { isMediaAttribute, isScalarAttribute, getWritableAttributes } = strapiUtils__default.default.contentTypes;
16
+ const { isAnyToOne } = strapiUtils__default.default.relations;
16
17
  const { ValidationError } = strapiUtils__default.default.errors;
17
18
  const isInteger = (value)=>Number.isInteger(value);
18
19
  const addMinMax = (validator, { attr, updatedAttribute })=>{
@@ -56,7 +57,7 @@ const addDefault = (createOrUpdate)=>{
56
57
  };
57
58
  };
58
59
  const preventCast = (validator)=>validator.transform((val, originalVal)=>originalVal);
59
- const createComponentValidator = (createOrUpdate)=>({ attr, updatedAttribute, componentContext }, { isDraft })=>{
60
+ const createComponentValidator = (createOrUpdate)=>({ attr, updatedAttribute, componentContext }, options)=>{
60
61
  const model = strapi.getModel(attr.component);
61
62
  if (!model) {
62
63
  throw new Error('Validation failed: Model not found');
@@ -67,16 +68,14 @@ const createComponentValidator = (createOrUpdate)=>({ attr, updatedAttribute, co
67
68
  componentContext,
68
69
  model,
69
70
  data: item
70
- }, {
71
- isDraft
72
- }).notNull()));
71
+ }, options).notNull()));
73
72
  validator = addRequiredValidation(createOrUpdate)(validator, {
74
73
  attr: {
75
74
  required: true
76
75
  },
77
76
  updatedAttribute
78
77
  });
79
- if (!isDraft) {
78
+ if (!options.isDraft) {
80
79
  validator = addMinMax(validator, {
81
80
  attr,
82
81
  updatedAttribute
@@ -88,18 +87,16 @@ const createComponentValidator = (createOrUpdate)=>({ attr, updatedAttribute, co
88
87
  model,
89
88
  data: updatedAttribute.value,
90
89
  componentContext
91
- }, {
92
- isDraft
93
- });
90
+ }, options);
94
91
  validator = addRequiredValidation(createOrUpdate)(validator, {
95
92
  attr: {
96
- required: !isDraft && attr.required
93
+ required: !options.isDraft && attr.required
97
94
  },
98
95
  updatedAttribute
99
96
  });
100
97
  return validator;
101
98
  };
102
- const createDzValidator = (createOrUpdate)=>({ attr, updatedAttribute, componentContext }, { isDraft })=>{
99
+ const createDzValidator = (createOrUpdate)=>({ attr, updatedAttribute, componentContext }, options)=>{
103
100
  let validator;
104
101
  validator = yup.array().of(yup.lazy((item)=>{
105
102
  const model = strapi.getModel(fp.prop('__component', item));
@@ -110,9 +107,7 @@ const createDzValidator = (createOrUpdate)=>({ attr, updatedAttribute, component
110
107
  model,
111
108
  data: item,
112
109
  componentContext
113
- }, {
114
- isDraft
115
- })) : schema;
110
+ }, options)) : schema;
116
111
  }));
117
112
  validator = addRequiredValidation(createOrUpdate)(validator, {
118
113
  attr: {
@@ -120,7 +115,7 @@ const createDzValidator = (createOrUpdate)=>({ attr, updatedAttribute, component
120
115
  },
121
116
  updatedAttribute
122
117
  });
123
- if (!isDraft) {
118
+ if (!options.isDraft) {
124
119
  validator = addMinMax(validator, {
125
120
  attr,
126
121
  updatedAttribute
@@ -128,15 +123,143 @@ const createDzValidator = (createOrUpdate)=>({ attr, updatedAttribute, component
128
123
  }
129
124
  return validator;
130
125
  };
131
- const createRelationValidator = ({ updatedAttribute })=>{
132
- let validator;
133
- if (Array.isArray(updatedAttribute.value)) {
134
- validator = yup.array().of(yup.mixed());
135
- } else {
136
- validator = yup.mixed();
126
+ /**
127
+ * A relation/media value can be supplied in several shapes: a bare id, an array of ids,
128
+ * or a connect/set object (`{ connect: [...] }` / `{ set: [...] }`). This normalises all
129
+ * of them to "does this attach at least one entry?" so required validation is consistent
130
+ * regardless of the input shape (mirrors `buildRelationsStore`'s source extraction).
131
+ */ const hasRelationValue = (value)=>{
132
+ if (_.isNil(value)) {
133
+ return false;
134
+ }
135
+ if (Array.isArray(value)) {
136
+ return value.length > 0;
137
+ }
138
+ if (fp.isObject(value)) {
139
+ const connect = value.connect;
140
+ const set = value.set;
141
+ if (!_.isNil(connect)) {
142
+ return Array.isArray(connect) ? connect.length > 0 : true;
143
+ }
144
+ if (!_.isNil(set)) {
145
+ return Array.isArray(set) ? set.length > 0 : true;
146
+ }
147
+ // Any other object shape (e.g. a bare `{ id }`) counts as a value.
148
+ return true;
149
+ }
150
+ return true;
151
+ };
152
+ /**
153
+ * A disconnect-only update payload removes entries without adding or replacing any:
154
+ * actual removals in `disconnect`, no `set`, and no effective additions in `connect`.
155
+ * `connect` counts as "no additions" when absent OR an empty array — the Content Manager
156
+ * initializes every relation as `{ connect: [], disconnect: [] }` and keeps `connect: []`
157
+ * in place when the user only removes entries, so `{ connect: [], disconnect: [id] }`
158
+ * is the shape real admin saves produce. A payload with empty `disconnect` (nothing
159
+ * removed) is NOT disconnect-only — it's the CM's untouched-relation no-op.
160
+ */ const isDisconnectOnly = (value)=>{
161
+ if (!fp.isObject(value)) {
162
+ return false;
163
+ }
164
+ const { connect, set, disconnect } = value;
165
+ if (!_.isNil(set)) {
166
+ return false;
167
+ }
168
+ const hasAdditions = !_.isNil(connect) && (!Array.isArray(connect) || connect.length > 0);
169
+ if (hasAdditions) {
170
+ return false;
171
+ }
172
+ return !_.isNil(disconnect) && (!Array.isArray(disconnect) || disconnect.length > 0);
173
+ };
174
+ /**
175
+ * Decides whether a required media/relation value should be rejected as empty,
176
+ * mirroring the scalar `required` semantics:
177
+ *
178
+ * - creation (`notNil` analogue): an absent key is a failure — the field must be
179
+ * populated, so anything without a value (`undefined`/`null`/`[]`/empty `set`/`connect`)
180
+ * is rejected.
181
+ * - update (`notNull` analogue): an absent key keeps the existing value, so it passes.
182
+ * Only reject when the request is present AND leaves the field empty. A bare
183
+ * `{ connect: [] }` (no `set`) neither adds nor removes anything → treated as a no-op
184
+ * that passes; `{ set: [] }` / `null` / `[]` explicitly empty the field → rejected.
185
+ *
186
+ * `isToOne` marks a to-one relation / single media, which can hold at most one entry.
187
+ * For those, a disconnect-only update (`{ disconnect: [...] }` with no `connect`/`set`)
188
+ * always leaves the field empty — no DB read needed — so it is rejected on non-draft
189
+ * updates. To-many fields can still hold other entries after a disconnect, so their
190
+ * resulting state can't be known without a DB read and they fall through to a pass
191
+ * (publish re-validates the populated draft for D&P types; documented limitation for
192
+ * non-D&P, tied to the null→[] follow-up).
193
+ *
194
+ * On creation there are no existing rows, so a disconnect-only payload attaches nothing
195
+ * and always leaves the field empty — for both to-one and to-many — so it is rejected
196
+ * regardless of `isToOne`.
197
+ */ const relationRequiredFails = (createOrUpdate, value, isToOne)=>{
198
+ if (createOrUpdate === 'creation') {
199
+ // No existing rows on create, so a disconnect attaches nothing and the field stays
200
+ // empty for both to-one and to-many — reject it before the generic value check, which
201
+ // would otherwise treat the bare `{ disconnect: [...] }` object as a present value.
202
+ if (isDisconnectOnly(value)) {
203
+ return true;
204
+ }
205
+ }
206
+ if (createOrUpdate === 'update') {
207
+ // Absent key on update = keep the existing value.
208
+ if (value === undefined) {
209
+ return false;
210
+ }
211
+ if (isDisconnectOnly(value)) {
212
+ // A disconnect on a to-one/single field deterministically empties it; on a to-many
213
+ // field the resulting state depends on the current DB rows, so we can't reject here.
214
+ return isToOne;
215
+ }
216
+ // A connect-only empty is a no-op, not an emptying operation.
217
+ if (fp.isObject(value) && !_.isNil(value.connect)) {
218
+ const connect = value.connect;
219
+ const hasSet = !_.isNil(value.set);
220
+ if (!hasSet && Array.isArray(connect) && connect.length === 0) {
221
+ return false;
222
+ }
223
+ }
137
224
  }
138
- return validator;
225
+ return !hasRelationValue(value);
139
226
  };
227
+ const createMediaAttributeValidator = (createOrUpdate)=>({ attr, updatedAttribute }, options)=>{
228
+ // Media values arrive in several shapes (a bare id, an array of ids, or a
229
+ // `{ connect }` / `{ set }` object on update). `yup.mixed()` accepts all of them;
230
+ // coercing `multiple` media to `yup.array()` would reject the connect/set object
231
+ // syntax that update requests legitimately send. Required-ness is asserted below
232
+ // via `relationRequiredFails`, which understands every shape.
233
+ let validator = yup.mixed();
234
+ // Relational required constraints are only enforced under the strictRelations flag,
235
+ // and never for drafts (mirrors scalar `required` handling via `!isDraft`).
236
+ if (options.strictRelations && !options.isDraft && attr.required) {
237
+ // A media value can arrive as an id, an array, or a connect/set object, so a simple
238
+ // `.notNil()`/`.min()` is not enough — normalise the shape before asserting.
239
+ // Single media holds at most one file, so a disconnect-only update always empties it.
240
+ const isToOne = !attr.multiple;
241
+ validator = validator.test('required-media', `${updatedAttribute.name} must be defined.`, ()=>!relationRequiredFails(createOrUpdate, updatedAttribute.value, isToOne));
242
+ }
243
+ return validator;
244
+ };
245
+ const createRelationValidator = (createOrUpdate)=>({ attr, updatedAttribute }, options)=>{
246
+ let validator;
247
+ if (Array.isArray(updatedAttribute.value)) {
248
+ validator = yup.array().of(yup.mixed());
249
+ } else {
250
+ validator = yup.mixed();
251
+ }
252
+ // Relational required constraints are only enforced under the strictRelations flag,
253
+ // and never for drafts (mirrors scalar `required` handling via `!isDraft`).
254
+ if (options.strictRelations && !options.isDraft && attr.required) {
255
+ // A relation value can arrive as an id, an array, or a connect/set object, so a
256
+ // simple `.notNil()`/`.min()` is not enough — normalise the shape before asserting.
257
+ // A to-one relation holds at most one target, so a disconnect-only update always empties it.
258
+ const isToOne = isAnyToOne(attr);
259
+ validator = validator.test('required-relation', `${updatedAttribute.name} must be defined.`, ()=>!relationRequiredFails(createOrUpdate, updatedAttribute.value, isToOne));
260
+ }
261
+ return validator;
262
+ };
140
263
  const createScalarAttributeValidator = (createOrUpdate)=>(metas, options)=>{
141
264
  let validator;
142
265
  if (fp.has(metas.attr.type, validators.Validators)) {
@@ -163,7 +286,10 @@ const createAttributeValidator = (createOrUpdate)=>(metas, options)=>{
163
286
  }
164
287
  }
165
288
  if (isMediaAttribute(metas.attr)) {
166
- validator = yup.mixed();
289
+ validator = createMediaAttributeValidator(createOrUpdate)({
290
+ attr: metas.attr,
291
+ updatedAttribute: metas.updatedAttribute
292
+ }, options);
167
293
  } else if (isScalarAttribute(metas.attr)) {
168
294
  validator = createScalarAttributeValidator(createOrUpdate)(metas, options);
169
295
  } else {
@@ -204,10 +330,10 @@ const createAttributeValidator = (createOrUpdate)=>(metas, options)=>{
204
330
  });
205
331
  validator = createDzValidator(createOrUpdate)(metas, options);
206
332
  } else if (metas.attr.type === 'relation') {
207
- validator = createRelationValidator({
333
+ validator = createRelationValidator(createOrUpdate)({
208
334
  attr: metas.attr,
209
335
  updatedAttribute: metas.updatedAttribute
210
- });
336
+ }, options);
211
337
  }
212
338
  validator = preventCast(validator);
213
339
  }
@@ -258,7 +384,8 @@ const createValidateEntity = (createOrUpdate)=>{
258
384
  }
259
385
  }, {
260
386
  isDraft: options?.isDraft ?? false,
261
- locale: options?.locale ?? null
387
+ locale: options?.locale ?? null,
388
+ strictRelations: options?.strictRelations ?? false
262
389
  }).test('relations-test', 'check that all relations exist', async function relationsValidation(data) {
263
390
  try {
264
391
  await checkRelationsExist(buildRelationsStore({