@magnet-cms/common 0.1.0 → 0.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/index.cjs +1867 -43
- package/dist/index.d.cts +4015 -815
- package/dist/index.d.ts +4015 -815
- package/dist/index.js +1766 -44
- package/package.json +6 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,494 +1,2627 @@
|
|
|
1
|
-
import { Type, DynamicModule, ValidationError } from '@nestjs/common';
|
|
1
|
+
import { Type, OnModuleDestroy, DynamicModule, ValidationError as ValidationError$1 } from '@nestjs/common';
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
type
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Event System Type Definitions
|
|
6
|
+
*
|
|
7
|
+
* Type-safe event system for decoupled communication between modules.
|
|
8
|
+
* Used by: Activity logging, Webhooks, Auth events, RBAC events, etc.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* All event names in the system
|
|
12
|
+
* Adding new events requires updating this union
|
|
13
|
+
*/
|
|
14
|
+
type EventName = 'content.created' | 'content.updated' | 'content.deleted' | 'content.published' | 'content.unpublished' | 'content.version.created' | 'content.version.restored' | 'user.created' | 'user.updated' | 'user.deleted' | 'user.registered' | 'user.login' | 'user.logout' | 'user.logout_all' | 'user.password_changed' | 'user.password_reset_requested' | 'user.password_reset' | 'user.password_reset_completed' | 'user.email_verification_requested' | 'user.email_verified' | 'user.session_revoked' | 'auth.token_refreshed' | 'auth.session_created' | 'auth.session_revoked' | 'auth.failed_login_attempt' | 'role.created' | 'role.updated' | 'role.deleted' | 'role.permissions_updated' | 'role.user_assigned' | 'settings.updated' | 'settings.group_updated' | 'media.uploaded' | 'media.deleted' | 'media.folder_created' | 'media.folder_deleted' | 'api_key.created' | 'api_key.revoked' | 'api_key.used' | 'webhook.created' | 'webhook.updated' | 'webhook.deleted' | 'webhook.delivery_success' | 'webhook.delivery_failed' | 'plugin.initialized' | 'plugin.destroyed' | 'notification.created' | 'system.startup' | 'system.shutdown';
|
|
15
|
+
/**
|
|
16
|
+
* Base event payload with common fields
|
|
17
|
+
*/
|
|
18
|
+
interface BaseEventPayload {
|
|
19
|
+
/** Timestamp when event was emitted */
|
|
20
|
+
timestamp: Date;
|
|
21
|
+
/** User who triggered the event (if applicable) */
|
|
22
|
+
userId?: string;
|
|
23
|
+
/** IP address of the request (if applicable) */
|
|
24
|
+
ipAddress?: string;
|
|
25
|
+
/** Request ID for tracing */
|
|
26
|
+
requestId?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Content event payload
|
|
30
|
+
*/
|
|
31
|
+
interface ContentEventPayload extends BaseEventPayload {
|
|
32
|
+
schema: string;
|
|
33
|
+
documentId: string;
|
|
34
|
+
locale?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Content version event payload
|
|
38
|
+
*/
|
|
39
|
+
interface ContentVersionEventPayload extends ContentEventPayload {
|
|
40
|
+
version: number;
|
|
41
|
+
previousVersion?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Field change record
|
|
45
|
+
*/
|
|
46
|
+
interface FieldChange {
|
|
47
|
+
field: string;
|
|
48
|
+
oldValue: unknown;
|
|
49
|
+
newValue: unknown;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* User event payload
|
|
53
|
+
*/
|
|
54
|
+
interface UserEventPayload extends BaseEventPayload {
|
|
55
|
+
targetUserId: string;
|
|
56
|
+
email?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Auth event payload
|
|
60
|
+
*/
|
|
61
|
+
interface AuthEventPayload extends BaseEventPayload {
|
|
62
|
+
userId: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Failed login event payload
|
|
66
|
+
*/
|
|
67
|
+
interface FailedLoginEventPayload extends BaseEventPayload {
|
|
68
|
+
email: string;
|
|
69
|
+
reason: 'invalid_password' | 'user_not_found' | 'account_locked' | 'email_not_verified';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Role event payload
|
|
73
|
+
*/
|
|
74
|
+
interface RoleEventPayload extends BaseEventPayload {
|
|
75
|
+
roleId: string;
|
|
76
|
+
roleName: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Settings event payload
|
|
80
|
+
*/
|
|
81
|
+
interface SettingsEventPayload extends BaseEventPayload {
|
|
70
82
|
key: string;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
83
|
+
oldValue: unknown;
|
|
84
|
+
newValue: unknown;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Settings group event payload
|
|
88
|
+
*/
|
|
89
|
+
interface SettingsGroupEventPayload extends BaseEventPayload {
|
|
90
|
+
group: string;
|
|
91
|
+
changes: Array<{
|
|
92
|
+
key: string;
|
|
93
|
+
oldValue: unknown;
|
|
94
|
+
newValue: unknown;
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Media event payload
|
|
99
|
+
*/
|
|
100
|
+
interface MediaEventPayload extends BaseEventPayload {
|
|
101
|
+
fileId: string;
|
|
102
|
+
filename: string;
|
|
103
|
+
mimeType: string;
|
|
104
|
+
size: number;
|
|
105
|
+
folder?: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Media folder event payload
|
|
109
|
+
*/
|
|
110
|
+
interface MediaFolderEventPayload extends BaseEventPayload {
|
|
111
|
+
folderId: string;
|
|
112
|
+
folderName: string;
|
|
113
|
+
parentFolder?: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* API Key event payload
|
|
117
|
+
*/
|
|
118
|
+
interface ApiKeyEventPayload extends BaseEventPayload {
|
|
119
|
+
apiKeyId: string;
|
|
108
120
|
name: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Webhook event payload
|
|
124
|
+
*/
|
|
125
|
+
interface WebhookEventPayload extends BaseEventPayload {
|
|
126
|
+
webhookId: string;
|
|
127
|
+
url: string;
|
|
128
|
+
events: string[];
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Webhook delivery event payload
|
|
132
|
+
*/
|
|
133
|
+
interface WebhookDeliveryEventPayload extends BaseEventPayload {
|
|
134
|
+
webhookId: string;
|
|
135
|
+
deliveryId: string;
|
|
136
|
+
event: string;
|
|
137
|
+
statusCode?: number;
|
|
138
|
+
duration?: number;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Plugin event payload
|
|
142
|
+
*/
|
|
143
|
+
interface PluginEventPayload extends BaseEventPayload {
|
|
144
|
+
pluginName: string;
|
|
145
|
+
version: string;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* System event payload
|
|
149
|
+
*/
|
|
150
|
+
interface SystemEventPayload extends BaseEventPayload {
|
|
151
|
+
version: string;
|
|
152
|
+
environment: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Notification created event payload
|
|
156
|
+
*/
|
|
157
|
+
interface NotificationEventPayload extends BaseEventPayload {
|
|
158
|
+
notificationId: string;
|
|
159
|
+
targetUserId: string;
|
|
109
160
|
type: string;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
required: boolean;
|
|
113
|
-
validations: SchemaPropertyValidation[];
|
|
114
|
-
ui: UIDecoratorOptions;
|
|
115
|
-
};
|
|
116
|
-
type SchemaMetadata = {
|
|
117
|
-
name: string;
|
|
118
|
-
properties: SchemaProperty[];
|
|
119
|
-
options?: SchemaOptions;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
type InitialConfig = {
|
|
123
|
-
title?: string;
|
|
124
|
-
description?: string;
|
|
125
|
-
env: string;
|
|
126
|
-
schemas: SchemaMetadata[];
|
|
127
|
-
settings: SchemaMetadata[];
|
|
128
|
-
};
|
|
129
|
-
|
|
161
|
+
channels: string[];
|
|
162
|
+
}
|
|
130
163
|
/**
|
|
131
|
-
*
|
|
164
|
+
* Event payload map - maps event names to their payload types
|
|
132
165
|
*/
|
|
133
|
-
interface
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
166
|
+
interface EventPayloadMap {
|
|
167
|
+
'content.created': ContentEventPayload;
|
|
168
|
+
'content.updated': ContentEventPayload & {
|
|
169
|
+
changes: FieldChange[];
|
|
170
|
+
};
|
|
171
|
+
'content.deleted': ContentEventPayload;
|
|
172
|
+
'content.published': ContentEventPayload;
|
|
173
|
+
'content.unpublished': ContentEventPayload;
|
|
174
|
+
'content.version.created': ContentVersionEventPayload;
|
|
175
|
+
'content.version.restored': ContentVersionEventPayload;
|
|
176
|
+
'user.created': UserEventPayload;
|
|
177
|
+
'user.updated': UserEventPayload & {
|
|
178
|
+
changes: string[];
|
|
179
|
+
};
|
|
180
|
+
'user.deleted': UserEventPayload;
|
|
181
|
+
'user.registered': UserEventPayload & {
|
|
182
|
+
name?: string;
|
|
183
|
+
};
|
|
184
|
+
'user.login': UserEventPayload & {
|
|
185
|
+
sessionId?: string;
|
|
186
|
+
};
|
|
187
|
+
'user.logout': UserEventPayload & {
|
|
188
|
+
sessionId?: string;
|
|
189
|
+
};
|
|
190
|
+
'user.logout_all': UserEventPayload;
|
|
191
|
+
'user.password_changed': UserEventPayload;
|
|
192
|
+
'user.password_reset_requested': UserEventPayload & {
|
|
193
|
+
token?: string;
|
|
194
|
+
plainToken?: string;
|
|
195
|
+
};
|
|
196
|
+
'user.password_reset': UserEventPayload;
|
|
197
|
+
'user.password_reset_completed': UserEventPayload;
|
|
198
|
+
'user.email_verification_requested': UserEventPayload & {
|
|
199
|
+
name?: string;
|
|
200
|
+
verificationToken?: string;
|
|
201
|
+
};
|
|
202
|
+
'user.email_verified': UserEventPayload;
|
|
203
|
+
'user.session_revoked': UserEventPayload & {
|
|
204
|
+
sessionId: string;
|
|
205
|
+
};
|
|
206
|
+
'auth.token_refreshed': AuthEventPayload;
|
|
207
|
+
'auth.session_created': AuthEventPayload & {
|
|
208
|
+
sessionId: string;
|
|
209
|
+
};
|
|
210
|
+
'auth.session_revoked': AuthEventPayload & {
|
|
211
|
+
sessionId: string;
|
|
212
|
+
reason: string;
|
|
213
|
+
};
|
|
214
|
+
'auth.failed_login_attempt': FailedLoginEventPayload;
|
|
215
|
+
'role.created': RoleEventPayload;
|
|
216
|
+
'role.updated': RoleEventPayload;
|
|
217
|
+
'role.deleted': RoleEventPayload;
|
|
218
|
+
'role.permissions_updated': RoleEventPayload & {
|
|
219
|
+
permissions: string[];
|
|
220
|
+
};
|
|
221
|
+
'role.user_assigned': RoleEventPayload & {
|
|
222
|
+
assignedUserId: string;
|
|
223
|
+
};
|
|
224
|
+
'settings.updated': SettingsEventPayload;
|
|
225
|
+
'settings.group_updated': SettingsGroupEventPayload;
|
|
226
|
+
'media.uploaded': MediaEventPayload;
|
|
227
|
+
'media.deleted': MediaEventPayload;
|
|
228
|
+
'media.folder_created': MediaFolderEventPayload;
|
|
229
|
+
'media.folder_deleted': MediaFolderEventPayload;
|
|
230
|
+
'api_key.created': ApiKeyEventPayload;
|
|
231
|
+
'api_key.revoked': ApiKeyEventPayload & {
|
|
232
|
+
reason?: string;
|
|
233
|
+
};
|
|
234
|
+
'api_key.used': ApiKeyEventPayload & {
|
|
235
|
+
endpoint: string;
|
|
236
|
+
};
|
|
237
|
+
'webhook.created': WebhookEventPayload;
|
|
238
|
+
'webhook.updated': WebhookEventPayload;
|
|
239
|
+
'webhook.deleted': WebhookEventPayload;
|
|
240
|
+
'webhook.delivery_success': WebhookDeliveryEventPayload;
|
|
241
|
+
'webhook.delivery_failed': WebhookDeliveryEventPayload & {
|
|
242
|
+
error: string;
|
|
243
|
+
};
|
|
244
|
+
'plugin.initialized': PluginEventPayload;
|
|
245
|
+
'plugin.destroyed': PluginEventPayload;
|
|
246
|
+
'notification.created': NotificationEventPayload;
|
|
247
|
+
'system.startup': SystemEventPayload;
|
|
248
|
+
'system.shutdown': SystemEventPayload;
|
|
142
249
|
}
|
|
143
250
|
/**
|
|
144
|
-
*
|
|
251
|
+
* Get payload type for an event name
|
|
145
252
|
*/
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
253
|
+
type EventPayload<E extends EventName> = EventPayloadMap[E];
|
|
254
|
+
/**
|
|
255
|
+
* Event handler function type
|
|
256
|
+
*/
|
|
257
|
+
type EventHandler<E extends EventName> = (payload: EventPayload<E>) => Promise<void> | void;
|
|
258
|
+
/**
|
|
259
|
+
* Event handler options
|
|
260
|
+
*/
|
|
261
|
+
interface EventHandlerOptions {
|
|
262
|
+
/** Priority (lower runs first, default 100) */
|
|
263
|
+
priority?: number;
|
|
264
|
+
/** Whether handler should run async (non-blocking) */
|
|
265
|
+
async?: boolean;
|
|
266
|
+
/** Handler name for debugging */
|
|
267
|
+
name?: string;
|
|
150
268
|
}
|
|
151
269
|
/**
|
|
152
|
-
*
|
|
270
|
+
* Required event handler options (after defaults applied)
|
|
153
271
|
*/
|
|
154
|
-
interface
|
|
155
|
-
|
|
156
|
-
|
|
272
|
+
interface RequiredEventHandlerOptions {
|
|
273
|
+
priority: number;
|
|
274
|
+
async: boolean;
|
|
157
275
|
name: string;
|
|
158
|
-
role?: string;
|
|
159
|
-
[key: string]: unknown;
|
|
160
276
|
}
|
|
161
277
|
/**
|
|
162
|
-
*
|
|
278
|
+
* Registered handler with metadata
|
|
163
279
|
*/
|
|
164
|
-
interface
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
expires_in?: number;
|
|
168
|
-
token_type?: string;
|
|
280
|
+
interface RegisteredHandler<E extends EventName> {
|
|
281
|
+
handler: EventHandler<E>;
|
|
282
|
+
options: RequiredEventHandlerOptions;
|
|
169
283
|
}
|
|
170
284
|
/**
|
|
171
|
-
*
|
|
285
|
+
* Event history entry for debugging
|
|
172
286
|
*/
|
|
173
|
-
interface
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
expiresIn?: string;
|
|
287
|
+
interface EventHistoryEntry {
|
|
288
|
+
event: EventName;
|
|
289
|
+
payload: BaseEventPayload;
|
|
290
|
+
timestamp: Date;
|
|
178
291
|
}
|
|
179
292
|
/**
|
|
180
|
-
*
|
|
293
|
+
* Event handler metadata (used by @OnEvent decorator)
|
|
181
294
|
*/
|
|
182
|
-
interface
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
/** JWT-specific configuration */
|
|
186
|
-
jwt?: JwtAuthConfig;
|
|
187
|
-
/** Allow extensible config for custom strategies */
|
|
188
|
-
[key: string]: unknown;
|
|
295
|
+
interface EventHandlerMetadata {
|
|
296
|
+
event: EventName;
|
|
297
|
+
options: EventHandlerOptions;
|
|
189
298
|
}
|
|
299
|
+
|
|
190
300
|
/**
|
|
191
|
-
*
|
|
192
|
-
|
|
301
|
+
* Metadata key for event handler registration
|
|
302
|
+
*/
|
|
303
|
+
declare const EVENT_HANDLER_METADATA = "magnet:event_handler";
|
|
304
|
+
/**
|
|
305
|
+
* Decorator to mark a method as an event handler
|
|
306
|
+
*
|
|
307
|
+
* Event handlers are automatically discovered and registered with the EventService
|
|
308
|
+
* on module initialization.
|
|
309
|
+
*
|
|
310
|
+
* @param event - The event name to listen for
|
|
311
|
+
* @param options - Optional handler configuration
|
|
193
312
|
*
|
|
194
313
|
* @example
|
|
195
314
|
* ```typescript
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* async
|
|
200
|
-
*
|
|
201
|
-
* }
|
|
202
|
-
*
|
|
203
|
-
* async login(credentials: LoginCredentials): Promise<AuthResult> {
|
|
204
|
-
* // Authenticate with Supabase
|
|
205
|
-
* }
|
|
206
|
-
*
|
|
207
|
-
* async register(data: RegisterData): Promise<AuthUser> {
|
|
208
|
-
* // Register user in Supabase
|
|
315
|
+
* @Injectable()
|
|
316
|
+
* export class ActivityService {
|
|
317
|
+
* @OnEvent('content.created', { priority: 10 })
|
|
318
|
+
* async logContentCreated(payload: EventPayload<'content.created'>): Promise<void> {
|
|
319
|
+
* await this.createActivityLog(payload)
|
|
209
320
|
* }
|
|
210
321
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
322
|
+
* // Async handlers run in the background and don't block the request
|
|
323
|
+
* @OnEvent('user.login', { priority: 50, async: true })
|
|
324
|
+
* async sendWelcomeEmail(payload: EventPayload<'user.login'>): Promise<void> {
|
|
325
|
+
* await this.emailService.sendWelcome(payload.targetUserId)
|
|
213
326
|
* }
|
|
214
327
|
* }
|
|
215
328
|
* ```
|
|
216
329
|
*/
|
|
217
|
-
declare
|
|
218
|
-
/**
|
|
219
|
-
* Unique identifier for this strategy
|
|
220
|
-
*/
|
|
221
|
-
abstract readonly name: string;
|
|
222
|
-
/**
|
|
223
|
-
* Initialize the auth strategy (optional setup)
|
|
224
|
-
*/
|
|
225
|
-
initialize?(): Promise<void>;
|
|
226
|
-
/**
|
|
227
|
-
* Validate a token/payload and return the authenticated user
|
|
228
|
-
* @param payload - Strategy-specific payload (e.g., JWT payload, session data)
|
|
229
|
-
* @returns Authenticated user or null if invalid
|
|
230
|
-
*/
|
|
231
|
-
abstract validate(payload: unknown): Promise<AuthUser | null>;
|
|
232
|
-
/**
|
|
233
|
-
* Authenticate user with credentials and return tokens
|
|
234
|
-
* @param credentials - Login credentials (strategy-specific)
|
|
235
|
-
* @returns Authentication result with access token
|
|
236
|
-
*/
|
|
237
|
-
abstract login(credentials: LoginCredentials): Promise<AuthResult>;
|
|
238
|
-
/**
|
|
239
|
-
* Register a new user
|
|
240
|
-
* @param data - Registration data
|
|
241
|
-
* @returns The created user
|
|
242
|
-
*/
|
|
243
|
-
abstract register(data: RegisterData): Promise<AuthUser>;
|
|
244
|
-
/**
|
|
245
|
-
* Validate user credentials (used internally by login)
|
|
246
|
-
* @param email - User email
|
|
247
|
-
* @param password - User password
|
|
248
|
-
* @returns User if valid, null otherwise
|
|
249
|
-
*/
|
|
250
|
-
abstract validateCredentials(email: string, password: string): Promise<AuthUser | null>;
|
|
251
|
-
/**
|
|
252
|
-
* Refresh an access token (optional)
|
|
253
|
-
* @param refreshToken - The refresh token
|
|
254
|
-
* @returns New authentication result
|
|
255
|
-
*/
|
|
256
|
-
refresh?(refreshToken: string): Promise<AuthResult>;
|
|
257
|
-
/**
|
|
258
|
-
* Logout/invalidate tokens (optional)
|
|
259
|
-
* @param token - The token to invalidate
|
|
260
|
-
*/
|
|
261
|
-
logout?(token: string): Promise<void>;
|
|
262
|
-
/**
|
|
263
|
-
* Get the Passport strategy name (for guards)
|
|
264
|
-
* Returns the name to use with AuthGuard()
|
|
265
|
-
*/
|
|
266
|
-
getPassportStrategyName(): string;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
type MongooseConfig = {
|
|
270
|
-
uri: string;
|
|
271
|
-
};
|
|
272
|
-
type TypeORMConfig = {
|
|
273
|
-
type: 'mysql' | 'postgres' | 'sqlite' | 'mariadb' | 'mssql' | 'oracle' | 'mongodb';
|
|
274
|
-
host: string;
|
|
275
|
-
port: number;
|
|
276
|
-
username: string;
|
|
277
|
-
password: string;
|
|
278
|
-
database: string;
|
|
279
|
-
};
|
|
280
|
-
type DBConfig = MongooseConfig | TypeORMConfig;
|
|
281
|
-
declare abstract class DatabaseAdapter {
|
|
282
|
-
abstract connect(options: MagnetModuleOptions): DynamicModule;
|
|
283
|
-
abstract forFeature(schemas: Type | Type[]): DynamicModule;
|
|
284
|
-
abstract model<T>(modelInstance: any): any;
|
|
285
|
-
abstract token(schema: string): string;
|
|
286
|
-
}
|
|
330
|
+
declare function OnEvent(event: EventName, options?: EventHandlerOptions): MethodDecorator;
|
|
287
331
|
|
|
288
332
|
/**
|
|
289
|
-
*
|
|
333
|
+
* Base options shared by all field decorators
|
|
290
334
|
*/
|
|
291
|
-
interface
|
|
292
|
-
/**
|
|
293
|
-
|
|
294
|
-
/**
|
|
335
|
+
interface BaseFieldOptions {
|
|
336
|
+
/** Field is required */
|
|
337
|
+
required?: boolean;
|
|
338
|
+
/** Field must be unique */
|
|
339
|
+
unique?: boolean;
|
|
340
|
+
/** Default value */
|
|
341
|
+
default?: unknown;
|
|
342
|
+
/** Admin UI tab placement */
|
|
343
|
+
tab?: string;
|
|
344
|
+
/** Admin UI group within tab */
|
|
345
|
+
group?: string;
|
|
346
|
+
/** Field label in admin UI */
|
|
347
|
+
label?: string;
|
|
348
|
+
/** Field description/help text */
|
|
295
349
|
description?: string;
|
|
296
|
-
/**
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
|
|
350
|
+
/** Field placeholder text */
|
|
351
|
+
placeholder?: string;
|
|
352
|
+
/** Hide field in admin UI */
|
|
353
|
+
hidden?: boolean;
|
|
354
|
+
/** Make field read-only in admin UI */
|
|
355
|
+
readonly?: boolean;
|
|
356
|
+
/** Field display order */
|
|
357
|
+
order?: number;
|
|
358
|
+
/** Show field in sidebar (instead of main content area) */
|
|
359
|
+
side?: boolean;
|
|
360
|
+
/** Display fields in a row layout */
|
|
361
|
+
row?: boolean;
|
|
362
|
+
/** Enable internationalization for this field */
|
|
363
|
+
intl?: boolean;
|
|
302
364
|
}
|
|
303
365
|
/**
|
|
304
|
-
*
|
|
366
|
+
* Text field options
|
|
305
367
|
*/
|
|
306
|
-
interface
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
/** Whether route requires authentication */
|
|
312
|
-
requiresAuth?: boolean;
|
|
313
|
-
/** Required permissions */
|
|
314
|
-
permissions?: string[];
|
|
315
|
-
/** Child routes */
|
|
316
|
-
children?: PluginRouteDefinition[];
|
|
368
|
+
interface TextFieldOptions extends BaseFieldOptions {
|
|
369
|
+
minLength?: number;
|
|
370
|
+
maxLength?: number;
|
|
371
|
+
pattern?: string;
|
|
372
|
+
transform?: 'none' | 'lowercase' | 'uppercase' | 'trim';
|
|
317
373
|
}
|
|
318
374
|
/**
|
|
319
|
-
*
|
|
375
|
+
* Number field options
|
|
320
376
|
*/
|
|
321
|
-
interface
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
url: string;
|
|
328
|
-
/** Lucide icon name (e.g., 'Boxes', 'Settings') */
|
|
329
|
-
icon: string;
|
|
330
|
-
/** Position in sidebar (lower = higher) */
|
|
331
|
-
order?: number;
|
|
332
|
-
/** Child items */
|
|
333
|
-
items?: PluginSidebarItem[];
|
|
334
|
-
/** Badge to show */
|
|
335
|
-
badge?: string | number;
|
|
377
|
+
interface NumberFieldOptions extends BaseFieldOptions {
|
|
378
|
+
min?: number;
|
|
379
|
+
max?: number;
|
|
380
|
+
integer?: boolean;
|
|
381
|
+
step?: number;
|
|
382
|
+
default?: number;
|
|
336
383
|
}
|
|
337
384
|
/**
|
|
338
|
-
*
|
|
385
|
+
* Boolean field options
|
|
339
386
|
*/
|
|
340
|
-
interface
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
/** Display title */
|
|
344
|
-
title: string;
|
|
345
|
-
/** Description */
|
|
346
|
-
description?: string;
|
|
347
|
-
/** Component identifier */
|
|
348
|
-
componentId: string;
|
|
387
|
+
interface BooleanFieldOptions extends BaseFieldOptions {
|
|
388
|
+
default?: boolean;
|
|
389
|
+
style?: 'switch' | 'checkbox';
|
|
349
390
|
}
|
|
350
391
|
/**
|
|
351
|
-
*
|
|
392
|
+
* Date field options
|
|
352
393
|
*/
|
|
353
|
-
interface
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
routes?: PluginRouteDefinition[];
|
|
358
|
-
/** Sidebar items */
|
|
359
|
-
sidebar?: PluginSidebarItem[];
|
|
360
|
-
/** Settings pages */
|
|
361
|
-
settings?: PluginSettingsPage[];
|
|
362
|
-
/** Required permissions */
|
|
363
|
-
permissions?: string[];
|
|
394
|
+
interface DateFieldOptions extends BaseFieldOptions {
|
|
395
|
+
min?: Date | string;
|
|
396
|
+
max?: Date | string;
|
|
397
|
+
default?: Date | string | 'now' | (() => Date);
|
|
364
398
|
}
|
|
365
399
|
/**
|
|
366
|
-
*
|
|
367
|
-
* Returned by GET /plugins/manifests endpoint
|
|
400
|
+
* DateTime field options
|
|
368
401
|
*/
|
|
369
|
-
interface
|
|
370
|
-
|
|
371
|
-
bundleUrl: string;
|
|
402
|
+
interface DateTimeFieldOptions extends DateFieldOptions {
|
|
403
|
+
timezone?: string;
|
|
372
404
|
}
|
|
373
405
|
/**
|
|
374
|
-
*
|
|
406
|
+
* Rich text field options
|
|
375
407
|
*/
|
|
376
|
-
interface
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
/** Plugin-specific options */
|
|
380
|
-
options?: Record<string, unknown>;
|
|
381
|
-
/** Whether the plugin is enabled (default: true) */
|
|
382
|
-
enabled?: boolean;
|
|
408
|
+
interface RichTextFieldOptions extends BaseFieldOptions {
|
|
409
|
+
toolbar?: 'minimal' | 'standard' | 'full';
|
|
410
|
+
maxLength?: number;
|
|
383
411
|
}
|
|
384
412
|
/**
|
|
385
|
-
*
|
|
413
|
+
* Markdown field options
|
|
386
414
|
*/
|
|
387
|
-
interface
|
|
388
|
-
|
|
389
|
-
|
|
415
|
+
interface MarkdownFieldOptions extends BaseFieldOptions {
|
|
416
|
+
preview?: boolean;
|
|
417
|
+
maxLength?: number;
|
|
390
418
|
}
|
|
391
419
|
/**
|
|
392
|
-
*
|
|
420
|
+
* Code field options
|
|
393
421
|
*/
|
|
394
|
-
interface
|
|
395
|
-
|
|
422
|
+
interface CodeFieldOptions extends BaseFieldOptions {
|
|
423
|
+
language?: string;
|
|
424
|
+
lineNumbers?: boolean;
|
|
396
425
|
}
|
|
397
426
|
/**
|
|
398
|
-
*
|
|
427
|
+
* JSON field options
|
|
399
428
|
*/
|
|
400
|
-
interface
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
version?: string;
|
|
404
|
-
dependencies?: string[];
|
|
405
|
-
frontend?: PluginFrontendManifest;
|
|
406
|
-
options?: Record<string, unknown>;
|
|
429
|
+
interface JSONFieldOptions extends BaseFieldOptions {
|
|
430
|
+
/** Optional JSON schema for validation */
|
|
431
|
+
schema?: unknown;
|
|
407
432
|
}
|
|
408
433
|
/**
|
|
409
|
-
*
|
|
410
|
-
* Plugins can implement these methods to respond to lifecycle events.
|
|
411
|
-
*
|
|
412
|
-
* @example
|
|
413
|
-
* ```ts
|
|
414
|
-
* @Plugin({ name: 'my-plugin', module: MyPluginModule })
|
|
415
|
-
* export class MyPlugin implements PluginLifecycle {
|
|
416
|
-
* onPluginInit() {
|
|
417
|
-
* console.log('Plugin initialized')
|
|
418
|
-
* }
|
|
419
|
-
*
|
|
420
|
-
* onPluginDestroy() {
|
|
421
|
-
* console.log('Plugin destroyed')
|
|
422
|
-
* }
|
|
423
|
-
* }
|
|
424
|
-
* ```
|
|
434
|
+
* Select option item
|
|
425
435
|
*/
|
|
426
|
-
interface
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
* Use this for plugin-specific setup that needs other services.
|
|
430
|
-
*/
|
|
431
|
-
onPluginInit?(): void | Promise<void>;
|
|
432
|
-
/**
|
|
433
|
-
* Called when the application is shutting down.
|
|
434
|
-
* Use this for cleanup operations.
|
|
435
|
-
*/
|
|
436
|
-
onPluginDestroy?(): void | Promise<void>;
|
|
436
|
+
interface SelectOptionItem {
|
|
437
|
+
label: string;
|
|
438
|
+
value: string | number;
|
|
437
439
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
maxFileSize?: number;
|
|
446
|
-
/** Allowed MIME types (default: all) */
|
|
447
|
-
allowedMimeTypes?: string[];
|
|
440
|
+
/**
|
|
441
|
+
* Select field options
|
|
442
|
+
*/
|
|
443
|
+
interface SelectFieldOptions extends BaseFieldOptions {
|
|
444
|
+
options: ReadonlyArray<SelectOptionItem> | ReadonlyArray<string | number>;
|
|
445
|
+
default?: string | number | (string | number)[];
|
|
446
|
+
multiple?: boolean;
|
|
448
447
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
/** AWS secret access key */
|
|
457
|
-
secretAccessKey: string;
|
|
458
|
-
/** Custom endpoint URL (for R2, MinIO, etc.) */
|
|
459
|
-
endpoint?: string;
|
|
460
|
-
/** Public URL for serving files (CDN URL) */
|
|
461
|
-
publicUrl?: string;
|
|
462
|
-
/** Enable path-style access (required for some S3-compatible services) */
|
|
463
|
-
forcePathStyle?: boolean;
|
|
448
|
+
/**
|
|
449
|
+
* Enum field options - generic preserves enum type
|
|
450
|
+
*/
|
|
451
|
+
interface EnumFieldOptions<E extends Record<string, string | number> = Record<string, string | number>> extends BaseFieldOptions {
|
|
452
|
+
enum: E;
|
|
453
|
+
default?: E[keyof E];
|
|
454
|
+
multiple?: boolean;
|
|
464
455
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
456
|
+
/**
|
|
457
|
+
* Tags field options
|
|
458
|
+
*/
|
|
459
|
+
interface TagsFieldOptions extends BaseFieldOptions {
|
|
460
|
+
suggestions?: string[];
|
|
461
|
+
maxTags?: number;
|
|
462
|
+
allowCreate?: boolean;
|
|
468
463
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
464
|
+
/**
|
|
465
|
+
* Relationship field options
|
|
466
|
+
*/
|
|
467
|
+
interface RelationshipFieldOptions extends BaseFieldOptions {
|
|
468
|
+
/** Reference schema name */
|
|
469
|
+
ref: string;
|
|
470
|
+
/** Allow multiple selections */
|
|
471
|
+
multiple?: boolean;
|
|
472
|
+
/** Fields to display in selection */
|
|
473
|
+
displayFields?: string[];
|
|
478
474
|
}
|
|
479
|
-
|
|
480
|
-
|
|
475
|
+
/**
|
|
476
|
+
* Image field options
|
|
477
|
+
*/
|
|
478
|
+
interface ImageFieldOptions extends BaseFieldOptions {
|
|
481
479
|
folder?: string;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
480
|
+
maxSize?: number;
|
|
481
|
+
formats?: ReadonlyArray<'jpg' | 'jpeg' | 'png' | 'gif' | 'webp' | 'svg'>;
|
|
482
|
+
dimensions?: {
|
|
483
|
+
minWidth?: number;
|
|
484
|
+
maxWidth?: number;
|
|
485
|
+
minHeight?: number;
|
|
486
|
+
maxHeight?: number;
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* File field options
|
|
491
|
+
*/
|
|
492
|
+
interface FileFieldOptions extends BaseFieldOptions {
|
|
493
|
+
folder?: string;
|
|
494
|
+
maxSize?: number;
|
|
495
|
+
accept?: string[];
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Gallery field options
|
|
499
|
+
*/
|
|
500
|
+
interface GalleryFieldOptions extends ImageFieldOptions {
|
|
501
|
+
maxItems?: number;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Slug field options
|
|
505
|
+
*/
|
|
506
|
+
interface SlugFieldOptions extends BaseFieldOptions {
|
|
507
|
+
/** Field to generate slug from */
|
|
508
|
+
from: string;
|
|
509
|
+
unique?: boolean;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Email field options
|
|
513
|
+
*/
|
|
514
|
+
interface EmailFieldOptions extends BaseFieldOptions {
|
|
515
|
+
pattern?: string;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* URL field options
|
|
519
|
+
*/
|
|
520
|
+
interface URLFieldOptions extends BaseFieldOptions {
|
|
521
|
+
protocols?: string[];
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Phone field options
|
|
525
|
+
*/
|
|
526
|
+
interface PhoneFieldOptions extends BaseFieldOptions {
|
|
527
|
+
defaultCountry?: string;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Address field options
|
|
531
|
+
*/
|
|
532
|
+
interface AddressFieldOptions extends BaseFieldOptions {
|
|
533
|
+
provider?: 'google' | 'mapbox' | 'none';
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Color field options
|
|
537
|
+
*/
|
|
538
|
+
interface ColorFieldOptions extends BaseFieldOptions {
|
|
539
|
+
format?: 'hex' | 'rgb' | 'hsl';
|
|
540
|
+
presets?: string[];
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Object field options
|
|
544
|
+
*/
|
|
545
|
+
interface ObjectFieldOptions extends BaseFieldOptions {
|
|
546
|
+
/** Optional schema for validation */
|
|
547
|
+
schema?: unknown;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* All field type identifiers
|
|
551
|
+
*/
|
|
552
|
+
type FieldTypeId = 'text' | 'number' | 'boolean' | 'date' | 'datetime' | 'richtext' | 'markdown' | 'code' | 'json' | 'select' | 'enum' | 'tags' | 'image' | 'file' | 'gallery' | 'slug' | 'email' | 'url' | 'phone' | 'address' | 'color' | 'object' | 'array' | 'blocks' | 'relationship' | 'textarea';
|
|
553
|
+
/**
|
|
554
|
+
* Field type definition for array items
|
|
555
|
+
*/
|
|
556
|
+
interface ArrayItemType {
|
|
557
|
+
type: FieldTypeId;
|
|
558
|
+
options?: BaseFieldOptions;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Array field options - generic preserves item type
|
|
562
|
+
*/
|
|
563
|
+
interface ArrayFieldOptions extends BaseFieldOptions {
|
|
564
|
+
of: ArrayItemType;
|
|
565
|
+
minItems?: number;
|
|
566
|
+
maxItems?: number;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Block type definition
|
|
570
|
+
*/
|
|
571
|
+
interface BlockTypeDefinition {
|
|
572
|
+
name: string;
|
|
573
|
+
label: string;
|
|
574
|
+
fields: Record<string, FieldTypeId>;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Blocks field options
|
|
578
|
+
*/
|
|
579
|
+
interface BlocksFieldOptions extends BaseFieldOptions {
|
|
580
|
+
types: string[];
|
|
581
|
+
maxBlocks?: number;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Textarea field options
|
|
585
|
+
*/
|
|
586
|
+
interface TextareaFieldOptions extends BaseFieldOptions {
|
|
587
|
+
minLength?: number;
|
|
588
|
+
maxLength?: number;
|
|
589
|
+
rows?: number;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Field metadata stored via reflection
|
|
593
|
+
*/
|
|
594
|
+
interface FieldMetadata<T extends BaseFieldOptions = BaseFieldOptions> {
|
|
595
|
+
/** Field type identifier */
|
|
596
|
+
type: FieldTypeId;
|
|
597
|
+
/** Typed options for this field */
|
|
598
|
+
options: T;
|
|
599
|
+
/** Property key */
|
|
600
|
+
propertyKey: string | symbol;
|
|
601
|
+
/** Target class */
|
|
602
|
+
target: Type<unknown>;
|
|
603
|
+
/** Design type from TypeScript metadata */
|
|
604
|
+
designType?: unknown;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Map of field type IDs to their option interfaces
|
|
608
|
+
*/
|
|
609
|
+
interface FieldOptionsMap {
|
|
610
|
+
text: TextFieldOptions;
|
|
611
|
+
number: NumberFieldOptions;
|
|
612
|
+
boolean: BooleanFieldOptions;
|
|
613
|
+
date: DateFieldOptions;
|
|
614
|
+
datetime: DateTimeFieldOptions;
|
|
615
|
+
richtext: RichTextFieldOptions;
|
|
616
|
+
markdown: MarkdownFieldOptions;
|
|
617
|
+
code: CodeFieldOptions;
|
|
618
|
+
json: JSONFieldOptions;
|
|
619
|
+
select: SelectFieldOptions;
|
|
620
|
+
enum: EnumFieldOptions;
|
|
621
|
+
tags: TagsFieldOptions;
|
|
622
|
+
image: ImageFieldOptions;
|
|
623
|
+
file: FileFieldOptions;
|
|
624
|
+
gallery: GalleryFieldOptions;
|
|
625
|
+
slug: SlugFieldOptions;
|
|
626
|
+
email: EmailFieldOptions;
|
|
627
|
+
url: URLFieldOptions;
|
|
628
|
+
phone: PhoneFieldOptions;
|
|
629
|
+
address: AddressFieldOptions;
|
|
630
|
+
color: ColorFieldOptions;
|
|
631
|
+
object: ObjectFieldOptions;
|
|
632
|
+
array: ArrayFieldOptions;
|
|
633
|
+
blocks: BlocksFieldOptions;
|
|
634
|
+
relationship: RelationshipFieldOptions;
|
|
635
|
+
textarea: TextareaFieldOptions;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Type guard to check if a value is a FieldMetadata
|
|
639
|
+
*/
|
|
640
|
+
declare function isFieldMetadata(value: unknown): value is FieldMetadata;
|
|
641
|
+
/**
|
|
642
|
+
* Type guard to check if field type is valid
|
|
643
|
+
*/
|
|
644
|
+
declare function isValidFieldType(type: unknown): type is FieldTypeId;
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Type-safe field decorator factory.
|
|
648
|
+
*
|
|
649
|
+
* Creates a property decorator that:
|
|
650
|
+
* 1. Stores field metadata under FIELD_METADATA_KEY
|
|
651
|
+
* 2. Emits legacy Prop metadata for backwards compatibility
|
|
652
|
+
* 3. Emits legacy UI metadata for backwards compatibility
|
|
653
|
+
* 4. Applies the adapter-specific Prop decorator
|
|
654
|
+
*
|
|
655
|
+
* @param type - The field type identifier
|
|
656
|
+
* @param defaultOptions - Default options to merge with user-provided options
|
|
657
|
+
* @returns A function that creates a PropertyDecorator
|
|
658
|
+
*/
|
|
659
|
+
declare function createFieldDecorator<T extends BaseFieldOptions>(type: FieldTypeId, defaultOptions?: Partial<T>): (options?: T) => PropertyDecorator;
|
|
660
|
+
/**
|
|
661
|
+
* Get all field metadata for a class.
|
|
662
|
+
*
|
|
663
|
+
* @param target - The class constructor
|
|
664
|
+
* @returns Array of field metadata entries
|
|
665
|
+
*/
|
|
666
|
+
declare function getFieldMetadata(target: Type<unknown>): FieldMetadata[];
|
|
667
|
+
/**
|
|
668
|
+
* Get field metadata for a specific property.
|
|
669
|
+
*
|
|
670
|
+
* @param target - The class constructor
|
|
671
|
+
* @param propertyKey - The property name
|
|
672
|
+
* @returns The field metadata or undefined
|
|
673
|
+
*/
|
|
674
|
+
declare function getFieldMetadataForProperty(target: Type<unknown>, propertyKey: string | symbol): FieldMetadata | undefined;
|
|
675
|
+
|
|
676
|
+
type ResolverOptions = {
|
|
677
|
+
schema: Type;
|
|
678
|
+
};
|
|
679
|
+
type ResolverInput = (() => Type) | ResolverOptions;
|
|
680
|
+
type ResolveOptions = {
|
|
681
|
+
type: Type | Type[];
|
|
682
|
+
isArray?: boolean;
|
|
683
|
+
description?: string;
|
|
684
|
+
};
|
|
685
|
+
type ResolveInput = (() => Type | Type[]) | (() => Type)[] | ResolveOptions;
|
|
686
|
+
/**
|
|
687
|
+
* Property default value - can be a primitive, object, array, or factory function
|
|
688
|
+
*/
|
|
689
|
+
type PropDefaultValue = string | number | boolean | null | Record<string, unknown> | unknown[] | (() => unknown);
|
|
690
|
+
type PropOptions = {
|
|
691
|
+
type?: Type | Type[];
|
|
692
|
+
description?: string;
|
|
693
|
+
required?: boolean;
|
|
694
|
+
unique?: boolean;
|
|
695
|
+
default?: PropDefaultValue;
|
|
696
|
+
nullable?: boolean;
|
|
697
|
+
intl?: boolean;
|
|
698
|
+
hidden?: boolean;
|
|
699
|
+
readonly?: boolean;
|
|
700
|
+
ref?: string;
|
|
701
|
+
};
|
|
702
|
+
type BaseSchemaOptions = {
|
|
703
|
+
timestamps?: boolean;
|
|
704
|
+
};
|
|
705
|
+
type SchemaIndexOption = {
|
|
706
|
+
keys: Record<string, 1 | -1>;
|
|
707
|
+
unique?: boolean;
|
|
708
|
+
};
|
|
709
|
+
type SchemaOptions = {
|
|
710
|
+
versioning?: boolean;
|
|
711
|
+
i18n?: boolean;
|
|
712
|
+
/**
|
|
713
|
+
* Whether the schema is visible in the Content Manager.
|
|
714
|
+
* Set to false for system schemas that have dedicated admin pages.
|
|
715
|
+
* @default true
|
|
716
|
+
*/
|
|
717
|
+
visible?: boolean;
|
|
718
|
+
/**
|
|
719
|
+
* Compound indexes (adapter-specific).
|
|
720
|
+
* Mongoose: schema.index(keys, { unique })
|
|
721
|
+
*/
|
|
722
|
+
indexes?: SchemaIndexOption[];
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Map field type and options to legacy Prop decorator options.
|
|
727
|
+
* This ensures backwards compatibility with existing adapters.
|
|
728
|
+
*/
|
|
729
|
+
declare function mapFieldTypeToProp(type: FieldTypeId, options: BaseFieldOptions): PropOptions;
|
|
730
|
+
|
|
731
|
+
type UIPresentationFields = 'tab' | 'collapsible' | 'row' | 'customUI';
|
|
732
|
+
type UITypes = 'array' | 'blocks' | 'checkbox' | 'code' | 'combobox' | 'date' | 'email' | 'fileUpload' | 'group' | 'json' | 'multiSelect' | 'number' | 'phone' | 'point' | 'quantity' | 'radio' | 'relationship' | 'richText' | 'select' | 'switch' | 'table' | 'text' | 'textarea' | 'upload';
|
|
733
|
+
type UIBase = {
|
|
734
|
+
label?: string;
|
|
735
|
+
description?: string;
|
|
736
|
+
placeholder?: string;
|
|
737
|
+
type?: UITypes;
|
|
738
|
+
row?: boolean;
|
|
739
|
+
collapsible?: boolean;
|
|
740
|
+
};
|
|
741
|
+
type UISelectItem = {
|
|
742
|
+
key: string;
|
|
743
|
+
value: string;
|
|
744
|
+
};
|
|
745
|
+
type UITab = UIBase & {
|
|
746
|
+
tab: string;
|
|
747
|
+
side?: never;
|
|
748
|
+
options?: UISelectItem[];
|
|
749
|
+
};
|
|
750
|
+
type UISide = UIBase & {
|
|
751
|
+
side: true;
|
|
752
|
+
tab?: never;
|
|
753
|
+
options?: UISelectItem[];
|
|
754
|
+
};
|
|
755
|
+
type UISelect = UIBase & {
|
|
756
|
+
type: 'select';
|
|
757
|
+
multi?: boolean;
|
|
758
|
+
options: UISelectItem[];
|
|
759
|
+
};
|
|
760
|
+
type UIMultiSelect = UIBase & {
|
|
761
|
+
type: 'multiSelect';
|
|
762
|
+
options: UISelectItem[];
|
|
763
|
+
};
|
|
764
|
+
type UICombobox = UIBase & {
|
|
765
|
+
type: 'combobox';
|
|
766
|
+
options: UISelectItem[];
|
|
767
|
+
};
|
|
768
|
+
type UITableColumn = {
|
|
769
|
+
key: string;
|
|
770
|
+
header: string;
|
|
771
|
+
type?: 'text' | 'badge' | 'status' | 'input' | 'code';
|
|
772
|
+
};
|
|
773
|
+
type UITable = UIBase & {
|
|
774
|
+
type: 'table';
|
|
775
|
+
columns?: UITableColumn[];
|
|
776
|
+
};
|
|
777
|
+
type UIDecoratorOptions = UITab | UISide | UISelect | UIMultiSelect | UICombobox | UITable;
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Map field type and options to legacy UI decorator options.
|
|
781
|
+
* This ensures backwards compatibility with existing admin UI.
|
|
782
|
+
*/
|
|
783
|
+
declare function mapFieldTypeToUI(type: FieldTypeId, options: BaseFieldOptions): UIDecoratorOptions;
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Field decorator namespace with full type safety.
|
|
787
|
+
*
|
|
788
|
+
* Each decorator combines @Prop and @UI functionality into a single,
|
|
789
|
+
* semantic decorator. Validation remains explicit via Field.Validators().
|
|
790
|
+
*
|
|
791
|
+
* @example
|
|
792
|
+
* ```typescript
|
|
793
|
+
* @Schema({ slug: 'posts' })
|
|
794
|
+
* export class Post {
|
|
795
|
+
* @Field.Text({ required: true, tab: 'General' })
|
|
796
|
+
* @Field.Validators(IsString(), Length(1, 200))
|
|
797
|
+
* title: string
|
|
798
|
+
*
|
|
799
|
+
* @Field.Slug({ from: 'title', unique: true })
|
|
800
|
+
* slug: string
|
|
801
|
+
*
|
|
802
|
+
* @Field.RichText({ toolbar: 'full' })
|
|
803
|
+
* content?: string
|
|
804
|
+
*
|
|
805
|
+
* @Field.Relationship({ ref: 'users', multiple: false })
|
|
806
|
+
* author: string
|
|
807
|
+
* }
|
|
808
|
+
* ```
|
|
809
|
+
*/
|
|
810
|
+
declare const Field: {
|
|
811
|
+
/**
|
|
812
|
+
* Text field - single line text input
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* ```typescript
|
|
816
|
+
* @Field.Text({ required: true, maxLength: 200 })
|
|
817
|
+
* title: string
|
|
818
|
+
* ```
|
|
819
|
+
*/
|
|
820
|
+
readonly Text: (options?: TextFieldOptions | undefined) => PropertyDecorator;
|
|
821
|
+
/**
|
|
822
|
+
* Number field - numeric input
|
|
823
|
+
*
|
|
824
|
+
* @example
|
|
825
|
+
* ```typescript
|
|
826
|
+
* @Field.Number({ min: 0, max: 100, integer: true })
|
|
827
|
+
* quantity: number
|
|
828
|
+
* ```
|
|
829
|
+
*/
|
|
830
|
+
readonly Number: (options?: NumberFieldOptions | undefined) => PropertyDecorator;
|
|
831
|
+
/**
|
|
832
|
+
* Boolean field - true/false toggle
|
|
833
|
+
*
|
|
834
|
+
* @example
|
|
835
|
+
* ```typescript
|
|
836
|
+
* @Field.Boolean({ default: false, style: 'switch' })
|
|
837
|
+
* isPublished: boolean
|
|
838
|
+
* ```
|
|
839
|
+
*/
|
|
840
|
+
readonly Boolean: (options?: BooleanFieldOptions | undefined) => PropertyDecorator;
|
|
841
|
+
/**
|
|
842
|
+
* Date field - date picker (without time)
|
|
843
|
+
*
|
|
844
|
+
* @example
|
|
845
|
+
* ```typescript
|
|
846
|
+
* @Field.Date({ min: '2024-01-01' })
|
|
847
|
+
* publishDate: Date
|
|
848
|
+
* ```
|
|
849
|
+
*/
|
|
850
|
+
readonly Date: (options?: DateFieldOptions | undefined) => PropertyDecorator;
|
|
851
|
+
/**
|
|
852
|
+
* DateTime field - date and time picker
|
|
853
|
+
*
|
|
854
|
+
* @example
|
|
855
|
+
* ```typescript
|
|
856
|
+
* @Field.DateTime({ timezone: 'UTC' })
|
|
857
|
+
* scheduledAt: Date
|
|
858
|
+
* ```
|
|
859
|
+
*/
|
|
860
|
+
readonly DateTime: (options?: DateTimeFieldOptions | undefined) => PropertyDecorator;
|
|
861
|
+
/**
|
|
862
|
+
* RichText field - WYSIWYG editor
|
|
863
|
+
*
|
|
864
|
+
* @example
|
|
865
|
+
* ```typescript
|
|
866
|
+
* @Field.RichText({ toolbar: 'full' })
|
|
867
|
+
* content: string
|
|
868
|
+
* ```
|
|
869
|
+
*/
|
|
870
|
+
readonly RichText: (options?: RichTextFieldOptions | undefined) => PropertyDecorator;
|
|
871
|
+
/**
|
|
872
|
+
* Markdown field - markdown editor with preview
|
|
873
|
+
*
|
|
874
|
+
* @example
|
|
875
|
+
* ```typescript
|
|
876
|
+
* @Field.Markdown({ preview: true })
|
|
877
|
+
* description: string
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
readonly Markdown: (options?: MarkdownFieldOptions | undefined) => PropertyDecorator;
|
|
881
|
+
/**
|
|
882
|
+
* Code field - code editor with syntax highlighting
|
|
883
|
+
*
|
|
884
|
+
* @example
|
|
885
|
+
* ```typescript
|
|
886
|
+
* @Field.Code({ language: 'typescript' })
|
|
887
|
+
* snippet: string
|
|
888
|
+
* ```
|
|
889
|
+
*/
|
|
890
|
+
readonly Code: (options?: CodeFieldOptions | undefined) => PropertyDecorator;
|
|
891
|
+
/**
|
|
892
|
+
* JSON field - JSON editor
|
|
893
|
+
*
|
|
894
|
+
* @example
|
|
895
|
+
* ```typescript
|
|
896
|
+
* @Field.JSON()
|
|
897
|
+
* metadata: Record<string, unknown>
|
|
898
|
+
* ```
|
|
899
|
+
*/
|
|
900
|
+
readonly JSON: (options?: JSONFieldOptions | undefined) => PropertyDecorator;
|
|
901
|
+
/**
|
|
902
|
+
* Textarea field - multi-line text input
|
|
903
|
+
*
|
|
904
|
+
* @example
|
|
905
|
+
* ```typescript
|
|
906
|
+
* @Field.Textarea({ rows: 5 })
|
|
907
|
+
* summary: string
|
|
908
|
+
* ```
|
|
909
|
+
*/
|
|
910
|
+
readonly Textarea: (options?: TextareaFieldOptions | undefined) => PropertyDecorator;
|
|
911
|
+
/**
|
|
912
|
+
* Select field - dropdown selection
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* ```typescript
|
|
916
|
+
* @Field.Select({
|
|
917
|
+
* options: [
|
|
918
|
+
* { label: 'Draft', value: 'draft' },
|
|
919
|
+
* { label: 'Published', value: 'published' }
|
|
920
|
+
* ],
|
|
921
|
+
* default: 'draft'
|
|
922
|
+
* })
|
|
923
|
+
* status: string
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
readonly Select: (options: SelectFieldOptions) => PropertyDecorator;
|
|
927
|
+
/**
|
|
928
|
+
* Enum field - dropdown from TypeScript enum
|
|
929
|
+
*
|
|
930
|
+
* @example
|
|
931
|
+
* ```typescript
|
|
932
|
+
* enum Status { Draft = 'draft', Published = 'published' }
|
|
933
|
+
*
|
|
934
|
+
* @Field.Enum({ enum: Status, default: Status.Draft })
|
|
935
|
+
* status: Status
|
|
936
|
+
* ```
|
|
937
|
+
*/
|
|
938
|
+
readonly Enum: <E extends Record<string, string | number>>(options: EnumFieldOptions<E>) => PropertyDecorator;
|
|
939
|
+
/**
|
|
940
|
+
* Tags field - multi-value tag input
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```typescript
|
|
944
|
+
* @Field.Tags({ suggestions: ['tech', 'news'], maxTags: 5 })
|
|
945
|
+
* tags: string[]
|
|
946
|
+
* ```
|
|
947
|
+
*/
|
|
948
|
+
readonly Tags: (options?: TagsFieldOptions | undefined) => PropertyDecorator;
|
|
949
|
+
/**
|
|
950
|
+
* Image field - single image upload
|
|
951
|
+
*
|
|
952
|
+
* @example
|
|
953
|
+
* ```typescript
|
|
954
|
+
* @Field.Image({ folder: 'covers', formats: ['jpg', 'png', 'webp'] })
|
|
955
|
+
* coverImage: string
|
|
956
|
+
* ```
|
|
957
|
+
*/
|
|
958
|
+
readonly Image: (options?: ImageFieldOptions | undefined) => PropertyDecorator;
|
|
959
|
+
/**
|
|
960
|
+
* File field - single file upload
|
|
961
|
+
*
|
|
962
|
+
* @example
|
|
963
|
+
* ```typescript
|
|
964
|
+
* @Field.File({ folder: 'documents', accept: ['application/pdf'] })
|
|
965
|
+
* attachment: string
|
|
966
|
+
* ```
|
|
967
|
+
*/
|
|
968
|
+
readonly File: (options?: FileFieldOptions | undefined) => PropertyDecorator;
|
|
969
|
+
/**
|
|
970
|
+
* Gallery field - multiple image upload
|
|
971
|
+
*
|
|
972
|
+
* @example
|
|
973
|
+
* ```typescript
|
|
974
|
+
* @Field.Gallery({ maxItems: 10 })
|
|
975
|
+
* images: string[]
|
|
976
|
+
* ```
|
|
977
|
+
*/
|
|
978
|
+
readonly Gallery: (options?: GalleryFieldOptions | undefined) => PropertyDecorator;
|
|
979
|
+
/**
|
|
980
|
+
* Slug field - auto-generated URL-friendly string
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* ```typescript
|
|
984
|
+
* @Field.Slug({ from: 'title', unique: true })
|
|
985
|
+
* slug: string
|
|
986
|
+
* ```
|
|
987
|
+
*/
|
|
988
|
+
readonly Slug: (options?: SlugFieldOptions | undefined) => PropertyDecorator;
|
|
989
|
+
/**
|
|
990
|
+
* Email field - email input with validation pattern
|
|
991
|
+
*
|
|
992
|
+
* @example
|
|
993
|
+
* ```typescript
|
|
994
|
+
* @Field.Email({ required: true })
|
|
995
|
+
* @Field.Validators(IsEmail())
|
|
996
|
+
* email: string
|
|
997
|
+
* ```
|
|
998
|
+
*/
|
|
999
|
+
readonly Email: (options?: EmailFieldOptions | undefined) => PropertyDecorator;
|
|
1000
|
+
/**
|
|
1001
|
+
* URL field - URL input
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* ```typescript
|
|
1005
|
+
* @Field.URL({ protocols: ['https'] })
|
|
1006
|
+
* @Field.Validators(IsUrl())
|
|
1007
|
+
* website: string
|
|
1008
|
+
* ```
|
|
1009
|
+
*/
|
|
1010
|
+
readonly URL: (options?: URLFieldOptions | undefined) => PropertyDecorator;
|
|
1011
|
+
/**
|
|
1012
|
+
* Phone field - phone number input
|
|
1013
|
+
*
|
|
1014
|
+
* @example
|
|
1015
|
+
* ```typescript
|
|
1016
|
+
* @Field.Phone({ defaultCountry: 'US' })
|
|
1017
|
+
* phone: string
|
|
1018
|
+
* ```
|
|
1019
|
+
*/
|
|
1020
|
+
readonly Phone: (options?: PhoneFieldOptions | undefined) => PropertyDecorator;
|
|
1021
|
+
/**
|
|
1022
|
+
* Address field - address input with optional geocoding
|
|
1023
|
+
*
|
|
1024
|
+
* @example
|
|
1025
|
+
* ```typescript
|
|
1026
|
+
* @Field.Address({ provider: 'google' })
|
|
1027
|
+
* address: string
|
|
1028
|
+
* ```
|
|
1029
|
+
*/
|
|
1030
|
+
readonly Address: (options?: AddressFieldOptions | undefined) => PropertyDecorator;
|
|
1031
|
+
/**
|
|
1032
|
+
* Color field - color picker
|
|
1033
|
+
*
|
|
1034
|
+
* @example
|
|
1035
|
+
* ```typescript
|
|
1036
|
+
* @Field.Color({ format: 'hex', presets: ['#ff0000', '#00ff00'] })
|
|
1037
|
+
* brandColor: string
|
|
1038
|
+
* ```
|
|
1039
|
+
*/
|
|
1040
|
+
readonly Color: (options?: ColorFieldOptions | undefined) => PropertyDecorator;
|
|
1041
|
+
/**
|
|
1042
|
+
* Object field - nested object structure
|
|
1043
|
+
*
|
|
1044
|
+
* @example
|
|
1045
|
+
* ```typescript
|
|
1046
|
+
* @Field.Object()
|
|
1047
|
+
* metadata: { key: string; value: string }
|
|
1048
|
+
* ```
|
|
1049
|
+
*/
|
|
1050
|
+
readonly Object: (options?: ObjectFieldOptions | undefined) => PropertyDecorator;
|
|
1051
|
+
/**
|
|
1052
|
+
* Array field - array of items
|
|
1053
|
+
*
|
|
1054
|
+
* @example
|
|
1055
|
+
* ```typescript
|
|
1056
|
+
* @Field.Array({ of: { type: 'text' }, maxItems: 10 })
|
|
1057
|
+
* items: string[]
|
|
1058
|
+
* ```
|
|
1059
|
+
*/
|
|
1060
|
+
readonly Array: (options: ArrayFieldOptions) => PropertyDecorator;
|
|
1061
|
+
/**
|
|
1062
|
+
* Blocks field - flexible content blocks
|
|
1063
|
+
*
|
|
1064
|
+
* @example
|
|
1065
|
+
* ```typescript
|
|
1066
|
+
* @Field.Blocks({ types: ['paragraph', 'image', 'quote'] })
|
|
1067
|
+
* content: Block[]
|
|
1068
|
+
* ```
|
|
1069
|
+
*/
|
|
1070
|
+
readonly Blocks: (options?: BlocksFieldOptions | undefined) => PropertyDecorator;
|
|
1071
|
+
/**
|
|
1072
|
+
* Relationship field - reference to another schema
|
|
1073
|
+
*
|
|
1074
|
+
* @example
|
|
1075
|
+
* ```typescript
|
|
1076
|
+
* @Field.Relationship({ ref: 'users', multiple: false })
|
|
1077
|
+
* author: string
|
|
1078
|
+
*
|
|
1079
|
+
* @Field.Relationship({ ref: 'categories', multiple: true })
|
|
1080
|
+
* categories: string[]
|
|
1081
|
+
* ```
|
|
1082
|
+
*/
|
|
1083
|
+
readonly Relationship: (options?: RelationshipFieldOptions | undefined) => PropertyDecorator;
|
|
1084
|
+
/**
|
|
1085
|
+
* Validators - apply class-validator decorators
|
|
1086
|
+
*
|
|
1087
|
+
* This is an alias for the existing @Validators decorator,
|
|
1088
|
+
* keeping validation explicit and using familiar class-validator syntax.
|
|
1089
|
+
*
|
|
1090
|
+
* @example
|
|
1091
|
+
* ```typescript
|
|
1092
|
+
* @Field.Text({ required: true })
|
|
1093
|
+
* @Field.Validators(IsString(), Length(1, 200), IsNotEmpty())
|
|
1094
|
+
* title: string
|
|
1095
|
+
* ```
|
|
1096
|
+
*/
|
|
1097
|
+
readonly Validators: (...validators: PropertyDecorator[]) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
1098
|
+
};
|
|
1099
|
+
/**
|
|
1100
|
+
* Type for the Field namespace
|
|
1101
|
+
*/
|
|
1102
|
+
type FieldNamespace = typeof Field;
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Role-Based Access Control (RBAC) Type Definitions
|
|
1106
|
+
*
|
|
1107
|
+
* Type-safe permission and role management system.
|
|
1108
|
+
* Used by: Permission guards, Role service, Admin UI permission matrix
|
|
1109
|
+
*/
|
|
1110
|
+
/**
|
|
1111
|
+
* Permission source - where the permission was discovered from
|
|
1112
|
+
*/
|
|
1113
|
+
type PermissionSource = 'schema' | 'controller' | 'plugin' | 'manual';
|
|
1114
|
+
/**
|
|
1115
|
+
* Permission definition - represents a single permission in the system
|
|
1116
|
+
*/
|
|
1117
|
+
interface PermissionDefinition {
|
|
1118
|
+
/** Unique permission identifier (e.g., 'content.posts.create') */
|
|
1119
|
+
id: string;
|
|
1120
|
+
/** Human-readable name */
|
|
1121
|
+
name: string;
|
|
1122
|
+
/** Description for admin UI */
|
|
1123
|
+
description?: string;
|
|
1124
|
+
/** Group for organization (e.g., 'Content', 'Users', 'Settings') */
|
|
1125
|
+
group?: string;
|
|
1126
|
+
/** Schema name if auto-generated from schema */
|
|
1127
|
+
schema?: string;
|
|
1128
|
+
/** API identifier (e.g., 'api::posts', 'plugin::playground') */
|
|
1129
|
+
apiId?: string;
|
|
1130
|
+
/** Source of this permission */
|
|
1131
|
+
source?: PermissionSource;
|
|
1132
|
+
/** Controller name if discovered from controller */
|
|
1133
|
+
controller?: string;
|
|
1134
|
+
/** Method name if discovered from controller */
|
|
1135
|
+
method?: string;
|
|
1136
|
+
/** Plugin name if from plugin */
|
|
1137
|
+
plugin?: string;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Permission item with checked state (for UI)
|
|
1141
|
+
*/
|
|
1142
|
+
interface PermissionItem {
|
|
1143
|
+
/** Permission identifier */
|
|
1144
|
+
id: string;
|
|
1145
|
+
/** Human-readable name */
|
|
1146
|
+
name: string;
|
|
1147
|
+
/** Description */
|
|
1148
|
+
description: string;
|
|
1149
|
+
/** Whether this permission is enabled */
|
|
1150
|
+
checked?: boolean;
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Permission group for UI display
|
|
1154
|
+
*/
|
|
1155
|
+
interface PermissionGroup {
|
|
1156
|
+
/** Group identifier */
|
|
1157
|
+
id: string;
|
|
1158
|
+
/** Display name */
|
|
1159
|
+
name: string;
|
|
1160
|
+
/** API identifier */
|
|
1161
|
+
apiId?: string;
|
|
1162
|
+
/** Permissions in this group */
|
|
1163
|
+
permissions: PermissionItem[];
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Permissions categorized by type
|
|
1167
|
+
*/
|
|
1168
|
+
interface CategorizedPermissions {
|
|
1169
|
+
/** Permissions for collection types (schemas) */
|
|
1170
|
+
collectionTypes: PermissionGroup[];
|
|
1171
|
+
/** Permissions from controllers (@RequirePermission) - grouped by controller */
|
|
1172
|
+
controllers: PermissionGroup[];
|
|
1173
|
+
/** Permissions from plugins */
|
|
1174
|
+
plugins: PermissionGroup[];
|
|
1175
|
+
/** System permissions (users, settings, etc.) */
|
|
1176
|
+
system: PermissionGroup[];
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Role definition
|
|
1180
|
+
*/
|
|
1181
|
+
interface Role {
|
|
1182
|
+
/** Unique role identifier */
|
|
1183
|
+
id: string;
|
|
1184
|
+
/** Role slug (e.g., 'admin', 'authenticated', 'editor') */
|
|
1185
|
+
name: string;
|
|
1186
|
+
/** Human-readable name */
|
|
1187
|
+
displayName: string;
|
|
1188
|
+
/** Role description */
|
|
1189
|
+
description?: string;
|
|
1190
|
+
/** Array of permission IDs */
|
|
1191
|
+
permissions: string[];
|
|
1192
|
+
/** Whether this is a system role (cannot be deleted) */
|
|
1193
|
+
isSystem: boolean;
|
|
1194
|
+
/** When the role was created */
|
|
1195
|
+
createdAt: Date;
|
|
1196
|
+
/** When the role was last updated */
|
|
1197
|
+
updatedAt?: Date;
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Role with resolved permission details for UI
|
|
1201
|
+
*/
|
|
1202
|
+
interface RoleWithPermissions extends Role {
|
|
1203
|
+
/** Collection type permissions */
|
|
1204
|
+
collectionTypes: PermissionGroup[];
|
|
1205
|
+
/** Controller permissions (grouped by controller) */
|
|
1206
|
+
controllers: PermissionGroup[];
|
|
1207
|
+
/** Plugin permissions */
|
|
1208
|
+
plugins: PermissionGroup[];
|
|
1209
|
+
/** System permissions */
|
|
1210
|
+
system: PermissionGroup[];
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Default system role names
|
|
1214
|
+
*/
|
|
1215
|
+
type SystemRoleName = 'admin' | 'authenticated' | 'public';
|
|
1216
|
+
/**
|
|
1217
|
+
* System role configuration
|
|
1218
|
+
*/
|
|
1219
|
+
interface SystemRoleConfig {
|
|
1220
|
+
name: SystemRoleName;
|
|
1221
|
+
displayName: string;
|
|
1222
|
+
description: string;
|
|
1223
|
+
permissions: string[];
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Options for @RequirePermission decorator
|
|
1227
|
+
*/
|
|
1228
|
+
interface PermissionOptions {
|
|
1229
|
+
/** Permission identifier (e.g., 'content.posts.create') */
|
|
1230
|
+
id: string;
|
|
1231
|
+
/** Human-readable name */
|
|
1232
|
+
name: string;
|
|
1233
|
+
/** Description for admin UI */
|
|
1234
|
+
description?: string;
|
|
1235
|
+
/** Group for organization */
|
|
1236
|
+
group?: string;
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Resolved permission after template substitution
|
|
1240
|
+
*/
|
|
1241
|
+
interface ResolvedPermission extends PermissionOptions {
|
|
1242
|
+
/** Original template (if any) */
|
|
1243
|
+
template?: string;
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Create role request
|
|
1247
|
+
*/
|
|
1248
|
+
interface CreateRoleDto {
|
|
1249
|
+
/** Role slug (lowercase, no spaces) */
|
|
1250
|
+
name: string;
|
|
1251
|
+
/** Human-readable name */
|
|
1252
|
+
displayName: string;
|
|
1253
|
+
/** Role description */
|
|
1254
|
+
description?: string;
|
|
1255
|
+
/** Initial permissions */
|
|
1256
|
+
permissions?: string[];
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Update role request
|
|
1260
|
+
*/
|
|
1261
|
+
interface UpdateRoleDto {
|
|
1262
|
+
/** Human-readable name */
|
|
1263
|
+
displayName?: string;
|
|
1264
|
+
/** Role description */
|
|
1265
|
+
description?: string;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Update role permissions request
|
|
1269
|
+
*/
|
|
1270
|
+
interface UpdatePermissionsDto {
|
|
1271
|
+
/** Array of permission IDs to set */
|
|
1272
|
+
permissions: string[];
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Duplicate role request
|
|
1276
|
+
*/
|
|
1277
|
+
interface DuplicateRoleDto {
|
|
1278
|
+
/** Name for the new role */
|
|
1279
|
+
name: string;
|
|
1280
|
+
/** Display name for the new role */
|
|
1281
|
+
displayName?: string;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Assign role to user request
|
|
1285
|
+
*/
|
|
1286
|
+
interface AssignRoleDto {
|
|
1287
|
+
/** Role name to assign */
|
|
1288
|
+
roleName: string;
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Permission check result
|
|
1292
|
+
*/
|
|
1293
|
+
interface PermissionCheckResult {
|
|
1294
|
+
/** Whether permission is granted */
|
|
1295
|
+
granted: boolean;
|
|
1296
|
+
/** The permission that was checked */
|
|
1297
|
+
permission: string;
|
|
1298
|
+
/** User's role name */
|
|
1299
|
+
role?: string;
|
|
1300
|
+
/** Reason if denied */
|
|
1301
|
+
reason?: string;
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Role audit log entry
|
|
1305
|
+
*/
|
|
1306
|
+
interface RoleAuditEntry {
|
|
1307
|
+
/** Action performed */
|
|
1308
|
+
action: 'created' | 'updated' | 'permissions_updated' | 'deleted' | 'duplicated';
|
|
1309
|
+
/** When the action occurred */
|
|
1310
|
+
timestamp: Date;
|
|
1311
|
+
/** User who performed the action */
|
|
1312
|
+
userId?: string;
|
|
1313
|
+
/** User name for display */
|
|
1314
|
+
userName?: string;
|
|
1315
|
+
/** Changed permissions (for permissions_updated) */
|
|
1316
|
+
permissions?: {
|
|
1317
|
+
added: string[];
|
|
1318
|
+
removed: string[];
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* RBAC module options
|
|
1323
|
+
*/
|
|
1324
|
+
interface RBACModuleOptions {
|
|
1325
|
+
/** Whether RBAC is enabled */
|
|
1326
|
+
enabled?: boolean;
|
|
1327
|
+
/** Default role for new users */
|
|
1328
|
+
defaultRole?: string;
|
|
1329
|
+
/** Whether to allow public (unauthenticated) access */
|
|
1330
|
+
allowPublicAccess?: boolean;
|
|
1331
|
+
/** Whether to cache permission checks */
|
|
1332
|
+
cachePermissions?: boolean;
|
|
1333
|
+
/** Cache TTL in seconds */
|
|
1334
|
+
cacheTTL?: number;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Decorator to mark controller methods with required permissions
|
|
1339
|
+
*
|
|
1340
|
+
* The guard will check if the authenticated user's role has the specified permission.
|
|
1341
|
+
* Supports dynamic placeholders like `{schema}` which are resolved at runtime.
|
|
1342
|
+
*
|
|
1343
|
+
* @param options - Permission configuration
|
|
1344
|
+
*
|
|
1345
|
+
* @example
|
|
1346
|
+
* ```typescript
|
|
1347
|
+
* // Static permission
|
|
1348
|
+
* @Post()
|
|
1349
|
+
* @RequirePermission({
|
|
1350
|
+
* id: 'content.posts.create',
|
|
1351
|
+
* name: 'Create Posts',
|
|
1352
|
+
* description: 'Create new blog posts',
|
|
1353
|
+
* group: 'Content'
|
|
1354
|
+
* })
|
|
1355
|
+
* async create(@Body() data: CreatePostDto) { ... }
|
|
1356
|
+
*
|
|
1357
|
+
* // Dynamic permission with schema placeholder
|
|
1358
|
+
* @Get(':schema')
|
|
1359
|
+
* @RequirePermission({
|
|
1360
|
+
* id: 'content.{schema}.find',
|
|
1361
|
+
* name: 'Find',
|
|
1362
|
+
* description: 'List entries',
|
|
1363
|
+
* })
|
|
1364
|
+
* async list(@Param('schema') schema: string) { ... }
|
|
1365
|
+
* ```
|
|
1366
|
+
*/
|
|
1367
|
+
declare function RequirePermission(options: PermissionOptions): MethodDecorator;
|
|
1368
|
+
/**
|
|
1369
|
+
* Decorator to mark methods that check permissions manually
|
|
1370
|
+
*
|
|
1371
|
+
* This decorator only stores the permission ID for metadata discovery.
|
|
1372
|
+
* The actual permission check must be done in the method implementation.
|
|
1373
|
+
*
|
|
1374
|
+
* @param permission - Permission ID to check
|
|
1375
|
+
*
|
|
1376
|
+
* @example
|
|
1377
|
+
* ```typescript
|
|
1378
|
+
* @Get('sensitive-data')
|
|
1379
|
+
* @HasPermission('admin.sensitive.read')
|
|
1380
|
+
* async getSensitiveData(): Promise<SensitiveData> {
|
|
1381
|
+
* // Permission is checked by guard
|
|
1382
|
+
* return this.service.getSensitiveData()
|
|
1383
|
+
* }
|
|
1384
|
+
* ```
|
|
1385
|
+
*/
|
|
1386
|
+
declare function HasPermission(permission: string): MethodDecorator;
|
|
1387
|
+
/**
|
|
1388
|
+
* Decorator to provide additional permission metadata
|
|
1389
|
+
*
|
|
1390
|
+
* Can be combined with @RequirePermission for extra configuration.
|
|
1391
|
+
*
|
|
1392
|
+
* @param options - Additional permission options
|
|
1393
|
+
*/
|
|
1394
|
+
declare function PermissionMeta(options: Partial<PermissionOptions>): MethodDecorator;
|
|
1395
|
+
/**
|
|
1396
|
+
* Helper to extract permission metadata from a method
|
|
1397
|
+
*/
|
|
1398
|
+
declare function getPermissionMetadata(target: object, propertyKey: string | symbol): PermissionOptions | undefined;
|
|
1399
|
+
/**
|
|
1400
|
+
* Helper to check if a method has permission decorator
|
|
1401
|
+
*/
|
|
1402
|
+
declare function hasPermissionDecorator(target: object, propertyKey: string | symbol): boolean;
|
|
1403
|
+
|
|
1404
|
+
type SettingType = 'string' | 'boolean' | 'number' | 'object' | 'array';
|
|
1405
|
+
/**
|
|
1406
|
+
* Settings object type for nested settings
|
|
1407
|
+
*/
|
|
1408
|
+
interface SettingObject {
|
|
1409
|
+
[key: string]: SettingValue;
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Settings value types - union of all possible setting values
|
|
1413
|
+
* This replaces `any` in settings-related code
|
|
1414
|
+
*/
|
|
1415
|
+
type SettingValue = string | number | boolean | string[] | number[] | SettingObject | null;
|
|
1416
|
+
interface SchemaSetting {
|
|
1417
|
+
key: string;
|
|
1418
|
+
value: SettingValue;
|
|
1419
|
+
type: SettingType | string;
|
|
1420
|
+
}
|
|
1421
|
+
interface SettingsFeatureOptions<T = unknown> {
|
|
1422
|
+
group: string;
|
|
1423
|
+
schema: new () => T;
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Settings update payload for a single setting
|
|
1427
|
+
*/
|
|
1428
|
+
interface SettingsUpdatePayload {
|
|
1429
|
+
group: string;
|
|
1430
|
+
key: string;
|
|
1431
|
+
value: SettingValue;
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Settings bulk update payload
|
|
1435
|
+
*/
|
|
1436
|
+
interface SettingsBulkUpdatePayload {
|
|
1437
|
+
settings: Array<{
|
|
1438
|
+
key: string;
|
|
1439
|
+
value: SettingValue;
|
|
1440
|
+
}>;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Settings record type for typed settings groups
|
|
1444
|
+
*/
|
|
1445
|
+
type SettingsRecord = Record<string, SettingValue>;
|
|
1446
|
+
/**
|
|
1447
|
+
* Section variant for special styling (e.g., danger zone)
|
|
1448
|
+
*/
|
|
1449
|
+
type SettingSectionVariant = 'default' | 'danger';
|
|
1450
|
+
/**
|
|
1451
|
+
* Section definition for grouping settings fields visually
|
|
1452
|
+
*/
|
|
1453
|
+
interface SettingSectionDefinition {
|
|
1454
|
+
/** Unique section identifier */
|
|
1455
|
+
name: string;
|
|
1456
|
+
/** Display label for the section */
|
|
1457
|
+
label: string;
|
|
1458
|
+
/** Icon identifier (lucide icon name) */
|
|
1459
|
+
icon?: string;
|
|
1460
|
+
/** Description of the section */
|
|
1461
|
+
description?: string;
|
|
1462
|
+
/** Display order within the settings group */
|
|
1463
|
+
order?: number;
|
|
1464
|
+
/** Visual variant for the section */
|
|
1465
|
+
variant?: SettingSectionVariant;
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Options for the @Settings() class decorator
|
|
1469
|
+
*/
|
|
1470
|
+
interface SettingsDecoratorOptions {
|
|
1471
|
+
/** Unique group identifier for this settings class */
|
|
1472
|
+
group: string;
|
|
1473
|
+
/** Display label in admin UI */
|
|
1474
|
+
label: string;
|
|
1475
|
+
/** Icon identifier for admin UI */
|
|
1476
|
+
icon?: string;
|
|
1477
|
+
/** Description of what these settings control */
|
|
1478
|
+
description?: string;
|
|
1479
|
+
/** Display order in settings list */
|
|
1480
|
+
order?: number;
|
|
1481
|
+
/** Section definitions for grouping fields visually */
|
|
1482
|
+
sections?: SettingSectionDefinition[];
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Base options for Setting field decorators
|
|
1486
|
+
*/
|
|
1487
|
+
interface SettingFieldBaseOptions {
|
|
1488
|
+
/** Display label in admin UI */
|
|
1489
|
+
label: string;
|
|
1490
|
+
/** Help text/description */
|
|
1491
|
+
description?: string;
|
|
1492
|
+
/** Default value */
|
|
1493
|
+
default?: SettingValue;
|
|
1494
|
+
/** Display order */
|
|
1495
|
+
order?: number;
|
|
1496
|
+
/** Hide from admin UI */
|
|
1497
|
+
hidden?: boolean;
|
|
1498
|
+
/** Make read-only in admin UI */
|
|
1499
|
+
readonly?: boolean;
|
|
1500
|
+
/** Section this field belongs to (references section name) */
|
|
1501
|
+
section?: string;
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Text setting field options
|
|
1505
|
+
*/
|
|
1506
|
+
interface SettingTextOptions extends SettingFieldBaseOptions {
|
|
1507
|
+
default?: string;
|
|
1508
|
+
minLength?: number;
|
|
1509
|
+
maxLength?: number;
|
|
1510
|
+
placeholder?: string;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Number setting field options
|
|
1514
|
+
*/
|
|
1515
|
+
interface SettingNumberOptions extends SettingFieldBaseOptions {
|
|
1516
|
+
default?: number;
|
|
1517
|
+
min?: number;
|
|
1518
|
+
max?: number;
|
|
1519
|
+
step?: number;
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Boolean setting field options
|
|
1523
|
+
*/
|
|
1524
|
+
interface SettingBooleanOptions extends SettingFieldBaseOptions {
|
|
1525
|
+
default?: boolean;
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Select setting field options
|
|
1529
|
+
*/
|
|
1530
|
+
interface SettingSelectOptions extends SettingFieldBaseOptions {
|
|
1531
|
+
options: ReadonlyArray<{
|
|
1532
|
+
label: string;
|
|
1533
|
+
value: string;
|
|
1534
|
+
}> | ReadonlyArray<string>;
|
|
1535
|
+
default?: string | string[];
|
|
1536
|
+
/** Enable multi-select mode */
|
|
1537
|
+
multiple?: boolean;
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Secret setting field options (for API keys, passwords, etc.)
|
|
1541
|
+
*/
|
|
1542
|
+
interface SettingSecretOptions extends SettingFieldBaseOptions {
|
|
1543
|
+
/** Mask value in UI (show only last 4 characters) */
|
|
1544
|
+
masked?: boolean;
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Image setting field options
|
|
1548
|
+
*/
|
|
1549
|
+
interface SettingImageOptions extends SettingFieldBaseOptions {
|
|
1550
|
+
folder?: string;
|
|
1551
|
+
maxSize?: number;
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* JSON setting field options
|
|
1555
|
+
*/
|
|
1556
|
+
interface SettingJSONOptions extends SettingFieldBaseOptions {
|
|
1557
|
+
default?: SettingObject;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Setting field type identifiers
|
|
1561
|
+
*/
|
|
1562
|
+
type SettingFieldTypeId = 'text' | 'number' | 'boolean' | 'select' | 'secret' | 'image' | 'json' | 'textarea';
|
|
1563
|
+
/**
|
|
1564
|
+
* Setting field metadata stored via reflection
|
|
1565
|
+
*/
|
|
1566
|
+
interface SettingFieldMetadata<T extends SettingFieldBaseOptions = SettingFieldBaseOptions> {
|
|
1567
|
+
/** Field type identifier */
|
|
1568
|
+
type: SettingFieldTypeId;
|
|
1569
|
+
/** Typed options for this field */
|
|
1570
|
+
options: T;
|
|
1571
|
+
/** Property key */
|
|
1572
|
+
propertyKey: string | symbol;
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Type guard to check if a value is SettingFieldMetadata
|
|
1576
|
+
*/
|
|
1577
|
+
declare function isSettingFieldMetadata(value: unknown): value is SettingFieldMetadata;
|
|
1578
|
+
|
|
1579
|
+
type MethodMetadata = {
|
|
1580
|
+
name: string;
|
|
1581
|
+
returnType: {
|
|
1582
|
+
type: Type;
|
|
1583
|
+
isArray: boolean;
|
|
1584
|
+
};
|
|
1585
|
+
params: {
|
|
1586
|
+
arg: string;
|
|
1587
|
+
type: string;
|
|
1588
|
+
name: string;
|
|
1589
|
+
}[];
|
|
1590
|
+
httpMethod: string;
|
|
1591
|
+
routePath: string;
|
|
1592
|
+
guards?: Type[];
|
|
1593
|
+
interceptors?: Type[];
|
|
1594
|
+
pipes?: Type[];
|
|
1595
|
+
};
|
|
1596
|
+
type ControllerMetadata = {
|
|
1597
|
+
name: string;
|
|
1598
|
+
basePath: string;
|
|
1599
|
+
methods: MethodMetadata[];
|
|
1600
|
+
};
|
|
1601
|
+
type SchemaPropertyValidation = {
|
|
1602
|
+
type: string;
|
|
1603
|
+
name: string;
|
|
1604
|
+
constraints?: unknown[];
|
|
1605
|
+
};
|
|
1606
|
+
type SchemaProperty = {
|
|
1607
|
+
name: string;
|
|
1608
|
+
type: string;
|
|
1609
|
+
isArray: boolean;
|
|
1610
|
+
unique: boolean;
|
|
1611
|
+
required: boolean;
|
|
1612
|
+
validations: SchemaPropertyValidation[];
|
|
1613
|
+
ui?: UIDecoratorOptions;
|
|
1614
|
+
ref?: string;
|
|
1615
|
+
};
|
|
1616
|
+
type SchemaMetadata = {
|
|
1617
|
+
name: string;
|
|
1618
|
+
className?: string;
|
|
1619
|
+
apiName?: string;
|
|
1620
|
+
displayName?: string;
|
|
1621
|
+
properties: SchemaProperty[];
|
|
1622
|
+
options?: SchemaOptions;
|
|
1623
|
+
/** Settings-specific options (only present for settings schemas) */
|
|
1624
|
+
settingsOptions?: SettingsDecoratorOptions;
|
|
1625
|
+
};
|
|
1626
|
+
|
|
1627
|
+
type InitialConfig = {
|
|
1628
|
+
title?: string;
|
|
1629
|
+
description?: string;
|
|
1630
|
+
env: string;
|
|
1631
|
+
schemas: SchemaMetadata[];
|
|
1632
|
+
settings: SchemaMetadata[];
|
|
1633
|
+
};
|
|
1634
|
+
|
|
1635
|
+
/**
|
|
1636
|
+
* Authenticated user returned from auth strategies
|
|
1637
|
+
*/
|
|
1638
|
+
interface AuthUser {
|
|
1639
|
+
/** Unique user identifier */
|
|
1640
|
+
id: string;
|
|
1641
|
+
/** User email address */
|
|
1642
|
+
email: string;
|
|
1643
|
+
/** User role for authorization */
|
|
1644
|
+
role: string;
|
|
1645
|
+
/** Optional additional user data */
|
|
1646
|
+
[key: string]: unknown;
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Login credentials (strategy-specific)
|
|
1650
|
+
*/
|
|
1651
|
+
interface LoginCredentials {
|
|
1652
|
+
email: string;
|
|
1653
|
+
password: string;
|
|
1654
|
+
[key: string]: unknown;
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Registration data (strategy-specific)
|
|
1658
|
+
*/
|
|
1659
|
+
interface RegisterData {
|
|
1660
|
+
email: string;
|
|
1661
|
+
password: string;
|
|
1662
|
+
name: string;
|
|
1663
|
+
role?: string;
|
|
1664
|
+
[key: string]: unknown;
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Authentication result containing access token
|
|
1668
|
+
*/
|
|
1669
|
+
interface AuthResult {
|
|
1670
|
+
access_token: string;
|
|
1671
|
+
refresh_token?: string;
|
|
1672
|
+
expires_in?: number;
|
|
1673
|
+
token_type?: string;
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* JWT-specific configuration
|
|
1677
|
+
*/
|
|
1678
|
+
interface JwtAuthConfig {
|
|
1679
|
+
/** JWT secret key */
|
|
1680
|
+
secret: string;
|
|
1681
|
+
/** Token expiration time (e.g., '7d', '1h') */
|
|
1682
|
+
expiresIn?: string;
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Supabase-specific auth configuration
|
|
1686
|
+
*/
|
|
1687
|
+
interface SupabaseAuthConfig {
|
|
1688
|
+
/** Supabase project URL */
|
|
1689
|
+
supabaseUrl: string;
|
|
1690
|
+
/** Supabase anon/public key */
|
|
1691
|
+
supabaseKey: string;
|
|
1692
|
+
/** Default role for new users */
|
|
1693
|
+
defaultRole?: string;
|
|
1694
|
+
}
|
|
1695
|
+
/**
|
|
1696
|
+
* Auth configuration for MagnetModuleOptions
|
|
1697
|
+
*/
|
|
1698
|
+
interface AuthConfig {
|
|
1699
|
+
/** Strategy name to use (default: 'jwt') */
|
|
1700
|
+
strategy?: string;
|
|
1701
|
+
/** JWT-specific configuration */
|
|
1702
|
+
jwt?: JwtAuthConfig;
|
|
1703
|
+
/** Supabase-specific configuration (when strategy: 'supabase') */
|
|
1704
|
+
supabaseUrl?: string;
|
|
1705
|
+
/** Supabase anon/public key */
|
|
1706
|
+
supabaseKey?: string;
|
|
1707
|
+
/** Supabase service role key (required for admin operations like listUsers) */
|
|
1708
|
+
supabaseServiceKey?: string;
|
|
1709
|
+
/** Default role for new Supabase users */
|
|
1710
|
+
defaultRole?: string;
|
|
1711
|
+
/** Allow extensible config for custom strategies */
|
|
1712
|
+
[key: string]: unknown;
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Information about the active authentication strategy, including
|
|
1716
|
+
* whether it's an external provider and what OAuth providers it has configured.
|
|
1717
|
+
*
|
|
1718
|
+
* Returned by `AuthStrategy.getAuthInfo()` for external adapters (Supabase, Clerk, etc.).
|
|
1719
|
+
*/
|
|
1720
|
+
interface ExternalAuthInfo {
|
|
1721
|
+
/** Strategy identifier (e.g., 'supabase', 'clerk', 'jwt') */
|
|
1722
|
+
strategy: string;
|
|
1723
|
+
/** Whether authentication is handled by an external provider */
|
|
1724
|
+
isExternal: boolean;
|
|
1725
|
+
/** List of OAuth provider names configured in the external service (e.g., ['google', 'github']) */
|
|
1726
|
+
providers: string[];
|
|
1727
|
+
/** Additional provider-specific settings (e.g., disable_signup, autoconfirm) */
|
|
1728
|
+
providerSettings?: Record<string, unknown>;
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Abstract base class for authentication strategies.
|
|
1732
|
+
* Similar to StorageAdapter, custom strategies must extend this class.
|
|
1733
|
+
*
|
|
1734
|
+
* @example
|
|
1735
|
+
* ```typescript
|
|
1736
|
+
* export class SupabaseAuthStrategy extends AuthStrategy {
|
|
1737
|
+
* readonly name = 'supabase'
|
|
1738
|
+
*
|
|
1739
|
+
* async validate(payload: unknown): Promise<AuthUser | null> {
|
|
1740
|
+
* // Validate Supabase JWT token
|
|
1741
|
+
* }
|
|
1742
|
+
*
|
|
1743
|
+
* async login(credentials: LoginCredentials): Promise<AuthResult> {
|
|
1744
|
+
* // Authenticate with Supabase
|
|
1745
|
+
* }
|
|
1746
|
+
*
|
|
1747
|
+
* async register(data: RegisterData): Promise<AuthUser> {
|
|
1748
|
+
* // Register user in Supabase
|
|
1749
|
+
* }
|
|
1750
|
+
*
|
|
1751
|
+
* async validateCredentials(email: string, password: string): Promise<AuthUser | null> {
|
|
1752
|
+
* // Validate user credentials
|
|
1753
|
+
* }
|
|
1754
|
+
* }
|
|
1755
|
+
* ```
|
|
1756
|
+
*/
|
|
1757
|
+
declare abstract class AuthStrategy {
|
|
1758
|
+
/**
|
|
1759
|
+
* Unique identifier for this strategy
|
|
1760
|
+
*/
|
|
1761
|
+
abstract readonly name: string;
|
|
1762
|
+
/**
|
|
1763
|
+
* Initialize the auth strategy (optional setup)
|
|
1764
|
+
*/
|
|
1765
|
+
initialize?(): Promise<void>;
|
|
1766
|
+
/**
|
|
1767
|
+
* Validate a token/payload and return the authenticated user
|
|
1768
|
+
* @param payload - Strategy-specific payload (e.g., JWT payload, session data)
|
|
1769
|
+
* @returns Authenticated user or null if invalid
|
|
1770
|
+
*/
|
|
1771
|
+
abstract validate(payload: unknown): Promise<AuthUser | null>;
|
|
1772
|
+
/**
|
|
1773
|
+
* Authenticate user with credentials and return tokens
|
|
1774
|
+
* @param credentials - Login credentials (strategy-specific)
|
|
1775
|
+
* @returns Authentication result with access token
|
|
1776
|
+
*/
|
|
1777
|
+
abstract login(credentials: LoginCredentials): Promise<AuthResult>;
|
|
1778
|
+
/**
|
|
1779
|
+
* Register a new user
|
|
1780
|
+
* @param data - Registration data
|
|
1781
|
+
* @returns The created user
|
|
1782
|
+
*/
|
|
1783
|
+
abstract register(data: RegisterData): Promise<AuthUser>;
|
|
1784
|
+
/**
|
|
1785
|
+
* Validate user credentials (used internally by login)
|
|
1786
|
+
* @param email - User email
|
|
1787
|
+
* @param password - User password
|
|
1788
|
+
* @returns User if valid, null otherwise
|
|
1789
|
+
*/
|
|
1790
|
+
abstract validateCredentials(email: string, password: string): Promise<AuthUser | null>;
|
|
1791
|
+
/**
|
|
1792
|
+
* Refresh an access token (optional)
|
|
1793
|
+
* @param refreshToken - The refresh token
|
|
1794
|
+
* @returns New authentication result
|
|
1795
|
+
*/
|
|
1796
|
+
refresh?(refreshToken: string): Promise<AuthResult>;
|
|
1797
|
+
/**
|
|
1798
|
+
* Logout/invalidate tokens (optional)
|
|
1799
|
+
* @param token - The token to invalidate
|
|
1800
|
+
*/
|
|
1801
|
+
logout?(token: string): Promise<void>;
|
|
1802
|
+
/**
|
|
1803
|
+
* Check if any users exist in the system (optional)
|
|
1804
|
+
* Strategies that manage their own user storage should implement this.
|
|
1805
|
+
* If not implemented, AuthService will fall back to checking the local database.
|
|
1806
|
+
* @returns true if users exist, false otherwise
|
|
1807
|
+
*/
|
|
1808
|
+
hasUsers?(): Promise<boolean>;
|
|
1809
|
+
/**
|
|
1810
|
+
* Get information about the auth strategy and its configured providers (optional).
|
|
1811
|
+
* External adapters (Supabase, Clerk) implement this to report which OAuth
|
|
1812
|
+
* providers are configured on their side. If not implemented, the strategy
|
|
1813
|
+
* is assumed to be built-in (JWT).
|
|
1814
|
+
* @returns Auth info including strategy name, external flag, and provider list
|
|
1815
|
+
*/
|
|
1816
|
+
getAuthInfo?(): Promise<ExternalAuthInfo>;
|
|
1817
|
+
/**
|
|
1818
|
+
* Get the Passport strategy name (for guards)
|
|
1819
|
+
* Returns the name to use with AuthGuard()
|
|
1820
|
+
*/
|
|
1821
|
+
getPassportStrategyName(): string;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
/**
|
|
1825
|
+
* SMTP configuration for Nodemailer adapter
|
|
1826
|
+
*/
|
|
1827
|
+
interface NodemailerConfig {
|
|
1828
|
+
/** SMTP server hostname */
|
|
1829
|
+
host: string;
|
|
1830
|
+
/** SMTP server port (default: 587) */
|
|
1831
|
+
port?: number;
|
|
1832
|
+
/** Use TLS (default: true for port 465, false otherwise) */
|
|
1833
|
+
secure?: boolean;
|
|
1834
|
+
/** SMTP authentication credentials */
|
|
1835
|
+
auth: {
|
|
1836
|
+
/** SMTP username */
|
|
1837
|
+
user: string;
|
|
1838
|
+
/** SMTP password */
|
|
1839
|
+
pass: string;
|
|
1840
|
+
};
|
|
1841
|
+
/** Connection timeout in milliseconds */
|
|
1842
|
+
connectionTimeout?: number;
|
|
1843
|
+
/** Greeting timeout in milliseconds */
|
|
1844
|
+
greetingTimeout?: number;
|
|
1845
|
+
/** Socket timeout in milliseconds */
|
|
1846
|
+
socketTimeout?: number;
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Resend API configuration
|
|
1850
|
+
*/
|
|
1851
|
+
interface ResendConfig {
|
|
1852
|
+
/** Resend API key */
|
|
1853
|
+
apiKey: string;
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Email adapter name type — extensible for custom adapters
|
|
1857
|
+
*/
|
|
1858
|
+
type EmailAdapterName = 'nodemailer' | 'resend' | (string & {});
|
|
1859
|
+
/**
|
|
1860
|
+
* Email system configuration for MagnetModuleOptions
|
|
1861
|
+
*/
|
|
1862
|
+
interface EmailConfig {
|
|
1863
|
+
/** Which email adapter to use */
|
|
1864
|
+
adapter: EmailAdapterName;
|
|
1865
|
+
/** Nodemailer SMTP configuration (when adapter is 'nodemailer') */
|
|
1866
|
+
nodemailer?: NodemailerConfig;
|
|
1867
|
+
/** Resend API configuration (when adapter is 'resend') */
|
|
1868
|
+
resend?: ResendConfig;
|
|
1869
|
+
/** Default email settings */
|
|
1870
|
+
defaults?: {
|
|
1871
|
+
/** Default sender email address */
|
|
1872
|
+
from?: string;
|
|
1873
|
+
/** Default reply-to address */
|
|
1874
|
+
replyTo?: string;
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Email attachment
|
|
1879
|
+
*/
|
|
1880
|
+
interface EmailAttachment {
|
|
1881
|
+
/** Attachment filename */
|
|
1882
|
+
filename: string;
|
|
1883
|
+
/** Attachment content (Buffer, string, or URL) */
|
|
1884
|
+
content: Buffer | string;
|
|
1885
|
+
/** MIME type of the attachment */
|
|
1886
|
+
contentType?: string;
|
|
1887
|
+
/** Content disposition (default: 'attachment') */
|
|
1888
|
+
disposition?: 'attachment' | 'inline';
|
|
1889
|
+
/** Content ID for inline attachments */
|
|
1890
|
+
cid?: string;
|
|
1891
|
+
}
|
|
1892
|
+
/**
|
|
1893
|
+
* Options for sending an email
|
|
1894
|
+
*/
|
|
1895
|
+
interface SendEmailOptions {
|
|
1896
|
+
/** Recipient email address(es) */
|
|
1897
|
+
to: string | string[];
|
|
1898
|
+
/** Sender email address (overrides default) */
|
|
1899
|
+
from?: string;
|
|
1900
|
+
/** Email subject line */
|
|
1901
|
+
subject: string;
|
|
1902
|
+
/** HTML body content */
|
|
1903
|
+
html?: string;
|
|
1904
|
+
/** Plain text body content */
|
|
1905
|
+
text?: string;
|
|
1906
|
+
/** Reply-to address (overrides default) */
|
|
1907
|
+
replyTo?: string;
|
|
1908
|
+
/** CC recipients */
|
|
1909
|
+
cc?: string | string[];
|
|
1910
|
+
/** BCC recipients */
|
|
1911
|
+
bcc?: string | string[];
|
|
1912
|
+
/** Email attachments */
|
|
1913
|
+
attachments?: EmailAttachment[];
|
|
1914
|
+
/** Custom headers */
|
|
1915
|
+
headers?: Record<string, string>;
|
|
1916
|
+
/** Tags for categorization (supported by some providers) */
|
|
1917
|
+
tags?: Array<{
|
|
1918
|
+
name: string;
|
|
1919
|
+
value: string;
|
|
1920
|
+
}>;
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* Result of sending an email
|
|
1924
|
+
*/
|
|
1925
|
+
interface SendEmailResult {
|
|
1926
|
+
/** Provider-assigned message ID */
|
|
1927
|
+
id?: string;
|
|
1928
|
+
/** Whether the email was accepted for delivery */
|
|
1929
|
+
accepted: boolean;
|
|
1930
|
+
/** List of accepted recipient addresses */
|
|
1931
|
+
acceptedAddresses?: string[];
|
|
1932
|
+
/** List of rejected recipient addresses */
|
|
1933
|
+
rejectedAddresses?: string[];
|
|
1934
|
+
/** Error message if sending failed */
|
|
1935
|
+
error?: string;
|
|
1936
|
+
}
|
|
1937
|
+
/**
|
|
1938
|
+
* Abstract base class for email adapters.
|
|
1939
|
+
*
|
|
1940
|
+
* All email providers must extend this class and implement the abstract methods.
|
|
1941
|
+
* Follows the same pattern as `StorageAdapter` and `DatabaseAdapter`.
|
|
1942
|
+
*
|
|
1943
|
+
* @example
|
|
1944
|
+
* ```typescript
|
|
1945
|
+
* export class NodemailerEmailAdapter extends EmailAdapter {
|
|
1946
|
+
* readonly name = 'nodemailer'
|
|
1947
|
+
*
|
|
1948
|
+
* async send(options: SendEmailOptions): Promise<SendEmailResult> {
|
|
1949
|
+
* // Send via SMTP using Nodemailer
|
|
1950
|
+
* }
|
|
1951
|
+
*
|
|
1952
|
+
* async sendBatch(emails: SendEmailOptions[]): Promise<SendEmailResult[]> {
|
|
1953
|
+
* return Promise.all(emails.map(email => this.send(email)))
|
|
1954
|
+
* }
|
|
1955
|
+
*
|
|
1956
|
+
* async verify(): Promise<boolean> {
|
|
1957
|
+
* // Test SMTP connection
|
|
1958
|
+
* }
|
|
1959
|
+
* }
|
|
1960
|
+
* ```
|
|
1961
|
+
*/
|
|
1962
|
+
declare abstract class EmailAdapter {
|
|
1963
|
+
/**
|
|
1964
|
+
* Unique identifier for this adapter
|
|
1965
|
+
*/
|
|
1966
|
+
abstract readonly name: string;
|
|
1967
|
+
/**
|
|
1968
|
+
* Send a single email
|
|
1969
|
+
* @param options - Email send options (to, subject, html, etc.)
|
|
1970
|
+
* @returns Result with delivery status
|
|
1971
|
+
*/
|
|
1972
|
+
abstract send(options: SendEmailOptions): Promise<SendEmailResult>;
|
|
1973
|
+
/**
|
|
1974
|
+
* Send multiple emails in batch
|
|
1975
|
+
* @param emails - Array of email options
|
|
1976
|
+
* @returns Array of results (one per email)
|
|
1977
|
+
*/
|
|
1978
|
+
abstract sendBatch(emails: SendEmailOptions[]): Promise<SendEmailResult[]>;
|
|
1979
|
+
/**
|
|
1980
|
+
* Verify the adapter connection/configuration
|
|
1981
|
+
* @returns true if the adapter is properly configured and can send emails
|
|
1982
|
+
*/
|
|
1983
|
+
abstract verify(): Promise<boolean>;
|
|
1984
|
+
/**
|
|
1985
|
+
* Optional cleanup/disconnect method
|
|
1986
|
+
*/
|
|
1987
|
+
dispose?(): Promise<void>;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
/**
|
|
1991
|
+
* Query operator types for MongoDB-style queries
|
|
1992
|
+
*/
|
|
1993
|
+
type QueryOperator<T> = {
|
|
1994
|
+
/** Equal to */
|
|
1995
|
+
$eq?: T;
|
|
1996
|
+
/** Not equal to */
|
|
1997
|
+
$ne?: T;
|
|
1998
|
+
/** Greater than */
|
|
1999
|
+
$gt?: T;
|
|
2000
|
+
/** Greater than or equal to */
|
|
2001
|
+
$gte?: T;
|
|
2002
|
+
/** Less than */
|
|
2003
|
+
$lt?: T;
|
|
2004
|
+
/** Less than or equal to */
|
|
2005
|
+
$lte?: T;
|
|
2006
|
+
/** In array */
|
|
2007
|
+
$in?: T[];
|
|
2008
|
+
/** Not in array */
|
|
2009
|
+
$nin?: T[];
|
|
2010
|
+
/** Field exists */
|
|
2011
|
+
$exists?: boolean;
|
|
2012
|
+
/** Regular expression match */
|
|
2013
|
+
$regex?: string | RegExp;
|
|
2014
|
+
/** Regex options (i, m, s, x) */
|
|
2015
|
+
$options?: string;
|
|
2016
|
+
};
|
|
2017
|
+
/**
|
|
2018
|
+
* Filter value that supports both direct values and operators
|
|
2019
|
+
*/
|
|
2020
|
+
type FilterValue<T> = T | QueryOperator<T>;
|
|
2021
|
+
/**
|
|
2022
|
+
* Full filter query type with logical operators
|
|
2023
|
+
*/
|
|
2024
|
+
type FilterQuery<Schema> = {
|
|
2025
|
+
[K in keyof Schema]?: FilterValue<Schema[K]>;
|
|
2026
|
+
} & {
|
|
2027
|
+
/** Logical AND */
|
|
2028
|
+
$and?: FilterQuery<Schema>[];
|
|
2029
|
+
/** Logical OR */
|
|
2030
|
+
$or?: FilterQuery<Schema>[];
|
|
2031
|
+
/** Logical NOR */
|
|
2032
|
+
$nor?: FilterQuery<Schema>[];
|
|
2033
|
+
};
|
|
2034
|
+
/**
|
|
2035
|
+
* Sort direction
|
|
2036
|
+
*/
|
|
2037
|
+
type SortDirection = 1 | -1 | 'asc' | 'desc';
|
|
2038
|
+
/**
|
|
2039
|
+
* Sort specification
|
|
2040
|
+
*/
|
|
2041
|
+
type SortQuery<Schema> = {
|
|
2042
|
+
[K in keyof Schema]?: SortDirection;
|
|
2043
|
+
};
|
|
2044
|
+
/**
|
|
2045
|
+
* Projection specification for field selection
|
|
2046
|
+
*/
|
|
2047
|
+
type ProjectionQuery<Schema> = {
|
|
2048
|
+
[K in keyof Schema]?: 0 | 1 | boolean;
|
|
2049
|
+
};
|
|
2050
|
+
/**
|
|
2051
|
+
* Query execution options
|
|
2052
|
+
*/
|
|
2053
|
+
interface QueryOptions {
|
|
2054
|
+
/** Maximum number of documents to return */
|
|
2055
|
+
limit?: number;
|
|
2056
|
+
/** Number of documents to skip */
|
|
2057
|
+
skip?: number;
|
|
2058
|
+
/** Return plain objects instead of documents */
|
|
2059
|
+
lean?: boolean;
|
|
2060
|
+
}
|
|
2061
|
+
/**
|
|
2062
|
+
* Paginated query result
|
|
2063
|
+
*/
|
|
2064
|
+
interface PaginatedResult<T> {
|
|
2065
|
+
/** Result data */
|
|
2066
|
+
data: T[];
|
|
2067
|
+
/** Total count of matching documents */
|
|
2068
|
+
total: number;
|
|
2069
|
+
/** Current page (if using skip/limit) */
|
|
2070
|
+
page?: number;
|
|
2071
|
+
/** Page size */
|
|
2072
|
+
limit?: number;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
/**
|
|
2076
|
+
* Abstract query builder for fluent database queries.
|
|
2077
|
+
* Provides chainable methods for filtering, sorting, pagination, and projection.
|
|
2078
|
+
*
|
|
2079
|
+
* @example
|
|
2080
|
+
* ```typescript
|
|
2081
|
+
* const users = await userModel.query()
|
|
2082
|
+
* .where({ status: 'active' })
|
|
2083
|
+
* .sort({ createdAt: -1 })
|
|
2084
|
+
* .limit(10)
|
|
2085
|
+
* .exec()
|
|
2086
|
+
* ```
|
|
2087
|
+
*/
|
|
2088
|
+
declare abstract class QueryBuilder<Schema> {
|
|
2089
|
+
/**
|
|
2090
|
+
* Add filter conditions to the query
|
|
2091
|
+
* @param filter Filter conditions with optional operators
|
|
2092
|
+
*/
|
|
2093
|
+
abstract where(filter: FilterQuery<Schema>): this;
|
|
2094
|
+
/**
|
|
2095
|
+
* Add additional AND conditions
|
|
2096
|
+
* @param filter Filter conditions to AND with existing filters
|
|
2097
|
+
*/
|
|
2098
|
+
abstract and(filter: FilterQuery<Schema>): this;
|
|
2099
|
+
/**
|
|
2100
|
+
* Add OR conditions
|
|
2101
|
+
* @param filters Array of filter conditions for OR logic
|
|
2102
|
+
*/
|
|
2103
|
+
abstract or(filters: FilterQuery<Schema>[]): this;
|
|
2104
|
+
/**
|
|
2105
|
+
* Sort results by specified fields
|
|
2106
|
+
* @param sort Sort specification with field names and directions
|
|
2107
|
+
*/
|
|
2108
|
+
abstract sort(sort: SortQuery<Schema>): this;
|
|
2109
|
+
/**
|
|
2110
|
+
* Limit the number of results
|
|
2111
|
+
* @param count Maximum number of documents to return
|
|
2112
|
+
*/
|
|
2113
|
+
abstract limit(count: number): this;
|
|
2114
|
+
/**
|
|
2115
|
+
* Skip a number of results (for pagination)
|
|
2116
|
+
* @param count Number of documents to skip
|
|
2117
|
+
*/
|
|
2118
|
+
abstract skip(count: number): this;
|
|
2119
|
+
/**
|
|
2120
|
+
* Execute with pagination info
|
|
2121
|
+
* @param page Page number (1-indexed)
|
|
2122
|
+
* @param perPage Items per page
|
|
2123
|
+
* @returns Data array with pagination metadata
|
|
2124
|
+
*/
|
|
2125
|
+
abstract paginate(page?: number, perPage?: number): Promise<PaginatedResult<BaseSchema<Schema>>>;
|
|
2126
|
+
/**
|
|
2127
|
+
* Select specific fields to return (inclusion)
|
|
2128
|
+
* @param projection Field selection (1 to include, 0 to exclude)
|
|
2129
|
+
*/
|
|
2130
|
+
abstract select(projection: ProjectionQuery<Schema>): this;
|
|
2131
|
+
/**
|
|
2132
|
+
* Exclude specific fields from results
|
|
2133
|
+
* @param fields Array of field names to exclude
|
|
2134
|
+
*/
|
|
2135
|
+
exclude(fields: (keyof Schema | string)[]): this;
|
|
2136
|
+
/**
|
|
2137
|
+
* Set the locale for query results
|
|
2138
|
+
* @param locale The locale to use
|
|
2139
|
+
*/
|
|
2140
|
+
abstract locale(locale: string): this;
|
|
2141
|
+
/**
|
|
2142
|
+
* Set the version filter for query
|
|
2143
|
+
* @param versionId The version ID or status
|
|
2144
|
+
*/
|
|
2145
|
+
abstract version(versionId: string): this;
|
|
2146
|
+
/**
|
|
2147
|
+
* Execute the query and return all matching documents
|
|
2148
|
+
*/
|
|
2149
|
+
abstract exec(): Promise<BaseSchema<Schema>[]>;
|
|
2150
|
+
/**
|
|
2151
|
+
* Execute the query and return a single document
|
|
2152
|
+
*/
|
|
2153
|
+
abstract execOne(): Promise<BaseSchema<Schema> | null>;
|
|
2154
|
+
/**
|
|
2155
|
+
* Count matching documents without fetching them
|
|
2156
|
+
*/
|
|
2157
|
+
abstract count(): Promise<number>;
|
|
2158
|
+
/**
|
|
2159
|
+
* Check if any matching documents exist
|
|
2160
|
+
*/
|
|
2161
|
+
abstract exists(): Promise<boolean>;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
type BaseSchema<T> = {
|
|
2165
|
+
id: string;
|
|
2166
|
+
} & T;
|
|
2167
|
+
interface ModelCreateOptions {
|
|
2168
|
+
/** Skip database-level validation (useful for draft documents) */
|
|
2169
|
+
skipValidation?: boolean;
|
|
2170
|
+
}
|
|
2171
|
+
interface ModelUpdateOptions {
|
|
2172
|
+
/** Skip database-level validation (useful for draft documents) */
|
|
2173
|
+
skipValidation?: boolean;
|
|
2174
|
+
}
|
|
2175
|
+
/**
|
|
2176
|
+
* Version document type for history/versioning
|
|
2177
|
+
*/
|
|
2178
|
+
interface VersionDocument {
|
|
2179
|
+
/** ID of the original document */
|
|
2180
|
+
documentId: string;
|
|
2181
|
+
/** Unique version identifier */
|
|
2182
|
+
versionId: string;
|
|
2183
|
+
/** Name of the schema/collection */
|
|
2184
|
+
schemaName: string;
|
|
2185
|
+
/** Version status */
|
|
2186
|
+
status: 'draft' | 'published' | 'archived';
|
|
2187
|
+
/** Snapshot of the document data at this version */
|
|
2188
|
+
data: Record<string, unknown>;
|
|
2189
|
+
/** When this version was created */
|
|
2190
|
+
createdAt: Date;
|
|
2191
|
+
/** Who created this version */
|
|
2192
|
+
createdBy?: string;
|
|
2193
|
+
/** Type of change that created this version */
|
|
2194
|
+
changeType?: 'create' | 'update' | 'restore';
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* Native access wrapper - provides type-safe native operations
|
|
2198
|
+
*/
|
|
2199
|
+
interface NativeAccess<T> {
|
|
2200
|
+
/**
|
|
2201
|
+
* Raw database/ORM instance (type varies by adapter)
|
|
2202
|
+
* For Mongoose: Model<Document>
|
|
2203
|
+
* For Drizzle: { db, table }
|
|
2204
|
+
*/
|
|
2205
|
+
readonly raw: unknown;
|
|
2206
|
+
/**
|
|
2207
|
+
* Execute raw query (adapter-specific syntax)
|
|
2208
|
+
* @param query - The raw query string
|
|
2209
|
+
* @param params - Query parameters
|
|
2210
|
+
*/
|
|
2211
|
+
rawQuery<R = unknown>(query: string, params?: unknown[]): Promise<R>;
|
|
2212
|
+
/**
|
|
2213
|
+
* Get adapter name for adapter-specific code
|
|
2214
|
+
*/
|
|
2215
|
+
readonly adapterName: AdapterName;
|
|
2216
|
+
}
|
|
2217
|
+
declare abstract class Model<Schema> {
|
|
2218
|
+
abstract create(data: Partial<BaseSchema<Schema>>, options?: ModelCreateOptions): Promise<BaseSchema<Schema>>;
|
|
2219
|
+
abstract find(): Promise<BaseSchema<Schema>[]>;
|
|
2220
|
+
abstract findById(id: string): Promise<BaseSchema<Schema> | null>;
|
|
2221
|
+
abstract findOne(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema> | null>;
|
|
2222
|
+
abstract findMany(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema>[]>;
|
|
2223
|
+
abstract update(query: Partial<BaseSchema<Schema>>, data: Partial<BaseSchema<Schema>>, options?: ModelUpdateOptions): Promise<BaseSchema<Schema>>;
|
|
2224
|
+
abstract delete(query: Partial<BaseSchema<Schema>>): Promise<boolean>;
|
|
2225
|
+
/**
|
|
2226
|
+
* Set the locale for subsequent operations
|
|
2227
|
+
* @param locale The locale to use
|
|
2228
|
+
* @returns Cloned model instance with locale set
|
|
2229
|
+
*/
|
|
2230
|
+
abstract locale(locale: string): this;
|
|
2231
|
+
/**
|
|
2232
|
+
* Get current locale
|
|
2233
|
+
*/
|
|
2234
|
+
getLocale(): string;
|
|
2235
|
+
/**
|
|
2236
|
+
* Set the version for subsequent operations
|
|
2237
|
+
* @param versionId The version ID or status ('draft', 'published', 'archived')
|
|
2238
|
+
* @returns Same instance (chainable)
|
|
2239
|
+
*/
|
|
2240
|
+
version(versionId: string): this;
|
|
2241
|
+
/**
|
|
2242
|
+
* Check if versioning is enabled for this model
|
|
2243
|
+
*/
|
|
2244
|
+
isVersioningEnabled(): boolean;
|
|
2245
|
+
/**
|
|
2246
|
+
* Create a version snapshot of a document
|
|
2247
|
+
* @param documentId The document ID
|
|
2248
|
+
* @param data The data to version
|
|
2249
|
+
* @returns Version record or null if versioning disabled
|
|
2250
|
+
*/
|
|
2251
|
+
createVersion(documentId: string, data: Partial<Schema>): Promise<VersionDocument | null>;
|
|
2252
|
+
/**
|
|
2253
|
+
* Find all versions of a document
|
|
2254
|
+
* @param documentId The document ID
|
|
2255
|
+
*/
|
|
2256
|
+
findVersions(documentId: string): Promise<VersionDocument[]>;
|
|
2257
|
+
/**
|
|
2258
|
+
* Find a specific version by ID
|
|
2259
|
+
* @param versionId The version ID
|
|
2260
|
+
*/
|
|
2261
|
+
findVersionById(versionId: string): Promise<VersionDocument | null>;
|
|
2262
|
+
/**
|
|
2263
|
+
* Restore a document to a specific version
|
|
2264
|
+
* @param versionId The version ID to restore
|
|
2265
|
+
*/
|
|
2266
|
+
restoreVersion(versionId: string): Promise<BaseSchema<Schema> | null>;
|
|
2267
|
+
/**
|
|
2268
|
+
* Create a query builder for advanced queries with sorting, pagination, and operators.
|
|
2269
|
+
* The query builder inherits the current locale/version context.
|
|
2270
|
+
*
|
|
2271
|
+
* @example
|
|
2272
|
+
* ```typescript
|
|
2273
|
+
* const results = await model.query()
|
|
2274
|
+
* .where({ status: 'active', age: { $gte: 18 } })
|
|
2275
|
+
* .sort({ createdAt: -1 })
|
|
2276
|
+
* .limit(10)
|
|
2277
|
+
* .exec()
|
|
2278
|
+
* ```
|
|
2279
|
+
*/
|
|
2280
|
+
query(): QueryBuilder<Schema>;
|
|
2281
|
+
/**
|
|
2282
|
+
* Get access to the native database model/collection.
|
|
2283
|
+
* Use with caution - bypasses Magnet abstractions like locale and versioning.
|
|
2284
|
+
*
|
|
2285
|
+
* @returns Typed native access object
|
|
2286
|
+
*/
|
|
2287
|
+
native(): NativeAccess<Schema>;
|
|
2288
|
+
/**
|
|
2289
|
+
* Get schema name
|
|
2290
|
+
*/
|
|
2291
|
+
getSchemaName(): string;
|
|
2292
|
+
/**
|
|
2293
|
+
* Get schema metadata
|
|
2294
|
+
*/
|
|
2295
|
+
getMetadata(): SchemaMetadata;
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
declare class Mixed {
|
|
2299
|
+
static schemaName: 'Mixed';
|
|
2300
|
+
defaultOptions: Record<string, unknown>;
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
type MongooseConfig = {
|
|
2304
|
+
uri: string;
|
|
2305
|
+
};
|
|
2306
|
+
/**
|
|
2307
|
+
* Drizzle ORM configuration for SQL databases.
|
|
2308
|
+
* Supports PostgreSQL, MySQL, and SQLite through Drizzle ORM.
|
|
2309
|
+
*/
|
|
2310
|
+
type DrizzleConfig = {
|
|
2311
|
+
/** Database connection string */
|
|
2312
|
+
connectionString: string;
|
|
2313
|
+
/** SQL dialect to use */
|
|
2314
|
+
dialect: 'postgresql' | 'mysql' | 'sqlite';
|
|
2315
|
+
/** Database driver to use (auto-detected if not specified) */
|
|
2316
|
+
driver?: 'pg' | 'neon' | 'mysql2' | 'better-sqlite3';
|
|
2317
|
+
/** Enable debug logging */
|
|
2318
|
+
debug?: boolean;
|
|
2319
|
+
/**
|
|
2320
|
+
* Migration configuration. If omitted, falls back to legacy CREATE TABLE IF NOT EXISTS behavior.
|
|
2321
|
+
*/
|
|
2322
|
+
migrations?: {
|
|
2323
|
+
/** Migration mode — 'auto' for development, 'manual' for production */
|
|
2324
|
+
mode?: 'auto' | 'manual';
|
|
2325
|
+
/** Directory where migration files are stored (default: './migrations') */
|
|
2326
|
+
directory?: string;
|
|
2327
|
+
/** Table name for tracking applied migrations (default: '_magnet_migrations') */
|
|
2328
|
+
tableName?: string;
|
|
2329
|
+
/** Whether to run each migration in a transaction (default: true) */
|
|
2330
|
+
transactional?: boolean;
|
|
2331
|
+
};
|
|
2332
|
+
};
|
|
2333
|
+
type DBConfig = MongooseConfig | DrizzleConfig;
|
|
2334
|
+
/**
|
|
2335
|
+
* Database model instance type - the native model from the adapter
|
|
2336
|
+
* This could be a Mongoose Model, Drizzle table, or other adapter-specific type
|
|
2337
|
+
*/
|
|
2338
|
+
type DatabaseModelInstance = unknown;
|
|
2339
|
+
/**
|
|
2340
|
+
* Model class constructor returned by adapter.model()
|
|
2341
|
+
*/
|
|
2342
|
+
type ModelClass<T> = new () => Model<T>;
|
|
2343
|
+
/**
|
|
2344
|
+
* Supported adapter names
|
|
2345
|
+
*/
|
|
2346
|
+
type AdapterName = 'mongoose' | 'drizzle' | 'prisma' | 'typeorm';
|
|
2347
|
+
/**
|
|
2348
|
+
* Features an adapter may support
|
|
2349
|
+
*/
|
|
2350
|
+
type AdapterFeature = 'transactions' | 'nested-transactions' | 'json-queries' | 'full-text-search' | 'geospatial' | 'change-streams' | 'migrations';
|
|
2351
|
+
/**
|
|
2352
|
+
* Database types supported by adapters
|
|
2353
|
+
*/
|
|
2354
|
+
type DatabaseType = 'mongodb' | 'postgresql' | 'mysql' | 'sqlite' | 'mssql';
|
|
2355
|
+
/**
|
|
2356
|
+
* Adapter capability declaration
|
|
2357
|
+
*/
|
|
2358
|
+
interface AdapterCapabilities {
|
|
2359
|
+
/** Supported database types */
|
|
2360
|
+
databases: DatabaseType[];
|
|
2361
|
+
/** Supported features */
|
|
2362
|
+
features: AdapterFeature[];
|
|
2363
|
+
/** Whether adapter handles its own versioning */
|
|
2364
|
+
handlesVersioning: boolean;
|
|
2365
|
+
/** Whether adapter supports lazy table/collection creation */
|
|
2366
|
+
supportsLazyCreation: boolean;
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Database adapter contract - all adapters MUST implement this interface
|
|
2370
|
+
*/
|
|
2371
|
+
declare abstract class DatabaseAdapter implements OnModuleDestroy {
|
|
2372
|
+
/**
|
|
2373
|
+
* Adapter identifier
|
|
2374
|
+
*/
|
|
2375
|
+
abstract readonly name: AdapterName;
|
|
2376
|
+
/**
|
|
2377
|
+
* Connect to database and return NestJS dynamic module.
|
|
2378
|
+
* @param config - Database configuration (MongooseConfig or DrizzleConfig)
|
|
2379
|
+
*/
|
|
2380
|
+
abstract connect(config: DBConfig): DynamicModule;
|
|
2381
|
+
/**
|
|
2382
|
+
* Register schemas as features
|
|
2383
|
+
*/
|
|
2384
|
+
abstract forFeature(schemas: Type | Type[]): DynamicModule;
|
|
2385
|
+
/**
|
|
2386
|
+
* Create a Model class for the given native model instance
|
|
2387
|
+
* @param modelInstance - The native model instance from the database driver
|
|
2388
|
+
* @returns A Model class constructor that can be instantiated
|
|
2389
|
+
*/
|
|
2390
|
+
abstract model<T>(modelInstance: DatabaseModelInstance): ModelClass<T>;
|
|
2391
|
+
/**
|
|
2392
|
+
* Get injection token for a schema
|
|
2393
|
+
*/
|
|
2394
|
+
abstract token(schema: string): string;
|
|
2395
|
+
/**
|
|
2396
|
+
* Cleanup on module destroy
|
|
2397
|
+
*/
|
|
2398
|
+
abstract onModuleDestroy(): Promise<void>;
|
|
2399
|
+
/**
|
|
2400
|
+
* Check if adapter supports a feature
|
|
2401
|
+
*/
|
|
2402
|
+
abstract supports(feature: AdapterFeature): boolean;
|
|
2403
|
+
/**
|
|
2404
|
+
* Get adapter capabilities
|
|
2405
|
+
*/
|
|
2406
|
+
abstract getCapabilities(): AdapterCapabilities;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
/**
|
|
2410
|
+
* Plugin metadata for the @Plugin decorator
|
|
2411
|
+
*/
|
|
2412
|
+
interface PluginMetadata {
|
|
2413
|
+
/** Unique plugin identifier (e.g., 'playground') */
|
|
2414
|
+
name: string;
|
|
2415
|
+
/** Human-readable description */
|
|
2416
|
+
description?: string;
|
|
2417
|
+
/** Semver version */
|
|
2418
|
+
version?: string;
|
|
2419
|
+
/** Plugin dependencies (other plugin names) */
|
|
2420
|
+
dependencies?: string[];
|
|
2421
|
+
/** NestJS module to be auto-imported (optional for backwards compatibility) */
|
|
2422
|
+
module?: Type<unknown>;
|
|
2423
|
+
}
|
|
2424
|
+
/**
|
|
2425
|
+
* Route definition for plugin frontend
|
|
2426
|
+
*/
|
|
2427
|
+
interface PluginRouteDefinition {
|
|
2428
|
+
/** Route path (e.g., '/playground') */
|
|
2429
|
+
path: string;
|
|
2430
|
+
/** Component identifier for lazy loading */
|
|
2431
|
+
componentId: string;
|
|
2432
|
+
/** Whether route requires authentication */
|
|
2433
|
+
requiresAuth?: boolean;
|
|
2434
|
+
/** Required permissions */
|
|
2435
|
+
permissions?: string[];
|
|
2436
|
+
/** Child routes */
|
|
2437
|
+
children?: PluginRouteDefinition[];
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2440
|
+
* Sidebar item for plugin frontend
|
|
2441
|
+
*/
|
|
2442
|
+
interface PluginSidebarItem {
|
|
2443
|
+
/** Unique identifier */
|
|
2444
|
+
id: string;
|
|
2445
|
+
/** Display title */
|
|
2446
|
+
title: string;
|
|
2447
|
+
/** Route path */
|
|
2448
|
+
url: string;
|
|
2449
|
+
/** Lucide icon name (e.g., 'Boxes', 'Settings') */
|
|
2450
|
+
icon: string;
|
|
2451
|
+
/** Position in sidebar (lower = higher) */
|
|
2452
|
+
order?: number;
|
|
2453
|
+
/** Child items */
|
|
2454
|
+
items?: PluginSidebarItem[];
|
|
2455
|
+
/** Badge to show */
|
|
2456
|
+
badge?: string | number;
|
|
2457
|
+
}
|
|
2458
|
+
/**
|
|
2459
|
+
* Settings page for plugin
|
|
2460
|
+
*/
|
|
2461
|
+
interface PluginSettingsPage {
|
|
2462
|
+
/** Settings group identifier */
|
|
2463
|
+
groupId: string;
|
|
2464
|
+
/** Display title */
|
|
2465
|
+
title: string;
|
|
2466
|
+
/** Description */
|
|
2467
|
+
description?: string;
|
|
2468
|
+
/** Component identifier */
|
|
2469
|
+
componentId: string;
|
|
2470
|
+
}
|
|
2471
|
+
/**
|
|
2472
|
+
* Frontend manifest exposed by plugins via discovery
|
|
2473
|
+
*/
|
|
2474
|
+
interface PluginFrontendManifest {
|
|
2475
|
+
/** Plugin identifier */
|
|
2476
|
+
pluginName: string;
|
|
2477
|
+
/** Routes to register */
|
|
2478
|
+
routes?: PluginRouteDefinition[];
|
|
2479
|
+
/** Sidebar items */
|
|
2480
|
+
sidebar?: PluginSidebarItem[];
|
|
2481
|
+
/** Settings pages */
|
|
2482
|
+
settings?: PluginSettingsPage[];
|
|
2483
|
+
/** Required permissions */
|
|
2484
|
+
permissions?: string[];
|
|
2485
|
+
}
|
|
2486
|
+
/**
|
|
2487
|
+
* Enriched plugin manifest with bundle URL for runtime loading
|
|
2488
|
+
* Returned by GET /plugins/manifests endpoint
|
|
2489
|
+
*/
|
|
2490
|
+
interface EnrichedPluginManifest extends PluginFrontendManifest {
|
|
2491
|
+
/** URL to load the plugin's frontend bundle */
|
|
2492
|
+
bundleUrl: string;
|
|
2493
|
+
}
|
|
2494
|
+
/**
|
|
2495
|
+
* Plugin configuration passed to MagnetModule.forRoot
|
|
2496
|
+
*/
|
|
2497
|
+
interface PluginConfig {
|
|
2498
|
+
/** The plugin class decorated with @Plugin */
|
|
2499
|
+
plugin: Type<unknown>;
|
|
2500
|
+
/** Plugin-specific options */
|
|
2501
|
+
options?: Record<string, unknown>;
|
|
2502
|
+
/** Whether the plugin is enabled (default: true) */
|
|
2503
|
+
enabled?: boolean;
|
|
2504
|
+
}
|
|
2505
|
+
/**
|
|
2506
|
+
* Hook definition for plugins
|
|
2507
|
+
*/
|
|
2508
|
+
interface PluginHook {
|
|
2509
|
+
instance: unknown;
|
|
2510
|
+
methodName: string | symbol;
|
|
2511
|
+
}
|
|
2512
|
+
/**
|
|
2513
|
+
* Options for PluginModule.forRoot
|
|
2514
|
+
*/
|
|
2515
|
+
interface PluginModuleOptions {
|
|
2516
|
+
plugins: PluginConfig[];
|
|
2517
|
+
}
|
|
2518
|
+
/**
|
|
2519
|
+
* Registered plugin info returned from API
|
|
2520
|
+
*/
|
|
2521
|
+
interface RegisteredPluginInfo {
|
|
2522
|
+
name: string;
|
|
2523
|
+
description?: string;
|
|
2524
|
+
version?: string;
|
|
2525
|
+
dependencies?: string[];
|
|
2526
|
+
frontend?: PluginFrontendManifest;
|
|
2527
|
+
options?: Record<string, unknown>;
|
|
2528
|
+
}
|
|
2529
|
+
/**
|
|
2530
|
+
* Interface for plugin lifecycle hooks.
|
|
2531
|
+
* Plugins can implement these methods to respond to lifecycle events.
|
|
2532
|
+
*
|
|
2533
|
+
* @example
|
|
2534
|
+
* ```ts
|
|
2535
|
+
* @Plugin({ name: 'my-plugin', module: MyPluginModule })
|
|
2536
|
+
* export class MyPlugin implements PluginLifecycle {
|
|
2537
|
+
* onPluginInit() {
|
|
2538
|
+
* console.log('Plugin initialized')
|
|
2539
|
+
* }
|
|
2540
|
+
*
|
|
2541
|
+
* onPluginDestroy() {
|
|
2542
|
+
* console.log('Plugin destroyed')
|
|
2543
|
+
* }
|
|
2544
|
+
* }
|
|
2545
|
+
* ```
|
|
2546
|
+
*/
|
|
2547
|
+
interface PluginLifecycle {
|
|
2548
|
+
/**
|
|
2549
|
+
* Called after the plugin module is initialized.
|
|
2550
|
+
* Use this for plugin-specific setup that needs other services.
|
|
2551
|
+
*/
|
|
2552
|
+
onPluginInit?(): void | Promise<void>;
|
|
2553
|
+
/**
|
|
2554
|
+
* Called when the application is shutting down.
|
|
2555
|
+
* Use this for cleanup operations.
|
|
2556
|
+
*/
|
|
2557
|
+
onPluginDestroy?(): void | Promise<void>;
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
interface LocalStorageConfig {
|
|
2561
|
+
/** Directory where files will be stored (e.g., './uploads') */
|
|
2562
|
+
uploadDir: string;
|
|
2563
|
+
/** Public URL path for serving files (e.g., '/media') */
|
|
2564
|
+
publicPath: string;
|
|
2565
|
+
/** Maximum file size in bytes (default: 50MB) */
|
|
2566
|
+
maxFileSize?: number;
|
|
2567
|
+
/** Allowed MIME types (default: all) */
|
|
2568
|
+
allowedMimeTypes?: string[];
|
|
2569
|
+
}
|
|
2570
|
+
interface S3StorageConfig {
|
|
2571
|
+
/** S3 bucket name */
|
|
2572
|
+
bucket: string;
|
|
2573
|
+
/** AWS region */
|
|
2574
|
+
region: string;
|
|
2575
|
+
/** AWS access key ID */
|
|
2576
|
+
accessKeyId: string;
|
|
2577
|
+
/** AWS secret access key */
|
|
2578
|
+
secretAccessKey: string;
|
|
2579
|
+
/** Custom endpoint URL (for R2, MinIO, etc.) */
|
|
2580
|
+
endpoint?: string;
|
|
2581
|
+
/** Public URL for serving files (CDN URL) */
|
|
2582
|
+
publicUrl?: string;
|
|
2583
|
+
/** Enable path-style access (required for some S3-compatible services) */
|
|
2584
|
+
forcePathStyle?: boolean;
|
|
2585
|
+
}
|
|
2586
|
+
interface R2StorageConfig extends S3StorageConfig {
|
|
2587
|
+
/** Cloudflare account ID */
|
|
2588
|
+
accountId: string;
|
|
2589
|
+
}
|
|
2590
|
+
interface SupabaseStorageConfig {
|
|
2591
|
+
/** Supabase project URL */
|
|
2592
|
+
supabaseUrl: string;
|
|
2593
|
+
/** Supabase service role key (required for storage operations) */
|
|
2594
|
+
supabaseKey: string;
|
|
2595
|
+
/** Storage bucket name */
|
|
2596
|
+
bucket: string;
|
|
2597
|
+
/** Public URL for the bucket (optional, uses Supabase URL if not provided) */
|
|
2598
|
+
publicUrl?: string;
|
|
2599
|
+
}
|
|
2600
|
+
interface StorageConfig {
|
|
2601
|
+
/** Storage adapter to use */
|
|
2602
|
+
adapter: 'local' | 's3' | 'r2' | 'supabase';
|
|
2603
|
+
/** Local storage configuration */
|
|
2604
|
+
local?: LocalStorageConfig;
|
|
2605
|
+
/** S3 storage configuration */
|
|
2606
|
+
s3?: S3StorageConfig;
|
|
2607
|
+
/** R2 storage configuration */
|
|
2608
|
+
r2?: R2StorageConfig;
|
|
2609
|
+
/** Supabase storage configuration */
|
|
2610
|
+
supabase?: SupabaseStorageConfig;
|
|
2611
|
+
}
|
|
2612
|
+
interface UploadOptions {
|
|
2613
|
+
/** Target folder for the file */
|
|
2614
|
+
folder?: string;
|
|
2615
|
+
/** Custom filename (auto-generated if not provided) */
|
|
2616
|
+
filename?: string;
|
|
2617
|
+
/** MIME type of the file */
|
|
2618
|
+
mimeType?: string;
|
|
2619
|
+
/** Tags for categorization */
|
|
2620
|
+
tags?: string[];
|
|
2621
|
+
/** Alt text for accessibility */
|
|
2622
|
+
alt?: string;
|
|
2623
|
+
/** Custom metadata fields */
|
|
2624
|
+
customFields?: Record<string, unknown>;
|
|
492
2625
|
}
|
|
493
2626
|
interface UploadResult {
|
|
494
2627
|
/** Unique identifier for the uploaded file */
|
|
@@ -564,489 +2697,1549 @@ interface PaginatedMedia<T = UploadResult> {
|
|
|
564
2697
|
/** Total number of pages */
|
|
565
2698
|
totalPages: number;
|
|
566
2699
|
}
|
|
567
|
-
declare abstract class StorageAdapter {
|
|
2700
|
+
declare abstract class StorageAdapter {
|
|
2701
|
+
/**
|
|
2702
|
+
* Initialize the storage adapter (create directories, check connections)
|
|
2703
|
+
*/
|
|
2704
|
+
abstract initialize(): Promise<void>;
|
|
2705
|
+
/**
|
|
2706
|
+
* Upload a file to storage
|
|
2707
|
+
* @param file - File buffer or readable stream
|
|
2708
|
+
* @param originalFilename - Original filename from user
|
|
2709
|
+
* @param options - Upload options
|
|
2710
|
+
*/
|
|
2711
|
+
abstract upload(file: Buffer | Readable, originalFilename: string, options?: UploadOptions): Promise<UploadResult>;
|
|
2712
|
+
/**
|
|
2713
|
+
* Upload a file using chunked/streaming upload (for large files)
|
|
2714
|
+
* @param stream - Readable stream of the file
|
|
2715
|
+
* @param originalFilename - Original filename from user
|
|
2716
|
+
* @param totalSize - Total size of the file in bytes
|
|
2717
|
+
* @param options - Upload options
|
|
2718
|
+
*/
|
|
2719
|
+
abstract uploadChunked(stream: Readable, originalFilename: string, totalSize: number, options?: UploadOptions): Promise<UploadResult>;
|
|
2720
|
+
/**
|
|
2721
|
+
* Delete a file from storage
|
|
2722
|
+
* @param path - Storage path or key of the file
|
|
2723
|
+
*/
|
|
2724
|
+
abstract delete(path: string): Promise<boolean>;
|
|
2725
|
+
/**
|
|
2726
|
+
* Delete multiple files from storage
|
|
2727
|
+
* @param paths - Array of storage paths or keys
|
|
2728
|
+
*/
|
|
2729
|
+
abstract deleteMany(paths: string[]): Promise<{
|
|
2730
|
+
deleted: number;
|
|
2731
|
+
failed: string[];
|
|
2732
|
+
}>;
|
|
2733
|
+
/**
|
|
2734
|
+
* Get the public URL for a file
|
|
2735
|
+
* @param path - Storage path or key
|
|
2736
|
+
* @param transform - Optional transform options for images
|
|
2737
|
+
*/
|
|
2738
|
+
abstract getUrl(path: string, transform?: TransformOptions): string;
|
|
2739
|
+
/**
|
|
2740
|
+
* Get a readable stream for a file
|
|
2741
|
+
* @param path - Storage path or key
|
|
2742
|
+
*/
|
|
2743
|
+
abstract getStream(path: string): Promise<Readable>;
|
|
2744
|
+
/**
|
|
2745
|
+
* Get the file as a buffer
|
|
2746
|
+
* @param path - Storage path or key
|
|
2747
|
+
*/
|
|
2748
|
+
abstract getBuffer(path: string): Promise<Buffer>;
|
|
2749
|
+
/**
|
|
2750
|
+
* Check if a file exists
|
|
2751
|
+
* @param path - Storage path or key
|
|
2752
|
+
*/
|
|
2753
|
+
abstract exists(path: string): Promise<boolean>;
|
|
2754
|
+
/**
|
|
2755
|
+
* Get a transformed version of an image
|
|
2756
|
+
* @param path - Storage path or key
|
|
2757
|
+
* @param options - Transform options
|
|
2758
|
+
*/
|
|
2759
|
+
abstract transform(path: string, options: TransformOptions): Promise<Buffer>;
|
|
2760
|
+
/**
|
|
2761
|
+
* Move/rename a file
|
|
2762
|
+
* @param path - Current storage path or key
|
|
2763
|
+
* @param newPath - New storage path or key
|
|
2764
|
+
*/
|
|
2765
|
+
move?(path: string, newPath: string): Promise<UploadResult>;
|
|
2766
|
+
/**
|
|
2767
|
+
* Copy a file
|
|
2768
|
+
* @param path - Source storage path or key
|
|
2769
|
+
* @param newPath - Destination storage path or key
|
|
2770
|
+
*/
|
|
2771
|
+
copy?(path: string, newPath: string): Promise<UploadResult>;
|
|
2772
|
+
/**
|
|
2773
|
+
* Generate a signed URL for temporary access (for S3/R2)
|
|
2774
|
+
* @param path - Storage path or key
|
|
2775
|
+
* @param expiresIn - Expiration time in seconds
|
|
2776
|
+
*/
|
|
2777
|
+
signedUrl?(path: string, expiresIn?: number): Promise<string>;
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
type VaultAdapterType = 'db' | 'hashicorp' | 'supabase';
|
|
2781
|
+
/**
|
|
2782
|
+
* Token-based authentication for HashiCorp Vault.
|
|
2783
|
+
* Token is read from VAULT_TOKEN env var.
|
|
2784
|
+
*/
|
|
2785
|
+
interface VaultTokenAuth {
|
|
2786
|
+
type: 'token';
|
|
2787
|
+
token: string;
|
|
2788
|
+
}
|
|
2789
|
+
/**
|
|
2790
|
+
* AppRole-based authentication for HashiCorp Vault.
|
|
2791
|
+
*/
|
|
2792
|
+
interface VaultAppRoleAuth {
|
|
2793
|
+
type: 'appRole';
|
|
2794
|
+
roleId: string;
|
|
2795
|
+
secretId?: string;
|
|
2796
|
+
}
|
|
2797
|
+
type VaultAuthConfig = VaultTokenAuth | VaultAppRoleAuth;
|
|
2798
|
+
/**
|
|
2799
|
+
* Configuration for the HashiCorp Vault adapter.
|
|
2800
|
+
*/
|
|
2801
|
+
interface HashiCorpVaultConfig {
|
|
2802
|
+
/** Vault server URL (e.g., 'https://vault.example.com:8200'). Defaults to VAULT_ADDR env var. */
|
|
2803
|
+
url?: string;
|
|
2804
|
+
/** Authentication configuration. Defaults to VAULT_TOKEN env var. */
|
|
2805
|
+
auth?: VaultAuthConfig;
|
|
2806
|
+
/** Secrets engine mount path (default: 'secret') */
|
|
2807
|
+
mountPath?: string;
|
|
2808
|
+
/** Vault Enterprise namespace (optional) */
|
|
2809
|
+
namespace?: string;
|
|
2810
|
+
}
|
|
2811
|
+
/**
|
|
2812
|
+
* Configuration for the Supabase Vault adapter.
|
|
2813
|
+
*/
|
|
2814
|
+
interface SupabaseVaultConfig {
|
|
2815
|
+
/** Supabase project URL */
|
|
2816
|
+
supabaseUrl: string;
|
|
2817
|
+
/** Supabase service role key (required for vault operations) */
|
|
2818
|
+
supabaseServiceKey: string;
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Top-level vault configuration passed to MagnetModuleOptions.
|
|
2822
|
+
*/
|
|
2823
|
+
interface VaultConfig {
|
|
2824
|
+
/** Vault adapter to use (default: 'db') */
|
|
2825
|
+
adapter?: VaultAdapterType;
|
|
2826
|
+
/** Cache TTL in seconds (default: 300) */
|
|
2827
|
+
cacheTtl?: number;
|
|
2828
|
+
/** HashiCorp Vault adapter configuration */
|
|
2829
|
+
hashicorp?: HashiCorpVaultConfig;
|
|
2830
|
+
/** Supabase Vault adapter configuration */
|
|
2831
|
+
supabase?: SupabaseVaultConfig;
|
|
2832
|
+
}
|
|
2833
|
+
/**
|
|
2834
|
+
* Abstract base class for vault adapters.
|
|
2835
|
+
*
|
|
2836
|
+
* All adapters encrypt secrets at rest. Implementations determine
|
|
2837
|
+
* the storage backend (app DB, HashiCorp Vault, Supabase Vault).
|
|
2838
|
+
*/
|
|
2839
|
+
declare abstract class VaultAdapter {
|
|
2840
|
+
/**
|
|
2841
|
+
* Retrieve a secret value by key.
|
|
2842
|
+
* @returns The decrypted string value or null if not found
|
|
2843
|
+
*/
|
|
2844
|
+
abstract get(key: string): Promise<string | null>;
|
|
2845
|
+
/**
|
|
2846
|
+
* Store or update a secret.
|
|
2847
|
+
*
|
|
2848
|
+
* @param key - Secret identifier
|
|
2849
|
+
* @param value - Plaintext value to encrypt and store
|
|
2850
|
+
* @param description - Optional human-readable description (stored unencrypted)
|
|
2851
|
+
*/
|
|
2852
|
+
abstract set(key: string, value: string, description?: string): Promise<void>;
|
|
2853
|
+
/**
|
|
2854
|
+
* Delete a secret by key.
|
|
2855
|
+
*/
|
|
2856
|
+
abstract delete(key: string): Promise<void>;
|
|
2857
|
+
/**
|
|
2858
|
+
* List all secrets with their metadata, optionally filtered by prefix.
|
|
2859
|
+
*/
|
|
2860
|
+
abstract list(prefix?: string): Promise<VaultSecretMeta[]>;
|
|
2861
|
+
/**
|
|
2862
|
+
* Check the health of the vault backend.
|
|
2863
|
+
*/
|
|
2864
|
+
abstract healthCheck(): Promise<boolean>;
|
|
2865
|
+
}
|
|
2866
|
+
/** Lightweight metadata returned by list() — does not include the secret value. */
|
|
2867
|
+
interface VaultSecretMeta {
|
|
2868
|
+
name: string;
|
|
2869
|
+
description?: string;
|
|
2870
|
+
/** ISO date string when the secret was last updated (adapter-dependent) */
|
|
2871
|
+
lastUpdated?: string;
|
|
2872
|
+
}
|
|
2873
|
+
interface VaultStatusResponse {
|
|
2874
|
+
/** Whether the vault adapter is healthy */
|
|
2875
|
+
healthy: boolean;
|
|
2876
|
+
/** The adapter type in use */
|
|
2877
|
+
adapter: VaultAdapterType;
|
|
2878
|
+
/** Whether VAULT_MASTER_KEY is set (db adapter only) */
|
|
2879
|
+
masterKeyConfigured?: boolean;
|
|
2880
|
+
}
|
|
2881
|
+
interface VaultSecretListResponse {
|
|
2882
|
+
secrets: VaultSecretMeta[];
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
interface InternationalizationOptions {
|
|
2886
|
+
locales: string[];
|
|
2887
|
+
defaultLocale: string;
|
|
2888
|
+
}
|
|
2889
|
+
interface PlaygroundOptions {
|
|
2890
|
+
/**
|
|
2891
|
+
* Path to the directory where module folders will be created
|
|
2892
|
+
* @example './src/modules'
|
|
2893
|
+
*/
|
|
2894
|
+
modulesPath?: string;
|
|
2895
|
+
/**
|
|
2896
|
+
* @deprecated Use modulesPath instead
|
|
2897
|
+
* Path to the directory where schema files will be saved
|
|
2898
|
+
*/
|
|
2899
|
+
schemasPath?: string;
|
|
2900
|
+
}
|
|
2901
|
+
interface AdminConfig {
|
|
2902
|
+
/** Enable static admin serving (default true when omitted) */
|
|
2903
|
+
enabled?: boolean;
|
|
2904
|
+
/** Path to serve admin UI (default: '/admin') */
|
|
2905
|
+
path?: string;
|
|
2906
|
+
/** Custom path to admin dist folder */
|
|
2907
|
+
distPath?: string;
|
|
2908
|
+
}
|
|
2909
|
+
declare class MagnetModuleOptions {
|
|
2910
|
+
db: DBConfig;
|
|
2911
|
+
jwt: {
|
|
2912
|
+
secret: string;
|
|
2913
|
+
};
|
|
2914
|
+
/**
|
|
2915
|
+
* Auth configuration (optional, uses JWT by default)
|
|
2916
|
+
*/
|
|
2917
|
+
auth?: AuthConfig;
|
|
2918
|
+
internationalization?: InternationalizationOptions;
|
|
2919
|
+
playground?: PlaygroundOptions;
|
|
2920
|
+
storage?: StorageConfig;
|
|
2921
|
+
/**
|
|
2922
|
+
* Email adapter configuration
|
|
2923
|
+
* @example
|
|
2924
|
+
* email: { adapter: 'nodemailer', nodemailer: { host: 'smtp.example.com', port: 587, auth: { user: 'user', pass: 'pass' } } }
|
|
2925
|
+
*/
|
|
2926
|
+
email?: EmailConfig;
|
|
2927
|
+
/**
|
|
2928
|
+
* Plugins to load with the Magnet module
|
|
2929
|
+
*/
|
|
2930
|
+
plugins?: PluginConfig[];
|
|
2931
|
+
/**
|
|
2932
|
+
* Admin panel configuration (enabled by default when omitted).
|
|
2933
|
+
* @example
|
|
2934
|
+
* // Explicit enable (same as omitting `admin`)
|
|
2935
|
+
* admin: true
|
|
2936
|
+
*
|
|
2937
|
+
* // Custom path
|
|
2938
|
+
* admin: { enabled: true, path: '/dashboard' }
|
|
2939
|
+
*
|
|
2940
|
+
* // Disable (for API-only mode)
|
|
2941
|
+
* admin: false
|
|
2942
|
+
*/
|
|
2943
|
+
admin?: boolean | AdminConfig;
|
|
2944
|
+
/**
|
|
2945
|
+
* RBAC (Role-Based Access Control) configuration
|
|
2946
|
+
* @example
|
|
2947
|
+
* rbac: { enabled: true, defaultRole: 'authenticated' }
|
|
2948
|
+
*/
|
|
2949
|
+
rbac?: RBACModuleOptions;
|
|
2950
|
+
/**
|
|
2951
|
+
* Vault configuration for encrypted secrets management.
|
|
2952
|
+
* Uses the built-in DB adapter by default (requires VAULT_MASTER_KEY env var).
|
|
2953
|
+
* @example
|
|
2954
|
+
* vault: { adapter: 'db' }
|
|
2955
|
+
* vault: { adapter: 'hashicorp', hashicorp: { url: 'https://vault.example.com:8200' } }
|
|
2956
|
+
*/
|
|
2957
|
+
vault?: VaultConfig;
|
|
2958
|
+
constructor({ db, jwt, auth, internationalization, playground, storage, email, plugins, admin, rbac, vault, }: MagnetModuleOptions);
|
|
2959
|
+
}
|
|
2960
|
+
type MagnetModuleOptionsAsync = {
|
|
2961
|
+
useFactory: (...args: unknown[]) => Promise<MagnetModuleOptions> | MagnetModuleOptions;
|
|
2962
|
+
inject?: Type[];
|
|
2963
|
+
imports?: Type[];
|
|
2964
|
+
};
|
|
2965
|
+
|
|
2966
|
+
type DiscoveredController = {
|
|
2967
|
+
path: string;
|
|
2968
|
+
schema: string;
|
|
2969
|
+
methods: DiscoveredMethod[];
|
|
2970
|
+
};
|
|
2971
|
+
type DiscoveredMethod = {
|
|
2972
|
+
name: string;
|
|
2973
|
+
method: string;
|
|
2974
|
+
};
|
|
2975
|
+
type DiscoveredSchema = {
|
|
2976
|
+
name: string;
|
|
2977
|
+
tableName: string;
|
|
2978
|
+
target: string | object;
|
|
2979
|
+
};
|
|
2980
|
+
|
|
2981
|
+
type Validations = {
|
|
2982
|
+
type: string;
|
|
2983
|
+
name: string;
|
|
2984
|
+
constraints: unknown[];
|
|
2985
|
+
}[];
|
|
2986
|
+
|
|
2987
|
+
type VersionStatus = 'draft' | 'published' | 'archived';
|
|
2988
|
+
interface VersionConfig {
|
|
2989
|
+
/**
|
|
2990
|
+
* Maximum number of versions to keep per document
|
|
2991
|
+
*/
|
|
2992
|
+
max?: number;
|
|
2993
|
+
/**
|
|
2994
|
+
* Enable drafts mode for this schema
|
|
2995
|
+
*/
|
|
2996
|
+
drafts?: boolean | {
|
|
2997
|
+
/**
|
|
2998
|
+
* Auto-publish drafts after a certain time
|
|
2999
|
+
*/
|
|
3000
|
+
autoPublish?: boolean;
|
|
3001
|
+
/**
|
|
3002
|
+
* Require approval before publishing
|
|
3003
|
+
*/
|
|
3004
|
+
requireApproval?: boolean;
|
|
3005
|
+
};
|
|
3006
|
+
}
|
|
3007
|
+
interface VersionData<T> {
|
|
3008
|
+
/**
|
|
3009
|
+
* Document ID this version belongs to
|
|
3010
|
+
*/
|
|
3011
|
+
documentId: string;
|
|
3012
|
+
/**
|
|
3013
|
+
* Version ID
|
|
3014
|
+
*/
|
|
3015
|
+
versionId: string;
|
|
3016
|
+
/**
|
|
3017
|
+
* Version status
|
|
3018
|
+
*/
|
|
3019
|
+
status: VersionStatus;
|
|
3020
|
+
/**
|
|
3021
|
+
* Version data
|
|
3022
|
+
*/
|
|
3023
|
+
data: T;
|
|
3024
|
+
/**
|
|
3025
|
+
* Version created at
|
|
3026
|
+
*/
|
|
3027
|
+
createdAt: Date;
|
|
3028
|
+
/**
|
|
3029
|
+
* Version created by
|
|
3030
|
+
*/
|
|
3031
|
+
createdBy?: string;
|
|
3032
|
+
/**
|
|
3033
|
+
* Version notes
|
|
3034
|
+
*/
|
|
3035
|
+
notes?: string;
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
/** Log severity levels, ordered from most critical to most verbose. */
|
|
3039
|
+
type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'verbose';
|
|
3040
|
+
/** Structured metadata attached to a log entry. */
|
|
3041
|
+
interface LogMetadata {
|
|
3042
|
+
operation?: string;
|
|
3043
|
+
schema?: string;
|
|
3044
|
+
resourceId?: string;
|
|
3045
|
+
duration?: number;
|
|
3046
|
+
method?: string;
|
|
3047
|
+
path?: string;
|
|
3048
|
+
statusCode?: number;
|
|
3049
|
+
[key: string]: unknown;
|
|
3050
|
+
}
|
|
3051
|
+
/** Serialized error information for structured logging. */
|
|
3052
|
+
interface ErrorLogData {
|
|
3053
|
+
name: string;
|
|
3054
|
+
message: string;
|
|
3055
|
+
code?: string;
|
|
3056
|
+
stack?: string;
|
|
3057
|
+
}
|
|
3058
|
+
/** A single structured log entry. */
|
|
3059
|
+
interface LogEntry {
|
|
3060
|
+
level: LogLevel;
|
|
3061
|
+
message: string;
|
|
3062
|
+
timestamp: string;
|
|
3063
|
+
context: string;
|
|
3064
|
+
requestId?: string;
|
|
3065
|
+
userId?: string;
|
|
3066
|
+
metadata?: LogMetadata;
|
|
3067
|
+
error?: ErrorLogData;
|
|
3068
|
+
}
|
|
3069
|
+
/** Configuration for MagnetLogger, read from environment variables. */
|
|
3070
|
+
interface LoggerConfig {
|
|
3071
|
+
/** Minimum log level to output. Default: 'info' */
|
|
3072
|
+
level: LogLevel;
|
|
3073
|
+
/** Output format. Default: 'pretty' */
|
|
3074
|
+
format: 'json' | 'pretty';
|
|
3075
|
+
/** Whether to include timestamps. Default: true */
|
|
3076
|
+
timestamps: boolean;
|
|
3077
|
+
/** Whether to include error stack traces. Default: true */
|
|
3078
|
+
stackTraces: boolean;
|
|
3079
|
+
/** Field names to redact from metadata. */
|
|
3080
|
+
redactFields: string[];
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
/**
|
|
3084
|
+
* Notification System Type Definitions
|
|
3085
|
+
*
|
|
3086
|
+
* Shared types used by core backend and admin client for the notifications system.
|
|
3087
|
+
*/
|
|
3088
|
+
/**
|
|
3089
|
+
* Available notification delivery channels.
|
|
3090
|
+
* - `platform`: Persists notification in DB, shown in admin drawer.
|
|
3091
|
+
* - `email`: Sends an email to the user (requires an adapter registered).
|
|
3092
|
+
*/
|
|
3093
|
+
type NotificationChannel = 'platform' | 'email';
|
|
3094
|
+
/**
|
|
3095
|
+
* Result of delivering a notification via a single channel.
|
|
3096
|
+
*/
|
|
3097
|
+
interface NotificationChannelResult {
|
|
3098
|
+
channel: NotificationChannel;
|
|
3099
|
+
success: boolean;
|
|
3100
|
+
error?: string;
|
|
3101
|
+
}
|
|
3102
|
+
/**
|
|
3103
|
+
* Interface that any notification channel adapter must implement.
|
|
3104
|
+
* Register adapters in `NotificationModule.forRoot({ adapters: [...] })`.
|
|
3105
|
+
*/
|
|
3106
|
+
interface NotificationChannelAdapter {
|
|
3107
|
+
/** Which channel this adapter handles */
|
|
3108
|
+
readonly channel: NotificationChannel;
|
|
568
3109
|
/**
|
|
569
|
-
*
|
|
3110
|
+
* Deliver a notification to a recipient.
|
|
3111
|
+
* @param payload - Notification data to send
|
|
3112
|
+
* @param recipient - Target user info (userId always present; email populated when available)
|
|
570
3113
|
*/
|
|
571
|
-
|
|
3114
|
+
send(payload: NotificationSendPayload, recipient: NotificationRecipient): Promise<NotificationChannelResult>;
|
|
3115
|
+
}
|
|
3116
|
+
/**
|
|
3117
|
+
* Minimal notification data passed to channel adapters for delivery.
|
|
3118
|
+
*/
|
|
3119
|
+
interface NotificationSendPayload {
|
|
3120
|
+
id: string;
|
|
3121
|
+
type: string;
|
|
3122
|
+
title: string;
|
|
3123
|
+
message: string;
|
|
3124
|
+
href?: string;
|
|
3125
|
+
metadata?: Record<string, unknown>;
|
|
3126
|
+
createdAt: Date;
|
|
3127
|
+
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Recipient info resolved when delivering a notification.
|
|
3130
|
+
*/
|
|
3131
|
+
interface NotificationRecipient {
|
|
3132
|
+
userId: string;
|
|
3133
|
+
email?: string;
|
|
3134
|
+
name?: string;
|
|
3135
|
+
}
|
|
3136
|
+
/**
|
|
3137
|
+
* DTO used to send a notification through NotificationService.notify().
|
|
3138
|
+
*/
|
|
3139
|
+
interface NotifyDto {
|
|
3140
|
+
/** Target user ID. Can be a single user or a list for bulk delivery. */
|
|
3141
|
+
userId: string | string[];
|
|
572
3142
|
/**
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
* @param originalFilename - Original filename from user
|
|
576
|
-
* @param options - Upload options
|
|
3143
|
+
* Which channels to deliver to.
|
|
3144
|
+
* Defaults to `['platform']` when not specified.
|
|
577
3145
|
*/
|
|
578
|
-
|
|
3146
|
+
channels?: NotificationChannel[];
|
|
579
3147
|
/**
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
* @param originalFilename - Original filename from user
|
|
583
|
-
* @param totalSize - Total size of the file in bytes
|
|
584
|
-
* @param options - Upload options
|
|
3148
|
+
* Notification category / type identifier.
|
|
3149
|
+
* Useful for filtering and icon mapping in the UI.
|
|
585
3150
|
*/
|
|
586
|
-
|
|
3151
|
+
type: string;
|
|
3152
|
+
/** Short title shown in the notification list. */
|
|
3153
|
+
title: string;
|
|
3154
|
+
/** Longer description or body of the notification. */
|
|
3155
|
+
message: string;
|
|
3156
|
+
/** Optional link the user will be taken to when clicking the notification. */
|
|
3157
|
+
href?: string;
|
|
587
3158
|
/**
|
|
588
|
-
*
|
|
589
|
-
*
|
|
3159
|
+
* Additional arbitrary metadata stored with the notification.
|
|
3160
|
+
* Not shown directly in UI but available for custom logic.
|
|
590
3161
|
*/
|
|
591
|
-
|
|
3162
|
+
metadata?: Record<string, unknown>;
|
|
3163
|
+
}
|
|
3164
|
+
/**
|
|
3165
|
+
* Options for querying a user's notifications.
|
|
3166
|
+
*/
|
|
3167
|
+
interface NotificationQueryOptions {
|
|
3168
|
+
/** Filter to only unread notifications. */
|
|
3169
|
+
unreadOnly?: boolean;
|
|
3170
|
+
/** Maximum number of results. Defaults to 20. */
|
|
3171
|
+
limit?: number;
|
|
3172
|
+
/** Number of results to skip for pagination. */
|
|
3173
|
+
offset?: number;
|
|
3174
|
+
}
|
|
3175
|
+
/**
|
|
3176
|
+
* Paginated response for notification lists.
|
|
3177
|
+
*/
|
|
3178
|
+
interface PaginatedNotifications<T> {
|
|
3179
|
+
items: T[];
|
|
3180
|
+
total: number;
|
|
3181
|
+
unreadCount: number;
|
|
3182
|
+
limit: number;
|
|
3183
|
+
offset: number;
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3186
|
+
/**
|
|
3187
|
+
* Result of a cache health check
|
|
3188
|
+
*/
|
|
3189
|
+
interface CacheHealthResult {
|
|
3190
|
+
/** Whether the cache adapter is healthy and reachable */
|
|
3191
|
+
healthy: boolean;
|
|
3192
|
+
/** Optional message with details about the health status */
|
|
3193
|
+
message?: string;
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Abstract base class for cache adapters.
|
|
3197
|
+
*
|
|
3198
|
+
* All cache providers must extend this class and implement the abstract methods.
|
|
3199
|
+
* Follows the same pattern as `StorageAdapter`, `EmailAdapter`, and `DatabaseAdapter`.
|
|
3200
|
+
*
|
|
3201
|
+
* @example
|
|
3202
|
+
* ```typescript
|
|
3203
|
+
* export class MemoryCacheAdapter extends CacheAdapter {
|
|
3204
|
+
* readonly name = 'memory'
|
|
3205
|
+
*
|
|
3206
|
+
* async get<T>(key: string): Promise<T | null> {
|
|
3207
|
+
* // Return cached value or null
|
|
3208
|
+
* }
|
|
3209
|
+
*
|
|
3210
|
+
* async set<T>(key: string, value: T, ttl?: number): Promise<void> {
|
|
3211
|
+
* // Store value with optional TTL in seconds
|
|
3212
|
+
* }
|
|
3213
|
+
*
|
|
3214
|
+
* // ... other methods
|
|
3215
|
+
* }
|
|
3216
|
+
* ```
|
|
3217
|
+
*/
|
|
3218
|
+
declare abstract class CacheAdapter {
|
|
592
3219
|
/**
|
|
593
|
-
*
|
|
594
|
-
* @param paths - Array of storage paths or keys
|
|
3220
|
+
* Unique identifier for this adapter
|
|
595
3221
|
*/
|
|
596
|
-
abstract
|
|
597
|
-
deleted: number;
|
|
598
|
-
failed: string[];
|
|
599
|
-
}>;
|
|
3222
|
+
abstract readonly name: string;
|
|
600
3223
|
/**
|
|
601
|
-
*
|
|
602
|
-
* @param
|
|
603
|
-
* @
|
|
3224
|
+
* Retrieve a cached value by key.
|
|
3225
|
+
* @param key - Cache key
|
|
3226
|
+
* @returns The cached value, or null if not found or expired
|
|
604
3227
|
*/
|
|
605
|
-
abstract
|
|
3228
|
+
abstract get<T>(key: string): Promise<T | null>;
|
|
606
3229
|
/**
|
|
607
|
-
*
|
|
608
|
-
* @param
|
|
3230
|
+
* Store a value in the cache.
|
|
3231
|
+
* @param key - Cache key
|
|
3232
|
+
* @param value - Value to cache (must be JSON-serializable)
|
|
3233
|
+
* @param ttl - Time-to-live in seconds. If omitted, uses adapter default.
|
|
609
3234
|
*/
|
|
610
|
-
abstract
|
|
3235
|
+
abstract set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
611
3236
|
/**
|
|
612
|
-
*
|
|
613
|
-
* @param
|
|
3237
|
+
* Remove a value from the cache by key.
|
|
3238
|
+
* @param key - Cache key to delete
|
|
614
3239
|
*/
|
|
615
|
-
abstract
|
|
3240
|
+
abstract delete(key: string): Promise<void>;
|
|
616
3241
|
/**
|
|
617
|
-
*
|
|
618
|
-
*
|
|
3242
|
+
* Remove all cache entries matching a glob pattern.
|
|
3243
|
+
* Supports `*` wildcards (e.g., `content:posts:*` removes all post entries).
|
|
3244
|
+
* @param pattern - Glob pattern to match keys against
|
|
619
3245
|
*/
|
|
620
|
-
abstract
|
|
3246
|
+
abstract deleteByPattern(pattern: string): Promise<void>;
|
|
621
3247
|
/**
|
|
622
|
-
*
|
|
623
|
-
* @param
|
|
624
|
-
* @
|
|
3248
|
+
* Check if a key exists in the cache (and has not expired).
|
|
3249
|
+
* @param key - Cache key to check
|
|
3250
|
+
* @returns true if the key exists and is not expired
|
|
625
3251
|
*/
|
|
626
|
-
abstract
|
|
3252
|
+
abstract has(key: string): Promise<boolean>;
|
|
627
3253
|
/**
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
* @param newPath - New storage path or key
|
|
3254
|
+
* Remove all entries from the cache.
|
|
3255
|
+
* For Redis, only clears keys within this adapter's key prefix.
|
|
631
3256
|
*/
|
|
632
|
-
|
|
3257
|
+
abstract clear(): Promise<void>;
|
|
633
3258
|
/**
|
|
634
|
-
*
|
|
635
|
-
* @
|
|
636
|
-
* @param newPath - Destination storage path or key
|
|
3259
|
+
* Check the health of the cache backend.
|
|
3260
|
+
* @returns Health result with status and optional message
|
|
637
3261
|
*/
|
|
638
|
-
|
|
3262
|
+
abstract healthCheck(): Promise<CacheHealthResult>;
|
|
639
3263
|
/**
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
* @param expiresIn - Expiration time in seconds
|
|
3264
|
+
* Optional cleanup/disconnect method.
|
|
3265
|
+
* Called when the module is destroyed.
|
|
643
3266
|
*/
|
|
644
|
-
|
|
3267
|
+
dispose?(): Promise<void>;
|
|
645
3268
|
}
|
|
646
3269
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
interface
|
|
652
|
-
/**
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
* @deprecated Use modulesPath instead
|
|
659
|
-
* Path to the directory where schema files will be saved
|
|
660
|
-
*/
|
|
661
|
-
schemasPath?: string;
|
|
3270
|
+
/**
|
|
3271
|
+
* Declares a required or optional environment variable for an adapter/plugin.
|
|
3272
|
+
* Used by MagnetModule to validate all env vars before NestJS bootstraps.
|
|
3273
|
+
*/
|
|
3274
|
+
interface EnvVarRequirement {
|
|
3275
|
+
/** Environment variable name (e.g., 'DATABASE_URL') */
|
|
3276
|
+
name: string;
|
|
3277
|
+
/** Whether this env var is required for the adapter to function */
|
|
3278
|
+
required: boolean;
|
|
3279
|
+
/** Human-readable description shown in error messages */
|
|
3280
|
+
description?: string;
|
|
662
3281
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
3282
|
+
/**
|
|
3283
|
+
* Global configuration options passed as the second argument to MagnetModule.forRoot().
|
|
3284
|
+
* Contains cross-cutting settings that don't belong to any specific adapter.
|
|
3285
|
+
*
|
|
3286
|
+
* @example
|
|
3287
|
+
* ```typescript
|
|
3288
|
+
* MagnetModule.forRoot([...providers], {
|
|
3289
|
+
* jwt: { secret: 'my-secret' },
|
|
3290
|
+
* admin: true,
|
|
3291
|
+
* rbac: { enabled: true },
|
|
3292
|
+
* })
|
|
3293
|
+
* ```
|
|
3294
|
+
*/
|
|
3295
|
+
interface MagnetGlobalOptions {
|
|
3296
|
+
/** JWT configuration for authentication */
|
|
3297
|
+
jwt?: {
|
|
3298
|
+
/** JWT signing secret. Auto-resolved from JWT_SECRET env var if not provided. */
|
|
3299
|
+
secret?: string;
|
|
3300
|
+
/** Token expiration time (e.g., '7d', '1h'). Default: '7d' */
|
|
3301
|
+
expiresIn?: string;
|
|
667
3302
|
};
|
|
668
3303
|
/**
|
|
669
|
-
*
|
|
3304
|
+
* Admin panel configuration (enabled by default when omitted).
|
|
3305
|
+
* @example admin: false // API-only / headless
|
|
3306
|
+
* @example admin: { enabled: true, path: '/dashboard' }
|
|
670
3307
|
*/
|
|
671
|
-
|
|
3308
|
+
admin?: boolean | AdminConfig;
|
|
3309
|
+
/** RBAC (Role-Based Access Control) configuration */
|
|
3310
|
+
rbac?: RBACModuleOptions;
|
|
3311
|
+
/** Internationalization settings */
|
|
672
3312
|
internationalization?: InternationalizationOptions;
|
|
3313
|
+
/** Global Playground (schema builder) settings */
|
|
673
3314
|
playground?: PlaygroundOptions;
|
|
674
|
-
|
|
3315
|
+
}
|
|
3316
|
+
/**
|
|
3317
|
+
* Base interface for all Magnet providers.
|
|
3318
|
+
* Every adapter and plugin declares its environment variable requirements.
|
|
3319
|
+
*/
|
|
3320
|
+
interface BaseMagnetProvider {
|
|
3321
|
+
/** Environment variables this provider needs */
|
|
3322
|
+
envVars: EnvVarRequirement[];
|
|
3323
|
+
}
|
|
3324
|
+
/**
|
|
3325
|
+
* Database adapter provider.
|
|
3326
|
+
* Returned by database adapter `.forRoot()` methods.
|
|
3327
|
+
*/
|
|
3328
|
+
interface DatabaseMagnetProvider extends BaseMagnetProvider {
|
|
3329
|
+
type: 'database';
|
|
3330
|
+
/** The database adapter instance */
|
|
3331
|
+
adapter: DatabaseAdapter;
|
|
3332
|
+
/** Resolved database configuration */
|
|
3333
|
+
config: DBConfig;
|
|
3334
|
+
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Storage adapter provider.
|
|
3337
|
+
* Returned by storage adapter `.forRoot()` methods.
|
|
3338
|
+
*/
|
|
3339
|
+
interface StorageMagnetProvider extends BaseMagnetProvider {
|
|
3340
|
+
type: 'storage';
|
|
3341
|
+
/** The storage adapter instance */
|
|
3342
|
+
adapter: StorageAdapter;
|
|
3343
|
+
/** Adapter-specific resolved configuration */
|
|
3344
|
+
config?: Record<string, unknown>;
|
|
3345
|
+
}
|
|
3346
|
+
/**
|
|
3347
|
+
* Email adapter provider.
|
|
3348
|
+
* Returned by email adapter `.forRoot()` methods.
|
|
3349
|
+
*/
|
|
3350
|
+
interface EmailMagnetProvider extends BaseMagnetProvider {
|
|
3351
|
+
type: 'email';
|
|
3352
|
+
/** The email adapter instance */
|
|
3353
|
+
adapter: EmailAdapter;
|
|
3354
|
+
/** Default email settings */
|
|
3355
|
+
defaults?: {
|
|
3356
|
+
from?: string;
|
|
3357
|
+
replyTo?: string;
|
|
3358
|
+
};
|
|
3359
|
+
}
|
|
3360
|
+
/**
|
|
3361
|
+
* Vault adapter provider.
|
|
3362
|
+
* Returned by vault adapter `.forRoot()` methods.
|
|
3363
|
+
*
|
|
3364
|
+
* Supports two patterns:
|
|
3365
|
+
* - Direct `adapter` for adapters that don't need NestJS DI (e.g., HashiCorp)
|
|
3366
|
+
* - `adapterFactory` for adapters that need ModuleRef (e.g., built-in DB vault)
|
|
3367
|
+
*/
|
|
3368
|
+
interface VaultMagnetProvider extends BaseMagnetProvider {
|
|
3369
|
+
type: 'vault';
|
|
3370
|
+
/** Direct adapter instance (for adapters that don't need DI) */
|
|
3371
|
+
adapter?: VaultAdapter;
|
|
3372
|
+
/** Factory function for adapters that need NestJS ModuleRef */
|
|
3373
|
+
adapterFactory?: (moduleRef: unknown) => VaultAdapter;
|
|
3374
|
+
/** Vault configuration */
|
|
3375
|
+
config?: {
|
|
3376
|
+
cacheTtl?: number;
|
|
3377
|
+
};
|
|
3378
|
+
}
|
|
3379
|
+
/**
|
|
3380
|
+
* Auth strategy provider.
|
|
3381
|
+
* Returned by auth adapter `.forRoot()` methods.
|
|
3382
|
+
*/
|
|
3383
|
+
interface AuthMagnetProvider extends BaseMagnetProvider {
|
|
3384
|
+
type: 'auth';
|
|
3385
|
+
/** Auth configuration including strategy name */
|
|
3386
|
+
config: AuthConfig;
|
|
3387
|
+
}
|
|
3388
|
+
/**
|
|
3389
|
+
* Plugin provider.
|
|
3390
|
+
* Returned by plugin `.forRoot()` methods.
|
|
3391
|
+
*/
|
|
3392
|
+
interface PluginMagnetProvider extends BaseMagnetProvider {
|
|
3393
|
+
type: 'plugin';
|
|
3394
|
+
/** The @Plugin() decorated class */
|
|
3395
|
+
plugin: Type<unknown>;
|
|
3396
|
+
/** Plugin-specific options (injected via PLUGIN_OPTIONS token) */
|
|
3397
|
+
options?: Record<string, unknown>;
|
|
3398
|
+
}
|
|
3399
|
+
/**
|
|
3400
|
+
* Cache adapter provider.
|
|
3401
|
+
* Returned by cache adapter `.forRoot()` methods.
|
|
3402
|
+
*
|
|
3403
|
+
* When not provided, CacheModule defaults to the built-in MemoryCacheAdapter.
|
|
3404
|
+
*
|
|
3405
|
+
* @example
|
|
3406
|
+
* ```typescript
|
|
3407
|
+
* MagnetModule.forRoot([
|
|
3408
|
+
* RedisCacheAdapter.forRoot(), // or omit for in-memory default
|
|
3409
|
+
* ])
|
|
3410
|
+
* ```
|
|
3411
|
+
*/
|
|
3412
|
+
interface CacheMagnetProvider extends BaseMagnetProvider {
|
|
3413
|
+
type: 'cache';
|
|
3414
|
+
/** The cache adapter instance */
|
|
3415
|
+
adapter: CacheAdapter;
|
|
3416
|
+
}
|
|
3417
|
+
/**
|
|
3418
|
+
* Discriminated union of all provider types.
|
|
3419
|
+
* Each adapter/plugin `.forRoot()` returns one of these variants.
|
|
3420
|
+
*
|
|
3421
|
+
* @example
|
|
3422
|
+
* ```typescript
|
|
3423
|
+
* const providers: MagnetProvider[] = [
|
|
3424
|
+
* MongooseDatabaseAdapter.forRoot(),
|
|
3425
|
+
* StripePlugin.forRoot({ currency: 'usd' }),
|
|
3426
|
+
* ]
|
|
3427
|
+
* MagnetModule.forRoot(providers, { admin: true })
|
|
3428
|
+
* ```
|
|
3429
|
+
*/
|
|
3430
|
+
type MagnetProvider = DatabaseMagnetProvider | StorageMagnetProvider | EmailMagnetProvider | VaultMagnetProvider | AuthMagnetProvider | PluginMagnetProvider | CacheMagnetProvider;
|
|
3431
|
+
|
|
3432
|
+
declare function Resolver(optionsOrFn: ResolverInput): ClassDecorator;
|
|
3433
|
+
|
|
3434
|
+
declare function Resolve(optionsOrFn: ResolveInput): MethodDecorator;
|
|
3435
|
+
|
|
3436
|
+
/**
|
|
3437
|
+
* Options for the @ExtendUser decorator
|
|
3438
|
+
*/
|
|
3439
|
+
interface ExtendUserOptions {
|
|
675
3440
|
/**
|
|
676
|
-
*
|
|
3441
|
+
* Whether to include timestamps (createdAt, updatedAt)
|
|
3442
|
+
* @default true
|
|
677
3443
|
*/
|
|
678
|
-
|
|
679
|
-
constructor({ db, jwt, auth, internationalization, playground, storage, plugins, }: MagnetModuleOptions);
|
|
3444
|
+
timestamps?: boolean;
|
|
680
3445
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
3446
|
+
/**
|
|
3447
|
+
* Marks a class as a User extension.
|
|
3448
|
+
*
|
|
3449
|
+
* The decorated class will inherit base User fields (id, email, password, role, etc.)
|
|
3450
|
+
* and can add additional custom fields using Field decorators.
|
|
3451
|
+
*
|
|
3452
|
+
* @example
|
|
3453
|
+
* ```typescript
|
|
3454
|
+
* @ExtendUser()
|
|
3455
|
+
* export class User {
|
|
3456
|
+
* // Base fields inherited: id, email, password, role, createdAt, updatedAt
|
|
3457
|
+
*
|
|
3458
|
+
* @Field.Text({ required: true })
|
|
3459
|
+
* @Field.Validators(IsString(), Length(1, 50))
|
|
3460
|
+
* firstName: string
|
|
3461
|
+
*
|
|
3462
|
+
* @Field.Text({ required: true })
|
|
3463
|
+
* @Field.Validators(IsString(), Length(1, 50))
|
|
3464
|
+
* lastName: string
|
|
3465
|
+
*
|
|
3466
|
+
* @Field.Image({ folder: 'avatars' })
|
|
3467
|
+
* avatar?: string
|
|
3468
|
+
*
|
|
3469
|
+
* get fullName() {
|
|
3470
|
+
* return `${this.firstName} ${this.lastName}`
|
|
3471
|
+
* }
|
|
3472
|
+
* }
|
|
3473
|
+
* ```
|
|
3474
|
+
*/
|
|
3475
|
+
declare function ExtendUser(options?: ExtendUserOptions): ClassDecorator;
|
|
3476
|
+
/**
|
|
3477
|
+
* Check if a class is marked as a User extension
|
|
3478
|
+
*/
|
|
3479
|
+
declare function isUserExtension(target: Function): boolean;
|
|
3480
|
+
/**
|
|
3481
|
+
* Get ExtendUser options from a class
|
|
3482
|
+
*/
|
|
3483
|
+
declare function getExtendUserOptions(target: Function): ExtendUserOptions | undefined;
|
|
686
3484
|
|
|
687
|
-
|
|
688
|
-
path: string;
|
|
689
|
-
schema: string;
|
|
690
|
-
methods: DiscoveredMethod[];
|
|
691
|
-
};
|
|
692
|
-
type DiscoveredMethod = {
|
|
693
|
-
name: string;
|
|
694
|
-
method: string;
|
|
695
|
-
};
|
|
696
|
-
type DiscoveredSchema = {
|
|
697
|
-
name: string;
|
|
698
|
-
tableName: string;
|
|
699
|
-
target: string | object;
|
|
700
|
-
};
|
|
3485
|
+
declare function Prop(options?: PropOptions): PropertyDecorator;
|
|
701
3486
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
name: string;
|
|
705
|
-
constraints: any[];
|
|
706
|
-
}[];
|
|
3487
|
+
declare function Schema(options?: SchemaOptions): ClassDecorator;
|
|
3488
|
+
declare function getSchemaOptions(target: Function): SchemaOptions;
|
|
707
3489
|
|
|
708
|
-
|
|
709
|
-
|
|
3490
|
+
declare function Setting(): ClassDecorator;
|
|
3491
|
+
|
|
3492
|
+
/**
|
|
3493
|
+
* Enhanced Settings class decorator.
|
|
3494
|
+
*
|
|
3495
|
+
* Marks a class as a settings schema with group, label, and icon configuration.
|
|
3496
|
+
*
|
|
3497
|
+
* @example
|
|
3498
|
+
* ```typescript
|
|
3499
|
+
* @Settings({ group: 'general', label: 'General Settings', icon: 'settings' })
|
|
3500
|
+
* export class GeneralSettings {
|
|
3501
|
+
* @SettingField.Text({ label: 'Site Name' })
|
|
3502
|
+
* siteName: string = 'My Site'
|
|
3503
|
+
*
|
|
3504
|
+
* @SettingField.Boolean({ label: 'Maintenance Mode' })
|
|
3505
|
+
* maintenanceMode: boolean = false
|
|
3506
|
+
* }
|
|
3507
|
+
* ```
|
|
3508
|
+
*/
|
|
3509
|
+
declare function Settings(options: SettingsDecoratorOptions): ClassDecorator;
|
|
3510
|
+
/**
|
|
3511
|
+
* Get settings options from a class
|
|
3512
|
+
*/
|
|
3513
|
+
declare function getSettingsOptions(target: Function): SettingsDecoratorOptions | undefined;
|
|
3514
|
+
/**
|
|
3515
|
+
* Get all setting field metadata from a class
|
|
3516
|
+
*/
|
|
3517
|
+
declare function getSettingFields(target: Function): SettingFieldMetadata[];
|
|
3518
|
+
/**
|
|
3519
|
+
* SettingField decorator namespace.
|
|
3520
|
+
*
|
|
3521
|
+
* Provides type-safe field decorators for settings classes.
|
|
3522
|
+
*
|
|
3523
|
+
* @example
|
|
3524
|
+
* ```typescript
|
|
3525
|
+
* @Settings({ group: 'email', label: 'Email Settings', icon: 'mail' })
|
|
3526
|
+
* export class EmailSettings {
|
|
3527
|
+
* @SettingField.Select({
|
|
3528
|
+
* label: 'Provider',
|
|
3529
|
+
* options: ['smtp', 'sendgrid', 'resend'],
|
|
3530
|
+
* default: 'smtp'
|
|
3531
|
+
* })
|
|
3532
|
+
* provider: string = 'smtp'
|
|
3533
|
+
*
|
|
3534
|
+
* @SettingField.Secret({ label: 'API Key', masked: true })
|
|
3535
|
+
* apiKey?: string
|
|
3536
|
+
* }
|
|
3537
|
+
* ```
|
|
3538
|
+
*/
|
|
3539
|
+
declare const SettingField: {
|
|
710
3540
|
/**
|
|
711
|
-
*
|
|
3541
|
+
* Text setting field
|
|
712
3542
|
*/
|
|
713
|
-
|
|
3543
|
+
readonly Text: (options: SettingTextOptions) => PropertyDecorator;
|
|
714
3544
|
/**
|
|
715
|
-
*
|
|
3545
|
+
* Number setting field
|
|
716
3546
|
*/
|
|
717
|
-
|
|
718
|
-
/**
|
|
719
|
-
* Auto-publish drafts after a certain time
|
|
720
|
-
*/
|
|
721
|
-
autoPublish?: boolean;
|
|
722
|
-
/**
|
|
723
|
-
* Require approval before publishing
|
|
724
|
-
*/
|
|
725
|
-
requireApproval?: boolean;
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
interface VersionData<T> {
|
|
3547
|
+
readonly Number: (options: SettingNumberOptions) => PropertyDecorator;
|
|
729
3548
|
/**
|
|
730
|
-
*
|
|
3549
|
+
* Boolean setting field (toggle/switch)
|
|
731
3550
|
*/
|
|
732
|
-
|
|
3551
|
+
readonly Boolean: (options: SettingBooleanOptions) => PropertyDecorator;
|
|
733
3552
|
/**
|
|
734
|
-
*
|
|
3553
|
+
* Select setting field (dropdown)
|
|
735
3554
|
*/
|
|
736
|
-
|
|
3555
|
+
readonly Select: (options: SettingSelectOptions) => PropertyDecorator;
|
|
737
3556
|
/**
|
|
738
|
-
*
|
|
3557
|
+
* Secret setting field (encrypted, masked in UI)
|
|
739
3558
|
*/
|
|
740
|
-
|
|
3559
|
+
readonly Secret: (options: SettingSecretOptions) => PropertyDecorator;
|
|
741
3560
|
/**
|
|
742
|
-
*
|
|
3561
|
+
* Image setting field
|
|
743
3562
|
*/
|
|
744
|
-
|
|
3563
|
+
readonly Image: (options: SettingImageOptions) => PropertyDecorator;
|
|
745
3564
|
/**
|
|
746
|
-
*
|
|
3565
|
+
* JSON setting field
|
|
747
3566
|
*/
|
|
748
|
-
|
|
3567
|
+
readonly JSON: (options: SettingJSONOptions) => PropertyDecorator;
|
|
749
3568
|
/**
|
|
750
|
-
*
|
|
3569
|
+
* Textarea setting field (multi-line text)
|
|
751
3570
|
*/
|
|
752
|
-
|
|
3571
|
+
readonly Textarea: (options: SettingTextOptions) => PropertyDecorator;
|
|
3572
|
+
};
|
|
3573
|
+
/**
|
|
3574
|
+
* Type for the SettingField namespace
|
|
3575
|
+
*/
|
|
3576
|
+
type SettingFieldNamespace = typeof SettingField;
|
|
3577
|
+
|
|
3578
|
+
interface UIFieldMetadata {
|
|
3579
|
+
propertyKey: string | symbol;
|
|
3580
|
+
options: UIDecoratorOptions & {
|
|
3581
|
+
designType: Function;
|
|
3582
|
+
};
|
|
3583
|
+
}
|
|
3584
|
+
declare function UI(options: UIDecoratorOptions): PropertyDecorator;
|
|
3585
|
+
|
|
3586
|
+
declare const Validators: (...validators: PropertyDecorator[]) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
3587
|
+
|
|
3588
|
+
declare const VERSION_METADATA_KEY = "version:metadata";
|
|
3589
|
+
/**
|
|
3590
|
+
* Decorator to enable versioning for a schema
|
|
3591
|
+
* @param config Version configuration
|
|
3592
|
+
*/
|
|
3593
|
+
declare function Version(config?: VersionConfig): ClassDecorator;
|
|
3594
|
+
|
|
3595
|
+
declare function InjectModel(model: Type): ParameterDecorator;
|
|
3596
|
+
|
|
3597
|
+
/**
|
|
3598
|
+
* Error codes for programmatic error handling
|
|
3599
|
+
* Organized by category for easy identification
|
|
3600
|
+
*/
|
|
3601
|
+
declare enum ErrorCode {
|
|
3602
|
+
VALIDATION_FAILED = 1000,
|
|
3603
|
+
REQUIRED_FIELD_MISSING = 1001,
|
|
3604
|
+
INVALID_FORMAT = 1002,
|
|
3605
|
+
VALUE_OUT_OF_RANGE = 1003,
|
|
3606
|
+
UNIQUE_CONSTRAINT_VIOLATION = 1004,
|
|
3607
|
+
AUTHENTICATION_REQUIRED = 2000,
|
|
3608
|
+
INVALID_CREDENTIALS = 2001,
|
|
3609
|
+
TOKEN_EXPIRED = 2002,
|
|
3610
|
+
TOKEN_INVALID = 2003,
|
|
3611
|
+
ACCOUNT_LOCKED = 2004,
|
|
3612
|
+
EMAIL_NOT_VERIFIED = 2005,
|
|
3613
|
+
PERMISSION_DENIED = 3000,
|
|
3614
|
+
INSUFFICIENT_PERMISSIONS = 3001,
|
|
3615
|
+
ROLE_NOT_FOUND = 3002,
|
|
3616
|
+
PERMISSION_NOT_FOUND = 3003,
|
|
3617
|
+
RESOURCE_NOT_FOUND = 4000,
|
|
3618
|
+
SCHEMA_NOT_FOUND = 4001,
|
|
3619
|
+
DOCUMENT_NOT_FOUND = 4002,
|
|
3620
|
+
USER_NOT_FOUND = 4003,
|
|
3621
|
+
FILE_NOT_FOUND = 4004,
|
|
3622
|
+
VERSION_NOT_FOUND = 4005,
|
|
3623
|
+
DATABASE_ERROR = 5000,
|
|
3624
|
+
CONNECTION_FAILED = 5001,
|
|
3625
|
+
QUERY_FAILED = 5002,
|
|
3626
|
+
TRANSACTION_FAILED = 5003,
|
|
3627
|
+
DUPLICATE_KEY = 5004,
|
|
3628
|
+
PLUGIN_ERROR = 6000,
|
|
3629
|
+
PLUGIN_NOT_FOUND = 6001,
|
|
3630
|
+
PLUGIN_INITIALIZATION_FAILED = 6002,
|
|
3631
|
+
HOOK_EXECUTION_FAILED = 6003,
|
|
3632
|
+
EXTERNAL_SERVICE_ERROR = 7000,
|
|
3633
|
+
STORAGE_ERROR = 7001,
|
|
3634
|
+
EMAIL_SERVICE_ERROR = 7002,
|
|
3635
|
+
WEBHOOK_DELIVERY_FAILED = 7003,
|
|
3636
|
+
INTERNAL_ERROR = 9000,
|
|
3637
|
+
CONFIGURATION_ERROR = 9001,
|
|
3638
|
+
UNEXPECTED_ERROR = 9999
|
|
3639
|
+
}
|
|
3640
|
+
/**
|
|
3641
|
+
* Operation types for error metadata
|
|
3642
|
+
*/
|
|
3643
|
+
type OperationType = 'create' | 'read' | 'update' | 'delete' | 'publish';
|
|
3644
|
+
/**
|
|
3645
|
+
* Error metadata for additional context
|
|
3646
|
+
*/
|
|
3647
|
+
interface ErrorMetadata {
|
|
3648
|
+
/** Schema/collection name */
|
|
3649
|
+
schema?: string;
|
|
3650
|
+
/** Field name that caused the error */
|
|
3651
|
+
field?: string;
|
|
3652
|
+
/** Document/resource ID */
|
|
3653
|
+
resourceId?: string;
|
|
3654
|
+
/** Operation being performed */
|
|
3655
|
+
operation?: OperationType;
|
|
3656
|
+
/** Additional context */
|
|
3657
|
+
context?: Record<string, unknown>;
|
|
3658
|
+
}
|
|
3659
|
+
/**
|
|
3660
|
+
* Validation error detail
|
|
3661
|
+
*/
|
|
3662
|
+
interface ValidationErrorDetail {
|
|
3663
|
+
field: string;
|
|
3664
|
+
message: string;
|
|
3665
|
+
constraint?: string;
|
|
3666
|
+
value?: unknown;
|
|
3667
|
+
}
|
|
3668
|
+
/**
|
|
3669
|
+
* Standard error response structure for API consumers
|
|
3670
|
+
*/
|
|
3671
|
+
interface ErrorResponse {
|
|
3672
|
+
error: {
|
|
3673
|
+
code: ErrorCode;
|
|
3674
|
+
message: string;
|
|
3675
|
+
name: string;
|
|
3676
|
+
timestamp: string;
|
|
3677
|
+
metadata?: ErrorMetadata;
|
|
3678
|
+
details?: ValidationErrorDetail[];
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3681
|
+
/**
|
|
3682
|
+
* Base error class for all Magnet errors
|
|
3683
|
+
* Provides consistent error handling with codes, metadata, and serialization
|
|
3684
|
+
*/
|
|
3685
|
+
declare abstract class MagnetError extends Error {
|
|
3686
|
+
abstract readonly code: ErrorCode;
|
|
3687
|
+
abstract readonly httpStatus: number;
|
|
3688
|
+
readonly timestamp: Date;
|
|
3689
|
+
readonly metadata: ErrorMetadata;
|
|
3690
|
+
constructor(message: string, metadata?: ErrorMetadata);
|
|
753
3691
|
/**
|
|
754
|
-
*
|
|
3692
|
+
* Convert to API response format
|
|
755
3693
|
*/
|
|
756
|
-
|
|
3694
|
+
toResponse(): ErrorResponse;
|
|
3695
|
+
/**
|
|
3696
|
+
* Convert to JSON for logging
|
|
3697
|
+
*/
|
|
3698
|
+
toJSON(): Record<string, unknown>;
|
|
757
3699
|
}
|
|
758
3700
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
3701
|
+
/**
|
|
3702
|
+
* Interface for class-validator error format
|
|
3703
|
+
*/
|
|
3704
|
+
interface ClassValidatorError {
|
|
3705
|
+
property: string;
|
|
3706
|
+
value?: unknown;
|
|
3707
|
+
constraints?: Record<string, string>;
|
|
3708
|
+
children?: ClassValidatorError[];
|
|
3709
|
+
}
|
|
3710
|
+
/**
|
|
3711
|
+
* Validation error with field-level details
|
|
3712
|
+
* Used for form validation and input validation failures
|
|
3713
|
+
*/
|
|
3714
|
+
declare class ValidationError extends MagnetError {
|
|
3715
|
+
readonly code = ErrorCode.VALIDATION_FAILED;
|
|
3716
|
+
readonly httpStatus = 400;
|
|
3717
|
+
readonly details: ValidationErrorDetail[];
|
|
3718
|
+
constructor(message: string, details: ValidationErrorDetail[], metadata?: ErrorMetadata);
|
|
3719
|
+
/**
|
|
3720
|
+
* Create ValidationError from class-validator errors
|
|
3721
|
+
*/
|
|
3722
|
+
static fromClassValidator(errors: ClassValidatorError[]): ValidationError;
|
|
3723
|
+
toResponse(): ErrorResponse;
|
|
3724
|
+
}
|
|
3725
|
+
/**
|
|
3726
|
+
* Required field missing error
|
|
3727
|
+
*/
|
|
3728
|
+
declare class RequiredFieldError extends MagnetError {
|
|
3729
|
+
readonly code = ErrorCode.REQUIRED_FIELD_MISSING;
|
|
3730
|
+
readonly httpStatus = 400;
|
|
3731
|
+
constructor(field: string, metadata?: ErrorMetadata);
|
|
3732
|
+
}
|
|
3733
|
+
/**
|
|
3734
|
+
* Invalid format error (e.g., invalid email, invalid URL)
|
|
3735
|
+
*/
|
|
3736
|
+
declare class InvalidFormatError extends MagnetError {
|
|
3737
|
+
readonly code = ErrorCode.INVALID_FORMAT;
|
|
3738
|
+
readonly httpStatus = 400;
|
|
3739
|
+
constructor(field: string, expectedFormat: string, metadata?: ErrorMetadata);
|
|
3740
|
+
}
|
|
3741
|
+
/**
|
|
3742
|
+
* Value out of range error
|
|
3743
|
+
*/
|
|
3744
|
+
declare class ValueOutOfRangeError extends MagnetError {
|
|
3745
|
+
readonly code = ErrorCode.VALUE_OUT_OF_RANGE;
|
|
3746
|
+
readonly httpStatus = 400;
|
|
3747
|
+
constructor(field: string, min?: number, max?: number, metadata?: ErrorMetadata);
|
|
3748
|
+
}
|
|
3749
|
+
|
|
3750
|
+
/**
|
|
3751
|
+
* Authentication required error
|
|
3752
|
+
* Thrown when a request requires authentication but none is provided
|
|
3753
|
+
*/
|
|
3754
|
+
declare class AuthenticationRequiredError extends MagnetError {
|
|
3755
|
+
readonly code = ErrorCode.AUTHENTICATION_REQUIRED;
|
|
3756
|
+
readonly httpStatus = 401;
|
|
3757
|
+
constructor(message?: string, metadata?: ErrorMetadata);
|
|
3758
|
+
}
|
|
3759
|
+
/**
|
|
3760
|
+
* Invalid credentials error
|
|
3761
|
+
* Thrown when email/password combination is incorrect
|
|
3762
|
+
*/
|
|
3763
|
+
declare class InvalidCredentialsError extends MagnetError {
|
|
3764
|
+
readonly code = ErrorCode.INVALID_CREDENTIALS;
|
|
3765
|
+
readonly httpStatus = 401;
|
|
3766
|
+
constructor(message?: string, metadata?: ErrorMetadata);
|
|
3767
|
+
}
|
|
3768
|
+
/**
|
|
3769
|
+
* Token expired error
|
|
3770
|
+
* Thrown when a JWT or refresh token has expired
|
|
3771
|
+
*/
|
|
3772
|
+
declare class TokenExpiredError extends MagnetError {
|
|
3773
|
+
readonly code = ErrorCode.TOKEN_EXPIRED;
|
|
3774
|
+
readonly httpStatus = 401;
|
|
3775
|
+
constructor(message?: string, metadata?: ErrorMetadata);
|
|
3776
|
+
}
|
|
3777
|
+
/**
|
|
3778
|
+
* Token invalid error
|
|
3779
|
+
* Thrown when a JWT or refresh token is malformed or invalid
|
|
3780
|
+
*/
|
|
3781
|
+
declare class TokenInvalidError extends MagnetError {
|
|
3782
|
+
readonly code = ErrorCode.TOKEN_INVALID;
|
|
3783
|
+
readonly httpStatus = 401;
|
|
3784
|
+
constructor(message?: string, metadata?: ErrorMetadata);
|
|
3785
|
+
}
|
|
3786
|
+
/**
|
|
3787
|
+
* Account locked error
|
|
3788
|
+
* Thrown when an account is temporarily locked due to too many failed attempts
|
|
3789
|
+
*/
|
|
3790
|
+
declare class AccountLockedError extends MagnetError {
|
|
3791
|
+
readonly code = ErrorCode.ACCOUNT_LOCKED;
|
|
3792
|
+
readonly httpStatus = 423;
|
|
3793
|
+
readonly unlockAt?: Date;
|
|
3794
|
+
constructor(message?: string, unlockAt?: Date, metadata?: ErrorMetadata);
|
|
3795
|
+
}
|
|
3796
|
+
/**
|
|
3797
|
+
* Email not verified error
|
|
3798
|
+
* Thrown when an action requires email verification
|
|
3799
|
+
*/
|
|
3800
|
+
declare class EmailNotVerifiedError extends MagnetError {
|
|
3801
|
+
readonly code = ErrorCode.EMAIL_NOT_VERIFIED;
|
|
3802
|
+
readonly httpStatus = 403;
|
|
3803
|
+
constructor(message?: string, metadata?: ErrorMetadata);
|
|
3804
|
+
}
|
|
3805
|
+
/**
|
|
3806
|
+
* Permission denied error
|
|
3807
|
+
* Thrown when user lacks permission for an action
|
|
3808
|
+
*/
|
|
3809
|
+
declare class PermissionDeniedError extends MagnetError {
|
|
3810
|
+
readonly code = ErrorCode.PERMISSION_DENIED;
|
|
3811
|
+
readonly httpStatus = 403;
|
|
3812
|
+
readonly requiredPermission?: string;
|
|
3813
|
+
constructor(message?: string, requiredPermission?: string, metadata?: ErrorMetadata);
|
|
3814
|
+
}
|
|
3815
|
+
/**
|
|
3816
|
+
* Insufficient permissions error
|
|
3817
|
+
* Thrown when user has some permissions but not enough
|
|
3818
|
+
*/
|
|
3819
|
+
declare class InsufficientPermissionsError extends MagnetError {
|
|
3820
|
+
readonly code = ErrorCode.INSUFFICIENT_PERMISSIONS;
|
|
3821
|
+
readonly httpStatus = 403;
|
|
3822
|
+
readonly requiredPermissions: string[];
|
|
3823
|
+
constructor(requiredPermissions: string[], metadata?: ErrorMetadata);
|
|
3824
|
+
}
|
|
3825
|
+
/**
|
|
3826
|
+
* Role not found error
|
|
3827
|
+
*/
|
|
3828
|
+
declare class RoleNotFoundError extends MagnetError {
|
|
3829
|
+
readonly code = ErrorCode.ROLE_NOT_FOUND;
|
|
3830
|
+
readonly httpStatus = 404;
|
|
3831
|
+
constructor(roleName: string, metadata?: ErrorMetadata);
|
|
3832
|
+
}
|
|
3833
|
+
/**
|
|
3834
|
+
* Permission not found error
|
|
3835
|
+
* Thrown when assigning invalid permission IDs to a role
|
|
3836
|
+
*/
|
|
3837
|
+
declare class PermissionNotFoundError extends MagnetError {
|
|
3838
|
+
readonly code = ErrorCode.PERMISSION_NOT_FOUND;
|
|
3839
|
+
readonly httpStatus = 400;
|
|
3840
|
+
readonly invalidPermissionIds: string[];
|
|
3841
|
+
constructor(invalidPermissionIds: string[], metadata?: ErrorMetadata);
|
|
3842
|
+
}
|
|
3843
|
+
|
|
3844
|
+
/**
|
|
3845
|
+
* Resource not found error
|
|
3846
|
+
* Generic error for any resource type
|
|
3847
|
+
*/
|
|
3848
|
+
declare class ResourceNotFoundError extends MagnetError {
|
|
3849
|
+
readonly code = ErrorCode.RESOURCE_NOT_FOUND;
|
|
3850
|
+
readonly httpStatus = 404;
|
|
3851
|
+
constructor(resourceType: string, identifier: string, metadata?: ErrorMetadata);
|
|
3852
|
+
}
|
|
3853
|
+
/**
|
|
3854
|
+
* Schema not found error
|
|
3855
|
+
* Thrown when a schema/collection doesn't exist
|
|
3856
|
+
*/
|
|
3857
|
+
declare class SchemaNotFoundError extends MagnetError {
|
|
3858
|
+
readonly code = ErrorCode.SCHEMA_NOT_FOUND;
|
|
3859
|
+
readonly httpStatus = 404;
|
|
3860
|
+
constructor(schemaName: string, metadata?: ErrorMetadata);
|
|
3861
|
+
}
|
|
3862
|
+
/**
|
|
3863
|
+
* Document not found error
|
|
3864
|
+
* Thrown when a document doesn't exist in a schema
|
|
3865
|
+
*/
|
|
3866
|
+
declare class DocumentNotFoundError extends MagnetError {
|
|
3867
|
+
readonly code = ErrorCode.DOCUMENT_NOT_FOUND;
|
|
3868
|
+
readonly httpStatus = 404;
|
|
3869
|
+
constructor(schema: string, id: string, metadata?: ErrorMetadata);
|
|
3870
|
+
}
|
|
3871
|
+
/**
|
|
3872
|
+
* User not found error
|
|
3873
|
+
*/
|
|
3874
|
+
declare class UserNotFoundError extends MagnetError {
|
|
3875
|
+
readonly code = ErrorCode.USER_NOT_FOUND;
|
|
3876
|
+
readonly httpStatus = 404;
|
|
3877
|
+
constructor(identifier: string, metadata?: ErrorMetadata);
|
|
3878
|
+
}
|
|
3879
|
+
/**
|
|
3880
|
+
* File not found error
|
|
3881
|
+
*/
|
|
3882
|
+
declare class FileNotFoundError extends MagnetError {
|
|
3883
|
+
readonly code = ErrorCode.FILE_NOT_FOUND;
|
|
3884
|
+
readonly httpStatus = 404;
|
|
3885
|
+
constructor(path: string, metadata?: ErrorMetadata);
|
|
3886
|
+
}
|
|
3887
|
+
/**
|
|
3888
|
+
* Version not found error
|
|
3889
|
+
* Thrown when a specific version of a document doesn't exist
|
|
3890
|
+
*/
|
|
3891
|
+
declare class VersionNotFoundError extends MagnetError {
|
|
3892
|
+
readonly code = ErrorCode.VERSION_NOT_FOUND;
|
|
3893
|
+
readonly httpStatus = 404;
|
|
3894
|
+
constructor(schema: string, documentId: string, versionId: string, metadata?: ErrorMetadata);
|
|
3895
|
+
}
|
|
3896
|
+
|
|
3897
|
+
/**
|
|
3898
|
+
* Generic database error
|
|
3899
|
+
* Used when a database operation fails
|
|
3900
|
+
*/
|
|
3901
|
+
declare class DatabaseError extends MagnetError {
|
|
3902
|
+
readonly code = ErrorCode.DATABASE_ERROR;
|
|
3903
|
+
readonly httpStatus = 500;
|
|
3904
|
+
readonly originalError?: unknown;
|
|
3905
|
+
constructor(message: string, originalError?: unknown, metadata?: ErrorMetadata);
|
|
3906
|
+
}
|
|
3907
|
+
/**
|
|
3908
|
+
* Database connection failed error
|
|
3909
|
+
*/
|
|
3910
|
+
declare class ConnectionFailedError extends MagnetError {
|
|
3911
|
+
readonly code = ErrorCode.CONNECTION_FAILED;
|
|
3912
|
+
readonly httpStatus = 503;
|
|
3913
|
+
constructor(message?: string, metadata?: ErrorMetadata);
|
|
3914
|
+
}
|
|
3915
|
+
/**
|
|
3916
|
+
* Query failed error
|
|
3917
|
+
*/
|
|
3918
|
+
declare class QueryFailedError extends MagnetError {
|
|
3919
|
+
readonly code = ErrorCode.QUERY_FAILED;
|
|
3920
|
+
readonly httpStatus = 500;
|
|
3921
|
+
readonly query?: string;
|
|
3922
|
+
constructor(message: string, query?: string, metadata?: ErrorMetadata);
|
|
3923
|
+
}
|
|
3924
|
+
/**
|
|
3925
|
+
* Transaction failed error
|
|
3926
|
+
*/
|
|
3927
|
+
declare class TransactionFailedError extends MagnetError {
|
|
3928
|
+
readonly code = ErrorCode.TRANSACTION_FAILED;
|
|
3929
|
+
readonly httpStatus = 500;
|
|
3930
|
+
constructor(message?: string, metadata?: ErrorMetadata);
|
|
3931
|
+
}
|
|
3932
|
+
/**
|
|
3933
|
+
* Duplicate key error
|
|
3934
|
+
* Thrown when attempting to insert a duplicate value for a unique field
|
|
3935
|
+
*/
|
|
3936
|
+
declare class DuplicateKeyError extends MagnetError {
|
|
3937
|
+
readonly code = ErrorCode.DUPLICATE_KEY;
|
|
3938
|
+
readonly httpStatus = 409;
|
|
3939
|
+
constructor(field: string, value: unknown, metadata?: ErrorMetadata);
|
|
3940
|
+
}
|
|
3941
|
+
|
|
3942
|
+
/**
|
|
3943
|
+
* Generic plugin error
|
|
3944
|
+
*/
|
|
3945
|
+
declare class PluginError extends MagnetError {
|
|
3946
|
+
readonly code = ErrorCode.PLUGIN_ERROR;
|
|
3947
|
+
readonly httpStatus = 500;
|
|
3948
|
+
constructor(pluginName: string, message: string, metadata?: ErrorMetadata);
|
|
3949
|
+
}
|
|
3950
|
+
/**
|
|
3951
|
+
* Plugin not found error
|
|
3952
|
+
*/
|
|
3953
|
+
declare class PluginNotFoundError extends MagnetError {
|
|
3954
|
+
readonly code = ErrorCode.PLUGIN_NOT_FOUND;
|
|
3955
|
+
readonly httpStatus = 404;
|
|
3956
|
+
constructor(pluginName: string, metadata?: ErrorMetadata);
|
|
3957
|
+
}
|
|
3958
|
+
/**
|
|
3959
|
+
* Plugin initialization error
|
|
3960
|
+
* Thrown when a plugin fails to initialize
|
|
3961
|
+
*/
|
|
3962
|
+
declare class PluginInitializationError extends MagnetError {
|
|
3963
|
+
readonly code = ErrorCode.PLUGIN_INITIALIZATION_FAILED;
|
|
3964
|
+
readonly httpStatus = 500;
|
|
3965
|
+
constructor(pluginName: string, reason: string, metadata?: ErrorMetadata);
|
|
3966
|
+
}
|
|
3967
|
+
/**
|
|
3968
|
+
* Hook execution error
|
|
3969
|
+
* Thrown when a plugin hook fails to execute
|
|
3970
|
+
*/
|
|
3971
|
+
declare class HookExecutionError extends MagnetError {
|
|
3972
|
+
readonly code = ErrorCode.HOOK_EXECUTION_FAILED;
|
|
3973
|
+
readonly httpStatus = 500;
|
|
3974
|
+
constructor(hookName: string, pluginName: string, reason: string, metadata?: ErrorMetadata);
|
|
3975
|
+
}
|
|
3976
|
+
|
|
3977
|
+
/**
|
|
3978
|
+
* Generic external service error
|
|
3979
|
+
*/
|
|
3980
|
+
declare class ExternalServiceError extends MagnetError {
|
|
3981
|
+
readonly code = ErrorCode.EXTERNAL_SERVICE_ERROR;
|
|
3982
|
+
readonly httpStatus = 502;
|
|
3983
|
+
constructor(serviceName: string, message: string, metadata?: ErrorMetadata);
|
|
3984
|
+
}
|
|
3985
|
+
/**
|
|
3986
|
+
* Storage service error
|
|
3987
|
+
*/
|
|
3988
|
+
declare class StorageError extends MagnetError {
|
|
3989
|
+
readonly code = ErrorCode.STORAGE_ERROR;
|
|
3990
|
+
readonly httpStatus = 502;
|
|
3991
|
+
constructor(message: string, metadata?: ErrorMetadata);
|
|
3992
|
+
}
|
|
3993
|
+
/**
|
|
3994
|
+
* Email service error
|
|
3995
|
+
*/
|
|
3996
|
+
declare class EmailServiceError extends MagnetError {
|
|
3997
|
+
readonly code = ErrorCode.EMAIL_SERVICE_ERROR;
|
|
3998
|
+
readonly httpStatus = 502;
|
|
3999
|
+
constructor(message: string, metadata?: ErrorMetadata);
|
|
4000
|
+
}
|
|
4001
|
+
/**
|
|
4002
|
+
* Webhook delivery error
|
|
4003
|
+
*/
|
|
4004
|
+
declare class WebhookDeliveryError extends MagnetError {
|
|
4005
|
+
readonly code = ErrorCode.WEBHOOK_DELIVERY_FAILED;
|
|
4006
|
+
readonly httpStatus = 502;
|
|
4007
|
+
readonly webhookUrl: string;
|
|
4008
|
+
readonly statusCode?: number;
|
|
4009
|
+
constructor(webhookUrl: string, reason: string, statusCode?: number, metadata?: ErrorMetadata);
|
|
4010
|
+
}
|
|
4011
|
+
|
|
4012
|
+
/**
|
|
4013
|
+
* Internal error
|
|
4014
|
+
* Used for unexpected server errors
|
|
4015
|
+
*/
|
|
4016
|
+
declare class InternalError extends MagnetError {
|
|
4017
|
+
readonly code = ErrorCode.INTERNAL_ERROR;
|
|
4018
|
+
readonly httpStatus = 500;
|
|
4019
|
+
constructor(message?: string, metadata?: ErrorMetadata);
|
|
4020
|
+
}
|
|
4021
|
+
/**
|
|
4022
|
+
* Configuration error
|
|
4023
|
+
* Used when application configuration is invalid
|
|
4024
|
+
*/
|
|
4025
|
+
declare class ConfigurationError extends MagnetError {
|
|
4026
|
+
readonly code = ErrorCode.CONFIGURATION_ERROR;
|
|
4027
|
+
readonly httpStatus = 500;
|
|
4028
|
+
constructor(message: string, metadata?: ErrorMetadata);
|
|
4029
|
+
}
|
|
4030
|
+
/**
|
|
4031
|
+
* Unexpected error
|
|
4032
|
+
* Used as a fallback for unknown error types
|
|
4033
|
+
*/
|
|
4034
|
+
declare class UnexpectedError extends MagnetError {
|
|
4035
|
+
readonly code = ErrorCode.UNEXPECTED_ERROR;
|
|
4036
|
+
readonly httpStatus = 500;
|
|
4037
|
+
readonly originalError?: unknown;
|
|
4038
|
+
constructor(message?: string, originalError?: unknown, metadata?: ErrorMetadata);
|
|
4039
|
+
}
|
|
4040
|
+
|
|
4041
|
+
/**
|
|
4042
|
+
* Context for error conversion
|
|
4043
|
+
*/
|
|
4044
|
+
interface ErrorContext {
|
|
4045
|
+
schema?: string;
|
|
4046
|
+
operation?: string;
|
|
4047
|
+
}
|
|
4048
|
+
/**
|
|
4049
|
+
* Convert MongoDB/Mongoose errors to typed Magnet errors
|
|
4050
|
+
* @param error - The original error from Mongoose
|
|
4051
|
+
* @param context - Optional context about the operation
|
|
4052
|
+
* @returns A typed MagnetError
|
|
4053
|
+
*/
|
|
4054
|
+
declare function fromMongooseError(error: unknown, context?: ErrorContext): MagnetError;
|
|
4055
|
+
/**
|
|
4056
|
+
* Convert Drizzle/PostgreSQL errors to typed Magnet errors
|
|
4057
|
+
* @param error - The original error from Drizzle
|
|
4058
|
+
* @param context - Optional context about the operation
|
|
4059
|
+
* @returns A typed MagnetError
|
|
4060
|
+
*/
|
|
4061
|
+
declare function fromDrizzleError(error: unknown, context?: ErrorContext): MagnetError;
|
|
4062
|
+
/**
|
|
4063
|
+
* Check if an error is a MagnetError
|
|
4064
|
+
*/
|
|
4065
|
+
declare function isMagnetError(error: unknown): error is MagnetError;
|
|
4066
|
+
/**
|
|
4067
|
+
* Wrap an unknown error as a MagnetError
|
|
4068
|
+
* If already a MagnetError, returns it unchanged
|
|
4069
|
+
*/
|
|
4070
|
+
declare function wrapError(error: unknown, fallbackMessage?: string): MagnetError;
|
|
4071
|
+
|
|
4072
|
+
declare class ValidationException extends Error {
|
|
4073
|
+
readonly errors: ValidationError$1[];
|
|
4074
|
+
constructor(errors: ValidationError$1[]);
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4077
|
+
type SupportedAdapter = 'mongoose' | 'drizzle';
|
|
4078
|
+
/**
|
|
4079
|
+
* Detect the database adapter based on configuration or installed packages.
|
|
4080
|
+
*
|
|
4081
|
+
* With the provider-based API, the adapter is typically auto-registered:
|
|
4082
|
+
* importing `@magnet-cms/adapter-db-mongoose` or `@magnet-cms/adapter-db-drizzle`
|
|
4083
|
+
* calls `setDatabaseAdapter()` as a module-level side effect, so detection
|
|
4084
|
+
* happens automatically before any `@Schema()` decorators evaluate.
|
|
4085
|
+
*
|
|
4086
|
+
* Detection priority:
|
|
4087
|
+
* 1. Cached value from `setDatabaseAdapter()` (set by adapter package import side effect)
|
|
4088
|
+
* 2. Configuration-based: If `connectionString` or `dialect` is present, use drizzle
|
|
4089
|
+
* 3. Configuration-based: If `uri` is present, use mongoose
|
|
4090
|
+
* 4. Package-based: Check which adapter packages are installed (mongoose > drizzle)
|
|
4091
|
+
*
|
|
4092
|
+
* @param dbConfig - Optional database configuration to determine adapter from
|
|
4093
|
+
*/
|
|
4094
|
+
declare function detectDatabaseAdapter(dbConfig?: DBConfig): SupportedAdapter;
|
|
4095
|
+
/**
|
|
4096
|
+
* Explicitly set the database adapter.
|
|
4097
|
+
*
|
|
4098
|
+
* With the provider-based API, this is called automatically as a side effect
|
|
4099
|
+
* when importing an adapter package (e.g., `@magnet-cms/adapter-db-drizzle`).
|
|
4100
|
+
* Manual calls are no longer needed in typical user code.
|
|
4101
|
+
*
|
|
4102
|
+
* @internal Called by adapter package index.ts as module-level side effect
|
|
4103
|
+
*/
|
|
4104
|
+
declare function setDatabaseAdapter(adapter: SupportedAdapter): void;
|
|
4105
|
+
/**
|
|
4106
|
+
* Clear the cached adapter (useful for testing)
|
|
4107
|
+
*/
|
|
4108
|
+
declare function clearAdapterCache(): void;
|
|
4109
|
+
|
|
4110
|
+
/**
|
|
4111
|
+
* Get model injection token for any adapter
|
|
4112
|
+
* @param schema Schema class or schema name
|
|
4113
|
+
* @returns Injection token string
|
|
4114
|
+
*/
|
|
4115
|
+
declare function getModelToken(schema: Type | string): string;
|
|
4116
|
+
/**
|
|
4117
|
+
* Get adapter injection token
|
|
4118
|
+
* @returns Injection token for the database adapter
|
|
4119
|
+
*/
|
|
4120
|
+
declare function getAdapterToken(): string;
|
|
4121
|
+
declare function registerModel(token: string, model: unknown): void;
|
|
4122
|
+
declare function getRegisteredModel<T>(token: string): T | undefined;
|
|
4123
|
+
|
|
4124
|
+
declare const getSchemaToken: (schema: Type) => string;
|
|
4125
|
+
declare const getSettingToken: (setting: Type) => string;
|
|
4126
|
+
|
|
4127
|
+
/**
|
|
4128
|
+
* Type guard utilities for safe type narrowing
|
|
4129
|
+
* Use these instead of type assertions (as any, as unknown as T)
|
|
4130
|
+
*/
|
|
4131
|
+
/**
|
|
4132
|
+
* Check if value is a non-null object
|
|
4133
|
+
*/
|
|
4134
|
+
declare function isObject(value: unknown): value is Record<string, unknown>;
|
|
4135
|
+
/**
|
|
4136
|
+
* Check if value has a specific property
|
|
4137
|
+
*/
|
|
4138
|
+
declare function hasProperty<K extends string>(value: unknown, key: K): value is Record<K, unknown>;
|
|
4139
|
+
/**
|
|
4140
|
+
* Check if value has multiple properties
|
|
4141
|
+
*/
|
|
4142
|
+
declare function hasProperties<K extends string>(value: unknown, keys: K[]): value is Record<K, unknown>;
|
|
4143
|
+
/**
|
|
4144
|
+
* Check if value is a string
|
|
4145
|
+
*/
|
|
4146
|
+
declare function isString(value: unknown): value is string;
|
|
4147
|
+
/**
|
|
4148
|
+
* Check if value is a number
|
|
4149
|
+
*/
|
|
4150
|
+
declare function isNumber(value: unknown): value is number;
|
|
4151
|
+
/**
|
|
4152
|
+
* Check if value is a boolean
|
|
4153
|
+
*/
|
|
4154
|
+
declare function isBoolean(value: unknown): value is boolean;
|
|
4155
|
+
/**
|
|
4156
|
+
* Check if value is an array
|
|
4157
|
+
*/
|
|
4158
|
+
declare function isArray(value: unknown): value is unknown[];
|
|
4159
|
+
/**
|
|
4160
|
+
* Check if value is a string array
|
|
4161
|
+
*/
|
|
4162
|
+
declare function isStringArray(value: unknown): value is string[];
|
|
4163
|
+
/**
|
|
4164
|
+
* Check if value is a function
|
|
4165
|
+
*/
|
|
4166
|
+
declare function isFunction(value: unknown): value is Function;
|
|
4167
|
+
/**
|
|
4168
|
+
* Check if value is a valid document with ID
|
|
4169
|
+
*/
|
|
4170
|
+
declare function isDocument(value: unknown): value is {
|
|
4171
|
+
id: string;
|
|
4172
|
+
[key: string]: unknown;
|
|
4173
|
+
};
|
|
4174
|
+
/**
|
|
4175
|
+
* Check if error is a MongoDB CastError
|
|
4176
|
+
*/
|
|
4177
|
+
declare function isCastError(error: unknown): error is {
|
|
4178
|
+
name: 'CastError';
|
|
4179
|
+
path: string;
|
|
762
4180
|
value: unknown;
|
|
763
|
-
|
|
764
|
-
}
|
|
765
|
-
interface SettingsFeatureOptions<T = unknown> {
|
|
766
|
-
group: string;
|
|
767
|
-
schema: new () => T;
|
|
768
|
-
}
|
|
769
|
-
|
|
4181
|
+
};
|
|
770
4182
|
/**
|
|
771
|
-
*
|
|
4183
|
+
* Check if error is a MongoDB duplicate key error
|
|
772
4184
|
*/
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
/** Not equal to */
|
|
777
|
-
$ne?: T;
|
|
778
|
-
/** Greater than */
|
|
779
|
-
$gt?: T;
|
|
780
|
-
/** Greater than or equal to */
|
|
781
|
-
$gte?: T;
|
|
782
|
-
/** Less than */
|
|
783
|
-
$lt?: T;
|
|
784
|
-
/** Less than or equal to */
|
|
785
|
-
$lte?: T;
|
|
786
|
-
/** In array */
|
|
787
|
-
$in?: T[];
|
|
788
|
-
/** Not in array */
|
|
789
|
-
$nin?: T[];
|
|
790
|
-
/** Field exists */
|
|
791
|
-
$exists?: boolean;
|
|
792
|
-
/** Regular expression match */
|
|
793
|
-
$regex?: string | RegExp;
|
|
794
|
-
/** Regex options (i, m, s, x) */
|
|
795
|
-
$options?: string;
|
|
4185
|
+
declare function isDuplicateKeyError(error: unknown): error is {
|
|
4186
|
+
code: 11000;
|
|
4187
|
+
keyValue: Record<string, unknown>;
|
|
796
4188
|
};
|
|
797
4189
|
/**
|
|
798
|
-
*
|
|
4190
|
+
* Check if error is a Mongoose validation error
|
|
799
4191
|
*/
|
|
800
|
-
|
|
4192
|
+
declare function isValidationError(error: unknown): error is {
|
|
4193
|
+
name: 'ValidationError';
|
|
4194
|
+
errors: Record<string, unknown>;
|
|
4195
|
+
};
|
|
801
4196
|
/**
|
|
802
|
-
*
|
|
4197
|
+
* Check if error is a PostgreSQL unique constraint violation
|
|
803
4198
|
*/
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
/** Logical AND */
|
|
808
|
-
$and?: FilterQuery<Schema>[];
|
|
809
|
-
/** Logical OR */
|
|
810
|
-
$or?: FilterQuery<Schema>[];
|
|
811
|
-
/** Logical NOR */
|
|
812
|
-
$nor?: FilterQuery<Schema>[];
|
|
4199
|
+
declare function isPostgresUniqueError(error: unknown): error is {
|
|
4200
|
+
code: string;
|
|
4201
|
+
detail?: string;
|
|
813
4202
|
};
|
|
814
4203
|
/**
|
|
815
|
-
*
|
|
4204
|
+
* Check if an object has a method
|
|
816
4205
|
*/
|
|
817
|
-
|
|
4206
|
+
declare function hasMethod<K extends string>(value: unknown, methodName: K): value is Record<K, Function>;
|
|
818
4207
|
/**
|
|
819
|
-
*
|
|
4208
|
+
* Check if value has setLocale method (for i18n documents)
|
|
820
4209
|
*/
|
|
821
|
-
|
|
822
|
-
|
|
4210
|
+
declare function hasSetLocale<T>(value: T): value is T & {
|
|
4211
|
+
setLocale: (locale: string) => T;
|
|
823
4212
|
};
|
|
824
4213
|
/**
|
|
825
|
-
*
|
|
4214
|
+
* Check if value has toString method that returns string
|
|
826
4215
|
*/
|
|
827
|
-
|
|
828
|
-
|
|
4216
|
+
declare function hasToString(value: unknown): value is {
|
|
4217
|
+
toString(): string;
|
|
829
4218
|
};
|
|
830
4219
|
/**
|
|
831
|
-
*
|
|
4220
|
+
* Assert value is defined (not null or undefined)
|
|
4221
|
+
* Throws if value is null or undefined
|
|
832
4222
|
*/
|
|
833
|
-
|
|
834
|
-
/** Maximum number of documents to return */
|
|
835
|
-
limit?: number;
|
|
836
|
-
/** Number of documents to skip */
|
|
837
|
-
skip?: number;
|
|
838
|
-
/** Return plain objects instead of documents */
|
|
839
|
-
lean?: boolean;
|
|
840
|
-
}
|
|
4223
|
+
declare function assertDefined<T>(value: T | null | undefined, message?: string): asserts value is T;
|
|
841
4224
|
/**
|
|
842
|
-
*
|
|
4225
|
+
* Assert condition is true
|
|
4226
|
+
* Throws if condition is false
|
|
843
4227
|
*/
|
|
844
|
-
|
|
845
|
-
/** Result data */
|
|
846
|
-
data: T[];
|
|
847
|
-
/** Total count of matching documents */
|
|
848
|
-
total: number;
|
|
849
|
-
/** Current page (if using skip/limit) */
|
|
850
|
-
page?: number;
|
|
851
|
-
/** Page size */
|
|
852
|
-
limit?: number;
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
declare function Resolver(optionsOrFn: ResolverInput): ClassDecorator;
|
|
856
|
-
|
|
857
|
-
declare function Resolve(optionsOrFn: ResolveInput): MethodDecorator;
|
|
858
|
-
|
|
859
|
-
declare function Prop(options?: PropOptions): PropertyDecorator;
|
|
860
|
-
|
|
861
|
-
declare function Schema(options?: SchemaOptions): ClassDecorator;
|
|
862
|
-
declare function getSchemaOptions(target: Function): SchemaOptions;
|
|
863
|
-
|
|
864
|
-
declare function Setting(): ClassDecorator;
|
|
865
|
-
|
|
866
|
-
interface UIFieldMetadata {
|
|
867
|
-
propertyKey: string | symbol;
|
|
868
|
-
options: UIDecoratorOptions & {
|
|
869
|
-
designType: Function;
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
declare function UI(options: UIDecoratorOptions): PropertyDecorator;
|
|
873
|
-
|
|
874
|
-
declare const Validators: (...validators: PropertyDecorator[]) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
875
|
-
|
|
876
|
-
declare const VERSION_METADATA_KEY = "version:metadata";
|
|
4228
|
+
declare function assert(condition: boolean, message?: string): asserts condition;
|
|
877
4229
|
/**
|
|
878
|
-
*
|
|
879
|
-
*
|
|
4230
|
+
* Safely get a string ID from a document that might have _id or id
|
|
4231
|
+
* Returns undefined if no valid ID found
|
|
880
4232
|
*/
|
|
881
|
-
declare function
|
|
882
|
-
|
|
883
|
-
declare function InjectModel(model: Type): ParameterDecorator;
|
|
884
|
-
|
|
885
|
-
declare class ValidationException extends Error {
|
|
886
|
-
readonly errors: ValidationError[];
|
|
887
|
-
constructor(errors: ValidationError[]);
|
|
888
|
-
}
|
|
889
|
-
|
|
4233
|
+
declare function getDocumentId(doc: unknown): string | undefined;
|
|
890
4234
|
/**
|
|
891
|
-
*
|
|
892
|
-
* Provides chainable methods for filtering, sorting, pagination, and projection.
|
|
893
|
-
*
|
|
894
|
-
* @example
|
|
895
|
-
* ```typescript
|
|
896
|
-
* const users = await userModel.query()
|
|
897
|
-
* .where({ status: 'active' })
|
|
898
|
-
* .sort({ createdAt: -1 })
|
|
899
|
-
* .limit(10)
|
|
900
|
-
* .exec()
|
|
901
|
-
* ```
|
|
4235
|
+
* Type guard for checking if an object matches a record with string values
|
|
902
4236
|
*/
|
|
903
|
-
declare
|
|
904
|
-
/**
|
|
905
|
-
* Add filter conditions to the query
|
|
906
|
-
* @param filter Filter conditions with optional operators
|
|
907
|
-
*/
|
|
908
|
-
abstract where(filter: FilterQuery<Schema>): this;
|
|
909
|
-
/**
|
|
910
|
-
* Add additional AND conditions
|
|
911
|
-
* @param filter Filter conditions to AND with existing filters
|
|
912
|
-
*/
|
|
913
|
-
abstract and(filter: FilterQuery<Schema>): this;
|
|
914
|
-
/**
|
|
915
|
-
* Add OR conditions
|
|
916
|
-
* @param filters Array of filter conditions for OR logic
|
|
917
|
-
*/
|
|
918
|
-
abstract or(filters: FilterQuery<Schema>[]): this;
|
|
919
|
-
/**
|
|
920
|
-
* Sort results by specified fields
|
|
921
|
-
* @param sort Sort specification with field names and directions
|
|
922
|
-
*/
|
|
923
|
-
abstract sort(sort: SortQuery<Schema>): this;
|
|
924
|
-
/**
|
|
925
|
-
* Limit the number of results
|
|
926
|
-
* @param count Maximum number of documents to return
|
|
927
|
-
*/
|
|
928
|
-
abstract limit(count: number): this;
|
|
929
|
-
/**
|
|
930
|
-
* Skip a number of results (for pagination)
|
|
931
|
-
* @param count Number of documents to skip
|
|
932
|
-
*/
|
|
933
|
-
abstract skip(count: number): this;
|
|
934
|
-
/**
|
|
935
|
-
* Select specific fields to return
|
|
936
|
-
* @param projection Field selection (1 to include, 0 to exclude)
|
|
937
|
-
*/
|
|
938
|
-
abstract select(projection: ProjectionQuery<Schema>): this;
|
|
939
|
-
/**
|
|
940
|
-
* Execute the query and return all matching documents
|
|
941
|
-
*/
|
|
942
|
-
abstract exec(): Promise<BaseSchema<Schema>[]>;
|
|
943
|
-
/**
|
|
944
|
-
* Execute the query and return a single document
|
|
945
|
-
*/
|
|
946
|
-
abstract execOne(): Promise<BaseSchema<Schema> | null>;
|
|
947
|
-
/**
|
|
948
|
-
* Count matching documents without fetching them
|
|
949
|
-
*/
|
|
950
|
-
abstract count(): Promise<number>;
|
|
951
|
-
/**
|
|
952
|
-
* Check if any matching documents exist
|
|
953
|
-
*/
|
|
954
|
-
abstract exists(): Promise<boolean>;
|
|
955
|
-
/**
|
|
956
|
-
* Execute with pagination info
|
|
957
|
-
* @returns Data array with total count
|
|
958
|
-
*/
|
|
959
|
-
abstract paginate(): Promise<PaginatedResult<BaseSchema<Schema>>>;
|
|
960
|
-
/**
|
|
961
|
-
* Set the locale for query results
|
|
962
|
-
* @param locale The locale to use
|
|
963
|
-
*/
|
|
964
|
-
abstract locale(locale: string): this;
|
|
965
|
-
/**
|
|
966
|
-
* Set the version filter for query
|
|
967
|
-
* @param versionId The version ID or status
|
|
968
|
-
*/
|
|
969
|
-
abstract version(versionId: string): this;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
type BaseSchema<T> = {
|
|
973
|
-
id: string;
|
|
974
|
-
} & T;
|
|
975
|
-
interface ModelCreateOptions {
|
|
976
|
-
/** Skip database-level validation (useful for draft documents) */
|
|
977
|
-
skipValidation?: boolean;
|
|
978
|
-
}
|
|
979
|
-
interface ModelUpdateOptions {
|
|
980
|
-
/** Skip database-level validation (useful for draft documents) */
|
|
981
|
-
skipValidation?: boolean;
|
|
982
|
-
}
|
|
983
|
-
declare abstract class Model<Schema> {
|
|
984
|
-
abstract create(data: Partial<BaseSchema<Schema>>, options?: ModelCreateOptions): Promise<BaseSchema<Schema>>;
|
|
985
|
-
abstract find(): Promise<BaseSchema<Schema>[]>;
|
|
986
|
-
abstract findById(id: string): Promise<BaseSchema<Schema> | null>;
|
|
987
|
-
abstract findOne(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema> | null>;
|
|
988
|
-
abstract findMany(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema>[]>;
|
|
989
|
-
abstract update(query: Partial<BaseSchema<Schema>>, data: Partial<BaseSchema<Schema>>, options?: ModelUpdateOptions): Promise<BaseSchema<Schema>>;
|
|
990
|
-
abstract delete(query: Partial<BaseSchema<Schema>>): Promise<boolean>;
|
|
991
|
-
/**
|
|
992
|
-
* Set the locale for subsequent operations
|
|
993
|
-
* @param locale The locale to use
|
|
994
|
-
*/
|
|
995
|
-
abstract locale(locale: string): this;
|
|
996
|
-
/**
|
|
997
|
-
* Set the version for subsequent operations
|
|
998
|
-
* @param versionId The version ID or status ('draft', 'published', 'archived')
|
|
999
|
-
*/
|
|
1000
|
-
version(versionId: string): this;
|
|
1001
|
-
/**
|
|
1002
|
-
* Find all versions of a document
|
|
1003
|
-
* @param documentId The document ID
|
|
1004
|
-
*/
|
|
1005
|
-
findVersions(documentId: string): Promise<any[]>;
|
|
1006
|
-
/**
|
|
1007
|
-
* Find a specific version by ID
|
|
1008
|
-
* @param versionId The version ID
|
|
1009
|
-
*/
|
|
1010
|
-
findVersionById(versionId: string): Promise<any | null>;
|
|
1011
|
-
/**
|
|
1012
|
-
* Restore a version
|
|
1013
|
-
* @param versionId The version ID to restore
|
|
1014
|
-
*/
|
|
1015
|
-
restoreVersion(versionId: string): Promise<BaseSchema<Schema> | null>;
|
|
1016
|
-
/**
|
|
1017
|
-
* Create a query builder for advanced queries with sorting, pagination, and operators.
|
|
1018
|
-
* The query builder inherits the current locale/version context.
|
|
1019
|
-
*
|
|
1020
|
-
* @example
|
|
1021
|
-
* ```typescript
|
|
1022
|
-
* const results = await model.query()
|
|
1023
|
-
* .where({ status: 'active', age: { $gte: 18 } })
|
|
1024
|
-
* .sort({ createdAt: -1 })
|
|
1025
|
-
* .limit(10)
|
|
1026
|
-
* .exec()
|
|
1027
|
-
* ```
|
|
1028
|
-
*/
|
|
1029
|
-
query(): QueryBuilder<Schema>;
|
|
1030
|
-
/**
|
|
1031
|
-
* Get access to the native database model/collection.
|
|
1032
|
-
* Use with caution - bypasses Magnet abstractions like locale and versioning.
|
|
1033
|
-
*
|
|
1034
|
-
* @returns The underlying database model (e.g., Mongoose Model)
|
|
1035
|
-
*/
|
|
1036
|
-
native(): unknown;
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
declare class Mixed {
|
|
1040
|
-
static schemaName: 'Mixed';
|
|
1041
|
-
defaultOptions: Record<string, any>;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
declare function detectDatabaseAdapter(): 'mongoose' | 'typeorm';
|
|
1045
|
-
|
|
1046
|
-
declare function getModelToken(schema: Type): string;
|
|
4237
|
+
declare function isStringRecord(value: unknown): value is Record<string, string>;
|
|
1047
4238
|
|
|
1048
|
-
|
|
1049
|
-
|
|
4239
|
+
/**
|
|
4240
|
+
* Check if value is a version document
|
|
4241
|
+
*/
|
|
4242
|
+
declare function isVersionDocument(value: unknown): value is VersionDocument;
|
|
1050
4243
|
|
|
1051
4244
|
declare const RESOLVER_METADATA_KEY = "magnet:resolver";
|
|
1052
4245
|
declare const RESOLVE_METADATA_KEY = "magnet:resolve";
|
|
@@ -1055,11 +4248,18 @@ declare const SCHEMA_OPTIONS_METADATA_KEY = "magnet:schema:options";
|
|
|
1055
4248
|
declare const BASE_SCHEMA_METADATA_KEY = "magnet:schema:base";
|
|
1056
4249
|
declare const PROP_METADATA_KEY = "magnet:schema:prop";
|
|
1057
4250
|
declare const UI_METADATA_KEY = "magnet:schema:ui";
|
|
4251
|
+
declare const FIELD_METADATA_KEY = "magnet:schema:field";
|
|
1058
4252
|
declare const SETTING_METADATA_KEY = "magnet:setting";
|
|
4253
|
+
declare const SETTINGS_OPTIONS_METADATA_KEY = "magnet:settings:options";
|
|
4254
|
+
declare const SETTING_FIELD_METADATA_KEY = "magnet:settings:field";
|
|
4255
|
+
declare const EXTEND_USER_METADATA_KEY = "magnet:extend:user";
|
|
4256
|
+
declare const PERMISSION_METADATA_KEY = "magnet:permission";
|
|
4257
|
+
declare const PERMISSION_OPTIONS_METADATA_KEY = "magnet:permission:options";
|
|
4258
|
+
declare const RESOLVED_PERMISSION_KEY = "magnet:permission:resolved";
|
|
1059
4259
|
declare const INJECT_MODEL = "magnet:inject:model";
|
|
1060
4260
|
declare const DESIGN_TYPE = "design:type";
|
|
1061
4261
|
declare const DESIGN_META = "design:metadata";
|
|
1062
4262
|
declare const DESIGN_PARAM_TYPES = "design:paramtypes";
|
|
1063
4263
|
declare const DESIGN_RETURN_TYPE = "design:returntype";
|
|
1064
4264
|
|
|
1065
|
-
export { type AuthConfig, type AuthResult, AuthStrategy, type AuthUser, BASE_SCHEMA_METADATA_KEY, type BaseSchema, type BaseSchemaOptions, type ControllerMetadata, type DBConfig, DESIGN_META, DESIGN_PARAM_TYPES, DESIGN_RETURN_TYPE, DESIGN_TYPE, DatabaseAdapter, type DiscoveredController, type DiscoveredMethod, type DiscoveredSchema, type EnrichedPluginManifest, type FilterQuery, type FilterValue, INJECT_MODEL, type InitialConfig, InjectModel, type InternationalizationOptions, type JwtAuthConfig, type LocalStorageConfig, type LoginCredentials, MagnetModuleOptions, type MagnetModuleOptionsAsync, type MediaQueryOptions, type MethodMetadata, Mixed, Model, type ModelCreateOptions, type ModelUpdateOptions, type MongooseConfig, PROP_METADATA_KEY, type PaginatedMedia, type PaginatedResult, type PlaygroundOptions, type PluginConfig, type PluginFrontendManifest, type PluginHook, type PluginLifecycle, type PluginMetadata, type PluginModuleOptions, type PluginRouteDefinition, type PluginSettingsPage, type PluginSidebarItem, type ProjectionQuery, Prop, type PropOptions, QueryBuilder, type QueryOperator, type QueryOptions, type R2StorageConfig, RESOLVER_METADATA_KEY, RESOLVE_METADATA_KEY, type RegisterData, type RegisteredPluginInfo, Resolve, type ResolveInput, type ResolveOptions, Resolver, type ResolverInput, type ResolverOptions, type S3StorageConfig, SCHEMA_METADATA_KEY, SCHEMA_OPTIONS_METADATA_KEY, SETTING_METADATA_KEY, Schema, type SchemaMetadata, type SchemaOptions, type SchemaProperty, type SchemaPropertyValidation, type SchemaSetting, Setting, type SettingType, type SettingsFeatureOptions, type SortDirection, type SortQuery, StorageAdapter, type StorageConfig, type
|
|
4265
|
+
export { AccountLockedError, type AdapterCapabilities, type AdapterFeature, type AdapterName, type AddressFieldOptions, type AdminConfig, type ApiKeyEventPayload, type ArrayFieldOptions, type ArrayItemType, type AssignRoleDto, type AuthConfig, type AuthEventPayload, type AuthMagnetProvider, type AuthResult, AuthStrategy, type AuthUser, AuthenticationRequiredError, BASE_SCHEMA_METADATA_KEY, type BaseEventPayload, type BaseFieldOptions, type BaseSchema, type BaseSchemaOptions, type BlockTypeDefinition, type BlocksFieldOptions, type BooleanFieldOptions, CacheAdapter, type CacheHealthResult, type CacheMagnetProvider, type CategorizedPermissions, type CodeFieldOptions, type ColorFieldOptions, ConfigurationError, ConnectionFailedError, type ContentEventPayload, type ContentVersionEventPayload, type ControllerMetadata, type CreateRoleDto, type DBConfig, DESIGN_META, DESIGN_PARAM_TYPES, DESIGN_RETURN_TYPE, DESIGN_TYPE, DatabaseAdapter, DatabaseError, type DatabaseMagnetProvider, type DatabaseModelInstance, type DatabaseType, type DateFieldOptions, type DateTimeFieldOptions, type DiscoveredController, type DiscoveredMethod, type DiscoveredSchema, DocumentNotFoundError, type DrizzleConfig, DuplicateKeyError, type DuplicateRoleDto, EVENT_HANDLER_METADATA, EXTEND_USER_METADATA_KEY, EmailAdapter, type EmailAdapterName, type EmailAttachment, type EmailConfig, type EmailFieldOptions, type EmailMagnetProvider, EmailNotVerifiedError, EmailServiceError, type EnrichedPluginManifest, type EnumFieldOptions, type EnvVarRequirement, ErrorCode, type ErrorLogData, type ErrorMetadata, type ErrorResponse, type EventHandler, type EventHandlerMetadata, type EventHandlerOptions, type EventHistoryEntry, type EventName, type EventPayload, type EventPayloadMap, ExtendUser, type ExtendUserOptions, type ExternalAuthInfo, ExternalServiceError, FIELD_METADATA_KEY, type FailedLoginEventPayload, Field, type FieldChange, type FieldMetadata, type FieldNamespace, type FieldOptionsMap, type FieldTypeId, type FileFieldOptions, FileNotFoundError, type FilterQuery, type FilterValue, type GalleryFieldOptions, HasPermission, type HashiCorpVaultConfig, HookExecutionError, INJECT_MODEL, type ImageFieldOptions, type InitialConfig, InjectModel, InsufficientPermissionsError, InternalError, type InternationalizationOptions, InvalidCredentialsError, InvalidFormatError, type JSONFieldOptions, type JwtAuthConfig, type LocalStorageConfig, type LogEntry, type LogLevel, type LogMetadata, type LoggerConfig, type LoginCredentials, MagnetError, type MagnetGlobalOptions, MagnetModuleOptions, type MagnetModuleOptionsAsync, type MagnetProvider, type MarkdownFieldOptions, type MediaEventPayload, type MediaFolderEventPayload, type MediaQueryOptions, type MethodMetadata, Mixed, Model, type ModelClass, type ModelCreateOptions, type ModelUpdateOptions, type MongooseConfig, type NativeAccess, type NodemailerConfig, type NotificationChannel, type NotificationChannelAdapter, type NotificationChannelResult, type NotificationEventPayload, type NotificationQueryOptions, type NotificationRecipient, type NotificationSendPayload, type NotifyDto, type NumberFieldOptions, type ObjectFieldOptions, OnEvent, type OperationType, PERMISSION_METADATA_KEY, PERMISSION_OPTIONS_METADATA_KEY, PROP_METADATA_KEY, type PaginatedMedia, type PaginatedNotifications, type PaginatedResult, type PermissionCheckResult, type PermissionDefinition, PermissionDeniedError, type PermissionGroup, type PermissionItem, PermissionMeta, PermissionNotFoundError, type PermissionOptions, type PermissionSource, type PhoneFieldOptions, type PlaygroundOptions, type PluginConfig, PluginError, type PluginEventPayload, type PluginFrontendManifest, type PluginHook, PluginInitializationError, type PluginLifecycle, type PluginMagnetProvider, type PluginMetadata, type PluginModuleOptions, PluginNotFoundError, type PluginRouteDefinition, type PluginSettingsPage, type PluginSidebarItem, type ProjectionQuery, Prop, type PropDefaultValue, type PropOptions, QueryBuilder, QueryFailedError, type QueryOperator, type QueryOptions, type R2StorageConfig, type RBACModuleOptions, RESOLVED_PERMISSION_KEY, RESOLVER_METADATA_KEY, RESOLVE_METADATA_KEY, type RegisterData, type RegisteredHandler, type RegisteredPluginInfo, type RelationshipFieldOptions, RequirePermission, type RequiredEventHandlerOptions, RequiredFieldError, type ResendConfig, Resolve, type ResolveInput, type ResolveOptions, type ResolvedPermission, Resolver, type ResolverInput, type ResolverOptions, ResourceNotFoundError, type RichTextFieldOptions, type Role, type RoleAuditEntry, type RoleEventPayload, RoleNotFoundError, type RoleWithPermissions, type S3StorageConfig, SCHEMA_METADATA_KEY, SCHEMA_OPTIONS_METADATA_KEY, SETTINGS_OPTIONS_METADATA_KEY, SETTING_FIELD_METADATA_KEY, SETTING_METADATA_KEY, Schema, type SchemaIndexOption, type SchemaMetadata, SchemaNotFoundError, type SchemaOptions, type SchemaProperty, type SchemaPropertyValidation, type SchemaSetting, type SelectFieldOptions, type SelectOptionItem, type SendEmailOptions, type SendEmailResult, Setting, type SettingBooleanOptions, SettingField, type SettingFieldBaseOptions, type SettingFieldMetadata, type SettingFieldNamespace, type SettingFieldTypeId, type SettingImageOptions, type SettingJSONOptions, type SettingNumberOptions, type SettingObject, type SettingSecretOptions, type SettingSectionDefinition, type SettingSectionVariant, type SettingSelectOptions, type SettingTextOptions, type SettingType, type SettingValue, Settings, type SettingsBulkUpdatePayload, type SettingsDecoratorOptions, type SettingsEventPayload, type SettingsFeatureOptions, type SettingsGroupEventPayload, type SettingsRecord, type SettingsUpdatePayload, type SlugFieldOptions, type SortDirection, type SortQuery, StorageAdapter, type StorageConfig, StorageError, type StorageMagnetProvider, type SupabaseAuthConfig, type SupabaseStorageConfig, type SupabaseVaultConfig, type SupportedAdapter, type SystemEventPayload, type SystemRoleConfig, type SystemRoleName, type TagsFieldOptions, type TextFieldOptions, type TextareaFieldOptions, TokenExpiredError, TokenInvalidError, TransactionFailedError, type TransformOptions, UI, type UIBase, type UICombobox, type UIDecoratorOptions, type UIFieldMetadata, type UIMultiSelect, type UIPresentationFields, type UISelect, type UISelectItem, type UISide, type UITab, type UITable, type UITableColumn, type UITypes, UI_METADATA_KEY, type URLFieldOptions, UnexpectedError, type UpdatePermissionsDto, type UpdateRoleDto, type UploadOptions, type UploadResult, type UserEventPayload, UserNotFoundError, VERSION_METADATA_KEY, ValidationError, type ValidationErrorDetail, ValidationException, type Validations, Validators, ValueOutOfRangeError, VaultAdapter, type VaultAdapterType, type VaultAppRoleAuth, type VaultAuthConfig, type VaultConfig, type VaultMagnetProvider, type VaultSecretListResponse, type VaultSecretMeta, type VaultStatusResponse, type VaultTokenAuth, Version, type VersionConfig, type VersionData, type VersionDocument, VersionNotFoundError, type VersionStatus, WebhookDeliveryError, type WebhookDeliveryEventPayload, type WebhookEventPayload, assert, assertDefined, clearAdapterCache, createFieldDecorator, detectDatabaseAdapter, fromDrizzleError, fromMongooseError, getAdapterToken, getDocumentId, getExtendUserOptions, getFieldMetadata, getFieldMetadataForProperty, getModelToken, getPermissionMetadata, getRegisteredModel, getSchemaOptions, getSchemaToken, getSettingFields, getSettingToken, getSettingsOptions, hasMethod, hasPermissionDecorator, hasProperties, hasProperty, hasSetLocale, hasToString, isArray, isBoolean, isCastError, isDocument, isDuplicateKeyError, isFieldMetadata, isFunction, isMagnetError, isNumber, isObject, isPostgresUniqueError, isSettingFieldMetadata, isString, isStringArray, isStringRecord, isUserExtension, isValidFieldType, isValidationError, isVersionDocument, mapFieldTypeToProp, mapFieldTypeToUI, registerModel, setDatabaseAdapter, wrapError };
|