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.js CHANGED
@@ -263,7 +263,7 @@ var require_logging = __commonJS({
263
263
  throw new Error("Unable to extract API error data");
264
264
  }
265
265
  }
266
- function formatLogMessageOneLine(logMessage) {
266
+ function formatLogMessageOneLine2(logMessage) {
267
267
  const { level } = logMessage;
268
268
  if (!level) {
269
269
  return logMessage.toString().replace(/\s+/g, " ");
@@ -299,7 +299,7 @@ var require_logging = __commonJS({
299
299
  return logMessage.error.toString().replace(/\s+/g, " ");
300
300
  }
301
301
  }
302
- exports2.formatLogMessageOneLine = formatLogMessageOneLine;
302
+ exports2.formatLogMessageOneLine = formatLogMessageOneLine2;
303
303
  function formatLogMessageLogfile(logMessage) {
304
304
  const { level } = logMessage;
305
305
  if (level === "info" || level === "warning") {
@@ -338,7 +338,7 @@ var require_logging = __commonJS({
338
338
 
339
339
  The following ${count.errors} errors and ${count.warnings} warnings occurred:
340
340
  `);
341
- errorLog.map((logMessage) => `${(0, format_1.default)((0, parseISO_1.default)(logMessage.ts), "HH:mm:ss")} - ${formatLogMessageOneLine(logMessage)}`).map((logMessage) => console.log(logMessage));
341
+ errorLog.map((logMessage) => `${(0, format_1.default)((0, parseISO_1.default)(logMessage.ts), "HH:mm:ss")} - ${formatLogMessageOneLine2(logMessage)}`).map((logMessage) => console.log(logMessage));
342
342
  return;
343
343
  }
344
344
  console.log("No errors or warnings occurred");
@@ -385,7 +385,7 @@ The following ${count.errors} errors and ${count.warnings} warnings occurred:
385
385
  exports2.setupLogging = setupLogging2;
386
386
  function logToTaskOutput(task) {
387
387
  function logToTask(logMessage) {
388
- const content = formatLogMessageOneLine(logMessage);
388
+ const content = formatLogMessageOneLine2(logMessage);
389
389
  const symbols = {
390
390
  info: figures_1.default.tick,
391
391
  warning: figures_1.default.warning,
@@ -598,12 +598,12 @@ var require_common = __commonJS({
598
598
  args.unshift("%O");
599
599
  }
600
600
  let index = 0;
601
- args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format2) => {
601
+ args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format3) => {
602
602
  if (match === "%%") {
603
603
  return "%";
604
604
  }
605
605
  index++;
606
- const formatter = createDebug.formatters[format2];
606
+ const formatter = createDebug.formatters[format3];
607
607
  if (typeof formatter === "function") {
608
608
  const val = args[index];
609
609
  match = formatter.call(self2, val);
@@ -2047,7 +2047,7 @@ __export(index_exports, {
2047
2047
  });
2048
2048
  module.exports = __toCommonJS(index_exports);
2049
2049
  var import_cli_table3 = __toESM(require("cli-table3"));
2050
- var import_date_fns2 = require("date-fns");
2050
+ var import_date_fns3 = require("date-fns");
2051
2051
  var import_listr3 = __toESM(require("listr"));
2052
2052
  var import_listr_update_renderer = __toESM(require("listr-update-renderer"));
2053
2053
  var import_listr_verbose_renderer2 = __toESM(require("listr-verbose-renderer"));
@@ -2313,8 +2313,8 @@ function getNative(object, key) {
2313
2313
  var getNative_default = getNative;
2314
2314
 
2315
2315
  // node_modules/lodash-es/_WeakMap.js
2316
- var WeakMap = getNative_default(root_default, "WeakMap");
2317
- var WeakMap_default = WeakMap;
2316
+ var WeakMap2 = getNative_default(root_default, "WeakMap");
2317
+ var WeakMap_default = WeakMap2;
2318
2318
 
2319
2319
  // node_modules/lodash-es/_baseCreate.js
2320
2320
  var objectCreate = Object.create;
@@ -3040,12 +3040,12 @@ function memoize(func, resolver) {
3040
3040
  throw new TypeError(FUNC_ERROR_TEXT);
3041
3041
  }
3042
3042
  var memoized = function() {
3043
- var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache;
3044
- if (cache.has(key)) {
3045
- return cache.get(key);
3043
+ var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache2 = memoized.cache;
3044
+ if (cache2.has(key)) {
3045
+ return cache2.get(key);
3046
3046
  }
3047
3047
  var result = func.apply(this, args);
3048
- memoized.cache = cache.set(key, result) || cache;
3048
+ memoized.cache = cache2.set(key, result) || cache2;
3049
3049
  return result;
3050
3050
  };
3051
3051
  memoized.cache = new (memoize.Cache || MapCache_default)();
@@ -3058,12 +3058,12 @@ var memoize_default = memoize;
3058
3058
  var MAX_MEMOIZE_SIZE = 500;
3059
3059
  function memoizeCapped(func) {
3060
3060
  var result = memoize_default(func, function(key) {
3061
- if (cache.size === MAX_MEMOIZE_SIZE) {
3062
- cache.clear();
3061
+ if (cache2.size === MAX_MEMOIZE_SIZE) {
3062
+ cache2.clear();
3063
3063
  }
3064
3064
  return key;
3065
3065
  });
3066
- var cache = result.cache;
3066
+ var cache2 = result.cache;
3067
3067
  return result;
3068
3068
  }
3069
3069
  var memoizeCapped_default = memoizeCapped;
@@ -4106,8 +4106,8 @@ function arraySome(array, predicate) {
4106
4106
  var arraySome_default = arraySome;
4107
4107
 
4108
4108
  // node_modules/lodash-es/_cacheHas.js
4109
- function cacheHas(cache, key) {
4110
- return cache.has(key);
4109
+ function cacheHas(cache2, key) {
4110
+ return cache2.has(key);
4111
4111
  }
4112
4112
  var cacheHas_default = cacheHas;
4113
4113
 
@@ -4889,7 +4889,7 @@ var startCase_default = startCase;
4889
4889
 
4890
4890
  // lib/index.ts
4891
4891
  var import_p_queue = __toESM(require("p-queue"));
4892
- var import_logging10 = __toESM(require_logging());
4892
+ var import_logging13 = __toESM(require_logging());
4893
4893
  var import_listr4 = __toESM(require_listr());
4894
4894
 
4895
4895
  // lib/tasks/init-client.ts
@@ -4955,6 +4955,34 @@ async function unpublishExoEntity(type, entity, unpublish) {
4955
4955
  return null;
4956
4956
  }
4957
4957
  }
4958
+ function filterVariantsToPublish(variants) {
4959
+ return variants.filter((v) => v.sys.publishedVersion);
4960
+ }
4961
+ function filterVariantsToArchive(variants) {
4962
+ return variants.filter((v) => v.sys.archivedVersion && !v.sys.publishedVersion);
4963
+ }
4964
+ async function publishVariant(type, entity, publish) {
4965
+ try {
4966
+ const result = await publish();
4967
+ import_logging2.logEmitter.emit("info", `PUBLISH ${type} ${entity.sys.variant}`);
4968
+ return result;
4969
+ } catch (err) {
4970
+ err.entity = entity;
4971
+ import_logging2.logEmitter.emit("error", err);
4972
+ return null;
4973
+ }
4974
+ }
4975
+ async function archiveVariant(type, entity, archive) {
4976
+ try {
4977
+ const result = await archive();
4978
+ import_logging2.logEmitter.emit("info", `ARCHIVE ${type} ${entity.sys.variant}`);
4979
+ return result;
4980
+ } catch (err) {
4981
+ err.entity = entity;
4982
+ import_logging2.logEmitter.emit("error", err);
4983
+ return null;
4984
+ }
4985
+ }
4958
4986
  function isExoEntitlementError(err) {
4959
4987
  if (!(err instanceof Error)) {
4960
4988
  return false;
@@ -5245,7 +5273,7 @@ async function getDestinationData({
5245
5273
  // lib/tasks/push-to-space/push-to-space.ts
5246
5274
  var import_listr = __toESM(require("listr"));
5247
5275
  var import_listr_verbose_renderer = __toESM(require("listr-verbose-renderer"));
5248
- var import_logging9 = __toESM(require_logging());
5276
+ var import_logging11 = __toESM(require_logging());
5249
5277
  var import_listr2 = __toESM(require_listr());
5250
5278
 
5251
5279
  // lib/tasks/push-to-space/assets.ts
@@ -5542,21 +5570,103 @@ function getPlainData(entity) {
5542
5570
 
5543
5571
  // lib/tasks/push-to-space/publishing.ts
5544
5572
  var import_get_entity_name3 = __toESM(require_get_entity_name());
5573
+ var import_logging7 = __toESM(require_logging());
5574
+
5575
+ // lib/utils/locale-publishing.ts
5545
5576
  var import_logging6 = __toESM(require_logging());
5546
- function publishEntity(client, spaceId, environmentId, entity) {
5577
+ var LOCALE_PUBLISHING_FEATURES = ["localeBasedPublishing", "localePublishing"];
5578
+ var ENABLE_ON_ENVIRONMENT = 'To preserve it, select "Locale-based (un)publishing" for the destination environment under Settings > Locales > Publishing options.';
5579
+ var NOT_ENTITLED = "Locale-based publishing is only available on some plans \u2014 contact Contentful to have it added.";
5580
+ async function spaceHasLocalePublishingEntitlement(client, spaceId) {
5581
+ try {
5582
+ const space = await client.space.get({ spaceId });
5583
+ const organizationId = space.sys.organization?.sys?.id;
5584
+ if (!organizationId) {
5585
+ return null;
5586
+ }
5587
+ const entitlements = await client.raw.get(
5588
+ `/organizations/${organizationId}/organization_entitlement_set`
5589
+ );
5590
+ const features = entitlements.features;
5591
+ const known = LOCALE_PUBLISHING_FEATURES.filter((feature) => features?.[feature]?.value !== void 0);
5592
+ if (!known.length) {
5593
+ return null;
5594
+ }
5595
+ return known.some((feature) => features?.[feature]?.value === true);
5596
+ } catch {
5597
+ return null;
5598
+ }
5599
+ }
5600
+ var localeScopingUnavailable = /* @__PURE__ */ new WeakSet();
5601
+ var entitlementChecks = /* @__PURE__ */ new WeakMap();
5602
+ async function ensureLocalePublishingEntitlement(client, spaceId) {
5603
+ if (localeScopingUnavailable.has(client)) {
5604
+ return;
5605
+ }
5606
+ let check = entitlementChecks.get(client);
5607
+ if (!check) {
5608
+ check = spaceHasLocalePublishingEntitlement(client, spaceId);
5609
+ entitlementChecks.set(client, check);
5610
+ }
5611
+ if (await check === false) {
5612
+ markLocaleScopingUnavailable(client, "The destination organization is not entitled to locale-based publishing.", NOT_ENTITLED);
5613
+ }
5614
+ }
5615
+ function isLocaleScopingUnavailable(client) {
5616
+ return localeScopingUnavailable.has(client);
5617
+ }
5618
+ function markLocaleScopingUnavailable(client, reason, remedy = ENABLE_ON_ENVIRONMENT) {
5619
+ if (localeScopingUnavailable.has(client)) {
5620
+ return;
5621
+ }
5622
+ localeScopingUnavailable.add(client);
5623
+ import_logging6.logEmitter.emit("warning", `${reason} Publishing every locale instead \u2014 the per-locale publish state in the content file cannot be preserved. ${remedy}`);
5624
+ }
5625
+ function isLocalePublishingRejectedError(err) {
5626
+ if (!(err instanceof Error)) {
5627
+ return false;
5628
+ }
5629
+ const status = err.status ?? err.response?.status;
5630
+ if (status === 403 || status === 400) {
5631
+ return true;
5632
+ }
5633
+ try {
5634
+ const parsed = JSON.parse(err.message)?.status;
5635
+ return parsed === 403 || parsed === 400;
5636
+ } catch {
5637
+ return false;
5638
+ }
5639
+ }
5640
+ function destinationScopedThePublish(publishedEntity) {
5641
+ return Boolean(publishedEntity?.sys?.fieldStatus?.["*"]);
5642
+ }
5643
+
5644
+ // lib/tasks/push-to-space/publishing.ts
5645
+ function publishEntity(client, spaceId, environmentId, entity, locales2) {
5547
5646
  const id = entity.sys.id;
5548
5647
  const type = entity.sys.type;
5549
5648
  if (type === "Entry") {
5550
- return client.entry.publish({ spaceId, environmentId, entryId: id }, entity);
5649
+ return client.entry.publish({ spaceId, environmentId, entryId: id, locales: locales2 }, entity);
5551
5650
  }
5552
5651
  if (type === "Asset") {
5553
- return client.asset.publish({ spaceId, environmentId, assetId: id }, entity);
5652
+ return client.asset.publish({ spaceId, environmentId, assetId: id, locales: locales2 }, entity);
5554
5653
  }
5555
5654
  if (type === "ContentType") {
5556
5655
  return client.contentType.publish({ spaceId, environmentId, contentTypeId: id }, entity);
5557
5656
  }
5558
5657
  throw new Error(`publishEntity: unsupported type "${type}"`);
5559
5658
  }
5659
+ function unpublishEntityLocales(client, spaceId, environmentId, entity, locales2) {
5660
+ const id = entity.sys.id;
5661
+ const type = entity.sys.type;
5662
+ if (type === "Entry") {
5663
+ return client.entry.unpublish({ spaceId, environmentId, entryId: id, locales: locales2 }, entity);
5664
+ }
5665
+ if (type === "Asset") {
5666
+ return client.asset.unpublish({ spaceId, environmentId, assetId: id, locales: locales2 }, entity);
5667
+ }
5668
+ throw new Error(`unpublishEntityLocales: unsupported type "${type}"`);
5669
+ }
5560
5670
  function archiveEntity(client, spaceId, environmentId, entity) {
5561
5671
  const id = entity.sys.id;
5562
5672
  const type = entity.sys.type;
@@ -5568,26 +5678,26 @@ function archiveEntity(client, spaceId, environmentId, entity) {
5568
5678
  }
5569
5679
  throw new Error(`archiveEntity: unsupported type "${type}"`);
5570
5680
  }
5571
- async function publishEntities({ entities, client, spaceId, environmentId, requestQueue }) {
5681
+ async function publishEntities({ entities, client, spaceId, environmentId, requestQueue, localePublishing }) {
5572
5682
  if (entities.length === 0) {
5573
- import_logging6.logEmitter.emit("info", "Skipping publishing since zero valid entities passed");
5683
+ import_logging7.logEmitter.emit("info", "Skipping publishing since zero valid entities passed");
5574
5684
  return [];
5575
5685
  }
5576
5686
  const entity = entities[0];
5577
5687
  const type = entity.sys.type || "unknown type";
5578
- import_logging6.logEmitter.emit("info", `Publishing ${entities.length} ${type}s`);
5579
- const result = await runQueue(entities, [], client, spaceId, environmentId, requestQueue);
5580
- import_logging6.logEmitter.emit("info", `Successfully published ${result.length} ${type}s`);
5688
+ import_logging7.logEmitter.emit("info", `Publishing ${entities.length} ${type}s`);
5689
+ const result = await runQueue(entities, [], client, spaceId, environmentId, requestQueue, localePublishing);
5690
+ import_logging7.logEmitter.emit("info", `Successfully published ${result.length} ${type}s`);
5581
5691
  return result;
5582
5692
  }
5583
5693
  async function archiveEntities({ entities, client, spaceId, environmentId, requestQueue }) {
5584
5694
  if (entities.length === 0) {
5585
- import_logging6.logEmitter.emit("info", "Skipping archiving since zero valid entities passed");
5695
+ import_logging7.logEmitter.emit("info", "Skipping archiving since zero valid entities passed");
5586
5696
  return [];
5587
5697
  }
5588
5698
  const entity = entities[0];
5589
5699
  const type = entity.sys.type || "unknown type";
5590
- import_logging6.logEmitter.emit("info", `Archiving ${entities.length} ${type}s`);
5700
+ import_logging7.logEmitter.emit("info", `Archiving ${entities.length} ${type}s`);
5591
5701
  const pendingArchivedEntities = entities.map((entity2) => {
5592
5702
  return requestQueue.add(async () => {
5593
5703
  try {
@@ -5597,28 +5707,66 @@ async function archiveEntities({ entities, client, spaceId, environmentId, reque
5597
5707
  if (err instanceof ContentfulEntityError) {
5598
5708
  err.entity = entity2;
5599
5709
  }
5600
- import_logging6.logEmitter.emit("error", err);
5710
+ import_logging7.logEmitter.emit("error", err);
5601
5711
  return null;
5602
5712
  }
5603
5713
  });
5604
5714
  });
5605
5715
  const allPossiblyArchivedEntities = await Promise.all(pendingArchivedEntities);
5606
5716
  const allArchivedEntities = allPossiblyArchivedEntities.filter((entity2) => entity2);
5607
- import_logging6.logEmitter.emit("info", `Successfully archived ${allArchivedEntities.length} ${type}s`);
5717
+ import_logging7.logEmitter.emit("info", `Successfully archived ${allArchivedEntities.length} ${type}s`);
5608
5718
  return allArchivedEntities;
5609
5719
  }
5610
- async function runQueue(queue, result = [], client, spaceId, environmentId, requestQueue) {
5720
+ async function publishEntityLocales(client, spaceId, environmentId, entity, locales2, localesToDemote) {
5721
+ let published;
5722
+ try {
5723
+ published = await publishEntity(client, spaceId, environmentId, entity, locales2);
5724
+ } catch (err) {
5725
+ if (!isLocalePublishingRejectedError(err)) {
5726
+ throw err;
5727
+ }
5728
+ markLocaleScopingUnavailable(client, `The destination rejected locale-scoped publishing, starting with ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}.`);
5729
+ return publishEntity(client, spaceId, environmentId, entity);
5730
+ }
5731
+ if (!destinationScopedThePublish(published)) {
5732
+ 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.`);
5733
+ return published;
5734
+ }
5735
+ if (!localesToDemote?.length) {
5736
+ return published;
5737
+ }
5738
+ import_logging7.logEmitter.emit("info", `Unpublishing locales ${localesToDemote.join(", ")} of ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}`);
5739
+ try {
5740
+ return await unpublishEntityLocales(client, spaceId, environmentId, published, localesToDemote);
5741
+ } catch (err) {
5742
+ if (isLocalePublishingRejectedError(err)) {
5743
+ markLocaleScopingUnavailable(client, `The destination rejected locale-scoped unpublishing, starting with ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}.`);
5744
+ return published;
5745
+ }
5746
+ if (err instanceof ContentfulEntityError) {
5747
+ err.entity = entity;
5748
+ }
5749
+ import_logging7.logEmitter.emit("error", err);
5750
+ return published;
5751
+ }
5752
+ }
5753
+ async function runQueue(queue, result = [], client, spaceId, environmentId, requestQueue, localePublishing) {
5611
5754
  const publishedEntities = [];
5612
5755
  for (const entity of queue) {
5613
- import_logging6.logEmitter.emit("info", `Publishing ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}`);
5756
+ const locales2 = localePublishing && !isLocaleScopingUnavailable(client) ? localePublishing.localesByEntityId.get(entity.sys.id) : void 0;
5757
+ if (locales2) {
5758
+ import_logging7.logEmitter.emit("info", `Publishing ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)} for locales ${locales2.join(", ")}`);
5759
+ } else {
5760
+ import_logging7.logEmitter.emit("info", `Publishing ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}`);
5761
+ }
5614
5762
  try {
5615
- const publishedEntity = await requestQueue.add(() => publishEntity(client, spaceId, environmentId, entity));
5763
+ const publishedEntity = await requestQueue.add(() => locales2 ? publishEntityLocales(client, spaceId, environmentId, entity, locales2, localePublishing?.demoteLocalesByEntityId.get(entity.sys.id)) : publishEntity(client, spaceId, environmentId, entity));
5616
5764
  publishedEntities.push(publishedEntity);
5617
5765
  } catch (err) {
5618
5766
  if (err instanceof ContentfulEntityError) {
5619
5767
  err.entity = entity;
5620
5768
  }
5621
- import_logging6.logEmitter.emit("error", err);
5769
+ import_logging7.logEmitter.emit("error", err);
5622
5770
  }
5623
5771
  }
5624
5772
  result = [
@@ -5630,9 +5778,9 @@ async function runQueue(queue, result = [], client, spaceId, environmentId, requ
5630
5778
  if (unpublishedEntities.length > 0) {
5631
5779
  if (queue.length === unpublishedEntities.length) {
5632
5780
  const unpublishedEntityNames = unpublishedEntities.map(import_get_entity_name3.default).join(", ");
5633
- import_logging6.logEmitter.emit("error", `Could not publish the following entities: ${unpublishedEntityNames}`);
5781
+ import_logging7.logEmitter.emit("error", `Could not publish the following entities: ${unpublishedEntityNames}`);
5634
5782
  } else {
5635
- return runQueue(unpublishedEntities, result, client, spaceId, environmentId, requestQueue);
5783
+ return runQueue(unpublishedEntities, result, client, spaceId, environmentId, requestQueue, localePublishing);
5636
5784
  }
5637
5785
  }
5638
5786
  return result;
@@ -5764,18 +5912,18 @@ function sortExperienceFragments(experienceFragments) {
5764
5912
  }
5765
5913
 
5766
5914
  // lib/utils/sort-or-report.ts
5767
- var import_logging7 = __toESM(require_logging());
5915
+ var import_logging8 = __toESM(require_logging());
5768
5916
  function sortOrReport(sortFn) {
5769
5917
  try {
5770
5918
  return sortFn();
5771
5919
  } catch (err) {
5772
- import_logging7.logEmitter.emit("error", err);
5920
+ import_logging8.logEmitter.emit("error", err);
5773
5921
  throw err;
5774
5922
  }
5775
5923
  }
5776
5924
 
5777
5925
  // lib/utils/import-exo-folders.ts
5778
- var import_logging8 = __toESM(require_logging());
5926
+ var import_logging9 = __toESM(require_logging());
5779
5927
  var FOLDER_CONCEPT_PREFIX = "contentful.folder-";
5780
5928
  var PARENT_FOLDER_GROUP_IDS = {
5781
5929
  designToken: "contentful.folder-group-designToken",
@@ -5845,7 +5993,7 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5845
5993
  existing = await client.concept.get({ organizationId, conceptId: destConceptId });
5846
5994
  } catch (err) {
5847
5995
  if (err?.name !== "NotFound") {
5848
- import_logging8.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
5996
+ import_logging9.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
5849
5997
  }
5850
5998
  }
5851
5999
  if (!existing) {
@@ -5855,9 +6003,9 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5855
6003
  // @ts-expect-error - CMA.js type needs to be updated to be aware of purpose: 'internal'
5856
6004
  { purpose: "internal", prefLabel, metadata: { spaces: [spaceLink] } }
5857
6005
  );
5858
- import_logging8.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
6006
+ import_logging9.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
5859
6007
  } catch (err) {
5860
- import_logging8.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
6008
+ import_logging9.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
5861
6009
  }
5862
6010
  } else {
5863
6011
  const patches = [];
@@ -5871,9 +6019,9 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5871
6019
  { organizationId, conceptId: destConceptId, version: existing.sys.version },
5872
6020
  patches
5873
6021
  );
5874
- import_logging8.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
6022
+ import_logging9.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
5875
6023
  } catch (err) {
5876
- import_logging8.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
6024
+ import_logging9.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
5877
6025
  }
5878
6026
  }
5879
6027
  }
@@ -5891,9 +6039,9 @@ async function linkChildConceptsToParentGroups(client, organizationId, childConc
5891
6039
  [{ op: "add", path: "/concepts/-", value: { sys: { type: "Link", linkType: "TaxonomyConcept", id: destConceptId } } }]
5892
6040
  );
5893
6041
  parentGroups.set(parentGroupId, updated);
5894
- import_logging8.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
6042
+ import_logging9.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
5895
6043
  } catch (err) {
5896
- import_logging8.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
6044
+ import_logging9.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
5897
6045
  }
5898
6046
  }
5899
6047
  }
@@ -5915,17 +6063,17 @@ async function importExoFolders({
5915
6063
  }) {
5916
6064
  const sourceSpaceId = getSourceSpaceId(sourceEntities);
5917
6065
  if (sourceSpaceId === destinationSpaceId) {
5918
- import_logging8.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
6066
+ import_logging9.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
5919
6067
  return;
5920
6068
  }
5921
6069
  const parentGroups = await ensureParentFolderGroupsExist(client, organizationId);
5922
6070
  if (parentGroups.size === 0) {
5923
- 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.");
6071
+ import_logging9.logEmitter.emit("warn", "One or more Experience Orchestration folder group concept schemes are missing in the destination organization. Please create them before importing.");
5924
6072
  return;
5925
6073
  }
5926
6074
  const childConceptMap = deriveChildConceptMap(sourceEntities, destinationSpaceId);
5927
6075
  if (childConceptMap.size === 0) return;
5928
- import_logging8.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
6076
+ import_logging9.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
5929
6077
  await createOrPatchChildConcepts(client, organizationId, destinationSpaceId, childConceptMap);
5930
6078
  await linkChildConceptsToParentGroups(client, organizationId, childConceptMap, parentGroups);
5931
6079
  const allEntities = [
@@ -5938,6 +6086,79 @@ async function importExoFolders({
5938
6086
  rewriteEntityFolderConcepts(allEntities, childConceptMap);
5939
6087
  }
5940
6088
 
6089
+ // lib/utils/resolve-publish-locales.ts
6090
+ var LIVE_FIELD_STATUSES = ["published", "changed"];
6091
+ function resolvePublishLocales(sys, destinationLocaleCodes) {
6092
+ const fieldStatus = sys.fieldStatus?.["*"];
6093
+ if (!fieldStatus || !destinationLocaleCodes) {
6094
+ return null;
6095
+ }
6096
+ const liveLocales = Object.keys(fieldStatus).filter((locale) => LIVE_FIELD_STATUSES.indexOf(fieldStatus[locale]) !== -1);
6097
+ const availableLocales = liveLocales.filter((locale) => destinationLocaleCodes.indexOf(locale) !== -1);
6098
+ if (availableLocales.length === 0) {
6099
+ return [];
6100
+ }
6101
+ const everyLocaleIsLive = destinationLocaleCodes.every((locale) => availableLocales.indexOf(locale) !== -1);
6102
+ return everyLocaleIsLive ? null : availableLocales;
6103
+ }
6104
+ function resolveLocalesToDemote(sys, destinationSys, destinationLocaleCodes) {
6105
+ const fieldStatus = sys.fieldStatus?.["*"];
6106
+ const destinationFieldStatus = destinationSys?.fieldStatus?.["*"];
6107
+ if (!fieldStatus || !destinationFieldStatus) {
6108
+ return [];
6109
+ }
6110
+ return Object.keys(fieldStatus).filter((locale) => fieldStatus[locale] === "draft").filter((locale) => destinationLocaleCodes.indexOf(locale) !== -1).filter((locale) => LIVE_FIELD_STATUSES.indexOf(destinationFieldStatus[locale]) !== -1);
6111
+ }
6112
+ function buildLocalePublishPlan(sourceEntities, destinationLocaleCodes, { destinationEntitiesById } = {}) {
6113
+ const localesByEntityId = /* @__PURE__ */ new Map();
6114
+ const skippedEntityIds = /* @__PURE__ */ new Set();
6115
+ const demoteLocalesByEntityId = /* @__PURE__ */ new Map();
6116
+ for (const { original } of sourceEntities) {
6117
+ const locales2 = resolvePublishLocales(original.sys, destinationLocaleCodes);
6118
+ if (locales2 === null) {
6119
+ continue;
6120
+ }
6121
+ if (locales2.length === 0) {
6122
+ skippedEntityIds.add(original.sys.id);
6123
+ continue;
6124
+ }
6125
+ localesByEntityId.set(original.sys.id, locales2);
6126
+ if (!destinationEntitiesById) {
6127
+ continue;
6128
+ }
6129
+ const localesToDemote = resolveLocalesToDemote(
6130
+ original.sys,
6131
+ destinationEntitiesById.get(original.sys.id)?.sys,
6132
+ destinationLocaleCodes
6133
+ );
6134
+ if (localesToDemote.length) {
6135
+ demoteLocalesByEntityId.set(original.sys.id, localesToDemote);
6136
+ }
6137
+ }
6138
+ return { localesByEntityId, skippedEntityIds, demoteLocalesByEntityId };
6139
+ }
6140
+
6141
+ // lib/utils/destination-locales.ts
6142
+ var import_logging10 = __toESM(require_logging());
6143
+ var cache = /* @__PURE__ */ new WeakMap();
6144
+ function getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueue }) {
6145
+ let byEnvironment = cache.get(client);
6146
+ if (!byEnvironment) {
6147
+ byEnvironment = /* @__PURE__ */ new Map();
6148
+ cache.set(client, byEnvironment);
6149
+ }
6150
+ const key = `${spaceId}/${environmentId}`;
6151
+ let localeCodes = byEnvironment.get(key);
6152
+ if (!localeCodes) {
6153
+ localeCodes = batchedPageQuery({ client, spaceId, environmentId, type: "locales", requestQueue }).then((items) => items.map((locale) => locale.code)).catch((err) => {
6154
+ import_logging10.logEmitter.emit("warning", `Could not read the locales of the destination environment, falling back to publishing all locales: ${err.message}`);
6155
+ return null;
6156
+ });
6157
+ byEnvironment.set(key, localeCodes);
6158
+ }
6159
+ return localeCodes;
6160
+ }
6161
+
5941
6162
  // lib/tasks/push-to-space/push-to-space.ts
5942
6163
  async function withGraphQLSchemaBackoff(fn) {
5943
6164
  let lastErr;
@@ -5949,7 +6170,7 @@ async function withGraphQLSchemaBackoff(fn) {
5949
6170
  throw err;
5950
6171
  }
5951
6172
  const delay = GRAPHQL_SCHEMA_STALE_DELAYS_MS[attempt];
5952
- import_logging9.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
6173
+ import_logging11.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
5953
6174
  await new Promise((resolve2) => setTimeout(resolve2, delay));
5954
6175
  lastErr = err;
5955
6176
  }
@@ -5972,11 +6193,13 @@ function pushToSpace({
5972
6193
  spaceId,
5973
6194
  environmentId,
5974
6195
  includeExperienceOrchestration,
6196
+ skipExoVariants,
5975
6197
  contentModelOnly,
5976
6198
  skipContentModel,
5977
6199
  skipContentUpdates,
5978
6200
  skipLocales,
5979
6201
  skipContentPublishing,
6202
+ unpublishDraftLocales,
5980
6203
  timeout,
5981
6204
  retryLimit,
5982
6205
  listrOptions,
@@ -5993,6 +6216,13 @@ function pushToSpace({
5993
6216
  ...DEFAULT_CONTENT_STRUCTURE,
5994
6217
  ...destinationData
5995
6218
  };
6219
+ const hasOptimizationVariants = [
6220
+ ...sourceData.experiences || [],
6221
+ ...sourceData.experienceFragments || []
6222
+ ].some((parent2) => (parent2.optimizationVariants || []).some(isRealOptimizationVariant));
6223
+ if (includeExperienceOrchestration && !skipExoVariants && hasOptimizationVariants) {
6224
+ import_logging11.logEmitter.emit("warning", "Optimization Variants are imported with new destination IDs and can be duplicated by re-imports. Set skipExoVariants to true to skip them.");
6225
+ }
5996
6226
  listrOptions = listrOptions || {
5997
6227
  renderer: import_listr_verbose_renderer.default
5998
6228
  };
@@ -6088,7 +6318,7 @@ function pushToSpace({
6088
6318
  const ctEditorInterface = await requestQueue.add(
6089
6319
  () => client.editorInterface.get({ spaceId, environmentId, contentTypeId: contentType.sys.id })
6090
6320
  );
6091
- import_logging9.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6321
+ import_logging11.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6092
6322
  const updatedData = {
6093
6323
  ...ctEditorInterface,
6094
6324
  controls: editorInterface.controls,
@@ -6106,7 +6336,7 @@ function pushToSpace({
6106
6336
  return updatedEditorInterface;
6107
6337
  } catch (err) {
6108
6338
  err.entity = editorInterface;
6109
- import_logging9.logEmitter.emit("error", err);
6339
+ import_logging11.logEmitter.emit("error", err);
6110
6340
  return null;
6111
6341
  }
6112
6342
  });
@@ -6124,7 +6354,7 @@ function pushToSpace({
6124
6354
  for (const file of Object.values(asset.transformed.fields.file)) {
6125
6355
  allPendingUploads.push(requestQueue.add(async () => {
6126
6356
  try {
6127
- import_logging9.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6357
+ import_logging11.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6128
6358
  const assetStream = await getAssetStreamForURL(file.upload, assetsDirectory);
6129
6359
  const upload = await client.upload.create(
6130
6360
  { spaceId, environmentId },
@@ -6140,7 +6370,7 @@ function pushToSpace({
6140
6370
  };
6141
6371
  return upload;
6142
6372
  } catch (err) {
6143
- import_logging9.logEmitter.emit("error", err);
6373
+ import_logging11.logEmitter.emit("error", err);
6144
6374
  }
6145
6375
  }));
6146
6376
  }
@@ -6185,7 +6415,8 @@ function pushToSpace({
6185
6415
  client,
6186
6416
  spaceId,
6187
6417
  environmentId,
6188
- requestQueue
6418
+ requestQueue,
6419
+ destinationEntitiesById: unpublishDraftLocales ? destinationDataById.assets : void 0
6189
6420
  });
6190
6421
  ctx.data.publishedAssets = publishedAssets;
6191
6422
  }),
@@ -6229,7 +6460,8 @@ function pushToSpace({
6229
6460
  client,
6230
6461
  spaceId,
6231
6462
  environmentId,
6232
- requestQueue
6463
+ requestQueue,
6464
+ destinationEntitiesById: unpublishDraftLocales ? destinationDataById.entries : void 0
6233
6465
  });
6234
6466
  ctx.data.publishedEntries = publishedEntries;
6235
6467
  }),
@@ -6284,7 +6516,7 @@ function pushToSpace({
6284
6516
  }
6285
6517
  });
6286
6518
  } catch (error) {
6287
- import_logging9.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6519
+ import_logging11.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6288
6520
  }
6289
6521
  }),
6290
6522
  skip: () => !includeExperienceOrchestration
@@ -6302,19 +6534,19 @@ function pushToSpace({
6302
6534
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6303
6535
  payload
6304
6536
  ));
6305
- import_logging9.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6537
+ import_logging11.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6306
6538
  } else {
6307
6539
  const payload = { ...omitSys(entity), sys: buildDataAssemblySys(entity, 0) };
6308
6540
  result = await withGraphQLSchemaBackoff(() => client.dataAssembly.update(
6309
6541
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6310
6542
  payload
6311
6543
  ));
6312
- import_logging9.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6544
+ import_logging11.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6313
6545
  }
6314
6546
  return result;
6315
6547
  } catch (err) {
6316
6548
  err.entity = entity;
6317
- import_logging9.logEmitter.emit("error", err);
6549
+ import_logging11.logEmitter.emit("error", err);
6318
6550
  return null;
6319
6551
  }
6320
6552
  }));
@@ -6344,17 +6576,17 @@ function pushToSpace({
6344
6576
  if (existing) {
6345
6577
  const payload = { ...entity, sys: { id: entity.sys.id, type: "DesignToken", version: existing.sys.version } };
6346
6578
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6347
- import_logging9.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6579
+ import_logging11.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6348
6580
  return result;
6349
6581
  } else {
6350
6582
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DesignToken" } };
6351
6583
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6352
- import_logging9.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6584
+ import_logging11.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6353
6585
  return result;
6354
6586
  }
6355
6587
  } catch (err) {
6356
6588
  err.entity = entity;
6357
- import_logging9.logEmitter.emit("error", err);
6589
+ import_logging11.logEmitter.emit("error", err);
6358
6590
  return null;
6359
6591
  }
6360
6592
  }));
@@ -6373,17 +6605,17 @@ function pushToSpace({
6373
6605
  if (existing) {
6374
6606
  const payload = { ...entity, sys: { id: entity.sys.id, type: "Component", version: existing.sys.version } };
6375
6607
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6376
- import_logging9.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6608
+ import_logging11.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6377
6609
  results.push(result);
6378
6610
  } else {
6379
6611
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "Component" } };
6380
6612
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6381
- import_logging9.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6613
+ import_logging11.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6382
6614
  results.push(result);
6383
6615
  }
6384
6616
  } catch (err) {
6385
6617
  err.entity = entity;
6386
- import_logging9.logEmitter.emit("error", err);
6618
+ import_logging11.logEmitter.emit("error", err);
6387
6619
  }
6388
6620
  }
6389
6621
  ctx.data.components = results;
@@ -6415,17 +6647,17 @@ function pushToSpace({
6415
6647
  if (existing) {
6416
6648
  const payload = { ...entity, sys: { id: entity.sys.id, type: "ExperienceTemplate", version: existing.sys.version } };
6417
6649
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6418
- import_logging9.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6650
+ import_logging11.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6419
6651
  return result;
6420
6652
  } else {
6421
6653
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "ExperienceTemplate" } };
6422
6654
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6423
- import_logging9.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6655
+ import_logging11.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6424
6656
  return result;
6425
6657
  }
6426
6658
  } catch (err) {
6427
6659
  err.entity = entity;
6428
- import_logging9.logEmitter.emit("error", err);
6660
+ import_logging11.logEmitter.emit("error", err);
6429
6661
  return null;
6430
6662
  }
6431
6663
  }));
@@ -6455,19 +6687,19 @@ function pushToSpace({
6455
6687
  try {
6456
6688
  const existing = destinationDataById.experienceFragments?.get(entity.sys.id);
6457
6689
  if (existing) {
6458
- const payload = { ...entity, sys: { id: entity.sys.id, type: "ExperienceFragment", version: existing.sys.version } };
6690
+ const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "ExperienceFragment", version: existing.sys.version } };
6459
6691
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6460
- import_logging9.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6692
+ import_logging11.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6461
6693
  results.push(result);
6462
6694
  } else {
6463
- const payload = { ...omitSys(entity), component: entity.sys.component, sys: { id: entity.sys.id, type: "ExperienceFragment" } };
6695
+ const payload = { ...omitSysAndOptimizationVariants(entity), component: entity.sys.component, sys: { id: entity.sys.id, type: "ExperienceFragment" } };
6464
6696
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6465
- import_logging9.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6697
+ import_logging11.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6466
6698
  results.push(result);
6467
6699
  }
6468
6700
  } catch (err) {
6469
6701
  err.entity = entity;
6470
- import_logging9.logEmitter.emit("error", err);
6702
+ import_logging11.logEmitter.emit("error", err);
6471
6703
  }
6472
6704
  }
6473
6705
  ctx.data.experienceFragments = results;
@@ -6497,19 +6729,19 @@ function pushToSpace({
6497
6729
  try {
6498
6730
  const existing = destinationDataById.experiences?.get(entity.sys.id);
6499
6731
  if (existing) {
6500
- const payload = { ...entity, sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
6732
+ const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
6501
6733
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6502
- import_logging9.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6734
+ import_logging11.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6503
6735
  return result;
6504
6736
  } else {
6505
- const payload = { ...omitSys(entity), experienceTemplate: entity.sys.experienceTemplate, sys: { id: entity.sys.id, type: "Experience" } };
6737
+ const payload = { ...omitSysAndOptimizationVariants(entity), experienceTemplate: entity.sys.experienceTemplate, sys: { id: entity.sys.id, type: "Experience" } };
6506
6738
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6507
- import_logging9.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6739
+ import_logging11.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6508
6740
  return result;
6509
6741
  }
6510
6742
  } catch (err) {
6511
6743
  err.entity = entity;
6512
- import_logging9.logEmitter.emit("error", err);
6744
+ import_logging11.logEmitter.emit("error", err);
6513
6745
  return null;
6514
6746
  }
6515
6747
  }));
@@ -6530,6 +6762,85 @@ function pushToSpace({
6530
6762
  }),
6531
6763
  skip: () => !includeExperienceOrchestration || skipContentPublishing || !(sourceData.experiences || []).length
6532
6764
  },
6765
+ // Optimization Variants are sub-resources nested on their parent Experience/ExperienceFragment
6766
+ // (sourceData.experiences[].optimizationVariants), not a flat top-level array. A variant's
6767
+ // sys.id is borrowed from its parent, not unique, so variant creation/publish/archive is
6768
+ // handled by new, self-contained functions (importVariantsForParents, filterVariantsToPublish,
6769
+ // filterVariantsToArchive, publishVariant, archiveVariant) rather than by generalizing
6770
+ // destinationDataById / filterExoEntitiesToPublish / filterExoEntitiesToUnpublish, which all
6771
+ // assume sys.id uniqueness.
6772
+ //
6773
+ // Unlike the base Experience/Fragment create/update, there is no upsert-by-known-ID for
6774
+ // variants: the upstream API's create endpoint always server-generates a fresh variantId
6775
+ // (crypto.randomUUID()) and its update endpoint 404s on an unknown variantId rather than
6776
+ // creating one. So every import run creates fresh destination variants via POST rather than
6777
+ // matching against any existing destination variant.
6778
+ {
6779
+ title: "Importing Experience Optimization Variants",
6780
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6781
+ const results = await importVariantsForParents({
6782
+ parents: ctx.data.experiences || [],
6783
+ sourceParents: sourceData.experiences || [],
6784
+ client,
6785
+ spaceId,
6786
+ environmentId,
6787
+ kind: "experience"
6788
+ });
6789
+ ctx.data.experienceVariants = results.flatMap((r) => r.created);
6790
+ }),
6791
+ skip: () => !includeExperienceOrchestration || skipExoVariants || !(sourceData.experiences || []).some((e) => (e.optimizationVariants || []).some(isRealOptimizationVariant))
6792
+ },
6793
+ {
6794
+ title: "Publishing Experience Optimization Variants",
6795
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6796
+ const toPublish = filterVariantsToPublish(ctx.data.experienceVariants || []);
6797
+ await Promise.all(toPublish.map(
6798
+ (v) => publishVariant("ExperienceVariant", v, () => client.experienceVariant.publish(
6799
+ { spaceId, environmentId, experienceId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6800
+ ))
6801
+ ));
6802
+ const toArchive = filterVariantsToArchive(ctx.data.experienceVariants || []);
6803
+ await Promise.all(toArchive.map(
6804
+ (v) => archiveVariant("ExperienceVariant", v, () => client.experienceVariant.archive(
6805
+ { spaceId, environmentId, experienceId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6806
+ ))
6807
+ ));
6808
+ }),
6809
+ skip: () => !includeExperienceOrchestration || skipExoVariants || skipContentPublishing || !(sourceData.experiences || []).some((e) => (e.optimizationVariants || []).some(isRealOptimizationVariant))
6810
+ },
6811
+ {
6812
+ title: "Importing Experience Fragment Optimization Variants",
6813
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6814
+ const results = await importVariantsForParents({
6815
+ parents: ctx.data.experienceFragments || [],
6816
+ sourceParents: sourceData.experienceFragments || [],
6817
+ client,
6818
+ spaceId,
6819
+ environmentId,
6820
+ kind: "experienceFragment"
6821
+ });
6822
+ ctx.data.experienceFragmentVariants = results.flatMap((r) => r.created);
6823
+ }),
6824
+ skip: () => !includeExperienceOrchestration || skipExoVariants || !(sourceData.experienceFragments || []).some((f) => (f.optimizationVariants || []).some(isRealOptimizationVariant))
6825
+ },
6826
+ {
6827
+ title: "Publishing Experience Fragment Optimization Variants",
6828
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6829
+ const toPublish = filterVariantsToPublish(ctx.data.experienceFragmentVariants || []);
6830
+ await Promise.all(toPublish.map(
6831
+ (v) => publishVariant("ExperienceFragmentVariant", v, () => client.experienceFragmentVariant.publish(
6832
+ { spaceId, environmentId, experienceFragmentId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6833
+ ))
6834
+ ));
6835
+ const toArchive = filterVariantsToArchive(ctx.data.experienceFragmentVariants || []);
6836
+ await Promise.all(toArchive.map(
6837
+ (v) => archiveVariant("ExperienceFragmentVariant", v, () => client.experienceFragmentVariant.archive(
6838
+ { spaceId, environmentId, experienceFragmentId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6839
+ ))
6840
+ ));
6841
+ }),
6842
+ skip: () => !includeExperienceOrchestration || skipExoVariants || skipContentPublishing || !(sourceData.experienceFragments || []).some((f) => (f.optimizationVariants || []).some(isRealOptimizationVariant))
6843
+ },
6533
6844
  // Unpublishing runs after all Importing/Publishing tasks, in reverse dependency order
6534
6845
  // (Experience first, DataAssembly last) - the API rejects unpublishing an entity that a
6535
6846
  // still-published parent references, so parents must unpublish before the children they embed.
@@ -6601,15 +6912,88 @@ function omitSys(entity) {
6601
6912
  const { sys: _sys, ...rest } = entity;
6602
6913
  return rest;
6603
6914
  }
6915
+ function omitSysAndOptimizationVariants(entity) {
6916
+ const { sys: _sys, optimizationVariants: _variants, ...rest } = entity;
6917
+ return rest;
6918
+ }
6919
+ function omitVariantSys(variant) {
6920
+ const { sys: _sys, ...rest } = variant;
6921
+ return rest;
6922
+ }
6923
+ function isRealOptimizationVariant(variant) {
6924
+ return Boolean(variant?.sys) && variant.sys.variantType !== "default";
6925
+ }
6926
+ async function importVariantsForParents({
6927
+ parents,
6928
+ sourceParents,
6929
+ client,
6930
+ spaceId,
6931
+ environmentId,
6932
+ kind
6933
+ }) {
6934
+ const ns = kind === "experience" ? client.experienceVariant : client.experienceFragmentVariant;
6935
+ const idParam = kind === "experience" ? "experienceId" : "experienceFragmentId";
6936
+ const label = kind === "experience" ? "ExperienceVariant" : "ExperienceFragmentVariant";
6937
+ const parentRefField = kind === "experience" ? "experienceTemplate" : "component";
6938
+ const sourceParentsById = new Map(sourceParents.map((sourceParent) => [sourceParent.sys.id, sourceParent]));
6939
+ return Promise.all(parents.map(async (parent2) => {
6940
+ const sourceParent = sourceParentsById.get(parent2.sys.id);
6941
+ const sourceVariants = (sourceParent?.optimizationVariants ?? []).filter(isRealOptimizationVariant);
6942
+ const created = [];
6943
+ for (const variant of sourceVariants) {
6944
+ try {
6945
+ const payload = { ...omitVariantSys(variant), [parentRefField]: variant.sys[parentRefField] };
6946
+ const result = await ns.create({ spaceId, environmentId, [idParam]: parent2.sys.id }, payload);
6947
+ import_logging11.logEmitter.emit("info", `CREATE ${label} ${result.sys.variant} (parent ${parent2.sys.id})`);
6948
+ created.push({ ...result, sys: { ...result.sys, publishedVersion: variant.sys.publishedVersion, archivedVersion: variant.sys.archivedVersion } });
6949
+ } catch (err) {
6950
+ err.entity = variant;
6951
+ import_logging11.logEmitter.emit("error", err);
6952
+ }
6953
+ }
6954
+ return { parentId: parent2.sys.id, created };
6955
+ }));
6956
+ }
6604
6957
  function archiveEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue }) {
6605
6958
  const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
6606
6959
  const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
6607
6960
  return archiveEntities({ entities: entitiesToArchive, client, spaceId, environmentId, requestQueue });
6608
6961
  }
6609
- function publishEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue }) {
6962
+ async function publishEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue, destinationEntitiesById }) {
6610
6963
  const entityIdsToPublish = sourceEntities.filter(({ original }) => original.sys.publishedVersion).map(({ original }) => original.sys.id);
6611
- const entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
6612
- return publishEntities({ entities: entitiesToPublish, client, spaceId, environmentId, requestQueue });
6964
+ let entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
6965
+ const publishAllLocales = () => publishEntities({ entities: entitiesToPublish, client, spaceId, environmentId, requestQueue });
6966
+ const hasFieldStatus = sourceEntities.some(({ original }) => original.sys.fieldStatus);
6967
+ if (!hasFieldStatus) {
6968
+ return publishAllLocales();
6969
+ }
6970
+ await ensureLocalePublishingEntitlement(client, spaceId);
6971
+ if (isLocaleScopingUnavailable(client)) {
6972
+ return publishAllLocales();
6973
+ }
6974
+ const destinationLocaleCodes = await getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueue });
6975
+ const localePublishing = buildLocalePublishPlan(
6976
+ sourceEntities,
6977
+ destinationLocaleCodes,
6978
+ { destinationEntitiesById }
6979
+ );
6980
+ if (localePublishing.skippedEntityIds.size) {
6981
+ entitiesToPublish = entitiesToPublish.filter((entity) => {
6982
+ if (!localePublishing.skippedEntityIds.has(entity.sys.id)) {
6983
+ return true;
6984
+ }
6985
+ import_logging11.logEmitter.emit("warning", `Not publishing ${entity.sys.type} ${entity.sys.id} because none of the locales it was published for exist in the destination environment`);
6986
+ return false;
6987
+ });
6988
+ }
6989
+ return publishEntities({
6990
+ entities: entitiesToPublish,
6991
+ client,
6992
+ spaceId,
6993
+ environmentId,
6994
+ requestQueue,
6995
+ localePublishing
6996
+ });
6613
6997
  }
6614
6998
 
6615
6999
  // lib/transform/transformers.ts
@@ -7149,6 +7533,7 @@ async function parseOptions(params) {
7149
7533
  skipContentModel: false,
7150
7534
  skipLocales: false,
7151
7535
  skipContentPublishing: false,
7536
+ unpublishDraftLocales: false,
7152
7537
  skipAssetUpdates: false,
7153
7538
  skipContentUpdates: false,
7154
7539
  useVerboseRenderer: false,
@@ -7157,6 +7542,7 @@ async function parseOptions(params) {
7157
7542
  uploadAssets: false,
7158
7543
  rateLimit: 7,
7159
7544
  includeExperienceOrchestration: true,
7545
+ skipExoVariants: false,
7160
7546
  host: "api.contentful.com"
7161
7547
  };
7162
7548
  const configFile = params.config ? require((0, import_path2.resolve)(process.cwd(), params.config)) : {};
@@ -7231,6 +7617,29 @@ async function parseOptions(params) {
7231
7617
  return options;
7232
7618
  }
7233
7619
 
7620
+ // lib/utils/display-error-log.ts
7621
+ var import_date_fns2 = require("date-fns");
7622
+ var import_logging12 = __toESM(require_logging());
7623
+ function displayErrorLog(errorLog) {
7624
+ if (errorLog.length) {
7625
+ const count = errorLog.reduce((count2, curr) => {
7626
+ if (Object.prototype.hasOwnProperty.call(curr, "warning")) {
7627
+ count2.warnings++;
7628
+ } else if (Object.prototype.hasOwnProperty.call(curr, "error")) {
7629
+ count2.errors++;
7630
+ }
7631
+ return count2;
7632
+ }, { warnings: 0, errors: 0 });
7633
+ console.log(`
7634
+
7635
+ The following ${count.errors} errors and ${count.warnings} warnings occurred:
7636
+ `);
7637
+ errorLog.map((logMessage) => `${(0, import_date_fns2.format)((0, import_date_fns2.parseISO)(logMessage.ts), "HH:mm:ss")} - ${(0, import_logging12.formatLogMessageOneLine)(logMessage)}`).map((logMessage) => console.log(logMessage));
7638
+ return;
7639
+ }
7640
+ console.log("No errors or warnings occurred");
7641
+ }
7642
+
7234
7643
  // lib/index.ts
7235
7644
  var ONE_SECOND = 1e3;
7236
7645
  var tableOptions = {
@@ -7257,7 +7666,7 @@ async function runContentfulImport(params) {
7257
7666
  intervalCap: options.rateLimit,
7258
7667
  carryoverConcurrencyCount: true
7259
7668
  });
7260
- (0, import_logging10.setupLogging)(log);
7669
+ (0, import_logging13.setupLogging)(log);
7261
7670
  const infoTable = new import_cli_table3.default(tableOptions);
7262
7671
  infoTable.push([{ colSpan: 2, content: "The following entities are going to be imported:" }]);
7263
7672
  Object.keys(options.content).forEach((type) => {
@@ -7272,6 +7681,20 @@ async function runContentfulImport(params) {
7272
7681
  }
7273
7682
  infoTable.push([startCase_default(type), options.content[type].length]);
7274
7683
  });
7684
+ const experienceVariantCount = (options.content.experiences || []).reduce(
7685
+ (sum, e) => sum + (e.optimizationVariants?.length ?? 0),
7686
+ 0
7687
+ );
7688
+ if (!options.skipExoVariants && experienceVariantCount > 0) {
7689
+ infoTable.push(["Experience Optimization Variants", experienceVariantCount]);
7690
+ }
7691
+ const experienceFragmentVariantCount = (options.content.experienceFragments || []).reduce(
7692
+ (sum, f) => sum + (f.optimizationVariants?.length ?? 0),
7693
+ 0
7694
+ );
7695
+ if (!options.skipExoVariants && experienceFragmentVariantCount > 0) {
7696
+ infoTable.push(["Experience Fragment Optimization Variants", experienceFragmentVariantCount]);
7697
+ }
7275
7698
  console.log(infoTable.toString());
7276
7699
  const tasks = new import_listr3.default([
7277
7700
  {
@@ -7320,11 +7743,13 @@ async function runContentfulImport(params) {
7320
7743
  client: ctx.client,
7321
7744
  spaceId: options.spaceId,
7322
7745
  includeExperienceOrchestration: options.includeExperienceOrchestration,
7746
+ skipExoVariants: options.skipExoVariants,
7323
7747
  environmentId: options.environmentId,
7324
7748
  contentModelOnly: options.contentModelOnly,
7325
7749
  skipLocales: options.skipLocales,
7326
7750
  skipContentModel: options.skipContentModel,
7327
7751
  skipContentPublishing: options.skipContentPublishing,
7752
+ unpublishDraftLocales: options.unpublishDraftLocales,
7328
7753
  skipAssetUpdates: options.skipAssetUpdates,
7329
7754
  skipContentUpdates: options.skipContentUpdates,
7330
7755
  timeout: options.timeout,
@@ -7353,8 +7778,8 @@ async function runContentfulImport(params) {
7353
7778
  console.log("No data was imported");
7354
7779
  }
7355
7780
  const endTime = /* @__PURE__ */ new Date();
7356
- const durationHuman = (0, import_date_fns2.formatDistance)(endTime, options.startTime);
7357
- const durationSeconds = (0, import_date_fns2.differenceInSeconds)(endTime, options.startTime);
7781
+ const durationHuman = (0, import_date_fns3.formatDistance)(endTime, options.startTime);
7782
+ const durationSeconds = (0, import_date_fns3.differenceInSeconds)(endTime, options.startTime);
7358
7783
  console.log(`The import took ${durationHuman} (${durationSeconds}s)`);
7359
7784
  return ctx.data;
7360
7785
  }).catch((err) => {
@@ -7366,9 +7791,9 @@ async function runContentfulImport(params) {
7366
7791
  }).then((data) => {
7367
7792
  const errorLog = log.filter((logMessage) => logMessage.level !== "info" && logMessage.level !== "warning");
7368
7793
  const displayLog = log.filter((logMessage) => logMessage.level !== "info");
7369
- (0, import_logging10.displayErrorLog)(displayLog);
7794
+ displayErrorLog(displayLog);
7370
7795
  if (errorLog.length) {
7371
- return (0, import_logging10.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7796
+ return (0, import_logging13.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7372
7797
  const multiError = new ContentfulMultiError("Errors occurred");
7373
7798
  multiError.name = "ContentfulMultiError";
7374
7799
  multiError.errors = errorLog;