@strapi/strapi 4.10.0-beta.0 → 4.10.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.
Files changed (85) hide show
  1. package/.eslintignore +2 -0
  2. package/.eslintrc.js +4 -0
  3. package/bin/strapi.js +2 -455
  4. package/ee/LICENSE.txt +21 -0
  5. package/ee/index.js +42 -5
  6. package/ee/license.js +11 -8
  7. package/lib/Strapi.js +14 -7
  8. package/lib/commands/{admin-create.js → actions/admin/create-user/action.js} +2 -2
  9. package/lib/commands/actions/admin/create-user/command.js +19 -0
  10. package/lib/commands/{admin-reset.js → actions/admin/reset-user-password/action.js} +2 -2
  11. package/lib/commands/actions/admin/reset-user-password/command.js +17 -0
  12. package/lib/commands/{configurationDump.js → actions/configuration/dump/action.js} +1 -1
  13. package/lib/commands/actions/configuration/dump/command.js +17 -0
  14. package/lib/commands/{configurationRestore.js → actions/configuration/restore/action.js} +1 -1
  15. package/lib/commands/actions/configuration/restore/command.js +17 -0
  16. package/lib/commands/{console.js → actions/console/action.js} +2 -2
  17. package/lib/commands/actions/console/command.js +14 -0
  18. package/lib/commands/{content-types/list.js → actions/content-types/list/action.js} +2 -2
  19. package/lib/commands/actions/content-types/list/command.js +14 -0
  20. package/lib/commands/{controllers/list.js → actions/controllers/list/action.js} +2 -2
  21. package/lib/commands/actions/controllers/list/command.js +14 -0
  22. package/lib/commands/{develop.js → actions/develop/action.js} +3 -3
  23. package/lib/commands/actions/develop/command.js +19 -0
  24. package/lib/commands/{transfer/export.js → actions/export/action.js} +27 -19
  25. package/lib/commands/actions/export/command.js +45 -0
  26. package/lib/commands/actions/generate/command.js +18 -0
  27. package/lib/commands/{hooks/list.js → actions/hooks/list/action.js} +2 -2
  28. package/lib/commands/actions/hooks/list/command.js +14 -0
  29. package/lib/commands/{transfer/import.js → actions/import/action.js} +39 -18
  30. package/lib/commands/actions/import/command.js +97 -0
  31. package/lib/commands/{install.js → actions/install/action.js} +1 -1
  32. package/lib/commands/actions/install/command.js +14 -0
  33. package/lib/commands/{middlewares/list.js → actions/middlewares/list/action.js} +2 -2
  34. package/lib/commands/actions/middlewares/list/command.js +14 -0
  35. package/lib/commands/{new.js → actions/new/action.js} +1 -1
  36. package/lib/commands/actions/new/command.js +35 -0
  37. package/lib/commands/{policies/list.js → actions/policies/list/action.js} +2 -2
  38. package/lib/commands/actions/policies/list/command.js +14 -0
  39. package/lib/commands/actions/report/action.js +35 -0
  40. package/lib/commands/actions/report/command.js +17 -0
  41. package/lib/commands/{routes/list.js → actions/routes/list/action.js} +2 -2
  42. package/lib/commands/actions/routes/list/command.js +14 -0
  43. package/lib/commands/{services/list.js → actions/services/list/action.js} +2 -2
  44. package/lib/commands/actions/services/list/command.js +14 -0
  45. package/lib/commands/{start.js → actions/start/action.js} +1 -1
  46. package/lib/commands/actions/start/command.js +14 -0
  47. package/lib/commands/{opt-out-telemetry.js → actions/telemetry/disable/action.js} +1 -1
  48. package/lib/commands/actions/telemetry/disable/command.js +14 -0
  49. package/lib/commands/{opt-in-telemetry.js → actions/telemetry/enable/action.js} +1 -1
  50. package/lib/commands/actions/telemetry/enable/command.js +14 -0
  51. package/lib/commands/actions/templates/generate/command.js +14 -0
  52. package/lib/commands/{transfer/transfer.js → actions/transfer/action.js} +61 -14
  53. package/lib/commands/actions/transfer/command.js +115 -0
  54. package/lib/commands/{ts/generate-types.js → actions/ts/generate-types/action.js} +2 -2
  55. package/lib/commands/actions/ts/generate-types/command.js +21 -0
  56. package/lib/commands/{uninstall.js → actions/uninstall/action.js} +1 -1
  57. package/lib/commands/actions/uninstall/command.js +15 -0
  58. package/lib/commands/actions/version/command.js +19 -0
  59. package/lib/commands/{watchAdmin.js → actions/watch-admin/action.js} +4 -4
  60. package/lib/commands/actions/watch-admin/command.js +15 -0
  61. package/lib/commands/index.js +66 -0
  62. package/lib/commands/utils/commander.js +14 -0
  63. package/lib/commands/{transfer/utils.js → utils/data-transfer.js} +54 -5
  64. package/lib/commands/utils/helpers.js +54 -3
  65. package/lib/core/domain/content-type/validator.js +1 -1
  66. package/lib/core/loaders/plugins/index.js +7 -2
  67. package/lib/core/registries/custom-fields.js +19 -2
  68. package/lib/core/registries/policies.d.ts +3 -3
  69. package/lib/core-api/controller/collection-type.js +0 -1
  70. package/lib/core-api/controller/index.js +1 -1
  71. package/lib/factories.js +3 -4
  72. package/lib/middlewares/logger.js +1 -17
  73. package/lib/middlewares/security.js +1 -1
  74. package/lib/services/event-hub.js +6 -1
  75. package/lib/services/metrics/index.js +1 -0
  76. package/lib/services/metrics/sender.js +1 -14
  77. package/lib/services/server/index.js +2 -2
  78. package/lib/services/server/register-routes.js +1 -1
  79. package/lib/services/utils/dynamic-zones.js +13 -0
  80. package/lib/services/webhook-runner.js +4 -4
  81. package/lib/types/core/commands/index.d.ts +6 -0
  82. package/lib/utils/fetch.js +23 -0
  83. package/package.json +22 -21
  84. package/lib/commands/build.js +0 -18
  85. /package/lib/commands/{generate-template.js → actions/templates/generate/action.js} +0 -0
package/lib/Strapi.js CHANGED
@@ -27,6 +27,7 @@ const createCustomFields = require('./services/custom-fields');
27
27
  const createContentAPI = require('./services/content-api');
28
28
  const createUpdateNotifier = require('./utils/update-notifier');
29
29
  const createStartupLogger = require('./utils/startup-logger');
30
+ const createStrapiFetch = require('./utils/fetch');
30
31
  const { LIFECYCLES } = require('./utils/lifecycles');
31
32
  const ee = require('./utils/ee');
32
33
  const contentTypesRegistry = require('./core/registries/content-types');
@@ -43,6 +44,7 @@ const apisRegistry = require('./core/registries/apis');
43
44
  const bootstrap = require('./core/bootstrap');
44
45
  const loaders = require('./core/loaders');
45
46
  const { destroyOnSignal } = require('./utils/signals');
47
+ const getNumberOfDynamicZones = require('./services/utils/dynamic-zones');
46
48
  const sanitizersRegistry = require('./core/registries/sanitizers');
47
49
  const convertCustomFieldType = require('./utils/convert-custom-field-type');
48
50
 
@@ -117,6 +119,7 @@ class Strapi {
117
119
  this.telemetry = createTelemetry(this);
118
120
  this.requestContext = requestContext;
119
121
  this.customFields = createCustomFields(this);
122
+ this.fetch = createStrapiFetch(this);
120
123
 
121
124
  createUpdateNotifier(this).notify();
122
125
 
@@ -249,6 +252,9 @@ class Strapi {
249
252
  groupProperties: {
250
253
  database: strapi.config.get('database.connection.client'),
251
254
  plugins: Object.keys(strapi.plugins),
255
+ numberOfAllContentTypes: _.size(this.contentTypes), // TODO: V5: This event should be renamed numberOfContentTypes in V5 as the name is already taken to describe the number of content types using i18n.
256
+ numberOfComponents: _.size(this.components),
257
+ numberOfDynamicZones: getNumberOfDynamicZones(),
252
258
  // TODO: to add back
253
259
  // providers: this.config.installedProviders,
254
260
  },
@@ -389,6 +395,7 @@ class Strapi {
389
395
  eventHub: this.eventHub,
390
396
  logger: this.log,
391
397
  configuration: this.config.get('server.webhooks', {}),
398
+ fetch: this.fetch,
392
399
  });
393
400
 
394
401
  this.registerInternalHooks();
@@ -468,7 +475,7 @@ class Strapi {
468
475
  await this.startWebhooks();
469
476
 
470
477
  await this.server.initMiddlewares();
471
- await this.server.initRouting();
478
+ this.server.initRouting();
472
479
 
473
480
  await this.contentAPI.permissions.registerActions();
474
481
 
@@ -536,17 +543,17 @@ class Strapi {
536
543
  // plugins
537
544
  await this.container.get('modules')[lifecycleName]();
538
545
 
539
- // user
540
- const userLifecycleFunction = this.app && this.app[lifecycleName];
541
- if (isFunction(userLifecycleFunction)) {
542
- await userLifecycleFunction({ strapi: this });
543
- }
544
-
545
546
  // admin
546
547
  const adminLifecycleFunction = this.admin && this.admin[lifecycleName];
547
548
  if (isFunction(adminLifecycleFunction)) {
548
549
  await adminLifecycleFunction({ strapi: this });
549
550
  }
551
+
552
+ // user
553
+ const userLifecycleFunction = this.app && this.app[lifecycleName];
554
+ if (isFunction(userLifecycleFunction)) {
555
+ await userLifecycleFunction({ strapi: this });
556
+ }
550
557
  }
551
558
 
552
559
  getModel(uid) {
@@ -3,7 +3,7 @@
3
3
  const { yup } = require('@strapi/utils');
4
4
  const _ = require('lodash');
5
5
  const inquirer = require('inquirer');
6
- const strapi = require('../index');
6
+ const strapi = require('../../../../index');
7
7
 
8
8
  const emailValidator = yup.string().email('Invalid email address').lowercase();
9
9
 
@@ -57,7 +57,7 @@ const promptQuestions = [
57
57
  * @param {string} cmdOptions.firstname - new admin's first name
58
58
  * @param {string} [cmdOptions.lastname] - new admin's last name
59
59
  */
60
- module.exports = async function (cmdOptions = {}) {
60
+ module.exports = async (cmdOptions = {}) => {
61
61
  let { email, password, firstname, lastname } = cmdOptions;
62
62
 
63
63
  if (
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi admin:create-user`
7
+ * @param {import('../../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('admin:create-user')
12
+ .alias('admin:create')
13
+ .description('Create a new admin')
14
+ .option('-e, --email <email>', 'Email of the new admin')
15
+ .option('-p, --password <password>', 'Password of the new admin')
16
+ .option('-f, --firstname <first name>', 'First name of the new admin')
17
+ .option('-l, --lastname <last name>', 'Last name of the new admin')
18
+ .action(getLocalScript('admin/create-user'));
19
+ };
@@ -2,7 +2,7 @@
2
2
 
3
3
  const _ = require('lodash');
4
4
  const inquirer = require('inquirer');
5
- const strapi = require('../index');
5
+ const strapi = require('../../../../index');
6
6
 
7
7
  const promptQuestions = [
8
8
  { type: 'input', name: 'email', message: 'User email?' },
@@ -20,7 +20,7 @@ const promptQuestions = [
20
20
  * @param {string} cmdOptions.email - user's email
21
21
  * @param {string} cmdOptions.password - user's new password
22
22
  */
23
- module.exports = async function (cmdOptions = {}) {
23
+ module.exports = async (cmdOptions = {}) => {
24
24
  const { email, password } = cmdOptions;
25
25
 
26
26
  if (_.isEmpty(email) && _.isEmpty(password) && process.stdin.isTTY) {
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi admin:reset-user-password`
7
+ * @param {import('../../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('admin:reset-user-password')
12
+ .alias('admin:reset-password')
13
+ .description("Reset an admin user's password")
14
+ .option('-e, --email <email>', 'The user email')
15
+ .option('-p, --password <password>', 'New password for the user')
16
+ .action(getLocalScript('admin/reset-user-password'));
17
+ };
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const fs = require('fs');
4
- const strapi = require('../index');
4
+ const strapi = require('../../../../index');
5
5
 
6
6
  const CHUNK_SIZE = 100;
7
7
 
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi configuration:dump`
7
+ * @param {import('../../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('configuration:dump')
12
+ .alias('config:dump')
13
+ .description('Dump configurations of your application')
14
+ .option('-f, --file <file>', 'Output file, default output is stdout')
15
+ .option('-p, --pretty', 'Format the output JSON with indentation and line breaks', false)
16
+ .action(getLocalScript('configuration/dump'));
17
+ };
@@ -3,7 +3,7 @@
3
3
  const fs = require('fs');
4
4
  const _ = require('lodash');
5
5
 
6
- const strapi = require('../index');
6
+ const strapi = require('../../../../index');
7
7
 
8
8
  /**
9
9
  * Will restore configurations. It reads from a file or stdin
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi configuration:restore`
7
+ * @param {import('../../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('configuration:restore')
12
+ .alias('config:restore')
13
+ .description('Restore configurations of your application')
14
+ .option('-f, --file <file>', 'Input file, default input is stdin')
15
+ .option('-s, --strategy <strategy>', 'Strategy name, one of: "replace", "merge", "keep"')
16
+ .action(getLocalScript('configuration/restore'));
17
+ };
@@ -2,7 +2,7 @@
2
2
 
3
3
  const REPL = require('repl');
4
4
 
5
- const strapi = require('../index');
5
+ const strapi = require('../../../index');
6
6
 
7
7
  /**
8
8
  * `$ strapi console`
@@ -14,7 +14,7 @@ module.exports = async () => {
14
14
  app.start().then(() => {
15
15
  const repl = REPL.start(app.config.info.name + ' > ' || 'strapi > '); // eslint-disable-line prefer-template
16
16
 
17
- repl.on('exit', function (err) {
17
+ repl.on('exit', (err) => {
18
18
  if (err) {
19
19
  app.log.error(err);
20
20
  process.exit(1);
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi console`
7
+ * @param {import('../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('console')
12
+ .description('Open the Strapi framework console')
13
+ .action(getLocalScript('console'));
14
+ };
@@ -3,9 +3,9 @@
3
3
  const CLITable = require('cli-table3');
4
4
  const chalk = require('chalk');
5
5
 
6
- const strapi = require('../../index');
6
+ const strapi = require('../../../../index');
7
7
 
8
- module.exports = async function () {
8
+ module.exports = async () => {
9
9
  const appContext = await strapi.compile();
10
10
  const app = await strapi(appContext).register();
11
11
 
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi content-types:list`
7
+ * @param {import('../../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('content-types:list')
12
+ .description('List all the application content-types')
13
+ .action(getLocalScript('content-types/list'));
14
+ };
@@ -3,9 +3,9 @@
3
3
  const CLITable = require('cli-table3');
4
4
  const chalk = require('chalk');
5
5
 
6
- const strapi = require('../../index');
6
+ const strapi = require('../../../../index');
7
7
 
8
- module.exports = async function () {
8
+ module.exports = async () => {
9
9
  const appContext = await strapi.compile();
10
10
  const app = await strapi(appContext).register();
11
11
 
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi controllers:list`
7
+ * @param {import('../../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('controllers:list')
12
+ .description('List all the application controllers')
13
+ .action(getLocalScript('controllers/list'));
14
+ };
@@ -9,9 +9,9 @@ const { getOr } = require('lodash/fp');
9
9
  const { joinBy } = require('@strapi/utils');
10
10
  const tsUtils = require('@strapi/typescript-utils');
11
11
 
12
- const loadConfiguration = require('../core/app-configuration');
13
- const strapi = require('../index');
14
- const { buildTypeScript, buildAdmin } = require('./builders');
12
+ const loadConfiguration = require('../../../core/app-configuration');
13
+ const strapi = require('../../../index');
14
+ const { buildTypeScript, buildAdmin } = require('../../builders');
15
15
 
16
16
  /**
17
17
  * `$ strapi develop`
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi develop`
7
+ * @param {import('../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('develop')
12
+ .alias('dev')
13
+ .option('--no-build', 'Disable build')
14
+ .option('--watch-admin', 'Enable watch', false)
15
+ .option('--polling', 'Watch for file changes in network directories', false)
16
+ .option('--browser <name>', 'Open the browser', true)
17
+ .description('Start your Strapi application in development mode')
18
+ .action(getLocalScript('develop'));
19
+ };
@@ -22,15 +22,22 @@ const {
22
22
  createStrapiInstance,
23
23
  formatDiagnostic,
24
24
  loadersFactory,
25
- } = require('./utils');
26
- const { exitWith } = require('../utils/helpers');
25
+ exitMessageText,
26
+ abortTransfer,
27
+ getTransferTelemetryPayload,
28
+ } = require('../../utils/data-transfer');
29
+ const { exitWith } = require('../../utils/helpers');
30
+
27
31
  /**
28
32
  * @typedef ExportCommandOptions Options given to the CLI import command
29
33
  *
30
- * @property {string} [file] The file path to import
34
+ * @property {string} [file] The file path to export to
31
35
  * @property {boolean} [encrypt] Used to encrypt the final archive
32
- * @property {string} [key] Encryption key, only useful when encryption is enabled
36
+ * @property {string} [key] Encryption key, used only when encryption is enabled
33
37
  * @property {boolean} [compress] Used to compress the final archive
38
+ * @property {(keyof import('@strapi/data-transfer/src/engine').TransferGroupFilter)[]} [only] If present, only include these filtered groups of data
39
+ * @property {(keyof import('@strapi/data-transfer/src/engine').TransferGroupFilter)[]} [exclude] If present, exclude these filtered groups of data
40
+ * @property {number|undefined} [throttle] Delay in ms after each record
34
41
  */
35
42
 
36
43
  const BYTES_IN_MB = 1024 * 1024;
@@ -58,6 +65,7 @@ module.exports = async (opts) => {
58
65
  schemaStrategy: 'ignore', // for an export to file, schemaStrategy will always be skipped
59
66
  exclude: opts.exclude,
60
67
  only: opts.only,
68
+ throttle: opts.throttle,
61
69
  transforms: {
62
70
  links: [
63
71
  {
@@ -97,23 +105,21 @@ module.exports = async (opts) => {
97
105
  updateLoader(stage, data);
98
106
  });
99
107
 
100
- const getTelemetryPayload = (/* payload */) => {
101
- return {
102
- eventProperties: {
103
- source: engine.sourceProvider.name,
104
- destination: engine.destinationProvider.name,
105
- },
106
- };
107
- };
108
-
109
108
  progress.on('transfer::start', async () => {
110
109
  console.log(`Starting export...`);
111
- await strapi.telemetry.send('didDEITSProcessStart', getTelemetryPayload());
110
+
111
+ await strapi.telemetry.send('didDEITSProcessStart', getTransferTelemetryPayload(engine));
112
112
  });
113
113
 
114
114
  let results;
115
115
  let outFile;
116
116
  try {
117
+ // Abort transfer if user interrupts process
118
+ ['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach((signal) => {
119
+ process.removeAllListeners(signal);
120
+ process.on(signal, () => abortTransfer({ engine, strapi }));
121
+ });
122
+
117
123
  results = await engine.transfer();
118
124
  outFile = results.destination.file.path;
119
125
  const outFileExists = await fs.pathExists(outFile);
@@ -121,11 +127,13 @@ module.exports = async (opts) => {
121
127
  throw new TransferEngineTransferError(`Export file not created "${outFile}"`);
122
128
  }
123
129
  } catch {
124
- await strapi.telemetry.send('didDEITSProcessFail', getTelemetryPayload());
125
- exitWith(1, 'Export process failed.');
130
+ await strapi.telemetry.send('didDEITSProcessFail', getTransferTelemetryPayload(engine));
131
+ exitWith(1, exitMessageText('export', true));
126
132
  }
127
133
 
128
- await strapi.telemetry.send('didDEITSProcessFinish', getTelemetryPayload());
134
+ // Note: we need to await telemetry or else the process ends before it is sent
135
+ await strapi.telemetry.send('didDEITSProcessFinish', getTransferTelemetryPayload(engine));
136
+
129
137
  try {
130
138
  const table = buildTransferTable(results.engine);
131
139
  console.log(table.toString());
@@ -133,8 +141,8 @@ module.exports = async (opts) => {
133
141
  console.error('There was an error displaying the results of the transfer.');
134
142
  }
135
143
 
136
- console.log(`${chalk.bold('Export process has been completed successfully!')}`);
137
- exitWith(0, `Export archive is in ${chalk.green(outFile)}`);
144
+ console.log(`Export archive is in ${chalk.green(outFile)}`);
145
+ exitWith(0, exitMessageText('export'));
138
146
  };
139
147
 
140
148
  /**
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ const { Option } = require('commander');
4
+ const {
5
+ excludeOption,
6
+ onlyOption,
7
+ throttleOption,
8
+ validateExcludeOnly,
9
+ } = require('../../utils/data-transfer');
10
+ const { promptEncryptionKey } = require('../../utils/commander');
11
+ const { getLocalScript } = require('../../utils/helpers');
12
+
13
+ /**
14
+ * `$ strapi export`
15
+ * @param {import('../../../types/core/commands').AddCommandOptions} options
16
+ */
17
+ module.exports = ({ command }) => {
18
+ command
19
+ .command('export')
20
+ .description('Export data from Strapi to file')
21
+ .allowExcessArguments(false)
22
+ .addOption(
23
+ new Option('--no-encrypt', `Disables 'aes-128-ecb' encryption of the output file`).default(
24
+ true
25
+ )
26
+ )
27
+ .addOption(
28
+ new Option('--no-compress', 'Disables gzip compression of output file').default(true)
29
+ )
30
+ .addOption(
31
+ new Option(
32
+ '-k, --key <string>',
33
+ 'Provide encryption key in command instead of using the prompt'
34
+ )
35
+ )
36
+ .addOption(
37
+ new Option('-f, --file <file>', 'name to use for exported file (without extensions)')
38
+ )
39
+ .addOption(excludeOption)
40
+ .addOption(onlyOption)
41
+ .addOption(throttleOption)
42
+ .hook('preAction', validateExcludeOnly)
43
+ .hook('preAction', promptEncryptionKey)
44
+ .action(getLocalScript('export'));
45
+ };
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ const { assertCwdContainsStrapiProject } = require('../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi generate`
7
+ * @param {import('../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command, argv }) => {
10
+ command
11
+ .command('generate')
12
+ .description('Launch the interactive API generator')
13
+ .action(() => {
14
+ assertCwdContainsStrapiProject('generate');
15
+ argv.splice(2, 1);
16
+ require('@strapi/generators').runCLI();
17
+ });
18
+ };
@@ -3,9 +3,9 @@
3
3
  const CLITable = require('cli-table3');
4
4
  const chalk = require('chalk');
5
5
 
6
- const strapi = require('../../index');
6
+ const strapi = require('../../../../index');
7
7
 
8
- module.exports = async function () {
8
+ module.exports = async () => {
9
9
  const appContext = await strapi.compile();
10
10
  const app = await strapi(appContext).register();
11
11
 
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const { getLocalScript } = require('../../../utils/helpers');
4
+
5
+ /**
6
+ * `$ strapi hooks:list`
7
+ * @param {import('../../../../types/core/commands').AddCommandOptions} options
8
+ */
9
+ module.exports = ({ command }) => {
10
+ command
11
+ .command('hooks:list')
12
+ .description('List all the application hooks')
13
+ .action(getLocalScript('hooks/list'));
14
+ };
@@ -18,13 +18,33 @@ const {
18
18
  createStrapiInstance,
19
19
  formatDiagnostic,
20
20
  loadersFactory,
21
- } = require('./utils');
22
- const { exitWith } = require('../utils/helpers');
21
+ exitMessageText,
22
+ abortTransfer,
23
+ getTransferTelemetryPayload,
24
+ } = require('../../utils/data-transfer');
25
+ const { exitWith } = require('../../utils/helpers');
23
26
 
24
27
  /**
25
- * @typedef {import('@strapi/data-transfer').ILocalFileSourceProviderOptions} ILocalFileSourceProviderOptions
28
+ * @typedef {import('@strapi/data-transfer/src/file/providers').ILocalFileSourceProviderOptions} ILocalFileSourceProviderOptions
26
29
  */
27
30
 
31
+ /**
32
+ * @typedef ImportCommandOptions Options given to the CLI import command
33
+ *
34
+ * @property {string} [file] The file path to import
35
+ * @property {string} [key] Encryption key, used when encryption is enabled
36
+ * @property {(keyof import('@strapi/data-transfer/src/engine').TransferGroupFilter)[]} [only] If present, only include these filtered groups of data
37
+ * @property {(keyof import('@strapi/data-transfer/src/engine').TransferGroupFilter)[]} [exclude] If present, exclude these filtered groups of data
38
+ * @property {number|undefined} [throttle] Delay in ms after each record
39
+ */
40
+
41
+ /**
42
+ * Import command.
43
+ *
44
+ * It transfers data from a file to a local Strapi instance
45
+ *
46
+ * @param {ImportCommandOptions} opts
47
+ */
28
48
  module.exports = async (opts) => {
29
49
  // validate inputs from Commander
30
50
  if (!isObject(opts)) {
@@ -63,6 +83,7 @@ module.exports = async (opts) => {
63
83
  schemaStrategy: opts.schemaStrategy || DEFAULT_SCHEMA_STRATEGY,
64
84
  exclude: opts.exclude,
65
85
  only: opts.only,
86
+ throttle: opts.throttle,
66
87
  rules: {
67
88
  links: [
68
89
  {
@@ -102,27 +123,26 @@ module.exports = async (opts) => {
102
123
  updateLoader(stage, data);
103
124
  });
104
125
 
105
- const getTelemetryPayload = () => {
106
- return {
107
- eventProperties: {
108
- source: engine.sourceProvider.name,
109
- destination: engine.destinationProvider.name,
110
- },
111
- };
112
- };
113
-
114
126
  progress.on('transfer::start', async () => {
115
127
  console.log('Starting import...');
116
- await strapiInstance.telemetry.send('didDEITSProcessStart', getTelemetryPayload());
128
+ await strapiInstance.telemetry.send(
129
+ 'didDEITSProcessStart',
130
+ getTransferTelemetryPayload(engine)
131
+ );
117
132
  });
118
133
 
119
134
  let results;
120
135
  try {
136
+ // Abort transfer if user interrupts process
137
+ ['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach((signal) => {
138
+ process.removeAllListeners(signal);
139
+ process.on(signal, () => abortTransfer({ engine, strapi }));
140
+ });
141
+
121
142
  results = await engine.transfer();
122
143
  } catch (e) {
123
- await strapiInstance.telemetry.send('didDEITSProcessFail', getTelemetryPayload());
124
- console.error('Import process failed.');
125
- process.exit(1);
144
+ await strapiInstance.telemetry.send('didDEITSProcessFail', getTransferTelemetryPayload(engine));
145
+ exitWith(1, exitMessageText('import', true));
126
146
  }
127
147
 
128
148
  try {
@@ -132,10 +152,11 @@ module.exports = async (opts) => {
132
152
  console.error('There was an error displaying the results of the transfer.');
133
153
  }
134
154
 
135
- await strapiInstance.telemetry.send('didDEITSProcessFinish', getTelemetryPayload());
155
+ // Note: we need to await telemetry or else the process ends before it is sent
156
+ await strapiInstance.telemetry.send('didDEITSProcessFinish', getTransferTelemetryPayload(engine));
136
157
  await strapiInstance.destroy();
137
158
 
138
- exitWith(0, 'Import process has been completed successfully!');
159
+ exitWith(0, exitMessageText('import'));
139
160
  };
140
161
 
141
162
  /**