directus 9.14.5 → 9.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/__utils__/items-utils.d.ts +2 -0
  2. package/dist/__utils__/items-utils.js +36 -0
  3. package/dist/__utils__/schemas.d.ts +13 -0
  4. package/dist/__utils__/schemas.js +304 -0
  5. package/dist/__utils__/snapshots.d.ts +5 -0
  6. package/dist/__utils__/snapshots.js +897 -0
  7. package/dist/app.js +2 -1
  8. package/dist/auth/drivers/ldap.js +18 -8
  9. package/dist/auth/drivers/oauth2.js +19 -9
  10. package/dist/auth/drivers/openid.js +19 -9
  11. package/dist/cache.d.ts +3 -0
  12. package/dist/cache.js +21 -2
  13. package/dist/cli/index.test.d.ts +1 -0
  14. package/dist/cli/index.test.js +58 -0
  15. package/dist/cli/utils/create-env/env-stub.liquid +2 -2
  16. package/dist/constants.d.ts +1 -0
  17. package/dist/constants.js +2 -1
  18. package/dist/controllers/assets.js +27 -1
  19. package/dist/controllers/extensions.js +3 -2
  20. package/dist/controllers/files.test.d.ts +1 -0
  21. package/dist/controllers/files.test.js +49 -0
  22. package/dist/controllers/server.js +0 -1
  23. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +3 -14
  24. package/dist/database/helpers/schema/dialects/cockroachdb.js +2 -8
  25. package/dist/database/helpers/schema/dialects/oracle.d.ts +3 -10
  26. package/dist/database/helpers/schema/dialects/oracle.js +2 -5
  27. package/dist/database/helpers/schema/types.d.ts +8 -18
  28. package/dist/database/helpers/schema/types.js +7 -36
  29. package/dist/database/migrations/20201105B-change-webhook-url-type.js +3 -2
  30. package/dist/database/migrations/20210312A-webhooks-collections-text.js +3 -2
  31. package/dist/database/migrations/20210415A-make-filesize-nullable.js +2 -2
  32. package/dist/database/migrations/20210510A-restructure-relations.js +3 -3
  33. package/dist/database/migrations/20210903A-add-auth-provider.js +2 -2
  34. package/dist/database/migrations/20210907A-webhooks-collections-not-null.js +4 -2
  35. package/dist/database/migrations/20210920A-webhooks-url-not-null.js +2 -2
  36. package/dist/database/migrations/20220303A-remove-default-project-color.js +2 -2
  37. package/dist/database/migrations/20220325B-add-default-language.js +2 -2
  38. package/dist/database/migrations/20220402A-remove-default-value-panel-icon.js +2 -2
  39. package/dist/database/migrations/20220801A-update-notifications-timestamp-column.d.ts +3 -0
  40. package/dist/database/migrations/20220801A-update-notifications-timestamp-column.js +19 -0
  41. package/dist/database/migrations/20220802A-add-custom-aspect-ratios.d.ts +3 -0
  42. package/dist/database/migrations/20220802A-add-custom-aspect-ratios.js +15 -0
  43. package/dist/database/migrations/run.test.d.ts +1 -0
  44. package/dist/database/migrations/run.test.js +92 -0
  45. package/dist/database/system-data/fields/settings.yaml +33 -0
  46. package/dist/env.js +8 -0
  47. package/dist/env.test.d.ts +8 -0
  48. package/dist/env.test.js +39 -0
  49. package/dist/extensions.d.ts +2 -2
  50. package/dist/extensions.js +10 -9
  51. package/dist/flows.js +2 -1
  52. package/dist/logger.js +0 -1
  53. package/dist/middleware/authenticate.test.d.ts +1 -0
  54. package/dist/middleware/authenticate.test.js +174 -0
  55. package/dist/middleware/cache.js +3 -3
  56. package/dist/middleware/extract-token.test.d.ts +1 -0
  57. package/dist/middleware/extract-token.test.js +60 -0
  58. package/dist/middleware/respond.js +2 -2
  59. package/dist/operations/exec/index.d.ts +5 -0
  60. package/dist/operations/exec/index.js +26 -0
  61. package/dist/operations/exec/index.test.d.ts +1 -0
  62. package/dist/operations/exec/index.test.js +95 -0
  63. package/dist/operations/item-create/index.js +1 -1
  64. package/dist/operations/item-delete/index.js +2 -2
  65. package/dist/operations/item-read/index.js +2 -2
  66. package/dist/operations/item-update/index.js +3 -3
  67. package/dist/operations/notification/index.js +9 -6
  68. package/dist/operations/request/index.js +22 -3
  69. package/dist/services/assets.d.ts +1 -1
  70. package/dist/services/assets.js +7 -2
  71. package/dist/services/authorization.js +2 -1
  72. package/dist/services/files.js +3 -2
  73. package/dist/services/files.test.d.ts +1 -0
  74. package/dist/services/files.test.js +53 -0
  75. package/dist/services/flows.js +4 -0
  76. package/dist/services/graphql/index.d.ts +2 -2
  77. package/dist/services/graphql/index.js +59 -40
  78. package/dist/services/graphql/types/hash.d.ts +2 -0
  79. package/dist/services/graphql/types/hash.js +9 -0
  80. package/dist/services/items.js +83 -39
  81. package/dist/services/items.test.d.ts +1 -0
  82. package/dist/services/items.test.js +765 -0
  83. package/dist/services/payload.d.ts +7 -4
  84. package/dist/services/payload.js +35 -8
  85. package/dist/services/payload.test.d.ts +1 -0
  86. package/dist/services/payload.test.js +94 -0
  87. package/dist/services/server.js +5 -3
  88. package/dist/services/specifications.js +1 -6
  89. package/dist/services/specifications.test.d.ts +1 -0
  90. package/dist/services/specifications.test.js +96 -0
  91. package/dist/types/items.d.ts +11 -0
  92. package/dist/utils/apply-query.js +15 -0
  93. package/dist/utils/apply-snapshot.js +15 -0
  94. package/dist/utils/apply-snapshot.test.d.ts +1 -0
  95. package/dist/utils/apply-snapshot.test.js +305 -0
  96. package/dist/utils/calculate-field-depth.test.d.ts +1 -0
  97. package/dist/utils/calculate-field-depth.test.js +76 -0
  98. package/dist/utils/compress.d.ts +3 -0
  99. package/dist/utils/compress.js +17 -0
  100. package/dist/utils/filter-items.test.d.ts +1 -0
  101. package/dist/utils/filter-items.test.js +60 -0
  102. package/dist/utils/get-ast-from-query.js +1 -1
  103. package/dist/utils/get-cache-key.test.d.ts +1 -0
  104. package/dist/utils/get-cache-key.test.js +53 -0
  105. package/dist/utils/get-column-path.test.d.ts +1 -0
  106. package/dist/utils/get-column-path.test.js +136 -0
  107. package/dist/utils/get-config-from-env.test.d.ts +1 -0
  108. package/dist/utils/get-config-from-env.test.js +19 -0
  109. package/dist/utils/get-graphql-type.d.ts +1 -1
  110. package/dist/utils/get-graphql-type.js +7 -1
  111. package/dist/utils/get-os-info.d.ts +9 -0
  112. package/dist/utils/get-os-info.js +47 -0
  113. package/dist/utils/get-permissions.js +2 -2
  114. package/dist/utils/get-relation-info.test.d.ts +1 -0
  115. package/dist/utils/get-relation-info.test.js +88 -0
  116. package/dist/utils/get-relation-type.test.d.ts +1 -0
  117. package/dist/utils/get-relation-type.test.js +69 -0
  118. package/dist/utils/get-schema.js +1 -2
  119. package/dist/utils/get-string-byte-size.test.d.ts +1 -0
  120. package/dist/utils/get-string-byte-size.test.js +8 -0
  121. package/dist/utils/is-directus-jwt.test.d.ts +1 -0
  122. package/dist/utils/is-directus-jwt.test.js +26 -0
  123. package/dist/utils/jwt.test.d.ts +1 -0
  124. package/dist/utils/jwt.test.js +36 -0
  125. package/dist/utils/merge-permissions.test.d.ts +1 -0
  126. package/dist/utils/merge-permissions.test.js +80 -0
  127. package/dist/utils/validate-keys.test.d.ts +1 -0
  128. package/dist/utils/validate-keys.test.js +97 -0
  129. package/package.json +12 -11
@@ -39,12 +39,16 @@ class FlowsService extends items_1.ItemsService {
39
39
  }
40
40
  async deleteOne(key, opts) {
41
41
  const flowManager = (0, flows_1.getFlowManager)();
42
+ // this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
43
+ await this.knex('directus_operations').update({ resolve: null, reject: null }).where('flow', key);
42
44
  const result = await super.deleteOne(key, opts);
43
45
  await flowManager.reload();
44
46
  return result;
45
47
  }
46
48
  async deleteMany(keys, opts) {
47
49
  const flowManager = (0, flows_1.getFlowManager)();
50
+ // this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
51
+ await this.knex('directus_operations').update({ resolve: null, reject: null }).whereIn('flow', keys);
48
52
  const result = await super.deleteMany(keys, opts);
49
53
  await flowManager.reload();
50
54
  return result;
@@ -1,6 +1,6 @@
1
1
  import { BaseException } from '@directus/shared/exceptions';
2
2
  import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
3
- import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLSchema, ObjectFieldNode, SelectionNode } from 'graphql';
3
+ import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLSchema, SelectionNode } from 'graphql';
4
4
  import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
5
5
  import { Knex } from 'knex';
6
6
  import { AbstractServiceOptions, GraphQLParams, Item } from '../../types';
@@ -44,7 +44,7 @@ export declare class GraphQLService {
44
44
  * In order to do that, we'll parse over all ArgumentNodes and ObjectFieldNodes to manually recreate an object structure
45
45
  * of arguments
46
46
  */
47
- parseArgs(args: readonly ArgumentNode[] | readonly ObjectFieldNode[], variableValues: GraphQLResolveInfo['variableValues']): Record<string, any>;
47
+ parseArgs(args: readonly ArgumentNode[], variableValues: GraphQLResolveInfo['variableValues']): Record<string, any>;
48
48
  /**
49
49
  * Get a Directus Query object from the parsed arguments (rawQuery) and GraphQL AST selectionSet. Converts SelectionSet into
50
50
  * Directus' `fields` query for use in the resolver. Also applies variables where appropriate.
@@ -50,6 +50,7 @@ const geojson_1 = require("./types/geojson");
50
50
  const string_or_float_1 = require("./types/string-or-float");
51
51
  const void_1 = require("./types/void");
52
52
  const add_path_to_validation_error_1 = require("./utils/add-path-to-validation-error");
53
+ const hash_1 = require("./types/hash");
53
54
  const validationRules = Array.from(graphql_1.specifiedRules);
54
55
  if (env_1.default.GRAPHQL_INTROSPECTION === false) {
55
56
  validationRules.push(graphql_1.NoSchemaIntrospectionCustomRule);
@@ -273,11 +274,13 @@ class GraphQLService {
273
274
  CollectionTypes[collection.collection] = schemaComposer.createObjectTC({
274
275
  name: action === 'read' ? collection.collection : `${action}_${collection.collection}`,
275
276
  fields: Object.values(collection.fields).reduce((acc, field) => {
276
- let type = (0, get_graphql_type_1.getGraphQLType)(field.type);
277
+ let type = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
277
278
  // GraphQL doesn't differentiate between not-null and has-to-be-submitted. We
278
279
  // can't non-null in update, as that would require every not-nullable field to be
279
280
  // submitted on updates
280
- if (field.nullable === false && action !== 'update') {
281
+ if (field.nullable === false &&
282
+ !constants_1.GENERATE_SPECIAL.some((flag) => field.special.includes(flag)) &&
283
+ action !== 'update') {
281
284
  type = (0, graphql_1.GraphQLNonNull)(type);
282
285
  }
283
286
  if (collection.primary === field.field) {
@@ -562,6 +565,23 @@ class GraphQLService {
562
565
  },
563
566
  },
564
567
  });
568
+ const HashFilterOperators = schemaComposer.createInputTC({
569
+ name: 'hash_filter_operators',
570
+ fields: {
571
+ _null: {
572
+ type: graphql_1.GraphQLBoolean,
573
+ },
574
+ _nnull: {
575
+ type: graphql_1.GraphQLBoolean,
576
+ },
577
+ _empty: {
578
+ type: graphql_1.GraphQLBoolean,
579
+ },
580
+ _nempty: {
581
+ type: graphql_1.GraphQLBoolean,
582
+ },
583
+ },
584
+ });
565
585
  const CountFunctionFilterOperators = schemaComposer.createInputTC({
566
586
  name: 'count_function_filter_operators',
567
587
  fields: {
@@ -619,7 +639,7 @@ class GraphQLService {
619
639
  ReadableCollectionFilterTypes[collection.collection] = schemaComposer.createInputTC({
620
640
  name: `${collection.collection}_filter`,
621
641
  fields: Object.values(collection.fields).reduce((acc, field) => {
622
- const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
642
+ const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
623
643
  let filterOperatorType;
624
644
  switch (graphqlType) {
625
645
  case graphql_1.GraphQLBoolean:
@@ -635,6 +655,9 @@ class GraphQLService {
635
655
  case geojson_1.GraphQLGeoJSON:
636
656
  filterOperatorType = GeometryFilterOperators;
637
657
  break;
658
+ case hash_1.GraphQLHash:
659
+ filterOperatorType = HashFilterOperators;
660
+ break;
638
661
  default:
639
662
  filterOperatorType = StringFilterOperators;
640
663
  }
@@ -669,7 +692,7 @@ class GraphQLService {
669
692
  AggregatedFields[collection.collection] = schemaComposer.createObjectTC({
670
693
  name: `${collection.collection}_aggregated_fields`,
671
694
  fields: Object.values(collection.fields).reduce((acc, field) => {
672
- const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
695
+ const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
673
696
  switch (graphqlType) {
674
697
  case graphql_1.GraphQLInt:
675
698
  case graphql_1.GraphQLFloat:
@@ -713,7 +736,7 @@ class GraphQLService {
713
736
  },
714
737
  };
715
738
  const hasNumericAggregates = Object.values(collection.fields).some((field) => {
716
- const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
739
+ const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type, field.special);
717
740
  if (graphqlType === graphql_1.GraphQLInt || graphqlType === graphql_1.GraphQLFloat) {
718
741
  return true;
719
742
  }
@@ -1184,38 +1207,29 @@ class GraphQLService {
1184
1207
  parseArgs(args, variableValues) {
1185
1208
  if (!args || args.length === 0)
1186
1209
  return {};
1187
- const parseObjectValue = (arg) => {
1188
- return this.parseArgs(arg.fields, variableValues);
1189
- };
1190
- const argsObject = {};
1191
- for (const argument of args) {
1192
- if (argument.value.kind === 'ObjectValue') {
1193
- argsObject[argument.name.value] = parseObjectValue(argument.value);
1194
- }
1195
- else if (argument.value.kind === 'Variable') {
1196
- argsObject[argument.name.value] = variableValues[argument.value.name.value];
1197
- }
1198
- else if (argument.value.kind === 'ListValue') {
1199
- const values = [];
1200
- for (const valueNode of argument.value.values) {
1201
- if (valueNode.kind === 'ObjectValue') {
1202
- values.push(this.parseArgs(valueNode.fields, variableValues));
1203
- }
1204
- else {
1205
- if (valueNode.kind === 'Variable') {
1206
- values.push(variableValues[valueNode.name.value]);
1207
- }
1208
- else {
1209
- values.push(valueNode.value);
1210
- }
1211
- }
1212
- }
1213
- argsObject[argument.name.value] = values;
1210
+ const parse = (node) => {
1211
+ switch (node.kind) {
1212
+ case 'Variable':
1213
+ return variableValues[node.name.value];
1214
+ case 'ListValue':
1215
+ return node.values.map(parse);
1216
+ case 'ObjectValue':
1217
+ return Object.fromEntries(node.fields.map((node) => [node.name.value, parse(node.value)]));
1218
+ case 'NullValue':
1219
+ return null;
1220
+ case 'StringValue':
1221
+ return String(node.value);
1222
+ case 'IntValue':
1223
+ case 'FloatValue':
1224
+ return Number(node.value);
1225
+ case 'BooleanValue':
1226
+ return Boolean(node.value);
1227
+ case 'EnumValue':
1228
+ default:
1229
+ return node.value;
1214
1230
  }
1215
- else {
1216
- argsObject[argument.name.value] = argument.value.value;
1217
- }
1218
- }
1231
+ };
1232
+ const argsObject = Object.fromEntries(args.map((arg) => [arg.name.value, parse(arg.value)]));
1219
1233
  return argsObject;
1220
1234
  }
1221
1235
  /**
@@ -1315,7 +1329,8 @@ class GraphQLService {
1315
1329
  result[currentKey] = Object.values(value)[0];
1316
1330
  }
1317
1331
  else {
1318
- result[currentKey] = (value === null || value === void 0 ? void 0 : value.constructor) === Object ? replaceFuncDeep(value) : value;
1332
+ result[currentKey] =
1333
+ (value === null || value === void 0 ? void 0 : value.constructor) === Object || (value === null || value === void 0 ? void 0 : value.constructor) === Array ? replaceFuncDeep(value) : value;
1319
1334
  }
1320
1335
  });
1321
1336
  }
@@ -1878,7 +1893,9 @@ class GraphQLService {
1878
1893
  name: 'directus_collections_meta',
1879
1894
  fields: Object.values(schema.read.collections['directus_collections'].fields).reduce((acc, field) => {
1880
1895
  acc[field.field] = {
1881
- type: field.nullable ? (0, get_graphql_type_1.getGraphQLType)(field.type) : (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type)),
1896
+ type: field.nullable
1897
+ ? (0, get_graphql_type_1.getGraphQLType)(field.type, field.special)
1898
+ : (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type, field.special)),
1882
1899
  description: field.note,
1883
1900
  };
1884
1901
  return acc;
@@ -1927,7 +1944,9 @@ class GraphQLService {
1927
1944
  name: 'directus_fields_meta',
1928
1945
  fields: Object.values(schema.read.collections['directus_fields'].fields).reduce((acc, field) => {
1929
1946
  acc[field.field] = {
1930
- type: field.nullable ? (0, get_graphql_type_1.getGraphQLType)(field.type) : (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type)),
1947
+ type: field.nullable
1948
+ ? (0, get_graphql_type_1.getGraphQLType)(field.type, field.special)
1949
+ : (0, graphql_1.GraphQLNonNull)((0, get_graphql_type_1.getGraphQLType)(field.type, field.special)),
1931
1950
  description: field.note,
1932
1951
  };
1933
1952
  return acc;
@@ -2014,7 +2033,7 @@ class GraphQLService {
2014
2033
  name: 'directus_relations_meta',
2015
2034
  fields: Object.values(schema.read.collections['directus_relations'].fields).reduce((acc, field) => {
2016
2035
  acc[field.field] = {
2017
- type: (0, get_graphql_type_1.getGraphQLType)(field.type),
2036
+ type: (0, get_graphql_type_1.getGraphQLType)(field.type, field.special),
2018
2037
  description: field.note,
2019
2038
  };
2020
2039
  return acc;
@@ -0,0 +1,2 @@
1
+ import { GraphQLScalarType } from 'graphql';
2
+ export declare const GraphQLHash: GraphQLScalarType;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GraphQLHash = void 0;
4
+ const graphql_1 = require("graphql");
5
+ exports.GraphQLHash = new graphql_1.GraphQLScalarType({
6
+ ...graphql_1.GraphQLString,
7
+ name: 'Hash',
8
+ description: 'Hashed string values',
9
+ });
@@ -52,6 +52,7 @@ class ItemsService {
52
52
  .filter((field) => field.alias === true)
53
53
  .map((field) => field.field);
54
54
  const payload = (0, lodash_1.cloneDeep)(data);
55
+ const nestedActionEvents = [];
55
56
  // By wrapping the logic in a transaction, we make sure we automatically roll back all the
56
57
  // changes in the DB if any of the parts contained within throws an error. This also means
57
58
  // that any errors thrown in any nested relational changes will bubble up and cancel the whole
@@ -84,8 +85,8 @@ class ItemsService {
84
85
  const payloadWithPresets = this.accountability
85
86
  ? await authorizationService.validatePayload('create', this.collection, payloadAfterHooks)
86
87
  : payloadAfterHooks;
87
- const { payload: payloadWithM2O, revisions: revisionsM2O } = await payloadService.processM2O(payloadWithPresets);
88
- const { payload: payloadWithA2O, revisions: revisionsA2O } = await payloadService.processA2O(payloadWithM2O);
88
+ const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
89
+ const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
89
90
  const payloadWithoutAliases = (0, lodash_1.pick)(payloadWithA2O, (0, lodash_1.without)(fields, ...aliases));
90
91
  const payloadWithTypeCasting = await payloadService.processValues('create', payloadWithoutAliases);
91
92
  // In case of manual string / UUID primary keys, the PK already exists in the object we're saving.
@@ -113,7 +114,10 @@ class ItemsService {
113
114
  // to read from it
114
115
  payload[primaryKeyField] = primaryKey;
115
116
  }
116
- const { revisions: revisionsO2M } = await payloadService.processO2M(payload, primaryKey);
117
+ const { revisions: revisionsO2M, nestedActionEvents: nestedActionEventsO2M } = await payloadService.processO2M(payload, primaryKey, opts);
118
+ nestedActionEvents.push(...nestedActionEventsM2O);
119
+ nestedActionEvents.push(...nestedActionEventsA2O);
120
+ nestedActionEvents.push(...nestedActionEventsO2M);
117
121
  // If this is an authenticated action, and accountability tracking is enabled, save activity row
118
122
  if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
119
123
  const activityService = new index_1.ActivityService({
@@ -154,17 +158,30 @@ class ItemsService {
154
158
  return primaryKey;
155
159
  });
156
160
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
157
- emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.create', `${this.collection}.items.create`] : `${this.eventScope}.create`, {
158
- payload,
159
- key: primaryKey,
160
- collection: this.collection,
161
- }, {
162
- // This hook is called async. If we would pass the transaction here, the hook can be
163
- // called after the transaction is done #5460
164
- database: this.knex || (0, database_1.default)(),
165
- schema: this.schema,
166
- accountability: this.accountability,
167
- });
161
+ const actionEvent = {
162
+ event: this.eventScope === 'items'
163
+ ? ['items.create', `${this.collection}.items.create`]
164
+ : `${this.eventScope}.create`,
165
+ meta: {
166
+ payload,
167
+ key: primaryKey,
168
+ collection: this.collection,
169
+ },
170
+ context: {
171
+ database: (0, database_1.default)(),
172
+ schema: this.schema,
173
+ accountability: this.accountability,
174
+ },
175
+ };
176
+ if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
177
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
178
+ }
179
+ else {
180
+ opts.bypassEmitAction(actionEvent);
181
+ }
182
+ for (const nestedActionEvent of nestedActionEvents) {
183
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
184
+ }
168
185
  }
169
186
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
170
187
  await this.cache.clear();
@@ -323,6 +340,7 @@ class ItemsService {
323
340
  .filter((field) => field.alias === true)
324
341
  .map((field) => field.field);
325
342
  const payload = (0, lodash_1.cloneDeep)(data);
343
+ const nestedActionEvents = [];
326
344
  const authorizationService = new authorization_1.AuthorizationService({
327
345
  accountability: this.accountability,
328
346
  knex: this.knex,
@@ -356,8 +374,8 @@ class ItemsService {
356
374
  knex: trx,
357
375
  schema: this.schema,
358
376
  });
359
- const { payload: payloadWithM2O, revisions: revisionsM2O } = await payloadService.processM2O(payloadWithPresets);
360
- const { payload: payloadWithA2O, revisions: revisionsA2O } = await payloadService.processA2O(payloadWithM2O);
377
+ const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
378
+ const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
361
379
  const payloadWithoutAliasAndPK = (0, lodash_1.pick)(payloadWithA2O, (0, lodash_1.without)(fields, primaryKeyField, ...aliases));
362
380
  const payloadWithTypeCasting = await payloadService.processValues('update', payloadWithoutAliasAndPK);
363
381
  if (Object.keys(payloadWithTypeCasting).length > 0) {
@@ -369,9 +387,12 @@ class ItemsService {
369
387
  }
370
388
  }
371
389
  const childrenRevisions = [...revisionsM2O, ...revisionsA2O];
390
+ nestedActionEvents.push(...nestedActionEventsM2O);
391
+ nestedActionEvents.push(...nestedActionEventsA2O);
372
392
  for (const key of keys) {
373
- const { revisions } = await payloadService.processO2M(payload, key);
393
+ const { revisions, nestedActionEvents: nestedActionEventsO2M } = await payloadService.processO2M(payload, key, opts);
374
394
  childrenRevisions.push(...revisions);
395
+ nestedActionEvents.push(...nestedActionEventsO2M);
375
396
  }
376
397
  // If this is an authenticated action, and accountability tracking is enabled, save activity row
377
398
  if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
@@ -427,17 +448,30 @@ class ItemsService {
427
448
  await this.cache.clear();
428
449
  }
429
450
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
430
- emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.update', `${this.collection}.items.update`] : `${this.eventScope}.update`, {
431
- payload,
432
- keys,
433
- collection: this.collection,
434
- }, {
435
- // This hook is called async. If we would pass the transaction here, the hook can be
436
- // called after the transaction is done #5460
437
- database: this.knex || (0, database_1.default)(),
438
- schema: this.schema,
439
- accountability: this.accountability,
440
- });
451
+ const actionEvent = {
452
+ event: this.eventScope === 'items'
453
+ ? ['items.update', `${this.collection}.items.update`]
454
+ : `${this.eventScope}.update`,
455
+ meta: {
456
+ payload,
457
+ keys,
458
+ collection: this.collection,
459
+ },
460
+ context: {
461
+ database: (0, database_1.default)(),
462
+ schema: this.schema,
463
+ accountability: this.accountability,
464
+ },
465
+ };
466
+ if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
467
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
468
+ }
469
+ else {
470
+ opts.bypassEmitAction(actionEvent);
471
+ }
472
+ for (const nestedActionEvent of nestedActionEvents) {
473
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
474
+ }
441
475
  }
442
476
  return keys;
443
477
  }
@@ -547,17 +581,27 @@ class ItemsService {
547
581
  await this.cache.clear();
548
582
  }
549
583
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
550
- emitter_1.default.emitAction(this.eventScope === 'items' ? ['items.delete', `${this.collection}.items.delete`] : `${this.eventScope}.delete`, {
551
- payload: keys,
552
- keys: keys,
553
- collection: this.collection,
554
- }, {
555
- // This hook is called async. If we would pass the transaction here, the hook can be
556
- // called after the transaction is done #5460
557
- database: this.knex || (0, database_1.default)(),
558
- schema: this.schema,
559
- accountability: this.accountability,
560
- });
584
+ const actionEvent = {
585
+ event: this.eventScope === 'items'
586
+ ? ['items.delete', `${this.collection}.items.delete`]
587
+ : `${this.eventScope}.delete`,
588
+ meta: {
589
+ payload: keys,
590
+ keys: keys,
591
+ collection: this.collection,
592
+ },
593
+ context: {
594
+ database: (0, database_1.default)(),
595
+ schema: this.schema,
596
+ accountability: this.accountability,
597
+ },
598
+ };
599
+ if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
600
+ emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
601
+ }
602
+ else {
603
+ opts.bypassEmitAction(actionEvent);
604
+ }
561
605
  }
562
606
  return keys;
563
607
  }
@@ -0,0 +1 @@
1
+ export {};