@directus/api 33.1.0 → 33.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.
- package/dist/ai/chat/utils/format-context.js +5 -0
- package/dist/app.js +4 -1
- 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/utils/sanitize-query.js +7 -2
- package/dist/utils/transformations.js +13 -2
- package/package.json +21 -20
|
@@ -25,6 +25,7 @@ function formatVisualElement(att) {
|
|
|
25
25
|
const display = escapeAngleBrackets(att.display);
|
|
26
26
|
return `### ${collection}/${item} — "${display}"
|
|
27
27
|
Editable fields: ${fields}
|
|
28
|
+
To update: items tool with collection="${collection}", keys=["${item}"], action="update"
|
|
28
29
|
\`\`\`json
|
|
29
30
|
${escapeAngleBrackets(JSON.stringify(att.snapshot, null, 2))}
|
|
30
31
|
\`\`\``;
|
|
@@ -106,6 +107,10 @@ ${itemLines}`);
|
|
|
106
107
|
## Selected Elements
|
|
107
108
|
The user selected these elements for editing in the visual editor.
|
|
108
109
|
|
|
110
|
+
IMPORTANT: For visual editor elements, ALWAYS use the items tool:
|
|
111
|
+
- To UPDATE: items tool with action: 'update', collection, keys, and data
|
|
112
|
+
- NEVER use form-values tools for visual editor elements
|
|
113
|
+
|
|
109
114
|
${elementLines}
|
|
110
115
|
</visual_editing>`);
|
|
111
116
|
}
|
package/dist/app.js
CHANGED
|
@@ -103,7 +103,10 @@ export default async function createApp() {
|
|
|
103
103
|
const app = express();
|
|
104
104
|
app.disable('x-powered-by');
|
|
105
105
|
app.set('trust proxy', env['IP_TRUST_PROXY']);
|
|
106
|
-
app.set('query parser', (str) => qs.parse(str, {
|
|
106
|
+
app.set('query parser', (str) => qs.parse(str, {
|
|
107
|
+
depth: Number(env['QUERYSTRING_MAX_PARSE_DEPTH']),
|
|
108
|
+
arrayLimit: Number(env['QUERYSTRING_ARRAY_LIMIT']),
|
|
109
|
+
}));
|
|
107
110
|
if (env['PRESSURE_LIMITER_ENABLED']) {
|
|
108
111
|
const sampleInterval = Number(env['PRESSURE_LIMITER_SAMPLE_INTERVAL']);
|
|
109
112
|
if (Number.isNaN(sampleInterval) === true || Number.isFinite(sampleInterval) === false) {
|
|
@@ -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
|
|
@@ -80,10 +80,15 @@ function sanitizeFields(rawFields) {
|
|
|
80
80
|
if (!rawFields)
|
|
81
81
|
return null;
|
|
82
82
|
let fields = [];
|
|
83
|
-
if (typeof rawFields === 'string')
|
|
83
|
+
if (typeof rawFields === 'string') {
|
|
84
84
|
fields = rawFields.split(',');
|
|
85
|
-
|
|
85
|
+
}
|
|
86
|
+
else if (Array.isArray(rawFields)) {
|
|
86
87
|
fields = rawFields;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
throw new InvalidQueryError({ reason: '"fields" must be a string or array' });
|
|
91
|
+
}
|
|
87
92
|
// Case where array item includes CSV (fe fields[]=id,name):
|
|
88
93
|
fields = flatten(fields.map((field) => (field.includes(',') ? field.split(',') : field)));
|
|
89
94
|
fields = fields.map((field) => field.trim());
|
|
@@ -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.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",
|
|
@@ -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/extensions": "3.0.17",
|
|
167
|
-
"@directus/constants": "14.0.0",
|
|
165
|
+
"@directus/env": "5.5.2",
|
|
166
|
+
"@directus/app": "15.2.0",
|
|
168
167
|
"@directus/errors": "2.2.0",
|
|
169
|
-
"@directus/extensions
|
|
170
|
-
"@directus/
|
|
168
|
+
"@directus/extensions": "3.0.18",
|
|
169
|
+
"@directus/extensions-registry": "3.0.18",
|
|
170
|
+
"@directus/extensions-sdk": "17.0.8",
|
|
171
171
|
"@directus/format-title": "12.1.1",
|
|
172
|
-
"@directus/
|
|
172
|
+
"@directus/memory": "3.1.1",
|
|
173
|
+
"@directus/pressure": "3.0.16",
|
|
173
174
|
"@directus/schema": "13.0.5",
|
|
174
|
-
"@directus/
|
|
175
|
-
"@directus/schema-builder": "0.0.12",
|
|
175
|
+
"@directus/schema-builder": "0.0.13",
|
|
176
176
|
"@directus/specs": "12.0.0",
|
|
177
177
|
"@directus/storage": "12.0.3",
|
|
178
|
-
"@directus/storage-driver-azure": "12.0.
|
|
179
|
-
"@directus/storage-driver-
|
|
180
|
-
"@directus/storage-driver-gcs": "12.0.15",
|
|
178
|
+
"@directus/storage-driver-azure": "12.0.16",
|
|
179
|
+
"@directus/storage-driver-cloudinary": "12.0.16",
|
|
181
180
|
"@directus/storage-driver-local": "12.0.3",
|
|
182
|
-
"@directus/
|
|
181
|
+
"@directus/constants": "14.0.0",
|
|
182
|
+
"@directus/storage-driver-gcs": "12.0.16",
|
|
183
|
+
"@directus/storage-driver-supabase": "3.0.16",
|
|
183
184
|
"@directus/system-data": "4.1.0",
|
|
184
|
-
"@directus/utils": "13.2.
|
|
185
|
-
"directus": "
|
|
186
|
-
"@directus/validation": "2.0.
|
|
187
|
-
"
|
|
185
|
+
"@directus/utils": "13.2.1",
|
|
186
|
+
"@directus/storage-driver-s3": "12.1.2",
|
|
187
|
+
"@directus/validation": "2.0.16",
|
|
188
|
+
"directus": "11.15.2"
|
|
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/schema-builder": "0.0.
|
|
231
|
-
"@directus/types": "14.
|
|
231
|
+
"@directus/schema-builder": "0.0.13",
|
|
232
|
+
"@directus/types": "14.2.0"
|
|
232
233
|
},
|
|
233
234
|
"optionalDependencies": {
|
|
234
235
|
"@keyv/redis": "3.0.1",
|