@directus/api 32.1.1 → 32.2.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 (165) hide show
  1. package/dist/ai/chat/constants/system-prompt.d.ts +1 -0
  2. package/dist/ai/chat/constants/system-prompt.js +51 -0
  3. package/dist/ai/chat/controllers/chat.post.d.ts +2 -0
  4. package/dist/ai/chat/controllers/chat.post.js +47 -0
  5. package/dist/ai/chat/lib/create-ui-stream.d.ts +15 -0
  6. package/dist/ai/chat/lib/create-ui-stream.js +42 -0
  7. package/dist/ai/chat/middleware/load-settings.d.ts +2 -0
  8. package/dist/ai/chat/middleware/load-settings.js +18 -0
  9. package/dist/ai/chat/models/chat-request.d.ts +34 -0
  10. package/dist/ai/chat/models/chat-request.js +26 -0
  11. package/dist/ai/chat/models/providers.d.ts +9 -0
  12. package/dist/ai/chat/models/providers.js +9 -0
  13. package/dist/ai/chat/router.d.ts +1 -0
  14. package/dist/ai/chat/router.js +5 -0
  15. package/dist/ai/chat/utils/chat-request-tool-to-ai-sdk-tool.d.ts +9 -0
  16. package/dist/ai/chat/utils/chat-request-tool-to-ai-sdk-tool.js +38 -0
  17. package/dist/ai/chat/utils/fix-error-tool-calls.d.ts +12 -0
  18. package/dist/ai/chat/utils/fix-error-tool-calls.js +30 -0
  19. package/dist/ai/chat/utils/parse-json-schema-7.d.ts +13 -0
  20. package/dist/ai/chat/utils/parse-json-schema-7.js +75 -0
  21. package/dist/{mcp → ai/mcp}/server.d.ts +13 -16
  22. package/dist/{mcp → ai/mcp}/server.js +4 -13
  23. package/dist/ai/mcp/types.d.ts +15 -0
  24. package/dist/{mcp/tools/assets.js → ai/tools/assets/index.js} +8 -5
  25. package/dist/{mcp/tools/collections.js → ai/tools/collections/index.js} +7 -4
  26. package/dist/{mcp/tools/fields.js → ai/tools/fields/index.js} +12 -9
  27. package/dist/{mcp/tools/files.js → ai/tools/files/index.js} +11 -5
  28. package/dist/{mcp/tools/flows.js → ai/tools/flows/index.js} +11 -5
  29. package/dist/{mcp/tools/folders.js → ai/tools/folders/index.js} +12 -5
  30. package/dist/ai/tools/index.d.ts +15 -0
  31. package/dist/ai/tools/index.js +29 -0
  32. package/dist/{mcp/tools/items.js → ai/tools/items/index.js} +13 -6
  33. package/dist/{mcp/tools/prompts/items.md → ai/tools/items/prompt.md} +19 -15
  34. package/dist/{mcp/tools/operations.d.ts → ai/tools/operations/index.d.ts} +46 -0
  35. package/dist/{mcp/tools/operations.js → ai/tools/operations/index.js} +12 -5
  36. package/dist/{mcp/tools/relations.js → ai/tools/relations/index.js} +7 -4
  37. package/dist/{mcp/tools/schema.d.ts → ai/tools/schema/index.d.ts} +1 -1
  38. package/dist/{mcp/tools/schema.js → ai/tools/schema/index.js} +9 -6
  39. package/dist/{mcp/tools/system.js → ai/tools/system/index.js} +7 -4
  40. package/dist/{mcp/tools/trigger-flow.js → ai/tools/trigger-flow/index.js} +8 -5
  41. package/dist/{mcp → ai/tools}/types.d.ts +1 -17
  42. package/dist/ai/tools/utils.d.ts +9 -0
  43. package/dist/ai/tools/utils.js +17 -0
  44. package/dist/app.js +5 -0
  45. package/dist/auth/drivers/saml.js +5 -2
  46. package/dist/controllers/assets.js +39 -2
  47. package/dist/controllers/mcp.js +1 -1
  48. package/dist/database/migrations/20240806A-permissions-policies.js +2 -2
  49. package/dist/database/migrations/20251103A-add-ai-settings.d.ts +3 -0
  50. package/dist/database/migrations/20251103A-add-ai-settings.js +14 -0
  51. package/dist/database/run-ast/run-ast.js +1 -1
  52. package/dist/extensions/lib/installation/manager.js +5 -9
  53. package/dist/extensions/lib/sync/status.d.ts +11 -0
  54. package/dist/extensions/lib/sync/status.js +34 -0
  55. package/dist/extensions/lib/sync/sync.d.ts +6 -0
  56. package/dist/extensions/lib/sync/sync.js +90 -0
  57. package/dist/extensions/lib/sync/tracker.d.ts +18 -0
  58. package/dist/extensions/lib/sync/tracker.js +71 -0
  59. package/dist/extensions/lib/sync/utils.d.ts +24 -0
  60. package/dist/extensions/lib/sync/utils.js +62 -0
  61. package/dist/extensions/manager.d.ts +8 -4
  62. package/dist/extensions/manager.js +30 -13
  63. package/dist/middleware/respond.js +2 -2
  64. package/dist/permissions/lib/fetch-policies.d.ts +1 -1
  65. package/dist/permissions/lib/fetch-roles-tree.d.ts +6 -3
  66. package/dist/permissions/lib/fetch-roles-tree.js +5 -27
  67. package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +9 -7
  68. package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +17 -9
  69. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +1 -1
  70. package/dist/permissions/utils/fetch-raw-permissions.d.ts +1 -1
  71. package/dist/permissions/utils/fetch-share-info.d.ts +1 -1
  72. package/dist/permissions/utils/fetch-share-info.js +1 -1
  73. package/dist/permissions/utils/filter-policies-by-ip.js +1 -1
  74. package/dist/permissions/utils/get-permissions-for-share.js +8 -8
  75. package/dist/permissions/utils/with-cache.d.ts +8 -6
  76. package/dist/permissions/utils/with-cache.js +12 -10
  77. package/dist/request/is-denied-ip.js +2 -2
  78. package/dist/services/assets/name-deduper.d.ts +7 -0
  79. package/dist/services/assets/name-deduper.js +23 -0
  80. package/dist/services/assets.d.ts +15 -2
  81. package/dist/services/assets.js +98 -5
  82. package/dist/services/authentication.js +4 -4
  83. package/dist/services/comments.js +2 -2
  84. package/dist/services/extensions.js +4 -0
  85. package/dist/services/folders.d.ts +27 -2
  86. package/dist/services/folders.js +75 -0
  87. package/dist/services/import-export.d.ts +1 -1
  88. package/dist/services/import-export.js +4 -5
  89. package/dist/services/notifications.js +2 -2
  90. package/dist/services/payload.js +20 -0
  91. package/dist/services/roles.js +2 -2
  92. package/dist/services/tus/server.js +3 -3
  93. package/dist/telemetry/utils/get-settings.d.ts +15 -0
  94. package/dist/telemetry/utils/get-settings.js +13 -1
  95. package/dist/test-utils/README.md +95 -24
  96. package/dist/test-utils/cache.d.ts +2 -2
  97. package/dist/test-utils/cache.js +2 -2
  98. package/dist/test-utils/{fields-service.d.ts → services/fields-service.d.ts} +1 -1
  99. package/dist/test-utils/{fields-service.js → services/fields-service.js} +3 -2
  100. package/dist/test-utils/services/files-service.d.ts +28 -0
  101. package/dist/test-utils/services/files-service.js +34 -0
  102. package/dist/test-utils/services/folders-service.d.ts +28 -0
  103. package/dist/test-utils/services/folders-service.js +33 -0
  104. package/dist/utils/encrypt.d.ts +2 -0
  105. package/dist/utils/encrypt.js +64 -0
  106. package/dist/utils/get-accountability-for-role.js +2 -2
  107. package/dist/utils/get-accountability-for-token.js +4 -4
  108. package/dist/utils/get-cache-key.js +2 -2
  109. package/dist/utils/require-text.d.ts +1 -0
  110. package/dist/utils/require-text.js +4 -0
  111. package/dist/utils/require-yaml.js +2 -2
  112. package/package.json +32 -26
  113. package/dist/extensions/lib/sync-extensions.d.ts +0 -3
  114. package/dist/extensions/lib/sync-extensions.js +0 -70
  115. package/dist/extensions/lib/sync-status.d.ts +0 -10
  116. package/dist/extensions/lib/sync-status.js +0 -27
  117. package/dist/mcp/tools/index.d.ts +0 -15
  118. package/dist/mcp/tools/index.js +0 -29
  119. package/dist/mcp/tools/prompts/index.d.ts +0 -16
  120. package/dist/mcp/tools/prompts/index.js +0 -19
  121. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +0 -5
  122. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +0 -7
  123. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +0 -5
  124. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +0 -10
  125. package/dist/permissions/modules/fetch-global-access/types.d.ts +0 -4
  126. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +0 -4
  127. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +0 -27
  128. package/dist/utils/get-date-formatted.d.ts +0 -1
  129. package/dist/utils/get-date-formatted.js +0 -10
  130. package/dist/utils/ip-in-networks.d.ts +0 -6
  131. package/dist/utils/ip-in-networks.js +0 -13
  132. /package/dist/{mcp → ai/mcp}/index.d.ts +0 -0
  133. /package/dist/{mcp → ai/mcp}/index.js +0 -0
  134. /package/dist/{mcp → ai/mcp}/transport.d.ts +0 -0
  135. /package/dist/{mcp → ai/mcp}/transport.js +0 -0
  136. /package/dist/{mcp → ai/mcp}/types.js +0 -0
  137. /package/dist/{mcp/tools/assets.d.ts → ai/tools/assets/index.d.ts} +0 -0
  138. /package/dist/{mcp/tools/prompts/assets.md → ai/tools/assets/prompt.md} +0 -0
  139. /package/dist/{mcp/tools/collections.d.ts → ai/tools/collections/index.d.ts} +0 -0
  140. /package/dist/{mcp/tools/prompts/collections.md → ai/tools/collections/prompt.md} +0 -0
  141. /package/dist/{mcp/define.d.ts → ai/tools/define-tool.d.ts} +0 -0
  142. /package/dist/{mcp/define.js → ai/tools/define-tool.js} +0 -0
  143. /package/dist/{mcp/tools/fields.d.ts → ai/tools/fields/index.d.ts} +0 -0
  144. /package/dist/{mcp/tools/prompts/fields.md → ai/tools/fields/prompt.md} +0 -0
  145. /package/dist/{mcp/tools/files.d.ts → ai/tools/files/index.d.ts} +0 -0
  146. /package/dist/{mcp/tools/prompts/files.md → ai/tools/files/prompt.md} +0 -0
  147. /package/dist/{mcp/tools/flows.d.ts → ai/tools/flows/index.d.ts} +0 -0
  148. /package/dist/{mcp/tools/prompts/flows.md → ai/tools/flows/prompt.md} +0 -0
  149. /package/dist/{mcp/tools/folders.d.ts → ai/tools/folders/index.d.ts} +0 -0
  150. /package/dist/{mcp/tools/prompts/folders.md → ai/tools/folders/prompt.md} +0 -0
  151. /package/dist/{mcp/tools/items.d.ts → ai/tools/items/index.d.ts} +0 -0
  152. /package/dist/{mcp/tools/prompts/operations.md → ai/tools/operations/prompt.md} +0 -0
  153. /package/dist/{mcp/tools/relations.d.ts → ai/tools/relations/index.d.ts} +0 -0
  154. /package/dist/{mcp/tools/prompts/relations.md → ai/tools/relations/prompt.md} +0 -0
  155. /package/dist/{mcp/tools/prompts/schema.md → ai/tools/schema/prompt.md} +0 -0
  156. /package/dist/{mcp → ai/tools}/schema.d.ts +0 -0
  157. /package/dist/{mcp → ai/tools}/schema.js +0 -0
  158. /package/dist/{mcp/tools/system.d.ts → ai/tools/system/index.d.ts} +0 -0
  159. /package/dist/{mcp/tools/prompts/system-prompt-description.md → ai/tools/system/prompt-description.md} +0 -0
  160. /package/dist/{mcp/tools/prompts/system-prompt.md → ai/tools/system/prompt.md} +0 -0
  161. /package/dist/{mcp/tools/trigger-flow.d.ts → ai/tools/trigger-flow/index.d.ts} +0 -0
  162. /package/dist/{mcp/tools/prompts/trigger-flow.md → ai/tools/trigger-flow/prompt.md} +0 -0
  163. /package/dist/{permissions/modules/fetch-global-access → ai/tools}/types.js +0 -0
  164. /package/dist/test-utils/{items-service.d.ts → services/items-service.d.ts} +0 -0
  165. /package/dist/test-utils/{items-service.js → services/items-service.js} +0 -0
@@ -3,6 +3,7 @@
3
3
  * Provides simplified mocks for src/services/fields module used in service testing
4
4
  */
5
5
  import { vi } from 'vitest';
6
+ import { mockItemsService } from './items-service.js';
6
7
  /**
7
8
  * Creates a standard FieldsService mock for service tests
8
9
  * This matches the pattern used in CollectionsService tests
@@ -13,7 +14,7 @@ import { vi } from 'vitest';
13
14
  * ```typescript
14
15
  * // Standard usage
15
16
  * vi.mock('./fields.js', async () => {
16
- * const { mockFieldsService } = await import('../__mocks__/fields-service.js');
17
+ * const { mockFieldsService } = await import('../test-utils/services/fields-service.js');
17
18
  * return mockFieldsService();
18
19
  * });
19
20
  *
@@ -25,7 +26,7 @@ import { vi } from 'vitest';
25
26
  * ```
26
27
  */
27
28
  export function mockFieldsService() {
28
- const FieldsService = vi.fn();
29
+ const { ItemsService: FieldsService } = mockItemsService();
29
30
  // Mock common methods used by other services (like CollectionsService)
30
31
  FieldsService.prototype.addColumnToTable = vi.fn().mockImplementation(() => { });
31
32
  FieldsService.prototype.addColumnIndex = vi.fn().mockResolvedValue(undefined);
@@ -0,0 +1,28 @@
1
+ /**
2
+ * FilesService mocking utilities for service tests
3
+ * Provides simplified mocks for src/services/files module used in service testing
4
+ */
5
+ /**
6
+ * Creates a standard FilesService mock for service tests
7
+ * This matches the pattern used in CollectionsService tests
8
+ *
9
+ * @returns Mock module object for vi.mock()
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Standard usage
14
+ * vi.mock('./files.js', async () => {
15
+ * const { mockFilesService } = await import('../test-utils/services/files-service.js');
16
+ * return mockFilesService();
17
+ * });
18
+ *
19
+ * // To dynamically change FilesService behavior during tests:
20
+ * import { FilesService } from './files.js';
21
+ * vi.spyOn(FilesService.prototype, 'addColumnToTable').mockImplementation((table, collection, field) => {
22
+ * // custom implementation
23
+ * });
24
+ * ```
25
+ */
26
+ export declare function mockFilesService(): {
27
+ FilesService: import("vitest").Mock<(...args: any[]) => any>;
28
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * FilesService mocking utilities for service tests
3
+ * Provides simplified mocks for src/services/files module used in service testing
4
+ */
5
+ import { vi } from 'vitest';
6
+ import { mockItemsService } from './items-service.js';
7
+ /**
8
+ * Creates a standard FilesService mock for service tests
9
+ * This matches the pattern used in CollectionsService tests
10
+ *
11
+ * @returns Mock module object for vi.mock()
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // Standard usage
16
+ * vi.mock('./files.js', async () => {
17
+ * const { mockFilesService } = await import('../test-utils/services/files-service.js');
18
+ * return mockFilesService();
19
+ * });
20
+ *
21
+ * // To dynamically change FilesService behavior during tests:
22
+ * import { FilesService } from './files.js';
23
+ * vi.spyOn(FilesService.prototype, 'addColumnToTable').mockImplementation((table, collection, field) => {
24
+ * // custom implementation
25
+ * });
26
+ * ```
27
+ */
28
+ export function mockFilesService() {
29
+ const { ItemsService: FilesService } = mockItemsService();
30
+ // non-crud methods
31
+ FilesService.prototype.uploadOne = vi.fn().mockResolvedValue(1);
32
+ FilesService.prototype.importOne = vi.fn().mockResolvedValue(1);
33
+ return { FilesService };
34
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * FoldersService mocking utilities for service tests
3
+ * Provides simplified mocks for src/services/folders module used in service testing
4
+ */
5
+ /**
6
+ * Creates a standard FoldersService mock for service tests
7
+ * This matches the pattern used in CollectionsService tests
8
+ *
9
+ * @returns Mock module object for vi.mock()
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Standard usage
14
+ * vi.mock('./folders.js', async () => {
15
+ * const { mockFoldersService } = await import('../test-utils/services/folders-service.js');
16
+ * return mockFoldersService();
17
+ * });
18
+ *
19
+ * // To dynamically change FoldersService behavior during tests:
20
+ * import { FoldersService } from './folders.js';
21
+ * vi.spyOn(FoldersService.prototype, 'addColumnToTable').mockImplementation((table, collection, field) => {
22
+ * // custom implementation
23
+ * });
24
+ * ```
25
+ */
26
+ export declare function mockFoldersService(): {
27
+ FoldersService: import("vitest").Mock<(...args: any[]) => any>;
28
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * FoldersService mocking utilities for service tests
3
+ * Provides simplified mocks for src/services/folders module used in service testing
4
+ */
5
+ import { vi } from 'vitest';
6
+ import { mockItemsService } from './items-service.js';
7
+ /**
8
+ * Creates a standard FoldersService mock for service tests
9
+ * This matches the pattern used in CollectionsService tests
10
+ *
11
+ * @returns Mock module object for vi.mock()
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // Standard usage
16
+ * vi.mock('./folders.js', async () => {
17
+ * const { mockFoldersService } = await import('../test-utils/services/folders-service.js');
18
+ * return mockFoldersService();
19
+ * });
20
+ *
21
+ * // To dynamically change FoldersService behavior during tests:
22
+ * import { FoldersService } from './folders.js';
23
+ * vi.spyOn(FoldersService.prototype, 'addColumnToTable').mockImplementation((table, collection, field) => {
24
+ * // custom implementation
25
+ * });
26
+ * ```
27
+ */
28
+ export function mockFoldersService() {
29
+ const { ItemsService: FoldersService } = mockItemsService();
30
+ // non-crud methods
31
+ FoldersService.prototype.buildTree = vi.fn().mockResolvedValue(new Map([['1', 'root']]));
32
+ return { FoldersService };
33
+ }
@@ -0,0 +1,2 @@
1
+ export declare const encrypt: (plainText: string, password: string) => Promise<string>;
2
+ export declare const decrypt: (encryptedText: string, password: string) => Promise<string>;
@@ -0,0 +1,64 @@
1
+ import crypto from 'node:crypto';
2
+ import { promisify } from 'node:util';
3
+ const VERSION = '1';
4
+ const KDF = 'scrypt';
5
+ const SCRYPT_DEFAULTS = { N: 2 ** 14, r: 8, p: 1 }; // ~16MB mem, good server default
6
+ const scryptAsync = promisify(crypto.scrypt);
7
+ const deriveKey = async (password, salt, opts = SCRYPT_DEFAULTS) => {
8
+ return await scryptAsync(password, salt, 32, opts);
9
+ };
10
+ export const encrypt = async (plainText, password) => {
11
+ const salt = crypto.randomBytes(16);
12
+ const keyBuf = await deriveKey(password, salt, SCRYPT_DEFAULTS);
13
+ // Generate a 12-byte IV for GCM and keep base64 string for storage
14
+ const ivBuf = crypto.randomBytes(12);
15
+ const iv = ivBuf.toString('base64');
16
+ const cipher = crypto.createCipheriv('aes-256-gcm', keyBuf, ivBuf);
17
+ let cipherText = cipher.update(plainText, 'utf8', 'base64');
18
+ cipherText += cipher.final('base64');
19
+ const tag = cipher.getAuthTag().toString('base64');
20
+ // 1||scrypt||N||r||p||salt||iv||cipherText||tag
21
+ return [
22
+ VERSION,
23
+ KDF,
24
+ SCRYPT_DEFAULTS.N,
25
+ SCRYPT_DEFAULTS.r,
26
+ SCRYPT_DEFAULTS.p,
27
+ salt.toString('base64'),
28
+ iv,
29
+ cipherText,
30
+ tag,
31
+ ].join('||');
32
+ };
33
+ export const decrypt = async (encryptedText, password) => {
34
+ const parts = encryptedText.split('||');
35
+ if (parts.length < 9)
36
+ throw new Error('Invalid encrypted payload');
37
+ const [version, kdf, nStr, rStr, pStr, saltB64, ivB64, cipherText, tagB64] = parts;
38
+ if (version !== VERSION)
39
+ throw new Error(`Unsupported version: ${version}`);
40
+ if (kdf !== KDF)
41
+ throw new Error(`Unsupported kdf: ${kdf}`);
42
+ if (!saltB64)
43
+ throw new Error('No salt in encrypted string');
44
+ if (!ivB64)
45
+ throw new Error('No iv in encrypted string');
46
+ if (cipherText === undefined)
47
+ throw new Error('No cipherText in encrypted string');
48
+ if (!tagB64)
49
+ throw new Error('No tag in encrypted string');
50
+ const opts = {
51
+ N: Number(nStr) || SCRYPT_DEFAULTS.N,
52
+ r: Number(rStr) || SCRYPT_DEFAULTS.r,
53
+ p: Number(pStr) || SCRYPT_DEFAULTS.p,
54
+ };
55
+ const salt = Buffer.from(saltB64, 'base64');
56
+ const keyBuf = await deriveKey(password, salt, opts);
57
+ const iv = Buffer.from(ivB64, 'base64');
58
+ const tag = Buffer.from(tagB64, 'base64');
59
+ const decipher = crypto.createDecipheriv('aes-256-gcm', keyBuf, iv);
60
+ decipher.setAuthTag(tag);
61
+ let plaintext = decipher.update(cipherText, 'base64', 'utf8');
62
+ plaintext += decipher.final('utf8');
63
+ return plaintext;
64
+ };
@@ -13,13 +13,13 @@ export async function getAccountabilityForRole(role, context) {
13
13
  });
14
14
  }
15
15
  else {
16
- const roles = await fetchRolesTree(role, context.database);
16
+ const roles = await fetchRolesTree(role, { knex: context.database });
17
17
  // The roles tree should always include the passed role. If it doesn't, it's because it
18
18
  // couldn't be read from the database and therefore doesn't exist
19
19
  if (roles.length === 0) {
20
20
  throw new Error(`Configured role "${role}" isn't a valid role ID or doesn't exist.`);
21
21
  }
22
- const globalAccess = await fetchGlobalAccess({ user: null, roles, ip: context.accountability?.ip ?? null }, context.database);
22
+ const globalAccess = await fetchGlobalAccess({ user: null, roles, ip: context.accountability?.ip ?? null }, { knex: context.database });
23
23
  generatedAccountability = createDefaultAccountability({
24
24
  role,
25
25
  roles,
@@ -25,8 +25,8 @@ export async function getAccountabilityForToken(token, accountability) {
25
25
  if (payload.id)
26
26
  accountability.user = payload.id;
27
27
  accountability.role = payload.role;
28
- accountability.roles = await fetchRolesTree(payload.role, database);
29
- const { admin, app } = await fetchGlobalAccess(accountability, database);
28
+ accountability.roles = await fetchRolesTree(payload.role, { knex: database });
29
+ const { admin, app } = await fetchGlobalAccess(accountability, { knex: database });
30
30
  accountability.admin = admin;
31
31
  accountability.app = app;
32
32
  }
@@ -44,8 +44,8 @@ export async function getAccountabilityForToken(token, accountability) {
44
44
  }
45
45
  accountability.user = user.id;
46
46
  accountability.role = user.role;
47
- accountability.roles = await fetchRolesTree(user.role, database);
48
- const { admin, app } = await fetchGlobalAccess(accountability, database);
47
+ accountability.roles = await fetchRolesTree(user.role, { knex: database });
48
+ const { admin, app } = await fetchGlobalAccess(accountability, { knex: database });
49
49
  accountability.admin = admin;
50
50
  accountability.app = app;
51
51
  }
@@ -1,10 +1,10 @@
1
+ import { ipInNetworks } from '@directus/utils/node';
2
+ import { version } from 'directus/version';
1
3
  import hash from 'object-hash';
2
4
  import url from 'url';
3
5
  import getDatabase from '../database/index.js';
4
6
  import { fetchPoliciesIpAccess } from '../permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js';
5
7
  import { getGraphqlQueryAndVariables } from './get-graphql-query-and-variables.js';
6
- import { version } from 'directus/version';
7
- import { ipInNetworks } from './ip-in-networks.js';
8
8
  export async function getCacheKey(req) {
9
9
  const path = url.parse(req.originalUrl).pathname;
10
10
  const isGraphQl = path?.startsWith('/graphql');
@@ -0,0 +1 @@
1
+ export declare function requireText(filepath: string): string;
@@ -0,0 +1,4 @@
1
+ import fse from 'fs-extra';
2
+ export function requireText(filepath) {
3
+ return fse.readFileSync(filepath, 'utf8');
4
+ }
@@ -1,6 +1,6 @@
1
- import fse from 'fs-extra';
2
1
  import yaml from 'js-yaml';
2
+ import { requireText } from './require-text.js';
3
3
  export function requireYAML(filepath) {
4
- const yamlRaw = fse.readFileSync(filepath, 'utf8');
4
+ const yamlRaw = requireText(filepath);
5
5
  return yaml.load(yamlRaw);
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/api",
3
- "version": "32.1.1",
3
+ "version": "32.2.0",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "keywords": [
6
6
  "directus",
@@ -58,15 +58,19 @@
58
58
  "dist"
59
59
  ],
60
60
  "dependencies": {
61
+ "@ai-sdk/anthropic": "3.0.0-beta.53",
62
+ "@ai-sdk/openai": "3.0.0-beta.57",
61
63
  "@authenio/samlify-node-xmllint": "2.0.0",
62
64
  "@aws-sdk/client-sesv2": "3.928.0",
63
65
  "@godaddy/terminus": "4.12.1",
64
- "@modelcontextprotocol/sdk": "1.21.1",
66
+ "@modelcontextprotocol/sdk": "1.24.0",
65
67
  "@rollup/plugin-alias": "5.1.1",
66
68
  "@rollup/plugin-node-resolve": "16.0.3",
67
69
  "@rollup/plugin-virtual": "3.0.2",
68
70
  "@tus/server": "2.3.0",
69
71
  "@tus/utils": "0.6.0",
72
+ "archiver": "7.0.1",
73
+ "ai": "6.0.0-beta.98",
70
74
  "argon2": "0.44.0",
71
75
  "async": "3.2.6",
72
76
  "async-mutex": "0.5.0",
@@ -81,6 +85,7 @@
81
85
  "cookie": "1.0.2",
82
86
  "cookie-parser": "1.4.7",
83
87
  "cors": "2.8.5",
88
+ "cron": "4.3.4",
84
89
  "date-fns": "4.1.0",
85
90
  "deep-diff": "1.0.2",
86
91
  "destroy": "1.2.0",
@@ -106,7 +111,7 @@
106
111
  "js-yaml": "4.1.1",
107
112
  "js2xmlparser": "5.0.0",
108
113
  "json2csv": "5.0.7",
109
- "jsonwebtoken": "9.0.2",
114
+ "jsonwebtoken": "9.0.3",
110
115
  "keyv": "5.5.3",
111
116
  "knex": "3.1.0",
112
117
  "ldapjs": "2.3.3",
@@ -120,7 +125,6 @@
120
125
  "ms": "2.1.3",
121
126
  "nanoid": "5.1.6",
122
127
  "node-machine-id": "1.1.12",
123
- "cron": "4.3.4",
124
128
  "nodemailer": "7.0.10",
125
129
  "object-hash": "3.0.0",
126
130
  "openapi3-ts": "4.5.0",
@@ -141,7 +145,8 @@
141
145
  "rate-limiter-flexible": "7.2.0",
142
146
  "rolldown": "1.0.0-beta.31",
143
147
  "rollup": "4.52.5",
144
- "samlify": "2.10.1",
148
+ "samlify": "2.10.2",
149
+ "sanitize-filename": "1.6.3",
145
150
  "sanitize-html": "2.17.0",
146
151
  "sharp": "0.34.5",
147
152
  "snappy": "7.3.3",
@@ -153,34 +158,35 @@
153
158
  "ws": "8.18.3",
154
159
  "zod": "4.1.12",
155
160
  "zod-validation-error": "4.0.2",
156
- "@directus/app": "14.3.0",
157
- "@directus/env": "5.3.2",
158
- "@directus/extensions-registry": "3.0.14",
159
- "@directus/extensions": "3.0.14",
160
- "@directus/errors": "2.0.5",
161
+ "@directus/app": "14.4.0",
162
+ "@directus/extensions": "3.0.15",
163
+ "@directus/errors": "2.1.0",
164
+ "@directus/extensions-sdk": "17.0.4",
161
165
  "@directus/constants": "14.0.0",
162
- "@directus/extensions-sdk": "17.0.3",
163
- "@directus/memory": "3.0.12",
166
+ "@directus/extensions-registry": "3.0.15",
167
+ "@directus/env": "5.3.3",
164
168
  "@directus/format-title": "12.1.1",
165
- "@directus/pressure": "3.0.12",
166
- "@directus/schema-builder": "0.0.9",
167
- "@directus/specs": "11.2.0",
169
+ "@directus/memory": "3.0.13",
170
+ "@directus/pressure": "3.0.13",
168
171
  "@directus/schema": "13.0.4",
169
172
  "@directus/storage": "12.0.3",
170
- "@directus/storage-driver-cloudinary": "12.0.12",
171
- "@directus/storage-driver-azure": "12.0.12",
173
+ "@directus/specs": "11.2.0",
174
+ "@directus/schema-builder": "0.0.10",
175
+ "@directus/storage-driver-azure": "12.0.13",
176
+ "@directus/storage-driver-cloudinary": "12.0.13",
177
+ "@directus/storage-driver-gcs": "12.0.13",
172
178
  "@directus/storage-driver-local": "12.0.3",
173
- "@directus/storage-driver-gcs": "12.0.12",
174
- "@directus/storage-driver-supabase": "3.0.12",
175
- "@directus/storage-driver-s3": "12.0.12",
176
- "@directus/utils": "13.0.13",
177
- "@directus/validation": "2.0.12",
178
- "@directus/system-data": "3.4.2",
179
- "directus": "11.13.4"
179
+ "@directus/system-data": "3.5.0",
180
+ "@directus/storage-driver-supabase": "3.0.13",
181
+ "@directus/utils": "13.1.0",
182
+ "@directus/validation": "2.0.13",
183
+ "@directus/storage-driver-s3": "12.0.13",
184
+ "directus": "11.14.0"
180
185
  },
181
186
  "devDependencies": {
182
187
  "@directus/tsconfig": "3.0.0",
183
188
  "@ngneat/falso": "8.0.2",
189
+ "@types/archiver": "7.0.0",
184
190
  "@types/async": "3.2.25",
185
191
  "@types/busboy": "1.5.4",
186
192
  "@types/bytes": "3.1.5",
@@ -219,8 +225,8 @@
219
225
  "knex-mock-client": "3.0.2",
220
226
  "typescript": "5.9.3",
221
227
  "vitest": "3.2.4",
222
- "@directus/schema-builder": "0.0.9",
223
- "@directus/types": "13.4.0"
228
+ "@directus/schema-builder": "0.0.10",
229
+ "@directus/types": "13.5.0"
224
230
  },
225
231
  "optionalDependencies": {
226
232
  "@keyv/redis": "3.0.1",
@@ -1,3 +0,0 @@
1
- export declare const syncExtensions: (options?: {
2
- force: boolean;
3
- }) => Promise<void>;
@@ -1,70 +0,0 @@
1
- import { useEnv } from '@directus/env';
2
- import { exists } from 'fs-extra';
3
- import mid from 'node-machine-id';
4
- import { createWriteStream } from 'node:fs';
5
- import { mkdir, rm } from 'node:fs/promises';
6
- import { dirname, join, relative, resolve, sep } from 'node:path';
7
- import { pipeline } from 'node:stream/promises';
8
- import Queue from 'p-queue';
9
- import { useBus } from '../../bus/index.js';
10
- import { useLock } from '../../lock/index.js';
11
- import { useLogger } from '../../logger/index.js';
12
- import { getStorage } from '../../storage/index.js';
13
- import { getExtensionsPath } from './get-extensions-path.js';
14
- import { SyncStatus, getSyncStatus, setSyncStatus } from './sync-status.js';
15
- export const syncExtensions = async (options) => {
16
- const lock = useLock();
17
- const messenger = useBus();
18
- const env = useEnv();
19
- const logger = useLogger();
20
- if (!options?.force) {
21
- const isDone = (await getSyncStatus()) === SyncStatus.DONE;
22
- if (isDone)
23
- return;
24
- }
25
- const machineId = await mid.machineId();
26
- const machineKey = `extensions-sync/${machineId}`;
27
- const processId = await lock.increment(machineKey);
28
- const currentProcessShouldHandleSync = processId === 1;
29
- if (currentProcessShouldHandleSync === false) {
30
- logger.trace('Extensions already being synced to this machine from another process.');
31
- // Wait until the process that called the lock publishes a message that the syncing is complete
32
- return new Promise((resolve) => {
33
- messenger.subscribe(machineKey, () => resolve());
34
- });
35
- }
36
- try {
37
- const extensionsPath = getExtensionsPath();
38
- const storageExtensionsPath = env['EXTENSIONS_PATH'];
39
- if (await exists(extensionsPath)) {
40
- // In case the FS still contains the cached extensions from a previous invocation. We have to
41
- // clear them out to ensure the remote extensions folder remains the source of truth for all
42
- // extensions that are loaded.
43
- await rm(extensionsPath, { recursive: true, force: true });
44
- }
45
- // Ensure that the local extensions cache path exists
46
- await mkdir(extensionsPath, { recursive: true });
47
- await setSyncStatus(SyncStatus.SYNCING);
48
- logger.trace('Syncing extensions from configured storage location...');
49
- const storage = await getStorage();
50
- const disk = storage.location(env['EXTENSIONS_LOCATION']);
51
- // Make sure we don't overload the file handles
52
- const queue = new Queue({ concurrency: 1000 });
53
- for await (const filepath of disk.list(storageExtensionsPath)) {
54
- const readStream = await disk.read(filepath);
55
- // We want files to be stored in the root of `$TEMP_PATH/extensions`, so gotta remove the
56
- // extensions path on disk from the start of the file path
57
- const destPath = join(extensionsPath, relative(resolve(sep, storageExtensionsPath), resolve(sep, filepath)));
58
- // Ensure that the directory path exists
59
- await mkdir(dirname(destPath), { recursive: true });
60
- const writeStream = createWriteStream(destPath);
61
- queue.add(() => pipeline(readStream, writeStream));
62
- }
63
- await queue.onIdle();
64
- await setSyncStatus(SyncStatus.DONE);
65
- messenger.publish(machineKey, { ready: true });
66
- }
67
- finally {
68
- await lock.delete(machineKey);
69
- }
70
- };
@@ -1,10 +0,0 @@
1
- export declare enum SyncStatus {
2
- UNKNOWN = "UNKNOWN",
3
- SYNCING = "SYNCING",
4
- DONE = "DONE"
5
- }
6
- /**
7
- * Retrieves the sync status from the `.status` file in the local extensions folder
8
- */
9
- export declare const getSyncStatus: () => Promise<string>;
10
- export declare const setSyncStatus: (status: SyncStatus) => Promise<void>;
@@ -1,27 +0,0 @@
1
- import { exists } from 'fs-extra';
2
- import { readFile, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { getExtensionsPath } from './get-extensions-path.js';
5
- export var SyncStatus;
6
- (function (SyncStatus) {
7
- SyncStatus["UNKNOWN"] = "UNKNOWN";
8
- SyncStatus["SYNCING"] = "SYNCING";
9
- SyncStatus["DONE"] = "DONE";
10
- })(SyncStatus || (SyncStatus = {}));
11
- /**
12
- * Retrieves the sync status from the `.status` file in the local extensions folder
13
- */
14
- export const getSyncStatus = async () => {
15
- const statusFilePath = join(getExtensionsPath(), '.status');
16
- if (await exists(statusFilePath)) {
17
- const status = await readFile(statusFilePath, 'utf8');
18
- return status;
19
- }
20
- else {
21
- return SyncStatus.UNKNOWN;
22
- }
23
- };
24
- export const setSyncStatus = async (status) => {
25
- const statusFilePath = join(getExtensionsPath(), '.status');
26
- await writeFile(statusFilePath, status);
27
- };
@@ -1,15 +0,0 @@
1
- import type { ToolConfig } from '../types.js';
2
- import { collections } from './collections.js';
3
- import { fields } from './fields.js';
4
- import { files } from './files.js';
5
- import { flows } from './flows.js';
6
- import { items } from './items.js';
7
- import { operations } from './operations.js';
8
- import { relations } from './relations.js';
9
- import { schema } from './schema.js';
10
- import { system } from './system.js';
11
- import { triggerFlow } from './trigger-flow.js';
12
- export declare const ALL_TOOLS: ToolConfig<any>[];
13
- export declare const getAllMcpTools: () => ToolConfig<any>[];
14
- export declare const findMcpTool: (name: string) => ToolConfig<any> | undefined;
15
- export { collections, fields, files, flows, items, operations, relations, schema, system, triggerFlow };
@@ -1,29 +0,0 @@
1
- import { assets } from './assets.js';
2
- import { collections } from './collections.js';
3
- import { fields } from './fields.js';
4
- import { files } from './files.js';
5
- import { flows } from './flows.js';
6
- import { folders } from './folders.js';
7
- import { items } from './items.js';
8
- import { operations } from './operations.js';
9
- import { relations } from './relations.js';
10
- import { schema } from './schema.js';
11
- import { system } from './system.js';
12
- import { triggerFlow } from './trigger-flow.js';
13
- export const ALL_TOOLS = [
14
- system,
15
- items,
16
- files,
17
- folders,
18
- assets,
19
- flows,
20
- triggerFlow,
21
- operations,
22
- schema,
23
- collections,
24
- fields,
25
- relations,
26
- ];
27
- export const getAllMcpTools = () => ALL_TOOLS;
28
- export const findMcpTool = (name) => ALL_TOOLS.find((tool) => tool.name === name);
29
- export { collections, fields, files, flows, items, operations, relations, schema, system, triggerFlow };
@@ -1,16 +0,0 @@
1
- declare const _default: {
2
- assets: string;
3
- collections: string;
4
- fields: string;
5
- files: string;
6
- folders: string;
7
- flows: string;
8
- items: string;
9
- operations: string;
10
- relations: string;
11
- schema: string;
12
- systemPrompt: string;
13
- systemPromptDescription: string;
14
- triggerFlow: string;
15
- };
16
- export default _default;
@@ -1,19 +0,0 @@
1
- import fse from 'fs-extra';
2
- import { dirname, join } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
- const __dirname = dirname(fileURLToPath(import.meta.url));
5
- export default {
6
- assets: fse.readFileSync(join(__dirname, 'assets.md'), 'utf8'),
7
- collections: fse.readFileSync(join(__dirname, 'collections.md'), 'utf8'),
8
- fields: fse.readFileSync(join(__dirname, 'fields.md'), 'utf8'),
9
- files: fse.readFileSync(join(__dirname, 'files.md'), 'utf8'),
10
- folders: fse.readFileSync(join(__dirname, 'folders.md'), 'utf8'),
11
- flows: fse.readFileSync(join(__dirname, 'flows.md'), 'utf8'),
12
- items: fse.readFileSync(join(__dirname, 'items.md'), 'utf8'),
13
- operations: fse.readFileSync(join(__dirname, 'operations.md'), 'utf8'),
14
- relations: fse.readFileSync(join(__dirname, 'relations.md'), 'utf8'),
15
- schema: fse.readFileSync(join(__dirname, 'schema.md'), 'utf8'),
16
- systemPrompt: fse.readFileSync(join(__dirname, 'system-prompt.md'), 'utf8'),
17
- systemPromptDescription: fse.readFileSync(join(__dirname, 'system-prompt-description.md'), 'utf8'),
18
- triggerFlow: fse.readFileSync(join(__dirname, 'trigger-flow.md'), 'utf8'),
19
- };
@@ -1,5 +0,0 @@
1
- import type { Accountability } from '@directus/types';
2
- import type { Knex } from 'knex';
3
- import type { GlobalAccess } from '../types.js';
4
- export declare const fetchGlobalAccessForRoles: typeof _fetchGlobalAccessForRoles;
5
- export declare function _fetchGlobalAccessForRoles(accountability: Pick<Accountability, 'roles' | 'ip'>, knex: Knex): Promise<GlobalAccess>;
@@ -1,7 +0,0 @@
1
- import { withCache } from '../../../utils/with-cache.js';
2
- import { fetchGlobalAccessForQuery } from '../utils/fetch-global-access-for-query.js';
3
- export const fetchGlobalAccessForRoles = withCache('global-access-role', _fetchGlobalAccessForRoles, ({ roles, ip }) => ({ roles, ip }));
4
- export async function _fetchGlobalAccessForRoles(accountability, knex) {
5
- const query = knex.where('role', 'in', accountability.roles);
6
- return await fetchGlobalAccessForQuery(query, accountability);
7
- }
@@ -1,5 +0,0 @@
1
- import type { Accountability } from '@directus/types';
2
- import type { Knex } from 'knex';
3
- import type { GlobalAccess } from '../types.js';
4
- export declare const fetchGlobalAccessForUser: typeof _fetchGlobalAccessForUser;
5
- export declare function _fetchGlobalAccessForUser(accountability: Pick<Accountability, 'user' | 'ip'>, knex: Knex): Promise<GlobalAccess>;