@directus/api 32.1.1 → 32.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/chat/constants/system-prompt.d.ts +1 -0
- package/dist/ai/chat/constants/system-prompt.js +51 -0
- package/dist/ai/chat/controllers/chat.post.d.ts +2 -0
- package/dist/ai/chat/controllers/chat.post.js +47 -0
- package/dist/ai/chat/lib/create-ui-stream.d.ts +15 -0
- package/dist/ai/chat/lib/create-ui-stream.js +42 -0
- package/dist/ai/chat/middleware/load-settings.d.ts +2 -0
- package/dist/ai/chat/middleware/load-settings.js +18 -0
- package/dist/ai/chat/models/chat-request.d.ts +34 -0
- package/dist/ai/chat/models/chat-request.js +26 -0
- package/dist/ai/chat/models/providers.d.ts +9 -0
- package/dist/ai/chat/models/providers.js +9 -0
- package/dist/ai/chat/router.d.ts +1 -0
- package/dist/ai/chat/router.js +5 -0
- package/dist/ai/chat/utils/chat-request-tool-to-ai-sdk-tool.d.ts +9 -0
- package/dist/ai/chat/utils/chat-request-tool-to-ai-sdk-tool.js +38 -0
- package/dist/ai/chat/utils/fix-error-tool-calls.d.ts +12 -0
- package/dist/ai/chat/utils/fix-error-tool-calls.js +30 -0
- package/dist/ai/chat/utils/parse-json-schema-7.d.ts +13 -0
- package/dist/ai/chat/utils/parse-json-schema-7.js +75 -0
- package/dist/{mcp → ai/mcp}/server.d.ts +13 -16
- package/dist/{mcp → ai/mcp}/server.js +4 -13
- package/dist/ai/mcp/types.d.ts +15 -0
- package/dist/{mcp/tools/assets.js → ai/tools/assets/index.js} +8 -5
- package/dist/{mcp/tools/collections.js → ai/tools/collections/index.js} +7 -4
- package/dist/{mcp/tools/fields.js → ai/tools/fields/index.js} +12 -9
- package/dist/{mcp/tools/files.js → ai/tools/files/index.js} +11 -5
- package/dist/{mcp/tools/flows.js → ai/tools/flows/index.js} +11 -5
- package/dist/{mcp/tools/folders.js → ai/tools/folders/index.js} +12 -5
- package/dist/ai/tools/index.d.ts +15 -0
- package/dist/ai/tools/index.js +29 -0
- package/dist/{mcp/tools/items.js → ai/tools/items/index.js} +13 -6
- package/dist/{mcp/tools/prompts/items.md → ai/tools/items/prompt.md} +19 -15
- package/dist/{mcp/tools/operations.d.ts → ai/tools/operations/index.d.ts} +46 -0
- package/dist/{mcp/tools/operations.js → ai/tools/operations/index.js} +12 -5
- package/dist/{mcp/tools/relations.js → ai/tools/relations/index.js} +7 -4
- package/dist/{mcp/tools/schema.d.ts → ai/tools/schema/index.d.ts} +1 -1
- package/dist/{mcp/tools/schema.js → ai/tools/schema/index.js} +9 -6
- package/dist/{mcp/tools/system.js → ai/tools/system/index.js} +7 -4
- package/dist/{mcp/tools/trigger-flow.js → ai/tools/trigger-flow/index.js} +8 -5
- package/dist/{mcp → ai/tools}/types.d.ts +1 -17
- package/dist/ai/tools/utils.d.ts +9 -0
- package/dist/ai/tools/utils.js +17 -0
- package/dist/app.js +5 -0
- package/dist/auth/drivers/saml.js +5 -2
- package/dist/controllers/assets.js +39 -2
- package/dist/controllers/mcp.js +1 -1
- package/dist/database/migrations/20240806A-permissions-policies.js +2 -2
- package/dist/database/migrations/20251103A-add-ai-settings.d.ts +3 -0
- package/dist/database/migrations/20251103A-add-ai-settings.js +14 -0
- package/dist/database/run-ast/run-ast.js +1 -1
- package/dist/extensions/lib/installation/manager.js +5 -9
- package/dist/extensions/lib/sync/status.d.ts +11 -0
- package/dist/extensions/lib/sync/status.js +34 -0
- package/dist/extensions/lib/sync/sync.d.ts +6 -0
- package/dist/extensions/lib/sync/sync.js +90 -0
- package/dist/extensions/lib/sync/tracker.d.ts +18 -0
- package/dist/extensions/lib/sync/tracker.js +71 -0
- package/dist/extensions/lib/sync/utils.d.ts +24 -0
- package/dist/extensions/lib/sync/utils.js +62 -0
- package/dist/extensions/manager.d.ts +8 -4
- package/dist/extensions/manager.js +30 -13
- package/dist/middleware/respond.js +2 -2
- package/dist/permissions/lib/fetch-policies.d.ts +1 -1
- package/dist/permissions/lib/fetch-roles-tree.d.ts +6 -3
- package/dist/permissions/lib/fetch-roles-tree.js +5 -27
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +9 -7
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +17 -9
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +1 -1
- package/dist/permissions/utils/fetch-raw-permissions.d.ts +1 -1
- package/dist/permissions/utils/fetch-share-info.d.ts +1 -1
- package/dist/permissions/utils/fetch-share-info.js +1 -1
- package/dist/permissions/utils/filter-policies-by-ip.js +1 -1
- package/dist/permissions/utils/get-permissions-for-share.js +8 -8
- package/dist/permissions/utils/with-cache.d.ts +8 -6
- package/dist/permissions/utils/with-cache.js +12 -10
- package/dist/request/is-denied-ip.js +2 -2
- package/dist/services/assets/name-deduper.d.ts +7 -0
- package/dist/services/assets/name-deduper.js +23 -0
- package/dist/services/assets.d.ts +15 -2
- package/dist/services/assets.js +98 -5
- package/dist/services/authentication.js +4 -4
- package/dist/services/comments.js +2 -2
- package/dist/services/extensions.js +4 -0
- package/dist/services/folders.d.ts +27 -2
- package/dist/services/folders.js +75 -0
- package/dist/services/import-export.d.ts +1 -1
- package/dist/services/import-export.js +4 -5
- package/dist/services/notifications.js +2 -2
- package/dist/services/payload.js +20 -0
- package/dist/services/roles.js +2 -2
- package/dist/services/tus/server.js +3 -3
- package/dist/telemetry/utils/get-settings.d.ts +15 -0
- package/dist/telemetry/utils/get-settings.js +13 -1
- package/dist/test-utils/README.md +95 -24
- package/dist/test-utils/cache.d.ts +2 -2
- package/dist/test-utils/cache.js +2 -2
- package/dist/test-utils/{fields-service.d.ts → services/fields-service.d.ts} +1 -1
- package/dist/test-utils/{fields-service.js → services/fields-service.js} +3 -2
- package/dist/test-utils/services/files-service.d.ts +28 -0
- package/dist/test-utils/services/files-service.js +34 -0
- package/dist/test-utils/services/folders-service.d.ts +28 -0
- package/dist/test-utils/services/folders-service.js +33 -0
- package/dist/utils/encrypt.d.ts +2 -0
- package/dist/utils/encrypt.js +64 -0
- package/dist/utils/get-accountability-for-role.js +2 -2
- package/dist/utils/get-accountability-for-token.js +4 -4
- package/dist/utils/get-cache-key.js +2 -2
- package/dist/utils/require-text.d.ts +1 -0
- package/dist/utils/require-text.js +4 -0
- package/dist/utils/require-yaml.js +2 -2
- package/package.json +32 -26
- package/dist/extensions/lib/sync-extensions.d.ts +0 -3
- package/dist/extensions/lib/sync-extensions.js +0 -70
- package/dist/extensions/lib/sync-status.d.ts +0 -10
- package/dist/extensions/lib/sync-status.js +0 -27
- package/dist/mcp/tools/index.d.ts +0 -15
- package/dist/mcp/tools/index.js +0 -29
- package/dist/mcp/tools/prompts/index.d.ts +0 -16
- package/dist/mcp/tools/prompts/index.js +0 -19
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +0 -5
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +0 -7
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +0 -5
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +0 -10
- package/dist/permissions/modules/fetch-global-access/types.d.ts +0 -4
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +0 -4
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +0 -27
- package/dist/utils/get-date-formatted.d.ts +0 -1
- package/dist/utils/get-date-formatted.js +0 -10
- package/dist/utils/ip-in-networks.d.ts +0 -6
- package/dist/utils/ip-in-networks.js +0 -13
- /package/dist/{mcp → ai/mcp}/index.d.ts +0 -0
- /package/dist/{mcp → ai/mcp}/index.js +0 -0
- /package/dist/{mcp → ai/mcp}/transport.d.ts +0 -0
- /package/dist/{mcp → ai/mcp}/transport.js +0 -0
- /package/dist/{mcp → ai/mcp}/types.js +0 -0
- /package/dist/{mcp/tools/assets.d.ts → ai/tools/assets/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/assets.md → ai/tools/assets/prompt.md} +0 -0
- /package/dist/{mcp/tools/collections.d.ts → ai/tools/collections/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/collections.md → ai/tools/collections/prompt.md} +0 -0
- /package/dist/{mcp/define.d.ts → ai/tools/define-tool.d.ts} +0 -0
- /package/dist/{mcp/define.js → ai/tools/define-tool.js} +0 -0
- /package/dist/{mcp/tools/fields.d.ts → ai/tools/fields/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/fields.md → ai/tools/fields/prompt.md} +0 -0
- /package/dist/{mcp/tools/files.d.ts → ai/tools/files/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/files.md → ai/tools/files/prompt.md} +0 -0
- /package/dist/{mcp/tools/flows.d.ts → ai/tools/flows/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/flows.md → ai/tools/flows/prompt.md} +0 -0
- /package/dist/{mcp/tools/folders.d.ts → ai/tools/folders/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/folders.md → ai/tools/folders/prompt.md} +0 -0
- /package/dist/{mcp/tools/items.d.ts → ai/tools/items/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/operations.md → ai/tools/operations/prompt.md} +0 -0
- /package/dist/{mcp/tools/relations.d.ts → ai/tools/relations/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/relations.md → ai/tools/relations/prompt.md} +0 -0
- /package/dist/{mcp/tools/prompts/schema.md → ai/tools/schema/prompt.md} +0 -0
- /package/dist/{mcp → ai/tools}/schema.d.ts +0 -0
- /package/dist/{mcp → ai/tools}/schema.js +0 -0
- /package/dist/{mcp/tools/system.d.ts → ai/tools/system/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/system-prompt-description.md → ai/tools/system/prompt-description.md} +0 -0
- /package/dist/{mcp/tools/prompts/system-prompt.md → ai/tools/system/prompt.md} +0 -0
- /package/dist/{mcp/tools/trigger-flow.d.ts → ai/tools/trigger-flow/index.d.ts} +0 -0
- /package/dist/{mcp/tools/prompts/trigger-flow.md → ai/tools/trigger-flow/prompt.md} +0 -0
- /package/dist/{permissions/modules/fetch-global-access → ai/tools}/types.js +0 -0
- /package/dist/test-utils/{items-service.d.ts → services/items-service.d.ts} +0 -0
- /package/dist/test-utils/{items-service.js → services/items-service.js} +0 -0
|
@@ -63,10 +63,10 @@ export class CommentsService extends ItemsService {
|
|
|
63
63
|
role: user['role']?.id ?? null,
|
|
64
64
|
admin: false,
|
|
65
65
|
app: false,
|
|
66
|
-
roles: await fetchRolesTree(user['role']?.id ?? null, this.knex),
|
|
66
|
+
roles: await fetchRolesTree(user['role']?.id ?? null, { knex: this.knex }),
|
|
67
67
|
ip: null,
|
|
68
68
|
};
|
|
69
|
-
const userGlobalAccess = await fetchGlobalAccess(accountability, this.knex);
|
|
69
|
+
const userGlobalAccess = await fetchGlobalAccess(accountability, { knex: this.knex });
|
|
70
70
|
accountability.admin = userGlobalAccess.admin;
|
|
71
71
|
accountability.app = userGlobalAccess.app;
|
|
72
72
|
const usersService = new UsersService({ schema: this.schema, accountability });
|
|
@@ -109,6 +109,8 @@ export class ExtensionsService {
|
|
|
109
109
|
await this.extensionsManager.install(versionId);
|
|
110
110
|
}
|
|
111
111
|
async readAll() {
|
|
112
|
+
// wait for extensions to be reloaded
|
|
113
|
+
await this.extensionsManager.isReloading();
|
|
112
114
|
const settings = await this.extensionsItemService.readByQuery({ limit: -1 });
|
|
113
115
|
const regular = settings.filter(({ bundle }) => bundle === null);
|
|
114
116
|
const bundled = settings.filter(({ bundle }) => bundle !== null);
|
|
@@ -138,6 +140,8 @@ export class ExtensionsService {
|
|
|
138
140
|
return output;
|
|
139
141
|
}
|
|
140
142
|
async readOne(id) {
|
|
143
|
+
// wait for extensions to be reloaded
|
|
144
|
+
await this.extensionsManager.isReloading();
|
|
141
145
|
const meta = await this.extensionsItemService.readOne(id);
|
|
142
146
|
const schema = this.extensionsManager.getExtension(meta.source, meta.folder) ?? null;
|
|
143
147
|
return {
|
|
@@ -1,5 +1,30 @@
|
|
|
1
|
-
import type { AbstractServiceOptions } from '@directus/types';
|
|
1
|
+
import type { AbstractServiceOptions, Folder } from '@directus/types';
|
|
2
2
|
import { ItemsService } from './items.js';
|
|
3
|
-
export declare class FoldersService extends ItemsService {
|
|
3
|
+
export declare class FoldersService extends ItemsService<Folder> {
|
|
4
4
|
constructor(options: AbstractServiceOptions);
|
|
5
|
+
/**
|
|
6
|
+
* Builds a full folder tree starting from a given root folder.
|
|
7
|
+
*
|
|
8
|
+
* This method returns a map of folder IDs to their corresponding paths
|
|
9
|
+
* relative to the root. It resolves all nested child folders and ensures
|
|
10
|
+
* that folder names are deduplicated within the same parent.
|
|
11
|
+
*
|
|
12
|
+
* Access control is applied automatically when non-admin, only folders the user has `read`
|
|
13
|
+
* access to are included.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} root - The ID of the root folder to start building the tree from.
|
|
16
|
+
* @returns {Promise<Map<string, string>>} A `Map` where:
|
|
17
|
+
* - Key: folder ID
|
|
18
|
+
* - Value: folder path relative to the root (e.g., "Documents/Photos")
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const foldersService = new FoldersService({ schema, accountability });
|
|
22
|
+
* const tree = await foldersService.buildTree('root-folder-id');
|
|
23
|
+
* console.log(tree.get('folder1')); // e.g., "RootFolder/SubFolder1"
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* - The returned `Map` includes the root folder itself.
|
|
27
|
+
* - If a folder has no name, its ID will be used as a fallback.
|
|
28
|
+
*/
|
|
29
|
+
buildTree(root: string): Promise<Map<string, string>>;
|
|
5
30
|
}
|
package/dist/services/folders.js
CHANGED
|
@@ -1,6 +1,81 @@
|
|
|
1
|
+
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
2
|
+
import { NameDeduper } from './assets/name-deduper.js';
|
|
1
3
|
import { ItemsService } from './items.js';
|
|
2
4
|
export class FoldersService extends ItemsService {
|
|
3
5
|
constructor(options) {
|
|
4
6
|
super('directus_folders', options);
|
|
5
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Builds a full folder tree starting from a given root folder.
|
|
10
|
+
*
|
|
11
|
+
* This method returns a map of folder IDs to their corresponding paths
|
|
12
|
+
* relative to the root. It resolves all nested child folders and ensures
|
|
13
|
+
* that folder names are deduplicated within the same parent.
|
|
14
|
+
*
|
|
15
|
+
* Access control is applied automatically when non-admin, only folders the user has `read`
|
|
16
|
+
* access to are included.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} root - The ID of the root folder to start building the tree from.
|
|
19
|
+
* @returns {Promise<Map<string, string>>} A `Map` where:
|
|
20
|
+
* - Key: folder ID
|
|
21
|
+
* - Value: folder path relative to the root (e.g., "Documents/Photos")
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const foldersService = new FoldersService({ schema, accountability });
|
|
25
|
+
* const tree = await foldersService.buildTree('root-folder-id');
|
|
26
|
+
* console.log(tree.get('folder1')); // e.g., "RootFolder/SubFolder1"
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* - The returned `Map` includes the root folder itself.
|
|
30
|
+
* - If a folder has no name, its ID will be used as a fallback.
|
|
31
|
+
*/
|
|
32
|
+
async buildTree(root) {
|
|
33
|
+
if (this.accountability && this.accountability.admin !== true) {
|
|
34
|
+
await validateAccess({
|
|
35
|
+
collection: 'directus_folders',
|
|
36
|
+
accountability: this.accountability,
|
|
37
|
+
action: 'read',
|
|
38
|
+
primaryKeys: [root],
|
|
39
|
+
}, {
|
|
40
|
+
knex: this.knex,
|
|
41
|
+
schema: this.schema,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const folders = await this.readByQuery({ limit: -1 });
|
|
45
|
+
// build folder and child lookup
|
|
46
|
+
const folderLookup = new Map();
|
|
47
|
+
const childFolderLookup = new Map();
|
|
48
|
+
for (const folder of folders) {
|
|
49
|
+
if (!folder['id'])
|
|
50
|
+
continue;
|
|
51
|
+
folderLookup.set(folder['id'], folder);
|
|
52
|
+
// root is always at the top level, we can therfor safely skip any parent references to it.
|
|
53
|
+
if (folder['parent'] && folder['id'] !== root) {
|
|
54
|
+
const children = childFolderLookup.get(folder['parent']) ?? [];
|
|
55
|
+
children.push(folder['id']);
|
|
56
|
+
childFolderLookup.set(folder['parent'], children);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const deduper = new NameDeduper();
|
|
60
|
+
const rootName = deduper.add(folderLookup.get(root)?.name, { fallback: root });
|
|
61
|
+
const stack = [[root, '']];
|
|
62
|
+
const tree = new Map();
|
|
63
|
+
// build tree from stack
|
|
64
|
+
while (stack.length > 0) {
|
|
65
|
+
const [folderId, path] = stack.pop() ?? [];
|
|
66
|
+
if (!folderId)
|
|
67
|
+
continue;
|
|
68
|
+
const folder = folderLookup.get(folderId);
|
|
69
|
+
if (!folder)
|
|
70
|
+
continue;
|
|
71
|
+
const children = childFolderLookup.get(folderId);
|
|
72
|
+
const folderName = deduper.add(folder['name'], { group: folder['parent'], fallback: folderId });
|
|
73
|
+
const folderPath = path === '' ? rootName : `${path}/${folderName}`;
|
|
74
|
+
tree.set(folderId, folderPath);
|
|
75
|
+
for (const childFolderId of children ?? []) {
|
|
76
|
+
stack.push([childFolderId, folderPath]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return tree;
|
|
80
|
+
}
|
|
6
81
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AbstractServiceOptions, Accountability, DirectusError, ExportFormat, File, Query, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
3
|
import type { Readable } from 'node:stream';
|
|
4
|
-
import type {
|
|
4
|
+
import type { FieldNode, FunctionFieldNode, NestedCollectionNode } from '../types/index.js';
|
|
5
5
|
export declare function createErrorTracker(): {
|
|
6
6
|
addCapturedError: (err: any, rowNumber: number) => void;
|
|
7
7
|
buildFinalErrors: () => DirectusError<any>[];
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { createError, ErrorCode, ForbiddenError, InvalidPayloadError, ServiceUnavailableError, UnsupportedMediaTypeError, } from '@directus/errors';
|
|
3
3
|
import { isSystemCollection } from '@directus/system-data';
|
|
4
|
-
import { parseJSON, toArray } from '@directus/utils';
|
|
4
|
+
import { getDateTimeFormatted, parseJSON, toArray } from '@directus/utils';
|
|
5
5
|
import { createTmpFile } from '@directus/utils/node';
|
|
6
6
|
import { queue } from 'async';
|
|
7
7
|
import destroyStream from 'destroy';
|
|
8
8
|
import { dump as toYAML } from 'js-yaml';
|
|
9
9
|
import { parse as toXML } from 'js2xmlparser';
|
|
10
10
|
import { Parser as CSVParser, transforms as CSVTransforms } from 'json2csv';
|
|
11
|
+
import { set } from 'lodash-es';
|
|
11
12
|
import { createReadStream, createWriteStream } from 'node:fs';
|
|
12
13
|
import { appendFile } from 'node:fs/promises';
|
|
13
14
|
import Papa from 'papaparse';
|
|
14
15
|
import StreamArray from 'stream-json/streamers/StreamArray.js';
|
|
16
|
+
import { parseFields } from '../database/get-ast-from-query/lib/parse-fields.js';
|
|
15
17
|
import getDatabase from '../database/index.js';
|
|
16
18
|
import emitter from '../emitter.js';
|
|
17
19
|
import { useLogger } from '../logger/index.js';
|
|
18
20
|
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
19
|
-
import { getDateFormatted } from '../utils/get-date-formatted.js';
|
|
20
21
|
import { getService } from '../utils/get-service.js';
|
|
21
22
|
import { transaction } from '../utils/transaction.js';
|
|
22
23
|
import { Url } from '../utils/url.js';
|
|
@@ -24,8 +25,6 @@ import { userName } from '../utils/user-name.js';
|
|
|
24
25
|
import { FilesService } from './files.js';
|
|
25
26
|
import { NotificationsService } from './notifications.js';
|
|
26
27
|
import { UsersService } from './users.js';
|
|
27
|
-
import { parseFields } from '../database/get-ast-from-query/lib/parse-fields.js';
|
|
28
|
-
import { set } from 'lodash-es';
|
|
29
28
|
const env = useEnv();
|
|
30
29
|
const logger = useLogger();
|
|
31
30
|
const MAX_IMPORT_ERRORS = env['MAX_IMPORT_ERRORS'];
|
|
@@ -514,7 +513,7 @@ export class ExportService {
|
|
|
514
513
|
schema: this.schema,
|
|
515
514
|
});
|
|
516
515
|
const storage = toArray(env['STORAGE_LOCATIONS'])[0];
|
|
517
|
-
const title = `export-${collection}-${
|
|
516
|
+
const title = `export-${collection}-${getDateTimeFormatted()}`;
|
|
518
517
|
const filename = `${title}.${format}`;
|
|
519
518
|
const fileWithDefaults = {
|
|
520
519
|
...(options?.file ?? {}),
|
|
@@ -29,12 +29,12 @@ export class NotificationsService extends ItemsService {
|
|
|
29
29
|
.addPath('admin', 'users', user['id'])
|
|
30
30
|
.toString();
|
|
31
31
|
const html = data.message ? md(data.message) : '';
|
|
32
|
-
const roles = await fetchRolesTree(user['role'], this.knex);
|
|
32
|
+
const roles = await fetchRolesTree(user['role'], { knex: this.knex });
|
|
33
33
|
const { app: app_access } = await fetchGlobalAccess({
|
|
34
34
|
user: user['id'],
|
|
35
35
|
roles,
|
|
36
36
|
ip: null,
|
|
37
|
-
}, this.knex);
|
|
37
|
+
}, { knex: this.knex });
|
|
38
38
|
const mailService = new MailService({
|
|
39
39
|
schema: this.schema,
|
|
40
40
|
knex: this.knex,
|
package/dist/services/payload.js
CHANGED
|
@@ -10,6 +10,8 @@ import { parse as wktToGeoJSON } from 'wellknown';
|
|
|
10
10
|
import { getHelpers } from '../database/helpers/index.js';
|
|
11
11
|
import getDatabase from '../database/index.js';
|
|
12
12
|
import { generateHash } from '../utils/generate-hash.js';
|
|
13
|
+
import { decrypt, encrypt } from '../utils/encrypt.js';
|
|
14
|
+
import { getSecret } from '../utils/get-secret.js';
|
|
13
15
|
/**
|
|
14
16
|
* Process a given payload for a collection to ensure the special fields (hash, uuid, date etc) are
|
|
15
17
|
* handled correctly.
|
|
@@ -124,6 +126,24 @@ export class PayloadService {
|
|
|
124
126
|
}
|
|
125
127
|
return value;
|
|
126
128
|
},
|
|
129
|
+
async encrypt({ action, value, accountability }) {
|
|
130
|
+
if (!value)
|
|
131
|
+
return value;
|
|
132
|
+
if (action === 'read') {
|
|
133
|
+
// In-system calls can still get the decrypted value
|
|
134
|
+
if (accountability === null) {
|
|
135
|
+
const key = getSecret();
|
|
136
|
+
return await decrypt(value, key);
|
|
137
|
+
}
|
|
138
|
+
// Requests from the API entrypoints have accountability and shouldn't get the raw value
|
|
139
|
+
return '**********';
|
|
140
|
+
}
|
|
141
|
+
if (typeof value === 'string') {
|
|
142
|
+
const key = getSecret();
|
|
143
|
+
return await encrypt(value, key);
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
},
|
|
127
147
|
};
|
|
128
148
|
async processValues(action, payload, aliasMap = {}, aggregate = {}) {
|
|
129
149
|
const processedPayload = toArray(payload);
|
package/dist/services/roles.js
CHANGED
|
@@ -3,8 +3,8 @@ import { UserIntegrityCheckFlag } from '@directus/types';
|
|
|
3
3
|
import { clearSystemCache } from '../cache.js';
|
|
4
4
|
import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
|
|
5
5
|
import { transaction } from '../utils/transaction.js';
|
|
6
|
-
import { ItemsService } from './items.js';
|
|
7
6
|
import { AccessService } from './access.js';
|
|
7
|
+
import { ItemsService } from './items.js';
|
|
8
8
|
import { PresetsService } from './presets.js';
|
|
9
9
|
import { UsersService } from './users.js';
|
|
10
10
|
export class RolesService extends ItemsService {
|
|
@@ -72,7 +72,7 @@ export class RolesService extends ItemsService {
|
|
|
72
72
|
if (ids.includes(parent)) {
|
|
73
73
|
throw new InvalidPayloadError({ reason: 'A role cannot be a parent of itself' });
|
|
74
74
|
}
|
|
75
|
-
const roles = await fetchRolesTree(parent, this.knex);
|
|
75
|
+
const roles = await fetchRolesTree(parent, { knex: this.knex });
|
|
76
76
|
if (ids.some((id) => roles.includes(id))) {
|
|
77
77
|
// The role tree up from the parent already includes this role, so it would create a circular reference
|
|
78
78
|
throw new InvalidPayloadError({ reason: 'A role cannot have a parent that is already a descendant of itself' });
|
|
@@ -41,7 +41,7 @@ export async function createTusServer(context) {
|
|
|
41
41
|
datastore: store,
|
|
42
42
|
locker: getTusLocker(),
|
|
43
43
|
...(RESUMABLE_UPLOADS.MAX_SIZE !== null && { maxSize: RESUMABLE_UPLOADS.MAX_SIZE }),
|
|
44
|
-
async onUploadFinish(
|
|
44
|
+
async onUploadFinish(_req, upload) {
|
|
45
45
|
const schema = await getSchema();
|
|
46
46
|
const service = new ItemsService('directus_files', {
|
|
47
47
|
schema,
|
|
@@ -92,9 +92,9 @@ export async function createTusServer(context) {
|
|
|
92
92
|
key: fileData.id,
|
|
93
93
|
collection: 'directus_files',
|
|
94
94
|
}, {
|
|
95
|
-
database: getDatabase(),
|
|
96
95
|
schema,
|
|
97
|
-
|
|
96
|
+
database: getDatabase(),
|
|
97
|
+
accountability: context.accountability ?? null,
|
|
98
98
|
});
|
|
99
99
|
return {
|
|
100
100
|
headers: {
|
|
@@ -5,5 +5,20 @@ export type TelemetrySettings = {
|
|
|
5
5
|
mcp_allow_deletes: boolean;
|
|
6
6
|
mcp_system_prompt_enabled: boolean;
|
|
7
7
|
visual_editor_urls: number;
|
|
8
|
+
ai_openai_api_key: boolean;
|
|
9
|
+
ai_anthropic_api_key: boolean;
|
|
10
|
+
ai_system_prompt: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type DatabaseSettings = {
|
|
13
|
+
project_id: string;
|
|
14
|
+
mcp_enabled?: boolean;
|
|
15
|
+
mcp_allow_deletes?: boolean;
|
|
16
|
+
mcp_system_prompt_enabled?: boolean;
|
|
17
|
+
visual_editor_urls?: {
|
|
18
|
+
url: string;
|
|
19
|
+
}[];
|
|
20
|
+
ai_openai_api_key?: string;
|
|
21
|
+
ai_anthropic_api_key?: string;
|
|
22
|
+
ai_system_prompt?: string;
|
|
8
23
|
};
|
|
9
24
|
export declare const getSettings: (db: Knex) => Promise<TelemetrySettings>;
|
|
@@ -6,7 +6,16 @@ export const getSettings = async (db) => {
|
|
|
6
6
|
schema: await getSchema({ database: db }),
|
|
7
7
|
});
|
|
8
8
|
const settings = (await settingsService.readSingleton({
|
|
9
|
-
fields: [
|
|
9
|
+
fields: [
|
|
10
|
+
'project_id',
|
|
11
|
+
'mcp_enabled',
|
|
12
|
+
'mcp_allow_deletes',
|
|
13
|
+
'mcp_system_prompt_enabled',
|
|
14
|
+
'visual_editor_urls',
|
|
15
|
+
'ai_openai_api_key',
|
|
16
|
+
'ai_anthropic_api_key',
|
|
17
|
+
'ai_system_prompt',
|
|
18
|
+
],
|
|
10
19
|
}));
|
|
11
20
|
return {
|
|
12
21
|
project_id: settings.project_id,
|
|
@@ -14,5 +23,8 @@ export const getSettings = async (db) => {
|
|
|
14
23
|
mcp_allow_deletes: settings?.mcp_allow_deletes || false,
|
|
15
24
|
mcp_system_prompt_enabled: settings?.mcp_system_prompt_enabled || false,
|
|
16
25
|
visual_editor_urls: settings.visual_editor_urls?.length || 0,
|
|
26
|
+
ai_openai_api_key: Boolean(settings?.ai_openai_api_key),
|
|
27
|
+
ai_anthropic_api_key: Boolean(settings?.ai_anthropic_api_key),
|
|
28
|
+
ai_system_prompt: Boolean(settings?.ai_system_prompt),
|
|
17
29
|
};
|
|
18
30
|
};
|
|
@@ -14,21 +14,23 @@ This directory contains mock implementations for commonly used modules in servic
|
|
|
14
14
|
- **[emitter.ts](#emitterts)** - Event emitter mocks
|
|
15
15
|
- **[items-service.ts](#items-servicets)** - ItemsService mocks
|
|
16
16
|
- **[fields-service.ts](#fields-servicets)** - FieldsService mocks
|
|
17
|
+
- **[files-service.ts](#files-servicets)** - FilesService mocks
|
|
18
|
+
- **[folders-service.ts](#folders-servicets)** - FoldersService mocks
|
|
17
19
|
- **[test-helpers.ts](#test-helpersts)** - Test data factory functions
|
|
18
20
|
|
|
19
21
|
## Quick Start
|
|
20
22
|
|
|
21
23
|
```typescript
|
|
22
|
-
import { createMockKnex, resetKnexMocks } from '../
|
|
24
|
+
import { createMockKnex, resetKnexMocks } from '../test-utils/knex.js';
|
|
23
25
|
|
|
24
26
|
// Set up mocks
|
|
25
27
|
vi.mock('../../src/database/index', async () => {
|
|
26
|
-
const { mockDatabase } = await import('../
|
|
28
|
+
const { mockDatabase } = await import('../test-utils/database.js');
|
|
27
29
|
return mockDatabase();
|
|
28
30
|
});
|
|
29
31
|
|
|
30
32
|
vi.mock('../cache.js', async () => {
|
|
31
|
-
const { mockCache } = await import('../
|
|
33
|
+
const { mockCache } = await import('../test-utils/cache.js');
|
|
32
34
|
return mockCache();
|
|
33
35
|
});
|
|
34
36
|
|
|
@@ -203,13 +205,13 @@ Creates a standard database module mock for service tests.
|
|
|
203
205
|
```typescript
|
|
204
206
|
// Standard PostgreSQL mock
|
|
205
207
|
vi.mock('../../src/database/index', async () => {
|
|
206
|
-
const { mockDatabase } = await import('../
|
|
208
|
+
const { mockDatabase } = await import('../test-utils/database.js');
|
|
207
209
|
return mockDatabase();
|
|
208
210
|
});
|
|
209
211
|
|
|
210
212
|
// MySQL-specific mock
|
|
211
213
|
vi.mock('../../src/database/index', async () => {
|
|
212
|
-
const { mockDatabase } = await import('../
|
|
214
|
+
const { mockDatabase } = await import('../test-utils/database.js');
|
|
213
215
|
return mockDatabase('mysql');
|
|
214
216
|
});
|
|
215
217
|
|
|
@@ -229,7 +231,7 @@ transaction wrapper).
|
|
|
229
231
|
|
|
230
232
|
```typescript
|
|
231
233
|
vi.mock('../utils/transaction.js', async () => {
|
|
232
|
-
const { mockTransaction } = await import('../
|
|
234
|
+
const { mockTransaction } = await import('../test-utils/database.js');
|
|
233
235
|
return mockTransaction();
|
|
234
236
|
});
|
|
235
237
|
|
|
@@ -269,13 +271,13 @@ mocks for vi.mock() declarations and spies for testing cache behavior.
|
|
|
269
271
|
```typescript
|
|
270
272
|
// Standard usage for vi.mock()
|
|
271
273
|
vi.mock('../cache.js', async () => {
|
|
272
|
-
const { mockCache } = await import('../
|
|
274
|
+
const { mockCache } = await import('../test-utils/cache.js');
|
|
273
275
|
return mockCache();
|
|
274
276
|
});
|
|
275
277
|
|
|
276
278
|
// Testing cache clearing with spies
|
|
277
279
|
import { getCache } from '../cache.js';
|
|
278
|
-
import { mockCache } from '../
|
|
280
|
+
import { mockCache } from '../test-utils/cache.js';
|
|
279
281
|
|
|
280
282
|
test('should clear cache after update', async () => {
|
|
281
283
|
const { spies } = mockCache();
|
|
@@ -305,7 +307,7 @@ Creates a standard schema inspector mock with tableInfo, columnInfo, primary, fo
|
|
|
305
307
|
```typescript
|
|
306
308
|
// Standard usage
|
|
307
309
|
vi.mock('@directus/schema', async () => {
|
|
308
|
-
const { mockSchema } = await import('../
|
|
310
|
+
const { mockSchema } = await import('../test-utils/schema.js');
|
|
309
311
|
return mockSchema();
|
|
310
312
|
});
|
|
311
313
|
|
|
@@ -340,7 +342,7 @@ Creates a standard emitter mock with emitAction, emitFilter, emitInit, and event
|
|
|
340
342
|
```typescript
|
|
341
343
|
// Standard usage
|
|
342
344
|
vi.mock('../emitter.js', async () => {
|
|
343
|
-
const { mockEmitter } = await import('../
|
|
345
|
+
const { mockEmitter } = await import('../test-utils/emitter.js');
|
|
344
346
|
return mockEmitter();
|
|
345
347
|
});
|
|
346
348
|
|
|
@@ -390,7 +392,7 @@ Creates a standard ItemsService mock with all CRUD methods pre-configured with s
|
|
|
390
392
|
```typescript
|
|
391
393
|
// Standard usage
|
|
392
394
|
vi.mock('./items.js', async () => {
|
|
393
|
-
const { mockItemsService } = await import('../
|
|
395
|
+
const { mockItemsService } = await import('../test-utils/services/items-service.js');
|
|
394
396
|
return mockItemsService();
|
|
395
397
|
});
|
|
396
398
|
|
|
@@ -423,6 +425,8 @@ Creates a standard FieldsService mock with common methods pre-configured.
|
|
|
423
425
|
|
|
424
426
|
**Mocked methods:**
|
|
425
427
|
|
|
428
|
+
In addition to the base `ItemsService` method the following `FieldsService` specific methods are available:
|
|
429
|
+
|
|
426
430
|
- `addColumnToTable` → no-op function
|
|
427
431
|
- `addColumnIndex` → resolves to undefined
|
|
428
432
|
- `deleteField` → resolves to undefined
|
|
@@ -434,7 +438,7 @@ Creates a standard FieldsService mock with common methods pre-configured.
|
|
|
434
438
|
```typescript
|
|
435
439
|
// Standard usage in CollectionsService tests
|
|
436
440
|
vi.mock('./fields.js', async () => {
|
|
437
|
-
const { mockFieldsService } = await import('../
|
|
441
|
+
const { mockFieldsService } = await import('../test-utils/services/fields-service.js');
|
|
438
442
|
return mockFieldsService();
|
|
439
443
|
});
|
|
440
444
|
|
|
@@ -450,43 +454,110 @@ expect(addColumnIndexSpy).toHaveBeenCalled();
|
|
|
450
454
|
|
|
451
455
|
---
|
|
452
456
|
|
|
457
|
+
### files-service.ts
|
|
458
|
+
|
|
459
|
+
Provides FilesService mocking utilities for testing services that depend on FilesService.
|
|
460
|
+
|
|
461
|
+
#### `mockFilesService()`
|
|
462
|
+
|
|
463
|
+
Creates a standard FilesService mock with common methods pre-configured.
|
|
464
|
+
|
|
465
|
+
**Returns:** Mock module object with `FilesService` class
|
|
466
|
+
|
|
467
|
+
**Mocked methods:**
|
|
468
|
+
|
|
469
|
+
In addition to the base `ItemsService` method the following `FilesService` specific methods are available:
|
|
470
|
+
|
|
471
|
+
- `uploadOne` → `1`
|
|
472
|
+
- `importOne` → `1`
|
|
473
|
+
|
|
474
|
+
**Example:**
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
// Standard usage in service tests
|
|
478
|
+
vi.mock('./files.js', async () => {
|
|
479
|
+
const { mockFilesService } = await import('../test-utils/services/files-service.js');
|
|
480
|
+
return mockFilesService();
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// Override specific methods during tests
|
|
484
|
+
import { FilesService } from './files.js';
|
|
485
|
+
|
|
486
|
+
const uploadOneSpy = vi.spyOn(FilesService.prototype, 'uploadOne').mockResolvedValue(`1`);
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
### folders-service.ts
|
|
492
|
+
|
|
493
|
+
Provides FoldersService mocking utilities for testing services that depend on FoldersService.
|
|
494
|
+
|
|
495
|
+
#### `mockFilesService()`
|
|
496
|
+
|
|
497
|
+
Creates a standard FoldersService mock with common methods pre-configured.
|
|
498
|
+
|
|
499
|
+
**Returns:** Mock module object with `FoldersService` class
|
|
500
|
+
|
|
501
|
+
**Mocked methods:**
|
|
502
|
+
|
|
503
|
+
In addition to the base `ItemsService` method the following `FoldersService` specific methods are available:
|
|
504
|
+
|
|
505
|
+
- `buildTree` → return `1` => `root` map
|
|
506
|
+
|
|
507
|
+
**Example:**
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
// Standard usage in service tests
|
|
511
|
+
vi.mock('./folders.js', async () => {
|
|
512
|
+
const { mockFoldersService } = await import('../test-utils/services/folders-service.js');
|
|
513
|
+
return mockFilesService();
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Override specific methods during tests
|
|
517
|
+
import { FoldersService } from './folders.js';
|
|
518
|
+
|
|
519
|
+
const buildTreeSpy = vi.spyOn(FoldersService.prototype, 'buildTree').mockResolvedValue(new Map('1', 'root-alt'));
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
453
524
|
## Common Patterns
|
|
454
525
|
|
|
455
526
|
### Full Service Test Setup
|
|
456
527
|
|
|
457
528
|
```typescript
|
|
458
|
-
import { createMockKnex, resetKnexMocks } from '../
|
|
529
|
+
import { createMockKnex, resetKnexMocks } from '../test-utils/knex.js';
|
|
459
530
|
import { SchemaBuilder } from '@directus/schema-builder';
|
|
460
531
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
461
532
|
|
|
462
533
|
// Mock all dependencies (before imports)
|
|
463
534
|
vi.mock('../../src/database/index', async () => {
|
|
464
|
-
const { mockDatabase } = await import('../
|
|
535
|
+
const { mockDatabase } = await import('../test-utils/database.js');
|
|
465
536
|
return mockDatabase();
|
|
466
537
|
});
|
|
467
538
|
|
|
468
539
|
vi.mock('@directus/schema', async () => {
|
|
469
|
-
const { mockSchema } = await import('../
|
|
540
|
+
const { mockSchema } = await import('../test-utils/schema.js');
|
|
470
541
|
return mockSchema();
|
|
471
542
|
});
|
|
472
543
|
|
|
473
544
|
vi.mock('../cache.js', async () => {
|
|
474
|
-
const { mockCache } = await import('../
|
|
545
|
+
const { mockCache } = await import('../test-utils/cache.js');
|
|
475
546
|
return mockCache();
|
|
476
547
|
});
|
|
477
548
|
|
|
478
549
|
vi.mock('../emitter.js', async () => {
|
|
479
|
-
const { mockEmitter } = await import('../
|
|
550
|
+
const { mockEmitter } = await import('../test-utils/emitter.js');
|
|
480
551
|
return mockEmitter();
|
|
481
552
|
});
|
|
482
553
|
|
|
483
554
|
vi.mock('./items.js', async () => {
|
|
484
|
-
const { mockItemsService } = await import('../
|
|
555
|
+
const { mockItemsService } = await import('../test-utils/services/items-service.js');
|
|
485
556
|
return mockItemsService();
|
|
486
557
|
});
|
|
487
558
|
|
|
488
559
|
vi.mock('../utils/transaction.js', async () => {
|
|
489
|
-
const { mockTransaction } = await import('../
|
|
560
|
+
const { mockTransaction } = await import('../test-utils/database.js');
|
|
490
561
|
return mockTransaction();
|
|
491
562
|
});
|
|
492
563
|
|
|
@@ -525,7 +596,7 @@ describe('Integration Tests', () => {
|
|
|
525
596
|
### Testing Schema Operations
|
|
526
597
|
|
|
527
598
|
```typescript
|
|
528
|
-
import { mockCreateTable, mockAlterTable, createMockTableBuilder } from '../
|
|
599
|
+
import { mockCreateTable, mockAlterTable, createMockTableBuilder } from '../test-utils/knex.js';
|
|
529
600
|
|
|
530
601
|
test('should create table with correct schema', async () => {
|
|
531
602
|
const { db, mockSchemaBuilder } = createMockKnex();
|
|
@@ -552,7 +623,7 @@ test('should alter table to add column', async () => {
|
|
|
552
623
|
|
|
553
624
|
```typescript
|
|
554
625
|
import { getCache } from '../cache.js';
|
|
555
|
-
import { mockCache } from '../
|
|
626
|
+
import { mockCache } from '../test-utils/cache.js';
|
|
556
627
|
|
|
557
628
|
test('should clear cache after update', async () => {
|
|
558
629
|
const { spies } = mockCache();
|
|
@@ -604,7 +675,7 @@ test('should read column info', async () => {
|
|
|
604
675
|
### Testing with System Collection Mocks
|
|
605
676
|
|
|
606
677
|
```typescript
|
|
607
|
-
import { setupSystemCollectionMocks } from '../
|
|
678
|
+
import { setupSystemCollectionMocks } from '../test-utils/knex.js';
|
|
608
679
|
|
|
609
680
|
describe('Service Tests', () => {
|
|
610
681
|
const { db, tracker, mockSchemaBuilder } = createMockKnex();
|
|
@@ -678,7 +749,7 @@ Always declare `vi.mock()` calls **before** importing the modules they mock:
|
|
|
678
749
|
```typescript
|
|
679
750
|
// ✅ Correct - mocks first
|
|
680
751
|
vi.mock('../cache.js', async () => {
|
|
681
|
-
const { mockCache } = await import('../
|
|
752
|
+
const { mockCache } = await import('../test-utils/cache.js');
|
|
682
753
|
return mockCache();
|
|
683
754
|
});
|
|
684
755
|
|
|
@@ -688,7 +759,7 @@ import { YourService } from './your-service.js';
|
|
|
688
759
|
import { YourService } from './your-service.js';
|
|
689
760
|
|
|
690
761
|
vi.mock('../cache.js', async () => {
|
|
691
|
-
const { mockCache } = await import('../
|
|
762
|
+
const { mockCache } = await import('../test-utils/cache.js');
|
|
692
763
|
return mockCache();
|
|
693
764
|
});
|
|
694
765
|
```
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
* ```typescript
|
|
13
13
|
* // Standard usage for vi.mock()
|
|
14
14
|
* vi.mock('../cache.js', async () => {
|
|
15
|
-
* const { mockCache } = await import('../
|
|
15
|
+
* const { mockCache } = await import('../test-utils/cache.js');
|
|
16
16
|
* return mockCache();
|
|
17
17
|
* });
|
|
18
18
|
*
|
|
19
19
|
* // Testing cache clearing with spies
|
|
20
20
|
* import { getCache } from '../cache.js';
|
|
21
|
-
* import { mockCache } from '../
|
|
21
|
+
* import { mockCache } from '../test-utils/cache.js';
|
|
22
22
|
*
|
|
23
23
|
* test('should clear cache after update', async () => {
|
|
24
24
|
* const { spies } = mockCache();
|
package/dist/test-utils/cache.js
CHANGED
|
@@ -13,13 +13,13 @@ import { vi } from 'vitest';
|
|
|
13
13
|
* ```typescript
|
|
14
14
|
* // Standard usage for vi.mock()
|
|
15
15
|
* vi.mock('../cache.js', async () => {
|
|
16
|
-
* const { mockCache } = await import('../
|
|
16
|
+
* const { mockCache } = await import('../test-utils/cache.js');
|
|
17
17
|
* return mockCache();
|
|
18
18
|
* });
|
|
19
19
|
*
|
|
20
20
|
* // Testing cache clearing with spies
|
|
21
21
|
* import { getCache } from '../cache.js';
|
|
22
|
-
* import { mockCache } from '../
|
|
22
|
+
* import { mockCache } from '../test-utils/cache.js';
|
|
23
23
|
*
|
|
24
24
|
* test('should clear cache after update', async () => {
|
|
25
25
|
* const { spies } = mockCache();
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* ```typescript
|
|
13
13
|
* // Standard usage
|
|
14
14
|
* vi.mock('./fields.js', async () => {
|
|
15
|
-
* const { mockFieldsService } = await import('../
|
|
15
|
+
* const { mockFieldsService } = await import('../test-utils/services/fields-service.js');
|
|
16
16
|
* return mockFieldsService();
|
|
17
17
|
* });
|
|
18
18
|
*
|