@strapi/core 5.58.0 → 5.59.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.
- package/dist/Strapi.d.ts.map +1 -1
- package/dist/Strapi.js +1 -0
- package/dist/Strapi.js.map +1 -1
- package/dist/Strapi.mjs +1 -0
- package/dist/Strapi.mjs.map +1 -1
- package/dist/ee/entitlements.d.ts.map +1 -1
- package/dist/ee/entitlements.js.map +1 -1
- package/dist/ee/entitlements.mjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/loaders/apis.d.ts.map +1 -1
- package/dist/loaders/apis.js.map +1 -1
- package/dist/loaders/apis.mjs.map +1 -1
- package/dist/loaders/plugins/get-enabled-plugins.js +2 -2
- package/dist/loaders/plugins/get-enabled-plugins.js.map +1 -1
- package/dist/loaders/plugins/get-enabled-plugins.mjs +2 -2
- package/dist/loaders/plugins/get-enabled-plugins.mjs.map +1 -1
- package/dist/middlewares/cors.d.ts.map +1 -1
- package/dist/middlewares/cors.js.map +1 -1
- package/dist/middlewares/cors.mjs.map +1 -1
- package/dist/migrations/database/5.0.0-discard-drafts.js +1 -1
- package/dist/migrations/database/5.0.0-discard-drafts.js.map +1 -1
- package/dist/migrations/database/5.0.0-discard-drafts.mjs +1 -1
- package/dist/migrations/database/5.0.0-discard-drafts.mjs.map +1 -1
- package/dist/migrations/first-published-at.js +1 -1
- package/dist/migrations/first-published-at.js.map +1 -1
- package/dist/migrations/first-published-at.mjs +1 -1
- package/dist/migrations/first-published-at.mjs.map +1 -1
- package/dist/package.json.js +15 -14
- package/dist/package.json.js.map +1 -1
- package/dist/package.json.mjs +15 -14
- package/dist/package.json.mjs.map +1 -1
- package/dist/registries/controllers.d.ts.map +1 -1
- package/dist/registries/controllers.js.map +1 -1
- package/dist/registries/controllers.mjs.map +1 -1
- package/dist/services/content-source-maps.js +6 -6
- package/dist/services/content-source-maps.js.map +1 -1
- package/dist/services/content-source-maps.mjs +6 -6
- package/dist/services/content-source-maps.mjs.map +1 -1
- package/dist/services/document-service/components.d.ts +13 -11
- package/dist/services/document-service/components.d.ts.map +1 -1
- package/dist/services/document-service/components.js +115 -66
- package/dist/services/document-service/components.js.map +1 -1
- package/dist/services/document-service/components.mjs +116 -67
- package/dist/services/document-service/components.mjs.map +1 -1
- package/dist/services/document-service/events.d.ts.map +1 -1
- package/dist/services/document-service/events.js +5 -1
- package/dist/services/document-service/events.js.map +1 -1
- package/dist/services/document-service/events.mjs +5 -1
- package/dist/services/document-service/events.mjs.map +1 -1
- package/dist/services/document-service/repository.d.ts.map +1 -1
- package/dist/services/document-service/repository.js +26 -7
- package/dist/services/document-service/repository.js.map +1 -1
- package/dist/services/document-service/repository.mjs +27 -8
- package/dist/services/document-service/repository.mjs.map +1 -1
- package/dist/services/document-service/transform/data.d.ts.map +1 -1
- package/dist/services/document-service/transform/data.js.map +1 -1
- package/dist/services/document-service/transform/data.mjs.map +1 -1
- package/dist/services/document-service/transform/relations/transform/default-locale.d.ts +4 -1
- package/dist/services/document-service/transform/relations/transform/default-locale.d.ts.map +1 -1
- package/dist/services/document-service/transform/relations/transform/default-locale.js +12 -9
- package/dist/services/document-service/transform/relations/transform/default-locale.js.map +1 -1
- package/dist/services/document-service/transform/relations/transform/default-locale.mjs +12 -10
- package/dist/services/document-service/transform/relations/transform/default-locale.mjs.map +1 -1
- package/dist/services/document-service/utils/clone-relations.d.ts +21 -4
- package/dist/services/document-service/utils/clone-relations.d.ts.map +1 -1
- package/dist/services/document-service/utils/clone-relations.js +619 -34
- package/dist/services/document-service/utils/clone-relations.js.map +1 -1
- package/dist/services/document-service/utils/clone-relations.mjs +619 -36
- package/dist/services/document-service/utils/clone-relations.mjs.map +1 -1
- package/dist/services/document-service/utils/unidirectional-relations.d.ts +4 -2
- package/dist/services/document-service/utils/unidirectional-relations.d.ts.map +1 -1
- package/dist/services/document-service/utils/unidirectional-relations.js +1 -10
- package/dist/services/document-service/utils/unidirectional-relations.js.map +1 -1
- package/dist/services/document-service/utils/unidirectional-relations.mjs +1 -10
- package/dist/services/document-service/utils/unidirectional-relations.mjs.map +1 -1
- package/dist/services/event-hub.d.ts.map +1 -1
- package/dist/services/event-hub.js +23 -5
- package/dist/services/event-hub.js.map +1 -1
- package/dist/services/event-hub.mjs +23 -5
- package/dist/services/event-hub.mjs.map +1 -1
- package/dist/services/webhook-store.js +2 -2
- package/dist/services/webhook-store.js.map +1 -1
- package/dist/services/webhook-store.mjs +2 -2
- package/dist/services/webhook-store.mjs.map +1 -1
- package/dist/utils/fetch.d.ts.map +1 -1
- package/dist/utils/fetch.js +44 -8
- package/dist/utils/fetch.js.map +1 -1
- package/dist/utils/fetch.mjs +37 -8
- package/dist/utils/fetch.mjs.map +1 -1
- package/dist/utils/transform-content-types-to-models.js +5 -5
- package/dist/utils/transform-content-types-to-models.js.map +1 -1
- package/dist/utils/transform-content-types-to-models.mjs +5 -5
- package/dist/utils/transform-content-types-to-models.mjs.map +1 -1
- package/package.json +15 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clone-relations.mjs","sources":["../../../../src/services/document-service/utils/clone-relations.ts"],"sourcesContent":["import { merge, set } from 'lodash';\n\nimport type { Core, Schema, UID } from '@strapi/types';\nimport type { traverseEntity } from '@strapi/utils';\nimport { traverseEntityRelations } from '../transform/relations/utils/map-relation';\n\nconst RELATION_OPERATIONS = ['connect', 'disconnect', 'set'] as const;\ntype TraversableData = Parameters<typeof traverseEntity>[2];\n\ntype CloneRelationAttribute = Schema.Attribute.Relation & {\n targetAttribute?: string;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n};\n\nconst isRelationAttribute = (attribute: unknown): attribute is CloneRelationAttribute => {\n return (\n isRecord(attribute) && attribute.type === 'relation' && typeof attribute.relation === 'string'\n );\n};\n\nconst isRelationOperationPayload = (value: unknown): value is Record<string, unknown> => {\n return (\n isRecord(value) &&\n RELATION_OPERATIONS.some((operation) => Object.prototype.hasOwnProperty.call(value, operation))\n );\n};\n\nconst hasMeaningfulRelationOperations = (value: Record<string, unknown>) => {\n if (Object.prototype.hasOwnProperty.call(value, 'set')) {\n return true;\n }\n\n return ['connect', 'disconnect'].some((operation) => {\n const operationValue = value[operation];\n return Array.isArray(operationValue) ? operationValue.length > 0 : operationValue != null;\n });\n};\n\nconst canTransformRelationOperationPayload = (attribute: Schema.Attribute.Relation) => {\n return attribute.useJoinTable !== false && attribute.relation !== 'morphToOne';\n};\n\nconst isBidirectionalOneToOne = (attribute: CloneRelationAttribute) => {\n return (\n attribute.relation === 'oneToOne' &&\n [attribute.inversedBy, attribute.mappedBy, attribute.targetAttribute].some(\n (inverseAttribute) => inverseAttribute != null\n )\n );\n};\n\nconst hasPopulatedRelation = (value: unknown) => {\n return isRecord(value) && ('id' in value || 'documentId' in value);\n};\n\nconst collectRelationOperationOverrides = async (\n submittedData: Record<string, unknown>,\n contentType: Schema.ContentType,\n getModel: (uid: string) => Schema.Schema\n) => {\n const overrides = new Map<string, Record<string, unknown>>();\n\n await traverseEntityRelations(\n ({ path, value }) => {\n if (isRelationOperationPayload(value) && hasMeaningfulRelationOperations(value)) {\n overrides.set(path.rawWithIndices!, value);\n }\n },\n { schema: contentType, getModel },\n submittedData as TraversableData\n );\n\n return [...overrides.entries()].sort(([leftPath], [rightPath]) =>\n leftPath.localeCompare(rightPath)\n );\n};\n\nexport const prepareCloneData = async (\n originalData: Record<string, unknown>,\n submittedData: Record<string, unknown> | undefined,\n contentType: Schema.ContentType,\n getModel: (uid: string) => Schema.Schema\n) => {\n const submitted = submittedData ?? {};\n const relationOperationOverrides = await collectRelationOperationOverrides(\n submitted,\n contentType,\n getModel\n );\n const data = merge({}, originalData, submitted) as Record<string, unknown>;\n const relationsToCopy: string[] = [];\n\n for (const [attributeName, attribute] of Object.entries(contentType.attributes)) {\n if (!isRelationAttribute(attribute) || !canTransformRelationOperationPayload(attribute)) {\n continue;\n }\n\n const relationWasSubmitted = Object.prototype.hasOwnProperty.call(submitted, attributeName);\n const submittedValue = submitted[attributeName];\n const isOperationPayload = isRelationOperationPayload(submittedValue);\n const hasMeaningfulOperations =\n isOperationPayload && hasMeaningfulRelationOperations(submittedValue);\n\n const relationIsUnchanged =\n !relationWasSubmitted || (isOperationPayload && !hasMeaningfulOperations);\n\n if (\n relationIsUnchanged &&\n isBidirectionalOneToOne(attribute) &&\n hasPopulatedRelation(originalData[attributeName])\n ) {\n // Normal relation attachment enforces oneToOne ownership by removing the source link.\n // Leave unchanged relations out of create and copy their join row after the clone exists.\n delete data[attributeName];\n relationsToCopy.push(attributeName);\n }\n }\n\n for (const [path, value] of relationOperationOverrides) {\n set(data, path, value);\n }\n\n return { data, relationsToCopy };\n};\n\nexport const copyCloneRelationRows = async (\n strapi: Core.Strapi,\n uid: UID.ContentType,\n sourceEntryId: number,\n targetEntryId: number,\n attributeNames: string[]\n) => {\n if (attributeNames.length === 0) {\n return;\n }\n\n const { attributes } = strapi.db.metadata.get(uid);\n const idColumn = strapi.db.metadata.identifiers.ID_COLUMN;\n const batchSize = strapi.db.dialect.getBatchInsertSize();\n\n await strapi.db.transaction(async ({ trx }) => {\n for (const attributeName of attributeNames) {\n const attribute = attributes[attributeName];\n\n if (attribute?.type !== 'relation' || !('joinTable' in attribute) || !attribute.joinTable) {\n continue;\n }\n\n const { joinTable } = attribute;\n const { joinColumn } = joinTable;\n const rows = (await strapi.db\n .connection(joinTable.name)\n .where({\n [joinColumn.name]: sourceEntryId,\n ...(('on' in joinTable && joinTable.on) || undefined),\n })\n .select('*')\n .transacting(trx)) as Record<string, unknown>[];\n\n const rowsToCopy = rows.map((row) => {\n const copiedRow = { ...row, [joinColumn.name]: targetEntryId };\n delete copiedRow[idColumn];\n return copiedRow;\n });\n\n for (let index = 0; index < rowsToCopy.length; index += batchSize) {\n await strapi.db\n .connection(joinTable.name)\n .insert(rowsToCopy.slice(index, index + batchSize))\n .transacting(trx);\n }\n }\n });\n};\n"],"names":["RELATION_OPERATIONS","isRecord","value","Array","isArray","isRelationAttribute","attribute","type","relation","isRelationOperationPayload","some","operation","Object","prototype","hasOwnProperty","call","hasMeaningfulRelationOperations","operationValue","length","canTransformRelationOperationPayload","useJoinTable","isBidirectionalOneToOne","inversedBy","mappedBy","targetAttribute","inverseAttribute","hasPopulatedRelation","collectRelationOperationOverrides","submittedData","contentType","getModel","overrides","Map","traverseEntityRelations","path","set","rawWithIndices","schema","entries","sort","leftPath","rightPath","localeCompare","prepareCloneData","originalData","submitted","relationOperationOverrides","data","merge","relationsToCopy","attributeName","attributes","relationWasSubmitted","submittedValue","isOperationPayload","hasMeaningfulOperations","relationIsUnchanged","push","copyCloneRelationRows","strapi","uid","sourceEntryId","targetEntryId","attributeNames","db","metadata","get","idColumn","identifiers","ID_COLUMN","batchSize","dialect","getBatchInsertSize","transaction","trx","joinTable","joinColumn","rows","connection","name","where","on","undefined","select","transacting","rowsToCopy","map","row","copiedRow","index","insert","slice"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"clone-relations.mjs","sources":["../../../../src/services/document-service/utils/clone-relations.ts"],"sourcesContent":["import { get, has, merge, set, unset } from 'lodash';\n\nimport type { Core, Schema, UID } from '@strapi/types';\nimport { contentTypes, traverseEntity } from '@strapi/utils';\nimport { traverseEntityRelations } from '../transform/relations/utils/map-relation';\nimport {\n getDefaultLocale,\n getRelationTargetLocale,\n isLocalizedContentType,\n} from '../transform/relations/utils/i18n';\nimport { getRelationTargetStatus } from '../transform/relations/utils/dp';\nimport { setDefaultLocaleToRelation } from '../transform/relations/transform/default-locale';\n\nconst RELATION_OPERATIONS = ['connect', 'disconnect', 'set'] as const;\ntype TraversableData = Parameters<typeof traverseEntity>[2];\ntype TraverseVisitor = Parameters<typeof traverseEntity>[0];\n\ntype CloneRelationAttribute = Schema.Attribute.Relation & {\n targetAttribute?: string;\n};\n\nexport type DeferredRelationCopy = {\n schemaUid: UID.Schema;\n attributeName: string;\n kind: 'joinTable' | 'fkColumn' | 'morphToOne';\n /** Lodash path to the owning entity in entry data (e.g. `details` or `items.0`). `null` = root entry. */\n ownerPath: string | null;\n};\n\nexport type PostCloneRelationUpdate = {\n dataPath: string;\n value: Record<string, unknown>;\n schemaUid: UID.Schema;\n ownerPath: string | null;\n};\n\nexport type PrepareCloneDataResult = {\n data: Record<string, unknown>;\n deferredCopies: DeferredRelationCopy[];\n postCloneUpdates: PostCloneRelationUpdate[];\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n};\n\nconst isRelationAttribute = (attribute: unknown): attribute is CloneRelationAttribute => {\n return (\n isRecord(attribute) && attribute.type === 'relation' && typeof attribute.relation === 'string'\n );\n};\n\nconst isRelationOperationPayload = (value: unknown): value is Record<string, unknown> => {\n return (\n isRecord(value) &&\n RELATION_OPERATIONS.some((operation) => Object.prototype.hasOwnProperty.call(value, operation))\n );\n};\n\nconst hasMeaningfulRelationOperations = (value: Record<string, unknown>) => {\n if (Object.prototype.hasOwnProperty.call(value, 'set')) {\n return true;\n }\n\n return ['connect', 'disconnect'].some((operation) => {\n const operationValue = value[operation];\n return Array.isArray(operationValue) ? operationValue.length > 0 : operationValue != null;\n });\n};\n\nconst usesJoinTable = (attribute: Schema.Attribute.Relation) => {\n return attribute.useJoinTable !== false;\n};\n\nconst isBidirectionalOneToOne = (attribute: CloneRelationAttribute) => {\n return (\n attribute.relation === 'oneToOne' &&\n [attribute.inversedBy, attribute.mappedBy, attribute.targetAttribute].some(\n (inverseAttribute) => inverseAttribute != null\n )\n );\n};\n\nconst isMorphToOneAttribute = (attribute: CloneRelationAttribute) => {\n return (attribute as { relation?: string }).relation === 'morphToOne';\n};\n\nconst isBidirectionalOneToManyMappedBy = (attribute: CloneRelationAttribute) => {\n return attribute.relation === 'oneToMany' && attribute.mappedBy != null;\n};\n\nconst hasPopulatedRelation = (value: unknown) => {\n return isRecord(value) && ('id' in value || 'documentId' in value);\n};\n\nconst hasPopulatedRelationList = (value: unknown) => {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every((item) => isRecord(item) && ('id' in item || 'documentId' in item))\n );\n};\n\nconst getSubmittedValueAtPath = (submitted: Record<string, unknown>, path: string) => {\n return get(submitted, path);\n};\n\nconst wasRelationSubmittedAtPath = (submitted: Record<string, unknown>, path: string) => {\n return has(submitted, path);\n};\n\nconst isRelationUnchangedAtPath = (\n submitted: Record<string, unknown>,\n path: string,\n submittedValue: unknown\n) => {\n if (!wasRelationSubmittedAtPath(submitted, path)) {\n return true;\n }\n\n const isOperationPayload = isRelationOperationPayload(submittedValue);\n const hasMeaningfulOperations =\n isOperationPayload && hasMeaningfulRelationOperations(submittedValue);\n\n return isOperationPayload && !hasMeaningfulOperations;\n};\n\n/**\n * The nested collector walks the source entry, so a relation under a component\n * the clone payload set to null still looks omitted. That copy has no owner on\n * the clone and must not be queued. Missing paths are not removal: those owners\n * still need their deferred copy, and a later resolution failure must still throw.\n */\nconst isOwnerExplicitlyRemoved = (submitted: Record<string, unknown>, ownerPath: string) => {\n const segments = ownerPath.split('.');\n let current: unknown = submitted;\n\n for (const segment of segments) {\n if (Array.isArray(current)) {\n const index = Number(segment);\n if (!Number.isInteger(index) || index < 0 || index >= current.length) {\n return false;\n }\n\n current = current[index];\n } else if (isRecord(current) && Object.prototype.hasOwnProperty.call(current, segment)) {\n current = current[segment];\n } else {\n return false;\n }\n\n if (current === null) {\n return true;\n }\n }\n\n return false;\n};\n\n/**\n * Like `traverseEntityRelations`, but also visits inline storage (`useJoinTable: false`\n * and `morphToOne`). Clone follow-up must see those relations; the shared transform\n * helper intentionally skips them because they are handled in processData.\n */\nconst traverseAllEntityRelations = async (\n visitor: TraverseVisitor,\n options: Parameters<typeof traverseEntity>[1],\n data: TraversableData\n) => {\n return traverseEntity(\n async (visitorOptions, utils) => {\n const { attribute } = visitorOptions;\n\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitor(visitorOptions, utils);\n },\n options,\n data\n );\n};\n\nconst collectRelationOperationOverrides = async (\n submittedData: Record<string, unknown>,\n contentType: Schema.ContentType,\n getModel: (uid: string) => Schema.Schema\n) => {\n const overrides = new Map<string, Record<string, unknown>>();\n\n await traverseEntityRelations(\n ({ path, value }) => {\n if (isRelationOperationPayload(value) && hasMeaningfulRelationOperations(value)) {\n overrides.set(path.rawWithIndices!, value);\n }\n },\n { schema: contentType, getModel },\n submittedData as TraversableData\n );\n\n return [...overrides.entries()].sort(([leftPath], [rightPath]) =>\n leftPath.localeCompare(rightPath)\n );\n};\n\nconst collectNestedCloneRelationAdjustments = async (\n originalData: Record<string, unknown>,\n submitted: Record<string, unknown>,\n contentType: Schema.ContentType,\n getModel: (uid: string) => Schema.Schema,\n data: Record<string, unknown>\n) => {\n const adjustedData = data;\n const deferredCopies: DeferredRelationCopy[] = [];\n const postCloneUpdates: PostCloneRelationUpdate[] = [];\n\n await traverseAllEntityRelations(\n ({ attribute, key, path, schema, value }) => {\n if (!attribute || !isRelationAttribute(attribute)) {\n return;\n }\n\n const relationPath = path.rawWithIndices!;\n // Owner is the parent of the relation path (preserve indices for\n // repeatable components / DZs, e.g. `items.0.tag` → `items.0`).\n // Top-level relations have no parent segment → null (root entry).\n const lastDot = relationPath.lastIndexOf('.');\n const ownerPath = lastDot === -1 ? null : relationPath.slice(0, lastDot);\n\n if (ownerPath == null) {\n return;\n }\n\n if (isOwnerExplicitlyRemoved(submitted, ownerPath)) {\n return;\n }\n\n const submittedValue = getSubmittedValueAtPath(submitted, relationPath);\n const relationIsUnchanged = isRelationUnchangedAtPath(\n submitted,\n relationPath,\n submittedValue\n );\n\n if (\n relationIsUnchanged &&\n isBidirectionalOneToOne(attribute) &&\n usesJoinTable(attribute) &&\n hasPopulatedRelation(value)\n ) {\n unset(adjustedData, relationPath);\n deferredCopies.push({\n schemaUid: schema.uid as UID.Schema,\n attributeName: key,\n kind: 'joinTable',\n ownerPath,\n });\n return;\n }\n\n if (\n relationIsUnchanged &&\n isBidirectionalOneToOne(attribute) &&\n !usesJoinTable(attribute) &&\n hasPopulatedRelation(value)\n ) {\n unset(adjustedData, relationPath);\n deferredCopies.push({\n schemaUid: schema.uid as UID.Schema,\n attributeName: key,\n kind: 'fkColumn',\n ownerPath,\n });\n return;\n }\n\n // Always defer morphToOne: populate can be null even when morph columns are set.\n if (relationIsUnchanged && isMorphToOneAttribute(attribute)) {\n unset(adjustedData, relationPath);\n deferredCopies.push({\n schemaUid: schema.uid as UID.Schema,\n attributeName: key,\n kind: 'morphToOne',\n ownerPath,\n });\n return;\n }\n\n if (\n relationIsUnchanged &&\n isBidirectionalOneToManyMappedBy(attribute) &&\n hasPopulatedRelationList(value)\n ) {\n unset(adjustedData, relationPath);\n return;\n }\n\n if (\n !relationIsUnchanged &&\n isRelationOperationPayload(submittedValue) &&\n hasMeaningfulRelationOperations(submittedValue) &&\n (!usesJoinTable(attribute) || isMorphToOneAttribute(attribute))\n ) {\n unset(adjustedData, relationPath);\n postCloneUpdates.push({\n dataPath: relationPath,\n value: submittedValue,\n schemaUid: schema.uid as UID.Schema,\n ownerPath,\n });\n }\n },\n { schema: contentType, getModel },\n originalData as TraversableData\n );\n\n return { data: adjustedData, deferredCopies, postCloneUpdates };\n};\n\nexport const prepareCloneData = async (\n originalData: Record<string, unknown>,\n submittedData: Record<string, unknown> | undefined,\n contentType: Schema.ContentType,\n getModel: (uid: string) => Schema.Schema\n): Promise<PrepareCloneDataResult> => {\n const submitted = submittedData ?? {};\n const relationOperationOverrides = await collectRelationOperationOverrides(\n submitted,\n contentType,\n getModel\n );\n let data = merge({}, originalData, submitted) as Record<string, unknown>;\n const deferredCopies: DeferredRelationCopy[] = [];\n const postCloneUpdates: PostCloneRelationUpdate[] = [];\n\n for (const [attributeName, attribute] of Object.entries(contentType.attributes)) {\n if (!isRelationAttribute(attribute)) {\n continue;\n }\n\n const submittedValue = submitted[attributeName];\n const relationWasSubmitted = Object.prototype.hasOwnProperty.call(submitted, attributeName);\n const isOperationPayload = isRelationOperationPayload(submittedValue);\n const hasMeaningfulOperations =\n isOperationPayload && hasMeaningfulRelationOperations(submittedValue);\n const relationIsUnchanged =\n !relationWasSubmitted || (isOperationPayload && !hasMeaningfulOperations);\n\n if (\n relationIsUnchanged &&\n isBidirectionalOneToOne(attribute) &&\n usesJoinTable(attribute) &&\n hasPopulatedRelation(originalData[attributeName])\n ) {\n delete data[attributeName];\n deferredCopies.push({\n schemaUid: contentType.uid as UID.Schema,\n attributeName,\n kind: 'joinTable',\n ownerPath: null,\n });\n continue;\n }\n\n if (\n relationIsUnchanged &&\n isBidirectionalOneToOne(attribute) &&\n !usesJoinTable(attribute) &&\n hasPopulatedRelation(originalData[attributeName])\n ) {\n delete data[attributeName];\n deferredCopies.push({\n schemaUid: contentType.uid as UID.Schema,\n attributeName,\n kind: 'fkColumn',\n ownerPath: null,\n });\n continue;\n }\n\n // Always defer morphToOne: populate can be null even when morph columns are set.\n if (relationIsUnchanged && isMorphToOneAttribute(attribute)) {\n delete data[attributeName];\n deferredCopies.push({\n schemaUid: contentType.uid as UID.Schema,\n attributeName,\n kind: 'morphToOne',\n ownerPath: null,\n });\n continue;\n }\n\n if (\n relationIsUnchanged &&\n isBidirectionalOneToManyMappedBy(attribute) &&\n hasPopulatedRelationList(originalData[attributeName])\n ) {\n delete data[attributeName];\n continue;\n }\n\n if (\n !relationIsUnchanged &&\n isRelationOperationPayload(submittedValue) &&\n hasMeaningfulOperations &&\n (!usesJoinTable(attribute) || isMorphToOneAttribute(attribute))\n ) {\n delete data[attributeName];\n postCloneUpdates.push({\n dataPath: attributeName,\n value: submittedValue,\n schemaUid: contentType.uid as UID.Schema,\n ownerPath: null,\n });\n }\n }\n\n const nested = await collectNestedCloneRelationAdjustments(\n originalData,\n submitted,\n contentType,\n getModel,\n data\n );\n data = nested.data;\n deferredCopies.push(...nested.deferredCopies);\n postCloneUpdates.push(...nested.postCloneUpdates);\n\n for (const [path, value] of relationOperationOverrides) {\n set(data, path, value);\n }\n\n return { data, deferredCopies, postCloneUpdates };\n};\n\nconst resolveOwnerEntryId = (\n entryData: Record<string, unknown>,\n ownerPath: string | null,\n fallbackId?: number\n) => {\n if (ownerPath == null) {\n return fallbackId;\n }\n\n const owner = get(entryData, ownerPath) as Record<string, unknown> | undefined;\n const ownerId = owner?.id;\n\n return typeof ownerId === 'number' ? ownerId : undefined;\n};\n\nconst ownerComponentUid = (owner: unknown) => {\n if (!isRecord(owner) || typeof owner.__component !== 'string') {\n return undefined;\n }\n\n return owner.__component;\n};\n\n/**\n * A dynamic-zone block can be replaced by another component at the same index.\n * Component ids are allocated per table, so a deferred write that still uses the\n * source component UID and the new row id updates an unrelated row.\n */\nconst ownerComponentWasReplaced = (\n originalData: Record<string, unknown>,\n clonedData: Record<string, unknown>,\n ownerPath: string | null\n) => {\n if (ownerPath == null) {\n return false;\n }\n\n const sourceComponent = ownerComponentUid(get(originalData, ownerPath));\n const cloneComponent = ownerComponentUid(get(clonedData, ownerPath));\n\n return sourceComponent != null && cloneComponent != null && sourceComponent !== cloneComponent;\n};\n\nexport const copyCloneRelationRows = async (\n strapi: Core.Strapi,\n uid: UID.Schema,\n attributeName: string,\n sourceEntryId: number,\n targetEntryId: number\n) => {\n const { attributes } = strapi.db.metadata.get(uid);\n const attribute = attributes[attributeName];\n\n if (attribute?.type !== 'relation' || !('joinTable' in attribute) || !attribute.joinTable) {\n return;\n }\n\n const idColumn = strapi.db.metadata.identifiers.ID_COLUMN;\n const batchSize = strapi.db.dialect.getBatchInsertSize();\n const { joinTable } = attribute;\n const { joinColumn } = joinTable;\n\n await strapi.db.transaction(async ({ trx }) => {\n const rows = (await strapi.db\n .getConnection(joinTable.name)\n .where({\n [joinColumn.name]: sourceEntryId,\n ...('on' in joinTable && joinTable.on),\n })\n .select('*')\n .transacting(trx)) as Record<string, unknown>[];\n\n const rowsToCopy = rows.map((row) => {\n const copiedRow = { ...row, [joinColumn.name]: targetEntryId };\n delete copiedRow[idColumn];\n return copiedRow;\n });\n\n for (let index = 0; index < rowsToCopy.length; index += batchSize) {\n await strapi.db\n .getConnection(joinTable.name)\n .insert(rowsToCopy.slice(index, index + batchSize))\n .transacting(trx);\n }\n });\n};\n\nconst copyFkColumnRelation = async (\n strapi: Core.Strapi,\n targetUid: UID.Schema,\n attributeName: string,\n sourceEntryId: number,\n targetEntryId: number,\n sourceUid: UID.Schema = targetUid\n) => {\n const meta = strapi.db.metadata.get(sourceUid);\n const attribute = meta.attributes[attributeName];\n\n if (attribute?.type !== 'relation' || !('joinColumn' in attribute) || !attribute.joinColumn) {\n return;\n }\n\n // Inverse/non-owning joinColumn.name is this table's `id`, not an FK. Copying it\n // attaches an unrelated row whose numeric id happens to match the source id.\n if (!('owner' in attribute) || attribute.owner !== true) {\n return;\n }\n\n const joinColumnName = attribute.joinColumn.name;\n\n await strapi.db.transaction(async ({ trx }) => {\n const sourceRow = await strapi.db\n .getConnection(meta.tableName)\n .where({ id: sourceEntryId })\n .select([joinColumnName])\n .transacting(trx)\n .first();\n\n if (!sourceRow) {\n return;\n }\n\n const targetAttribute = strapi.db.metadata.get(targetUid).attributes[attributeName];\n if (targetAttribute?.type !== 'relation' || targetAttribute.relation !== 'oneToOne') {\n return;\n }\n\n // processData only accepts attribute names, not raw join-column names\n await strapi.db.query(targetUid).update({\n where: { id: targetEntryId },\n data: { [attributeName]: sourceRow[joinColumnName] ?? null },\n });\n });\n};\n\nconst copyMorphToOneRelation = async (\n strapi: Core.Strapi,\n targetUid: UID.Schema,\n attributeName: string,\n sourceEntryId: number,\n targetEntryId: number,\n sourceUid: UID.Schema = targetUid\n) => {\n const sourceAttribute = strapi.db.metadata.get(sourceUid).attributes[attributeName];\n const targetAttribute = strapi.db.metadata.get(targetUid).attributes[attributeName];\n\n if (\n sourceAttribute?.type !== 'relation' ||\n sourceAttribute.relation !== 'morphToOne' ||\n !sourceAttribute.morphColumn ||\n targetAttribute?.type !== 'relation' ||\n targetAttribute.relation !== 'morphToOne'\n ) {\n return;\n }\n\n const { idColumn, typeColumn } = sourceAttribute.morphColumn;\n const typeField = targetAttribute.morphColumn?.typeField ?? '__type';\n const sourceMeta = strapi.db.metadata.get(sourceUid);\n\n await strapi.db.transaction(async ({ trx }) => {\n const sourceRow = await strapi.db\n .getConnection(sourceMeta.tableName)\n .where({ id: sourceEntryId })\n .select([idColumn.name, typeColumn.name])\n .transacting(trx)\n .first();\n\n if (!sourceRow) {\n return;\n }\n\n const morphId = sourceRow[idColumn.name];\n const morphType = sourceRow[typeColumn.name];\n\n // processData only accepts the morph attribute ({ id, __type }), not raw column names\n await strapi.db.query(targetUid).update({\n where: { id: targetEntryId },\n data: {\n [attributeName]:\n morphId != null && morphType != null ? { id: morphId, [typeField]: morphType } : null,\n },\n });\n });\n};\n\nexport const applyDeferredCloneRelationCopies = async (\n strapi: Core.Strapi,\n rootUid: UID.ContentType,\n sourceRootId: number,\n targetRootId: number,\n originalData: Record<string, unknown>,\n clonedData: Record<string, unknown>,\n deferredCopies: DeferredRelationCopy[]\n) => {\n if (deferredCopies.length === 0) {\n return;\n }\n\n for (const task of deferredCopies) {\n // Root owners (ownerPath === null) resolve via the fallback root IDs.\n // Nested owners must resolve explicitly — falling back to the root ID would\n // copy component-schema relation rows using an unrelated document ID.\n const sourceEntryId = resolveOwnerEntryId(originalData, task.ownerPath, sourceRootId);\n const targetEntryId = resolveOwnerEntryId(clonedData, task.ownerPath, targetRootId);\n\n if (sourceEntryId == null || targetEntryId == null) {\n throw new Error(\n `Unable to resolve clone relation owner for \"${task.attributeName}\" at path \"${task.ownerPath ?? '<root>'}\"`\n );\n }\n\n if (ownerComponentWasReplaced(originalData, clonedData, task.ownerPath)) {\n continue;\n }\n\n if (sourceEntryId === targetEntryId) {\n continue;\n }\n\n if (task.kind === 'joinTable') {\n await copyCloneRelationRows(\n strapi,\n task.schemaUid,\n task.attributeName,\n sourceEntryId,\n targetEntryId\n );\n } else if (task.kind === 'fkColumn') {\n await copyFkColumnRelation(\n strapi,\n task.schemaUid,\n task.attributeName,\n sourceEntryId,\n targetEntryId\n );\n } else if (task.kind === 'morphToOne') {\n await copyMorphToOneRelation(\n strapi,\n task.schemaUid,\n task.attributeName,\n sourceEntryId,\n targetEntryId\n );\n }\n }\n};\n\nconst toRelationRef = (value: unknown): Record<string, unknown> | undefined => {\n if (isRecord(value)) {\n return value;\n }\n\n if (typeof value === 'number') {\n return { id: value };\n }\n\n // Document Service shorthand: a string is a documentId, matching mapRelation.\n if (typeof value === 'string') {\n return { documentId: value };\n }\n\n return undefined;\n};\n\nconst relationOperationList = (value: unknown): unknown[] => {\n if (Array.isArray(value)) {\n return value;\n }\n\n if (value == null) {\n return [];\n }\n\n return [value];\n};\n\nconst pickInlineRelationTargetRef = (\n value: Record<string, unknown>\n): Record<string, unknown> | null | undefined => {\n if (Object.prototype.hasOwnProperty.call(value, 'set')) {\n const setValue = value.set;\n if (setValue == null) {\n return null;\n }\n\n const list = relationOperationList(setValue);\n if (list.length === 0) {\n return null;\n }\n\n return toRelationRef(list[list.length - 1]);\n }\n\n const connect = value.connect;\n if (connect == null) {\n return undefined;\n }\n\n const list = relationOperationList(connect);\n if (list.length === 0) {\n return undefined;\n }\n\n return toRelationRef(list[list.length - 1]);\n};\n\nconst toRelationStatus = (value: unknown): 'draft' | 'published' | undefined => {\n return value === 'draft' || value === 'published' ? value : undefined;\n};\n\nconst resolveEntryIdsByRef = async (\n strapi: Core.Strapi,\n sourceUid: UID.Schema,\n targetUid: UID.Schema,\n ref: Record<string, unknown>,\n opts: { locale?: string }\n): Promise<number[]> => {\n if (typeof ref.id === 'number') {\n return [ref.id];\n }\n\n if (typeof ref.documentId !== 'string') {\n return [];\n }\n\n let relation = {\n documentId: ref.documentId,\n locale: typeof ref.locale === 'string' ? ref.locale : undefined,\n status: toRelationStatus(ref.status),\n };\n\n if (\n relation.locale == null &&\n !isLocalizedContentType(sourceUid) &&\n isLocalizedContentType(targetUid)\n ) {\n relation = setDefaultLocaleToRelation(relation, await getDefaultLocale());\n }\n\n const targetLocale = getRelationTargetLocale(relation, {\n targetUid,\n sourceUid,\n sourceLocale: opts.locale,\n });\n\n const sourceHasDP = contentTypes.hasDraftAndPublish(strapi.getModel(sourceUid));\n const statuses = getRelationTargetStatus(relation, {\n targetUid,\n sourceUid,\n sourceStatus: sourceHasDP ? 'draft' : undefined,\n });\n\n const model = strapi.getModel(targetUid);\n const targetHasDP = contentTypes.hasDraftAndPublish(model);\n const resolvedIds: number[] = [];\n\n for (const status of statuses) {\n const where: Record<string, unknown> = { documentId: ref.documentId };\n\n if (targetHasDP) {\n where.publishedAt = status === 'draft' ? null : { $ne: null };\n }\n\n if (targetLocale) {\n where.locale = targetLocale;\n }\n\n const row = await strapi.db.query(targetUid).findOne({\n where,\n select: ['id'],\n });\n\n if (typeof row?.id === 'number' && !resolvedIds.includes(row.id)) {\n resolvedIds.push(row.id);\n }\n }\n\n return resolvedIds;\n};\n\nconst resolveEntryIdByRef = async (\n strapi: Core.Strapi,\n sourceUid: UID.Schema,\n targetUid: UID.Schema,\n ref: Record<string, unknown>,\n opts: { locale?: string }\n): Promise<number | undefined> => {\n const resolvedIds = await resolveEntryIdsByRef(strapi, sourceUid, targetUid, ref, opts);\n\n // Single-column FK/morph storage can keep only one id; match entity-manager and\n // keep the last resolved status when the standard resolver returns both.\n return resolvedIds.at(-1);\n};\n\nconst getMorphTypeField = (attribute: CloneRelationAttribute) => {\n const morphColumn =\n 'morphColumn' in attribute\n ? (attribute as { morphColumn?: { typeField?: string } }).morphColumn\n : undefined;\n\n return morphColumn?.typeField ?? '__type';\n};\n\nconst hasRelationTarget = (\n attribute: CloneRelationAttribute\n): attribute is CloneRelationAttribute & { target: string } => {\n return 'target' in attribute && typeof (attribute as { target?: unknown }).target === 'string';\n};\n\nconst currentAssociationId = (current: unknown): number | undefined => {\n if (typeof current === 'number') {\n return current;\n }\n\n if (isRecord(current) && typeof current.id === 'number') {\n return current.id;\n }\n\n return undefined;\n};\n\nconst disconnectMatchesCurrent = async (\n strapi: Core.Strapi,\n sourceUid: UID.Schema,\n attribute: CloneRelationAttribute,\n disconnect: unknown[],\n current: unknown,\n opts: { locale?: string }\n): Promise<boolean> => {\n const currentId = currentAssociationId(current);\n\n if (currentId == null) {\n return false;\n }\n\n const typeField = getMorphTypeField(attribute);\n const currentType =\n isMorphToOneAttribute(attribute) && isRecord(current) && typeof current[typeField] === 'string'\n ? current[typeField]\n : undefined;\n\n for (const item of disconnect) {\n let ref: Record<string, unknown> | null = null;\n if (isRecord(item)) {\n ref = item;\n } else if (typeof item === 'number' || typeof item === 'string') {\n ref = { id: item };\n }\n\n if (!ref) {\n continue;\n }\n\n if (isMorphToOneAttribute(attribute)) {\n const targetUid = ref[typeField];\n if (typeof targetUid !== 'string') {\n continue;\n }\n\n if (currentType != null && currentType !== targetUid) {\n continue;\n }\n\n const ids = await resolveEntryIdsByRef(strapi, sourceUid, targetUid as UID.Schema, ref, opts);\n if (ids.includes(currentId)) {\n return true;\n }\n\n continue;\n }\n\n if (!hasRelationTarget(attribute)) {\n continue;\n }\n\n const ids = await resolveEntryIdsByRef(\n strapi,\n sourceUid,\n attribute.target as UID.Schema,\n ref,\n opts\n );\n if (ids.includes(currentId)) {\n return true;\n }\n }\n\n return false;\n};\n\nconst resolveInlineRelationAssignment = async (\n strapi: Core.Strapi,\n attribute: CloneRelationAttribute,\n value: Record<string, unknown>,\n opts: {\n locale?: string;\n ownerUid: UID.Schema;\n /** Schema to read the current association from. Differs from ownerUid after a dynamic-zone component swap. */\n sourceSchemaUid?: UID.Schema;\n sourceUid: UID.Schema;\n originalValue?: unknown;\n sourceOwnerId?: number;\n targetOwnerId: number;\n attributeName: string;\n }\n): Promise<unknown> => {\n const targetRef = pickInlineRelationTargetRef(value);\n\n if (targetRef === undefined) {\n const disconnect = relationOperationList(value.disconnect);\n if (disconnect.length === 0) {\n return undefined;\n }\n\n const matches = await disconnectMatchesCurrent(\n strapi,\n opts.sourceUid,\n attribute,\n disconnect,\n opts.originalValue,\n { locale: opts.locale }\n );\n\n if (matches) {\n return null;\n }\n\n // Unmatched disconnect must keep the original association. The clone was\n // created without the relation (stripped from create data), so copy it now.\n if (opts.sourceOwnerId == null) {\n throw new Error(\n `Unable to restore clone relation \"${opts.attributeName}\" after unmatched disconnect`\n );\n }\n\n const readUid = opts.sourceSchemaUid ?? opts.ownerUid;\n\n if (isMorphToOneAttribute(attribute)) {\n await copyMorphToOneRelation(\n strapi,\n opts.ownerUid,\n opts.attributeName,\n opts.sourceOwnerId,\n opts.targetOwnerId,\n readUid\n );\n } else {\n await copyFkColumnRelation(\n strapi,\n opts.ownerUid,\n opts.attributeName,\n opts.sourceOwnerId,\n opts.targetOwnerId,\n readUid\n );\n }\n\n return undefined;\n }\n\n if (targetRef === null) {\n return null;\n }\n\n if (isMorphToOneAttribute(attribute)) {\n const typeField = getMorphTypeField(attribute);\n const targetUid = targetRef[typeField];\n\n if (typeof targetUid !== 'string') {\n throw new Error(`Inline morphToOne clone update requires ${typeField}`);\n }\n\n const id = await resolveEntryIdByRef(\n strapi,\n opts.sourceUid,\n targetUid as UID.Schema,\n targetRef,\n opts\n );\n\n if (id == null) {\n throw new Error(`Unable to resolve morphToOne target for clone relation update`);\n }\n\n return { id, [typeField]: targetUid };\n }\n\n if (!hasRelationTarget(attribute)) {\n throw new Error('Inline FK clone update requires a relation target');\n }\n\n const id = await resolveEntryIdByRef(\n strapi,\n opts.sourceUid,\n attribute.target as UID.Schema,\n targetRef,\n opts\n );\n\n if (id == null) {\n throw new Error(`Unable to resolve relation target for clone relation update`);\n }\n\n return id;\n};\n\nexport const applyPostCloneRelationUpdates = async (\n strapi: Core.Strapi,\n rootUid: UID.ContentType,\n sourceRootId: number,\n clonedEntryId: number,\n clonedData: Record<string, unknown>,\n originalData: Record<string, unknown>,\n postCloneUpdates: PostCloneRelationUpdate[]\n) => {\n if (postCloneUpdates.length === 0) {\n return;\n }\n\n const locale = clonedData.locale as string | undefined;\n\n for (const update of postCloneUpdates) {\n const attributeName = update.dataPath.split('.').pop()!;\n const ownerEntryId = resolveOwnerEntryId(clonedData, update.ownerPath, clonedEntryId);\n const sourceOwnerId = resolveOwnerEntryId(originalData, update.ownerPath, sourceRootId);\n\n if (ownerEntryId == null) {\n throw new Error(\n `Unable to resolve clone relation owner for \"${attributeName}\" at path \"${update.ownerPath ?? '<root>'}\"`\n );\n }\n\n let schemaUid = update.schemaUid;\n\n if (ownerComponentWasReplaced(originalData, clonedData, update.ownerPath)) {\n const cloneComponent = ownerComponentUid(get(clonedData, update.ownerPath!));\n const cloneAttribute = cloneComponent\n ? (strapi.db.metadata.get(cloneComponent).attributes[attributeName] as\n CloneRelationAttribute | undefined)\n : undefined;\n\n // The submitted operation belongs to the new block. Do not apply it with the\n // source component UID, or the new row id will match an unrelated row.\n if (!cloneComponent || cloneAttribute?.type !== 'relation') {\n continue;\n }\n\n schemaUid = cloneComponent as UID.Schema;\n }\n\n const attribute = strapi.db.metadata.get(schemaUid).attributes[attributeName] as\n CloneRelationAttribute | undefined;\n\n if (!attribute || attribute.type !== 'relation') {\n throw new Error(\n `Unable to resolve clone relation attribute \"${attributeName}\" on \"${schemaUid}\"`\n );\n }\n\n const assignment = await resolveInlineRelationAssignment(strapi, attribute, update.value, {\n locale,\n ownerUid: schemaUid,\n sourceSchemaUid: update.schemaUid,\n sourceUid: rootUid,\n originalValue: get(originalData, update.dataPath),\n sourceOwnerId,\n targetOwnerId: ownerEntryId,\n attributeName,\n });\n\n if (assignment === undefined) {\n continue;\n }\n\n // processData accepts attribute names (FK id / morph { id, __type }), not operation payloads.\n await strapi.db.query(schemaUid).update({\n where: { id: ownerEntryId },\n data: { [attributeName]: assignment },\n });\n }\n};\n"],"names":["RELATION_OPERATIONS","isRecord","value","Array","isArray","isRelationAttribute","attribute","type","relation","isRelationOperationPayload","some","operation","Object","prototype","hasOwnProperty","call","hasMeaningfulRelationOperations","operationValue","length","usesJoinTable","useJoinTable","isBidirectionalOneToOne","inversedBy","mappedBy","targetAttribute","inverseAttribute","isMorphToOneAttribute","isBidirectionalOneToManyMappedBy","hasPopulatedRelation","hasPopulatedRelationList","every","item","getSubmittedValueAtPath","submitted","path","get","wasRelationSubmittedAtPath","has","isRelationUnchangedAtPath","submittedValue","isOperationPayload","hasMeaningfulOperations","isOwnerExplicitlyRemoved","ownerPath","segments","split","current","segment","index","Number","isInteger","traverseAllEntityRelations","visitor","options","data","traverseEntity","visitorOptions","utils","collectRelationOperationOverrides","submittedData","contentType","getModel","overrides","Map","traverseEntityRelations","set","rawWithIndices","schema","entries","sort","leftPath","rightPath","localeCompare","collectNestedCloneRelationAdjustments","originalData","adjustedData","deferredCopies","postCloneUpdates","key","relationPath","lastDot","lastIndexOf","slice","relationIsUnchanged","unset","push","schemaUid","uid","attributeName","kind","dataPath","prepareCloneData","relationOperationOverrides","merge","attributes","relationWasSubmitted","nested","resolveOwnerEntryId","entryData","fallbackId","owner","ownerId","id","undefined","ownerComponentUid","__component","ownerComponentWasReplaced","clonedData","sourceComponent","cloneComponent","copyCloneRelationRows","strapi","sourceEntryId","targetEntryId","db","metadata","joinTable","idColumn","identifiers","ID_COLUMN","batchSize","dialect","getBatchInsertSize","joinColumn","transaction","trx","rows","getConnection","name","where","on","select","transacting","rowsToCopy","map","row","copiedRow","insert","copyFkColumnRelation","targetUid","sourceUid","meta","joinColumnName","sourceRow","tableName","first","query","update","copyMorphToOneRelation","sourceAttribute","morphColumn","typeColumn","typeField","sourceMeta","morphId","morphType","applyDeferredCloneRelationCopies","rootUid","sourceRootId","targetRootId","task","Error","toRelationRef","documentId","relationOperationList","pickInlineRelationTargetRef","setValue","list","connect","toRelationStatus","resolveEntryIdsByRef","ref","opts","locale","status","isLocalizedContentType","setDefaultLocaleToRelation","getDefaultLocale","targetLocale","getRelationTargetLocale","sourceLocale","sourceHasDP","contentTypes","hasDraftAndPublish","statuses","getRelationTargetStatus","sourceStatus","model","targetHasDP","resolvedIds","publishedAt","$ne","findOne","includes","resolveEntryIdByRef","at","getMorphTypeField","hasRelationTarget","target","currentAssociationId","disconnectMatchesCurrent","disconnect","currentId","currentType","ids","resolveInlineRelationAssignment","targetRef","matches","originalValue","sourceOwnerId","readUid","sourceSchemaUid","ownerUid","targetOwnerId","applyPostCloneRelationUpdates","clonedEntryId","pop","ownerEntryId","cloneAttribute","assignment"],"mappings":"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"}
|
|
@@ -16,10 +16,12 @@ interface RelationUpdate {
|
|
|
16
16
|
}
|
|
17
17
|
interface RelationFilterOptions {
|
|
18
18
|
/**
|
|
19
|
-
* Function
|
|
19
|
+
* Function returning which relations should be propagated to new document versions
|
|
20
20
|
* This replaces the hardcoded component-specific logic
|
|
21
|
+
*
|
|
22
|
+
* It receives all the relations of a join table at once, so it can batch its lookups
|
|
21
23
|
*/
|
|
22
|
-
|
|
24
|
+
filterRelationsToPropagate?: (relations: Record<string, any>[], model: Schema.Component | Schema.ContentType, trx: any) => Promise<Record<string, any>[]>;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Loads lingering relations that need to be updated when overriding a published or draft entry.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unidirectional-relations.d.ts","sourceRoot":"","sources":["../../../../src/services/document-service/utils/unidirectional-relations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,UAAU,WAAW;IACnB,WAAW,EAAE;QAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/C,WAAW,EAAE;QAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChD;AAED,UAAU,cAAc;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CAClC;AAED,UAAU,qBAAqB;IAC7B
|
|
1
|
+
{"version":3,"file":"unidirectional-relations.d.ts","sourceRoot":"","sources":["../../../../src/services/document-service/utils/unidirectional-relations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,UAAU,WAAW;IACnB,WAAW,EAAE;QAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/C,WAAW,EAAE;QAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChD;AAED,UAAU,cAAc;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CAClC;AAED,UAAU,qBAAqB;IAC7B;;;;;OAKG;IACH,0BAA0B,CAAC,EAAE,CAC3B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAChC,KAAK,EAAE,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,WAAW,EAC5C,GAAG,EAAE,GAAG,KACL,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;CACrC;AAED;;;;GAIG;AACH,QAAA,MAAM,IAAI,GACR,KAAK,GAAG,CAAC,WAAW,EACpB,8BAA8B,WAAW,EACzC,UAAS,qBAA0B,KAClC,OAAO,CAAC,cAAc,EAAE,CAmH1B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,QAAA,MAAM,IAAI,GACR,YAAY;IAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,EAC7C,YAAY;IAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,EAC7C,cAAc;IAAE,SAAS,EAAE,GAAG,CAAC;IAAC,SAAS,EAAE,GAAG,EAAE,CAAA;CAAE,EAAE,kBAiCrD,CAAC;AAEF,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACtB,YAAY,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -70,16 +70,7 @@ var _ = require('lodash');
|
|
|
70
70
|
const ids = newVersions.map((entry)=>entry.id);
|
|
71
71
|
// This is the step were we query the join table based on the id of the document
|
|
72
72
|
const newVersionsRelations = await strapi.db.getConnection().select('*').from(joinTable.name).whereIn(targetColumnName, ids).transacting(trx);
|
|
73
|
-
|
|
74
|
-
if (options.shouldPropagateRelation) {
|
|
75
|
-
const relationsToPropagate = [];
|
|
76
|
-
for (const relation of newVersionsRelations){
|
|
77
|
-
if (await options.shouldPropagateRelation(relation, model, trx)) {
|
|
78
|
-
relationsToPropagate.push(relation);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
versionRelations = relationsToPropagate;
|
|
82
|
-
}
|
|
73
|
+
const versionRelations = options.filterRelationsToPropagate ? await options.filterRelationsToPropagate(newVersionsRelations, model, trx) : newVersionsRelations;
|
|
83
74
|
if (versionRelations.length > 0) {
|
|
84
75
|
// when publishing a draft that doesn't have a published version yet,
|
|
85
76
|
// copy the links to the draft over to the published version
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unidirectional-relations.js","sources":["../../../../src/services/document-service/utils/unidirectional-relations.ts"],"sourcesContent":["/* eslint-disable no-continue */\nimport { keyBy, omit } from 'lodash';\n\nimport type { Data, UID, Schema } from '@strapi/types';\n\nimport type { JoinTable } from '@strapi/database';\n\ninterface LoadContext {\n oldVersions: { id: Data.ID; locale: string }[];\n newVersions: { id: Data.ID; locale: string }[];\n}\n\ninterface RelationUpdate {\n joinTable: JoinTable;\n relations: Record<string, any>[];\n}\n\ninterface RelationFilterOptions {\n /**\n * Function to determine if a relation should be propagated to new document versions\n * This replaces the hardcoded component-specific logic\n */\n shouldPropagateRelation?: (\n relation: Record<string, any>,\n model: Schema.Component | Schema.ContentType,\n trx: any\n ) => Promise<boolean>;\n}\n\n/**\n * Loads lingering relations that need to be updated when overriding a published or draft entry.\n * This is necessary because the relations are uni-directional and the target entry is not aware of the source entry.\n * This is not the case for bi-directional relations, where the target entry is also linked to the source entry.\n */\nconst load = async (\n uid: UID.ContentType,\n { oldVersions, newVersions }: LoadContext,\n options: RelationFilterOptions = {}\n): Promise<RelationUpdate[]> => {\n const updates: RelationUpdate[] = [];\n\n // Iterate all components and content types to find relations that need to be updated\n await strapi.db.transaction(async ({ trx }) => {\n const contentTypes = Object.values(strapi.contentTypes) as Schema.ContentType[];\n const components = Object.values(strapi.components) as Schema.Component[];\n\n for (const model of [...contentTypes, ...components]) {\n const dbModel = strapi.db.metadata.get(model.uid);\n\n for (const attribute of Object.values(dbModel.attributes) as any) {\n /**\n * Only consider unidirectional relations.\n * Bidirectional relations (inversedBy/mappedBy) are handled by bidirectionalRelations.\n * Self-referential relations (model.uid === uid) are included here, but rows where\n * the source entry is also being republished in this same operation are excluded —\n * those are handled by selfReferentialRelations, which remaps both sides simultaneously.\n * Without this guard, we would insert rows pointing to a source entry that is about\n * to be deleted, creating stale foreign-key values.\n */\n if (\n attribute.type !== 'relation' ||\n attribute.target !== uid ||\n attribute.inversedBy ||\n attribute.mappedBy\n ) {\n continue;\n }\n\n // TODO: joinColumn relations\n const joinTable = attribute.joinTable;\n if (!joinTable) {\n continue;\n }\n\n const { name: sourceColumnName } = joinTable.joinColumn;\n const { name: targetColumnName } = joinTable.inverseJoinColumn;\n\n /**\n * Load all relations that need to be updated\n */\n // NOTE: when the model has draft and publish, we can assume relation are only draft to draft & published to published\n const ids = oldVersions.map((entry) => entry.id);\n\n let oldVersionsQuery = strapi.db\n .getConnection()\n .select('*')\n .from(joinTable.name)\n .whereIn(targetColumnName, ids);\n\n /**\n * For self-referential relations, exclude join rows where the source entry is also\n * being republished. When both sides are in the same publish batch, selfReferentialRelations\n * already handles remapping them. Including them here too would insert a row whose\n * source FK points at an entry that is about to be deleted.\n */\n if (model.uid === uid && ids.length > 0) {\n oldVersionsQuery = oldVersionsQuery.whereNotIn(sourceColumnName, ids);\n }\n\n const oldVersionsRelations = await oldVersionsQuery.transacting(trx);\n\n if (oldVersionsRelations.length > 0) {\n updates.push({ joinTable, relations: oldVersionsRelations });\n }\n\n /**\n * if publishing\n * if published version exists\n * updated published versions links\n * else\n * create link to newly published version\n *\n * if discarding\n * if published version link exists & not draft version link\n * create link to new draft version\n */\n if (!model.options?.draftAndPublish) {\n const ids = newVersions.map((entry) => entry.id);\n\n // This is the step were we query the join table based on the id of the document\n const newVersionsRelations = await strapi.db\n .getConnection()\n .select('*')\n .from(joinTable.name)\n .whereIn(targetColumnName, ids)\n .transacting(trx);\n\n let versionRelations = newVersionsRelations;\n if (options.shouldPropagateRelation) {\n const relationsToPropagate = [];\n for (const relation of newVersionsRelations) {\n if (await options.shouldPropagateRelation(relation, model, trx)) {\n relationsToPropagate.push(relation);\n }\n }\n versionRelations = relationsToPropagate;\n }\n\n if (versionRelations.length > 0) {\n // when publishing a draft that doesn't have a published version yet,\n // copy the links to the draft over to the published version\n // when discarding a published version, if no drafts exists\n const discardToAdd = versionRelations\n .filter((relation) => {\n const matchingOldVersion = oldVersionsRelations.find((oldRelation) => {\n return oldRelation[sourceColumnName] === relation[sourceColumnName];\n });\n\n return !matchingOldVersion;\n })\n .map((value) => omit(value, strapi.db.metadata.identifiers.ID_COLUMN));\n\n updates.push({ joinTable, relations: discardToAdd });\n }\n }\n }\n }\n });\n\n return updates;\n};\n\n/**\n * Updates uni directional relations to target the right entries when overriding published or draft entries.\n *\n * This function:\n * 1. Creates new relations pointing to the new entry versions\n * 2. Precisely deletes only the old relations being replaced to prevent orphaned links\n *\n * @param oldEntries The old entries that are being overridden\n * @param newEntries The new entries that are overriding the old ones\n * @param oldRelations The relations that were previously loaded with `load` @see load\n */\nconst sync = async (\n oldEntries: { id: Data.ID; locale: string }[],\n newEntries: { id: Data.ID; locale: string }[],\n oldRelations: { joinTable: any; relations: any[] }[]\n) => {\n /**\n * Create a map of old entry ids to new entry ids\n *\n * Will be used to update the relation target ids\n */\n const newEntryByLocale = keyBy(newEntries, 'locale');\n const oldEntriesMap = oldEntries.reduce(\n (acc, entry) => {\n const newEntry = newEntryByLocale[entry.locale];\n if (!newEntry) return acc;\n acc[String(entry.id)] = newEntry.id;\n return acc;\n },\n {} as Record<string, Data.ID>\n );\n\n await strapi.db.transaction(async ({ trx }) => {\n // Iterate old relations that are deleted and insert the new ones\n for (const { joinTable, relations } of oldRelations) {\n // Update old ids with the new ones\n const column = joinTable.inverseJoinColumn.name;\n\n const newRelations = relations.map((relation) => {\n const newId = oldEntriesMap[relation[column]];\n return { ...relation, [column]: newId };\n });\n\n const batchSize = strapi.db.dialect.getBatchInsertSize();\n await trx.batchInsert(joinTable.name, newRelations, batchSize);\n }\n });\n};\n\nexport { load, sync };\nexport type { RelationFilterOptions };\n"],"names":["load","uid","oldVersions","newVersions","options","updates","strapi","db","transaction","trx","contentTypes","Object","values","components","model","dbModel","metadata","get","attribute","attributes","type","target","inversedBy","mappedBy","joinTable","name","sourceColumnName","joinColumn","targetColumnName","inverseJoinColumn","ids","map","entry","id","oldVersionsQuery","getConnection","select","from","whereIn","length","whereNotIn","oldVersionsRelations","transacting","push","relations","draftAndPublish","newVersionsRelations","versionRelations","shouldPropagateRelation","relationsToPropagate","relation","discardToAdd","filter","matchingOldVersion","find","oldRelation","value","omit","identifiers","ID_COLUMN","sync","oldEntries","newEntries","oldRelations","newEntryByLocale","keyBy","oldEntriesMap","reduce","acc","newEntry","locale","String","column","newRelations","newId","batchSize","dialect","getBatchInsertSize","batchInsert"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"unidirectional-relations.js","sources":["../../../../src/services/document-service/utils/unidirectional-relations.ts"],"sourcesContent":["/* eslint-disable no-continue */\nimport { keyBy, omit } from 'lodash';\n\nimport type { Data, UID, Schema } from '@strapi/types';\n\nimport type { JoinTable } from '@strapi/database';\n\ninterface LoadContext {\n oldVersions: { id: Data.ID; locale: string }[];\n newVersions: { id: Data.ID; locale: string }[];\n}\n\ninterface RelationUpdate {\n joinTable: JoinTable;\n relations: Record<string, any>[];\n}\n\ninterface RelationFilterOptions {\n /**\n * Function returning which relations should be propagated to new document versions\n * This replaces the hardcoded component-specific logic\n *\n * It receives all the relations of a join table at once, so it can batch its lookups\n */\n filterRelationsToPropagate?: (\n relations: Record<string, any>[],\n model: Schema.Component | Schema.ContentType,\n trx: any\n ) => Promise<Record<string, any>[]>;\n}\n\n/**\n * Loads lingering relations that need to be updated when overriding a published or draft entry.\n * This is necessary because the relations are uni-directional and the target entry is not aware of the source entry.\n * This is not the case for bi-directional relations, where the target entry is also linked to the source entry.\n */\nconst load = async (\n uid: UID.ContentType,\n { oldVersions, newVersions }: LoadContext,\n options: RelationFilterOptions = {}\n): Promise<RelationUpdate[]> => {\n const updates: RelationUpdate[] = [];\n\n // Iterate all components and content types to find relations that need to be updated\n await strapi.db.transaction(async ({ trx }) => {\n const contentTypes = Object.values(strapi.contentTypes) as Schema.ContentType[];\n const components = Object.values(strapi.components) as Schema.Component[];\n\n for (const model of [...contentTypes, ...components]) {\n const dbModel = strapi.db.metadata.get(model.uid);\n\n for (const attribute of Object.values(dbModel.attributes) as any) {\n /**\n * Only consider unidirectional relations.\n * Bidirectional relations (inversedBy/mappedBy) are handled by bidirectionalRelations.\n * Self-referential relations (model.uid === uid) are included here, but rows where\n * the source entry is also being republished in this same operation are excluded —\n * those are handled by selfReferentialRelations, which remaps both sides simultaneously.\n * Without this guard, we would insert rows pointing to a source entry that is about\n * to be deleted, creating stale foreign-key values.\n */\n if (\n attribute.type !== 'relation' ||\n attribute.target !== uid ||\n attribute.inversedBy ||\n attribute.mappedBy\n ) {\n continue;\n }\n\n // TODO: joinColumn relations\n const joinTable = attribute.joinTable;\n if (!joinTable) {\n continue;\n }\n\n const { name: sourceColumnName } = joinTable.joinColumn;\n const { name: targetColumnName } = joinTable.inverseJoinColumn;\n\n /**\n * Load all relations that need to be updated\n */\n // NOTE: when the model has draft and publish, we can assume relation are only draft to draft & published to published\n const ids = oldVersions.map((entry) => entry.id);\n\n let oldVersionsQuery = strapi.db\n .getConnection()\n .select('*')\n .from(joinTable.name)\n .whereIn(targetColumnName, ids);\n\n /**\n * For self-referential relations, exclude join rows where the source entry is also\n * being republished. When both sides are in the same publish batch, selfReferentialRelations\n * already handles remapping them. Including them here too would insert a row whose\n * source FK points at an entry that is about to be deleted.\n */\n if (model.uid === uid && ids.length > 0) {\n oldVersionsQuery = oldVersionsQuery.whereNotIn(sourceColumnName, ids);\n }\n\n const oldVersionsRelations = await oldVersionsQuery.transacting(trx);\n\n if (oldVersionsRelations.length > 0) {\n updates.push({ joinTable, relations: oldVersionsRelations });\n }\n\n /**\n * if publishing\n * if published version exists\n * updated published versions links\n * else\n * create link to newly published version\n *\n * if discarding\n * if published version link exists & not draft version link\n * create link to new draft version\n */\n if (!model.options?.draftAndPublish) {\n const ids = newVersions.map((entry) => entry.id);\n\n // This is the step were we query the join table based on the id of the document\n const newVersionsRelations = await strapi.db\n .getConnection()\n .select('*')\n .from(joinTable.name)\n .whereIn(targetColumnName, ids)\n .transacting(trx);\n\n const versionRelations = options.filterRelationsToPropagate\n ? await options.filterRelationsToPropagate(newVersionsRelations, model, trx)\n : newVersionsRelations;\n\n if (versionRelations.length > 0) {\n // when publishing a draft that doesn't have a published version yet,\n // copy the links to the draft over to the published version\n // when discarding a published version, if no drafts exists\n const discardToAdd = versionRelations\n .filter((relation) => {\n const matchingOldVersion = oldVersionsRelations.find((oldRelation) => {\n return oldRelation[sourceColumnName] === relation[sourceColumnName];\n });\n\n return !matchingOldVersion;\n })\n .map((value) => omit(value, strapi.db.metadata.identifiers.ID_COLUMN));\n\n updates.push({ joinTable, relations: discardToAdd });\n }\n }\n }\n }\n });\n\n return updates;\n};\n\n/**\n * Updates uni directional relations to target the right entries when overriding published or draft entries.\n *\n * This function:\n * 1. Creates new relations pointing to the new entry versions\n * 2. Precisely deletes only the old relations being replaced to prevent orphaned links\n *\n * @param oldEntries The old entries that are being overridden\n * @param newEntries The new entries that are overriding the old ones\n * @param oldRelations The relations that were previously loaded with `load` @see load\n */\nconst sync = async (\n oldEntries: { id: Data.ID; locale: string }[],\n newEntries: { id: Data.ID; locale: string }[],\n oldRelations: { joinTable: any; relations: any[] }[]\n) => {\n /**\n * Create a map of old entry ids to new entry ids\n *\n * Will be used to update the relation target ids\n */\n const newEntryByLocale = keyBy(newEntries, 'locale');\n const oldEntriesMap = oldEntries.reduce(\n (acc, entry) => {\n const newEntry = newEntryByLocale[entry.locale];\n if (!newEntry) return acc;\n acc[String(entry.id)] = newEntry.id;\n return acc;\n },\n {} as Record<string, Data.ID>\n );\n\n await strapi.db.transaction(async ({ trx }) => {\n // Iterate old relations that are deleted and insert the new ones\n for (const { joinTable, relations } of oldRelations) {\n // Update old ids with the new ones\n const column = joinTable.inverseJoinColumn.name;\n\n const newRelations = relations.map((relation) => {\n const newId = oldEntriesMap[relation[column]];\n return { ...relation, [column]: newId };\n });\n\n const batchSize = strapi.db.dialect.getBatchInsertSize();\n await trx.batchInsert(joinTable.name, newRelations, batchSize);\n }\n });\n};\n\nexport { load, sync };\nexport type { RelationFilterOptions };\n"],"names":["load","uid","oldVersions","newVersions","options","updates","strapi","db","transaction","trx","contentTypes","Object","values","components","model","dbModel","metadata","get","attribute","attributes","type","target","inversedBy","mappedBy","joinTable","name","sourceColumnName","joinColumn","targetColumnName","inverseJoinColumn","ids","map","entry","id","oldVersionsQuery","getConnection","select","from","whereIn","length","whereNotIn","oldVersionsRelations","transacting","push","relations","draftAndPublish","newVersionsRelations","versionRelations","filterRelationsToPropagate","discardToAdd","filter","relation","matchingOldVersion","find","oldRelation","value","omit","identifiers","ID_COLUMN","sync","oldEntries","newEntries","oldRelations","newEntryByLocale","keyBy","oldEntriesMap","reduce","acc","newEntry","locale","String","column","newRelations","newId","batchSize","dialect","getBatchInsertSize","batchInsert"],"mappings":"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"}
|
|
@@ -68,16 +68,7 @@ import { omit, keyBy } from 'lodash';
|
|
|
68
68
|
const ids = newVersions.map((entry)=>entry.id);
|
|
69
69
|
// This is the step were we query the join table based on the id of the document
|
|
70
70
|
const newVersionsRelations = await strapi.db.getConnection().select('*').from(joinTable.name).whereIn(targetColumnName, ids).transacting(trx);
|
|
71
|
-
|
|
72
|
-
if (options.shouldPropagateRelation) {
|
|
73
|
-
const relationsToPropagate = [];
|
|
74
|
-
for (const relation of newVersionsRelations){
|
|
75
|
-
if (await options.shouldPropagateRelation(relation, model, trx)) {
|
|
76
|
-
relationsToPropagate.push(relation);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
versionRelations = relationsToPropagate;
|
|
80
|
-
}
|
|
71
|
+
const versionRelations = options.filterRelationsToPropagate ? await options.filterRelationsToPropagate(newVersionsRelations, model, trx) : newVersionsRelations;
|
|
81
72
|
if (versionRelations.length > 0) {
|
|
82
73
|
// when publishing a draft that doesn't have a published version yet,
|
|
83
74
|
// copy the links to the draft over to the published version
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unidirectional-relations.mjs","sources":["../../../../src/services/document-service/utils/unidirectional-relations.ts"],"sourcesContent":["/* eslint-disable no-continue */\nimport { keyBy, omit } from 'lodash';\n\nimport type { Data, UID, Schema } from '@strapi/types';\n\nimport type { JoinTable } from '@strapi/database';\n\ninterface LoadContext {\n oldVersions: { id: Data.ID; locale: string }[];\n newVersions: { id: Data.ID; locale: string }[];\n}\n\ninterface RelationUpdate {\n joinTable: JoinTable;\n relations: Record<string, any>[];\n}\n\ninterface RelationFilterOptions {\n /**\n * Function to determine if a relation should be propagated to new document versions\n * This replaces the hardcoded component-specific logic\n */\n shouldPropagateRelation?: (\n relation: Record<string, any>,\n model: Schema.Component | Schema.ContentType,\n trx: any\n ) => Promise<boolean>;\n}\n\n/**\n * Loads lingering relations that need to be updated when overriding a published or draft entry.\n * This is necessary because the relations are uni-directional and the target entry is not aware of the source entry.\n * This is not the case for bi-directional relations, where the target entry is also linked to the source entry.\n */\nconst load = async (\n uid: UID.ContentType,\n { oldVersions, newVersions }: LoadContext,\n options: RelationFilterOptions = {}\n): Promise<RelationUpdate[]> => {\n const updates: RelationUpdate[] = [];\n\n // Iterate all components and content types to find relations that need to be updated\n await strapi.db.transaction(async ({ trx }) => {\n const contentTypes = Object.values(strapi.contentTypes) as Schema.ContentType[];\n const components = Object.values(strapi.components) as Schema.Component[];\n\n for (const model of [...contentTypes, ...components]) {\n const dbModel = strapi.db.metadata.get(model.uid);\n\n for (const attribute of Object.values(dbModel.attributes) as any) {\n /**\n * Only consider unidirectional relations.\n * Bidirectional relations (inversedBy/mappedBy) are handled by bidirectionalRelations.\n * Self-referential relations (model.uid === uid) are included here, but rows where\n * the source entry is also being republished in this same operation are excluded —\n * those are handled by selfReferentialRelations, which remaps both sides simultaneously.\n * Without this guard, we would insert rows pointing to a source entry that is about\n * to be deleted, creating stale foreign-key values.\n */\n if (\n attribute.type !== 'relation' ||\n attribute.target !== uid ||\n attribute.inversedBy ||\n attribute.mappedBy\n ) {\n continue;\n }\n\n // TODO: joinColumn relations\n const joinTable = attribute.joinTable;\n if (!joinTable) {\n continue;\n }\n\n const { name: sourceColumnName } = joinTable.joinColumn;\n const { name: targetColumnName } = joinTable.inverseJoinColumn;\n\n /**\n * Load all relations that need to be updated\n */\n // NOTE: when the model has draft and publish, we can assume relation are only draft to draft & published to published\n const ids = oldVersions.map((entry) => entry.id);\n\n let oldVersionsQuery = strapi.db\n .getConnection()\n .select('*')\n .from(joinTable.name)\n .whereIn(targetColumnName, ids);\n\n /**\n * For self-referential relations, exclude join rows where the source entry is also\n * being republished. When both sides are in the same publish batch, selfReferentialRelations\n * already handles remapping them. Including them here too would insert a row whose\n * source FK points at an entry that is about to be deleted.\n */\n if (model.uid === uid && ids.length > 0) {\n oldVersionsQuery = oldVersionsQuery.whereNotIn(sourceColumnName, ids);\n }\n\n const oldVersionsRelations = await oldVersionsQuery.transacting(trx);\n\n if (oldVersionsRelations.length > 0) {\n updates.push({ joinTable, relations: oldVersionsRelations });\n }\n\n /**\n * if publishing\n * if published version exists\n * updated published versions links\n * else\n * create link to newly published version\n *\n * if discarding\n * if published version link exists & not draft version link\n * create link to new draft version\n */\n if (!model.options?.draftAndPublish) {\n const ids = newVersions.map((entry) => entry.id);\n\n // This is the step were we query the join table based on the id of the document\n const newVersionsRelations = await strapi.db\n .getConnection()\n .select('*')\n .from(joinTable.name)\n .whereIn(targetColumnName, ids)\n .transacting(trx);\n\n let versionRelations = newVersionsRelations;\n if (options.shouldPropagateRelation) {\n const relationsToPropagate = [];\n for (const relation of newVersionsRelations) {\n if (await options.shouldPropagateRelation(relation, model, trx)) {\n relationsToPropagate.push(relation);\n }\n }\n versionRelations = relationsToPropagate;\n }\n\n if (versionRelations.length > 0) {\n // when publishing a draft that doesn't have a published version yet,\n // copy the links to the draft over to the published version\n // when discarding a published version, if no drafts exists\n const discardToAdd = versionRelations\n .filter((relation) => {\n const matchingOldVersion = oldVersionsRelations.find((oldRelation) => {\n return oldRelation[sourceColumnName] === relation[sourceColumnName];\n });\n\n return !matchingOldVersion;\n })\n .map((value) => omit(value, strapi.db.metadata.identifiers.ID_COLUMN));\n\n updates.push({ joinTable, relations: discardToAdd });\n }\n }\n }\n }\n });\n\n return updates;\n};\n\n/**\n * Updates uni directional relations to target the right entries when overriding published or draft entries.\n *\n * This function:\n * 1. Creates new relations pointing to the new entry versions\n * 2. Precisely deletes only the old relations being replaced to prevent orphaned links\n *\n * @param oldEntries The old entries that are being overridden\n * @param newEntries The new entries that are overriding the old ones\n * @param oldRelations The relations that were previously loaded with `load` @see load\n */\nconst sync = async (\n oldEntries: { id: Data.ID; locale: string }[],\n newEntries: { id: Data.ID; locale: string }[],\n oldRelations: { joinTable: any; relations: any[] }[]\n) => {\n /**\n * Create a map of old entry ids to new entry ids\n *\n * Will be used to update the relation target ids\n */\n const newEntryByLocale = keyBy(newEntries, 'locale');\n const oldEntriesMap = oldEntries.reduce(\n (acc, entry) => {\n const newEntry = newEntryByLocale[entry.locale];\n if (!newEntry) return acc;\n acc[String(entry.id)] = newEntry.id;\n return acc;\n },\n {} as Record<string, Data.ID>\n );\n\n await strapi.db.transaction(async ({ trx }) => {\n // Iterate old relations that are deleted and insert the new ones\n for (const { joinTable, relations } of oldRelations) {\n // Update old ids with the new ones\n const column = joinTable.inverseJoinColumn.name;\n\n const newRelations = relations.map((relation) => {\n const newId = oldEntriesMap[relation[column]];\n return { ...relation, [column]: newId };\n });\n\n const batchSize = strapi.db.dialect.getBatchInsertSize();\n await trx.batchInsert(joinTable.name, newRelations, batchSize);\n }\n });\n};\n\nexport { load, sync };\nexport type { RelationFilterOptions };\n"],"names":["load","uid","oldVersions","newVersions","options","updates","strapi","db","transaction","trx","contentTypes","Object","values","components","model","dbModel","metadata","get","attribute","attributes","type","target","inversedBy","mappedBy","joinTable","name","sourceColumnName","joinColumn","targetColumnName","inverseJoinColumn","ids","map","entry","id","oldVersionsQuery","getConnection","select","from","whereIn","length","whereNotIn","oldVersionsRelations","transacting","push","relations","draftAndPublish","newVersionsRelations","versionRelations","shouldPropagateRelation","relationsToPropagate","relation","discardToAdd","filter","matchingOldVersion","find","oldRelation","value","omit","identifiers","ID_COLUMN","sync","oldEntries","newEntries","oldRelations","newEntryByLocale","keyBy","oldEntriesMap","reduce","acc","newEntry","locale","String","column","newRelations","newId","batchSize","dialect","getBatchInsertSize","batchInsert"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"unidirectional-relations.mjs","sources":["../../../../src/services/document-service/utils/unidirectional-relations.ts"],"sourcesContent":["/* eslint-disable no-continue */\nimport { keyBy, omit } from 'lodash';\n\nimport type { Data, UID, Schema } from '@strapi/types';\n\nimport type { JoinTable } from '@strapi/database';\n\ninterface LoadContext {\n oldVersions: { id: Data.ID; locale: string }[];\n newVersions: { id: Data.ID; locale: string }[];\n}\n\ninterface RelationUpdate {\n joinTable: JoinTable;\n relations: Record<string, any>[];\n}\n\ninterface RelationFilterOptions {\n /**\n * Function returning which relations should be propagated to new document versions\n * This replaces the hardcoded component-specific logic\n *\n * It receives all the relations of a join table at once, so it can batch its lookups\n */\n filterRelationsToPropagate?: (\n relations: Record<string, any>[],\n model: Schema.Component | Schema.ContentType,\n trx: any\n ) => Promise<Record<string, any>[]>;\n}\n\n/**\n * Loads lingering relations that need to be updated when overriding a published or draft entry.\n * This is necessary because the relations are uni-directional and the target entry is not aware of the source entry.\n * This is not the case for bi-directional relations, where the target entry is also linked to the source entry.\n */\nconst load = async (\n uid: UID.ContentType,\n { oldVersions, newVersions }: LoadContext,\n options: RelationFilterOptions = {}\n): Promise<RelationUpdate[]> => {\n const updates: RelationUpdate[] = [];\n\n // Iterate all components and content types to find relations that need to be updated\n await strapi.db.transaction(async ({ trx }) => {\n const contentTypes = Object.values(strapi.contentTypes) as Schema.ContentType[];\n const components = Object.values(strapi.components) as Schema.Component[];\n\n for (const model of [...contentTypes, ...components]) {\n const dbModel = strapi.db.metadata.get(model.uid);\n\n for (const attribute of Object.values(dbModel.attributes) as any) {\n /**\n * Only consider unidirectional relations.\n * Bidirectional relations (inversedBy/mappedBy) are handled by bidirectionalRelations.\n * Self-referential relations (model.uid === uid) are included here, but rows where\n * the source entry is also being republished in this same operation are excluded —\n * those are handled by selfReferentialRelations, which remaps both sides simultaneously.\n * Without this guard, we would insert rows pointing to a source entry that is about\n * to be deleted, creating stale foreign-key values.\n */\n if (\n attribute.type !== 'relation' ||\n attribute.target !== uid ||\n attribute.inversedBy ||\n attribute.mappedBy\n ) {\n continue;\n }\n\n // TODO: joinColumn relations\n const joinTable = attribute.joinTable;\n if (!joinTable) {\n continue;\n }\n\n const { name: sourceColumnName } = joinTable.joinColumn;\n const { name: targetColumnName } = joinTable.inverseJoinColumn;\n\n /**\n * Load all relations that need to be updated\n */\n // NOTE: when the model has draft and publish, we can assume relation are only draft to draft & published to published\n const ids = oldVersions.map((entry) => entry.id);\n\n let oldVersionsQuery = strapi.db\n .getConnection()\n .select('*')\n .from(joinTable.name)\n .whereIn(targetColumnName, ids);\n\n /**\n * For self-referential relations, exclude join rows where the source entry is also\n * being republished. When both sides are in the same publish batch, selfReferentialRelations\n * already handles remapping them. Including them here too would insert a row whose\n * source FK points at an entry that is about to be deleted.\n */\n if (model.uid === uid && ids.length > 0) {\n oldVersionsQuery = oldVersionsQuery.whereNotIn(sourceColumnName, ids);\n }\n\n const oldVersionsRelations = await oldVersionsQuery.transacting(trx);\n\n if (oldVersionsRelations.length > 0) {\n updates.push({ joinTable, relations: oldVersionsRelations });\n }\n\n /**\n * if publishing\n * if published version exists\n * updated published versions links\n * else\n * create link to newly published version\n *\n * if discarding\n * if published version link exists & not draft version link\n * create link to new draft version\n */\n if (!model.options?.draftAndPublish) {\n const ids = newVersions.map((entry) => entry.id);\n\n // This is the step were we query the join table based on the id of the document\n const newVersionsRelations = await strapi.db\n .getConnection()\n .select('*')\n .from(joinTable.name)\n .whereIn(targetColumnName, ids)\n .transacting(trx);\n\n const versionRelations = options.filterRelationsToPropagate\n ? await options.filterRelationsToPropagate(newVersionsRelations, model, trx)\n : newVersionsRelations;\n\n if (versionRelations.length > 0) {\n // when publishing a draft that doesn't have a published version yet,\n // copy the links to the draft over to the published version\n // when discarding a published version, if no drafts exists\n const discardToAdd = versionRelations\n .filter((relation) => {\n const matchingOldVersion = oldVersionsRelations.find((oldRelation) => {\n return oldRelation[sourceColumnName] === relation[sourceColumnName];\n });\n\n return !matchingOldVersion;\n })\n .map((value) => omit(value, strapi.db.metadata.identifiers.ID_COLUMN));\n\n updates.push({ joinTable, relations: discardToAdd });\n }\n }\n }\n }\n });\n\n return updates;\n};\n\n/**\n * Updates uni directional relations to target the right entries when overriding published or draft entries.\n *\n * This function:\n * 1. Creates new relations pointing to the new entry versions\n * 2. Precisely deletes only the old relations being replaced to prevent orphaned links\n *\n * @param oldEntries The old entries that are being overridden\n * @param newEntries The new entries that are overriding the old ones\n * @param oldRelations The relations that were previously loaded with `load` @see load\n */\nconst sync = async (\n oldEntries: { id: Data.ID; locale: string }[],\n newEntries: { id: Data.ID; locale: string }[],\n oldRelations: { joinTable: any; relations: any[] }[]\n) => {\n /**\n * Create a map of old entry ids to new entry ids\n *\n * Will be used to update the relation target ids\n */\n const newEntryByLocale = keyBy(newEntries, 'locale');\n const oldEntriesMap = oldEntries.reduce(\n (acc, entry) => {\n const newEntry = newEntryByLocale[entry.locale];\n if (!newEntry) return acc;\n acc[String(entry.id)] = newEntry.id;\n return acc;\n },\n {} as Record<string, Data.ID>\n );\n\n await strapi.db.transaction(async ({ trx }) => {\n // Iterate old relations that are deleted and insert the new ones\n for (const { joinTable, relations } of oldRelations) {\n // Update old ids with the new ones\n const column = joinTable.inverseJoinColumn.name;\n\n const newRelations = relations.map((relation) => {\n const newId = oldEntriesMap[relation[column]];\n return { ...relation, [column]: newId };\n });\n\n const batchSize = strapi.db.dialect.getBatchInsertSize();\n await trx.batchInsert(joinTable.name, newRelations, batchSize);\n }\n });\n};\n\nexport { load, sync };\nexport type { RelationFilterOptions };\n"],"names":["load","uid","oldVersions","newVersions","options","updates","strapi","db","transaction","trx","contentTypes","Object","values","components","model","dbModel","metadata","get","attribute","attributes","type","target","inversedBy","mappedBy","joinTable","name","sourceColumnName","joinColumn","targetColumnName","inverseJoinColumn","ids","map","entry","id","oldVersionsQuery","getConnection","select","from","whereIn","length","whereNotIn","oldVersionsRelations","transacting","push","relations","draftAndPublish","newVersionsRelations","versionRelations","filterRelationsToPropagate","discardToAdd","filter","relation","matchingOldVersion","find","oldRelation","value","omit","identifiers","ID_COLUMN","sync","oldEntries","newEntries","oldRelations","newEntryByLocale","keyBy","oldEntriesMap","reduce","acc","newEntry","locale","String","column","newRelations","newId","batchSize","dialect","getBatchInsertSize","batchInsert"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-hub.d.ts","sourceRoot":"","sources":["../../src/services/event-hub.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAC9E,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,IAAI,CAAC;IAC9C,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1C,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IACtD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IACxD,OAAO,IAAI,QAAQ,CAAC;IACpB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5D,kBAAkB,IAAI,QAAQ,CAAC;IAC/B,oBAAoB,IAAI,QAAQ,CAAC;IACjC,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;CAChE;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,IAAI,QAAQ,
|
|
1
|
+
{"version":3,"file":"event-hub.d.ts","sourceRoot":"","sources":["../../src/services/event-hub.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAC9E,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,IAAI,CAAC;IAC9C,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1C,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IACtD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IACjD,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;IACxD,OAAO,IAAI,QAAQ,CAAC;IACpB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5D,kBAAkB,IAAI,QAAQ,CAAC;IAC/B,oBAAoB,IAAI,QAAQ,CAAC;IACjC,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAC;CAChE;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,IAAI,QAAQ,CAkHjD"}
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
// Default subscriber to easily add listeners with the on() method
|
|
8
8
|
const defaultSubscriber = async (eventName, ...args)=>{
|
|
9
9
|
if (listeners.has(eventName)) {
|
|
10
|
-
|
|
10
|
+
// Iterate over a copy so that a listener removed during the emit (e.g. a once() listener)
|
|
11
|
+
// does not make the loop skip the next listener
|
|
12
|
+
for (const listener of listeners.get(eventName).slice()){
|
|
11
13
|
await listener(...args);
|
|
12
14
|
}
|
|
13
15
|
}
|
|
@@ -50,13 +52,29 @@
|
|
|
50
52
|
};
|
|
51
53
|
},
|
|
52
54
|
off (eventName, listener) {
|
|
53
|
-
listeners.get(eventName)
|
|
55
|
+
const eventListeners = listeners.get(eventName);
|
|
56
|
+
if (eventListeners === undefined) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const listenerIndex = eventListeners.indexOf(listener);
|
|
60
|
+
// Only remove the listener if it exists
|
|
61
|
+
if (listenerIndex >= 0) {
|
|
62
|
+
eventListeners.splice(listenerIndex, 1);
|
|
63
|
+
}
|
|
54
64
|
},
|
|
55
65
|
once (eventName, listener) {
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
let fired = false;
|
|
67
|
+
// Remove the registered wrapper, not the original listener which is not in the list
|
|
68
|
+
const wrapper = async (...args)=>{
|
|
69
|
+
// An emit that copied the listener list before the removal can still call the wrapper
|
|
70
|
+
if (fired === true) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
fired = true;
|
|
74
|
+
eventHub.off(eventName, wrapper);
|
|
58
75
|
return listener(...args);
|
|
59
|
-
}
|
|
76
|
+
};
|
|
77
|
+
return eventHub.on(eventName, wrapper);
|
|
60
78
|
},
|
|
61
79
|
destroy () {
|
|
62
80
|
this.removeAllListeners();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-hub.js","sources":["../../src/services/event-hub.ts"],"sourcesContent":["export type Subscriber = (eventName: string, ...args: any[]) => Promise<void>;\nexport type Listener = (...args: any[]) => Promise<void>;\n\nexport interface EventHub {\n emit(eventName: string, ...args: unknown[]): Promise<void>;\n subscribe(subscriber: Subscriber): () => void;\n unsubscribe(subscriber: Subscriber): void;\n on(eventName: string, listener: Listener): () => void;\n off(eventName: string, listener: Listener): void;\n once(eventName: string, listener: Listener): () => void;\n destroy(): EventHub;\n removeListener(eventName: string, listener: Listener): void;\n removeAllListeners(): EventHub;\n removeAllSubscribers(): EventHub;\n addListener(eventName: string, listener: Listener): () => void;\n}\n\n/**\n * The event hub is Strapi's event control center.\n */\nexport default function createEventHub(): EventHub {\n const listeners = new Map();\n\n // Default subscriber to easily add listeners with the on() method\n const defaultSubscriber = async (eventName: string, ...args: unknown[]) => {\n if (listeners.has(eventName)) {\n for (const listener of listeners.get(eventName)) {\n await listener(...args);\n }\n }\n };\n\n // Store of subscribers that will be called when an event is emitted\n const subscribers = [defaultSubscriber];\n\n const eventHub: EventHub = {\n async emit(eventName, ...args) {\n for (const subscriber of subscribers) {\n await subscriber(eventName, ...args);\n }\n },\n\n subscribe(subscriber) {\n subscribers.push(subscriber);\n\n // Return a function to remove the subscriber\n return () => {\n eventHub.unsubscribe(subscriber);\n };\n },\n\n unsubscribe(subscriber) {\n const subscriberIndex = subscribers.indexOf(subscriber);\n\n // Only remove the subscriber if it exists\n if (subscriberIndex >= 0) {\n subscribers.splice(subscriberIndex, 1);\n }\n },\n\n on(eventName, listener) {\n if (!listeners.has(eventName)) {\n listeners.set(eventName, [listener]);\n } else {\n listeners.get(eventName).push(listener);\n }\n\n // Return a function to remove the listener\n return () => {\n eventHub.off(eventName, listener);\n };\n },\n\n off(eventName, listener) {\n listeners.get(eventName)
|
|
1
|
+
{"version":3,"file":"event-hub.js","sources":["../../src/services/event-hub.ts"],"sourcesContent":["export type Subscriber = (eventName: string, ...args: any[]) => Promise<void>;\nexport type Listener = (...args: any[]) => Promise<void>;\n\nexport interface EventHub {\n emit(eventName: string, ...args: unknown[]): Promise<void>;\n subscribe(subscriber: Subscriber): () => void;\n unsubscribe(subscriber: Subscriber): void;\n on(eventName: string, listener: Listener): () => void;\n off(eventName: string, listener: Listener): void;\n once(eventName: string, listener: Listener): () => void;\n destroy(): EventHub;\n removeListener(eventName: string, listener: Listener): void;\n removeAllListeners(): EventHub;\n removeAllSubscribers(): EventHub;\n addListener(eventName: string, listener: Listener): () => void;\n}\n\n/**\n * The event hub is Strapi's event control center.\n */\nexport default function createEventHub(): EventHub {\n const listeners = new Map();\n\n // Default subscriber to easily add listeners with the on() method\n const defaultSubscriber = async (eventName: string, ...args: unknown[]) => {\n if (listeners.has(eventName)) {\n // Iterate over a copy so that a listener removed during the emit (e.g. a once() listener)\n // does not make the loop skip the next listener\n for (const listener of listeners.get(eventName).slice()) {\n await listener(...args);\n }\n }\n };\n\n // Store of subscribers that will be called when an event is emitted\n const subscribers = [defaultSubscriber];\n\n const eventHub: EventHub = {\n async emit(eventName, ...args) {\n for (const subscriber of subscribers) {\n await subscriber(eventName, ...args);\n }\n },\n\n subscribe(subscriber) {\n subscribers.push(subscriber);\n\n // Return a function to remove the subscriber\n return () => {\n eventHub.unsubscribe(subscriber);\n };\n },\n\n unsubscribe(subscriber) {\n const subscriberIndex = subscribers.indexOf(subscriber);\n\n // Only remove the subscriber if it exists\n if (subscriberIndex >= 0) {\n subscribers.splice(subscriberIndex, 1);\n }\n },\n\n on(eventName, listener) {\n if (!listeners.has(eventName)) {\n listeners.set(eventName, [listener]);\n } else {\n listeners.get(eventName).push(listener);\n }\n\n // Return a function to remove the listener\n return () => {\n eventHub.off(eventName, listener);\n };\n },\n\n off(eventName, listener) {\n const eventListeners = listeners.get(eventName);\n\n if (eventListeners === undefined) {\n return;\n }\n\n const listenerIndex = eventListeners.indexOf(listener);\n\n // Only remove the listener if it exists\n if (listenerIndex >= 0) {\n eventListeners.splice(listenerIndex, 1);\n }\n },\n\n once(eventName, listener) {\n let fired = false;\n\n // Remove the registered wrapper, not the original listener which is not in the list\n const wrapper: Listener = async (...args) => {\n // An emit that copied the listener list before the removal can still call the wrapper\n if (fired === true) {\n return;\n }\n\n fired = true;\n eventHub.off(eventName, wrapper);\n return listener(...args);\n };\n\n return eventHub.on(eventName, wrapper);\n },\n\n destroy() {\n this.removeAllListeners();\n this.removeAllSubscribers();\n return this;\n },\n\n removeListener(eventName, listener) {\n return eventHub.off(eventName, listener);\n },\n\n removeAllListeners() {\n listeners.clear();\n return this;\n },\n\n removeAllSubscribers() {\n subscribers.length = 0;\n return this;\n },\n\n addListener(eventName, listener) {\n return eventHub.on(eventName, listener);\n },\n };\n\n return eventHub;\n}\n"],"names":["createEventHub","listeners","Map","defaultSubscriber","eventName","args","has","listener","get","slice","subscribers","eventHub","emit","subscriber","subscribe","push","unsubscribe","subscriberIndex","indexOf","splice","on","set","off","eventListeners","undefined","listenerIndex","once","fired","wrapper","destroy","removeAllListeners","removeAllSubscribers","removeListener","clear","length","addListener"],"mappings":"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"}
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
// Default subscriber to easily add listeners with the on() method
|
|
6
6
|
const defaultSubscriber = async (eventName, ...args)=>{
|
|
7
7
|
if (listeners.has(eventName)) {
|
|
8
|
-
|
|
8
|
+
// Iterate over a copy so that a listener removed during the emit (e.g. a once() listener)
|
|
9
|
+
// does not make the loop skip the next listener
|
|
10
|
+
for (const listener of listeners.get(eventName).slice()){
|
|
9
11
|
await listener(...args);
|
|
10
12
|
}
|
|
11
13
|
}
|
|
@@ -48,13 +50,29 @@
|
|
|
48
50
|
};
|
|
49
51
|
},
|
|
50
52
|
off (eventName, listener) {
|
|
51
|
-
listeners.get(eventName)
|
|
53
|
+
const eventListeners = listeners.get(eventName);
|
|
54
|
+
if (eventListeners === undefined) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const listenerIndex = eventListeners.indexOf(listener);
|
|
58
|
+
// Only remove the listener if it exists
|
|
59
|
+
if (listenerIndex >= 0) {
|
|
60
|
+
eventListeners.splice(listenerIndex, 1);
|
|
61
|
+
}
|
|
52
62
|
},
|
|
53
63
|
once (eventName, listener) {
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
let fired = false;
|
|
65
|
+
// Remove the registered wrapper, not the original listener which is not in the list
|
|
66
|
+
const wrapper = async (...args)=>{
|
|
67
|
+
// An emit that copied the listener list before the removal can still call the wrapper
|
|
68
|
+
if (fired === true) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
fired = true;
|
|
72
|
+
eventHub.off(eventName, wrapper);
|
|
56
73
|
return listener(...args);
|
|
57
|
-
}
|
|
74
|
+
};
|
|
75
|
+
return eventHub.on(eventName, wrapper);
|
|
58
76
|
},
|
|
59
77
|
destroy () {
|
|
60
78
|
this.removeAllListeners();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-hub.mjs","sources":["../../src/services/event-hub.ts"],"sourcesContent":["export type Subscriber = (eventName: string, ...args: any[]) => Promise<void>;\nexport type Listener = (...args: any[]) => Promise<void>;\n\nexport interface EventHub {\n emit(eventName: string, ...args: unknown[]): Promise<void>;\n subscribe(subscriber: Subscriber): () => void;\n unsubscribe(subscriber: Subscriber): void;\n on(eventName: string, listener: Listener): () => void;\n off(eventName: string, listener: Listener): void;\n once(eventName: string, listener: Listener): () => void;\n destroy(): EventHub;\n removeListener(eventName: string, listener: Listener): void;\n removeAllListeners(): EventHub;\n removeAllSubscribers(): EventHub;\n addListener(eventName: string, listener: Listener): () => void;\n}\n\n/**\n * The event hub is Strapi's event control center.\n */\nexport default function createEventHub(): EventHub {\n const listeners = new Map();\n\n // Default subscriber to easily add listeners with the on() method\n const defaultSubscriber = async (eventName: string, ...args: unknown[]) => {\n if (listeners.has(eventName)) {\n for (const listener of listeners.get(eventName)) {\n await listener(...args);\n }\n }\n };\n\n // Store of subscribers that will be called when an event is emitted\n const subscribers = [defaultSubscriber];\n\n const eventHub: EventHub = {\n async emit(eventName, ...args) {\n for (const subscriber of subscribers) {\n await subscriber(eventName, ...args);\n }\n },\n\n subscribe(subscriber) {\n subscribers.push(subscriber);\n\n // Return a function to remove the subscriber\n return () => {\n eventHub.unsubscribe(subscriber);\n };\n },\n\n unsubscribe(subscriber) {\n const subscriberIndex = subscribers.indexOf(subscriber);\n\n // Only remove the subscriber if it exists\n if (subscriberIndex >= 0) {\n subscribers.splice(subscriberIndex, 1);\n }\n },\n\n on(eventName, listener) {\n if (!listeners.has(eventName)) {\n listeners.set(eventName, [listener]);\n } else {\n listeners.get(eventName).push(listener);\n }\n\n // Return a function to remove the listener\n return () => {\n eventHub.off(eventName, listener);\n };\n },\n\n off(eventName, listener) {\n listeners.get(eventName)
|
|
1
|
+
{"version":3,"file":"event-hub.mjs","sources":["../../src/services/event-hub.ts"],"sourcesContent":["export type Subscriber = (eventName: string, ...args: any[]) => Promise<void>;\nexport type Listener = (...args: any[]) => Promise<void>;\n\nexport interface EventHub {\n emit(eventName: string, ...args: unknown[]): Promise<void>;\n subscribe(subscriber: Subscriber): () => void;\n unsubscribe(subscriber: Subscriber): void;\n on(eventName: string, listener: Listener): () => void;\n off(eventName: string, listener: Listener): void;\n once(eventName: string, listener: Listener): () => void;\n destroy(): EventHub;\n removeListener(eventName: string, listener: Listener): void;\n removeAllListeners(): EventHub;\n removeAllSubscribers(): EventHub;\n addListener(eventName: string, listener: Listener): () => void;\n}\n\n/**\n * The event hub is Strapi's event control center.\n */\nexport default function createEventHub(): EventHub {\n const listeners = new Map();\n\n // Default subscriber to easily add listeners with the on() method\n const defaultSubscriber = async (eventName: string, ...args: unknown[]) => {\n if (listeners.has(eventName)) {\n // Iterate over a copy so that a listener removed during the emit (e.g. a once() listener)\n // does not make the loop skip the next listener\n for (const listener of listeners.get(eventName).slice()) {\n await listener(...args);\n }\n }\n };\n\n // Store of subscribers that will be called when an event is emitted\n const subscribers = [defaultSubscriber];\n\n const eventHub: EventHub = {\n async emit(eventName, ...args) {\n for (const subscriber of subscribers) {\n await subscriber(eventName, ...args);\n }\n },\n\n subscribe(subscriber) {\n subscribers.push(subscriber);\n\n // Return a function to remove the subscriber\n return () => {\n eventHub.unsubscribe(subscriber);\n };\n },\n\n unsubscribe(subscriber) {\n const subscriberIndex = subscribers.indexOf(subscriber);\n\n // Only remove the subscriber if it exists\n if (subscriberIndex >= 0) {\n subscribers.splice(subscriberIndex, 1);\n }\n },\n\n on(eventName, listener) {\n if (!listeners.has(eventName)) {\n listeners.set(eventName, [listener]);\n } else {\n listeners.get(eventName).push(listener);\n }\n\n // Return a function to remove the listener\n return () => {\n eventHub.off(eventName, listener);\n };\n },\n\n off(eventName, listener) {\n const eventListeners = listeners.get(eventName);\n\n if (eventListeners === undefined) {\n return;\n }\n\n const listenerIndex = eventListeners.indexOf(listener);\n\n // Only remove the listener if it exists\n if (listenerIndex >= 0) {\n eventListeners.splice(listenerIndex, 1);\n }\n },\n\n once(eventName, listener) {\n let fired = false;\n\n // Remove the registered wrapper, not the original listener which is not in the list\n const wrapper: Listener = async (...args) => {\n // An emit that copied the listener list before the removal can still call the wrapper\n if (fired === true) {\n return;\n }\n\n fired = true;\n eventHub.off(eventName, wrapper);\n return listener(...args);\n };\n\n return eventHub.on(eventName, wrapper);\n },\n\n destroy() {\n this.removeAllListeners();\n this.removeAllSubscribers();\n return this;\n },\n\n removeListener(eventName, listener) {\n return eventHub.off(eventName, listener);\n },\n\n removeAllListeners() {\n listeners.clear();\n return this;\n },\n\n removeAllSubscribers() {\n subscribers.length = 0;\n return this;\n },\n\n addListener(eventName, listener) {\n return eventHub.on(eventName, listener);\n },\n };\n\n return eventHub;\n}\n"],"names":["createEventHub","listeners","Map","defaultSubscriber","eventName","args","has","listener","get","slice","subscribers","eventHub","emit","subscriber","subscribe","push","unsubscribe","subscriberIndex","indexOf","splice","on","set","off","eventListeners","undefined","listenerIndex","once","fired","wrapper","destroy","removeAllListeners","removeAllSubscribers","removeListener","clear","length","addListener"],"mappings":"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"}
|
|
@@ -48,9 +48,9 @@ const fromDBObject = (row)=>{
|
|
|
48
48
|
};
|
|
49
49
|
};
|
|
50
50
|
const webhookEventValidator = async (allowedEvents, events)=>{
|
|
51
|
-
const allowedValues = Array.from(allowedEvents.values());
|
|
51
|
+
const allowedValues = new Set(Array.from(allowedEvents.values()));
|
|
52
52
|
events.forEach((event)=>{
|
|
53
|
-
if (allowedValues.
|
|
53
|
+
if (allowedValues.has(event)) {
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
throw new ValidationError(`Webhook event ${event} is not supported`);
|