@strapi/core 0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00 → 0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d
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/configuration/index.d.ts +1 -0
- package/dist/configuration/index.d.ts.map +1 -1
- package/dist/ee/license.d.ts.map +1 -1
- package/dist/index.js +174 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -24
- 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/dist/services/metrics/admin-user-hash.d.ts.map +1 -1
- package/dist/services/metrics/sender.d.ts.map +1 -1
- package/package.json +13 -13
package/dist/index.mjs
CHANGED
@@ -7,7 +7,7 @@ import { errors as errors$1, Database } from '@strapi/database';
|
|
7
7
|
import tsUtils from '@strapi/typescript-utils';
|
8
8
|
import os from 'os';
|
9
9
|
import dotenv from 'dotenv';
|
10
|
-
import strapiUtils, { strings, importDefault, env, errors, contentTypes, pagination,
|
10
|
+
import strapiUtils, { strings, importDefault, env, errors, contentTypes, pagination, installID, policy, yup as yup$1, async, traverseEntity, relations, traverse, sanitize, validate, hooks, queryParams, providerFactory } from '@strapi/utils';
|
11
11
|
import fs, { existsSync, statSync } from 'fs';
|
12
12
|
import { AsyncLocalStorage } from 'async_hooks';
|
13
13
|
import open from 'open';
|
@@ -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-experimental.
|
293
|
+
var version = "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d";
|
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-experimental.
|
350
|
-
"@strapi/database": "0.0.0-experimental.
|
351
|
-
"@strapi/generators": "0.0.0-experimental.
|
352
|
-
"@strapi/logger": "0.0.0-experimental.
|
353
|
-
"@strapi/permissions": "0.0.0-experimental.
|
354
|
-
"@strapi/types": "0.0.0-experimental.
|
355
|
-
"@strapi/typescript-utils": "0.0.0-experimental.
|
356
|
-
"@strapi/utils": "0.0.0-experimental.
|
349
|
+
"@strapi/admin": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
350
|
+
"@strapi/database": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
351
|
+
"@strapi/generators": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
352
|
+
"@strapi/logger": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
353
|
+
"@strapi/permissions": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
354
|
+
"@strapi/types": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
355
|
+
"@strapi/typescript-utils": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
356
|
+
"@strapi/utils": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
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-experimental.
|
419
|
+
"eslint-config-custom": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
|
420
420
|
supertest: "6.3.3",
|
421
|
-
tsconfig: "0.0.0-experimental.
|
421
|
+
tsconfig: "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d"
|
422
422
|
};
|
423
423
|
var engines = {
|
424
424
|
node: ">=18.0.0 <=22.x.x",
|
@@ -494,6 +494,7 @@ const loadConfiguration = (opts)=>{
|
|
494
494
|
autoReload,
|
495
495
|
environment: process.env.NODE_ENV,
|
496
496
|
uuid: _.get(pkgJSON, 'strapi.uuid'),
|
497
|
+
installId: _.get(pkgJSON, 'strapi.installId'),
|
497
498
|
packageJsonStrapi: _.omit(_.get(pkgJSON, 'strapi', {}), 'uuid'),
|
498
499
|
info: {
|
499
500
|
...pkgJSON,
|
@@ -1224,6 +1225,7 @@ const throwError = ()=>{
|
|
1224
1225
|
throw new LicenseCheckError('Could not proceed to the online validation of your license.', true);
|
1225
1226
|
};
|
1226
1227
|
const fetchLicense = async ({ strapi }, key, projectId)=>{
|
1228
|
+
const { installId: installIdFromPackageJson } = strapi.config;
|
1227
1229
|
const response = await strapi.fetch(`https://license.strapi.io/api/licenses/validate`, {
|
1228
1230
|
method: 'POST',
|
1229
1231
|
headers: {
|
@@ -1232,7 +1234,7 @@ const fetchLicense = async ({ strapi }, key, projectId)=>{
|
|
1232
1234
|
body: JSON.stringify({
|
1233
1235
|
key,
|
1234
1236
|
projectId,
|
1235
|
-
|
1237
|
+
installId: installID(projectId, installIdFromPackageJson)
|
1236
1238
|
})
|
1237
1239
|
}).catch(throwError);
|
1238
1240
|
const contentType = response.headers.get('Content-Type');
|
@@ -6748,7 +6750,7 @@ const EVENTS = {
|
|
6748
6750
|
* Loads lingering relations that need to be updated when overriding a published or draft entry.
|
6749
6751
|
* This is necessary because the relations are uni-directional and the target entry is not aware of the source entry.
|
6750
6752
|
* 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 })=>{
|
6753
|
+
*/ const load$1 = async (uid, { oldVersions, newVersions })=>{
|
6752
6754
|
const updates = [];
|
6753
6755
|
// Iterate all components and content types to find relations that need to be updated
|
6754
6756
|
await strapi.db.transaction(async ({ trx })=>{
|
@@ -6823,7 +6825,7 @@ const EVENTS = {
|
|
6823
6825
|
* @param oldEntries The old entries that are being overridden
|
6824
6826
|
* @param newEntries The new entries that are overriding the old ones
|
6825
6827
|
* @param oldRelations The relations that were previously loaded with `load` @see load
|
6826
|
-
*/ const sync = async (oldEntries, newEntries, oldRelations)=>{
|
6828
|
+
*/ const sync$1 = async (oldEntries, newEntries, oldRelations)=>{
|
6827
6829
|
/**
|
6828
6830
|
* Create a map of old entry ids to new entry ids
|
6829
6831
|
*
|
@@ -6853,6 +6855,137 @@ const EVENTS = {
|
|
6853
6855
|
});
|
6854
6856
|
};
|
6855
6857
|
|
6858
|
+
/**
|
6859
|
+
* Loads all bidirectional relations that need to be synchronized when content entries change state
|
6860
|
+
* (e.g., during publish/unpublish operations).
|
6861
|
+
*
|
6862
|
+
* In Strapi, bidirectional relations allow maintaining order from both sides of the relation.
|
6863
|
+
* When an entry is published, the following occurs:
|
6864
|
+
*
|
6865
|
+
* 1. The old published entry is deleted
|
6866
|
+
* 2. A new entry is created with all its relations
|
6867
|
+
*
|
6868
|
+
* This process affects relation ordering in the following way:
|
6869
|
+
*
|
6870
|
+
* Initial state (Entry A related to X, Y, Z):
|
6871
|
+
* ```
|
6872
|
+
* Entry A (draft) Entry A (published)
|
6873
|
+
* │ │
|
6874
|
+
* ├──(1)→ X ├──(1)→ X
|
6875
|
+
* ├──(2)→ Y ├──(2)→ Y
|
6876
|
+
* └──(3)→ Z └──(3)→ Z
|
6877
|
+
*
|
6878
|
+
* X's perspective: Y's perspective: Z's perspective:
|
6879
|
+
* └──(2)→ Entry A └──(1)→ Entry A └──(3)→ Entry A
|
6880
|
+
* ```
|
6881
|
+
*
|
6882
|
+
* After publishing Entry A (without relation order sync):
|
6883
|
+
* ```
|
6884
|
+
* Entry A (draft) Entry A (new published)
|
6885
|
+
* │ │
|
6886
|
+
* ├──(1)→ X ├──(1)→ X
|
6887
|
+
* ├──(2)→ Y ├──(2)→ Y
|
6888
|
+
* └──(3)→ Z └──(3)→ Z
|
6889
|
+
*
|
6890
|
+
* X's perspective: Y's perspective: Z's perspective:
|
6891
|
+
* └──(3)→ Entry A └──(3)→ Entry A └──(3)→ Entry A
|
6892
|
+
* (all relations appear last in order)
|
6893
|
+
* ```
|
6894
|
+
*
|
6895
|
+
* This module preserves the original ordering from both perspectives by:
|
6896
|
+
* 1. Capturing the relation order before the entry state changes
|
6897
|
+
* 2. Restoring this order after the new relations are created
|
6898
|
+
*
|
6899
|
+
* @param uid - The unique identifier of the content type being processed
|
6900
|
+
* @param context - Object containing arrays of old and new entry versions
|
6901
|
+
* @returns Array of objects containing join table metadata and relations to be updated
|
6902
|
+
*/ const load = async (uid, { oldVersions })=>{
|
6903
|
+
const relationsToUpdate = [];
|
6904
|
+
await strapi.db.transaction(async ({ trx })=>{
|
6905
|
+
const contentTypes = Object.values(strapi.contentTypes);
|
6906
|
+
const components = Object.values(strapi.components);
|
6907
|
+
for (const model of [
|
6908
|
+
...contentTypes,
|
6909
|
+
...components
|
6910
|
+
]){
|
6911
|
+
const dbModel = strapi.db.metadata.get(model.uid);
|
6912
|
+
for (const attribute of Object.values(dbModel.attributes)){
|
6913
|
+
// Skip if not a bidirectional relation targeting our content type
|
6914
|
+
if (attribute.type !== 'relation' || attribute.target !== uid || !(attribute.inversedBy || attribute.mappedBy)) {
|
6915
|
+
continue;
|
6916
|
+
}
|
6917
|
+
const joinTable = attribute.joinTable;
|
6918
|
+
if (!joinTable) {
|
6919
|
+
continue;
|
6920
|
+
}
|
6921
|
+
const { name: targetColumnName } = joinTable.inverseJoinColumn;
|
6922
|
+
// Load all relations that need their order preserved
|
6923
|
+
const oldEntryIds = oldVersions.map((entry)=>entry.id);
|
6924
|
+
const existingRelations = await strapi.db.getConnection().select('*').from(joinTable.name).whereIn(targetColumnName, oldEntryIds).transacting(trx);
|
6925
|
+
if (existingRelations.length > 0) {
|
6926
|
+
relationsToUpdate.push({
|
6927
|
+
joinTable,
|
6928
|
+
relations: existingRelations
|
6929
|
+
});
|
6930
|
+
}
|
6931
|
+
}
|
6932
|
+
}
|
6933
|
+
});
|
6934
|
+
return relationsToUpdate;
|
6935
|
+
};
|
6936
|
+
/**
|
6937
|
+
* Synchronizes the order of bidirectional relations after content entries have changed state.
|
6938
|
+
*
|
6939
|
+
* When entries change state (e.g., draft → published), their IDs change and all relations are recreated.
|
6940
|
+
* While the order of relations from the entry's perspective is maintained (as they're created in order),
|
6941
|
+
* the inverse relations (from related entries' perspective) would all appear last in order since they're new.
|
6942
|
+
*
|
6943
|
+
* Example:
|
6944
|
+
* ```
|
6945
|
+
* Before publish:
|
6946
|
+
* Article(id:1) →(order:1)→ Category(id:5)
|
6947
|
+
* Category(id:5) →(order:3)→ Article(id:1)
|
6948
|
+
*
|
6949
|
+
* After publish (without sync):
|
6950
|
+
* Article(id:2) →(order:1)→ Category(id:5) [order preserved]
|
6951
|
+
* Category(id:5) →(order:99)→ Article(id:2) [order lost - appears last]
|
6952
|
+
*
|
6953
|
+
* After sync:
|
6954
|
+
* Article(id:2) →(order:1)→ Category(id:5) [order preserved]
|
6955
|
+
* Category(id:5) →(order:3)→ Article(id:2) [order restored]
|
6956
|
+
* ```
|
6957
|
+
*
|
6958
|
+
* @param oldEntries - Array of previous entry versions with their IDs and locales
|
6959
|
+
* @param newEntries - Array of new entry versions with their IDs and locales
|
6960
|
+
* @param existingRelations - Array of join table data containing the relations to be updated
|
6961
|
+
*/ const sync = async (oldEntries, newEntries, existingRelations)=>{
|
6962
|
+
// Group new entries by locale for easier lookup
|
6963
|
+
const newEntriesByLocale = keyBy('locale', newEntries);
|
6964
|
+
// Create a mapping of old entry IDs to new entry IDs based on locale
|
6965
|
+
const entryIdMapping = oldEntries.reduce((acc, oldEntry)=>{
|
6966
|
+
const newEntry = newEntriesByLocale[oldEntry.locale];
|
6967
|
+
if (!newEntry) return acc;
|
6968
|
+
acc[oldEntry.id] = newEntry.id;
|
6969
|
+
return acc;
|
6970
|
+
}, {});
|
6971
|
+
await strapi.db.transaction(async ({ trx })=>{
|
6972
|
+
for (const { joinTable, relations } of existingRelations){
|
6973
|
+
const sourceColumn = joinTable.inverseJoinColumn.name;
|
6974
|
+
const targetColumn = joinTable.joinColumn.name;
|
6975
|
+
const orderColumn = joinTable.orderColumnName;
|
6976
|
+
// Update order values for each relation
|
6977
|
+
// TODO: Find a way to batch it more efficiently
|
6978
|
+
await async.map(relations, (relation)=>{
|
6979
|
+
const { [sourceColumn]: oldSourceId, [targetColumn]: targetId, [orderColumn]: originalOrder } = relation;
|
6980
|
+
// Update the order column for the new relation entry
|
6981
|
+
return trx.from(joinTable.name).where(sourceColumn, entryIdMapping[oldSourceId]).where(targetColumn, targetId).update({
|
6982
|
+
[orderColumn]: originalOrder
|
6983
|
+
});
|
6984
|
+
});
|
6985
|
+
}
|
6986
|
+
});
|
6987
|
+
};
|
6988
|
+
|
6856
6989
|
const textNodeValidator = yup$1.object().shape({
|
6857
6990
|
type: yup$1.string().equals([
|
6858
6991
|
'text'
|
@@ -7881,7 +8014,11 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7881
8014
|
})
|
7882
8015
|
]);
|
7883
8016
|
// Load any unidirectional relation targetting the old published entries
|
7884
|
-
const relationsToSync = await load(uid, {
|
8017
|
+
const relationsToSync = await load$1(uid, {
|
8018
|
+
newVersions: draftsToPublish,
|
8019
|
+
oldVersions: oldPublishedVersions
|
8020
|
+
});
|
8021
|
+
const bidirectionalRelationsToSync = await load(uid, {
|
7885
8022
|
newVersions: draftsToPublish,
|
7886
8023
|
oldVersions: oldPublishedVersions
|
7887
8024
|
});
|
@@ -7890,10 +8027,14 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7890
8027
|
// Transform draft entry data and create published versions
|
7891
8028
|
const publishedEntries = await async.map(draftsToPublish, (draft)=>entries.publish(draft, queryParams));
|
7892
8029
|
// Sync unidirectional relations with the new published entries
|
7893
|
-
await sync([
|
8030
|
+
await sync$1([
|
7894
8031
|
...oldPublishedVersions,
|
7895
8032
|
...draftsToPublish
|
7896
8033
|
], publishedEntries, relationsToSync);
|
8034
|
+
await sync([
|
8035
|
+
...oldPublishedVersions,
|
8036
|
+
...draftsToPublish
|
8037
|
+
], publishedEntries, bidirectionalRelationsToSync);
|
7897
8038
|
publishedEntries.forEach(emitEvent('entry.publish'));
|
7898
8039
|
return {
|
7899
8040
|
documentId,
|
@@ -7951,7 +8092,11 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7951
8092
|
})
|
7952
8093
|
]);
|
7953
8094
|
// Load any unidirectional relation targeting the old drafts
|
7954
|
-
const relationsToSync = await load(uid, {
|
8095
|
+
const relationsToSync = await load$1(uid, {
|
8096
|
+
newVersions: versionsToDraft,
|
8097
|
+
oldVersions: oldDrafts
|
8098
|
+
});
|
8099
|
+
const bidirectionalRelationsToSync = await load(uid, {
|
7955
8100
|
newVersions: versionsToDraft,
|
7956
8101
|
oldVersions: oldDrafts
|
7957
8102
|
});
|
@@ -7960,10 +8105,14 @@ const createContentTypeRepository = (uid, validator = entityValidator)=>{
|
|
7960
8105
|
// Transform published entry data and create draft versions
|
7961
8106
|
const draftEntries = await async.map(versionsToDraft, (entry)=>entries.discardDraft(entry, queryParams));
|
7962
8107
|
// Sync unidirectional relations with the new draft entries
|
7963
|
-
await sync([
|
8108
|
+
await sync$1([
|
7964
8109
|
...oldDrafts,
|
7965
8110
|
...versionsToDraft
|
7966
8111
|
], draftEntries, relationsToSync);
|
8112
|
+
await sync([
|
8113
|
+
...oldDrafts,
|
8114
|
+
...versionsToDraft
|
8115
|
+
], draftEntries, bidirectionalRelationsToSync);
|
7967
8116
|
draftEntries.forEach(emitEvent('entry.draft-discard'));
|
7968
8117
|
return {
|
7969
8118
|
documentId,
|
@@ -8411,8 +8560,8 @@ const ANALYTICS_URI = 'https://analytics.strapi.io';
|
|
8411
8560
|
/**
|
8412
8561
|
* Create a send function for event with all the necessary metadata
|
8413
8562
|
*/ var createSender = ((strapi)=>{
|
8414
|
-
const { uuid } = strapi.config;
|
8415
|
-
const
|
8563
|
+
const { uuid, installId: installIdFromPackageJson } = strapi.config;
|
8564
|
+
const installId = installID(uuid, installIdFromPackageJson);
|
8416
8565
|
const serverRootPath = strapi.dirs.app.root;
|
8417
8566
|
const adminRootPath = path.join(strapi.dirs.app.root, 'src', 'admin');
|
8418
8567
|
const anonymousUserProperties = {
|
@@ -8435,12 +8584,14 @@ const ANALYTICS_URI = 'https://analytics.strapi.io';
|
|
8435
8584
|
addPackageJsonStrapiMetadata(anonymousGroupProperties, strapi);
|
8436
8585
|
return async (event, payload = {}, opts = {})=>{
|
8437
8586
|
const userId = generateAdminUserHash(strapi);
|
8587
|
+
// TO REMOVE
|
8588
|
+
console.log(`[TELEMETRY INSTANCE] installId: ${installId}, userId: ${userId}`);
|
8438
8589
|
const reqParams = {
|
8439
8590
|
method: 'POST',
|
8440
8591
|
body: JSON.stringify({
|
8441
8592
|
event,
|
8442
8593
|
userId,
|
8443
|
-
|
8594
|
+
installId,
|
8444
8595
|
eventProperties: payload.eventProperties,
|
8445
8596
|
userProperties: userId ? {
|
8446
8597
|
...anonymousUserProperties,
|