contentful-import 9.1.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,167 +1,1325 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
4
10
  };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const cli_table3_1 = __importDefault(require("cli-table3"));
7
- const differenceInSeconds_1 = __importDefault(require("date-fns/differenceInSeconds"));
8
- const formatDistance_1 = __importDefault(require("date-fns/formatDistance"));
9
- const listr_1 = __importDefault(require("listr"));
10
- const listr_update_renderer_1 = __importDefault(require("listr-update-renderer"));
11
- const listr_verbose_renderer_1 = __importDefault(require("listr-verbose-renderer"));
12
- const lodash_1 = require("lodash");
13
- const p_queue_1 = __importDefault(require("p-queue"));
14
- const logging_1 = require("contentful-batch-libs/dist/logging");
15
- const listr_2 = require("contentful-batch-libs/dist/listr");
16
- const init_client_1 = __importDefault(require("./tasks/init-client"));
17
- const get_destination_data_1 = __importDefault(require("./tasks/get-destination-data"));
18
- const push_to_space_1 = __importDefault(require("./tasks/push-to-space/push-to-space"));
19
- const transform_space_1 = __importDefault(require("./transform/transform-space"));
20
- const validations_1 = require("./utils/validations");
21
- const parseOptions_1 = __importDefault(require("./parseOptions"));
22
- const ONE_SECOND = 1000;
23
- function createListrOptions(options) {
24
- if (options.useVerboseRenderer) {
25
- return {
26
- renderer: listr_verbose_renderer_1.default
27
- };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // lib/index.ts
30
+ var lib_exports = {};
31
+ __export(lib_exports, {
32
+ default: () => lib_default
33
+ });
34
+ module.exports = __toCommonJS(lib_exports);
35
+ var import_cli_table3 = __toESM(require("cli-table3"));
36
+ var import_differenceInSeconds = __toESM(require("date-fns/differenceInSeconds"));
37
+ var import_formatDistance = __toESM(require("date-fns/formatDistance"));
38
+ var import_listr3 = __toESM(require("listr"));
39
+ var import_listr_update_renderer = __toESM(require("listr-update-renderer"));
40
+ var import_listr_verbose_renderer2 = __toESM(require("listr-verbose-renderer"));
41
+ var import_lodash = require("lodash");
42
+ var import_p_queue = __toESM(require("p-queue"));
43
+ var import_logging7 = require("contentful-batch-libs/dist/logging");
44
+ var import_listr4 = require("contentful-batch-libs/dist/listr");
45
+
46
+ // lib/tasks/init-client.js
47
+ var import_contentful_management = require("contentful-management");
48
+ var import_logging = require("contentful-batch-libs/dist/logging");
49
+ function logHandler(level, data) {
50
+ import_logging.logEmitter.emit(level, data);
51
+ }
52
+ function initClient(opts) {
53
+ const defaultOpts = {
54
+ timeout: 3e4,
55
+ logHandler
56
+ };
57
+ const config = {
58
+ ...defaultOpts,
59
+ ...opts
60
+ };
61
+ return (0, import_contentful_management.createClient)(config);
62
+ }
63
+
64
+ // lib/tasks/get-destination-data.js
65
+ var import_bluebird = __toESM(require("bluebird"));
66
+ var import_logging2 = require("contentful-batch-libs/dist/logging");
67
+ var BATCH_CHAR_LIMIT = 1990;
68
+ var BATCH_SIZE_LIMIT = 100;
69
+ var METHODS = {
70
+ contentTypes: { name: "content types", method: "getContentTypes" },
71
+ locales: { name: "locales", method: "getLocales" },
72
+ entries: { name: "entries", method: "getEntries" },
73
+ assets: { name: "assets", method: "getAssets" }
74
+ };
75
+ async function batchedIdQuery({ environment, type, ids, requestQueue }) {
76
+ const method = METHODS[type].method;
77
+ const entityTypeName = METHODS[type].name;
78
+ const batches = getIdBatches(ids);
79
+ let totalFetched = 0;
80
+ const allPendingResponses = batches.map((idBatch) => {
81
+ return requestQueue.add(async () => {
82
+ const response = await environment[method]({
83
+ "sys.id[in]": idBatch,
84
+ limit: idBatch.split(",").length
85
+ });
86
+ totalFetched = totalFetched + response.items.length;
87
+ import_logging2.logEmitter.emit("info", `Fetched ${totalFetched} of ${response.total} ${entityTypeName}`);
88
+ return response.items;
89
+ });
90
+ });
91
+ const responses = await import_bluebird.default.all(allPendingResponses);
92
+ return responses.flat();
93
+ }
94
+ function getIdBatches(ids) {
95
+ const batches = [];
96
+ let currentBatch = "";
97
+ let currentSize = 0;
98
+ while (ids.length > 0) {
99
+ const id = ids.splice(0, 1);
100
+ currentBatch += id;
101
+ currentSize = currentSize + 1;
102
+ if (currentSize === BATCH_SIZE_LIMIT || currentBatch.length > BATCH_CHAR_LIMIT || ids.length === 0) {
103
+ batches.push(currentBatch);
104
+ currentBatch = "";
105
+ currentSize = 0;
106
+ } else {
107
+ currentBatch += ",";
28
108
  }
29
- return {
30
- renderer: listr_update_renderer_1.default,
31
- collapse: false
32
- };
109
+ }
110
+ return batches;
33
111
  }
34
- async function runContentfulImport(params) {
35
- const log = [];
36
- const options = await (0, parseOptions_1.default)(params);
37
- const listrOptions = createListrOptions(options);
38
- const requestQueue = new p_queue_1.default({
39
- interval: ONE_SECOND,
40
- intervalCap: options.rateLimit,
41
- carryoverConcurrencyCount: true
112
+ async function getDestinationData({
113
+ client,
114
+ spaceId,
115
+ environmentId,
116
+ sourceData,
117
+ contentModelOnly,
118
+ skipLocales,
119
+ skipContentModel,
120
+ requestQueue
121
+ }) {
122
+ const space = await client.getSpace(spaceId);
123
+ const environment = await space.getEnvironment(environmentId);
124
+ const result = {
125
+ contentTypes: [],
126
+ tags: [],
127
+ locales: [],
128
+ entries: [],
129
+ assets: []
130
+ };
131
+ sourceData = {
132
+ ...result,
133
+ ...sourceData
134
+ };
135
+ if (!skipContentModel) {
136
+ const contentTypeIds = sourceData.contentTypes.map((e) => e.sys.id);
137
+ result.contentTypes = batchedIdQuery({
138
+ environment,
139
+ type: "contentTypes",
140
+ ids: contentTypeIds,
141
+ requestQueue
42
142
  });
43
- // Setup custom log listener to store log messages for later
44
- (0, logging_1.setupLogging)(log);
45
- const infoTable = new cli_table3_1.default();
46
- infoTable.push([{ colSpan: 2, content: 'The following entities are going to be imported:' }]);
47
- Object.keys(options.content).forEach((type) => {
48
- if (options.skipLocales && type === 'locales') {
49
- return;
50
- }
51
- if (options.skipContentModel && (['contentTypes', 'editorInterfaces'].indexOf(type) >= 0)) {
52
- return;
53
- }
54
- if (options.contentModelOnly && !(['contentTypes', 'editorInterfaces', 'locales'].indexOf(type) >= 0)) {
55
- return;
56
- }
57
- infoTable.push([(0, lodash_1.startCase)(type), options.content[type].length]);
143
+ if (!skipLocales) {
144
+ const localeIds = sourceData.locales.map((e) => e.sys.id);
145
+ result.locales = batchedIdQuery({
146
+ environment,
147
+ type: "locales",
148
+ ids: localeIds,
149
+ requestQueue
150
+ });
151
+ }
152
+ }
153
+ result.tags = environment.getTags().then((response) => response.items).catch((e) => {
154
+ delete result.tags;
155
+ });
156
+ if (contentModelOnly) {
157
+ return import_bluebird.default.props(result);
158
+ }
159
+ const entryIds = sourceData.entries.map((e) => e.sys.id);
160
+ const assetIds = sourceData.assets.map((e) => e.sys.id);
161
+ result.entries = batchedIdQuery({
162
+ environment,
163
+ type: "entries",
164
+ ids: entryIds,
165
+ requestQueue
166
+ });
167
+ result.assets = batchedIdQuery({
168
+ environment,
169
+ type: "assets",
170
+ ids: assetIds,
171
+ requestQueue
172
+ });
173
+ result.webhooks = [];
174
+ return import_bluebird.default.props(result);
175
+ }
176
+
177
+ // lib/tasks/push-to-space/push-to-space.js
178
+ var import_listr = __toESM(require("listr"));
179
+ var import_listr_verbose_renderer = __toESM(require("listr-verbose-renderer"));
180
+ var import_logging6 = require("contentful-batch-libs/dist/logging");
181
+ var import_listr2 = require("contentful-batch-libs/dist/listr");
182
+
183
+ // lib/tasks/push-to-space/assets.js
184
+ var import_fs = __toESM(require("fs"));
185
+ var import_path = require("path");
186
+ var import_util = require("util");
187
+ var import_get_entity_name = __toESM(require("contentful-batch-libs/dist/get-entity-name"));
188
+ var import_logging3 = require("contentful-batch-libs/dist/logging");
189
+ var stat = (0, import_util.promisify)(import_fs.default.stat);
190
+ async function getAssetStreamForURL(url, assetsDirectory) {
191
+ const [, assetPath] = url.split("//");
192
+ const filePath = (0, import_path.join)(assetsDirectory, assetPath);
193
+ try {
194
+ await stat(filePath);
195
+ return import_fs.default.createReadStream(filePath);
196
+ } catch (err) {
197
+ const error = new Error("Cannot open asset from filesystem");
198
+ error.filePath = filePath;
199
+ throw error;
200
+ }
201
+ }
202
+ async function processAssetForLocale(locale, asset, processingOptions) {
203
+ try {
204
+ return await asset.processForLocale(locale, processingOptions);
205
+ } catch (err) {
206
+ err.entity = asset;
207
+ import_logging3.logEmitter.emit("error", err);
208
+ throw err;
209
+ }
210
+ }
211
+ async function lastResult(promises) {
212
+ if (!promises.length)
213
+ throw new RangeError("No last result from no promises");
214
+ const results = [];
215
+ await Promise.all(
216
+ promises.map(
217
+ (p) => p.then((v) => {
218
+ results.push(v);
219
+ })
220
+ )
221
+ );
222
+ return results[results.length - 1];
223
+ }
224
+ async function processAssets({
225
+ assets: assets2,
226
+ timeout,
227
+ retryLimit,
228
+ requestQueue
229
+ }) {
230
+ const processingOptions = Object.assign(
231
+ {},
232
+ timeout && { processingCheckWait: timeout },
233
+ retryLimit && { processingCheckRetry: retryLimit }
234
+ );
235
+ const pendingProcessingAssets = assets2.map(async (asset) => {
236
+ import_logging3.logEmitter.emit("info", `Processing Asset ${(0, import_get_entity_name.default)(asset)}`);
237
+ const locales2 = Object.keys(asset.fields.file || {});
238
+ let latestAssetVersion = asset;
239
+ try {
240
+ latestAssetVersion = await lastResult(
241
+ locales2.map((locale) => {
242
+ return requestQueue.add(
243
+ () => processAssetForLocale(locale, asset, processingOptions)
244
+ );
245
+ })
246
+ );
247
+ } catch (err) {
248
+ return null;
249
+ }
250
+ return latestAssetVersion;
251
+ });
252
+ const potentiallyProcessedAssets = await Promise.all(pendingProcessingAssets);
253
+ return potentiallyProcessedAssets.filter((asset) => asset);
254
+ }
255
+
256
+ // lib/tasks/push-to-space/creation.js
257
+ var import_collection = require("lodash/collection");
258
+ var import_object = require("lodash/object");
259
+ var import_get_entity_name2 = __toESM(require("contentful-batch-libs/dist/get-entity-name"));
260
+ var import_logging4 = require("contentful-batch-libs/dist/logging");
261
+ function createEntities({ context, entities, destinationEntitiesById, requestQueue }) {
262
+ return createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, requestQueue });
263
+ }
264
+ function createLocales({ context, entities, destinationEntitiesById, requestQueue }) {
265
+ return createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue });
266
+ }
267
+ async function createEntitiesWithConcurrency({ context, entities, destinationEntitiesById, requestQueue }) {
268
+ const pendingCreatedEntities = entities.map((entity) => {
269
+ const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
270
+ const operation = destinationEntity ? "update" : "create";
271
+ return requestQueue.add(async () => {
272
+ try {
273
+ const createdEntity = await (destinationEntity ? updateDestinationWithSourceData(destinationEntity, entity.transformed) : createInDestination(context, entity.transformed));
274
+ creationSuccessNotifier(operation, createdEntity);
275
+ return createdEntity;
276
+ } catch (err) {
277
+ return handleCreationErrors(entity, err);
278
+ }
58
279
  });
59
- console.log(infoTable.toString());
60
- const tasks = new listr_1.default([
61
- {
62
- title: 'Validating content-file',
63
- task: (ctx) => {
64
- (0, validations_1.assertPayload)(options.content);
65
- }
66
- },
67
- {
68
- title: 'Initialize client',
69
- task: (0, listr_2.wrapTask)(async (ctx) => {
70
- ctx.client = (0, init_client_1.default)({ ...options, content: undefined });
71
- })
72
- },
73
- {
74
- title: 'Checking if destination space already has any content and retrieving it',
75
- task: (0, listr_2.wrapTask)(async (ctx, task) => {
76
- const destinationData = await (0, get_destination_data_1.default)({
77
- client: ctx.client,
78
- spaceId: options.spaceId,
79
- environmentId: options.environmentId,
80
- sourceData: options.content,
81
- skipLocales: options.skipLocales,
82
- skipContentModel: options.skipContentModel,
83
- requestQueue
84
- });
85
- ctx.sourceDataUntransformed = options.content;
86
- ctx.destinationData = destinationData;
87
- (0, validations_1.assertDefaultLocale)(ctx.sourceDataUntransformed, ctx.destinationData);
88
- })
89
- },
90
- {
91
- title: 'Apply transformations to source data',
92
- task: (0, listr_2.wrapTask)(async (ctx) => {
93
- const transformedSourceData = (0, transform_space_1.default)(ctx.sourceDataUntransformed, ctx.destinationData);
94
- ctx.sourceData = transformedSourceData;
95
- })
96
- },
97
- {
98
- title: 'Push content to destination space',
99
- task: (ctx, task) => {
100
- return (0, push_to_space_1.default)({
101
- sourceData: ctx.sourceData,
102
- destinationData: ctx.destinationData,
103
- client: ctx.client,
104
- spaceId: options.spaceId,
105
- environmentId: options.environmentId,
106
- contentModelOnly: options.contentModelOnly,
107
- skipLocales: options.skipLocales,
108
- skipContentModel: options.skipContentModel,
109
- skipContentPublishing: options.skipContentPublishing,
110
- timeout: options.timeout,
111
- retryLimit: options.retryLimit,
112
- uploadAssets: options.uploadAssets,
113
- assetsDirectory: options.assetsDirectory,
114
- listrOptions,
115
- requestQueue
280
+ });
281
+ const createdEntities = await Promise.all(pendingCreatedEntities);
282
+ return createdEntities.filter((entity) => entity);
283
+ }
284
+ async function createEntitiesInSequence({ context, entities, destinationEntitiesById, requestQueue }) {
285
+ const createdEntities = [];
286
+ for (const entity of entities) {
287
+ const destinationEntity = getDestinationEntityForSourceEntity(destinationEntitiesById, entity.transformed);
288
+ const operation = destinationEntity ? "update" : "create";
289
+ try {
290
+ const createdEntity = await requestQueue.add(async () => {
291
+ const createdOrUpdatedEntity = await (destinationEntity ? updateDestinationWithSourceData(destinationEntity, entity.transformed) : createInDestination(context, entity.transformed));
292
+ return createdOrUpdatedEntity;
293
+ });
294
+ creationSuccessNotifier(operation, createdEntity);
295
+ createdEntities.push(createdEntity);
296
+ } catch (err) {
297
+ const maybeSubstituteEntity = handleCreationErrors(entity, err);
298
+ if (maybeSubstituteEntity) {
299
+ createdEntities.push(maybeSubstituteEntity);
300
+ }
301
+ }
302
+ }
303
+ return createdEntities;
304
+ }
305
+ async function createEntries({ context, entities, destinationEntitiesById, requestQueue }) {
306
+ const createdEntries = await Promise.all(entities.map((entry) => {
307
+ return createEntry({ entry, target: context.target, skipContentModel: context.skipContentModel, destinationEntitiesById, requestQueue });
308
+ }));
309
+ return createdEntries.filter((entry) => entry);
310
+ }
311
+ async function createEntry({ entry, target, skipContentModel, destinationEntitiesById, requestQueue }) {
312
+ const contentTypeId = entry.original.sys.contentType.sys.id;
313
+ const destinationEntry = getDestinationEntityForSourceEntity(
314
+ destinationEntitiesById,
315
+ entry.transformed
316
+ );
317
+ const operation = destinationEntry ? "update" : "create";
318
+ try {
319
+ const createdOrUpdatedEntry = await requestQueue.add(() => {
320
+ return destinationEntry ? updateDestinationWithSourceData(destinationEntry, entry.transformed) : createEntryInDestination(target, contentTypeId, entry.transformed);
321
+ });
322
+ creationSuccessNotifier(operation, createdOrUpdatedEntry);
323
+ return createdOrUpdatedEntry;
324
+ } catch (err) {
325
+ if (skipContentModel && err.name === "UnknownField") {
326
+ const errors = (0, import_object.get)(JSON.parse(err.message), "details.errors");
327
+ entry.transformed.fields = cleanupUnknownFields(entry.transformed.fields, errors);
328
+ return createEntry({ entry, target, skipContentModel, destinationEntitiesById, requestQueue });
329
+ }
330
+ err.entity = entry;
331
+ import_logging4.logEmitter.emit("error", err);
332
+ return null;
333
+ }
334
+ }
335
+ function updateDestinationWithSourceData(destinationEntity, sourceEntity) {
336
+ const plainData = getPlainData(sourceEntity);
337
+ (0, import_object.assign)(destinationEntity, plainData);
338
+ return destinationEntity.update();
339
+ }
340
+ function createInDestination(context, sourceEntity) {
341
+ const { type, target } = context;
342
+ if (type === "Tag") {
343
+ return createTagInDestination(context, sourceEntity);
344
+ }
345
+ const id = (0, import_object.get)(sourceEntity, "sys.id");
346
+ const plainData = getPlainData(sourceEntity);
347
+ return id ? target[`create${type}WithId`](id, plainData) : target[`create${type}`](plainData);
348
+ }
349
+ function createEntryInDestination(space, contentTypeId, sourceEntity) {
350
+ const id = sourceEntity.sys.id;
351
+ const plainData = getPlainData(sourceEntity);
352
+ return id ? space.createEntryWithId(contentTypeId, id, plainData) : space.createEntry(contentTypeId, plainData);
353
+ }
354
+ function createTagInDestination(context, sourceEntity) {
355
+ const id = sourceEntity.sys.id;
356
+ const visibility = sourceEntity.sys.visibility || "private";
357
+ const name = sourceEntity.name;
358
+ return context.target.createTag(id, name, visibility);
359
+ }
360
+ function handleCreationErrors(entity, err) {
361
+ if ((0, import_object.get)(err, "error.sys.id") === "ValidationFailed") {
362
+ const errors = (0, import_object.get)(err, "error.details.errors");
363
+ if (errors && errors.length > 0 && errors[0].name === "taken") {
364
+ return entity;
365
+ }
366
+ }
367
+ err.entity = entity.original;
368
+ import_logging4.logEmitter.emit("error", err);
369
+ return null;
370
+ }
371
+ function cleanupUnknownFields(fields, errors) {
372
+ return (0, import_object.omitBy)(fields, (field, fieldId) => {
373
+ return (0, import_collection.find)(errors, (error) => {
374
+ const [, errorFieldId] = error.path;
375
+ return error.name === "unknown" && errorFieldId === fieldId;
376
+ });
377
+ });
378
+ }
379
+ function getDestinationEntityForSourceEntity(destinationEntitiesById, sourceEntity) {
380
+ return destinationEntitiesById.get((0, import_object.get)(sourceEntity, "sys.id")) || null;
381
+ }
382
+ function creationSuccessNotifier(method, createdEntity) {
383
+ const verb = method[0].toUpperCase() + method.substr(1, method.length) + "d";
384
+ import_logging4.logEmitter.emit("info", `${verb} ${createdEntity.sys.type} ${(0, import_get_entity_name2.default)(createdEntity)}`);
385
+ return createdEntity;
386
+ }
387
+ function getPlainData(entity) {
388
+ const data = entity.toPlainObject ? entity.toPlainObject() : entity;
389
+ return (0, import_object.omit)(data, "sys");
390
+ }
391
+
392
+ // lib/tasks/push-to-space/publishing.js
393
+ var import_get_entity_name3 = __toESM(require("contentful-batch-libs/dist/get-entity-name"));
394
+ var import_logging5 = require("contentful-batch-libs/dist/logging");
395
+ async function publishEntities({ entities, requestQueue }) {
396
+ const entitiesToPublish = entities.filter((entity2) => {
397
+ if (!entity2 || !entity2.publish) {
398
+ import_logging5.logEmitter.emit("warning", `Unable to publish ${(0, import_get_entity_name3.default)(entity2)}`);
399
+ return false;
400
+ }
401
+ return true;
402
+ });
403
+ if (entitiesToPublish.length === 0) {
404
+ import_logging5.logEmitter.emit("info", "Skipping publishing since zero valid entities passed");
405
+ return [];
406
+ }
407
+ const entity = entities[0].original || entities[0];
408
+ const type = entity.sys.type || "unknown type";
409
+ import_logging5.logEmitter.emit("info", `Publishing ${entities.length} ${type}s`);
410
+ const result = await runQueue(entitiesToPublish, [], requestQueue);
411
+ import_logging5.logEmitter.emit("info", `Successfully published ${result.length} ${type}s`);
412
+ return result;
413
+ }
414
+ async function archiveEntities({ entities, requestQueue }) {
415
+ const entitiesToArchive = entities.filter((entity2) => {
416
+ if (!entity2 || !entity2.archive) {
417
+ import_logging5.logEmitter.emit("warning", `Unable to archive ${(0, import_get_entity_name3.default)(entity2)}`);
418
+ return false;
419
+ }
420
+ return true;
421
+ });
422
+ if (entitiesToArchive.length === 0) {
423
+ import_logging5.logEmitter.emit("info", "Skipping archiving since zero valid entities passed");
424
+ return [];
425
+ }
426
+ const entity = entities[0].original || entities[0];
427
+ const type = entity.sys.type || "unknown type";
428
+ import_logging5.logEmitter.emit("info", `Archiving ${entities.length} ${type}s`);
429
+ const pendingArchivedEntities = entitiesToArchive.map((entity2) => {
430
+ return requestQueue.add(async () => {
431
+ try {
432
+ const archivedEntity = await entity2.archive();
433
+ return archivedEntity;
434
+ } catch (err) {
435
+ err.entity = entity2;
436
+ import_logging5.logEmitter.emit("error", err);
437
+ return null;
438
+ }
439
+ });
440
+ });
441
+ const allPossiblyArchivedEntities = await Promise.all(pendingArchivedEntities);
442
+ const allArchivedEntities = allPossiblyArchivedEntities.filter((entity2) => entity2);
443
+ import_logging5.logEmitter.emit("info", `Successfully archived ${allArchivedEntities.length} ${type}s`);
444
+ return allArchivedEntities;
445
+ }
446
+ async function runQueue(queue, result = [], requestQueue) {
447
+ const publishedEntities = [];
448
+ for (const entity of queue) {
449
+ import_logging5.logEmitter.emit("info", `Publishing ${entity.sys.type} ${(0, import_get_entity_name3.default)(entity)}`);
450
+ try {
451
+ const publishedEntity = await requestQueue.add(() => entity.publish());
452
+ publishedEntities.push(publishedEntity);
453
+ } catch (err) {
454
+ err.entity = entity;
455
+ import_logging5.logEmitter.emit("error", err);
456
+ }
457
+ }
458
+ result = [
459
+ ...result,
460
+ ...publishedEntities
461
+ ];
462
+ const publishedEntityIds = new Set(publishedEntities.map((entity) => entity.sys.id));
463
+ const unpublishedEntities = queue.filter((entity) => !publishedEntityIds.has(entity.sys.id));
464
+ if (unpublishedEntities.length > 0) {
465
+ if (queue.length === unpublishedEntities.length) {
466
+ const unpublishedEntityNames = unpublishedEntities.map(import_get_entity_name3.default).join(", ");
467
+ import_logging5.logEmitter.emit("error", `Could not publish the following entities: ${unpublishedEntityNames}`);
468
+ } else {
469
+ return runQueue(unpublishedEntities, result, requestQueue);
470
+ }
471
+ }
472
+ return result;
473
+ }
474
+
475
+ // lib/tasks/push-to-space/push-to-space.js
476
+ var DEFAULT_CONTENT_STRUCTURE = {
477
+ entries: [],
478
+ assets: [],
479
+ contentTypes: [],
480
+ tags: [],
481
+ locales: [],
482
+ webhooks: [],
483
+ editorInterfaces: []
484
+ };
485
+ function pushToSpace({
486
+ sourceData,
487
+ destinationData = {},
488
+ client,
489
+ spaceId,
490
+ environmentId,
491
+ contentModelOnly,
492
+ skipContentModel,
493
+ skipLocales,
494
+ skipContentPublishing,
495
+ timeout,
496
+ retryLimit,
497
+ listrOptions,
498
+ uploadAssets,
499
+ assetsDirectory,
500
+ requestQueue
501
+ }) {
502
+ sourceData = {
503
+ ...DEFAULT_CONTENT_STRUCTURE,
504
+ ...sourceData
505
+ };
506
+ destinationData = {
507
+ ...DEFAULT_CONTENT_STRUCTURE,
508
+ ...destinationData
509
+ };
510
+ listrOptions = listrOptions || {
511
+ renderer: import_listr_verbose_renderer.default
512
+ };
513
+ const destinationDataById = {};
514
+ for (const [entityType, entities] of Object.entries(destinationData)) {
515
+ const entitiesById = /* @__PURE__ */ new Map();
516
+ for (const entity of entities) {
517
+ entitiesById.set(entity.sys.id, entity);
518
+ }
519
+ destinationDataById[entityType] = entitiesById;
520
+ }
521
+ return new import_listr.default([
522
+ {
523
+ title: "Connecting to space",
524
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
525
+ const space = await client.getSpace(spaceId);
526
+ const environment = await space.getEnvironment(environmentId);
527
+ ctx.space = space;
528
+ ctx.environment = environment;
529
+ })
530
+ },
531
+ {
532
+ title: "Importing Locales",
533
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
534
+ const locales2 = await createLocales({
535
+ context: { target: ctx.environment, type: "Locale" },
536
+ entities: sourceData.locales,
537
+ destinationEntitiesById: destinationDataById.locales,
538
+ requestQueue
539
+ });
540
+ ctx.data.locales = locales2;
541
+ }),
542
+ skip: () => skipContentModel || skipLocales
543
+ },
544
+ {
545
+ title: "Importing Content Types",
546
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
547
+ const contentTypes2 = await createEntities({
548
+ context: { target: ctx.environment, type: "ContentType" },
549
+ entities: sourceData.contentTypes,
550
+ destinationEntitiesById: destinationDataById.contentTypes,
551
+ requestQueue
552
+ });
553
+ ctx.data.contentTypes = contentTypes2;
554
+ }),
555
+ skip: () => skipContentModel
556
+ },
557
+ {
558
+ title: "Publishing Content Types",
559
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
560
+ const publishedContentTypes = await publishEntities2({
561
+ entities: ctx.data.contentTypes,
562
+ sourceEntities: sourceData.contentTypes,
563
+ requestQueue
564
+ });
565
+ ctx.data.contentTypes = publishedContentTypes;
566
+ }),
567
+ skip: (ctx) => skipContentModel
568
+ },
569
+ {
570
+ title: "Importing Tags",
571
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
572
+ const tags2 = await createEntities({
573
+ context: { target: ctx.environment, type: "Tag" },
574
+ entities: sourceData.tags,
575
+ destinationEntitiesById: destinationDataById.tags,
576
+ requestQueue
577
+ });
578
+ ctx.data.tags = tags2;
579
+ }),
580
+ // we remove `tags` from destination data if an error was thrown trying to access them
581
+ // this means the user doesn't have access to this feature, skip importing tags
582
+ skip: () => !destinationDataById.tags
583
+ },
584
+ {
585
+ title: "Importing Editor Interfaces",
586
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
587
+ const allEditorInterfacesBeingFetched = ctx.data.contentTypes.map(async (contentType) => {
588
+ const editorInterface = sourceData.editorInterfaces.find((editorInterface2) => {
589
+ return editorInterface2.sys.contentType.sys.id === contentType.sys.id;
590
+ });
591
+ if (!editorInterface) {
592
+ return;
593
+ }
594
+ try {
595
+ const ctEditorInterface = await requestQueue.add(() => ctx.environment.getEditorInterfaceForContentType(contentType.sys.id));
596
+ import_logging6.logEmitter.emit("info", `Fetched editor interface for ${contentType.name}`);
597
+ ctEditorInterface.controls = editorInterface.controls;
598
+ ctEditorInterface.groupControls = editorInterface.groupControls;
599
+ ctEditorInterface.editorLayout = editorInterface.editorLayout;
600
+ const updatedEditorInterface = await requestQueue.add(() => ctEditorInterface.update());
601
+ return updatedEditorInterface;
602
+ } catch (err) {
603
+ err.entity = editorInterface;
604
+ throw err;
605
+ }
606
+ });
607
+ const allEditorInterfaces = await Promise.all(allEditorInterfacesBeingFetched);
608
+ const editorInterfaces = allEditorInterfaces.filter((editorInterface) => editorInterface);
609
+ ctx.data.editorInterfaces = editorInterfaces;
610
+ }),
611
+ skip: (ctx) => skipContentModel || ctx.data.contentTypes.length === 0
612
+ },
613
+ {
614
+ title: "Uploading Assets",
615
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
616
+ const allPendingUploads = [];
617
+ for (const asset of sourceData.assets) {
618
+ for (const file of Object.values(asset.transformed.fields.file)) {
619
+ allPendingUploads.push(requestQueue.add(async () => {
620
+ try {
621
+ import_logging6.logEmitter.emit("info", `Uploading Asset file ${file.upload}`);
622
+ const assetStream = await getAssetStreamForURL(file.upload, assetsDirectory);
623
+ const upload = await ctx.environment.createUpload({
624
+ fileName: asset.transformed.sys.id,
625
+ file: assetStream
116
626
  });
117
- }
118
- }
119
- ], listrOptions);
120
- return tasks.run({
121
- data: {}
122
- })
123
- .then((ctx) => {
124
- console.log('Finished importing all data');
125
- const resultTypes = Object.keys(ctx.data);
126
- if (resultTypes.length) {
127
- const resultTable = new cli_table3_1.default();
128
- resultTable.push([{ colSpan: 2, content: 'Imported entities' }]);
129
- resultTypes.forEach((type) => {
130
- resultTable.push([(0, lodash_1.startCase)(type), ctx.data[type].length]);
131
- });
132
- console.log(resultTable.toString());
133
- }
134
- else {
135
- console.log('No data was imported');
627
+ delete file.upload;
628
+ file.uploadFrom = {
629
+ sys: {
630
+ type: "Link",
631
+ linkType: "Upload",
632
+ id: upload.sys.id
633
+ }
634
+ };
635
+ return upload;
636
+ } catch (err) {
637
+ import_logging6.logEmitter.emit("error", err);
638
+ }
639
+ }));
640
+ }
136
641
  }
137
- const endTime = new Date();
138
- const durationHuman = (0, formatDistance_1.default)(endTime, options.startTime);
139
- const durationSeconds = (0, differenceInSeconds_1.default)(endTime, options.startTime);
140
- console.log(`The import took ${durationHuman} (${durationSeconds}s)`);
141
- return ctx.data;
142
- })
143
- .catch((err) => {
144
- log.push({
145
- ts: (new Date()).toJSON(),
146
- level: 'error',
147
- error: err
642
+ const uploads = await Promise.all(allPendingUploads);
643
+ ctx.data.uploadedAssetFiles = uploads;
644
+ }),
645
+ skip: (ctx) => !uploadAssets || !sourceData.assets.length
646
+ },
647
+ {
648
+ title: "Importing Assets",
649
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
650
+ const assetsToProcess = await createEntities({
651
+ context: { target: ctx.environment, type: "Asset" },
652
+ entities: sourceData.assets,
653
+ destinationEntitiesById: destinationDataById.assets,
654
+ requestQueue
148
655
  });
149
- })
150
- .then((data) => {
151
- const errorLog = log.filter((logMessage) => logMessage.level !== 'info' && logMessage.level !== 'warning');
152
- const displayLog = log.filter((logMessage) => logMessage.level !== 'info');
153
- (0, logging_1.displayErrorLog)(displayLog);
154
- if (errorLog.length) {
155
- return (0, logging_1.writeErrorLogFile)(options.errorLogFile, errorLog)
156
- .then(() => {
157
- const multiError = new Error('Errors occurred');
158
- multiError.name = 'ContentfulMultiError';
159
- multiError.errors = errorLog;
160
- throw multiError;
161
- });
162
- }
163
- console.log('The import was successful.');
164
- return data;
656
+ const processedAssets = await processAssets({
657
+ assets: assetsToProcess,
658
+ timeout,
659
+ retryLimit,
660
+ requestQueue
661
+ });
662
+ ctx.data.assets = processedAssets;
663
+ }),
664
+ skip: (ctx) => contentModelOnly
665
+ },
666
+ {
667
+ title: "Publishing Assets",
668
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
669
+ const publishedAssets = await publishEntities2({
670
+ entities: ctx.data.assets,
671
+ sourceEntities: sourceData.assets,
672
+ requestQueue
673
+ });
674
+ ctx.data.publishedAssets = publishedAssets;
675
+ }),
676
+ skip: (ctx) => contentModelOnly || skipContentPublishing
677
+ },
678
+ {
679
+ title: "Archiving Assets",
680
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
681
+ const archivedAssets = await archiveEntities2({
682
+ entities: ctx.data.assets,
683
+ sourceEntities: sourceData.assets,
684
+ requestQueue
685
+ });
686
+ ctx.data.archivedAssets = archivedAssets;
687
+ }),
688
+ skip: (ctx) => contentModelOnly || skipContentPublishing
689
+ },
690
+ {
691
+ title: "Importing Content Entries",
692
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
693
+ const entries2 = await createEntries({
694
+ context: { target: ctx.environment, skipContentModel },
695
+ entities: sourceData.entries,
696
+ destinationEntitiesById: destinationDataById.entries,
697
+ requestQueue
698
+ });
699
+ ctx.data.entries = entries2;
700
+ }),
701
+ skip: (ctx) => contentModelOnly
702
+ },
703
+ {
704
+ title: "Publishing Content Entries",
705
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
706
+ const publishedEntries = await publishEntities2({
707
+ entities: ctx.data.entries,
708
+ sourceEntities: sourceData.entries,
709
+ requestQueue
710
+ });
711
+ ctx.data.publishedEntries = publishedEntries;
712
+ }),
713
+ skip: (ctx) => contentModelOnly || skipContentPublishing
714
+ },
715
+ {
716
+ title: "Archiving Entries",
717
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
718
+ const archivedEntries = await archiveEntities2({
719
+ entities: ctx.data.entries,
720
+ sourceEntities: sourceData.entries,
721
+ requestQueue
722
+ });
723
+ ctx.data.archivedEntries = archivedEntries;
724
+ }),
725
+ skip: (ctx) => contentModelOnly || skipContentPublishing
726
+ },
727
+ {
728
+ title: "Creating Web Hooks",
729
+ task: (0, import_listr2.wrapTask)(async (ctx, task) => {
730
+ const webhooks2 = await createEntities({
731
+ context: { target: ctx.space, type: "Webhook" },
732
+ entities: sourceData.webhooks,
733
+ destinationEntitiesById: destinationDataById.webhooks,
734
+ requestQueue
735
+ });
736
+ ctx.data.webhooks = webhooks2;
737
+ }),
738
+ skip: (ctx) => contentModelOnly || environmentId !== "master" && "Webhooks can only be imported in master environment"
739
+ }
740
+ ], listrOptions);
741
+ }
742
+ function archiveEntities2({ entities, sourceEntities, requestQueue }) {
743
+ const entityIdsToArchive = sourceEntities.filter(({ original }) => original.sys.archivedVersion).map(({ original }) => original.sys.id);
744
+ const entitiesToArchive = entities.filter((entity) => entityIdsToArchive.indexOf(entity.sys.id) !== -1);
745
+ return archiveEntities({ entities: entitiesToArchive, requestQueue });
746
+ }
747
+ function publishEntities2({ entities, sourceEntities, requestQueue }) {
748
+ const entityIdsToPublish = sourceEntities.filter(({ original }) => original.sys.publishedVersion).map(({ original }) => original.sys.id);
749
+ const entitiesToPublish = entities.filter((entity) => entityIdsToPublish.indexOf(entity.sys.id) !== -1);
750
+ return publishEntities({ entities: entitiesToPublish, requestQueue });
751
+ }
752
+
753
+ // lib/transform/transform-space.js
754
+ var import_object3 = require("lodash/object");
755
+
756
+ // lib/transform/transformers.js
757
+ var transformers_exports = {};
758
+ __export(transformers_exports, {
759
+ assets: () => assets,
760
+ contentTypes: () => contentTypes,
761
+ entries: () => entries,
762
+ locales: () => locales,
763
+ tags: () => tags,
764
+ webhooks: () => webhooks
765
+ });
766
+ var import_object2 = require("lodash/object");
767
+ var import_collection2 = require("lodash/collection");
768
+ function contentTypes(contentType) {
769
+ return contentType;
770
+ }
771
+ function tags(tag) {
772
+ return tag;
773
+ }
774
+ function entries(entry, _, tagsEnabled = false) {
775
+ return removeMetadataTags(entry, tagsEnabled);
776
+ }
777
+ function webhooks(webhook) {
778
+ if (webhook.httpBasicUsername) {
779
+ delete webhook.httpBasicUsername;
780
+ }
781
+ if (webhook.headers) {
782
+ webhook.headers = webhook.headers.filter((header) => !header.secret);
783
+ }
784
+ return webhook;
785
+ }
786
+ function assets(asset, _, tagsEnabled = false) {
787
+ const transformedAsset = (0, import_object2.omit)(asset, "sys");
788
+ transformedAsset.sys = (0, import_object2.pick)(asset.sys, "id");
789
+ transformedAsset.fields = (0, import_object2.pick)(asset.fields, "title", "description");
790
+ transformedAsset.fields.file = (0, import_collection2.reduce)(
791
+ asset.fields.file,
792
+ (newFile, localizedFile, locale) => {
793
+ newFile[locale] = (0, import_object2.pick)(localizedFile, "contentType", "fileName");
794
+ if (!localizedFile.uploadFrom) {
795
+ const assetUrl = localizedFile.url || localizedFile.upload;
796
+ newFile[locale].upload = `${/^(http|https):\/\//i.test(assetUrl) ? "" : "https:"}${assetUrl}`;
797
+ } else {
798
+ newFile[locale].uploadFrom = localizedFile.uploadFrom;
799
+ }
800
+ return newFile;
801
+ },
802
+ {}
803
+ );
804
+ return removeMetadataTags(transformedAsset, tagsEnabled);
805
+ }
806
+ function locales(locale, destinationLocales) {
807
+ const transformedLocale = (0, import_object2.pick)(locale, "code", "name", "contentManagementApi", "contentDeliveryApi", "fallbackCode", "optional");
808
+ const destinationLocale = (0, import_collection2.find)(destinationLocales, { code: locale.code });
809
+ if (destinationLocale) {
810
+ transformedLocale.sys = (0, import_object2.pick)(destinationLocale.sys, "id");
811
+ }
812
+ return transformedLocale;
813
+ }
814
+ function removeMetadataTags(entity, tagsEnabled = false) {
815
+ if (!tagsEnabled) {
816
+ delete entity.metadata;
817
+ }
818
+ return entity;
819
+ }
820
+
821
+ // lib/utils/sort-entries.js
822
+ var import_collection3 = require("lodash/collection");
823
+ var _o = __toESM(require("lodash/object"));
824
+ var import_array = require("lodash/array");
825
+ function sortEntries(entries2) {
826
+ const linkedEntries = getLinkedEntries(entries2);
827
+ const mergedLinkedEntries = mergeSort(linkedEntries, (a) => {
828
+ return hasLinkedIndexesInFront(a);
829
+ });
830
+ return (0, import_collection3.map)(mergedLinkedEntries, (linkInfo) => entries2[linkInfo.index]);
831
+ function hasLinkedIndexesInFront(item) {
832
+ if (hasLinkedIndexes(item)) {
833
+ return (0, import_collection3.some)(item.linkIndexes, (index) => index > item.index) ? 1 : -1;
834
+ }
835
+ return 0;
836
+ }
837
+ function hasLinkedIndexes(item) {
838
+ return item.linkIndexes.length > 0;
839
+ }
840
+ }
841
+ function getLinkedEntries(entries2) {
842
+ return (0, import_collection3.map)(entries2, function(entry) {
843
+ const entryIndex = entries2.indexOf(entry);
844
+ const rawLinks = (0, import_collection3.map)(entry.fields, (field) => {
845
+ field = _o.values(field)[0];
846
+ if (isEntryLink(field)) {
847
+ return getFieldEntriesIndex(field, entries2);
848
+ } else if (isEntityArray(field) && isEntryLink(field[0])) {
849
+ return (0, import_collection3.map)(field, (item) => getFieldEntriesIndex(item, entries2));
850
+ }
851
+ });
852
+ return {
853
+ index: entryIndex,
854
+ linkIndexes: (0, import_collection3.filter)((0, import_array.flatten)(rawLinks), (index) => index >= 0)
855
+ };
856
+ });
857
+ }
858
+ function getFieldEntriesIndex(field, entries2) {
859
+ const id = _o.get(field, "sys.id");
860
+ return entries2.findIndex((entry) => entry.sys.id === id);
861
+ }
862
+ function isEntryLink(item) {
863
+ return _o.get(item, "sys.type") === "Entry" || _o.get(item, "sys.linkType") === "Entry";
864
+ }
865
+ function isEntityArray(item) {
866
+ return Array.isArray(item) && item.length > 0 && _o.has(item[0], "sys");
867
+ }
868
+ function mergeSort(arr, compareFn) {
869
+ if (arr.length < 2)
870
+ return arr;
871
+ if (compareFn == null)
872
+ compareFn = defaultCompare;
873
+ const mid = ~~(arr.length / 2);
874
+ const left = mergeSort(arr.slice(0, mid), compareFn);
875
+ const right = mergeSort(arr.slice(mid, arr.length), compareFn);
876
+ return merge(left, right, compareFn);
877
+ }
878
+ function defaultCompare(a, b) {
879
+ return a < b ? -1 : a > b ? 1 : 0;
880
+ }
881
+ function merge(left, right, compareFn) {
882
+ const result = [];
883
+ while (left.length && right.length) {
884
+ if (compareFn(left[0], right[0]) <= 0) {
885
+ result.push(left.shift());
886
+ } else {
887
+ result.push(right.shift());
888
+ }
889
+ }
890
+ if (left.length)
891
+ result.push.apply(result, left);
892
+ if (right.length)
893
+ result.push.apply(result, right);
894
+ return result;
895
+ }
896
+
897
+ // lib/utils/sort-locales.js
898
+ function sortLocales(locales2) {
899
+ const localeByFallback = {};
900
+ locales2.forEach((locale) => {
901
+ if (locale.fallbackCode === null) {
902
+ locale.fallbackCode = void 0;
903
+ }
904
+ if (!localeByFallback[locale.fallbackCode]) {
905
+ localeByFallback[locale.fallbackCode] = [];
906
+ }
907
+ localeByFallback[locale.fallbackCode].push(locale);
908
+ });
909
+ return sortByFallbackKey(localeByFallback);
910
+ }
911
+ function sortByFallbackKey(localeByFallback, key) {
912
+ if (!localeByFallback[key]) {
913
+ return [];
914
+ }
915
+ const sortedLocales = localeByFallback[key];
916
+ sortedLocales.forEach((locale) => {
917
+ sortByFallbackKey(localeByFallback, locale.code).forEach((x) => sortedLocales.push(x));
918
+ });
919
+ sortedLocales.forEach((locale) => {
920
+ if (!locale.fallbackCode) {
921
+ locale.fallbackCode = null;
922
+ }
923
+ });
924
+ return sortedLocales;
925
+ }
926
+
927
+ // lib/transform/transform-space.js
928
+ var spaceEntities = [
929
+ "contentTypes",
930
+ "entries",
931
+ "assets",
932
+ "locales",
933
+ "webhooks",
934
+ "tags"
935
+ ];
936
+ function transform_space_default(sourceData, destinationData, customTransformers, entities = spaceEntities) {
937
+ const transformers = (0, import_object3.defaults)(customTransformers, transformers_exports);
938
+ const baseSpaceData = (0, import_object3.omit)(sourceData, ...entities);
939
+ sourceData.locales = sortLocales(sourceData.locales);
940
+ const tagsEnabled = !!destinationData.tags;
941
+ return entities.reduce((transformedSpaceData, type) => {
942
+ const sortedEntities = type === "tags" ? sourceData[type] : sortEntries(sourceData[type]);
943
+ const transformedEntities = sortedEntities.map((entity) => ({
944
+ original: entity,
945
+ transformed: transformers[type](entity, destinationData[type], tagsEnabled)
946
+ }));
947
+ transformedSpaceData[type] = transformedEntities;
948
+ return transformedSpaceData;
949
+ }, baseSpaceData);
950
+ }
951
+
952
+ // lib/utils/schema.js
953
+ var import_joi = __toESM(require("joi"));
954
+ var entrySchema = {
955
+ sys: import_joi.default.object(),
956
+ fields: import_joi.default.object()
957
+ };
958
+ var tagSchema = {
959
+ name: import_joi.default.string().required(),
960
+ sys: import_joi.default.object()
961
+ };
962
+ var contentTypeSchema = {
963
+ sys: import_joi.default.object(),
964
+ fields: import_joi.default.array().required().items(import_joi.default.object().keys({
965
+ id: import_joi.default.string().required(),
966
+ name: import_joi.default.string().required(),
967
+ type: import_joi.default.string().required().regex(/^Symbol|Text|Integer|Number|Date|Object|Boolean|Array|Link|Location$/),
968
+ validations: import_joi.default.array(),
969
+ disabled: import_joi.default.boolean(),
970
+ omitted: import_joi.default.boolean(),
971
+ required: import_joi.default.boolean(),
972
+ localized: import_joi.default.boolean(),
973
+ linkType: import_joi.default.string().when("type", { is: "Link", then: import_joi.default.string().regex(/^Asset|Entry$/), otherwise: import_joi.default.forbidden() })
974
+ }))
975
+ };
976
+ var assetSchema = {
977
+ sys: import_joi.default.object(),
978
+ fields: import_joi.default.object({
979
+ file: import_joi.default.object().pattern(/.+/, import_joi.default.object({
980
+ url: import_joi.default.string().required(),
981
+ details: import_joi.default.object({
982
+ size: import_joi.default.number(),
983
+ image: import_joi.default.object({
984
+ width: import_joi.default.number(),
985
+ height: import_joi.default.number()
986
+ })
987
+ }),
988
+ fileName: import_joi.default.string().required(),
989
+ contentType: import_joi.default.string().required()
990
+ }))
991
+ }).required()
992
+ };
993
+ var editorInterfaceSchema = {
994
+ sys: import_joi.default.object(),
995
+ controls: import_joi.default.array().items({ fieldId: import_joi.default.string(), widgetId: import_joi.default.string() })
996
+ };
997
+ var localeSchema = {
998
+ name: import_joi.default.string().required(),
999
+ internal_code: import_joi.default.string(),
1000
+ code: import_joi.default.string().required(),
1001
+ fallbackCode: import_joi.default.string().allow(null),
1002
+ default: import_joi.default.boolean(),
1003
+ contentManagementApi: import_joi.default.boolean(),
1004
+ contentDeliveryApi: import_joi.default.boolean(),
1005
+ optional: import_joi.default.boolean(),
1006
+ sys: import_joi.default.object()
1007
+ };
1008
+ var webhookSchema = {
1009
+ name: import_joi.default.string(),
1010
+ url: import_joi.default.string().replace(/{[^}{]+?}/g, "x").regex(/^https?:\/\/[^ /}{][^ }{]*$/i).required(),
1011
+ topics: import_joi.default.array().required(),
1012
+ httpBasicUsername: import_joi.default.string().allow("", null)
1013
+ };
1014
+ var payloadSchema = import_joi.default.object({
1015
+ entries: import_joi.default.array().items(entrySchema),
1016
+ contentTypes: import_joi.default.array().items(contentTypeSchema),
1017
+ tags: import_joi.default.array().items(tagSchema),
1018
+ assets: import_joi.default.array().items(assetSchema),
1019
+ locales: import_joi.default.array().items(localeSchema),
1020
+ editorInterfaces: import_joi.default.array().items(editorInterfaceSchema),
1021
+ webhooks: import_joi.default.array().items(webhookSchema)
1022
+ });
1023
+
1024
+ // lib/utils/validations.js
1025
+ var import_get_entity_name4 = __toESM(require("contentful-batch-libs/dist/get-entity-name"));
1026
+ var attachEntityName = (details, payload) => {
1027
+ details.map((detail) => {
1028
+ if (detail.path.length >= 2) {
1029
+ detail.entity = (0, import_get_entity_name4.default)(payload[detail.path[0]][detail.path[1]]);
1030
+ }
1031
+ return detail;
1032
+ });
1033
+ };
1034
+ var countInvalidEntities = (validationData) => {
1035
+ const entityCount = validationData.reduce((entities, currentDetail) => {
1036
+ if (!entities[currentDetail.path[0]]) {
1037
+ entities[currentDetail.path[0]] = 1;
1038
+ } else {
1039
+ entities[currentDetail.path[0]]++;
1040
+ }
1041
+ return entities;
1042
+ }, {});
1043
+ return Object.keys(entityCount).map((key) => `${key}:${entityCount[key]}`);
1044
+ };
1045
+ var assertPayload = (payload) => {
1046
+ const result = payloadSchema.validate(payload, { allowUnknown: true, abortEarly: false });
1047
+ if (result.error) {
1048
+ attachEntityName(result.error.details, payload);
1049
+ const invalidEntityCount = countInvalidEntities(result.error.details).join(", ");
1050
+ result.error.message = `${invalidEntityCount} - Get further details in the error log file`;
1051
+ delete result.error._object;
1052
+ throw result.error;
1053
+ }
1054
+ };
1055
+ var assertDefaultLocale = (source, destination) => {
1056
+ const sourceDefaultLocale = source.locales.find((locale) => locale.default === true);
1057
+ const destinationDefaultLocale = destination.locales.find((locale) => locale.default === true);
1058
+ if (!sourceDefaultLocale || !destinationDefaultLocale) {
1059
+ return;
1060
+ }
1061
+ if (sourceDefaultLocale.code !== destinationDefaultLocale.code) {
1062
+ throw new Error(`
1063
+ Please make sure the destination space have the same default locale as the source
1064
+
1065
+ Default locale for source space : ${sourceDefaultLocale.code}
1066
+
1067
+ Default locale for destination space: ${destinationDefaultLocale.code}
1068
+
1069
+ `);
1070
+ }
1071
+ };
1072
+
1073
+ // lib/parseOptions.js
1074
+ var import_fs2 = __toESM(require("fs"));
1075
+ var import_path2 = require("path");
1076
+ var import_format = __toESM(require("date-fns/format"));
1077
+
1078
+ // package.json
1079
+ var version = "0.0.0-determined-by-semantic-release";
1080
+
1081
+ // lib/utils/headers.js
1082
+ function getHeadersConfig(value) {
1083
+ if (!value) {
1084
+ return {};
1085
+ }
1086
+ const values2 = Array.isArray(value) ? value : [value];
1087
+ return values2.reduce((headers, value2) => {
1088
+ value2 = value2.trim();
1089
+ const separatorIndex = value2.indexOf(":");
1090
+ if (separatorIndex === -1) {
1091
+ return headers;
1092
+ }
1093
+ const headerKey = value2.slice(0, separatorIndex).trim();
1094
+ const headerValue = value2.slice(separatorIndex + 1).trim();
1095
+ return {
1096
+ ...headers,
1097
+ [headerKey]: headerValue
1098
+ };
1099
+ }, {});
1100
+ }
1101
+
1102
+ // lib/parseOptions.js
1103
+ var import_proxy = require("contentful-batch-libs/dist/proxy");
1104
+ var import_add_sequence_header = __toESM(require("contentful-batch-libs/dist/add-sequence-header"));
1105
+ var import_json_ext = require("@discoveryjs/json-ext");
1106
+ var SUPPORTED_ENTITY_TYPES = [
1107
+ "contentTypes",
1108
+ "tags",
1109
+ "entries",
1110
+ "assets",
1111
+ "locales",
1112
+ "webhooks",
1113
+ "editorInterfaces"
1114
+ ];
1115
+ async function parseOptions(params) {
1116
+ const defaultOptions = {
1117
+ skipContentModel: false,
1118
+ skipLocales: false,
1119
+ skipContentPublishing: false,
1120
+ useVerboseRenderer: false,
1121
+ environmentId: "master",
1122
+ rawProxy: false,
1123
+ uploadAssets: false,
1124
+ rateLimit: 7
1125
+ };
1126
+ const configFile = params.config ? require((0, import_path2.resolve)(process.cwd(), params.config)) : {};
1127
+ const options = {
1128
+ ...defaultOptions,
1129
+ ...configFile,
1130
+ ...params,
1131
+ headers: (0, import_add_sequence_header.default)(params.headers || getHeadersConfig(params.header))
1132
+ };
1133
+ if (!options.spaceId) {
1134
+ throw new Error("The `spaceId` option is required.");
1135
+ }
1136
+ if (!options.managementToken) {
1137
+ throw new Error("The `managementToken` option is required.");
1138
+ }
1139
+ if (!options.contentFile && !options.content) {
1140
+ throw new Error("Either the `contentFile` or `content` option are required.");
1141
+ }
1142
+ if (options.contentModelOnly && options.skipContentModel) {
1143
+ throw new Error("`contentModelOnly` and `skipContentModel` cannot be used together");
1144
+ }
1145
+ if (options.skipLocales && !options.contentModelOnly) {
1146
+ throw new Error("`skipLocales` can only be used together with `contentModelOnly`");
1147
+ }
1148
+ const proxySimpleExp = /.+:\d+/;
1149
+ const proxyAuthExp = /.+:.+@.+:\d+/;
1150
+ if (typeof options.proxy === "string" && options.proxy && !(proxySimpleExp.test(options.proxy) || proxyAuthExp.test(options.proxy))) {
1151
+ throw new Error("Please provide the proxy config in the following format:\nhost:port or user:password@host:port");
1152
+ }
1153
+ options.startTime = /* @__PURE__ */ new Date();
1154
+ if (!options.errorLogFile) {
1155
+ options.errorLogFile = (0, import_path2.resolve)(process.cwd(), `contentful-import-error-log-${options.spaceId}-${(0, import_format.default)(options.startTime, "yyyy-MM-dd'T'HH-mm-ss")}.json`);
1156
+ } else {
1157
+ options.errorLogFile = (0, import_path2.resolve)(process.cwd(), options.errorLogFile);
1158
+ }
1159
+ options.accessToken = options.managementToken;
1160
+ if (!options.content) {
1161
+ const fileStream = import_fs2.default.createReadStream(options.contentFile, { encoding: "utf8" });
1162
+ options.content = await (0, import_json_ext.parseChunked)(fileStream);
1163
+ }
1164
+ Object.keys(options.content).forEach((type) => {
1165
+ if (SUPPORTED_ENTITY_TYPES.indexOf(type) === -1) {
1166
+ delete options.content[type];
1167
+ }
1168
+ });
1169
+ SUPPORTED_ENTITY_TYPES.forEach((type) => {
1170
+ options.content[type] = options.content[type] || [];
1171
+ });
1172
+ if (typeof options.proxy === "string") {
1173
+ options.proxy = (0, import_proxy.proxyStringToObject)(options.proxy);
1174
+ }
1175
+ if (!options.rawProxy && options.proxy) {
1176
+ options.httpsAgent = (0, import_proxy.agentFromProxy)(options.proxy);
1177
+ delete options.proxy;
1178
+ }
1179
+ options.application = options.managementApplication || `contentful.import/${version}`;
1180
+ options.feature = options.managementFeature || "library-import";
1181
+ return options;
1182
+ }
1183
+
1184
+ // lib/index.ts
1185
+ var ONE_SECOND = 1e3;
1186
+ function createListrOptions(options) {
1187
+ if (options.useVerboseRenderer) {
1188
+ return {
1189
+ renderer: import_listr_verbose_renderer2.default
1190
+ };
1191
+ }
1192
+ return {
1193
+ renderer: import_listr_update_renderer.default,
1194
+ collapse: false
1195
+ };
1196
+ }
1197
+ async function runContentfulImport(params) {
1198
+ const log = [];
1199
+ const options = await parseOptions(params);
1200
+ const listrOptions = createListrOptions(options);
1201
+ const requestQueue = new import_p_queue.default({
1202
+ interval: ONE_SECOND,
1203
+ intervalCap: options.rateLimit,
1204
+ carryoverConcurrencyCount: true
1205
+ });
1206
+ (0, import_logging7.setupLogging)(log);
1207
+ const infoTable = new import_cli_table3.default();
1208
+ infoTable.push([{ colSpan: 2, content: "The following entities are going to be imported:" }]);
1209
+ Object.keys(options.content).forEach((type) => {
1210
+ if (options.skipLocales && type === "locales") {
1211
+ return;
1212
+ }
1213
+ if (options.skipContentModel && ["contentTypes", "editorInterfaces"].indexOf(type) >= 0) {
1214
+ return;
1215
+ }
1216
+ if (options.contentModelOnly && !(["contentTypes", "editorInterfaces", "locales"].indexOf(type) >= 0)) {
1217
+ return;
1218
+ }
1219
+ infoTable.push([(0, import_lodash.startCase)(type), options.content[type].length]);
1220
+ });
1221
+ console.log(infoTable.toString());
1222
+ const tasks = new import_listr3.default([
1223
+ {
1224
+ title: "Validating content-file",
1225
+ task: (ctx) => {
1226
+ assertPayload(options.content);
1227
+ }
1228
+ },
1229
+ {
1230
+ title: "Initialize client",
1231
+ task: (0, import_listr4.wrapTask)(async (ctx) => {
1232
+ ctx.client = initClient({ ...options, content: void 0 });
1233
+ })
1234
+ },
1235
+ {
1236
+ title: "Checking if destination space already has any content and retrieving it",
1237
+ task: (0, import_listr4.wrapTask)(async (ctx, task) => {
1238
+ const destinationData = await getDestinationData({
1239
+ client: ctx.client,
1240
+ spaceId: options.spaceId,
1241
+ environmentId: options.environmentId,
1242
+ sourceData: options.content,
1243
+ skipLocales: options.skipLocales,
1244
+ skipContentModel: options.skipContentModel,
1245
+ requestQueue
1246
+ });
1247
+ ctx.sourceDataUntransformed = options.content;
1248
+ ctx.destinationData = destinationData;
1249
+ assertDefaultLocale(ctx.sourceDataUntransformed, ctx.destinationData);
1250
+ })
1251
+ },
1252
+ {
1253
+ title: "Apply transformations to source data",
1254
+ task: (0, import_listr4.wrapTask)(async (ctx) => {
1255
+ const transformedSourceData = transform_space_default(ctx.sourceDataUntransformed, ctx.destinationData);
1256
+ ctx.sourceData = transformedSourceData;
1257
+ })
1258
+ },
1259
+ {
1260
+ title: "Push content to destination space",
1261
+ task: (ctx, task) => {
1262
+ return pushToSpace({
1263
+ sourceData: ctx.sourceData,
1264
+ destinationData: ctx.destinationData,
1265
+ client: ctx.client,
1266
+ spaceId: options.spaceId,
1267
+ environmentId: options.environmentId,
1268
+ contentModelOnly: options.contentModelOnly,
1269
+ skipLocales: options.skipLocales,
1270
+ skipContentModel: options.skipContentModel,
1271
+ skipContentPublishing: options.skipContentPublishing,
1272
+ timeout: options.timeout,
1273
+ retryLimit: options.retryLimit,
1274
+ uploadAssets: options.uploadAssets,
1275
+ assetsDirectory: options.assetsDirectory,
1276
+ listrOptions,
1277
+ requestQueue
1278
+ });
1279
+ }
1280
+ }
1281
+ ], listrOptions);
1282
+ return tasks.run({
1283
+ data: {}
1284
+ }).then((ctx) => {
1285
+ console.log("Finished importing all data");
1286
+ const resultTypes = Object.keys(ctx.data);
1287
+ if (resultTypes.length) {
1288
+ const resultTable = new import_cli_table3.default();
1289
+ resultTable.push([{ colSpan: 2, content: "Imported entities" }]);
1290
+ resultTypes.forEach((type) => {
1291
+ resultTable.push([(0, import_lodash.startCase)(type), ctx.data[type].length]);
1292
+ });
1293
+ console.log(resultTable.toString());
1294
+ } else {
1295
+ console.log("No data was imported");
1296
+ }
1297
+ const endTime = /* @__PURE__ */ new Date();
1298
+ const durationHuman = (0, import_formatDistance.default)(endTime, options.startTime);
1299
+ const durationSeconds = (0, import_differenceInSeconds.default)(endTime, options.startTime);
1300
+ console.log(`The import took ${durationHuman} (${durationSeconds}s)`);
1301
+ return ctx.data;
1302
+ }).catch((err) => {
1303
+ log.push({
1304
+ ts: (/* @__PURE__ */ new Date()).toJSON(),
1305
+ level: "error",
1306
+ error: err
165
1307
  });
1308
+ }).then((data) => {
1309
+ const errorLog = log.filter((logMessage) => logMessage.level !== "info" && logMessage.level !== "warning");
1310
+ const displayLog = log.filter((logMessage) => logMessage.level !== "info");
1311
+ (0, import_logging7.displayErrorLog)(displayLog);
1312
+ if (errorLog.length) {
1313
+ return (0, import_logging7.writeErrorLogFile)(options.errorLogFile, errorLog).then(() => {
1314
+ const multiError = new Error("Errors occurred");
1315
+ multiError.name = "ContentfulMultiError";
1316
+ multiError.errors = errorLog;
1317
+ throw multiError;
1318
+ });
1319
+ }
1320
+ console.log("The import was successful.");
1321
+ return data;
1322
+ });
166
1323
  }
167
- exports.default = runContentfulImport;
1324
+ var lib_default = runContentfulImport;
1325
+ module.exports = runContentfulImport;