@directus/api 27.0.2 → 28.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/drivers/oauth2.js +4 -4
- package/dist/auth/drivers/openid.d.ts +2 -1
- package/dist/auth/drivers/openid.js +78 -43
- package/dist/database/errors/dialects/mssql.d.ts +2 -1
- package/dist/database/errors/dialects/mssql.js +124 -120
- package/dist/database/errors/dialects/mysql.d.ts +2 -1
- package/dist/database/errors/dialects/mysql.js +112 -108
- package/dist/database/errors/dialects/postgres.d.ts +2 -1
- package/dist/database/errors/dialects/postgres.js +75 -71
- package/dist/database/errors/dialects/sqlite.d.ts +2 -1
- package/dist/database/errors/dialects/sqlite.js +6 -5
- package/dist/database/errors/translate.d.ts +2 -1
- package/dist/database/errors/translate.js +5 -5
- package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +5 -3
- package/dist/database/get-ast-from-query/lib/convert-wildcards.js +26 -16
- package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +2 -1
- package/dist/database/get-ast-from-query/lib/parse-fields.js +5 -4
- package/dist/database/migrations/20250609A-license-banner.d.ts +3 -0
- package/dist/database/migrations/20250609A-license-banner.js +14 -0
- package/dist/database/migrations/20250613A-add-project-id.d.ts +3 -0
- package/dist/database/migrations/20250613A-add-project-id.js +26 -0
- package/dist/extensions/lib/get-extensions-settings.js +14 -8
- package/dist/extensions/manager.js +26 -0
- package/dist/flows.d.ts +5 -1
- package/dist/flows.js +61 -4
- package/dist/operations/condition/index.js +1 -1
- package/dist/permissions/utils/get-permissions-for-share.js +2 -0
- package/dist/permissions/utils/merge-fields.d.ts +1 -0
- package/dist/permissions/utils/merge-fields.js +29 -0
- package/dist/permissions/utils/merge-permissions.js +3 -14
- package/dist/services/fields.js +3 -3
- package/dist/services/graphql/resolvers/mutation.js +1 -1
- package/dist/services/graphql/resolvers/query.js +1 -1
- package/dist/services/graphql/resolvers/system.js +4 -4
- package/dist/services/graphql/schema/parse-query.d.ts +1 -1
- package/dist/services/graphql/schema/parse-query.js +8 -1
- package/dist/services/graphql/schema/read.js +4 -4
- package/dist/services/graphql/subscription.js +1 -1
- package/dist/services/graphql/utils/filter-replace-m2a.d.ts +3 -0
- package/dist/services/graphql/utils/filter-replace-m2a.js +59 -0
- package/dist/services/items.js +2 -2
- package/dist/services/payload.js +2 -2
- package/dist/services/relations.d.ts +1 -1
- package/dist/services/relations.js +5 -2
- package/dist/services/specifications.js +6 -2
- package/dist/services/users.js +3 -0
- package/dist/telemetry/lib/get-report.js +4 -1
- package/dist/telemetry/types/report.d.ts +4 -0
- package/dist/telemetry/utils/get-project-id.d.ts +2 -0
- package/dist/telemetry/utils/get-project-id.js +4 -0
- package/dist/utils/get-ip-from-req.d.ts +2 -1
- package/dist/utils/get-ip-from-req.js +29 -2
- package/dist/utils/get-schema.js +1 -1
- package/dist/utils/is-url-allowed.js +1 -1
- package/dist/utils/sanitize-query.js +6 -0
- package/dist/utils/validate-query.js +1 -0
- package/dist/websocket/controllers/base.d.ts +2 -2
- package/dist/websocket/controllers/base.js +33 -5
- package/dist/websocket/types.d.ts +1 -0
- package/dist/websocket/utils/items.d.ts +1 -1
- package/package.json +27 -24
- package/dist/utils/map-values-deep.d.ts +0 -1
- package/dist/utils/map-values-deep.js +0 -25
|
@@ -58,7 +58,7 @@ export class RelationsService {
|
|
|
58
58
|
}
|
|
59
59
|
return foreignKeys;
|
|
60
60
|
}
|
|
61
|
-
async readAll(collection, opts) {
|
|
61
|
+
async readAll(collection, opts, bypassCache) {
|
|
62
62
|
if (this.accountability) {
|
|
63
63
|
await validateAccess({
|
|
64
64
|
accountability: this.accountability,
|
|
@@ -87,7 +87,10 @@ export class RelationsService {
|
|
|
87
87
|
return true;
|
|
88
88
|
return metaRow.many_collection === collection;
|
|
89
89
|
});
|
|
90
|
-
|
|
90
|
+
let schemaRows = bypassCache ? await this.schemaInspector.foreignKeys() : await this.foreignKeys(collection);
|
|
91
|
+
if (collection && bypassCache) {
|
|
92
|
+
schemaRows = schemaRows.filter((row) => row.table === collection);
|
|
93
|
+
}
|
|
91
94
|
const results = this.stitchRelations(metaRows, schemaRows);
|
|
92
95
|
return await this.filterForbidden(results);
|
|
93
96
|
}
|
|
@@ -3,8 +3,8 @@ import formatTitle from '@directus/format-title';
|
|
|
3
3
|
import { spec } from '@directus/specs';
|
|
4
4
|
import { isSystemCollection } from '@directus/system-data';
|
|
5
5
|
import { getRelation } from '@directus/utils';
|
|
6
|
-
import { version } from 'directus/version';
|
|
7
6
|
import { cloneDeep, mergeWith } from 'lodash-es';
|
|
7
|
+
import hash from 'object-hash';
|
|
8
8
|
import { OAS_REQUIRED_SCHEMAS } from '../constants.js';
|
|
9
9
|
import getDatabase from '../database/index.js';
|
|
10
10
|
import { fetchPermissions } from '../permissions/lib/fetch-permissions.js';
|
|
@@ -54,12 +54,16 @@ class OASSpecsService {
|
|
|
54
54
|
const components = await this.generateComponents(schemaForSpec, tags);
|
|
55
55
|
const isDefaultPublicUrl = env['PUBLIC_URL'] === '/';
|
|
56
56
|
const url = isDefaultPublicUrl && host ? host : env['PUBLIC_URL'];
|
|
57
|
+
const hashedVersion = hash({
|
|
58
|
+
now: new Date().toISOString(),
|
|
59
|
+
user: this.accountability?.user,
|
|
60
|
+
});
|
|
57
61
|
const spec = {
|
|
58
62
|
openapi: '3.0.1',
|
|
59
63
|
info: {
|
|
60
64
|
title: 'Dynamic API Specification',
|
|
61
65
|
description: 'This is a dynamically generated API specification for all endpoints existing on the current project.',
|
|
62
|
-
version:
|
|
66
|
+
version: hashedVersion,
|
|
63
67
|
},
|
|
64
68
|
servers: [
|
|
65
69
|
{
|
package/dist/services/users.js
CHANGED
|
@@ -40,6 +40,7 @@ export class UsersService extends ItemsService {
|
|
|
40
40
|
throw new RecordNotUniqueError({
|
|
41
41
|
collection: 'directus_users',
|
|
42
42
|
field: 'email',
|
|
43
|
+
value: '[' + String(duplicates) + ']',
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
46
|
const query = this.knex
|
|
@@ -54,6 +55,7 @@ export class UsersService extends ItemsService {
|
|
|
54
55
|
throw new RecordNotUniqueError({
|
|
55
56
|
collection: 'directus_users',
|
|
56
57
|
field: 'email',
|
|
58
|
+
value: '[' + String(emails) + ']',
|
|
57
59
|
});
|
|
58
60
|
}
|
|
59
61
|
}
|
|
@@ -210,6 +212,7 @@ export class UsersService extends ItemsService {
|
|
|
210
212
|
throw new RecordNotUniqueError({
|
|
211
213
|
collection: 'directus_users',
|
|
212
214
|
field: 'email',
|
|
215
|
+
value: data['email'],
|
|
213
216
|
});
|
|
214
217
|
}
|
|
215
218
|
this.validateEmail(data['email']);
|
|
@@ -8,6 +8,7 @@ import { getFieldCount } from '../utils/get-field-count.js';
|
|
|
8
8
|
import { getFilesizeSum } from '../utils/get-filesize-sum.js';
|
|
9
9
|
import { getItemCount } from '../utils/get-item-count.js';
|
|
10
10
|
import { getUserItemCount } from '../utils/get-user-item-count.js';
|
|
11
|
+
import { getProjectId } from '../utils/get-project-id.js';
|
|
11
12
|
const basicCountTasks = [
|
|
12
13
|
{ collection: 'directus_dashboards' },
|
|
13
14
|
{ collection: 'directus_files' },
|
|
@@ -25,7 +26,7 @@ export const getReport = async () => {
|
|
|
25
26
|
const db = getDatabase();
|
|
26
27
|
const env = useEnv();
|
|
27
28
|
const helpers = getHelpers(db);
|
|
28
|
-
const [basicCounts, userCounts, userItemCount, fieldsCounts, extensionsCounts, databaseSize, filesizes] = await Promise.all([
|
|
29
|
+
const [basicCounts, userCounts, userItemCount, fieldsCounts, extensionsCounts, databaseSize, filesizes, projectId] = await Promise.all([
|
|
29
30
|
getItemCount(db, basicCountTasks),
|
|
30
31
|
fetchUserCount({ knex: db }),
|
|
31
32
|
getUserItemCount(db),
|
|
@@ -33,6 +34,7 @@ export const getReport = async () => {
|
|
|
33
34
|
getExtensionCount(db),
|
|
34
35
|
helpers.schema.getDatabaseSize(),
|
|
35
36
|
getFilesizeSum(db),
|
|
37
|
+
getProjectId(db),
|
|
36
38
|
]);
|
|
37
39
|
return {
|
|
38
40
|
url: env['PUBLIC_URL'],
|
|
@@ -53,5 +55,6 @@ export const getReport = async () => {
|
|
|
53
55
|
extensions: extensionsCounts.totalEnabled,
|
|
54
56
|
database_size: databaseSize ?? 0,
|
|
55
57
|
files_size_total: filesizes.total,
|
|
58
|
+
project_id: projectId,
|
|
56
59
|
};
|
|
57
60
|
};
|
|
@@ -1,12 +1,39 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { isIP } from 'net';
|
|
3
|
+
import proxyAddr from 'proxy-addr';
|
|
3
4
|
import { useLogger } from '../logger/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Generate the trusted ip list
|
|
7
|
+
*
|
|
8
|
+
* Adapted to have feature parity with the express equivalent https://github.com/expressjs/express/blob/9f4dbe3a1332cd883069ba9b73a9eed99234cfc7/lib/utils.js#L192
|
|
9
|
+
*/
|
|
10
|
+
function getTrustValue(trust) {
|
|
11
|
+
if (typeof trust === 'boolean') {
|
|
12
|
+
// Support plain true/false
|
|
13
|
+
return (_addr, _i) => trust;
|
|
14
|
+
}
|
|
15
|
+
else if (typeof trust === 'number') {
|
|
16
|
+
// Support trusting hop count
|
|
17
|
+
return (_addr, i) => i < trust;
|
|
18
|
+
}
|
|
19
|
+
else if (typeof trust === 'string') {
|
|
20
|
+
// Support comma-separated values
|
|
21
|
+
trust = trust.split(',').map((v) => v.trim());
|
|
22
|
+
}
|
|
23
|
+
return proxyAddr.compile(trust || []);
|
|
24
|
+
}
|
|
4
25
|
export function getIPFromReq(req) {
|
|
5
26
|
const env = useEnv();
|
|
6
27
|
const logger = useLogger();
|
|
7
|
-
let ip = req.ip;
|
|
28
|
+
let ip = 'ip' in req ? req.ip : proxyAddr(req, getTrustValue(env['IP_TRUST_PROXY']));
|
|
8
29
|
if (env['IP_CUSTOM_HEADER']) {
|
|
9
|
-
const
|
|
30
|
+
const customIPHeaderName = env['IP_CUSTOM_HEADER'].toLowerCase();
|
|
31
|
+
// All req.headers are auto lower-cased
|
|
32
|
+
let customIPHeaderValue = req.headers[customIPHeaderName];
|
|
33
|
+
// // Done to have feature parity with https://github.com/expressjs/express/blob/9f4dbe3a1332cd883069ba9b73a9eed99234cfc7/lib/request.js#L63
|
|
34
|
+
if (customIPHeaderName === 'referer' || customIPHeaderName === 'referrer') {
|
|
35
|
+
customIPHeaderValue = req.headers['referrer'] || req.headers['referer'];
|
|
36
|
+
}
|
|
10
37
|
if (typeof customIPHeaderValue === 'string' && isIP(customIPHeaderValue) !== 0) {
|
|
11
38
|
ip = customIPHeaderValue;
|
|
12
39
|
}
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -162,6 +162,6 @@ async function getDatabaseSchema(database, schemaInspector) {
|
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
const relationsService = new RelationsService({ knex: database, schema: result });
|
|
165
|
-
result.relations = await relationsService.readAll();
|
|
165
|
+
result.relations = await relationsService.readAll(undefined, undefined, true);
|
|
166
166
|
return result;
|
|
167
167
|
}
|
|
@@ -71,6 +71,9 @@ export async function sanitizeQuery(rawQuery, schema, accountability) {
|
|
|
71
71
|
if (rawQuery['alias']) {
|
|
72
72
|
query.alias = sanitizeAlias(rawQuery['alias']);
|
|
73
73
|
}
|
|
74
|
+
if ('backlink' in rawQuery) {
|
|
75
|
+
query.backlink = sanitizeBacklink(rawQuery['backlink']);
|
|
76
|
+
}
|
|
74
77
|
return query;
|
|
75
78
|
}
|
|
76
79
|
function sanitizeFields(rawFields) {
|
|
@@ -171,6 +174,9 @@ function sanitizeMeta(rawMeta) {
|
|
|
171
174
|
}
|
|
172
175
|
return [rawMeta];
|
|
173
176
|
}
|
|
177
|
+
function sanitizeBacklink(rawBacklink) {
|
|
178
|
+
return rawBacklink !== false && rawBacklink !== 'false';
|
|
179
|
+
}
|
|
174
180
|
async function sanitizeDeep(deep, schema, accountability) {
|
|
175
181
|
const logger = useLogger();
|
|
176
182
|
const result = {};
|
|
@@ -22,8 +22,8 @@ export default abstract class SocketController {
|
|
|
22
22
|
protected getRateLimiter(): RateLimiterAbstract | null;
|
|
23
23
|
private catchInvalidMessages;
|
|
24
24
|
protected handleUpgrade(request: IncomingMessage, socket: internal.Duplex, head: Buffer): Promise<void>;
|
|
25
|
-
protected handleTokenUpgrade({ request, socket, head }: UpgradeContext, token: string | null): Promise<void>;
|
|
26
|
-
protected handleHandshakeUpgrade({ request, socket, head }: UpgradeContext): Promise<void>;
|
|
25
|
+
protected handleTokenUpgrade({ request, socket, head, accountabilityOverrides }: UpgradeContext, token: string | null): Promise<void>;
|
|
26
|
+
protected handleHandshakeUpgrade({ request, socket, head, accountabilityOverrides }: UpgradeContext): Promise<void>;
|
|
27
27
|
createClient(ws: WebSocket, { accountability, expires_at }: AuthenticationState): WebSocketClient;
|
|
28
28
|
protected parseMessage(data: string): WebSocketMessage;
|
|
29
29
|
protected handleAuthRequest(client: WebSocketClient, message: WebSocketAuthMessage): Promise<void>;
|
|
@@ -8,15 +8,16 @@ import WebSocket, { WebSocketServer } from 'ws';
|
|
|
8
8
|
import { fromZodError } from 'zod-validation-error';
|
|
9
9
|
import emitter from '../../emitter.js';
|
|
10
10
|
import { useLogger } from '../../logger/index.js';
|
|
11
|
+
import { createDefaultAccountability } from '../../permissions/utils/create-default-accountability.js';
|
|
11
12
|
import { createRateLimiter } from '../../rate-limiter.js';
|
|
12
13
|
import { getAccountabilityForToken } from '../../utils/get-accountability-for-token.js';
|
|
14
|
+
import { getIPFromReq } from '../../utils/get-ip-from-req.js';
|
|
13
15
|
import { authenticateConnection, authenticationSuccess } from '../authenticate.js';
|
|
14
16
|
import { WebSocketError, handleWebSocketError } from '../errors.js';
|
|
15
17
|
import { AuthMode, WebSocketAuthMessage, WebSocketMessage } from '../messages.js';
|
|
16
18
|
import { getExpiresAtForToken } from '../utils/get-expires-at-for-token.js';
|
|
17
19
|
import { getMessageType } from '../utils/message.js';
|
|
18
20
|
import { waitForAnyMessage, waitForMessageType } from '../utils/wait-for-message.js';
|
|
19
|
-
import { createDefaultAccountability } from '../../permissions/utils/create-default-accountability.js';
|
|
20
21
|
const TOKEN_CHECK_INTERVAL = 15 * 60 * 1000; // 15 minutes
|
|
21
22
|
const logger = useLogger();
|
|
22
23
|
export default class SocketController {
|
|
@@ -98,8 +99,17 @@ export default class SocketController {
|
|
|
98
99
|
}
|
|
99
100
|
const env = useEnv();
|
|
100
101
|
const cookies = request.headers.cookie ? cookie.parse(request.headers.cookie) : {};
|
|
101
|
-
const context = { request, socket, head };
|
|
102
102
|
const sessionCookieName = env['SESSION_COOKIE_NAME'];
|
|
103
|
+
const accountabilityOverrides = {
|
|
104
|
+
ip: getIPFromReq(request) ?? null,
|
|
105
|
+
};
|
|
106
|
+
const userAgent = request.headers['user-agent']?.substring(0, 1024);
|
|
107
|
+
if (userAgent)
|
|
108
|
+
accountabilityOverrides.userAgent = userAgent;
|
|
109
|
+
const origin = request.headers['origin'];
|
|
110
|
+
if (origin)
|
|
111
|
+
accountabilityOverrides.origin = origin;
|
|
112
|
+
const context = { request, socket, head, accountabilityOverrides };
|
|
103
113
|
if (this.authentication.mode === 'strict' || query['access_token'] || cookies[sessionCookieName]) {
|
|
104
114
|
let token = null;
|
|
105
115
|
if (typeof query['access_token'] === 'string') {
|
|
@@ -117,11 +127,14 @@ export default class SocketController {
|
|
|
117
127
|
}
|
|
118
128
|
this.server.handleUpgrade(request, socket, head, async (ws) => {
|
|
119
129
|
this.catchInvalidMessages(ws);
|
|
120
|
-
const state = {
|
|
130
|
+
const state = {
|
|
131
|
+
accountability: createDefaultAccountability(accountabilityOverrides),
|
|
132
|
+
expires_at: null,
|
|
133
|
+
};
|
|
121
134
|
this.server.emit('connection', ws, state);
|
|
122
135
|
});
|
|
123
136
|
}
|
|
124
|
-
async handleTokenUpgrade({ request, socket, head }, token) {
|
|
137
|
+
async handleTokenUpgrade({ request, socket, head, accountabilityOverrides }, token) {
|
|
125
138
|
let accountability = null;
|
|
126
139
|
let expires_at = null;
|
|
127
140
|
if (token) {
|
|
@@ -149,13 +162,14 @@ export default class SocketController {
|
|
|
149
162
|
socket.destroy();
|
|
150
163
|
return;
|
|
151
164
|
}
|
|
165
|
+
Object.assign(accountability, accountabilityOverrides);
|
|
152
166
|
this.server.handleUpgrade(request, socket, head, async (ws) => {
|
|
153
167
|
this.catchInvalidMessages(ws);
|
|
154
168
|
const state = { accountability, expires_at };
|
|
155
169
|
this.server.emit('connection', ws, state);
|
|
156
170
|
});
|
|
157
171
|
}
|
|
158
|
-
async handleHandshakeUpgrade({ request, socket, head }) {
|
|
172
|
+
async handleHandshakeUpgrade({ request, socket, head, accountabilityOverrides }) {
|
|
159
173
|
this.server.handleUpgrade(request, socket, head, async (ws) => {
|
|
160
174
|
this.catchInvalidMessages(ws);
|
|
161
175
|
try {
|
|
@@ -163,6 +177,9 @@ export default class SocketController {
|
|
|
163
177
|
if (getMessageType(payload) !== 'auth')
|
|
164
178
|
throw new Error();
|
|
165
179
|
const state = await authenticateConnection(WebSocketAuthMessage.parse(payload));
|
|
180
|
+
if (state.accountability) {
|
|
181
|
+
Object.assign(state.accountability, accountabilityOverrides);
|
|
182
|
+
}
|
|
166
183
|
this.checkUserRequirements(state.accountability);
|
|
167
184
|
ws.send(authenticationSuccess(payload['uid'], state.refresh_token));
|
|
168
185
|
this.server.emit('connection', ws, state);
|
|
@@ -253,6 +270,17 @@ export default class SocketController {
|
|
|
253
270
|
try {
|
|
254
271
|
const { accountability, expires_at, refresh_token } = await authenticateConnection(message);
|
|
255
272
|
this.checkUserRequirements(accountability);
|
|
273
|
+
/**
|
|
274
|
+
* Re-use the existing ip, userAgent and origin accountability properties.
|
|
275
|
+
* They are only sent in the original connection request
|
|
276
|
+
*/
|
|
277
|
+
if (accountability && client.accountability) {
|
|
278
|
+
Object.assign(accountability, {
|
|
279
|
+
ip: client.accountability.ip,
|
|
280
|
+
userAgent: client.accountability.userAgent,
|
|
281
|
+
origin: client.accountability.origin,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
256
284
|
client.accountability = accountability;
|
|
257
285
|
client.expires_at = expires_at;
|
|
258
286
|
this.setTokenExpireTimer(client);
|
|
@@ -39,5 +39,5 @@ export declare function getFieldsPayload(subscription: PSubscription, accountabi
|
|
|
39
39
|
* @param event Event data
|
|
40
40
|
* @returns the fetched data
|
|
41
41
|
*/
|
|
42
|
-
export declare function getItemsPayload(subscription: PSubscription, accountability: Accountability | null, schema: SchemaOverview, event?: WebSocketEvent): Promise<string | number |
|
|
42
|
+
export declare function getItemsPayload(subscription: PSubscription, accountability: Accountability | null, schema: SchemaOverview, event?: WebSocketEvent): Promise<string | number | import("@directus/types").Item | (string | number)[] | import("@directus/types").Item[]>;
|
|
43
43
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "28.0.0",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
@@ -136,44 +136,46 @@
|
|
|
136
136
|
"pino-pretty": "13.0.0",
|
|
137
137
|
"pm2": "5.4.3",
|
|
138
138
|
"prom-client": "15.1.3",
|
|
139
|
+
"proxy-addr": "2.0.7",
|
|
139
140
|
"qs": "6.14.0",
|
|
140
141
|
"rate-limiter-flexible": "5.0.5",
|
|
141
142
|
"rollup": "4.34.9",
|
|
142
|
-
"samlify": "2.
|
|
143
|
+
"samlify": "2.10.0",
|
|
143
144
|
"sanitize-html": "2.14.0",
|
|
144
145
|
"sharp": "0.33.5",
|
|
145
146
|
"snappy": "7.2.2",
|
|
146
147
|
"stream-json": "1.9.1",
|
|
147
148
|
"tar": "7.4.3",
|
|
148
149
|
"tsx": "4.19.3",
|
|
150
|
+
"uuid": "11.1.0",
|
|
149
151
|
"wellknown": "0.5.0",
|
|
150
152
|
"ws": "8.18.1",
|
|
151
153
|
"zod": "3.24.2",
|
|
152
154
|
"zod-validation-error": "3.4.0",
|
|
153
|
-
"@directus/app": "13.
|
|
155
|
+
"@directus/app": "13.11.0",
|
|
154
156
|
"@directus/constants": "13.0.1",
|
|
155
|
-
"@directus/
|
|
156
|
-
"@directus/
|
|
157
|
-
"@directus/extensions": "3.0.
|
|
158
|
-
"@directus/extensions-registry": "3.0.
|
|
159
|
-
"@directus/extensions-sdk": "
|
|
157
|
+
"@directus/env": "5.1.0",
|
|
158
|
+
"@directus/errors": "2.0.2",
|
|
159
|
+
"@directus/extensions": "3.0.7",
|
|
160
|
+
"@directus/extensions-registry": "3.0.7",
|
|
161
|
+
"@directus/extensions-sdk": "14.0.0",
|
|
160
162
|
"@directus/format-title": "12.0.1",
|
|
163
|
+
"@directus/pressure": "3.0.6",
|
|
161
164
|
"@directus/schema": "13.0.1",
|
|
162
|
-
"@directus/memory": "3.0.
|
|
163
|
-
"@directus/schema-builder": "0.0.1",
|
|
165
|
+
"@directus/memory": "3.0.6",
|
|
164
166
|
"@directus/specs": "11.1.0",
|
|
167
|
+
"@directus/schema-builder": "0.0.3",
|
|
165
168
|
"@directus/storage": "12.0.0",
|
|
166
|
-
"@directus/
|
|
167
|
-
"@directus/storage-driver-
|
|
168
|
-
"@directus/storage-driver-
|
|
169
|
-
"@directus/storage-driver-gcs": "12.0.
|
|
170
|
-
"@directus/storage-driver-
|
|
171
|
-
"@directus/storage-driver-
|
|
172
|
-
"@directus/utils": "13.0.
|
|
173
|
-
"@directus/validation": "2.0.
|
|
174
|
-
"@directus/system-data": "3.1.
|
|
175
|
-
"directus": "11.
|
|
176
|
-
"@directus/storage-driver-local": "12.0.0"
|
|
169
|
+
"@directus/storage-driver-azure": "12.0.6",
|
|
170
|
+
"@directus/storage-driver-cloudinary": "12.0.6",
|
|
171
|
+
"@directus/storage-driver-local": "12.0.0",
|
|
172
|
+
"@directus/storage-driver-gcs": "12.0.6",
|
|
173
|
+
"@directus/storage-driver-supabase": "3.0.6",
|
|
174
|
+
"@directus/storage-driver-s3": "12.0.6",
|
|
175
|
+
"@directus/utils": "13.0.7",
|
|
176
|
+
"@directus/validation": "2.0.6",
|
|
177
|
+
"@directus/system-data": "3.1.1",
|
|
178
|
+
"directus": "11.9.0"
|
|
177
179
|
},
|
|
178
180
|
"devDependencies": {
|
|
179
181
|
"@directus/tsconfig": "3.0.0",
|
|
@@ -204,6 +206,7 @@
|
|
|
204
206
|
"@types/nodemailer": "6.4.17",
|
|
205
207
|
"@types/object-hash": "3.0.6",
|
|
206
208
|
"@types/papaparse": "5.3.15",
|
|
209
|
+
"@types/proxy-addr": "2.0.3",
|
|
207
210
|
"@types/qs": "6.9.18",
|
|
208
211
|
"@types/sanitize-html": "2.13.0",
|
|
209
212
|
"@types/stream-json": "1.7.8",
|
|
@@ -216,9 +219,9 @@
|
|
|
216
219
|
"knex-mock-client": "3.0.2",
|
|
217
220
|
"typescript": "5.8.2",
|
|
218
221
|
"vitest": "2.1.9",
|
|
222
|
+
"@directus/schema-builder": "0.0.3",
|
|
219
223
|
"@directus/random": "2.0.1",
|
|
220
|
-
"@directus/
|
|
221
|
-
"@directus/types": "13.1.1"
|
|
224
|
+
"@directus/types": "13.2.0"
|
|
222
225
|
},
|
|
223
226
|
"optionalDependencies": {
|
|
224
227
|
"@keyv/redis": "3.0.1",
|
|
@@ -233,7 +236,7 @@
|
|
|
233
236
|
"node": ">=22"
|
|
234
237
|
},
|
|
235
238
|
"scripts": {
|
|
236
|
-
"build": "tsc --project tsconfig.prod.json && copyfiles \"src/**/*.{yaml,liquid}\" -u 1 dist",
|
|
239
|
+
"build": "rimraf ./dist && tsc --project tsconfig.prod.json && copyfiles \"src/**/*.{yaml,liquid}\" -u 1 dist",
|
|
237
240
|
"cli": "NODE_ENV=development SERVE_APP=false tsx src/cli/run.ts",
|
|
238
241
|
"dev": "NODE_ENV=development SERVE_APP=true tsx watch --ignore extensions --clear-screen=false src/start.ts",
|
|
239
242
|
"test": "vitest run",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function mapValuesDeep(obj: Record<string, any>, fn: (key: string, value: any) => any): Record<string, any>;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export function mapValuesDeep(obj, fn) {
|
|
2
|
-
return recurse(obj);
|
|
3
|
-
function recurse(obj, prefix = '') {
|
|
4
|
-
if (Array.isArray(obj)) {
|
|
5
|
-
return obj.map((value, index) => {
|
|
6
|
-
if (typeof value === 'object' && value !== null) {
|
|
7
|
-
return recurse(value, prefix + `[${index}]`);
|
|
8
|
-
}
|
|
9
|
-
else {
|
|
10
|
-
return fn(prefix + `[${index}]`, value);
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
return Object.fromEntries(Object.entries(obj).map(([key, value]) => {
|
|
16
|
-
if (typeof value === 'object' && value !== null) {
|
|
17
|
-
return [key, recurse(value, prefix + (prefix ? '.' : '') + key)];
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
return [key, fn(prefix + (prefix ? '.' : '') + key, value)];
|
|
21
|
-
}
|
|
22
|
-
}));
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|