@magnet-cms/common 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,82 +1,1580 @@
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;
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 */
11
1123
  description?: string;
12
- };
13
- type ResolveInput = (() => Type | [Type]) | ResolveOptions;
14
- type PropOptions = {
15
- type?: Type | [Type] | any;
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 */
16
1189
  description?: string;
17
- required?: boolean;
18
- unique?: boolean;
19
- default?: any;
20
- nullable?: boolean;
21
- intl?: boolean;
22
- hidden?: boolean;
23
- readonly?: boolean;
24
- ref?: string;
25
- };
26
- type BaseSchemaOptions = {
27
- timestamps?: boolean;
28
- };
29
- type SchemaOptions = {
30
- versioning?: boolean;
31
- i18n?: boolean;
32
- };
33
-
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;
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 */
38
1234
  description?: string;
39
- type?: UITypes;
40
- row?: boolean;
41
- collapsible?: boolean;
42
- };
43
- type UISelectItem = {
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 {
44
1417
  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 = {
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;
71
1430
  key: string;
72
- header: string;
73
- type?: 'text' | 'badge' | 'status' | 'input' | 'code';
74
- };
75
- type UITable = UIBase & {
76
- type: 'table';
77
- columns?: UITableColumn[];
78
- };
79
- type UIDecoratorOptions = UITab | UISide | UISelect | UIMultiSelect | UICombobox | UITable;
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;
80
1578
 
81
1579
  type MethodMetadata = {
82
1580
  name: string;
@@ -103,7 +1601,7 @@ type ControllerMetadata = {
103
1601
  type SchemaPropertyValidation = {
104
1602
  type: string;
105
1603
  name: string;
106
- constraints: number[];
1604
+ constraints?: unknown[];
107
1605
  };
108
1606
  type SchemaProperty = {
109
1607
  name: string;
@@ -112,7 +1610,7 @@ type SchemaProperty = {
112
1610
  unique: boolean;
113
1611
  required: boolean;
114
1612
  validations: SchemaPropertyValidation[];
115
- ui: UIDecoratorOptions;
1613
+ ui?: UIDecoratorOptions;
116
1614
  ref?: string;
117
1615
  };
118
1616
  type SchemaMetadata = {
@@ -122,6 +1620,8 @@ type SchemaMetadata = {
122
1620
  displayName?: string;
123
1621
  properties: SchemaProperty[];
124
1622
  options?: SchemaOptions;
1623
+ /** Settings-specific options (only present for settings schemas) */
1624
+ settingsOptions?: SettingsDecoratorOptions;
125
1625
  };
126
1626
 
127
1627
  type InitialConfig = {
@@ -193,108 +1693,611 @@ interface SupabaseAuthConfig {
193
1693
  defaultRole?: string;
194
1694
  }
195
1695
  /**
196
- * Auth configuration for MagnetModuleOptions
1696
+ * Auth configuration for MagnetModuleOptions
1697
+ */
1698
+ interface AuthConfig {
1699
+ /** Strategy name to use (default: 'jwt') */
1700
+ strategy?: string;
1701
+ /** JWT-specific configuration */
1702
+ jwt?: JwtAuthConfig;
1703
+ /** Supabase-specific configuration (when strategy: 'supabase') */
1704
+ supabaseUrl?: string;
1705
+ /** Supabase anon/public key */
1706
+ supabaseKey?: string;
1707
+ /** Supabase service role key (required for admin operations like listUsers) */
1708
+ supabaseServiceKey?: string;
1709
+ /** Default role for new Supabase users */
1710
+ defaultRole?: string;
1711
+ /** Allow extensible config for custom strategies */
1712
+ [key: string]: unknown;
1713
+ }
1714
+ /**
1715
+ * Information about the active authentication strategy, including
1716
+ * whether it's an external provider and what OAuth providers it has configured.
1717
+ *
1718
+ * Returned by `AuthStrategy.getAuthInfo()` for external adapters (Supabase, Clerk, etc.).
1719
+ */
1720
+ interface ExternalAuthInfo {
1721
+ /** Strategy identifier (e.g., 'supabase', 'clerk', 'jwt') */
1722
+ strategy: string;
1723
+ /** Whether authentication is handled by an external provider */
1724
+ isExternal: boolean;
1725
+ /** List of OAuth provider names configured in the external service (e.g., ['google', 'github']) */
1726
+ providers: string[];
1727
+ /** Additional provider-specific settings (e.g., disable_signup, autoconfirm) */
1728
+ providerSettings?: Record<string, unknown>;
1729
+ }
1730
+ /**
1731
+ * Abstract base class for authentication strategies.
1732
+ * Similar to StorageAdapter, custom strategies must extend this class.
1733
+ *
1734
+ * @example
1735
+ * ```typescript
1736
+ * export class SupabaseAuthStrategy extends AuthStrategy {
1737
+ * readonly name = 'supabase'
1738
+ *
1739
+ * async validate(payload: unknown): Promise<AuthUser | null> {
1740
+ * // Validate Supabase JWT token
1741
+ * }
1742
+ *
1743
+ * async login(credentials: LoginCredentials): Promise<AuthResult> {
1744
+ * // Authenticate with Supabase
1745
+ * }
1746
+ *
1747
+ * async register(data: RegisterData): Promise<AuthUser> {
1748
+ * // Register user in Supabase
1749
+ * }
1750
+ *
1751
+ * async validateCredentials(email: string, password: string): Promise<AuthUser | null> {
1752
+ * // Validate user credentials
1753
+ * }
1754
+ * }
1755
+ * ```
1756
+ */
1757
+ declare abstract class AuthStrategy {
1758
+ /**
1759
+ * Unique identifier for this strategy
1760
+ */
1761
+ abstract readonly name: string;
1762
+ /**
1763
+ * Initialize the auth strategy (optional setup)
1764
+ */
1765
+ initialize?(): Promise<void>;
1766
+ /**
1767
+ * Validate a token/payload and return the authenticated user
1768
+ * @param payload - Strategy-specific payload (e.g., JWT payload, session data)
1769
+ * @returns Authenticated user or null if invalid
1770
+ */
1771
+ abstract validate(payload: unknown): Promise<AuthUser | null>;
1772
+ /**
1773
+ * Authenticate user with credentials and return tokens
1774
+ * @param credentials - Login credentials (strategy-specific)
1775
+ * @returns Authentication result with access token
1776
+ */
1777
+ abstract login(credentials: LoginCredentials): Promise<AuthResult>;
1778
+ /**
1779
+ * Register a new user
1780
+ * @param data - Registration data
1781
+ * @returns The created user
1782
+ */
1783
+ abstract register(data: RegisterData): Promise<AuthUser>;
1784
+ /**
1785
+ * Validate user credentials (used internally by login)
1786
+ * @param email - User email
1787
+ * @param password - User password
1788
+ * @returns User if valid, null otherwise
1789
+ */
1790
+ abstract validateCredentials(email: string, password: string): Promise<AuthUser | null>;
1791
+ /**
1792
+ * Refresh an access token (optional)
1793
+ * @param refreshToken - The refresh token
1794
+ * @returns New authentication result
1795
+ */
1796
+ refresh?(refreshToken: string): Promise<AuthResult>;
1797
+ /**
1798
+ * Logout/invalidate tokens (optional)
1799
+ * @param token - The token to invalidate
1800
+ */
1801
+ logout?(token: string): Promise<void>;
1802
+ /**
1803
+ * Check if any users exist in the system (optional)
1804
+ * Strategies that manage their own user storage should implement this.
1805
+ * If not implemented, AuthService will fall back to checking the local database.
1806
+ * @returns true if users exist, false otherwise
1807
+ */
1808
+ hasUsers?(): Promise<boolean>;
1809
+ /**
1810
+ * Get information about the auth strategy and its configured providers (optional).
1811
+ * External adapters (Supabase, Clerk) implement this to report which OAuth
1812
+ * providers are configured on their side. If not implemented, the strategy
1813
+ * is assumed to be built-in (JWT).
1814
+ * @returns Auth info including strategy name, external flag, and provider list
1815
+ */
1816
+ getAuthInfo?(): Promise<ExternalAuthInfo>;
1817
+ /**
1818
+ * Get the Passport strategy name (for guards)
1819
+ * Returns the name to use with AuthGuard()
1820
+ */
1821
+ getPassportStrategyName(): string;
1822
+ }
1823
+
1824
+ /**
1825
+ * SMTP configuration for Nodemailer adapter
1826
+ */
1827
+ interface NodemailerConfig {
1828
+ /** SMTP server hostname */
1829
+ host: string;
1830
+ /** SMTP server port (default: 587) */
1831
+ port?: number;
1832
+ /** Use TLS (default: true for port 465, false otherwise) */
1833
+ secure?: boolean;
1834
+ /** SMTP authentication credentials */
1835
+ auth: {
1836
+ /** SMTP username */
1837
+ user: string;
1838
+ /** SMTP password */
1839
+ pass: string;
1840
+ };
1841
+ /** Connection timeout in milliseconds */
1842
+ connectionTimeout?: number;
1843
+ /** Greeting timeout in milliseconds */
1844
+ greetingTimeout?: number;
1845
+ /** Socket timeout in milliseconds */
1846
+ socketTimeout?: number;
1847
+ }
1848
+ /**
1849
+ * Resend API configuration
1850
+ */
1851
+ interface ResendConfig {
1852
+ /** Resend API key */
1853
+ apiKey: string;
1854
+ }
1855
+ /**
1856
+ * Email adapter name type — extensible for custom adapters
1857
+ */
1858
+ type EmailAdapterName = 'nodemailer' | 'resend' | (string & {});
1859
+ /**
1860
+ * Email system configuration for MagnetModuleOptions
1861
+ */
1862
+ interface EmailConfig {
1863
+ /** Which email adapter to use */
1864
+ adapter: EmailAdapterName;
1865
+ /** Nodemailer SMTP configuration (when adapter is 'nodemailer') */
1866
+ nodemailer?: NodemailerConfig;
1867
+ /** Resend API configuration (when adapter is 'resend') */
1868
+ resend?: ResendConfig;
1869
+ /** Default email settings */
1870
+ defaults?: {
1871
+ /** Default sender email address */
1872
+ from?: string;
1873
+ /** Default reply-to address */
1874
+ replyTo?: string;
1875
+ };
1876
+ }
1877
+ /**
1878
+ * Email attachment
1879
+ */
1880
+ interface EmailAttachment {
1881
+ /** Attachment filename */
1882
+ filename: string;
1883
+ /** Attachment content (Buffer, string, or URL) */
1884
+ content: Buffer | string;
1885
+ /** MIME type of the attachment */
1886
+ contentType?: string;
1887
+ /** Content disposition (default: 'attachment') */
1888
+ disposition?: 'attachment' | 'inline';
1889
+ /** Content ID for inline attachments */
1890
+ cid?: string;
1891
+ }
1892
+ /**
1893
+ * Options for sending an email
1894
+ */
1895
+ interface SendEmailOptions {
1896
+ /** Recipient email address(es) */
1897
+ to: string | string[];
1898
+ /** Sender email address (overrides default) */
1899
+ from?: string;
1900
+ /** Email subject line */
1901
+ subject: string;
1902
+ /** HTML body content */
1903
+ html?: string;
1904
+ /** Plain text body content */
1905
+ text?: string;
1906
+ /** Reply-to address (overrides default) */
1907
+ replyTo?: string;
1908
+ /** CC recipients */
1909
+ cc?: string | string[];
1910
+ /** BCC recipients */
1911
+ bcc?: string | string[];
1912
+ /** Email attachments */
1913
+ attachments?: EmailAttachment[];
1914
+ /** Custom headers */
1915
+ headers?: Record<string, string>;
1916
+ /** Tags for categorization (supported by some providers) */
1917
+ tags?: Array<{
1918
+ name: string;
1919
+ value: string;
1920
+ }>;
1921
+ }
1922
+ /**
1923
+ * Result of sending an email
1924
+ */
1925
+ interface SendEmailResult {
1926
+ /** Provider-assigned message ID */
1927
+ id?: string;
1928
+ /** Whether the email was accepted for delivery */
1929
+ accepted: boolean;
1930
+ /** List of accepted recipient addresses */
1931
+ acceptedAddresses?: string[];
1932
+ /** List of rejected recipient addresses */
1933
+ rejectedAddresses?: string[];
1934
+ /** Error message if sending failed */
1935
+ error?: string;
1936
+ }
1937
+ /**
1938
+ * Abstract base class for email adapters.
1939
+ *
1940
+ * All email providers must extend this class and implement the abstract methods.
1941
+ * Follows the same pattern as `StorageAdapter` and `DatabaseAdapter`.
1942
+ *
1943
+ * @example
1944
+ * ```typescript
1945
+ * export class NodemailerEmailAdapter extends EmailAdapter {
1946
+ * readonly name = 'nodemailer'
1947
+ *
1948
+ * async send(options: SendEmailOptions): Promise<SendEmailResult> {
1949
+ * // Send via SMTP using Nodemailer
1950
+ * }
1951
+ *
1952
+ * async sendBatch(emails: SendEmailOptions[]): Promise<SendEmailResult[]> {
1953
+ * return Promise.all(emails.map(email => this.send(email)))
1954
+ * }
1955
+ *
1956
+ * async verify(): Promise<boolean> {
1957
+ * // Test SMTP connection
1958
+ * }
1959
+ * }
1960
+ * ```
1961
+ */
1962
+ declare abstract class EmailAdapter {
1963
+ /**
1964
+ * Unique identifier for this adapter
1965
+ */
1966
+ abstract readonly name: string;
1967
+ /**
1968
+ * Send a single email
1969
+ * @param options - Email send options (to, subject, html, etc.)
1970
+ * @returns Result with delivery status
1971
+ */
1972
+ abstract send(options: SendEmailOptions): Promise<SendEmailResult>;
1973
+ /**
1974
+ * Send multiple emails in batch
1975
+ * @param emails - Array of email options
1976
+ * @returns Array of results (one per email)
1977
+ */
1978
+ abstract sendBatch(emails: SendEmailOptions[]): Promise<SendEmailResult[]>;
1979
+ /**
1980
+ * Verify the adapter connection/configuration
1981
+ * @returns true if the adapter is properly configured and can send emails
1982
+ */
1983
+ abstract verify(): Promise<boolean>;
1984
+ /**
1985
+ * Optional cleanup/disconnect method
1986
+ */
1987
+ dispose?(): Promise<void>;
1988
+ }
1989
+
1990
+ /**
1991
+ * Query operator types for MongoDB-style queries
1992
+ */
1993
+ type QueryOperator<T> = {
1994
+ /** Equal to */
1995
+ $eq?: T;
1996
+ /** Not equal to */
1997
+ $ne?: T;
1998
+ /** Greater than */
1999
+ $gt?: T;
2000
+ /** Greater than or equal to */
2001
+ $gte?: T;
2002
+ /** Less than */
2003
+ $lt?: T;
2004
+ /** Less than or equal to */
2005
+ $lte?: T;
2006
+ /** In array */
2007
+ $in?: T[];
2008
+ /** Not in array */
2009
+ $nin?: T[];
2010
+ /** Field exists */
2011
+ $exists?: boolean;
2012
+ /** Regular expression match */
2013
+ $regex?: string | RegExp;
2014
+ /** Regex options (i, m, s, x) */
2015
+ $options?: string;
2016
+ };
2017
+ /**
2018
+ * Filter value that supports both direct values and operators
2019
+ */
2020
+ type FilterValue<T> = T | QueryOperator<T>;
2021
+ /**
2022
+ * Full filter query type with logical operators
2023
+ */
2024
+ type FilterQuery<Schema> = {
2025
+ [K in keyof Schema]?: FilterValue<Schema[K]>;
2026
+ } & {
2027
+ /** Logical AND */
2028
+ $and?: FilterQuery<Schema>[];
2029
+ /** Logical OR */
2030
+ $or?: FilterQuery<Schema>[];
2031
+ /** Logical NOR */
2032
+ $nor?: FilterQuery<Schema>[];
2033
+ };
2034
+ /**
2035
+ * Sort direction
2036
+ */
2037
+ type SortDirection = 1 | -1 | 'asc' | 'desc';
2038
+ /**
2039
+ * Sort specification
2040
+ */
2041
+ type SortQuery<Schema> = {
2042
+ [K in keyof Schema]?: SortDirection;
2043
+ };
2044
+ /**
2045
+ * Projection specification for field selection
2046
+ */
2047
+ type ProjectionQuery<Schema> = {
2048
+ [K in keyof Schema]?: 0 | 1 | boolean;
2049
+ };
2050
+ /**
2051
+ * Query execution options
2052
+ */
2053
+ interface QueryOptions {
2054
+ /** Maximum number of documents to return */
2055
+ limit?: number;
2056
+ /** Number of documents to skip */
2057
+ skip?: number;
2058
+ /** Return plain objects instead of documents */
2059
+ lean?: boolean;
2060
+ }
2061
+ /**
2062
+ * Paginated query result
197
2063
  */
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;
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;
213
2073
  }
2074
+
214
2075
  /**
215
- * Abstract base class for authentication strategies.
216
- * Similar to StorageAdapter, custom strategies must extend this class.
2076
+ * Abstract query builder for fluent database queries.
2077
+ * Provides chainable methods for filtering, sorting, pagination, and projection.
217
2078
  *
218
2079
  * @example
219
2080
  * ```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
- * }
2081
+ * const users = await userModel.query()
2082
+ * .where({ status: 'active' })
2083
+ * .sort({ createdAt: -1 })
2084
+ * .limit(10)
2085
+ * .exec()
239
2086
  * ```
240
2087
  */
241
- declare abstract class AuthStrategy {
2088
+ declare abstract class QueryBuilder<Schema> {
242
2089
  /**
243
- * Unique identifier for this strategy
2090
+ * Add filter conditions to the query
2091
+ * @param filter Filter conditions with optional operators
244
2092
  */
245
- abstract readonly name: string;
2093
+ abstract where(filter: FilterQuery<Schema>): this;
246
2094
  /**
247
- * Initialize the auth strategy (optional setup)
2095
+ * Add additional AND conditions
2096
+ * @param filter Filter conditions to AND with existing filters
248
2097
  */
249
- initialize?(): Promise<void>;
2098
+ abstract and(filter: FilterQuery<Schema>): this;
250
2099
  /**
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
2100
+ * Add OR conditions
2101
+ * @param filters Array of filter conditions for OR logic
254
2102
  */
255
- abstract validate(payload: unknown): Promise<AuthUser | null>;
2103
+ abstract or(filters: FilterQuery<Schema>[]): this;
256
2104
  /**
257
- * Authenticate user with credentials and return tokens
258
- * @param credentials - Login credentials (strategy-specific)
259
- * @returns Authentication result with access token
2105
+ * Sort results by specified fields
2106
+ * @param sort Sort specification with field names and directions
260
2107
  */
261
- abstract login(credentials: LoginCredentials): Promise<AuthResult>;
2108
+ abstract sort(sort: SortQuery<Schema>): this;
262
2109
  /**
263
- * Register a new user
264
- * @param data - Registration data
265
- * @returns The created user
2110
+ * Limit the number of results
2111
+ * @param count Maximum number of documents to return
266
2112
  */
267
- abstract register(data: RegisterData): Promise<AuthUser>;
2113
+ abstract limit(count: number): this;
268
2114
  /**
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
2115
+ * Skip a number of results (for pagination)
2116
+ * @param count Number of documents to skip
273
2117
  */
274
- abstract validateCredentials(email: string, password: string): Promise<AuthUser | null>;
2118
+ abstract skip(count: number): this;
275
2119
  /**
276
- * Refresh an access token (optional)
277
- * @param refreshToken - The refresh token
278
- * @returns New authentication result
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
279
2124
  */
280
- refresh?(refreshToken: string): Promise<AuthResult>;
2125
+ abstract paginate(page?: number, perPage?: number): Promise<PaginatedResult<BaseSchema<Schema>>>;
281
2126
  /**
282
- * Logout/invalidate tokens (optional)
283
- * @param token - The token to invalidate
2127
+ * Select specific fields to return (inclusion)
2128
+ * @param projection Field selection (1 to include, 0 to exclude)
284
2129
  */
285
- logout?(token: string): Promise<void>;
2130
+ abstract select(projection: ProjectionQuery<Schema>): this;
286
2131
  /**
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
2132
+ * Exclude specific fields from results
2133
+ * @param fields Array of field names to exclude
291
2134
  */
292
- hasUsers?(): Promise<boolean>;
2135
+ exclude(fields: (keyof Schema | string)[]): this;
293
2136
  /**
294
- * Get the Passport strategy name (for guards)
295
- * Returns the name to use with AuthGuard()
2137
+ * Set the locale for query results
2138
+ * @param locale The locale to use
296
2139
  */
297
- getPassportStrategyName(): string;
2140
+ abstract locale(locale: string): this;
2141
+ /**
2142
+ * Set the version filter for query
2143
+ * @param versionId The version ID or status
2144
+ */
2145
+ abstract version(versionId: string): this;
2146
+ /**
2147
+ * Execute the query and return all matching documents
2148
+ */
2149
+ abstract exec(): Promise<BaseSchema<Schema>[]>;
2150
+ /**
2151
+ * Execute the query and return a single document
2152
+ */
2153
+ abstract execOne(): Promise<BaseSchema<Schema> | null>;
2154
+ /**
2155
+ * Count matching documents without fetching them
2156
+ */
2157
+ abstract count(): Promise<number>;
2158
+ /**
2159
+ * Check if any matching documents exist
2160
+ */
2161
+ abstract exists(): Promise<boolean>;
2162
+ }
2163
+
2164
+ type BaseSchema<T> = {
2165
+ id: string;
2166
+ } & T;
2167
+ interface ModelCreateOptions {
2168
+ /** Skip database-level validation (useful for draft documents) */
2169
+ skipValidation?: boolean;
2170
+ }
2171
+ interface ModelUpdateOptions {
2172
+ /** Skip database-level validation (useful for draft documents) */
2173
+ skipValidation?: boolean;
2174
+ }
2175
+ /**
2176
+ * Version document type for history/versioning
2177
+ */
2178
+ interface VersionDocument {
2179
+ /** ID of the original document */
2180
+ documentId: string;
2181
+ /** Unique version identifier */
2182
+ versionId: string;
2183
+ /** Name of the schema/collection */
2184
+ schemaName: string;
2185
+ /** Version status */
2186
+ status: 'draft' | 'published' | 'archived';
2187
+ /** Snapshot of the document data at this version */
2188
+ data: Record<string, unknown>;
2189
+ /** When this version was created */
2190
+ createdAt: Date;
2191
+ /** Who created this version */
2192
+ createdBy?: string;
2193
+ /** Type of change that created this version */
2194
+ changeType?: 'create' | 'update' | 'restore';
2195
+ }
2196
+ /**
2197
+ * Native access wrapper - provides type-safe native operations
2198
+ */
2199
+ interface NativeAccess<T> {
2200
+ /**
2201
+ * Raw database/ORM instance (type varies by adapter)
2202
+ * For Mongoose: Model<Document>
2203
+ * For Drizzle: { db, table }
2204
+ */
2205
+ readonly raw: unknown;
2206
+ /**
2207
+ * Execute raw query (adapter-specific syntax)
2208
+ * @param query - The raw query string
2209
+ * @param params - Query parameters
2210
+ */
2211
+ rawQuery<R = unknown>(query: string, params?: unknown[]): Promise<R>;
2212
+ /**
2213
+ * Get adapter name for adapter-specific code
2214
+ */
2215
+ readonly adapterName: AdapterName;
2216
+ }
2217
+ declare abstract class Model<Schema> {
2218
+ abstract create(data: Partial<BaseSchema<Schema>>, options?: ModelCreateOptions): Promise<BaseSchema<Schema>>;
2219
+ abstract find(): Promise<BaseSchema<Schema>[]>;
2220
+ abstract findById(id: string): Promise<BaseSchema<Schema> | null>;
2221
+ abstract findOne(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema> | null>;
2222
+ abstract findMany(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema>[]>;
2223
+ abstract update(query: Partial<BaseSchema<Schema>>, data: Partial<BaseSchema<Schema>>, options?: ModelUpdateOptions): Promise<BaseSchema<Schema>>;
2224
+ abstract delete(query: Partial<BaseSchema<Schema>>): Promise<boolean>;
2225
+ /**
2226
+ * Set the locale for subsequent operations
2227
+ * @param locale The locale to use
2228
+ * @returns Cloned model instance with locale set
2229
+ */
2230
+ abstract locale(locale: string): this;
2231
+ /**
2232
+ * Get current locale
2233
+ */
2234
+ getLocale(): string;
2235
+ /**
2236
+ * Set the version for subsequent operations
2237
+ * @param versionId The version ID or status ('draft', 'published', 'archived')
2238
+ * @returns Same instance (chainable)
2239
+ */
2240
+ version(versionId: string): this;
2241
+ /**
2242
+ * Check if versioning is enabled for this model
2243
+ */
2244
+ isVersioningEnabled(): boolean;
2245
+ /**
2246
+ * Create a version snapshot of a document
2247
+ * @param documentId The document ID
2248
+ * @param data The data to version
2249
+ * @returns Version record or null if versioning disabled
2250
+ */
2251
+ createVersion(documentId: string, data: Partial<Schema>): Promise<VersionDocument | null>;
2252
+ /**
2253
+ * Find all versions of a document
2254
+ * @param documentId The document ID
2255
+ */
2256
+ findVersions(documentId: string): Promise<VersionDocument[]>;
2257
+ /**
2258
+ * Find a specific version by ID
2259
+ * @param versionId The version ID
2260
+ */
2261
+ findVersionById(versionId: string): Promise<VersionDocument | null>;
2262
+ /**
2263
+ * Restore a document to a specific version
2264
+ * @param versionId The version ID to restore
2265
+ */
2266
+ restoreVersion(versionId: string): Promise<BaseSchema<Schema> | null>;
2267
+ /**
2268
+ * Create a query builder for advanced queries with sorting, pagination, and operators.
2269
+ * The query builder inherits the current locale/version context.
2270
+ *
2271
+ * @example
2272
+ * ```typescript
2273
+ * const results = await model.query()
2274
+ * .where({ status: 'active', age: { $gte: 18 } })
2275
+ * .sort({ createdAt: -1 })
2276
+ * .limit(10)
2277
+ * .exec()
2278
+ * ```
2279
+ */
2280
+ query(): QueryBuilder<Schema>;
2281
+ /**
2282
+ * Get access to the native database model/collection.
2283
+ * Use with caution - bypasses Magnet abstractions like locale and versioning.
2284
+ *
2285
+ * @returns Typed native access object
2286
+ */
2287
+ native(): NativeAccess<Schema>;
2288
+ /**
2289
+ * Get schema name
2290
+ */
2291
+ getSchemaName(): string;
2292
+ /**
2293
+ * Get schema metadata
2294
+ */
2295
+ getMetadata(): SchemaMetadata;
2296
+ }
2297
+
2298
+ declare class Mixed {
2299
+ static schemaName: 'Mixed';
2300
+ defaultOptions: Record<string, unknown>;
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;
@@ -601,520 +2685,1421 @@ interface MediaQueryOptions {
601
2685
  /** Sort direction */
602
2686
  sortOrder?: 'asc' | 'desc';
603
2687
  }
604
- interface PaginatedMedia<T = UploadResult> {
605
- /** List of items */
606
- items: T[];
607
- /** Total number of items */
608
- total: number;
609
- /** Current page */
610
- page: number;
611
- /** Items per page */
612
- limit: number;
613
- /** Total number of pages */
614
- totalPages: number;
2688
+ interface PaginatedMedia<T = UploadResult> {
2689
+ /** List of items */
2690
+ items: T[];
2691
+ /** Total number of items */
2692
+ total: number;
2693
+ /** Current page */
2694
+ page: number;
2695
+ /** Items per page */
2696
+ limit: number;
2697
+ /** Total number of pages */
2698
+ totalPages: number;
2699
+ }
2700
+ declare abstract class StorageAdapter {
2701
+ /**
2702
+ * Initialize the storage adapter (create directories, check connections)
2703
+ */
2704
+ abstract initialize(): Promise<void>;
2705
+ /**
2706
+ * Upload a file to storage
2707
+ * @param file - File buffer or readable stream
2708
+ * @param originalFilename - Original filename from user
2709
+ * @param options - Upload options
2710
+ */
2711
+ abstract upload(file: Buffer | Readable, originalFilename: string, options?: UploadOptions): Promise<UploadResult>;
2712
+ /**
2713
+ * Upload a file using chunked/streaming upload (for large files)
2714
+ * @param stream - Readable stream of the file
2715
+ * @param originalFilename - Original filename from user
2716
+ * @param totalSize - Total size of the file in bytes
2717
+ * @param options - Upload options
2718
+ */
2719
+ abstract uploadChunked(stream: Readable, originalFilename: string, totalSize: number, options?: UploadOptions): Promise<UploadResult>;
2720
+ /**
2721
+ * Delete a file from storage
2722
+ * @param path - Storage path or key of the file
2723
+ */
2724
+ abstract delete(path: string): Promise<boolean>;
2725
+ /**
2726
+ * Delete multiple files from storage
2727
+ * @param paths - Array of storage paths or keys
2728
+ */
2729
+ abstract deleteMany(paths: string[]): Promise<{
2730
+ deleted: number;
2731
+ failed: string[];
2732
+ }>;
2733
+ /**
2734
+ * Get the public URL for a file
2735
+ * @param path - Storage path or key
2736
+ * @param transform - Optional transform options for images
2737
+ */
2738
+ abstract getUrl(path: string, transform?: TransformOptions): string;
2739
+ /**
2740
+ * Get a readable stream for a file
2741
+ * @param path - Storage path or key
2742
+ */
2743
+ abstract getStream(path: string): Promise<Readable>;
2744
+ /**
2745
+ * Get the file as a buffer
2746
+ * @param path - Storage path or key
2747
+ */
2748
+ abstract getBuffer(path: string): Promise<Buffer>;
2749
+ /**
2750
+ * Check if a file exists
2751
+ * @param path - Storage path or key
2752
+ */
2753
+ abstract exists(path: string): Promise<boolean>;
2754
+ /**
2755
+ * Get a transformed version of an image
2756
+ * @param path - Storage path or key
2757
+ * @param options - Transform options
2758
+ */
2759
+ abstract transform(path: string, options: TransformOptions): Promise<Buffer>;
2760
+ /**
2761
+ * Move/rename a file
2762
+ * @param path - Current storage path or key
2763
+ * @param newPath - New storage path or key
2764
+ */
2765
+ move?(path: string, newPath: string): Promise<UploadResult>;
2766
+ /**
2767
+ * Copy a file
2768
+ * @param path - Source storage path or key
2769
+ * @param newPath - Destination storage path or key
2770
+ */
2771
+ copy?(path: string, newPath: string): Promise<UploadResult>;
2772
+ /**
2773
+ * Generate a signed URL for temporary access (for S3/R2)
2774
+ * @param path - Storage path or key
2775
+ * @param expiresIn - Expiration time in seconds
2776
+ */
2777
+ signedUrl?(path: string, expiresIn?: number): Promise<string>;
2778
+ }
2779
+
2780
+ type VaultAdapterType = 'db' | 'hashicorp' | 'supabase';
2781
+ /**
2782
+ * Token-based authentication for HashiCorp Vault.
2783
+ * Token is read from VAULT_TOKEN env var.
2784
+ */
2785
+ interface VaultTokenAuth {
2786
+ type: 'token';
2787
+ token: string;
2788
+ }
2789
+ /**
2790
+ * AppRole-based authentication for HashiCorp Vault.
2791
+ */
2792
+ interface VaultAppRoleAuth {
2793
+ type: 'appRole';
2794
+ roleId: string;
2795
+ secretId?: string;
2796
+ }
2797
+ type VaultAuthConfig = VaultTokenAuth | VaultAppRoleAuth;
2798
+ /**
2799
+ * Configuration for the HashiCorp Vault adapter.
2800
+ */
2801
+ interface HashiCorpVaultConfig {
2802
+ /** Vault server URL (e.g., 'https://vault.example.com:8200'). Defaults to VAULT_ADDR env var. */
2803
+ url?: string;
2804
+ /** Authentication configuration. Defaults to VAULT_TOKEN env var. */
2805
+ auth?: VaultAuthConfig;
2806
+ /** Secrets engine mount path (default: 'secret') */
2807
+ mountPath?: string;
2808
+ /** Vault Enterprise namespace (optional) */
2809
+ namespace?: string;
2810
+ }
2811
+ /**
2812
+ * Configuration for the Supabase Vault adapter.
2813
+ */
2814
+ interface SupabaseVaultConfig {
2815
+ /** Supabase project URL */
2816
+ supabaseUrl: string;
2817
+ /** Supabase service role key (required for vault operations) */
2818
+ supabaseServiceKey: string;
2819
+ }
2820
+ /**
2821
+ * Top-level vault configuration passed to MagnetModuleOptions.
2822
+ */
2823
+ interface VaultConfig {
2824
+ /** Vault adapter to use (default: 'db') */
2825
+ adapter?: VaultAdapterType;
2826
+ /** Cache TTL in seconds (default: 300) */
2827
+ cacheTtl?: number;
2828
+ /** HashiCorp Vault adapter configuration */
2829
+ hashicorp?: HashiCorpVaultConfig;
2830
+ /** Supabase Vault adapter configuration */
2831
+ supabase?: SupabaseVaultConfig;
2832
+ }
2833
+ /**
2834
+ * Abstract base class for vault adapters.
2835
+ *
2836
+ * All adapters encrypt secrets at rest. Implementations determine
2837
+ * the storage backend (app DB, HashiCorp Vault, Supabase Vault).
2838
+ */
2839
+ declare abstract class VaultAdapter {
2840
+ /**
2841
+ * Retrieve a secret value by key.
2842
+ * @returns The decrypted string value or null if not found
2843
+ */
2844
+ abstract get(key: string): Promise<string | null>;
2845
+ /**
2846
+ * Store or update a secret.
2847
+ *
2848
+ * @param key - Secret identifier
2849
+ * @param value - Plaintext value to encrypt and store
2850
+ * @param description - Optional human-readable description (stored unencrypted)
2851
+ */
2852
+ abstract set(key: string, value: string, description?: string): Promise<void>;
2853
+ /**
2854
+ * Delete a secret by key.
2855
+ */
2856
+ abstract delete(key: string): Promise<void>;
2857
+ /**
2858
+ * List all secrets with their metadata, optionally filtered by prefix.
2859
+ */
2860
+ abstract list(prefix?: string): Promise<VaultSecretMeta[]>;
2861
+ /**
2862
+ * Check the health of the vault backend.
2863
+ */
2864
+ abstract healthCheck(): Promise<boolean>;
2865
+ }
2866
+ /** Lightweight metadata returned by list() — does not include the secret value. */
2867
+ interface VaultSecretMeta {
2868
+ name: string;
2869
+ description?: string;
2870
+ /** ISO date string when the secret was last updated (adapter-dependent) */
2871
+ lastUpdated?: string;
2872
+ }
2873
+ interface VaultStatusResponse {
2874
+ /** Whether the vault adapter is healthy */
2875
+ healthy: boolean;
2876
+ /** The adapter type in use */
2877
+ adapter: VaultAdapterType;
2878
+ /** Whether VAULT_MASTER_KEY is set (db adapter only) */
2879
+ masterKeyConfigured?: boolean;
2880
+ }
2881
+ interface VaultSecretListResponse {
2882
+ secrets: VaultSecretMeta[];
2883
+ }
2884
+
2885
+ interface InternationalizationOptions {
2886
+ locales: string[];
2887
+ defaultLocale: string;
2888
+ }
2889
+ interface PlaygroundOptions {
2890
+ /**
2891
+ * Path to the directory where module folders will be created
2892
+ * @example './src/modules'
2893
+ */
2894
+ modulesPath?: string;
2895
+ /**
2896
+ * @deprecated Use modulesPath instead
2897
+ * Path to the directory where schema files will be saved
2898
+ */
2899
+ schemasPath?: string;
2900
+ }
2901
+ interface AdminConfig {
2902
+ /** Enable static admin serving (default true when omitted) */
2903
+ enabled?: boolean;
2904
+ /** Path to serve admin UI (default: '/admin') */
2905
+ path?: string;
2906
+ /** Custom path to admin dist folder */
2907
+ distPath?: string;
2908
+ }
2909
+ declare class MagnetModuleOptions {
2910
+ db: DBConfig;
2911
+ jwt: {
2912
+ secret: string;
2913
+ };
2914
+ /**
2915
+ * Auth configuration (optional, uses JWT by default)
2916
+ */
2917
+ auth?: AuthConfig;
2918
+ internationalization?: InternationalizationOptions;
2919
+ playground?: PlaygroundOptions;
2920
+ storage?: StorageConfig;
2921
+ /**
2922
+ * Email adapter configuration
2923
+ * @example
2924
+ * email: { adapter: 'nodemailer', nodemailer: { host: 'smtp.example.com', port: 587, auth: { user: 'user', pass: 'pass' } } }
2925
+ */
2926
+ email?: EmailConfig;
2927
+ /**
2928
+ * Plugins to load with the Magnet module
2929
+ */
2930
+ plugins?: PluginConfig[];
2931
+ /**
2932
+ * Admin panel configuration (enabled by default when omitted).
2933
+ * @example
2934
+ * // Explicit enable (same as omitting `admin`)
2935
+ * admin: true
2936
+ *
2937
+ * // Custom path
2938
+ * admin: { enabled: true, path: '/dashboard' }
2939
+ *
2940
+ * // Disable (for API-only mode)
2941
+ * admin: false
2942
+ */
2943
+ admin?: boolean | AdminConfig;
2944
+ /**
2945
+ * RBAC (Role-Based Access Control) configuration
2946
+ * @example
2947
+ * rbac: { enabled: true, defaultRole: 'authenticated' }
2948
+ */
2949
+ rbac?: RBACModuleOptions;
2950
+ /**
2951
+ * Vault configuration for encrypted secrets management.
2952
+ * Uses the built-in DB adapter by default (requires VAULT_MASTER_KEY env var).
2953
+ * @example
2954
+ * vault: { adapter: 'db' }
2955
+ * vault: { adapter: 'hashicorp', hashicorp: { url: 'https://vault.example.com:8200' } }
2956
+ */
2957
+ vault?: VaultConfig;
2958
+ constructor({ db, jwt, auth, internationalization, playground, storage, email, plugins, admin, rbac, vault, }: MagnetModuleOptions);
2959
+ }
2960
+ type MagnetModuleOptionsAsync = {
2961
+ useFactory: (...args: unknown[]) => Promise<MagnetModuleOptions> | MagnetModuleOptions;
2962
+ inject?: Type[];
2963
+ imports?: Type[];
2964
+ };
2965
+
2966
+ type DiscoveredController = {
2967
+ path: string;
2968
+ schema: string;
2969
+ methods: DiscoveredMethod[];
2970
+ };
2971
+ type DiscoveredMethod = {
2972
+ name: string;
2973
+ method: string;
2974
+ };
2975
+ type DiscoveredSchema = {
2976
+ name: string;
2977
+ tableName: string;
2978
+ target: string | object;
2979
+ };
2980
+
2981
+ type Validations = {
2982
+ type: string;
2983
+ name: string;
2984
+ constraints: unknown[];
2985
+ }[];
2986
+
2987
+ type VersionStatus = 'draft' | 'published' | 'archived';
2988
+ interface VersionConfig {
2989
+ /**
2990
+ * Maximum number of versions to keep per document
2991
+ */
2992
+ max?: number;
2993
+ /**
2994
+ * Enable drafts mode for this schema
2995
+ */
2996
+ drafts?: boolean | {
2997
+ /**
2998
+ * Auto-publish drafts after a certain time
2999
+ */
3000
+ autoPublish?: boolean;
3001
+ /**
3002
+ * Require approval before publishing
3003
+ */
3004
+ requireApproval?: boolean;
3005
+ };
3006
+ }
3007
+ interface VersionData<T> {
3008
+ /**
3009
+ * Document ID this version belongs to
3010
+ */
3011
+ documentId: string;
3012
+ /**
3013
+ * Version ID
3014
+ */
3015
+ versionId: string;
3016
+ /**
3017
+ * Version status
3018
+ */
3019
+ status: VersionStatus;
3020
+ /**
3021
+ * Version data
3022
+ */
3023
+ data: T;
3024
+ /**
3025
+ * Version created at
3026
+ */
3027
+ createdAt: Date;
3028
+ /**
3029
+ * Version created by
3030
+ */
3031
+ createdBy?: string;
3032
+ /**
3033
+ * Version notes
3034
+ */
3035
+ notes?: string;
3036
+ }
3037
+
3038
+ /** Log severity levels, ordered from most critical to most verbose. */
3039
+ type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'verbose';
3040
+ /** Structured metadata attached to a log entry. */
3041
+ interface LogMetadata {
3042
+ operation?: string;
3043
+ schema?: string;
3044
+ resourceId?: string;
3045
+ duration?: number;
3046
+ method?: string;
3047
+ path?: string;
3048
+ statusCode?: number;
3049
+ [key: string]: unknown;
615
3050
  }
616
- declare abstract class StorageAdapter {
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;
617
3109
  /**
618
- * Initialize the storage adapter (create directories, check connections)
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)
619
3113
  */
620
- abstract initialize(): Promise<void>;
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[];
621
3142
  /**
622
- * Upload a file to storage
623
- * @param file - File buffer or readable stream
624
- * @param originalFilename - Original filename from user
625
- * @param options - Upload options
3143
+ * Which channels to deliver to.
3144
+ * Defaults to `['platform']` when not specified.
626
3145
  */
627
- abstract upload(file: Buffer | Readable, originalFilename: string, options?: UploadOptions): Promise<UploadResult>;
3146
+ channels?: NotificationChannel[];
628
3147
  /**
629
- * Upload a file using chunked/streaming upload (for large files)
630
- * @param stream - Readable stream of the file
631
- * @param originalFilename - Original filename from user
632
- * @param totalSize - Total size of the file in bytes
633
- * @param options - Upload options
3148
+ * Notification category / type identifier.
3149
+ * Useful for filtering and icon mapping in the UI.
634
3150
  */
635
- abstract uploadChunked(stream: Readable, originalFilename: string, totalSize: number, options?: UploadOptions): Promise<UploadResult>;
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;
636
3158
  /**
637
- * Delete a file from storage
638
- * @param path - Storage path or key of the file
3159
+ * Additional arbitrary metadata stored with the notification.
3160
+ * Not shown directly in UI but available for custom logic.
639
3161
  */
640
- abstract delete(path: string): Promise<boolean>;
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 {
641
3219
  /**
642
- * Delete multiple files from storage
643
- * @param paths - Array of storage paths or keys
3220
+ * Unique identifier for this adapter
644
3221
  */
645
- abstract deleteMany(paths: string[]): Promise<{
646
- deleted: number;
647
- failed: string[];
648
- }>;
3222
+ abstract readonly name: string;
649
3223
  /**
650
- * Get the public URL for a file
651
- * @param path - Storage path or key
652
- * @param transform - Optional transform options for images
3224
+ * Retrieve a cached value by key.
3225
+ * @param key - Cache key
3226
+ * @returns The cached value, or null if not found or expired
653
3227
  */
654
- abstract getUrl(path: string, transform?: TransformOptions): string;
3228
+ abstract get<T>(key: string): Promise<T | null>;
655
3229
  /**
656
- * Get a readable stream for a file
657
- * @param path - Storage path or key
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.
658
3234
  */
659
- abstract getStream(path: string): Promise<Readable>;
3235
+ abstract set<T>(key: string, value: T, ttl?: number): Promise<void>;
660
3236
  /**
661
- * Get the file as a buffer
662
- * @param path - Storage path or key
3237
+ * Remove a value from the cache by key.
3238
+ * @param key - Cache key to delete
663
3239
  */
664
- abstract getBuffer(path: string): Promise<Buffer>;
3240
+ abstract delete(key: string): Promise<void>;
665
3241
  /**
666
- * Check if a file exists
667
- * @param path - Storage path or key
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
668
3245
  */
669
- abstract exists(path: string): Promise<boolean>;
3246
+ abstract deleteByPattern(pattern: string): Promise<void>;
670
3247
  /**
671
- * Get a transformed version of an image
672
- * @param path - Storage path or key
673
- * @param options - Transform options
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
674
3251
  */
675
- abstract transform(path: string, options: TransformOptions): Promise<Buffer>;
3252
+ abstract has(key: string): Promise<boolean>;
676
3253
  /**
677
- * Move/rename a file
678
- * @param path - Current storage path or key
679
- * @param newPath - New storage path or key
3254
+ * Remove all entries from the cache.
3255
+ * For Redis, only clears keys within this adapter's key prefix.
680
3256
  */
681
- move?(path: string, newPath: string): Promise<UploadResult>;
3257
+ abstract clear(): Promise<void>;
682
3258
  /**
683
- * Copy a file
684
- * @param path - Source storage path or key
685
- * @param newPath - Destination storage path or key
3259
+ * Check the health of the cache backend.
3260
+ * @returns Health result with status and optional message
686
3261
  */
687
- copy?(path: string, newPath: string): Promise<UploadResult>;
3262
+ abstract healthCheck(): Promise<CacheHealthResult>;
688
3263
  /**
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
3264
+ * Optional cleanup/disconnect method.
3265
+ * Called when the module is destroyed.
692
3266
  */
693
- signedUrl?(path: string, expiresIn?: number): Promise<string>;
3267
+ dispose?(): Promise<void>;
694
3268
  }
695
3269
 
696
- interface InternationalizationOptions {
697
- locales: string[];
698
- defaultLocale: string;
3270
+ /**
3271
+ * Declares a required or optional environment variable for an adapter/plugin.
3272
+ * Used by MagnetModule to validate all env vars before NestJS bootstraps.
3273
+ */
3274
+ interface EnvVarRequirement {
3275
+ /** Environment variable name (e.g., 'DATABASE_URL') */
3276
+ name: string;
3277
+ /** Whether this env var is required for the adapter to function */
3278
+ required: boolean;
3279
+ /** Human-readable description shown in error messages */
3280
+ description?: string;
699
3281
  }
700
- interface PlaygroundOptions {
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
+ };
701
3303
  /**
702
- * Path to the directory where module folders will be created
703
- * @example './src/modules'
3304
+ * Admin panel configuration (enabled by default when omitted).
3305
+ * @example admin: false // API-only / headless
3306
+ * @example admin: { enabled: true, path: '/dashboard' }
704
3307
  */
705
- modulesPath?: string;
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 {
706
3440
  /**
707
- * @deprecated Use modulesPath instead
708
- * Path to the directory where schema files will be saved
3441
+ * Whether to include timestamps (createdAt, updatedAt)
3442
+ * @default true
709
3443
  */
710
- schemasPath?: string;
3444
+ timestamps?: boolean;
711
3445
  }
712
- declare class MagnetModuleOptions {
713
- db: DBConfig;
714
- jwt: {
715
- secret: string;
716
- };
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: {
717
3540
  /**
718
- * Auth configuration (optional, uses JWT by default)
3541
+ * Text setting field
719
3542
  */
720
- auth?: AuthConfig;
721
- internationalization?: InternationalizationOptions;
722
- playground?: PlaygroundOptions;
723
- storage?: StorageConfig;
3543
+ readonly Text: (options: SettingTextOptions) => PropertyDecorator;
724
3544
  /**
725
- * Plugins to load with the Magnet module
3545
+ * Number setting field
726
3546
  */
727
- plugins?: PluginConfig[];
728
- constructor({ db, jwt, auth, internationalization, playground, storage, plugins, }: MagnetModuleOptions);
729
- }
730
- type MagnetModuleOptionsAsync = {
731
- useFactory: (...args: any[]) => Promise<MagnetModuleOptions> | MagnetModuleOptions;
732
- inject?: Type[];
733
- imports?: Type[];
734
- };
735
-
736
- type DiscoveredController = {
737
- path: string;
738
- schema: string;
739
- methods: DiscoveredMethod[];
740
- };
741
- type DiscoveredMethod = {
742
- name: string;
743
- method: string;
744
- };
745
- type DiscoveredSchema = {
746
- name: string;
747
- tableName: string;
748
- target: string | object;
749
- };
750
-
751
- type Validations = {
752
- type: string;
753
- name: string;
754
- constraints: any[];
755
- }[];
756
-
757
- type VersionStatus = 'draft' | 'published' | 'archived';
758
- interface VersionConfig {
3547
+ readonly Number: (options: SettingNumberOptions) => PropertyDecorator;
759
3548
  /**
760
- * Maximum number of versions to keep per document
3549
+ * Boolean setting field (toggle/switch)
761
3550
  */
762
- max?: number;
3551
+ readonly Boolean: (options: SettingBooleanOptions) => PropertyDecorator;
763
3552
  /**
764
- * Enable drafts mode for this schema
3553
+ * Select setting field (dropdown)
765
3554
  */
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> {
3555
+ readonly Select: (options: SettingSelectOptions) => PropertyDecorator;
778
3556
  /**
779
- * Document ID this version belongs to
3557
+ * Secret setting field (encrypted, masked in UI)
780
3558
  */
781
- documentId: string;
3559
+ readonly Secret: (options: SettingSecretOptions) => PropertyDecorator;
782
3560
  /**
783
- * Version ID
3561
+ * Image setting field
784
3562
  */
785
- versionId: string;
3563
+ readonly Image: (options: SettingImageOptions) => PropertyDecorator;
786
3564
  /**
787
- * Version status
3565
+ * JSON setting field
788
3566
  */
789
- status: VersionStatus;
3567
+ readonly JSON: (options: SettingJSONOptions) => PropertyDecorator;
790
3568
  /**
791
- * Version data
3569
+ * Textarea setting field (multi-line text)
792
3570
  */
793
- data: T;
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);
794
3691
  /**
795
- * Version created at
3692
+ * Convert to API response format
796
3693
  */
797
- createdAt: Date;
3694
+ toResponse(): ErrorResponse;
798
3695
  /**
799
- * Version created by
3696
+ * Convert to JSON for logging
800
3697
  */
801
- createdBy?: string;
3698
+ toJSON(): Record<string, unknown>;
3699
+ }
3700
+
3701
+ /**
3702
+ * Interface for class-validator error format
3703
+ */
3704
+ interface ClassValidatorError {
3705
+ property: string;
3706
+ value?: unknown;
3707
+ constraints?: Record<string, string>;
3708
+ children?: ClassValidatorError[];
3709
+ }
3710
+ /**
3711
+ * Validation error with field-level details
3712
+ * Used for form validation and input validation failures
3713
+ */
3714
+ declare class ValidationError extends MagnetError {
3715
+ readonly code = ErrorCode.VALIDATION_FAILED;
3716
+ readonly httpStatus = 400;
3717
+ readonly details: ValidationErrorDetail[];
3718
+ constructor(message: string, details: ValidationErrorDetail[], metadata?: ErrorMetadata);
802
3719
  /**
803
- * Version notes
3720
+ * Create ValidationError from class-validator errors
804
3721
  */
805
- notes?: string;
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);
806
3748
  }
807
3749
 
808
- type SettingType = 'string' | 'boolean' | 'number' | 'object' | 'array';
809
- interface SchemaSetting {
810
- key: string;
811
- value: unknown;
812
- type: SettingType | string;
3750
+ /**
3751
+ * Authentication required error
3752
+ * Thrown when a request requires authentication but none is provided
3753
+ */
3754
+ declare class AuthenticationRequiredError extends MagnetError {
3755
+ readonly code = ErrorCode.AUTHENTICATION_REQUIRED;
3756
+ readonly httpStatus = 401;
3757
+ constructor(message?: string, metadata?: ErrorMetadata);
813
3758
  }
814
- interface SettingsFeatureOptions<T = unknown> {
815
- group: string;
816
- schema: new () => T;
3759
+ /**
3760
+ * Invalid credentials error
3761
+ * Thrown when email/password combination is incorrect
3762
+ */
3763
+ declare class InvalidCredentialsError extends MagnetError {
3764
+ readonly code = ErrorCode.INVALID_CREDENTIALS;
3765
+ readonly httpStatus = 401;
3766
+ constructor(message?: string, metadata?: ErrorMetadata);
3767
+ }
3768
+ /**
3769
+ * Token expired error
3770
+ * Thrown when a JWT or refresh token has expired
3771
+ */
3772
+ declare class TokenExpiredError extends MagnetError {
3773
+ readonly code = ErrorCode.TOKEN_EXPIRED;
3774
+ readonly httpStatus = 401;
3775
+ constructor(message?: string, metadata?: ErrorMetadata);
3776
+ }
3777
+ /**
3778
+ * Token invalid error
3779
+ * Thrown when a JWT or refresh token is malformed or invalid
3780
+ */
3781
+ declare class TokenInvalidError extends MagnetError {
3782
+ readonly code = ErrorCode.TOKEN_INVALID;
3783
+ readonly httpStatus = 401;
3784
+ constructor(message?: string, metadata?: ErrorMetadata);
3785
+ }
3786
+ /**
3787
+ * Account locked error
3788
+ * Thrown when an account is temporarily locked due to too many failed attempts
3789
+ */
3790
+ declare class AccountLockedError extends MagnetError {
3791
+ readonly code = ErrorCode.ACCOUNT_LOCKED;
3792
+ readonly httpStatus = 423;
3793
+ readonly unlockAt?: Date;
3794
+ constructor(message?: string, unlockAt?: Date, metadata?: ErrorMetadata);
3795
+ }
3796
+ /**
3797
+ * Email not verified error
3798
+ * Thrown when an action requires email verification
3799
+ */
3800
+ declare class EmailNotVerifiedError extends MagnetError {
3801
+ readonly code = ErrorCode.EMAIL_NOT_VERIFIED;
3802
+ readonly httpStatus = 403;
3803
+ constructor(message?: string, metadata?: ErrorMetadata);
3804
+ }
3805
+ /**
3806
+ * Permission denied error
3807
+ * Thrown when user lacks permission for an action
3808
+ */
3809
+ declare class PermissionDeniedError extends MagnetError {
3810
+ readonly code = ErrorCode.PERMISSION_DENIED;
3811
+ readonly httpStatus = 403;
3812
+ readonly requiredPermission?: string;
3813
+ constructor(message?: string, requiredPermission?: string, metadata?: ErrorMetadata);
3814
+ }
3815
+ /**
3816
+ * Insufficient permissions error
3817
+ * Thrown when user has some permissions but not enough
3818
+ */
3819
+ declare class InsufficientPermissionsError extends MagnetError {
3820
+ readonly code = ErrorCode.INSUFFICIENT_PERMISSIONS;
3821
+ readonly httpStatus = 403;
3822
+ readonly requiredPermissions: string[];
3823
+ constructor(requiredPermissions: string[], metadata?: ErrorMetadata);
3824
+ }
3825
+ /**
3826
+ * Role not found error
3827
+ */
3828
+ declare class RoleNotFoundError extends MagnetError {
3829
+ readonly code = ErrorCode.ROLE_NOT_FOUND;
3830
+ readonly httpStatus = 404;
3831
+ constructor(roleName: string, metadata?: ErrorMetadata);
3832
+ }
3833
+ /**
3834
+ * Permission not found error
3835
+ * Thrown when assigning invalid permission IDs to a role
3836
+ */
3837
+ declare class PermissionNotFoundError extends MagnetError {
3838
+ readonly code = ErrorCode.PERMISSION_NOT_FOUND;
3839
+ readonly httpStatus = 400;
3840
+ readonly invalidPermissionIds: string[];
3841
+ constructor(invalidPermissionIds: string[], metadata?: ErrorMetadata);
817
3842
  }
818
3843
 
819
3844
  /**
820
- * Query operator types for MongoDB-style queries
3845
+ * Resource not found error
3846
+ * Generic error for any resource type
821
3847
  */
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
- };
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
+ }
846
3853
  /**
847
- * Filter value that supports both direct values and operators
3854
+ * Schema not found error
3855
+ * Thrown when a schema/collection doesn't exist
848
3856
  */
849
- type FilterValue<T> = T | QueryOperator<T>;
3857
+ declare class SchemaNotFoundError extends MagnetError {
3858
+ readonly code = ErrorCode.SCHEMA_NOT_FOUND;
3859
+ readonly httpStatus = 404;
3860
+ constructor(schemaName: string, metadata?: ErrorMetadata);
3861
+ }
850
3862
  /**
851
- * Full filter query type with logical operators
3863
+ * Document not found error
3864
+ * Thrown when a document doesn't exist in a schema
852
3865
  */
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
- };
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
+ }
863
3871
  /**
864
- * Sort direction
3872
+ * User not found error
865
3873
  */
866
- type SortDirection = 1 | -1 | 'asc' | 'desc';
3874
+ declare class UserNotFoundError extends MagnetError {
3875
+ readonly code = ErrorCode.USER_NOT_FOUND;
3876
+ readonly httpStatus = 404;
3877
+ constructor(identifier: string, metadata?: ErrorMetadata);
3878
+ }
867
3879
  /**
868
- * Sort specification
3880
+ * File not found error
869
3881
  */
870
- type SortQuery<Schema> = {
871
- [K in keyof Schema]?: SortDirection;
872
- };
3882
+ declare class FileNotFoundError extends MagnetError {
3883
+ readonly code = ErrorCode.FILE_NOT_FOUND;
3884
+ readonly httpStatus = 404;
3885
+ constructor(path: string, metadata?: ErrorMetadata);
3886
+ }
873
3887
  /**
874
- * Projection specification for field selection
3888
+ * Version not found error
3889
+ * Thrown when a specific version of a document doesn't exist
875
3890
  */
876
- type ProjectionQuery<Schema> = {
877
- [K in keyof Schema]?: 0 | 1 | boolean;
878
- };
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
+
879
3897
  /**
880
- * Query execution options
3898
+ * Generic database error
3899
+ * Used when a database operation fails
881
3900
  */
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;
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);
889
4000
  }
890
4001
  /**
891
- * Paginated query result
4002
+ * Webhook delivery error
892
4003
  */
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;
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);
902
4010
  }
903
4011
 
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
- };
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);
920
4020
  }
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
4021
  /**
927
- * Decorator to enable versioning for a schema
928
- * @param config Version configuration
4022
+ * Configuration error
4023
+ * Used when application configuration is invalid
929
4024
  */
930
- declare function Version(config?: VersionConfig): ClassDecorator;
931
-
932
- declare function InjectModel(model: Type): ParameterDecorator;
933
-
934
- declare class ValidationException extends Error {
935
- readonly errors: ValidationError[];
936
- constructor(errors: ValidationError[]);
4025
+ declare class ConfigurationError extends MagnetError {
4026
+ readonly code = ErrorCode.CONFIGURATION_ERROR;
4027
+ readonly httpStatus = 500;
4028
+ constructor(message: string, metadata?: ErrorMetadata);
937
4029
  }
938
-
939
4030
  /**
940
- * Abstract query builder for fluent database queries.
941
- * Provides chainable methods for filtering, sorting, pagination, and projection.
942
- *
943
- * @example
944
- * ```typescript
945
- * const users = await userModel.query()
946
- * .where({ status: 'active' })
947
- * .sort({ createdAt: -1 })
948
- * .limit(10)
949
- * .exec()
950
- * ```
4031
+ * Unexpected error
4032
+ * Used as a fallback for unknown error types
951
4033
  */
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;
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);
1019
4039
  }
1020
4040
 
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;
4041
+ /**
4042
+ * Context for error conversion
4043
+ */
4044
+ interface ErrorContext {
4045
+ schema?: string;
4046
+ operation?: string;
1086
4047
  }
4048
+ /**
4049
+ * Convert MongoDB/Mongoose errors to typed Magnet errors
4050
+ * @param error - The original error from Mongoose
4051
+ * @param context - Optional context about the operation
4052
+ * @returns A typed MagnetError
4053
+ */
4054
+ declare function fromMongooseError(error: unknown, context?: ErrorContext): MagnetError;
4055
+ /**
4056
+ * Convert Drizzle/PostgreSQL errors to typed Magnet errors
4057
+ * @param error - The original error from Drizzle
4058
+ * @param context - Optional context about the operation
4059
+ * @returns A typed MagnetError
4060
+ */
4061
+ declare function fromDrizzleError(error: unknown, context?: ErrorContext): MagnetError;
4062
+ /**
4063
+ * Check if an error is a MagnetError
4064
+ */
4065
+ declare function isMagnetError(error: unknown): error is MagnetError;
4066
+ /**
4067
+ * Wrap an unknown error as a MagnetError
4068
+ * If already a MagnetError, returns it unchanged
4069
+ */
4070
+ declare function wrapError(error: unknown, fallbackMessage?: string): MagnetError;
1087
4071
 
1088
- declare class Mixed {
1089
- static schemaName: 'Mixed';
1090
- defaultOptions: Record<string, any>;
4072
+ declare class ValidationException extends Error {
4073
+ readonly errors: ValidationError$1[];
4074
+ constructor(errors: ValidationError$1[]);
1091
4075
  }
1092
4076
 
1093
4077
  type SupportedAdapter = 'mongoose' | 'drizzle';
1094
4078
  /**
1095
4079
  * Detect the database adapter based on configuration or installed packages.
1096
4080
  *
4081
+ * With the provider-based API, the adapter is typically auto-registered:
4082
+ * importing `@magnet-cms/adapter-db-mongoose` or `@magnet-cms/adapter-db-drizzle`
4083
+ * calls `setDatabaseAdapter()` as a module-level side effect, so detection
4084
+ * happens automatically before any `@Schema()` decorators evaluate.
4085
+ *
1097
4086
  * 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)
4087
+ * 1. Cached value from `setDatabaseAdapter()` (set by adapter package import side effect)
4088
+ * 2. Configuration-based: If `connectionString` or `dialect` is present, use drizzle
4089
+ * 3. Configuration-based: If `uri` is present, use mongoose
4090
+ * 4. Package-based: Check which adapter packages are installed (mongoose > drizzle)
1101
4091
  *
1102
4092
  * @param dbConfig - Optional database configuration to determine adapter from
1103
4093
  */
1104
4094
  declare function detectDatabaseAdapter(dbConfig?: DBConfig): SupportedAdapter;
1105
4095
  /**
1106
4096
  * Explicitly set the database adapter.
1107
- * Call this at the very start of your application, before importing any schemas.
1108
4097
  *
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')
4098
+ * With the provider-based API, this is called automatically as a side effect
4099
+ * when importing an adapter package (e.g., `@magnet-cms/adapter-db-drizzle`).
4100
+ * Manual calls are no longer needed in typical user code.
1114
4101
  *
1115
- * // Then import your modules
1116
- * import { MagnetModule } from '@magnet-cms/core'
1117
- * ```
4102
+ * @internal Called by adapter package index.ts as module-level side effect
1118
4103
  */
1119
4104
  declare function setDatabaseAdapter(adapter: SupportedAdapter): void;
1120
4105
  /**
@@ -1122,11 +4107,140 @@ declare function setDatabaseAdapter(adapter: SupportedAdapter): void;
1122
4107
  */
1123
4108
  declare function clearAdapterCache(): void;
1124
4109
 
1125
- declare function getModelToken(schema: Type): string;
4110
+ /**
4111
+ * Get model injection token for any adapter
4112
+ * @param schema Schema class or schema name
4113
+ * @returns Injection token string
4114
+ */
4115
+ declare function getModelToken(schema: Type | string): string;
4116
+ /**
4117
+ * Get adapter injection token
4118
+ * @returns Injection token for the database adapter
4119
+ */
4120
+ declare function getAdapterToken(): string;
4121
+ declare function registerModel(token: string, model: unknown): void;
4122
+ declare function getRegisteredModel<T>(token: string): T | undefined;
1126
4123
 
1127
4124
  declare const getSchemaToken: (schema: Type) => string;
1128
4125
  declare const getSettingToken: (setting: Type) => string;
1129
4126
 
4127
+ /**
4128
+ * Type guard utilities for safe type narrowing
4129
+ * Use these instead of type assertions (as any, as unknown as T)
4130
+ */
4131
+ /**
4132
+ * Check if value is a non-null object
4133
+ */
4134
+ declare function isObject(value: unknown): value is Record<string, unknown>;
4135
+ /**
4136
+ * Check if value has a specific property
4137
+ */
4138
+ declare function hasProperty<K extends string>(value: unknown, key: K): value is Record<K, unknown>;
4139
+ /**
4140
+ * Check if value has multiple properties
4141
+ */
4142
+ declare function hasProperties<K extends string>(value: unknown, keys: K[]): value is Record<K, unknown>;
4143
+ /**
4144
+ * Check if value is a string
4145
+ */
4146
+ declare function isString(value: unknown): value is string;
4147
+ /**
4148
+ * Check if value is a number
4149
+ */
4150
+ declare function isNumber(value: unknown): value is number;
4151
+ /**
4152
+ * Check if value is a boolean
4153
+ */
4154
+ declare function isBoolean(value: unknown): value is boolean;
4155
+ /**
4156
+ * Check if value is an array
4157
+ */
4158
+ declare function isArray(value: unknown): value is unknown[];
4159
+ /**
4160
+ * Check if value is a string array
4161
+ */
4162
+ declare function isStringArray(value: unknown): value is string[];
4163
+ /**
4164
+ * Check if value is a function
4165
+ */
4166
+ declare function isFunction(value: unknown): value is Function;
4167
+ /**
4168
+ * Check if value is a valid document with ID
4169
+ */
4170
+ declare function isDocument(value: unknown): value is {
4171
+ id: string;
4172
+ [key: string]: unknown;
4173
+ };
4174
+ /**
4175
+ * Check if error is a MongoDB CastError
4176
+ */
4177
+ declare function isCastError(error: unknown): error is {
4178
+ name: 'CastError';
4179
+ path: string;
4180
+ value: unknown;
4181
+ };
4182
+ /**
4183
+ * Check if error is a MongoDB duplicate key error
4184
+ */
4185
+ declare function isDuplicateKeyError(error: unknown): error is {
4186
+ code: 11000;
4187
+ keyValue: Record<string, unknown>;
4188
+ };
4189
+ /**
4190
+ * Check if error is a Mongoose validation error
4191
+ */
4192
+ declare function isValidationError(error: unknown): error is {
4193
+ name: 'ValidationError';
4194
+ errors: Record<string, unknown>;
4195
+ };
4196
+ /**
4197
+ * Check if error is a PostgreSQL unique constraint violation
4198
+ */
4199
+ declare function isPostgresUniqueError(error: unknown): error is {
4200
+ code: string;
4201
+ detail?: string;
4202
+ };
4203
+ /**
4204
+ * Check if an object has a method
4205
+ */
4206
+ declare function hasMethod<K extends string>(value: unknown, methodName: K): value is Record<K, Function>;
4207
+ /**
4208
+ * Check if value has setLocale method (for i18n documents)
4209
+ */
4210
+ declare function hasSetLocale<T>(value: T): value is T & {
4211
+ setLocale: (locale: string) => T;
4212
+ };
4213
+ /**
4214
+ * Check if value has toString method that returns string
4215
+ */
4216
+ declare function hasToString(value: unknown): value is {
4217
+ toString(): string;
4218
+ };
4219
+ /**
4220
+ * Assert value is defined (not null or undefined)
4221
+ * Throws if value is null or undefined
4222
+ */
4223
+ declare function assertDefined<T>(value: T | null | undefined, message?: string): asserts value is T;
4224
+ /**
4225
+ * Assert condition is true
4226
+ * Throws if condition is false
4227
+ */
4228
+ declare function assert(condition: boolean, message?: string): asserts condition;
4229
+ /**
4230
+ * Safely get a string ID from a document that might have _id or id
4231
+ * Returns undefined if no valid ID found
4232
+ */
4233
+ declare function getDocumentId(doc: unknown): string | undefined;
4234
+ /**
4235
+ * Type guard for checking if an object matches a record with string values
4236
+ */
4237
+ declare function isStringRecord(value: unknown): value is Record<string, string>;
4238
+
4239
+ /**
4240
+ * Check if value is a version document
4241
+ */
4242
+ declare function isVersionDocument(value: unknown): value is VersionDocument;
4243
+
1130
4244
  declare const RESOLVER_METADATA_KEY = "magnet:resolver";
1131
4245
  declare const RESOLVE_METADATA_KEY = "magnet:resolve";
1132
4246
  declare const SCHEMA_METADATA_KEY = "magnet:schema";
@@ -1134,11 +4248,18 @@ declare const SCHEMA_OPTIONS_METADATA_KEY = "magnet:schema:options";
1134
4248
  declare const BASE_SCHEMA_METADATA_KEY = "magnet:schema:base";
1135
4249
  declare const PROP_METADATA_KEY = "magnet:schema:prop";
1136
4250
  declare const UI_METADATA_KEY = "magnet:schema:ui";
4251
+ declare const FIELD_METADATA_KEY = "magnet:schema:field";
1137
4252
  declare const SETTING_METADATA_KEY = "magnet:setting";
4253
+ declare const SETTINGS_OPTIONS_METADATA_KEY = "magnet:settings:options";
4254
+ declare const SETTING_FIELD_METADATA_KEY = "magnet:settings:field";
4255
+ declare const EXTEND_USER_METADATA_KEY = "magnet:extend:user";
4256
+ declare const PERMISSION_METADATA_KEY = "magnet:permission";
4257
+ declare const PERMISSION_OPTIONS_METADATA_KEY = "magnet:permission:options";
4258
+ declare const RESOLVED_PERMISSION_KEY = "magnet:permission:resolved";
1138
4259
  declare const INJECT_MODEL = "magnet:inject:model";
1139
4260
  declare const DESIGN_TYPE = "design:type";
1140
4261
  declare const DESIGN_META = "design:metadata";
1141
4262
  declare const DESIGN_PARAM_TYPES = "design:paramtypes";
1142
4263
  declare const DESIGN_RETURN_TYPE = "design:returntype";
1143
4264
 
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 };
4265
+ export { AccountLockedError, type AdapterCapabilities, type AdapterFeature, type AdapterName, type AddressFieldOptions, type AdminConfig, type ApiKeyEventPayload, type ArrayFieldOptions, type ArrayItemType, type AssignRoleDto, type AuthConfig, type AuthEventPayload, type AuthMagnetProvider, type AuthResult, AuthStrategy, type AuthUser, AuthenticationRequiredError, BASE_SCHEMA_METADATA_KEY, type BaseEventPayload, type BaseFieldOptions, type BaseSchema, type BaseSchemaOptions, type BlockTypeDefinition, type BlocksFieldOptions, type BooleanFieldOptions, CacheAdapter, type CacheHealthResult, type CacheMagnetProvider, type CategorizedPermissions, type CodeFieldOptions, type ColorFieldOptions, ConfigurationError, ConnectionFailedError, type ContentEventPayload, type ContentVersionEventPayload, type ControllerMetadata, type CreateRoleDto, type DBConfig, DESIGN_META, DESIGN_PARAM_TYPES, DESIGN_RETURN_TYPE, DESIGN_TYPE, DatabaseAdapter, DatabaseError, type DatabaseMagnetProvider, type DatabaseModelInstance, type DatabaseType, type DateFieldOptions, type DateTimeFieldOptions, type DiscoveredController, type DiscoveredMethod, type DiscoveredSchema, DocumentNotFoundError, type DrizzleConfig, DuplicateKeyError, type DuplicateRoleDto, EVENT_HANDLER_METADATA, EXTEND_USER_METADATA_KEY, EmailAdapter, type EmailAdapterName, type EmailAttachment, type EmailConfig, type EmailFieldOptions, type EmailMagnetProvider, EmailNotVerifiedError, EmailServiceError, type EnrichedPluginManifest, type EnumFieldOptions, type EnvVarRequirement, ErrorCode, type ErrorLogData, type ErrorMetadata, type ErrorResponse, type EventHandler, type EventHandlerMetadata, type EventHandlerOptions, type EventHistoryEntry, type EventName, type EventPayload, type EventPayloadMap, ExtendUser, type ExtendUserOptions, type ExternalAuthInfo, ExternalServiceError, FIELD_METADATA_KEY, type FailedLoginEventPayload, Field, type FieldChange, type FieldMetadata, type FieldNamespace, type FieldOptionsMap, type FieldTypeId, type FileFieldOptions, FileNotFoundError, type FilterQuery, type FilterValue, type GalleryFieldOptions, HasPermission, type HashiCorpVaultConfig, HookExecutionError, INJECT_MODEL, type ImageFieldOptions, type InitialConfig, InjectModel, InsufficientPermissionsError, InternalError, type InternationalizationOptions, InvalidCredentialsError, InvalidFormatError, type JSONFieldOptions, type JwtAuthConfig, type LocalStorageConfig, type LogEntry, type LogLevel, type LogMetadata, type LoggerConfig, type LoginCredentials, MagnetError, type MagnetGlobalOptions, MagnetModuleOptions, type MagnetModuleOptionsAsync, type MagnetProvider, type MarkdownFieldOptions, type MediaEventPayload, type MediaFolderEventPayload, type MediaQueryOptions, type MethodMetadata, Mixed, Model, type ModelClass, type ModelCreateOptions, type ModelUpdateOptions, type MongooseConfig, type NativeAccess, type NodemailerConfig, type NotificationChannel, type NotificationChannelAdapter, type NotificationChannelResult, type NotificationEventPayload, type NotificationQueryOptions, type NotificationRecipient, type NotificationSendPayload, type NotifyDto, type NumberFieldOptions, type ObjectFieldOptions, OnEvent, type OperationType, PERMISSION_METADATA_KEY, PERMISSION_OPTIONS_METADATA_KEY, PROP_METADATA_KEY, type PaginatedMedia, type PaginatedNotifications, type PaginatedResult, type PermissionCheckResult, type PermissionDefinition, PermissionDeniedError, type PermissionGroup, type PermissionItem, PermissionMeta, PermissionNotFoundError, type PermissionOptions, type PermissionSource, type PhoneFieldOptions, type PlaygroundOptions, type PluginConfig, PluginError, type PluginEventPayload, type PluginFrontendManifest, type PluginHook, PluginInitializationError, type PluginLifecycle, type PluginMagnetProvider, type PluginMetadata, type PluginModuleOptions, PluginNotFoundError, type PluginRouteDefinition, type PluginSettingsPage, type PluginSidebarItem, type ProjectionQuery, Prop, type PropDefaultValue, type PropOptions, QueryBuilder, QueryFailedError, type QueryOperator, type QueryOptions, type R2StorageConfig, type RBACModuleOptions, RESOLVED_PERMISSION_KEY, RESOLVER_METADATA_KEY, RESOLVE_METADATA_KEY, type RegisterData, type RegisteredHandler, type RegisteredPluginInfo, type RelationshipFieldOptions, RequirePermission, type RequiredEventHandlerOptions, RequiredFieldError, type ResendConfig, Resolve, type ResolveInput, type ResolveOptions, type ResolvedPermission, Resolver, type ResolverInput, type ResolverOptions, ResourceNotFoundError, type RichTextFieldOptions, type Role, type RoleAuditEntry, type RoleEventPayload, RoleNotFoundError, type RoleWithPermissions, type S3StorageConfig, SCHEMA_METADATA_KEY, SCHEMA_OPTIONS_METADATA_KEY, SETTINGS_OPTIONS_METADATA_KEY, SETTING_FIELD_METADATA_KEY, SETTING_METADATA_KEY, Schema, type SchemaIndexOption, type SchemaMetadata, SchemaNotFoundError, type SchemaOptions, type SchemaProperty, type SchemaPropertyValidation, type SchemaSetting, type SelectFieldOptions, type SelectOptionItem, type SendEmailOptions, type SendEmailResult, Setting, type SettingBooleanOptions, SettingField, type SettingFieldBaseOptions, type SettingFieldMetadata, type SettingFieldNamespace, type SettingFieldTypeId, type SettingImageOptions, type SettingJSONOptions, type SettingNumberOptions, type SettingObject, type SettingSecretOptions, type SettingSectionDefinition, type SettingSectionVariant, type SettingSelectOptions, type SettingTextOptions, type SettingType, type SettingValue, Settings, type SettingsBulkUpdatePayload, type SettingsDecoratorOptions, type SettingsEventPayload, type SettingsFeatureOptions, type SettingsGroupEventPayload, type SettingsRecord, type SettingsUpdatePayload, type SlugFieldOptions, type SortDirection, type SortQuery, StorageAdapter, type StorageConfig, StorageError, type StorageMagnetProvider, type SupabaseAuthConfig, type SupabaseStorageConfig, type SupabaseVaultConfig, type SupportedAdapter, type SystemEventPayload, type SystemRoleConfig, type SystemRoleName, type TagsFieldOptions, type TextFieldOptions, type TextareaFieldOptions, TokenExpiredError, TokenInvalidError, TransactionFailedError, type TransformOptions, UI, type UIBase, type UICombobox, type UIDecoratorOptions, type UIFieldMetadata, type UIMultiSelect, type UIPresentationFields, type UISelect, type UISelectItem, type UISide, type UITab, type UITable, type UITableColumn, type UITypes, UI_METADATA_KEY, type URLFieldOptions, UnexpectedError, type UpdatePermissionsDto, type UpdateRoleDto, type UploadOptions, type UploadResult, type UserEventPayload, UserNotFoundError, VERSION_METADATA_KEY, ValidationError, type ValidationErrorDetail, ValidationException, type Validations, Validators, ValueOutOfRangeError, VaultAdapter, type VaultAdapterType, type VaultAppRoleAuth, type VaultAuthConfig, type VaultConfig, type VaultMagnetProvider, type VaultSecretListResponse, type VaultSecretMeta, type VaultStatusResponse, type VaultTokenAuth, Version, type VersionConfig, type VersionData, type VersionDocument, VersionNotFoundError, type VersionStatus, WebhookDeliveryError, type WebhookDeliveryEventPayload, type WebhookEventPayload, assert, assertDefined, clearAdapterCache, createFieldDecorator, detectDatabaseAdapter, fromDrizzleError, fromMongooseError, getAdapterToken, getDocumentId, getExtendUserOptions, getFieldMetadata, getFieldMetadataForProperty, getModelToken, getPermissionMetadata, getRegisteredModel, getSchemaOptions, getSchemaToken, getSettingFields, getSettingToken, getSettingsOptions, hasMethod, hasPermissionDecorator, hasProperties, hasProperty, hasSetLocale, hasToString, isArray, isBoolean, isCastError, isDocument, isDuplicateKeyError, isFieldMetadata, isFunction, isMagnetError, isNumber, isObject, isPostgresUniqueError, isSettingFieldMetadata, isString, isStringArray, isStringRecord, isUserExtension, isValidFieldType, isValidationError, isVersionDocument, mapFieldTypeToProp, mapFieldTypeToUI, registerModel, setDatabaseAdapter, wrapError };