@directus/api 33.1.1 → 33.3.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.
- package/dist/controllers/assets.js +2 -1
- package/dist/database/helpers/fn/types.js +2 -1
- package/dist/deployment/drivers/index.d.ts +1 -0
- package/dist/deployment/drivers/index.js +1 -0
- package/dist/deployment/drivers/netlify.d.ts +33 -0
- package/dist/deployment/drivers/netlify.js +260 -0
- package/dist/deployment.js +2 -1
- package/dist/services/authentication.js +11 -0
- package/dist/services/graphql/resolvers/query.js +5 -4
- package/dist/services/graphql/utils/aggregate-query.js +3 -0
- package/dist/utils/transformations.js +13 -2
- package/package.json +22 -21
|
@@ -28,7 +28,8 @@ router.post('/folder/:pk', asyncHandler(async (req, res) => {
|
|
|
28
28
|
});
|
|
29
29
|
const { archive, complete, metadata } = await service.zipFolder(req.params['pk']);
|
|
30
30
|
res.setHeader('Content-Type', 'application/zip');
|
|
31
|
-
|
|
31
|
+
const folderName = `folder-${metadata['name'] ? metadata['name'] : 'unknown'}-${getDateTimeFormatted()}.zip`;
|
|
32
|
+
res.setHeader('Content-Disposition', contentDisposition(folderName, { type: 'attachment' }));
|
|
32
33
|
archive.pipe(res);
|
|
33
34
|
await complete();
|
|
34
35
|
}));
|
|
@@ -31,6 +31,7 @@ export class FnHelper extends DatabaseHelper {
|
|
|
31
31
|
};
|
|
32
32
|
countQuery = applyFilter(this.knex, this.schema, countQuery, options.relationalCountOptions.query.filter, relation.collection, aliasMap, options.relationalCountOptions.cases, options.relationalCountOptions.permissions).query;
|
|
33
33
|
}
|
|
34
|
-
|
|
34
|
+
const { sql, bindings } = countQuery.toSQL();
|
|
35
|
+
return this.knex.raw(`(${sql})`, bindings);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Credentials, Deployment, Details, Log, Options, Project, TriggerResult } from '@directus/types';
|
|
2
|
+
import { DeploymentDriver } from '../deployment.js';
|
|
3
|
+
export interface NetlifyCredentials extends Credentials {
|
|
4
|
+
access_token: string;
|
|
5
|
+
}
|
|
6
|
+
export interface NetlifyOptions extends Options {
|
|
7
|
+
account_slug?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class NetlifyDriver extends DeploymentDriver<NetlifyCredentials, NetlifyOptions> {
|
|
10
|
+
private api;
|
|
11
|
+
constructor(credentials: NetlifyCredentials, options?: NetlifyOptions);
|
|
12
|
+
private handleApiError;
|
|
13
|
+
private mapStatus;
|
|
14
|
+
testConnection(): Promise<void>;
|
|
15
|
+
private mapSiteBase;
|
|
16
|
+
listProjects(): Promise<Project[]>;
|
|
17
|
+
getProject(projectId: string): Promise<Project>;
|
|
18
|
+
private mapDeployUrl;
|
|
19
|
+
listDeployments(projectId: string, limit?: number): Promise<Deployment[]>;
|
|
20
|
+
getDeployment(deploymentId: string): Promise<Details>;
|
|
21
|
+
triggerDeployment(projectId: string, options?: {
|
|
22
|
+
preview?: boolean;
|
|
23
|
+
clearCache?: boolean;
|
|
24
|
+
}): Promise<TriggerResult>;
|
|
25
|
+
cancelDeployment(deploymentId: string): Promise<void>;
|
|
26
|
+
private closeWsConnection;
|
|
27
|
+
private setupWsIdleTimeout;
|
|
28
|
+
private setupWsConnectionTimeout;
|
|
29
|
+
private getWsConnection;
|
|
30
|
+
getDeploymentLogs(deploymentId: string, options?: {
|
|
31
|
+
since?: Date;
|
|
32
|
+
}): Promise<Log[]>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { InvalidCredentialsError, ServiceUnavailableError } from '@directus/errors';
|
|
2
|
+
import { NetlifyAPI } from '@netlify/api';
|
|
3
|
+
import { isNumber } from 'lodash-es';
|
|
4
|
+
import { DeploymentDriver } from '../deployment.js';
|
|
5
|
+
const WS_CONNECTIONS = new Map();
|
|
6
|
+
const WS_IDLE_TIMEOUT = 60_000; // 60 seconds
|
|
7
|
+
const WS_CONNECTION_TIMEOUT = 10_000; // 10 seconds
|
|
8
|
+
// eslint-disable-next-line no-control-regex
|
|
9
|
+
const ANSI_REGEX = /[\x1b]\[[0-9;]*m/g;
|
|
10
|
+
const WS_URL = 'wss://socketeer.services.netlify.com/build/logs';
|
|
11
|
+
export class NetlifyDriver extends DeploymentDriver {
|
|
12
|
+
api;
|
|
13
|
+
constructor(credentials, options = {}) {
|
|
14
|
+
super(credentials, options);
|
|
15
|
+
this.api = new NetlifyAPI(this.credentials.access_token);
|
|
16
|
+
}
|
|
17
|
+
async handleApiError(cb) {
|
|
18
|
+
try {
|
|
19
|
+
return await cb(this.api);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error instanceof Error && 'status' in error && isNumber(error.status) && error.status >= 400) {
|
|
23
|
+
if (error.status === 401 || error.status === 403) {
|
|
24
|
+
throw new InvalidCredentialsError();
|
|
25
|
+
}
|
|
26
|
+
throw new ServiceUnavailableError({ service: 'netlify', reason: 'Netlify API error: ' + error.message });
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
mapStatus(netlifyState) {
|
|
32
|
+
const normalized = netlifyState?.toLowerCase();
|
|
33
|
+
switch (normalized) {
|
|
34
|
+
case 'ready':
|
|
35
|
+
return 'ready';
|
|
36
|
+
case 'error':
|
|
37
|
+
return 'error';
|
|
38
|
+
case 'canceled':
|
|
39
|
+
return 'canceled';
|
|
40
|
+
default:
|
|
41
|
+
return 'building';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async testConnection() {
|
|
45
|
+
await this.handleApiError((api) => api.listSites({ per_page: 1 }));
|
|
46
|
+
}
|
|
47
|
+
mapSiteBase(site) {
|
|
48
|
+
const result = {
|
|
49
|
+
id: site.id,
|
|
50
|
+
name: site.name,
|
|
51
|
+
deployable: Boolean(site.build_settings?.provider && site.build_settings?.repo_url),
|
|
52
|
+
};
|
|
53
|
+
// Use custom domain if available, otherwise ssl_url or url
|
|
54
|
+
if (site.custom_domain) {
|
|
55
|
+
result.url = `https://${site.custom_domain}`;
|
|
56
|
+
}
|
|
57
|
+
else if (site.ssl_url) {
|
|
58
|
+
result.url = site.ssl_url;
|
|
59
|
+
}
|
|
60
|
+
else if (site.url) {
|
|
61
|
+
result.url = site.url;
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
async listProjects() {
|
|
66
|
+
const params = { per_page: '100' };
|
|
67
|
+
const response = await this.handleApiError((api) => {
|
|
68
|
+
return this.options.account_slug
|
|
69
|
+
? api.listSitesForAccount({
|
|
70
|
+
account_slug: this.options.account_slug,
|
|
71
|
+
...params,
|
|
72
|
+
})
|
|
73
|
+
: api.listSites(params);
|
|
74
|
+
});
|
|
75
|
+
return response.map((site) => this.mapSiteBase(site));
|
|
76
|
+
}
|
|
77
|
+
async getProject(projectId) {
|
|
78
|
+
const site = await this.handleApiError((api) => api.getSite({ siteId: projectId }));
|
|
79
|
+
const result = this.mapSiteBase(site);
|
|
80
|
+
// Add published deploy info if available
|
|
81
|
+
if (site.published_deploy) {
|
|
82
|
+
const deploy = site.published_deploy;
|
|
83
|
+
if (deploy.state && deploy.created_at) {
|
|
84
|
+
result.latest_deployment = {
|
|
85
|
+
status: this.mapStatus(deploy.state),
|
|
86
|
+
created_at: new Date(deploy.created_at),
|
|
87
|
+
...(deploy.published_at && { finished_at: new Date(deploy.published_at) }),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (site.created_at) {
|
|
92
|
+
result.created_at = new Date(site.created_at);
|
|
93
|
+
}
|
|
94
|
+
if (site.updated_at) {
|
|
95
|
+
result.updated_at = new Date(site.updated_at);
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
mapDeployUrl(deploy) {
|
|
100
|
+
return deploy['ssl_url'] ?? deploy['deploy_ssl_url'] ?? deploy['deploy_url'] ?? deploy['url'];
|
|
101
|
+
}
|
|
102
|
+
async listDeployments(projectId, limit = 20) {
|
|
103
|
+
const response = await this.handleApiError((api) => api.listSiteDeploys({ site_id: projectId, per_page: limit }));
|
|
104
|
+
return response.map((deploy) => {
|
|
105
|
+
const result = {
|
|
106
|
+
id: deploy.id,
|
|
107
|
+
project_id: deploy.site_id,
|
|
108
|
+
status: this.mapStatus(deploy.state),
|
|
109
|
+
created_at: new Date(deploy.created_at),
|
|
110
|
+
};
|
|
111
|
+
const url = this.mapDeployUrl(deploy);
|
|
112
|
+
if (url)
|
|
113
|
+
result.url = url;
|
|
114
|
+
if (deploy.published_at) {
|
|
115
|
+
result.finished_at = new Date(deploy.published_at);
|
|
116
|
+
}
|
|
117
|
+
if (deploy.error_message) {
|
|
118
|
+
result.error_message = deploy.error_message;
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async getDeployment(deploymentId) {
|
|
124
|
+
const deploy = await this.handleApiError((api) => api.getDeploy({ deployId: deploymentId }));
|
|
125
|
+
const result = {
|
|
126
|
+
id: deploy.id,
|
|
127
|
+
project_id: deploy.site_id,
|
|
128
|
+
status: this.mapStatus(deploy.state),
|
|
129
|
+
created_at: new Date(deploy.created_at),
|
|
130
|
+
};
|
|
131
|
+
const url = this.mapDeployUrl(deploy);
|
|
132
|
+
if (url)
|
|
133
|
+
result.url = url;
|
|
134
|
+
if (deploy.published_at) {
|
|
135
|
+
result.finished_at = new Date(deploy.published_at);
|
|
136
|
+
}
|
|
137
|
+
if (deploy.error_message) {
|
|
138
|
+
result.error_message = deploy.error_message;
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
async triggerDeployment(projectId, options) {
|
|
143
|
+
// Netlify builds endpoint returns a Build object with deploy_id and deploy_state
|
|
144
|
+
const buildResponse = await this.handleApiError((api) => api.createSiteBuild({
|
|
145
|
+
site_id: projectId,
|
|
146
|
+
clear_cache: options?.clearCache || false,
|
|
147
|
+
}));
|
|
148
|
+
const deployState = await this.handleApiError((api) => api.getDeploy({ deployId: buildResponse.deploy_id }));
|
|
149
|
+
const triggerResult = {
|
|
150
|
+
deployment_id: buildResponse.deploy_id,
|
|
151
|
+
status: this.mapStatus(deployState.state),
|
|
152
|
+
};
|
|
153
|
+
return triggerResult;
|
|
154
|
+
}
|
|
155
|
+
async cancelDeployment(deploymentId) {
|
|
156
|
+
await this.handleApiError((api) => api.cancelSiteDeploy({ deployId: deploymentId }));
|
|
157
|
+
this.closeWsConnection(deploymentId);
|
|
158
|
+
}
|
|
159
|
+
closeWsConnection(deploymentId, remove = true) {
|
|
160
|
+
const connection = WS_CONNECTIONS.get(deploymentId);
|
|
161
|
+
if (!connection)
|
|
162
|
+
return;
|
|
163
|
+
connection.ws.close();
|
|
164
|
+
if (remove) {
|
|
165
|
+
WS_CONNECTIONS.delete(deploymentId);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
setupWsIdleTimeout(connection) {
|
|
169
|
+
if (connection.idleTimeout) {
|
|
170
|
+
clearTimeout(connection.idleTimeout);
|
|
171
|
+
}
|
|
172
|
+
connection.idleTimeout = setTimeout(() => {
|
|
173
|
+
this.closeWsConnection(connection.deploymentId);
|
|
174
|
+
}, WS_IDLE_TIMEOUT);
|
|
175
|
+
}
|
|
176
|
+
setupWsConnectionTimeout(connection, reject) {
|
|
177
|
+
if (connection.connectionTimeout) {
|
|
178
|
+
clearTimeout(connection.connectionTimeout);
|
|
179
|
+
}
|
|
180
|
+
connection.connectionTimeout = setTimeout(() => {
|
|
181
|
+
this.closeWsConnection(connection.deploymentId);
|
|
182
|
+
reject(new ServiceUnavailableError({ service: 'netlify', reason: 'WebSocket connection timeout' }));
|
|
183
|
+
}, WS_CONNECTION_TIMEOUT);
|
|
184
|
+
}
|
|
185
|
+
getWsConnection(deploymentId) {
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
const existingConnection = WS_CONNECTIONS.get(deploymentId);
|
|
188
|
+
if (existingConnection) {
|
|
189
|
+
this.setupWsIdleTimeout(existingConnection);
|
|
190
|
+
return resolve(existingConnection);
|
|
191
|
+
}
|
|
192
|
+
let resolveCompleted;
|
|
193
|
+
const completed = new Promise((res) => {
|
|
194
|
+
resolveCompleted = res;
|
|
195
|
+
});
|
|
196
|
+
const connection = {
|
|
197
|
+
ws: new WebSocket(WS_URL),
|
|
198
|
+
logs: [],
|
|
199
|
+
deploymentId,
|
|
200
|
+
completed,
|
|
201
|
+
resolveCompleted: resolveCompleted,
|
|
202
|
+
};
|
|
203
|
+
this.setupWsConnectionTimeout(connection, reject);
|
|
204
|
+
connection.ws.addEventListener('open', () => {
|
|
205
|
+
if (connection.connectionTimeout) {
|
|
206
|
+
clearTimeout(connection.connectionTimeout);
|
|
207
|
+
connection.connectionTimeout = undefined;
|
|
208
|
+
}
|
|
209
|
+
this.setupWsIdleTimeout(connection);
|
|
210
|
+
const payload = JSON.stringify({
|
|
211
|
+
deploy_id: deploymentId,
|
|
212
|
+
access_token: this.credentials.access_token,
|
|
213
|
+
});
|
|
214
|
+
connection.ws.send(payload);
|
|
215
|
+
resolve(connection);
|
|
216
|
+
WS_CONNECTIONS.set(deploymentId, connection);
|
|
217
|
+
});
|
|
218
|
+
connection.ws.addEventListener('message', (event) => {
|
|
219
|
+
const data = JSON.parse(event.data);
|
|
220
|
+
const cleanMessage = data.message.replace(/\r/g, '').replace(ANSI_REGEX, '');
|
|
221
|
+
let logType = 'stdout';
|
|
222
|
+
if (data.type === 'report') {
|
|
223
|
+
logType = cleanMessage.includes('Failing build') ? 'stderr' : 'info';
|
|
224
|
+
}
|
|
225
|
+
connection.logs.push({
|
|
226
|
+
timestamp: new Date(data.ts),
|
|
227
|
+
type: logType,
|
|
228
|
+
message: cleanMessage,
|
|
229
|
+
});
|
|
230
|
+
// If we receive a "report" type message, the build is complete.
|
|
231
|
+
// Close the WebSocket connection but don't yet remove the logs, allowing the client to fetch them until the idle timeout expires.
|
|
232
|
+
if (data.type === 'report') {
|
|
233
|
+
connection.resolveCompleted();
|
|
234
|
+
this.closeWsConnection(deploymentId, false);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
connection.ws.addEventListener('error', () => {
|
|
238
|
+
this.closeWsConnection(deploymentId);
|
|
239
|
+
reject(new ServiceUnavailableError({ service: 'netlify', reason: 'WebSocket connection error' }));
|
|
240
|
+
});
|
|
241
|
+
connection.ws.addEventListener('close', () => {
|
|
242
|
+
if (connection.connectionTimeout) {
|
|
243
|
+
clearTimeout(connection.connectionTimeout);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
async getDeploymentLogs(deploymentId, options) {
|
|
249
|
+
const deploy = await this.handleApiError((api) => api.getDeploy({ deployId: deploymentId }));
|
|
250
|
+
const connection = await this.getWsConnection(deploymentId);
|
|
251
|
+
// Build already finished — WS is replaying logs, wait for all of them
|
|
252
|
+
if (this.mapStatus(deploy.state) !== 'building') {
|
|
253
|
+
await connection.completed;
|
|
254
|
+
}
|
|
255
|
+
if (options?.since) {
|
|
256
|
+
return connection.logs.filter((log) => log.timestamp >= options.since);
|
|
257
|
+
}
|
|
258
|
+
return connection.logs;
|
|
259
|
+
}
|
|
260
|
+
}
|
package/dist/deployment.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VercelDriver } from './deployment/drivers/index.js';
|
|
1
|
+
import { NetlifyDriver, VercelDriver } from './deployment/drivers/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Registry of deployment driver constructors
|
|
4
4
|
*/
|
|
@@ -8,6 +8,7 @@ const drivers = new Map();
|
|
|
8
8
|
*/
|
|
9
9
|
export function registerDeploymentDrivers() {
|
|
10
10
|
drivers.set('vercel', VercelDriver);
|
|
11
|
+
drivers.set('netlify', NetlifyDriver);
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* Get a deployment driver instance
|
|
@@ -411,6 +411,17 @@ export class AuthenticationService {
|
|
|
411
411
|
const user = record;
|
|
412
412
|
const provider = getAuthProvider(user.provider);
|
|
413
413
|
await provider.logout(clone(user));
|
|
414
|
+
if (this.accountability) {
|
|
415
|
+
await this.activityService.createOne({
|
|
416
|
+
action: Action.LOGOUT,
|
|
417
|
+
user: user.id,
|
|
418
|
+
ip: this.accountability.ip,
|
|
419
|
+
user_agent: this.accountability.userAgent,
|
|
420
|
+
origin: this.accountability.origin,
|
|
421
|
+
collection: 'directus_users',
|
|
422
|
+
item: user.id,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
414
425
|
await this.knex.delete().from('directus_sessions').where('token', refreshToken);
|
|
415
426
|
}
|
|
416
427
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { parseFilterFunctionPath } from '@directus/utils';
|
|
2
|
-
import { omit } from 'lodash-es';
|
|
3
2
|
import { parseArgs } from '../schema/parse-args.js';
|
|
4
3
|
import { getQuery } from '../schema/parse-query.js';
|
|
5
4
|
import { getAggregateQuery } from '../utils/aggregate-query.js';
|
|
@@ -42,10 +41,12 @@ export async function resolveQuery(gql, info) {
|
|
|
42
41
|
if (args['id'])
|
|
43
42
|
return result;
|
|
44
43
|
if (query.group) {
|
|
45
|
-
// for every entry in result add a group field based on query.group;
|
|
46
|
-
const aggregateKeys = Object.keys(query.aggregate ?? {});
|
|
47
44
|
result['map']((field) => {
|
|
48
|
-
|
|
45
|
+
const groupValues = {};
|
|
46
|
+
for (const key of query.group) {
|
|
47
|
+
groupValues[key] = field[key];
|
|
48
|
+
}
|
|
49
|
+
field['group'] = groupValues;
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
return result;
|
|
@@ -15,6 +15,9 @@ export async function getAggregateQuery(rawQuery, selections, schema, accountabi
|
|
|
15
15
|
// filter out graphql pointers, like __typename
|
|
16
16
|
if (aggregationGroup.name.value.startsWith('__'))
|
|
17
17
|
continue;
|
|
18
|
+
// skip the 'group' field — it holds grouped values, not an aggregate function
|
|
19
|
+
if (aggregationGroup.name.value === 'group')
|
|
20
|
+
continue;
|
|
18
21
|
const aggregateProperty = aggregationGroup.name.value;
|
|
19
22
|
query.aggregate[aggregateProperty] =
|
|
20
23
|
aggregationGroup.selectionSet?.selections
|
|
@@ -11,8 +11,19 @@ export function resolvePreset({ transformationParams, acceptFormat }, file) {
|
|
|
11
11
|
]);
|
|
12
12
|
}
|
|
13
13
|
if ((transformationParams.width || transformationParams.height) && file.width && file.height) {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
let toWidth = transformationParams.width ? Number(transformationParams.width) : undefined;
|
|
15
|
+
let toHeight = transformationParams.height ? Number(transformationParams.height) : undefined;
|
|
16
|
+
/*
|
|
17
|
+
* When withoutEnlargement is true, clamp target dimensions to original dimensions to prevent "bad extract area" errors when using focal points.
|
|
18
|
+
*/
|
|
19
|
+
if (transformationParams.withoutEnlargement) {
|
|
20
|
+
if (toWidth !== undefined) {
|
|
21
|
+
toWidth = Math.min(toWidth, file.width);
|
|
22
|
+
}
|
|
23
|
+
if (toHeight !== undefined) {
|
|
24
|
+
toHeight = Math.min(toHeight, file.height);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
16
27
|
const toFocalPointX = transformationParams.focal_point_x
|
|
17
28
|
? Number(transformationParams.focal_point_x)
|
|
18
29
|
: file.focal_point_x;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "33.
|
|
3
|
+
"version": "33.3.0",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"@aws-sdk/client-sesv2": "3.928.0",
|
|
67
67
|
"@godaddy/terminus": "4.12.1",
|
|
68
68
|
"@modelcontextprotocol/sdk": "1.26.0",
|
|
69
|
+
"@netlify/api": "14.0.14",
|
|
69
70
|
"@rollup/plugin-alias": "5.1.1",
|
|
70
71
|
"@rollup/plugin-node-resolve": "16.0.3",
|
|
71
72
|
"@rollup/plugin-virtual": "3.0.2",
|
|
@@ -161,30 +162,30 @@
|
|
|
161
162
|
"zod": "4.1.12",
|
|
162
163
|
"zod-validation-error": "4.0.2",
|
|
163
164
|
"@directus/ai": "1.1.0",
|
|
164
|
-
"@directus/
|
|
165
|
-
"@directus/
|
|
166
|
-
"@directus/env": "5.5.1",
|
|
167
|
-
"@directus/extensions": "3.0.17",
|
|
165
|
+
"@directus/constants": "14.1.0",
|
|
166
|
+
"@directus/env": "5.5.3",
|
|
168
167
|
"@directus/errors": "2.2.0",
|
|
169
|
-
"@directus/extensions-
|
|
170
|
-
"@directus/extensions
|
|
171
|
-
"@directus/
|
|
168
|
+
"@directus/extensions-registry": "3.0.19",
|
|
169
|
+
"@directus/extensions": "3.0.19",
|
|
170
|
+
"@directus/extensions-sdk": "17.0.9",
|
|
171
|
+
"@directus/memory": "3.1.2",
|
|
172
172
|
"@directus/format-title": "12.1.1",
|
|
173
|
-
"@directus/pressure": "3.0.
|
|
173
|
+
"@directus/pressure": "3.0.17",
|
|
174
174
|
"@directus/schema": "13.0.5",
|
|
175
|
-
"@directus/schema-builder": "0.0.12",
|
|
176
|
-
"@directus/specs": "12.0.0",
|
|
177
175
|
"@directus/storage": "12.0.3",
|
|
178
|
-
"@directus/
|
|
179
|
-
"@directus/
|
|
176
|
+
"@directus/schema-builder": "0.0.14",
|
|
177
|
+
"@directus/app": "15.3.0",
|
|
178
|
+
"@directus/specs": "12.0.0",
|
|
179
|
+
"@directus/storage-driver-azure": "12.0.17",
|
|
180
|
+
"@directus/storage-driver-gcs": "12.0.17",
|
|
180
181
|
"@directus/storage-driver-local": "12.0.3",
|
|
181
|
-
"@directus/storage-driver-s3": "12.1.
|
|
182
|
-
"@directus/storage-driver-
|
|
183
|
-
"@directus/storage-driver-
|
|
184
|
-
"@directus/utils": "13.2.
|
|
182
|
+
"@directus/storage-driver-s3": "12.1.3",
|
|
183
|
+
"@directus/storage-driver-cloudinary": "12.0.17",
|
|
184
|
+
"@directus/storage-driver-supabase": "3.0.17",
|
|
185
|
+
"@directus/utils": "13.2.2",
|
|
185
186
|
"@directus/system-data": "4.1.0",
|
|
186
|
-
"@directus/validation": "2.0.
|
|
187
|
-
"directus": "11.15.
|
|
187
|
+
"@directus/validation": "2.0.17",
|
|
188
|
+
"directus": "11.15.3"
|
|
188
189
|
},
|
|
189
190
|
"devDependencies": {
|
|
190
191
|
"@directus/tsconfig": "3.0.0",
|
|
@@ -227,8 +228,8 @@
|
|
|
227
228
|
"knex-mock-client": "3.0.2",
|
|
228
229
|
"typescript": "5.9.3",
|
|
229
230
|
"vitest": "3.2.4",
|
|
230
|
-
"@directus/
|
|
231
|
-
"@directus/
|
|
231
|
+
"@directus/types": "14.2.1",
|
|
232
|
+
"@directus/schema-builder": "0.0.14"
|
|
232
233
|
},
|
|
233
234
|
"optionalDependencies": {
|
|
234
235
|
"@keyv/redis": "3.0.1",
|