@directus/api 19.0.0 → 19.0.2

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 (57) hide show
  1. package/dist/cli/utils/create-env/env-stub.liquid +2 -2
  2. package/dist/controllers/assets.js +9 -12
  3. package/dist/controllers/collections.js +1 -2
  4. package/dist/controllers/dashboards.js +1 -2
  5. package/dist/controllers/flows.js +1 -2
  6. package/dist/controllers/folders.js +1 -2
  7. package/dist/controllers/items.js +3 -4
  8. package/dist/controllers/notifications.js +1 -2
  9. package/dist/controllers/operations.js +1 -2
  10. package/dist/controllers/panels.js +1 -2
  11. package/dist/controllers/presets.js +1 -2
  12. package/dist/controllers/roles.js +1 -2
  13. package/dist/controllers/translations.js +1 -2
  14. package/dist/controllers/users.js +1 -2
  15. package/dist/controllers/utils.js +2 -1
  16. package/dist/database/run-ast.js +4 -3
  17. package/dist/services/activity.d.ts +2 -1
  18. package/dist/services/authorization.d.ts +2 -2
  19. package/dist/services/collections.d.ts +1 -1
  20. package/dist/services/collections.js +8 -7
  21. package/dist/services/extensions.js +2 -1
  22. package/dist/services/fields.js +4 -3
  23. package/dist/services/files.d.ts +2 -2
  24. package/dist/services/flows.d.ts +2 -2
  25. package/dist/services/graphql/index.d.ts +2 -2
  26. package/dist/services/graphql/index.js +5 -0
  27. package/dist/services/import-export.js +4 -3
  28. package/dist/services/items.d.ts +2 -2
  29. package/dist/services/items.js +9 -8
  30. package/dist/services/notifications.d.ts +2 -2
  31. package/dist/services/operations.d.ts +2 -2
  32. package/dist/services/payload.d.ts +2 -2
  33. package/dist/services/permissions/index.d.ts +2 -2
  34. package/dist/services/relations.js +4 -3
  35. package/dist/services/revisions.d.ts +2 -1
  36. package/dist/services/roles.d.ts +2 -2
  37. package/dist/services/roles.js +2 -1
  38. package/dist/services/shares.d.ts +2 -1
  39. package/dist/services/shares.js +1 -1
  40. package/dist/services/tfa.d.ts +2 -1
  41. package/dist/services/tfa.js +1 -1
  42. package/dist/services/users.d.ts +2 -2
  43. package/dist/services/users.js +3 -2
  44. package/dist/services/utils.d.ts +5 -3
  45. package/dist/services/utils.js +9 -5
  46. package/dist/services/versions.d.ts +1 -1
  47. package/dist/services/webhooks.d.ts +2 -1
  48. package/dist/types/items.d.ts +1 -8
  49. package/dist/types/services.d.ts +1 -2
  50. package/dist/utils/apply-diff.js +2 -1
  51. package/dist/utils/get-schema.js +21 -12
  52. package/dist/utils/merge-version-data.js +1 -1
  53. package/dist/utils/transaction.d.ts +9 -0
  54. package/dist/utils/transaction.js +15 -0
  55. package/dist/utils/validate-keys.d.ts +1 -2
  56. package/dist/websocket/utils/items.d.ts +1 -1
  57. package/package.json +19 -19
@@ -312,8 +312,8 @@ EXTENSIONS_AUTO_RELOAD=false
312
312
  ####################################################################################################
313
313
  ### Email
314
314
 
315
- # Email address from which emails are sent ["no-reply@directus.io"]
316
- EMAIL_FROM="no-reply@directus.io"
315
+ # Email address from which emails are sent ["no-reply@example.com"]
316
+ EMAIL_FROM="no-reply@example.com"
317
317
 
318
318
  # What to use to send emails. One of
319
319
  # sendmail, smtp, mailgun, sendgrid, ses.
@@ -180,17 +180,10 @@ asyncHandler(async (req, res) => {
180
180
  res.setHeader('Content-Length', stat.size);
181
181
  return res.end();
182
182
  }
183
- let isDataSent = false;
184
- stream.on('data', (chunk) => {
185
- isDataSent = true;
186
- res.write(chunk);
187
- });
188
- stream.on('end', () => {
189
- res.end();
190
- });
191
- stream.on('error', (e) => {
192
- logger.error(e, `Couldn't stream file ${file.id} to the client`);
193
- if (!isDataSent) {
183
+ stream
184
+ .on('error', (error) => {
185
+ logger.error(error, `Couldn't stream file ${file.id} to the client`);
186
+ if (!res.headersSent) {
194
187
  res.removeHeader('Content-Type');
195
188
  res.removeHeader('Content-Disposition');
196
189
  res.removeHeader('Cache-Control');
@@ -205,7 +198,11 @@ asyncHandler(async (req, res) => {
205
198
  ],
206
199
  });
207
200
  }
208
- });
201
+ else {
202
+ res.end();
203
+ }
204
+ })
205
+ .pipe(res);
209
206
  return undefined;
210
207
  }));
211
208
  export default router;
@@ -1,6 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import { Router } from 'express';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import { validateBatch } from '../middleware/validate-batch.js';
6
5
  import { CollectionsService } from '../services/collections.js';
@@ -1,6 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import useCollection from '../middleware/use-collection.js';
6
5
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -1,7 +1,6 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
3
  import { UUID_REGEX } from '../constants.js';
4
- import { ErrorCode } from '@directus/errors';
5
4
  import { getFlowManager } from '../flows.js';
6
5
  import { respond } from '../middleware/respond.js';
7
6
  import useCollection from '../middleware/use-collection.js';
@@ -1,6 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import useCollection from '../middleware/use-collection.js';
6
5
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -1,15 +1,14 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, ForbiddenError, RouteNotFoundError, isDirectusError } from '@directus/errors';
2
+ import { isSystemCollection } from '@directus/system-data';
2
3
  import express from 'express';
3
- import { ErrorCode, ForbiddenError, RouteNotFoundError } from '@directus/errors';
4
4
  import collectionExists from '../middleware/collection-exists.js';
5
+ import { mergeContentVersions } from '../middleware/merge-content-versions.js';
5
6
  import { respond } from '../middleware/respond.js';
6
7
  import { validateBatch } from '../middleware/validate-batch.js';
7
- import { mergeContentVersions } from '../middleware/merge-content-versions.js';
8
8
  import { ItemsService } from '../services/items.js';
9
9
  import { MetaService } from '../services/meta.js';
10
10
  import asyncHandler from '../utils/async-handler.js';
11
11
  import { sanitizeQuery } from '../utils/sanitize-query.js';
12
- import { isSystemCollection } from '@directus/system-data';
13
12
  const router = express.Router();
14
13
  router.post('/:collection', collectionExists, asyncHandler(async (req, res, next) => {
15
14
  if (isSystemCollection(req.params['collection']))
@@ -1,6 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import useCollection from '../middleware/use-collection.js';
6
5
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -1,6 +1,5 @@
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
1
2
  import express from 'express';
2
- import { isDirectusError } from '@directus/errors';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import useCollection from '../middleware/use-collection.js';
6
5
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -1,6 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import useCollection from '../middleware/use-collection.js';
6
5
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -1,6 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import useCollection from '../middleware/use-collection.js';
6
5
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -1,6 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import useCollection from '../middleware/use-collection.js';
6
5
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -1,6 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
- import { ErrorCode } from '@directus/errors';
4
3
  import { respond } from '../middleware/respond.js';
5
4
  import useCollection from '../middleware/use-collection.js';
6
5
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -1,7 +1,6 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, ForbiddenError, InvalidCredentialsError, InvalidPayloadError, isDirectusError, } from '@directus/errors';
2
2
  import express from 'express';
3
3
  import Joi from 'joi';
4
- import { ErrorCode, ForbiddenError, InvalidCredentialsError, InvalidPayloadError } from '@directus/errors';
5
4
  import { respond } from '../middleware/respond.js';
6
5
  import useCollection from '../middleware/use-collection.js';
7
6
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -114,7 +114,8 @@ router.post('/cache/clear', asyncHandler(async (req, res) => {
114
114
  accountability: req.accountability,
115
115
  schema: req.schema,
116
116
  });
117
- await service.clearCache();
117
+ const clearSystemCache = 'system' in req.query && (req.query['system'] === '' || Boolean(req.query['system']));
118
+ await service.clearCache({ system: clearSystemCache });
118
119
  res.status(200).end();
119
120
  }));
120
121
  export default router;
@@ -324,14 +324,15 @@ function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
324
324
  nestedItem[nestedNode.relation.field]?.[schema.collections[nestedNode.relation.related_collection].primary] == parentItem[schema.collections[nestedNode.relation.related_collection].primary]);
325
325
  });
326
326
  parentItem[nestedNode.fieldKey].push(...itemChildren);
327
+ const limit = nestedNode.query.limit ?? Number(env['QUERY_LIMIT_DEFAULT']);
327
328
  if (nestedNode.query.page && nestedNode.query.page > 1) {
328
- parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice((nestedNode.query.limit ?? Number(env['QUERY_LIMIT_DEFAULT'])) * (nestedNode.query.page - 1));
329
+ parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(limit * (nestedNode.query.page - 1));
329
330
  }
330
331
  if (nestedNode.query.offset && nestedNode.query.offset >= 0) {
331
332
  parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(nestedNode.query.offset);
332
333
  }
333
- if (nestedNode.query.limit !== -1) {
334
- parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(0, nestedNode.query.limit ?? Number(env['QUERY_LIMIT_DEFAULT']));
334
+ if (limit !== -1) {
335
+ parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(0, limit);
335
336
  }
336
337
  parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].sort((a, b) => {
337
338
  // This is pre-filled in get-ast-from-query
@@ -1,4 +1,5 @@
1
- import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types/index.js';
1
+ import type { Item, PrimaryKey } from '@directus/types';
2
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
2
3
  import { ItemsService } from './items.js';
3
4
  import { NotificationsService } from './notifications.js';
4
5
  import { UsersService } from './users.js';
@@ -1,6 +1,6 @@
1
- import type { Accountability, PermissionsAction, SchemaOverview } from '@directus/types';
1
+ import type { Accountability, Item, PermissionsAction, PrimaryKey, SchemaOverview } from '@directus/types';
2
2
  import type { Knex } from 'knex';
3
- import type { AST, AbstractServiceOptions, Item, PrimaryKey } from '../types/index.js';
3
+ import type { AST, AbstractServiceOptions } from '../types/index.js';
4
4
  import { PayloadService } from './payload.js';
5
5
  export declare class AuthorizationService {
6
6
  knex: Knex;
@@ -1,10 +1,10 @@
1
1
  import type { SchemaInspector, Table } from '@directus/schema';
2
+ import { type BaseCollectionMeta } from '@directus/system-data';
2
3
  import type { Accountability, RawField, SchemaOverview } from '@directus/types';
3
4
  import type Keyv from 'keyv';
4
5
  import type { Knex } from 'knex';
5
6
  import type { Helpers } from '../database/helpers/index.js';
6
7
  import type { AbstractServiceOptions, Collection, MutationOptions } from '../types/index.js';
7
- import { type BaseCollectionMeta } from '@directus/system-data';
8
8
  export type RawCollection = {
9
9
  collection: string;
10
10
  fields?: RawField[];
@@ -1,6 +1,7 @@
1
1
  import { useEnv } from '@directus/env';
2
2
  import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
3
3
  import { createInspector } from '@directus/schema';
4
+ import { systemCollectionRows } from '@directus/system-data';
4
5
  import { addFieldFlag } from '@directus/utils';
5
6
  import { chunk, groupBy, merge, omit } from 'lodash-es';
6
7
  import { clearSystemCache, getCache } from '../cache.js';
@@ -10,9 +11,9 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
10
11
  import emitter from '../emitter.js';
11
12
  import { getSchema } from '../utils/get-schema.js';
12
13
  import { shouldClearCache } from '../utils/should-clear-cache.js';
14
+ import { transaction } from '../utils/transaction.js';
13
15
  import { FieldsService } from './fields.js';
14
16
  import { ItemsService } from './items.js';
15
- import { systemCollectionRows } from '@directus/system-data';
16
17
  export class CollectionsService {
17
18
  knex;
18
19
  helpers;
@@ -56,7 +57,7 @@ export class CollectionsService {
56
57
  // Create the collection/fields in a transaction so it'll be reverted in case of errors or
57
58
  // permission problems. This might not work reliably in MySQL, as it doesn't support DDL in
58
59
  // transactions.
59
- await this.knex.transaction(async (trx) => {
60
+ await transaction(this.knex, async (trx) => {
60
61
  if (payload.schema) {
61
62
  // Directus heavily relies on the primary key of a collection, so we have to make sure that
62
63
  // every collection that is created has a primary key. If no primary key field is created
@@ -167,7 +168,7 @@ export class CollectionsService {
167
168
  async createMany(payloads, opts) {
168
169
  const nestedActionEvents = [];
169
170
  try {
170
- const collections = await this.knex.transaction(async (trx) => {
171
+ const collections = await transaction(this.knex, async (trx) => {
171
172
  const service = new CollectionsService({
172
173
  schema: this.schema,
173
174
  accountability: this.accountability,
@@ -361,7 +362,7 @@ export class CollectionsService {
361
362
  const collectionKeys = [];
362
363
  const nestedActionEvents = [];
363
364
  try {
364
- await this.knex.transaction(async (trx) => {
365
+ await transaction(this.knex, async (trx) => {
365
366
  const collectionItemsService = new CollectionsService({
366
367
  knex: trx,
367
368
  accountability: this.accountability,
@@ -406,7 +407,7 @@ export class CollectionsService {
406
407
  }
407
408
  const nestedActionEvents = [];
408
409
  try {
409
- await this.knex.transaction(async (trx) => {
410
+ await transaction(this.knex, async (trx) => {
410
411
  const service = new CollectionsService({
411
412
  schema: this.schema,
412
413
  accountability: this.accountability,
@@ -453,7 +454,7 @@ export class CollectionsService {
453
454
  if (!!collectionToBeDeleted === false) {
454
455
  throw new ForbiddenError();
455
456
  }
456
- await this.knex.transaction(async (trx) => {
457
+ await transaction(this.knex, async (trx) => {
457
458
  if (collectionToBeDeleted.schema) {
458
459
  await trx.schema.dropTable(collectionKey);
459
460
  }
@@ -552,7 +553,7 @@ export class CollectionsService {
552
553
  }
553
554
  const nestedActionEvents = [];
554
555
  try {
555
- await this.knex.transaction(async (trx) => {
556
+ await transaction(this.knex, async (trx) => {
556
557
  const service = new CollectionsService({
557
558
  schema: this.schema,
558
559
  accountability: this.accountability,
@@ -4,6 +4,7 @@ import { describe } from '@directus/extensions-registry';
4
4
  import { isObject } from '@directus/utils';
5
5
  import getDatabase from '../database/index.js';
6
6
  import { getExtensionManager } from '../extensions/index.js';
7
+ import { transaction } from '../utils/transaction.js';
7
8
  import { ItemsService } from './items.js';
8
9
  export class ExtensionReadError extends Error {
9
10
  originalError;
@@ -147,7 +148,7 @@ export class ExtensionsService {
147
148
  };
148
149
  }
149
150
  async updateOne(id, data) {
150
- const result = await this.knex.transaction(async (trx) => {
151
+ const result = await transaction(this.knex, async (trx) => {
151
152
  if (!isObject(data.meta)) {
152
153
  throw new InvalidPayloadError({ reason: `"meta" is required` });
153
154
  }
@@ -15,6 +15,7 @@ import getLocalType from '../utils/get-local-type.js';
15
15
  import { getSchema } from '../utils/get-schema.js';
16
16
  import { sanitizeColumn } from '../utils/sanitize-schema.js';
17
17
  import { shouldClearCache } from '../utils/should-clear-cache.js';
18
+ import { transaction } from '../utils/transaction.js';
18
19
  import { ItemsService } from './items.js';
19
20
  import { PayloadService } from './payload.js';
20
21
  import { RelationsService } from './relations.js';
@@ -218,7 +219,7 @@ export class FieldsService {
218
219
  if (flagToAdd) {
219
220
  addFieldFlag(field, flagToAdd);
220
221
  }
221
- await this.knex.transaction(async (trx) => {
222
+ await transaction(this.knex, async (trx) => {
222
223
  const itemsService = new ItemsService('directus_fields', {
223
224
  knex: trx,
224
225
  accountability: this.accountability,
@@ -337,7 +338,7 @@ export class FieldsService {
337
338
  const columnToCompare = opts?.bypassLimits && opts.autoPurgeSystemCache === false ? sanitizeColumn(existingColumn) : existingColumn;
338
339
  if (!isEqual(columnToCompare, hookAdjustedField.schema)) {
339
340
  try {
340
- await this.knex.transaction(async (trx) => {
341
+ await transaction(this.knex, async (trx) => {
341
342
  await trx.schema.alterTable(collection, async (table) => {
342
343
  if (!hookAdjustedField.schema)
343
344
  return;
@@ -451,7 +452,7 @@ export class FieldsService {
451
452
  accountability: this.accountability,
452
453
  });
453
454
  }
454
- await this.knex.transaction(async (trx) => {
455
+ await transaction(this.knex, async (trx) => {
455
456
  const relations = this.schema.relations.filter((relation) => {
456
457
  return ((relation.collection === collection && relation.field === field) ||
457
458
  (relation.related_collection === collection && relation.meta?.one_field === field));
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import type { BusboyFileStream, File } from '@directus/types';
2
+ import type { BusboyFileStream, File, PrimaryKey } from '@directus/types';
3
3
  import type { Readable } from 'node:stream';
4
- import type { AbstractServiceOptions, MutationOptions, PrimaryKey } from '../types/index.js';
4
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
5
5
  import { ItemsService } from './items.js';
6
6
  type Metadata = Partial<Pick<File, 'height' | 'width' | 'description' | 'title' | 'tags' | 'metadata'>>;
7
7
  export declare class FilesService extends ItemsService {
@@ -1,5 +1,5 @@
1
- import type { FlowRaw } from '@directus/types';
2
- import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types/index.js';
1
+ import type { FlowRaw, Item, PrimaryKey } from '@directus/types';
2
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
3
3
  import { ItemsService } from './items.js';
4
4
  export declare class FlowsService extends ItemsService<FlowRaw> {
5
5
  constructor(options: AbstractServiceOptions);
@@ -1,10 +1,10 @@
1
1
  import { type DirectusError } from '@directus/errors';
2
- import type { Accountability, Filter, Query, SchemaOverview } from '@directus/types';
2
+ import type { Accountability, Filter, Item, Query, SchemaOverview } from '@directus/types';
3
3
  import type { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLResolveInfo, SelectionNode } from 'graphql';
4
4
  import { GraphQLError, GraphQLSchema } from 'graphql';
5
5
  import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
6
6
  import type { Knex } from 'knex';
7
- import type { AbstractServiceOptions, GraphQLParams, Item } from '../../types/index.js';
7
+ import type { AbstractServiceOptions, GraphQLParams } from '../../types/index.js';
8
8
  export declare class GraphQLService {
9
9
  accountability: Accountability | null;
10
10
  knex: Knex;
@@ -929,6 +929,11 @@ export class GraphQLService {
929
929
  },
930
930
  };
931
931
  }
932
+ else {
933
+ resolver.args = {
934
+ version: GraphQLString,
935
+ };
936
+ }
932
937
  ReadCollectionTypes[collection.collection].addResolver(resolver);
933
938
  ReadCollectionTypes[collection.collection].addResolver({
934
939
  name: `${collection.collection}_aggregated`,
@@ -16,6 +16,7 @@ import getDatabase from '../database/index.js';
16
16
  import emitter from '../emitter.js';
17
17
  import { useLogger } from '../logger.js';
18
18
  import { getDateFormatted } from '../utils/get-date-formatted.js';
19
+ import { transaction } from '../utils/transaction.js';
19
20
  import { Url } from '../utils/url.js';
20
21
  import { userName } from '../utils/user-name.js';
21
22
  import { FilesService } from './files.js';
@@ -54,7 +55,7 @@ export class ImportService {
54
55
  importJSON(collection, stream) {
55
56
  const extractJSON = StreamArray.withParser();
56
57
  const nestedActionEvents = [];
57
- return this.knex.transaction((trx) => {
58
+ return transaction(this.knex, (trx) => {
58
59
  const service = new ItemsService(collection, {
59
60
  knex: trx,
60
61
  schema: this.schema,
@@ -92,7 +93,7 @@ export class ImportService {
92
93
  if (!tmpFile)
93
94
  throw new Error('Failed to create temporary file for import');
94
95
  const nestedActionEvents = [];
95
- return this.knex.transaction((trx) => {
96
+ return transaction(this.knex, (trx) => {
96
97
  const service = new ItemsService(collection, {
97
98
  knex: trx,
98
99
  schema: this.schema,
@@ -211,7 +212,7 @@ export class ExportService {
211
212
  yaml: 'text/yaml',
212
213
  };
213
214
  const database = getDatabase();
214
- await database.transaction(async (trx) => {
215
+ await transaction(database, async (trx) => {
215
216
  const service = new ItemsService(collection, {
216
217
  accountability: this.accountability,
217
218
  schema: this.schema,
@@ -1,7 +1,7 @@
1
- import type { Accountability, PermissionsAction, Query, SchemaOverview } from '@directus/types';
1
+ import type { Accountability, Item as AnyItem, PermissionsAction, PrimaryKey, Query, SchemaOverview } from '@directus/types';
2
2
  import type Keyv from 'keyv';
3
3
  import type { Knex } from 'knex';
4
- import type { AbstractService, AbstractServiceOptions, Item as AnyItem, MutationOptions, PrimaryKey } from '../types/index.js';
4
+ import type { AbstractService, AbstractServiceOptions, MutationOptions } from '../types/index.js';
5
5
  export type QueryOptions = {
6
6
  stripNonRequested?: boolean;
7
7
  permissionsAction?: PermissionsAction;
@@ -1,6 +1,7 @@
1
1
  import { Action } from '@directus/constants';
2
2
  import { useEnv } from '@directus/env';
3
3
  import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
4
+ import { isSystemCollection } from '@directus/system-data';
4
5
  import { assign, clone, cloneDeep, omit, pick, without } from 'lodash-es';
5
6
  import { getCache } from '../cache.js';
6
7
  import { translateDatabaseError } from '../database/errors/translate.js';
@@ -10,10 +11,10 @@ import runAST from '../database/run-ast.js';
10
11
  import emitter from '../emitter.js';
11
12
  import getASTFromQuery from '../utils/get-ast-from-query.js';
12
13
  import { shouldClearCache } from '../utils/should-clear-cache.js';
14
+ import { transaction } from '../utils/transaction.js';
13
15
  import { validateKeys } from '../utils/validate-keys.js';
14
16
  import { AuthorizationService } from './authorization.js';
15
17
  import { PayloadService } from './payload.js';
16
- import { isSystemCollection } from '@directus/system-data';
17
18
  const env = useEnv();
18
19
  export class ItemsService {
19
20
  collection;
@@ -82,7 +83,7 @@ export class ItemsService {
82
83
  // changes in the DB if any of the parts contained within throws an error. This also means
83
84
  // that any errors thrown in any nested relational changes will bubble up and cancel the whole
84
85
  // update tree
85
- const primaryKey = await this.knex.transaction(async (trx) => {
86
+ const primaryKey = await transaction(this.knex, async (trx) => {
86
87
  // We're creating new services instances so they can use the transaction as their Knex interface
87
88
  const payloadService = new PayloadService(this.collection, {
88
89
  accountability: this.accountability,
@@ -255,11 +256,11 @@ export class ItemsService {
255
256
  async createMany(data, opts = {}) {
256
257
  if (!opts.mutationTracker)
257
258
  opts.mutationTracker = this.createMutationTracker();
258
- const { primaryKeys, nestedActionEvents } = await this.knex.transaction(async (trx) => {
259
+ const { primaryKeys, nestedActionEvents } = await transaction(this.knex, async (knex) => {
259
260
  const service = new ItemsService(this.collection, {
260
261
  accountability: this.accountability,
261
262
  schema: this.schema,
262
- knex: trx,
263
+ knex: knex,
263
264
  });
264
265
  const primaryKeys = [];
265
266
  const nestedActionEvents = [];
@@ -418,7 +419,7 @@ export class ItemsService {
418
419
  const primaryKeyField = this.schema.collections[this.collection].primary;
419
420
  const keys = [];
420
421
  try {
421
- await this.knex.transaction(async (trx) => {
422
+ await transaction(this.knex, async (trx) => {
422
423
  const service = new ItemsService(this.collection, {
423
424
  accountability: this.accountability,
424
425
  knex: trx,
@@ -488,7 +489,7 @@ export class ItemsService {
488
489
  if (opts.preMutationError) {
489
490
  throw opts.preMutationError;
490
491
  }
491
- await this.knex.transaction(async (trx) => {
492
+ await transaction(this.knex, async (trx) => {
492
493
  const payloadService = new PayloadService(this.collection, {
493
494
  accountability: this.accountability,
494
495
  knex: trx,
@@ -629,7 +630,7 @@ export class ItemsService {
629
630
  async upsertMany(payloads, opts = {}) {
630
631
  if (!opts.mutationTracker)
631
632
  opts.mutationTracker = this.createMutationTracker();
632
- const primaryKeys = await this.knex.transaction(async (trx) => {
633
+ const primaryKeys = await transaction(this.knex, async (trx) => {
633
634
  const service = new ItemsService(this.collection, {
634
635
  accountability: this.accountability,
635
636
  schema: this.schema,
@@ -697,7 +698,7 @@ export class ItemsService {
697
698
  accountability: this.accountability,
698
699
  });
699
700
  }
700
- await this.knex.transaction(async (trx) => {
701
+ await transaction(this.knex, async (trx) => {
701
702
  await trx(this.collection).whereIn(primaryKeyField, keys).delete();
702
703
  if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
703
704
  const activityService = new ActivityService({
@@ -1,5 +1,5 @@
1
- import type { Notification } from '@directus/types';
2
- import type { AbstractServiceOptions, MutationOptions, PrimaryKey } from '../types/index.js';
1
+ import type { Notification, PrimaryKey } from '@directus/types';
2
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
3
3
  import { ItemsService } from './items.js';
4
4
  import { MailService } from './mail/index.js';
5
5
  import { UsersService } from './users.js';
@@ -1,5 +1,5 @@
1
- import type { OperationRaw } from '@directus/types';
2
- import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types/index.js';
1
+ import type { Item, OperationRaw, PrimaryKey } from '@directus/types';
2
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
3
3
  import { ItemsService } from './items.js';
4
4
  export declare class OperationsService extends ItemsService<OperationRaw> {
5
5
  constructor(options: AbstractServiceOptions);
@@ -1,7 +1,7 @@
1
- import type { Accountability, SchemaOverview } from '@directus/types';
1
+ import type { Accountability, Item, PrimaryKey, SchemaOverview } from '@directus/types';
2
2
  import type { Knex } from 'knex';
3
3
  import type { Helpers } from '../database/helpers/index.js';
4
- import type { AbstractServiceOptions, ActionEventParams, Item, MutationOptions, PrimaryKey } from '../types/index.js';
4
+ import type { AbstractServiceOptions, ActionEventParams, MutationOptions } from '../types/index.js';
5
5
  type Action = 'create' | 'read' | 'update';
6
6
  type Transformers = {
7
7
  [type: string]: (context: {
@@ -1,6 +1,6 @@
1
- import type { ItemPermissions, PermissionsAction, Query } from '@directus/types';
1
+ import type { Item, ItemPermissions, PermissionsAction, PrimaryKey, Query } from '@directus/types';
2
2
  import type Keyv from 'keyv';
3
- import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../../types/index.js';
3
+ import type { AbstractServiceOptions, MutationOptions } from '../../types/index.js';
4
4
  import type { QueryOptions } from '../items.js';
5
5
  import { ItemsService } from '../items.js';
6
6
  export declare class PermissionsService extends ItemsService {
@@ -8,6 +8,7 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
8
8
  import emitter from '../emitter.js';
9
9
  import { getDefaultIndexName } from '../utils/get-default-index-name.js';
10
10
  import { getSchema } from '../utils/get-schema.js';
11
+ import { transaction } from '../utils/transaction.js';
11
12
  import { ItemsService } from './items.js';
12
13
  import { PermissionsService } from './permissions/index.js';
13
14
  export class RelationsService {
@@ -150,7 +151,7 @@ export class RelationsService {
150
151
  many_field: relation.field,
151
152
  one_collection: relation.related_collection || null,
152
153
  };
153
- await this.knex.transaction(async (trx) => {
154
+ await transaction(this.knex, async (trx) => {
154
155
  if (relation.related_collection) {
155
156
  await trx.schema.alterTable(relation.collection, async (table) => {
156
157
  this.alterType(table, relation, fieldSchema.nullable);
@@ -221,7 +222,7 @@ export class RelationsService {
221
222
  this.helpers.schema.preRelationChange(relation);
222
223
  const nestedActionEvents = [];
223
224
  try {
224
- await this.knex.transaction(async (trx) => {
225
+ await transaction(this.knex, async (trx) => {
225
226
  if (existingRelation.related_collection) {
226
227
  await trx.schema.alterTable(collection, async (table) => {
227
228
  let constraintName = getDefaultIndexName('foreign', collection, field);
@@ -308,7 +309,7 @@ export class RelationsService {
308
309
  const runPostColumnChange = await this.helpers.schema.preColumnChange();
309
310
  const nestedActionEvents = [];
310
311
  try {
311
- await this.knex.transaction(async (trx) => {
312
+ await transaction(this.knex, async (trx) => {
312
313
  const existingConstraints = await this.schemaInspector.foreignKeys();
313
314
  const constraintNames = existingConstraints.map((key) => key.constraint_name);
314
315
  if (existingRelation.schema?.constraint_name &&
@@ -1,4 +1,5 @@
1
- import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types/index.js';
1
+ import type { Item, PrimaryKey } from '@directus/types';
2
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
2
3
  import { ItemsService } from './items.js';
3
4
  export declare class RevisionsService extends ItemsService {
4
5
  constructor(options: AbstractServiceOptions);
@@ -1,5 +1,5 @@
1
- import type { Query } from '@directus/types';
2
- import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types/index.js';
1
+ import type { Item, PrimaryKey, Query } from '@directus/types';
2
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
3
3
  import { ItemsService } from './items.js';
4
4
  export declare class RolesService extends ItemsService {
5
5
  constructor(options: AbstractServiceOptions);
@@ -1,5 +1,6 @@
1
1
  import { ForbiddenError, InvalidPayloadError, UnprocessableContentError } from '@directus/errors';
2
2
  import { getMatch } from 'ip-matching';
3
+ import { transaction } from '../utils/transaction.js';
3
4
  import { ItemsService } from './items.js';
4
5
  import { PermissionsService } from './permissions/index.js';
5
6
  import { PresetsService } from './presets.js';
@@ -217,7 +218,7 @@ export class RolesService extends ItemsService {
217
218
  catch (err) {
218
219
  opts.preMutationError = err;
219
220
  }
220
- await this.knex.transaction(async (trx) => {
221
+ await transaction(this.knex, async (trx) => {
221
222
  const itemsService = new ItemsService('directus_roles', {
222
223
  knex: trx,
223
224
  accountability: this.accountability,
@@ -1,4 +1,5 @@
1
- import type { AbstractServiceOptions, Item, LoginResult, MutationOptions, PrimaryKey } from '../types/index.js';
1
+ import type { Item, PrimaryKey } from '@directus/types';
2
+ import type { AbstractServiceOptions, LoginResult, MutationOptions } from '../types/index.js';
2
3
  import { AuthorizationService } from './authorization.js';
3
4
  import { ItemsService } from './items.js';
4
5
  export declare class SharesService extends ItemsService {
@@ -2,6 +2,7 @@ import { useEnv } from '@directus/env';
2
2
  import { ForbiddenError, InvalidCredentialsError } from '@directus/errors';
3
3
  import argon2 from 'argon2';
4
4
  import jwt from 'jsonwebtoken';
5
+ import { useLogger } from '../logger.js';
5
6
  import { getMilliseconds } from '../utils/get-milliseconds.js';
6
7
  import { md } from '../utils/md.js';
7
8
  import { Url } from '../utils/url.js';
@@ -10,7 +11,6 @@ import { AuthorizationService } from './authorization.js';
10
11
  import { ItemsService } from './items.js';
11
12
  import { MailService } from './mail/index.js';
12
13
  import { UsersService } from './users.js';
13
- import { useLogger } from '../logger.js';
14
14
  const env = useEnv();
15
15
  const logger = useLogger();
16
16
  export class SharesService extends ItemsService {
@@ -1,5 +1,6 @@
1
+ import type { PrimaryKey } from '@directus/types';
1
2
  import type { Knex } from 'knex';
2
- import type { AbstractServiceOptions, PrimaryKey } from '../types/index.js';
3
+ import type { AbstractServiceOptions } from '../types/index.js';
3
4
  import { ItemsService } from './items.js';
4
5
  export declare class TFAService {
5
6
  knex: Knex;
@@ -1,6 +1,6 @@
1
+ import { InvalidPayloadError } from '@directus/errors';
1
2
  import { authenticator } from 'otplib';
2
3
  import getDatabase from '../database/index.js';
3
- import { InvalidPayloadError } from '@directus/errors';
4
4
  import { ItemsService } from './items.js';
5
5
  export class TFAService {
6
6
  knex;
@@ -1,5 +1,5 @@
1
- import type { Query } from '@directus/types';
2
- import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types/index.js';
1
+ import type { Item, PrimaryKey, Query } from '@directus/types';
2
+ import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
3
3
  import { ItemsService } from './items.js';
4
4
  export declare class UsersService extends ItemsService {
5
5
  constructor(options: AbstractServiceOptions);
@@ -7,14 +7,15 @@ import jwt from 'jsonwebtoken';
7
7
  import { cloneDeep, isEmpty } from 'lodash-es';
8
8
  import { performance } from 'perf_hooks';
9
9
  import getDatabase from '../database/index.js';
10
+ import { useLogger } from '../logger.js';
10
11
  import isUrlAllowed from '../utils/is-url-allowed.js';
11
12
  import { verifyJWT } from '../utils/jwt.js';
12
13
  import { stall } from '../utils/stall.js';
14
+ import { transaction } from '../utils/transaction.js';
13
15
  import { Url } from '../utils/url.js';
14
16
  import { ItemsService } from './items.js';
15
17
  import { MailService } from './mail/index.js';
16
18
  import { SettingsService } from './settings.js';
17
- import { useLogger } from '../logger.js';
18
19
  const env = useEnv();
19
20
  const logger = useLogger();
20
21
  export class UsersService extends ItemsService {
@@ -195,7 +196,7 @@ export class UsersService extends ItemsService {
195
196
  opts.mutationTracker = this.createMutationTracker();
196
197
  const primaryKeyField = this.schema.collections[this.collection].primary;
197
198
  const keys = [];
198
- await this.knex.transaction(async (trx) => {
199
+ await transaction(this.knex, async (trx) => {
199
200
  const service = new UsersService({
200
201
  accountability: this.accountability,
201
202
  knex: trx,
@@ -1,6 +1,6 @@
1
- import type { Accountability, SchemaOverview } from '@directus/types';
1
+ import type { Accountability, PrimaryKey, SchemaOverview } from '@directus/types';
2
2
  import type { Knex } from 'knex';
3
- import type { AbstractServiceOptions, PrimaryKey } from '../types/index.js';
3
+ import type { AbstractServiceOptions } from '../types/index.js';
4
4
  export declare class UtilsService {
5
5
  knex: Knex;
6
6
  accountability: Accountability | null;
@@ -10,5 +10,7 @@ export declare class UtilsService {
10
10
  item: PrimaryKey;
11
11
  to: PrimaryKey;
12
12
  }): Promise<void>;
13
- clearCache(): Promise<void>;
13
+ clearCache({ system }: {
14
+ system: boolean;
15
+ }): Promise<void>;
14
16
  }
@@ -1,8 +1,8 @@
1
- import { flushCaches, getCache } from '../cache.js';
2
- import getDatabase from '../database/index.js';
1
+ import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
3
2
  import { systemCollectionRows } from '@directus/system-data';
3
+ import { clearSystemCache, getCache } from '../cache.js';
4
+ import getDatabase from '../database/index.js';
4
5
  import emitter from '../emitter.js';
5
- import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
6
6
  import { shouldClearCache } from '../utils/should-clear-cache.js';
7
7
  export class UtilsService {
8
8
  knex;
@@ -112,10 +112,14 @@ export class UtilsService {
112
112
  accountability: this.accountability,
113
113
  });
114
114
  }
115
- async clearCache() {
115
+ async clearCache({ system }) {
116
116
  if (this.accountability?.admin !== true) {
117
117
  throw new ForbiddenError();
118
118
  }
119
- return flushCaches(true);
119
+ const { cache } = getCache();
120
+ if (system) {
121
+ await clearSystemCache({ forced: true });
122
+ }
123
+ return cache?.clear();
120
124
  }
121
125
  }
@@ -17,5 +17,5 @@ export declare class VersionsService extends ItemsService {
17
17
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
18
18
  updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
19
19
  save(key: PrimaryKey, data: Partial<Item>): Promise<Partial<Item>>;
20
- promote(version: PrimaryKey, mainHash: string, fields?: string[]): Promise<import("../types/items.js").PrimaryKey>;
20
+ promote(version: PrimaryKey, mainHash: string, fields?: string[]): Promise<PrimaryKey>;
21
21
  }
@@ -1,6 +1,7 @@
1
1
  import { type DirectusError } from '@directus/errors';
2
2
  import type { Bus } from '@directus/memory';
3
- import type { AbstractServiceOptions, MutationOptions, PrimaryKey, Webhook } from '../types/index.js';
3
+ import type { PrimaryKey } from '@directus/types';
4
+ import type { AbstractServiceOptions, MutationOptions, Webhook } from '../types/index.js';
4
5
  import { ItemsService } from './items.js';
5
6
  export declare class WebhooksService extends ItemsService<Webhook> {
6
7
  messenger: Bus;
@@ -1,13 +1,6 @@
1
1
  import type { DirectusError } from '@directus/errors';
2
- import type { EventContext } from '@directus/types';
2
+ import type { EventContext, PrimaryKey } from '@directus/types';
3
3
  import type { MutationTracker } from '../services/items.js';
4
- export type Item = Record<string, any>;
5
- export type PrimaryKey = string | number;
6
- export type Alterations<T extends Item = Item, K extends keyof T | undefined = undefined> = {
7
- create: Partial<T>[];
8
- update: (K extends keyof T ? Partial<T> & Pick<T, K> : Partial<T>)[];
9
- delete: (K extends keyof T ? T[K] : PrimaryKey)[];
10
- };
11
4
  export type MutationOptions = {
12
5
  /**
13
6
  * Callback function that's fired whenever a revision is made in the mutation
@@ -1,6 +1,5 @@
1
- import type { Accountability, Query, SchemaOverview } from '@directus/types';
1
+ import type { Accountability, Item, PrimaryKey, Query, SchemaOverview } from '@directus/types';
2
2
  import type { Knex } from 'knex';
3
- import type { Item, PrimaryKey } from './items.js';
4
3
  export type AbstractServiceOptions = {
5
4
  knex?: Knex | undefined;
6
5
  accountability?: Accountability | null | undefined;
@@ -9,6 +9,7 @@ import { CollectionsService } from '../services/collections.js';
9
9
  import { FieldsService } from '../services/fields.js';
10
10
  import { RelationsService } from '../services/relations.js';
11
11
  import { DiffKind } from '../types/index.js';
12
+ import { transaction } from '../utils/transaction.js';
12
13
  import { getSchema } from './get-schema.js';
13
14
  const logger = useLogger();
14
15
  export async function applyDiff(currentSnapshot, snapshotDiff, options) {
@@ -22,7 +23,7 @@ export async function applyDiff(currentSnapshot, snapshotDiff, options) {
22
23
  bypassLimits: true,
23
24
  };
24
25
  const runPostColumnChange = await helpers.schema.preColumnChange();
25
- await database.transaction(async (trx) => {
26
+ await transaction(database, async (trx) => {
26
27
  const collectionsService = new CollectionsService({ knex: trx, schema });
27
28
  const getNestedCollectionsToCreate = (currentLevelCollection) => snapshotDiff.collections.filter(({ diff }) => diff[0].rhs?.meta?.group === currentLevelCollection);
28
29
  const getNestedCollectionsToDelete = (currentLevelCollection) => snapshotDiff.collections.filter(({ diff }) => diff[0].lhs?.meta?.group === currentLevelCollection);
@@ -34,24 +34,33 @@ export async function getSchema(options, attempt = 0) {
34
34
  const lockKey = 'schemaCache--preparing';
35
35
  const messageKey = 'schemaCache--done';
36
36
  const processId = await lock.increment(lockKey);
37
+ if (processId >= env['CACHE_SCHEMA_MAX_ITERATIONS']) {
38
+ await lock.delete(lockKey);
39
+ }
37
40
  const currentProcessShouldHandleOperation = processId === 1;
38
41
  if (currentProcessShouldHandleOperation === false) {
39
42
  logger.trace('Schema cache is prepared in another process, waiting for result.');
40
- return new Promise((resolve) => {
43
+ return new Promise((resolve, reject) => {
41
44
  const TIMEOUT = 10000;
42
- let timeout;
43
- const callback = async () => {
44
- if (timeout)
45
- clearTimeout(timeout);
46
- const schema = await getSchema(options, attempt + 1);
47
- resolve(schema);
48
- bus.unsubscribe(messageKey, callback);
49
- };
50
- bus.subscribe(messageKey, callback);
51
- timeout = setTimeout(async () => {
45
+ const timeout = setTimeout(() => {
52
46
  logger.trace('Did not receive schema callback message in time. Pulling schema...');
53
- callback();
47
+ callback().catch(reject);
54
48
  }, TIMEOUT);
49
+ bus.subscribe(messageKey, callback);
50
+ async function callback() {
51
+ try {
52
+ if (timeout)
53
+ clearTimeout(timeout);
54
+ const schema = await getSchema(options, attempt + 1);
55
+ resolve(schema);
56
+ }
57
+ catch (error) {
58
+ reject(error);
59
+ }
60
+ finally {
61
+ bus.unsubscribe(messageKey, callback);
62
+ }
63
+ }
55
64
  });
56
65
  }
57
66
  try {
@@ -1,5 +1,5 @@
1
- import Joi from 'joi';
2
1
  import { isObject } from '@directus/utils';
2
+ import Joi from 'joi';
3
3
  import { cloneDeep } from 'lodash-es';
4
4
  const alterationSchema = Joi.object({
5
5
  create: Joi.array().items(Joi.object().unknown()),
@@ -0,0 +1,9 @@
1
+ import type { Knex } from 'knex';
2
+ /**
3
+ * Execute the given handler within the current transaction or a newly created one
4
+ * if the current knex state isn't a transaction yet.
5
+ *
6
+ * Can be used to ensure the handler is run within a transaction,
7
+ * while preventing nested transactions.
8
+ */
9
+ export declare const transaction: <T = unknown>(knex: Knex, handler: (knex: Knex) => Promise<T>) => Promise<T>;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Execute the given handler within the current transaction or a newly created one
3
+ * if the current knex state isn't a transaction yet.
4
+ *
5
+ * Can be used to ensure the handler is run within a transaction,
6
+ * while preventing nested transactions.
7
+ */
8
+ export const transaction = (knex, handler) => {
9
+ if (knex.isTransaction) {
10
+ return handler(knex);
11
+ }
12
+ else {
13
+ return knex.transaction((trx) => handler(trx));
14
+ }
15
+ };
@@ -1,5 +1,4 @@
1
- import type { SchemaOverview } from '@directus/types';
2
- import type { PrimaryKey } from '../types/index.js';
1
+ import type { PrimaryKey, SchemaOverview } from '@directus/types';
3
2
  /**
4
3
  * Validate keys based on its type
5
4
  */
@@ -39,5 +39,5 @@ export declare function getFieldsPayload(subscription: PSubscription, accountabi
39
39
  * @param event Event data
40
40
  * @returns the fetched data
41
41
  */
42
- export declare function getItemsPayload(subscription: PSubscription, accountability: Accountability | null, schema: SchemaOverview, event?: WebSocketEvent): Promise<string | number | import("../../types/items.js").Item | (string | number)[] | import("../../types/items.js").Item[]>;
42
+ export declare function getItemsPayload(subscription: PSubscription, accountability: Accountability | null, schema: SchemaOverview, event?: WebSocketEvent): Promise<string | number | import("@directus/types").Item | (string | number)[] | import("@directus/types").Item[]>;
43
43
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/api",
3
- "version": "19.0.0",
3
+ "version": "19.0.2",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "keywords": [
6
6
  "directus",
@@ -59,7 +59,7 @@
59
59
  ],
60
60
  "dependencies": {
61
61
  "@authenio/samlify-node-xmllint": "2.0.0",
62
- "@aws-sdk/client-ses": "3.533.0",
62
+ "@aws-sdk/client-ses": "3.552.0",
63
63
  "@godaddy/terminus": "4.12.1",
64
64
  "@rollup/plugin-alias": "5.1.0",
65
65
  "@rollup/plugin-node-resolve": "15.2.3",
@@ -67,7 +67,7 @@
67
67
  "@types/cookie": "0.6.0",
68
68
  "argon2": "0.40.1",
69
69
  "async": "3.2.5",
70
- "axios": "1.6.7",
70
+ "axios": "1.6.8",
71
71
  "busboy": "1.6.0",
72
72
  "bytes": "3.1.2",
73
73
  "camelcase": "8.0.0",
@@ -116,12 +116,12 @@
116
116
  "minimatch": "9.0.3",
117
117
  "mnemonist": "0.39.8",
118
118
  "ms": "2.1.3",
119
- "nanoid": "5.0.6",
119
+ "nanoid": "5.0.7",
120
120
  "node-machine-id": "1.1.12",
121
121
  "node-schedule": "2.1.1",
122
122
  "nodemailer": "6.9.13",
123
123
  "object-hash": "3.0.0",
124
- "openapi3-ts": "4.2.2",
124
+ "openapi3-ts": "4.3.1",
125
125
  "openid-client": "5.6.5",
126
126
  "ora": "8.0.1",
127
127
  "otplib": "12.0.1",
@@ -146,29 +146,29 @@
146
146
  "ws": "8.16.0",
147
147
  "zod": "3.22.4",
148
148
  "zod-validation-error": "3.0.3",
149
- "@directus/app": "12.0.0",
150
- "@directus/env": "1.1.1",
151
- "@directus/errors": "0.2.4",
149
+ "@directus/env": "1.1.2",
150
+ "@directus/app": "12.0.2",
151
+ "@directus/extensions": "1.0.3",
152
152
  "@directus/constants": "11.0.3",
153
- "@directus/extensions-registry": "1.0.2",
154
- "@directus/extensions-sdk": "11.0.2",
155
- "@directus/extensions": "1.0.2",
153
+ "@directus/extensions-registry": "1.0.3",
154
+ "@directus/errors": "0.2.4",
155
+ "@directus/extensions-sdk": "11.0.3",
156
156
  "@directus/format-title": "10.1.1",
157
- "@directus/memory": "1.0.6",
158
157
  "@directus/pressure": "1.0.18",
159
158
  "@directus/schema": "11.0.1",
159
+ "@directus/memory": "1.0.6",
160
160
  "@directus/specs": "10.2.8",
161
- "@directus/storage": "10.0.11",
162
- "@directus/storage-driver-cloudinary": "10.0.19",
163
161
  "@directus/storage-driver-azure": "10.0.19",
164
162
  "@directus/storage-driver-gcs": "10.0.19",
163
+ "@directus/storage-driver-cloudinary": "10.0.19",
165
164
  "@directus/storage-driver-local": "10.0.18",
165
+ "@directus/storage": "10.0.11",
166
166
  "@directus/storage-driver-s3": "10.0.20",
167
167
  "@directus/storage-driver-supabase": "1.0.11",
168
- "@directus/utils": "11.0.7",
169
168
  "@directus/system-data": "1.0.2",
170
- "@directus/validation": "0.0.14",
171
- "directus": "10.10.5"
169
+ "directus": "10.10.7",
170
+ "@directus/utils": "11.0.7",
171
+ "@directus/validation": "0.0.14"
172
172
  },
173
173
  "devDependencies": {
174
174
  "@ngneat/falso": "7.2.0",
@@ -211,8 +211,8 @@
211
211
  "typescript": "5.3.3",
212
212
  "vitest": "1.3.1",
213
213
  "@directus/random": "0.2.7",
214
- "@directus/tsconfig": "1.0.1",
215
- "@directus/types": "11.0.8"
214
+ "@directus/types": "11.1.0",
215
+ "@directus/tsconfig": "1.0.1"
216
216
  },
217
217
  "optionalDependencies": {
218
218
  "@keyv/redis": "2.8.4",