@directus/api 30.0.0 → 31.0.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 (83) hide show
  1. package/dist/app.js +5 -0
  2. package/dist/auth/drivers/oauth2.js +17 -3
  3. package/dist/auth/drivers/openid.js +17 -3
  4. package/dist/controllers/mcp.d.ts +2 -0
  5. package/dist/controllers/mcp.js +33 -0
  6. package/dist/controllers/users.js +17 -7
  7. package/dist/controllers/versions.js +3 -2
  8. package/dist/database/errors/dialects/mssql.d.ts +1 -1
  9. package/dist/database/errors/dialects/mssql.js +18 -10
  10. package/dist/database/migrations/20250813A-add-mcp.d.ts +3 -0
  11. package/dist/database/migrations/20250813A-add-mcp.js +18 -0
  12. package/dist/database/run-ast/README.md +46 -0
  13. package/dist/mcp/define.d.ts +2 -0
  14. package/dist/mcp/define.js +3 -0
  15. package/dist/mcp/index.d.ts +1 -0
  16. package/dist/mcp/index.js +1 -0
  17. package/dist/mcp/schema.d.ts +485 -0
  18. package/dist/mcp/schema.js +219 -0
  19. package/dist/mcp/server.d.ts +97 -0
  20. package/dist/mcp/server.js +310 -0
  21. package/dist/mcp/tools/assets.d.ts +3 -0
  22. package/dist/mcp/tools/assets.js +54 -0
  23. package/dist/mcp/tools/collections.d.ts +84 -0
  24. package/dist/mcp/tools/collections.js +90 -0
  25. package/dist/mcp/tools/fields.d.ts +101 -0
  26. package/dist/mcp/tools/fields.js +157 -0
  27. package/dist/mcp/tools/files.d.ts +235 -0
  28. package/dist/mcp/tools/files.js +103 -0
  29. package/dist/mcp/tools/flows.d.ts +323 -0
  30. package/dist/mcp/tools/flows.js +85 -0
  31. package/dist/mcp/tools/folders.d.ts +95 -0
  32. package/dist/mcp/tools/folders.js +96 -0
  33. package/dist/mcp/tools/index.d.ts +15 -0
  34. package/dist/mcp/tools/index.js +29 -0
  35. package/dist/mcp/tools/items.d.ts +87 -0
  36. package/dist/mcp/tools/items.js +141 -0
  37. package/dist/mcp/tools/operations.d.ts +171 -0
  38. package/dist/mcp/tools/operations.js +77 -0
  39. package/dist/mcp/tools/prompts/assets.md +8 -0
  40. package/dist/mcp/tools/prompts/collections.md +336 -0
  41. package/dist/mcp/tools/prompts/fields.md +521 -0
  42. package/dist/mcp/tools/prompts/files.md +180 -0
  43. package/dist/mcp/tools/prompts/flows.md +495 -0
  44. package/dist/mcp/tools/prompts/folders.md +34 -0
  45. package/dist/mcp/tools/prompts/index.d.ts +16 -0
  46. package/dist/mcp/tools/prompts/index.js +19 -0
  47. package/dist/mcp/tools/prompts/items.md +317 -0
  48. package/dist/mcp/tools/prompts/operations.md +721 -0
  49. package/dist/mcp/tools/prompts/relations.md +386 -0
  50. package/dist/mcp/tools/prompts/schema.md +130 -0
  51. package/dist/mcp/tools/prompts/system-prompt-description.md +1 -0
  52. package/dist/mcp/tools/prompts/system-prompt.md +44 -0
  53. package/dist/mcp/tools/prompts/trigger-flow.md +214 -0
  54. package/dist/mcp/tools/relations.d.ts +73 -0
  55. package/dist/mcp/tools/relations.js +93 -0
  56. package/dist/mcp/tools/schema.d.ts +54 -0
  57. package/dist/mcp/tools/schema.js +317 -0
  58. package/dist/mcp/tools/system.d.ts +3 -0
  59. package/dist/mcp/tools/system.js +22 -0
  60. package/dist/mcp/tools/trigger-flow.d.ts +8 -0
  61. package/dist/mcp/tools/trigger-flow.js +48 -0
  62. package/dist/mcp/transport.d.ts +13 -0
  63. package/dist/mcp/transport.js +18 -0
  64. package/dist/mcp/types.d.ts +56 -0
  65. package/dist/mcp/types.js +1 -0
  66. package/dist/services/authentication.js +36 -0
  67. package/dist/services/fields.js +4 -4
  68. package/dist/services/items.js +14 -4
  69. package/dist/services/payload.d.ts +7 -3
  70. package/dist/services/payload.js +26 -12
  71. package/dist/services/server.js +1 -0
  72. package/dist/services/tfa.d.ts +1 -1
  73. package/dist/services/tfa.js +20 -5
  74. package/dist/services/versions.d.ts +6 -4
  75. package/dist/services/versions.js +84 -25
  76. package/dist/types/auth.d.ts +2 -1
  77. package/dist/utils/versioning/deep-map-with-schema.d.ts +23 -0
  78. package/dist/utils/versioning/deep-map-with-schema.js +81 -0
  79. package/dist/utils/versioning/handle-version.d.ts +2 -2
  80. package/dist/utils/versioning/handle-version.js +47 -43
  81. package/dist/utils/versioning/split-recursive.d.ts +4 -0
  82. package/dist/utils/versioning/split-recursive.js +27 -0
  83. package/package.json +30 -29
@@ -1,7 +1,7 @@
1
1
  import { ForbiddenError } from '@directus/errors';
2
- import { deepMapResponse } from '../deep-map-response.js';
3
2
  import { transaction } from '../transaction.js';
4
- import { mergeVersionsRaw } from './merge-version-data.js';
3
+ import { deepMapWithSchema } from './deep-map-with-schema.js';
4
+ import { splitRecursive } from './split-recursive.js';
5
5
  export async function handleVersion(self, key, queryWithKey, opts) {
6
6
  const { VersionsService } = await import('../../services/versions.js');
7
7
  const { ItemsService } = await import('../../services/items.js');
@@ -15,19 +15,21 @@ export async function handleVersion(self, key, queryWithKey, opts) {
15
15
  accountability: self.accountability,
16
16
  knex: self.knex,
17
17
  });
18
- const versionData = await versionsService.getVersionSaves(queryWithKey.version, self.collection, key);
19
- if (!versionData || versionData.length === 0)
20
- return [originalData[0]];
21
- return [mergeVersionsRaw(originalData[0], versionData)];
18
+ const version = await versionsService.getVersionSave(queryWithKey.version, self.collection, key);
19
+ return Object.assign(originalData[0], version?.delta);
22
20
  }
23
- let results = [];
21
+ let result;
24
22
  const versionsService = new VersionsService({
25
23
  schema: self.schema,
26
24
  accountability: self.accountability,
27
25
  knex: self.knex,
28
26
  });
29
27
  const createdIDs = {};
30
- const versionData = await versionsService.getVersionSaves(queryWithKey.version, self.collection, key);
28
+ const version = await versionsService.getVersionSave(queryWithKey.version, self.collection, key, false);
29
+ if (!version) {
30
+ throw new ForbiddenError();
31
+ }
32
+ const { delta } = version;
31
33
  await transaction(self.knex, async (trx) => {
32
34
  const itemsServiceAdmin = new ItemsService(self.collection, {
33
35
  schema: self.schema,
@@ -36,57 +38,59 @@ export async function handleVersion(self, key, queryWithKey, opts) {
36
38
  },
37
39
  knex: trx,
38
40
  });
39
- await Promise.all((versionData ?? []).map((data) => {
40
- return itemsServiceAdmin.updateOne(key, data, {
41
+ if (delta) {
42
+ const { rawDelta, defaultOverwrites } = splitRecursive(delta);
43
+ await itemsServiceAdmin.updateOne(key, rawDelta, {
41
44
  emitEvents: false,
42
45
  autoPurgeCache: false,
43
46
  skipTracking: true,
47
+ overwriteDefaults: defaultOverwrites,
44
48
  onItemCreate: (collection, pk) => {
45
49
  if (collection in createdIDs === false)
46
50
  createdIDs[collection] = [];
47
51
  createdIDs[collection].push(pk);
48
52
  },
49
53
  });
50
- }));
54
+ }
51
55
  const itemsServiceUser = new ItemsService(self.collection, {
52
56
  schema: self.schema,
53
57
  accountability: self.accountability,
54
58
  knex: trx,
55
59
  });
56
- results = await itemsServiceUser.readByQuery(queryWithKey, opts);
60
+ result = (await itemsServiceUser.readByQuery(queryWithKey, opts))[0];
57
61
  await trx.rollback();
58
62
  });
59
- results = results.map((result) => {
60
- return deepMapResponse(result, ([key, value], context) => {
61
- if (context.relationType === 'm2o' || context.relationType === 'a2o') {
62
- const ids = createdIDs[context.relation.related_collection];
63
- const match = ids?.find((id) => String(id) === String(value));
64
- if (match) {
65
- return [key, null];
66
- }
67
- }
68
- else if (context.relationType === 'o2m' && Array.isArray(value)) {
69
- const ids = createdIDs[context.relation.collection];
70
- return [
71
- key,
72
- value.map((val) => {
73
- const match = ids?.find((id) => String(id) === String(val));
74
- if (match) {
75
- return null;
76
- }
77
- return val;
78
- }),
79
- ];
63
+ if (!result) {
64
+ throw new ForbiddenError();
65
+ }
66
+ return deepMapWithSchema(result, ([key, value], context) => {
67
+ if (context.relationType === 'm2o' || context.relationType === 'a2o') {
68
+ const ids = createdIDs[context.relation.related_collection];
69
+ const match = ids?.find((id) => String(id) === String(value));
70
+ if (match) {
71
+ return [key, null];
80
72
  }
81
- if (context.field.field === context.collection.primary) {
82
- const ids = createdIDs[context.collection.collection];
83
- const match = ids?.find((id) => String(id) === String(value));
84
- if (match) {
85
- return [key, null];
86
- }
73
+ }
74
+ else if (context.relationType === 'o2m' && Array.isArray(value)) {
75
+ const ids = createdIDs[context.relation.collection];
76
+ return [
77
+ key,
78
+ value.map((val) => {
79
+ const match = ids?.find((id) => String(id) === String(val));
80
+ if (match) {
81
+ return null;
82
+ }
83
+ return val;
84
+ }),
85
+ ];
86
+ }
87
+ if (context.field.field === context.collection.primary) {
88
+ const ids = createdIDs[context.collection.collection];
89
+ const match = ids?.find((id) => String(id) === String(value));
90
+ if (match) {
91
+ return [key, null];
87
92
  }
88
- return [key, value];
89
- }, { collection: self.collection, schema: self.schema });
90
- });
91
- return results;
93
+ }
94
+ return [key, value];
95
+ }, { collection: self.collection, schema: self.schema });
92
96
  }
@@ -0,0 +1,4 @@
1
+ export declare function splitRecursive(object: unknown): {
2
+ rawDelta: Record<string, any>;
3
+ defaultOverwrites: Record<string, any> | undefined;
4
+ };
@@ -0,0 +1,27 @@
1
+ import { isPlainObject } from 'lodash-es';
2
+ export function splitRecursive(object) {
3
+ if (isPlainObject(object) && typeof object === 'object' && object !== null) {
4
+ const { _user, _date, ...rest } = object;
5
+ const defaultOverwrites = { _user, _date };
6
+ for (const key in rest) {
7
+ const { rawDelta, defaultOverwrites: innerDefaultOverwrites } = splitRecursive(rest[key]);
8
+ rest[key] = rawDelta;
9
+ if (innerDefaultOverwrites)
10
+ defaultOverwrites[key] = innerDefaultOverwrites;
11
+ }
12
+ return { rawDelta: rest, defaultOverwrites };
13
+ }
14
+ else if (Array.isArray(object)) {
15
+ const rest = [];
16
+ const defaultOverwrites = [];
17
+ for (const key in object) {
18
+ const { rawDelta, defaultOverwrites: innerDefaultOverwrites } = splitRecursive(object[key]);
19
+ rest[key] = rawDelta;
20
+ if (innerDefaultOverwrites)
21
+ defaultOverwrites[key] = innerDefaultOverwrites;
22
+ }
23
+ object.map((value) => splitRecursive(value));
24
+ return { rawDelta: rest, defaultOverwrites };
25
+ }
26
+ return { rawDelta: object, defaultOverwrites: undefined };
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/api",
3
- "version": "30.0.0",
3
+ "version": "31.0.0",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "keywords": [
6
6
  "directus",
@@ -61,6 +61,7 @@
61
61
  "@authenio/samlify-node-xmllint": "2.0.0",
62
62
  "@aws-sdk/client-sesv2": "3.864.0",
63
63
  "@godaddy/terminus": "4.12.1",
64
+ "@modelcontextprotocol/sdk": "1.17.1",
64
65
  "@rollup/plugin-alias": "5.1.1",
65
66
  "@rollup/plugin-node-resolve": "16.0.1",
66
67
  "@rollup/plugin-virtual": "3.0.2",
@@ -102,7 +103,7 @@
102
103
  "ioredis": "5.7.0",
103
104
  "ip-matching": "2.1.2",
104
105
  "isolated-vm": "5.0.3",
105
- "joi": "17.13.3",
106
+ "joi": "18.0.1",
106
107
  "js-yaml": "4.1.0",
107
108
  "js2xmlparser": "5.0.0",
108
109
  "json2csv": "5.0.7",
@@ -139,8 +140,8 @@
139
140
  "proxy-addr": "2.0.7",
140
141
  "qs": "6.14.0",
141
142
  "rate-limiter-flexible": "7.2.0",
142
- "rollup": "4.46.2",
143
143
  "rolldown": "1.0.0-beta.31",
144
+ "rollup": "4.46.2",
144
145
  "samlify": "2.10.1",
145
146
  "sanitize-html": "2.17.0",
146
147
  "sharp": "0.34.3",
@@ -153,30 +154,30 @@
153
154
  "ws": "8.18.3",
154
155
  "zod": "4.0.14",
155
156
  "zod-validation-error": "4.0.1",
156
- "@directus/app": "13.14.0",
157
- "@directus/constants": "13.0.2",
158
- "@directus/env": "5.1.2",
159
- "@directus/errors": "2.0.3",
160
- "@directus/extensions-sdk": "16.0.1",
161
- "@directus/extensions-registry": "3.0.10",
162
- "@directus/format-title": "12.0.1",
163
- "@directus/extensions": "3.0.10",
164
- "@directus/memory": "3.0.8",
165
- "@directus/pressure": "3.0.8",
157
+ "@directus/env": "5.2.0",
158
+ "@directus/app": "14.0.0",
159
+ "@directus/errors": "2.0.4",
160
+ "@directus/extensions": "3.0.11",
161
+ "@directus/constants": "13.0.3",
162
+ "@directus/extensions-registry": "3.0.11",
163
+ "@directus/extensions-sdk": "16.0.2",
164
+ "@directus/format-title": "12.1.0",
165
+ "@directus/memory": "3.0.9",
166
+ "@directus/pressure": "3.0.9",
167
+ "@directus/schema": "13.0.3",
168
+ "@directus/schema-builder": "0.0.6",
166
169
  "@directus/specs": "11.1.1",
167
- "@directus/schema-builder": "0.0.5",
168
- "@directus/storage": "12.0.1",
169
- "@directus/storage-driver-azure": "12.0.8",
170
- "@directus/schema": "13.0.2",
171
- "@directus/storage-driver-cloudinary": "12.0.8",
172
- "@directus/storage-driver-gcs": "12.0.8",
173
- "@directus/storage-driver-s3": "12.0.8",
174
- "@directus/storage-driver-supabase": "3.0.8",
175
- "@directus/storage-driver-local": "12.0.1",
176
- "@directus/utils": "13.0.9",
177
- "@directus/system-data": "3.2.1",
178
- "@directus/validation": "2.0.8",
179
- "directus": "11.11.0"
170
+ "@directus/storage": "12.0.2",
171
+ "@directus/storage-driver-azure": "12.0.9",
172
+ "@directus/storage-driver-cloudinary": "12.0.9",
173
+ "@directus/storage-driver-gcs": "12.0.9",
174
+ "@directus/storage-driver-local": "12.0.2",
175
+ "@directus/storage-driver-s3": "12.0.9",
176
+ "@directus/storage-driver-supabase": "3.0.9",
177
+ "@directus/system-data": "3.3.0",
178
+ "@directus/utils": "13.0.10",
179
+ "directus": "11.12.0",
180
+ "@directus/validation": "2.0.9"
180
181
  },
181
182
  "devDependencies": {
182
183
  "@directus/tsconfig": "3.0.0",
@@ -220,8 +221,8 @@
220
221
  "knex-mock-client": "3.0.2",
221
222
  "typescript": "5.8.3",
222
223
  "vitest": "3.2.4",
223
- "@directus/schema-builder": "0.0.5",
224
- "@directus/types": "13.2.2"
224
+ "@directus/schema-builder": "0.0.6",
225
+ "@directus/types": "13.2.3"
225
226
  },
226
227
  "optionalDependencies": {
227
228
  "@keyv/redis": "3.0.1",
@@ -236,7 +237,7 @@
236
237
  "node": ">=22"
237
238
  },
238
239
  "scripts": {
239
- "build": "rimraf ./dist && tsc --project tsconfig.prod.json && copyfiles \"src/**/*.{yaml,liquid}\" -u 1 dist",
240
+ "build": "rimraf ./dist && tsc --project tsconfig.prod.json && copyfiles \"src/**/*.{yaml,liquid,md}\" -u 1 dist",
240
241
  "cli": "NODE_ENV=development SERVE_APP=false tsx src/cli/run.ts",
241
242
  "dev": "NODE_ENV=development SERVE_APP=true tsx watch --ignore extensions --clear-screen=false src/start.ts",
242
243
  "test": "vitest run",