@strapi/data-transfer 4.25.17 → 4.25.19

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 (40) hide show
  1. package/dist/commands/data-transfer.d.ts +1 -1
  2. package/dist/commands/data-transfer.d.ts.map +1 -1
  3. package/dist/commands/export/action.d.ts +1 -0
  4. package/dist/commands/export/action.d.ts.map +1 -1
  5. package/dist/commands/export/command.d.ts.map +1 -1
  6. package/dist/commands/import/action.d.ts +1 -0
  7. package/dist/commands/import/action.d.ts.map +1 -1
  8. package/dist/commands/import/command.d.ts.map +1 -1
  9. package/dist/commands/transfer/action.d.ts +1 -0
  10. package/dist/commands/transfer/action.d.ts.map +1 -1
  11. package/dist/commands/transfer/command.d.ts.map +1 -1
  12. package/dist/engine/index.d.ts +1 -1
  13. package/dist/engine/index.d.ts.map +1 -1
  14. package/dist/errors/constants.d.ts +1 -1
  15. package/dist/errors/constants.d.ts.map +1 -1
  16. package/dist/file/providers/destination/index.d.ts +2 -1
  17. package/dist/file/providers/destination/index.d.ts.map +1 -1
  18. package/dist/file/providers/source/index.d.ts.map +1 -1
  19. package/dist/index.js +272 -39
  20. package/dist/index.js.map +1 -1
  21. package/dist/index.mjs +273 -40
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/strapi/providers/local-destination/index.d.ts +2 -1
  24. package/dist/strapi/providers/local-destination/index.d.ts.map +1 -1
  25. package/dist/strapi/providers/local-source/index.d.ts +3 -1
  26. package/dist/strapi/providers/local-source/index.d.ts.map +1 -1
  27. package/dist/strapi/providers/remote-destination/index.d.ts +2 -1
  28. package/dist/strapi/providers/remote-destination/index.d.ts.map +1 -1
  29. package/dist/strapi/providers/remote-source/index.d.ts.map +1 -1
  30. package/dist/strapi/providers/utils.d.ts +3 -2
  31. package/dist/strapi/providers/utils.d.ts.map +1 -1
  32. package/dist/strapi/remote/handlers/abstract.d.ts +4 -0
  33. package/dist/strapi/remote/handlers/abstract.d.ts.map +1 -1
  34. package/dist/strapi/remote/handlers/pull.d.ts.map +1 -1
  35. package/dist/strapi/remote/handlers/push.d.ts.map +1 -1
  36. package/dist/strapi/remote/handlers/utils.d.ts.map +1 -1
  37. package/dist/{engine → utils}/diagnostic.d.ts +1 -0
  38. package/dist/utils/diagnostic.d.ts.map +1 -0
  39. package/package.json +6 -6
  40. package/dist/engine/diagnostic.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -21,7 +21,7 @@ import tar$1 from "tar-stream";
21
21
  import { stringer } from "stream-json/jsonl/Stringer";
22
22
  import { Option, InvalidOptionArgumentError } from "commander";
23
23
  import Table from "cli-table3";
24
- import { createLogger, configs } from "@strapi/logger";
24
+ import { createLogger, configs, formats } from "@strapi/logger";
25
25
  import strapiFactory from "@strapi/strapi";
26
26
  import ora from "ora";
27
27
  import inquirer from "inquirer";
@@ -579,7 +579,7 @@ class TransferEngine {
579
579
  reportInfo(message, params) {
580
580
  this.diagnostics.report({
581
581
  kind: "info",
582
- details: { createdAt: /* @__PURE__ */ new Date(), message, params }
582
+ details: { createdAt: /* @__PURE__ */ new Date(), message, params, origin: "engine" }
583
583
  });
584
584
  }
585
585
  /**
@@ -871,8 +871,8 @@ ${formattedDiffs}`,
871
871
  */
872
872
  async bootstrap() {
873
873
  const results = await Promise.allSettled([
874
- this.sourceProvider.bootstrap?.(),
875
- this.destinationProvider.bootstrap?.()
874
+ this.sourceProvider.bootstrap?.(this.diagnostics),
875
+ this.destinationProvider.bootstrap?.(this.diagnostics)
876
876
  ]);
877
877
  results.forEach((result) => {
878
878
  if (result.status === "rejected") {
@@ -1863,6 +1863,7 @@ class LocalStrapiDestinationProvider {
1863
1863
  transaction;
1864
1864
  uploadsBackupDirectoryName;
1865
1865
  onWarning;
1866
+ #diagnostics;
1866
1867
  /**
1867
1868
  * The entities mapper is used to map old entities to their new IDs
1868
1869
  */
@@ -1872,7 +1873,8 @@ class LocalStrapiDestinationProvider {
1872
1873
  this.#entitiesMapper = {};
1873
1874
  this.uploadsBackupDirectoryName = `uploads_backup_${Date.now()}`;
1874
1875
  }
1875
- async bootstrap() {
1876
+ async bootstrap(diagnostics) {
1877
+ this.#diagnostics = diagnostics;
1876
1878
  this.#validateOptions();
1877
1879
  this.strapi = await this.options.getStrapi();
1878
1880
  if (!this.strapi) {
@@ -1889,6 +1891,16 @@ class LocalStrapiDestinationProvider {
1889
1891
  const excluded = this.options.restore?.entities?.exclude && this.options.restore?.entities.exclude.includes(type);
1890
1892
  return !excluded && !notIncluded;
1891
1893
  };
1894
+ #reportInfo(message) {
1895
+ this.#diagnostics?.report({
1896
+ details: {
1897
+ createdAt: /* @__PURE__ */ new Date(),
1898
+ message,
1899
+ origin: "local-destination-provider"
1900
+ },
1901
+ kind: "info"
1902
+ });
1903
+ }
1892
1904
  async close() {
1893
1905
  const { autoDestroy } = this.options;
1894
1906
  this.transaction?.end();
@@ -1897,6 +1909,7 @@ class LocalStrapiDestinationProvider {
1897
1909
  }
1898
1910
  }
1899
1911
  #validateOptions() {
1912
+ this.#reportInfo("validating options");
1900
1913
  if (!VALID_CONFLICT_STRATEGIES.includes(this.options.strategy)) {
1901
1914
  throw new ProviderValidationError(`Invalid strategy ${this.options.strategy}`, {
1902
1915
  check: "strategy",
@@ -1913,10 +1926,12 @@ class LocalStrapiDestinationProvider {
1913
1926
  if (!this.options.restore) {
1914
1927
  throw new ProviderValidationError("Missing restore options");
1915
1928
  }
1929
+ this.#reportInfo("deleting record");
1916
1930
  return deleteRecords(this.strapi, this.options.restore);
1917
1931
  }
1918
1932
  async #deleteAllAssets(trx) {
1919
1933
  assertValidStrapi(this.strapi);
1934
+ this.#reportInfo("deleting all assets");
1920
1935
  if (!this.#areAssetsIncluded()) {
1921
1936
  return;
1922
1937
  }
@@ -1929,9 +1944,12 @@ class LocalStrapiDestinationProvider {
1929
1944
  }
1930
1945
  }
1931
1946
  }
1947
+ this.#reportInfo("deleted all assets");
1932
1948
  }
1933
1949
  async rollback() {
1950
+ this.#reportInfo("Rolling back transaction");
1934
1951
  await this.transaction?.rollback();
1952
+ this.#reportInfo("Rolled back transaction");
1935
1953
  }
1936
1954
  async beforeTransfer() {
1937
1955
  if (!this.strapi) {
@@ -1950,6 +1968,7 @@ class LocalStrapiDestinationProvider {
1950
1968
  });
1951
1969
  }
1952
1970
  getMetadata() {
1971
+ this.#reportInfo("getting metadata");
1953
1972
  assertValidStrapi(this.strapi, "Not able to get Schemas");
1954
1973
  const strapiVersion = this.strapi.config.get("info.strapi");
1955
1974
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -1961,6 +1980,7 @@ class LocalStrapiDestinationProvider {
1961
1980
  };
1962
1981
  }
1963
1982
  getSchemas() {
1983
+ this.#reportInfo("getting schema");
1964
1984
  assertValidStrapi(this.strapi, "Not able to get Schemas");
1965
1985
  const schemas = {
1966
1986
  ...this.strapi.contentTypes,
@@ -1970,6 +1990,7 @@ class LocalStrapiDestinationProvider {
1970
1990
  }
1971
1991
  createEntitiesWriteStream() {
1972
1992
  assertValidStrapi(this.strapi, "Not able to import entities");
1993
+ this.#reportInfo("creating entities stream");
1973
1994
  const { strategy } = this.options;
1974
1995
  const updateMappingTable = (type, oldID, newID) => {
1975
1996
  if (!this.#entitiesMapper[type]) {
@@ -1996,6 +2017,7 @@ class LocalStrapiDestinationProvider {
1996
2017
  return;
1997
2018
  }
1998
2019
  if (this.strapi.config.get("plugin.upload").provider === "local") {
2020
+ this.#reportInfo("creating assets backup directory");
1999
2021
  const assetsDirectory = path.join(this.strapi.dirs.static.public, "uploads");
2000
2022
  const backupDirectory = path.join(
2001
2023
  this.strapi.dirs.static.public,
@@ -2011,6 +2033,7 @@ class LocalStrapiDestinationProvider {
2011
2033
  await fse.move(assetsDirectory, backupDirectory);
2012
2034
  await fse.mkdir(assetsDirectory);
2013
2035
  await fse.outputFile(path.join(assetsDirectory, ".gitkeep"), "");
2036
+ this.#reportInfo(`created assets backup directory ${backupDirectory}`);
2014
2037
  } catch (err) {
2015
2038
  throw new ProviderTransferError(
2016
2039
  "The backup folder for the assets could not be created inside the public folder. Please ensure Strapi has write permissions on the public directory",
@@ -2028,17 +2051,20 @@ class LocalStrapiDestinationProvider {
2028
2051
  return;
2029
2052
  }
2030
2053
  if (this.strapi.config.get("plugin.upload").provider === "local") {
2054
+ this.#reportInfo("removing assets backup");
2031
2055
  assertValidStrapi(this.strapi);
2032
2056
  const backupDirectory = path.join(
2033
2057
  this.strapi.dirs.static.public,
2034
2058
  this.uploadsBackupDirectoryName
2035
2059
  );
2036
2060
  await fse.rm(backupDirectory, { recursive: true, force: true });
2061
+ this.#reportInfo("successfully removed assets backup");
2037
2062
  }
2038
2063
  }
2039
2064
  // TODO: Move this logic to the restore strategy
2040
2065
  async createAssetsWriteStream() {
2041
2066
  assertValidStrapi(this.strapi, "Not able to stream Assets");
2067
+ this.#reportInfo("creating assets write stream");
2042
2068
  if (!this.#areAssetsIncluded()) {
2043
2069
  throw new ProviderTransferError(
2044
2070
  "Attempting to transfer assets when `assets` is not set in restore options"
@@ -2133,6 +2159,7 @@ class LocalStrapiDestinationProvider {
2133
2159
  }
2134
2160
  async createConfigurationWriteStream() {
2135
2161
  assertValidStrapi(this.strapi, "Not able to stream Configurations");
2162
+ this.#reportInfo("creating configuration write stream");
2136
2163
  const { strategy } = this.options;
2137
2164
  if (strategy === "restore") {
2138
2165
  return createConfigurationWriteStream(this.strapi, this.transaction);
@@ -2144,6 +2171,7 @@ class LocalStrapiDestinationProvider {
2144
2171
  });
2145
2172
  }
2146
2173
  async createLinksWriteStream() {
2174
+ this.#reportInfo("creating links write stream");
2147
2175
  if (!this.strapi) {
2148
2176
  throw new Error("Not able to stream links. Strapi instance not found");
2149
2177
  }
@@ -2344,12 +2372,24 @@ class LocalStrapiSourceProvider {
2344
2372
  type = "source";
2345
2373
  options;
2346
2374
  strapi;
2375
+ #diagnostics;
2347
2376
  constructor(options) {
2348
2377
  this.options = options;
2349
2378
  }
2350
- async bootstrap() {
2379
+ async bootstrap(diagnostics) {
2380
+ this.#diagnostics = diagnostics;
2351
2381
  this.strapi = await this.options.getStrapi();
2352
2382
  }
2383
+ #reportInfo(message) {
2384
+ this.#diagnostics?.report({
2385
+ details: {
2386
+ createdAt: /* @__PURE__ */ new Date(),
2387
+ message,
2388
+ origin: "local-source-provider"
2389
+ },
2390
+ kind: "info"
2391
+ });
2392
+ }
2353
2393
  async close() {
2354
2394
  const { autoDestroy } = this.options;
2355
2395
  if (autoDestroy === void 0 || autoDestroy === true) {
@@ -2357,6 +2397,7 @@ class LocalStrapiSourceProvider {
2357
2397
  }
2358
2398
  }
2359
2399
  getMetadata() {
2400
+ this.#reportInfo("getting metadata");
2360
2401
  const strapiVersion = strapi.config.get("info.strapi");
2361
2402
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
2362
2403
  return {
@@ -2368,6 +2409,7 @@ class LocalStrapiSourceProvider {
2368
2409
  }
2369
2410
  async createEntitiesReadStream() {
2370
2411
  assertValidStrapi(this.strapi, "Not able to stream entities");
2412
+ this.#reportInfo("creating entities read stream");
2371
2413
  return chain([
2372
2414
  // Entities stream
2373
2415
  createEntitiesStream(this.strapi),
@@ -2377,14 +2419,17 @@ class LocalStrapiSourceProvider {
2377
2419
  }
2378
2420
  createLinksReadStream() {
2379
2421
  assertValidStrapi(this.strapi, "Not able to stream links");
2422
+ this.#reportInfo("creating links read stream");
2380
2423
  return createLinksStream(this.strapi);
2381
2424
  }
2382
2425
  createConfigurationReadStream() {
2383
2426
  assertValidStrapi(this.strapi, "Not able to stream configuration");
2427
+ this.#reportInfo("creating configuration read stream");
2384
2428
  return createConfigurationStream(this.strapi);
2385
2429
  }
2386
2430
  getSchemas() {
2387
2431
  assertValidStrapi(this.strapi, "Not able to get Schemas");
2432
+ this.#reportInfo("getting schemas");
2388
2433
  const schemas = {
2389
2434
  ...this.strapi.contentTypes,
2390
2435
  ...this.strapi.components
@@ -2396,13 +2441,14 @@ class LocalStrapiSourceProvider {
2396
2441
  }
2397
2442
  createAssetsReadStream() {
2398
2443
  assertValidStrapi(this.strapi, "Not able to stream assets");
2444
+ this.#reportInfo("creating assets read stream");
2399
2445
  return createAssetsStream(this.strapi);
2400
2446
  }
2401
2447
  }
2402
2448
  const createDispatcher = (ws, retryMessageOptions = {
2403
2449
  retryMessageMaxRetries: 5,
2404
2450
  retryMessageTimeout: 3e4
2405
- }) => {
2451
+ }, reportInfo) => {
2406
2452
  const state = {};
2407
2453
  const dispatch = async (message, options = {}) => {
2408
2454
  if (!ws) {
@@ -2415,6 +2461,16 @@ const createDispatcher = (ws, retryMessageOptions = {
2415
2461
  if (options.attachTransfer) {
2416
2462
  Object.assign(payload, { transferID: state.transfer?.id });
2417
2463
  }
2464
+ if (message.type === "command") {
2465
+ reportInfo?.(
2466
+ `dispatching message command:${message.command} uuid:${uuid} sent:${numberOfTimesMessageWasSent}`
2467
+ );
2468
+ } else if (message.type === "transfer") {
2469
+ const messageToSend = message;
2470
+ reportInfo?.(
2471
+ `dispatching message action:${messageToSend.action} ${messageToSend.kind === "step" ? `step:${messageToSend.step}` : ""} uuid:${uuid} sent:${numberOfTimesMessageWasSent}`
2472
+ );
2473
+ }
2418
2474
  const stringifiedPayload = JSON.stringify(payload);
2419
2475
  ws.send(stringifiedPayload, (error) => {
2420
2476
  if (error) {
@@ -2437,6 +2493,16 @@ const createDispatcher = (ws, retryMessageOptions = {
2437
2493
  const interval = setInterval(sendPeriodically, retryMessageTimeout);
2438
2494
  const onResponse = (raw) => {
2439
2495
  const response = JSON.parse(raw.toString());
2496
+ if (message.type === "command") {
2497
+ reportInfo?.(
2498
+ `received response to message command: ${message.command} uuid: ${uuid} sent: ${numberOfTimesMessageWasSent}`
2499
+ );
2500
+ } else if (message.type === "transfer") {
2501
+ const messageToSend = message;
2502
+ reportInfo?.(
2503
+ `received response to message action:${messageToSend.action} ${messageToSend.kind === "step" ? `step:${messageToSend.step}` : ""} uuid:${uuid} sent:${numberOfTimesMessageWasSent}`
2504
+ );
2505
+ }
2440
2506
  if (response.uuid === uuid) {
2441
2507
  clearInterval(interval);
2442
2508
  if (response.error) {
@@ -2493,7 +2559,7 @@ const createDispatcher = (ws, retryMessageOptions = {
2493
2559
  dispatchTransferStep
2494
2560
  };
2495
2561
  };
2496
- const connectToWebsocket = (address, options) => {
2562
+ const connectToWebsocket = (address, options, diagnostics) => {
2497
2563
  return new Promise((resolve, reject2) => {
2498
2564
  const server = new WebSocket(address, options);
2499
2565
  server.once("open", () => {
@@ -2527,6 +2593,14 @@ const connectToWebsocket = (address, options) => {
2527
2593
  )
2528
2594
  );
2529
2595
  });
2596
+ server.on("message", (raw) => {
2597
+ const response = JSON.parse(raw.toString());
2598
+ if (response.diagnostic) {
2599
+ diagnostics?.report({
2600
+ ...response.diagnostic
2601
+ });
2602
+ }
2603
+ });
2530
2604
  server.once("error", (err) => {
2531
2605
  reject2(
2532
2606
  new ProviderTransferError(err.message, {
@@ -2567,6 +2641,7 @@ class RemoteStrapiDestinationProvider {
2567
2641
  ws;
2568
2642
  dispatcher;
2569
2643
  transferID;
2644
+ #diagnostics;
2570
2645
  constructor(options) {
2571
2646
  this.options = options;
2572
2647
  this.ws = null;
@@ -2665,7 +2740,18 @@ class RemoteStrapiDestinationProvider {
2665
2740
  }
2666
2741
  });
2667
2742
  }
2668
- async bootstrap() {
2743
+ #reportInfo(message) {
2744
+ this.#diagnostics?.report({
2745
+ details: {
2746
+ createdAt: /* @__PURE__ */ new Date(),
2747
+ message,
2748
+ origin: "remote-destination-provider"
2749
+ },
2750
+ kind: "info"
2751
+ });
2752
+ }
2753
+ async bootstrap(diagnostics) {
2754
+ this.#diagnostics = diagnostics;
2669
2755
  const { url, auth } = this.options;
2670
2756
  const validProtocols = ["https:", "http:"];
2671
2757
  let ws;
@@ -2682,6 +2768,7 @@ class RemoteStrapiDestinationProvider {
2682
2768
  const wsUrl = `${wsProtocol}//${url.host}${trimTrailingSlash(
2683
2769
  url.pathname
2684
2770
  )}${TRANSFER_PATH}/push`;
2771
+ this.#reportInfo("establishing websocket connection");
2685
2772
  if (!auth) {
2686
2773
  ws = await connectToWebsocket(wsUrl);
2687
2774
  } else if (auth.type === "token") {
@@ -2695,10 +2782,19 @@ class RemoteStrapiDestinationProvider {
2695
2782
  }
2696
2783
  });
2697
2784
  }
2785
+ this.#reportInfo("established websocket connection");
2698
2786
  this.ws = ws;
2699
2787
  const { retryMessageOptions } = this.options;
2700
- this.dispatcher = createDispatcher(this.ws, retryMessageOptions);
2788
+ this.#reportInfo("creating dispatcher");
2789
+ this.dispatcher = createDispatcher(
2790
+ this.ws,
2791
+ retryMessageOptions,
2792
+ (message) => this.#reportInfo(message)
2793
+ );
2794
+ this.#reportInfo("created dispatcher");
2795
+ this.#reportInfo("initialize transfer");
2701
2796
  this.transferID = await this.initTransfer();
2797
+ this.#reportInfo(`initialized transfer ${this.transferID}`);
2702
2798
  this.dispatcher.setTransferProperties({ id: this.transferID, kind: "push" });
2703
2799
  await this.dispatcher.dispatchTransferAction("bootstrap");
2704
2800
  }
@@ -2819,6 +2915,7 @@ class RemoteStrapiSourceProvider {
2819
2915
  options;
2820
2916
  ws;
2821
2917
  dispatcher;
2918
+ #diagnostics;
2822
2919
  constructor(options) {
2823
2920
  this.options = options;
2824
2921
  this.ws = null;
@@ -3007,6 +3104,16 @@ class RemoteStrapiSourceProvider {
3007
3104
  }
3008
3105
  return res.transferID;
3009
3106
  }
3107
+ #reportInfo(message) {
3108
+ this.#diagnostics?.report({
3109
+ details: {
3110
+ createdAt: /* @__PURE__ */ new Date(),
3111
+ message,
3112
+ origin: "remote-source-provider"
3113
+ },
3114
+ kind: "info"
3115
+ });
3116
+ }
3010
3117
  async bootstrap() {
3011
3118
  const { url, auth } = this.options;
3012
3119
  let ws;
@@ -3015,6 +3122,7 @@ class RemoteStrapiSourceProvider {
3015
3122
  const wsUrl = `${wsProtocol}//${url.host}${trimTrailingSlash(
3016
3123
  url.pathname
3017
3124
  )}${TRANSFER_PATH}/pull`;
3125
+ this.#reportInfo("establishing websocket connection");
3018
3126
  if (!auth) {
3019
3127
  ws = await connectToWebsocket(wsUrl);
3020
3128
  } else if (auth.type === "token") {
@@ -3028,10 +3136,19 @@ class RemoteStrapiSourceProvider {
3028
3136
  }
3029
3137
  });
3030
3138
  }
3139
+ this.#reportInfo("established websocket connection");
3031
3140
  this.ws = ws;
3032
3141
  const { retryMessageOptions } = this.options;
3033
- this.dispatcher = createDispatcher(this.ws, retryMessageOptions);
3142
+ this.#reportInfo("creating dispatcher");
3143
+ this.dispatcher = createDispatcher(
3144
+ this.ws,
3145
+ retryMessageOptions,
3146
+ (message) => this.#reportInfo(message)
3147
+ );
3148
+ this.#reportInfo("creating dispatcher");
3149
+ this.#reportInfo("initialize transfer");
3034
3150
  const transferID = await this.initTransfer();
3151
+ this.#reportInfo(`initialized transfer ${transferID}`);
3035
3152
  this.dispatcher.setTransferProperties({ id: transferID, kind: "pull" });
3036
3153
  await this.dispatcher.dispatchTransferAction("bootstrap");
3037
3154
  }
@@ -3263,6 +3380,7 @@ const handlerControllerFactory = (implementation) => (options) => {
3263
3380
  const cb = (ws) => {
3264
3381
  const state = { id: void 0 };
3265
3382
  const messageUUIDs = /* @__PURE__ */ new Set();
3383
+ const diagnostics = createDiagnosticReporter();
3266
3384
  const cannotRespondHandler = (err) => {
3267
3385
  strapi?.log?.error(
3268
3386
  "[Data transfer] Cannot send error response to client, closing connection"
@@ -3296,6 +3414,9 @@ const handlerControllerFactory = (implementation) => (options) => {
3296
3414
  set response(response) {
3297
3415
  state.response = response;
3298
3416
  },
3417
+ get diagnostics() {
3418
+ return diagnostics;
3419
+ },
3299
3420
  addUUID(uuid) {
3300
3421
  messageUUIDs.add(uuid);
3301
3422
  },
@@ -3411,6 +3532,10 @@ const handlerControllerFactory = (implementation) => (options) => {
3411
3532
  onError() {
3412
3533
  },
3413
3534
  onClose() {
3535
+ },
3536
+ onInfo() {
3537
+ },
3538
+ onWarning() {
3414
3539
  }
3415
3540
  };
3416
3541
  const handler = Object.assign(Object.create(prototype), implementation(prototype));
@@ -3443,6 +3568,14 @@ const handlerControllerFactory = (implementation) => (options) => {
3443
3568
  cannotRespondHandler(err);
3444
3569
  }
3445
3570
  });
3571
+ diagnostics.onDiagnostic((diagnostic) => {
3572
+ const uuid = randomUUID();
3573
+ const payload = JSON.stringify({
3574
+ diagnostic,
3575
+ uuid
3576
+ });
3577
+ handler.send(payload);
3578
+ });
3446
3579
  };
3447
3580
  try {
3448
3581
  handleWSUpgrade(wss, ctx, cb);
@@ -3478,6 +3611,26 @@ const createPushController = handlerControllerFactory((proto) => ({
3478
3611
  verifyAuth() {
3479
3612
  return proto.verifyAuth.call(this, TRANSFER_KIND$1);
3480
3613
  },
3614
+ onInfo(message) {
3615
+ this.diagnostics?.report({
3616
+ details: {
3617
+ message,
3618
+ origin: "push-handler",
3619
+ createdAt: /* @__PURE__ */ new Date()
3620
+ },
3621
+ kind: "info"
3622
+ });
3623
+ },
3624
+ onWarning(message) {
3625
+ this.diagnostics?.report({
3626
+ details: {
3627
+ message,
3628
+ createdAt: /* @__PURE__ */ new Date(),
3629
+ origin: "push-handler"
3630
+ },
3631
+ kind: "warning"
3632
+ });
3633
+ },
3481
3634
  cleanup() {
3482
3635
  proto.cleanup.call(this);
3483
3636
  this.streams = {};
@@ -3554,6 +3707,7 @@ const createPushController = handlerControllerFactory((proto) => ({
3554
3707
  proto.addUUID(uuid);
3555
3708
  if (type === "command") {
3556
3709
  const { command: command2 } = msg;
3710
+ this.onInfo(`received command:${command2} uuid:${uuid}`);
3557
3711
  await this.executeAndRespond(uuid, () => {
3558
3712
  this.assertValidTransferCommand(command2);
3559
3713
  if (command2 === "status") {
@@ -3562,6 +3716,7 @@ const createPushController = handlerControllerFactory((proto) => ({
3562
3716
  return this[command2](msg.params);
3563
3717
  });
3564
3718
  } else if (type === "transfer") {
3719
+ this.onInfo(`received transfer action:${msg.action} step:${msg.kind} uuid:${uuid}`);
3565
3720
  await this.executeAndRespond(uuid, async () => {
3566
3721
  await this.verifyAuth();
3567
3722
  this.assertValidTransfer();
@@ -3656,6 +3811,9 @@ const createPushController = handlerControllerFactory((proto) => ({
3656
3811
  }
3657
3812
  this.flow?.set(step);
3658
3813
  }
3814
+ if (action2 === "bootstrap") {
3815
+ return this.provider?.[action2](this.diagnostics);
3816
+ }
3659
3817
  return this.provider?.[action2]();
3660
3818
  },
3661
3819
  async streamAsset(payload) {
@@ -3713,6 +3871,7 @@ const createPushController = handlerControllerFactory((proto) => ({
3713
3871
  getStrapi: () => strapi
3714
3872
  });
3715
3873
  this.provider.onWarning = (message) => {
3874
+ this.onWarning(message);
3716
3875
  strapi.log.warn(message);
3717
3876
  };
3718
3877
  return { transferID: this.transferID };
@@ -3753,6 +3912,26 @@ const createPullController = handlerControllerFactory((proto) => ({
3753
3912
  this.streams = {};
3754
3913
  delete this.provider;
3755
3914
  },
3915
+ onInfo(message) {
3916
+ this.diagnostics?.report({
3917
+ details: {
3918
+ message,
3919
+ origin: "pull-handler",
3920
+ createdAt: /* @__PURE__ */ new Date()
3921
+ },
3922
+ kind: "info"
3923
+ });
3924
+ },
3925
+ onWarning(message) {
3926
+ this.diagnostics?.report({
3927
+ details: {
3928
+ message,
3929
+ createdAt: /* @__PURE__ */ new Date(),
3930
+ origin: "pull-handler"
3931
+ },
3932
+ kind: "warning"
3933
+ });
3934
+ },
3756
3935
  assertValidTransferAction(action2) {
3757
3936
  const validActions = VALID_TRANSFER_ACTIONS;
3758
3937
  if (validActions.includes(action2)) {
@@ -3782,6 +3961,7 @@ const createPullController = handlerControllerFactory((proto) => ({
3782
3961
  proto.addUUID(uuid);
3783
3962
  if (type === "command") {
3784
3963
  const { command: command2 } = msg;
3964
+ this.onInfo(`received command:${command2} uuid:${uuid}`);
3785
3965
  await this.executeAndRespond(uuid, () => {
3786
3966
  this.assertValidTransferCommand(command2);
3787
3967
  if (command2 === "status") {
@@ -3790,6 +3970,7 @@ const createPullController = handlerControllerFactory((proto) => ({
3790
3970
  return this[command2](msg.params);
3791
3971
  });
3792
3972
  } else if (type === "transfer") {
3973
+ this.onInfo(`received transfer action:${msg.action} step:${msg.kind} uuid:${uuid}`);
3793
3974
  await this.executeAndRespond(uuid, async () => {
3794
3975
  await this.verifyAuth();
3795
3976
  this.assertValidTransfer();
@@ -3811,6 +3992,9 @@ const createPullController = handlerControllerFactory((proto) => ({
3811
3992
  async onTransferAction(msg) {
3812
3993
  const { action: action2 } = msg;
3813
3994
  this.assertValidTransferAction(action2);
3995
+ if (action2 === "bootstrap") {
3996
+ return this.provider?.[action2](this.diagnostics);
3997
+ }
3814
3998
  return this.provider?.[action2]();
3815
3999
  },
3816
4000
  async flush(stage, id) {
@@ -4009,6 +4193,7 @@ class LocalFileSourceProvider {
4009
4193
  name = "source::local-file";
4010
4194
  options;
4011
4195
  #metadata;
4196
+ #diagnostics;
4012
4197
  constructor(options) {
4013
4198
  this.options = options;
4014
4199
  const { encryption } = this.options;
@@ -4016,6 +4201,16 @@ class LocalFileSourceProvider {
4016
4201
  throw new Error("Missing encryption key");
4017
4202
  }
4018
4203
  }
4204
+ #reportInfo(message) {
4205
+ this.#diagnostics?.report({
4206
+ details: {
4207
+ createdAt: /* @__PURE__ */ new Date(),
4208
+ message,
4209
+ origin: "file-source-provider"
4210
+ },
4211
+ kind: "info"
4212
+ });
4213
+ }
4019
4214
  /**
4020
4215
  * Pre flight checks regarding the provided options, making sure that the file can be opened (decrypted, decompressed), etc.
4021
4216
  */
@@ -4044,12 +4239,14 @@ class LocalFileSourceProvider {
4044
4239
  return this.#parseJSONFile(backupStream, path2);
4045
4240
  }
4046
4241
  async getMetadata() {
4242
+ this.#reportInfo("getting metadata");
4047
4243
  if (!this.#metadata) {
4048
4244
  await this.#loadMetadata();
4049
4245
  }
4050
4246
  return this.#metadata ?? null;
4051
4247
  }
4052
4248
  async getSchemas() {
4249
+ this.#reportInfo("getting schemas");
4053
4250
  const schemas = await collect(this.createSchemasReadStream());
4054
4251
  if (isEmpty(schemas)) {
4055
4252
  throw new ProviderInitializationError("Could not load schemas from Strapi data file.");
@@ -4057,21 +4254,26 @@ class LocalFileSourceProvider {
4057
4254
  return keyBy("uid", schemas);
4058
4255
  }
4059
4256
  createEntitiesReadStream() {
4257
+ this.#reportInfo("creating entities read stream");
4060
4258
  return this.#streamJsonlDirectory("entities");
4061
4259
  }
4062
4260
  createSchemasReadStream() {
4261
+ this.#reportInfo("creating schemas read stream");
4063
4262
  return this.#streamJsonlDirectory("schemas");
4064
4263
  }
4065
4264
  createLinksReadStream() {
4265
+ this.#reportInfo("creating links read stream");
4066
4266
  return this.#streamJsonlDirectory("links");
4067
4267
  }
4068
4268
  createConfigurationReadStream() {
4269
+ this.#reportInfo("creating configuration read stream");
4069
4270
  return this.#streamJsonlDirectory("configuration");
4070
4271
  }
4071
4272
  createAssetsReadStream() {
4072
4273
  const inStream = this.#getBackupStream();
4073
4274
  const outStream = new PassThrough({ objectMode: true });
4074
4275
  const loadAssetMetadata = this.#loadAssetMetadata.bind(this);
4276
+ this.#reportInfo("creating assets read stream");
4075
4277
  pipeline(
4076
4278
  [
4077
4279
  inStream,
@@ -4272,9 +4474,20 @@ class LocalFileDestinationProvider {
4272
4474
  results = {};
4273
4475
  #providersMetadata = {};
4274
4476
  #archive = {};
4477
+ #diagnostics;
4275
4478
  constructor(options) {
4276
4479
  this.options = options;
4277
4480
  }
4481
+ #reportInfo(message) {
4482
+ this.#diagnostics?.report({
4483
+ details: {
4484
+ createdAt: /* @__PURE__ */ new Date(),
4485
+ message,
4486
+ origin: "file-destination-provider"
4487
+ },
4488
+ kind: "info"
4489
+ });
4490
+ }
4278
4491
  get #archivePath() {
4279
4492
  const { encryption, compression, file: file2 } = this.options;
4280
4493
  let filePath = `${file2.path}.tar`;
@@ -4291,9 +4504,11 @@ class LocalFileDestinationProvider {
4291
4504
  return this;
4292
4505
  }
4293
4506
  createGzip() {
4507
+ this.#reportInfo("creating gzip");
4294
4508
  return zip$1.createGzip();
4295
4509
  }
4296
- bootstrap() {
4510
+ bootstrap(diagnostics) {
4511
+ this.#diagnostics = diagnostics;
4297
4512
  const { compression, encryption } = this.options;
4298
4513
  if (encryption.enabled && !encryption.key) {
4299
4514
  throw new Error("Can't encrypt without a key");
@@ -4332,6 +4547,7 @@ class LocalFileDestinationProvider {
4332
4547
  }
4333
4548
  }
4334
4549
  async rollback() {
4550
+ this.#reportInfo("rolling back");
4335
4551
  await this.close();
4336
4552
  await rm(this.#archivePath, { force: true });
4337
4553
  }
@@ -4339,6 +4555,7 @@ class LocalFileDestinationProvider {
4339
4555
  return null;
4340
4556
  }
4341
4557
  async #writeMetadata() {
4558
+ this.#reportInfo("writing metadata");
4342
4559
  const metadata = this.#providersMetadata.source;
4343
4560
  if (metadata) {
4344
4561
  await new Promise((resolve) => {
@@ -4359,6 +4576,7 @@ class LocalFileDestinationProvider {
4359
4576
  if (!this.#archive.stream) {
4360
4577
  throw new Error("Archive stream is unavailable");
4361
4578
  }
4579
+ this.#reportInfo("creating schemas write stream");
4362
4580
  const filePathFactory = createFilePathFactory("schemas");
4363
4581
  const entryStream = createTarEntryStream(
4364
4582
  this.#archive.stream,
@@ -4371,6 +4589,7 @@ class LocalFileDestinationProvider {
4371
4589
  if (!this.#archive.stream) {
4372
4590
  throw new Error("Archive stream is unavailable");
4373
4591
  }
4592
+ this.#reportInfo("creating entities write stream");
4374
4593
  const filePathFactory = createFilePathFactory("entities");
4375
4594
  const entryStream = createTarEntryStream(
4376
4595
  this.#archive.stream,
@@ -4383,6 +4602,7 @@ class LocalFileDestinationProvider {
4383
4602
  if (!this.#archive.stream) {
4384
4603
  throw new Error("Archive stream is unavailable");
4385
4604
  }
4605
+ this.#reportInfo("creating links write stream");
4386
4606
  const filePathFactory = createFilePathFactory("links");
4387
4607
  const entryStream = createTarEntryStream(
4388
4608
  this.#archive.stream,
@@ -4395,6 +4615,7 @@ class LocalFileDestinationProvider {
4395
4615
  if (!this.#archive.stream) {
4396
4616
  throw new Error("Archive stream is unavailable");
4397
4617
  }
4618
+ this.#reportInfo("creating configuration write stream");
4398
4619
  const filePathFactory = createFilePathFactory("configuration");
4399
4620
  const entryStream = createTarEntryStream(
4400
4621
  this.#archive.stream,
@@ -4408,6 +4629,7 @@ class LocalFileDestinationProvider {
4408
4629
  if (!archiveStream) {
4409
4630
  throw new Error("Archive stream is unavailable");
4410
4631
  }
4632
+ this.#reportInfo("creating assets write stream");
4411
4633
  return new Writable({
4412
4634
  objectMode: true,
4413
4635
  write(data, _encoding, callback) {
@@ -4743,30 +4965,41 @@ const errorColors = {
4743
4965
  error: chalk.red,
4744
4966
  silly: chalk.yellow
4745
4967
  };
4746
- const formatDiagnostic = (operation) => ({ details, kind }) => {
4747
- const logger = createLogger(
4748
- configs.createOutputFileConfiguration(`${operation}_error_log_${Date.now()}.log`)
4749
- );
4750
- try {
4751
- if (kind === "error") {
4752
- const { message, severity = "fatal" } = details;
4753
- const colorizeError = errorColors[severity];
4754
- const errorMessage = colorizeError(`[${severity.toUpperCase()}] ${message}`);
4755
- logger.error(errorMessage);
4756
- }
4757
- if (kind === "info") {
4758
- const { message, params } = details;
4759
- const msg = `${message}
4760
- ${params ? JSON.stringify(params, null, 2) : ""}`;
4761
- logger.info(msg);
4968
+ const formatDiagnostic = (operation, info) => {
4969
+ let logger;
4970
+ const getLogger = () => {
4971
+ if (!logger) {
4972
+ logger = createLogger(
4973
+ configs.createOutputFileConfiguration(`${operation}_${Date.now()}.log`, {
4974
+ level: "info",
4975
+ format: formats?.detailedLogs
4976
+ })
4977
+ );
4762
4978
  }
4763
- if (kind === "warning") {
4764
- const { origin: origin2, message } = details;
4765
- logger.warn(`(${origin2 ?? "transfer"}) ${message}`);
4979
+ return logger;
4980
+ };
4981
+ return ({ details, kind }) => {
4982
+ try {
4983
+ if (kind === "error") {
4984
+ const { message, severity = "fatal" } = details;
4985
+ const colorizeError = errorColors[severity];
4986
+ const errorMessage = colorizeError(`[${severity.toUpperCase()}] ${message}`);
4987
+ getLogger().error(errorMessage);
4988
+ }
4989
+ if (kind === "info" && info) {
4990
+ const { message, params, origin: origin2 } = details;
4991
+ const msg = `[${origin2 ?? "transfer"}] ${message}
4992
+ ${params ? JSON.stringify(params, null, 2) : ""}`;
4993
+ getLogger().info(msg);
4994
+ }
4995
+ if (kind === "warning") {
4996
+ const { origin: origin2, message } = details;
4997
+ getLogger().warn(`(${origin2 ?? "transfer"}) ${message}`);
4998
+ }
4999
+ } catch (err) {
5000
+ getLogger().error(err);
4766
5001
  }
4767
- } catch (err) {
4768
- logger.error(err);
4769
- }
5002
+ };
4770
5003
  };
4771
5004
  const loadersFactory = (defaultLoaders = {}) => {
4772
5005
  const loaders = defaultLoaders;
@@ -4948,7 +5181,7 @@ const action$2 = async (opts) => {
4948
5181
  ]
4949
5182
  }
4950
5183
  });
4951
- engine.diagnostics.onDiagnostic(formatDiagnostic("export"));
5184
+ engine.diagnostics.onDiagnostic(formatDiagnostic("export", opts.verbose));
4952
5185
  const progress = engine.progress.stream;
4953
5186
  const { updateLoader } = loadersFactory();
4954
5187
  progress.on(`stage::start`, ({ stage, data }) => {
@@ -5020,7 +5253,7 @@ const command$2 = ({ command: command2 }) => {
5020
5253
  )
5021
5254
  ).addOption(
5022
5255
  new Option("--no-compress", "Disables gzip compression of output file").default(true)
5023
- ).addOption(
5256
+ ).addOption(new Option("--verbose", "Enable verbose logs")).addOption(
5024
5257
  new Option(
5025
5258
  "-k, --key <string>",
5026
5259
  "Provide encryption key in command instead of using the prompt"
@@ -5076,7 +5309,7 @@ const action$1 = async (opts) => {
5076
5309
  destination.onWarning = (message) => console.warn(`
5077
5310
  ${chalk.yellow("warn")}: ${message}`);
5078
5311
  const engine2 = createTransferEngine$1(source, destination, engineOptions);
5079
- engine2.diagnostics.onDiagnostic(formatDiagnostic("import"));
5312
+ engine2.diagnostics.onDiagnostic(formatDiagnostic("import", opts.verbose));
5080
5313
  const progress = engine2.progress.stream;
5081
5314
  const { updateLoader } = loadersFactory();
5082
5315
  engine2.onSchemaDiff(getDiffHandler(engine2, { force: opts.force, action: "import" }));
@@ -5134,7 +5367,7 @@ const command$1 = ({ command: command2 }) => {
5134
5367
  "-k, --key <string>",
5135
5368
  "Provide encryption key in command instead of using the prompt"
5136
5369
  )
5137
- ).addOption(forceOption).addOption(excludeOption).addOption(onlyOption).addOption(throttleOption).hook("preAction", validateExcludeOnly).hook("preAction", async (thisCommand) => {
5370
+ ).addOption(new Option("--verbose", "Enable verbose logs")).addOption(forceOption).addOption(excludeOption).addOption(onlyOption).addOption(throttleOption).hook("preAction", validateExcludeOnly).hook("preAction", async (thisCommand) => {
5138
5371
  const opts = thisCommand.opts();
5139
5372
  const ext = path.extname(String(opts.file));
5140
5373
  if (ext === ".enc") {
@@ -5264,7 +5497,7 @@ const action = async (opts) => {
5264
5497
  ]
5265
5498
  }
5266
5499
  });
5267
- engine.diagnostics.onDiagnostic(formatDiagnostic("transfer"));
5500
+ engine.diagnostics.onDiagnostic(formatDiagnostic("transfer", opts.verbose));
5268
5501
  const progress = engine.progress.stream;
5269
5502
  const { updateLoader } = loadersFactory();
5270
5503
  engine.onSchemaDiff(getDiffHandler(engine, { force: opts.force, action: "transfer" }));
@@ -5316,7 +5549,7 @@ const command = ({ command: command2 }) => {
5316
5549
  "--to <destinationURL>",
5317
5550
  `URL of the remote Strapi instance to send data to`
5318
5551
  ).argParser(parseURL)
5319
- ).addOption(new Option("--to-token <token>", `Transfer token for the remote Strapi destination`)).addOption(forceOption).addOption(excludeOption).addOption(onlyOption).addOption(throttleOption).hook("preAction", validateExcludeOnly).hook(
5552
+ ).addOption(new Option("--to-token <token>", `Transfer token for the remote Strapi destination`)).addOption(new Option("--verbose", "Enable verbose logs")).addOption(forceOption).addOption(excludeOption).addOption(onlyOption).addOption(throttleOption).hook("preAction", validateExcludeOnly).hook(
5320
5553
  "preAction",
5321
5554
  ifOptions(
5322
5555
  (opts) => !(opts.from || opts.to) || opts.from && opts.to,