@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/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, machineID, policy, yup as yup$1, async, traverseEntity, relations, traverse, sanitize, validate, hooks, queryParams, providerFactory } from '@strapi/utils';
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.e9144fc8a193f9875a2c0832d689c14001c79f00";
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.e9144fc8a193f9875a2c0832d689c14001c79f00",
350
- "@strapi/database": "0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00",
351
- "@strapi/generators": "0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00",
352
- "@strapi/logger": "0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00",
353
- "@strapi/permissions": "0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00",
354
- "@strapi/types": "0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00",
355
- "@strapi/typescript-utils": "0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00",
356
- "@strapi/utils": "0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00",
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.2",
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.19.2",
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.e9144fc8a193f9875a2c0832d689c14001c79f00",
419
+ "eslint-config-custom": "0.0.0-experimental.eb2d102c03f0984bece7258f6e0e8ab9ee39c82d",
420
420
  supertest: "6.3.3",
421
- tsconfig: "0.0.0-experimental.e9144fc8a193f9875a2c0832d689c14001c79f00"
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
- deviceId: machineID()
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 deviceId = machineID();
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
- deviceId,
8594
+ installId,
8444
8595
  eventProperties: payload.eventProperties,
8445
8596
  userProperties: userId ? {
8446
8597
  ...anonymousUserProperties,