@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.mjs
CHANGED
@@ -290,7 +290,7 @@ const getDirs = ({ appDir, distDir }, config)=>({
|
|
290
290
|
});
|
291
291
|
|
292
292
|
var name = "@strapi/core";
|
293
|
-
var version = "0.0.0-next.
|
293
|
+
var version = "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f";
|
294
294
|
var description = "Core of Strapi";
|
295
295
|
var homepage = "https://strapi.io";
|
296
296
|
var bugs = {
|
@@ -346,14 +346,14 @@ var dependencies = {
|
|
346
346
|
"@koa/cors": "5.0.0",
|
347
347
|
"@koa/router": "12.0.2",
|
348
348
|
"@paralleldrive/cuid2": "2.2.2",
|
349
|
-
"@strapi/admin": "0.0.0-next.
|
350
|
-
"@strapi/database": "0.0.0-next.
|
351
|
-
"@strapi/generators": "0.0.0-next.
|
352
|
-
"@strapi/logger": "0.0.0-next.
|
353
|
-
"@strapi/permissions": "0.0.0-next.
|
354
|
-
"@strapi/types": "0.0.0-next.
|
355
|
-
"@strapi/typescript-utils": "0.0.0-next.
|
356
|
-
"@strapi/utils": "0.0.0-next.
|
349
|
+
"@strapi/admin": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
350
|
+
"@strapi/database": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
351
|
+
"@strapi/generators": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
352
|
+
"@strapi/logger": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
353
|
+
"@strapi/permissions": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
354
|
+
"@strapi/types": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
355
|
+
"@strapi/typescript-utils": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
356
|
+
"@strapi/utils": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
357
357
|
bcryptjs: "2.4.3",
|
358
358
|
boxen: "5.1.2",
|
359
359
|
chalk: "4.1.2",
|
@@ -372,7 +372,7 @@ var dependencies = {
|
|
372
372
|
"http-errors": "2.0.0",
|
373
373
|
inquirer: "8.2.5",
|
374
374
|
"is-docker": "2.2.1",
|
375
|
-
koa: "2.15.
|
375
|
+
koa: "2.15.4",
|
376
376
|
"koa-body": "6.0.1",
|
377
377
|
"koa-compose": "4.1.0",
|
378
378
|
"koa-compress": "5.1.1",
|
@@ -393,7 +393,7 @@ var dependencies = {
|
|
393
393
|
semver: "7.5.4",
|
394
394
|
statuses: "2.0.1",
|
395
395
|
typescript: "5.4.4",
|
396
|
-
undici: "6.
|
396
|
+
undici: "6.21.1",
|
397
397
|
yup: "0.32.9"
|
398
398
|
};
|
399
399
|
var devDependencies = {
|
@@ -416,9 +416,9 @@ var devDependencies = {
|
|
416
416
|
"@types/node": "18.19.24",
|
417
417
|
"@types/node-schedule": "2.1.7",
|
418
418
|
"@types/statuses": "2.0.1",
|
419
|
-
"eslint-config-custom": "0.0.0-next.
|
419
|
+
"eslint-config-custom": "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f",
|
420
420
|
supertest: "6.3.3",
|
421
|
-
tsconfig: "0.0.0-next.
|
421
|
+
tsconfig: "0.0.0-next.2c83a77afa7fccd559dd22ce09acee5c216d070f"
|
422
422
|
};
|
423
423
|
var engines = {
|
424
424
|
node: ">=18.0.0 <=22.x.x",
|
@@ -6748,7 +6748,7 @@ const EVENTS = {
|
|
6748
6748
|
* Loads lingering relations that need to be updated when overriding a published or draft entry.
|
6749
6749
|
* This is necessary because the relations are uni-directional and the target entry is not aware of the source entry.
|
6750
6750
|
* This is not the case for bi-directional relations, where the target entry is also linked to the source entry.
|
6751
|
-
*/ const load = async (uid, { oldVersions, newVersions })=>{
|
6751
|
+
*/ const load$1 = async (uid, { oldVersions, newVersions })=>{
|
6752
6752
|
const updates = [];
|
6753
6753
|
// Iterate all components and content types to find relations that need to be updated
|
6754
6754
|
await strapi.db.transaction(async ({ trx })=>{
|
@@ -6823,7 +6823,7 @@ const EVENTS = {
|
|
6823
6823
|
* @param oldEntries The old entries that are being overridden
|
6824
6824
|
* @param newEntries The new entries that are overriding the old ones
|
6825
6825
|
* @param oldRelations The relations that were previously loaded with `load` @see load
|
6826
|
-
*/ const sync = async (oldEntries, newEntries, oldRelations)=>{
|
6826
|
+
*/ const sync$1 = async (oldEntries, newEntries, oldRelations)=>{
|
6827
6827
|
/**
|
6828
6828
|
* Create a map of old entry ids to new entry ids
|
6829
6829
|
*
|
@@ -6853,6 +6853,146 @@ const EVENTS = {
|
|
6853
6853
|
});
|
6854
6854
|
};
|
6855
6855
|
|
6856
|
+
/**
|
6857
|
+
* Loads all bidirectional relations that need to be synchronized when content entries change state
|
6858
|
+
* (e.g., during publish/unpublish operations).
|
6859
|
+
*
|
6860
|
+
* In Strapi, bidirectional relations allow maintaining order from both sides of the relation.
|
6861
|
+
* When an entry is published, the following occurs:
|
6862
|
+
*
|
6863
|
+
* 1. The old published entry is deleted
|
6864
|
+
* 2. A new entry is created with all its relations
|
6865
|
+
*
|
6866
|
+
* This process affects relation ordering in the following way:
|
6867
|
+
*
|
6868
|
+
* Initial state (Entry A related to X, Y, Z):
|
6869
|
+
* ```
|
6870
|
+
* Entry A (draft) Entry A (published)
|
6871
|
+
* │ │
|
6872
|
+
* ├──(1)→ X ├──(1)→ X
|
6873
|
+
* ├──(2)→ Y ├──(2)→ Y
|
6874
|
+
* └──(3)→ Z └──(3)→ Z
|
6875
|
+
*
|
6876
|
+
* X's perspective: Y's perspective: Z's perspective:
|
6877
|
+
* └──(2)→ Entry A └──(1)→ Entry A └──(3)→ Entry A
|
6878
|
+
* ```
|
6879
|
+
*
|
6880
|
+
* After publishing Entry A (without relation order sync):
|
6881
|
+
* ```
|
6882
|
+
* Entry A (draft) Entry A (new published)
|
6883
|
+
* │ │
|
6884
|
+
* ├──(1)→ X ├──(1)→ X
|
6885
|
+
* ├──(2)→ Y ├──(2)→ Y
|
6886
|
+
* └──(3)→ Z └──(3)→ Z
|
6887
|
+
*
|
6888
|
+
* X's perspective: Y's perspective: Z's perspective:
|
6889
|
+
* └──(3)→ Entry A └──(3)→ Entry A └──(3)→ Entry A
|
6890
|
+
* (all relations appear last in order)
|
6891
|
+
* ```
|
6892
|
+
*
|
6893
|
+
* This module preserves the original ordering from both perspectives by:
|
6894
|
+
* 1. Capturing the relation order before the entry state changes
|
6895
|
+
* 2. Restoring this order after the new relations are created
|
6896
|
+
*
|
6897
|
+
* @param uid - The unique identifier of the content type being processed
|
6898
|
+
* @param context - Object containing arrays of old and new entry versions
|
6899
|
+
* @returns Array of objects containing join table metadata and relations to be updated
|
6900
|
+
*/ const load = async (uid, { oldVersions })=>{
|
6901
|
+
const relationsToUpdate = [];
|
6902
|
+
await strapi.db.transaction(async ({ trx })=>{
|
6903
|
+
const contentTypes = Object.values(strapi.contentTypes);
|
6904
|
+
const components = Object.values(strapi.components);
|
6905
|
+
for (const model of [
|
6906
|
+
...contentTypes,
|
6907
|
+
...components
|
6908
|
+
]){
|
6909
|
+
const dbModel = strapi.db.metadata.get(model.uid);
|
6910
|
+
for (const attribute of Object.values(dbModel.attributes)){
|
6911
|
+
// Skip if not a bidirectional relation targeting our content type
|
6912
|
+
if (attribute.type !== 'relation' || attribute.target !== uid || !(attribute.inversedBy || attribute.mappedBy)) {
|
6913
|
+
continue;
|
6914
|
+
}
|
6915
|
+
// If it's a self referencing relation, there is no need to sync any relation
|
6916
|
+
// The order will already be handled as both sides are inside the same content type
|
6917
|
+
if (model.uid === uid) {
|
6918
|
+
continue;
|
6919
|
+
}
|
6920
|
+
const joinTable = attribute.joinTable;
|
6921
|
+
if (!joinTable) {
|
6922
|
+
continue;
|
6923
|
+
}
|
6924
|
+
const { name: targetColumnName } = joinTable.inverseJoinColumn;
|
6925
|
+
// Load all relations that need their order preserved
|
6926
|
+
const oldEntryIds = oldVersions.map((entry)=>entry.id);
|
6927
|
+
const existingRelations = await strapi.db.getConnection().select('*').from(joinTable.name).whereIn(targetColumnName, oldEntryIds).transacting(trx);
|
6928
|
+
if (existingRelations.length > 0) {
|
6929
|
+
relationsToUpdate.push({
|
6930
|
+
joinTable,
|
6931
|
+
relations: existingRelations
|
6932
|
+
});
|
6933
|
+
}
|
6934
|
+
}
|
6935
|
+
}
|
6936
|
+
});
|
6937
|
+
return relationsToUpdate;
|
6938
|
+
};
|
6939
|
+
/**
|
6940
|
+
* Synchronizes the order of bidirectional relations after content entries have changed state.
|
6941
|
+
*
|
6942
|
+
* When entries change state (e.g., draft → published), their IDs change and all relations are recreated.
|
6943
|
+
* While the order of relations from the entry's perspective is maintained (as they're created in order),
|
6944
|
+
* the inverse relations (from related entries' perspective) would all appear last in order since they're new.
|
6945
|
+
*
|
6946
|
+
* Example:
|
6947
|
+
* ```
|
6948
|
+
* Before publish:
|
6949
|
+
* Article(id:1) →(order:1)→ Category(id:5)
|
6950
|
+
* Category(id:5) →(order:3)→ Article(id:1)
|
6951
|
+
*
|
6952
|
+
* After publish (without sync):
|
6953
|
+
* Article(id:2) →(order:1)→ Category(id:5) [order preserved]
|
6954
|
+
* Category(id:5) →(order:99)→ Article(id:2) [order lost - appears last]
|
6955
|
+
*
|
6956
|
+
* After sync:
|
6957
|
+
* Article(id:2) →(order:1)→ Category(id:5) [order preserved]
|
6958
|
+
* Category(id:5) →(order:3)→ Article(id:2) [order restored]
|
6959
|
+
* ```
|
6960
|
+
*
|
6961
|
+
* @param oldEntries - Array of previous entry versions with their IDs and locales
|
6962
|
+
* @param newEntries - Array of new entry versions with their IDs and locales
|
6963
|
+
* @param existingRelations - Array of join table data containing the relations to be updated
|
6964
|
+
*/ const sync = async (oldEntries, newEntries, existingRelations)=>{
|
6965
|
+
// Group new entries by locale for easier lookup
|
6966
|
+
const newEntriesByLocale = keyBy('locale', newEntries);
|
6967
|
+
// Create a mapping of old entry IDs to new entry IDs based on locale
|
6968
|
+
const entryIdMapping = oldEntries.reduce((acc, oldEntry)=>{
|
6969
|
+
const newEntry = newEntriesByLocale[oldEntry.locale];
|
6970
|
+
if (!newEntry) return acc;
|
6971
|
+
acc[oldEntry.id] = newEntry.id;
|
6972
|
+
return acc;
|
6973
|
+
}, {});
|
6974
|
+
await strapi.db.transaction(async ({ trx })=>{
|
6975
|
+
for (const { joinTable, relations } of existingRelations){
|
6976
|
+
const sourceColumn = joinTable.inverseJoinColumn.name;
|
6977
|
+
const targetColumn = joinTable.joinColumn.name;
|
6978
|
+
const orderColumn = joinTable.orderColumnName;
|
6979
|
+
// Failsafe in case those don't exist
|
6980
|
+
if (!sourceColumn || !targetColumn || !orderColumn) {
|
6981
|
+
continue;
|
6982
|
+
}
|
6983
|
+
// Update order values for each relation
|
6984
|
+
// TODO: Find a way to batch it more efficiently
|
6985
|
+
await async.map(relations, (relation)=>{
|
6986
|
+
const { [sourceColumn]: oldSourceId, [targetColumn]: targetId, [orderColumn]: originalOrder } = relation;
|
6987
|
+
// Update the order column for the new relation entry
|
6988
|
+
return trx.from(joinTable.name).where(sourceColumn, entryIdMapping[oldSourceId]).where(targetColumn, targetId).update({
|
6989
|
+
[orderColumn]: originalOrder
|
6990
|
+
});
|
6991
|
+
});
|
6992
|
+
}
|
6993
|
+
});
|
6994
|
+
};
|
6995
|
+
|
6856
6996
|
const textNodeValidator = yup$1.object().shape({
|
6857
6997
|
type: yup$1.string().equals([
|
6858
6998
|
'text'
|
@@ -7881,7 +8021,11 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7881
8021
|
})
|
7882
8022
|
]);
|
7883
8023
|
// Load any unidirectional relation targetting the old published entries
|
7884
|
-
const relationsToSync = await load(uid, {
|
8024
|
+
const relationsToSync = await load$1(uid, {
|
8025
|
+
newVersions: draftsToPublish,
|
8026
|
+
oldVersions: oldPublishedVersions
|
8027
|
+
});
|
8028
|
+
const bidirectionalRelationsToSync = await load(uid, {
|
7885
8029
|
newVersions: draftsToPublish,
|
7886
8030
|
oldVersions: oldPublishedVersions
|
7887
8031
|
});
|
@@ -7890,10 +8034,14 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7890
8034
|
// Transform draft entry data and create published versions
|
7891
8035
|
const publishedEntries = await async.map(draftsToPublish, (draft)=>entries.publish(draft, queryParams));
|
7892
8036
|
// Sync unidirectional relations with the new published entries
|
7893
|
-
await sync([
|
8037
|
+
await sync$1([
|
7894
8038
|
...oldPublishedVersions,
|
7895
8039
|
...draftsToPublish
|
7896
8040
|
], publishedEntries, relationsToSync);
|
8041
|
+
await sync([
|
8042
|
+
...oldPublishedVersions,
|
8043
|
+
...draftsToPublish
|
8044
|
+
], publishedEntries, bidirectionalRelationsToSync);
|
7897
8045
|
publishedEntries.forEach(emitEvent('entry.publish'));
|
7898
8046
|
return {
|
7899
8047
|
documentId,
|
@@ -7951,7 +8099,11 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7951
8099
|
})
|
7952
8100
|
]);
|
7953
8101
|
// Load any unidirectional relation targeting the old drafts
|
7954
|
-
const relationsToSync = await load(uid, {
|
8102
|
+
const relationsToSync = await load$1(uid, {
|
8103
|
+
newVersions: versionsToDraft,
|
8104
|
+
oldVersions: oldDrafts
|
8105
|
+
});
|
8106
|
+
const bidirectionalRelationsToSync = await load(uid, {
|
7955
8107
|
newVersions: versionsToDraft,
|
7956
8108
|
oldVersions: oldDrafts
|
7957
8109
|
});
|
@@ -7960,10 +8112,14 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7960
8112
|
// Transform published entry data and create draft versions
|
7961
8113
|
const draftEntries = await async.map(versionsToDraft, (entry)=>entries.discardDraft(entry, queryParams));
|
7962
8114
|
// Sync unidirectional relations with the new draft entries
|
7963
|
-
await sync([
|
8115
|
+
await sync$1([
|
7964
8116
|
...oldDrafts,
|
7965
8117
|
...versionsToDraft
|
7966
8118
|
], draftEntries, relationsToSync);
|
8119
|
+
await sync([
|
8120
|
+
...oldDrafts,
|
8121
|
+
...versionsToDraft
|
8122
|
+
], draftEntries, bidirectionalRelationsToSync);
|
7967
8123
|
draftEntries.forEach(emitEvent('entry.draft-discard'));
|
7968
8124
|
return {
|
7969
8125
|
documentId,
|