directus 9.22.1 → 9.22.3

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/controllers/extensions.js +4 -4
  2. package/dist/controllers/utils.js +4 -1
  3. package/dist/database/helpers/fn/dialects/mysql.d.ts +8 -8
  4. package/dist/database/helpers/fn/dialects/mysql.js +16 -22
  5. package/dist/database/helpers/schema/dialects/mssql.d.ts +1 -0
  6. package/dist/database/helpers/schema/dialects/mssql.js +10 -0
  7. package/dist/database/helpers/schema/dialects/mysql.js +1 -1
  8. package/dist/database/helpers/schema/types.d.ts +1 -0
  9. package/dist/database/helpers/schema/types.js +6 -1
  10. package/dist/database/run-ast.js +16 -26
  11. package/dist/env.js +2 -0
  12. package/dist/extensions.d.ts +3 -2
  13. package/dist/extensions.js +45 -19
  14. package/dist/flows.js +2 -0
  15. package/dist/operations/exec/index.js +3 -0
  16. package/dist/operations/item-delete/index.js +1 -1
  17. package/dist/operations/item-delete/index.test.d.ts +1 -0
  18. package/dist/operations/item-read/index.js +1 -1
  19. package/dist/operations/item-read/index.test.d.ts +1 -0
  20. package/dist/operations/mail/index.js +4 -1
  21. package/dist/services/import-export.js +2 -0
  22. package/dist/services/roles.js +1 -1
  23. package/dist/services/server.js +5 -2
  24. package/dist/utils/apply-function-to-column-name.test.d.ts +1 -0
  25. package/dist/utils/apply-query.d.ts +2 -2
  26. package/dist/utils/apply-query.js +4 -6
  27. package/dist/utils/get-cache-key.js +6 -7
  28. package/dist/utils/get-date-formatted.test.d.ts +1 -0
  29. package/dist/utils/get-graphql-query-and-variables.d.ts +2 -0
  30. package/dist/utils/get-graphql-query-and-variables.js +10 -0
  31. package/dist/utils/get-graphql-query-and-variables.test.d.ts +1 -0
  32. package/dist/utils/get-permissions.js +13 -6
  33. package/dist/utils/md.test.d.ts +1 -0
  34. package/dist/utils/sanitize-query.test.d.ts +1 -0
  35. package/dist/utils/stall.test.d.ts +1 -0
  36. package/dist/utils/strip-function.test.d.ts +1 -0
  37. package/dist/utils/user-name.test.d.ts +1 -0
  38. package/dist/utils/validate-env.test.d.ts +1 -0
  39. package/dist/utils/validate-query.js +3 -3
  40. package/package.json +50 -52
@@ -7,16 +7,16 @@ const express_1 = require("express");
7
7
  const async_handler_1 = __importDefault(require("../utils/async-handler"));
8
8
  const exceptions_1 = require("../exceptions");
9
9
  const extensions_1 = require("../extensions");
10
- const respond_1 = require("../middleware/respond");
11
- const utils_1 = require("@directus/shared/utils");
12
- const constants_1 = require("@directus/shared/constants");
13
10
  const ms_1 = __importDefault(require("ms"));
14
11
  const env_1 = __importDefault(require("../env"));
15
12
  const get_cache_headers_1 = require("../utils/get-cache-headers");
13
+ const respond_1 = require("../middleware/respond");
14
+ const utils_1 = require("@directus/shared/utils");
15
+ const constants_1 = require("@directus/shared/constants");
16
16
  const router = (0, express_1.Router)();
17
17
  router.get('/:type', (0, async_handler_1.default)(async (req, res, next) => {
18
18
  const type = (0, utils_1.depluralize)(req.params.type);
19
- if (!(0, utils_1.isIn)(type, constants_1.APP_OR_HYBRID_EXTENSION_TYPES)) {
19
+ if (!(0, utils_1.isIn)(type, constants_1.EXTENSION_TYPES)) {
20
20
  throw new exceptions_1.RouteNotFoundException(req.path);
21
21
  }
22
22
  const extensionManager = (0, extensions_1.getExtensionManager)();
@@ -14,6 +14,7 @@ const async_handler_1 = __importDefault(require("../utils/async-handler"));
14
14
  const busboy_1 = __importDefault(require("busboy"));
15
15
  const cache_1 = require("../cache");
16
16
  const generate_hash_1 = require("../utils/generate-hash");
17
+ const sanitize_query_1 = require("../utils/sanitize-query");
17
18
  const router = (0, express_1.Router)();
18
19
  router.get('/random/string', (0, async_handler_1.default)(async (req, res) => {
19
20
  var _a;
@@ -96,6 +97,7 @@ router.post('/import/:collection', collection_exists_1.default, (0, async_handle
96
97
  req.pipe(busboy);
97
98
  }));
98
99
  router.post('/export/:collection', collection_exists_1.default, (0, async_handler_1.default)(async (req, res, next) => {
100
+ var _a;
99
101
  if (!req.body.query) {
100
102
  throw new exceptions_1.InvalidPayloadException(`"query" is required.`);
101
103
  }
@@ -106,8 +108,9 @@ router.post('/export/:collection', collection_exists_1.default, (0, async_handle
106
108
  accountability: req.accountability,
107
109
  schema: req.schema,
108
110
  });
111
+ const sanitizedQuery = (0, sanitize_query_1.sanitizeQuery)(req.body.query, (_a = req.accountability) !== null && _a !== void 0 ? _a : null);
109
112
  // We're not awaiting this, as it's supposed to run async in the background
110
- service.exportToFile(req.params.collection, req.body.query, req.body.format, {
113
+ service.exportToFile(req.params.collection, sanitizedQuery, req.body.format, {
111
114
  file: req.body.file,
112
115
  });
113
116
  return next();
@@ -1,13 +1,13 @@
1
1
  import { FnHelper, FnHelperOptions } from '../types';
2
2
  import { Knex } from 'knex';
3
3
  export declare class FnHelperMySQL extends FnHelper {
4
- year(table: string, column: string, options: FnHelperOptions): Knex.Raw;
5
- month(table: string, column: string, options: FnHelperOptions): Knex.Raw;
6
- week(table: string, column: string, options: FnHelperOptions): Knex.Raw;
7
- day(table: string, column: string, options: FnHelperOptions): Knex.Raw;
8
- weekday(table: string, column: string, options: FnHelperOptions): Knex.Raw;
9
- hour(table: string, column: string, options: FnHelperOptions): Knex.Raw;
10
- minute(table: string, column: string, options: FnHelperOptions): Knex.Raw;
11
- second(table: string, column: string, options: FnHelperOptions): Knex.Raw;
4
+ year(table: string, column: string): Knex.Raw;
5
+ month(table: string, column: string): Knex.Raw;
6
+ week(table: string, column: string): Knex.Raw;
7
+ day(table: string, column: string): Knex.Raw;
8
+ weekday(table: string, column: string): Knex.Raw;
9
+ hour(table: string, column: string): Knex.Raw;
10
+ minute(table: string, column: string): Knex.Raw;
11
+ second(table: string, column: string): Knex.Raw;
12
12
  count(table: string, column: string, options?: FnHelperOptions): Knex.Raw;
13
13
  }
@@ -2,36 +2,30 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FnHelperMySQL = void 0;
4
4
  const types_1 = require("../types");
5
- const parseLocaltime = (columnType) => {
6
- if (columnType === 'timestamp') {
7
- return `CONVERT_TZ(??.??, @@GLOBAL.time_zone, '+00:00')`;
8
- }
9
- return '??.??';
10
- };
11
5
  class FnHelperMySQL extends types_1.FnHelper {
12
- year(table, column, options) {
13
- return this.knex.raw(`YEAR(${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)})`, [table, column]);
6
+ year(table, column) {
7
+ return this.knex.raw('YEAR(??.??)', [table, column]);
14
8
  }
15
- month(table, column, options) {
16
- return this.knex.raw(`MONTH(${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)})`, [table, column]);
9
+ month(table, column) {
10
+ return this.knex.raw('MONTH(??.??)', [table, column]);
17
11
  }
18
- week(table, column, options) {
19
- return this.knex.raw(`WEEK(${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)})`, [table, column]);
12
+ week(table, column) {
13
+ return this.knex.raw('WEEK(??.??)', [table, column]);
20
14
  }
21
- day(table, column, options) {
22
- return this.knex.raw(`DAYOFMONTH(${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)})`, [table, column]);
15
+ day(table, column) {
16
+ return this.knex.raw('DAYOFMONTH(??.??)', [table, column]);
23
17
  }
24
- weekday(table, column, options) {
25
- return this.knex.raw(`DAYOFWEEK(${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)})`, [table, column]);
18
+ weekday(table, column) {
19
+ return this.knex.raw('DAYOFWEEK(??.??)', [table, column]);
26
20
  }
27
- hour(table, column, options) {
28
- return this.knex.raw(`HOUR(${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)})`, [table, column]);
21
+ hour(table, column) {
22
+ return this.knex.raw('HOUR(??.??)', [table, column]);
29
23
  }
30
- minute(table, column, options) {
31
- return this.knex.raw(`MINUTE(${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)})`, [table, column]);
24
+ minute(table, column) {
25
+ return this.knex.raw('MINUTE(??.??)', [table, column]);
32
26
  }
33
- second(table, column, options) {
34
- return this.knex.raw(`SECOND(${parseLocaltime(options === null || options === void 0 ? void 0 : options.type)})`, [table, column]);
27
+ second(table, column) {
28
+ return this.knex.raw('SECOND(??.??)', [table, column]);
35
29
  }
36
30
  count(table, column, options) {
37
31
  var _a, _b, _c, _d, _e;
@@ -1,6 +1,7 @@
1
1
  import { Knex } from 'knex';
2
2
  import { SchemaHelper } from '../types';
3
3
  export declare class SchemaHelperMSSQL extends SchemaHelper {
4
+ applyLimit(rootQuery: Knex.QueryBuilder, limit: number): void;
4
5
  applyOffset(rootQuery: Knex.QueryBuilder, offset: number): void;
5
6
  formatUUID(uuid: string): string;
6
7
  }
@@ -3,6 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SchemaHelperMSSQL = void 0;
4
4
  const types_1 = require("../types");
5
5
  class SchemaHelperMSSQL extends types_1.SchemaHelper {
6
+ applyLimit(rootQuery, limit) {
7
+ // The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries,
8
+ // and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
9
+ if (limit === -1) {
10
+ rootQuery.limit(Number.MAX_SAFE_INTEGER);
11
+ }
12
+ else {
13
+ rootQuery.limit(limit);
14
+ }
15
+ }
6
16
  applyOffset(rootQuery, offset) {
7
17
  rootQuery.offset(offset);
8
18
  rootQuery.orderBy(1);
@@ -7,7 +7,7 @@ class SchemaHelperMySQL extends types_1.SchemaHelper {
7
7
  applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields) {
8
8
  var _a;
9
9
  if ((_a = (0, database_1.getDatabaseVersion)()) === null || _a === void 0 ? void 0 : _a.startsWith('5.7')) {
10
- dbQuery.orderByRaw(`?? asc, ${orderByString.slice(9)}`, [`${table}.${primaryKey}`, ...orderByFields]);
10
+ dbQuery.orderByRaw(`?? asc, ${orderByString}`, [`${table}.${primaryKey}`, ...orderByFields]);
11
11
  dbQuery = knex
12
12
  .select(knex.raw(`??, ( @rank := IF ( @cur_id = deep.${primaryKey}, @rank + 1, 1 ) ) AS directus_row_number, ( @cur_id := deep.${primaryKey} ) AS current_id`, 'deep.*'))
13
13
  .from(knex.raw('? as ??, (SELECT @rank := 0, @cur_id := null) vars', [dbQuery, 'deep']));
@@ -15,6 +15,7 @@ export declare abstract class SchemaHelper extends DatabaseHelper {
15
15
  preColumnChange(): Promise<boolean>;
16
16
  postColumnChange(): Promise<void>;
17
17
  constraintName(existingName: string): string;
18
+ applyLimit(rootQuery: Knex.QueryBuilder, limit: number): void;
18
19
  applyOffset(rootQuery: Knex.QueryBuilder, offset: number): void;
19
20
  castA2oPrimaryKey(): string;
20
21
  applyMultiRelationalSort(knex: Knex, dbQuery: Knex.QueryBuilder, table: string, primaryKey: string, orderByString: string, orderByFields: Knex.Raw[]): Knex.QueryBuilder;
@@ -67,6 +67,11 @@ class SchemaHelper extends types_1.DatabaseHelper {
67
67
  // reference issue #14873
68
68
  return existingName;
69
69
  }
70
+ applyLimit(rootQuery, limit) {
71
+ if (limit !== -1) {
72
+ rootQuery.limit(limit);
73
+ }
74
+ }
70
75
  applyOffset(rootQuery, offset) {
71
76
  rootQuery.offset(offset);
72
77
  }
@@ -74,7 +79,7 @@ class SchemaHelper extends types_1.DatabaseHelper {
74
79
  return 'CAST(?? AS CHAR(255))';
75
80
  }
76
81
  applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields) {
77
- dbQuery.rowNumber(knex.ref('directus_row_number').toQuery(), knex.raw(`partition by ??${orderByString}`, [`${table}.${primaryKey}`, ...orderByFields]));
82
+ dbQuery.rowNumber(knex.ref('directus_row_number').toQuery(), knex.raw(`partition by ?? order by ${orderByString}`, [`${table}.${primaryKey}`, ...orderByFields]));
78
83
  return dbQuery;
79
84
  }
80
85
  formatUUID(uuid) {
@@ -218,41 +218,29 @@ async function getDBQuery(schema, knex, table, fieldNodes, query) {
218
218
  }
219
219
  if (sortRecords) {
220
220
  if (needsInnerQuery) {
221
+ let orderByString = '';
222
+ const orderByFields = [];
221
223
  sortRecords.map((sortRecord) => {
224
+ if (orderByString.length !== 0) {
225
+ orderByString += ', ';
226
+ }
222
227
  const sortAlias = `sort_${(0, apply_query_1.generateAlias)()}`;
223
228
  if (sortRecord.column.includes('.')) {
224
229
  const [alias, field] = sortRecord.column.split('.');
225
- dbQuery.select((0, get_column_1.getColumn)(knex, alias, field, sortAlias, schema, {
226
- originalCollectionName: (0, get_collection_from_alias_1.getCollectionFromAlias)(alias, aliasMap),
227
- }));
230
+ const originalCollectionName = (0, get_collection_from_alias_1.getCollectionFromAlias)(alias, aliasMap);
231
+ dbQuery.select((0, get_column_1.getColumn)(knex, alias, field, sortAlias, schema, { originalCollectionName }));
232
+ orderByString += `?? ${sortRecord.order}`;
233
+ orderByFields.push((0, get_column_1.getColumn)(knex, alias, field, false, schema, { originalCollectionName }));
228
234
  }
229
235
  else {
230
236
  dbQuery.select((0, get_column_1.getColumn)(knex, table, sortRecord.column, sortAlias, schema));
237
+ orderByString += `?? ${sortRecord.order}`;
238
+ orderByFields.push((0, get_column_1.getColumn)(knex, table, sortRecord.column, false, schema));
231
239
  }
232
240
  innerQuerySortRecords.push({ alias: sortAlias, order: sortRecord.order });
233
241
  });
242
+ dbQuery.orderByRaw(orderByString, orderByFields);
234
243
  if (hasMultiRelationalSort) {
235
- let orderByString = '';
236
- const orderByFields = [];
237
- sortRecords.map((sortRecord) => {
238
- if (orderByString.length === 0) {
239
- orderByString += ' order by';
240
- }
241
- else {
242
- orderByString += ',';
243
- }
244
- if (sortRecord.column.includes('.')) {
245
- orderByString += ` ?? ${sortRecord.order}`;
246
- const [alias, field] = sortRecord.column.split('.');
247
- orderByFields.push((0, get_column_1.getColumn)(knex, alias, field, false, schema, {
248
- originalCollectionName: (0, get_collection_from_alias_1.getCollectionFromAlias)(alias, aliasMap),
249
- }));
250
- }
251
- else {
252
- orderByString += ` ?? ${sortRecord.order}`;
253
- orderByFields.push((0, get_column_1.getColumn)(knex, table, sortRecord.column, false, schema));
254
- }
255
- });
256
244
  dbQuery = helpers.schema.applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields);
257
245
  }
258
246
  }
@@ -285,7 +273,7 @@ async function getDBQuery(schema, knex, table, fieldNodes, query) {
285
273
  });
286
274
  if (hasMultiRelationalSort) {
287
275
  wrapperQuery.where('inner.directus_row_number', '=', 1);
288
- (0, apply_query_1.applyLimit)(wrapperQuery, queryCopy.limit);
276
+ (0, apply_query_1.applyLimit)(knex, wrapperQuery, queryCopy.limit);
289
277
  }
290
278
  }
291
279
  return wrapperQuery;
@@ -452,7 +440,9 @@ function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, parentItem
452
440
  for (const nestedNode of nestedCollectionNodes[relatedCollection]) {
453
441
  item[nestedNode.fieldKey] = removeTemporaryFields(schema, item[nestedNode.fieldKey], nestedNode, schema.collections[nestedNode.relation.collection].primary, item);
454
442
  }
455
- item = fields[relatedCollection].length > 0 ? (0, lodash_1.pick)(rawItem, fields[relatedCollection]) : rawItem[primaryKeyField];
443
+ const fieldsWithFunctionsApplied = fields[relatedCollection].map((field) => (0, apply_function_to_column_name_1.applyFunctionToColumnName)(field));
444
+ item =
445
+ fields[relatedCollection].length > 0 ? (0, lodash_1.pick)(rawItem, fieldsWithFunctionsApplied) : rawItem[primaryKeyField];
456
446
  items.push(item);
457
447
  }
458
448
  }
package/dist/env.js CHANGED
@@ -183,6 +183,7 @@ const allowedEnvironmentVars = [
183
183
  'EXPORT_BATCH_SIZE',
184
184
  // flows
185
185
  'FLOWS_EXEC_ALLOWED_MODULES',
186
+ 'FLOWS_ENV_ALLOW_LIST',
186
187
  ].map((name) => new RegExp(`^${name}$`));
187
188
  const acceptedEnvTypes = ['string', 'number', 'regex', 'array', 'json'];
188
189
  const defaults = {
@@ -247,6 +248,7 @@ const defaults = {
247
248
  FILE_METADATA_ALLOW_LIST: 'ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISO',
248
249
  GRAPHQL_INTROSPECTION: true,
249
250
  FLOWS_EXEC_ALLOWED_MODULES: false,
251
+ FLOWS_ENV_ALLOW_LIST: false,
250
252
  };
251
253
  // Allows us to force certain environment variable into a type, instead of relying
252
254
  // on the auto-parsed type in processValues. ref #3705
@@ -1,4 +1,4 @@
1
- import { ExtensionType } from '@directus/shared/types';
1
+ import { Extension, ExtensionInfo, ExtensionType } from '@directus/shared/types';
2
2
  import { Router } from 'express';
3
3
  export declare function getExtensionManager(): ExtensionManager;
4
4
  type Options = {
@@ -21,7 +21,8 @@ declare class ExtensionManager {
21
21
  constructor();
22
22
  initialize(options?: Partial<Options>): Promise<void>;
23
23
  reload(): void;
24
- getExtensionsList(type?: ExtensionType): string[];
24
+ getExtensionsList(type?: ExtensionType): ExtensionInfo[];
25
+ getExtension(name: string): Extension | undefined;
25
26
  getAppExtensions(): string | null;
26
27
  getEndpointRouter(): Router;
27
28
  getEmbeds(): {
@@ -97,7 +97,7 @@ class ExtensionManager {
97
97
  await this.load();
98
98
  const loadedExtensions = this.getExtensionsList();
99
99
  if (loadedExtensions.length > 0) {
100
- logger_1.default.info(`Loaded extensions: ${loadedExtensions.join(', ')}`);
100
+ logger_1.default.info(`Loaded extensions: ${loadedExtensions.map((ext) => ext.name).join(', ')}`);
101
101
  }
102
102
  }
103
103
  if (!prevOptions.watch && this.options.watch) {
@@ -130,12 +130,36 @@ class ExtensionManager {
130
130
  }
131
131
  getExtensionsList(type) {
132
132
  if (type === undefined) {
133
- return this.extensions.map((extension) => extension.name);
133
+ return this.extensions.map(mapInfo);
134
134
  }
135
135
  else {
136
- return this.extensions.filter((extension) => extension.type === type).map((extension) => extension.name);
136
+ return this.extensions.map(mapInfo).filter((extension) => extension.type === type);
137
+ }
138
+ function mapInfo(extension) {
139
+ const extensionInfo = {
140
+ name: extension.name,
141
+ type: extension.type,
142
+ local: extension.local,
143
+ host: extension.host,
144
+ version: extension.version,
145
+ };
146
+ if (extension.type === 'bundle') {
147
+ return {
148
+ ...extensionInfo,
149
+ entries: extension.entries.map((entry) => ({
150
+ name: entry.name,
151
+ type: entry.type,
152
+ })),
153
+ };
154
+ }
155
+ else {
156
+ return extensionInfo;
157
+ }
137
158
  }
138
159
  }
160
+ getExtension(name) {
161
+ return this.extensions.find((extension) => extension.name === name);
162
+ }
139
163
  getAppExtensions() {
140
164
  return this.appExtensions;
141
165
  }
@@ -155,7 +179,7 @@ class ExtensionManager {
155
179
  }
156
180
  async load() {
157
181
  try {
158
- await (0, node_1.ensureExtensionDirs)(env_1.default.EXTENSIONS_PATH, env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_OR_HYBRID_EXTENSION_TYPES);
182
+ await (0, node_1.ensureExtensionDirs)(env_1.default.EXTENSIONS_PATH, constants_1.NESTED_EXTENSION_TYPES);
159
183
  this.extensions = await this.getExtensions();
160
184
  }
161
185
  catch (err) {
@@ -182,11 +206,14 @@ class ExtensionManager {
182
206
  initializeWatcher() {
183
207
  if (!this.watcher) {
184
208
  logger_1.default.info('Watching extensions for changes...');
185
- const localExtensionPaths = (env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_OR_HYBRID_EXTENSION_TYPES).flatMap((type) => {
209
+ const localExtensionPaths = constants_1.NESTED_EXTENSION_TYPES.flatMap((type) => {
186
210
  const typeDir = path_1.default.posix.join((0, node_1.pathToRelativeUrl)(env_1.default.EXTENSIONS_PATH), (0, utils_1.pluralize)(type));
187
- return (0, utils_1.isIn)(type, constants_1.HYBRID_EXTENSION_TYPES)
188
- ? [path_1.default.posix.join(typeDir, '*', 'app.js'), path_1.default.posix.join(typeDir, '*', 'api.js')]
189
- : path_1.default.posix.join(typeDir, '*', 'index.js');
211
+ if ((0, utils_1.isIn)(type, constants_1.HYBRID_EXTENSION_TYPES)) {
212
+ return [path_1.default.posix.join(typeDir, '*', 'app.js'), path_1.default.posix.join(typeDir, '*', 'api.js')];
213
+ }
214
+ else {
215
+ return path_1.default.posix.join(typeDir, '*', 'index.js');
216
+ }
190
217
  });
191
218
  this.watcher = chokidar_1.default.watch([path_1.default.resolve('package.json'), ...localExtensionPaths], {
192
219
  ignoreInitial: true,
@@ -206,14 +233,12 @@ class ExtensionManager {
206
233
  if (this.watcher) {
207
234
  const toPackageExtensionPaths = (extensions) => extensions
208
235
  .filter((extension) => !extension.local)
209
- .flatMap((extension) => extension.type === 'pack'
210
- ? path_1.default.resolve(extension.path, 'package.json')
211
- : (0, utils_1.isTypeIn)(extension, constants_1.HYBRID_EXTENSION_TYPES) || extension.type === 'bundle'
212
- ? [
213
- path_1.default.resolve(extension.path, extension.entrypoint.app),
214
- path_1.default.resolve(extension.path, extension.entrypoint.api),
215
- ]
216
- : path_1.default.resolve(extension.path, extension.entrypoint));
236
+ .flatMap((extension) => (0, utils_1.isTypeIn)(extension, constants_1.HYBRID_EXTENSION_TYPES) || extension.type === 'bundle'
237
+ ? [
238
+ path_1.default.resolve(extension.path, extension.entrypoint.app),
239
+ path_1.default.resolve(extension.path, extension.entrypoint.api),
240
+ ]
241
+ : path_1.default.resolve(extension.path, extension.entrypoint));
217
242
  const addedPackageExtensionPaths = toPackageExtensionPaths(added);
218
243
  const removedPackageExtensionPaths = toPackageExtensionPaths(removed);
219
244
  this.watcher.add(addedPackageExtensionPaths);
@@ -221,9 +246,10 @@ class ExtensionManager {
221
246
  }
222
247
  }
223
248
  async getExtensions() {
224
- const packageExtensions = await (0, node_1.getPackageExtensions)('.', env_1.default.SERVE_APP ? constants_1.EXTENSION_PACKAGE_TYPES : constants_1.API_OR_HYBRID_EXTENSION_PACKAGE_TYPES);
225
- const localExtensions = await (0, node_1.getLocalExtensions)(env_1.default.EXTENSIONS_PATH, env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_OR_HYBRID_EXTENSION_TYPES);
226
- return [...packageExtensions, ...localExtensions];
249
+ const packageExtensions = await (0, node_1.getPackageExtensions)('.');
250
+ const localPackageExtensions = await (0, node_1.resolvePackageExtensions)(env_1.default.EXTENSIONS_PATH);
251
+ const localExtensions = await (0, node_1.getLocalExtensions)(env_1.default.EXTENSIONS_PATH);
252
+ return [...packageExtensions, ...localPackageExtensions, ...localExtensions].filter((extension) => env_1.default.SERVE_APP || constants_1.APP_EXTENSION_TYPES.includes(extension.type) === false);
227
253
  }
228
254
  async generateExtensionBundle() {
229
255
  const sharedDepsMapping = await this.getSharedDepsMapping(constants_1.APP_SHARED_DEPS);
package/dist/flows.js CHANGED
@@ -64,6 +64,7 @@ exports.getFlowManager = getFlowManager;
64
64
  const TRIGGER_KEY = '$trigger';
65
65
  const ACCOUNTABILITY_KEY = '$accountability';
66
66
  const LAST_KEY = '$last';
67
+ const ENV_KEY = '$env';
67
68
  class FlowManager {
68
69
  constructor() {
69
70
  this.isLoaded = false;
@@ -265,6 +266,7 @@ class FlowManager {
265
266
  [TRIGGER_KEY]: data,
266
267
  [LAST_KEY]: data,
267
268
  [ACCOUNTABILITY_KEY]: (_c = context === null || context === void 0 ? void 0 : context.accountability) !== null && _c !== void 0 ? _c : null,
269
+ [ENV_KEY]: (0, lodash_1.pick)(env_1.default, env_1.default.FLOWS_ENV_ALLOW_LIST ? (0, utils_1.toArray)(env_1.default.FLOWS_ENV_ALLOW_LIST) : []),
268
270
  };
269
271
  let nextOperation = flow.operation;
270
272
  let lastOperationStatus = 'unknown';
@@ -5,10 +5,13 @@ const vm2_1 = require("vm2");
5
5
  exports.default = (0, utils_1.defineOperationApi)({
6
6
  id: 'exec',
7
7
  handler: async ({ code }, { data, env }) => {
8
+ var _a;
8
9
  const allowedModules = env.FLOWS_EXEC_ALLOWED_MODULES ? (0, utils_1.toArray)(env.FLOWS_EXEC_ALLOWED_MODULES) : [];
10
+ const allowedEnv = (_a = data.$env) !== null && _a !== void 0 ? _a : {};
9
11
  const opts = {
10
12
  eval: false,
11
13
  wasm: false,
14
+ env: allowedEnv,
12
15
  };
13
16
  if (allowedModules.length > 0) {
14
17
  opts.require = {
@@ -30,7 +30,7 @@ exports.default = (0, utils_1.defineOperationApi)({
30
30
  const sanitizedQueryObject = (0, sanitize_query_1.sanitizeQuery)(queryObject, customAccountability);
31
31
  let result;
32
32
  if (!key || (Array.isArray(key) && key.length === 0)) {
33
- result = await itemsService.deleteByQuery(sanitizedQueryObject);
33
+ result = await itemsService.deleteByQuery(sanitizedQueryObject, { emitEvents: !!emitEvents });
34
34
  }
35
35
  else {
36
36
  const keys = (0, utils_1.toArray)(key);
@@ -0,0 +1 @@
1
+ export {};
@@ -30,7 +30,7 @@ exports.default = (0, utils_1.defineOperationApi)({
30
30
  const sanitizedQueryObject = (0, sanitize_query_1.sanitizeQuery)(queryObject, customAccountability);
31
31
  let result;
32
32
  if (!key || (Array.isArray(key) && key.length === 0)) {
33
- result = await itemsService.readByQuery(sanitizedQueryObject);
33
+ result = await itemsService.readByQuery(sanitizedQueryObject, { emitEvents: !!emitEvents });
34
34
  }
35
35
  else {
36
36
  const keys = (0, utils_1.toArray)(key);
@@ -0,0 +1 @@
1
+ export {};
@@ -7,8 +7,11 @@ exports.default = (0, utils_1.defineOperationApi)({
7
7
  id: 'mail',
8
8
  handler: async ({ body, to, type, subject }, { accountability, database, getSchema }) => {
9
9
  const mailService = new services_1.MailService({ schema: await getSchema({ database }), accountability, knex: database });
10
+ // If 'body' is of type object/undefined (happens when body consists solely of a placeholder)
11
+ // convert it to JSON string
12
+ const safeBody = typeof body !== 'string' ? JSON.stringify(body) : body;
10
13
  await mailService.send({
11
- html: type === 'wysiwyg' ? body : (0, md_1.md)(body),
14
+ html: type === 'wysiwyg' ? safeBody : (0, md_1.md)(safeBody),
12
15
  to,
13
16
  subject,
14
17
  });
@@ -265,6 +265,8 @@ class ExportService {
265
265
  return string;
266
266
  }
267
267
  if (format === 'csv') {
268
+ if (input.length === 0)
269
+ return '';
268
270
  const parser = new json2csv_1.Parser({
269
271
  transforms: [json2csv_1.transforms.flatten({ separator: '.' })],
270
272
  header: (options === null || options === void 0 ? void 0 : options.includeHeader) !== false,
@@ -35,7 +35,7 @@ class RolesService extends items_1.ItemsService {
35
35
  userKeys = users.update.map((user) => user.id).filter((id) => id);
36
36
  }
37
37
  const usersThatWereInRoleBefore = (await this.knex.select('id').from('directus_users').where('role', '=', key)).map((user) => user.id);
38
- const usersThatAreRemoved = usersThatWereInRoleBefore.filter((id) => userKeys.includes(id) === false);
38
+ const usersThatAreRemoved = usersThatWereInRoleBefore.filter((id) => Array.isArray(users) ? userKeys.includes(id) === false : users.delete.includes(id) === true);
39
39
  const usersThatAreAdded = Array.isArray(users) ? users : users.create;
40
40
  // If the role the users are moved to is an admin-role, and there's at least 1 (new) admin
41
41
  // user, we don't have to check for other admin
@@ -264,8 +264,11 @@ class ServerService {
264
264
  const startTime = perf_hooks_1.performance.now();
265
265
  try {
266
266
  await disk.write(`health-${checkID}`, node_stream_1.Readable.from(['check']));
267
- await disk.read(`health-${checkID}`);
268
- await disk.delete(`health-${checkID}`);
267
+ const fileStream = await disk.read(`health-${checkID}`);
268
+ fileStream.on('data', async () => {
269
+ fileStream.destroy();
270
+ await disk.delete(`health-${checkID}`);
271
+ });
269
272
  }
270
273
  catch (err) {
271
274
  checks[`storage:${location}:responseTime`][0].status = 'error';
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,7 @@
1
1
  import { Aggregate, Filter, Query, SchemaOverview } from '@directus/shared/types';
2
2
  import { Knex } from 'knex';
3
3
  import { AliasMap } from './get-column-path';
4
- export declare const generateAlias: () => string;
4
+ export declare const generateAlias: (size?: number | undefined) => string;
5
5
  /**
6
6
  * Apply the Query to a given Knex query builder instance
7
7
  */
@@ -24,7 +24,7 @@ export declare function applySort(knex: Knex, schema: SchemaOverview, rootQuery:
24
24
  }[];
25
25
  hasMultiRelationalSort: boolean;
26
26
  } | undefined;
27
- export declare function applyLimit(rootQuery: Knex.QueryBuilder, limit: any): void;
27
+ export declare function applyLimit(knex: Knex, rootQuery: Knex.QueryBuilder, limit: any): void;
28
28
  export declare function applyOffset(knex: Knex, rootQuery: Knex.QueryBuilder, offset: any): void;
29
29
  export declare function applyFilter(knex: Knex, schema: SchemaOverview, rootQuery: Knex.QueryBuilder, rootFilter: Filter, collection: string, aliasMap: AliasMap): {
30
30
  query: Knex.QueryBuilder<any, any>;
@@ -26,9 +26,7 @@ function applyQuery(knex, collection, dbQuery, query, schema, options) {
26
26
  if (query.sort && !(options === null || options === void 0 ? void 0 : options.isInnerQuery) && !(options === null || options === void 0 ? void 0 : options.hasMultiRelationalSort)) {
27
27
  applySort(knex, schema, dbQuery, query.sort, collection, aliasMap);
28
28
  }
29
- if (!(options === null || options === void 0 ? void 0 : options.hasMultiRelationalSort)) {
30
- applyLimit(dbQuery, query.limit);
31
- }
29
+ applyLimit(knex, dbQuery, query.limit);
32
30
  if (query.offset) {
33
31
  applyOffset(knex, dbQuery, query.offset);
34
32
  }
@@ -175,9 +173,9 @@ function applySort(knex, schema, rootQuery, rootSort, collection, aliasMap, retu
175
173
  rootQuery.orderBy(sortRecords);
176
174
  }
177
175
  exports.applySort = applySort;
178
- function applyLimit(rootQuery, limit) {
179
- if (typeof limit === 'number' && limit !== -1) {
180
- rootQuery.limit(limit);
176
+ function applyLimit(knex, rootQuery, limit) {
177
+ if (typeof limit === 'number') {
178
+ (0, helpers_1.getHelpers)(knex).schema.applyLimit(rootQuery, limit);
181
179
  }
182
180
  }
183
181
  exports.applyLimit = applyLimit;
@@ -4,18 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getCacheKey = void 0;
7
- const url_1 = __importDefault(require("url"));
8
7
  const object_hash_1 = __importDefault(require("object-hash"));
9
- const lodash_1 = require("lodash");
8
+ const url_1 = __importDefault(require("url"));
9
+ const get_graphql_query_and_variables_1 = require("./get-graphql-query-and-variables");
10
10
  function getCacheKey(req) {
11
- var _a, _b;
11
+ var _a;
12
12
  const path = url_1.default.parse(req.originalUrl).pathname;
13
- const isGraphQl = path === null || path === void 0 ? void 0 : path.includes('/graphql');
14
- const isGet = ((_a = req.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'get';
13
+ const isGraphQl = path === null || path === void 0 ? void 0 : path.startsWith('/graphql');
15
14
  const info = {
16
- user: ((_b = req.accountability) === null || _b === void 0 ? void 0 : _b.user) || null,
15
+ user: ((_a = req.accountability) === null || _a === void 0 ? void 0 : _a.user) || null,
17
16
  path,
18
- query: isGraphQl ? (0, lodash_1.pick)(isGet ? req.query : req.body, ['query', 'variables']) : req.sanitizedQuery,
17
+ query: isGraphQl ? (0, get_graphql_query_and_variables_1.getGraphqlQueryAndVariables)(req) : req.sanitizedQuery,
19
18
  };
20
19
  const key = (0, object_hash_1.default)(info);
21
20
  return key;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Request } from 'express';
2
+ export declare function getGraphqlQueryAndVariables(req: Request): Pick<any, "query" | "variables">;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getGraphqlQueryAndVariables = void 0;
4
+ const lodash_1 = require("lodash");
5
+ function getGraphqlQueryAndVariables(req) {
6
+ var _a;
7
+ const isGet = ((_a = req.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'get';
8
+ return (0, lodash_1.pick)(isGet ? req.query : req.body, ['query', 'variables']);
9
+ }
10
+ exports.getGraphqlQueryAndVariables = getGraphqlQueryAndVariables;
@@ -15,19 +15,26 @@ const roles_1 = require("../services/roles");
15
15
  const users_1 = require("../services/users");
16
16
  const merge_permissions_1 = require("../utils/merge-permissions");
17
17
  const merge_permissions_for_share_1 = require("./merge-permissions-for-share");
18
+ const logger_1 = __importDefault(require("../logger"));
18
19
  async function getPermissions(accountability, schema) {
19
20
  const database = (0, database_1.default)();
20
21
  const { cache } = (0, cache_1.getCache)();
21
22
  let permissions = [];
22
23
  const { user, role, app, admin, share_scope } = accountability;
23
24
  const cacheKey = `permissions-${(0, object_hash_1.default)({ user, role, app, admin, share_scope })}`;
24
- if (env_1.default.CACHE_PERMISSIONS !== false) {
25
- const cachedPermissions = await (0, cache_1.getSystemCache)(cacheKey);
25
+ if (cache && env_1.default.CACHE_PERMISSIONS !== false) {
26
+ let cachedPermissions;
27
+ try {
28
+ cachedPermissions = await (0, cache_1.getSystemCache)(cacheKey);
29
+ }
30
+ catch (err) {
31
+ logger_1.default.warn(err, `[cache] Couldn't read key ${cacheKey}. ${err.message}`);
32
+ }
26
33
  if (cachedPermissions) {
27
34
  if (!cachedPermissions.containDynamicData) {
28
35
  return processPermissions(accountability, cachedPermissions.permissions, {});
29
36
  }
30
- const cachedFilterContext = await (cache === null || cache === void 0 ? void 0 : cache.get(`filterContext-${(0, object_hash_1.default)({ user, role, permissions: cachedPermissions.permissions })}`));
37
+ const cachedFilterContext = await (0, cache_1.getCacheValue)(cache, `filterContext-${(0, object_hash_1.default)({ user, role, permissions: cachedPermissions.permissions })}`);
31
38
  if (cachedFilterContext) {
32
39
  return processPermissions(accountability, cachedPermissions.permissions, cachedFilterContext);
33
40
  }
@@ -38,7 +45,7 @@ async function getPermissions(accountability, schema) {
38
45
  ? await getFilterContext(schema, accountability, requiredPermissionData)
39
46
  : {};
40
47
  if (containDynamicData && env_1.default.CACHE_ENABLED !== false) {
41
- await (cache === null || cache === void 0 ? void 0 : cache.set(`filterContext-${(0, object_hash_1.default)({ user, role, permissions })}`, filterContext));
48
+ await (0, cache_1.setCacheValue)(cache, `filterContext-${(0, object_hash_1.default)({ user, role, permissions })}`, filterContext);
42
49
  }
43
50
  return processPermissions(accountability, permissions, filterContext);
44
51
  }
@@ -64,10 +71,10 @@ async function getPermissions(accountability, schema) {
64
71
  const filterContext = containDynamicData
65
72
  ? await getFilterContext(schema, accountability, requiredPermissionData)
66
73
  : {};
67
- if (env_1.default.CACHE_PERMISSIONS !== false) {
74
+ if (cache && env_1.default.CACHE_PERMISSIONS !== false) {
68
75
  await (0, cache_1.setSystemCache)(cacheKey, { permissions, containDynamicData });
69
76
  if (containDynamicData && env_1.default.CACHE_ENABLED !== false) {
70
- await (cache === null || cache === void 0 ? void 0 : cache.set(`filterContext-${(0, object_hash_1.default)({ user, role, permissions })}`, filterContext));
77
+ await (0, cache_1.setCacheValue)(cache, `filterContext-${(0, object_hash_1.default)({ user, role, permissions })}`, filterContext);
71
78
  }
72
79
  }
73
80
  return processPermissions(accountability, permissions, filterContext);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -15,9 +15,9 @@ const querySchema = joi_1.default.object({
15
15
  group: joi_1.default.array().items(joi_1.default.string()),
16
16
  sort: joi_1.default.array().items(joi_1.default.string()),
17
17
  filter: joi_1.default.object({}).unknown(),
18
- limit: joi_1.default.number(),
19
- offset: joi_1.default.number(),
20
- page: joi_1.default.number(),
18
+ limit: joi_1.default.number().integer().min(-1),
19
+ offset: joi_1.default.number().integer().min(1),
20
+ page: joi_1.default.number().integer().min(0),
21
21
  meta: joi_1.default.array().items(joi_1.default.string().valid('total_count', 'filter_count')),
22
22
  search: joi_1.default.string(),
23
23
  export: joi_1.default.string().valid('json', 'csv', 'xml'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "directus",
3
- "version": "9.22.1",
3
+ "version": "9.22.3",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "keywords": [
6
6
  "directus",
@@ -65,7 +65,7 @@
65
65
  ],
66
66
  "dependencies": {
67
67
  "@authenio/samlify-node-xmllint": "2.0.0",
68
- "@aws-sdk/client-ses": "3.211.0",
68
+ "@aws-sdk/client-ses": "3.236.0",
69
69
  "@directus/format-title": "9.15.0",
70
70
  "@godaddy/terminus": "4.11.2",
71
71
  "@rollup/plugin-alias": "4.0.2",
@@ -73,7 +73,7 @@
73
73
  "argon2": "0.30.2",
74
74
  "async": "3.2.4",
75
75
  "async-mutex": "0.4.0",
76
- "axios": "1.1.3",
76
+ "axios": "1.2.1",
77
77
  "busboy": "1.6.0",
78
78
  "bytes": "3.1.2",
79
79
  "camelcase": "6.3.0",
@@ -90,15 +90,15 @@
90
90
  "encodeurl": "1.0.2",
91
91
  "eventemitter2": "6.4.9",
92
92
  "execa": "5.1.1",
93
- "exif-reader": "1.0.3",
93
+ "exif-reader": "1.1.0",
94
94
  "express": "4.18.2",
95
95
  "fast-redact": "3.1.2",
96
96
  "flat": "5.0.2",
97
- "fs-extra": "10.1.0",
97
+ "fs-extra": "11.1.0",
98
98
  "globby": "11.0.4",
99
99
  "graphql": "16.6.0",
100
100
  "graphql-compose": "9.0.10",
101
- "helmet": "6.0.0",
101
+ "helmet": "6.0.1",
102
102
  "icc": "2.0.0",
103
103
  "inquirer": "8.2.4",
104
104
  "ioredis": "5.2.4",
@@ -106,69 +106,69 @@
106
106
  "js-yaml": "4.1.0",
107
107
  "js2xmlparser": "5.0.0",
108
108
  "json2csv": "5.0.7",
109
- "jsonwebtoken": "8.5.1",
109
+ "jsonwebtoken": "9.0.0",
110
110
  "keyv": "4.5.2",
111
111
  "knex": "2.3.0",
112
112
  "knex-schema-inspector": "3.0.0",
113
113
  "ldapjs": "2.3.3",
114
- "liquidjs": "9.42.1",
114
+ "liquidjs": "10.3.3",
115
115
  "lodash": "4.17.21",
116
- "marked": "4.2.2",
116
+ "marked": "4.2.4",
117
117
  "micromustache": "8.0.3",
118
118
  "mime-types": "2.1.35",
119
119
  "ms": "2.1.3",
120
- "nanoid": "3.1.23",
120
+ "nanoid": "3.3.4",
121
121
  "node-cron": "3.0.2",
122
122
  "node-machine-id": "1.1.12",
123
123
  "nodemailer": "6.8.0",
124
124
  "object-hash": "3.0.0",
125
- "openapi3-ts": "3.1.1",
126
- "openid-client": "5.3.0",
125
+ "openapi3-ts": "3.1.2",
126
+ "openid-client": "5.3.1",
127
127
  "ora": "5.4.0",
128
128
  "otplib": "12.0.1",
129
- "pino": "8.7.0",
130
- "pino-http": "8.2.1",
129
+ "pino": "8.8.0",
130
+ "pino-http": "8.3.0",
131
131
  "pino-http-print": "3.1.0",
132
132
  "pino-pretty": "9.1.1",
133
133
  "qs": "6.11.0",
134
134
  "rate-limiter-flexible": "2.4.1",
135
- "rollup": "3.3.0",
135
+ "rollup": "3.7.5",
136
136
  "samlify": "2.8.7",
137
- "sanitize-html": "2.7.3",
138
- "sharp": "0.31.2",
137
+ "sanitize-html": "2.8.1",
138
+ "sharp": "0.31.3",
139
139
  "snappy": "7.2.2",
140
- "stream-json": "1.7.4",
140
+ "stream-json": "1.7.5",
141
141
  "strip-bom-stream": "4.0.0",
142
142
  "tmp-promise": "3.0.3",
143
143
  "update-check": "1.5.4",
144
144
  "uuid": "9.0.0",
145
145
  "uuid-validate": "0.0.3",
146
- "vm2": "3.9.11",
146
+ "vm2": "3.9.13",
147
147
  "wellknown": "0.5.0",
148
- "@directus/app": "9.22.1",
149
- "@directus/extensions-sdk": "9.22.1",
150
- "@directus/schema": "9.22.1",
151
- "@directus/shared": "9.22.1",
152
- "@directus/specs": "9.22.1",
153
- "@directus/storage": "9.22.1",
154
- "@directus/storage-driver-azure": "9.22.1",
155
- "@directus/storage-driver-cloudinary": "9.22.1",
156
- "@directus/storage-driver-gcs": "9.22.1",
157
- "@directus/storage-driver-local": "9.22.1",
158
- "@directus/storage-driver-s3": "9.22.1"
148
+ "@directus/app": "9.22.2",
149
+ "@directus/extensions-sdk": "9.22.3",
150
+ "@directus/schema": "9.22.3",
151
+ "@directus/shared": "9.22.2",
152
+ "@directus/specs": "9.22.3",
153
+ "@directus/storage": "9.22.3",
154
+ "@directus/storage-driver-azure": "9.22.3",
155
+ "@directus/storage-driver-cloudinary": "9.22.3",
156
+ "@directus/storage-driver-gcs": "9.22.3",
157
+ "@directus/storage-driver-local": "9.22.3",
158
+ "@directus/storage-driver-s3": "9.22.3"
159
159
  },
160
160
  "devDependencies": {
161
- "@ngneat/falso": "6.3.0",
162
- "@types/async": "3.2.15",
161
+ "@ngneat/falso": "6.3.2",
162
+ "@types/async": "3.2.16",
163
163
  "@types/busboy": "1.5.0",
164
164
  "@types/bytes": "3.1.1",
165
165
  "@types/cookie-parser": "1.4.3",
166
- "@types/cors": "2.8.12",
167
- "@types/deep-diff": "1.0.1",
166
+ "@types/cors": "2.8.13",
167
+ "@types/deep-diff": "1.0.2",
168
168
  "@types/destroy": "1.0.0",
169
169
  "@types/encodeurl": "1.0.0",
170
170
  "@types/exif-reader": "1.0.0",
171
- "@types/express": "4.17.14",
171
+ "@types/express": "4.17.15",
172
172
  "@types/express-serve-static-core": "4.17.31",
173
173
  "@types/fast-redact": "3.0.2",
174
174
  "@types/flat": "5.0.2",
@@ -179,42 +179,40 @@
179
179
  "@types/jsonwebtoken": "8.5.9",
180
180
  "@types/keyv": "3.1.4",
181
181
  "@types/ldapjs": "2.2.5",
182
- "@types/lodash": "4.14.189",
183
- "@types/marked": "4.0.7",
182
+ "@types/lodash": "4.14.191",
183
+ "@types/marked": "4.0.8",
184
184
  "@types/mime-types": "2.1.1",
185
185
  "@types/ms": "0.7.31",
186
- "@types/node": "18.11.9",
186
+ "@types/node": "18.11.17",
187
187
  "@types/node-cron": "3.0.6",
188
- "@types/nodemailer": "6.4.6",
189
- "@types/object-hash": "2.2.1",
190
- "@types/pino": "7.0.4",
191
- "@types/pino-http": "5.8.1",
188
+ "@types/nodemailer": "6.4.7",
189
+ "@types/object-hash": "3.0.2",
192
190
  "@types/qs": "6.9.7",
193
- "@types/sanitize-html": "2.6.2",
191
+ "@types/sanitize-html": "2.8.0",
194
192
  "@types/sharp": "0.31.0",
195
193
  "@types/stream-json": "1.7.2",
196
- "@types/uuid": "8.3.4",
194
+ "@types/uuid": "9.0.0",
197
195
  "@types/uuid-validate": "0.0.1",
198
196
  "@types/wellknown": "0.5.4",
199
- "@vitest/coverage-c8": "0.25.2",
197
+ "@vitest/coverage-c8": "0.26.2",
200
198
  "copyfiles": "2.4.1",
201
199
  "form-data": "4.0.0",
202
- "knex-mock-client": "1.8.4",
200
+ "knex-mock-client": "1.11.0",
203
201
  "ts-node": "10.9.1",
204
202
  "ts-node-dev": "2.0.0",
205
- "typescript": "4.9.3",
206
- "vitest": "0.25.2"
203
+ "typescript": "4.9.4",
204
+ "vitest": "0.26.2"
207
205
  },
208
206
  "optionalDependencies": {
209
- "@keyv/redis": "2.5.3",
210
- "keyv-memcache": "1.2.5",
207
+ "@keyv/redis": "2.5.4",
208
+ "keyv-memcache": "1.3.3",
211
209
  "memcached": "2.2.2",
212
210
  "mysql": "2.18.1",
213
211
  "nodemailer-mailgun-transport": "2.1.5",
214
212
  "nodemailer-sendgrid": "1.0.3",
215
213
  "pg": "8.8.0",
216
- "sqlite3": "5.1.2",
217
- "tedious": "15.1.1"
214
+ "sqlite3": "5.1.4",
215
+ "tedious": "15.1.2"
218
216
  },
219
217
  "engines": {
220
218
  "node": ">=12.20.0"