@tsed/directus-sdk 8.19.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/.barrelsby.json +5 -0
- package/lib/esm/bootstrap/attach-logger.js +25 -0
- package/lib/esm/bootstrap/runtime.js +11 -0
- package/lib/esm/decorators/cache.js +60 -0
- package/lib/esm/fn/defineEndpoint.js +82 -0
- package/lib/esm/fn/defineHook.js +134 -0
- package/lib/esm/fn/defineOperationApi.js +120 -0
- package/lib/esm/fn/wrapEndpoint.js +80 -0
- package/lib/esm/fn/wrapOperation.js +63 -0
- package/lib/esm/index.js +14 -0
- package/lib/esm/services/DirectusCacheInterceptor.js +184 -0
- package/lib/esm/services/DirectusContextService.js +162 -0
- package/lib/esm/services/DirectusItemsRepository.js +240 -0
- package/lib/types/bootstrap/attach-logger.d.ts +1 -0
- package/lib/types/bootstrap/runtime.d.ts +1 -0
- package/lib/types/decorators/cache.d.ts +57 -0
- package/lib/types/fn/defineEndpoint.d.ts +73 -0
- package/lib/types/fn/defineHook.d.ts +105 -0
- package/lib/types/fn/defineOperationApi.d.ts +114 -0
- package/lib/types/fn/wrapEndpoint.d.ts +35 -0
- package/lib/types/fn/wrapOperation.d.ts +35 -0
- package/lib/types/index.d.ts +14 -0
- package/lib/types/services/DirectusCacheInterceptor.d.ts +154 -0
- package/lib/types/services/DirectusContextService.d.ts +149 -0
- package/lib/types/services/DirectusItemsRepository.d.ts +226 -0
- package/package.json +63 -0
- package/readme.md +493 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { EndpointConfig } from "@directus/types";
|
|
2
|
+
/**
|
|
3
|
+
* Defines a Directus endpoint with Ts.ED dependency injection support.
|
|
4
|
+
*
|
|
5
|
+
* This function wraps the Directus SDK's `defineEndpoint` to automatically integrate
|
|
6
|
+
* Ts.ED's DI container, allowing you to use `inject()` to access services within
|
|
7
|
+
* your endpoint handlers. It also provides automatic error handling and logging.
|
|
8
|
+
*
|
|
9
|
+
* ### Basic endpoint with service injection
|
|
10
|
+
* ```ts
|
|
11
|
+
* import {defineEndpoint} from "@tsed/directus-sdk";
|
|
12
|
+
* import {inject} from "@tsed/di";
|
|
13
|
+
*
|
|
14
|
+
* export default defineEndpoint({
|
|
15
|
+
* id: "my-custom-endpoint",
|
|
16
|
+
* handler: (router) => {
|
|
17
|
+
* router.get("/hello", async (req, res) => {
|
|
18
|
+
* const myService = inject(MyService);
|
|
19
|
+
* const result = await myService.doSomething();
|
|
20
|
+
*
|
|
21
|
+
* return res.json(result);
|
|
22
|
+
* });
|
|
23
|
+
* }
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* ### Function-only configuration
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* import {defineEndpoint} from "@tsed/directus-sdk";
|
|
31
|
+
*
|
|
32
|
+
* export default defineEndpoint((router, context) => {
|
|
33
|
+
* router.post("/webhook", async (req, res) => {
|
|
34
|
+
* const myService = inject(MyService);
|
|
35
|
+
* const result = await myService.doSomething();
|
|
36
|
+
* // Handle webhook
|
|
37
|
+
* res.json({ success: true });
|
|
38
|
+
* });
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* ### With multiple routes and error handling
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* import {defineEndpoint} from "@tsed/directus-sdk";
|
|
46
|
+
* import {inject} from "@tsed/di";
|
|
47
|
+
*
|
|
48
|
+
* export default defineEndpoint({
|
|
49
|
+
* id: "api-v1",
|
|
50
|
+
* handler: (router) => {
|
|
51
|
+
* router.get("/users", async (req, res) => {
|
|
52
|
+
* const userService = inject(UserService);
|
|
53
|
+
* const users = await userService.findAll();
|
|
54
|
+
* res.json(users);
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* router.post("/auth/login", async (req, res) => {
|
|
58
|
+
* const authService = inject(AuthService);
|
|
59
|
+
* const token = await authService.authenticate(req.body);
|
|
60
|
+
* res.json({ token });
|
|
61
|
+
* });
|
|
62
|
+
* }
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
* @param config - The endpoint configuration. Can be either:
|
|
66
|
+
* - A function that receives the router and context
|
|
67
|
+
* - An object with `id` and `handler` properties
|
|
68
|
+
*
|
|
69
|
+
* @returns The configured endpoint ready to be exported from your extension file
|
|
70
|
+
* @see {@link wrapEndpoint} for the underlying wrapper implementation
|
|
71
|
+
* @see {@link https://docs.directus.io/extensions/endpoints.html} Directus Endpoints Documentation
|
|
72
|
+
*/
|
|
73
|
+
export declare function defineEndpoint(config: EndpointConfig): EndpointConfig;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import "../bootstrap/runtime.js";
|
|
2
|
+
import type { HookExtensionContext, RegisterFunctions } from "@directus/types";
|
|
3
|
+
/**
|
|
4
|
+
* Defines a Directus hook with Ts.ED dependency injection support.
|
|
5
|
+
*
|
|
6
|
+
* This function wraps your hook callback to provide:
|
|
7
|
+
* - Automatic Ts.ED DI container initialization
|
|
8
|
+
* - Service injection via `inject()` function
|
|
9
|
+
* - Proper error handling and logging
|
|
10
|
+
* - Access to Directus context through `DirectusContextService`
|
|
11
|
+
*
|
|
12
|
+
* Hooks allow you to trigger custom logic when certain events occur in Directus,
|
|
13
|
+
* such as when items are created, updated, or deleted.
|
|
14
|
+
*
|
|
15
|
+
* ### Basic hook with filters
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import {defineHook} from "@tsed/directus-sdk";
|
|
19
|
+
* import {inject} from "@tsed/di";
|
|
20
|
+
*
|
|
21
|
+
* export default defineHook(({filter, action}) => {
|
|
22
|
+
* const validationService = inject(ValidationService);
|
|
23
|
+
*
|
|
24
|
+
* // Filter runs before the event (can modify data or cancel operation)
|
|
25
|
+
* filter("items.create", async (payload, meta) => {
|
|
26
|
+
* await validationService.validate(payload);
|
|
27
|
+
* return payload;
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Action runs after the event (cannot modify data)
|
|
31
|
+
* action("items.create", async (meta) => {
|
|
32
|
+
* console.log("Item created:", meta.key);
|
|
33
|
+
* });
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ### Hook with service injection and multiple events
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* import {defineHook} from "@tsed/directus-sdk";
|
|
41
|
+
* import {inject} from "@tsed/di";
|
|
42
|
+
* import {logger} from "@tsed/logger";
|
|
43
|
+
*
|
|
44
|
+
* export default defineHook(({filter, action}) => {
|
|
45
|
+
* const productService = inject(ProductService);
|
|
46
|
+
* const notificationService = inject(NotificationService);
|
|
47
|
+
*
|
|
48
|
+
* logger().info("Product hooks loaded");
|
|
49
|
+
*
|
|
50
|
+
* // Ensure unique product rankings on create
|
|
51
|
+
* filter("product_rankings.items.create", async (payload) => {
|
|
52
|
+
* return productService.ensureUniqueRanking(payload);
|
|
53
|
+
* });
|
|
54
|
+
*
|
|
55
|
+
* // Ensure unique product rankings on update
|
|
56
|
+
* filter("product_rankings.items.update", async (payload) => {
|
|
57
|
+
* return productService.ensureUniqueRanking(payload);
|
|
58
|
+
* });
|
|
59
|
+
*
|
|
60
|
+
* // Send notification when product is published
|
|
61
|
+
* action("products.items.update", async (meta, context) => {
|
|
62
|
+
* if (context.payload.status === "published") {
|
|
63
|
+
* await notificationService.sendPublishedNotification(meta.key);
|
|
64
|
+
* }
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* // Return services for debugging/testing
|
|
68
|
+
* return {
|
|
69
|
+
* productService,
|
|
70
|
+
* notificationService
|
|
71
|
+
* };
|
|
72
|
+
* });
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* ### Hook with init and schedule
|
|
76
|
+
*
|
|
77
|
+
* ```ts
|
|
78
|
+
* import {defineHook} from "@tsed/directus-sdk";
|
|
79
|
+
* import {inject} from "@tsed/di";
|
|
80
|
+
*
|
|
81
|
+
* export default defineHook(({init, schedule}) => {
|
|
82
|
+
* const cacheService = inject(CacheService);
|
|
83
|
+
* const syncService = inject(SyncService);
|
|
84
|
+
*
|
|
85
|
+
* // Run once when Directus starts
|
|
86
|
+
* init("app.before", async () => {
|
|
87
|
+
* await cacheService.warmup();
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* // Run on a schedule (cron syntax)
|
|
91
|
+
* schedule("0 * * * *", async () => {
|
|
92
|
+
* await syncService.hourlySync();
|
|
93
|
+
* });
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @param callback - The hook initialization function that receives:
|
|
98
|
+
* - `register` - Functions to register event handlers (filter, action, init, schedule, embed)
|
|
99
|
+
* - `context` - The Directus hook extension context
|
|
100
|
+
*
|
|
101
|
+
* @returns A wrapped hook function ready to be exported from your extension file
|
|
102
|
+
* @see {@link DirectusContextService} to access Directus context within injected services
|
|
103
|
+
* @see {@link https://docs.directus.io/extensions/hooks.html} Directus Hooks Documentation
|
|
104
|
+
*/
|
|
105
|
+
export declare function defineHook(callback: (register: RegisterFunctions, context: HookExtensionContext) => unknown | Promise<unknown> | void): (register: RegisterFunctions, context: HookExtensionContext) => Promise<unknown>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { OperationApiConfig } from "@directus/types";
|
|
2
|
+
/**
|
|
3
|
+
* Defines a Directus operation with Ts.ED dependency injection support.
|
|
4
|
+
*
|
|
5
|
+
* This function wraps the Directus SDK's `defineOperationApi` to automatically integrate
|
|
6
|
+
* Ts.ED's DI container, allowing you to use `inject()` to access services within your
|
|
7
|
+
* operation handlers. Operations are custom logic that can be used in Directus flows.
|
|
8
|
+
*
|
|
9
|
+
* ### Basic operation with service injection
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import {defineOperationApi} from "@tsed/directus-sdk";
|
|
13
|
+
* import {inject} from "@tsed/di";
|
|
14
|
+
*
|
|
15
|
+
* type SendEmailOptions = {
|
|
16
|
+
* to: string;
|
|
17
|
+
* subject: string;
|
|
18
|
+
* body: string;
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* export default defineOperationApi<SendEmailOptions>({
|
|
22
|
+
* id: "send-email",
|
|
23
|
+
* handler: async (options, context) => {
|
|
24
|
+
* const emailService = inject(EmailService);
|
|
25
|
+
*
|
|
26
|
+
* await emailService.send({
|
|
27
|
+
* to: options.to,
|
|
28
|
+
* subject: options.subject,
|
|
29
|
+
* body: options.body
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* return {
|
|
33
|
+
* success: true,
|
|
34
|
+
* sentAt: new Date().toISOString()
|
|
35
|
+
* };
|
|
36
|
+
* }
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* ### Operation with external API call
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* import {defineOperationApi} from "@tsed/directus-sdk";
|
|
44
|
+
* import {inject} from "@tsed/di";
|
|
45
|
+
*
|
|
46
|
+
* type WebhookOptions = {
|
|
47
|
+
* url: string;
|
|
48
|
+
* method: "GET" | "POST";
|
|
49
|
+
* payload?: Record<string, any>;
|
|
50
|
+
* };
|
|
51
|
+
*
|
|
52
|
+
* export default defineOperationApi<WebhookOptions>({
|
|
53
|
+
* id: "call-webhook",
|
|
54
|
+
* handler: async (options, context) => {
|
|
55
|
+
* const httpClient = inject(HttpClient);
|
|
56
|
+
*
|
|
57
|
+
* const response = await httpClient.request({
|
|
58
|
+
* url: options.url,
|
|
59
|
+
* method: options.method,
|
|
60
|
+
* data: options.payload
|
|
61
|
+
* });
|
|
62
|
+
*
|
|
63
|
+
* return {
|
|
64
|
+
* statusCode: response.status,
|
|
65
|
+
* data: response.data
|
|
66
|
+
* };
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* ### Operation with database access
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* import {defineOperationApi} from "@tsed/directus-sdk";
|
|
75
|
+
* import {inject} from "@tsed/di";
|
|
76
|
+
*
|
|
77
|
+
* type CalculateStatsOptions = {
|
|
78
|
+
* collection: string;
|
|
79
|
+
* field: string;
|
|
80
|
+
* };
|
|
81
|
+
*
|
|
82
|
+
* export default defineOperationApi<CalculateStatsOptions>({
|
|
83
|
+
* id: "calculate-stats",
|
|
84
|
+
* handler: async (options, context) => {
|
|
85
|
+
* const statsService = inject(StatsService);
|
|
86
|
+
* const directusContext = inject(DirectusContextService);
|
|
87
|
+
*
|
|
88
|
+
* const itemsService = await directusContext.getItemsService(options.collection);
|
|
89
|
+
* const items = await itemsService.readByQuery({ limit: -1 });
|
|
90
|
+
*
|
|
91
|
+
* const stats = statsService.calculate(items, options.field);
|
|
92
|
+
*
|
|
93
|
+
* return {
|
|
94
|
+
* count: items.length,
|
|
95
|
+
* average: stats.avg,
|
|
96
|
+
* min: stats.min,
|
|
97
|
+
* max: stats.max
|
|
98
|
+
* };
|
|
99
|
+
* }
|
|
100
|
+
* });
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @template Options - Type definition for the operation's configuration options
|
|
104
|
+
*
|
|
105
|
+
* @param config - The operation configuration containing:
|
|
106
|
+
* - `id` - Unique identifier for the operation
|
|
107
|
+
* - `handler` - Function that executes the operation logic
|
|
108
|
+
*
|
|
109
|
+
* @returns The configured operation ready to be exported from your extension file
|
|
110
|
+
* @see {@link wrapOperation} for the underlying wrapper implementation
|
|
111
|
+
* @see {@link DirectusContextService} to access Directus context
|
|
112
|
+
* @see {@link https://docs.directus.io/extensions/operations.html} Directus Operations Documentation
|
|
113
|
+
*/
|
|
114
|
+
export declare function defineOperationApi<Options = Record<string, unknown>>(config: OperationApiConfig<Options>): OperationApiConfig<Options>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import "../bootstrap/runtime.js";
|
|
2
|
+
import type { EndpointExtensionContext } from "@directus/types";
|
|
3
|
+
import type { Router } from "express";
|
|
4
|
+
/**
|
|
5
|
+
* Wraps an endpoint handler to provide Ts.ED dependency injection and error handling.
|
|
6
|
+
*
|
|
7
|
+
* This function is internally used by `defineEndpoint` to:
|
|
8
|
+
* - Initialize the Ts.ED DI container for each request
|
|
9
|
+
* - Wrap all Express router methods (GET, POST, PUT, DELETE, etc.)
|
|
10
|
+
* - Provide automatic error handling with proper HTTP responses
|
|
11
|
+
* - Attach Directus logger to the DI context
|
|
12
|
+
* - Log errors with structured error information
|
|
13
|
+
*
|
|
14
|
+
* Each route handler executes within its own DI context, allowing proper isolation
|
|
15
|
+
* and service injection via `inject()`.
|
|
16
|
+
*
|
|
17
|
+
* @param callback - The endpoint handler function that receives router and context
|
|
18
|
+
*
|
|
19
|
+
* @returns A wrapped handler that integrates with Directus and Ts.ED
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* This is typically used internally by `defineEndpoint`. You generally don't need to
|
|
23
|
+
* call this directly unless you're building custom endpoint wrappers.
|
|
24
|
+
*
|
|
25
|
+
* Error handling:
|
|
26
|
+
* - Errors are caught and returned as JSON responses
|
|
27
|
+
* - Status code is extracted from `error.status` (defaults to 500)
|
|
28
|
+
* - Error message is extracted from `error.error_message` or `error.message`
|
|
29
|
+
* - Additional errors array is included if present in `error.errors`
|
|
30
|
+
* - All errors are logged with structured information (name, message, description, stack)
|
|
31
|
+
*
|
|
32
|
+
* @see {@link defineEndpoint} for the recommended way to create endpoints
|
|
33
|
+
* @see {@link DirectusContextService} to access Directus context in injected services
|
|
34
|
+
*/
|
|
35
|
+
export declare function wrapEndpoint(callback: (router: Router, context: EndpointExtensionContext) => void): (router: Router, context: EndpointExtensionContext) => void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import "../bootstrap/runtime.js";
|
|
2
|
+
import type { OperationContext } from "@directus/types";
|
|
3
|
+
/**
|
|
4
|
+
* Wraps an operation handler to provide Ts.ED dependency injection and error handling.
|
|
5
|
+
*
|
|
6
|
+
* This function is internally used by `defineOperationApi` to:
|
|
7
|
+
* - Initialize the Ts.ED DI container for each operation execution
|
|
8
|
+
* - Enable service injection via `inject()` within the operation handler
|
|
9
|
+
* - Provide automatic error handling and logging
|
|
10
|
+
* - Set up the Directus context in `DirectusContextService`
|
|
11
|
+
* - Create an isolated DI context for each operation run
|
|
12
|
+
*
|
|
13
|
+
* Each operation execution runs within its own DI context with platform set to
|
|
14
|
+
* `DIRECTUS_OPERATION`, ensuring proper isolation between different operation runs.
|
|
15
|
+
*
|
|
16
|
+
* @template Options - Type definition for the operation's configuration options
|
|
17
|
+
*
|
|
18
|
+
* @param callback - The operation handler function that receives options and context
|
|
19
|
+
*
|
|
20
|
+
* @returns A wrapped handler that integrates with Directus flows and Ts.ED
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* This is typically used internally by `defineOperationApi`. You generally don't need
|
|
24
|
+
* to call this directly unless you're building custom operation wrappers.
|
|
25
|
+
*
|
|
26
|
+
* Error handling:
|
|
27
|
+
* - Errors are caught and logged with structured information
|
|
28
|
+
* - Error details include name, message, description, and stack trace
|
|
29
|
+
* - Logger is automatically flushed after error logging
|
|
30
|
+
* - The error is not re-thrown, allowing the flow to continue
|
|
31
|
+
*
|
|
32
|
+
* @see {@link defineOperationApi} for the recommended way to create operations
|
|
33
|
+
* @see {@link DirectusContextService} to access Directus context in injected services
|
|
34
|
+
*/
|
|
35
|
+
export declare function wrapOperation<Options = Record<string, unknown>>(callback: (options: Options, context: OperationContext) => unknown | Promise<unknown> | void): (options: Options, context: OperationContext) => Promise<unknown>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Automatically generated by @tsed/barrels.
|
|
3
|
+
*/
|
|
4
|
+
export * from "./bootstrap/attach-logger.js";
|
|
5
|
+
export * from "./bootstrap/runtime.js";
|
|
6
|
+
export * from "./decorators/cache.js";
|
|
7
|
+
export * from "./fn/defineEndpoint.js";
|
|
8
|
+
export * from "./fn/defineHook.js";
|
|
9
|
+
export * from "./fn/defineOperationApi.js";
|
|
10
|
+
export * from "./fn/wrapEndpoint.js";
|
|
11
|
+
export * from "./fn/wrapOperation.js";
|
|
12
|
+
export * from "./services/DirectusCacheInterceptor.js";
|
|
13
|
+
export * from "./services/DirectusContextService.js";
|
|
14
|
+
export * from "./services/DirectusItemsRepository.js";
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { Type } from "@tsed/core";
|
|
2
|
+
import { type InterceptorContext, type InterceptorMethods } from "@tsed/di";
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for the DirectusCacheInterceptor.
|
|
5
|
+
*
|
|
6
|
+
* @property ttl - Time to live in milliseconds for cached values (default: 900000ms = 15 minutes)
|
|
7
|
+
* @property keyGenerator - Custom function to generate cache keys from method arguments
|
|
8
|
+
* @property namespace - Custom namespace for cache keys (default: className:methodName)
|
|
9
|
+
* @property useSystemCache - Whether to use Directus system cache (true) or regular cache (false). Default: true
|
|
10
|
+
*/
|
|
11
|
+
export type DirectusCacheOptions = {
|
|
12
|
+
ttl?: number;
|
|
13
|
+
keyGenerator?: (...args: unknown[]) => string;
|
|
14
|
+
namespace?: string;
|
|
15
|
+
useSystemCache?: boolean;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Interceptor that provides caching functionality using Directus cache infrastructure.
|
|
19
|
+
*
|
|
20
|
+
* This interceptor wraps method calls to cache their results based on method arguments.
|
|
21
|
+
* It integrates with Directus's built-in caching system and supports both system cache
|
|
22
|
+
* and regular cache.
|
|
23
|
+
*
|
|
24
|
+
* Features:
|
|
25
|
+
* - Automatic cache key generation based on class name, method name, and arguments
|
|
26
|
+
* - Configurable TTL (time to live) for cached values
|
|
27
|
+
* - Custom key generators for complex caching strategies
|
|
28
|
+
* - Support for both system cache and regular cache
|
|
29
|
+
* - Proper handling of falsy values (null, false, 0, empty string)
|
|
30
|
+
*
|
|
31
|
+
* ### Direct usage with bindIntercept
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* import {DirectusCacheInterceptor, useDirectusCache} from "@tsed/directus-sdk";
|
|
35
|
+
* import {injectable} from "@tsed/di";
|
|
36
|
+
*
|
|
37
|
+
* export class MyService {
|
|
38
|
+
* search(query: string) {
|
|
39
|
+
* // Expensive operation
|
|
40
|
+
* return this.performSearch(query);
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* // Apply cache programmatically
|
|
45
|
+
* injectable(MyService);
|
|
46
|
+
* useDirectusCache(MyService, "search", { ttl: 60000 });
|
|
47
|
+
* ```
|
|
48
|
+
* @remarks
|
|
49
|
+
* This interceptor is typically used via the `@Cache` decorator rather than directly.
|
|
50
|
+
* The cache key format is: `{namespace}:{keyGenerator(...args)}`
|
|
51
|
+
* where namespace defaults to `snake_case_class_name:methodName`
|
|
52
|
+
*
|
|
53
|
+
* @see {@link Cache} decorator for the recommended usage
|
|
54
|
+
* @see {@link useDirectusCache} for programmatic cache binding
|
|
55
|
+
*/
|
|
56
|
+
export declare class DirectusCacheInterceptor implements InterceptorMethods {
|
|
57
|
+
/**
|
|
58
|
+
* Intercepts a method call to provide caching functionality.
|
|
59
|
+
*
|
|
60
|
+
* The interception flow:
|
|
61
|
+
* 1. Extract cache options from the context
|
|
62
|
+
* 2. Generate a cache key based on class name, method name, and arguments
|
|
63
|
+
* 3. Check if a cached value exists
|
|
64
|
+
* 4. If cached, return the cached value (skipping method execution)
|
|
65
|
+
* 5. If not cached, execute the method
|
|
66
|
+
* 6. Store the result in cache with the specified TTL
|
|
67
|
+
* 7. Return the result
|
|
68
|
+
*
|
|
69
|
+
* ### Cache key generation
|
|
70
|
+
*
|
|
71
|
+
* ```ts
|
|
72
|
+
* // For class MyService with method search("query")
|
|
73
|
+
* // Default namespace: "my_service:search"
|
|
74
|
+
* // Full cache key: "my_service:search:["query"]"
|
|
75
|
+
* ```
|
|
76
|
+
* @param context - The interceptor context containing method information and options
|
|
77
|
+
*
|
|
78
|
+
* @returns The cached or newly computed result of the method call
|
|
79
|
+
*
|
|
80
|
+
* @remarks
|
|
81
|
+
* - The cache check uses strict inequality (`!== undefined`) to allow caching falsy values
|
|
82
|
+
* - Cache keys are automatically namespaced to avoid collisions between different methods
|
|
83
|
+
* - The system cache is used by default, which persists across requests
|
|
84
|
+
*/
|
|
85
|
+
intercept(context: InterceptorContext<unknown, DirectusCacheOptions>): Promise<any>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Programmatically applies cache interception to a method using Directus cache infrastructure.
|
|
89
|
+
*
|
|
90
|
+
* This function allows you to add caching to a method after the class is defined,
|
|
91
|
+
* which is useful when you can't use decorators or need dynamic cache configuration.
|
|
92
|
+
*
|
|
93
|
+
* @example Basic usage
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* import {injectable} from "@tsed/di";
|
|
97
|
+
* import {useDirectusCache} from "@tsed/directus-sdk";
|
|
98
|
+
*
|
|
99
|
+
* export class JiraIssueClient {
|
|
100
|
+
* search(query: string) {
|
|
101
|
+
* // Expensive search operation
|
|
102
|
+
* return this.performSearch(query);
|
|
103
|
+
* }
|
|
104
|
+
* }
|
|
105
|
+
*
|
|
106
|
+
* injectable(JiraIssueClient)
|
|
107
|
+
*
|
|
108
|
+
* // Apply cache after class definition
|
|
109
|
+
* useDirectusCache(JiraIssueClient, "search", { ttl: 900000 });
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* ### With custom options
|
|
113
|
+
* ```ts
|
|
114
|
+
*
|
|
115
|
+
* useDirectusCache(MyService, "fetchData", {
|
|
116
|
+
* ttl: 60000,
|
|
117
|
+
* keyGenerator: (id: string) => `data:${id}`,
|
|
118
|
+
* namespace: "my_service:data",
|
|
119
|
+
* useSystemCache: false
|
|
120
|
+
* });
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* ### Applying cache to multiple methods
|
|
124
|
+
*
|
|
125
|
+
* ```ts
|
|
126
|
+
* import {injectable} from "@tsed/di";
|
|
127
|
+
* import {useDirectusCache} from "@tsed/directus-sdk";
|
|
128
|
+
*
|
|
129
|
+
* export class ApiClient {
|
|
130
|
+
* getUser(id: string) { }
|
|
131
|
+
* getPosts() { }
|
|
132
|
+
* }
|
|
133
|
+
* injectable(ApiClient);
|
|
134
|
+
*
|
|
135
|
+
* useDirectusCache(ApiClient, "getUser", { ttl: 300000 });
|
|
136
|
+
* useDirectusCache(ApiClient, "getPosts", { ttl: 60000 });
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* @template K - The class type
|
|
140
|
+
* @template Props - The property key type (method name)
|
|
141
|
+
*
|
|
142
|
+
* @param token - The class constructor to apply caching to
|
|
143
|
+
* @param propertyKey - The name of the method to cache
|
|
144
|
+
* @param opts - Cache configuration options
|
|
145
|
+
*
|
|
146
|
+
* @remarks
|
|
147
|
+
* - Must be called after the class is marked as `@Injectable()` or `injectable()`
|
|
148
|
+
* - The class must be registered in the DI container
|
|
149
|
+
* - Prefer using the `@Cache` decorator when possible for better readability
|
|
150
|
+
*
|
|
151
|
+
* @see {@link Cache} decorator for the declarative approach
|
|
152
|
+
* @see {@link DirectusCacheInterceptor} for implementation details
|
|
153
|
+
*/
|
|
154
|
+
export declare function useDirectusCache<K, Props = keyof K>(token: Type<K>, propertyKey: Props, opts: DirectusCacheOptions): void;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { EndpointExtensionContext, HookExtensionContext, Item, OperationContext } from "@directus/types";
|
|
2
|
+
type DirectusContext = EndpointExtensionContext | OperationContext | HookExtensionContext;
|
|
3
|
+
/**
|
|
4
|
+
* Service to manage and access Directus context within Ts.ED dependency injection container.
|
|
5
|
+
*
|
|
6
|
+
* This service provides a bridge between Directus extensions and Ts.ED's DI system,
|
|
7
|
+
* allowing you to access the Directus context and its services from anywhere in your code.
|
|
8
|
+
*
|
|
9
|
+
* The context is automatically set by `wrapEndpoint`, `wrapOperation`, and `defineHook`
|
|
10
|
+
* wrappers, making it available through the DI container.
|
|
11
|
+
*
|
|
12
|
+
* ### Accessing context in a custom service
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import {Injectable} from "@tsed/di";
|
|
16
|
+
* import {DirectusContextService} from "@tsed/directus-sdk";
|
|
17
|
+
*
|
|
18
|
+
* @Injectable()
|
|
19
|
+
* export class MyService {
|
|
20
|
+
* directusContext = inject(DirectusContextService);
|
|
21
|
+
*
|
|
22
|
+
* async doSomething() {
|
|
23
|
+
* const context = this.directusContext.get();
|
|
24
|
+
* const schema = await context.getSchema();
|
|
25
|
+
* // Use schema...
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* ### Using inject() to get the service
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* import {inject} from "@tsed/di";
|
|
34
|
+
* import {DirectusContextService} from "@tsed/directus-sdk";
|
|
35
|
+
*
|
|
36
|
+
* const contextService = inject(DirectusContextService);
|
|
37
|
+
* const context = contextService.get();
|
|
38
|
+
*```
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* This service is automatically registered as injectable and can be accessed via `inject()`.
|
|
42
|
+
*/
|
|
43
|
+
export declare class DirectusContextService {
|
|
44
|
+
/**
|
|
45
|
+
* Sets the Directus context in the current DI context.
|
|
46
|
+
*
|
|
47
|
+
* This method is typically called internally by the wrapper functions
|
|
48
|
+
* (`wrapEndpoint`, `wrapOperation`, `defineHook`) and should not be
|
|
49
|
+
* called manually in most cases.
|
|
50
|
+
*
|
|
51
|
+
* @param ctx - The Directus extension context (endpoint, operation, or hook)
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const contextService = inject(DirectusContextService);
|
|
56
|
+
* contextService.set(directusContext);
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
set(ctx: DirectusContext): void;
|
|
60
|
+
/**
|
|
61
|
+
* Retrieves the Directus context from the current DI context.
|
|
62
|
+
*
|
|
63
|
+
* Returns the context that was previously set by the wrapper functions.
|
|
64
|
+
* This allows you to access Directus services, schema, logger, and other
|
|
65
|
+
* context-specific functionality.
|
|
66
|
+
*
|
|
67
|
+
* ### Accessing context properties
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* const contextService = inject(DirectusContextService);
|
|
71
|
+
* const context = contextService.get();
|
|
72
|
+
*
|
|
73
|
+
* // Access logger
|
|
74
|
+
* context.logger.info("Message");
|
|
75
|
+
*
|
|
76
|
+
* // Access schema
|
|
77
|
+
* const schema = await context.getSchema();
|
|
78
|
+
*
|
|
79
|
+
* // Access services
|
|
80
|
+
* const itemsService = new context.services.ItemsService("users");
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @returns The Directus extension context, or undefined if not set
|
|
84
|
+
*
|
|
85
|
+
*/
|
|
86
|
+
get(): DirectusContext;
|
|
87
|
+
/**
|
|
88
|
+
* Creates and returns an ItemsService instance for the specified collection.
|
|
89
|
+
*
|
|
90
|
+
* This is a convenience method that:
|
|
91
|
+
*
|
|
92
|
+
* 1. Retrieves the Directus context
|
|
93
|
+
* 2. Fetches the database schema
|
|
94
|
+
* 3. Creates an ItemsService with the schema and provided options
|
|
95
|
+
*
|
|
96
|
+
* The ItemsService allows you to perform CRUD operations on Directus collections.
|
|
97
|
+
*
|
|
98
|
+
* ### Basic usage
|
|
99
|
+
*
|
|
100
|
+
* ```ts
|
|
101
|
+
* const contextService = inject(DirectusContextService);
|
|
102
|
+
* const usersService = await contextService.getItemsService("users");
|
|
103
|
+
*
|
|
104
|
+
* // Read all users
|
|
105
|
+
* const users = await usersService.readByQuery({ limit: -1 });
|
|
106
|
+
*
|
|
107
|
+
* // Create a user
|
|
108
|
+
* const userId = await usersService.createOne({ name: "John", email: "john@example.com" });
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* ### With typed collection
|
|
112
|
+
*
|
|
113
|
+
* ```ts
|
|
114
|
+
* type User = Item & {
|
|
115
|
+
* id: string;
|
|
116
|
+
* name: string;
|
|
117
|
+
* email: string;
|
|
118
|
+
* };
|
|
119
|
+
*
|
|
120
|
+
* const contextService = inject(DirectusContextService);
|
|
121
|
+
* const usersService = await contextService.getItemsService<User, "users">("users");
|
|
122
|
+
*
|
|
123
|
+
* // Now fully typed
|
|
124
|
+
* const users: User[] = await usersService.readByQuery({ limit: -1 });
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* ### With accountability options
|
|
128
|
+
*
|
|
129
|
+
* ```ts
|
|
130
|
+
* const contextService = inject(DirectusContextService);
|
|
131
|
+
* const itemsService = await contextService.getItemsService("articles", {
|
|
132
|
+
* accountability: {
|
|
133
|
+
* user: "user-id",
|
|
134
|
+
* role: "admin"
|
|
135
|
+
* }
|
|
136
|
+
* });
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* @template T - The item type extending Directus Item
|
|
140
|
+
* @template Collection - The collection name as a string literal type
|
|
141
|
+
* @param collection - The name of the collection to access
|
|
142
|
+
* @param options - Additional options for the ItemsService (e.g., accountability, knex instance)
|
|
143
|
+
* @returns A promise resolving to an ItemsService instance for the collection
|
|
144
|
+
* @throws {Error} If no Directus context is available (called outside extension context)
|
|
145
|
+
* @see {@link DirectusItemsRepository} for a more structured way to work with collections
|
|
146
|
+
*/
|
|
147
|
+
getItemsService<T extends Item = Item, Collection extends string = string>(collection: Collection, options?: any): Promise<import("@directus/types").AbstractService<T>>;
|
|
148
|
+
}
|
|
149
|
+
export {};
|