@kontent-ai/migration-toolkit 2.9.1 → 3.0.0-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +19 -17
  2. package/dist/core/config.js +3 -3
  3. package/dist/core/config.js.map +1 -1
  4. package/dist/core/helpers/workflow-helper.d.ts +1 -1
  5. package/dist/core/helpers/workflow-helper.js +28 -26
  6. package/dist/core/helpers/workflow-helper.js.map +1 -1
  7. package/dist/core/logs/loggers.d.ts +1 -1
  8. package/dist/core/logs/loggers.js +15 -15
  9. package/dist/core/logs/loggers.js.map +1 -1
  10. package/dist/core/models/core.models.d.ts +29 -22
  11. package/dist/core/models/error.models.d.ts +26 -0
  12. package/dist/core/models/error.models.js +104 -0
  13. package/dist/core/models/error.models.js.map +1 -0
  14. package/dist/core/models/log.models.d.ts +2 -2
  15. package/dist/core/models/migration.models.d.ts +17 -19
  16. package/dist/core/models/migration.schema.d.ts +9 -9
  17. package/dist/core/models/migration.schema.js +28 -28
  18. package/dist/core/models/migration.schema.js.map +1 -1
  19. package/dist/core/utils/array.utils.d.ts +1 -1
  20. package/dist/core/utils/array.utils.js +4 -3
  21. package/dist/core/utils/array.utils.js.map +1 -1
  22. package/dist/core/utils/binary-data.utils.d.ts +1 -1
  23. package/dist/core/utils/binary-data.utils.js +6 -6
  24. package/dist/core/utils/binary-data.utils.js.map +1 -1
  25. package/dist/core/utils/confirm.utils.d.ts +3 -1
  26. package/dist/core/utils/confirm.utils.js +92 -24
  27. package/dist/core/utils/confirm.utils.js.map +1 -1
  28. package/dist/core/utils/error.utils.d.ts +1 -1
  29. package/dist/core/utils/error.utils.js +16 -14
  30. package/dist/core/utils/error.utils.js.map +1 -1
  31. package/dist/core/utils/external-id.utils.js +1 -1
  32. package/dist/core/utils/external-id.utils.js.map +1 -1
  33. package/dist/core/utils/global.utils.d.ts +3 -3
  34. package/dist/core/utils/global.utils.js +20 -18
  35. package/dist/core/utils/global.utils.js.map +1 -1
  36. package/dist/core/utils/http.utils.d.ts +2 -2
  37. package/dist/core/utils/http.utils.js +4 -4
  38. package/dist/core/utils/http.utils.js.map +1 -1
  39. package/dist/core/utils/management-client-utils.d.ts +4 -4
  40. package/dist/core/utils/management-client-utils.js +37 -34
  41. package/dist/core/utils/management-client-utils.js.map +1 -1
  42. package/dist/core/utils/processing-utils.d.ts +4 -4
  43. package/dist/core/utils/processing-utils.js +13 -13
  44. package/dist/core/utils/processing-utils.js.map +1 -1
  45. package/dist/core/utils/run.utils.d.ts +2 -2
  46. package/dist/core/utils/run.utils.js +7 -7
  47. package/dist/core/utils/run.utils.js.map +1 -1
  48. package/dist/core/utils/try.utils.d.ts +14 -0
  49. package/dist/core/utils/try.utils.js +18 -0
  50. package/dist/core/utils/try.utils.js.map +1 -0
  51. package/dist/export/context/export-context-fetcher.d.ts +1 -1
  52. package/dist/export/context/export-context-fetcher.js +92 -62
  53. package/dist/export/context/export-context-fetcher.js.map +1 -1
  54. package/dist/export/export-manager.d.ts +2 -2
  55. package/dist/export/export-manager.js +73 -59
  56. package/dist/export/export-manager.js.map +1 -1
  57. package/dist/export/export.models.d.ts +21 -2
  58. package/dist/export/utils/export.utils.d.ts +1 -1
  59. package/dist/export/utils/export.utils.js +3 -2
  60. package/dist/export/utils/export.utils.js.map +1 -1
  61. package/dist/file/file-manager.d.ts +2 -2
  62. package/dist/file/file-manager.js +7 -7
  63. package/dist/file/file-manager.js.map +1 -1
  64. package/dist/import/comparers/asset-comparer.d.ts +2 -2
  65. package/dist/import/comparers/asset-comparer.js +12 -11
  66. package/dist/import/comparers/asset-comparer.js.map +1 -1
  67. package/dist/import/context/import-context-fetcher.d.ts +1 -1
  68. package/dist/import/context/import-context-fetcher.js +77 -73
  69. package/dist/import/context/import-context-fetcher.js.map +1 -1
  70. package/dist/import/import-manager.d.ts +1 -1
  71. package/dist/import/import-manager.js +59 -56
  72. package/dist/import/import-manager.js.map +1 -1
  73. package/dist/import/import.models.d.ts +6 -2
  74. package/dist/import/importers/assets-importer.d.ts +3 -3
  75. package/dist/import/importers/assets-importer.js +46 -43
  76. package/dist/import/importers/assets-importer.js.map +1 -1
  77. package/dist/import/importers/content-items-importer.d.ts +3 -3
  78. package/dist/import/importers/content-items-importer.js +28 -25
  79. package/dist/import/importers/content-items-importer.js.map +1 -1
  80. package/dist/import/importers/language-variant-importer.d.ts +3 -3
  81. package/dist/import/importers/language-variant-importer.js +45 -45
  82. package/dist/import/importers/language-variant-importer.js.map +1 -1
  83. package/dist/import/importers/workflow-importer.d.ts +3 -2
  84. package/dist/import/importers/workflow-importer.js +58 -54
  85. package/dist/import/importers/workflow-importer.js.map +1 -1
  86. package/dist/import/utils/import.utils.d.ts +1 -1
  87. package/dist/import/utils/import.utils.js +3 -2
  88. package/dist/import/utils/import.utils.js.map +1 -1
  89. package/dist/index.d.ts +11 -7
  90. package/dist/index.js +10 -8
  91. package/dist/index.js.map +1 -1
  92. package/dist/metadata.js +3 -3
  93. package/dist/metadata.js.map +1 -1
  94. package/dist/node/cli/actions/export-action.d.ts +1 -1
  95. package/dist/node/cli/actions/export-action.js +29 -23
  96. package/dist/node/cli/actions/export-action.js.map +1 -1
  97. package/dist/node/cli/actions/import-action.d.ts +1 -1
  98. package/dist/node/cli/actions/import-action.js +15 -12
  99. package/dist/node/cli/actions/import-action.js.map +1 -1
  100. package/dist/node/cli/actions/migrate-action.d.ts +1 -1
  101. package/dist/node/cli/actions/migrate-action.js +26 -22
  102. package/dist/node/cli/actions/migrate-action.js.map +1 -1
  103. package/dist/node/cli/app.js +13 -12
  104. package/dist/node/cli/app.js.map +1 -1
  105. package/dist/node/cli/args/args-fetcher.d.ts +1 -1
  106. package/dist/node/cli/args/args-fetcher.js +14 -13
  107. package/dist/node/cli/args/args-fetcher.js.map +1 -1
  108. package/dist/node/cli/args/args-setter.d.ts +1 -1
  109. package/dist/node/cli/args/args-setter.js +8 -6
  110. package/dist/node/cli/args/args-setter.js.map +1 -1
  111. package/dist/node/cli/cli.models.d.ts +2 -2
  112. package/dist/node/cli/commands.js +68 -56
  113. package/dist/node/cli/commands.js.map +1 -1
  114. package/dist/toolkit/elements-builder.d.ts +1 -1
  115. package/dist/toolkit/elements-builder.js +12 -12
  116. package/dist/toolkit/elements-builder.js.map +1 -1
  117. package/dist/toolkit/export.d.ts +2 -2
  118. package/dist/toolkit/export.js +8 -8
  119. package/dist/toolkit/export.js.map +1 -1
  120. package/dist/toolkit/file.d.ts +2 -1
  121. package/dist/toolkit/file.js +18 -16
  122. package/dist/toolkit/file.js.map +1 -1
  123. package/dist/toolkit/import.d.ts +1 -1
  124. package/dist/toolkit/import.js +12 -12
  125. package/dist/toolkit/import.js.map +1 -1
  126. package/dist/toolkit/migrate.d.ts +13 -4
  127. package/dist/toolkit/migrate.js +15 -13
  128. package/dist/toolkit/migrate.js.map +1 -1
  129. package/dist/translation/extraction/items-extraction.processor.d.ts +5 -4
  130. package/dist/translation/extraction/items-extraction.processor.js +73 -35
  131. package/dist/translation/extraction/items-extraction.processor.js.map +1 -1
  132. package/dist/translation/helpers/export-placeholder.utils.d.ts +11 -0
  133. package/dist/translation/helpers/export-placeholder.utils.js +11 -0
  134. package/dist/translation/helpers/export-placeholder.utils.js.map +1 -0
  135. package/dist/translation/helpers/rich-text.processor.d.ts +6 -6
  136. package/dist/translation/helpers/rich-text.processor.js +38 -38
  137. package/dist/translation/helpers/rich-text.processor.js.map +1 -1
  138. package/dist/translation/transforms/export-transforms.d.ts +2 -2
  139. package/dist/translation/transforms/export-transforms.js +93 -67
  140. package/dist/translation/transforms/export-transforms.js.map +1 -1
  141. package/dist/translation/transforms/import-transforms.d.ts +2 -2
  142. package/dist/translation/transforms/import-transforms.js +38 -40
  143. package/dist/translation/transforms/import-transforms.js.map +1 -1
  144. package/dist/zip/zip-manager.d.ts +4 -3
  145. package/dist/zip/zip-manager.js +9 -9
  146. package/dist/zip/zip-manager.js.map +1 -1
  147. package/dist/zip/zip-packager.d.ts +2 -2
  148. package/dist/zip/zip-packager.js +19 -17
  149. package/dist/zip/zip-packager.js.map +1 -1
  150. package/dist/zip/zip-transformer.d.ts +3 -2
  151. package/dist/zip/zip-transformer.js +16 -15
  152. package/dist/zip/zip-transformer.js.map +1 -1
  153. package/dist/zip/zip.models.d.ts +2 -2
  154. package/lib/core/config.ts +6 -6
  155. package/lib/core/helpers/workflow-helper.ts +190 -185
  156. package/lib/core/logs/loggers.ts +52 -52
  157. package/lib/core/models/core.models.ts +130 -119
  158. package/lib/core/models/error.models.ts +148 -0
  159. package/lib/core/models/log.models.ts +20 -20
  160. package/lib/core/models/migration.models.ts +54 -57
  161. package/lib/core/models/migration.schema.ts +108 -108
  162. package/lib/core/utils/array.utils.ts +43 -42
  163. package/lib/core/utils/binary-data.utils.ts +16 -16
  164. package/lib/core/utils/confirm.utils.ts +184 -106
  165. package/lib/core/utils/error.utils.ts +69 -72
  166. package/lib/core/utils/external-id.utils.ts +4 -4
  167. package/lib/core/utils/global.utils.ts +81 -75
  168. package/lib/core/utils/http.utils.ts +23 -23
  169. package/lib/core/utils/management-client-utils.ts +189 -186
  170. package/lib/core/utils/processing-utils.ts +82 -82
  171. package/lib/core/utils/run.utils.ts +50 -50
  172. package/lib/core/utils/try.utils.ts +30 -0
  173. package/lib/export/context/export-context-fetcher.ts +472 -447
  174. package/lib/export/export-manager.ts +250 -232
  175. package/lib/export/export.models.ts +71 -56
  176. package/lib/export/utils/export.utils.ts +9 -7
  177. package/lib/file/file-manager.ts +28 -28
  178. package/lib/import/comparers/asset-comparer.ts +107 -106
  179. package/lib/import/context/import-context-fetcher.ts +462 -462
  180. package/lib/import/import-manager.ts +243 -240
  181. package/lib/import/import.models.ts +69 -67
  182. package/lib/import/importers/assets-importer.ts +240 -236
  183. package/lib/import/importers/content-items-importer.ts +126 -122
  184. package/lib/import/importers/language-variant-importer.ts +324 -328
  185. package/lib/import/importers/workflow-importer.ts +372 -367
  186. package/lib/import/utils/import.utils.ts +9 -7
  187. package/lib/index.ts +12 -8
  188. package/lib/metadata.ts +3 -4
  189. package/lib/node/cli/actions/export-action.ts +44 -38
  190. package/lib/node/cli/actions/import-action.ts +32 -29
  191. package/lib/node/cli/actions/migrate-action.ts +50 -46
  192. package/lib/node/cli/app.ts +21 -21
  193. package/lib/node/cli/args/args-fetcher.ts +55 -54
  194. package/lib/node/cli/args/args-setter.ts +34 -32
  195. package/lib/node/cli/cli.models.ts +17 -17
  196. package/lib/node/cli/commands.ts +165 -153
  197. package/lib/toolkit/elements-builder.ts +69 -69
  198. package/lib/toolkit/export.ts +21 -21
  199. package/lib/toolkit/file.ts +59 -53
  200. package/lib/toolkit/import.ts +32 -32
  201. package/lib/toolkit/migrate.ts +60 -49
  202. package/lib/translation/extraction/items-extraction.processor.ts +203 -173
  203. package/lib/translation/helpers/export-placeholder.utils.ts +10 -0
  204. package/lib/translation/helpers/rich-text.processor.ts +311 -311
  205. package/lib/translation/transforms/export-transforms.ts +313 -279
  206. package/lib/translation/transforms/import-transforms.ts +188 -191
  207. package/lib/zip/zip-manager.ts +28 -27
  208. package/lib/zip/zip-packager.ts +75 -73
  209. package/lib/zip/zip-transformer.ts +112 -107
  210. package/lib/zip/zip.models.ts +7 -7
  211. package/package.json +91 -84
  212. package/dist/core/index.d.ts +0 -16
  213. package/dist/core/index.js +0 -17
  214. package/dist/core/index.js.map +0 -1
  215. package/dist/export/index.d.ts +0 -2
  216. package/dist/export/index.js +0 -3
  217. package/dist/export/index.js.map +0 -1
  218. package/dist/file/index.d.ts +0 -1
  219. package/dist/file/index.js +0 -2
  220. package/dist/file/index.js.map +0 -1
  221. package/dist/import/index.d.ts +0 -2
  222. package/dist/import/index.js +0 -3
  223. package/dist/import/index.js.map +0 -1
  224. package/dist/toolkit/index.d.ts +0 -5
  225. package/dist/toolkit/index.js +0 -6
  226. package/dist/toolkit/index.js.map +0 -1
  227. package/dist/translation/index.d.ts +0 -4
  228. package/dist/translation/index.js +0 -5
  229. package/dist/translation/index.js.map +0 -1
  230. package/dist/zip/index.d.ts +0 -4
  231. package/dist/zip/index.js +0 -5
  232. package/dist/zip/index.js.map +0 -1
  233. package/lib/core/index.ts +0 -18
  234. package/lib/export/index.ts +0 -2
  235. package/lib/file/index.ts +0 -1
  236. package/lib/import/index.ts +0 -2
  237. package/lib/toolkit/index.ts +0 -5
  238. package/lib/translation/index.ts +0 -4
  239. package/lib/zip/index.ts +0 -4
@@ -1,453 +1,478 @@
1
1
  import type {
2
- AssetModels,
3
- CollectionModels,
4
- ContentItemModels,
5
- LanguageModels,
6
- LanguageVariantModels,
7
- WorkflowModels
8
- } from '@kontent-ai/management-sdk';
9
- import chalk from 'chalk';
2
+ AssetModels,
3
+ CollectionModels,
4
+ ContentItemModels,
5
+ LanguageModels,
6
+ LanguageVariantModels,
7
+ WorkflowModels,
8
+ } from "@kontent-ai/management-sdk";
9
+ import chalk from "chalk";
10
+ import { workflowHelper } from "../../core/helpers/workflow-helper.js";
10
11
  import type {
11
- AssetStateInSourceEnvironmentById,
12
- FlattenedContentType,
13
- ItemStateInSourceEnvironmentById,
14
- LogSpinnerData
15
- } from '../../core/index.js';
16
- import {
17
- findRequired,
18
- is404Error,
19
- managementClientUtils,
20
- processItemsAsync,
21
- runMapiRequestAsync,
22
- workflowHelper
23
- } from '../../core/index.js';
24
- import { itemsExtractionProcessor } from '../../translation/index.js';
12
+ AssetStateInSourceEnvironmentById,
13
+ FlattenedContentType,
14
+ ItemStateInSourceEnvironmentById,
15
+ } from "../../core/models/core.models.js";
16
+ import type { LogSpinnerData } from "../../core/models/log.models.js";
17
+ import { findRequired } from "../../core/utils/array.utils.js";
18
+ import { is404Error } from "../../core/utils/error.utils.js";
19
+ import { managementClientUtils } from "../../core/utils/management-client-utils.js";
20
+ import { processItemsAsync } from "../../core/utils/processing-utils.js";
21
+ import { runMapiRequestAsync } from "../../core/utils/run.utils.js";
22
+ import { itemsExtractionProcessor } from "../../translation/extraction/items-extraction.processor.js";
25
23
  import type {
26
- DefaultExportContextConfig,
27
- ExportContext,
28
- ExportContextEnvironmentData,
29
- ExportItem,
30
- ExportItemVersion,
31
- GetFlattenedElementByIds,
32
- SourceExportItem
33
- } from '../export.models.js';
34
- import { throwErrorForItemRequest } from '../utils/export.utils.js';
24
+ DefaultExportContextConfig,
25
+ ExportContext,
26
+ ExportContextEnvironmentData,
27
+ ExportItem,
28
+ ExportItemVersion,
29
+ GetFlattenedElementByIds,
30
+ SourceExportItem,
31
+ } from "../export.models.js";
32
+ import { throwErrorForItemRequest } from "../utils/export.utils.js";
35
33
 
36
34
  export async function exportContextFetcherAsync(config: DefaultExportContextConfig) {
37
- const getEnvironmentDataAsync = async (): Promise<ExportContextEnvironmentData> => {
38
- const mapiUtils = managementClientUtils(config.managementClient, config.logger);
39
-
40
- return await config.logger.logWithSpinnerAsync(async (spinnerData) => {
41
- spinnerData({ type: 'info', message: `Loading environment data` });
42
-
43
- const environmentData: ExportContextEnvironmentData = {
44
- collections: await mapiUtils.getAllCollectionsAsync(spinnerData),
45
- contentTypes: await mapiUtils.getFlattenedContentTypesAsync(spinnerData),
46
- languages: await mapiUtils.getAllLanguagesAsync(spinnerData),
47
- workflows: await mapiUtils.getAllWorkflowsAsync(spinnerData),
48
- taxonomies: await mapiUtils.getAllTaxonomiesAsync(spinnerData),
49
- assetFolders: await mapiUtils.getAllAssetFoldersAsync(spinnerData)
50
- };
51
-
52
- spinnerData({ type: 'info', message: `Environmental data loaded` });
53
-
54
- return environmentData;
55
- });
56
- };
57
-
58
- const environmentData = await getEnvironmentDataAsync();
59
-
60
- const getContentItemAsync = async (
61
- sourceItem: SourceExportItem,
62
- logSpinner: LogSpinnerData
63
- ): Promise<Readonly<ContentItemModels.ContentItem>> => {
64
- return await runMapiRequestAsync({
65
- logger: config.logger,
66
- logSpinner: logSpinner,
67
- func: async () => {
68
- return (await config.managementClient.viewContentItem().byItemCodename(sourceItem.itemCodename).toPromise()).data;
69
- },
70
- action: 'view',
71
- type: 'contentItem',
72
- itemName: `codename -> ${sourceItem.itemCodename}`
73
- });
74
- };
75
-
76
- const getLatestLanguageVariantAsync = async (
77
- sourceItem: SourceExportItem,
78
- logSpinner: LogSpinnerData
79
- ): Promise<Readonly<LanguageVariantModels.ContentItemLanguageVariant>> => {
80
- return await runMapiRequestAsync({
81
- logger: config.logger,
82
- logSpinner: logSpinner,
83
- func: async () => {
84
- return (
85
- await config.managementClient
86
- .viewLanguageVariant()
87
- .byItemCodename(sourceItem.itemCodename)
88
- .byLanguageCodename(sourceItem.languageCodename)
89
- .toPromise()
90
- ).data;
91
- },
92
-
93
- action: 'view',
94
- type: 'languageVariant',
95
- itemName: `codename -> ${sourceItem.itemCodename} -> latest (${sourceItem.languageCodename})`
96
- });
97
- };
98
-
99
- const isLanguageVariantPublished = (languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>): boolean => {
100
- return environmentData.workflows.find((workflow) => workflow.publishedStep.id === languageVariant.workflow.stepIdentifier.id)
101
- ? true
102
- : false;
103
- };
104
-
105
- const mapToExportVersionItem = (
106
- sourceItem: SourceExportItem,
107
- contentItem: Readonly<ContentItemModels.ContentItem>,
108
- languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>
109
- ): ExportItemVersion => {
110
- return {
111
- languageVariant: languageVariant,
112
- workflowStepCodename: validateExportItem({
113
- sourceItem: sourceItem,
114
- contentItem: contentItem,
115
- languageVariant: languageVariant
116
- }).workflowStepCodename
117
- };
118
- };
119
-
120
- const getExportItemVersionsAsync = async (
121
- sourceItem: SourceExportItem,
122
- contentItem: Readonly<ContentItemModels.ContentItem>,
123
- logSpinner: LogSpinnerData
124
- ): Promise<readonly ExportItemVersion[]> => {
125
- const latestLanguageVariant = await getLatestLanguageVariantAsync(sourceItem, logSpinner);
126
- const latestExportVersion = mapToExportVersionItem(sourceItem, contentItem, latestLanguageVariant);
127
-
128
- if (isLanguageVariantPublished(latestLanguageVariant)) {
129
- // latest language variant is also published = no need to fetch published version
130
- return [latestExportVersion];
131
- }
132
-
133
- const publishedLanguageVariant = await getPublishedLanguageVariantAsync(sourceItem, logSpinner);
134
-
135
- if (!publishedLanguageVariant) {
136
- return [latestExportVersion];
137
- }
138
-
139
- return [latestExportVersion, mapToExportVersionItem(sourceItem, contentItem, publishedLanguageVariant)];
140
- };
141
-
142
- const getPublishedLanguageVariantAsync = async (
143
- sourceItem: SourceExportItem,
144
- logSpinner: LogSpinnerData
145
- ): Promise<Readonly<LanguageVariantModels.ContentItemLanguageVariant> | undefined> => {
146
- return await runMapiRequestAsync({
147
- logger: config.logger,
148
- logSpinner: logSpinner,
149
- func: async () => {
150
- try {
151
- return (
152
- await config.managementClient
153
- .viewLanguageVariant()
154
- .byItemCodename(sourceItem.itemCodename)
155
- .byLanguageCodename(sourceItem.languageCodename)
156
- .published()
157
- .toPromise()
158
- ).data;
159
- } catch (error) {
160
- if (is404Error(error)) {
161
- return undefined;
162
- }
163
- throw error;
164
- }
165
- },
166
- action: 'view',
167
- type: 'languageVariant',
168
- itemName: `codename -> ${sourceItem.itemCodename} -> published (${sourceItem.languageCodename})`
169
- });
170
- };
171
-
172
- const validateExportItem = (data: {
173
- readonly sourceItem: SourceExportItem;
174
- readonly contentItem: Readonly<ContentItemModels.ContentItem>;
175
- readonly languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>;
176
- }): {
177
- readonly collection: Readonly<CollectionModels.Collection>;
178
- readonly language: Readonly<LanguageModels.LanguageModel>;
179
- readonly workflow: Readonly<WorkflowModels.Workflow>;
180
- readonly contentType: Readonly<FlattenedContentType>;
181
- readonly workflowStepCodename: string;
182
- } => {
183
- const collection = findRequired(
184
- environmentData.collections,
185
- (collection) => collection.id === data.contentItem.collection.id,
186
- () => {
187
- throwErrorForItemRequest(data.sourceItem, `Invalid collection '${chalk.yellow(data.contentItem.collection.id ?? '')}'`);
188
- }
189
- );
190
-
191
- const contentType = findRequired(
192
- environmentData.contentTypes,
193
- (contentType) => contentType.contentTypeId === data.contentItem.type.id,
194
- () => {
195
- throwErrorForItemRequest(data.sourceItem, `Invalid content type '${chalk.red(data.contentItem.type.id)}'`);
196
- }
197
- );
198
-
199
- const language = findRequired(
200
- environmentData.languages,
201
- (language) => language.id === data.languageVariant.language.id,
202
- () => {
203
- throwErrorForItemRequest(data.sourceItem, `Invalid language '${chalk.red(data.languageVariant.language.id ?? '')}'`);
204
- }
205
- );
206
-
207
- const workflow = findRequired(
208
- environmentData.workflows,
209
- (workflow) => workflow.id === data.languageVariant.workflow.workflowIdentifier.id,
210
- () => {
211
- throwErrorForItemRequest(
212
- data.sourceItem,
213
- `Invalid workflow '${chalk.red(data.languageVariant.workflow.workflowIdentifier.id ?? '')}'`
214
- );
215
- }
216
- );
217
-
218
- const workflowStep = workflowHelper(environmentData.workflows).getWorkflowStep(workflow, {
219
- match: (step) => step.id === data.languageVariant.workflow.stepIdentifier.id,
220
- errorMessage: `Invalid workflow step '${chalk.red(data.languageVariant.workflow.stepIdentifier.id ?? '')}'`
221
- });
222
-
223
- return {
224
- collection,
225
- language,
226
- workflow,
227
- contentType,
228
- workflowStepCodename: workflowStep.codename
229
- };
230
- };
231
-
232
- const prepareExportItemsAsync = async (exportItems: readonly SourceExportItem[]): Promise<readonly ExportItem[]> => {
233
- config.logger.log({
234
- type: 'info',
235
- message: `Preparing '${chalk.yellow(config.exportItems.length.toString())}' items for export`
236
- });
237
-
238
- return (
239
- await processItemsAsync<SourceExportItem, ExportItem>({
240
- logger: config.logger,
241
- action: 'Preparing content items & language variants',
242
- parallelLimit: 1,
243
- itemInfo: (input) => {
244
- return {
245
- title: `${input.itemCodename} (${input.languageCodename})`,
246
- itemType: 'exportItem'
247
- };
248
- },
249
- items: exportItems,
250
- processAsync: async (requestItem, logSpinner) => {
251
- const contentItem = await getContentItemAsync(requestItem, logSpinner);
252
- const versions = await getExportItemVersionsAsync(requestItem, contentItem, logSpinner);
253
-
254
- // get shared attributes from any version
255
- const anyVersion = versions[0];
256
- if (!anyVersion) {
257
- throwErrorForItemRequest(requestItem, `Expected at least 1 version of the content item`);
258
- }
259
-
260
- const { collection, contentType, language, workflow } = validateExportItem({
261
- sourceItem: requestItem,
262
- contentItem: contentItem,
263
- languageVariant: anyVersion.languageVariant
264
- });
265
-
266
- return {
267
- contentItem,
268
- versions,
269
- contentType,
270
- requestItem,
271
- workflow,
272
- collection,
273
- language
274
- };
275
- }
276
- })
277
- )
278
- .filter((m) => m.state === 'valid')
279
- .map((m) => m.outputItem);
280
- };
281
-
282
- const getContentItemsByIdsAsync = async (itemIds: ReadonlySet<string>): Promise<readonly Readonly<ContentItemModels.ContentItem>[]> => {
283
- return (
284
- await processItemsAsync<string, Readonly<ContentItemModels.ContentItem>>({
285
- logger: config.logger,
286
- action: 'Fetching content items',
287
- parallelLimit: 1,
288
- items: Array.from(itemIds),
289
- itemInfo: (id) => {
290
- return {
291
- itemType: 'contentItem',
292
- title: id
293
- };
294
- },
295
- processAsync: async (id, logSpinner) => {
296
- try {
297
- return await runMapiRequestAsync({
298
- logSpinner: logSpinner,
299
- logger: config.logger,
300
- func: async () => (await config.managementClient.viewContentItem().byItemId(id).toPromise()).data,
301
- action: 'view',
302
- type: 'contentItem',
303
- itemName: `id -> ${id}`
304
- });
305
- } catch (error) {
306
- if (!is404Error(error)) {
307
- throw error;
308
- }
309
-
310
- return '404';
311
- }
312
- }
313
- })
314
- )
315
- .filter((m) => m.state === 'valid')
316
- .map((m) => m.outputItem);
317
- };
318
-
319
- const getAssetsByIdsAsync = async (itemIds: ReadonlySet<string>): Promise<readonly Readonly<AssetModels.Asset>[]> => {
320
- return (
321
- await processItemsAsync<string, Readonly<AssetModels.Asset>>({
322
- logger: config.logger,
323
- action: 'Fetching assets',
324
- parallelLimit: 1,
325
- items: Array.from(itemIds),
326
- itemInfo: (id) => {
327
- return {
328
- itemType: 'asset',
329
- title: id
330
- };
331
- },
332
- processAsync: async (id, logSpinner) => {
333
- try {
334
- return await runMapiRequestAsync({
335
- logger: config.logger,
336
- logSpinner: logSpinner,
337
- func: async () => (await config.managementClient.viewAsset().byAssetId(id).toPromise()).data,
338
- action: 'view',
339
- type: 'asset',
340
- itemName: `id -> ${id}`
341
- });
342
- } catch (error) {
343
- if (!is404Error(error)) {
344
- throw error;
345
- }
346
-
347
- return '404';
348
- }
349
- }
350
- })
351
- )
352
- .filter((m) => m.state === 'valid')
353
- .map((m) => m.outputItem);
354
- };
355
-
356
- const getItemStatesAsync = async (itemIds: ReadonlySet<string>): Promise<readonly ItemStateInSourceEnvironmentById[]> => {
357
- const items = await getContentItemsByIdsAsync(itemIds);
358
-
359
- return Array.from(itemIds).map<ItemStateInSourceEnvironmentById>((itemId) => {
360
- const item = items.find((m) => m.id === itemId);
361
- return {
362
- id: itemId,
363
- item: item,
364
- state: item ? 'exists' : 'doesNotExists'
365
- };
366
- });
367
- };
368
-
369
- const getAssetStatesAsync = async (assetIds: ReadonlySet<string>): Promise<readonly AssetStateInSourceEnvironmentById[]> => {
370
- const assets = await getAssetsByIdsAsync(assetIds);
371
-
372
- return Array.from(assetIds).map<AssetStateInSourceEnvironmentById>((assetId) => {
373
- const asset = assets.find((m) => m.id === assetId);
374
- return {
375
- id: assetId,
376
- asset: asset,
377
- state: asset ? 'exists' : 'doesNotExists'
378
- };
379
- });
380
- };
381
-
382
- const getElementByIds = (): GetFlattenedElementByIds => {
383
- const getFunc: GetFlattenedElementByIds = (contentTypeId: string, elementId: string) => {
384
- const contentType = findRequired(
385
- environmentData.contentTypes,
386
- (contentType) => contentType.contentTypeId === contentTypeId,
387
- `Could not find content type with id '${chalk.red(contentTypeId)}'`
388
- );
389
-
390
- const element = findRequired(
391
- contentType.elements,
392
- (element) => element.id === elementId,
393
- `Could not find element with id '${chalk.red(elementId)}' in content type '${chalk.red(contentType.contentTypeCodename)}'`
394
- );
395
-
396
- return element;
397
- };
398
-
399
- return getFunc;
400
- };
401
-
402
- const getExportContextAsync = async (): Promise<ExportContext> => {
403
- const preparedItems = await prepareExportItemsAsync(config.exportItems);
404
-
405
- config.logger.log({
406
- type: 'info',
407
- message: `Extracting referenced items & assets from content`
408
- });
409
-
410
- const referencedData = itemsExtractionProcessor().extractReferencedDataFromExtractItems(
411
- preparedItems.map((exportItem) => {
412
- return {
413
- contentTypeId: exportItem.contentType.contentTypeId,
414
- elements: exportItem.versions.flatMap((m) => m.languageVariant).flatMap((s) => s.elements)
415
- };
416
- }),
417
- getElementByIds()
418
- );
419
-
420
- const itemStates: readonly ItemStateInSourceEnvironmentById[] = await getItemStatesAsync(
421
- // fetch both referenced items and items that are set to be exported
422
- new Set<string>([...referencedData.itemIds, ...preparedItems.map((m) => m.contentItem.id)])
423
- );
424
- const assetStates: readonly AssetStateInSourceEnvironmentById[] = await getAssetStatesAsync(
425
- new Set<string>([...referencedData.assetIds])
426
- );
427
-
428
- const exportContext: ExportContext = {
429
- getElement: getElementByIds(),
430
- exportItems: preparedItems,
431
- environmentData: environmentData,
432
- referencedData: referencedData,
433
- getAssetStateInSourceEnvironment: (id) =>
434
- findRequired(
435
- assetStates,
436
- (m) => m.id === id,
437
- `Invalid state for asset '${chalk.red(id)}'. It is expected that all asset states will be initialized`
438
- ),
439
- getItemStateInSourceEnvironment: (id) =>
440
- findRequired(
441
- itemStates,
442
- (m) => m.id === id,
443
- `Invalid state for item '${chalk.red(id)}'. It is expected that all item states will be initialized`
444
- )
445
- };
446
-
447
- return exportContext;
448
- };
449
-
450
- return {
451
- getExportContextAsync
452
- };
35
+ const getEnvironmentDataAsync = async (): Promise<ExportContextEnvironmentData> => {
36
+ const mapiUtils = managementClientUtils(config.managementClient, config.logger);
37
+
38
+ return await config.logger.logWithSpinnerAsync(async (spinnerData) => {
39
+ spinnerData({ type: "info", message: `Loading environment data` });
40
+
41
+ const environmentData: ExportContextEnvironmentData = {
42
+ collections: await mapiUtils.getAllCollectionsAsync(spinnerData),
43
+ contentTypes: await mapiUtils.getFlattenedContentTypesAsync(spinnerData),
44
+ languages: await mapiUtils.getAllLanguagesAsync(spinnerData),
45
+ workflows: await mapiUtils.getAllWorkflowsAsync(spinnerData),
46
+ taxonomies: await mapiUtils.getAllTaxonomiesAsync(spinnerData),
47
+ assetFolders: await mapiUtils.getAllAssetFoldersAsync(spinnerData),
48
+ };
49
+
50
+ spinnerData({ type: "info", message: `Environmental data loaded` });
51
+
52
+ return environmentData;
53
+ });
54
+ };
55
+
56
+ const environmentData = await getEnvironmentDataAsync();
57
+
58
+ const getContentItemAsync = async (
59
+ sourceItem: SourceExportItem,
60
+ logSpinner: LogSpinnerData,
61
+ ): Promise<Readonly<ContentItemModels.ContentItem>> => {
62
+ return await runMapiRequestAsync({
63
+ logger: config.logger,
64
+ logSpinner: logSpinner,
65
+ func: async () => {
66
+ return (await config.managementClient.viewContentItem().byItemCodename(sourceItem.itemCodename).toPromise()).data;
67
+ },
68
+ action: "view",
69
+ type: "contentItem",
70
+ itemName: `codename -> ${sourceItem.itemCodename}`,
71
+ });
72
+ };
73
+
74
+ const getLatestLanguageVariantAsync = async (
75
+ sourceItem: SourceExportItem,
76
+ logSpinner: LogSpinnerData,
77
+ ): Promise<Readonly<LanguageVariantModels.ContentItemLanguageVariant>> => {
78
+ return await runMapiRequestAsync({
79
+ logger: config.logger,
80
+ logSpinner: logSpinner,
81
+ func: async () => {
82
+ return (
83
+ await config.managementClient
84
+ .viewLanguageVariant()
85
+ .byItemCodename(sourceItem.itemCodename)
86
+ .byLanguageCodename(sourceItem.languageCodename)
87
+ .toPromise()
88
+ ).data;
89
+ },
90
+
91
+ action: "view",
92
+ type: "languageVariant",
93
+ itemName: `codename -> ${sourceItem.itemCodename} -> latest (${sourceItem.languageCodename})`,
94
+ });
95
+ };
96
+
97
+ const isLanguageVariantPublished = (languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>): boolean => {
98
+ return environmentData.workflows.some((workflow) => workflow.publishedStep.id === languageVariant.workflow.stepIdentifier.id);
99
+ };
100
+
101
+ const mapToExportVersionItem = (
102
+ sourceItem: SourceExportItem,
103
+ contentItem: Readonly<ContentItemModels.ContentItem>,
104
+ languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>,
105
+ ): ExportItemVersion => {
106
+ return {
107
+ languageVariant: languageVariant,
108
+ workflowStepCodename: validateExportItem({
109
+ sourceItem: sourceItem,
110
+ contentItem: contentItem,
111
+ languageVariant: languageVariant,
112
+ }).workflowStepCodename,
113
+ };
114
+ };
115
+
116
+ const getExportItemVersionsAsync = async (
117
+ sourceItem: SourceExportItem,
118
+ contentItem: Readonly<ContentItemModels.ContentItem>,
119
+ logSpinner: LogSpinnerData,
120
+ ): Promise<readonly ExportItemVersion[]> => {
121
+ const latestLanguageVariant = await getLatestLanguageVariantAsync(sourceItem, logSpinner);
122
+ const latestExportVersion = mapToExportVersionItem(sourceItem, contentItem, latestLanguageVariant);
123
+
124
+ if (isLanguageVariantPublished(latestLanguageVariant)) {
125
+ // latest language variant is also published = no need to fetch published version
126
+ return [latestExportVersion];
127
+ }
128
+
129
+ const publishedLanguageVariant = await getPublishedLanguageVariantAsync(sourceItem, logSpinner);
130
+
131
+ if (!publishedLanguageVariant) {
132
+ return [latestExportVersion];
133
+ }
134
+
135
+ return [latestExportVersion, mapToExportVersionItem(sourceItem, contentItem, publishedLanguageVariant)];
136
+ };
137
+
138
+ const getPublishedLanguageVariantAsync = async (
139
+ sourceItem: SourceExportItem,
140
+ logSpinner: LogSpinnerData,
141
+ ): Promise<Readonly<LanguageVariantModels.ContentItemLanguageVariant> | undefined> => {
142
+ return await runMapiRequestAsync({
143
+ logger: config.logger,
144
+ logSpinner: logSpinner,
145
+ func: async () => {
146
+ try {
147
+ return (
148
+ await config.managementClient
149
+ .viewLanguageVariant()
150
+ .byItemCodename(sourceItem.itemCodename)
151
+ .byLanguageCodename(sourceItem.languageCodename)
152
+ .published()
153
+ .toPromise()
154
+ ).data;
155
+ } catch (error) {
156
+ if (is404Error(error)) {
157
+ return undefined;
158
+ }
159
+ throw error;
160
+ }
161
+ },
162
+ action: "view",
163
+ type: "languageVariant",
164
+ itemName: `codename -> ${sourceItem.itemCodename} -> published (${sourceItem.languageCodename})`,
165
+ });
166
+ };
167
+
168
+ const validateExportItem = (data: {
169
+ readonly sourceItem: SourceExportItem;
170
+ readonly contentItem: Readonly<ContentItemModels.ContentItem>;
171
+ readonly languageVariant: Readonly<LanguageVariantModels.ContentItemLanguageVariant>;
172
+ }): {
173
+ readonly collection: Readonly<CollectionModels.Collection>;
174
+ readonly language: Readonly<LanguageModels.LanguageModel>;
175
+ readonly workflow: Readonly<WorkflowModels.Workflow>;
176
+ readonly contentType: Readonly<FlattenedContentType>;
177
+ readonly workflowStepCodename: string;
178
+ } => {
179
+ const collection = findRequired(
180
+ environmentData.collections,
181
+ (collection) => collection.id === data.contentItem.collection.id,
182
+ () => {
183
+ throwErrorForItemRequest(data.sourceItem, `Invalid collection '${chalk.yellow(data.contentItem.collection.id)}'`);
184
+ },
185
+ );
186
+
187
+ const contentType = findRequired(
188
+ environmentData.contentTypes,
189
+ (contentType) => contentType.contentTypeId === data.contentItem.type.id,
190
+ () => {
191
+ throwErrorForItemRequest(data.sourceItem, `Invalid content type '${chalk.red(data.contentItem.type.id)}'`);
192
+ },
193
+ );
194
+
195
+ const language = findRequired(
196
+ environmentData.languages,
197
+ (language) => language.id === data.languageVariant.language.id,
198
+ () => {
199
+ throwErrorForItemRequest(data.sourceItem, `Invalid language '${chalk.red(data.languageVariant.language.id ?? "")}'`);
200
+ },
201
+ );
202
+
203
+ const workflow = findRequired(
204
+ environmentData.workflows,
205
+ (workflow) => workflow.id === data.languageVariant.workflow.workflowIdentifier.id,
206
+ () => {
207
+ throwErrorForItemRequest(
208
+ data.sourceItem,
209
+ `Invalid workflow '${chalk.red(data.languageVariant.workflow.workflowIdentifier.id ?? "")}'`,
210
+ );
211
+ },
212
+ );
213
+
214
+ const workflowStep = workflowHelper(environmentData.workflows).getWorkflowStep(workflow, {
215
+ match: (step) => step.id === data.languageVariant.workflow.stepIdentifier.id,
216
+ errorMessage: `Invalid workflow step '${chalk.red(data.languageVariant.workflow.stepIdentifier.id ?? "")}'`,
217
+ });
218
+
219
+ return {
220
+ collection,
221
+ language,
222
+ workflow,
223
+ contentType,
224
+ workflowStepCodename: workflowStep.codename,
225
+ };
226
+ };
227
+
228
+ const prepareExportItemsAsync = async (exportItems: readonly SourceExportItem[]): Promise<readonly ExportItem[]> => {
229
+ config.logger.log({
230
+ type: "info",
231
+ message: `Preparing '${chalk.yellow(config.exportItems.length.toString())}' items for export`,
232
+ });
233
+
234
+ return (
235
+ await processItemsAsync<SourceExportItem, ExportItem>({
236
+ logger: config.logger,
237
+ action: "Preparing content items & language variants",
238
+ parallelLimit: 1,
239
+ itemInfo: (input) => {
240
+ return {
241
+ title: `${input.itemCodename} (${input.languageCodename})`,
242
+ itemType: "exportItem",
243
+ };
244
+ },
245
+ items: exportItems,
246
+ processAsync: async (requestItem, logSpinner) => {
247
+ const contentItem = await getContentItemAsync(requestItem, logSpinner);
248
+ const versions = await getExportItemVersionsAsync(requestItem, contentItem, logSpinner);
249
+
250
+ // get shared attributes from any version
251
+ const anyVersion = versions.length ? versions[0] : undefined;
252
+ if (!anyVersion) {
253
+ throwErrorForItemRequest(requestItem, `Expected at least 1 version of the content item`);
254
+ }
255
+
256
+ const { collection, contentType, language, workflow } = validateExportItem({
257
+ sourceItem: requestItem,
258
+ contentItem: contentItem,
259
+ languageVariant: anyVersion.languageVariant,
260
+ });
261
+
262
+ return {
263
+ contentItem,
264
+ versions,
265
+ contentType,
266
+ requestItem,
267
+ workflow,
268
+ collection,
269
+ language,
270
+ };
271
+ },
272
+ })
273
+ )
274
+ .filter((m) => m.state === "valid")
275
+ .map((m) => m.outputItem);
276
+ };
277
+
278
+ const getContentItemsByIdsAsync = async (itemIds: ReadonlySet<string>): Promise<readonly Readonly<ContentItemModels.ContentItem>[]> => {
279
+ return (
280
+ await processItemsAsync<string, Readonly<ContentItemModels.ContentItem>>({
281
+ logger: config.logger,
282
+ action: "Fetching content items",
283
+ parallelLimit: 1,
284
+ items: Array.from(itemIds),
285
+ itemInfo: (id) => {
286
+ return {
287
+ itemType: "contentItem",
288
+ title: id,
289
+ };
290
+ },
291
+ processAsync: async (id, logSpinner) => {
292
+ try {
293
+ return await runMapiRequestAsync({
294
+ logSpinner: logSpinner,
295
+ logger: config.logger,
296
+ func: async () => (await config.managementClient.viewContentItem().byItemId(id).toPromise()).data,
297
+ action: "view",
298
+ type: "contentItem",
299
+ itemName: `id -> ${id}`,
300
+ });
301
+ } catch (error) {
302
+ if (!is404Error(error)) {
303
+ throw error;
304
+ }
305
+
306
+ return "404";
307
+ }
308
+ },
309
+ })
310
+ )
311
+ .filter((m) => m.state === "valid")
312
+ .map((m) => m.outputItem);
313
+ };
314
+
315
+ const getAssetsByIdsAsync = async (itemIds: ReadonlySet<string>): Promise<readonly Readonly<AssetModels.Asset>[]> => {
316
+ return (
317
+ await processItemsAsync<string, Readonly<AssetModels.Asset>>({
318
+ logger: config.logger,
319
+ action: "Fetching assets",
320
+ parallelLimit: 1,
321
+ items: Array.from(itemIds),
322
+ itemInfo: (id) => {
323
+ return {
324
+ itemType: "asset",
325
+ title: id,
326
+ };
327
+ },
328
+ processAsync: async (id, logSpinner) => {
329
+ try {
330
+ return await runMapiRequestAsync({
331
+ logger: config.logger,
332
+ logSpinner: logSpinner,
333
+ func: async () => (await config.managementClient.viewAsset().byAssetId(id).toPromise()).data,
334
+ action: "view",
335
+ type: "asset",
336
+ itemName: `id -> ${id}`,
337
+ });
338
+ } catch (error) {
339
+ if (!is404Error(error)) {
340
+ throw error;
341
+ }
342
+
343
+ return "404";
344
+ }
345
+ },
346
+ })
347
+ )
348
+ .filter((m) => m.state === "valid")
349
+ .map((m) => m.outputItem);
350
+ };
351
+
352
+ const getItemStatesAsync = async (itemIds: ReadonlySet<string>): Promise<readonly ItemStateInSourceEnvironmentById[]> => {
353
+ const items = await getContentItemsByIdsAsync(itemIds);
354
+
355
+ return Array.from(itemIds).map<ItemStateInSourceEnvironmentById>((itemId) => {
356
+ const item = items.find((m) => m.id === itemId);
357
+
358
+ if (item) {
359
+ return {
360
+ id: itemId,
361
+ data: item,
362
+ state: "exists",
363
+ };
364
+ }
365
+ if (config.skipMissingReferences) {
366
+ return {
367
+ id: itemId,
368
+ state: "skip",
369
+ };
370
+ }
371
+ return {
372
+ id: itemId,
373
+ state: "doesNotExists",
374
+ };
375
+ });
376
+ };
377
+
378
+ const getAssetStatesAsync = async (assetIds: ReadonlySet<string>): Promise<readonly AssetStateInSourceEnvironmentById[]> => {
379
+ const assets = await getAssetsByIdsAsync(assetIds);
380
+
381
+ return Array.from(assetIds).map<AssetStateInSourceEnvironmentById>((assetId) => {
382
+ const asset = assets.find((m) => m.id === assetId);
383
+
384
+ if (asset) {
385
+ return {
386
+ id: assetId,
387
+ data: asset,
388
+ state: "exists",
389
+ };
390
+ }
391
+ if (config.skipMissingReferences) {
392
+ return {
393
+ id: assetId,
394
+ state: "skip",
395
+ };
396
+ }
397
+ return {
398
+ id: assetId,
399
+ state: "doesNotExists",
400
+ };
401
+ });
402
+ };
403
+
404
+ const getElementByIds = (): GetFlattenedElementByIds => {
405
+ const getFunc: GetFlattenedElementByIds = (contentTypeId: string, elementId: string) => {
406
+ const contentType = findRequired(
407
+ environmentData.contentTypes,
408
+ (contentType) => contentType.contentTypeId === contentTypeId,
409
+ `Could not find content type with id '${chalk.red(contentTypeId)}'`,
410
+ );
411
+
412
+ const element = findRequired(
413
+ contentType.elements,
414
+ (element) => element.id === elementId,
415
+ `Could not find element with id '${chalk.red(elementId)}' in content type '${chalk.red(contentType.contentTypeCodename)}'`,
416
+ );
417
+
418
+ return element;
419
+ };
420
+
421
+ return getFunc;
422
+ };
423
+
424
+ const getExportContextAsync = async (): Promise<ExportContext> => {
425
+ const preparedItems = await prepareExportItemsAsync(config.exportItems);
426
+
427
+ config.logger.log({
428
+ type: "info",
429
+ message: `Extracting referenced items & assets from content`,
430
+ });
431
+
432
+ const referencedData = itemsExtractionProcessor().extractReferencedDataFromExtractItems(
433
+ preparedItems.map((exportItem) => {
434
+ return {
435
+ contentTypeId: exportItem.contentType.contentTypeId,
436
+ elements: exportItem.versions.flatMap((m) => m.languageVariant).flatMap((s) => s.elements),
437
+ };
438
+ }),
439
+ getElementByIds(),
440
+ );
441
+
442
+ const itemStates: readonly ItemStateInSourceEnvironmentById[] = await getItemStatesAsync(
443
+ // fetch both referenced items and items that are set to be exported
444
+ new Set<string>([...referencedData.itemIds, ...preparedItems.map((m) => m.contentItem.id)]),
445
+ );
446
+ const assetStates: readonly AssetStateInSourceEnvironmentById[] = await getAssetStatesAsync(
447
+ new Set<string>([...referencedData.assetIds]),
448
+ );
449
+
450
+ const exportContext: ExportContext = {
451
+ getElement: getElementByIds(),
452
+ exportItems: preparedItems,
453
+ environmentData: environmentData,
454
+ referencedData: referencedData,
455
+ exportContextOptions: {
456
+ skipMissingReferences: config.skipMissingReferences,
457
+ },
458
+ getAssetStateInSourceEnvironment: (id) =>
459
+ findRequired(
460
+ assetStates,
461
+ (m) => m.id === id,
462
+ `Invalid state for asset '${chalk.red(id)}'. It is expected that all asset states will be initialized`,
463
+ ),
464
+ getItemStateInSourceEnvironment: (id) =>
465
+ findRequired(
466
+ itemStates,
467
+ (m) => m.id === id,
468
+ `Invalid state for item '${chalk.red(id)}'. It is expected that all item states will be initialized`,
469
+ ),
470
+ };
471
+
472
+ return exportContext;
473
+ };
474
+
475
+ return {
476
+ getExportContextAsync,
477
+ };
453
478
  }