@magnet-cms/common 0.1.1 → 0.3.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.d.cts CHANGED
@@ -1,300 +1,2303 @@
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
+ * 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 {
82
+ key: string;
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;
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;
160
+ type: string;
161
+ channels: string[];
162
+ }
163
+ /**
164
+ * Event payload map - maps event names to their payload types
165
+ */
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;
249
+ }
250
+ /**
251
+ * Get payload type for an event name
252
+ */
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;
268
+ }
269
+ /**
270
+ * Required event handler options (after defaults applied)
271
+ */
272
+ interface RequiredEventHandlerOptions {
273
+ priority: number;
274
+ async: boolean;
275
+ name: string;
276
+ }
277
+ /**
278
+ * Registered handler with metadata
279
+ */
280
+ interface RegisteredHandler<E extends EventName> {
281
+ handler: EventHandler<E>;
282
+ options: RequiredEventHandlerOptions;
283
+ }
284
+ /**
285
+ * Event history entry for debugging
286
+ */
287
+ interface EventHistoryEntry {
288
+ event: EventName;
289
+ payload: BaseEventPayload;
290
+ timestamp: Date;
291
+ }
292
+ /**
293
+ * Event handler metadata (used by @OnEvent decorator)
294
+ */
295
+ interface EventHandlerMetadata {
296
+ event: EventName;
297
+ options: EventHandlerOptions;
298
+ }
299
+
300
+ /**
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
312
+ *
313
+ * @example
314
+ * ```typescript
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)
320
+ * }
321
+ *
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)
326
+ * }
327
+ * }
328
+ * ```
329
+ */
330
+ declare function OnEvent(event: EventName, options?: EventHandlerOptions): MethodDecorator;
331
+
332
+ /**
333
+ * Base options shared by all field decorators
334
+ */
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 */
349
+ description?: string;
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;
364
+ }
365
+ /**
366
+ * Text field options
367
+ */
368
+ interface TextFieldOptions extends BaseFieldOptions {
369
+ minLength?: number;
370
+ maxLength?: number;
371
+ pattern?: string;
372
+ transform?: 'none' | 'lowercase' | 'uppercase' | 'trim';
373
+ }
374
+ /**
375
+ * Number field options
376
+ */
377
+ interface NumberFieldOptions extends BaseFieldOptions {
378
+ min?: number;
379
+ max?: number;
380
+ integer?: boolean;
381
+ step?: number;
382
+ default?: number;
383
+ }
384
+ /**
385
+ * Boolean field options
386
+ */
387
+ interface BooleanFieldOptions extends BaseFieldOptions {
388
+ default?: boolean;
389
+ style?: 'switch' | 'checkbox';
390
+ }
391
+ /**
392
+ * Date field options
393
+ */
394
+ interface DateFieldOptions extends BaseFieldOptions {
395
+ min?: Date | string;
396
+ max?: Date | string;
397
+ default?: Date | string | 'now' | (() => Date);
398
+ }
399
+ /**
400
+ * DateTime field options
401
+ */
402
+ interface DateTimeFieldOptions extends DateFieldOptions {
403
+ timezone?: string;
404
+ }
405
+ /**
406
+ * Rich text field options
407
+ */
408
+ interface RichTextFieldOptions extends BaseFieldOptions {
409
+ toolbar?: 'minimal' | 'standard' | 'full';
410
+ maxLength?: number;
411
+ }
412
+ /**
413
+ * Markdown field options
414
+ */
415
+ interface MarkdownFieldOptions extends BaseFieldOptions {
416
+ preview?: boolean;
417
+ maxLength?: number;
418
+ }
419
+ /**
420
+ * Code field options
421
+ */
422
+ interface CodeFieldOptions extends BaseFieldOptions {
423
+ language?: string;
424
+ lineNumbers?: boolean;
425
+ }
426
+ /**
427
+ * JSON field options
428
+ */
429
+ interface JSONFieldOptions extends BaseFieldOptions {
430
+ /** Optional JSON schema for validation */
431
+ schema?: unknown;
432
+ }
433
+ /**
434
+ * Select option item
435
+ */
436
+ interface SelectOptionItem {
437
+ label: string;
438
+ value: string | number;
439
+ }
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;
447
+ }
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;
455
+ }
456
+ /**
457
+ * Tags field options
458
+ */
459
+ interface TagsFieldOptions extends BaseFieldOptions {
460
+ suggestions?: string[];
461
+ maxTags?: number;
462
+ allowCreate?: boolean;
463
+ }
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[];
474
+ }
475
+ /**
476
+ * Image field options
477
+ */
478
+ interface ImageFieldOptions extends BaseFieldOptions {
479
+ folder?: string;
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
+
4
676
  type ResolverOptions = {
5
677
  schema: Type;
6
678
  };
7
- type ResolverInput = (() => Type) | ResolverOptions;
8
- type ResolveOptions = {
9
- type: Type | [Type];
10
- isArray?: boolean;
11
- description?: string;
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[];
12
1605
  };
13
- type ResolveInput = (() => Type | [Type]) | ResolveOptions;
14
- type PropOptions = {
15
- type?: Type | [Type] | any;
16
- description?: string;
17
- required?: boolean;
18
- unique?: boolean;
19
- default?: any;
20
- nullable?: boolean;
21
- intl?: boolean;
22
- hidden?: boolean;
23
- readonly?: boolean;
1606
+ type SchemaProperty = {
1607
+ name: string;
1608
+ type: string;
1609
+ isArray: boolean;
1610
+ unique: boolean;
1611
+ required: boolean;
1612
+ validations: SchemaPropertyValidation[];
1613
+ ui?: UIDecoratorOptions;
24
1614
  ref?: string;
25
1615
  };
26
- type BaseSchemaOptions = {
27
- timestamps?: boolean;
28
- };
29
- type SchemaOptions = {
30
- versioning?: boolean;
31
- i18n?: boolean;
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;
32
1625
  };
33
1626
 
34
- type UIPresentationFields = 'tab' | 'collapsible' | 'row' | 'customUI';
35
- 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';
36
- type UIBase = {
37
- label?: string;
1627
+ type InitialConfig = {
1628
+ title?: string;
38
1629
  description?: string;
39
- type?: UITypes;
40
- row?: boolean;
41
- collapsible?: boolean;
42
- };
43
- type UISelectItem = {
44
- key: string;
45
- value: string;
46
- };
47
- type UITab = UIBase & {
48
- tab: string;
49
- side?: never;
50
- options?: UISelectItem[];
51
- };
52
- type UISide = UIBase & {
53
- side: true;
54
- tab?: never;
55
- options?: UISelectItem[];
56
- };
57
- type UISelect = UIBase & {
58
- type: 'select';
59
- multi?: boolean;
60
- options: UISelectItem[];
61
- };
62
- type UIMultiSelect = UIBase & {
63
- type: 'multiSelect';
64
- options: UISelectItem[];
65
- };
66
- type UICombobox = UIBase & {
67
- type: 'combobox';
68
- options: UISelectItem[];
69
- };
70
- type UITableColumn = {
71
- key: string;
72
- header: string;
73
- type?: 'text' | 'badge' | 'status' | 'input' | 'code';
74
- };
75
- type UITable = UIBase & {
76
- type: 'table';
77
- columns?: UITableColumn[];
1630
+ env: string;
1631
+ schemas: SchemaMetadata[];
1632
+ settings: SchemaMetadata[];
78
1633
  };
79
- type UIDecoratorOptions = UITab | UISide | UISelect | UIMultiSelect | UICombobox | UITable;
80
1634
 
81
- type MethodMetadata = {
82
- name: string;
83
- returnType: {
84
- type: Type;
85
- isArray: boolean;
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;
86
1840
  };
87
- params: {
88
- arg: string;
89
- type: string;
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<{
90
1918
  name: string;
91
- }[];
92
- httpMethod: string;
93
- routePath: string;
94
- guards?: Type[];
95
- interceptors?: Type[];
96
- pipes?: Type[];
97
- };
98
- type ControllerMetadata = {
99
- name: string;
100
- basePath: string;
101
- methods: MethodMetadata[];
102
- };
103
- type SchemaPropertyValidation = {
104
- type: string;
105
- name: string;
106
- constraints: number[];
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;
107
2016
  };
108
- type SchemaProperty = {
109
- name: string;
110
- type: string;
111
- isArray: boolean;
112
- unique: boolean;
113
- required: boolean;
114
- validations: SchemaPropertyValidation[];
115
- ui: UIDecoratorOptions;
116
- ref?: string;
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>[];
117
2033
  };
118
- type SchemaMetadata = {
119
- name: string;
120
- className?: string;
121
- apiName?: string;
122
- displayName?: string;
123
- properties: SchemaProperty[];
124
- options?: SchemaOptions;
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;
125
2043
  };
126
-
127
- type InitialConfig = {
128
- title?: string;
129
- description?: string;
130
- env: string;
131
- schemas: SchemaMetadata[];
132
- settings: SchemaMetadata[];
2044
+ /**
2045
+ * Projection specification for field selection
2046
+ */
2047
+ type ProjectionQuery<Schema> = {
2048
+ [K in keyof Schema]?: 0 | 1 | boolean;
133
2049
  };
134
-
135
2050
  /**
136
- * Authenticated user returned from auth strategies
2051
+ * Query execution options
137
2052
  */
138
- interface AuthUser {
139
- /** Unique user identifier */
140
- id: string;
141
- /** User email address */
142
- email: string;
143
- /** User role for authorization */
144
- role: string;
145
- /** Optional additional user data */
146
- [key: string]: unknown;
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;
147
2060
  }
148
2061
  /**
149
- * Login credentials (strategy-specific)
2062
+ * Paginated query result
150
2063
  */
151
- interface LoginCredentials {
152
- email: string;
153
- password: string;
154
- [key: string]: unknown;
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;
155
2073
  }
2074
+
156
2075
  /**
157
- * Registration data (strategy-specific)
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
+ * ```
158
2087
  */
159
- interface RegisterData {
160
- email: string;
161
- password: string;
162
- name: string;
163
- role?: string;
164
- [key: string]: unknown;
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>;
165
2162
  }
166
- /**
167
- * Authentication result containing access token
168
- */
169
- interface AuthResult {
170
- access_token: string;
171
- refresh_token?: string;
172
- expires_in?: number;
173
- token_type?: string;
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;
174
2170
  }
175
- /**
176
- * JWT-specific configuration
177
- */
178
- interface JwtAuthConfig {
179
- /** JWT secret key */
180
- secret: string;
181
- /** Token expiration time (e.g., '7d', '1h') */
182
- expiresIn?: string;
2171
+ interface ModelUpdateOptions {
2172
+ /** Skip database-level validation (useful for draft documents) */
2173
+ skipValidation?: boolean;
183
2174
  }
184
2175
  /**
185
- * Supabase-specific auth configuration
2176
+ * Version document type for history/versioning
186
2177
  */
187
- interface SupabaseAuthConfig {
188
- /** Supabase project URL */
189
- supabaseUrl: string;
190
- /** Supabase anon/public key */
191
- supabaseKey: string;
192
- /** Default role for new users */
193
- defaultRole?: string;
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';
194
2195
  }
195
2196
  /**
196
- * Auth configuration for MagnetModuleOptions
2197
+ * Native access wrapper - provides type-safe native operations
197
2198
  */
198
- interface AuthConfig {
199
- /** Strategy name to use (default: 'jwt') */
200
- strategy?: string;
201
- /** JWT-specific configuration */
202
- jwt?: JwtAuthConfig;
203
- /** Supabase-specific configuration (when strategy: 'supabase') */
204
- supabaseUrl?: string;
205
- /** Supabase anon/public key */
206
- supabaseKey?: string;
207
- /** Supabase service role key (required for admin operations like listUsers) */
208
- supabaseServiceKey?: string;
209
- /** Default role for new Supabase users */
210
- defaultRole?: string;
211
- /** Allow extensible config for custom strategies */
212
- [key: string]: unknown;
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;
213
2216
  }
214
- /**
215
- * Abstract base class for authentication strategies.
216
- * Similar to StorageAdapter, custom strategies must extend this class.
217
- *
218
- * @example
219
- * ```typescript
220
- * export class SupabaseAuthStrategy extends AuthStrategy {
221
- * readonly name = 'supabase'
222
- *
223
- * async validate(payload: unknown): Promise<AuthUser | null> {
224
- * // Validate Supabase JWT token
225
- * }
226
- *
227
- * async login(credentials: LoginCredentials): Promise<AuthResult> {
228
- * // Authenticate with Supabase
229
- * }
230
- *
231
- * async register(data: RegisterData): Promise<AuthUser> {
232
- * // Register user in Supabase
233
- * }
234
- *
235
- * async validateCredentials(email: string, password: string): Promise<AuthUser | null> {
236
- * // Validate user credentials
237
- * }
238
- * }
239
- * ```
240
- */
241
- declare abstract class AuthStrategy {
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>;
242
2225
  /**
243
- * Unique identifier for this strategy
2226
+ * Set the locale for subsequent operations
2227
+ * @param locale The locale to use
2228
+ * @returns Cloned model instance with locale set
244
2229
  */
245
- abstract readonly name: string;
2230
+ abstract locale(locale: string): this;
246
2231
  /**
247
- * Initialize the auth strategy (optional setup)
2232
+ * Get current locale
248
2233
  */
249
- initialize?(): Promise<void>;
2234
+ getLocale(): string;
250
2235
  /**
251
- * Validate a token/payload and return the authenticated user
252
- * @param payload - Strategy-specific payload (e.g., JWT payload, session data)
253
- * @returns Authenticated user or null if invalid
2236
+ * Set the version for subsequent operations
2237
+ * @param versionId The version ID or status ('draft', 'published', 'archived')
2238
+ * @returns Same instance (chainable)
254
2239
  */
255
- abstract validate(payload: unknown): Promise<AuthUser | null>;
2240
+ version(versionId: string): this;
256
2241
  /**
257
- * Authenticate user with credentials and return tokens
258
- * @param credentials - Login credentials (strategy-specific)
259
- * @returns Authentication result with access token
2242
+ * Check if versioning is enabled for this model
260
2243
  */
261
- abstract login(credentials: LoginCredentials): Promise<AuthResult>;
2244
+ isVersioningEnabled(): boolean;
262
2245
  /**
263
- * Register a new user
264
- * @param data - Registration data
265
- * @returns The created user
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
266
2250
  */
267
- abstract register(data: RegisterData): Promise<AuthUser>;
2251
+ createVersion(documentId: string, data: Partial<Schema>): Promise<VersionDocument | null>;
268
2252
  /**
269
- * Validate user credentials (used internally by login)
270
- * @param email - User email
271
- * @param password - User password
272
- * @returns User if valid, null otherwise
2253
+ * Find all versions of a document
2254
+ * @param documentId The document ID
273
2255
  */
274
- abstract validateCredentials(email: string, password: string): Promise<AuthUser | null>;
2256
+ findVersions(documentId: string): Promise<VersionDocument[]>;
275
2257
  /**
276
- * Refresh an access token (optional)
277
- * @param refreshToken - The refresh token
278
- * @returns New authentication result
2258
+ * Find a specific version by ID
2259
+ * @param versionId The version ID
279
2260
  */
280
- refresh?(refreshToken: string): Promise<AuthResult>;
2261
+ findVersionById(versionId: string): Promise<VersionDocument | null>;
281
2262
  /**
282
- * Logout/invalidate tokens (optional)
283
- * @param token - The token to invalidate
2263
+ * Restore a document to a specific version
2264
+ * @param versionId The version ID to restore
284
2265
  */
285
- logout?(token: string): Promise<void>;
2266
+ restoreVersion(versionId: string): Promise<BaseSchema<Schema> | null>;
286
2267
  /**
287
- * Check if any users exist in the system (optional)
288
- * Strategies that manage their own user storage should implement this.
289
- * If not implemented, AuthService will fall back to checking the local database.
290
- * @returns true if users exist, false otherwise
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
+ * ```
291
2279
  */
292
- hasUsers?(): Promise<boolean>;
2280
+ query(): QueryBuilder<Schema>;
293
2281
  /**
294
- * Get the Passport strategy name (for guards)
295
- * Returns the name to use with AuthGuard()
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
296
2286
  */
297
- getPassportStrategyName(): string;
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>;
298
2301
  }
299
2302
 
300
2303
  type MongooseConfig = {
@@ -313,20 +2316,101 @@ type DrizzleConfig = {
313
2316
  driver?: 'pg' | 'neon' | 'mysql2' | 'better-sqlite3';
314
2317
  /** Enable debug logging */
315
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
+ };
316
2332
  };
317
2333
  type DBConfig = MongooseConfig | DrizzleConfig;
318
- declare abstract class DatabaseAdapter {
319
- abstract connect(options: MagnetModuleOptions): DynamicModule;
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
+ */
320
2384
  abstract forFeature(schemas: Type | Type[]): DynamicModule;
321
- abstract model<T>(modelInstance: any): any;
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
+ */
322
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;
323
2407
  }
324
2408
 
325
2409
  /**
326
2410
  * Plugin metadata for the @Plugin decorator
327
2411
  */
328
2412
  interface PluginMetadata {
329
- /** Unique plugin identifier (e.g., 'content-builder') */
2413
+ /** Unique plugin identifier (e.g., 'playground') */
330
2414
  name: string;
331
2415
  /** Human-readable description */
332
2416
  description?: string;
@@ -686,11 +2770,116 @@ declare abstract class StorageAdapter {
686
2770
  */
687
2771
  copy?(path: string, newPath: string): Promise<UploadResult>;
688
2772
  /**
689
- * Generate a signed URL for temporary access (for S3/R2)
690
- * @param path - Storage path or key
691
- * @param expiresIn - Expiration time in seconds
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.
692
2863
  */
693
- signedUrl?(path: string, expiresIn?: number): Promise<string>;
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[];
694
2883
  }
695
2884
 
696
2885
  interface InternationalizationOptions {
@@ -709,6 +2898,14 @@ interface PlaygroundOptions {
709
2898
  */
710
2899
  schemasPath?: string;
711
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
+ }
712
2909
  declare class MagnetModuleOptions {
713
2910
  db: DBConfig;
714
2911
  jwt: {
@@ -721,14 +2918,47 @@ declare class MagnetModuleOptions {
721
2918
  internationalization?: InternationalizationOptions;
722
2919
  playground?: PlaygroundOptions;
723
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;
724
2927
  /**
725
2928
  * Plugins to load with the Magnet module
726
2929
  */
727
2930
  plugins?: PluginConfig[];
728
- constructor({ db, jwt, auth, internationalization, playground, storage, plugins, }: MagnetModuleOptions);
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);
729
2959
  }
730
2960
  type MagnetModuleOptionsAsync = {
731
- useFactory: (...args: any[]) => Promise<MagnetModuleOptions> | MagnetModuleOptions;
2961
+ useFactory: (...args: unknown[]) => Promise<MagnetModuleOptions> | MagnetModuleOptions;
732
2962
  inject?: Type[];
733
2963
  imports?: Type[];
734
2964
  };
@@ -751,370 +2981,1148 @@ type DiscoveredSchema = {
751
2981
  type Validations = {
752
2982
  type: string;
753
2983
  name: string;
754
- constraints: any[];
2984
+ constraints: unknown[];
755
2985
  }[];
756
2986
 
757
2987
  type VersionStatus = 'draft' | 'published' | 'archived';
758
2988
  interface VersionConfig {
759
2989
  /**
760
- * Maximum number of versions to keep per document
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;
3109
+ /**
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)
3113
+ */
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[];
3142
+ /**
3143
+ * Which channels to deliver to.
3144
+ * Defaults to `['platform']` when not specified.
3145
+ */
3146
+ channels?: NotificationChannel[];
3147
+ /**
3148
+ * Notification category / type identifier.
3149
+ * Useful for filtering and icon mapping in the UI.
3150
+ */
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;
3158
+ /**
3159
+ * Additional arbitrary metadata stored with the notification.
3160
+ * Not shown directly in UI but available for custom logic.
3161
+ */
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 {
3219
+ /**
3220
+ * Unique identifier for this adapter
3221
+ */
3222
+ abstract readonly name: string;
3223
+ /**
3224
+ * Retrieve a cached value by key.
3225
+ * @param key - Cache key
3226
+ * @returns The cached value, or null if not found or expired
3227
+ */
3228
+ abstract get<T>(key: string): Promise<T | null>;
3229
+ /**
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.
3234
+ */
3235
+ abstract set<T>(key: string, value: T, ttl?: number): Promise<void>;
3236
+ /**
3237
+ * Remove a value from the cache by key.
3238
+ * @param key - Cache key to delete
3239
+ */
3240
+ abstract delete(key: string): Promise<void>;
3241
+ /**
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
3245
+ */
3246
+ abstract deleteByPattern(pattern: string): Promise<void>;
3247
+ /**
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
3251
+ */
3252
+ abstract has(key: string): Promise<boolean>;
3253
+ /**
3254
+ * Remove all entries from the cache.
3255
+ * For Redis, only clears keys within this adapter's key prefix.
3256
+ */
3257
+ abstract clear(): Promise<void>;
3258
+ /**
3259
+ * Check the health of the cache backend.
3260
+ * @returns Health result with status and optional message
3261
+ */
3262
+ abstract healthCheck(): Promise<CacheHealthResult>;
3263
+ /**
3264
+ * Optional cleanup/disconnect method.
3265
+ * Called when the module is destroyed.
3266
+ */
3267
+ dispose?(): Promise<void>;
3268
+ }
3269
+
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;
3281
+ }
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;
3302
+ };
3303
+ /**
3304
+ * Admin panel configuration (enabled by default when omitted).
3305
+ * @example admin: false // API-only / headless
3306
+ * @example admin: { enabled: true, path: '/dashboard' }
3307
+ */
3308
+ admin?: boolean | AdminConfig;
3309
+ /** RBAC (Role-Based Access Control) configuration */
3310
+ rbac?: RBACModuleOptions;
3311
+ /** Internationalization settings */
3312
+ internationalization?: InternationalizationOptions;
3313
+ /** Global Playground (schema builder) settings */
3314
+ playground?: PlaygroundOptions;
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 {
3440
+ /**
3441
+ * Whether to include timestamps (createdAt, updatedAt)
3442
+ * @default true
3443
+ */
3444
+ timestamps?: boolean;
3445
+ }
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;
3484
+
3485
+ declare function Prop(options?: PropOptions): PropertyDecorator;
3486
+
3487
+ declare function Schema(options?: SchemaOptions): ClassDecorator;
3488
+ declare function getSchemaOptions(target: Function): SchemaOptions;
3489
+
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: {
3540
+ /**
3541
+ * Text setting field
761
3542
  */
762
- max?: number;
3543
+ readonly Text: (options: SettingTextOptions) => PropertyDecorator;
763
3544
  /**
764
- * Enable drafts mode for this schema
3545
+ * Number setting field
765
3546
  */
766
- drafts?: boolean | {
767
- /**
768
- * Auto-publish drafts after a certain time
769
- */
770
- autoPublish?: boolean;
771
- /**
772
- * Require approval before publishing
773
- */
774
- requireApproval?: boolean;
775
- };
776
- }
777
- interface VersionData<T> {
3547
+ readonly Number: (options: SettingNumberOptions) => PropertyDecorator;
778
3548
  /**
779
- * Document ID this version belongs to
3549
+ * Boolean setting field (toggle/switch)
780
3550
  */
781
- documentId: string;
3551
+ readonly Boolean: (options: SettingBooleanOptions) => PropertyDecorator;
782
3552
  /**
783
- * Version ID
3553
+ * Select setting field (dropdown)
784
3554
  */
785
- versionId: string;
3555
+ readonly Select: (options: SettingSelectOptions) => PropertyDecorator;
786
3556
  /**
787
- * Version status
3557
+ * Secret setting field (encrypted, masked in UI)
788
3558
  */
789
- status: VersionStatus;
3559
+ readonly Secret: (options: SettingSecretOptions) => PropertyDecorator;
790
3560
  /**
791
- * Version data
3561
+ * Image setting field
792
3562
  */
793
- data: T;
3563
+ readonly Image: (options: SettingImageOptions) => PropertyDecorator;
794
3564
  /**
795
- * Version created at
3565
+ * JSON setting field
796
3566
  */
797
- createdAt: Date;
3567
+ readonly JSON: (options: SettingJSONOptions) => PropertyDecorator;
798
3568
  /**
799
- * Version created by
3569
+ * Textarea setting field (multi-line text)
800
3570
  */
801
- createdBy?: string;
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);
802
3691
  /**
803
- * Version notes
3692
+ * Convert to API response format
804
3693
  */
805
- notes?: string;
3694
+ toResponse(): ErrorResponse;
3695
+ /**
3696
+ * Convert to JSON for logging
3697
+ */
3698
+ toJSON(): Record<string, unknown>;
806
3699
  }
807
3700
 
808
- type SettingType = 'string' | 'boolean' | 'number' | 'object' | 'array';
809
- interface SchemaSetting {
810
- key: string;
811
- value: unknown;
812
- type: SettingType | string;
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[];
813
3709
  }
814
- interface SettingsFeatureOptions<T = unknown> {
815
- group: string;
816
- schema: new () => T;
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);
817
3748
  }
818
3749
 
819
3750
  /**
820
- * Query operator types for MongoDB-style queries
3751
+ * Authentication required error
3752
+ * Thrown when a request requires authentication but none is provided
821
3753
  */
822
- type QueryOperator<T> = {
823
- /** Equal to */
824
- $eq?: T;
825
- /** Not equal to */
826
- $ne?: T;
827
- /** Greater than */
828
- $gt?: T;
829
- /** Greater than or equal to */
830
- $gte?: T;
831
- /** Less than */
832
- $lt?: T;
833
- /** Less than or equal to */
834
- $lte?: T;
835
- /** In array */
836
- $in?: T[];
837
- /** Not in array */
838
- $nin?: T[];
839
- /** Field exists */
840
- $exists?: boolean;
841
- /** Regular expression match */
842
- $regex?: string | RegExp;
843
- /** Regex options (i, m, s, x) */
844
- $options?: string;
845
- };
3754
+ declare class AuthenticationRequiredError extends MagnetError {
3755
+ readonly code = ErrorCode.AUTHENTICATION_REQUIRED;
3756
+ readonly httpStatus = 401;
3757
+ constructor(message?: string, metadata?: ErrorMetadata);
3758
+ }
846
3759
  /**
847
- * Filter value that supports both direct values and operators
3760
+ * Invalid credentials error
3761
+ * Thrown when email/password combination is incorrect
848
3762
  */
849
- type FilterValue<T> = T | QueryOperator<T>;
3763
+ declare class InvalidCredentialsError extends MagnetError {
3764
+ readonly code = ErrorCode.INVALID_CREDENTIALS;
3765
+ readonly httpStatus = 401;
3766
+ constructor(message?: string, metadata?: ErrorMetadata);
3767
+ }
850
3768
  /**
851
- * Full filter query type with logical operators
3769
+ * Token expired error
3770
+ * Thrown when a JWT or refresh token has expired
852
3771
  */
853
- type FilterQuery<Schema> = {
854
- [K in keyof Schema]?: FilterValue<Schema[K]>;
855
- } & {
856
- /** Logical AND */
857
- $and?: FilterQuery<Schema>[];
858
- /** Logical OR */
859
- $or?: FilterQuery<Schema>[];
860
- /** Logical NOR */
861
- $nor?: FilterQuery<Schema>[];
862
- };
3772
+ declare class TokenExpiredError extends MagnetError {
3773
+ readonly code = ErrorCode.TOKEN_EXPIRED;
3774
+ readonly httpStatus = 401;
3775
+ constructor(message?: string, metadata?: ErrorMetadata);
3776
+ }
863
3777
  /**
864
- * Sort direction
3778
+ * Token invalid error
3779
+ * Thrown when a JWT or refresh token is malformed or invalid
865
3780
  */
866
- type SortDirection = 1 | -1 | 'asc' | 'desc';
3781
+ declare class TokenInvalidError extends MagnetError {
3782
+ readonly code = ErrorCode.TOKEN_INVALID;
3783
+ readonly httpStatus = 401;
3784
+ constructor(message?: string, metadata?: ErrorMetadata);
3785
+ }
867
3786
  /**
868
- * Sort specification
3787
+ * Account locked error
3788
+ * Thrown when an account is temporarily locked due to too many failed attempts
869
3789
  */
870
- type SortQuery<Schema> = {
871
- [K in keyof Schema]?: SortDirection;
872
- };
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
+ }
873
3796
  /**
874
- * Projection specification for field selection
3797
+ * Email not verified error
3798
+ * Thrown when an action requires email verification
875
3799
  */
876
- type ProjectionQuery<Schema> = {
877
- [K in keyof Schema]?: 0 | 1 | boolean;
878
- };
3800
+ declare class EmailNotVerifiedError extends MagnetError {
3801
+ readonly code = ErrorCode.EMAIL_NOT_VERIFIED;
3802
+ readonly httpStatus = 403;
3803
+ constructor(message?: string, metadata?: ErrorMetadata);
3804
+ }
879
3805
  /**
880
- * Query execution options
3806
+ * Permission denied error
3807
+ * Thrown when user lacks permission for an action
881
3808
  */
882
- interface QueryOptions {
883
- /** Maximum number of documents to return */
884
- limit?: number;
885
- /** Number of documents to skip */
886
- skip?: number;
887
- /** Return plain objects instead of documents */
888
- lean?: boolean;
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);
889
4029
  }
890
4030
  /**
891
- * Paginated query result
4031
+ * Unexpected error
4032
+ * Used as a fallback for unknown error types
892
4033
  */
893
- interface PaginatedResult<T> {
894
- /** Result data */
895
- data: T[];
896
- /** Total count of matching documents */
897
- total: number;
898
- /** Current page (if using skip/limit) */
899
- page?: number;
900
- /** Page size */
901
- limit?: number;
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);
902
4039
  }
903
4040
 
904
- declare function Resolver(optionsOrFn: ResolverInput): ClassDecorator;
905
-
906
- declare function Resolve(optionsOrFn: ResolveInput): MethodDecorator;
907
-
908
- declare function Prop(options?: PropOptions): PropertyDecorator;
909
-
910
- declare function Schema(options?: SchemaOptions): ClassDecorator;
911
- declare function getSchemaOptions(target: Function): SchemaOptions;
912
-
913
- declare function Setting(): ClassDecorator;
914
-
915
- interface UIFieldMetadata {
916
- propertyKey: string | symbol;
917
- options: UIDecoratorOptions & {
918
- designType: Function;
919
- };
4041
+ /**
4042
+ * Context for error conversion
4043
+ */
4044
+ interface ErrorContext {
4045
+ schema?: string;
4046
+ operation?: string;
920
4047
  }
921
- declare function UI(options: UIDecoratorOptions): PropertyDecorator;
922
-
923
- declare const Validators: (...validators: PropertyDecorator[]) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
924
-
925
- declare const VERSION_METADATA_KEY = "version:metadata";
926
4048
  /**
927
- * Decorator to enable versioning for a schema
928
- * @param config Version configuration
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
929
4053
  */
930
- declare function Version(config?: VersionConfig): ClassDecorator;
931
-
932
- declare function InjectModel(model: Type): ParameterDecorator;
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;
933
4071
 
934
4072
  declare class ValidationException extends Error {
935
- readonly errors: ValidationError[];
936
- constructor(errors: ValidationError[]);
4073
+ readonly errors: ValidationError$1[];
4074
+ constructor(errors: ValidationError$1[]);
937
4075
  }
938
4076
 
939
4077
  /**
940
- * Abstract query builder for fluent database queries.
941
- * Provides chainable methods for filtering, sorting, pagination, and projection.
4078
+ * Register the getter that returns the same `DatabaseAdapter` singleton used by
4079
+ * the adapter’s `forRoot()` / `getInstance()`.
942
4080
  *
943
- * @example
944
- * ```typescript
945
- * const users = await userModel.query()
946
- * .where({ status: 'active' })
947
- * .sort({ createdAt: -1 })
948
- * .limit(10)
949
- * .exec()
950
- * ```
4081
+ * **Why:** With Nest’s compiled CJS output, feature modules are often
4082
+ * `require()`’d before the `AppModule` class decorator runs
4083
+ * `MagnetModule.forRoot()`, so `DatabaseModule.register()` has not executed yet
4084
+ * and core cannot resolve the adapter from registration alone.
4085
+ *
4086
+ * Official packages `@magnet-cms/adapter-db-drizzle` and
4087
+ * `@magnet-cms/adapter-db-mongoose` call this from their entry `index.ts`.
4088
+ * Custom third-party DB adapters should register here if they see the same
4089
+ * load-order failure.
951
4090
  */
952
- declare abstract class QueryBuilder<Schema> {
953
- /**
954
- * Add filter conditions to the query
955
- * @param filter Filter conditions with optional operators
956
- */
957
- abstract where(filter: FilterQuery<Schema>): this;
958
- /**
959
- * Add additional AND conditions
960
- * @param filter Filter conditions to AND with existing filters
961
- */
962
- abstract and(filter: FilterQuery<Schema>): this;
963
- /**
964
- * Add OR conditions
965
- * @param filters Array of filter conditions for OR logic
966
- */
967
- abstract or(filters: FilterQuery<Schema>[]): this;
968
- /**
969
- * Sort results by specified fields
970
- * @param sort Sort specification with field names and directions
971
- */
972
- abstract sort(sort: SortQuery<Schema>): this;
973
- /**
974
- * Limit the number of results
975
- * @param count Maximum number of documents to return
976
- */
977
- abstract limit(count: number): this;
978
- /**
979
- * Skip a number of results (for pagination)
980
- * @param count Number of documents to skip
981
- */
982
- abstract skip(count: number): this;
983
- /**
984
- * Select specific fields to return
985
- * @param projection Field selection (1 to include, 0 to exclude)
986
- */
987
- abstract select(projection: ProjectionQuery<Schema>): this;
988
- /**
989
- * Execute the query and return all matching documents
990
- */
991
- abstract exec(): Promise<BaseSchema<Schema>[]>;
992
- /**
993
- * Execute the query and return a single document
994
- */
995
- abstract execOne(): Promise<BaseSchema<Schema> | null>;
996
- /**
997
- * Count matching documents without fetching them
998
- */
999
- abstract count(): Promise<number>;
1000
- /**
1001
- * Check if any matching documents exist
1002
- */
1003
- abstract exists(): Promise<boolean>;
1004
- /**
1005
- * Execute with pagination info
1006
- * @returns Data array with total count
1007
- */
1008
- abstract paginate(): Promise<PaginatedResult<BaseSchema<Schema>>>;
1009
- /**
1010
- * Set the locale for query results
1011
- * @param locale The locale to use
1012
- */
1013
- abstract locale(locale: string): this;
1014
- /**
1015
- * Set the version filter for query
1016
- * @param versionId The version ID or status
1017
- */
1018
- abstract version(versionId: string): this;
1019
- }
1020
-
1021
- type BaseSchema<T> = {
1022
- id: string;
1023
- } & T;
1024
- interface ModelCreateOptions {
1025
- /** Skip database-level validation (useful for draft documents) */
1026
- skipValidation?: boolean;
1027
- }
1028
- interface ModelUpdateOptions {
1029
- /** Skip database-level validation (useful for draft documents) */
1030
- skipValidation?: boolean;
1031
- }
1032
- declare abstract class Model<Schema> {
1033
- abstract create(data: Partial<BaseSchema<Schema>>, options?: ModelCreateOptions): Promise<BaseSchema<Schema>>;
1034
- abstract find(): Promise<BaseSchema<Schema>[]>;
1035
- abstract findById(id: string): Promise<BaseSchema<Schema> | null>;
1036
- abstract findOne(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema> | null>;
1037
- abstract findMany(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema>[]>;
1038
- abstract update(query: Partial<BaseSchema<Schema>>, data: Partial<BaseSchema<Schema>>, options?: ModelUpdateOptions): Promise<BaseSchema<Schema>>;
1039
- abstract delete(query: Partial<BaseSchema<Schema>>): Promise<boolean>;
1040
- /**
1041
- * Set the locale for subsequent operations
1042
- * @param locale The locale to use
1043
- */
1044
- abstract locale(locale: string): this;
1045
- /**
1046
- * Set the version for subsequent operations
1047
- * @param versionId The version ID or status ('draft', 'published', 'archived')
1048
- */
1049
- version(versionId: string): this;
1050
- /**
1051
- * Find all versions of a document
1052
- * @param documentId The document ID
1053
- */
1054
- findVersions(documentId: string): Promise<any[]>;
1055
- /**
1056
- * Find a specific version by ID
1057
- * @param versionId The version ID
1058
- */
1059
- findVersionById(versionId: string): Promise<any | null>;
1060
- /**
1061
- * Restore a version
1062
- * @param versionId The version ID to restore
1063
- */
1064
- restoreVersion(versionId: string): Promise<BaseSchema<Schema> | null>;
1065
- /**
1066
- * Create a query builder for advanced queries with sorting, pagination, and operators.
1067
- * The query builder inherits the current locale/version context.
1068
- *
1069
- * @example
1070
- * ```typescript
1071
- * const results = await model.query()
1072
- * .where({ status: 'active', age: { $gte: 18 } })
1073
- * .sort({ createdAt: -1 })
1074
- * .limit(10)
1075
- * .exec()
1076
- * ```
1077
- */
1078
- query(): QueryBuilder<Schema>;
1079
- /**
1080
- * Get access to the native database model/collection.
1081
- * Use with caution - bypasses Magnet abstractions like locale and versioning.
1082
- *
1083
- * @returns The underlying database model (e.g., Mongoose Model)
1084
- */
1085
- native(): unknown;
1086
- }
1087
-
1088
- declare class Mixed {
1089
- static schemaName: 'Mixed';
1090
- defaultOptions: Record<string, any>;
1091
- }
4091
+ declare function registerDatabaseAdapterSingletonForFeature(getter: () => DatabaseAdapter): void;
4092
+ /**
4093
+ * @internal Used by `@magnet-cms/core` `DatabaseModule.forFeature` when
4094
+ * `DatabaseModule.register()` has not run yet.
4095
+ */
4096
+ declare function getDatabaseAdapterSingletonForFeature(): DatabaseAdapter | null;
4097
+ /** @internal Clear between isolated tests if needed */
4098
+ declare function clearDatabaseAdapterSingletonForFeature(): void;
1092
4099
 
1093
4100
  type SupportedAdapter = 'mongoose' | 'drizzle';
1094
4101
  /**
1095
4102
  * Detect the database adapter based on configuration or installed packages.
1096
4103
  *
4104
+ * With the provider-based API, the adapter is typically auto-registered:
4105
+ * importing `@magnet-cms/adapter-db-mongoose` or `@magnet-cms/adapter-db-drizzle`
4106
+ * calls `setDatabaseAdapter()` as a module-level side effect, so detection
4107
+ * happens automatically before any `@Schema()` decorators evaluate.
4108
+ *
1097
4109
  * Detection priority:
1098
- * 1. Configuration-based: If `connectionString` or `dialect` is present, use drizzle
1099
- * 2. Configuration-based: If `uri` is present, use mongoose
1100
- * 3. Package-based: Check which adapter packages are installed (mongoose > drizzle)
4110
+ * 1. Cached value from `setDatabaseAdapter()` (set by adapter package import side effect)
4111
+ * 2. Configuration-based: If `connectionString` or `dialect` is present, use drizzle
4112
+ * 3. Configuration-based: If `uri` is present, use mongoose
4113
+ * 4. Package-based: Check which adapter packages are installed (mongoose > drizzle)
1101
4114
  *
1102
4115
  * @param dbConfig - Optional database configuration to determine adapter from
1103
4116
  */
1104
4117
  declare function detectDatabaseAdapter(dbConfig?: DBConfig): SupportedAdapter;
1105
4118
  /**
1106
4119
  * Explicitly set the database adapter.
1107
- * Call this at the very start of your application, before importing any schemas.
1108
4120
  *
1109
- * @example
1110
- * ```typescript
1111
- * // main.ts or app.module.ts (at the top, before other imports)
1112
- * import { setDatabaseAdapter } from '@magnet-cms/common'
1113
- * setDatabaseAdapter('drizzle')
4121
+ * With the provider-based API, this is called automatically as a side effect
4122
+ * when importing an adapter package (e.g., `@magnet-cms/adapter-db-drizzle`).
4123
+ * Manual calls are no longer needed in typical user code.
1114
4124
  *
1115
- * // Then import your modules
1116
- * import { MagnetModule } from '@magnet-cms/core'
1117
- * ```
4125
+ * @internal Called by adapter package index.ts as module-level side effect
1118
4126
  */
1119
4127
  declare function setDatabaseAdapter(adapter: SupportedAdapter): void;
1120
4128
  /**
@@ -1122,11 +4130,140 @@ declare function setDatabaseAdapter(adapter: SupportedAdapter): void;
1122
4130
  */
1123
4131
  declare function clearAdapterCache(): void;
1124
4132
 
1125
- declare function getModelToken(schema: Type): string;
4133
+ /**
4134
+ * Get model injection token for any adapter
4135
+ * @param schema Schema class or schema name
4136
+ * @returns Injection token string
4137
+ */
4138
+ declare function getModelToken(schema: Type | string): string;
4139
+ /**
4140
+ * Get adapter injection token
4141
+ * @returns Injection token for the database adapter
4142
+ */
4143
+ declare function getAdapterToken(): string;
4144
+ declare function registerModel(token: string, model: unknown): void;
4145
+ declare function getRegisteredModel<T>(token: string): T | undefined;
1126
4146
 
1127
4147
  declare const getSchemaToken: (schema: Type) => string;
1128
4148
  declare const getSettingToken: (setting: Type) => string;
1129
4149
 
4150
+ /**
4151
+ * Type guard utilities for safe type narrowing
4152
+ * Use these instead of type assertions (as any, as unknown as T)
4153
+ */
4154
+ /**
4155
+ * Check if value is a non-null object
4156
+ */
4157
+ declare function isObject(value: unknown): value is Record<string, unknown>;
4158
+ /**
4159
+ * Check if value has a specific property
4160
+ */
4161
+ declare function hasProperty<K extends string>(value: unknown, key: K): value is Record<K, unknown>;
4162
+ /**
4163
+ * Check if value has multiple properties
4164
+ */
4165
+ declare function hasProperties<K extends string>(value: unknown, keys: K[]): value is Record<K, unknown>;
4166
+ /**
4167
+ * Check if value is a string
4168
+ */
4169
+ declare function isString(value: unknown): value is string;
4170
+ /**
4171
+ * Check if value is a number
4172
+ */
4173
+ declare function isNumber(value: unknown): value is number;
4174
+ /**
4175
+ * Check if value is a boolean
4176
+ */
4177
+ declare function isBoolean(value: unknown): value is boolean;
4178
+ /**
4179
+ * Check if value is an array
4180
+ */
4181
+ declare function isArray(value: unknown): value is unknown[];
4182
+ /**
4183
+ * Check if value is a string array
4184
+ */
4185
+ declare function isStringArray(value: unknown): value is string[];
4186
+ /**
4187
+ * Check if value is a function
4188
+ */
4189
+ declare function isFunction(value: unknown): value is Function;
4190
+ /**
4191
+ * Check if value is a valid document with ID
4192
+ */
4193
+ declare function isDocument(value: unknown): value is {
4194
+ id: string;
4195
+ [key: string]: unknown;
4196
+ };
4197
+ /**
4198
+ * Check if error is a MongoDB CastError
4199
+ */
4200
+ declare function isCastError(error: unknown): error is {
4201
+ name: 'CastError';
4202
+ path: string;
4203
+ value: unknown;
4204
+ };
4205
+ /**
4206
+ * Check if error is a MongoDB duplicate key error
4207
+ */
4208
+ declare function isDuplicateKeyError(error: unknown): error is {
4209
+ code: 11000;
4210
+ keyValue: Record<string, unknown>;
4211
+ };
4212
+ /**
4213
+ * Check if error is a Mongoose validation error
4214
+ */
4215
+ declare function isValidationError(error: unknown): error is {
4216
+ name: 'ValidationError';
4217
+ errors: Record<string, unknown>;
4218
+ };
4219
+ /**
4220
+ * Check if error is a PostgreSQL unique constraint violation
4221
+ */
4222
+ declare function isPostgresUniqueError(error: unknown): error is {
4223
+ code: string;
4224
+ detail?: string;
4225
+ };
4226
+ /**
4227
+ * Check if an object has a method
4228
+ */
4229
+ declare function hasMethod<K extends string>(value: unknown, methodName: K): value is Record<K, Function>;
4230
+ /**
4231
+ * Check if value has setLocale method (for i18n documents)
4232
+ */
4233
+ declare function hasSetLocale<T>(value: T): value is T & {
4234
+ setLocale: (locale: string) => T;
4235
+ };
4236
+ /**
4237
+ * Check if value has toString method that returns string
4238
+ */
4239
+ declare function hasToString(value: unknown): value is {
4240
+ toString(): string;
4241
+ };
4242
+ /**
4243
+ * Assert value is defined (not null or undefined)
4244
+ * Throws if value is null or undefined
4245
+ */
4246
+ declare function assertDefined<T>(value: T | null | undefined, message?: string): asserts value is T;
4247
+ /**
4248
+ * Assert condition is true
4249
+ * Throws if condition is false
4250
+ */
4251
+ declare function assert(condition: boolean, message?: string): asserts condition;
4252
+ /**
4253
+ * Safely get a string ID from a document that might have _id or id
4254
+ * Returns undefined if no valid ID found
4255
+ */
4256
+ declare function getDocumentId(doc: unknown): string | undefined;
4257
+ /**
4258
+ * Type guard for checking if an object matches a record with string values
4259
+ */
4260
+ declare function isStringRecord(value: unknown): value is Record<string, string>;
4261
+
4262
+ /**
4263
+ * Check if value is a version document
4264
+ */
4265
+ declare function isVersionDocument(value: unknown): value is VersionDocument;
4266
+
1130
4267
  declare const RESOLVER_METADATA_KEY = "magnet:resolver";
1131
4268
  declare const RESOLVE_METADATA_KEY = "magnet:resolve";
1132
4269
  declare const SCHEMA_METADATA_KEY = "magnet:schema";
@@ -1134,11 +4271,18 @@ declare const SCHEMA_OPTIONS_METADATA_KEY = "magnet:schema:options";
1134
4271
  declare const BASE_SCHEMA_METADATA_KEY = "magnet:schema:base";
1135
4272
  declare const PROP_METADATA_KEY = "magnet:schema:prop";
1136
4273
  declare const UI_METADATA_KEY = "magnet:schema:ui";
4274
+ declare const FIELD_METADATA_KEY = "magnet:schema:field";
1137
4275
  declare const SETTING_METADATA_KEY = "magnet:setting";
4276
+ declare const SETTINGS_OPTIONS_METADATA_KEY = "magnet:settings:options";
4277
+ declare const SETTING_FIELD_METADATA_KEY = "magnet:settings:field";
4278
+ declare const EXTEND_USER_METADATA_KEY = "magnet:extend:user";
4279
+ declare const PERMISSION_METADATA_KEY = "magnet:permission";
4280
+ declare const PERMISSION_OPTIONS_METADATA_KEY = "magnet:permission:options";
4281
+ declare const RESOLVED_PERMISSION_KEY = "magnet:permission:resolved";
1138
4282
  declare const INJECT_MODEL = "magnet:inject:model";
1139
4283
  declare const DESIGN_TYPE = "design:type";
1140
4284
  declare const DESIGN_META = "design:metadata";
1141
4285
  declare const DESIGN_PARAM_TYPES = "design:paramtypes";
1142
4286
  declare const DESIGN_RETURN_TYPE = "design:returntype";
1143
4287
 
1144
- 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 DrizzleConfig, 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 SupabaseAuthConfig, type SupabaseStorageConfig, type SupportedAdapter, 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 UploadOptions, type UploadResult, VERSION_METADATA_KEY, ValidationException, type Validations, Validators, Version, type VersionConfig, type VersionData, type VersionStatus, clearAdapterCache, detectDatabaseAdapter, getModelToken, getSchemaOptions, getSchemaToken, getSettingToken, setDatabaseAdapter };
4288
+ 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, clearDatabaseAdapterSingletonForFeature, createFieldDecorator, detectDatabaseAdapter, fromDrizzleError, fromMongooseError, getAdapterToken, getDatabaseAdapterSingletonForFeature, 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, registerDatabaseAdapterSingletonForFeature, registerModel, setDatabaseAdapter, wrapError };