contentful-import 10.2.3 → 10.4.0

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
@@ -235,7 +235,7 @@ var require_logging = __commonJS({
235
235
  throw new Error("Unable to extract API error data");
236
236
  }
237
237
  }
238
- function formatLogMessageOneLine(logMessage) {
238
+ function formatLogMessageOneLine2(logMessage) {
239
239
  const { level } = logMessage;
240
240
  if (!level) {
241
241
  return logMessage.toString().replace(/\s+/g, " ");
@@ -271,7 +271,7 @@ var require_logging = __commonJS({
271
271
  return logMessage.error.toString().replace(/\s+/g, " ");
272
272
  }
273
273
  }
274
- exports2.formatLogMessageOneLine = formatLogMessageOneLine;
274
+ exports2.formatLogMessageOneLine = formatLogMessageOneLine2;
275
275
  function formatLogMessageLogfile(logMessage) {
276
276
  const { level } = logMessage;
277
277
  if (level === "info" || level === "warning") {
@@ -310,7 +310,7 @@ var require_logging = __commonJS({
310
310
 
311
311
  The following ${count.errors} errors and ${count.warnings} warnings occurred:
312
312
  `);
313
- errorLog.map((logMessage) => `${(0, format_1.default)((0, parseISO_1.default)(logMessage.ts), "HH:mm:ss")} - ${formatLogMessageOneLine(logMessage)}`).map((logMessage) => console.log(logMessage));
313
+ errorLog.map((logMessage) => `${(0, format_1.default)((0, parseISO_1.default)(logMessage.ts), "HH:mm:ss")} - ${formatLogMessageOneLine2(logMessage)}`).map((logMessage) => console.log(logMessage));
314
314
  return;
315
315
  }
316
316
  console.log("No errors or warnings occurred");
@@ -357,7 +357,7 @@ The following ${count.errors} errors and ${count.warnings} warnings occurred:
357
357
  exports2.setupLogging = setupLogging2;
358
358
  function logToTaskOutput(task) {
359
359
  function logToTask(logMessage) {
360
- const content = formatLogMessageOneLine(logMessage);
360
+ const content = formatLogMessageOneLine2(logMessage);
361
361
  const symbols = {
362
362
  info: figures_1.default.tick,
363
363
  warning: figures_1.default.warning,
@@ -570,12 +570,12 @@ var require_common = __commonJS({
570
570
  args.unshift("%O");
571
571
  }
572
572
  let index = 0;
573
- args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format2) => {
573
+ args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format3) => {
574
574
  if (match === "%%") {
575
575
  return "%";
576
576
  }
577
577
  index++;
578
- const formatter = createDebug.formatters[format2];
578
+ const formatter = createDebug.formatters[format3];
579
579
  if (typeof formatter === "function") {
580
580
  const val = args[index];
581
581
  match = formatter.call(self2, val);
@@ -2280,8 +2280,8 @@ function getNative(object, key) {
2280
2280
  var getNative_default = getNative;
2281
2281
 
2282
2282
  // node_modules/lodash-es/_WeakMap.js
2283
- var WeakMap = getNative_default(root_default, "WeakMap");
2284
- var WeakMap_default = WeakMap;
2283
+ var WeakMap2 = getNative_default(root_default, "WeakMap");
2284
+ var WeakMap_default = WeakMap2;
2285
2285
 
2286
2286
  // node_modules/lodash-es/_baseCreate.js
2287
2287
  var objectCreate = Object.create;
@@ -3007,12 +3007,12 @@ function memoize(func, resolver) {
3007
3007
  throw new TypeError(FUNC_ERROR_TEXT);
3008
3008
  }
3009
3009
  var memoized = function() {
3010
- var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache;
3011
- if (cache.has(key)) {
3012
- return cache.get(key);
3010
+ var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache2 = memoized.cache;
3011
+ if (cache2.has(key)) {
3012
+ return cache2.get(key);
3013
3013
  }
3014
3014
  var result = func.apply(this, args);
3015
- memoized.cache = cache.set(key, result) || cache;
3015
+ memoized.cache = cache2.set(key, result) || cache2;
3016
3016
  return result;
3017
3017
  };
3018
3018
  memoized.cache = new (memoize.Cache || MapCache_default)();
@@ -3025,12 +3025,12 @@ var memoize_default = memoize;
3025
3025
  var MAX_MEMOIZE_SIZE = 500;
3026
3026
  function memoizeCapped(func) {
3027
3027
  var result = memoize_default(func, function(key) {
3028
- if (cache.size === MAX_MEMOIZE_SIZE) {
3029
- cache.clear();
3028
+ if (cache2.size === MAX_MEMOIZE_SIZE) {
3029
+ cache2.clear();
3030
3030
  }
3031
3031
  return key;
3032
3032
  });
3033
- var cache = result.cache;
3033
+ var cache2 = result.cache;
3034
3034
  return result;
3035
3035
  }
3036
3036
  var memoizeCapped_default = memoizeCapped;
@@ -4073,8 +4073,8 @@ function arraySome(array, predicate) {
4073
4073
  var arraySome_default = arraySome;
4074
4074
 
4075
4075
  // node_modules/lodash-es/_cacheHas.js
4076
- function cacheHas(cache, key) {
4077
- return cache.has(key);
4076
+ function cacheHas(cache2, key) {
4077
+ return cache2.has(key);
4078
4078
  }
4079
4079
  var cacheHas_default = cacheHas;
4080
4080
 
@@ -4855,7 +4855,7 @@ var startCase = createCompounder_default(function(result, word, index) {
4855
4855
  var startCase_default = startCase;
4856
4856
 
4857
4857
  // lib/index.ts
4858
- var import_logging10 = __toESM(require_logging());
4858
+ var import_logging13 = __toESM(require_logging());
4859
4859
  var import_listr4 = __toESM(require_listr());
4860
4860
  import PQueue from "p-queue";
4861
4861
 
@@ -4922,6 +4922,34 @@ async function unpublishExoEntity(type, entity, unpublish) {
4922
4922
  return null;
4923
4923
  }
4924
4924
  }
4925
+ function filterVariantsToPublish(variants) {
4926
+ return variants.filter((v) => v.sys.publishedVersion);
4927
+ }
4928
+ function filterVariantsToArchive(variants) {
4929
+ return variants.filter((v) => v.sys.archivedVersion && !v.sys.publishedVersion);
4930
+ }
4931
+ async function publishVariant(type, entity, publish) {
4932
+ try {
4933
+ const result = await publish();
4934
+ import_logging2.logEmitter.emit("info", `PUBLISH ${type} ${entity.sys.variant}`);
4935
+ return result;
4936
+ } catch (err) {
4937
+ err.entity = entity;
4938
+ import_logging2.logEmitter.emit("error", err);
4939
+ return null;
4940
+ }
4941
+ }
4942
+ async function archiveVariant(type, entity, archive) {
4943
+ try {
4944
+ const result = await archive();
4945
+ import_logging2.logEmitter.emit("info", `ARCHIVE ${type} ${entity.sys.variant}`);
4946
+ return result;
4947
+ } catch (err) {
4948
+ err.entity = entity;
4949
+ import_logging2.logEmitter.emit("error", err);
4950
+ return null;
4951
+ }
4952
+ }
4925
4953
  function isExoEntitlementError(err) {
4926
4954
  if (!(err instanceof Error)) {
4927
4955
  return false;
@@ -5210,7 +5238,7 @@ async function getDestinationData({
5210
5238
  }
5211
5239
 
5212
5240
  // lib/tasks/push-to-space/push-to-space.ts
5213
- var import_logging9 = __toESM(require_logging());
5241
+ var import_logging11 = __toESM(require_logging());
5214
5242
  var import_listr2 = __toESM(require_listr());
5215
5243
  import Listr from "listr";
5216
5244
  import verboseRenderer from "listr-verbose-renderer";
@@ -5509,21 +5537,103 @@ function getPlainData(entity) {
5509
5537
 
5510
5538
  // lib/tasks/push-to-space/publishing.ts
5511
5539
  var import_get_entity_name3 = __toESM(require_get_entity_name());
5540
+ var import_logging7 = __toESM(require_logging());
5541
+
5542
+ // lib/utils/locale-publishing.ts
5512
5543
  var import_logging6 = __toESM(require_logging());
5513
- function publishEntity(client, spaceId, environmentId, entity) {
5544
+ var LOCALE_PUBLISHING_FEATURES = ["localeBasedPublishing", "localePublishing"];
5545
+ var ENABLE_ON_ENVIRONMENT = 'To preserve it, select "Locale-based (un)publishing" for the destination environment under Settings > Locales > Publishing options.';
5546
+ var NOT_ENTITLED = "Locale-based publishing is only available on some plans \u2014 contact Contentful to have it added.";
5547
+ async function spaceHasLocalePublishingEntitlement(client, spaceId) {
5548
+ try {
5549
+ const space = await client.space.get({ spaceId });
5550
+ const organizationId = space.sys.organization?.sys?.id;
5551
+ if (!organizationId) {
5552
+ return null;
5553
+ }
5554
+ const entitlements = await client.raw.get(
5555
+ `/organizations/${organizationId}/organization_entitlement_set`
5556
+ );
5557
+ const features = entitlements.features;
5558
+ const known = LOCALE_PUBLISHING_FEATURES.filter((feature) => features?.[feature]?.value !== void 0);
5559
+ if (!known.length) {
5560
+ return null;
5561
+ }
5562
+ return known.some((feature) => features?.[feature]?.value === true);
5563
+ } catch {
5564
+ return null;
5565
+ }
5566
+ }
5567
+ var localeScopingUnavailable = /* @__PURE__ */ new WeakSet();
5568
+ var entitlementChecks = /* @__PURE__ */ new WeakMap();
5569
+ async function ensureLocalePublishingEntitlement(client, spaceId) {
5570
+ if (localeScopingUnavailable.has(client)) {
5571
+ return;
5572
+ }
5573
+ let check = entitlementChecks.get(client);
5574
+ if (!check) {
5575
+ check = spaceHasLocalePublishingEntitlement(client, spaceId);
5576
+ entitlementChecks.set(client, check);
5577
+ }
5578
+ if (await check === false) {
5579
+ markLocaleScopingUnavailable(client, "The destination organization is not entitled to locale-based publishing.", NOT_ENTITLED);
5580
+ }
5581
+ }
5582
+ function isLocaleScopingUnavailable(client) {
5583
+ return localeScopingUnavailable.has(client);
5584
+ }
5585
+ function markLocaleScopingUnavailable(client, reason, remedy = ENABLE_ON_ENVIRONMENT) {
5586
+ if (localeScopingUnavailable.has(client)) {
5587
+ return;
5588
+ }
5589
+ localeScopingUnavailable.add(client);
5590
+ import_logging6.logEmitter.emit("warning", `${reason} Publishing every locale instead \u2014 the per-locale publish state in the content file cannot be preserved. ${remedy}`);
5591
+ }
5592
+ function isLocalePublishingRejectedError(err) {
5593
+ if (!(err instanceof Error)) {
5594
+ return false;
5595
+ }
5596
+ const status = err.status ?? err.response?.status;
5597
+ if (status === 403 || status === 400) {
5598
+ return true;
5599
+ }
5600
+ try {
5601
+ const parsed = JSON.parse(err.message)?.status;
5602
+ return parsed === 403 || parsed === 400;
5603
+ } catch {
5604
+ return false;
5605
+ }
5606
+ }
5607
+ function destinationScopedThePublish(publishedEntity) {
5608
+ return Boolean(publishedEntity?.sys?.fieldStatus?.["*"]);
5609
+ }
5610
+
5611
+ // lib/tasks/push-to-space/publishing.ts
5612
+ function publishEntity(client, spaceId, environmentId, entity, locales2) {
5514
5613
  const id = entity.sys.id;
5515
5614
  const type = entity.sys.type;
5516
5615
  if (type === "Entry") {
5517
- return client.entry.publish({ spaceId, environmentId, entryId: id }, entity);
5616
+ return client.entry.publish({ spaceId, environmentId, entryId: id, locales: locales2 }, entity);
5518
5617
  }
5519
5618
  if (type === "Asset") {
5520
- return client.asset.publish({ spaceId, environmentId, assetId: id }, entity);
5619
+ return client.asset.publish({ spaceId, environmentId, assetId: id, locales: locales2 }, entity);
5521
5620
  }
5522
5621
  if (type === "ContentType") {
5523
5622
  return client.contentType.publish({ spaceId, environmentId, contentTypeId: id }, entity);
5524
5623
  }
5525
5624
  throw new Error(`publishEntity: unsupported type "${type}"`);
5526
5625
  }
5626
+ function unpublishEntityLocales(client, spaceId, environmentId, entity, locales2) {
5627
+ const id = entity.sys.id;
5628
+ const type = entity.sys.type;
5629
+ if (type === "Entry") {
5630
+ return client.entry.unpublish({ spaceId, environmentId, entryId: id, locales: locales2 }, entity);
5631
+ }
5632
+ if (type === "Asset") {
5633
+ return client.asset.unpublish({ spaceId, environmentId, assetId: id, locales: locales2 }, entity);
5634
+ }
5635
+ throw new Error(`unpublishEntityLocales: unsupported type "${type}"`);
5636
+ }
5527
5637
  function archiveEntity(client, spaceId, environmentId, entity) {
5528
5638
  const id = entity.sys.id;
5529
5639
  const type = entity.sys.type;
@@ -5535,26 +5645,26 @@ function archiveEntity(client, spaceId, environmentId, entity) {
5535
5645
  }
5536
5646
  throw new Error(`archiveEntity: unsupported type "${type}"`);
5537
5647
  }
5538
- async function publishEntities({ entities, client, spaceId, environmentId, requestQueue }) {
5648
+ async function publishEntities({ entities, client, spaceId, environmentId, requestQueue, localePublishing }) {
5539
5649
  if (entities.length === 0) {
5540
- import_logging6.logEmitter.emit("info", "Skipping publishing since zero valid entities passed");
5650
+ import_logging7.logEmitter.emit("info", "Skipping publishing since zero valid entities passed");
5541
5651
  return [];
5542
5652
  }
5543
5653
  const entity = entities[0];
5544
5654
  const type = entity.sys.type || "unknown type";
5545
- import_logging6.logEmitter.emit("info", `Publishing ${entities.length} ${type}s`);
5546
- const result = await runQueue(entities, [], client, spaceId, environmentId, requestQueue);
5547
- import_logging6.logEmitter.emit("info", `Successfully published ${result.length} ${type}s`);
5655
+ import_logging7.logEmitter.emit("info", `Publishing ${entities.length} ${type}s`);
5656
+ const result = await runQueue(entities, [], client, spaceId, environmentId, requestQueue, localePublishing);
5657
+ import_logging7.logEmitter.emit("info", `Successfully published ${result.length} ${type}s`);
5548
5658
  return result;
5549
5659
  }
5550
5660
  async function archiveEntities({ entities, client, spaceId, environmentId, requestQueue }) {
5551
5661
  if (entities.length === 0) {
5552
- import_logging6.logEmitter.emit("info", "Skipping archiving since zero valid entities passed");
5662
+ import_logging7.logEmitter.emit("info", "Skipping archiving since zero valid entities passed");
5553
5663
  return [];
5554
5664
  }
5555
5665
  const entity = entities[0];
5556
5666
  const type = entity.sys.type || "unknown type";
5557
- import_logging6.logEmitter.emit("info", `Archiving ${entities.length} ${type}s`);
5667
+ import_logging7.logEmitter.emit("info", `Archiving ${entities.length} ${type}s`);
5558
5668
  const pendingArchivedEntities = entities.map((entity2) => {
5559
5669
  return requestQueue.add(async () => {
5560
5670
  try {
@@ -5564,28 +5674,66 @@ async function archiveEntities({ entities, client, spaceId, environmentId, reque
5564
5674
  if (err instanceof ContentfulEntityError) {
5565
5675
  err.entity = entity2;
5566
5676
  }
5567
- import_logging6.logEmitter.emit("error", err);
5677
+ import_logging7.logEmitter.emit("error", err);
5568
5678
  return null;
5569
5679
  }
5570
5680
  });
5571
5681
  });
5572
5682
  const allPossiblyArchivedEntities = await Promise.all(pendingArchivedEntities);
5573
5683
  const allArchivedEntities = allPossiblyArchivedEntities.filter((entity2) => entity2);
5574
- import_logging6.logEmitter.emit("info", `Successfully archived ${allArchivedEntities.length} ${type}s`);
5684
+ import_logging7.logEmitter.emit("info", `Successfully archived ${allArchivedEntities.length} ${type}s`);
5575
5685
  return allArchivedEntities;
5576
5686
  }
5577
- async function runQueue(queue, result = [], client, spaceId, environmentId, requestQueue) {
5687
+ async function publishEntityLocales(client, spaceId, environmentId, entity, locales2, localesToDemote) {
5688
+ let published;
5689
+ try {
5690
+ published = await publishEntity(client, spaceId, environmentId, entity, locales2);
5691
+ } catch (err) {
5692
+ if (!isLocalePublishingRejectedError(err)) {
5693
+ throw err;
5694
+ }
5695
+ markLocaleScopingUnavailable(client, `The destination rejected locale-scoped publishing, starting with ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}.`);
5696
+ return publishEntity(client, spaceId, environmentId, entity);
5697
+ }
5698
+ if (!destinationScopedThePublish(published)) {
5699
+ markLocaleScopingUnavailable(client, `The destination published ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)} without honouring the requested locales (${locales2.join(", ")}) and reported no per-locale state back.`);
5700
+ return published;
5701
+ }
5702
+ if (!localesToDemote?.length) {
5703
+ return published;
5704
+ }
5705
+ import_logging7.logEmitter.emit("info", `Unpublishing locales ${localesToDemote.join(", ")} of ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}`);
5706
+ try {
5707
+ return await unpublishEntityLocales(client, spaceId, environmentId, published, localesToDemote);
5708
+ } catch (err) {
5709
+ if (isLocalePublishingRejectedError(err)) {
5710
+ markLocaleScopingUnavailable(client, `The destination rejected locale-scoped unpublishing, starting with ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}.`);
5711
+ return published;
5712
+ }
5713
+ if (err instanceof ContentfulEntityError) {
5714
+ err.entity = entity;
5715
+ }
5716
+ import_logging7.logEmitter.emit("error", err);
5717
+ return published;
5718
+ }
5719
+ }
5720
+ async function runQueue(queue, result = [], client, spaceId, environmentId, requestQueue, localePublishing) {
5578
5721
  const publishedEntities = [];
5579
5722
  for (const entity of queue) {
5580
- import_logging6.logEmitter.emit("info", `Publishing ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}`);
5723
+ const locales2 = localePublishing && !isLocaleScopingUnavailable(client) ? localePublishing.localesByEntityId.get(entity.sys.id) : void 0;
5724
+ if (locales2) {
5725
+ import_logging7.logEmitter.emit("info", `Publishing ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)} for locales ${locales2.join(", ")}`);
5726
+ } else {
5727
+ import_logging7.logEmitter.emit("info", `Publishing ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}`);
5728
+ }
5581
5729
  try {
5582
- const publishedEntity = await requestQueue.add(() => publishEntity(client, spaceId, environmentId, entity));
5730
+ const publishedEntity = await requestQueue.add(() => locales2 ? publishEntityLocales(client, spaceId, environmentId, entity, locales2, localePublishing?.demoteLocalesByEntityId.get(entity.sys.id)) : publishEntity(client, spaceId, environmentId, entity));
5583
5731
  publishedEntities.push(publishedEntity);
5584
5732
  } catch (err) {
5585
5733
  if (err instanceof ContentfulEntityError) {
5586
5734
  err.entity = entity;
5587
5735
  }
5588
- import_logging6.logEmitter.emit("error", err);
5736
+ import_logging7.logEmitter.emit("error", err);
5589
5737
  }
5590
5738
  }
5591
5739
  result = [
@@ -5597,9 +5745,9 @@ async function runQueue(queue, result = [], client, spaceId, environmentId, requ
5597
5745
  if (unpublishedEntities.length > 0) {
5598
5746
  if (queue.length === unpublishedEntities.length) {
5599
5747
  const unpublishedEntityNames = unpublishedEntities.map(import_get_entity_name3.default).join(", ");
5600
- import_logging6.logEmitter.emit("error", `Could not publish the following entities: ${unpublishedEntityNames}`);
5748
+ import_logging7.logEmitter.emit("error", `Could not publish the following entities: ${unpublishedEntityNames}`);
5601
5749
  } else {
5602
- return runQueue(unpublishedEntities, result, client, spaceId, environmentId, requestQueue);
5750
+ return runQueue(unpublishedEntities, result, client, spaceId, environmentId, requestQueue, localePublishing);
5603
5751
  }
5604
5752
  }
5605
5753
  return result;
@@ -5731,18 +5879,18 @@ function sortExperienceFragments(experienceFragments) {
5731
5879
  }
5732
5880
 
5733
5881
  // lib/utils/sort-or-report.ts
5734
- var import_logging7 = __toESM(require_logging());
5882
+ var import_logging8 = __toESM(require_logging());
5735
5883
  function sortOrReport(sortFn) {
5736
5884
  try {
5737
5885
  return sortFn();
5738
5886
  } catch (err) {
5739
- import_logging7.logEmitter.emit("error", err);
5887
+ import_logging8.logEmitter.emit("error", err);
5740
5888
  throw err;
5741
5889
  }
5742
5890
  }
5743
5891
 
5744
5892
  // lib/utils/import-exo-folders.ts
5745
- var import_logging8 = __toESM(require_logging());
5893
+ var import_logging9 = __toESM(require_logging());
5746
5894
  var FOLDER_CONCEPT_PREFIX = "contentful.folder-";
5747
5895
  var PARENT_FOLDER_GROUP_IDS = {
5748
5896
  designToken: "contentful.folder-group-designToken",
@@ -5812,7 +5960,7 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5812
5960
  existing = await client.concept.get({ organizationId, conceptId: destConceptId });
5813
5961
  } catch (err) {
5814
5962
  if (err?.name !== "NotFound") {
5815
- import_logging8.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
5963
+ import_logging9.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
5816
5964
  }
5817
5965
  }
5818
5966
  if (!existing) {
@@ -5822,9 +5970,9 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5822
5970
  // @ts-expect-error - CMA.js type needs to be updated to be aware of purpose: 'internal'
5823
5971
  { purpose: "internal", prefLabel, metadata: { spaces: [spaceLink] } }
5824
5972
  );
5825
- import_logging8.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
5973
+ import_logging9.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
5826
5974
  } catch (err) {
5827
- import_logging8.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
5975
+ import_logging9.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
5828
5976
  }
5829
5977
  } else {
5830
5978
  const patches = [];
@@ -5838,9 +5986,9 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5838
5986
  { organizationId, conceptId: destConceptId, version: existing.sys.version },
5839
5987
  patches
5840
5988
  );
5841
- import_logging8.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
5989
+ import_logging9.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
5842
5990
  } catch (err) {
5843
- import_logging8.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
5991
+ import_logging9.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
5844
5992
  }
5845
5993
  }
5846
5994
  }
@@ -5858,9 +6006,9 @@ async function linkChildConceptsToParentGroups(client, organizationId, childConc
5858
6006
  [{ op: "add", path: "/concepts/-", value: { sys: { type: "Link", linkType: "TaxonomyConcept", id: destConceptId } } }]
5859
6007
  );
5860
6008
  parentGroups.set(parentGroupId, updated);
5861
- import_logging8.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
6009
+ import_logging9.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
5862
6010
  } catch (err) {
5863
- import_logging8.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
6011
+ import_logging9.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
5864
6012
  }
5865
6013
  }
5866
6014
  }
@@ -5882,17 +6030,17 @@ async function importExoFolders({
5882
6030
  }) {
5883
6031
  const sourceSpaceId = getSourceSpaceId(sourceEntities);
5884
6032
  if (sourceSpaceId === destinationSpaceId) {
5885
- import_logging8.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
6033
+ import_logging9.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
5886
6034
  return;
5887
6035
  }
5888
6036
  const parentGroups = await ensureParentFolderGroupsExist(client, organizationId);
5889
6037
  if (parentGroups.size === 0) {
5890
- import_logging8.logEmitter.emit("warn", "One or more Experience Orchestration folder group concept schemes are missing in the destination organization. Please create them before importing.");
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.");
5891
6039
  return;
5892
6040
  }
5893
6041
  const childConceptMap = deriveChildConceptMap(sourceEntities, destinationSpaceId);
5894
6042
  if (childConceptMap.size === 0) return;
5895
- import_logging8.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
6043
+ import_logging9.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
5896
6044
  await createOrPatchChildConcepts(client, organizationId, destinationSpaceId, childConceptMap);
5897
6045
  await linkChildConceptsToParentGroups(client, organizationId, childConceptMap, parentGroups);
5898
6046
  const allEntities = [
@@ -5905,6 +6053,79 @@ async function importExoFolders({
5905
6053
  rewriteEntityFolderConcepts(allEntities, childConceptMap);
5906
6054
  }
5907
6055
 
6056
+ // lib/utils/resolve-publish-locales.ts
6057
+ var LIVE_FIELD_STATUSES = ["published", "changed"];
6058
+ function resolvePublishLocales(sys, destinationLocaleCodes) {
6059
+ const fieldStatus = sys.fieldStatus?.["*"];
6060
+ if (!fieldStatus || !destinationLocaleCodes) {
6061
+ return null;
6062
+ }
6063
+ const liveLocales = Object.keys(fieldStatus).filter((locale) => LIVE_FIELD_STATUSES.indexOf(fieldStatus[locale]) !== -1);
6064
+ const availableLocales = liveLocales.filter((locale) => destinationLocaleCodes.indexOf(locale) !== -1);
6065
+ if (availableLocales.length === 0) {
6066
+ return [];
6067
+ }
6068
+ const everyLocaleIsLive = destinationLocaleCodes.every((locale) => availableLocales.indexOf(locale) !== -1);
6069
+ return everyLocaleIsLive ? null : availableLocales;
6070
+ }
6071
+ function resolveLocalesToDemote(sys, destinationSys, destinationLocaleCodes) {
6072
+ const fieldStatus = sys.fieldStatus?.["*"];
6073
+ const destinationFieldStatus = destinationSys?.fieldStatus?.["*"];
6074
+ if (!fieldStatus || !destinationFieldStatus) {
6075
+ return [];
6076
+ }
6077
+ return Object.keys(fieldStatus).filter((locale) => fieldStatus[locale] === "draft").filter((locale) => destinationLocaleCodes.indexOf(locale) !== -1).filter((locale) => LIVE_FIELD_STATUSES.indexOf(destinationFieldStatus[locale]) !== -1);
6078
+ }
6079
+ function buildLocalePublishPlan(sourceEntities, destinationLocaleCodes, { destinationEntitiesById } = {}) {
6080
+ const localesByEntityId = /* @__PURE__ */ new Map();
6081
+ const skippedEntityIds = /* @__PURE__ */ new Set();
6082
+ const demoteLocalesByEntityId = /* @__PURE__ */ new Map();
6083
+ for (const { original } of sourceEntities) {
6084
+ const locales2 = resolvePublishLocales(original.sys, destinationLocaleCodes);
6085
+ if (locales2 === null) {
6086
+ continue;
6087
+ }
6088
+ if (locales2.length === 0) {
6089
+ skippedEntityIds.add(original.sys.id);
6090
+ continue;
6091
+ }
6092
+ localesByEntityId.set(original.sys.id, locales2);
6093
+ if (!destinationEntitiesById) {
6094
+ continue;
6095
+ }
6096
+ const localesToDemote = resolveLocalesToDemote(
6097
+ original.sys,
6098
+ destinationEntitiesById.get(original.sys.id)?.sys,
6099
+ destinationLocaleCodes
6100
+ );
6101
+ if (localesToDemote.length) {
6102
+ demoteLocalesByEntityId.set(original.sys.id, localesToDemote);
6103
+ }
6104
+ }
6105
+ return { localesByEntityId, skippedEntityIds, demoteLocalesByEntityId };
6106
+ }
6107
+
6108
+ // lib/utils/destination-locales.ts
6109
+ var import_logging10 = __toESM(require_logging());
6110
+ var cache = /* @__PURE__ */ new WeakMap();
6111
+ function getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueue }) {
6112
+ let byEnvironment = cache.get(client);
6113
+ if (!byEnvironment) {
6114
+ byEnvironment = /* @__PURE__ */ new Map();
6115
+ cache.set(client, byEnvironment);
6116
+ }
6117
+ const key = `${spaceId}/${environmentId}`;
6118
+ let localeCodes = byEnvironment.get(key);
6119
+ if (!localeCodes) {
6120
+ 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}`);
6122
+ return null;
6123
+ });
6124
+ byEnvironment.set(key, localeCodes);
6125
+ }
6126
+ return localeCodes;
6127
+ }
6128
+
5908
6129
  // lib/tasks/push-to-space/push-to-space.ts
5909
6130
  async function withGraphQLSchemaBackoff(fn) {
5910
6131
  let lastErr;
@@ -5916,7 +6137,7 @@ async function withGraphQLSchemaBackoff(fn) {
5916
6137
  throw err;
5917
6138
  }
5918
6139
  const delay = GRAPHQL_SCHEMA_STALE_DELAYS_MS[attempt];
5919
- import_logging9.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
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})`);
5920
6141
  await new Promise((resolve2) => setTimeout(resolve2, delay));
5921
6142
  lastErr = err;
5922
6143
  }
@@ -5939,11 +6160,13 @@ function pushToSpace({
5939
6160
  spaceId,
5940
6161
  environmentId,
5941
6162
  includeExperienceOrchestration,
6163
+ skipExoVariants,
5942
6164
  contentModelOnly,
5943
6165
  skipContentModel,
5944
6166
  skipContentUpdates,
5945
6167
  skipLocales,
5946
6168
  skipContentPublishing,
6169
+ unpublishDraftLocales,
5947
6170
  timeout,
5948
6171
  retryLimit,
5949
6172
  listrOptions,
@@ -5960,6 +6183,13 @@ function pushToSpace({
5960
6183
  ...DEFAULT_CONTENT_STRUCTURE,
5961
6184
  ...destinationData
5962
6185
  };
6186
+ const hasOptimizationVariants = [
6187
+ ...sourceData.experiences || [],
6188
+ ...sourceData.experienceFragments || []
6189
+ ].some((parent2) => (parent2.optimizationVariants || []).some(isRealOptimizationVariant));
6190
+ 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.");
6192
+ }
5963
6193
  listrOptions = listrOptions || {
5964
6194
  renderer: verboseRenderer
5965
6195
  };
@@ -6055,7 +6285,7 @@ function pushToSpace({
6055
6285
  const ctEditorInterface = await requestQueue.add(
6056
6286
  () => client.editorInterface.get({ spaceId, environmentId, contentTypeId: contentType.sys.id })
6057
6287
  );
6058
- import_logging9.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6288
+ import_logging11.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6059
6289
  const updatedData = {
6060
6290
  ...ctEditorInterface,
6061
6291
  controls: editorInterface.controls,
@@ -6073,7 +6303,7 @@ function pushToSpace({
6073
6303
  return updatedEditorInterface;
6074
6304
  } catch (err) {
6075
6305
  err.entity = editorInterface;
6076
- import_logging9.logEmitter.emit("error", err);
6306
+ import_logging11.logEmitter.emit("error", err);
6077
6307
  return null;
6078
6308
  }
6079
6309
  });
@@ -6091,7 +6321,7 @@ function pushToSpace({
6091
6321
  for (const file of Object.values(asset.transformed.fields.file)) {
6092
6322
  allPendingUploads.push(requestQueue.add(async () => {
6093
6323
  try {
6094
- import_logging9.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6324
+ import_logging11.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6095
6325
  const assetStream = await getAssetStreamForURL(file.upload, assetsDirectory);
6096
6326
  const upload = await client.upload.create(
6097
6327
  { spaceId, environmentId },
@@ -6107,7 +6337,7 @@ function pushToSpace({
6107
6337
  };
6108
6338
  return upload;
6109
6339
  } catch (err) {
6110
- import_logging9.logEmitter.emit("error", err);
6340
+ import_logging11.logEmitter.emit("error", err);
6111
6341
  }
6112
6342
  }));
6113
6343
  }
@@ -6152,7 +6382,8 @@ function pushToSpace({
6152
6382
  client,
6153
6383
  spaceId,
6154
6384
  environmentId,
6155
- requestQueue
6385
+ requestQueue,
6386
+ destinationEntitiesById: unpublishDraftLocales ? destinationDataById.assets : void 0
6156
6387
  });
6157
6388
  ctx.data.publishedAssets = publishedAssets;
6158
6389
  }),
@@ -6196,7 +6427,8 @@ function pushToSpace({
6196
6427
  client,
6197
6428
  spaceId,
6198
6429
  environmentId,
6199
- requestQueue
6430
+ requestQueue,
6431
+ destinationEntitiesById: unpublishDraftLocales ? destinationDataById.entries : void 0
6200
6432
  });
6201
6433
  ctx.data.publishedEntries = publishedEntries;
6202
6434
  }),
@@ -6251,7 +6483,7 @@ function pushToSpace({
6251
6483
  }
6252
6484
  });
6253
6485
  } catch (error) {
6254
- import_logging9.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6486
+ import_logging11.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6255
6487
  }
6256
6488
  }),
6257
6489
  skip: () => !includeExperienceOrchestration
@@ -6269,19 +6501,19 @@ function pushToSpace({
6269
6501
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6270
6502
  payload
6271
6503
  ));
6272
- import_logging9.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6504
+ import_logging11.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6273
6505
  } else {
6274
6506
  const payload = { ...omitSys(entity), sys: buildDataAssemblySys(entity, 0) };
6275
6507
  result = await withGraphQLSchemaBackoff(() => client.dataAssembly.update(
6276
6508
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6277
6509
  payload
6278
6510
  ));
6279
- import_logging9.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6511
+ import_logging11.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6280
6512
  }
6281
6513
  return result;
6282
6514
  } catch (err) {
6283
6515
  err.entity = entity;
6284
- import_logging9.logEmitter.emit("error", err);
6516
+ import_logging11.logEmitter.emit("error", err);
6285
6517
  return null;
6286
6518
  }
6287
6519
  }));
@@ -6311,17 +6543,17 @@ function pushToSpace({
6311
6543
  if (existing) {
6312
6544
  const payload = { ...entity, sys: { id: entity.sys.id, type: "DesignToken", version: existing.sys.version } };
6313
6545
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6314
- import_logging9.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6546
+ import_logging11.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6315
6547
  return result;
6316
6548
  } else {
6317
6549
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DesignToken" } };
6318
6550
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6319
- import_logging9.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6551
+ import_logging11.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6320
6552
  return result;
6321
6553
  }
6322
6554
  } catch (err) {
6323
6555
  err.entity = entity;
6324
- import_logging9.logEmitter.emit("error", err);
6556
+ import_logging11.logEmitter.emit("error", err);
6325
6557
  return null;
6326
6558
  }
6327
6559
  }));
@@ -6340,17 +6572,17 @@ function pushToSpace({
6340
6572
  if (existing) {
6341
6573
  const payload = { ...entity, sys: { id: entity.sys.id, type: "Component", version: existing.sys.version } };
6342
6574
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6343
- import_logging9.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6575
+ import_logging11.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6344
6576
  results.push(result);
6345
6577
  } else {
6346
6578
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "Component" } };
6347
6579
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6348
- import_logging9.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6580
+ import_logging11.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6349
6581
  results.push(result);
6350
6582
  }
6351
6583
  } catch (err) {
6352
6584
  err.entity = entity;
6353
- import_logging9.logEmitter.emit("error", err);
6585
+ import_logging11.logEmitter.emit("error", err);
6354
6586
  }
6355
6587
  }
6356
6588
  ctx.data.components = results;
@@ -6382,17 +6614,17 @@ function pushToSpace({
6382
6614
  if (existing) {
6383
6615
  const payload = { ...entity, sys: { id: entity.sys.id, type: "ExperienceTemplate", version: existing.sys.version } };
6384
6616
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6385
- import_logging9.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6617
+ import_logging11.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6386
6618
  return result;
6387
6619
  } else {
6388
6620
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "ExperienceTemplate" } };
6389
6621
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6390
- import_logging9.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6622
+ import_logging11.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6391
6623
  return result;
6392
6624
  }
6393
6625
  } catch (err) {
6394
6626
  err.entity = entity;
6395
- import_logging9.logEmitter.emit("error", err);
6627
+ import_logging11.logEmitter.emit("error", err);
6396
6628
  return null;
6397
6629
  }
6398
6630
  }));
@@ -6422,19 +6654,19 @@ function pushToSpace({
6422
6654
  try {
6423
6655
  const existing = destinationDataById.experienceFragments?.get(entity.sys.id);
6424
6656
  if (existing) {
6425
- const payload = { ...entity, sys: { id: entity.sys.id, type: "ExperienceFragment", version: existing.sys.version } };
6657
+ const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "ExperienceFragment", version: existing.sys.version } };
6426
6658
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6427
- import_logging9.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6659
+ import_logging11.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6428
6660
  results.push(result);
6429
6661
  } else {
6430
- const payload = { ...omitSys(entity), component: entity.sys.component, sys: { id: entity.sys.id, type: "ExperienceFragment" } };
6662
+ const payload = { ...omitSysAndOptimizationVariants(entity), component: entity.sys.component, sys: { id: entity.sys.id, type: "ExperienceFragment" } };
6431
6663
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6432
- import_logging9.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6664
+ import_logging11.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6433
6665
  results.push(result);
6434
6666
  }
6435
6667
  } catch (err) {
6436
6668
  err.entity = entity;
6437
- import_logging9.logEmitter.emit("error", err);
6669
+ import_logging11.logEmitter.emit("error", err);
6438
6670
  }
6439
6671
  }
6440
6672
  ctx.data.experienceFragments = results;
@@ -6464,19 +6696,19 @@ function pushToSpace({
6464
6696
  try {
6465
6697
  const existing = destinationDataById.experiences?.get(entity.sys.id);
6466
6698
  if (existing) {
6467
- const payload = { ...entity, sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
6699
+ const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
6468
6700
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6469
- import_logging9.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6701
+ import_logging11.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6470
6702
  return result;
6471
6703
  } else {
6472
- const payload = { ...omitSys(entity), experienceTemplate: entity.sys.experienceTemplate, sys: { id: entity.sys.id, type: "Experience" } };
6704
+ const payload = { ...omitSysAndOptimizationVariants(entity), experienceTemplate: entity.sys.experienceTemplate, sys: { id: entity.sys.id, type: "Experience" } };
6473
6705
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6474
- import_logging9.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6706
+ import_logging11.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6475
6707
  return result;
6476
6708
  }
6477
6709
  } catch (err) {
6478
6710
  err.entity = entity;
6479
- import_logging9.logEmitter.emit("error", err);
6711
+ import_logging11.logEmitter.emit("error", err);
6480
6712
  return null;
6481
6713
  }
6482
6714
  }));
@@ -6497,6 +6729,85 @@ function pushToSpace({
6497
6729
  }),
6498
6730
  skip: () => !includeExperienceOrchestration || skipContentPublishing || !(sourceData.experiences || []).length
6499
6731
  },
6732
+ // Optimization Variants are sub-resources nested on their parent Experience/ExperienceFragment
6733
+ // (sourceData.experiences[].optimizationVariants), not a flat top-level array. A variant's
6734
+ // sys.id is borrowed from its parent, not unique, so variant creation/publish/archive is
6735
+ // handled by new, self-contained functions (importVariantsForParents, filterVariantsToPublish,
6736
+ // filterVariantsToArchive, publishVariant, archiveVariant) rather than by generalizing
6737
+ // destinationDataById / filterExoEntitiesToPublish / filterExoEntitiesToUnpublish, which all
6738
+ // assume sys.id uniqueness.
6739
+ //
6740
+ // Unlike the base Experience/Fragment create/update, there is no upsert-by-known-ID for
6741
+ // variants: the upstream API's create endpoint always server-generates a fresh variantId
6742
+ // (crypto.randomUUID()) and its update endpoint 404s on an unknown variantId rather than
6743
+ // creating one. So every import run creates fresh destination variants via POST rather than
6744
+ // matching against any existing destination variant.
6745
+ {
6746
+ title: "Importing Experience Optimization Variants",
6747
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6748
+ const results = await importVariantsForParents({
6749
+ parents: ctx.data.experiences || [],
6750
+ sourceParents: sourceData.experiences || [],
6751
+ client,
6752
+ spaceId,
6753
+ environmentId,
6754
+ kind: "experience"
6755
+ });
6756
+ ctx.data.experienceVariants = results.flatMap((r) => r.created);
6757
+ }),
6758
+ skip: () => !includeExperienceOrchestration || skipExoVariants || !(sourceData.experiences || []).some((e) => (e.optimizationVariants || []).some(isRealOptimizationVariant))
6759
+ },
6760
+ {
6761
+ title: "Publishing Experience Optimization Variants",
6762
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6763
+ const toPublish = filterVariantsToPublish(ctx.data.experienceVariants || []);
6764
+ await Promise.all(toPublish.map(
6765
+ (v) => publishVariant("ExperienceVariant", v, () => client.experienceVariant.publish(
6766
+ { spaceId, environmentId, experienceId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6767
+ ))
6768
+ ));
6769
+ const toArchive = filterVariantsToArchive(ctx.data.experienceVariants || []);
6770
+ await Promise.all(toArchive.map(
6771
+ (v) => archiveVariant("ExperienceVariant", v, () => client.experienceVariant.archive(
6772
+ { spaceId, environmentId, experienceId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6773
+ ))
6774
+ ));
6775
+ }),
6776
+ skip: () => !includeExperienceOrchestration || skipExoVariants || skipContentPublishing || !(sourceData.experiences || []).some((e) => (e.optimizationVariants || []).some(isRealOptimizationVariant))
6777
+ },
6778
+ {
6779
+ title: "Importing Experience Fragment Optimization Variants",
6780
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6781
+ const results = await importVariantsForParents({
6782
+ parents: ctx.data.experienceFragments || [],
6783
+ sourceParents: sourceData.experienceFragments || [],
6784
+ client,
6785
+ spaceId,
6786
+ environmentId,
6787
+ kind: "experienceFragment"
6788
+ });
6789
+ ctx.data.experienceFragmentVariants = results.flatMap((r) => r.created);
6790
+ }),
6791
+ skip: () => !includeExperienceOrchestration || skipExoVariants || !(sourceData.experienceFragments || []).some((f) => (f.optimizationVariants || []).some(isRealOptimizationVariant))
6792
+ },
6793
+ {
6794
+ title: "Publishing Experience Fragment Optimization Variants",
6795
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6796
+ const toPublish = filterVariantsToPublish(ctx.data.experienceFragmentVariants || []);
6797
+ await Promise.all(toPublish.map(
6798
+ (v) => publishVariant("ExperienceFragmentVariant", v, () => client.experienceFragmentVariant.publish(
6799
+ { spaceId, environmentId, experienceFragmentId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6800
+ ))
6801
+ ));
6802
+ const toArchive = filterVariantsToArchive(ctx.data.experienceFragmentVariants || []);
6803
+ await Promise.all(toArchive.map(
6804
+ (v) => archiveVariant("ExperienceFragmentVariant", v, () => client.experienceFragmentVariant.archive(
6805
+ { spaceId, environmentId, experienceFragmentId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6806
+ ))
6807
+ ));
6808
+ }),
6809
+ skip: () => !includeExperienceOrchestration || skipExoVariants || skipContentPublishing || !(sourceData.experienceFragments || []).some((f) => (f.optimizationVariants || []).some(isRealOptimizationVariant))
6810
+ },
6500
6811
  // Unpublishing runs after all Importing/Publishing tasks, in reverse dependency order
6501
6812
  // (Experience first, DataAssembly last) - the API rejects unpublishing an entity that a
6502
6813
  // still-published parent references, so parents must unpublish before the children they embed.
@@ -6568,15 +6879,88 @@ function omitSys(entity) {
6568
6879
  const { sys: _sys, ...rest } = entity;
6569
6880
  return rest;
6570
6881
  }
6882
+ function omitSysAndOptimizationVariants(entity) {
6883
+ const { sys: _sys, optimizationVariants: _variants, ...rest } = entity;
6884
+ return rest;
6885
+ }
6886
+ function omitVariantSys(variant) {
6887
+ const { sys: _sys, ...rest } = variant;
6888
+ return rest;
6889
+ }
6890
+ function isRealOptimizationVariant(variant) {
6891
+ return Boolean(variant?.sys) && variant.sys.variantType !== "default";
6892
+ }
6893
+ async function importVariantsForParents({
6894
+ parents,
6895
+ sourceParents,
6896
+ client,
6897
+ spaceId,
6898
+ environmentId,
6899
+ kind
6900
+ }) {
6901
+ const ns = kind === "experience" ? client.experienceVariant : client.experienceFragmentVariant;
6902
+ const idParam = kind === "experience" ? "experienceId" : "experienceFragmentId";
6903
+ const label = kind === "experience" ? "ExperienceVariant" : "ExperienceFragmentVariant";
6904
+ const parentRefField = kind === "experience" ? "experienceTemplate" : "component";
6905
+ const sourceParentsById = new Map(sourceParents.map((sourceParent) => [sourceParent.sys.id, sourceParent]));
6906
+ return Promise.all(parents.map(async (parent2) => {
6907
+ const sourceParent = sourceParentsById.get(parent2.sys.id);
6908
+ const sourceVariants = (sourceParent?.optimizationVariants ?? []).filter(isRealOptimizationVariant);
6909
+ const created = [];
6910
+ for (const variant of sourceVariants) {
6911
+ try {
6912
+ const payload = { ...omitVariantSys(variant), [parentRefField]: variant.sys[parentRefField] };
6913
+ 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})`);
6915
+ created.push({ ...result, sys: { ...result.sys, publishedVersion: variant.sys.publishedVersion, archivedVersion: variant.sys.archivedVersion } });
6916
+ } catch (err) {
6917
+ err.entity = variant;
6918
+ import_logging11.logEmitter.emit("error", err);
6919
+ }
6920
+ }
6921
+ return { parentId: parent2.sys.id, created };
6922
+ }));
6923
+ }
6571
6924
  function archiveEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue }) {
6572
6925
  const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
6573
6926
  const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
6574
6927
  return archiveEntities({ entities: entitiesToArchive, client, spaceId, environmentId, requestQueue });
6575
6928
  }
6576
- function publishEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue }) {
6929
+ async function publishEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue, destinationEntitiesById }) {
6577
6930
  const entityIdsToPublish = sourceEntities.filter(({ original }) => original.sys.publishedVersion).map(({ original }) => original.sys.id);
6578
- const entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
6579
- return publishEntities({ entities: entitiesToPublish, client, spaceId, environmentId, requestQueue });
6931
+ let entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
6932
+ const publishAllLocales = () => publishEntities({ entities: entitiesToPublish, client, spaceId, environmentId, requestQueue });
6933
+ const hasFieldStatus = sourceEntities.some(({ original }) => original.sys.fieldStatus);
6934
+ if (!hasFieldStatus) {
6935
+ return publishAllLocales();
6936
+ }
6937
+ await ensureLocalePublishingEntitlement(client, spaceId);
6938
+ if (isLocaleScopingUnavailable(client)) {
6939
+ return publishAllLocales();
6940
+ }
6941
+ const destinationLocaleCodes = await getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueue });
6942
+ const localePublishing = buildLocalePublishPlan(
6943
+ sourceEntities,
6944
+ destinationLocaleCodes,
6945
+ { destinationEntitiesById }
6946
+ );
6947
+ if (localePublishing.skippedEntityIds.size) {
6948
+ entitiesToPublish = entitiesToPublish.filter((entity) => {
6949
+ if (!localePublishing.skippedEntityIds.has(entity.sys.id)) {
6950
+ return true;
6951
+ }
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`);
6953
+ return false;
6954
+ });
6955
+ }
6956
+ return publishEntities({
6957
+ entities: entitiesToPublish,
6958
+ client,
6959
+ spaceId,
6960
+ environmentId,
6961
+ requestQueue,
6962
+ localePublishing
6963
+ });
6580
6964
  }
6581
6965
 
6582
6966
  // lib/transform/transformers.ts
@@ -7113,6 +7497,7 @@ async function parseOptions(params) {
7113
7497
  skipContentModel: false,
7114
7498
  skipLocales: false,
7115
7499
  skipContentPublishing: false,
7500
+ unpublishDraftLocales: false,
7116
7501
  skipAssetUpdates: false,
7117
7502
  skipContentUpdates: false,
7118
7503
  useVerboseRenderer: false,
@@ -7121,6 +7506,7 @@ async function parseOptions(params) {
7121
7506
  uploadAssets: false,
7122
7507
  rateLimit: 7,
7123
7508
  includeExperienceOrchestration: true,
7509
+ skipExoVariants: false,
7124
7510
  host: "api.contentful.com"
7125
7511
  };
7126
7512
  const configFile = params.config ? __require(resolve(process.cwd(), params.config)) : {};
@@ -7195,6 +7581,29 @@ async function parseOptions(params) {
7195
7581
  return options;
7196
7582
  }
7197
7583
 
7584
+ // lib/utils/display-error-log.ts
7585
+ var import_logging12 = __toESM(require_logging());
7586
+ import { format as format2, parseISO } from "date-fns";
7587
+ function displayErrorLog(errorLog) {
7588
+ if (errorLog.length) {
7589
+ const count = errorLog.reduce((count2, curr) => {
7590
+ if (Object.prototype.hasOwnProperty.call(curr, "warning")) {
7591
+ count2.warnings++;
7592
+ } else if (Object.prototype.hasOwnProperty.call(curr, "error")) {
7593
+ count2.errors++;
7594
+ }
7595
+ return count2;
7596
+ }, { warnings: 0, errors: 0 });
7597
+ console.log(`
7598
+
7599
+ The following ${count.errors} errors and ${count.warnings} warnings occurred:
7600
+ `);
7601
+ errorLog.map((logMessage) => `${format2(parseISO(logMessage.ts), "HH:mm:ss")} - ${(0, import_logging12.formatLogMessageOneLine)(logMessage)}`).map((logMessage) => console.log(logMessage));
7602
+ return;
7603
+ }
7604
+ console.log("No errors or warnings occurred");
7605
+ }
7606
+
7198
7607
  // lib/index.ts
7199
7608
  var ONE_SECOND = 1e3;
7200
7609
  var tableOptions = {
@@ -7221,7 +7630,7 @@ async function runContentfulImport(params) {
7221
7630
  intervalCap: options.rateLimit,
7222
7631
  carryoverConcurrencyCount: true
7223
7632
  });
7224
- (0, import_logging10.setupLogging)(log);
7633
+ (0, import_logging13.setupLogging)(log);
7225
7634
  const infoTable = new Table(tableOptions);
7226
7635
  infoTable.push([{ colSpan: 2, content: "The following entities are going to be imported:" }]);
7227
7636
  Object.keys(options.content).forEach((type) => {
@@ -7236,6 +7645,20 @@ async function runContentfulImport(params) {
7236
7645
  }
7237
7646
  infoTable.push([startCase_default(type), options.content[type].length]);
7238
7647
  });
7648
+ const experienceVariantCount = (options.content.experiences || []).reduce(
7649
+ (sum, e) => sum + (e.optimizationVariants?.length ?? 0),
7650
+ 0
7651
+ );
7652
+ if (!options.skipExoVariants && experienceVariantCount > 0) {
7653
+ infoTable.push(["Experience Optimization Variants", experienceVariantCount]);
7654
+ }
7655
+ const experienceFragmentVariantCount = (options.content.experienceFragments || []).reduce(
7656
+ (sum, f) => sum + (f.optimizationVariants?.length ?? 0),
7657
+ 0
7658
+ );
7659
+ if (!options.skipExoVariants && experienceFragmentVariantCount > 0) {
7660
+ infoTable.push(["Experience Fragment Optimization Variants", experienceFragmentVariantCount]);
7661
+ }
7239
7662
  console.log(infoTable.toString());
7240
7663
  const tasks = new Listr2([
7241
7664
  {
@@ -7284,11 +7707,13 @@ async function runContentfulImport(params) {
7284
7707
  client: ctx.client,
7285
7708
  spaceId: options.spaceId,
7286
7709
  includeExperienceOrchestration: options.includeExperienceOrchestration,
7710
+ skipExoVariants: options.skipExoVariants,
7287
7711
  environmentId: options.environmentId,
7288
7712
  contentModelOnly: options.contentModelOnly,
7289
7713
  skipLocales: options.skipLocales,
7290
7714
  skipContentModel: options.skipContentModel,
7291
7715
  skipContentPublishing: options.skipContentPublishing,
7716
+ unpublishDraftLocales: options.unpublishDraftLocales,
7292
7717
  skipAssetUpdates: options.skipAssetUpdates,
7293
7718
  skipContentUpdates: options.skipContentUpdates,
7294
7719
  timeout: options.timeout,
@@ -7330,9 +7755,9 @@ async function runContentfulImport(params) {
7330
7755
  }).then((data) => {
7331
7756
  const errorLog = log.filter((logMessage) => logMessage.level !== "info" && logMessage.level !== "warning");
7332
7757
  const displayLog = log.filter((logMessage) => logMessage.level !== "info");
7333
- (0, import_logging10.displayErrorLog)(displayLog);
7758
+ displayErrorLog(displayLog);
7334
7759
  if (errorLog.length) {
7335
- return (0, import_logging10.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7760
+ return (0, import_logging13.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7336
7761
  const multiError = new ContentfulMultiError("Errors occurred");
7337
7762
  multiError.name = "ContentfulMultiError";
7338
7763
  multiError.errors = errorLog;