@magnet-cms/common 0.1.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.
@@ -0,0 +1,1065 @@
1
+ import { Type, DynamicModule, ValidationError } from '@nestjs/common';
2
+ import { Readable } from 'node:stream';
3
+
4
+ type ResolverOptions = {
5
+ schema: Type;
6
+ };
7
+ type ResolverInput = (() => Type) | ResolverOptions;
8
+ type ResolveOptions = {
9
+ type: Type | [Type];
10
+ isArray?: boolean;
11
+ description?: string;
12
+ };
13
+ type ResolveInput = (() => Type | [Type]) | ResolveOptions;
14
+ type PropOptions = {
15
+ type?: Type | [Type];
16
+ description?: string;
17
+ required?: boolean;
18
+ unique?: boolean;
19
+ default?: any;
20
+ nullable?: boolean;
21
+ intl?: boolean;
22
+ hidden?: boolean;
23
+ readonly?: boolean;
24
+ };
25
+ type BaseSchemaOptions = {
26
+ timestamps?: boolean;
27
+ };
28
+ type SchemaOptions = {
29
+ versioning?: boolean;
30
+ i18n?: boolean;
31
+ };
32
+
33
+ type UIPresentationFields = 'tab' | 'collapsible' | 'row' | 'customUI';
34
+ 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';
35
+ type UIBase = {
36
+ label?: string;
37
+ description?: string;
38
+ type?: UITypes;
39
+ row?: boolean;
40
+ collapsible?: boolean;
41
+ };
42
+ type UISelectItem = {
43
+ key: string;
44
+ value: string;
45
+ };
46
+ type UITab = UIBase & {
47
+ tab: string;
48
+ side?: never;
49
+ options?: UISelectItem[];
50
+ };
51
+ type UISide = UIBase & {
52
+ side: true;
53
+ tab?: never;
54
+ options?: UISelectItem[];
55
+ };
56
+ type UISelect = UIBase & {
57
+ type: 'select';
58
+ multi?: boolean;
59
+ options: UISelectItem[];
60
+ };
61
+ type UIMultiSelect = UIBase & {
62
+ type: 'multiSelect';
63
+ options: UISelectItem[];
64
+ };
65
+ type UICombobox = UIBase & {
66
+ type: 'combobox';
67
+ options: UISelectItem[];
68
+ };
69
+ type UITableColumn = {
70
+ key: string;
71
+ header: string;
72
+ type?: 'text' | 'badge' | 'status' | 'input' | 'code';
73
+ };
74
+ type UITable = UIBase & {
75
+ type: 'table';
76
+ columns?: UITableColumn[];
77
+ };
78
+ type UIDecoratorOptions = UITab | UISide | UISelect | UIMultiSelect | UICombobox | UITable;
79
+
80
+ type MethodMetadata = {
81
+ name: string;
82
+ returnType: {
83
+ type: Type;
84
+ isArray: boolean;
85
+ };
86
+ params: {
87
+ arg: string;
88
+ type: string;
89
+ name: string;
90
+ }[];
91
+ httpMethod: string;
92
+ routePath: string;
93
+ guards?: Type[];
94
+ interceptors?: Type[];
95
+ pipes?: Type[];
96
+ };
97
+ type ControllerMetadata = {
98
+ name: string;
99
+ basePath: string;
100
+ methods: MethodMetadata[];
101
+ };
102
+ type SchemaPropertyValidation = {
103
+ type: string;
104
+ name: string;
105
+ constraints: number[];
106
+ };
107
+ type SchemaProperty = {
108
+ name: string;
109
+ type: string;
110
+ isArray: boolean;
111
+ unique: boolean;
112
+ required: boolean;
113
+ validations: SchemaPropertyValidation[];
114
+ ui: UIDecoratorOptions;
115
+ };
116
+ type SchemaMetadata = {
117
+ name: string;
118
+ properties: SchemaProperty[];
119
+ options?: SchemaOptions;
120
+ };
121
+
122
+ type InitialConfig = {
123
+ title?: string;
124
+ description?: string;
125
+ env: string;
126
+ schemas: SchemaMetadata[];
127
+ settings: SchemaMetadata[];
128
+ };
129
+
130
+ /**
131
+ * Authenticated user returned from auth strategies
132
+ */
133
+ interface AuthUser {
134
+ /** Unique user identifier */
135
+ id: string;
136
+ /** User email address */
137
+ email: string;
138
+ /** User role for authorization */
139
+ role: string;
140
+ /** Optional additional user data */
141
+ [key: string]: unknown;
142
+ }
143
+ /**
144
+ * Login credentials (strategy-specific)
145
+ */
146
+ interface LoginCredentials {
147
+ email: string;
148
+ password: string;
149
+ [key: string]: unknown;
150
+ }
151
+ /**
152
+ * Registration data (strategy-specific)
153
+ */
154
+ interface RegisterData {
155
+ email: string;
156
+ password: string;
157
+ name: string;
158
+ role?: string;
159
+ [key: string]: unknown;
160
+ }
161
+ /**
162
+ * Authentication result containing access token
163
+ */
164
+ interface AuthResult {
165
+ access_token: string;
166
+ refresh_token?: string;
167
+ expires_in?: number;
168
+ token_type?: string;
169
+ }
170
+ /**
171
+ * JWT-specific configuration
172
+ */
173
+ interface JwtAuthConfig {
174
+ /** JWT secret key */
175
+ secret: string;
176
+ /** Token expiration time (e.g., '7d', '1h') */
177
+ expiresIn?: string;
178
+ }
179
+ /**
180
+ * Auth configuration for MagnetModuleOptions
181
+ */
182
+ interface AuthConfig {
183
+ /** Strategy name to use (default: 'jwt') */
184
+ strategy?: string;
185
+ /** JWT-specific configuration */
186
+ jwt?: JwtAuthConfig;
187
+ /** Allow extensible config for custom strategies */
188
+ [key: string]: unknown;
189
+ }
190
+ /**
191
+ * Abstract base class for authentication strategies.
192
+ * Similar to StorageAdapter, custom strategies must extend this class.
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * export class SupabaseAuthStrategy extends AuthStrategy {
197
+ * readonly name = 'supabase'
198
+ *
199
+ * async validate(payload: unknown): Promise<AuthUser | null> {
200
+ * // Validate Supabase JWT token
201
+ * }
202
+ *
203
+ * async login(credentials: LoginCredentials): Promise<AuthResult> {
204
+ * // Authenticate with Supabase
205
+ * }
206
+ *
207
+ * async register(data: RegisterData): Promise<AuthUser> {
208
+ * // Register user in Supabase
209
+ * }
210
+ *
211
+ * async validateCredentials(email: string, password: string): Promise<AuthUser | null> {
212
+ * // Validate user credentials
213
+ * }
214
+ * }
215
+ * ```
216
+ */
217
+ declare abstract class AuthStrategy {
218
+ /**
219
+ * Unique identifier for this strategy
220
+ */
221
+ abstract readonly name: string;
222
+ /**
223
+ * Initialize the auth strategy (optional setup)
224
+ */
225
+ initialize?(): Promise<void>;
226
+ /**
227
+ * Validate a token/payload and return the authenticated user
228
+ * @param payload - Strategy-specific payload (e.g., JWT payload, session data)
229
+ * @returns Authenticated user or null if invalid
230
+ */
231
+ abstract validate(payload: unknown): Promise<AuthUser | null>;
232
+ /**
233
+ * Authenticate user with credentials and return tokens
234
+ * @param credentials - Login credentials (strategy-specific)
235
+ * @returns Authentication result with access token
236
+ */
237
+ abstract login(credentials: LoginCredentials): Promise<AuthResult>;
238
+ /**
239
+ * Register a new user
240
+ * @param data - Registration data
241
+ * @returns The created user
242
+ */
243
+ abstract register(data: RegisterData): Promise<AuthUser>;
244
+ /**
245
+ * Validate user credentials (used internally by login)
246
+ * @param email - User email
247
+ * @param password - User password
248
+ * @returns User if valid, null otherwise
249
+ */
250
+ abstract validateCredentials(email: string, password: string): Promise<AuthUser | null>;
251
+ /**
252
+ * Refresh an access token (optional)
253
+ * @param refreshToken - The refresh token
254
+ * @returns New authentication result
255
+ */
256
+ refresh?(refreshToken: string): Promise<AuthResult>;
257
+ /**
258
+ * Logout/invalidate tokens (optional)
259
+ * @param token - The token to invalidate
260
+ */
261
+ logout?(token: string): Promise<void>;
262
+ /**
263
+ * Get the Passport strategy name (for guards)
264
+ * Returns the name to use with AuthGuard()
265
+ */
266
+ getPassportStrategyName(): string;
267
+ }
268
+
269
+ type MongooseConfig = {
270
+ uri: string;
271
+ };
272
+ type TypeORMConfig = {
273
+ type: 'mysql' | 'postgres' | 'sqlite' | 'mariadb' | 'mssql' | 'oracle' | 'mongodb';
274
+ host: string;
275
+ port: number;
276
+ username: string;
277
+ password: string;
278
+ database: string;
279
+ };
280
+ type DBConfig = MongooseConfig | TypeORMConfig;
281
+ declare abstract class DatabaseAdapter {
282
+ abstract connect(options: MagnetModuleOptions): DynamicModule;
283
+ abstract forFeature(schemas: Type | Type[]): DynamicModule;
284
+ abstract model<T>(modelInstance: any): any;
285
+ abstract token(schema: string): string;
286
+ }
287
+
288
+ /**
289
+ * Plugin metadata for the @Plugin decorator
290
+ */
291
+ interface PluginMetadata {
292
+ /** Unique plugin identifier (e.g., 'content-builder') */
293
+ name: string;
294
+ /** Human-readable description */
295
+ description?: string;
296
+ /** Semver version */
297
+ version?: string;
298
+ /** Plugin dependencies (other plugin names) */
299
+ dependencies?: string[];
300
+ /** NestJS module to be auto-imported (optional for backwards compatibility) */
301
+ module?: Type<unknown>;
302
+ }
303
+ /**
304
+ * Route definition for plugin frontend
305
+ */
306
+ interface PluginRouteDefinition {
307
+ /** Route path (e.g., '/playground') */
308
+ path: string;
309
+ /** Component identifier for lazy loading */
310
+ componentId: string;
311
+ /** Whether route requires authentication */
312
+ requiresAuth?: boolean;
313
+ /** Required permissions */
314
+ permissions?: string[];
315
+ /** Child routes */
316
+ children?: PluginRouteDefinition[];
317
+ }
318
+ /**
319
+ * Sidebar item for plugin frontend
320
+ */
321
+ interface PluginSidebarItem {
322
+ /** Unique identifier */
323
+ id: string;
324
+ /** Display title */
325
+ title: string;
326
+ /** Route path */
327
+ url: string;
328
+ /** Lucide icon name (e.g., 'Boxes', 'Settings') */
329
+ icon: string;
330
+ /** Position in sidebar (lower = higher) */
331
+ order?: number;
332
+ /** Child items */
333
+ items?: PluginSidebarItem[];
334
+ /** Badge to show */
335
+ badge?: string | number;
336
+ }
337
+ /**
338
+ * Settings page for plugin
339
+ */
340
+ interface PluginSettingsPage {
341
+ /** Settings group identifier */
342
+ groupId: string;
343
+ /** Display title */
344
+ title: string;
345
+ /** Description */
346
+ description?: string;
347
+ /** Component identifier */
348
+ componentId: string;
349
+ }
350
+ /**
351
+ * Frontend manifest exposed by plugins via discovery
352
+ */
353
+ interface PluginFrontendManifest {
354
+ /** Plugin identifier */
355
+ pluginName: string;
356
+ /** Routes to register */
357
+ routes?: PluginRouteDefinition[];
358
+ /** Sidebar items */
359
+ sidebar?: PluginSidebarItem[];
360
+ /** Settings pages */
361
+ settings?: PluginSettingsPage[];
362
+ /** Required permissions */
363
+ permissions?: string[];
364
+ }
365
+ /**
366
+ * Enriched plugin manifest with bundle URL for runtime loading
367
+ * Returned by GET /plugins/manifests endpoint
368
+ */
369
+ interface EnrichedPluginManifest extends PluginFrontendManifest {
370
+ /** URL to load the plugin's frontend bundle */
371
+ bundleUrl: string;
372
+ }
373
+ /**
374
+ * Plugin configuration passed to MagnetModule.forRoot
375
+ */
376
+ interface PluginConfig {
377
+ /** The plugin class decorated with @Plugin */
378
+ plugin: Type<unknown>;
379
+ /** Plugin-specific options */
380
+ options?: Record<string, unknown>;
381
+ /** Whether the plugin is enabled (default: true) */
382
+ enabled?: boolean;
383
+ }
384
+ /**
385
+ * Hook definition for plugins
386
+ */
387
+ interface PluginHook {
388
+ instance: unknown;
389
+ methodName: string | symbol;
390
+ }
391
+ /**
392
+ * Options for PluginModule.forRoot
393
+ */
394
+ interface PluginModuleOptions {
395
+ plugins: PluginConfig[];
396
+ }
397
+ /**
398
+ * Registered plugin info returned from API
399
+ */
400
+ interface RegisteredPluginInfo {
401
+ name: string;
402
+ description?: string;
403
+ version?: string;
404
+ dependencies?: string[];
405
+ frontend?: PluginFrontendManifest;
406
+ options?: Record<string, unknown>;
407
+ }
408
+ /**
409
+ * Interface for plugin lifecycle hooks.
410
+ * Plugins can implement these methods to respond to lifecycle events.
411
+ *
412
+ * @example
413
+ * ```ts
414
+ * @Plugin({ name: 'my-plugin', module: MyPluginModule })
415
+ * export class MyPlugin implements PluginLifecycle {
416
+ * onPluginInit() {
417
+ * console.log('Plugin initialized')
418
+ * }
419
+ *
420
+ * onPluginDestroy() {
421
+ * console.log('Plugin destroyed')
422
+ * }
423
+ * }
424
+ * ```
425
+ */
426
+ interface PluginLifecycle {
427
+ /**
428
+ * Called after the plugin module is initialized.
429
+ * Use this for plugin-specific setup that needs other services.
430
+ */
431
+ onPluginInit?(): void | Promise<void>;
432
+ /**
433
+ * Called when the application is shutting down.
434
+ * Use this for cleanup operations.
435
+ */
436
+ onPluginDestroy?(): void | Promise<void>;
437
+ }
438
+
439
+ interface LocalStorageConfig {
440
+ /** Directory where files will be stored (e.g., './uploads') */
441
+ uploadDir: string;
442
+ /** Public URL path for serving files (e.g., '/media') */
443
+ publicPath: string;
444
+ /** Maximum file size in bytes (default: 50MB) */
445
+ maxFileSize?: number;
446
+ /** Allowed MIME types (default: all) */
447
+ allowedMimeTypes?: string[];
448
+ }
449
+ interface S3StorageConfig {
450
+ /** S3 bucket name */
451
+ bucket: string;
452
+ /** AWS region */
453
+ region: string;
454
+ /** AWS access key ID */
455
+ accessKeyId: string;
456
+ /** AWS secret access key */
457
+ secretAccessKey: string;
458
+ /** Custom endpoint URL (for R2, MinIO, etc.) */
459
+ endpoint?: string;
460
+ /** Public URL for serving files (CDN URL) */
461
+ publicUrl?: string;
462
+ /** Enable path-style access (required for some S3-compatible services) */
463
+ forcePathStyle?: boolean;
464
+ }
465
+ interface R2StorageConfig extends S3StorageConfig {
466
+ /** Cloudflare account ID */
467
+ accountId: string;
468
+ }
469
+ interface StorageConfig {
470
+ /** Storage adapter to use */
471
+ adapter: 'local' | 's3' | 'r2';
472
+ /** Local storage configuration */
473
+ local?: LocalStorageConfig;
474
+ /** S3 storage configuration */
475
+ s3?: S3StorageConfig;
476
+ /** R2 storage configuration */
477
+ r2?: R2StorageConfig;
478
+ }
479
+ interface UploadOptions {
480
+ /** Target folder for the file */
481
+ folder?: string;
482
+ /** Custom filename (auto-generated if not provided) */
483
+ filename?: string;
484
+ /** MIME type of the file */
485
+ mimeType?: string;
486
+ /** Tags for categorization */
487
+ tags?: string[];
488
+ /** Alt text for accessibility */
489
+ alt?: string;
490
+ /** Custom metadata fields */
491
+ customFields?: Record<string, unknown>;
492
+ }
493
+ interface UploadResult {
494
+ /** Unique identifier for the uploaded file */
495
+ id: string;
496
+ /** Stored filename */
497
+ filename: string;
498
+ /** Original filename from user */
499
+ originalFilename: string;
500
+ /** MIME type of the file */
501
+ mimeType: string;
502
+ /** File size in bytes */
503
+ size: number;
504
+ /** Storage path or key */
505
+ path: string;
506
+ /** Public URL to access the file */
507
+ url: string;
508
+ /** Folder the file is stored in */
509
+ folder?: string;
510
+ /** Tags associated with the file */
511
+ tags?: string[];
512
+ /** Alt text for accessibility */
513
+ alt?: string;
514
+ /** Image width in pixels (for images only) */
515
+ width?: number;
516
+ /** Image height in pixels (for images only) */
517
+ height?: number;
518
+ /** Custom metadata fields */
519
+ customFields?: Record<string, unknown>;
520
+ /** Upload timestamp */
521
+ createdAt: Date;
522
+ /** Last update timestamp */
523
+ updatedAt: Date;
524
+ }
525
+ interface TransformOptions {
526
+ /** Target width in pixels */
527
+ width?: number;
528
+ /** Target height in pixels */
529
+ height?: number;
530
+ /** Resize fit mode */
531
+ fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
532
+ /** Output format */
533
+ format?: 'jpeg' | 'png' | 'webp' | 'avif';
534
+ /** Quality (1-100) */
535
+ quality?: number;
536
+ }
537
+ interface MediaQueryOptions {
538
+ /** Filter by folder */
539
+ folder?: string;
540
+ /** Filter by MIME type (supports partial match) */
541
+ mimeType?: string;
542
+ /** Filter by tags (any match) */
543
+ tags?: string[];
544
+ /** Search in filename and alt text */
545
+ search?: string;
546
+ /** Page number (1-indexed) */
547
+ page?: number;
548
+ /** Items per page */
549
+ limit?: number;
550
+ /** Sort field */
551
+ sortBy?: 'createdAt' | 'filename' | 'size';
552
+ /** Sort direction */
553
+ sortOrder?: 'asc' | 'desc';
554
+ }
555
+ interface PaginatedMedia<T = UploadResult> {
556
+ /** List of items */
557
+ items: T[];
558
+ /** Total number of items */
559
+ total: number;
560
+ /** Current page */
561
+ page: number;
562
+ /** Items per page */
563
+ limit: number;
564
+ /** Total number of pages */
565
+ totalPages: number;
566
+ }
567
+ declare abstract class StorageAdapter {
568
+ /**
569
+ * Initialize the storage adapter (create directories, check connections)
570
+ */
571
+ abstract initialize(): Promise<void>;
572
+ /**
573
+ * Upload a file to storage
574
+ * @param file - File buffer or readable stream
575
+ * @param originalFilename - Original filename from user
576
+ * @param options - Upload options
577
+ */
578
+ abstract upload(file: Buffer | Readable, originalFilename: string, options?: UploadOptions): Promise<UploadResult>;
579
+ /**
580
+ * Upload a file using chunked/streaming upload (for large files)
581
+ * @param stream - Readable stream of the file
582
+ * @param originalFilename - Original filename from user
583
+ * @param totalSize - Total size of the file in bytes
584
+ * @param options - Upload options
585
+ */
586
+ abstract uploadChunked(stream: Readable, originalFilename: string, totalSize: number, options?: UploadOptions): Promise<UploadResult>;
587
+ /**
588
+ * Delete a file from storage
589
+ * @param path - Storage path or key of the file
590
+ */
591
+ abstract delete(path: string): Promise<boolean>;
592
+ /**
593
+ * Delete multiple files from storage
594
+ * @param paths - Array of storage paths or keys
595
+ */
596
+ abstract deleteMany(paths: string[]): Promise<{
597
+ deleted: number;
598
+ failed: string[];
599
+ }>;
600
+ /**
601
+ * Get the public URL for a file
602
+ * @param path - Storage path or key
603
+ * @param transform - Optional transform options for images
604
+ */
605
+ abstract getUrl(path: string, transform?: TransformOptions): string;
606
+ /**
607
+ * Get a readable stream for a file
608
+ * @param path - Storage path or key
609
+ */
610
+ abstract getStream(path: string): Promise<Readable>;
611
+ /**
612
+ * Get the file as a buffer
613
+ * @param path - Storage path or key
614
+ */
615
+ abstract getBuffer(path: string): Promise<Buffer>;
616
+ /**
617
+ * Check if a file exists
618
+ * @param path - Storage path or key
619
+ */
620
+ abstract exists(path: string): Promise<boolean>;
621
+ /**
622
+ * Get a transformed version of an image
623
+ * @param path - Storage path or key
624
+ * @param options - Transform options
625
+ */
626
+ abstract transform(path: string, options: TransformOptions): Promise<Buffer>;
627
+ /**
628
+ * Move/rename a file
629
+ * @param path - Current storage path or key
630
+ * @param newPath - New storage path or key
631
+ */
632
+ move?(path: string, newPath: string): Promise<UploadResult>;
633
+ /**
634
+ * Copy a file
635
+ * @param path - Source storage path or key
636
+ * @param newPath - Destination storage path or key
637
+ */
638
+ copy?(path: string, newPath: string): Promise<UploadResult>;
639
+ /**
640
+ * Generate a signed URL for temporary access (for S3/R2)
641
+ * @param path - Storage path or key
642
+ * @param expiresIn - Expiration time in seconds
643
+ */
644
+ signedUrl?(path: string, expiresIn?: number): Promise<string>;
645
+ }
646
+
647
+ interface InternationalizationOptions {
648
+ locales: string[];
649
+ defaultLocale: string;
650
+ }
651
+ interface PlaygroundOptions {
652
+ /**
653
+ * Path to the directory where module folders will be created
654
+ * @example './src/modules'
655
+ */
656
+ modulesPath?: string;
657
+ /**
658
+ * @deprecated Use modulesPath instead
659
+ * Path to the directory where schema files will be saved
660
+ */
661
+ schemasPath?: string;
662
+ }
663
+ declare class MagnetModuleOptions {
664
+ db: DBConfig;
665
+ jwt: {
666
+ secret: string;
667
+ };
668
+ /**
669
+ * Auth configuration (optional, uses JWT by default)
670
+ */
671
+ auth?: AuthConfig;
672
+ internationalization?: InternationalizationOptions;
673
+ playground?: PlaygroundOptions;
674
+ storage?: StorageConfig;
675
+ /**
676
+ * Plugins to load with the Magnet module
677
+ */
678
+ plugins?: PluginConfig[];
679
+ constructor({ db, jwt, auth, internationalization, playground, storage, plugins, }: MagnetModuleOptions);
680
+ }
681
+ type MagnetModuleOptionsAsync = {
682
+ useFactory: (...args: any[]) => Promise<MagnetModuleOptions> | MagnetModuleOptions;
683
+ inject?: Type[];
684
+ imports?: Type[];
685
+ };
686
+
687
+ type DiscoveredController = {
688
+ path: string;
689
+ schema: string;
690
+ methods: DiscoveredMethod[];
691
+ };
692
+ type DiscoveredMethod = {
693
+ name: string;
694
+ method: string;
695
+ };
696
+ type DiscoveredSchema = {
697
+ name: string;
698
+ tableName: string;
699
+ target: string | object;
700
+ };
701
+
702
+ type Validations = {
703
+ type: string;
704
+ name: string;
705
+ constraints: any[];
706
+ }[];
707
+
708
+ type VersionStatus = 'draft' | 'published' | 'archived';
709
+ interface VersionConfig {
710
+ /**
711
+ * Maximum number of versions to keep per document
712
+ */
713
+ max?: number;
714
+ /**
715
+ * Enable drafts mode for this schema
716
+ */
717
+ drafts?: boolean | {
718
+ /**
719
+ * Auto-publish drafts after a certain time
720
+ */
721
+ autoPublish?: boolean;
722
+ /**
723
+ * Require approval before publishing
724
+ */
725
+ requireApproval?: boolean;
726
+ };
727
+ }
728
+ interface VersionData<T> {
729
+ /**
730
+ * Document ID this version belongs to
731
+ */
732
+ documentId: string;
733
+ /**
734
+ * Version ID
735
+ */
736
+ versionId: string;
737
+ /**
738
+ * Version status
739
+ */
740
+ status: VersionStatus;
741
+ /**
742
+ * Version data
743
+ */
744
+ data: T;
745
+ /**
746
+ * Version created at
747
+ */
748
+ createdAt: Date;
749
+ /**
750
+ * Version created by
751
+ */
752
+ createdBy?: string;
753
+ /**
754
+ * Version notes
755
+ */
756
+ notes?: string;
757
+ }
758
+
759
+ type SettingType = 'string' | 'boolean' | 'number' | 'object' | 'array';
760
+ interface SchemaSetting {
761
+ key: string;
762
+ value: unknown;
763
+ type: SettingType | string;
764
+ }
765
+ interface SettingsFeatureOptions<T = unknown> {
766
+ group: string;
767
+ schema: new () => T;
768
+ }
769
+
770
+ /**
771
+ * Query operator types for MongoDB-style queries
772
+ */
773
+ type QueryOperator<T> = {
774
+ /** Equal to */
775
+ $eq?: T;
776
+ /** Not equal to */
777
+ $ne?: T;
778
+ /** Greater than */
779
+ $gt?: T;
780
+ /** Greater than or equal to */
781
+ $gte?: T;
782
+ /** Less than */
783
+ $lt?: T;
784
+ /** Less than or equal to */
785
+ $lte?: T;
786
+ /** In array */
787
+ $in?: T[];
788
+ /** Not in array */
789
+ $nin?: T[];
790
+ /** Field exists */
791
+ $exists?: boolean;
792
+ /** Regular expression match */
793
+ $regex?: string | RegExp;
794
+ /** Regex options (i, m, s, x) */
795
+ $options?: string;
796
+ };
797
+ /**
798
+ * Filter value that supports both direct values and operators
799
+ */
800
+ type FilterValue<T> = T | QueryOperator<T>;
801
+ /**
802
+ * Full filter query type with logical operators
803
+ */
804
+ type FilterQuery<Schema> = {
805
+ [K in keyof Schema]?: FilterValue<Schema[K]>;
806
+ } & {
807
+ /** Logical AND */
808
+ $and?: FilterQuery<Schema>[];
809
+ /** Logical OR */
810
+ $or?: FilterQuery<Schema>[];
811
+ /** Logical NOR */
812
+ $nor?: FilterQuery<Schema>[];
813
+ };
814
+ /**
815
+ * Sort direction
816
+ */
817
+ type SortDirection = 1 | -1 | 'asc' | 'desc';
818
+ /**
819
+ * Sort specification
820
+ */
821
+ type SortQuery<Schema> = {
822
+ [K in keyof Schema]?: SortDirection;
823
+ };
824
+ /**
825
+ * Projection specification for field selection
826
+ */
827
+ type ProjectionQuery<Schema> = {
828
+ [K in keyof Schema]?: 0 | 1 | boolean;
829
+ };
830
+ /**
831
+ * Query execution options
832
+ */
833
+ interface QueryOptions {
834
+ /** Maximum number of documents to return */
835
+ limit?: number;
836
+ /** Number of documents to skip */
837
+ skip?: number;
838
+ /** Return plain objects instead of documents */
839
+ lean?: boolean;
840
+ }
841
+ /**
842
+ * Paginated query result
843
+ */
844
+ interface PaginatedResult<T> {
845
+ /** Result data */
846
+ data: T[];
847
+ /** Total count of matching documents */
848
+ total: number;
849
+ /** Current page (if using skip/limit) */
850
+ page?: number;
851
+ /** Page size */
852
+ limit?: number;
853
+ }
854
+
855
+ declare function Resolver(optionsOrFn: ResolverInput): ClassDecorator;
856
+
857
+ declare function Resolve(optionsOrFn: ResolveInput): MethodDecorator;
858
+
859
+ declare function Prop(options?: PropOptions): PropertyDecorator;
860
+
861
+ declare function Schema(options?: SchemaOptions): ClassDecorator;
862
+ declare function getSchemaOptions(target: Function): SchemaOptions;
863
+
864
+ declare function Setting(): ClassDecorator;
865
+
866
+ interface UIFieldMetadata {
867
+ propertyKey: string | symbol;
868
+ options: UIDecoratorOptions & {
869
+ designType: Function;
870
+ };
871
+ }
872
+ declare function UI(options: UIDecoratorOptions): PropertyDecorator;
873
+
874
+ declare const Validators: (...validators: PropertyDecorator[]) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
875
+
876
+ declare const VERSION_METADATA_KEY = "version:metadata";
877
+ /**
878
+ * Decorator to enable versioning for a schema
879
+ * @param config Version configuration
880
+ */
881
+ declare function Version(config?: VersionConfig): ClassDecorator;
882
+
883
+ declare function InjectModel(model: Type): ParameterDecorator;
884
+
885
+ declare class ValidationException extends Error {
886
+ readonly errors: ValidationError[];
887
+ constructor(errors: ValidationError[]);
888
+ }
889
+
890
+ /**
891
+ * Abstract query builder for fluent database queries.
892
+ * Provides chainable methods for filtering, sorting, pagination, and projection.
893
+ *
894
+ * @example
895
+ * ```typescript
896
+ * const users = await userModel.query()
897
+ * .where({ status: 'active' })
898
+ * .sort({ createdAt: -1 })
899
+ * .limit(10)
900
+ * .exec()
901
+ * ```
902
+ */
903
+ declare abstract class QueryBuilder<Schema> {
904
+ /**
905
+ * Add filter conditions to the query
906
+ * @param filter Filter conditions with optional operators
907
+ */
908
+ abstract where(filter: FilterQuery<Schema>): this;
909
+ /**
910
+ * Add additional AND conditions
911
+ * @param filter Filter conditions to AND with existing filters
912
+ */
913
+ abstract and(filter: FilterQuery<Schema>): this;
914
+ /**
915
+ * Add OR conditions
916
+ * @param filters Array of filter conditions for OR logic
917
+ */
918
+ abstract or(filters: FilterQuery<Schema>[]): this;
919
+ /**
920
+ * Sort results by specified fields
921
+ * @param sort Sort specification with field names and directions
922
+ */
923
+ abstract sort(sort: SortQuery<Schema>): this;
924
+ /**
925
+ * Limit the number of results
926
+ * @param count Maximum number of documents to return
927
+ */
928
+ abstract limit(count: number): this;
929
+ /**
930
+ * Skip a number of results (for pagination)
931
+ * @param count Number of documents to skip
932
+ */
933
+ abstract skip(count: number): this;
934
+ /**
935
+ * Select specific fields to return
936
+ * @param projection Field selection (1 to include, 0 to exclude)
937
+ */
938
+ abstract select(projection: ProjectionQuery<Schema>): this;
939
+ /**
940
+ * Execute the query and return all matching documents
941
+ */
942
+ abstract exec(): Promise<BaseSchema<Schema>[]>;
943
+ /**
944
+ * Execute the query and return a single document
945
+ */
946
+ abstract execOne(): Promise<BaseSchema<Schema> | null>;
947
+ /**
948
+ * Count matching documents without fetching them
949
+ */
950
+ abstract count(): Promise<number>;
951
+ /**
952
+ * Check if any matching documents exist
953
+ */
954
+ abstract exists(): Promise<boolean>;
955
+ /**
956
+ * Execute with pagination info
957
+ * @returns Data array with total count
958
+ */
959
+ abstract paginate(): Promise<PaginatedResult<BaseSchema<Schema>>>;
960
+ /**
961
+ * Set the locale for query results
962
+ * @param locale The locale to use
963
+ */
964
+ abstract locale(locale: string): this;
965
+ /**
966
+ * Set the version filter for query
967
+ * @param versionId The version ID or status
968
+ */
969
+ abstract version(versionId: string): this;
970
+ }
971
+
972
+ type BaseSchema<T> = {
973
+ id: string;
974
+ } & T;
975
+ interface ModelCreateOptions {
976
+ /** Skip database-level validation (useful for draft documents) */
977
+ skipValidation?: boolean;
978
+ }
979
+ interface ModelUpdateOptions {
980
+ /** Skip database-level validation (useful for draft documents) */
981
+ skipValidation?: boolean;
982
+ }
983
+ declare abstract class Model<Schema> {
984
+ abstract create(data: Partial<BaseSchema<Schema>>, options?: ModelCreateOptions): Promise<BaseSchema<Schema>>;
985
+ abstract find(): Promise<BaseSchema<Schema>[]>;
986
+ abstract findById(id: string): Promise<BaseSchema<Schema> | null>;
987
+ abstract findOne(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema> | null>;
988
+ abstract findMany(query: Partial<BaseSchema<Schema>>): Promise<BaseSchema<Schema>[]>;
989
+ abstract update(query: Partial<BaseSchema<Schema>>, data: Partial<BaseSchema<Schema>>, options?: ModelUpdateOptions): Promise<BaseSchema<Schema>>;
990
+ abstract delete(query: Partial<BaseSchema<Schema>>): Promise<boolean>;
991
+ /**
992
+ * Set the locale for subsequent operations
993
+ * @param locale The locale to use
994
+ */
995
+ abstract locale(locale: string): this;
996
+ /**
997
+ * Set the version for subsequent operations
998
+ * @param versionId The version ID or status ('draft', 'published', 'archived')
999
+ */
1000
+ version(versionId: string): this;
1001
+ /**
1002
+ * Find all versions of a document
1003
+ * @param documentId The document ID
1004
+ */
1005
+ findVersions(documentId: string): Promise<any[]>;
1006
+ /**
1007
+ * Find a specific version by ID
1008
+ * @param versionId The version ID
1009
+ */
1010
+ findVersionById(versionId: string): Promise<any | null>;
1011
+ /**
1012
+ * Restore a version
1013
+ * @param versionId The version ID to restore
1014
+ */
1015
+ restoreVersion(versionId: string): Promise<BaseSchema<Schema> | null>;
1016
+ /**
1017
+ * Create a query builder for advanced queries with sorting, pagination, and operators.
1018
+ * The query builder inherits the current locale/version context.
1019
+ *
1020
+ * @example
1021
+ * ```typescript
1022
+ * const results = await model.query()
1023
+ * .where({ status: 'active', age: { $gte: 18 } })
1024
+ * .sort({ createdAt: -1 })
1025
+ * .limit(10)
1026
+ * .exec()
1027
+ * ```
1028
+ */
1029
+ query(): QueryBuilder<Schema>;
1030
+ /**
1031
+ * Get access to the native database model/collection.
1032
+ * Use with caution - bypasses Magnet abstractions like locale and versioning.
1033
+ *
1034
+ * @returns The underlying database model (e.g., Mongoose Model)
1035
+ */
1036
+ native(): unknown;
1037
+ }
1038
+
1039
+ declare class Mixed {
1040
+ static schemaName: 'Mixed';
1041
+ defaultOptions: Record<string, any>;
1042
+ }
1043
+
1044
+ declare function detectDatabaseAdapter(): 'mongoose' | 'typeorm';
1045
+
1046
+ declare function getModelToken(schema: Type): string;
1047
+
1048
+ declare const getSchemaToken: (schema: Type) => string;
1049
+ declare const getSettingToken: (setting: Type) => string;
1050
+
1051
+ declare const RESOLVER_METADATA_KEY = "magnet:resolver";
1052
+ declare const RESOLVE_METADATA_KEY = "magnet:resolve";
1053
+ declare const SCHEMA_METADATA_KEY = "magnet:schema";
1054
+ declare const SCHEMA_OPTIONS_METADATA_KEY = "magnet:schema:options";
1055
+ declare const BASE_SCHEMA_METADATA_KEY = "magnet:schema:base";
1056
+ declare const PROP_METADATA_KEY = "magnet:schema:prop";
1057
+ declare const UI_METADATA_KEY = "magnet:schema:ui";
1058
+ declare const SETTING_METADATA_KEY = "magnet:setting";
1059
+ declare const INJECT_MODEL = "magnet:inject:model";
1060
+ declare const DESIGN_TYPE = "design:type";
1061
+ declare const DESIGN_META = "design:metadata";
1062
+ declare const DESIGN_PARAM_TYPES = "design:paramtypes";
1063
+ declare const DESIGN_RETURN_TYPE = "design:returntype";
1064
+
1065
+ export { type AuthConfig, type AuthResult, AuthStrategy, type AuthUser, BASE_SCHEMA_METADATA_KEY, type BaseSchema, type BaseSchemaOptions, type ControllerMetadata, type DBConfig, DESIGN_META, DESIGN_PARAM_TYPES, DESIGN_RETURN_TYPE, DESIGN_TYPE, DatabaseAdapter, type DiscoveredController, type DiscoveredMethod, type DiscoveredSchema, type EnrichedPluginManifest, type FilterQuery, type FilterValue, INJECT_MODEL, type InitialConfig, InjectModel, type InternationalizationOptions, type JwtAuthConfig, type LocalStorageConfig, type LoginCredentials, MagnetModuleOptions, type MagnetModuleOptionsAsync, type MediaQueryOptions, type MethodMetadata, Mixed, Model, type ModelCreateOptions, type ModelUpdateOptions, type MongooseConfig, PROP_METADATA_KEY, type PaginatedMedia, type PaginatedResult, type PlaygroundOptions, type PluginConfig, type PluginFrontendManifest, type PluginHook, type PluginLifecycle, type PluginMetadata, type PluginModuleOptions, type PluginRouteDefinition, type PluginSettingsPage, type PluginSidebarItem, type ProjectionQuery, Prop, type PropOptions, QueryBuilder, type QueryOperator, type QueryOptions, type R2StorageConfig, RESOLVER_METADATA_KEY, RESOLVE_METADATA_KEY, type RegisterData, type RegisteredPluginInfo, Resolve, type ResolveInput, type ResolveOptions, Resolver, type ResolverInput, type ResolverOptions, type S3StorageConfig, SCHEMA_METADATA_KEY, SCHEMA_OPTIONS_METADATA_KEY, SETTING_METADATA_KEY, Schema, type SchemaMetadata, type SchemaOptions, type SchemaProperty, type SchemaPropertyValidation, type SchemaSetting, Setting, type SettingType, type SettingsFeatureOptions, type SortDirection, type SortQuery, StorageAdapter, type StorageConfig, type TransformOptions, type TypeORMConfig, 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, detectDatabaseAdapter, getModelToken, getSchemaOptions, getSchemaToken, getSettingToken };