contentful-import 10.3.0 → 10.5.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);
@@ -4517,34 +4517,6 @@ var createBaseEach_default = createBaseEach;
4517
4517
  var baseEach = createBaseEach_default(baseForOwn_default);
4518
4518
  var baseEach_default = baseEach;
4519
4519
 
4520
- // node_modules/lodash-es/defaults.js
4521
- var objectProto18 = Object.prototype;
4522
- var hasOwnProperty15 = objectProto18.hasOwnProperty;
4523
- var defaults = baseRest_default(function(object, sources) {
4524
- object = Object(object);
4525
- var index = -1;
4526
- var length = sources.length;
4527
- var guard = length > 2 ? sources[2] : void 0;
4528
- if (guard && isIterateeCall_default(sources[0], sources[1], guard)) {
4529
- length = 1;
4530
- }
4531
- while (++index < length) {
4532
- var source = sources[index];
4533
- var props = keysIn_default(source);
4534
- var propsIndex = -1;
4535
- var propsLength = props.length;
4536
- while (++propsIndex < propsLength) {
4537
- var key = props[propsIndex];
4538
- var value = object[key];
4539
- if (value === void 0 || eq_default(value, objectProto18[key]) && !hasOwnProperty15.call(object, key)) {
4540
- object[key] = source[key];
4541
- }
4542
- }
4543
- }
4544
- return object;
4545
- });
4546
- var defaults_default = defaults;
4547
-
4548
4520
  // node_modules/lodash-es/last.js
4549
4521
  function last(array) {
4550
4522
  var length = array == null ? 0 : array.length;
@@ -4625,10 +4597,10 @@ function map(collection, iteratee) {
4625
4597
  var map_default = map;
4626
4598
 
4627
4599
  // node_modules/lodash-es/_baseHas.js
4628
- var objectProto19 = Object.prototype;
4629
- var hasOwnProperty16 = objectProto19.hasOwnProperty;
4600
+ var objectProto18 = Object.prototype;
4601
+ var hasOwnProperty15 = objectProto18.hasOwnProperty;
4630
4602
  function baseHas(object, key) {
4631
- return object != null && hasOwnProperty16.call(object, key);
4603
+ return object != null && hasOwnProperty15.call(object, key);
4632
4604
  }
4633
4605
  var baseHas_default = baseHas;
4634
4606
 
@@ -4682,8 +4654,8 @@ function negate(predicate) {
4682
4654
  var negate_default = negate;
4683
4655
 
4684
4656
  // node_modules/lodash-es/_baseUnset.js
4685
- var objectProto20 = Object.prototype;
4686
- var hasOwnProperty17 = objectProto20.hasOwnProperty;
4657
+ var objectProto19 = Object.prototype;
4658
+ var hasOwnProperty16 = objectProto19.hasOwnProperty;
4687
4659
  function baseUnset(object, path) {
4688
4660
  path = castPath_default(path, object);
4689
4661
  var index = -1, length = path.length;
@@ -4692,7 +4664,7 @@ function baseUnset(object, path) {
4692
4664
  }
4693
4665
  while (++index < length) {
4694
4666
  var key = toKey_default(path[index]);
4695
- if (key === "__proto__" && !hasOwnProperty17.call(object, "__proto__")) {
4667
+ if (key === "__proto__" && !hasOwnProperty16.call(object, "__proto__")) {
4696
4668
  return false;
4697
4669
  }
4698
4670
  if ((key === "constructor" || key === "prototype") && index < length - 1) {
@@ -4855,7 +4827,7 @@ var startCase = createCompounder_default(function(result, word, index) {
4855
4827
  var startCase_default = startCase;
4856
4828
 
4857
4829
  // lib/index.ts
4858
- var import_logging12 = __toESM(require_logging());
4830
+ var import_logging14 = __toESM(require_logging());
4859
4831
  var import_listr4 = __toESM(require_listr());
4860
4832
  import PQueue from "p-queue";
4861
4833
 
@@ -4889,17 +4861,17 @@ import Promise3 from "bluebird";
4889
4861
 
4890
4862
  // lib/utils/publish-exo-entities.ts
4891
4863
  var import_logging2 = __toESM(require_logging());
4892
- function filterExoEntitiesToPublish(entities, sourceEntities) {
4864
+ function filterExoEntitiesToPublish(entities2, sourceEntities) {
4893
4865
  const entityIdsToPublish = new Set(
4894
4866
  sourceEntities.filter((entity) => entity.sys.publishedVersion).map((entity) => entity.sys.id)
4895
4867
  );
4896
- return entities.filter((entity) => entityIdsToPublish.has(entity.sys.id));
4868
+ return entities2.filter((entity) => entityIdsToPublish.has(entity.sys.id));
4897
4869
  }
4898
- function filterExoEntitiesToUnpublish(entities, sourceEntities) {
4870
+ function filterExoEntitiesToUnpublish(entities2, sourceEntities) {
4899
4871
  const entityIdsPublishedInSource = new Set(
4900
4872
  sourceEntities.filter((entity) => entity.sys.publishedVersion).map((entity) => entity.sys.id)
4901
4873
  );
4902
- return entities.filter((entity) => entity.sys.publishedVersion && !entityIdsPublishedInSource.has(entity.sys.id));
4874
+ return entities2.filter((entity) => entity.sys.publishedVersion && !entityIdsPublishedInSource.has(entity.sys.id));
4903
4875
  }
4904
4876
  async function publishExoEntity(type, entity, publish) {
4905
4877
  try {
@@ -4922,6 +4894,34 @@ async function unpublishExoEntity(type, entity, unpublish) {
4922
4894
  return null;
4923
4895
  }
4924
4896
  }
4897
+ function filterVariantsToPublish(variants) {
4898
+ return variants.filter((v) => v.sys.publishedVersion);
4899
+ }
4900
+ function filterVariantsToArchive(variants) {
4901
+ return variants.filter((v) => v.sys.archivedVersion && !v.sys.publishedVersion);
4902
+ }
4903
+ async function publishVariant(type, entity, publish) {
4904
+ try {
4905
+ const result = await publish();
4906
+ import_logging2.logEmitter.emit("info", `PUBLISH ${type} ${entity.sys.variant}`);
4907
+ return result;
4908
+ } catch (err) {
4909
+ err.entity = entity;
4910
+ import_logging2.logEmitter.emit("error", err);
4911
+ return null;
4912
+ }
4913
+ }
4914
+ async function archiveVariant(type, entity, archive) {
4915
+ try {
4916
+ const result = await archive();
4917
+ import_logging2.logEmitter.emit("info", `ARCHIVE ${type} ${entity.sys.variant}`);
4918
+ return result;
4919
+ } catch (err) {
4920
+ err.entity = entity;
4921
+ import_logging2.logEmitter.emit("error", err);
4922
+ return null;
4923
+ }
4924
+ }
4925
4925
  function isExoEntitlementError(err) {
4926
4926
  if (!(err instanceof Error)) {
4927
4927
  return false;
@@ -4933,6 +4933,17 @@ function isExoEntitlementError(err) {
4933
4933
  return false;
4934
4934
  }
4935
4935
  }
4936
+ function isTimelineEntitlementError(err) {
4937
+ if (!(err instanceof Error)) {
4938
+ return false;
4939
+ }
4940
+ try {
4941
+ const parsed = JSON.parse(err.message);
4942
+ return parsed?.status === 403 && parsed?.details?.reasons === "Timeline is not enabled for this organization";
4943
+ } catch {
4944
+ return false;
4945
+ }
4946
+ }
4936
4947
  var EXO_M1_FEATURE = "exoM1";
4937
4948
  async function spaceHasExoM1Entitlement(client, spaceId) {
4938
4949
  try {
@@ -4959,7 +4970,8 @@ var CURSOR_QUERY_METHODS = {
4959
4970
  experienceTemplates: { name: "experience templates", namespace: "experienceTemplate" },
4960
4971
  experienceFragments: { name: "experience fragments", namespace: "experienceFragment" },
4961
4972
  dataAssemblies: { name: "data assemblies", namespace: "dataAssembly" },
4962
- experiences: { name: "experiences", namespace: "experience" }
4973
+ experiences: { name: "experiences", namespace: "experience" },
4974
+ releases: { name: "releases", namespace: "release" }
4963
4975
  };
4964
4976
  var ENTITY_METHODS = {
4965
4977
  contentTypes: { name: "content types", ns: "contentType" },
@@ -5062,11 +5074,26 @@ async function cursorPaginatedQuery({ client, spaceId, environmentId, type, requ
5062
5074
  const allItems = [];
5063
5075
  do {
5064
5076
  const items = await requestQueue.add(async () => {
5065
- const response = await client[namespace].getMany({
5066
- spaceId,
5067
- environmentId,
5068
- query: { limit: BATCH_SIZE_LIMIT, ...pageNext && { pageNext } }
5069
- });
5077
+ let response;
5078
+ if (type === "releases") {
5079
+ response = await client.release.query({
5080
+ environmentId,
5081
+ spaceId,
5082
+ query: {
5083
+ "metadata.annotations.Contentful:Timeline.type[nin]": "Staging,Hidden",
5084
+ "sys.schemaVersion": "Release.v2",
5085
+ "sys.status[in]": "active",
5086
+ limit: BATCH_SIZE_LIMIT,
5087
+ ...pageNext && { pageNext }
5088
+ }
5089
+ });
5090
+ } else {
5091
+ response = await client[namespace].getMany({
5092
+ spaceId,
5093
+ environmentId,
5094
+ query: { limit: BATCH_SIZE_LIMIT, ...pageNext && { pageNext } }
5095
+ });
5096
+ }
5070
5097
  totalFetched += response.items.length;
5071
5098
  import_logging3.logEmitter.emit("info", `Fetched ${totalFetched} ${entityTypeName}`);
5072
5099
  pageNext = response.pages?.next;
@@ -5083,6 +5110,8 @@ async function cursorPaginatedQueryOrWarn(params) {
5083
5110
  const { name: entityTypeName } = CURSOR_QUERY_METHODS[params.type];
5084
5111
  if (isExoEntitlementError(err)) {
5085
5112
  import_logging3.logEmitter.emit("error", new Error(`Skipping ${entityTypeName} import: Experience Orchestration (ExO) is not enabled for this space`));
5113
+ } else if (isTimelineEntitlementError(err)) {
5114
+ import_logging3.logEmitter.emit("error", new Error(`Skipping ${entityTypeName} import: Timeline (Releases) is not enabled for this organization`));
5086
5115
  } else {
5087
5116
  import_logging3.logEmitter.emit("error", err instanceof Error ? err : new Error(String(err)));
5088
5117
  }
@@ -5112,7 +5141,8 @@ async function getDestinationData({
5112
5141
  experienceFragments: [],
5113
5142
  dataAssemblies: [],
5114
5143
  designTokens: [],
5115
- webhooks: []
5144
+ webhooks: [],
5145
+ releases: []
5116
5146
  };
5117
5147
  sourceData = {
5118
5148
  ...result,
@@ -5173,6 +5203,7 @@ async function getDestinationData({
5173
5203
  requestQueue
5174
5204
  });
5175
5205
  }
5206
+ result.releases = cursorPaginatedQueryOrWarn({ client, spaceId, environmentId, type: "releases", requestQueue });
5176
5207
  if (includeExperienceOrchestration && client) {
5177
5208
  const sourceHasDesignTokens = Boolean(sourceData.designTokens?.length);
5178
5209
  const sourceHasComponents = Boolean(sourceData.components?.length);
@@ -5210,7 +5241,7 @@ async function getDestinationData({
5210
5241
  }
5211
5242
 
5212
5243
  // lib/tasks/push-to-space/push-to-space.ts
5213
- var import_logging11 = __toESM(require_logging());
5244
+ var import_logging12 = __toESM(require_logging());
5214
5245
  var import_listr2 = __toESM(require_listr());
5215
5246
  import Listr from "listr";
5216
5247
  import verboseRenderer from "listr-verbose-renderer";
@@ -5312,14 +5343,14 @@ async function processAssets({
5312
5343
  // lib/tasks/push-to-space/creation.ts
5313
5344
  var import_get_entity_name2 = __toESM(require_get_entity_name());
5314
5345
  var import_logging5 = __toESM(require_logging());
5315
- function createEntities({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5316
- return createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, skipUpdates, requestQueue });
5346
+ function createEntities({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5347
+ return createEntitiesWithConcurrency({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue });
5317
5348
  }
5318
- function createLocales({ context, entities, destinationEntitiesById, requestQueue }) {
5319
- return createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue });
5349
+ function createLocales({ context, entities: entities2, destinationEntitiesById, requestQueue }) {
5350
+ return createEntitiesInSequence({ context, entities: entities2, destinationEntitiesById, requestQueue });
5320
5351
  }
5321
- async function createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5322
- const pendingCreatedEntities = entities.map((entity) => {
5352
+ async function createEntitiesWithConcurrency({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5353
+ const pendingCreatedEntities = entities2.map((entity) => {
5323
5354
  const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
5324
5355
  const updateOperation = skipUpdates ? "skip" : "update";
5325
5356
  const operation = destinationEntity ? updateOperation : "create";
@@ -5340,9 +5371,9 @@ async function createEntitiesWithConcurrency({ context, entities, destinationEnt
5340
5371
  const createdEntities = await Promise.all(pendingCreatedEntities);
5341
5372
  return createdEntities.filter((entity) => entity);
5342
5373
  }
5343
- async function createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue }) {
5374
+ async function createEntitiesInSequence({ context, entities: entities2, destinationEntitiesById, requestQueue }) {
5344
5375
  const createdEntities = [];
5345
- for (const entity of entities) {
5376
+ for (const entity of entities2) {
5346
5377
  const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
5347
5378
  const operation = destinationEntity ? "update" : "create";
5348
5379
  try {
@@ -5361,8 +5392,8 @@ async function createEntitiesInSequence({ context, entities, destinationEntities
5361
5392
  }
5362
5393
  return createdEntities;
5363
5394
  }
5364
- async function createEntries({ context, entities, destinationEntitiesById, skipUpdates, requestQueue }) {
5365
- const createdEntries = await Promise.all(entities.map((entry) => {
5395
+ async function createEntries({ context, entities: entities2, destinationEntitiesById, skipUpdates, requestQueue }) {
5396
+ const createdEntries = await Promise.all(entities2.map((entry) => {
5366
5397
  return createEntry({ entry, context, destinationEntitiesById, skipUpdates, requestQueue });
5367
5398
  }));
5368
5399
  return createdEntries.filter((entry) => entry);
@@ -5617,27 +5648,27 @@ function archiveEntity(client, spaceId, environmentId, entity) {
5617
5648
  }
5618
5649
  throw new Error(`archiveEntity: unsupported type "${type}"`);
5619
5650
  }
5620
- async function publishEntities({ entities, client, spaceId, environmentId, requestQueue, localePublishing }) {
5621
- if (entities.length === 0) {
5651
+ async function publishEntities({ entities: entities2, client, spaceId, environmentId, requestQueue, localePublishing }) {
5652
+ if (entities2.length === 0) {
5622
5653
  import_logging7.logEmitter.emit("info", "Skipping publishing since zero valid entities passed");
5623
5654
  return [];
5624
5655
  }
5625
- const entity = entities[0];
5656
+ const entity = entities2[0];
5626
5657
  const type = entity.sys.type || "unknown type";
5627
- import_logging7.logEmitter.emit("info", `Publishing ${entities.length} ${type}s`);
5628
- const result = await runQueue(entities, [], client, spaceId, environmentId, requestQueue, localePublishing);
5658
+ import_logging7.logEmitter.emit("info", `Publishing ${entities2.length} ${type}s`);
5659
+ const result = await runQueue(entities2, [], client, spaceId, environmentId, requestQueue, localePublishing);
5629
5660
  import_logging7.logEmitter.emit("info", `Successfully published ${result.length} ${type}s`);
5630
5661
  return result;
5631
5662
  }
5632
- async function archiveEntities({ entities, client, spaceId, environmentId, requestQueue }) {
5633
- if (entities.length === 0) {
5663
+ async function archiveEntities({ entities: entities2, client, spaceId, environmentId, requestQueue }) {
5664
+ if (entities2.length === 0) {
5634
5665
  import_logging7.logEmitter.emit("info", "Skipping archiving since zero valid entities passed");
5635
5666
  return [];
5636
5667
  }
5637
- const entity = entities[0];
5668
+ const entity = entities2[0];
5638
5669
  const type = entity.sys.type || "unknown type";
5639
- import_logging7.logEmitter.emit("info", `Archiving ${entities.length} ${type}s`);
5640
- const pendingArchivedEntities = entities.map((entity2) => {
5670
+ import_logging7.logEmitter.emit("info", `Archiving ${entities2.length} ${type}s`);
5671
+ const pendingArchivedEntities = entities2.map((entity2) => {
5641
5672
  return requestQueue.add(async () => {
5642
5673
  try {
5643
5674
  const archivedEntity = await archiveEntity(client, spaceId, environmentId, entity2);
@@ -5725,6 +5756,55 @@ async function runQueue(queue, result = [], client, spaceId, environmentId, requ
5725
5756
  return result;
5726
5757
  }
5727
5758
 
5759
+ // lib/tasks/push-to-space/releases.ts
5760
+ var import_logging8 = __toESM(require_logging());
5761
+ function partitionReleasesBySchemaVersion(releases2) {
5762
+ return {
5763
+ supported: releases2.filter((release) => release.transformed.sys.schemaVersion === "Release.v2"),
5764
+ unsupported: releases2.filter((release) => release.transformed.sys.schemaVersion !== "Release.v2")
5765
+ };
5766
+ }
5767
+ function logUnsupportedRelease(release) {
5768
+ import_logging8.logEmitter.emit("error", new Error(
5769
+ `Skipping Release "${release.transformed.sys.id}": only Release.v2 ("Releases") is supported, got schemaVersion "${release.transformed.sys.schemaVersion}"`
5770
+ ));
5771
+ }
5772
+ function buildReleaseCreatePayload(release) {
5773
+ const { id: _sourceId, ...sourceSysWithoutId } = release.transformed.sys;
5774
+ return {
5775
+ ...release.transformed,
5776
+ entities: release.transformed.entities,
5777
+ sys: { ...sourceSysWithoutId, type: "Release", schemaVersion: "Release.v2" }
5778
+ };
5779
+ }
5780
+ function buildReleaseUpdatePayload(release) {
5781
+ return {
5782
+ ...release.transformed,
5783
+ entities: release.transformed.entities,
5784
+ sys: { type: "Release", schemaVersion: "Release.v2" }
5785
+ };
5786
+ }
5787
+ async function importRelease(release, existing, context) {
5788
+ const { client, spaceId, environmentId } = context;
5789
+ try {
5790
+ if (existing) {
5791
+ const result2 = await client.release.update(
5792
+ { spaceId, environmentId, releaseId: existing.sys.id, version: existing.sys.version },
5793
+ buildReleaseUpdatePayload(release)
5794
+ );
5795
+ import_logging8.logEmitter.emit("info", `UPDATE Release ${existing.sys.id}`);
5796
+ return result2;
5797
+ }
5798
+ const result = await client.release.create({ spaceId, environmentId }, buildReleaseCreatePayload(release));
5799
+ import_logging8.logEmitter.emit("info", `CREATE Release ${result.sys.id}`);
5800
+ return result;
5801
+ } catch (err) {
5802
+ err.entity = release.transformed;
5803
+ import_logging8.logEmitter.emit("error", err);
5804
+ return null;
5805
+ }
5806
+ }
5807
+
5728
5808
  // lib/utils/graphql-schema-backoff.ts
5729
5809
  var GRAPHQL_SCHEMA_STALE_DELAYS_MS = [500, 1500, 6e3];
5730
5810
  function isGraphQLSchemaStaleError(err) {
@@ -5851,18 +5931,18 @@ function sortExperienceFragments(experienceFragments) {
5851
5931
  }
5852
5932
 
5853
5933
  // lib/utils/sort-or-report.ts
5854
- var import_logging8 = __toESM(require_logging());
5934
+ var import_logging9 = __toESM(require_logging());
5855
5935
  function sortOrReport(sortFn) {
5856
5936
  try {
5857
5937
  return sortFn();
5858
5938
  } catch (err) {
5859
- import_logging8.logEmitter.emit("error", err);
5939
+ import_logging9.logEmitter.emit("error", err);
5860
5940
  throw err;
5861
5941
  }
5862
5942
  }
5863
5943
 
5864
5944
  // lib/utils/import-exo-folders.ts
5865
- var import_logging9 = __toESM(require_logging());
5945
+ var import_logging10 = __toESM(require_logging());
5866
5946
  var FOLDER_CONCEPT_PREFIX = "contentful.folder-";
5867
5947
  var PARENT_FOLDER_GROUP_IDS = {
5868
5948
  designToken: "contentful.folder-group-designToken",
@@ -5879,8 +5959,8 @@ var ENTITY_TYPE_TO_PARENT_GROUP_ID = {
5879
5959
  experiences: PARENT_FOLDER_GROUP_IDS.experience
5880
5960
  };
5881
5961
  function getSourceSpaceId(sourceEntities) {
5882
- for (const entities of Object.values(sourceEntities)) {
5883
- for (const entity of entities ?? []) {
5962
+ for (const entities2 of Object.values(sourceEntities)) {
5963
+ for (const entity of entities2 ?? []) {
5884
5964
  const spaceId = entity.sys?.space?.sys?.id;
5885
5965
  if (spaceId) return spaceId;
5886
5966
  }
@@ -5932,7 +6012,7 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5932
6012
  existing = await client.concept.get({ organizationId, conceptId: destConceptId });
5933
6013
  } catch (err) {
5934
6014
  if (err?.name !== "NotFound") {
5935
- import_logging9.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
6015
+ import_logging10.logEmitter.emit("warning", `Could not fetch destination child concept ${destConceptId}: ${err?.message ?? err}`);
5936
6016
  }
5937
6017
  }
5938
6018
  if (!existing) {
@@ -5942,9 +6022,9 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5942
6022
  // @ts-expect-error - CMA.js type needs to be updated to be aware of purpose: 'internal'
5943
6023
  { purpose: "internal", prefLabel, metadata: { spaces: [spaceLink] } }
5944
6024
  );
5945
- import_logging9.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
6025
+ import_logging10.logEmitter.emit("info", `Created child folder concept ${destConceptId}`);
5946
6026
  } catch (err) {
5947
- import_logging9.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
6027
+ import_logging10.logEmitter.emit("error", `Failed to create child folder concept ${destConceptId}: ${err?.message ?? err}`);
5948
6028
  }
5949
6029
  } else {
5950
6030
  const patches = [];
@@ -5958,9 +6038,9 @@ async function createOrPatchChildConcepts(client, organizationId, destinationSpa
5958
6038
  { organizationId, conceptId: destConceptId, version: existing.sys.version },
5959
6039
  patches
5960
6040
  );
5961
- import_logging9.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
6041
+ import_logging10.logEmitter.emit("info", `Patched child folder concept ${destConceptId} (${patches.map((p) => p.path).join(", ")})`);
5962
6042
  } catch (err) {
5963
- import_logging9.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
6043
+ import_logging10.logEmitter.emit("error", `Failed to patch child folder concept ${destConceptId}: ${err?.message ?? err}`);
5964
6044
  }
5965
6045
  }
5966
6046
  }
@@ -5978,14 +6058,14 @@ async function linkChildConceptsToParentGroups(client, organizationId, childConc
5978
6058
  [{ op: "add", path: "/concepts/-", value: { sys: { type: "Link", linkType: "TaxonomyConcept", id: destConceptId } } }]
5979
6059
  );
5980
6060
  parentGroups.set(parentGroupId, updated);
5981
- import_logging9.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
6061
+ import_logging10.logEmitter.emit("info", `Linked child concept ${destConceptId} to parent group ${parentGroupId}`);
5982
6062
  } catch (err) {
5983
- import_logging9.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
6063
+ import_logging10.logEmitter.emit("error", `Failed to link child concept ${destConceptId} to parent group ${parentGroupId}: ${err?.message ?? err}`);
5984
6064
  }
5985
6065
  }
5986
6066
  }
5987
- function rewriteEntityFolderConcepts(entities, childConceptMap) {
5988
- for (const entity of entities) {
6067
+ function rewriteEntityFolderConcepts(entities2, childConceptMap) {
6068
+ for (const entity of entities2) {
5989
6069
  if (!entity.metadata?.concepts) continue;
5990
6070
  entity.metadata.concepts = entity.metadata.concepts.map((link) => {
5991
6071
  const entry = childConceptMap.get(link.sys.id);
@@ -6002,17 +6082,17 @@ async function importExoFolders({
6002
6082
  }) {
6003
6083
  const sourceSpaceId = getSourceSpaceId(sourceEntities);
6004
6084
  if (sourceSpaceId === destinationSpaceId) {
6005
- import_logging9.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
6085
+ import_logging10.logEmitter.emit("info", "Source and destination space are the same \u2014 skipping ExO folder import");
6006
6086
  return;
6007
6087
  }
6008
6088
  const parentGroups = await ensureParentFolderGroupsExist(client, organizationId);
6009
6089
  if (parentGroups.size === 0) {
6010
- 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.");
6090
+ import_logging10.logEmitter.emit("warn", "One or more Experience Orchestration folder group concept schemes are missing in the destination organization. Please create them before importing.");
6011
6091
  return;
6012
6092
  }
6013
6093
  const childConceptMap = deriveChildConceptMap(sourceEntities, destinationSpaceId);
6014
6094
  if (childConceptMap.size === 0) return;
6015
- import_logging9.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
6095
+ import_logging10.logEmitter.emit("info", `Importing ${childConceptMap.size} ExO folder concept(s) into destination space ${destinationSpaceId}`);
6016
6096
  await createOrPatchChildConcepts(client, organizationId, destinationSpaceId, childConceptMap);
6017
6097
  await linkChildConceptsToParentGroups(client, organizationId, childConceptMap, parentGroups);
6018
6098
  const allEntities = [
@@ -6078,7 +6158,7 @@ function buildLocalePublishPlan(sourceEntities, destinationLocaleCodes, { destin
6078
6158
  }
6079
6159
 
6080
6160
  // lib/utils/destination-locales.ts
6081
- var import_logging10 = __toESM(require_logging());
6161
+ var import_logging11 = __toESM(require_logging());
6082
6162
  var cache = /* @__PURE__ */ new WeakMap();
6083
6163
  function getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueue }) {
6084
6164
  let byEnvironment = cache.get(client);
@@ -6090,7 +6170,7 @@ function getDestinationLocaleCodes({ client, spaceId, environmentId, requestQueu
6090
6170
  let localeCodes = byEnvironment.get(key);
6091
6171
  if (!localeCodes) {
6092
6172
  localeCodes = batchedPageQuery({ client, spaceId, environmentId, type: "locales", requestQueue }).then((items) => items.map((locale) => locale.code)).catch((err) => {
6093
- import_logging10.logEmitter.emit("warning", `Could not read the locales of the destination environment, falling back to publishing all locales: ${err.message}`);
6173
+ import_logging11.logEmitter.emit("warning", `Could not read the locales of the destination environment, falling back to publishing all locales: ${err.message}`);
6094
6174
  return null;
6095
6175
  });
6096
6176
  byEnvironment.set(key, localeCodes);
@@ -6109,7 +6189,7 @@ async function withGraphQLSchemaBackoff(fn) {
6109
6189
  throw err;
6110
6190
  }
6111
6191
  const delay = GRAPHQL_SCHEMA_STALE_DELAYS_MS[attempt];
6112
- import_logging11.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
6192
+ import_logging12.logEmitter.emit("warning", `DataAssembly GraphQL schema not yet current, retrying in ${delay}ms (attempt ${attempt + 1}/${GRAPHQL_SCHEMA_STALE_DELAYS_MS.length})`);
6113
6193
  await new Promise((resolve2) => setTimeout(resolve2, delay));
6114
6194
  lastErr = err;
6115
6195
  }
@@ -6132,6 +6212,7 @@ function pushToSpace({
6132
6212
  spaceId,
6133
6213
  environmentId,
6134
6214
  includeExperienceOrchestration,
6215
+ skipExoVariants,
6135
6216
  contentModelOnly,
6136
6217
  skipContentModel,
6137
6218
  skipContentUpdates,
@@ -6154,13 +6235,20 @@ function pushToSpace({
6154
6235
  ...DEFAULT_CONTENT_STRUCTURE,
6155
6236
  ...destinationData
6156
6237
  };
6238
+ const hasOptimizationVariants = [
6239
+ ...sourceData.experiences || [],
6240
+ ...sourceData.experienceFragments || []
6241
+ ].some((parent2) => (parent2.optimizationVariants || []).some(isRealOptimizationVariant));
6242
+ if (includeExperienceOrchestration && !skipExoVariants && hasOptimizationVariants) {
6243
+ import_logging12.logEmitter.emit("warning", "Optimization Variants are imported with new destination IDs and can be duplicated by re-imports. Set skipExoVariants to true to skip them.");
6244
+ }
6157
6245
  listrOptions = listrOptions || {
6158
6246
  renderer: verboseRenderer
6159
6247
  };
6160
6248
  const destinationDataById = {};
6161
- for (const [entityType, entities] of Object.entries(destinationData)) {
6249
+ for (const [entityType, entities2] of Object.entries(destinationData)) {
6162
6250
  const entitiesById = /* @__PURE__ */ new Map();
6163
- for (const entity of entities) {
6251
+ for (const entity of entities2) {
6164
6252
  entitiesById.set(entity.sys.id, entity);
6165
6253
  }
6166
6254
  destinationDataById[entityType] = entitiesById;
@@ -6249,7 +6337,7 @@ function pushToSpace({
6249
6337
  const ctEditorInterface = await requestQueue.add(
6250
6338
  () => client.editorInterface.get({ spaceId, environmentId, contentTypeId: contentType.sys.id })
6251
6339
  );
6252
- import_logging11.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6340
+ import_logging12.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
6253
6341
  const updatedData = {
6254
6342
  ...ctEditorInterface,
6255
6343
  controls: editorInterface.controls,
@@ -6267,7 +6355,7 @@ function pushToSpace({
6267
6355
  return updatedEditorInterface;
6268
6356
  } catch (err) {
6269
6357
  err.entity = editorInterface;
6270
- import_logging11.logEmitter.emit("error", err);
6358
+ import_logging12.logEmitter.emit("error", err);
6271
6359
  return null;
6272
6360
  }
6273
6361
  });
@@ -6285,7 +6373,7 @@ function pushToSpace({
6285
6373
  for (const file of Object.values(asset.transformed.fields.file)) {
6286
6374
  allPendingUploads.push(requestQueue.add(async () => {
6287
6375
  try {
6288
- import_logging11.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6376
+ import_logging12.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
6289
6377
  const assetStream = await getAssetStreamForURL(file.upload, assetsDirectory);
6290
6378
  const upload = await client.upload.create(
6291
6379
  { spaceId, environmentId },
@@ -6301,7 +6389,7 @@ function pushToSpace({
6301
6389
  };
6302
6390
  return upload;
6303
6391
  } catch (err) {
6304
- import_logging11.logEmitter.emit("error", err);
6392
+ import_logging12.logEmitter.emit("error", err);
6305
6393
  }
6306
6394
  }));
6307
6395
  }
@@ -6447,7 +6535,7 @@ function pushToSpace({
6447
6535
  }
6448
6536
  });
6449
6537
  } catch (error) {
6450
- import_logging11.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6538
+ import_logging12.logEmitter.emit("warning", `Unable to create Experience Orchestration (ExO) folders, error: ${error}`);
6451
6539
  }
6452
6540
  }),
6453
6541
  skip: () => !includeExperienceOrchestration
@@ -6465,19 +6553,19 @@ function pushToSpace({
6465
6553
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6466
6554
  payload
6467
6555
  ));
6468
- import_logging11.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6556
+ import_logging12.logEmitter.emit("info", `UPDATE DataAssembly ${entity.sys.id}`);
6469
6557
  } else {
6470
6558
  const payload = { ...omitSys(entity), sys: buildDataAssemblySys(entity, 0) };
6471
6559
  result = await withGraphQLSchemaBackoff(() => client.dataAssembly.update(
6472
6560
  { spaceId, environmentId, dataAssemblyId: entity.sys.id },
6473
6561
  payload
6474
6562
  ));
6475
- import_logging11.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6563
+ import_logging12.logEmitter.emit("info", `CREATE DataAssembly ${entity.sys.id}`);
6476
6564
  }
6477
6565
  return result;
6478
6566
  } catch (err) {
6479
6567
  err.entity = entity;
6480
- import_logging11.logEmitter.emit("error", err);
6568
+ import_logging12.logEmitter.emit("error", err);
6481
6569
  return null;
6482
6570
  }
6483
6571
  }));
@@ -6507,17 +6595,17 @@ function pushToSpace({
6507
6595
  if (existing) {
6508
6596
  const payload = { ...entity, sys: { id: entity.sys.id, type: "DesignToken", version: existing.sys.version } };
6509
6597
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6510
- import_logging11.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6598
+ import_logging12.logEmitter.emit("info", `UPDATE DesignToken ${entity.sys.id}`);
6511
6599
  return result;
6512
6600
  } else {
6513
6601
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "DesignToken" } };
6514
6602
  const result = await client.designToken.upsert({ spaceId, environmentId, designTokenId: entity.sys.id }, payload);
6515
- import_logging11.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6603
+ import_logging12.logEmitter.emit("info", `CREATE DesignToken ${entity.sys.id}`);
6516
6604
  return result;
6517
6605
  }
6518
6606
  } catch (err) {
6519
6607
  err.entity = entity;
6520
- import_logging11.logEmitter.emit("error", err);
6608
+ import_logging12.logEmitter.emit("error", err);
6521
6609
  return null;
6522
6610
  }
6523
6611
  }));
@@ -6536,17 +6624,17 @@ function pushToSpace({
6536
6624
  if (existing) {
6537
6625
  const payload = { ...entity, sys: { id: entity.sys.id, type: "Component", version: existing.sys.version } };
6538
6626
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6539
- import_logging11.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6627
+ import_logging12.logEmitter.emit("info", `UPDATE Component ${entity.sys.id}`);
6540
6628
  results.push(result);
6541
6629
  } else {
6542
6630
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "Component" } };
6543
6631
  const result = await client.component.upsert({ spaceId, environmentId, componentId: entity.sys.id }, payload);
6544
- import_logging11.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6632
+ import_logging12.logEmitter.emit("info", `CREATE Component ${entity.sys.id}`);
6545
6633
  results.push(result);
6546
6634
  }
6547
6635
  } catch (err) {
6548
6636
  err.entity = entity;
6549
- import_logging11.logEmitter.emit("error", err);
6637
+ import_logging12.logEmitter.emit("error", err);
6550
6638
  }
6551
6639
  }
6552
6640
  ctx.data.components = results;
@@ -6578,17 +6666,17 @@ function pushToSpace({
6578
6666
  if (existing) {
6579
6667
  const payload = { ...entity, sys: { id: entity.sys.id, type: "ExperienceTemplate", version: existing.sys.version } };
6580
6668
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6581
- import_logging11.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6669
+ import_logging12.logEmitter.emit("info", `UPDATE ExperienceTemplate ${entity.sys.id}`);
6582
6670
  return result;
6583
6671
  } else {
6584
6672
  const payload = { ...omitSys(entity), sys: { id: entity.sys.id, type: "ExperienceTemplate" } };
6585
6673
  const result = await client.experienceTemplate.upsert({ spaceId, environmentId, experienceTemplateId: entity.sys.id }, payload);
6586
- import_logging11.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6674
+ import_logging12.logEmitter.emit("info", `CREATE ExperienceTemplate ${entity.sys.id}`);
6587
6675
  return result;
6588
6676
  }
6589
6677
  } catch (err) {
6590
6678
  err.entity = entity;
6591
- import_logging11.logEmitter.emit("error", err);
6679
+ import_logging12.logEmitter.emit("error", err);
6592
6680
  return null;
6593
6681
  }
6594
6682
  }));
@@ -6618,19 +6706,19 @@ function pushToSpace({
6618
6706
  try {
6619
6707
  const existing = destinationDataById.experienceFragments?.get(entity.sys.id);
6620
6708
  if (existing) {
6621
- const payload = { ...entity, sys: { id: entity.sys.id, type: "ExperienceFragment", version: existing.sys.version } };
6709
+ const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "ExperienceFragment", version: existing.sys.version } };
6622
6710
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6623
- import_logging11.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6711
+ import_logging12.logEmitter.emit("info", `UPDATE ExperienceFragment ${entity.sys.id}`);
6624
6712
  results.push(result);
6625
6713
  } else {
6626
- const payload = { ...omitSys(entity), component: entity.sys.component, sys: { id: entity.sys.id, type: "ExperienceFragment" } };
6714
+ const payload = { ...omitSysAndOptimizationVariants(entity), component: entity.sys.component, sys: { id: entity.sys.id, type: "ExperienceFragment" } };
6627
6715
  const result = await client.experienceFragment.upsert({ spaceId, environmentId, experienceFragmentId: entity.sys.id }, payload);
6628
- import_logging11.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6716
+ import_logging12.logEmitter.emit("info", `CREATE ExperienceFragment ${entity.sys.id}`);
6629
6717
  results.push(result);
6630
6718
  }
6631
6719
  } catch (err) {
6632
6720
  err.entity = entity;
6633
- import_logging11.logEmitter.emit("error", err);
6721
+ import_logging12.logEmitter.emit("error", err);
6634
6722
  }
6635
6723
  }
6636
6724
  ctx.data.experienceFragments = results;
@@ -6660,19 +6748,19 @@ function pushToSpace({
6660
6748
  try {
6661
6749
  const existing = destinationDataById.experiences?.get(entity.sys.id);
6662
6750
  if (existing) {
6663
- const payload = { ...entity, sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
6751
+ const payload = { ...omitSysAndOptimizationVariants(entity), sys: { id: entity.sys.id, type: "Experience", version: existing.sys.version } };
6664
6752
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6665
- import_logging11.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6753
+ import_logging12.logEmitter.emit("info", `UPDATE Experience ${entity.sys.id}`);
6666
6754
  return result;
6667
6755
  } else {
6668
- const payload = { ...omitSys(entity), experienceTemplate: entity.sys.experienceTemplate, sys: { id: entity.sys.id, type: "Experience" } };
6756
+ const payload = { ...omitSysAndOptimizationVariants(entity), experienceTemplate: entity.sys.experienceTemplate, sys: { id: entity.sys.id, type: "Experience" } };
6669
6757
  const result = await client.experience.upsert({ spaceId, environmentId, experienceId: entity.sys.id }, payload);
6670
- import_logging11.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6758
+ import_logging12.logEmitter.emit("info", `CREATE Experience ${entity.sys.id}`);
6671
6759
  return result;
6672
6760
  }
6673
6761
  } catch (err) {
6674
6762
  err.entity = entity;
6675
- import_logging11.logEmitter.emit("error", err);
6763
+ import_logging12.logEmitter.emit("error", err);
6676
6764
  return null;
6677
6765
  }
6678
6766
  }));
@@ -6693,6 +6781,85 @@ function pushToSpace({
6693
6781
  }),
6694
6782
  skip: () => !includeExperienceOrchestration || skipContentPublishing || !(sourceData.experiences || []).length
6695
6783
  },
6784
+ // Optimization Variants are sub-resources nested on their parent Experience/ExperienceFragment
6785
+ // (sourceData.experiences[].optimizationVariants), not a flat top-level array. A variant's
6786
+ // sys.id is borrowed from its parent, not unique, so variant creation/publish/archive is
6787
+ // handled by new, self-contained functions (importVariantsForParents, filterVariantsToPublish,
6788
+ // filterVariantsToArchive, publishVariant, archiveVariant) rather than by generalizing
6789
+ // destinationDataById / filterExoEntitiesToPublish / filterExoEntitiesToUnpublish, which all
6790
+ // assume sys.id uniqueness.
6791
+ //
6792
+ // Unlike the base Experience/Fragment create/update, there is no upsert-by-known-ID for
6793
+ // variants: the upstream API's create endpoint always server-generates a fresh variantId
6794
+ // (crypto.randomUUID()) and its update endpoint 404s on an unknown variantId rather than
6795
+ // creating one. So every import run creates fresh destination variants via POST rather than
6796
+ // matching against any existing destination variant.
6797
+ {
6798
+ title: "Importing Experience Optimization Variants",
6799
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6800
+ const results = await importVariantsForParents({
6801
+ parents: ctx.data.experiences || [],
6802
+ sourceParents: sourceData.experiences || [],
6803
+ client,
6804
+ spaceId,
6805
+ environmentId,
6806
+ kind: "experience"
6807
+ });
6808
+ ctx.data.experienceVariants = results.flatMap((r) => r.created);
6809
+ }),
6810
+ skip: () => !includeExperienceOrchestration || skipExoVariants || !(sourceData.experiences || []).some((e) => (e.optimizationVariants || []).some(isRealOptimizationVariant))
6811
+ },
6812
+ {
6813
+ title: "Publishing Experience Optimization Variants",
6814
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6815
+ const toPublish = filterVariantsToPublish(ctx.data.experienceVariants || []);
6816
+ await Promise.all(toPublish.map(
6817
+ (v) => publishVariant("ExperienceVariant", v, () => client.experienceVariant.publish(
6818
+ { spaceId, environmentId, experienceId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6819
+ ))
6820
+ ));
6821
+ const toArchive = filterVariantsToArchive(ctx.data.experienceVariants || []);
6822
+ await Promise.all(toArchive.map(
6823
+ (v) => archiveVariant("ExperienceVariant", v, () => client.experienceVariant.archive(
6824
+ { spaceId, environmentId, experienceId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6825
+ ))
6826
+ ));
6827
+ }),
6828
+ skip: () => !includeExperienceOrchestration || skipExoVariants || skipContentPublishing || !(sourceData.experiences || []).some((e) => (e.optimizationVariants || []).some(isRealOptimizationVariant))
6829
+ },
6830
+ {
6831
+ title: "Importing Experience Fragment Optimization Variants",
6832
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6833
+ const results = await importVariantsForParents({
6834
+ parents: ctx.data.experienceFragments || [],
6835
+ sourceParents: sourceData.experienceFragments || [],
6836
+ client,
6837
+ spaceId,
6838
+ environmentId,
6839
+ kind: "experienceFragment"
6840
+ });
6841
+ ctx.data.experienceFragmentVariants = results.flatMap((r) => r.created);
6842
+ }),
6843
+ skip: () => !includeExperienceOrchestration || skipExoVariants || !(sourceData.experienceFragments || []).some((f) => (f.optimizationVariants || []).some(isRealOptimizationVariant))
6844
+ },
6845
+ {
6846
+ title: "Publishing Experience Fragment Optimization Variants",
6847
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6848
+ const toPublish = filterVariantsToPublish(ctx.data.experienceFragmentVariants || []);
6849
+ await Promise.all(toPublish.map(
6850
+ (v) => publishVariant("ExperienceFragmentVariant", v, () => client.experienceFragmentVariant.publish(
6851
+ { spaceId, environmentId, experienceFragmentId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6852
+ ))
6853
+ ));
6854
+ const toArchive = filterVariantsToArchive(ctx.data.experienceFragmentVariants || []);
6855
+ await Promise.all(toArchive.map(
6856
+ (v) => archiveVariant("ExperienceFragmentVariant", v, () => client.experienceFragmentVariant.archive(
6857
+ { spaceId, environmentId, experienceFragmentId: v.sys.id, variantId: v.sys.variant, version: v.sys.version }
6858
+ ))
6859
+ ));
6860
+ }),
6861
+ skip: () => !includeExperienceOrchestration || skipExoVariants || skipContentPublishing || !(sourceData.experienceFragments || []).some((f) => (f.optimizationVariants || []).some(isRealOptimizationVariant))
6862
+ },
6696
6863
  // Unpublishing runs after all Importing/Publishing tasks, in reverse dependency order
6697
6864
  // (Experience first, DataAssembly last) - the API rejects unpublishing an entity that a
6698
6865
  // still-published parent references, so parents must unpublish before the children they embed.
@@ -6757,6 +6924,18 @@ function pushToSpace({
6757
6924
  ));
6758
6925
  }),
6759
6926
  skip: () => !includeExperienceOrchestration || skipContentPublishing || !(sourceData.dataAssemblies || []).length
6927
+ },
6928
+ {
6929
+ title: "Importing Releases",
6930
+ task: (0, import_listr2.wrapTask)(async (ctx) => {
6931
+ const { supported: v2Releases, unsupported: v1Releases } = partitionReleasesBySchemaVersion(sourceData.releases || []);
6932
+ v1Releases.forEach(logUnsupportedRelease);
6933
+ const results = await Promise.all(v2Releases.map(
6934
+ (release) => importRelease(release, destinationDataById.releases?.get(release.transformed.sys.id), { client, spaceId, environmentId })
6935
+ ));
6936
+ ctx.data.releases = results.filter(Boolean);
6937
+ }),
6938
+ skip: () => !(sourceData.releases || []).length
6760
6939
  }
6761
6940
  ], listrOptions);
6762
6941
  }
@@ -6764,14 +6943,56 @@ function omitSys(entity) {
6764
6943
  const { sys: _sys, ...rest } = entity;
6765
6944
  return rest;
6766
6945
  }
6767
- function archiveEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue }) {
6946
+ function omitSysAndOptimizationVariants(entity) {
6947
+ const { sys: _sys, optimizationVariants: _variants, ...rest } = entity;
6948
+ return rest;
6949
+ }
6950
+ function omitVariantSys(variant) {
6951
+ const { sys: _sys, ...rest } = variant;
6952
+ return rest;
6953
+ }
6954
+ function isRealOptimizationVariant(variant) {
6955
+ return Boolean(variant?.sys) && variant.sys.variantType !== "default";
6956
+ }
6957
+ async function importVariantsForParents({
6958
+ parents,
6959
+ sourceParents,
6960
+ client,
6961
+ spaceId,
6962
+ environmentId,
6963
+ kind
6964
+ }) {
6965
+ const ns = kind === "experience" ? client.experienceVariant : client.experienceFragmentVariant;
6966
+ const idParam = kind === "experience" ? "experienceId" : "experienceFragmentId";
6967
+ const label = kind === "experience" ? "ExperienceVariant" : "ExperienceFragmentVariant";
6968
+ const parentRefField = kind === "experience" ? "experienceTemplate" : "component";
6969
+ const sourceParentsById = new Map(sourceParents.map((sourceParent) => [sourceParent.sys.id, sourceParent]));
6970
+ return Promise.all(parents.map(async (parent2) => {
6971
+ const sourceParent = sourceParentsById.get(parent2.sys.id);
6972
+ const sourceVariants = (sourceParent?.optimizationVariants ?? []).filter(isRealOptimizationVariant);
6973
+ const created = [];
6974
+ for (const variant of sourceVariants) {
6975
+ try {
6976
+ const payload = { ...omitVariantSys(variant), [parentRefField]: variant.sys[parentRefField] };
6977
+ const result = await ns.create({ spaceId, environmentId, [idParam]: parent2.sys.id }, payload);
6978
+ import_logging12.logEmitter.emit("info", `CREATE ${label} ${result.sys.variant} (parent ${parent2.sys.id})`);
6979
+ created.push({ ...result, sys: { ...result.sys, publishedVersion: variant.sys.publishedVersion, archivedVersion: variant.sys.archivedVersion } });
6980
+ } catch (err) {
6981
+ err.entity = variant;
6982
+ import_logging12.logEmitter.emit("error", err);
6983
+ }
6984
+ }
6985
+ return { parentId: parent2.sys.id, created };
6986
+ }));
6987
+ }
6988
+ function archiveEntities2({ entities: entities2, sourceEntities, client, spaceId, environmentId, requestQueue }) {
6768
6989
  const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
6769
- const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
6990
+ const entitiesToArchive = entities2.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
6770
6991
  return archiveEntities({ entities: entitiesToArchive, client, spaceId, environmentId, requestQueue });
6771
6992
  }
6772
- async function publishEntities2({ entities, sourceEntities, client, spaceId, environmentId, requestQueue, destinationEntitiesById }) {
6993
+ async function publishEntities2({ entities: entities2, sourceEntities, client, spaceId, environmentId, requestQueue, destinationEntitiesById }) {
6773
6994
  const entityIdsToPublish = sourceEntities.filter(({ original }) => original.sys.publishedVersion).map(({ original }) => original.sys.id);
6774
- let entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
6995
+ let entitiesToPublish = entities2.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
6775
6996
  const publishAllLocales = () => publishEntities({ entities: entitiesToPublish, client, spaceId, environmentId, requestQueue });
6776
6997
  const hasFieldStatus = sourceEntities.some(({ original }) => original.sys.fieldStatus);
6777
6998
  if (!hasFieldStatus) {
@@ -6792,7 +7013,7 @@ async function publishEntities2({ entities, sourceEntities, client, spaceId, env
6792
7013
  if (!localePublishing.skippedEntityIds.has(entity.sys.id)) {
6793
7014
  return true;
6794
7015
  }
6795
- 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`);
7016
+ import_logging12.logEmitter.emit("warning", `Not publishing ${entity.sys.type} ${entity.sys.id} because none of the locales it was published for exist in the destination environment`);
6796
7017
  return false;
6797
7018
  });
6798
7019
  }
@@ -6813,6 +7034,7 @@ __export(transformers_exports, {
6813
7034
  contentTypes: () => contentTypes,
6814
7035
  entries: () => entries,
6815
7036
  locales: () => locales,
7037
+ releases: () => releases,
6816
7038
  tags: () => tags,
6817
7039
  webhooks: () => webhooks
6818
7040
  });
@@ -6868,6 +7090,9 @@ function removeMetadataTags(entity, tagsEnabled = false) {
6868
7090
  }
6869
7091
  return entity;
6870
7092
  }
7093
+ function releases(release) {
7094
+ return release;
7095
+ }
6871
7096
 
6872
7097
  // lib/utils/sort-entries.ts
6873
7098
  function sortEntries(entries2) {
@@ -7117,24 +7342,24 @@ function upgradeExoResources(resources) {
7117
7342
  }
7118
7343
 
7119
7344
  // lib/transform/transform-space.ts
7120
- var spaceEntities = [
7345
+ var entities = [
7121
7346
  "contentTypes",
7122
7347
  "entries",
7123
7348
  "assets",
7124
7349
  "locales",
7125
7350
  "webhooks",
7126
- "tags"
7351
+ "tags",
7352
+ "releases"
7127
7353
  ];
7128
- function transform_space_default(sourceData, destinationData, customTransformers, entities = spaceEntities) {
7129
- const transformers = defaults_default(customTransformers, transformers_exports);
7354
+ function transform_space_default(sourceData, destinationData) {
7130
7355
  const baseSpaceData = upgradeExoResources(omit_default(sourceData, ...entities));
7131
7356
  sourceData.locales = sortLocales(sourceData.locales);
7132
7357
  const tagsEnabled = !!destinationData.tags;
7133
7358
  return entities.reduce((transformedSpaceData, type) => {
7134
- const sortedEntities = type === "tags" ? sourceData[type] ?? [] : sortEntries(sourceData[type] ?? []);
7359
+ const sortedEntities = type === "tags" || type === "releases" ? sourceData[type] ?? [] : sortEntries(sourceData[type] ?? []);
7135
7360
  const transformedEntities = sortedEntities.map((entity) => ({
7136
7361
  original: entity,
7137
- transformed: transformers[type](entity, destinationData[type], tagsEnabled)
7362
+ transformed: transformers_exports[type](entity, destinationData[type], tagsEnabled)
7138
7363
  }));
7139
7364
  transformedSpaceData[type] = transformedEntities;
7140
7365
  return transformedSpaceData;
@@ -7224,13 +7449,13 @@ var attachEntityName = (details, payload) => {
7224
7449
  });
7225
7450
  };
7226
7451
  var countInvalidEntities = (validationData) => {
7227
- const entityCount = validationData.reduce((entities, currentDetail) => {
7228
- if (!entities[currentDetail.path[0]]) {
7229
- entities[currentDetail.path[0]] = 1;
7452
+ const entityCount = validationData.reduce((entities2, currentDetail) => {
7453
+ if (!entities2[currentDetail.path[0]]) {
7454
+ entities2[currentDetail.path[0]] = 1;
7230
7455
  } else {
7231
- entities[currentDetail.path[0]]++;
7456
+ entities2[currentDetail.path[0]]++;
7232
7457
  }
7233
- return entities;
7458
+ return entities2;
7234
7459
  }, {});
7235
7460
  return Object.keys(entityCount).map((key) => `${key}:${entityCount[key]}`);
7236
7461
  };
@@ -7333,7 +7558,8 @@ var SUPPORTED_ENTITY_TYPES = [
7333
7558
  "experienceTemplates",
7334
7559
  "dataAssemblies",
7335
7560
  "experienceFragments",
7336
- "experiences"
7561
+ "experiences",
7562
+ "releases"
7337
7563
  ];
7338
7564
  async function parseOptions(params) {
7339
7565
  const defaultOptions = {
@@ -7349,6 +7575,7 @@ async function parseOptions(params) {
7349
7575
  uploadAssets: false,
7350
7576
  rateLimit: 7,
7351
7577
  includeExperienceOrchestration: true,
7578
+ skipExoVariants: false,
7352
7579
  host: "api.contentful.com"
7353
7580
  };
7354
7581
  const configFile = params.config ? __require(resolve(process.cwd(), params.config)) : {};
@@ -7423,6 +7650,29 @@ async function parseOptions(params) {
7423
7650
  return options;
7424
7651
  }
7425
7652
 
7653
+ // lib/utils/display-error-log.ts
7654
+ var import_logging13 = __toESM(require_logging());
7655
+ import { format as format2, parseISO } from "date-fns";
7656
+ function displayErrorLog(errorLog) {
7657
+ if (errorLog.length) {
7658
+ const count = errorLog.reduce((count2, curr) => {
7659
+ if (Object.prototype.hasOwnProperty.call(curr, "warning")) {
7660
+ count2.warnings++;
7661
+ } else if (Object.prototype.hasOwnProperty.call(curr, "error")) {
7662
+ count2.errors++;
7663
+ }
7664
+ return count2;
7665
+ }, { warnings: 0, errors: 0 });
7666
+ console.log(`
7667
+
7668
+ The following ${count.errors} errors and ${count.warnings} warnings occurred:
7669
+ `);
7670
+ errorLog.map((logMessage) => `${format2(parseISO(logMessage.ts), "HH:mm:ss")} - ${(0, import_logging13.formatLogMessageOneLine)(logMessage)}`).map((logMessage) => console.log(logMessage));
7671
+ return;
7672
+ }
7673
+ console.log("No errors or warnings occurred");
7674
+ }
7675
+
7426
7676
  // lib/index.ts
7427
7677
  var ONE_SECOND = 1e3;
7428
7678
  var tableOptions = {
@@ -7449,7 +7699,7 @@ async function runContentfulImport(params) {
7449
7699
  intervalCap: options.rateLimit,
7450
7700
  carryoverConcurrencyCount: true
7451
7701
  });
7452
- (0, import_logging12.setupLogging)(log);
7702
+ (0, import_logging14.setupLogging)(log);
7453
7703
  const infoTable = new Table(tableOptions);
7454
7704
  infoTable.push([{ colSpan: 2, content: "The following entities are going to be imported:" }]);
7455
7705
  Object.keys(options.content).forEach((type) => {
@@ -7464,6 +7714,20 @@ async function runContentfulImport(params) {
7464
7714
  }
7465
7715
  infoTable.push([startCase_default(type), options.content[type].length]);
7466
7716
  });
7717
+ const experienceVariantCount = (options.content.experiences || []).reduce(
7718
+ (sum, e) => sum + (e.optimizationVariants?.length ?? 0),
7719
+ 0
7720
+ );
7721
+ if (!options.skipExoVariants && experienceVariantCount > 0) {
7722
+ infoTable.push(["Experience Optimization Variants", experienceVariantCount]);
7723
+ }
7724
+ const experienceFragmentVariantCount = (options.content.experienceFragments || []).reduce(
7725
+ (sum, f) => sum + (f.optimizationVariants?.length ?? 0),
7726
+ 0
7727
+ );
7728
+ if (!options.skipExoVariants && experienceFragmentVariantCount > 0) {
7729
+ infoTable.push(["Experience Fragment Optimization Variants", experienceFragmentVariantCount]);
7730
+ }
7467
7731
  console.log(infoTable.toString());
7468
7732
  const tasks = new Listr2([
7469
7733
  {
@@ -7512,6 +7776,7 @@ async function runContentfulImport(params) {
7512
7776
  client: ctx.client,
7513
7777
  spaceId: options.spaceId,
7514
7778
  includeExperienceOrchestration: options.includeExperienceOrchestration,
7779
+ skipExoVariants: options.skipExoVariants,
7515
7780
  environmentId: options.environmentId,
7516
7781
  contentModelOnly: options.contentModelOnly,
7517
7782
  skipLocales: options.skipLocales,
@@ -7559,9 +7824,9 @@ async function runContentfulImport(params) {
7559
7824
  }).then((data) => {
7560
7825
  const errorLog = log.filter((logMessage) => logMessage.level !== "info" && logMessage.level !== "warning");
7561
7826
  const displayLog = log.filter((logMessage) => logMessage.level !== "info");
7562
- (0, import_logging12.displayErrorLog)(displayLog);
7827
+ displayErrorLog(displayLog);
7563
7828
  if (errorLog.length) {
7564
- return (0, import_logging12.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7829
+ return (0, import_logging14.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
7565
7830
  const multiError = new ContentfulMultiError("Errors occurred");
7566
7831
  multiError.name = "ContentfulMultiError";
7567
7832
  multiError.errors = errorLog;