contentful-import 10.4.0 → 10.5.1

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 CHANGED
@@ -4550,34 +4550,6 @@ var createBaseEach_default = createBaseEach;
4550
4550
  var baseEach = createBaseEach_default(baseForOwn_default);
4551
4551
  var baseEach_default = baseEach;
4552
4552
 
4553
- // node_modules/lodash-es/defaults.js
4554
- var objectProto18 = Object.prototype;
4555
- var hasOwnProperty15 = objectProto18.hasOwnProperty;
4556
- var defaults = baseRest_default(function(object, sources) {
4557
- object = Object(object);
4558
- var index = -1;
4559
- var length = sources.length;
4560
- var guard = length > 2 ? sources[2] : void 0;
4561
- if (guard && isIterateeCall_default(sources[0], sources[1], guard)) {
4562
- length = 1;
4563
- }
4564
- while (++index < length) {
4565
- var source = sources[index];
4566
- var props = keysIn_default(source);
4567
- var propsIndex = -1;
4568
- var propsLength = props.length;
4569
- while (++propsIndex < propsLength) {
4570
- var key = props[propsIndex];
4571
- var value = object[key];
4572
- if (value === void 0 || eq_default(value, objectProto18[key]) && !hasOwnProperty15.call(object, key)) {
4573
- object[key] = source[key];
4574
- }
4575
- }
4576
- }
4577
- return object;
4578
- });
4579
- var defaults_default = defaults;
4580
-
4581
4553
  // node_modules/lodash-es/last.js
4582
4554
  function last(array) {
4583
4555
  var length = array == null ? 0 : array.length;
@@ -4658,10 +4630,10 @@ function map(collection, iteratee) {
4658
4630
  var map_default = map;
4659
4631
 
4660
4632
  // node_modules/lodash-es/_baseHas.js
4661
- var objectProto19 = Object.prototype;
4662
- var hasOwnProperty16 = objectProto19.hasOwnProperty;
4633
+ var objectProto18 = Object.prototype;
4634
+ var hasOwnProperty15 = objectProto18.hasOwnProperty;
4663
4635
  function baseHas(object, key) {
4664
- return object != null && hasOwnProperty16.call(object, key);
4636
+ return object != null && hasOwnProperty15.call(object, key);
4665
4637
  }
4666
4638
  var baseHas_default = baseHas;
4667
4639
 
@@ -4715,8 +4687,8 @@ function negate(predicate) {
4715
4687
  var negate_default = negate;
4716
4688
 
4717
4689
  // node_modules/lodash-es/_baseUnset.js
4718
- var objectProto20 = Object.prototype;
4719
- var hasOwnProperty17 = objectProto20.hasOwnProperty;
4690
+ var objectProto19 = Object.prototype;
4691
+ var hasOwnProperty16 = objectProto19.hasOwnProperty;
4720
4692
  function baseUnset(object, path) {
4721
4693
  path = castPath_default(path, object);
4722
4694
  var index = -1, length = path.length;
@@ -4725,7 +4697,7 @@ function baseUnset(object, path) {
4725
4697
  }
4726
4698
  while (++index < length) {
4727
4699
  var key = toKey_default(path[index]);
4728
- if (key === "__proto__" && !hasOwnProperty17.call(object, "__proto__")) {
4700
+ if (key === "__proto__" && !hasOwnProperty16.call(object, "__proto__")) {
4729
4701
  return false;
4730
4702
  }
4731
4703
  if ((key === "constructor" || key === "prototype") && index < length - 1) {
@@ -4889,7 +4861,7 @@ var startCase_default = startCase;
4889
4861
 
4890
4862
  // lib/index.ts
4891
4863
  var import_p_queue = __toESM(require("p-queue"));
4892
- var import_logging13 = __toESM(require_logging());
4864
+ var import_logging14 = __toESM(require_logging());
4893
4865
  var import_listr4 = __toESM(require_listr());
4894
4866
 
4895
4867
  // lib/tasks/init-client.ts
@@ -4922,17 +4894,17 @@ var import_logging3 = __toESM(require_logging());
4922
4894
 
4923
4895
  // lib/utils/publish-exo-entities.ts
4924
4896
  var import_logging2 = __toESM(require_logging());
4925
- function filterExoEntitiesToPublish(entities, sourceEntities) {
4897
+ function filterExoEntitiesToPublish(entities2, sourceEntities) {
4926
4898
  const entityIdsToPublish = new Set(
4927
4899
  sourceEntities.filter((entity) => entity.sys.publishedVersion).map((entity) => entity.sys.id)
4928
4900
  );
4929
- return entities.filter((entity) => entityIdsToPublish.has(entity.sys.id));
4901
+ return entities2.filter((entity) => entityIdsToPublish.has(entity.sys.id));
4930
4902
  }
4931
- function filterExoEntitiesToUnpublish(entities, sourceEntities) {
4903
+ function filterExoEntitiesToUnpublish(entities2, sourceEntities) {
4932
4904
  const entityIdsPublishedInSource = new Set(
4933
4905
  sourceEntities.filter((entity) => entity.sys.publishedVersion).map((entity) => entity.sys.id)
4934
4906
  );
4935
- return entities.filter((entity) => entity.sys.publishedVersion && !entityIdsPublishedInSource.has(entity.sys.id));
4907
+ return entities2.filter((entity) => entity.sys.publishedVersion && !entityIdsPublishedInSource.has(entity.sys.id));
4936
4908
  }
4937
4909
  async function publishExoEntity(type, entity, publish) {
4938
4910
  try {
@@ -4994,6 +4966,17 @@ function isExoEntitlementError(err) {
4994
4966
  return false;
4995
4967
  }
4996
4968
  }
4969
+ function isTimelineEntitlementError(err) {
4970
+ if (!(err instanceof Error)) {
4971
+ return false;
4972
+ }
4973
+ try {
4974
+ const parsed = JSON.parse(err.message);
4975
+ return parsed?.status === 403 && parsed?.details?.reasons === "Timeline is not enabled for this organization";
4976
+ } catch {
4977
+ return false;
4978
+ }
4979
+ }
4997
4980
  var EXO_M1_FEATURE = "exoM1";
4998
4981
  async function spaceHasExoM1Entitlement(client, spaceId) {
4999
4982
  try {
@@ -5020,7 +5003,8 @@ var CURSOR_QUERY_METHODS = {
5020
5003
  experienceTemplates: { name: "experience templates", namespace: "experienceTemplate" },
5021
5004
  experienceFragments: { name: "experience fragments", namespace: "experienceFragment" },
5022
5005
  dataAssemblies: { name: "data assemblies", namespace: "dataAssembly" },
5023
- experiences: { name: "experiences", namespace: "experience" }
5006
+ experiences: { name: "experiences", namespace: "experience" },
5007
+ releases: { name: "releases", namespace: "release" }
5024
5008
  };
5025
5009
  var ENTITY_METHODS = {
5026
5010
  contentTypes: { name: "content types", ns: "contentType" },
@@ -5123,11 +5107,26 @@ async function cursorPaginatedQuery({ client, spaceId, environmentId, type, requ
5123
5107
  const allItems = [];
5124
5108
  do {
5125
5109
  const items = await requestQueue.add(async () => {
5126
- const response = await client[namespace].getMany({
5127
- spaceId,
5128
- environmentId,
5129
- query: { limit: BATCH_SIZE_LIMIT, ...pageNext && { pageNext } }
5130
- });
5110
+ let response;
5111
+ if (type === "releases") {
5112
+ response = await client.release.query({
5113
+ environmentId,
5114
+ spaceId,
5115
+ query: {
5116
+ "metadata.annotations.Contentful:Timeline.type[nin]": "Staging,Hidden",
5117
+ "sys.schemaVersion": "Release.v2",
5118
+ "sys.status[in]": "active",
5119
+ limit: BATCH_SIZE_LIMIT,
5120
+ ...pageNext && { pageNext }
5121
+ }
5122
+ });
5123
+ } else {
5124
+ response = await client[namespace].getMany({
5125
+ spaceId,
5126
+ environmentId,
5127
+ query: { limit: BATCH_SIZE_LIMIT, ...pageNext && { pageNext } }
5128
+ });
5129
+ }
5131
5130
  totalFetched += response.items.length;
5132
5131
  import_logging3.logEmitter.emit("info", `Fetched ${totalFetched} ${entityTypeName}`);
5133
5132
  pageNext = response.pages?.next;
@@ -5144,6 +5143,8 @@ async function cursorPaginatedQueryOrWarn(params) {
5144
5143
  const { name: entityTypeName } = CURSOR_QUERY_METHODS[params.type];
5145
5144
  if (isExoEntitlementError(err)) {
5146
5145
  import_logging3.logEmitter.emit("error", new Error(`Skipping ${entityTypeName} import: Experience Orchestration (ExO) is not enabled for this space`));
5146
+ } else if (isTimelineEntitlementError(err)) {
5147
+ import_logging3.logEmitter.emit("error", new Error(`Skipping ${entityTypeName} import: Timeline (Releases) is not enabled for this organization`));
5147
5148
  } else {
5148
5149
  import_logging3.logEmitter.emit("error", err instanceof Error ? err : new Error(String(err)));
5149
5150
  }
@@ -5173,7 +5174,8 @@ async function getDestinationData({
5173
5174
  experienceFragments: [],
5174
5175
  dataAssemblies: [],
5175
5176
  designTokens: [],
5176
- webhooks: []
5177
+ webhooks: [],
5178
+ releases: []
5177
5179
  };
5178
5180
  sourceData = {
5179
5181
  ...result,
@@ -5234,6 +5236,7 @@ async function getDestinationData({
5234
5236
  requestQueue
5235
5237
  });
5236
5238
  }
5239
+ result.releases = cursorPaginatedQueryOrWarn({ client, spaceId, environmentId, type: "releases", requestQueue });
5237
5240
  if (includeExperienceOrchestration && client) {
5238
5241
  const sourceHasDesignTokens = Boolean(sourceData.designTokens?.length);
5239
5242
  const sourceHasComponents = Boolean(sourceData.components?.length);
@@ -5273,7 +5276,7 @@ async function getDestinationData({
5273
5276
  // lib/tasks/push-to-space/push-to-space.ts
5274
5277
  var import_listr = __toESM(require("listr"));
5275
5278
  var import_listr_verbose_renderer = __toESM(require("listr-verbose-renderer"));
5276
- var import_logging11 = __toESM(require_logging());
5279
+ var import_logging12 = __toESM(require_logging());
5277
5280
  var import_listr2 = __toESM(require_listr());
5278
5281
 
5279
5282
  // lib/tasks/push-to-space/assets.ts
@@ -5373,14 +5376,14 @@ async function processAssets({
5373
5376
  // lib/tasks/push-to-space/creation.ts
5374
5377
  var import_get_entity_name2 = __toESM(require_get_entity_name());
5375
5378
  var import_logging5 = __toESM(require_logging());
5376
- function createEntities({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5377
- return createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, skipUpdates, requestQueue });
5379
+ function createEntities({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5380
+ return createEntitiesWithConcurrency({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue });
5378
5381
  }
5379
- function createLocales({ context, entities, destinationEntitiesById, requestQueue }) {
5380
- return createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue });
5382
+ function createLocales({ context, entities: entities2, destinationEntitiesById, requestQueue }) {
5383
+ return createEntitiesInSequence({ context, entities: entities2, destinationEntitiesById, requestQueue });
5381
5384
  }
5382
- async function createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5383
- const pendingCreatedEntities = entities.map((entity) => {
5385
+ async function createEntitiesWithConcurrency({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5386
+ const pendingCreatedEntities = entities2.map((entity) => {
5384
5387
  const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
5385
5388
  const updateOperation = skipUpdates ? "skip" : "update";
5386
5389
  const operation = destinationEntity ? updateOperation : "create";
@@ -5401,9 +5404,9 @@ async function createEntitiesWithConcurrency({ context, entities, destinationEnt
5401
5404
  const createdEntities = await Promise.all(pendingCreatedEntities);
5402
5405
  return createdEntities.filter((entity) => entity);
5403
5406
  }
5404
- async function createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue }) {
5407
+ async function createEntitiesInSequence({ context, entities: entities2, destinationEntitiesById, requestQueue }) {
5405
5408
  const createdEntities = [];
5406
- for (const entity of entities) {
5409
+ for (const entity of entities2) {
5407
5410
  const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
5408
5411
  const operation = destinationEntity ? "update" : "create";
5409
5412
  try {
@@ -5422,8 +5425,8 @@ async function createEntitiesInSequence({ context, entities, destinationEntities
5422
5425
  }
5423
5426
  return createdEntities;
5424
5427
  }
5425
- async function createEntries({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5426
- const createdEntries = await Promise.all(entities.map((entry) => {
5428
+ async function createEntries({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5429
+ const createdEntries = await Promise.all(entities2.map((entry) => {
5427
5430
  return createEntry({ entry, context, destinationEntitiesById, skipUpdates, requestQueue });
5428
5431
  }));
5429
5432
  return createdEntries.filter((entry) => entry);
@@ -5678,27 +5681,27 @@ function archiveEntity(client, spaceId, environmentId, entity) {
5678
5681
  }
5679
5682
  throw new Error(`archiveEntity: unsupported type "${type}"`);
5680
5683
  }
5681
- async function publishEntities({ entities, client, spaceId, environmentId, requestQueue, localePublishing }) {
5682
- if (entities.length === 0) {
5684
+ async function publishEntities({ entities: entities2, client, spaceId, environmentId, requestQueue, localePublishing }) {
5685
+ if (entities2.length === 0) {
5683
5686
  import_logging7.logEmitter.emit("info", "Skipping publishing since zero valid entities passed");
5684
5687
  return [];
5685
5688
  }
5686
- const entity = entities[0];
5689
+ const entity = entities2[0];
5687
5690
  const type = entity.sys.type || "unknown type";
5688
- import_logging7.logEmitter.emit("info", `Publishing ${entities.length} ${type}s`);
5689
- const result = await runQueue(entities, [], client, spaceId, environmentId, requestQueue, localePublishing);
5691
+ import_logging7.logEmitter.emit("info", `Publishing ${entities2.length} ${type}s`);
5692
+ const result = await runQueue(entities2, [], client, spaceId, environmentId, requestQueue, localePublishing);
5690
5693
  import_logging7.logEmitter.emit("info", `Successfully published ${result.length} ${type}s`);
5691
5694
  return result;
5692
5695
  }
5693
- async function archiveEntities({ entities, client, spaceId, environmentId, requestQueue }) {
5694
- if (entities.length === 0) {
5696
+ async function archiveEntities({ entities: entities2, client, spaceId, environmentId, requestQueue }) {
5697
+ if (entities2.length === 0) {
5695
5698
  import_logging7.logEmitter.emit("info", "Skipping archiving since zero valid entities passed");
5696
5699
  return [];
5697
5700
  }
5698
- const entity = entities[0];
5701
+ const entity = entities2[0];
5699
5702
  const type = entity.sys.type || "unknown type";
5700
- import_logging7.logEmitter.emit("info", `Archiving ${entities.length} ${type}s`);
5701
- const pendingArchivedEntities = entities.map((entity2) => {
5703
+ import_logging7.logEmitter.emit("info", `Archiving ${entities2.length} ${type}s`);
5704
+ const pendingArchivedEntities = entities2.map((entity2) => {
5702
5705
  return requestQueue.add(async () => {
5703
5706
  try {
5704
5707
  const archivedEntity = await archiveEntity(client, spaceId, environmentId, entity2);
@@ -5786,6 +5789,55 @@ async function runQueue(queue, result = [], client, spaceId, environmentId, requ
5786
5789
  return result;
5787
5790
  }
5788
5791
 
5792
+ // lib/tasks/push-to-space/releases.ts
5793
+ var import_logging8 = __toESM(require_logging());
5794
+ function partitionReleasesBySchemaVersion(releases2) {
5795
+ return {
5796
+ supported: releases2.filter((release) => release.transformed.sys.schemaVersion === "Release.v2"),
5797
+ unsupported: releases2.filter((release) => release.transformed.sys.schemaVersion !== "Release.v2")
5798
+ };
5799
+ }
5800
+ function logUnsupportedRelease(release) {
5801
+ import_logging8.logEmitter.emit("error", new Error(
5802
+ `Skipping Release "${release.transformed.sys.id}": only Release.v2 ("Releases") is supported, got schemaVersion "${release.transformed.sys.schemaVersion}"`
5803
+ ));
5804
+ }
5805
+ function buildReleaseCreatePayload(release) {
5806
+ const { id: _sourceId, ...sourceSysWithoutId } = release.transformed.sys;
5807
+ return {
5808
+ ...release.transformed,
5809
+ entities: release.transformed.entities,
5810
+ sys: { ...sourceSysWithoutId, type: "Release", schemaVersion: "Release.v2" }
5811
+ };
5812
+ }
5813
+ function buildReleaseUpdatePayload(release) {
5814
+ return {
5815
+ ...release.transformed,
5816
+ entities: release.transformed.entities,
5817
+ sys: { type: "Release", schemaVersion: "Release.v2" }
5818
+ };
5819
+ }
5820
+ async function importRelease(release, existing, context) {
5821
+ const { client, spaceId, environmentId } = context;
5822
+ try {
5823
+ if (existing) {
5824
+ const result2 = await client.release.update(
5825
+ { spaceId, environmentId, releaseId: existing.sys.id, version: existing.sys.version },
5826
+ buildReleaseUpdatePayload(release)
5827
+ );
5828
+ import_logging8.logEmitter.emit("info", `UPDATE Release ${existing.sys.id}`);
5829
+ return result2;
5830
+ }
5831
+ const result = await client.release.create({ spaceId, environmentId }, buildReleaseCreatePayload(release));
5832
+ import_logging8.logEmitter.emit("info", `CREATE Release ${result.sys.id}`);
5833
+ return result;
5834
+ } catch (err) {
5835
+ err.entity = release.transformed;
5836
+ import_logging8.logEmitter.emit("error", err);
5837
+ return null;
5838
+ }
5839
+ }
5840
+
5789
5841
  // lib/utils/graphql-schema-backoff.ts
5790
5842
  var GRAPHQL_SCHEMA_STALE_DELAYS_MS = [500, 1500, 6e3];
5791
5843
  function isGraphQLSchemaStaleError(err) {
@@ -5912,18 +5964,18 @@ function sortExperienceFragments(experienceFragments) {
5912
5964
  }
5913
5965
 
5914
5966
  // lib/utils/sort-or-report.ts
5915
- var import_logging8 = __toESM(require_logging());
5967
+ var import_logging9 = __toESM(require_logging());
5916
5968
  function sortOrReport(sortFn) {
5917
5969
  try {
5918
5970
  return sortFn();
5919
5971
  } catch (err) {
5920
- import_logging8.logEmitter.emit("error", err);
5972
+ import_logging9.logEmitter.emit("error", err);
5921
5973
  throw err;
5922
5974
  }
5923
5975
  }
5924
5976
 
5925
5977
  // lib/utils/import-exo-folders.ts
5926
- var import_logging9 = __toESM(require_logging());
5978
+ var import_logging10 = __toESM(require_logging());
5927
5979
  var FOLDER_CONCEPT_PREFIX = "contentful.folder-";
5928
5980
  var PARENT_FOLDER_GROUP_IDS = {
5929
5981
  designToken: "contentful.folder-group-designToken",
@@ -5932,6 +5984,29 @@ var PARENT_FOLDER_GROUP_IDS = {
5932
5984
  fragment: "contentful.folder-group-fragment",
5933
5985
  experience: "contentful.folder-group-experience"
5934
5986
  };
5987
+ var VERSION_CONFLICT_MAX_ATTEMPTS = 5;
5988
+ var VERSION_CONFLICT_RETRY_DELAY_MS = 250;
5989
+ var MissingExoFolderGroupSchemesError = class extends Error {
5990
+ constructor(missingSchemeIds) {
5991
+ super(`Missing ExO folder-group concept scheme(s) in the destination organization: ${missingSchemeIds.join(", ")}`);
5992
+ this.name = "MissingExoFolderGroupSchemesError";
5993
+ this.missingSchemeIds = missingSchemeIds;
5994
+ }
5995
+ };
5996
+ var SourceExoFolderConceptReadError = class extends Error {
5997
+ constructor(sourceConceptId, error) {
5998
+ const reason = error instanceof Error ? error.message : String(error);
5999
+ super(`Unable to read source ExO folder concept ${sourceConceptId}: ${reason}`);
6000
+ this.name = "SourceExoFolderConceptReadError";
6001
+ this.sourceConceptId = sourceConceptId;
6002
+ }
6003
+ };
6004
+ var SourceOrganizationResolutionError = class extends Error {
6005
+ constructor(message) {
6006
+ super(message);
6007
+ this.name = "SourceOrganizationResolutionError";
6008
+ }
6009
+ };
5935
6010
  var ENTITY_TYPE_TO_PARENT_GROUP_ID = {
5936
6011
  designTokens: PARENT_FOLDER_GROUP_IDS.designToken,
5937
6012
  components: PARENT_FOLDER_GROUP_IDS.componentType,
@@ -5940,27 +6015,52 @@ var ENTITY_TYPE_TO_PARENT_GROUP_ID = {
5940
6015
  experiences: PARENT_FOLDER_GROUP_IDS.experience
5941
6016
  };
5942
6017
  function getSourceSpaceId(sourceEntities) {
5943
- for (const entities of Object.values(sourceEntities)) {
5944
- for (const entity of entities ?? []) {
6018
+ for (const entities2 of Object.values(sourceEntities)) {
6019
+ for (const entity of entities2 ?? []) {
5945
6020
  const spaceId = entity.sys?.space?.sys?.id;
5946
6021
  if (spaceId) return spaceId;
5947
6022
  }
5948
6023
  }
5949
6024
  return void 0;
5950
6025
  }
5951
- async function ensureParentFolderGroupsExist(client, organizationId) {
6026
+ function isVersionMismatchError(err) {
6027
+ return err?.error?.sys?.id === "VersionMismatch";
6028
+ }
6029
+ async function waitForVersionConflictRetry(attempt) {
6030
+ await new Promise((resolve2) => setTimeout(resolve2, VERSION_CONFLICT_RETRY_DELAY_MS * attempt));
6031
+ }
6032
+ async function resolveSourceOrganizationId(client, sourceSpaceId) {
6033
+ if (!sourceSpaceId) {
6034
+ throw new SourceOrganizationResolutionError(
6035
+ "Unable to resolve the source organization because the exported ExO entity does not include a source space ID"
6036
+ );
6037
+ }
6038
+ try {
6039
+ const sourceSpace = await client.space.get({ spaceId: sourceSpaceId });
6040
+ const sourceOrganizationId = sourceSpace?.sys?.organization?.sys?.id;
6041
+ if (!sourceOrganizationId) {
6042
+ throw new Error(`source space ${sourceSpaceId} has no organization link`);
6043
+ }
6044
+ return sourceOrganizationId;
6045
+ } catch (err) {
6046
+ if (err instanceof SourceOrganizationResolutionError) throw err;
6047
+ const reason = err instanceof Error ? err.message : String(err);
6048
+ throw new SourceOrganizationResolutionError(
6049
+ `Unable to resolve the source organization for space ${sourceSpaceId}: ${reason}`
6050
+ );
6051
+ }
6052
+ }
6053
+ async function ensureParentFolderGroupsExist(client, destinationOrganizationId, requiredParentGroupIds) {
6054
+ if (requiredParentGroupIds.size === 0) return /* @__PURE__ */ new Map();
5952
6055
  const { items } = await client.conceptScheme.getMany({
5953
- organizationId,
6056
+ organizationId: destinationOrganizationId,
5954
6057
  query: { purpose: "internal" }
5955
6058
  });
5956
6059
  const existingIds = new Set(items.map((s) => s.sys.id));
5957
- const allParentFolderGroupsExist = Object.values(PARENT_FOLDER_GROUP_IDS).every((id) => existingIds.has(id));
5958
- if (!allParentFolderGroupsExist) {
5959
- return /* @__PURE__ */ new Map();
5960
- }
5961
- const parentGroupIdSet = new Set(Object.values(PARENT_FOLDER_GROUP_IDS));
6060
+ const missingSchemeIds = [...requiredParentGroupIds].filter((id) => !existingIds.has(id));
6061
+ if (missingSchemeIds.length > 0) throw new MissingExoFolderGroupSchemesError(missingSchemeIds);
5962
6062
  return new Map(
5963
- items.filter((s) => parentGroupIdSet.has(s.sys.id)).map((s) => [s.sys.id, s])
6063
+ items.filter((s) => requiredParentGroupIds.has(s.sys.id)).map((s) => [s.sys.id, s])
5964
6064
  );
5965
6065
  }
5966
6066
  function deriveChildConceptMap(sourceEntities, destinationSpaceId) {
@@ -5979,33 +6079,54 @@ function deriveChildConceptMap(sourceEntities, destinationSpaceId) {
5979
6079
  }
5980
6080
  return childConceptMap;
5981
6081
  }
5982
- async function createOrPatchChildConcepts(client, organizationId, destinationSpaceId, childConceptMap) {
6082
+ async function createOrPatchChildConcepts(client, sourceOrganizationId, destinationOrganizationId, destinationSpaceId, childConceptMap) {
5983
6083
  const spaceLink = { sys: { type: "Link", linkType: "Space", id: destinationSpaceId } };
6084
+ const existingDestinationConcepts = /* @__PURE__ */ new Map();
5984
6085
  for (const [sourceConceptId, { destConceptId }] of childConceptMap) {
5985
- let prefLabel = { "en-US": destConceptId };
5986
- try {
5987
- const sourceConcept = await client.concept.get({ organizationId, conceptId: sourceConceptId });
5988
- if (sourceConcept?.prefLabel) prefLabel = sourceConcept.prefLabel;
5989
- } catch {
5990
- }
5991
6086
  let existing = null;
5992
6087
  try {
5993
- existing = await client.concept.get({ organizationId, conceptId: destConceptId });
6088
+ existing = await client.concept.get({
6089
+ organizationId: destinationOrganizationId,
6090
+ conceptId: destConceptId
6091
+ });
5994
6092
  } catch (err) {
5995
6093
  if (err?.name !== "NotFound") {
5996
- import_logging9.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
6094
+ import_logging10.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
5997
6095
  }
5998
6096
  }
6097
+ existingDestinationConcepts.set(sourceConceptId, existing);
6098
+ }
6099
+ const sourceConceptLabels = /* @__PURE__ */ new Map();
6100
+ for (const [sourceConceptId] of childConceptMap) {
6101
+ if (existingDestinationConcepts.get(sourceConceptId)) {
6102
+ continue;
6103
+ }
6104
+ try {
6105
+ const sourceConcept = await client.concept.get({
6106
+ organizationId: sourceOrganizationId,
6107
+ conceptId: sourceConceptId
6108
+ });
6109
+ if (!sourceConcept?.prefLabel) {
6110
+ throw new Error("source concept has no prefLabel");
6111
+ }
6112
+ sourceConceptLabels.set(sourceConceptId, sourceConcept.prefLabel);
6113
+ } catch (err) {
6114
+ throw new SourceExoFolderConceptReadError(sourceConceptId, err);
6115
+ }
6116
+ }
6117
+ for (const [sourceConceptId, { destConceptId }] of childConceptMap) {
6118
+ const existing = existingDestinationConcepts.get(sourceConceptId);
5999
6119
  if (!existing) {
6120
+ const prefLabel = sourceConceptLabels.get(sourceConceptId);
6000
6121
  try {
6001
6122
  await client.concept.createWithId(
6002
- { organizationId, conceptId: destConceptId },
6123
+ { organizationId: destinationOrganizationId, conceptId: destConceptId },
6003
6124
  // @ts-expect-error - CMA.js type needs to be updated to be aware of purpose: 'internal'
6004
6125
  { purpose: "internal", prefLabel, metadata: { spaces: [spaceLink] } }
6005
6126
  );
6006
- import_logging9.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
6127
+ import_logging10.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
6007
6128
  } catch (err) {
6008
- import_logging9.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
6129
+ import_logging10.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
6009
6130
  }
6010
6131
  } else {
6011
6132
  const patches = [];
@@ -6016,37 +6137,49 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
6016
6137
  if (patches.length > 0) {
6017
6138
  try {
6018
6139
  await client.concept.patch(
6019
- { organizationId, conceptId: destConceptId, version: existing.sys.version },
6140
+ { organizationId: destinationOrganizationId, conceptId: destConceptId, version: existing.sys.version },
6020
6141
  patches
6021
6142
  );
6022
- import_logging9.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
6143
+ import_logging10.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
6023
6144
  } catch (err) {
6024
- import_logging9.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
6145
+ import_logging10.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
6025
6146
  }
6026
6147
  }
6027
6148
  }
6028
6149
  }
6029
6150
  }
6030
- async function linkChildConceptsToParentGroups(client, organizationId, childConceptMap, parentGroups) {
6151
+ async function linkChildConceptsToParentGroups(client, destinationOrganizationId, childConceptMap, parentGroups) {
6031
6152
  for (const [, { destConceptId, parentGroupId }] of childConceptMap) {
6032
- const parentGroup = parentGroups.get(parentGroupId);
6033
- if (!parentGroup) continue;
6034
- const alreadyLinked = (parentGroup.concepts ?? []).some((c) => c.sys.id === destConceptId);
6035
- if (alreadyLinked) continue;
6036
- try {
6037
- const updated = await client.conceptScheme.patch(
6038
- { organizationId, conceptSchemeId: parentGroupId, version: parentGroup.sys.version },
6039
- [{ op: "add", path: "/concepts/-", value: { sys: { type: "Link", linkType: "TaxonomyConcept", id: destConceptId } } }]
6040
- );
6041
- parentGroups.set(parentGroupId, updated);
6042
- import_logging9.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
6043
- } catch (err) {
6044
- import_logging9.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
6153
+ let parentGroup = parentGroups.get(parentGroupId);
6154
+ for (let attempt = 1; parentGroup && attempt <= VERSION_CONFLICT_MAX_ATTEMPTS; attempt++) {
6155
+ const alreadyLinked = (parentGroup.concepts ?? []).some((c) => c.sys.id === destConceptId);
6156
+ if (alreadyLinked) break;
6157
+ try {
6158
+ const updated = await client.conceptScheme.patch(
6159
+ { organizationId: destinationOrganizationId, conceptSchemeId: parentGroupId, version: parentGroup.sys.version },
6160
+ [{ op: "add", path: "/concepts/-", value: { sys: { type: "Link", linkType: "TaxonomyConcept", id: destConceptId } } }]
6161
+ );
6162
+ parentGroups.set(parentGroupId, updated);
6163
+ import_logging10.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
6164
+ break;
6165
+ } catch (err) {
6166
+ if (!isVersionMismatchError(err) || attempt === VERSION_CONFLICT_MAX_ATTEMPTS) {
6167
+ import_logging10.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
6168
+ break;
6169
+ }
6170
+ import_logging10.logEmitter.emit("warning", `Version mismatch linking child concept ${destConceptId} to parent group ${parentGroupId}; retrying`);
6171
+ await waitForVersionConflictRetry(attempt);
6172
+ parentGroup = await client.conceptScheme.get({
6173
+ organizationId: destinationOrganizationId,
6174
+ conceptSchemeId: parentGroupId
6175
+ });
6176
+ parentGroups.set(parentGroupId, parentGroup);
6177
+ }
6045
6178
  }
6046
6179
  }
6047
6180
  }
6048
- function rewriteEntityFolderConcepts(entities, childConceptMap) {
6049
- for (const entity of entities) {
6181
+ function rewriteEntityFolderConcepts(entities2, childConceptMap) {
6182
+ for (const entity of entities2) {
6050
6183
  if (!entity.metadata?.concepts) continue;
6051
6184
  entity.metadata.concepts = entity.metadata.concepts.map((link) => {
6052
6185
  const entry = childConceptMap.get(link.sys.id);
@@ -6057,25 +6190,31 @@ function rewriteEntityFolderConcepts(entities, childConceptMap) {
6057
6190
  }
6058
6191
  async function importExoFolders({
6059
6192
  client,
6060
- organizationId,
6193
+ destinationOrganizationId,
6061
6194
  destinationSpaceId,
6062
6195
  sourceEntities
6063
6196
  }) {
6064
6197
  const sourceSpaceId = getSourceSpaceId(sourceEntities);
6065
6198
  if (sourceSpaceId === destinationSpaceId) {
6066
- import_logging9.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
6067
- return;
6068
- }
6069
- const parentGroups = await ensureParentFolderGroupsExist(client, organizationId);
6070
- if (parentGroups.size === 0) {
6071
- import_logging9.logEmitter.emit("warn", "One or more Experience Orchestration folder group concept schemes are missing in the destination organization. Please create them before importing.");
6199
+ import_logging10.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
6072
6200
  return;
6073
6201
  }
6074
6202
  const childConceptMap = deriveChildConceptMap(sourceEntities, destinationSpaceId);
6075
6203
  if (childConceptMap.size === 0) return;
6076
- import_logging9.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
6077
- await createOrPatchChildConcepts(client, organizationId, destinationSpaceId, childConceptMap);
6078
- await linkChildConceptsToParentGroups(client, organizationId, childConceptMap, parentGroups);
6204
+ const requiredParentGroupIds = new Set(
6205
+ [...childConceptMap.values()].map(({ parentGroupId }) => parentGroupId)
6206
+ );
6207
+ const sourceOrganizationId = await resolveSourceOrganizationId(client, sourceSpaceId);
6208
+ const parentGroups = await ensureParentFolderGroupsExist(client, destinationOrganizationId, requiredParentGroupIds);
6209
+ import_logging10.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
6210
+ await createOrPatchChildConcepts(
6211
+ client,
6212
+ sourceOrganizationId,
6213
+ destinationOrganizationId,
6214
+ destinationSpaceId,
6215
+ childConceptMap
6216
+ );
6217
+ await linkChildConceptsToParentGroups(client, destinationOrganizationId, childConceptMap, parentGroups);
6079
6218
  const allEntities = [
6080
6219
  ...sourceEntities.designTokens ?? [],
6081
6220
  ...sourceEntities.components ?? [],
@@ -6139,7 +6278,7 @@ function buildLocalePublishPlan(sourceEntities, destinationLocaleCodes, { destin
6139
6278
  }
6140
6279
 
6141
6280
  // lib/utils/destination-locales.ts
6142
- var import_logging10 = __toESM(require_logging());
6281
+ var import_logging11 = __toESM(require_logging());
6143
6282
  var cache = /* @__PURE__ */ new WeakMap();
6144
6283
  function getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueue }) {
6145
6284
  let byEnvironment = cache.get(client);
@@ -6151,7 +6290,7 @@ function getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueu
6151
6290
  let localeCodes = byEnvironment.get(key);
6152
6291
  if (!localeCodes) {
6153
6292
  localeCodes = batchedPageQuery({ client, spaceId, environmentId, type: "locales", requestQueue }).then((items) => items.map((locale) => locale.code)).catch((err) => {
6154
- import_logging10.logEmitter.emit("warning", `Could not read the locales of the destination environment, falling back to publishing all locales: ${err.message}`);
6293
+ import_logging11.logEmitter.emit("warning", `Could not read the locales of the destination environment, falling back to publishing all locales: ${err.message}`);
6155
6294
  return null;
6156
6295
  });
6157
6296
  byEnvironment.set(key, localeCodes);
@@ -6170,7 +6309,7 @@ async function withGraphQLSchemaBackoff(fn) {
6170
6309
  throw err;
6171
6310
  }
6172
6311
  const delay = GRAPHQL_SCHEMA_STALE_DELAYS_MS[attempt];
6173
- import_logging11.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
6312
+ import_logging12.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
6174
6313
  await new Promise((resolve2) => setTimeout(resolve2, delay));
6175
6314
  lastErr = err;
6176
6315
  }
@@ -6221,15 +6360,15 @@ function pushToSpace({
6221
6360
  ...sourceData.experienceFragments || []
6222
6361
  ].some((parent2) => (parent2.optimizationVariants || []).some(isRealOptimizationVariant));
6223
6362
  if (includeExperienceOrchestration && !skipExoVariants && hasOptimizationVariants) {
6224
- import_logging11.logEmitter.emit("warning", "Optimization Variants are imported with new destination IDs and can be duplicated by re-imports. Set skipExoVariants to true to skip them.");
6363
+ import_logging12.logEmitter.emit("warning", "Optimization Variants are imported with new destination IDs and can be duplicated by re-imports. Set skipExoVariants to true to skip them.");
6225
6364
  }
6226
6365
  listrOptions = listrOptions || {
6227
6366
  renderer: import_listr_verbose_renderer.default
6228
6367
  };
6229
6368
  const destinationDataById = {};
6230
- for (const [entityType, entities] of Object.entries(destinationData)) {
6369
+ for (const [entityType, entities2] of Object.entries(destinationData)) {
6231
6370
  const entitiesById = /* @__PURE__ */ new Map();
6232
- for (const entity of entities) {
6371
+ for (const entity of entities2) {
6233
6372
  entitiesById.set(entity.sys.id, entity);
6234
6373
  }
6235
6374
  destinationDataById[entityType] = entitiesById;
@@ -6318,7 +6457,7 @@ function pushToSpace({
6318
6457
  const ctEditorInterface = await requestQueue.add(
6319
6458
  () => client.editorInterface.get({ spaceId, environmentId, contentTypeId: contentType.sys.id })
6320
6459
  );
6321
- import_logging11.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6460
+ import_logging12.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6322
6461
  const updatedData = {
6323
6462
  ...ctEditorInterface,
6324
6463
  controls: editorInterface.controls,
@@ -6336,7 +6475,7 @@ function pushToSpace({
6336
6475
  return updatedEditorInterface;
6337
6476
  } catch (err) {
6338
6477
  err.entity = editorInterface;
6339
- import_logging11.logEmitter.emit("error", err);
6478
+ import_logging12.logEmitter.emit("error", err);
6340
6479
  return null;
6341
6480
  }
6342
6481
  });
@@ -6354,7 +6493,7 @@ function pushToSpace({
6354
6493
  for (const file of Object.values(asset.transformed.fields.file)) {
6355
6494
  allPendingUploads.push(requestQueue.add(async () => {
6356
6495
  try {
6357
- import_logging11.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6496
+ import_logging12.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6358
6497
  const assetStream = await getAssetStreamForURL(file.upload, assetsDirectory);
6359
6498
  const upload = await client.upload.create(
6360
6499
  { spaceId, environmentId },
@@ -6370,7 +6509,7 @@ function pushToSpace({
6370
6509
  };
6371
6510
  return upload;
6372
6511
  } catch (err) {
6373
- import_logging11.logEmitter.emit("error", err);
6512
+ import_logging12.logEmitter.emit("error", err);
6374
6513
  }
6375
6514
  }));
6376
6515
  }
@@ -6505,7 +6644,7 @@ function pushToSpace({
6505
6644
  const space = await client.space.get({ spaceId });
6506
6645
  await importExoFolders({
6507
6646
  client,
6508
- organizationId: space.sys.organization.sys.id,
6647
+ destinationOrganizationId: space.sys.organization.sys.id,
6509
6648
  destinationSpaceId: spaceId,
6510
6649
  sourceEntities: {
6511
6650
  designTokens: sourceData.designTokens,
@@ -6516,7 +6655,10 @@ function pushToSpace({
6516
6655
  }
6517
6656
  });
6518
6657
  } catch (error) {
6519
- import_logging11.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6658
+ if (error instanceof MissingExoFolderGroupSchemesError || error instanceof SourceOrganizationResolutionError || error instanceof SourceExoFolderConceptReadError) {
6659
+ throw error;
6660
+ }
6661
+ import_logging12.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6520
6662
  }
6521
6663
  }),
6522
6664
  skip: () => !includeExperienceOrchestration
@@ -6534,19 +6676,19 @@ function pushToSpace({
6534
6676
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6535
6677
  payload
6536
6678
  ));
6537
- import_logging11.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6679
+ import_logging12.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6538
6680
  } else {
6539
6681
  const payload = { ...omitSys(entity), sys: buildDataAssemblySys(entity, 0) };
6540
6682
  result = await withGraphQLSchemaBackoff(() => client.dataAssembly.update(
6541
6683
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6542
6684
  payload
6543
6685
  ));
6544
- import_logging11.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6686
+ import_logging12.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6545
6687
  }
6546
6688
  return result;
6547
6689
  } catch (err) {
6548
6690
  err.entity = entity;
6549
- import_logging11.logEmitter.emit("error", err);
6691
+ import_logging12.logEmitter.emit("error", err);
6550
6692
  return null;
6551
6693
  }
6552
6694
  }));
@@ -6576,17 +6718,17 @@ function pushToSpace({
6576
6718
  if (existing) {
6577
6719
  const payload = { ...entity, sys: { id: entity.sys.id, type: "DesignToken", version: existing.sys.version } };
6578
6720
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6579
- import_logging11.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6721
+ import_logging12.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6580
6722
  return result;
6581
6723
  } else {
6582
6724
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DesignToken" } };
6583
6725
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6584
- import_logging11.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6726
+ import_logging12.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6585
6727
  return result;
6586
6728
  }
6587
6729
  } catch (err) {
6588
6730
  err.entity = entity;
6589
- import_logging11.logEmitter.emit("error", err);
6731
+ import_logging12.logEmitter.emit("error", err);
6590
6732
  return null;
6591
6733
  }
6592
6734
  }));
@@ -6605,17 +6747,17 @@ function pushToSpace({
6605
6747
  if (existing) {
6606
6748
  const payload = { ...entity, sys: { id: entity.sys.id, type: "Component", version: existing.sys.version } };
6607
6749
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6608
- import_logging11.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6750
+ import_logging12.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6609
6751
  results.push(result);
6610
6752
  } else {
6611
6753
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "Component" } };
6612
6754
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6613
- import_logging11.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6755
+ import_logging12.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6614
6756
  results.push(result);
6615
6757
  }
6616
6758
  } catch (err) {
6617
6759
  err.entity = entity;
6618
- import_logging11.logEmitter.emit("error", err);
6760
+ import_logging12.logEmitter.emit("error", err);
6619
6761
  }
6620
6762
  }
6621
6763
  ctx.data.components = results;
@@ -6647,17 +6789,17 @@ function pushToSpace({
6647
6789
  if (existing) {
6648
6790
  const payload = { ...entity, sys: { id: entity.sys.id, type: "ExperienceTemplate", version: existing.sys.version } };
6649
6791
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6650
- import_logging11.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6792
+ import_logging12.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6651
6793
  return result;
6652
6794
  } else {
6653
6795
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "ExperienceTemplate" } };
6654
6796
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6655
- import_logging11.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6797
+ import_logging12.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6656
6798
  return result;
6657
6799
  }
6658
6800
  } catch (err) {
6659
6801
  err.entity = entity;
6660
- import_logging11.logEmitter.emit("error", err);
6802
+ import_logging12.logEmitter.emit("error", err);
6661
6803
  return null;
6662
6804
  }
6663
6805
  }));
@@ -6689,17 +6831,17 @@ function pushToSpace({
6689
6831
  if (existing) {
6690
6832
  const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "ExperienceFragment", version: existing.sys.version } };
6691
6833
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6692
- import_logging11.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6834
+ import_logging12.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6693
6835
  results.push(result);
6694
6836
  } else {
6695
6837
  const payload = { ...omitSysAndOptimizationVariants(entity), component: entity.sys.component, sys: { id: entity.sys.id, type: "ExperienceFragment" } };
6696
6838
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6697
- import_logging11.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6839
+ import_logging12.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6698
6840
  results.push(result);
6699
6841
  }
6700
6842
  } catch (err) {
6701
6843
  err.entity = entity;
6702
- import_logging11.logEmitter.emit("error", err);
6844
+ import_logging12.logEmitter.emit("error", err);
6703
6845
  }
6704
6846
  }
6705
6847
  ctx.data.experienceFragments = results;
@@ -6731,17 +6873,17 @@ function pushToSpace({
6731
6873
  if (existing) {
6732
6874
  const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
6733
6875
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6734
- import_logging11.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6876
+ import_logging12.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6735
6877
  return result;
6736
6878
  } else {
6737
6879
  const payload = { ...omitSysAndOptimizationVariants(entity), experienceTemplate: entity.sys.experienceTemplate, sys: { id: entity.sys.id, type: "Experience" } };
6738
6880
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6739
- import_logging11.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6881
+ import_logging12.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6740
6882
  return result;
6741
6883
  }
6742
6884
  } catch (err) {
6743
6885
  err.entity = entity;
6744
- import_logging11.logEmitter.emit("error", err);
6886
+ import_logging12.logEmitter.emit("error", err);
6745
6887
  return null;
6746
6888
  }
6747
6889
  }));
@@ -6905,6 +7047,18 @@ function pushToSpace({
6905
7047
  ));
6906
7048
  }),
6907
7049
  skip: () => !includeExperienceOrchestration || skipContentPublishing || !(sourceData.dataAssemblies || []).length
7050
+ },
7051
+ {
7052
+ title: "Importing Releases",
7053
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
7054
+ const { supported: v2Releases, unsupported: v1Releases } = partitionReleasesBySchemaVersion(sourceData.releases || []);
7055
+ v1Releases.forEach(logUnsupportedRelease);
7056
+ const results = await Promise.all(v2Releases.map(
7057
+ (release) => importRelease(release, destinationDataById.releases?.get(release.transformed.sys.id), { client, spaceId, environmentId })
7058
+ ));
7059
+ ctx.data.releases = results.filter(Boolean);
7060
+ }),
7061
+ skip: () => !(sourceData.releases || []).length
6908
7062
  }
6909
7063
  ], listrOptions);
6910
7064
  }
@@ -6944,24 +7098,24 @@ async function importVariantsForParents({
6944
7098
  try {
6945
7099
  const payload = { ...omitVariantSys(variant), [parentRefField]: variant.sys[parentRefField] };
6946
7100
  const result = await ns.create({ spaceId, environmentId, [idParam]: parent2.sys.id }, payload);
6947
- import_logging11.logEmitter.emit("info", `CREATE ${label} ${result.sys.variant} (parent ${parent2.sys.id})`);
7101
+ import_logging12.logEmitter.emit("info", `CREATE ${label} ${result.sys.variant} (parent ${parent2.sys.id})`);
6948
7102
  created.push({ ...result, sys: { ...result.sys, publishedVersion: variant.sys.publishedVersion, archivedVersion: variant.sys.archivedVersion } });
6949
7103
  } catch (err) {
6950
7104
  err.entity = variant;
6951
- import_logging11.logEmitter.emit("error", err);
7105
+ import_logging12.logEmitter.emit("error", err);
6952
7106
  }
6953
7107
  }
6954
7108
  return { parentId: parent2.sys.id, created };
6955
7109
  }));
6956
7110
  }
6957
- function archiveEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue }) {
7111
+ function archiveEntities2({ entities: entities2, sourceEntities, client, spaceId, environmentId, requestQueue }) {
6958
7112
  const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
6959
- const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
7113
+ const entitiesToArchive = entities2.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
6960
7114
  return archiveEntities({ entities: entitiesToArchive, client, spaceId, environmentId, requestQueue });
6961
7115
  }
6962
- async function publishEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue, destinationEntitiesById }) {
7116
+ async function publishEntities2({ entities: entities2, sourceEntities, client, spaceId, environmentId, requestQueue, destinationEntitiesById }) {
6963
7117
  const entityIdsToPublish = sourceEntities.filter(({ original }) => original.sys.publishedVersion).map(({ original }) => original.sys.id);
6964
- let entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
7118
+ let entitiesToPublish = entities2.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
6965
7119
  const publishAllLocales = () => publishEntities({ entities: entitiesToPublish, client, spaceId, environmentId, requestQueue });
6966
7120
  const hasFieldStatus = sourceEntities.some(({ original }) => original.sys.fieldStatus);
6967
7121
  if (!hasFieldStatus) {
@@ -6982,7 +7136,7 @@ async function publishEntities2({ entities, sourceEntities, client, spaceId, env
6982
7136
  if (!localePublishing.skippedEntityIds.has(entity.sys.id)) {
6983
7137
  return true;
6984
7138
  }
6985
- import_logging11.logEmitter.emit("warning", `Not publishing ${entity.sys.type} ${entity.sys.id} because none of the locales it was published for exist in the destination environment`);
7139
+ import_logging12.logEmitter.emit("warning", `Not publishing ${entity.sys.type} ${entity.sys.id} because none of the locales it was published for exist in the destination environment`);
6986
7140
  return false;
6987
7141
  });
6988
7142
  }
@@ -7003,6 +7157,7 @@ __export(transformers_exports, {
7003
7157
  contentTypes: () => contentTypes,
7004
7158
  entries: () => entries,
7005
7159
  locales: () => locales,
7160
+ releases: () => releases,
7006
7161
  tags: () => tags,
7007
7162
  webhooks: () => webhooks
7008
7163
  });
@@ -7058,6 +7213,9 @@ function removeMetadataTags(entity, tagsEnabled = false) {
7058
7213
  }
7059
7214
  return entity;
7060
7215
  }
7216
+ function releases(release) {
7217
+ return release;
7218
+ }
7061
7219
 
7062
7220
  // lib/utils/sort-entries.ts
7063
7221
  function sortEntries(entries2) {
@@ -7307,24 +7465,24 @@ function upgradeExoResources(resources) {
7307
7465
  }
7308
7466
 
7309
7467
  // lib/transform/transform-space.ts
7310
- var spaceEntities = [
7468
+ var entities = [
7311
7469
  "contentTypes",
7312
7470
  "entries",
7313
7471
  "assets",
7314
7472
  "locales",
7315
7473
  "webhooks",
7316
- "tags"
7474
+ "tags",
7475
+ "releases"
7317
7476
  ];
7318
- function transform_space_default(sourceData, destinationData, customTransformers, entities = spaceEntities) {
7319
- const transformers = defaults_default(customTransformers, transformers_exports);
7477
+ function transform_space_default(sourceData, destinationData) {
7320
7478
  const baseSpaceData = upgradeExoResources(omit_default(sourceData, ...entities));
7321
7479
  sourceData.locales = sortLocales(sourceData.locales);
7322
7480
  const tagsEnabled = !!destinationData.tags;
7323
7481
  return entities.reduce((transformedSpaceData, type) => {
7324
- const sortedEntities = type === "tags" ? sourceData[type] ?? [] : sortEntries(sourceData[type] ?? []);
7482
+ const sortedEntities = type === "tags" || type === "releases" ? sourceData[type] ?? [] : sortEntries(sourceData[type] ?? []);
7325
7483
  const transformedEntities = sortedEntities.map((entity) => ({
7326
7484
  original: entity,
7327
- transformed: transformers[type](entity, destinationData[type], tagsEnabled)
7485
+ transformed: transformers_exports[type](entity, destinationData[type], tagsEnabled)
7328
7486
  }));
7329
7487
  transformedSpaceData[type] = transformedEntities;
7330
7488
  return transformedSpaceData;
@@ -7414,13 +7572,13 @@ var attachEntityName = (details, payload) => {
7414
7572
  });
7415
7573
  };
7416
7574
  var countInvalidEntities = (validationData) => {
7417
- const entityCount = validationData.reduce((entities, currentDetail) => {
7418
- if (!entities[currentDetail.path[0]]) {
7419
- entities[currentDetail.path[0]] = 1;
7575
+ const entityCount = validationData.reduce((entities2, currentDetail) => {
7576
+ if (!entities2[currentDetail.path[0]]) {
7577
+ entities2[currentDetail.path[0]] = 1;
7420
7578
  } else {
7421
- entities[currentDetail.path[0]]++;
7579
+ entities2[currentDetail.path[0]]++;
7422
7580
  }
7423
- return entities;
7581
+ return entities2;
7424
7582
  }, {});
7425
7583
  return Object.keys(entityCount).map((key) => `${key}:${entityCount[key]}`);
7426
7584
  };
@@ -7526,7 +7684,8 @@ var SUPPORTED_ENTITY_TYPES = [
7526
7684
  "experienceTemplates",
7527
7685
  "dataAssemblies",
7528
7686
  "experienceFragments",
7529
- "experiences"
7687
+ "experiences",
7688
+ "releases"
7530
7689
  ];
7531
7690
  async function parseOptions(params) {
7532
7691
  const defaultOptions = {
@@ -7619,7 +7778,7 @@ async function parseOptions(params) {
7619
7778
 
7620
7779
  // lib/utils/display-error-log.ts
7621
7780
  var import_date_fns2 = require("date-fns");
7622
- var import_logging12 = __toESM(require_logging());
7781
+ var import_logging13 = __toESM(require_logging());
7623
7782
  function displayErrorLog(errorLog) {
7624
7783
  if (errorLog.length) {
7625
7784
  const count = errorLog.reduce((count2, curr) => {
@@ -7634,7 +7793,7 @@ function displayErrorLog(errorLog) {
7634
7793
 
7635
7794
  The following ${count.errors} errors and ${count.warnings} warnings occurred:
7636
7795
  `);
7637
- errorLog.map((logMessage) => `${(0, import_date_fns2.format)((0, import_date_fns2.parseISO)(logMessage.ts), "HH:mm:ss")} - ${(0, import_logging12.formatLogMessageOneLine)(logMessage)}`).map((logMessage) => console.log(logMessage));
7796
+ errorLog.map((logMessage) => `${(0, import_date_fns2.format)((0, import_date_fns2.parseISO)(logMessage.ts), "HH:mm:ss")} - ${(0, import_logging13.formatLogMessageOneLine)(logMessage)}`).map((logMessage) => console.log(logMessage));
7638
7797
  return;
7639
7798
  }
7640
7799
  console.log("No errors or warnings occurred");
@@ -7666,7 +7825,7 @@ async function runContentfulImport(params) {
7666
7825
  intervalCap: options.rateLimit,
7667
7826
  carryoverConcurrencyCount: true
7668
7827
  });
7669
- (0, import_logging13.setupLogging)(log);
7828
+ (0, import_logging14.setupLogging)(log);
7670
7829
  const infoTable = new import_cli_table3.default(tableOptions);
7671
7830
  infoTable.push([{ colSpan: 2, content: "The following entities are going to be imported:" }]);
7672
7831
  Object.keys(options.content).forEach((type) => {
@@ -7793,7 +7952,7 @@ async function runContentfulImport(params) {
7793
7952
  const displayLog = log.filter((logMessage) => logMessage.level !== "info");
7794
7953
  displayErrorLog(displayLog);
7795
7954
  if (errorLog.length) {
7796
- return (0, import_logging13.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7955
+ return (0, import_logging14.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7797
7956
  const multiError = new ContentfulMultiError("Errors occurred");
7798
7957
  multiError.name = "ContentfulMultiError";
7799
7958
  multiError.errors = errorLog;