@strapi/core 0.0.0-next.2a1c7a281f8080296033541146ea489dcd441daa → 0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f
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/index.js +175 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -19
- package/dist/index.mjs.map +1 -1
- package/dist/services/document-service/repository.d.ts.map +1 -1
- package/dist/services/document-service/utils/bidirectional-relations.d.ts +95 -0
- package/dist/services/document-service/utils/bidirectional-relations.d.ts.map +1 -0
- package/package.json +13 -13
package/dist/index.js
CHANGED
@@ -312,7 +312,7 @@ const getDirs = ({ appDir, distDir }, config)=>({
|
|
312
312
|
});
|
313
313
|
|
314
314
|
var name = "@strapi/core";
|
315
|
-
var version = "0.0.0-next.
|
315
|
+
var version = "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f";
|
316
316
|
var description = "Core of Strapi";
|
317
317
|
var homepage = "https://strapi.io";
|
318
318
|
var bugs = {
|
@@ -368,14 +368,14 @@ var dependencies = {
|
|
368
368
|
"@koa/cors": "5.0.0",
|
369
369
|
"@koa/router": "12.0.2",
|
370
370
|
"@paralleldrive/cuid2": "2.2.2",
|
371
|
-
"@strapi/admin": "0.0.0-next.
|
372
|
-
"@strapi/database": "0.0.0-next.
|
373
|
-
"@strapi/generators": "0.0.0-next.
|
374
|
-
"@strapi/logger": "0.0.0-next.
|
375
|
-
"@strapi/permissions": "0.0.0-next.
|
376
|
-
"@strapi/types": "0.0.0-next.
|
377
|
-
"@strapi/typescript-utils": "0.0.0-next.
|
378
|
-
"@strapi/utils": "0.0.0-next.
|
371
|
+
"@strapi/admin": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
372
|
+
"@strapi/database": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
373
|
+
"@strapi/generators": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
374
|
+
"@strapi/logger": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
375
|
+
"@strapi/permissions": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
376
|
+
"@strapi/types": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
377
|
+
"@strapi/typescript-utils": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
378
|
+
"@strapi/utils": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
379
379
|
bcryptjs: "2.4.3",
|
380
380
|
boxen: "5.1.2",
|
381
381
|
chalk: "4.1.2",
|
@@ -394,7 +394,7 @@ var dependencies = {
|
|
394
394
|
"http-errors": "2.0.0",
|
395
395
|
inquirer: "8.2.5",
|
396
396
|
"is-docker": "2.2.1",
|
397
|
-
koa: "2.15.
|
397
|
+
koa: "2.15.4",
|
398
398
|
"koa-body": "6.0.1",
|
399
399
|
"koa-compose": "4.1.0",
|
400
400
|
"koa-compress": "5.1.1",
|
@@ -415,7 +415,7 @@ var dependencies = {
|
|
415
415
|
semver: "7.5.4",
|
416
416
|
statuses: "2.0.1",
|
417
417
|
typescript: "5.4.4",
|
418
|
-
undici: "6.
|
418
|
+
undici: "6.21.1",
|
419
419
|
yup: "0.32.9"
|
420
420
|
};
|
421
421
|
var devDependencies = {
|
@@ -438,9 +438,9 @@ var devDependencies = {
|
|
438
438
|
"@types/node": "18.19.24",
|
439
439
|
"@types/node-schedule": "2.1.7",
|
440
440
|
"@types/statuses": "2.0.1",
|
441
|
-
"eslint-config-custom": "0.0.0-next.
|
441
|
+
"eslint-config-custom": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
442
442
|
supertest: "6.3.3",
|
443
|
-
tsconfig: "0.0.0-next.
|
443
|
+
tsconfig: "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f"
|
444
444
|
};
|
445
445
|
var engines = {
|
446
446
|
node: ">=18.0.0 <=22.x.x",
|
@@ -6770,7 +6770,7 @@ const EVENTS = {
|
|
6770
6770
|
* Loads lingering relations that need to be updated when overriding a published or draft entry.
|
6771
6771
|
* This is necessary because the relations are uni-directional and the target entry is not aware of the source entry.
|
6772
6772
|
* This is not the case for bi-directional relations, where the target entry is also linked to the source entry.
|
6773
|
-
*/ const load = async (uid, { oldVersions, newVersions })=>{
|
6773
|
+
*/ const load$1 = async (uid, { oldVersions, newVersions })=>{
|
6774
6774
|
const updates = [];
|
6775
6775
|
// Iterate all components and content types to find relations that need to be updated
|
6776
6776
|
await strapi.db.transaction(async ({ trx })=>{
|
@@ -6845,7 +6845,7 @@ const EVENTS = {
|
|
6845
6845
|
* @param oldEntries The old entries that are being overridden
|
6846
6846
|
* @param newEntries The new entries that are overriding the old ones
|
6847
6847
|
* @param oldRelations The relations that were previously loaded with `load` @see load
|
6848
|
-
*/ const sync = async (oldEntries, newEntries, oldRelations)=>{
|
6848
|
+
*/ const sync$1 = async (oldEntries, newEntries, oldRelations)=>{
|
6849
6849
|
/**
|
6850
6850
|
* Create a map of old entry ids to new entry ids
|
6851
6851
|
*
|
@@ -6875,6 +6875,146 @@ const EVENTS = {
|
|
6875
6875
|
});
|
6876
6876
|
};
|
6877
6877
|
|
6878
|
+
/**
|
6879
|
+
* Loads all bidirectional relations that need to be synchronized when content entries change state
|
6880
|
+
* (e.g., during publish/unpublish operations).
|
6881
|
+
*
|
6882
|
+
* In Strapi, bidirectional relations allow maintaining order from both sides of the relation.
|
6883
|
+
* When an entry is published, the following occurs:
|
6884
|
+
*
|
6885
|
+
* 1. The old published entry is deleted
|
6886
|
+
* 2. A new entry is created with all its relations
|
6887
|
+
*
|
6888
|
+
* This process affects relation ordering in the following way:
|
6889
|
+
*
|
6890
|
+
* Initial state (Entry A related to X, Y, Z):
|
6891
|
+
* ```
|
6892
|
+
* Entry A (draft) Entry A (published)
|
6893
|
+
* │ │
|
6894
|
+
* ├──(1)→ X ├──(1)→ X
|
6895
|
+
* ├──(2)→ Y ├──(2)→ Y
|
6896
|
+
* └──(3)→ Z └──(3)→ Z
|
6897
|
+
*
|
6898
|
+
* X's perspective: Y's perspective: Z's perspective:
|
6899
|
+
* └──(2)→ Entry A └──(1)→ Entry A └──(3)→ Entry A
|
6900
|
+
* ```
|
6901
|
+
*
|
6902
|
+
* After publishing Entry A (without relation order sync):
|
6903
|
+
* ```
|
6904
|
+
* Entry A (draft) Entry A (new published)
|
6905
|
+
* │ │
|
6906
|
+
* ├──(1)→ X ├──(1)→ X
|
6907
|
+
* ├──(2)→ Y ├──(2)→ Y
|
6908
|
+
* └──(3)→ Z └──(3)→ Z
|
6909
|
+
*
|
6910
|
+
* X's perspective: Y's perspective: Z's perspective:
|
6911
|
+
* └──(3)→ Entry A └──(3)→ Entry A └──(3)→ Entry A
|
6912
|
+
* (all relations appear last in order)
|
6913
|
+
* ```
|
6914
|
+
*
|
6915
|
+
* This module preserves the original ordering from both perspectives by:
|
6916
|
+
* 1. Capturing the relation order before the entry state changes
|
6917
|
+
* 2. Restoring this order after the new relations are created
|
6918
|
+
*
|
6919
|
+
* @param uid - The unique identifier of the content type being processed
|
6920
|
+
* @param context - Object containing arrays of old and new entry versions
|
6921
|
+
* @returns Array of objects containing join table metadata and relations to be updated
|
6922
|
+
*/ const load = async (uid, { oldVersions })=>{
|
6923
|
+
const relationsToUpdate = [];
|
6924
|
+
await strapi.db.transaction(async ({ trx })=>{
|
6925
|
+
const contentTypes = Object.values(strapi.contentTypes);
|
6926
|
+
const components = Object.values(strapi.components);
|
6927
|
+
for (const model of [
|
6928
|
+
...contentTypes,
|
6929
|
+
...components
|
6930
|
+
]){
|
6931
|
+
const dbModel = strapi.db.metadata.get(model.uid);
|
6932
|
+
for (const attribute of Object.values(dbModel.attributes)){
|
6933
|
+
// Skip if not a bidirectional relation targeting our content type
|
6934
|
+
if (attribute.type !== 'relation' || attribute.target !== uid || !(attribute.inversedBy || attribute.mappedBy)) {
|
6935
|
+
continue;
|
6936
|
+
}
|
6937
|
+
// If it's a self referencing relation, there is no need to sync any relation
|
6938
|
+
// The order will already be handled as both sides are inside the same content type
|
6939
|
+
if (model.uid === uid) {
|
6940
|
+
continue;
|
6941
|
+
}
|
6942
|
+
const joinTable = attribute.joinTable;
|
6943
|
+
if (!joinTable) {
|
6944
|
+
continue;
|
6945
|
+
}
|
6946
|
+
const { name: targetColumnName } = joinTable.inverseJoinColumn;
|
6947
|
+
// Load all relations that need their order preserved
|
6948
|
+
const oldEntryIds = oldVersions.map((entry)=>entry.id);
|
6949
|
+
const existingRelations = await strapi.db.getConnection().select('*').from(joinTable.name).whereIn(targetColumnName, oldEntryIds).transacting(trx);
|
6950
|
+
if (existingRelations.length > 0) {
|
6951
|
+
relationsToUpdate.push({
|
6952
|
+
joinTable,
|
6953
|
+
relations: existingRelations
|
6954
|
+
});
|
6955
|
+
}
|
6956
|
+
}
|
6957
|
+
}
|
6958
|
+
});
|
6959
|
+
return relationsToUpdate;
|
6960
|
+
};
|
6961
|
+
/**
|
6962
|
+
* Synchronizes the order of bidirectional relations after content entries have changed state.
|
6963
|
+
*
|
6964
|
+
* When entries change state (e.g., draft → published), their IDs change and all relations are recreated.
|
6965
|
+
* While the order of relations from the entry's perspective is maintained (as they're created in order),
|
6966
|
+
* the inverse relations (from related entries' perspective) would all appear last in order since they're new.
|
6967
|
+
*
|
6968
|
+
* Example:
|
6969
|
+
* ```
|
6970
|
+
* Before publish:
|
6971
|
+
* Article(id:1) →(order:1)→ Category(id:5)
|
6972
|
+
* Category(id:5) →(order:3)→ Article(id:1)
|
6973
|
+
*
|
6974
|
+
* After publish (without sync):
|
6975
|
+
* Article(id:2) →(order:1)→ Category(id:5) [order preserved]
|
6976
|
+
* Category(id:5) →(order:99)→ Article(id:2) [order lost - appears last]
|
6977
|
+
*
|
6978
|
+
* After sync:
|
6979
|
+
* Article(id:2) →(order:1)→ Category(id:5) [order preserved]
|
6980
|
+
* Category(id:5) →(order:3)→ Article(id:2) [order restored]
|
6981
|
+
* ```
|
6982
|
+
*
|
6983
|
+
* @param oldEntries - Array of previous entry versions with their IDs and locales
|
6984
|
+
* @param newEntries - Array of new entry versions with their IDs and locales
|
6985
|
+
* @param existingRelations - Array of join table data containing the relations to be updated
|
6986
|
+
*/ const sync = async (oldEntries, newEntries, existingRelations)=>{
|
6987
|
+
// Group new entries by locale for easier lookup
|
6988
|
+
const newEntriesByLocale = fp.keyBy('locale', newEntries);
|
6989
|
+
// Create a mapping of old entry IDs to new entry IDs based on locale
|
6990
|
+
const entryIdMapping = oldEntries.reduce((acc, oldEntry)=>{
|
6991
|
+
const newEntry = newEntriesByLocale[oldEntry.locale];
|
6992
|
+
if (!newEntry) return acc;
|
6993
|
+
acc[oldEntry.id] = newEntry.id;
|
6994
|
+
return acc;
|
6995
|
+
}, {});
|
6996
|
+
await strapi.db.transaction(async ({ trx })=>{
|
6997
|
+
for (const { joinTable, relations } of existingRelations){
|
6998
|
+
const sourceColumn = joinTable.inverseJoinColumn.name;
|
6999
|
+
const targetColumn = joinTable.joinColumn.name;
|
7000
|
+
const orderColumn = joinTable.orderColumnName;
|
7001
|
+
// Failsafe in case those don't exist
|
7002
|
+
if (!sourceColumn || !targetColumn || !orderColumn) {
|
7003
|
+
continue;
|
7004
|
+
}
|
7005
|
+
// Update order values for each relation
|
7006
|
+
// TODO: Find a way to batch it more efficiently
|
7007
|
+
await strapiUtils.async.map(relations, (relation)=>{
|
7008
|
+
const { [sourceColumn]: oldSourceId, [targetColumn]: targetId, [orderColumn]: originalOrder } = relation;
|
7009
|
+
// Update the order column for the new relation entry
|
7010
|
+
return trx.from(joinTable.name).where(sourceColumn, entryIdMapping[oldSourceId]).where(targetColumn, targetId).update({
|
7011
|
+
[orderColumn]: originalOrder
|
7012
|
+
});
|
7013
|
+
});
|
7014
|
+
}
|
7015
|
+
});
|
7016
|
+
};
|
7017
|
+
|
6878
7018
|
const textNodeValidator = strapiUtils.yup.object().shape({
|
6879
7019
|
type: strapiUtils.yup.string().equals([
|
6880
7020
|
'text'
|
@@ -7903,7 +8043,11 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7903
8043
|
})
|
7904
8044
|
]);
|
7905
8045
|
// Load any unidirectional relation targetting the old published entries
|
7906
|
-
const relationsToSync = await load(uid, {
|
8046
|
+
const relationsToSync = await load$1(uid, {
|
8047
|
+
newVersions: draftsToPublish,
|
8048
|
+
oldVersions: oldPublishedVersions
|
8049
|
+
});
|
8050
|
+
const bidirectionalRelationsToSync = await load(uid, {
|
7907
8051
|
newVersions: draftsToPublish,
|
7908
8052
|
oldVersions: oldPublishedVersions
|
7909
8053
|
});
|
@@ -7912,10 +8056,14 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7912
8056
|
// Transform draft entry data and create published versions
|
7913
8057
|
const publishedEntries = await strapiUtils.async.map(draftsToPublish, (draft)=>entries.publish(draft, queryParams));
|
7914
8058
|
// Sync unidirectional relations with the new published entries
|
7915
|
-
await sync([
|
8059
|
+
await sync$1([
|
7916
8060
|
...oldPublishedVersions,
|
7917
8061
|
...draftsToPublish
|
7918
8062
|
], publishedEntries, relationsToSync);
|
8063
|
+
await sync([
|
8064
|
+
...oldPublishedVersions,
|
8065
|
+
...draftsToPublish
|
8066
|
+
], publishedEntries, bidirectionalRelationsToSync);
|
7919
8067
|
publishedEntries.forEach(emitEvent('entry.publish'));
|
7920
8068
|
return {
|
7921
8069
|
documentId,
|
@@ -7973,7 +8121,11 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7973
8121
|
})
|
7974
8122
|
]);
|
7975
8123
|
// Load any unidirectional relation targeting the old drafts
|
7976
|
-
const relationsToSync = await load(uid, {
|
8124
|
+
const relationsToSync = await load$1(uid, {
|
8125
|
+
newVersions: versionsToDraft,
|
8126
|
+
oldVersions: oldDrafts
|
8127
|
+
});
|
8128
|
+
const bidirectionalRelationsToSync = await load(uid, {
|
7977
8129
|
newVersions: versionsToDraft,
|
7978
8130
|
oldVersions: oldDrafts
|
7979
8131
|
});
|
@@ -7982,10 +8134,14 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7982
8134
|
// Transform published entry data and create draft versions
|
7983
8135
|
const draftEntries = await strapiUtils.async.map(versionsToDraft, (entry)=>entries.discardDraft(entry, queryParams));
|
7984
8136
|
// Sync unidirectional relations with the new draft entries
|
7985
|
-
await sync([
|
8137
|
+
await sync$1([
|
7986
8138
|
...oldDrafts,
|
7987
8139
|
...versionsToDraft
|
7988
8140
|
], draftEntries, relationsToSync);
|
8141
|
+
await sync([
|
8142
|
+
...oldDrafts,
|
8143
|
+
...versionsToDraft
|
8144
|
+
], draftEntries, bidirectionalRelationsToSync);
|
7989
8145
|
draftEntries.forEach(emitEvent('entry.draft-discard'));
|
7990
8146
|
return {
|
7991
8147
|
documentId,
|