@directus/api 32.2.0 → 33.1.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 (297) hide show
  1. package/dist/ai/chat/controllers/chat.post.js +19 -4
  2. package/dist/ai/chat/lib/create-ui-stream.d.ts +8 -7
  3. package/dist/ai/chat/lib/create-ui-stream.js +28 -25
  4. package/dist/ai/chat/middleware/load-settings.js +31 -7
  5. package/dist/ai/chat/models/chat-request.d.ts +135 -2
  6. package/dist/ai/chat/models/chat-request.js +56 -2
  7. package/dist/ai/chat/models/providers.d.ts +16 -2
  8. package/dist/ai/chat/models/providers.js +16 -2
  9. package/dist/ai/chat/utils/chat-request-tool-to-ai-sdk-tool.js +3 -4
  10. package/dist/ai/chat/utils/format-context.d.ts +5 -0
  11. package/dist/ai/chat/utils/format-context.js +122 -0
  12. package/dist/ai/mcp/server.d.ts +27 -1
  13. package/dist/ai/providers/index.d.ts +3 -0
  14. package/dist/ai/providers/index.js +3 -0
  15. package/dist/ai/providers/options.d.ts +14 -0
  16. package/dist/ai/providers/options.js +26 -0
  17. package/dist/ai/providers/registry.d.ts +6 -0
  18. package/dist/ai/providers/registry.js +65 -0
  19. package/dist/ai/providers/types.d.ts +34 -0
  20. package/dist/ai/providers/types.js +1 -0
  21. package/dist/ai/tools/assets/index.js +1 -1
  22. package/dist/ai/tools/collections/index.js +2 -2
  23. package/dist/ai/tools/fields/index.js +2 -2
  24. package/dist/ai/tools/files/index.js +1 -1
  25. package/dist/ai/tools/flows/index.js +1 -1
  26. package/dist/ai/tools/folders/index.js +1 -1
  27. package/dist/ai/tools/items/index.js +6 -3
  28. package/dist/ai/tools/items/prompt.md +7 -9
  29. package/dist/ai/tools/relations/index.js +1 -1
  30. package/dist/ai/tools/schema.js +1 -1
  31. package/dist/ai/tools/trigger-flow/index.js +1 -1
  32. package/dist/app.js +12 -8
  33. package/dist/auth/drivers/ldap.d.ts +1 -1
  34. package/dist/auth/drivers/ldap.js +144 -139
  35. package/dist/auth/drivers/local.js +1 -1
  36. package/dist/auth/drivers/oauth2.d.ts +1 -2
  37. package/dist/auth/drivers/oauth2.js +22 -17
  38. package/dist/auth/drivers/openid.d.ts +1 -2
  39. package/dist/auth/drivers/openid.js +18 -13
  40. package/dist/auth/drivers/saml.js +3 -3
  41. package/dist/auth/utils/generate-callback-url.d.ts +11 -0
  42. package/dist/auth/utils/generate-callback-url.js +40 -0
  43. package/dist/auth/utils/is-login-redirect-allowed.d.ts +7 -0
  44. package/dist/{utils → auth/utils}/is-login-redirect-allowed.js +12 -9
  45. package/dist/cache.d.ts +12 -0
  46. package/dist/cache.js +27 -3
  47. package/dist/cli/commands/bootstrap/index.js +2 -2
  48. package/dist/cli/commands/database/install.js +1 -1
  49. package/dist/cli/commands/database/migrate.js +1 -1
  50. package/dist/cli/commands/init/index.js +2 -2
  51. package/dist/cli/commands/roles/create.js +4 -4
  52. package/dist/cli/commands/schema/apply.js +3 -3
  53. package/dist/cli/commands/schema/snapshot.js +1 -1
  54. package/dist/cli/utils/create-db-connection.d.ts +1 -1
  55. package/dist/cli/utils/create-db-connection.js +1 -1
  56. package/dist/cli/utils/create-env/env-stub.liquid +3 -0
  57. package/dist/cli/utils/create-env/index.js +1 -1
  58. package/dist/constants.d.ts +7 -3
  59. package/dist/constants.js +7 -3
  60. package/dist/controllers/access.js +1 -1
  61. package/dist/controllers/assets.js +1 -1
  62. package/dist/controllers/deployment.js +481 -0
  63. package/dist/controllers/extensions.js +1 -1
  64. package/dist/controllers/fields.js +8 -6
  65. package/dist/controllers/files.js +1 -1
  66. package/dist/controllers/items.js +1 -1
  67. package/dist/controllers/not-found.js +1 -1
  68. package/dist/controllers/relations.js +1 -1
  69. package/dist/database/errors/dialects/mysql.d.ts +1 -1
  70. package/dist/database/errors/dialects/postgres.d.ts +1 -1
  71. package/dist/database/errors/dialects/sqlite.d.ts +1 -1
  72. package/dist/database/errors/translate.d.ts +1 -1
  73. package/dist/database/errors/translate.js +1 -1
  74. package/dist/database/get-ast-from-query/lib/parse-fields.js +2 -2
  75. package/dist/database/helpers/date/dialects/mssql.js +1 -1
  76. package/dist/database/helpers/date/dialects/mysql.js +1 -1
  77. package/dist/database/helpers/date/types.js +1 -1
  78. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -0
  79. package/dist/database/helpers/schema/dialects/cockroachdb.js +24 -1
  80. package/dist/database/helpers/schema/dialects/mssql.d.ts +1 -1
  81. package/dist/database/helpers/schema/dialects/mysql.d.ts +2 -1
  82. package/dist/database/helpers/schema/dialects/mysql.js +16 -3
  83. package/dist/database/helpers/schema/dialects/postgres.d.ts +1 -1
  84. package/dist/database/helpers/schema/types.d.ts +13 -0
  85. package/dist/database/helpers/schema/types.js +24 -0
  86. package/dist/database/index.js +4 -4
  87. package/dist/database/migrations/20220429A-add-flows.js +1 -1
  88. package/dist/database/migrations/20230526A-migrate-translation-strings.js +1 -1
  89. package/dist/database/migrations/20231009A-update-csv-fields-to-text.js +1 -1
  90. package/dist/database/migrations/20240204A-marketplace.js +9 -7
  91. package/dist/database/migrations/20240311A-deprecate-webhooks.d.ts +15 -0
  92. package/dist/database/migrations/20240311A-deprecate-webhooks.js +1 -1
  93. package/dist/database/migrations/20240806A-permissions-policies.js +2 -2
  94. package/dist/database/migrations/20240924A-migrate-legacy-comments.js +1 -1
  95. package/dist/database/migrations/20251014A-add-project-owner.js +1 -1
  96. package/dist/database/migrations/20251224A-remove-webhooks.d.ts +3 -0
  97. package/dist/database/migrations/20251224A-remove-webhooks.js +19 -0
  98. package/dist/database/migrations/20260110A-add-ai-provider-settings.d.ts +3 -0
  99. package/dist/database/migrations/20260110A-add-ai-provider-settings.js +35 -0
  100. package/dist/database/migrations/20260113A-add-revisions-index.d.ts +3 -0
  101. package/dist/database/migrations/20260113A-add-revisions-index.js +41 -0
  102. package/dist/database/migrations/20260128A-add-collaborative-editing.d.ts +3 -0
  103. package/dist/database/migrations/20260128A-add-collaborative-editing.js +10 -0
  104. package/dist/database/migrations/20260204A-add-deployment.d.ts +3 -0
  105. package/dist/database/migrations/20260204A-add-deployment.js +32 -0
  106. package/dist/database/migrations/run.js +3 -3
  107. package/dist/database/run-ast/lib/apply-query/add-join.js +1 -1
  108. package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.d.ts +2 -2
  109. package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.js +1 -1
  110. package/dist/database/run-ast/lib/apply-query/filter/index.js +1 -1
  111. package/dist/database/run-ast/lib/apply-query/filter/operator.js +1 -1
  112. package/dist/database/run-ast/lib/apply-query/sort.js +1 -1
  113. package/dist/database/run-ast/utils/get-column-pre-processor.js +2 -2
  114. package/dist/database/run-ast/utils/get-column.js +1 -1
  115. package/dist/database/seeds/run.js +3 -3
  116. package/dist/deployment/deployment.d.ts +94 -0
  117. package/dist/deployment/deployment.js +29 -0
  118. package/dist/deployment/drivers/index.d.ts +1 -0
  119. package/dist/deployment/drivers/index.js +1 -0
  120. package/dist/deployment/drivers/vercel.d.ts +32 -0
  121. package/dist/deployment/drivers/vercel.js +208 -0
  122. package/dist/deployment/index.d.ts +2 -0
  123. package/dist/deployment/index.js +2 -0
  124. package/dist/deployment.d.ts +24 -0
  125. package/dist/deployment.js +39 -0
  126. package/dist/extensions/lib/get-extensions-path.js +1 -1
  127. package/dist/extensions/lib/get-extensions-settings.js +1 -1
  128. package/dist/extensions/lib/get-extensions.js +1 -1
  129. package/dist/extensions/lib/get-shared-deps-mapping.js +3 -3
  130. package/dist/extensions/lib/installation/manager.js +3 -3
  131. package/dist/extensions/lib/sandbox/register/route.d.ts +1 -1
  132. package/dist/extensions/lib/sync/status.js +1 -1
  133. package/dist/extensions/lib/sync/sync.js +7 -7
  134. package/dist/extensions/lib/sync/utils.js +2 -2
  135. package/dist/extensions/manager.d.ts +1 -1
  136. package/dist/extensions/manager.js +8 -8
  137. package/dist/flows.d.ts +1 -1
  138. package/dist/logger/index.js +1 -1
  139. package/dist/logger/logs-stream.d.ts +1 -1
  140. package/dist/logger/logs-stream.js +1 -1
  141. package/dist/mailer.js +1 -1
  142. package/dist/metrics/lib/create-metrics.js +2 -2
  143. package/dist/middleware/authenticate.js +3 -3
  144. package/dist/middleware/collection-exists.js +1 -1
  145. package/dist/middleware/extract-token.js +1 -1
  146. package/dist/middleware/graphql.js +2 -2
  147. package/dist/middleware/respond.js +27 -14
  148. package/dist/middleware/validate-batch.js +1 -1
  149. package/dist/operations/exec/index.js +2 -1
  150. package/dist/operations/mail/index.js +1 -1
  151. package/dist/operations/mail/rate-limiter.js +2 -2
  152. package/dist/permissions/cache.js +5 -0
  153. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +1 -1
  154. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +1 -1
  155. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +2 -2
  156. package/dist/permissions/modules/process-ast/lib/inject-cases.js +1 -1
  157. package/dist/permissions/modules/process-ast/process-ast.js +1 -1
  158. package/dist/permissions/modules/process-ast/utils/find-related-collection.js +1 -1
  159. package/dist/permissions/modules/process-payload/process-payload.js +1 -1
  160. package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +14 -2
  161. package/dist/permissions/modules/validate-access/lib/validate-item-access.js +72 -13
  162. package/dist/permissions/modules/validate-access/validate-access.js +3 -2
  163. package/dist/rate-limiter.js +1 -1
  164. package/dist/request/is-denied-ip.js +1 -1
  165. package/dist/schedules/project.js +1 -1
  166. package/dist/schedules/telemetry.js +1 -1
  167. package/dist/schedules/tus.js +1 -1
  168. package/dist/server.js +6 -5
  169. package/dist/services/assets.d.ts +2 -1
  170. package/dist/services/assets.js +35 -8
  171. package/dist/services/authentication.js +2 -2
  172. package/dist/services/collections.js +1 -1
  173. package/dist/services/deployment-projects.d.ts +20 -0
  174. package/dist/services/deployment-projects.js +34 -0
  175. package/dist/services/deployment-runs.d.ts +13 -0
  176. package/dist/services/deployment-runs.js +6 -0
  177. package/dist/services/deployment.d.ts +40 -0
  178. package/dist/services/deployment.js +202 -0
  179. package/dist/services/extensions.d.ts +1 -1
  180. package/dist/services/files/utils/get-metadata.d.ts +1 -1
  181. package/dist/services/files/utils/get-metadata.js +1 -1
  182. package/dist/services/files.d.ts +1 -1
  183. package/dist/services/files.js +4 -4
  184. package/dist/services/graphql/index.d.ts +1 -1
  185. package/dist/services/graphql/index.js +1 -1
  186. package/dist/services/graphql/resolvers/mutation.js +1 -1
  187. package/dist/services/graphql/resolvers/system-admin.js +2 -3
  188. package/dist/services/graphql/schema/get-types.d.ts +1 -1
  189. package/dist/services/graphql/schema/read.js +1 -1
  190. package/dist/services/graphql/subscription.d.ts +1 -1
  191. package/dist/services/graphql/types/date.js +1 -1
  192. package/dist/services/graphql/types/hash.js +1 -1
  193. package/dist/services/graphql/utils/add-path-to-validation-error.js +1 -1
  194. package/dist/services/graphql/utils/filter-replace-m2a.js +3 -4
  195. package/dist/services/import-export.d.ts +1 -1
  196. package/dist/services/import-export.js +2 -2
  197. package/dist/services/index.d.ts +3 -1
  198. package/dist/services/index.js +3 -1
  199. package/dist/services/mail/index.js +2 -2
  200. package/dist/services/mail/rate-limiter.js +2 -2
  201. package/dist/services/payload.js +2 -2
  202. package/dist/services/schema.js +1 -1
  203. package/dist/services/server.js +13 -4
  204. package/dist/services/settings.js +2 -2
  205. package/dist/services/specifications.js +2 -2
  206. package/dist/services/tfa.js +1 -1
  207. package/dist/services/translations.js +1 -1
  208. package/dist/services/tus/data-store.d.ts +1 -3
  209. package/dist/services/tus/data-store.js +2 -5
  210. package/dist/services/tus/server.js +6 -6
  211. package/dist/services/users.js +4 -4
  212. package/dist/services/versions.js +1 -1
  213. package/dist/telemetry/lib/get-report.js +2 -0
  214. package/dist/telemetry/lib/send-report.d.ts +1 -1
  215. package/dist/telemetry/lib/send-report.js +1 -1
  216. package/dist/telemetry/lib/track.js +1 -1
  217. package/dist/telemetry/types/report.d.ts +8 -0
  218. package/dist/telemetry/utils/get-settings.d.ts +2 -0
  219. package/dist/telemetry/utils/get-settings.js +5 -0
  220. package/dist/test-utils/knex.js +1 -1
  221. package/dist/types/collection.d.ts +1 -1
  222. package/dist/utils/async-handler.d.ts +1 -1
  223. package/dist/utils/calculate-field-depth.js +1 -1
  224. package/dist/utils/compress.js +1 -1
  225. package/dist/utils/deep-map-response.d.ts +1 -1
  226. package/dist/utils/deep-map-response.js +2 -2
  227. package/dist/utils/get-cache-key.js +1 -1
  228. package/dist/utils/get-column-path.js +1 -1
  229. package/dist/utils/get-field-system-rows.js +1 -1
  230. package/dist/utils/get-ip-from-req.d.ts +1 -1
  231. package/dist/utils/get-ip-from-req.js +1 -1
  232. package/dist/utils/get-local-type.js +7 -3
  233. package/dist/utils/get-service.js +7 -3
  234. package/dist/utils/get-snapshot-diff.js +1 -1
  235. package/dist/utils/is-field-allowed.d.ts +4 -0
  236. package/dist/utils/is-field-allowed.js +9 -0
  237. package/dist/utils/is-url-allowed.js +1 -1
  238. package/dist/utils/jwt.js +1 -1
  239. package/dist/utils/sanitize-schema.d.ts +1 -1
  240. package/dist/utils/should-clear-cache.d.ts +1 -1
  241. package/dist/utils/should-skip-cache.js +2 -2
  242. package/dist/utils/validate-diff.js +1 -1
  243. package/dist/utils/validate-snapshot.js +3 -3
  244. package/dist/utils/validate-storage.js +2 -2
  245. package/dist/utils/verify-session-jwt.js +1 -1
  246. package/dist/utils/versioning/handle-version.js +1 -1
  247. package/dist/websocket/collab/calculate-cache-metadata.d.ts +9 -0
  248. package/dist/websocket/collab/calculate-cache-metadata.js +121 -0
  249. package/dist/websocket/collab/collab.d.ts +63 -0
  250. package/dist/websocket/collab/collab.js +481 -0
  251. package/dist/websocket/collab/constants.d.ts +1 -0
  252. package/dist/websocket/collab/constants.js +13 -0
  253. package/dist/websocket/collab/filter-to-fields.d.ts +2 -0
  254. package/dist/websocket/collab/filter-to-fields.js +11 -0
  255. package/dist/websocket/collab/messenger.d.ts +43 -0
  256. package/dist/websocket/collab/messenger.js +225 -0
  257. package/dist/websocket/collab/payload-permissions.d.ts +18 -0
  258. package/dist/websocket/collab/payload-permissions.js +158 -0
  259. package/dist/websocket/collab/permissions-cache.d.ts +52 -0
  260. package/dist/websocket/collab/permissions-cache.js +204 -0
  261. package/dist/websocket/collab/room.d.ts +125 -0
  262. package/dist/websocket/collab/room.js +593 -0
  263. package/dist/websocket/collab/store.d.ts +7 -0
  264. package/dist/websocket/collab/store.js +33 -0
  265. package/dist/websocket/collab/types.d.ts +21 -0
  266. package/dist/websocket/collab/types.js +1 -0
  267. package/dist/websocket/collab/verify-permissions.d.ts +11 -0
  268. package/dist/websocket/collab/verify-permissions.js +100 -0
  269. package/dist/websocket/controllers/base.d.ts +2 -2
  270. package/dist/websocket/controllers/base.js +3 -3
  271. package/dist/websocket/controllers/graphql.d.ts +1 -1
  272. package/dist/websocket/controllers/graphql.js +1 -1
  273. package/dist/websocket/controllers/logs.d.ts +1 -1
  274. package/dist/websocket/controllers/rest.d.ts +1 -1
  275. package/dist/websocket/controllers/rest.js +2 -2
  276. package/dist/websocket/handlers/heartbeat.js +1 -1
  277. package/dist/websocket/handlers/index.d.ts +2 -0
  278. package/dist/websocket/handlers/index.js +9 -0
  279. package/dist/websocket/handlers/items.js +2 -2
  280. package/dist/websocket/handlers/subscribe.js +1 -1
  281. package/dist/websocket/types.d.ts +1 -1
  282. package/dist/websocket/utils/items.d.ts +2 -2
  283. package/dist/websocket/utils/message.d.ts +1 -1
  284. package/dist/websocket/utils/message.js +2 -2
  285. package/dist/websocket/utils/wait-for-message.js +1 -1
  286. package/package.json +35 -33
  287. package/dist/controllers/webhooks.js +0 -74
  288. package/dist/services/webhooks.d.ts +0 -14
  289. package/dist/services/webhooks.js +0 -32
  290. package/dist/utils/get-relation-info.d.ts +0 -6
  291. package/dist/utils/get-relation-info.js +0 -43
  292. package/dist/utils/get-relation-type.d.ts +0 -6
  293. package/dist/utils/get-relation-type.js +0 -18
  294. package/dist/utils/is-login-redirect-allowed.d.ts +0 -4
  295. package/dist/utils/versioning/deep-map-with-schema.d.ts +0 -23
  296. package/dist/utils/versioning/deep-map-with-schema.js +0 -81
  297. /package/dist/controllers/{webhooks.d.ts → deployment.d.ts} +0 -0
@@ -0,0 +1,94 @@
1
+ import type { Credentials, Deployment, Details, Log, Options, Project, TriggerResult } from '@directus/types';
2
+ import type { AxiosRequestConfig, AxiosResponse } from 'axios';
3
+ export type DeploymentRequestOptions = Pick<AxiosRequestConfig<string>, 'method' | 'headers'> & {
4
+ body?: string | null;
5
+ params?: Record<string, string>;
6
+ };
7
+ export declare abstract class DeploymentDriver<TCredentials extends Credentials = Credentials, TOptions extends Options = Options> {
8
+ credentials: TCredentials;
9
+ options: TOptions;
10
+ constructor(credentials: TCredentials, options?: TOptions);
11
+ protected axiosRequest<T>(apiUrl: string, endpoint: string, options?: DeploymentRequestOptions): Promise<AxiosResponse<T>>;
12
+ /**
13
+ * Test connection with provider using credentials
14
+ *
15
+ * @throws {InvalidCredentialsError} When API credentials are invalid
16
+ */
17
+ abstract testConnection(): Promise<void>;
18
+ /**
19
+ * List all available projects from provider
20
+ *
21
+ * @returns Array of external projects
22
+ * @throws {InvalidCredentialsError} When API credentials are invalid
23
+ * @throws {HitRateLimitError} When rate limit is exceeded
24
+ * @throws {ServiceUnavailableError} When provider API fails
25
+ */
26
+ abstract listProjects(): Promise<Project[]>;
27
+ /**
28
+ * Get project details by ID
29
+ *
30
+ * @param projectId External project ID
31
+ * @returns Project details
32
+ * @throws {InvalidCredentialsError} When API credentials are invalid
33
+ * @throws {HitRateLimitError} When rate limit is exceeded
34
+ * @throws {ServiceUnavailableError} When provider API fails
35
+ */
36
+ abstract getProject(projectId: string): Promise<Project>;
37
+ /**
38
+ * List deployments for a project
39
+ *
40
+ * @param projectId External project ID
41
+ * @param limit Number of deployments to return
42
+ * @returns Array of deployments
43
+ * @throws {InvalidCredentialsError} When API credentials are invalid
44
+ * @throws {HitRateLimitError} When rate limit is exceeded
45
+ * @throws {ServiceUnavailableError} When provider API fails
46
+ */
47
+ abstract listDeployments(projectId: string, limit?: number): Promise<Deployment[]>;
48
+ /**
49
+ * Get deployment details including logs
50
+ *
51
+ * @param deploymentId External deployment ID
52
+ * @returns Deployment details with logs
53
+ * @throws {InvalidCredentialsError} When API credentials are invalid
54
+ * @throws {HitRateLimitError} When rate limit is exceeded
55
+ * @throws {ServiceUnavailableError} When provider API fails
56
+ */
57
+ abstract getDeployment(deploymentId: string): Promise<Details>;
58
+ /**
59
+ * Trigger a new deployment
60
+ *
61
+ * @param projectId External project ID
62
+ * @param options Deployment options
63
+ * @returns Deployment result
64
+ * @throws {InvalidCredentialsError} When API credentials are invalid
65
+ * @throws {HitRateLimitError} When rate limit is exceeded
66
+ * @throws {ServiceUnavailableError} When provider API fails
67
+ */
68
+ abstract triggerDeployment(projectId: string, options?: {
69
+ preview?: boolean;
70
+ clearCache?: boolean;
71
+ }): Promise<TriggerResult>;
72
+ /**
73
+ * Cancel a running deployment
74
+ *
75
+ * @param deploymentId External deployment ID
76
+ * @throws {InvalidCredentialsError} When API credentials are invalid
77
+ * @throws {HitRateLimitError} When rate limit is exceeded
78
+ * @throws {ServiceUnavailableError} When provider API fails
79
+ */
80
+ abstract cancelDeployment(deploymentId: string): Promise<void>;
81
+ /**
82
+ * Get deployment build logs
83
+ *
84
+ * @param deploymentId External deployment ID
85
+ * @param options.since Only return logs after this timestamp
86
+ * @returns Array of log entries
87
+ * @throws {InvalidCredentialsError} When API credentials are invalid
88
+ * @throws {HitRateLimitError} When rate limit is exceeded
89
+ * @throws {ServiceUnavailableError} When provider API fails
90
+ */
91
+ abstract getDeploymentLogs(deploymentId: string, options?: {
92
+ since?: Date;
93
+ }): Promise<Log[]>;
94
+ }
@@ -0,0 +1,29 @@
1
+ import { getAxios } from '../request/index.js';
2
+ export class DeploymentDriver {
3
+ credentials;
4
+ options;
5
+ constructor(credentials, options = {}) {
6
+ this.credentials = credentials;
7
+ this.options = options;
8
+ }
9
+ async axiosRequest(apiUrl, endpoint, options = {}) {
10
+ const { params, ...requestOptions } = options;
11
+ const url = new URL(endpoint, apiUrl);
12
+ if (params) {
13
+ for (const [key, value] of Object.entries(params)) {
14
+ url.searchParams.set(key, value);
15
+ }
16
+ }
17
+ const axios = await getAxios();
18
+ const requestConfig = {
19
+ url: url.toString(),
20
+ method: requestOptions.method ?? 'GET',
21
+ validateStatus: () => true,
22
+ headers: requestOptions.headers ?? {},
23
+ };
24
+ if (requestOptions.body) {
25
+ requestConfig.data = requestOptions.body;
26
+ }
27
+ return await axios.request(requestConfig);
28
+ }
29
+ }
@@ -0,0 +1 @@
1
+ export * from './vercel.js';
@@ -0,0 +1 @@
1
+ export * from './vercel.js';
@@ -0,0 +1,32 @@
1
+ import type { Credentials, Deployment, Details, Log, Options, Project, TriggerResult } from '@directus/types';
2
+ import { DeploymentDriver } from '../deployment.js';
3
+ export interface VercelCredentials extends Credentials {
4
+ access_token: string;
5
+ }
6
+ export interface VercelOptions extends Options {
7
+ team_id?: string;
8
+ }
9
+ export declare class VercelDriver extends DeploymentDriver<VercelCredentials, VercelOptions> {
10
+ private static readonly API_URL;
11
+ private requestLimit;
12
+ constructor(credentials: VercelCredentials, options?: VercelOptions);
13
+ /**
14
+ * Make authenticated request with retry on rate limit and concurrency control
15
+ */
16
+ private request;
17
+ private mapStatus;
18
+ testConnection(): Promise<void>;
19
+ private mapProjectBase;
20
+ listProjects(): Promise<Project[]>;
21
+ getProject(projectId: string): Promise<Project>;
22
+ listDeployments(projectId: string, limit?: number): Promise<Deployment[]>;
23
+ getDeployment(deploymentId: string): Promise<Details>;
24
+ triggerDeployment(projectId: string, options?: {
25
+ preview?: boolean;
26
+ clearCache?: boolean;
27
+ }): Promise<TriggerResult>;
28
+ cancelDeployment(deploymentId: string): Promise<void>;
29
+ getDeploymentLogs(deploymentId: string, options?: {
30
+ since?: Date;
31
+ }): Promise<Log[]>;
32
+ }
@@ -0,0 +1,208 @@
1
+ import { HitRateLimitError, InvalidCredentialsError, ServiceUnavailableError } from '@directus/errors';
2
+ import pLimit from 'p-limit';
3
+ import { DeploymentDriver } from '../deployment.js';
4
+ export class VercelDriver extends DeploymentDriver {
5
+ static API_URL = 'https://api.vercel.com';
6
+ requestLimit = pLimit(5);
7
+ constructor(credentials, options = {}) {
8
+ super(credentials, options);
9
+ }
10
+ /**
11
+ * Make authenticated request with retry on rate limit and concurrency control
12
+ */
13
+ async request(endpoint, options = {}, retryCount = 0) {
14
+ return this.requestLimit(async () => {
15
+ const response = await this.axiosRequest(VercelDriver.API_URL, endpoint, {
16
+ ...options,
17
+ headers: {
18
+ Authorization: `Bearer ${this.credentials.access_token}`,
19
+ 'Content-Type': 'application/json',
20
+ ...(options.headers ?? {}),
21
+ },
22
+ params: {
23
+ ...(this.options.team_id ? { teamId: this.options.team_id } : {}),
24
+ ...(options.params ?? {}),
25
+ },
26
+ });
27
+ // Handle rate limiting with retry (max 3 retries)
28
+ if (response.status === 429) {
29
+ const resetAt = parseInt(response.headers['x-ratelimit-reset'] || '0');
30
+ const limit = parseInt(response.headers['x-ratelimit-limit'] || '0');
31
+ if (retryCount < 3) {
32
+ const waitTime = resetAt > 0 ? Math.max(resetAt * 1000 - Date.now(), 1000) : 1000 * (retryCount + 1);
33
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
34
+ return this.request(endpoint, options, retryCount + 1);
35
+ }
36
+ // Max retries exceeded
37
+ throw new HitRateLimitError({
38
+ limit,
39
+ reset: new Date(resetAt > 0 ? resetAt * 1000 : Date.now()),
40
+ });
41
+ }
42
+ const body = response.data;
43
+ if (response.status >= 400) {
44
+ const message = typeof body === 'object' && body !== null && 'error' in body
45
+ ? body.error?.message || `Vercel API error: ${response.status}`
46
+ : `Vercel API error: ${response.status}`;
47
+ if (response.status === 401 || response.status === 403) {
48
+ throw new InvalidCredentialsError();
49
+ }
50
+ throw new ServiceUnavailableError({ service: 'vercel', reason: message });
51
+ }
52
+ return body;
53
+ });
54
+ }
55
+ mapStatus(vercelStatus) {
56
+ const normalized = vercelStatus?.toLowerCase();
57
+ switch (normalized) {
58
+ case 'building':
59
+ case 'error':
60
+ case 'canceled':
61
+ case 'ready':
62
+ return normalized;
63
+ case 'queued':
64
+ case 'initializing':
65
+ case 'analyzing':
66
+ case 'deploying':
67
+ default:
68
+ return 'building';
69
+ }
70
+ }
71
+ async testConnection() {
72
+ return await this.request('/v9/projects', { params: { limit: '1' } });
73
+ }
74
+ mapProjectBase(project) {
75
+ const result = {
76
+ id: project.id,
77
+ name: project.name,
78
+ deployable: Boolean(project.link?.type),
79
+ };
80
+ if (project.framework) {
81
+ result.framework = project.framework;
82
+ }
83
+ return result;
84
+ }
85
+ async listProjects() {
86
+ const response = await this.request('/v9/projects');
87
+ return response.projects.map((project) => this.mapProjectBase(project));
88
+ }
89
+ async getProject(projectId) {
90
+ const project = await this.request(`/v9/projects/${projectId}`);
91
+ const result = this.mapProjectBase(project);
92
+ const production = project.targets?.production;
93
+ if (production?.alias?.[0]) {
94
+ result.url = `https://${production.alias[0]}`;
95
+ }
96
+ // Latest deployment info from detail endpoint
97
+ if (production?.readyState && production.createdAt) {
98
+ result.latest_deployment = {
99
+ status: this.mapStatus(production.readyState),
100
+ created_at: new Date(production.createdAt),
101
+ ...(production.readyAt && { finished_at: new Date(production.readyAt) }),
102
+ };
103
+ }
104
+ if (project.createdAt) {
105
+ result.created_at = new Date(project.createdAt);
106
+ }
107
+ if (project.updatedAt) {
108
+ result.updated_at = new Date(project.updatedAt);
109
+ }
110
+ return result;
111
+ }
112
+ async listDeployments(projectId, limit = 20) {
113
+ const url = `/v6/deployments?projectId=${encodeURIComponent(projectId)}&limit=${limit}`;
114
+ const response = await this.request(url);
115
+ return response.deployments.map((deployment) => {
116
+ const result = {
117
+ id: deployment.uid,
118
+ project_id: deployment.projectId ?? projectId,
119
+ status: this.mapStatus(deployment.state),
120
+ created_at: new Date(deployment.createdAt),
121
+ };
122
+ if (deployment.url) {
123
+ result.url = `https://${deployment.url}`;
124
+ }
125
+ if (deployment.ready) {
126
+ result.finished_at = new Date(deployment.ready);
127
+ }
128
+ return result;
129
+ });
130
+ }
131
+ async getDeployment(deploymentId) {
132
+ const deployment = await this.request(`/v13/deployments/${encodeURIComponent(deploymentId)}`);
133
+ const result = {
134
+ id: deployment.id,
135
+ project_id: deployment.projectId ?? '',
136
+ status: this.mapStatus(deployment.status || deployment.state),
137
+ created_at: new Date(deployment.createdAt),
138
+ };
139
+ if (deployment.url) {
140
+ result.url = `https://${deployment.url}`;
141
+ }
142
+ if (deployment.ready) {
143
+ result.finished_at = new Date(deployment.ready);
144
+ }
145
+ return result;
146
+ }
147
+ async triggerDeployment(projectId, options) {
148
+ // Fetch project to get realtime required name needed for the vercel request
149
+ const project = await this.request(`/v9/projects/${projectId}`);
150
+ const body = {
151
+ name: project.name,
152
+ project: projectId,
153
+ };
154
+ if (!options?.preview) {
155
+ body['target'] = 'production';
156
+ }
157
+ // Add required gitSource
158
+ if (project.link?.type) {
159
+ body['gitSource'] = {
160
+ type: project.link.type,
161
+ ref: project.link.productionBranch,
162
+ repoId: project.link.repoId,
163
+ };
164
+ }
165
+ // forceNew=1 skips build cache when clearCache is true
166
+ const response = await this.request('/v13/deployments', {
167
+ method: 'POST',
168
+ body: JSON.stringify(body),
169
+ ...(options?.clearCache && { params: { forceNew: '1' } }),
170
+ });
171
+ const triggerResult = {
172
+ deployment_id: response.id,
173
+ status: this.mapStatus(response.status),
174
+ };
175
+ if (response.url) {
176
+ triggerResult.url = `https://${response.url}`;
177
+ }
178
+ return triggerResult;
179
+ }
180
+ async cancelDeployment(deploymentId) {
181
+ await this.request(`/v12/deployments/${encodeURIComponent(deploymentId)}/cancel`, {
182
+ method: 'PATCH',
183
+ });
184
+ }
185
+ async getDeploymentLogs(deploymentId, options) {
186
+ let url = `/v3/deployments/${encodeURIComponent(deploymentId)}/events`;
187
+ // Vercel's since parameter uses milliseconds timestamp
188
+ if (options?.since) {
189
+ const sinceMs = options.since.getTime();
190
+ url += `?since=${sinceMs}`;
191
+ }
192
+ const response = await this.request(url);
193
+ const mapEventType = (type) => {
194
+ if (type === 'stderr')
195
+ return 'stderr';
196
+ if (type === 'command')
197
+ return 'info';
198
+ return 'stdout';
199
+ };
200
+ return response
201
+ .filter((event) => event.type === 'stdout' || event.type === 'stderr' || event.type === 'command')
202
+ .map((event) => ({
203
+ timestamp: new Date(event.created),
204
+ type: mapEventType(event.type),
205
+ message: event.text || event.payload?.text || '',
206
+ }));
207
+ }
208
+ }
@@ -0,0 +1,2 @@
1
+ export * from './deployment.js';
2
+ export * from './drivers/index.js';
@@ -0,0 +1,2 @@
1
+ export * from './deployment.js';
2
+ export * from './drivers/index.js';
@@ -0,0 +1,24 @@
1
+ import type { Credentials, Options, ProviderType } from '@directus/types';
2
+ import type { DeploymentDriver } from './deployment/deployment.js';
3
+ /**
4
+ * Register all deployment drivers
5
+ */
6
+ export declare function registerDeploymentDrivers(): void;
7
+ /**
8
+ * Get a deployment driver instance
9
+ *
10
+ * @param provider Provider name (vercel, netlify, aws, etc.)
11
+ * @param credentials Provider credentials (decrypted from DB)
12
+ * @param options Additional provider options
13
+ * @returns Deployment driver instance
14
+ * @throws Error if provider is not supported
15
+ */
16
+ export declare function getDeploymentDriver(provider: ProviderType, credentials: Credentials, options?: Options): DeploymentDriver;
17
+ /**
18
+ * Check if a provider is supported
19
+ */
20
+ export declare function isValidProviderType(provider: string): provider is ProviderType;
21
+ /**
22
+ * Get list of supported provider types
23
+ */
24
+ export declare function getSupportedProviderTypes(): ProviderType[];
@@ -0,0 +1,39 @@
1
+ import { VercelDriver } from './deployment/drivers/index.js';
2
+ /**
3
+ * Registry of deployment driver constructors
4
+ */
5
+ const drivers = new Map();
6
+ /**
7
+ * Register all deployment drivers
8
+ */
9
+ export function registerDeploymentDrivers() {
10
+ drivers.set('vercel', VercelDriver);
11
+ }
12
+ /**
13
+ * Get a deployment driver instance
14
+ *
15
+ * @param provider Provider name (vercel, netlify, aws, etc.)
16
+ * @param credentials Provider credentials (decrypted from DB)
17
+ * @param options Additional provider options
18
+ * @returns Deployment driver instance
19
+ * @throws Error if provider is not supported
20
+ */
21
+ export function getDeploymentDriver(provider, credentials, options) {
22
+ const Driver = drivers.get(provider);
23
+ if (!Driver) {
24
+ throw new Error(`Deployment driver "${provider}" is not supported`);
25
+ }
26
+ return new Driver(credentials, options);
27
+ }
28
+ /**
29
+ * Check if a provider is supported
30
+ */
31
+ export function isValidProviderType(provider) {
32
+ return drivers.has(provider);
33
+ }
34
+ /**
35
+ * Get list of supported provider types
36
+ */
37
+ export function getSupportedProviderTypes() {
38
+ return Array.from(drivers.keys());
39
+ }
@@ -1,5 +1,5 @@
1
- import { useEnv } from '@directus/env';
2
1
  import { join } from 'path';
2
+ import { useEnv } from '@directus/env';
3
3
  export const getExtensionsPath = () => {
4
4
  const env = useEnv();
5
5
  if (env['EXTENSIONS_LOCATION']) {
@@ -1,8 +1,8 @@
1
1
  import { randomUUID } from 'node:crypto';
2
+ import { list } from '@directus/extensions-registry';
2
3
  import getDatabase from '../../database/index.js';
3
4
  import { ExtensionsService } from '../../services/extensions.js';
4
5
  import { getSchema } from '../../utils/get-schema.js';
5
- import { list } from '@directus/extensions-registry';
6
6
  /**
7
7
  * Loads stored settings for all extensions. Creates empty new rows in extensions tables for
8
8
  * extensions that don't have settings yet, and remove any settings for extensions that are no
@@ -1,6 +1,6 @@
1
+ import { join } from 'node:path';
1
2
  import { useEnv } from '@directus/env';
2
3
  import { resolveFsExtensions, resolveModuleExtensions } from '@directus/extensions/node';
3
- import { join } from 'node:path';
4
4
  import { getExtensionsPath } from './get-extensions-path.js';
5
5
  export const getExtensions = async () => {
6
6
  const env = useEnv();
@@ -1,10 +1,10 @@
1
- import { useEnv } from '@directus/env';
2
- import { resolvePackage } from '@directus/utils/node';
3
- import { escapeRegExp } from 'lodash-es';
4
1
  import { readdir } from 'node:fs/promises';
5
2
  import { dirname } from 'node:path';
6
3
  import { fileURLToPath } from 'node:url';
7
4
  import path from 'path';
5
+ import { useEnv } from '@directus/env';
6
+ import { resolvePackage } from '@directus/utils/node';
7
+ import { escapeRegExp } from 'lodash-es';
8
8
  import { useLogger } from '../../logger/index.js';
9
9
  import { Url } from '../../utils/url.js';
10
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -1,13 +1,13 @@
1
+ import { mkdir, readFile, rm } from 'node:fs/promises';
2
+ import { Readable } from 'node:stream';
3
+ import { join } from 'path';
1
4
  import { useEnv } from '@directus/env';
2
5
  import { ErrorCode, isDirectusError, ServiceUnavailableError } from '@directus/errors';
3
6
  import { EXTENSION_PKG_KEY, ExtensionManifest } from '@directus/extensions';
4
7
  import { download } from '@directus/extensions-registry';
5
8
  import DriverLocal from '@directus/storage-driver-local';
6
9
  import { move, remove } from 'fs-extra';
7
- import { mkdir, readFile, rm } from 'node:fs/promises';
8
- import { Readable } from 'node:stream';
9
10
  import Queue from 'p-queue';
10
- import { join } from 'path';
11
11
  import { extract } from 'tar';
12
12
  import { useLogger } from '../../../logger/index.js';
13
13
  import { getStorage } from '../../../storage/index.js';
@@ -1,6 +1,6 @@
1
+ import type { IncomingHttpHeaders } from 'node:http';
1
2
  import type { Router } from 'express';
2
3
  import type { Reference } from 'isolated-vm';
3
- import type { IncomingHttpHeaders } from 'node:http';
4
4
  export declare function registerRouteGenerator(endpointName: string, endpointRouter: Router): {
5
5
  register: (path: Reference<string>, method: Reference<"GET" | "POST" | "PUT" | "PATCH" | "DELETE">, cb: Reference<(req: {
6
6
  url: string;
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Utility functions to write a `.status` file on the filesystem to indicate active synchronization
3
3
  */
4
+ import { rm, writeFile } from 'node:fs/promises';
4
5
  import { join } from 'node:path';
5
6
  import { exists } from 'fs-extra';
6
- import { writeFile, rm } from 'node:fs/promises';
7
7
  import { getExtensionsPath } from '../get-extensions-path.js';
8
8
  export const SyncStatus = {
9
9
  SYNCING: 'SYNCING',
@@ -1,19 +1,19 @@
1
- import Queue from 'p-queue';
2
- import mid from 'node-machine-id';
3
- import { useEnv } from '@directus/env';
4
1
  import { createWriteStream } from 'node:fs';
5
2
  import { mkdir, rm } from 'node:fs/promises';
6
- import { pipeline } from 'node:stream/promises';
7
3
  import { dirname, join, relative, resolve, sep } from 'node:path';
8
- import { getSyncPaths, compareFileMetadata } from './utils.js';
4
+ import { pipeline } from 'node:stream/promises';
5
+ import { useEnv } from '@directus/env';
6
+ import { normalizePath } from '@directus/utils';
7
+ import mid from 'node-machine-id';
8
+ import Queue from 'p-queue';
9
9
  import { useBus } from '../../../bus/index.js';
10
- import { SyncFileTracker } from './tracker.js';
11
10
  import { useLock } from '../../../lock/index.js';
12
11
  import { useLogger } from '../../../logger/index.js';
13
12
  import { getStorage } from '../../../storage/index.js';
14
13
  import { getExtensionsPath } from '../get-extensions-path.js';
15
14
  import { isSynchronizing, setSyncStatus, SyncStatus } from './status.js';
16
- import { normalizePath } from '@directus/utils';
15
+ import { SyncFileTracker } from './tracker.js';
16
+ import { compareFileMetadata, getSyncPaths } from './utils.js';
17
17
  export async function syncExtensions(options) {
18
18
  if (options?.skipSync === true)
19
19
  return;
@@ -1,8 +1,8 @@
1
- import { join, relative, resolve, sep } from 'node:path';
2
1
  import { stat } from 'node:fs/promises';
3
- import { getExtensionsPath } from '../get-extensions-path.js';
2
+ import { join, relative, resolve, sep } from 'node:path';
4
3
  import { useEnv } from '@directus/env';
5
4
  import { normalizePath } from '@directus/utils';
5
+ import { getExtensionsPath } from '../get-extensions-path.js';
6
6
  /**
7
7
  * Returns the directory depth of the provided path
8
8
  */
@@ -1,6 +1,6 @@
1
+ import type { ReadStream } from 'node:fs';
1
2
  import type { Extension, ExtensionManagerOptions } from '@directus/types';
2
3
  import { Router } from 'express';
3
- import type { ReadStream } from 'node:fs';
4
4
  import { type ExtensionSyncOptions } from './lib/sync/sync.js';
5
5
  export declare class ExtensionManager {
6
6
  private options;
@@ -1,7 +1,13 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import { dirname, join, relative, resolve, sep } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import path from 'path';
6
+ import { HYBRID_EXTENSION_TYPES } from '@directus/constants';
1
7
  import { useEnv } from '@directus/env';
2
8
  import { APP_SHARED_DEPS } from '@directus/extensions';
3
- import { HYBRID_EXTENSION_TYPES } from '@directus/constants';
4
9
  import { generateExtensionsEntrypoint } from '@directus/extensions/node';
10
+ import DriverLocal from '@directus/storage-driver-local';
5
11
  import { isTypeIn, toBoolean } from '@directus/utils';
6
12
  import { pathToRelativeUrl, processId } from '@directus/utils/node';
7
13
  import aliasDefault from '@rollup/plugin-alias';
@@ -11,11 +17,6 @@ import chokidar, { FSWatcher } from 'chokidar';
11
17
  import express, { Router } from 'express';
12
18
  import ivm from 'isolated-vm';
13
19
  import { clone, debounce, isPlainObject } from 'lodash-es';
14
- import { readFile, readdir } from 'node:fs/promises';
15
- import os from 'node:os';
16
- import { dirname, join, relative, resolve, sep } from 'node:path';
17
- import { fileURLToPath } from 'node:url';
18
- import path from 'path';
19
20
  import { rolldown } from 'rolldown';
20
21
  import { rollup } from 'rollup';
21
22
  import { useBus } from '../bus/index.js';
@@ -37,9 +38,8 @@ import { getSharedDepsMapping } from './lib/get-shared-deps-mapping.js';
37
38
  import { getInstallationManager } from './lib/installation/index.js';
38
39
  import { generateApiExtensionsSandboxEntrypoint } from './lib/sandbox/generate-api-extensions-sandbox-entrypoint.js';
39
40
  import { instantiateSandboxSdk } from './lib/sandbox/sdk/instantiate.js';
40
- import { wrapEmbeds } from './lib/wrap-embeds.js';
41
- import DriverLocal from '@directus/storage-driver-local';
42
41
  import { syncExtensions } from './lib/sync/sync.js';
42
+ import { wrapEmbeds } from './lib/wrap-embeds.js';
43
43
  // Workaround for https://github.com/rollup/plugins/issues/1329
44
44
  const virtual = virtualDefault;
45
45
  const alias = aliasDefault;
package/dist/flows.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Accountability, SchemaOverview, OperationHandler } from '@directus/types';
1
+ import type { Accountability, OperationHandler, SchemaOverview } from '@directus/types';
2
2
  export declare function getFlowManager(): FlowManager;
3
3
  declare class FlowManager {
4
4
  private isLoaded;
@@ -1,7 +1,7 @@
1
+ import { URL } from 'node:url';
1
2
  import { useEnv } from '@directus/env';
2
3
  import { REDACTED_TEXT, toArray, toBoolean } from '@directus/utils';
3
4
  import { merge } from 'lodash-es';
4
- import { URL } from 'node:url';
5
5
  import { pino } from 'pino';
6
6
  import { pinoHttp, stdSerializers } from 'pino-http';
7
7
  import { httpPrintFactory } from 'pino-http-print';
@@ -1,5 +1,5 @@
1
- import type { Bus } from '@directus/memory';
2
1
  import { Writable } from 'stream';
2
+ import type { Bus } from '@directus/memory';
3
3
  type PrettyType = 'basic' | 'http' | false;
4
4
  export declare class LogsStream extends Writable {
5
5
  messenger: Bus;
@@ -1,5 +1,5 @@
1
- import { nanoid } from 'nanoid';
2
1
  import { Writable } from 'stream';
2
+ import { nanoid } from 'nanoid';
3
3
  import { useBus } from '../bus/index.js';
4
4
  const nodeId = nanoid(8);
5
5
  export class LogsStream extends Writable {
package/dist/mailer.js CHANGED
@@ -1,8 +1,8 @@
1
+ import { createRequire } from 'node:module';
1
2
  import { useEnv } from '@directus/env';
2
3
  import nodemailer from 'nodemailer';
3
4
  import { useLogger } from './logger/index.js';
4
5
  import { getConfigFromEnv } from './utils/get-config-from-env.js';
5
- import { createRequire } from 'node:module';
6
6
  const require = createRequire(import.meta.url);
7
7
  let transporter;
8
8
  export default function getMailer() {