@strapi/strapi 4.3.7 → 4.4.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -86,7 +86,7 @@ Complete installation requirements can be found in the documentation under <a hr
86
86
 
87
87
  **Node:**
88
88
 
89
- - NodeJS >= 14 <= 16
89
+ - NodeJS >= 14 <= 18
90
90
  - NPM >= 6.x
91
91
 
92
92
  **Database:**
package/lib/Strapi.js CHANGED
@@ -22,6 +22,7 @@ const createCronService = require('./services/cron');
22
22
  const entityValidator = require('./services/entity-validator');
23
23
  const createTelemetry = require('./services/metrics');
24
24
  const createAuth = require('./services/auth');
25
+ const createContentAPI = require('./services/content-api');
25
26
  const createUpdateNotifier = require('./utils/update-notifier');
26
27
  const createStartupLogger = require('./utils/startup-logger');
27
28
  const { LIFECYCLES } = require('./utils/lifecycles');
@@ -74,7 +75,7 @@ class Strapi {
74
75
  // Load the app configuration from the dist directory
75
76
  const appConfig = loadConfiguration({ appDir: rootDirs.app, distDir: rootDirs.dist }, opts);
76
77
 
77
- // Instanciate the Strapi container
78
+ // Instantiate the Strapi container
78
79
  this.container = createContainer(this);
79
80
 
80
81
  // Register every Strapi registry in the container
@@ -89,6 +90,7 @@ class Strapi {
89
90
  this.container.register('plugins', pluginsRegistry(this));
90
91
  this.container.register('apis', apisRegistry(this));
91
92
  this.container.register('auth', createAuth(this));
93
+ this.container.register('content-api', createContentAPI(this));
92
94
  this.container.register('sanitizers', sanitizersRegistry(this));
93
95
 
94
96
  // Create a mapping of every useful directory (for the app, dist and static directories)
@@ -98,7 +100,7 @@ class Strapi {
98
100
  this.isLoaded = false;
99
101
  this.reload = this.reload();
100
102
 
101
- // Instanciate the Koa app & the HTTP server
103
+ // Instantiate the Koa app & the HTTP server
102
104
  this.server = createServer(this);
103
105
 
104
106
  // Strapi utils instanciation
@@ -188,6 +190,10 @@ class Strapi {
188
190
  return this.container.get('auth');
189
191
  }
190
192
 
193
+ get contentAPI() {
194
+ return this.container.get('content-api');
195
+ }
196
+
191
197
  get sanitizers() {
192
198
  return this.container.get('sanitizers');
193
199
  }
@@ -445,6 +451,8 @@ class Strapi {
445
451
  await this.server.initMiddlewares();
446
452
  await this.server.initRouting();
447
453
 
454
+ await this.contentAPI.permissions.registerActions();
455
+
448
456
  await this.runLifecyclesFunctions(LIFECYCLES.BOOTSTRAP);
449
457
 
450
458
  this.cron.start();
@@ -9,7 +9,7 @@ const CHUNK_SIZE = 100;
9
9
  * Will dump configurations to a file or stdout
10
10
  * @param {string} file filepath to use as output
11
11
  */
12
- module.exports = async function ({ file: filePath, pretty }) {
12
+ module.exports = async ({ file: filePath, pretty }) => {
13
13
  const output = filePath ? fs.createWriteStream(filePath) : process.stdout;
14
14
 
15
15
  const appContext = await strapi.compile();
@@ -21,7 +21,7 @@ module.exports = async function ({ file: filePath, pretty }) {
21
21
 
22
22
  const pageCount = Math.ceil(count / CHUNK_SIZE);
23
23
 
24
- for (let page = 0; page < pageCount; page++) {
24
+ for (let page = 0; page < pageCount; page += 1) {
25
25
  const results = await app
26
26
  .query('strapi::core-store')
27
27
  .findMany({ limit: CHUNK_SIZE, offset: page * CHUNK_SIZE, orderBy: 'key' });
@@ -40,7 +40,7 @@ module.exports = async (strapi) => {
40
40
 
41
41
  validateContentTypesUnicity(apis);
42
42
 
43
- for (const apiName in apis) {
43
+ for (const apiName of Object.keys(apis)) {
44
44
  strapi.container.get('apis').add(apiName, apis[apiName]);
45
45
  }
46
46
  };
@@ -59,7 +59,9 @@ const getEnabledPlugins = async (strapi) => {
59
59
  }
60
60
 
61
61
  const installedPlugins = {};
62
- for (const dep in strapi.config.get('info.dependencies', {})) {
62
+ const dependencies = strapi.config.get('info.dependencies', {});
63
+
64
+ for (const dep of Object.keys(dependencies)) {
63
65
  const packagePath = join(dep, 'package.json');
64
66
  let packageInfo;
65
67
  try {
@@ -34,10 +34,10 @@ const applyUserExtension = async (plugins) => {
34
34
  const extendedSchemas = await loadFiles(extensionsDir, '**/content-types/**/schema.json');
35
35
  const strapiServers = await loadFiles(extensionsDir, '**/strapi-server.js');
36
36
 
37
- for (const pluginName in plugins) {
37
+ for (const pluginName of Object.keys(plugins)) {
38
38
  const plugin = plugins[pluginName];
39
39
  // first: load json schema
40
- for (const ctName in plugin.contentTypes) {
40
+ for (const ctName of Object.keys(plugin.contentTypes)) {
41
41
  const extendedSchema = get([pluginName, 'content-types', ctName, 'schema'], extendedSchemas);
42
42
  if (extendedSchema) {
43
43
  plugin.contentTypes[ctName].schema = {
@@ -57,7 +57,7 @@ const applyUserExtension = async (plugins) => {
57
57
  const applyUserConfig = async (plugins) => {
58
58
  const userPluginsConfig = await getUserPluginsConfig();
59
59
 
60
- for (const pluginName in plugins) {
60
+ for (const pluginName of Object.keys(plugins)) {
61
61
  const plugin = plugins[pluginName];
62
62
  const userPluginConfig = getOr({}, `${pluginName}.config`, userPluginsConfig);
63
63
  const defaultConfig =
@@ -82,7 +82,7 @@ const loadPlugins = async (strapi) => {
82
82
 
83
83
  strapi.config.set('enabledPlugins', enabledPlugins);
84
84
 
85
- for (const pluginName in enabledPlugins) {
85
+ for (const pluginName of Object.keys(enabledPlugins)) {
86
86
  const enabledPlugin = enabledPlugins[pluginName];
87
87
 
88
88
  const serverEntrypointPath = join(enabledPlugin.pathToPlugin, 'strapi-server.js');
@@ -100,7 +100,7 @@ const loadPlugins = async (strapi) => {
100
100
  await applyUserConfig(plugins);
101
101
  await applyUserExtension(plugins);
102
102
 
103
- for (const pluginName in plugins) {
103
+ for (const pluginName of Object.keys(plugins)) {
104
104
  strapi.container.get('plugins').add(pluginName, plugins[pluginName]);
105
105
  }
106
106
  };
@@ -5,7 +5,7 @@ const { createContentType } = require('../domain/content-type');
5
5
  const { addNamespace, hasNamespace } = require('../utils');
6
6
 
7
7
  const validateKeySameToSingularName = (contentTypes) => {
8
- for (const ctName in contentTypes) {
8
+ for (const ctName of Object.keys(contentTypes)) {
9
9
  const contentType = contentTypes[ctName];
10
10
 
11
11
  if (ctName !== contentType.schema.info.singularName) {
@@ -63,7 +63,7 @@ const contentTypesRegistry = () => {
63
63
  add(namespace, newContentTypes) {
64
64
  validateKeySameToSingularName(newContentTypes);
65
65
 
66
- for (const rawCtName in newContentTypes) {
66
+ for (const rawCtName of Object.keys(newContentTypes)) {
67
67
  const uid = addNamespace(rawCtName, namespace);
68
68
 
69
69
  if (has(uid, contentTypes)) {
@@ -48,7 +48,7 @@ const controllersRegistry = () => {
48
48
  const filteredControllers = pickBy((_, uid) => hasNamespace(uid, namespace))(controllers);
49
49
 
50
50
  const map = {};
51
- for (const uid in filteredControllers) {
51
+ for (const uid of Object.keys(filteredControllers)) {
52
52
  Object.defineProperty(map, uid, {
53
53
  enumerable: true,
54
54
  get: () => {
@@ -78,7 +78,7 @@ const controllersRegistry = () => {
78
78
  * @returns
79
79
  */
80
80
  add(namespace, newControllers) {
81
- for (const controllerName in newControllers) {
81
+ for (const controllerName of Object.keys(newControllers)) {
82
82
  const controller = newControllers[controllerName];
83
83
  const uid = addNamespace(controllerName, namespace);
84
84
 
@@ -54,7 +54,7 @@ const hooksRegistry = () => {
54
54
  * @returns
55
55
  */
56
56
  add(namespace, hooks) {
57
- for (const hookName in hooks) {
57
+ for (const hookName of Object.keys(hooks)) {
58
58
  const hook = hooks[hookName];
59
59
  const uid = addNamespace(hookName, namespace);
60
60
 
@@ -54,8 +54,8 @@ const middlewaresRegistry = () => {
54
54
  * @param {{ [key: string]: Middleware }} newMiddlewares
55
55
  * @returns
56
56
  */
57
- add(namespace, rawMiddlewares) {
58
- for (const middlewareName in rawMiddlewares) {
57
+ add(namespace, rawMiddlewares = {}) {
58
+ for (const middlewareName of Object.keys(rawMiddlewares)) {
59
59
  const middleware = rawMiddlewares[middlewareName];
60
60
  const uid = addNamespace(middlewareName, namespace);
61
61
 
@@ -55,7 +55,7 @@ const policiesRegistry = () => {
55
55
  * @returns
56
56
  */
57
57
  add(namespace, newPolicies) {
58
- for (const policyName in newPolicies) {
58
+ for (const policyName of Object.keys(newPolicies)) {
59
59
  const policy = newPolicies[policyName];
60
60
  const uid = addNamespace(policyName, namespace);
61
61
 
@@ -48,7 +48,7 @@ const servicesRegistry = (strapi) => {
48
48
 
49
49
  // create lazy accessor to avoid instantiating the services;
50
50
  const map = {};
51
- for (const uid in filteredServices) {
51
+ for (const uid of Object.keys(filteredServices)) {
52
52
  Object.defineProperty(map, uid, {
53
53
  enumerable: true,
54
54
  get: () => {
@@ -78,7 +78,7 @@ const servicesRegistry = (strapi) => {
78
78
  * @returns
79
79
  */
80
80
  add(namespace, newServices) {
81
- for (const serviceName in newServices) {
81
+ for (const serviceName of Object.keys(newServices)) {
82
82
  const service = newServices[serviceName];
83
83
  const uid = addNamespace(serviceName, namespace);
84
84
 
@@ -56,7 +56,7 @@ const transformEntry = (entry, type) => {
56
56
 
57
57
  const attributeValues = {};
58
58
 
59
- for (const key in properties) {
59
+ for (const key of Object.keys(properties)) {
60
60
  const property = properties[key];
61
61
  const attribute = type && type.attributes[key];
62
62
 
@@ -32,6 +32,7 @@ const createAuthentication = () => {
32
32
 
33
33
  return this;
34
34
  },
35
+
35
36
  async authenticate(ctx, next) {
36
37
  const { route } = ctx.state;
37
38
 
@@ -47,7 +48,7 @@ const createAuthentication = () => {
47
48
  for (const strategy of strategiesToUse) {
48
49
  const result = await strategy.authenticate(ctx);
49
50
 
50
- const { authenticated = false, error = null, credentials } = result || {};
51
+ const { authenticated = false, credentials, ability = null, error = null } = result || {};
51
52
 
52
53
  if (error !== null) {
53
54
  return ctx.unauthorized(error);
@@ -58,6 +59,7 @@ const createAuthentication = () => {
58
59
  ctx.state.auth = {
59
60
  strategy,
60
61
  credentials,
62
+ ability,
61
63
  };
62
64
 
63
65
  return next();
@@ -66,6 +68,7 @@ const createAuthentication = () => {
66
68
 
67
69
  return ctx.unauthorized('Missing or invalid credentials');
68
70
  },
71
+
69
72
  async verify(auth, config = {}) {
70
73
  if (config === false) {
71
74
  return;
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const instantiatePermissionsUtilities = require('./permissions');
5
+
6
+ const transformRoutePrefixFor = (pluginName) => (route) => {
7
+ const prefix = route.config && route.config.prefix;
8
+ const path = prefix !== undefined ? `${prefix}${route.path}` : `/${pluginName}${route.path}`;
9
+
10
+ return {
11
+ ...route,
12
+ path,
13
+ };
14
+ };
15
+
16
+ /**
17
+ * Create a content API container that holds logic, tools and utils. (eg: permissions, ...)
18
+ */
19
+ const createContentAPI = (strapi) => {
20
+ const getRoutesMap = async () => {
21
+ const routesMap = {};
22
+
23
+ _.forEach(strapi.api, (api, apiName) => {
24
+ const routes = _.flatMap(api.routes, (route) => {
25
+ if (_.has(route, 'routes')) {
26
+ return route.routes;
27
+ }
28
+
29
+ return route;
30
+ }).filter((route) => route.info.type === 'content-api');
31
+
32
+ if (routes.length === 0) {
33
+ return;
34
+ }
35
+
36
+ const apiPrefix = strapi.config.get('api.rest.prefix');
37
+ routesMap[`api::${apiName}`] = routes.map((route) => ({
38
+ ...route,
39
+ path: `${apiPrefix}${route.path}`,
40
+ }));
41
+ });
42
+
43
+ _.forEach(strapi.plugins, (plugin, pluginName) => {
44
+ const transformPrefix = transformRoutePrefixFor(pluginName);
45
+
46
+ const routes = _.flatMap(plugin.routes, (route) => {
47
+ if (_.has(route, 'routes')) {
48
+ return route.routes.map(transformPrefix);
49
+ }
50
+
51
+ return transformPrefix(route);
52
+ }).filter((route) => route.info.type === 'content-api');
53
+
54
+ if (routes.length === 0) {
55
+ return;
56
+ }
57
+
58
+ const apiPrefix = strapi.config.get('api.rest.prefix');
59
+ routesMap[`plugin::${pluginName}`] = routes.map((route) => ({
60
+ ...route,
61
+ path: `${apiPrefix}${route.path}`,
62
+ }));
63
+ });
64
+
65
+ return routesMap;
66
+ };
67
+
68
+ return {
69
+ permissions: instantiatePermissionsUtilities(strapi),
70
+ getRoutesMap,
71
+ };
72
+ };
73
+
74
+ module.exports = createContentAPI;
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ const permissions = require('@strapi/permissions');
4
+
5
+ module.exports = ({ providers }) => permissions.engine.new({ providers });
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const { createActionProvider, createConditionProvider } = require('./providers');
5
+ const createPermissionEngine = require('./engine');
6
+
7
+ /**
8
+ * Creates an handler that checks if the permission's action exists in the action registry
9
+ */
10
+ const createValidatePermissionHandler =
11
+ (actionProvider) =>
12
+ ({ permission }) => {
13
+ const action = actionProvider.get(permission.action);
14
+
15
+ // If the action isn't registered into the action provider, then ignore the permission and warn the user
16
+ if (!action) {
17
+ strapi.log.debug(
18
+ `Unknown action "${permission.action}" supplied when registering a new permission`
19
+ );
20
+ return false;
21
+ }
22
+ };
23
+
24
+ /**
25
+ * Create instances of providers and permission engine for the core content-API service.
26
+ * Also, expose utilities to get informations about available actions and such.
27
+ *
28
+ * @param {Strapi.Strapi} strapi
29
+ */
30
+ module.exports = (strapi) => {
31
+ // NOTE: Here we define both an action and condition provider,
32
+ // but at the moment, we're only using the action one.
33
+ const providers = {
34
+ action: createActionProvider(),
35
+ condition: createConditionProvider(),
36
+ };
37
+
38
+ /**
39
+ * Get a tree representation of the available Content API actions
40
+ * based on the methods of the Content API controllers.
41
+ *
42
+ * @note Only actions bound to a content-API route are returned.
43
+ *
44
+ * @return {{ [api: string]: { [controller: string]: string[] }}}
45
+ */
46
+ const getActionsMap = () => {
47
+ const actionMap = {};
48
+
49
+ /**
50
+ * Check if a controller's action is bound to the
51
+ * content-api by looking at a potential __type__ symbol
52
+ *
53
+ * @param {object} action
54
+ *
55
+ * @return {boolean}
56
+ */
57
+ const isContentApi = (action) => {
58
+ if (!_.has(action, Symbol.for('__type__'))) {
59
+ return false;
60
+ }
61
+
62
+ return action[Symbol.for('__type__')].includes('content-api');
63
+ };
64
+
65
+ /**
66
+ * Register actions from a specific API source into the result tree
67
+ *
68
+ * @param {{ [apiName]: { controllers: { [controller]: object } }}} apis The API container
69
+ * @param {string} source The prefix to use in front the API name
70
+ *
71
+ * @return {void}
72
+ */
73
+ const registerAPIsActions = (apis, source) => {
74
+ _.forEach(apis, (api, apiName) => {
75
+ const controllers = _.reduce(
76
+ api.controllers,
77
+ (acc, controller, controllerName) => {
78
+ const contentApiActions = _.pickBy(controller, isContentApi);
79
+
80
+ if (_.isEmpty(contentApiActions)) {
81
+ return acc;
82
+ }
83
+
84
+ acc[controllerName] = Object.keys(contentApiActions);
85
+
86
+ return acc;
87
+ },
88
+ {}
89
+ );
90
+
91
+ if (!_.isEmpty(controllers)) {
92
+ actionMap[`${source}::${apiName}`] = { controllers };
93
+ }
94
+ });
95
+ };
96
+
97
+ registerAPIsActions(strapi.api, 'api');
98
+ registerAPIsActions(strapi.plugins, 'plugin');
99
+
100
+ return actionMap;
101
+ };
102
+
103
+ /**
104
+ * Register all the content-API's controllers actions into the action provider.
105
+ * This method make use of the {@link getActionsMap} to generate the list of actions to register.
106
+ *
107
+ * @return {void}
108
+ */
109
+ const registerActions = async () => {
110
+ const actionsMap = getActionsMap();
111
+
112
+ // For each API
113
+ for (const [api, value] of Object.entries(actionsMap)) {
114
+ const { controllers } = value;
115
+
116
+ // Register controllers methods as actions
117
+ for (const [controller, actions] of Object.entries(controllers)) {
118
+ // Register each action individually
119
+ await Promise.all(
120
+ actions.map((action) => {
121
+ const actionUID = `${api}.${controller}.${action}`;
122
+
123
+ return providers.action.register(actionUID, {
124
+ api,
125
+ controller,
126
+ action,
127
+ uid: actionUID,
128
+ });
129
+ })
130
+ );
131
+ }
132
+ }
133
+ };
134
+
135
+ // Create an instance of a content-API permission engine
136
+ // and binds a custom validation handler to it
137
+ const engine = createPermissionEngine({ providers }).on(
138
+ 'before-format::validate.permission',
139
+ createValidatePermissionHandler(providers.action)
140
+ );
141
+
142
+ return {
143
+ engine,
144
+ providers,
145
+ registerActions,
146
+ getActionsMap,
147
+ };
148
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { providerFactory } = require('@strapi/utils');
4
+
5
+ module.exports = (options = {}) => {
6
+ const provider = providerFactory(options);
7
+
8
+ return {
9
+ ...provider,
10
+
11
+ async register(action, payload) {
12
+ if (strapi.isLoaded) {
13
+ throw new Error(`You can't register new actions outside the bootstrap function.`);
14
+ }
15
+
16
+ return provider.register(action, payload);
17
+ },
18
+ };
19
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { providerFactory } = require('@strapi/utils');
4
+
5
+ module.exports = (options = {}) => {
6
+ const provider = providerFactory(options);
7
+
8
+ return {
9
+ ...provider,
10
+
11
+ async register(condition) {
12
+ if (strapi.isLoaded) {
13
+ throw new Error(`You can't register new conditions outside the bootstrap function.`);
14
+ }
15
+
16
+ return provider.register(condition.name, condition);
17
+ },
18
+ };
19
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const createActionProvider = require('./action');
4
+ const createConditionProvider = require('./condition');
5
+
6
+ module.exports = {
7
+ createActionProvider,
8
+ createConditionProvider,
9
+ };
@@ -9,7 +9,7 @@ const createCronService = () => {
9
9
 
10
10
  return {
11
11
  add(tasks = {}) {
12
- for (const taskExpression in tasks) {
12
+ for (const taskExpression of Object.keys(tasks)) {
13
13
  const taskValue = tasks[taskExpression];
14
14
 
15
15
  let fn;
@@ -18,11 +18,11 @@ const omitComponentData = (contentType, data) => {
18
18
 
19
19
  // NOTE: we could generalize the logic to allow CRUD of relation directly in the DB layer
20
20
  const createComponents = async (uid, data) => {
21
- const { attributes } = strapi.getModel(uid);
21
+ const { attributes = {} } = strapi.getModel(uid);
22
22
 
23
23
  const componentBody = {};
24
24
 
25
- for (const attributeName in attributes) {
25
+ for (const attributeName of Object.keys(attributes)) {
26
26
  const attribute = attributes[attributeName];
27
27
 
28
28
  if (!has(attributeName, data) || !contentTypesUtils.isComponentAttribute(attribute)) {
@@ -118,11 +118,11 @@ const getComponents = async (uid, entity) => {
118
118
  create or update
119
119
  */
120
120
  const updateComponents = async (uid, entityToUpdate, data) => {
121
- const { attributes } = strapi.getModel(uid);
121
+ const { attributes = {} } = strapi.getModel(uid);
122
122
 
123
123
  const componentBody = {};
124
124
 
125
- for (const attributeName in attributes) {
125
+ for (const attributeName of Object.keys(attributes)) {
126
126
  const attribute = attributes[attributeName];
127
127
 
128
128
  if (!has(attributeName, data)) {
@@ -275,9 +275,9 @@ const deleteOldDZComponents = async (uid, entityToUpdate, attributeName, dynamic
275
275
  };
276
276
 
277
277
  const deleteComponents = async (uid, entityToDelete) => {
278
- const { attributes } = strapi.getModel(uid);
278
+ const { attributes = {} } = strapi.getModel(uid);
279
279
 
280
- for (const attributeName in attributes) {
280
+ for (const attributeName of Object.keys(attributes)) {
281
281
  const attribute = attributes[attributeName];
282
282
 
283
283
  if (attribute.type === 'component') {
@@ -14,51 +14,56 @@ const { isMediaAttribute, isScalarAttribute, getWritableAttributes } = strapiUti
14
14
  const { ValidationError } = strapiUtils.errors;
15
15
 
16
16
  const addMinMax = (validator, { attr, updatedAttribute }) => {
17
+ let nextValidator = validator;
18
+
17
19
  if (
18
20
  Number.isInteger(attr.min) &&
19
21
  (attr.required || (Array.isArray(updatedAttribute.value) && updatedAttribute.value.length > 0))
20
22
  ) {
21
- validator = validator.min(attr.min);
23
+ nextValidator = nextValidator.min(attr.min);
22
24
  }
23
25
  if (Number.isInteger(attr.max)) {
24
- validator = validator.max(attr.max);
26
+ nextValidator = nextValidator.max(attr.max);
25
27
  }
26
- return validator;
28
+ return nextValidator;
27
29
  };
28
30
 
29
- const addRequiredValidation =
30
- (createOrUpdate) =>
31
- (validator, { attr: { required } }) => {
31
+ const addRequiredValidation = (createOrUpdate) => {
32
+ return (validator, { attr: { required } }) => {
33
+ let nextValidator = validator;
32
34
  if (required) {
33
35
  if (createOrUpdate === 'creation') {
34
- validator = validator.notNil();
36
+ nextValidator = nextValidator.notNil();
35
37
  } else if (createOrUpdate === 'update') {
36
- validator = validator.notNull();
38
+ nextValidator = nextValidator.notNull();
37
39
  }
38
40
  } else {
39
- validator = validator.nullable();
41
+ nextValidator = nextValidator.nullable();
40
42
  }
41
- return validator;
43
+ return nextValidator;
42
44
  };
45
+ };
46
+
47
+ const addDefault = (createOrUpdate) => {
48
+ return (validator, { attr }) => {
49
+ let nextValidator = validator;
43
50
 
44
- const addDefault =
45
- (createOrUpdate) =>
46
- (validator, { attr }) => {
47
51
  if (createOrUpdate === 'creation') {
48
52
  if (
49
53
  ((attr.type === 'component' && attr.repeatable) || attr.type === 'dynamiczone') &&
50
54
  !attr.required
51
55
  ) {
52
- validator = validator.default([]);
56
+ nextValidator = nextValidator.default([]);
53
57
  } else {
54
- validator = validator.default(attr.default);
58
+ nextValidator = nextValidator.default(attr.default);
55
59
  }
56
60
  } else {
57
- validator = validator.default(undefined);
61
+ nextValidator = nextValidator.default(undefined);
58
62
  }
59
63
 
60
- return validator;
64
+ return nextValidator;
61
65
  };
66
+ };
62
67
 
63
68
  const preventCast = (validator) => validator.transform((val, originalVal) => originalVal);
64
69
 
@@ -22,7 +22,7 @@ const createMiddleware = ({ sendEvent }) => {
22
22
  sendEvent('didReceiveRequest', { url: ctx.request.url });
23
23
 
24
24
  // Increase counter.
25
- _state.counter++;
25
+ _state.counter += 1;
26
26
  }
27
27
  }
28
28
 
@@ -50,7 +50,7 @@ const registerAdminRoutes = (strapi) => {
50
50
  * @param {import('../../').Strapi} strapi
51
51
  */
52
52
  const registerPluginRoutes = (strapi) => {
53
- for (const pluginName in strapi.plugins) {
53
+ for (const pluginName of Object.keys(strapi.plugins)) {
54
54
  const plugin = strapi.plugins[pluginName];
55
55
 
56
56
  const generateRouteScope = createRouteScopeGenerator(`plugin::${pluginName}`);
@@ -86,7 +86,7 @@ const registerPluginRoutes = (strapi) => {
86
86
  * @param {import('../../').Strapi} strapi
87
87
  */
88
88
  const registerAPIRoutes = (strapi) => {
89
- for (const apiName in strapi.api) {
89
+ for (const apiName of Object.keys(strapi.api)) {
90
90
  const api = strapi.api[apiName];
91
91
 
92
92
  const generateRouteScope = createRouteScopeGenerator(`api::${apiName}`);
@@ -27,7 +27,7 @@ module.exports = async (uid, entity, files) => {
27
27
  let tmpModel = modelDef;
28
28
  let modelUID = uid;
29
29
 
30
- for (let i = 0; i < path.length; i++) {
30
+ for (let i = 0; i < path.length; i += 1) {
31
31
  if (!tmpModel) return {};
32
32
  const part = path[i];
33
33
  const attr = tmpModel.attributes[part];
@@ -26,7 +26,7 @@ module.exports = class WorkerQueue {
26
26
  enqueue(payload) {
27
27
  debug('Enqueue event in worker queue');
28
28
  if (this.running < this.concurrency) {
29
- this.running++;
29
+ this.running += 1;
30
30
  this.execute(payload);
31
31
  } else {
32
32
  this.queue.unshift(payload);
@@ -40,7 +40,7 @@ module.exports = class WorkerQueue {
40
40
  if (payload) {
41
41
  this.execute(payload);
42
42
  } else {
43
- this.running--;
43
+ this.running -= 1;
44
44
  }
45
45
  }
46
46
 
@@ -24,6 +24,11 @@ export interface Strapi {
24
24
  */
25
25
  readonly auth: any;
26
26
 
27
+ /**
28
+ * Getter for the Strapi content API container
29
+ */
30
+ readonly contentAPI: any;
31
+
27
32
  /**
28
33
  * Getter for the Strapi sanitizers container
29
34
  */
@@ -1,10 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  module.exports = (path) => {
4
- if (typeof path !== 'string') throw new Error('admin.url must be a string');
5
- if (path === '' || path === '/') return '/';
4
+ let tmpPath = path;
5
+ if (typeof tmpPath !== 'string') throw new Error('admin.url must be a string');
6
+ if (tmpPath === '' || tmpPath === '/') return '/';
6
7
 
7
- if (path[0] != '/') path = `/${path}`;
8
- if (path[path.length - 1] != '/') path += '/';
9
- return path;
8
+ if (tmpPath[0] !== '/') tmpPath = `/${tmpPath}`;
9
+ if (tmpPath[tmpPath.length - 1] !== '/') tmpPath += '/';
10
+ return tmpPath;
10
11
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/strapi",
3
- "version": "4.3.7",
3
+ "version": "4.4.0-beta.1",
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,17 +80,18 @@
80
80
  "dependencies": {
81
81
  "@koa/cors": "3.4.1",
82
82
  "@koa/router": "10.1.1",
83
- "@strapi/admin": "4.3.7",
84
- "@strapi/database": "4.3.7",
85
- "@strapi/generate-new": "4.3.7",
86
- "@strapi/generators": "4.3.7",
87
- "@strapi/logger": "4.3.7",
88
- "@strapi/plugin-content-manager": "4.3.7",
89
- "@strapi/plugin-content-type-builder": "4.3.7",
90
- "@strapi/plugin-email": "4.3.7",
91
- "@strapi/plugin-upload": "4.3.7",
92
- "@strapi/typescript-utils": "4.3.7",
93
- "@strapi/utils": "4.3.7",
83
+ "@strapi/admin": "4.4.0-beta.1",
84
+ "@strapi/database": "4.4.0-beta.1",
85
+ "@strapi/generate-new": "4.4.0-beta.1",
86
+ "@strapi/generators": "4.4.0-beta.1",
87
+ "@strapi/logger": "4.4.0-beta.1",
88
+ "@strapi/permissions": "4.4.0-beta.1",
89
+ "@strapi/plugin-content-manager": "4.4.0-beta.1",
90
+ "@strapi/plugin-content-type-builder": "4.4.0-beta.1",
91
+ "@strapi/plugin-email": "4.4.0-beta.1",
92
+ "@strapi/plugin-upload": "4.4.0-beta.1",
93
+ "@strapi/typescript-utils": "4.4.0-beta.1",
94
+ "@strapi/utils": "4.4.0-beta.1",
94
95
  "bcryptjs": "2.4.3",
95
96
  "boxen": "5.1.2",
96
97
  "chalk": "4.1.2",
@@ -136,8 +137,8 @@
136
137
  "typescript": "4.6.2"
137
138
  },
138
139
  "engines": {
139
- "node": ">=14.19.1 <=16.x.x",
140
+ "node": ">=14.19.1 <=18.x.x",
140
141
  "npm": ">=6.0.0"
141
142
  },
142
- "gitHead": "73f523b98322cea8992c72977b94a73a624d2e79"
143
+ "gitHead": "cae16f7f259fa4473a55e8fea57839cda98f34ae"
143
144
  }