@magnet-cms/core 1.0.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,576 @@
1
+ import * as _magnet_cms_common from '@magnet-cms/common';
2
+ import { MagnetModuleOptions, AuthConfig, Model, AuthStrategy, AuthUser, LoginCredentials, AuthResult, RegisterData, StorageAdapter, LocalStorageConfig, UploadOptions, UploadResult, TransformOptions, StorageConfig, MediaQueryOptions, PaginatedMedia, PluginMetadata, PluginFrontendManifest, PluginConfig, EnrichedPluginManifest, RegisteredPluginInfo, PluginModuleOptions } from '@magnet-cms/common';
3
+ import * as _nestjs_common from '@nestjs/common';
4
+ import { DynamicModule, Type, OnModuleInit, OnApplicationShutdown } from '@nestjs/common';
5
+ import * as passport_jwt from 'passport-jwt';
6
+ import { Strategy } from 'passport-jwt';
7
+ import { Readable } from 'node:stream';
8
+ import { ModuleRef, ModulesContainer, DiscoveryService } from '@nestjs/core';
9
+
10
+ declare class MagnetModule {
11
+ static forRoot(options?: MagnetModuleOptions): DynamicModule;
12
+ static forFeature(schemas: Type | Type[]): Promise<DynamicModule>;
13
+ }
14
+
15
+ /**
16
+ * Injection token for the auth strategy
17
+ */
18
+ declare const AUTH_STRATEGY = "AUTH_STRATEGY";
19
+ /**
20
+ * Injection token for auth configuration
21
+ */
22
+ declare const AUTH_CONFIG = "AUTH_CONFIG";
23
+
24
+ declare class AuthModule {
25
+ /**
26
+ * Register the auth module with configuration.
27
+ * Supports dynamic strategy registration via config.
28
+ *
29
+ * @param authConfig - Auth configuration (optional, uses JWT by default)
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // Default JWT strategy
34
+ * AuthModule.forRoot()
35
+ *
36
+ * // Custom JWT config
37
+ * AuthModule.forRoot({
38
+ * strategy: 'jwt',
39
+ * jwt: { secret: 'my-secret', expiresIn: '24h' }
40
+ * })
41
+ *
42
+ * // Custom strategy (register before module init)
43
+ * AuthStrategyFactory.registerStrategy('custom', CustomAuthStrategy)
44
+ * AuthModule.forRoot({ strategy: 'custom' })
45
+ * ```
46
+ */
47
+ static forRoot(authConfig?: AuthConfig): DynamicModule;
48
+ }
49
+
50
+ declare class CreateUserDto {
51
+ email: string;
52
+ password: string;
53
+ name: string;
54
+ role?: string;
55
+ }
56
+
57
+ declare class User {
58
+ email: string;
59
+ password: string;
60
+ name: string;
61
+ role?: string;
62
+ hashPassword(): Promise<void>;
63
+ }
64
+
65
+ declare class UserService {
66
+ private readonly userModel;
67
+ constructor(userModel: Model<User>);
68
+ findAll(): Promise<_magnet_cms_common.BaseSchema<User>[]>;
69
+ findOne(query: Partial<User>): Promise<_magnet_cms_common.BaseSchema<User> | null>;
70
+ findOneById(id: string): Promise<_magnet_cms_common.BaseSchema<User> | null>;
71
+ create(userData: CreateUserDto): Promise<_magnet_cms_common.BaseSchema<User>>;
72
+ update(id: string, updateUserDto: Partial<CreateUserDto>): Promise<_magnet_cms_common.BaseSchema<User>>;
73
+ remove(id: string): Promise<boolean>;
74
+ }
75
+
76
+ type AuthStrategyConstructor = new (config: AuthConfig, userService: UserService) => AuthStrategy;
77
+ /**
78
+ * Factory for creating auth strategy instances based on configuration.
79
+ * The JWT strategy is built-in, while other strategies can be registered
80
+ * dynamically or loaded from separate packages.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Register a custom strategy before module initialization
85
+ * AuthStrategyFactory.registerStrategy('custom', CustomAuthStrategy)
86
+ *
87
+ * // Later, in MagnetModule.forRoot()
88
+ * MagnetModule.forRoot({
89
+ * auth: { strategy: 'custom', customOption: 'value' }
90
+ * })
91
+ * ```
92
+ */
93
+ declare class AuthStrategyFactory {
94
+ private static cachedStrategy;
95
+ private static cachedConfig;
96
+ private static customStrategies;
97
+ /**
98
+ * Register a custom auth strategy class.
99
+ * Call this before MagnetModule.forRoot() to register custom strategies.
100
+ *
101
+ * @param name - Unique name for the strategy (used in config.strategy)
102
+ * @param strategyClass - Class constructor implementing AuthStrategy
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * import { AuthStrategyFactory } from '@magnet-cms/core'
107
+ * import { SupabaseAuthStrategy } from './strategies/supabase-auth.strategy'
108
+ *
109
+ * AuthStrategyFactory.registerStrategy('supabase', SupabaseAuthStrategy)
110
+ * ```
111
+ */
112
+ static registerStrategy(name: string, strategyClass: AuthStrategyConstructor): void;
113
+ /**
114
+ * Get or create an auth strategy based on configuration.
115
+ * This is called internally by AuthModule.forRoot().
116
+ *
117
+ * @param config - Auth configuration
118
+ * @param userService - UserService for database operations
119
+ * @param jwtSecret - JWT secret (fallback from jwt.secret)
120
+ */
121
+ static getStrategy(config: AuthConfig | undefined, userService: UserService, jwtSecret: string): AuthStrategy;
122
+ /**
123
+ * Clear the cached strategy (useful for testing)
124
+ */
125
+ static clearCache(): void;
126
+ /**
127
+ * Clear all registered custom strategies (useful for testing)
128
+ */
129
+ static clearStrategies(): void;
130
+ /**
131
+ * Check if a strategy is registered
132
+ */
133
+ static hasStrategy(name: string): boolean;
134
+ /**
135
+ * Check if the provided config matches the cached config
136
+ */
137
+ private static configMatches;
138
+ }
139
+
140
+ declare const JwtAuthStrategy_base: new (...args: [opt: passport_jwt.StrategyOptionsWithRequest] | [opt: passport_jwt.StrategyOptionsWithoutRequest]) => Strategy & {
141
+ validate(...args: any[]): unknown;
142
+ };
143
+ /**
144
+ * JWT-based authentication strategy.
145
+ * This is the default strategy that maintains backward compatibility.
146
+ *
147
+ * Extends PassportStrategy for integration with NestJS guards,
148
+ * and implements AuthStrategy for the extensible auth system.
149
+ */
150
+ declare class JwtAuthStrategy extends JwtAuthStrategy_base implements AuthStrategy {
151
+ private readonly userService;
152
+ readonly name = "jwt";
153
+ private readonly secret;
154
+ private readonly expiresIn;
155
+ constructor(config: AuthConfig, userService: UserService);
156
+ /**
157
+ * Passport validate method - called by JwtAuthGuard after JWT is verified.
158
+ * Also satisfies the AuthStrategy.validate() interface.
159
+ */
160
+ validate(payload: {
161
+ sub: string;
162
+ email: string;
163
+ role: string;
164
+ } | unknown): Promise<AuthUser | null>;
165
+ /**
166
+ * Validate user credentials against the database
167
+ */
168
+ validateCredentials(email: string, password: string): Promise<AuthUser | null>;
169
+ /**
170
+ * Authenticate and return JWT token
171
+ */
172
+ login(credentials: LoginCredentials): Promise<AuthResult>;
173
+ /**
174
+ * Register a new user and return the user object
175
+ */
176
+ register(data: RegisterData): Promise<AuthUser>;
177
+ getPassportStrategyName(): string;
178
+ }
179
+
180
+ /**
181
+ * Media schema for storing file metadata.
182
+ * This entity has a custom UI (Media Library) and doesn't use the standard content manager.
183
+ */
184
+ declare class Media {
185
+ filename: string;
186
+ originalFilename: string;
187
+ mimeType: string;
188
+ size: number;
189
+ path: string;
190
+ url: string;
191
+ folder?: string;
192
+ tags?: string[];
193
+ alt?: string;
194
+ width?: number;
195
+ height?: number;
196
+ customFields?: Record<string, unknown>;
197
+ createdAt: Date;
198
+ updatedAt: Date;
199
+ createdBy?: string;
200
+ }
201
+
202
+ declare class LocalStorageAdapter implements StorageAdapter {
203
+ private readonly logger;
204
+ private readonly config;
205
+ private readonly cacheDir;
206
+ private sharp;
207
+ constructor(config: LocalStorageConfig);
208
+ initialize(): Promise<void>;
209
+ upload(file: Buffer | Readable, originalFilename: string, options?: UploadOptions): Promise<UploadResult>;
210
+ uploadChunked(stream: Readable, originalFilename: string, totalSize: number, options?: UploadOptions): Promise<UploadResult>;
211
+ delete(storagePath: string): Promise<boolean>;
212
+ deleteMany(paths: string[]): Promise<{
213
+ deleted: number;
214
+ failed: string[];
215
+ }>;
216
+ getUrl(storagePath: string, transform?: TransformOptions): string;
217
+ getStream(storagePath: string): Promise<Readable>;
218
+ getBuffer(storagePath: string): Promise<Buffer>;
219
+ exists(storagePath: string): Promise<boolean>;
220
+ transform(storagePath: string, options: TransformOptions): Promise<Buffer>;
221
+ /**
222
+ * Get the MIME type for a transformed image
223
+ */
224
+ getTransformMimeType(originalMimeType: string, transform?: TransformOptions): string;
225
+ private streamToBuffer;
226
+ private getMimeType;
227
+ private isImage;
228
+ private getCacheKey;
229
+ private deleteCachedTransforms;
230
+ private ensureFileExists;
231
+ private formatSize;
232
+ }
233
+
234
+ /**
235
+ * Factory for creating storage adapters based on configuration.
236
+ * The local adapter is built-in, while S3/R2 adapters are loaded dynamically
237
+ * from separate packages.
238
+ */
239
+ declare class StorageAdapterFactory {
240
+ private static cachedAdapter;
241
+ private static cachedConfig;
242
+ /**
243
+ * Get or create a storage adapter based on configuration.
244
+ * @param config - Storage configuration (optional, will use defaults if not provided)
245
+ */
246
+ static getAdapter(config?: StorageConfig): StorageAdapter;
247
+ /**
248
+ * Clear the cached adapter (useful for testing)
249
+ */
250
+ static clearCache(): void;
251
+ /**
252
+ * Detect the storage adapter to use based on environment variables
253
+ */
254
+ private static detectStorageAdapter;
255
+ /**
256
+ * Check if the provided config matches the cached config
257
+ */
258
+ private static configMatches;
259
+ }
260
+
261
+ declare class StorageService implements OnModuleInit {
262
+ private readonly adapter;
263
+ private readonly moduleRef;
264
+ private readonly logger;
265
+ private mediaModel;
266
+ constructor(adapter: StorageAdapter, moduleRef: ModuleRef);
267
+ onModuleInit(): Promise<void>;
268
+ /**
269
+ * Get the Media model lazily
270
+ */
271
+ private getMediaModel;
272
+ /**
273
+ * Upload a file and store its metadata
274
+ */
275
+ upload(file: Buffer | Readable, originalFilename: string, options?: UploadOptions & {
276
+ createdBy?: string;
277
+ }): Promise<Media>;
278
+ /**
279
+ * Upload multiple files
280
+ */
281
+ uploadMany(files: Array<{
282
+ file: Buffer | Readable;
283
+ originalFilename: string;
284
+ }>, options?: UploadOptions & {
285
+ createdBy?: string;
286
+ }): Promise<Media[]>;
287
+ /**
288
+ * Upload a large file using chunked/streaming upload
289
+ */
290
+ uploadChunked(stream: Readable, originalFilename: string, totalSize: number, options?: UploadOptions & {
291
+ createdBy?: string;
292
+ }): Promise<Media>;
293
+ /**
294
+ * Delete a media file and its metadata
295
+ */
296
+ delete(id: string): Promise<boolean>;
297
+ /**
298
+ * Delete multiple media files
299
+ */
300
+ deleteMany(ids: string[]): Promise<{
301
+ deleted: number;
302
+ failed: string[];
303
+ }>;
304
+ /**
305
+ * Find media by ID
306
+ */
307
+ findById(id: string): Promise<Media | null>;
308
+ /**
309
+ * Find media by filename
310
+ */
311
+ findByFilename(filename: string): Promise<Media | null>;
312
+ /**
313
+ * Find media by path
314
+ */
315
+ findByPath(path: string): Promise<Media | null>;
316
+ /**
317
+ * List media with pagination and filtering
318
+ */
319
+ list(options?: MediaQueryOptions): Promise<PaginatedMedia<Media>>;
320
+ /**
321
+ * Update media metadata
322
+ */
323
+ update(id: string, data: Partial<Pick<Media, 'alt' | 'tags' | 'folder' | 'customFields'>>): Promise<Media | null>;
324
+ /**
325
+ * Get the public URL for a media file
326
+ */
327
+ getUrl(media: Media, transform?: TransformOptions): string;
328
+ /**
329
+ * Get a readable stream for a media file
330
+ */
331
+ getStream(id: string): Promise<Readable>;
332
+ /**
333
+ * Get a media file as a buffer
334
+ */
335
+ getBuffer(id: string): Promise<Buffer>;
336
+ /**
337
+ * Get a transformed version of an image
338
+ */
339
+ transform(id: string, options: TransformOptions): Promise<Buffer>;
340
+ /**
341
+ * Check if a media file exists
342
+ */
343
+ exists(id: string): Promise<boolean>;
344
+ /**
345
+ * Get all unique folders
346
+ */
347
+ getFolders(): Promise<string[]>;
348
+ /**
349
+ * Get all unique tags
350
+ */
351
+ getTags(): Promise<string[]>;
352
+ /**
353
+ * Get storage statistics
354
+ */
355
+ getStats(): Promise<{
356
+ totalFiles: number;
357
+ totalSize: number;
358
+ byMimeType: Record<string, {
359
+ count: number;
360
+ size: number;
361
+ }>;
362
+ }>;
363
+ }
364
+
365
+ /**
366
+ * Injection token for the storage adapter
367
+ */
368
+ declare const STORAGE_ADAPTER = "STORAGE_ADAPTER";
369
+ /**
370
+ * Injection token for storage configuration
371
+ */
372
+ declare const STORAGE_CONFIG = "STORAGE_CONFIG";
373
+
374
+ declare class StorageModule {
375
+ /**
376
+ * Register the storage module with configuration
377
+ * @param config - Storage configuration (optional, uses defaults if not provided)
378
+ */
379
+ static forRoot(config?: StorageConfig): DynamicModule;
380
+ }
381
+
382
+ declare const PLUGIN_METADATA = "plugin:metadata";
383
+ declare const PLUGIN_FRONTEND_MANIFEST = "plugin:frontend:manifest";
384
+ declare const PLUGIN_MODULE = "plugin:module";
385
+ /**
386
+ * Generate standardized options token for a plugin.
387
+ *
388
+ * @param pluginName - The plugin name (e.g., 'content-builder')
389
+ * @returns Token string (e.g., 'PLUGIN_CONTENT_BUILDER_OPTIONS')
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * getPluginOptionsToken('content-builder') // 'PLUGIN_CONTENT_BUILDER_OPTIONS'
394
+ * ```
395
+ */
396
+ declare function getPluginOptionsToken(pluginName: string): string;
397
+
398
+ declare function Hook(hookName: string): MethodDecorator;
399
+
400
+ /**
401
+ * Decorator to inject plugin options using standardized token.
402
+ *
403
+ * @param pluginName - The plugin name (e.g., 'content-builder')
404
+ *
405
+ * @example
406
+ * ```ts
407
+ * @Injectable()
408
+ * export class MyService {
409
+ * constructor(
410
+ * @InjectPluginOptions('content-builder')
411
+ * private readonly options: ContentBuilderOptions
412
+ * ) {}
413
+ * }
414
+ * ```
415
+ */
416
+ declare function InjectPluginOptions(pluginName: string): ParameterDecorator;
417
+
418
+ interface PluginDecoratorOptions extends Omit<PluginMetadata, 'module'> {
419
+ /** Frontend manifest for this plugin */
420
+ frontend?: Omit<PluginFrontendManifest, 'pluginName'>;
421
+ /** NestJS module containing controllers/services (auto-imported) */
422
+ module?: Type<unknown>;
423
+ }
424
+ /**
425
+ * Decorator to mark a class as a Magnet plugin
426
+ *
427
+ * @example
428
+ * ```ts
429
+ * @Plugin({
430
+ * name: 'content-builder',
431
+ * description: 'Visual schema builder',
432
+ * version: '1.0.0',
433
+ * frontend: {
434
+ * routes: [{ path: 'playground', componentId: 'PlaygroundIndex' }],
435
+ * sidebar: [{ id: 'playground', title: 'Playground', url: '/playground', icon: 'Boxes' }]
436
+ * }
437
+ * })
438
+ * export class ContentBuilderPlugin {}
439
+ * ```
440
+ */
441
+ declare function Plugin(options: PluginDecoratorOptions): ClassDecorator;
442
+
443
+ interface RegisteredPlugin {
444
+ metadata: PluginMetadata;
445
+ instance: unknown;
446
+ frontendManifest?: PluginFrontendManifest;
447
+ config: PluginConfig;
448
+ }
449
+ declare class PluginRegistryService implements OnModuleInit {
450
+ private readonly modulesContainer;
451
+ private readonly pluginsConfig;
452
+ private plugins;
453
+ constructor(modulesContainer: ModulesContainer, pluginsConfig?: PluginConfig[]);
454
+ onModuleInit(): void;
455
+ private discoverPlugins;
456
+ /**
457
+ * Get a registered plugin by name
458
+ */
459
+ getPlugin(name: string): RegisteredPlugin | undefined;
460
+ /**
461
+ * Get all registered plugins
462
+ */
463
+ getAllPlugins(): RegisteredPlugin[];
464
+ /**
465
+ * Get metadata for all plugins
466
+ */
467
+ getPluginMetadata(): PluginMetadata[];
468
+ /**
469
+ * Get frontend manifests for all plugins with bundle URLs
470
+ * Used by admin UI to dynamically load plugin frontends at runtime
471
+ */
472
+ getFrontendManifests(): EnrichedPluginManifest[];
473
+ /**
474
+ * Get full plugin info for API response
475
+ */
476
+ getPluginInfo(name: string): RegisteredPluginInfo | null;
477
+ /**
478
+ * Get all plugins info for API response
479
+ */
480
+ getAllPluginsInfo(): RegisteredPluginInfo[];
481
+ }
482
+
483
+ declare class PluginController {
484
+ private readonly registry;
485
+ constructor(registry: PluginRegistryService);
486
+ /**
487
+ * Get all registered plugins
488
+ * GET /plugins
489
+ */
490
+ getPlugins(): _magnet_cms_common.RegisteredPluginInfo[];
491
+ /**
492
+ * Get all frontend manifests for registered plugins
493
+ * Used by admin UI to discover plugin routes and sidebar items
494
+ * GET /plugins/manifests
495
+ */
496
+ getFrontendManifests(): _magnet_cms_common.EnrichedPluginManifest[];
497
+ /**
498
+ * Get plugin details by name
499
+ * GET /plugins/:name
500
+ */
501
+ getPlugin(name: string): _magnet_cms_common.RegisteredPluginInfo | {
502
+ error: string;
503
+ };
504
+ }
505
+
506
+ /**
507
+ * @deprecated Use PluginModuleOptions instead
508
+ */
509
+ interface PluginOptions$1 {
510
+ plugins: Type[];
511
+ }
512
+ declare class PluginModule {
513
+ /**
514
+ * Configure the plugin module with plugin configurations
515
+ *
516
+ * @example
517
+ * ```ts
518
+ * PluginModule.forRoot({
519
+ * plugins: [
520
+ * { plugin: ContentBuilderPlugin, options: { modulesPath: './src/modules' } },
521
+ * { plugin: SeoPlugin, enabled: true }
522
+ * ]
523
+ * })
524
+ * ```
525
+ */
526
+ static forRoot(options?: PluginModuleOptions): DynamicModule;
527
+ /**
528
+ * @deprecated Use forRoot with PluginConfig[] instead
529
+ */
530
+ static forRootLegacy(options?: PluginOptions$1): DynamicModule;
531
+ /**
532
+ * Collect NestJS modules from plugins that define them
533
+ */
534
+ private static collectPluginModules;
535
+ private static createPluginProviders;
536
+ }
537
+
538
+ /**
539
+ * Service that manages plugin lifecycle hooks.
540
+ * Calls onPluginInit() after module initialization and onPluginDestroy() on shutdown.
541
+ */
542
+ declare class PluginLifecycleService implements OnModuleInit, OnApplicationShutdown {
543
+ private readonly modulesContainer;
544
+ private readonly pluginsConfig;
545
+ private readonly logger;
546
+ private pluginInstances;
547
+ constructor(modulesContainer: ModulesContainer, pluginsConfig?: PluginConfig[]);
548
+ onModuleInit(): Promise<void>;
549
+ onApplicationShutdown(): Promise<void>;
550
+ }
551
+
552
+ /**
553
+ * @deprecated Use PluginModuleOptions instead
554
+ */
555
+ interface PluginOptions {
556
+ plugins: Type[];
557
+ }
558
+ declare class PluginService implements OnModuleInit {
559
+ private readonly discovery;
560
+ private readonly modulesContainer;
561
+ private readonly options;
562
+ private plugins;
563
+ private hooks;
564
+ constructor(discovery: DiscoveryService, modulesContainer: ModulesContainer, options: PluginOptions);
565
+ onModuleInit(): void;
566
+ private discoverPlugins;
567
+ private discoverHooks;
568
+ getPlugin(name: string): any;
569
+ getAllPlugins(): Map<string, any>;
570
+ executeHook(hookName: string, ...args: any[]): Promise<any[]>;
571
+ }
572
+
573
+ declare const IS_RESTRICTED_ROUTE = "IS_RESTRICTED_ROUTE";
574
+ declare function RestrictedRoute(): _nestjs_common.CustomDecorator<string>;
575
+
576
+ export { AUTH_CONFIG, AUTH_STRATEGY, AuthModule, AuthStrategyFactory, Hook, IS_RESTRICTED_ROUTE, InjectPluginOptions, JwtAuthStrategy, LocalStorageAdapter, MagnetModule, Media, PLUGIN_FRONTEND_MANIFEST, PLUGIN_METADATA, PLUGIN_MODULE, Plugin, PluginController, type PluginDecoratorOptions, PluginLifecycleService, PluginModule, PluginRegistryService, PluginService, RestrictedRoute, STORAGE_ADAPTER, STORAGE_CONFIG, StorageAdapterFactory, StorageModule, StorageService, getPluginOptionsToken };