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.mjs CHANGED
@@ -4517,34 +4517,6 @@ var createBaseEach_default = createBaseEach;
4517
4517
  var baseEach = createBaseEach_default(baseForOwn_default);
4518
4518
  var baseEach_default = baseEach;
4519
4519
 
4520
- // node_modules/lodash-es/defaults.js
4521
- var objectProto18 = Object.prototype;
4522
- var hasOwnProperty15 = objectProto18.hasOwnProperty;
4523
- var defaults = baseRest_default(function(object, sources) {
4524
- object = Object(object);
4525
- var index = -1;
4526
- var length = sources.length;
4527
- var guard = length > 2 ? sources[2] : void 0;
4528
- if (guard && isIterateeCall_default(sources[0], sources[1], guard)) {
4529
- length = 1;
4530
- }
4531
- while (++index < length) {
4532
- var source = sources[index];
4533
- var props = keysIn_default(source);
4534
- var propsIndex = -1;
4535
- var propsLength = props.length;
4536
- while (++propsIndex < propsLength) {
4537
- var key = props[propsIndex];
4538
- var value = object[key];
4539
- if (value === void 0 || eq_default(value, objectProto18[key]) && !hasOwnProperty15.call(object, key)) {
4540
- object[key] = source[key];
4541
- }
4542
- }
4543
- }
4544
- return object;
4545
- });
4546
- var defaults_default = defaults;
4547
-
4548
4520
  // node_modules/lodash-es/last.js
4549
4521
  function last(array) {
4550
4522
  var length = array == null ? 0 : array.length;
@@ -4625,10 +4597,10 @@ function map(collection, iteratee) {
4625
4597
  var map_default = map;
4626
4598
 
4627
4599
  // node_modules/lodash-es/_baseHas.js
4628
- var objectProto19 = Object.prototype;
4629
- var hasOwnProperty16 = objectProto19.hasOwnProperty;
4600
+ var objectProto18 = Object.prototype;
4601
+ var hasOwnProperty15 = objectProto18.hasOwnProperty;
4630
4602
  function baseHas(object, key) {
4631
- return object != null && hasOwnProperty16.call(object, key);
4603
+ return object != null && hasOwnProperty15.call(object, key);
4632
4604
  }
4633
4605
  var baseHas_default = baseHas;
4634
4606
 
@@ -4682,8 +4654,8 @@ function negate(predicate) {
4682
4654
  var negate_default = negate;
4683
4655
 
4684
4656
  // node_modules/lodash-es/_baseUnset.js
4685
- var objectProto20 = Object.prototype;
4686
- var hasOwnProperty17 = objectProto20.hasOwnProperty;
4657
+ var objectProto19 = Object.prototype;
4658
+ var hasOwnProperty16 = objectProto19.hasOwnProperty;
4687
4659
  function baseUnset(object, path) {
4688
4660
  path = castPath_default(path, object);
4689
4661
  var index = -1, length = path.length;
@@ -4692,7 +4664,7 @@ function baseUnset(object, path) {
4692
4664
  }
4693
4665
  while (++index < length) {
4694
4666
  var key = toKey_default(path[index]);
4695
- if (key === "__proto__" && !hasOwnProperty17.call(object, "__proto__")) {
4667
+ if (key === "__proto__" && !hasOwnProperty16.call(object, "__proto__")) {
4696
4668
  return false;
4697
4669
  }
4698
4670
  if ((key === "constructor" || key === "prototype") && index < length - 1) {
@@ -4855,7 +4827,7 @@ var startCase = createCompounder_default(function(result, word, index) {
4855
4827
  var startCase_default = startCase;
4856
4828
 
4857
4829
  // lib/index.ts
4858
- var import_logging13 = __toESM(require_logging());
4830
+ var import_logging14 = __toESM(require_logging());
4859
4831
  var import_listr4 = __toESM(require_listr());
4860
4832
  import PQueue from "p-queue";
4861
4833
 
@@ -4889,17 +4861,17 @@ import Promise3 from "bluebird";
4889
4861
 
4890
4862
  // lib/utils/publish-exo-entities.ts
4891
4863
  var import_logging2 = __toESM(require_logging());
4892
- function filterExoEntitiesToPublish(entities, sourceEntities) {
4864
+ function filterExoEntitiesToPublish(entities2, sourceEntities) {
4893
4865
  const entityIdsToPublish = new Set(
4894
4866
  sourceEntities.filter((entity) => entity.sys.publishedVersion).map((entity) => entity.sys.id)
4895
4867
  );
4896
- return entities.filter((entity) => entityIdsToPublish.has(entity.sys.id));
4868
+ return entities2.filter((entity) => entityIdsToPublish.has(entity.sys.id));
4897
4869
  }
4898
- function filterExoEntitiesToUnpublish(entities, sourceEntities) {
4870
+ function filterExoEntitiesToUnpublish(entities2, sourceEntities) {
4899
4871
  const entityIdsPublishedInSource = new Set(
4900
4872
  sourceEntities.filter((entity) => entity.sys.publishedVersion).map((entity) => entity.sys.id)
4901
4873
  );
4902
- return entities.filter((entity) => entity.sys.publishedVersion && !entityIdsPublishedInSource.has(entity.sys.id));
4874
+ return entities2.filter((entity) => entity.sys.publishedVersion && !entityIdsPublishedInSource.has(entity.sys.id));
4903
4875
  }
4904
4876
  async function publishExoEntity(type, entity, publish) {
4905
4877
  try {
@@ -4961,6 +4933,17 @@ function isExoEntitlementError(err) {
4961
4933
  return false;
4962
4934
  }
4963
4935
  }
4936
+ function isTimelineEntitlementError(err) {
4937
+ if (!(err instanceof Error)) {
4938
+ return false;
4939
+ }
4940
+ try {
4941
+ const parsed = JSON.parse(err.message);
4942
+ return parsed?.status === 403 && parsed?.details?.reasons === "Timeline is not enabled for this organization";
4943
+ } catch {
4944
+ return false;
4945
+ }
4946
+ }
4964
4947
  var EXO_M1_FEATURE = "exoM1";
4965
4948
  async function spaceHasExoM1Entitlement(client, spaceId) {
4966
4949
  try {
@@ -4987,7 +4970,8 @@ var CURSOR_QUERY_METHODS = {
4987
4970
  experienceTemplates: { name: "experience templates", namespace: "experienceTemplate" },
4988
4971
  experienceFragments: { name: "experience fragments", namespace: "experienceFragment" },
4989
4972
  dataAssemblies: { name: "data assemblies", namespace: "dataAssembly" },
4990
- experiences: { name: "experiences", namespace: "experience" }
4973
+ experiences: { name: "experiences", namespace: "experience" },
4974
+ releases: { name: "releases", namespace: "release" }
4991
4975
  };
4992
4976
  var ENTITY_METHODS = {
4993
4977
  contentTypes: { name: "content types", ns: "contentType" },
@@ -5090,11 +5074,26 @@ async function cursorPaginatedQuery({ client, spaceId, environmentId, type, requ
5090
5074
  const allItems = [];
5091
5075
  do {
5092
5076
  const items = await requestQueue.add(async () => {
5093
- const response = await client[namespace].getMany({
5094
- spaceId,
5095
- environmentId,
5096
- query: { limit: BATCH_SIZE_LIMIT, ...pageNext && { pageNext } }
5097
- });
5077
+ let response;
5078
+ if (type === "releases") {
5079
+ response = await client.release.query({
5080
+ environmentId,
5081
+ spaceId,
5082
+ query: {
5083
+ "metadata.annotations.Contentful:Timeline.type[nin]": "Staging,Hidden",
5084
+ "sys.schemaVersion": "Release.v2",
5085
+ "sys.status[in]": "active",
5086
+ limit: BATCH_SIZE_LIMIT,
5087
+ ...pageNext && { pageNext }
5088
+ }
5089
+ });
5090
+ } else {
5091
+ response = await client[namespace].getMany({
5092
+ spaceId,
5093
+ environmentId,
5094
+ query: { limit: BATCH_SIZE_LIMIT, ...pageNext && { pageNext } }
5095
+ });
5096
+ }
5098
5097
  totalFetched += response.items.length;
5099
5098
  import_logging3.logEmitter.emit("info", `Fetched ${totalFetched} ${entityTypeName}`);
5100
5099
  pageNext = response.pages?.next;
@@ -5111,6 +5110,8 @@ async function cursorPaginatedQueryOrWarn(params) {
5111
5110
  const { name: entityTypeName } = CURSOR_QUERY_METHODS[params.type];
5112
5111
  if (isExoEntitlementError(err)) {
5113
5112
  import_logging3.logEmitter.emit("error", new Error(`Skipping ${entityTypeName} import: Experience Orchestration (ExO) is not enabled for this space`));
5113
+ } else if (isTimelineEntitlementError(err)) {
5114
+ import_logging3.logEmitter.emit("error", new Error(`Skipping ${entityTypeName} import: Timeline (Releases) is not enabled for this organization`));
5114
5115
  } else {
5115
5116
  import_logging3.logEmitter.emit("error", err instanceof Error ? err : new Error(String(err)));
5116
5117
  }
@@ -5140,7 +5141,8 @@ async function getDestinationData({
5140
5141
  experienceFragments: [],
5141
5142
  dataAssemblies: [],
5142
5143
  designTokens: [],
5143
- webhooks: []
5144
+ webhooks: [],
5145
+ releases: []
5144
5146
  };
5145
5147
  sourceData = {
5146
5148
  ...result,
@@ -5201,6 +5203,7 @@ async function getDestinationData({
5201
5203
  requestQueue
5202
5204
  });
5203
5205
  }
5206
+ result.releases = cursorPaginatedQueryOrWarn({ client, spaceId, environmentId, type: "releases", requestQueue });
5204
5207
  if (includeExperienceOrchestration && client) {
5205
5208
  const sourceHasDesignTokens = Boolean(sourceData.designTokens?.length);
5206
5209
  const sourceHasComponents = Boolean(sourceData.components?.length);
@@ -5238,7 +5241,7 @@ async function getDestinationData({
5238
5241
  }
5239
5242
 
5240
5243
  // lib/tasks/push-to-space/push-to-space.ts
5241
- var import_logging11 = __toESM(require_logging());
5244
+ var import_logging12 = __toESM(require_logging());
5242
5245
  var import_listr2 = __toESM(require_listr());
5243
5246
  import Listr from "listr";
5244
5247
  import verboseRenderer from "listr-verbose-renderer";
@@ -5340,14 +5343,14 @@ async function processAssets({
5340
5343
  // lib/tasks/push-to-space/creation.ts
5341
5344
  var import_get_entity_name2 = __toESM(require_get_entity_name());
5342
5345
  var import_logging5 = __toESM(require_logging());
5343
- function createEntities({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5344
- return createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, skipUpdates, requestQueue });
5346
+ function createEntities({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5347
+ return createEntitiesWithConcurrency({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue });
5345
5348
  }
5346
- function createLocales({ context, entities, destinationEntitiesById, requestQueue }) {
5347
- return createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue });
5349
+ function createLocales({ context, entities: entities2, destinationEntitiesById, requestQueue }) {
5350
+ return createEntitiesInSequence({ context, entities: entities2, destinationEntitiesById, requestQueue });
5348
5351
  }
5349
- async function createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5350
- const pendingCreatedEntities = entities.map((entity) => {
5352
+ async function createEntitiesWithConcurrency({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5353
+ const pendingCreatedEntities = entities2.map((entity) => {
5351
5354
  const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
5352
5355
  const updateOperation = skipUpdates ? "skip" : "update";
5353
5356
  const operation = destinationEntity ? updateOperation : "create";
@@ -5368,9 +5371,9 @@ async function createEntitiesWithConcurrency({ context, entities, destinationEnt
5368
5371
  const createdEntities = await Promise.all(pendingCreatedEntities);
5369
5372
  return createdEntities.filter((entity) => entity);
5370
5373
  }
5371
- async function createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue }) {
5374
+ async function createEntitiesInSequence({ context, entities: entities2, destinationEntitiesById, requestQueue }) {
5372
5375
  const createdEntities = [];
5373
- for (const entity of entities) {
5376
+ for (const entity of entities2) {
5374
5377
  const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
5375
5378
  const operation = destinationEntity ? "update" : "create";
5376
5379
  try {
@@ -5389,8 +5392,8 @@ async function createEntitiesInSequence({ context, entities, destinationEntities
5389
5392
  }
5390
5393
  return createdEntities;
5391
5394
  }
5392
- async function createEntries({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5393
- const createdEntries = await Promise.all(entities.map((entry) => {
5395
+ async function createEntries({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5396
+ const createdEntries = await Promise.all(entities2.map((entry) => {
5394
5397
  return createEntry({ entry, context, destinationEntitiesById, skipUpdates, requestQueue });
5395
5398
  }));
5396
5399
  return createdEntries.filter((entry) => entry);
@@ -5645,27 +5648,27 @@ function archiveEntity(client, spaceId, environmentId, entity) {
5645
5648
  }
5646
5649
  throw new Error(`archiveEntity: unsupported type "${type}"`);
5647
5650
  }
5648
- async function publishEntities({ entities, client, spaceId, environmentId, requestQueue, localePublishing }) {
5649
- if (entities.length === 0) {
5651
+ async function publishEntities({ entities: entities2, client, spaceId, environmentId, requestQueue, localePublishing }) {
5652
+ if (entities2.length === 0) {
5650
5653
  import_logging7.logEmitter.emit("info", "Skipping publishing since zero valid entities passed");
5651
5654
  return [];
5652
5655
  }
5653
- const entity = entities[0];
5656
+ const entity = entities2[0];
5654
5657
  const type = entity.sys.type || "unknown type";
5655
- import_logging7.logEmitter.emit("info", `Publishing ${entities.length} ${type}s`);
5656
- const result = await runQueue(entities, [], client, spaceId, environmentId, requestQueue, localePublishing);
5658
+ import_logging7.logEmitter.emit("info", `Publishing ${entities2.length} ${type}s`);
5659
+ const result = await runQueue(entities2, [], client, spaceId, environmentId, requestQueue, localePublishing);
5657
5660
  import_logging7.logEmitter.emit("info", `Successfully published ${result.length} ${type}s`);
5658
5661
  return result;
5659
5662
  }
5660
- async function archiveEntities({ entities, client, spaceId, environmentId, requestQueue }) {
5661
- if (entities.length === 0) {
5663
+ async function archiveEntities({ entities: entities2, client, spaceId, environmentId, requestQueue }) {
5664
+ if (entities2.length === 0) {
5662
5665
  import_logging7.logEmitter.emit("info", "Skipping archiving since zero valid entities passed");
5663
5666
  return [];
5664
5667
  }
5665
- const entity = entities[0];
5668
+ const entity = entities2[0];
5666
5669
  const type = entity.sys.type || "unknown type";
5667
- import_logging7.logEmitter.emit("info", `Archiving ${entities.length} ${type}s`);
5668
- const pendingArchivedEntities = entities.map((entity2) => {
5670
+ import_logging7.logEmitter.emit("info", `Archiving ${entities2.length} ${type}s`);
5671
+ const pendingArchivedEntities = entities2.map((entity2) => {
5669
5672
  return requestQueue.add(async () => {
5670
5673
  try {
5671
5674
  const archivedEntity = await archiveEntity(client, spaceId, environmentId, entity2);
@@ -5753,6 +5756,55 @@ async function runQueue(queue, result = [], client, spaceId, environmentId, requ
5753
5756
  return result;
5754
5757
  }
5755
5758
 
5759
+ // lib/tasks/push-to-space/releases.ts
5760
+ var import_logging8 = __toESM(require_logging());
5761
+ function partitionReleasesBySchemaVersion(releases2) {
5762
+ return {
5763
+ supported: releases2.filter((release) => release.transformed.sys.schemaVersion === "Release.v2"),
5764
+ unsupported: releases2.filter((release) => release.transformed.sys.schemaVersion !== "Release.v2")
5765
+ };
5766
+ }
5767
+ function logUnsupportedRelease(release) {
5768
+ import_logging8.logEmitter.emit("error", new Error(
5769
+ `Skipping Release "${release.transformed.sys.id}": only Release.v2 ("Releases") is supported, got schemaVersion "${release.transformed.sys.schemaVersion}"`
5770
+ ));
5771
+ }
5772
+ function buildReleaseCreatePayload(release) {
5773
+ const { id: _sourceId, ...sourceSysWithoutId } = release.transformed.sys;
5774
+ return {
5775
+ ...release.transformed,
5776
+ entities: release.transformed.entities,
5777
+ sys: { ...sourceSysWithoutId, type: "Release", schemaVersion: "Release.v2" }
5778
+ };
5779
+ }
5780
+ function buildReleaseUpdatePayload(release) {
5781
+ return {
5782
+ ...release.transformed,
5783
+ entities: release.transformed.entities,
5784
+ sys: { type: "Release", schemaVersion: "Release.v2" }
5785
+ };
5786
+ }
5787
+ async function importRelease(release, existing, context) {
5788
+ const { client, spaceId, environmentId } = context;
5789
+ try {
5790
+ if (existing) {
5791
+ const result2 = await client.release.update(
5792
+ { spaceId, environmentId, releaseId: existing.sys.id, version: existing.sys.version },
5793
+ buildReleaseUpdatePayload(release)
5794
+ );
5795
+ import_logging8.logEmitter.emit("info", `UPDATE Release ${existing.sys.id}`);
5796
+ return result2;
5797
+ }
5798
+ const result = await client.release.create({ spaceId, environmentId }, buildReleaseCreatePayload(release));
5799
+ import_logging8.logEmitter.emit("info", `CREATE Release ${result.sys.id}`);
5800
+ return result;
5801
+ } catch (err) {
5802
+ err.entity = release.transformed;
5803
+ import_logging8.logEmitter.emit("error", err);
5804
+ return null;
5805
+ }
5806
+ }
5807
+
5756
5808
  // lib/utils/graphql-schema-backoff.ts
5757
5809
  var GRAPHQL_SCHEMA_STALE_DELAYS_MS = [500, 1500, 6e3];
5758
5810
  function isGraphQLSchemaStaleError(err) {
@@ -5879,18 +5931,18 @@ function sortExperienceFragments(experienceFragments) {
5879
5931
  }
5880
5932
 
5881
5933
  // lib/utils/sort-or-report.ts
5882
- var import_logging8 = __toESM(require_logging());
5934
+ var import_logging9 = __toESM(require_logging());
5883
5935
  function sortOrReport(sortFn) {
5884
5936
  try {
5885
5937
  return sortFn();
5886
5938
  } catch (err) {
5887
- import_logging8.logEmitter.emit("error", err);
5939
+ import_logging9.logEmitter.emit("error", err);
5888
5940
  throw err;
5889
5941
  }
5890
5942
  }
5891
5943
 
5892
5944
  // lib/utils/import-exo-folders.ts
5893
- var import_logging9 = __toESM(require_logging());
5945
+ var import_logging10 = __toESM(require_logging());
5894
5946
  var FOLDER_CONCEPT_PREFIX = "contentful.folder-";
5895
5947
  var PARENT_FOLDER_GROUP_IDS = {
5896
5948
  designToken: "contentful.folder-group-designToken",
@@ -5899,6 +5951,29 @@ var PARENT_FOLDER_GROUP_IDS = {
5899
5951
  fragment: "contentful.folder-group-fragment",
5900
5952
  experience: "contentful.folder-group-experience"
5901
5953
  };
5954
+ var VERSION_CONFLICT_MAX_ATTEMPTS = 5;
5955
+ var VERSION_CONFLICT_RETRY_DELAY_MS = 250;
5956
+ var MissingExoFolderGroupSchemesError = class extends Error {
5957
+ constructor(missingSchemeIds) {
5958
+ super(`Missing ExO folder-group concept scheme(s) in the destination organization: ${missingSchemeIds.join(", ")}`);
5959
+ this.name = "MissingExoFolderGroupSchemesError";
5960
+ this.missingSchemeIds = missingSchemeIds;
5961
+ }
5962
+ };
5963
+ var SourceExoFolderConceptReadError = class extends Error {
5964
+ constructor(sourceConceptId, error) {
5965
+ const reason = error instanceof Error ? error.message : String(error);
5966
+ super(`Unable to read source ExO folder concept ${sourceConceptId}: ${reason}`);
5967
+ this.name = "SourceExoFolderConceptReadError";
5968
+ this.sourceConceptId = sourceConceptId;
5969
+ }
5970
+ };
5971
+ var SourceOrganizationResolutionError = class extends Error {
5972
+ constructor(message) {
5973
+ super(message);
5974
+ this.name = "SourceOrganizationResolutionError";
5975
+ }
5976
+ };
5902
5977
  var ENTITY_TYPE_TO_PARENT_GROUP_ID = {
5903
5978
  designTokens: PARENT_FOLDER_GROUP_IDS.designToken,
5904
5979
  components: PARENT_FOLDER_GROUP_IDS.componentType,
@@ -5907,27 +5982,52 @@ var ENTITY_TYPE_TO_PARENT_GROUP_ID = {
5907
5982
  experiences: PARENT_FOLDER_GROUP_IDS.experience
5908
5983
  };
5909
5984
  function getSourceSpaceId(sourceEntities) {
5910
- for (const entities of Object.values(sourceEntities)) {
5911
- for (const entity of entities ?? []) {
5985
+ for (const entities2 of Object.values(sourceEntities)) {
5986
+ for (const entity of entities2 ?? []) {
5912
5987
  const spaceId = entity.sys?.space?.sys?.id;
5913
5988
  if (spaceId) return spaceId;
5914
5989
  }
5915
5990
  }
5916
5991
  return void 0;
5917
5992
  }
5918
- async function ensureParentFolderGroupsExist(client, organizationId) {
5993
+ function isVersionMismatchError(err) {
5994
+ return err?.error?.sys?.id === "VersionMismatch";
5995
+ }
5996
+ async function waitForVersionConflictRetry(attempt) {
5997
+ await new Promise((resolve2) => setTimeout(resolve2, VERSION_CONFLICT_RETRY_DELAY_MS * attempt));
5998
+ }
5999
+ async function resolveSourceOrganizationId(client, sourceSpaceId) {
6000
+ if (!sourceSpaceId) {
6001
+ throw new SourceOrganizationResolutionError(
6002
+ "Unable to resolve the source organization because the exported ExO entity does not include a source space ID"
6003
+ );
6004
+ }
6005
+ try {
6006
+ const sourceSpace = await client.space.get({ spaceId: sourceSpaceId });
6007
+ const sourceOrganizationId = sourceSpace?.sys?.organization?.sys?.id;
6008
+ if (!sourceOrganizationId) {
6009
+ throw new Error(`source space ${sourceSpaceId} has no organization link`);
6010
+ }
6011
+ return sourceOrganizationId;
6012
+ } catch (err) {
6013
+ if (err instanceof SourceOrganizationResolutionError) throw err;
6014
+ const reason = err instanceof Error ? err.message : String(err);
6015
+ throw new SourceOrganizationResolutionError(
6016
+ `Unable to resolve the source organization for space ${sourceSpaceId}: ${reason}`
6017
+ );
6018
+ }
6019
+ }
6020
+ async function ensureParentFolderGroupsExist(client, destinationOrganizationId, requiredParentGroupIds) {
6021
+ if (requiredParentGroupIds.size === 0) return /* @__PURE__ */ new Map();
5919
6022
  const { items } = await client.conceptScheme.getMany({
5920
- organizationId,
6023
+ organizationId: destinationOrganizationId,
5921
6024
  query: { purpose: "internal" }
5922
6025
  });
5923
6026
  const existingIds = new Set(items.map((s) => s.sys.id));
5924
- const allParentFolderGroupsExist = Object.values(PARENT_FOLDER_GROUP_IDS).every((id) => existingIds.has(id));
5925
- if (!allParentFolderGroupsExist) {
5926
- return /* @__PURE__ */ new Map();
5927
- }
5928
- const parentGroupIdSet = new Set(Object.values(PARENT_FOLDER_GROUP_IDS));
6027
+ const missingSchemeIds = [...requiredParentGroupIds].filter((id) => !existingIds.has(id));
6028
+ if (missingSchemeIds.length > 0) throw new MissingExoFolderGroupSchemesError(missingSchemeIds);
5929
6029
  return new Map(
5930
- items.filter((s) => parentGroupIdSet.has(s.sys.id)).map((s) => [s.sys.id, s])
6030
+ items.filter((s) => requiredParentGroupIds.has(s.sys.id)).map((s) => [s.sys.id, s])
5931
6031
  );
5932
6032
  }
5933
6033
  function deriveChildConceptMap(sourceEntities, destinationSpaceId) {
@@ -5946,33 +6046,54 @@ function deriveChildConceptMap(sourceEntities, destinationSpaceId) {
5946
6046
  }
5947
6047
  return childConceptMap;
5948
6048
  }
5949
- async function createOrPatchChildConcepts(client, organizationId, destinationSpaceId, childConceptMap) {
6049
+ async function createOrPatchChildConcepts(client, sourceOrganizationId, destinationOrganizationId, destinationSpaceId, childConceptMap) {
5950
6050
  const spaceLink = { sys: { type: "Link", linkType: "Space", id: destinationSpaceId } };
6051
+ const existingDestinationConcepts = /* @__PURE__ */ new Map();
5951
6052
  for (const [sourceConceptId, { destConceptId }] of childConceptMap) {
5952
- let prefLabel = { "en-US": destConceptId };
5953
- try {
5954
- const sourceConcept = await client.concept.get({ organizationId, conceptId: sourceConceptId });
5955
- if (sourceConcept?.prefLabel) prefLabel = sourceConcept.prefLabel;
5956
- } catch {
5957
- }
5958
6053
  let existing = null;
5959
6054
  try {
5960
- existing = await client.concept.get({ organizationId, conceptId: destConceptId });
6055
+ existing = await client.concept.get({
6056
+ organizationId: destinationOrganizationId,
6057
+ conceptId: destConceptId
6058
+ });
5961
6059
  } catch (err) {
5962
6060
  if (err?.name !== "NotFound") {
5963
- import_logging9.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
6061
+ import_logging10.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
5964
6062
  }
5965
6063
  }
6064
+ existingDestinationConcepts.set(sourceConceptId, existing);
6065
+ }
6066
+ const sourceConceptLabels = /* @__PURE__ */ new Map();
6067
+ for (const [sourceConceptId] of childConceptMap) {
6068
+ if (existingDestinationConcepts.get(sourceConceptId)) {
6069
+ continue;
6070
+ }
6071
+ try {
6072
+ const sourceConcept = await client.concept.get({
6073
+ organizationId: sourceOrganizationId,
6074
+ conceptId: sourceConceptId
6075
+ });
6076
+ if (!sourceConcept?.prefLabel) {
6077
+ throw new Error("source concept has no prefLabel");
6078
+ }
6079
+ sourceConceptLabels.set(sourceConceptId, sourceConcept.prefLabel);
6080
+ } catch (err) {
6081
+ throw new SourceExoFolderConceptReadError(sourceConceptId, err);
6082
+ }
6083
+ }
6084
+ for (const [sourceConceptId, { destConceptId }] of childConceptMap) {
6085
+ const existing = existingDestinationConcepts.get(sourceConceptId);
5966
6086
  if (!existing) {
6087
+ const prefLabel = sourceConceptLabels.get(sourceConceptId);
5967
6088
  try {
5968
6089
  await client.concept.createWithId(
5969
- { organizationId, conceptId: destConceptId },
6090
+ { organizationId: destinationOrganizationId, conceptId: destConceptId },
5970
6091
  // @ts-expect-error - CMA.js type needs to be updated to be aware of purpose: 'internal'
5971
6092
  { purpose: "internal", prefLabel, metadata: { spaces: [spaceLink] } }
5972
6093
  );
5973
- import_logging9.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
6094
+ import_logging10.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
5974
6095
  } catch (err) {
5975
- import_logging9.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
6096
+ import_logging10.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
5976
6097
  }
5977
6098
  } else {
5978
6099
  const patches = [];
@@ -5983,37 +6104,49 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5983
6104
  if (patches.length > 0) {
5984
6105
  try {
5985
6106
  await client.concept.patch(
5986
- { organizationId, conceptId: destConceptId, version: existing.sys.version },
6107
+ { organizationId: destinationOrganizationId, conceptId: destConceptId, version: existing.sys.version },
5987
6108
  patches
5988
6109
  );
5989
- import_logging9.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
6110
+ import_logging10.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
5990
6111
  } catch (err) {
5991
- import_logging9.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
6112
+ import_logging10.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
5992
6113
  }
5993
6114
  }
5994
6115
  }
5995
6116
  }
5996
6117
  }
5997
- async function linkChildConceptsToParentGroups(client, organizationId, childConceptMap, parentGroups) {
6118
+ async function linkChildConceptsToParentGroups(client, destinationOrganizationId, childConceptMap, parentGroups) {
5998
6119
  for (const [, { destConceptId, parentGroupId }] of childConceptMap) {
5999
- const parentGroup = parentGroups.get(parentGroupId);
6000
- if (!parentGroup) continue;
6001
- const alreadyLinked = (parentGroup.concepts ?? []).some((c) => c.sys.id === destConceptId);
6002
- if (alreadyLinked) continue;
6003
- try {
6004
- const updated = await client.conceptScheme.patch(
6005
- { organizationId, conceptSchemeId: parentGroupId, version: parentGroup.sys.version },
6006
- [{ op: "add", path: "/concepts/-", value: { sys: { type: "Link", linkType: "TaxonomyConcept", id: destConceptId } } }]
6007
- );
6008
- parentGroups.set(parentGroupId, updated);
6009
- import_logging9.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
6010
- } catch (err) {
6011
- import_logging9.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
6120
+ let parentGroup = parentGroups.get(parentGroupId);
6121
+ for (let attempt = 1; parentGroup && attempt <= VERSION_CONFLICT_MAX_ATTEMPTS; attempt++) {
6122
+ const alreadyLinked = (parentGroup.concepts ?? []).some((c) => c.sys.id === destConceptId);
6123
+ if (alreadyLinked) break;
6124
+ try {
6125
+ const updated = await client.conceptScheme.patch(
6126
+ { organizationId: destinationOrganizationId, conceptSchemeId: parentGroupId, version: parentGroup.sys.version },
6127
+ [{ op: "add", path: "/concepts/-", value: { sys: { type: "Link", linkType: "TaxonomyConcept", id: destConceptId } } }]
6128
+ );
6129
+ parentGroups.set(parentGroupId, updated);
6130
+ import_logging10.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
6131
+ break;
6132
+ } catch (err) {
6133
+ if (!isVersionMismatchError(err) || attempt === VERSION_CONFLICT_MAX_ATTEMPTS) {
6134
+ import_logging10.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
6135
+ break;
6136
+ }
6137
+ import_logging10.logEmitter.emit("warning", `Version mismatch linking child concept ${destConceptId} to parent group ${parentGroupId}; retrying`);
6138
+ await waitForVersionConflictRetry(attempt);
6139
+ parentGroup = await client.conceptScheme.get({
6140
+ organizationId: destinationOrganizationId,
6141
+ conceptSchemeId: parentGroupId
6142
+ });
6143
+ parentGroups.set(parentGroupId, parentGroup);
6144
+ }
6012
6145
  }
6013
6146
  }
6014
6147
  }
6015
- function rewriteEntityFolderConcepts(entities, childConceptMap) {
6016
- for (const entity of entities) {
6148
+ function rewriteEntityFolderConcepts(entities2, childConceptMap) {
6149
+ for (const entity of entities2) {
6017
6150
  if (!entity.metadata?.concepts) continue;
6018
6151
  entity.metadata.concepts = entity.metadata.concepts.map((link) => {
6019
6152
  const entry = childConceptMap.get(link.sys.id);
@@ -6024,25 +6157,31 @@ function rewriteEntityFolderConcepts(entities, childConceptMap) {
6024
6157
  }
6025
6158
  async function importExoFolders({
6026
6159
  client,
6027
- organizationId,
6160
+ destinationOrganizationId,
6028
6161
  destinationSpaceId,
6029
6162
  sourceEntities
6030
6163
  }) {
6031
6164
  const sourceSpaceId = getSourceSpaceId(sourceEntities);
6032
6165
  if (sourceSpaceId === destinationSpaceId) {
6033
- import_logging9.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
6034
- return;
6035
- }
6036
- const parentGroups = await ensureParentFolderGroupsExist(client, organizationId);
6037
- if (parentGroups.size === 0) {
6038
- 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.");
6166
+ import_logging10.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
6039
6167
  return;
6040
6168
  }
6041
6169
  const childConceptMap = deriveChildConceptMap(sourceEntities, destinationSpaceId);
6042
6170
  if (childConceptMap.size === 0) return;
6043
- import_logging9.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
6044
- await createOrPatchChildConcepts(client, organizationId, destinationSpaceId, childConceptMap);
6045
- await linkChildConceptsToParentGroups(client, organizationId, childConceptMap, parentGroups);
6171
+ const requiredParentGroupIds = new Set(
6172
+ [...childConceptMap.values()].map(({ parentGroupId }) => parentGroupId)
6173
+ );
6174
+ const sourceOrganizationId = await resolveSourceOrganizationId(client, sourceSpaceId);
6175
+ const parentGroups = await ensureParentFolderGroupsExist(client, destinationOrganizationId, requiredParentGroupIds);
6176
+ import_logging10.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
6177
+ await createOrPatchChildConcepts(
6178
+ client,
6179
+ sourceOrganizationId,
6180
+ destinationOrganizationId,
6181
+ destinationSpaceId,
6182
+ childConceptMap
6183
+ );
6184
+ await linkChildConceptsToParentGroups(client, destinationOrganizationId, childConceptMap, parentGroups);
6046
6185
  const allEntities = [
6047
6186
  ...sourceEntities.designTokens ?? [],
6048
6187
  ...sourceEntities.components ?? [],
@@ -6106,7 +6245,7 @@ function buildLocalePublishPlan(sourceEntities, destinationLocaleCodes, { destin
6106
6245
  }
6107
6246
 
6108
6247
  // lib/utils/destination-locales.ts
6109
- var import_logging10 = __toESM(require_logging());
6248
+ var import_logging11 = __toESM(require_logging());
6110
6249
  var cache = /* @__PURE__ */ new WeakMap();
6111
6250
  function getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueue }) {
6112
6251
  let byEnvironment = cache.get(client);
@@ -6118,7 +6257,7 @@ function getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueu
6118
6257
  let localeCodes = byEnvironment.get(key);
6119
6258
  if (!localeCodes) {
6120
6259
  localeCodes = batchedPageQuery({ client, spaceId, environmentId, type: "locales", requestQueue }).then((items) => items.map((locale) => locale.code)).catch((err) => {
6121
- import_logging10.logEmitter.emit("warning", `Could not read the locales of the destination environment, falling back to publishing all locales: ${err.message}`);
6260
+ import_logging11.logEmitter.emit("warning", `Could not read the locales of the destination environment, falling back to publishing all locales: ${err.message}`);
6122
6261
  return null;
6123
6262
  });
6124
6263
  byEnvironment.set(key, localeCodes);
@@ -6137,7 +6276,7 @@ async function withGraphQLSchemaBackoff(fn) {
6137
6276
  throw err;
6138
6277
  }
6139
6278
  const delay = GRAPHQL_SCHEMA_STALE_DELAYS_MS[attempt];
6140
- import_logging11.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
6279
+ import_logging12.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
6141
6280
  await new Promise((resolve2) => setTimeout(resolve2, delay));
6142
6281
  lastErr = err;
6143
6282
  }
@@ -6188,15 +6327,15 @@ function pushToSpace({
6188
6327
  ...sourceData.experienceFragments || []
6189
6328
  ].some((parent2) => (parent2.optimizationVariants || []).some(isRealOptimizationVariant));
6190
6329
  if (includeExperienceOrchestration && !skipExoVariants && hasOptimizationVariants) {
6191
- 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.");
6330
+ 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.");
6192
6331
  }
6193
6332
  listrOptions = listrOptions || {
6194
6333
  renderer: verboseRenderer
6195
6334
  };
6196
6335
  const destinationDataById = {};
6197
- for (const [entityType, entities] of Object.entries(destinationData)) {
6336
+ for (const [entityType, entities2] of Object.entries(destinationData)) {
6198
6337
  const entitiesById = /* @__PURE__ */ new Map();
6199
- for (const entity of entities) {
6338
+ for (const entity of entities2) {
6200
6339
  entitiesById.set(entity.sys.id, entity);
6201
6340
  }
6202
6341
  destinationDataById[entityType] = entitiesById;
@@ -6285,7 +6424,7 @@ function pushToSpace({
6285
6424
  const ctEditorInterface = await requestQueue.add(
6286
6425
  () => client.editorInterface.get({ spaceId, environmentId, contentTypeId: contentType.sys.id })
6287
6426
  );
6288
- import_logging11.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6427
+ import_logging12.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6289
6428
  const updatedData = {
6290
6429
  ...ctEditorInterface,
6291
6430
  controls: editorInterface.controls,
@@ -6303,7 +6442,7 @@ function pushToSpace({
6303
6442
  return updatedEditorInterface;
6304
6443
  } catch (err) {
6305
6444
  err.entity = editorInterface;
6306
- import_logging11.logEmitter.emit("error", err);
6445
+ import_logging12.logEmitter.emit("error", err);
6307
6446
  return null;
6308
6447
  }
6309
6448
  });
@@ -6321,7 +6460,7 @@ function pushToSpace({
6321
6460
  for (const file of Object.values(asset.transformed.fields.file)) {
6322
6461
  allPendingUploads.push(requestQueue.add(async () => {
6323
6462
  try {
6324
- import_logging11.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6463
+ import_logging12.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6325
6464
  const assetStream = await getAssetStreamForURL(file.upload, assetsDirectory);
6326
6465
  const upload = await client.upload.create(
6327
6466
  { spaceId, environmentId },
@@ -6337,7 +6476,7 @@ function pushToSpace({
6337
6476
  };
6338
6477
  return upload;
6339
6478
  } catch (err) {
6340
- import_logging11.logEmitter.emit("error", err);
6479
+ import_logging12.logEmitter.emit("error", err);
6341
6480
  }
6342
6481
  }));
6343
6482
  }
@@ -6472,7 +6611,7 @@ function pushToSpace({
6472
6611
  const space = await client.space.get({ spaceId });
6473
6612
  await importExoFolders({
6474
6613
  client,
6475
- organizationId: space.sys.organization.sys.id,
6614
+ destinationOrganizationId: space.sys.organization.sys.id,
6476
6615
  destinationSpaceId: spaceId,
6477
6616
  sourceEntities: {
6478
6617
  designTokens: sourceData.designTokens,
@@ -6483,7 +6622,10 @@ function pushToSpace({
6483
6622
  }
6484
6623
  });
6485
6624
  } catch (error) {
6486
- import_logging11.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6625
+ if (error instanceof MissingExoFolderGroupSchemesError || error instanceof SourceOrganizationResolutionError || error instanceof SourceExoFolderConceptReadError) {
6626
+ throw error;
6627
+ }
6628
+ import_logging12.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6487
6629
  }
6488
6630
  }),
6489
6631
  skip: () => !includeExperienceOrchestration
@@ -6501,19 +6643,19 @@ function pushToSpace({
6501
6643
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6502
6644
  payload
6503
6645
  ));
6504
- import_logging11.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6646
+ import_logging12.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6505
6647
  } else {
6506
6648
  const payload = { ...omitSys(entity), sys: buildDataAssemblySys(entity, 0) };
6507
6649
  result = await withGraphQLSchemaBackoff(() => client.dataAssembly.update(
6508
6650
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6509
6651
  payload
6510
6652
  ));
6511
- import_logging11.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6653
+ import_logging12.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6512
6654
  }
6513
6655
  return result;
6514
6656
  } catch (err) {
6515
6657
  err.entity = entity;
6516
- import_logging11.logEmitter.emit("error", err);
6658
+ import_logging12.logEmitter.emit("error", err);
6517
6659
  return null;
6518
6660
  }
6519
6661
  }));
@@ -6543,17 +6685,17 @@ function pushToSpace({
6543
6685
  if (existing) {
6544
6686
  const payload = { ...entity, sys: { id: entity.sys.id, type: "DesignToken", version: existing.sys.version } };
6545
6687
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6546
- import_logging11.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6688
+ import_logging12.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6547
6689
  return result;
6548
6690
  } else {
6549
6691
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DesignToken" } };
6550
6692
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6551
- import_logging11.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6693
+ import_logging12.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6552
6694
  return result;
6553
6695
  }
6554
6696
  } catch (err) {
6555
6697
  err.entity = entity;
6556
- import_logging11.logEmitter.emit("error", err);
6698
+ import_logging12.logEmitter.emit("error", err);
6557
6699
  return null;
6558
6700
  }
6559
6701
  }));
@@ -6572,17 +6714,17 @@ function pushToSpace({
6572
6714
  if (existing) {
6573
6715
  const payload = { ...entity, sys: { id: entity.sys.id, type: "Component", version: existing.sys.version } };
6574
6716
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6575
- import_logging11.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6717
+ import_logging12.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6576
6718
  results.push(result);
6577
6719
  } else {
6578
6720
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "Component" } };
6579
6721
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6580
- import_logging11.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6722
+ import_logging12.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6581
6723
  results.push(result);
6582
6724
  }
6583
6725
  } catch (err) {
6584
6726
  err.entity = entity;
6585
- import_logging11.logEmitter.emit("error", err);
6727
+ import_logging12.logEmitter.emit("error", err);
6586
6728
  }
6587
6729
  }
6588
6730
  ctx.data.components = results;
@@ -6614,17 +6756,17 @@ function pushToSpace({
6614
6756
  if (existing) {
6615
6757
  const payload = { ...entity, sys: { id: entity.sys.id, type: "ExperienceTemplate", version: existing.sys.version } };
6616
6758
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6617
- import_logging11.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6759
+ import_logging12.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6618
6760
  return result;
6619
6761
  } else {
6620
6762
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "ExperienceTemplate" } };
6621
6763
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6622
- import_logging11.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6764
+ import_logging12.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6623
6765
  return result;
6624
6766
  }
6625
6767
  } catch (err) {
6626
6768
  err.entity = entity;
6627
- import_logging11.logEmitter.emit("error", err);
6769
+ import_logging12.logEmitter.emit("error", err);
6628
6770
  return null;
6629
6771
  }
6630
6772
  }));
@@ -6656,17 +6798,17 @@ function pushToSpace({
6656
6798
  if (existing) {
6657
6799
  const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "ExperienceFragment", version: existing.sys.version } };
6658
6800
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6659
- import_logging11.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6801
+ import_logging12.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6660
6802
  results.push(result);
6661
6803
  } else {
6662
6804
  const payload = { ...omitSysAndOptimizationVariants(entity), component: entity.sys.component, sys: { id: entity.sys.id, type: "ExperienceFragment" } };
6663
6805
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6664
- import_logging11.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6806
+ import_logging12.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6665
6807
  results.push(result);
6666
6808
  }
6667
6809
  } catch (err) {
6668
6810
  err.entity = entity;
6669
- import_logging11.logEmitter.emit("error", err);
6811
+ import_logging12.logEmitter.emit("error", err);
6670
6812
  }
6671
6813
  }
6672
6814
  ctx.data.experienceFragments = results;
@@ -6698,17 +6840,17 @@ function pushToSpace({
6698
6840
  if (existing) {
6699
6841
  const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
6700
6842
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6701
- import_logging11.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6843
+ import_logging12.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6702
6844
  return result;
6703
6845
  } else {
6704
6846
  const payload = { ...omitSysAndOptimizationVariants(entity), experienceTemplate: entity.sys.experienceTemplate, sys: { id: entity.sys.id, type: "Experience" } };
6705
6847
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6706
- import_logging11.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6848
+ import_logging12.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6707
6849
  return result;
6708
6850
  }
6709
6851
  } catch (err) {
6710
6852
  err.entity = entity;
6711
- import_logging11.logEmitter.emit("error", err);
6853
+ import_logging12.logEmitter.emit("error", err);
6712
6854
  return null;
6713
6855
  }
6714
6856
  }));
@@ -6872,6 +7014,18 @@ function pushToSpace({
6872
7014
  ));
6873
7015
  }),
6874
7016
  skip: () => !includeExperienceOrchestration || skipContentPublishing || !(sourceData.dataAssemblies || []).length
7017
+ },
7018
+ {
7019
+ title: "Importing Releases",
7020
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
7021
+ const { supported: v2Releases, unsupported: v1Releases } = partitionReleasesBySchemaVersion(sourceData.releases || []);
7022
+ v1Releases.forEach(logUnsupportedRelease);
7023
+ const results = await Promise.all(v2Releases.map(
7024
+ (release) => importRelease(release, destinationDataById.releases?.get(release.transformed.sys.id), { client, spaceId, environmentId })
7025
+ ));
7026
+ ctx.data.releases = results.filter(Boolean);
7027
+ }),
7028
+ skip: () => !(sourceData.releases || []).length
6875
7029
  }
6876
7030
  ], listrOptions);
6877
7031
  }
@@ -6911,24 +7065,24 @@ async function importVariantsForParents({
6911
7065
  try {
6912
7066
  const payload = { ...omitVariantSys(variant), [parentRefField]: variant.sys[parentRefField] };
6913
7067
  const result = await ns.create({ spaceId, environmentId, [idParam]: parent2.sys.id }, payload);
6914
- import_logging11.logEmitter.emit("info", `CREATE ${label} ${result.sys.variant} (parent ${parent2.sys.id})`);
7068
+ import_logging12.logEmitter.emit("info", `CREATE ${label} ${result.sys.variant} (parent ${parent2.sys.id})`);
6915
7069
  created.push({ ...result, sys: { ...result.sys, publishedVersion: variant.sys.publishedVersion, archivedVersion: variant.sys.archivedVersion } });
6916
7070
  } catch (err) {
6917
7071
  err.entity = variant;
6918
- import_logging11.logEmitter.emit("error", err);
7072
+ import_logging12.logEmitter.emit("error", err);
6919
7073
  }
6920
7074
  }
6921
7075
  return { parentId: parent2.sys.id, created };
6922
7076
  }));
6923
7077
  }
6924
- function archiveEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue }) {
7078
+ function archiveEntities2({ entities: entities2, sourceEntities, client, spaceId, environmentId, requestQueue }) {
6925
7079
  const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
6926
- const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
7080
+ const entitiesToArchive = entities2.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
6927
7081
  return archiveEntities({ entities: entitiesToArchive, client, spaceId, environmentId, requestQueue });
6928
7082
  }
6929
- async function publishEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue, destinationEntitiesById }) {
7083
+ async function publishEntities2({ entities: entities2, sourceEntities, client, spaceId, environmentId, requestQueue, destinationEntitiesById }) {
6930
7084
  const entityIdsToPublish = sourceEntities.filter(({ original }) => original.sys.publishedVersion).map(({ original }) => original.sys.id);
6931
- let entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
7085
+ let entitiesToPublish = entities2.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
6932
7086
  const publishAllLocales = () => publishEntities({ entities: entitiesToPublish, client, spaceId, environmentId, requestQueue });
6933
7087
  const hasFieldStatus = sourceEntities.some(({ original }) => original.sys.fieldStatus);
6934
7088
  if (!hasFieldStatus) {
@@ -6949,7 +7103,7 @@ async function publishEntities2({ entities, sourceEntities, client, spaceId, env
6949
7103
  if (!localePublishing.skippedEntityIds.has(entity.sys.id)) {
6950
7104
  return true;
6951
7105
  }
6952
- 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`);
7106
+ 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`);
6953
7107
  return false;
6954
7108
  });
6955
7109
  }
@@ -6970,6 +7124,7 @@ __export(transformers_exports, {
6970
7124
  contentTypes: () => contentTypes,
6971
7125
  entries: () => entries,
6972
7126
  locales: () => locales,
7127
+ releases: () => releases,
6973
7128
  tags: () => tags,
6974
7129
  webhooks: () => webhooks
6975
7130
  });
@@ -7025,6 +7180,9 @@ function removeMetadataTags(entity, tagsEnabled = false) {
7025
7180
  }
7026
7181
  return entity;
7027
7182
  }
7183
+ function releases(release) {
7184
+ return release;
7185
+ }
7028
7186
 
7029
7187
  // lib/utils/sort-entries.ts
7030
7188
  function sortEntries(entries2) {
@@ -7274,24 +7432,24 @@ function upgradeExoResources(resources) {
7274
7432
  }
7275
7433
 
7276
7434
  // lib/transform/transform-space.ts
7277
- var spaceEntities = [
7435
+ var entities = [
7278
7436
  "contentTypes",
7279
7437
  "entries",
7280
7438
  "assets",
7281
7439
  "locales",
7282
7440
  "webhooks",
7283
- "tags"
7441
+ "tags",
7442
+ "releases"
7284
7443
  ];
7285
- function transform_space_default(sourceData, destinationData, customTransformers, entities = spaceEntities) {
7286
- const transformers = defaults_default(customTransformers, transformers_exports);
7444
+ function transform_space_default(sourceData, destinationData) {
7287
7445
  const baseSpaceData = upgradeExoResources(omit_default(sourceData, ...entities));
7288
7446
  sourceData.locales = sortLocales(sourceData.locales);
7289
7447
  const tagsEnabled = !!destinationData.tags;
7290
7448
  return entities.reduce((transformedSpaceData, type) => {
7291
- const sortedEntities = type === "tags" ? sourceData[type] ?? [] : sortEntries(sourceData[type] ?? []);
7449
+ const sortedEntities = type === "tags" || type === "releases" ? sourceData[type] ?? [] : sortEntries(sourceData[type] ?? []);
7292
7450
  const transformedEntities = sortedEntities.map((entity) => ({
7293
7451
  original: entity,
7294
- transformed: transformers[type](entity, destinationData[type], tagsEnabled)
7452
+ transformed: transformers_exports[type](entity, destinationData[type], tagsEnabled)
7295
7453
  }));
7296
7454
  transformedSpaceData[type] = transformedEntities;
7297
7455
  return transformedSpaceData;
@@ -7381,13 +7539,13 @@ var attachEntityName = (details, payload) => {
7381
7539
  });
7382
7540
  };
7383
7541
  var countInvalidEntities = (validationData) => {
7384
- const entityCount = validationData.reduce((entities, currentDetail) => {
7385
- if (!entities[currentDetail.path[0]]) {
7386
- entities[currentDetail.path[0]] = 1;
7542
+ const entityCount = validationData.reduce((entities2, currentDetail) => {
7543
+ if (!entities2[currentDetail.path[0]]) {
7544
+ entities2[currentDetail.path[0]] = 1;
7387
7545
  } else {
7388
- entities[currentDetail.path[0]]++;
7546
+ entities2[currentDetail.path[0]]++;
7389
7547
  }
7390
- return entities;
7548
+ return entities2;
7391
7549
  }, {});
7392
7550
  return Object.keys(entityCount).map((key) => `${key}:${entityCount[key]}`);
7393
7551
  };
@@ -7490,7 +7648,8 @@ var SUPPORTED_ENTITY_TYPES = [
7490
7648
  "experienceTemplates",
7491
7649
  "dataAssemblies",
7492
7650
  "experienceFragments",
7493
- "experiences"
7651
+ "experiences",
7652
+ "releases"
7494
7653
  ];
7495
7654
  async function parseOptions(params) {
7496
7655
  const defaultOptions = {
@@ -7582,7 +7741,7 @@ async function parseOptions(params) {
7582
7741
  }
7583
7742
 
7584
7743
  // lib/utils/display-error-log.ts
7585
- var import_logging12 = __toESM(require_logging());
7744
+ var import_logging13 = __toESM(require_logging());
7586
7745
  import { format as format2, parseISO } from "date-fns";
7587
7746
  function displayErrorLog(errorLog) {
7588
7747
  if (errorLog.length) {
@@ -7598,7 +7757,7 @@ function displayErrorLog(errorLog) {
7598
7757
 
7599
7758
  The following ${count.errors} errors and ${count.warnings} warnings occurred:
7600
7759
  `);
7601
- errorLog.map((logMessage) => `${format2(parseISO(logMessage.ts), "HH:mm:ss")} - ${(0, import_logging12.formatLogMessageOneLine)(logMessage)}`).map((logMessage) => console.log(logMessage));
7760
+ errorLog.map((logMessage) => `${format2(parseISO(logMessage.ts), "HH:mm:ss")} - ${(0, import_logging13.formatLogMessageOneLine)(logMessage)}`).map((logMessage) => console.log(logMessage));
7602
7761
  return;
7603
7762
  }
7604
7763
  console.log("No errors or warnings occurred");
@@ -7630,7 +7789,7 @@ async function runContentfulImport(params) {
7630
7789
  intervalCap: options.rateLimit,
7631
7790
  carryoverConcurrencyCount: true
7632
7791
  });
7633
- (0, import_logging13.setupLogging)(log);
7792
+ (0, import_logging14.setupLogging)(log);
7634
7793
  const infoTable = new Table(tableOptions);
7635
7794
  infoTable.push([{ colSpan: 2, content: "The following entities are going to be imported:" }]);
7636
7795
  Object.keys(options.content).forEach((type) => {
@@ -7757,7 +7916,7 @@ async function runContentfulImport(params) {
7757
7916
  const displayLog = log.filter((logMessage) => logMessage.level !== "info");
7758
7917
  displayErrorLog(displayLog);
7759
7918
  if (errorLog.length) {
7760
- return (0, import_logging13.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7919
+ return (0, import_logging14.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7761
7920
  const multiError = new ContentfulMultiError("Errors occurred");
7762
7921
  multiError.name = "ContentfulMultiError";
7763
7922
  multiError.errors = errorLog;