@strapi/strapi 4.5.2 → 4.6.0-alpha.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/bin/strapi.js CHANGED
@@ -7,11 +7,18 @@
7
7
  const _ = require('lodash');
8
8
  const resolveCwd = require('resolve-cwd');
9
9
  const { yellow } = require('chalk');
10
- const { Command } = require('commander');
10
+ const { Command, Option } = require('commander');
11
+ const inquirer = require('inquirer');
11
12
 
12
13
  const program = new Command();
13
14
 
14
15
  const packageJSON = require('../package.json');
16
+ const {
17
+ parseInputList,
18
+ parseInputBool,
19
+ promptEncryptionKey,
20
+ confirmKeyValue,
21
+ } = require('../lib/commands/utils/commander');
15
22
 
16
23
  const checkCwdIsStrapiApp = (name) => {
17
24
  const logErrorAndExit = () => {
@@ -60,6 +67,13 @@ const getLocalScript =
60
67
  });
61
68
  };
62
69
 
70
+ // option to exclude types of data for the export, import, and transfer commands
71
+ // TODO: validate these inputs. Hopefully here, but worst case it may require adding a hook on each command
72
+ const excludeOption = new Option(
73
+ '--exclude <data,to,exclude>',
74
+ 'Comma-separated list of data to exclude (files [localMediaFiles, providerMediaFiles], content [entities, links], schema, configuration)' // ['webhooks', 'content', 'localmedia', 'providermedia', 'relations']
75
+ ).argParser(parseInputList);
76
+
63
77
  // Initial program setup
64
78
  program.storeOptionsAsProperties(false).allowUnknownOption(true);
65
79
 
@@ -255,4 +269,97 @@ program
255
269
  .option('-s, --silent', `Run the generation silently, without any output`, false)
256
270
  .action(getLocalScript('ts/generate-types'));
257
271
 
272
+ // `$ strapi export`
273
+ program
274
+ .command('export')
275
+ .description('Export data from Strapi to file')
276
+ .addOption(
277
+ new Option(
278
+ '--encrypt <boolean>',
279
+ `Encrypt output file using the 'aes-128-ecb' algorithm. Prompts for key unless key option is used.`
280
+ )
281
+ .default(true)
282
+ .argParser(parseInputBool)
283
+ )
284
+ .addOption(
285
+ new Option('--compress <boolean>', 'Compress output file using gzip compression')
286
+ .default(true)
287
+ .argParser(parseInputBool)
288
+ )
289
+ .addOption(
290
+ new Option('--key <string>', 'Provide encryption key in command instead of using a prompt')
291
+ )
292
+ .addOption(
293
+ new Option('--max-size <max MB per file>', 'split final file when exceeding size in MB')
294
+ )
295
+ .addOption(
296
+ new Option(
297
+ '--max-size-jsonl <max MB per internal backup file>',
298
+ 'split internal jsonl files when exceeding max size in MB'
299
+ )
300
+ )
301
+ .addOption(new Option('-f, --file <file>', 'name to use for exported file (without extensions)'))
302
+ .addOption(excludeOption)
303
+ .allowExcessArguments(false)
304
+ .hook('preAction', promptEncryptionKey)
305
+ .action(getLocalScript('transfer/export'));
306
+
307
+ // `$ strapi import`
308
+ program
309
+ .command('import')
310
+ .description('Import data from file to Strapi')
311
+ .addOption(
312
+ new Option('--conflictStrategy <conflictStrategy>', 'Which strategy to use for ID conflicts')
313
+ .choices(['restore', 'abort', 'keep', 'replace'])
314
+ .default('restore')
315
+ )
316
+ .addOption(excludeOption)
317
+ .addOption(
318
+ new Option(
319
+ '--schemaComparison <schemaComparison>',
320
+ 'exact requires every field to match, strict requires Strapi version and content type schema fields do not break, subset requires source schema to exist in destination, bypass skips checks',
321
+ parseInputList
322
+ )
323
+ .choices(['exact', 'strict', 'subset', 'bypass'])
324
+ .default('exact')
325
+ )
326
+ .requiredOption(
327
+ '-f, --file <file>',
328
+ 'path and filename to the Strapi export file you want to import'
329
+ )
330
+ .addOption(
331
+ new Option('--key <string>', 'Provide encryption key in command instead of using a prompt')
332
+ )
333
+ .allowExcessArguments(false)
334
+ .hook('preAction', async (thisCommand) => {
335
+ const opts = thisCommand.opts();
336
+
337
+ // check extension to guess if we should prompt for key
338
+ if (String(opts.file).endsWith('.enc')) {
339
+ if (!opts.key) {
340
+ const answers = await inquirer.prompt([
341
+ {
342
+ type: 'password',
343
+ message: 'Please enter your decryption key',
344
+ name: 'key',
345
+ },
346
+ ]);
347
+ if (!answers.key?.length) {
348
+ console.log('No key entered, aborting import.');
349
+ process.exit(0);
350
+ }
351
+ opts.key = answers.key;
352
+ }
353
+ }
354
+ })
355
+ .hook(
356
+ 'preAction',
357
+ confirmKeyValue(
358
+ 'conflictStrategy',
359
+ 'restore',
360
+ "Using strategy 'restore' will delete all data in your database. Are you sure you want to proceed?"
361
+ )
362
+ )
363
+ .action(getLocalScript('transfer/import'));
364
+
258
365
  program.parseAsync(process.argv);
@@ -0,0 +1,166 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ createLocalFileDestinationProvider,
5
+ createLocalStrapiSourceProvider,
6
+ createTransferEngine,
7
+ // TODO: we need to solve this issue with typescript modules
8
+ // eslint-disable-next-line import/no-unresolved, node/no-missing-require
9
+ } = require('@strapi/data-transfer');
10
+ const _ = require('lodash/fp');
11
+ const Table = require('cli-table3');
12
+ const fs = require('fs-extra');
13
+
14
+ const chalk = require('chalk');
15
+ const strapi = require('../../index');
16
+ const { readableBytes } = require('../utils');
17
+
18
+ const pad = (n) => {
19
+ return (n < 10 ? '0' : '') + String(n);
20
+ };
21
+
22
+ const yyyymmddHHMMSS = () => {
23
+ const date = new Date();
24
+
25
+ return (
26
+ date.getFullYear() +
27
+ pad(date.getMonth() + 1) +
28
+ pad(date.getDate()) +
29
+ pad(date.getHours()) +
30
+ pad(date.getMinutes()) +
31
+ pad(date.getSeconds())
32
+ );
33
+ };
34
+
35
+ const getDefaultExportName = () => {
36
+ return `export_${yyyymmddHHMMSS()}`;
37
+ };
38
+
39
+ const logger = console;
40
+
41
+ const BYTES_IN_MB = 1024 * 1024;
42
+
43
+ module.exports = async (opts) => {
44
+ // validate inputs from Commander
45
+ if (!_.isObject(opts)) {
46
+ logger.error('Could not parse arguments');
47
+ process.exit(1);
48
+ }
49
+ const filename = opts.file;
50
+
51
+ /**
52
+ * From local Strapi instance
53
+ */
54
+ const sourceOptions = {
55
+ async getStrapi() {
56
+ return strapi(await strapi.compile()).load();
57
+ },
58
+ };
59
+ const source = createLocalStrapiSourceProvider(sourceOptions);
60
+
61
+ const file = _.isString(filename) && filename.length > 0 ? filename : getDefaultExportName();
62
+
63
+ /**
64
+ * To a Strapi backup file
65
+ */
66
+ // treat any unknown arguments as filenames
67
+ const destinationOptions = {
68
+ file: {
69
+ path: file,
70
+ maxSize: _.isFinite(opts.maxSize) ? Math.floor(opts.maxSize) * BYTES_IN_MB : undefined,
71
+ maxSizeJsonl: _.isFinite(opts.maxSizeJsonl)
72
+ ? Math.floor(opts.maxSizeJsonl) * BYTES_IN_MB
73
+ : undefined,
74
+ },
75
+ encryption: {
76
+ enabled: opts.encrypt,
77
+ key: opts.key,
78
+ },
79
+ compression: {
80
+ enabled: opts.compress,
81
+ },
82
+ };
83
+ const destination = createLocalFileDestinationProvider(destinationOptions);
84
+
85
+ /**
86
+ * Configure and run the transfer engine
87
+ */
88
+ const engineOptions = {
89
+ strategy: 'restore', // for an export to file, strategy will always be 'restore'
90
+ versionMatching: 'ignore', // for an export to file, versionMatching will always be skipped
91
+ exclude: opts.exclude,
92
+ };
93
+ const engine = createTransferEngine(source, destination, engineOptions);
94
+
95
+ try {
96
+ let resultData = [];
97
+ logger.log(`Starting export...`);
98
+
99
+ engine.progress.stream.on('start', ({ stage }) => {
100
+ logger.log(`Starting transfer of ${stage}...`);
101
+ });
102
+
103
+ // engine.progress.stream..on('progress', ({ stage, data }) => {
104
+ // logger.log('progress');
105
+ // });
106
+
107
+ engine.progress.stream.on('complete', ({ stage, data }) => {
108
+ logger.log(`...${stage} complete`);
109
+ resultData = data;
110
+ });
111
+
112
+ const results = await engine.transfer();
113
+
114
+ // Build pretty table
115
+ const table = new Table({
116
+ head: ['Type', 'Count', 'Size'],
117
+ });
118
+
119
+ let totalBytes = 0;
120
+ let totalItems = 0;
121
+ Object.keys(resultData).forEach((key) => {
122
+ const item = resultData[key];
123
+
124
+ table.push([
125
+ { hAlign: 'left', content: chalk.bold(key) },
126
+ { hAlign: 'right', content: item.count },
127
+ { hAlign: 'right', content: `${readableBytes(item.bytes, 1, 11)} ` },
128
+ ]);
129
+ totalBytes += item.bytes;
130
+ totalItems += item.count;
131
+
132
+ if (item.aggregates) {
133
+ Object.keys(item.aggregates).forEach((subkey) => {
134
+ const subitem = item.aggregates[subkey];
135
+
136
+ table.push([
137
+ { hAlign: 'left', content: `-- ${chalk.bold(subkey)}` },
138
+ { hAlign: 'right', content: subitem.count },
139
+ { hAlign: 'right', content: `(${chalk.grey(readableBytes(subitem.bytes, 1, 11))})` },
140
+ ]);
141
+ });
142
+ }
143
+ });
144
+ table.push([
145
+ { hAlign: 'left', content: chalk.bold.green('Total') },
146
+ { hAlign: 'right', content: chalk.bold.green(totalItems) },
147
+ { hAlign: 'right', content: `${chalk.bold.green(readableBytes(totalBytes, 1, 11))} ` },
148
+ ]);
149
+ logger.log(table.toString());
150
+
151
+ // TODO: once archiving is implemented, we need to check file extensions
152
+ if (!fs.pathExistsSync(results.destination.file.path)) {
153
+ logger.log(file);
154
+ throw new Error('Export file not created');
155
+ }
156
+
157
+ logger.log(`
158
+ ${chalk.bold('Export process has been completed successfully!')}
159
+ Export archive is in ${chalk.green(results.destination.file.path)}
160
+ `);
161
+ process.exit(0);
162
+ } catch (e) {
163
+ logger.error('Export process failed unexpectedly:', e.toString());
164
+ process.exit(1);
165
+ }
166
+ };
@@ -0,0 +1,65 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ createLocalFileSourceProvider,
5
+ createLocalStrapiDestinationProvider,
6
+ createTransferEngine,
7
+ // TODO: we need to solve this issue with typescript modules
8
+ // eslint-disable-next-line import/no-unresolved, node/no-missing-require
9
+ } = require('@strapi/data-transfer');
10
+ const { isObject } = require('lodash/fp');
11
+ const strapi = require('../../index');
12
+
13
+ const logger = console;
14
+
15
+ module.exports = async (opts) => {
16
+ // validate inputs from Commander
17
+ if (!isObject(opts)) {
18
+ logger.error('Could not parse arguments');
19
+ process.exit(1);
20
+ }
21
+ const filename = opts.file;
22
+
23
+ /**
24
+ * From strapi backup file
25
+ */
26
+
27
+ // treat any unknown arguments as filenames
28
+ const sourceOptions = {
29
+ backupFilePath: filename,
30
+ };
31
+ const source = createLocalFileSourceProvider(sourceOptions);
32
+
33
+ /**
34
+ * To local Strapi instance
35
+ */
36
+ const destinationOptions = {
37
+ async getStrapi() {
38
+ return strapi(await strapi.compile()).load();
39
+ },
40
+ };
41
+ const destination = createLocalStrapiDestinationProvider(destinationOptions);
42
+
43
+ /**
44
+ * Configure and run the transfer engine
45
+ */
46
+ const engineOptions = {
47
+ strategy: opts.conflictStrategy,
48
+ versionMatching: opts.schemaComparison,
49
+ exclude: opts.exclude,
50
+ };
51
+ const engine = createTransferEngine(source, destination, engineOptions);
52
+
53
+ try {
54
+ logger.log('Importing data...');
55
+ const result = await engine.transfer();
56
+ logger.log('Import process has been completed successfully!');
57
+
58
+ // TODO: this won't dump the entire results, we will print a pretty summary
59
+ logger.log('Results:', result);
60
+ process.exit(0);
61
+ } catch (e) {
62
+ logger.log(`Import process failed unexpectedly: ${e.message}`);
63
+ process.exit(1);
64
+ }
65
+ };
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ const { parseType } = require('@strapi/utils/lib');
4
+ const inquirer = require('inquirer');
5
+
6
+ /**
7
+ * argsParser: Parse a string argument from the command line as a boolean
8
+ */
9
+ const parseInputBool = (arg) => {
10
+ try {
11
+ return parseType({ type: 'boolean', value: arg });
12
+ } catch (e) {
13
+ console.error(e.message);
14
+ process.exit(1);
15
+ }
16
+ };
17
+
18
+ /**
19
+ * argsParser: Parse a comma-delimited string as an array
20
+ */
21
+ const parseInputList = (value) => {
22
+ return value.split(',');
23
+ };
24
+
25
+ /**
26
+ * hook: if encrpyt=true and key not provided, prompt for it
27
+ */
28
+ const promptEncryptionKey = async (thisCommand) => {
29
+ const opts = thisCommand.opts();
30
+
31
+ if (!opts.encrypt && opts.key) {
32
+ console.error('Key may not be present unless encryption is used');
33
+ process.exit(1);
34
+ }
35
+
36
+ // if encrypt is set but we have no key, prompt for it
37
+ if (opts.encrypt && !(opts.key && opts.key.length > 0)) {
38
+ try {
39
+ const answers = await inquirer.prompt([
40
+ {
41
+ type: 'password',
42
+ message: 'Please enter an encryption key',
43
+ name: 'key',
44
+ validate(key) {
45
+ if (key.length > 0) return true;
46
+
47
+ return 'Key must be present when using the encrypt option';
48
+ },
49
+ },
50
+ ]);
51
+ opts.key = answers.key;
52
+ } catch (e) {
53
+ console.error('Failed to get encryption key');
54
+ process.exit(1);
55
+ }
56
+ if (!opts.key) {
57
+ console.error('Failed to get encryption key');
58
+ process.exit(1);
59
+ }
60
+ }
61
+ };
62
+
63
+ /**
64
+ * hook: confirm that key has a value with a provided message
65
+ */
66
+ const confirmKeyValue = (key, value, message) => {
67
+ return async (thisCommand) => {
68
+ const opts = thisCommand.opts();
69
+
70
+ if (!opts[key] || opts[key] !== value) {
71
+ console.error(`Could not confirm key ${key}, halting operation.`);
72
+ process.exit(1);
73
+ }
74
+ const answers = await inquirer.prompt([
75
+ {
76
+ type: 'confirm',
77
+ message,
78
+ name: `confirm_${key}`,
79
+ },
80
+ ]);
81
+ if (!answers[`confirm_${key}`]) {
82
+ process.exit(0);
83
+ }
84
+ };
85
+ };
86
+
87
+ module.exports = {
88
+ parseInputList,
89
+ parseInputBool,
90
+ promptEncryptionKey,
91
+ confirmKeyValue,
92
+ };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const bytesPerKb = 1024;
4
+ const sizes = ['B ', 'KB', 'MB', 'GB', 'TB', 'PB'];
5
+
6
+ const readableBytes = (bytes, decimals = 1, padStart = 0) => {
7
+ if (!bytes) {
8
+ return '0';
9
+ }
10
+ const i = Math.floor(Math.log(bytes) / Math.log(bytesPerKb));
11
+ const result = `${parseFloat((bytes / bytesPerKb ** i).toFixed(decimals))} ${sizes[i].padStart(
12
+ 2
13
+ )}`;
14
+
15
+ return result.padStart(padStart);
16
+ };
17
+
18
+ module.exports = {
19
+ readableBytes,
20
+ };
@@ -3,7 +3,7 @@ import { Attribute, ConfigurableOption, PrivateOption } from './base';
3
3
  import { GetAttributesByType, GetAttributesValues } from './utils';
4
4
 
5
5
  export type BasicRelationsType = 'oneToOne' | 'oneToMany' | 'manyToOne' | 'manyToMany';
6
- export type PolymorphicRelationsType = 'morphToOne' | 'morphToMany' | 'morphOne' | 'morphMany';
6
+ export type PolymorphicRelationsType = 'morphToOne' | 'morphToMany' | 'morphOne' | 'morphMany';
7
7
  export type RelationsType = BasicRelationsType | PolymorphicRelationsType;
8
8
 
9
9
  export interface BasicRelationAttributeProperties<
@@ -17,16 +17,14 @@ export interface BasicRelationAttributeProperties<
17
17
  mappedBy?: RelationsKeysFromTo<T, S>;
18
18
  }
19
19
 
20
- export interface PolymorphicRelationAttributeProperties<
21
- R extends RelationsType,
22
- > {
20
+ export interface PolymorphicRelationAttributeProperties<R extends RelationsType> {
23
21
  relation: R;
24
22
  }
25
23
 
26
24
  export type RelationAttribute<
27
25
  S extends SchemaUID,
28
26
  R extends RelationsType,
29
- T extends R extends PolymorphicRelationsType ? never: SchemaUID = never
27
+ T extends R extends PolymorphicRelationsType ? never : SchemaUID = never
30
28
  > = Attribute<'relation'> &
31
29
  // Properties
32
30
  (R extends BasicRelationsType
@@ -34,22 +32,21 @@ export type RelationAttribute<
34
32
  : PolymorphicRelationAttributeProperties<R>) &
35
33
  // Options
36
34
  ConfigurableOption &
37
- PrivateOption
35
+ PrivateOption;
38
36
 
39
37
  export type RelationsKeysFromTo<
40
38
  TTarget extends SchemaUID,
41
39
  TSource extends SchemaUID
42
40
  > = keyof PickRelationsFromTo<TTarget, TSource>;
43
41
 
44
- export type PickRelationsFromTo<TTarget extends SchemaUID, TSource extends SchemaUID> = GetAttributesByType<
45
- TTarget,
46
- 'relation',
47
- { target: TSource }
48
- >;
42
+ export type PickRelationsFromTo<
43
+ TTarget extends SchemaUID,
44
+ TSource extends SchemaUID
45
+ > = GetAttributesByType<TTarget, 'relation', { target: TSource }>;
49
46
 
50
47
  export type RelationPluralityModifier<
51
48
  TRelation extends RelationsType,
52
- TValue extends Object
49
+ TValue extends Record<string, unknown>
53
50
  > = TRelation extends `${string}Many` ? TValue[] : TValue;
54
51
 
55
52
  export type RelationValue<
@@ -1,5 +1,5 @@
1
1
  import { Attribute, ComponentAttribute } from '../attributes';
2
- import { KeysBy, StringRecord } from '../../utils';
2
+ import { KeysBy, SchemaUID, StringRecord } from '../../utils';
3
3
 
4
4
  /**
5
5
  * Literal union type representing the possible natures of a content type
@@ -98,6 +98,11 @@ export interface PluginOptions {}
98
98
  export interface ContentTypeSchema extends Schema {
99
99
  modelType: 'contentType';
100
100
 
101
+ /**
102
+ * Unique identifier of the schema
103
+ */
104
+ uid: SchemaUID;
105
+
101
106
  /**
102
107
  * Determine the type of the content type (single-type or collection-type)
103
108
  */
@@ -2,8 +2,8 @@ import type Koa from 'koa';
2
2
  import { Database } from '@strapi/database';
3
3
 
4
4
  import type { StringMap } from './utils';
5
- import type { GenericController } from '../../../core-api/controller'
6
- import type { GenericService } from '../../../core-api/service'
5
+ import type { GenericController } from '../../../core-api/controller';
6
+ import type { GenericService } from '../../../core-api/service';
7
7
 
8
8
  // TODO move custom fields types to a separate file
9
9
  interface CustomFieldServerOptions {
@@ -92,9 +92,16 @@ export interface Strapi {
92
92
  */
93
93
  contentType(uid: string): any;
94
94
 
95
+ /**
96
+ * Getter for the Strapi component container
97
+ *
98
+ * It returns all the registered components
99
+ */
100
+ readonly components: any;
101
+
95
102
  /**
96
103
  * The custom fields registry
97
- *
104
+ *
98
105
  * It returns the custom fields interface
99
106
  */
100
107
  readonly customFields: CustomFields;
@@ -361,7 +368,6 @@ export interface Strapi {
361
368
  */
362
369
  log: any;
363
370
 
364
-
365
371
  /**
366
372
  * Used to manage cron within Strapi
367
373
  */
@@ -1,8 +1,8 @@
1
- import { Service,GenericService } from '../core-api/service';
1
+ import { Service, GenericService } from '../core-api/service';
2
2
  import { Controller, GenericController } from '../core-api/controller';
3
3
  import { Middleware } from '../middlewares';
4
4
  import { Policy } from '../core/registries/policies';
5
- import { Strapi } from '@strapi/strapi';
5
+ import { Strapi } from './core/strapi';
6
6
 
7
7
  type ControllerConfig<T extends Controller = Controller> = T;
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/strapi",
3
- "version": "4.5.2",
3
+ "version": "4.6.0-alpha.0",
4
4
  "description": "An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite",
5
5
  "keywords": [
6
6
  "strapi",
@@ -80,18 +80,19 @@
80
80
  "dependencies": {
81
81
  "@koa/cors": "3.4.3",
82
82
  "@koa/router": "10.1.1",
83
- "@strapi/admin": "4.5.2",
84
- "@strapi/database": "4.5.2",
85
- "@strapi/generate-new": "4.5.2",
86
- "@strapi/generators": "4.5.2",
87
- "@strapi/logger": "4.5.2",
88
- "@strapi/permissions": "4.5.2",
89
- "@strapi/plugin-content-manager": "4.5.2",
90
- "@strapi/plugin-content-type-builder": "4.5.2",
91
- "@strapi/plugin-email": "4.5.2",
92
- "@strapi/plugin-upload": "4.5.2",
93
- "@strapi/typescript-utils": "4.5.2",
94
- "@strapi/utils": "4.5.2",
83
+ "@strapi/admin": "4.6.0-alpha.0",
84
+ "@strapi/data-transfer": "4.6.0-alpha.0",
85
+ "@strapi/database": "4.6.0-alpha.0",
86
+ "@strapi/generate-new": "4.6.0-alpha.0",
87
+ "@strapi/generators": "4.6.0-alpha.0",
88
+ "@strapi/logger": "4.6.0-alpha.0",
89
+ "@strapi/permissions": "4.6.0-alpha.0",
90
+ "@strapi/plugin-content-manager": "4.6.0-alpha.0",
91
+ "@strapi/plugin-content-type-builder": "4.6.0-alpha.0",
92
+ "@strapi/plugin-email": "4.6.0-alpha.0",
93
+ "@strapi/plugin-upload": "4.6.0-alpha.0",
94
+ "@strapi/typescript-utils": "4.6.0-alpha.0",
95
+ "@strapi/utils": "4.6.0-alpha.0",
95
96
  "bcryptjs": "2.4.3",
96
97
  "boxen": "5.1.2",
97
98
  "chalk": "4.1.2",
@@ -140,5 +141,5 @@
140
141
  "node": ">=14.19.1 <=18.x.x",
141
142
  "npm": ">=6.0.0"
142
143
  },
143
- "gitHead": "bcb1b7f472aae2556a9b59d59ee66d241c497a3e"
144
+ "gitHead": "b7a87dcffc6f44e18eedef92e354096ffe32ce0c"
144
145
  }