@momentumcms/core 0.0.1

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,459 @@
1
+ /**
2
+ * Seeding Types
3
+ *
4
+ * Type definitions for the Momentum CMS seeding system.
5
+ * Provides declarative data seeding with strict typing and IntelliSense support.
6
+ */
7
+ /**
8
+ * Options for individual seed entity creation.
9
+ */
10
+ export interface SeedEntityOptions {
11
+ /**
12
+ * What to do if seedId already exists.
13
+ * - 'skip': Don't touch existing seed (default)
14
+ * - 'update': Update if data changed (checksum differs)
15
+ * - 'error': Throw SeedConflictError
16
+ */
17
+ onConflict?: 'skip' | 'update' | 'error';
18
+ /**
19
+ * Skip lifecycle hooks during seeding.
20
+ * Useful for performance when seeding large amounts of data.
21
+ * @default false
22
+ */
23
+ skipHooks?: boolean;
24
+ /**
25
+ * Skip access control checks during seeding.
26
+ * Seeds typically run in a trusted context.
27
+ * @default true
28
+ */
29
+ skipAccessControl?: boolean;
30
+ /**
31
+ * Use Better Auth signup API to create this user with proper password hashing.
32
+ * When true, the seed executor will call auth.api.signUpEmail() instead of
33
+ * directly inserting into the database, ensuring passwords are properly hashed
34
+ * and account/session tables are populated.
35
+ *
36
+ * Requires `auth` to be passed to `initializeMomentum` options.
37
+ * Only applies to new seeds (not on 'skip' or 'update').
38
+ * @default false
39
+ */
40
+ useAuthSignup?: boolean;
41
+ }
42
+ /**
43
+ * Represents a single seed entity definition.
44
+ */
45
+ export interface SeedEntity<TData = Record<string, unknown>> {
46
+ /**
47
+ * Unique identifier for this seed (user-provided).
48
+ * Used to track seeding state and prevent duplicates.
49
+ */
50
+ seedId: string;
51
+ /**
52
+ * Collection slug to seed into.
53
+ */
54
+ collection: string;
55
+ /**
56
+ * Data to create in the collection.
57
+ */
58
+ data: TData;
59
+ /**
60
+ * Options for this specific entity.
61
+ */
62
+ options?: SeedEntityOptions;
63
+ }
64
+ /**
65
+ * Result of a seeded document operation.
66
+ */
67
+ export interface SeededDocument<T = Record<string, unknown>> {
68
+ /**
69
+ * The actual document ID in the database.
70
+ */
71
+ id: string;
72
+ /**
73
+ * The seed ID used to create/identify this seed.
74
+ */
75
+ seedId: string;
76
+ /**
77
+ * Collection slug where the document was seeded.
78
+ */
79
+ collection: string;
80
+ /**
81
+ * The created/updated document data.
82
+ */
83
+ data: T;
84
+ /**
85
+ * What action was taken.
86
+ * - 'created': New document was created
87
+ * - 'updated': Existing document was updated (data changed)
88
+ * - 'skipped': Document already exists and was not modified
89
+ */
90
+ action: 'created' | 'updated' | 'skipped';
91
+ }
92
+ /**
93
+ * Typed seed builder for a specific collection.
94
+ * Provides IntelliSense for collection document types.
95
+ */
96
+ export interface CollectionSeedBuilder<TDoc> {
97
+ /**
98
+ * Create a seed entity for this collection.
99
+ *
100
+ * @param seedId - Unique identifier for this seed
101
+ * @param data - Document data to seed
102
+ * @param options - Optional seed options
103
+ * @returns Seed entity definition
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * collection<PostDoc>('posts').create('welcome-post', {
108
+ * title: 'Welcome!',
109
+ * status: 'published',
110
+ * });
111
+ * ```
112
+ */
113
+ create: (seedId: string, data: Partial<TDoc>, options?: SeedEntityOptions) => SeedEntity<TDoc>;
114
+ }
115
+ /**
116
+ * User seed data structure for Better Auth user table.
117
+ */
118
+ export interface UserSeedData {
119
+ /**
120
+ * User's display name.
121
+ */
122
+ name: string;
123
+ /**
124
+ * User's email address (unique).
125
+ */
126
+ email: string;
127
+ /**
128
+ * User's role.
129
+ * @default 'user'
130
+ */
131
+ role?: string;
132
+ /**
133
+ * Whether email is verified.
134
+ * @default false
135
+ */
136
+ emailVerified?: boolean;
137
+ /**
138
+ * User's avatar image URL.
139
+ */
140
+ image?: string;
141
+ }
142
+ /**
143
+ * Admin seed data structure for the first admin user.
144
+ * Same as UserSeedData but with admin-friendly defaults.
145
+ */
146
+ export interface AdminSeedData {
147
+ /**
148
+ * Admin's display name.
149
+ */
150
+ name: string;
151
+ /**
152
+ * Admin's email address (unique).
153
+ */
154
+ email: string;
155
+ /**
156
+ * Admin's role.
157
+ * @default 'admin'
158
+ */
159
+ role?: string;
160
+ /**
161
+ * Whether email is verified.
162
+ * @default true (admins are typically pre-verified)
163
+ */
164
+ emailVerified?: boolean;
165
+ /**
166
+ * Admin's avatar image URL.
167
+ */
168
+ image?: string;
169
+ }
170
+ /**
171
+ * Auth user seed data structure.
172
+ * Seeds directly into the Better Auth user table (auth-user collection).
173
+ * When used with the `authUser()` helper, the seed executor calls
174
+ * Better Auth's signUpEmail API to ensure proper password hashing.
175
+ */
176
+ export interface AuthUserSeedData {
177
+ [key: string]: unknown;
178
+ /**
179
+ * User's display name.
180
+ */
181
+ name: string;
182
+ /**
183
+ * User's email address (unique).
184
+ */
185
+ email: string;
186
+ /**
187
+ * User's password (will be hashed by Better Auth).
188
+ * Must be at least 8 characters.
189
+ */
190
+ password: string;
191
+ /**
192
+ * User's role.
193
+ * @default 'user'
194
+ */
195
+ role?: string;
196
+ /**
197
+ * Whether email is verified.
198
+ * @default true
199
+ */
200
+ emailVerified?: boolean;
201
+ }
202
+ /**
203
+ * Default entity helpers with IntelliSense support.
204
+ * Used in the `defaults` function of SeedingConfig.
205
+ */
206
+ export interface DefaultEntityHelpers {
207
+ /**
208
+ * Create the first admin user seed entity.
209
+ * Pre-configured with admin role and verified email.
210
+ *
211
+ * @param seedId - Unique identifier for this seed
212
+ * @param data - Admin user data
213
+ * @param options - Optional seed options
214
+ * @returns Seed entity definition
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * admin('first-admin', {
219
+ * name: 'System Admin',
220
+ * email: 'admin@example.com',
221
+ * });
222
+ * ```
223
+ */
224
+ admin: (seedId: string, data: AdminSeedData, options?: SeedEntityOptions) => SeedEntity<UserSeedData>;
225
+ /**
226
+ * Create a user seed entity for the Better Auth user table.
227
+ *
228
+ * @param seedId - Unique identifier for this seed
229
+ * @param data - User data
230
+ * @param options - Optional seed options
231
+ * @returns Seed entity definition
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * user('regular-user', {
236
+ * name: 'John Doe',
237
+ * email: 'john@example.com',
238
+ * });
239
+ * ```
240
+ */
241
+ user: (seedId: string, data: UserSeedData, options?: SeedEntityOptions) => SeedEntity<UserSeedData>;
242
+ /**
243
+ * Create a typed collection seed builder.
244
+ * Provides full IntelliSense for the document type.
245
+ *
246
+ * @param slug - Collection slug
247
+ * @returns Collection seed builder
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * collection<PostDoc>('posts').create('welcome', {
252
+ * title: 'Welcome!',
253
+ * status: 'published',
254
+ * });
255
+ * ```
256
+ */
257
+ collection: <TDoc>(slug: string) => CollectionSeedBuilder<TDoc>;
258
+ /**
259
+ * Create an auth user seed entity with password support.
260
+ * Seeds into the Better Auth user table using signUpEmail API
261
+ * to ensure proper password hashing and account creation.
262
+ *
263
+ * @param seedId - Unique identifier for this seed
264
+ * @param data - User data including password
265
+ * @param options - Optional seed options
266
+ * @returns Seed entity definition with useAuthSignup enabled
267
+ *
268
+ * @example
269
+ * ```typescript
270
+ * authUser('admin-user', {
271
+ * name: 'Admin',
272
+ * email: 'admin@example.com',
273
+ * password: 'SecurePass123!',
274
+ * role: 'admin',
275
+ * });
276
+ * ```
277
+ */
278
+ authUser: (seedId: string, data: AuthUserSeedData, options?: SeedEntityOptions) => SeedEntity<AuthUserSeedData>;
279
+ }
280
+ /**
281
+ * Context passed to the custom seed function.
282
+ * Provides utilities for seeding with dependency resolution.
283
+ */
284
+ export interface SeedContext {
285
+ /**
286
+ * Get a previously seeded document by its seedId.
287
+ * Useful for creating relationships between seeded entities.
288
+ *
289
+ * @param seedId - The seed ID to look up
290
+ * @returns The seeded document or null if not found
291
+ *
292
+ * @example
293
+ * ```typescript
294
+ * const adminUser = await ctx.getSeeded('admin-user');
295
+ * await ctx.seed({
296
+ * seedId: 'admin-post',
297
+ * collection: 'posts',
298
+ * data: { authorId: adminUser?.id },
299
+ * });
300
+ * ```
301
+ */
302
+ getSeeded: <T = Record<string, unknown>>(seedId: string) => Promise<SeededDocument<T> | null>;
303
+ /**
304
+ * Create a seed entity with tracking.
305
+ *
306
+ * @param entity - Seed entity definition
307
+ * @returns The seeded document result
308
+ */
309
+ seed: <T = Record<string, unknown>>(entity: SeedEntity<T>) => Promise<SeededDocument<T>>;
310
+ /**
311
+ * Log a message (respects quiet mode).
312
+ *
313
+ * @param message - Message to log
314
+ */
315
+ log: (message: string) => void;
316
+ }
317
+ /**
318
+ * Global seeding options.
319
+ */
320
+ export interface SeedingOptions {
321
+ /**
322
+ * Default conflict handling strategy.
323
+ * @default 'skip'
324
+ */
325
+ onConflict?: 'skip' | 'update' | 'error';
326
+ /**
327
+ * When to run seeding on server start.
328
+ * - 'development': Only when NODE_ENV is 'development'
329
+ * - 'always': Run on every server start
330
+ * - true: Same as 'always'
331
+ * - false: Never run automatically (can be triggered via CLI)
332
+ * @default 'development'
333
+ */
334
+ runOnStart?: boolean | 'development' | 'always';
335
+ /**
336
+ * Suppress seeding log messages.
337
+ * @default false
338
+ */
339
+ quiet?: boolean;
340
+ }
341
+ /**
342
+ * Seeding configuration for Momentum CMS.
343
+ */
344
+ export interface SeedingConfig {
345
+ /**
346
+ * Default entities to seed first (in order).
347
+ * Uses helper methods for strict typing.
348
+ *
349
+ * @param helpers - Typed entity helpers
350
+ * @returns Array of seed entities to create
351
+ *
352
+ * @example
353
+ * ```typescript
354
+ * defaults: ({ user, collection }) => [
355
+ * user('admin', { name: 'Admin', email: 'admin@example.com', role: 'admin' }),
356
+ * collection<PostDoc>('posts').create('welcome', { title: 'Welcome!' }),
357
+ * ]
358
+ * ```
359
+ */
360
+ defaults?: (helpers: DefaultEntityHelpers) => SeedEntity[];
361
+ /**
362
+ * Custom seed function for complex scenarios.
363
+ * Runs after defaults, has access to SeedContext for dependency resolution.
364
+ *
365
+ * @param ctx - Seeding context with utilities
366
+ *
367
+ * @example
368
+ * ```typescript
369
+ * seed: async (ctx) => {
370
+ * const admin = await ctx.getSeeded('admin');
371
+ * await ctx.seed({
372
+ * seedId: 'admin-post',
373
+ * collection: 'posts',
374
+ * data: { authorId: admin?.id },
375
+ * });
376
+ * }
377
+ * ```
378
+ */
379
+ seed?: (ctx: SeedContext) => Promise<void>;
380
+ /**
381
+ * Global seeding options.
382
+ */
383
+ options?: SeedingOptions;
384
+ }
385
+ /**
386
+ * Internal seed tracking document stored in _momentum_seeds table.
387
+ */
388
+ export interface SeedTrackingDocument {
389
+ /**
390
+ * Primary key (auto-generated UUID).
391
+ */
392
+ id: string;
393
+ /**
394
+ * User-provided unique seed identifier.
395
+ */
396
+ seedId: string;
397
+ /**
398
+ * Collection the entity was seeded into.
399
+ */
400
+ collection: string;
401
+ /**
402
+ * Actual document ID in the target collection.
403
+ */
404
+ documentId: string;
405
+ /**
406
+ * SHA-256 hash of the seed data for change detection.
407
+ */
408
+ checksum: string;
409
+ /**
410
+ * When this seed was first created.
411
+ */
412
+ createdAt: string;
413
+ /**
414
+ * When this seed was last updated.
415
+ */
416
+ updatedAt: string;
417
+ }
418
+ /**
419
+ * Slug for the internal seed tracking collection.
420
+ */
421
+ export declare const SEED_TRACKING_COLLECTION_SLUG = "_momentum_seeds";
422
+ /**
423
+ * Error thrown when a seed conflict occurs with onConflict: 'error'.
424
+ */
425
+ export declare class SeedConflictError extends Error {
426
+ readonly seedId: string;
427
+ readonly collection: string;
428
+ constructor(seedId: string, collection: string);
429
+ }
430
+ /**
431
+ * Information about a seed that was rolled back.
432
+ */
433
+ export interface RolledBackSeed {
434
+ /** The seed ID that was rolled back */
435
+ seedId: string;
436
+ /** The collection the seed was in */
437
+ collection: string;
438
+ /** The document ID that was deleted */
439
+ documentId: string;
440
+ }
441
+ /**
442
+ * Error thrown when seeding fails and rollback is performed.
443
+ * Contains the original error and information about rolled back seeds.
444
+ */
445
+ export declare class SeedRollbackError extends Error {
446
+ /** The original error that caused the rollback */
447
+ readonly originalError: Error;
448
+ /** Seeds that were successfully rolled back */
449
+ readonly rolledBackSeeds: RolledBackSeed[];
450
+ /** Seeds that failed to roll back */
451
+ readonly rollbackFailures: Array<{
452
+ seed: RolledBackSeed;
453
+ error: Error;
454
+ }>;
455
+ constructor(originalError: Error, rolledBackSeeds: RolledBackSeed[], rollbackFailures: Array<{
456
+ seed: RolledBackSeed;
457
+ error: Error;
458
+ }>);
459
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Storage module for Momentum CMS
3
+ * Defines interfaces for file storage adapters
4
+ */
5
+ /**
6
+ * Represents an uploaded file before storage.
7
+ */
8
+ export interface UploadedFile {
9
+ /** Original filename */
10
+ originalName: string;
11
+ /** MIME type of the file */
12
+ mimeType: string;
13
+ /** File size in bytes */
14
+ size: number;
15
+ /** File content as Buffer */
16
+ buffer: Buffer;
17
+ }
18
+ /**
19
+ * Represents a file after it has been stored.
20
+ */
21
+ export interface StoredFile {
22
+ /** Storage path/key */
23
+ path: string;
24
+ /** Public URL to access the file */
25
+ url: string;
26
+ /** Stored filename */
27
+ filename: string;
28
+ /** MIME type */
29
+ mimeType: string;
30
+ /** File size in bytes */
31
+ size: number;
32
+ }
33
+ /**
34
+ * Options for upload operations.
35
+ */
36
+ export interface UploadOptions {
37
+ /** Custom filename (without extension) */
38
+ filename?: string;
39
+ /** Subdirectory within the upload directory */
40
+ directory?: string;
41
+ }
42
+ /**
43
+ * Storage adapter interface.
44
+ * Implement this interface to create custom storage backends.
45
+ */
46
+ export interface StorageAdapter {
47
+ /**
48
+ * Upload a file to storage.
49
+ * @param file - The file to upload
50
+ * @param options - Upload options
51
+ * @returns The stored file metadata
52
+ */
53
+ upload(file: UploadedFile, options?: UploadOptions): Promise<StoredFile>;
54
+ /**
55
+ * Delete a file from storage.
56
+ * @param path - The storage path/key of the file
57
+ * @returns True if deleted, false if not found
58
+ */
59
+ delete(path: string): Promise<boolean>;
60
+ /**
61
+ * Get the public URL for a stored file.
62
+ * @param path - The storage path/key
63
+ * @returns The public URL
64
+ */
65
+ getUrl(path: string): string;
66
+ /**
67
+ * Check if a file exists in storage.
68
+ * @param path - The storage path/key
69
+ * @returns True if exists
70
+ */
71
+ exists(path: string): Promise<boolean>;
72
+ /**
73
+ * Get a signed URL for temporary access (optional).
74
+ * Useful for private S3 buckets.
75
+ * @param path - The storage path/key
76
+ * @param expiresIn - Expiration time in seconds
77
+ * @returns The signed URL
78
+ */
79
+ getSignedUrl?(path: string, expiresIn?: number): Promise<string>;
80
+ /**
81
+ * Read a file from storage.
82
+ * @param path - The storage path/key
83
+ * @returns The file as a Buffer, or null if not found
84
+ */
85
+ read?(path: string): Promise<Buffer | null>;
86
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Versions module
3
+ * Types and utilities for document versioning and drafts
4
+ */
5
+ export * from './version.types';
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Version Types for Momentum CMS
3
+ * Defines the structure of document versions and drafts
4
+ */
5
+ /**
6
+ * Status of a document in a versioned collection.
7
+ * - 'draft': Document is not publicly visible
8
+ * - 'published': Document is publicly visible
9
+ */
10
+ export type DocumentStatus = 'draft' | 'published';
11
+ /**
12
+ * Represents a single version of a document.
13
+ * Each version stores a full snapshot of the document at a point in time.
14
+ */
15
+ export interface DocumentVersion {
16
+ /** Unique identifier for this version */
17
+ id: string;
18
+ /** ID of the parent document this version belongs to */
19
+ parent: string;
20
+ /** Full document snapshot stored as JSON string */
21
+ version: string;
22
+ /** Status of this version */
23
+ _status: DocumentStatus;
24
+ /** Whether this version was created by autosave */
25
+ autosave: boolean;
26
+ /** When this version was published (if published) */
27
+ publishedAt?: string;
28
+ /** When this version was created */
29
+ createdAt: string;
30
+ /** When this version was last updated */
31
+ updatedAt: string;
32
+ }
33
+ /**
34
+ * Document version with parsed version data.
35
+ * Used when returning versions to the API layer.
36
+ */
37
+ export interface DocumentVersionParsed<T = Record<string, unknown>> {
38
+ /** Unique identifier for this version */
39
+ id: string;
40
+ /** ID of the parent document this version belongs to */
41
+ parent: string;
42
+ /** Parsed document snapshot */
43
+ version: T;
44
+ /** Status of this version */
45
+ _status: DocumentStatus;
46
+ /** Whether this version was created by autosave */
47
+ autosave: boolean;
48
+ /** When this version was published (if published) */
49
+ publishedAt?: string;
50
+ /** When this version was created */
51
+ createdAt: string;
52
+ /** When this version was last updated */
53
+ updatedAt: string;
54
+ }
55
+ /**
56
+ * Options for counting versions (filter options only, no pagination).
57
+ */
58
+ export interface VersionCountOptions {
59
+ /** Include autosave versions in count (default: false) */
60
+ includeAutosave?: boolean;
61
+ /** Filter by status */
62
+ status?: DocumentStatus;
63
+ }
64
+ /**
65
+ * Options for querying versions of a document.
66
+ */
67
+ export interface VersionQueryOptions extends VersionCountOptions {
68
+ /** Maximum number of versions to return */
69
+ limit?: number;
70
+ /** Page number for pagination (1-based) */
71
+ page?: number;
72
+ /** Sort order (default: 'desc' = newest first) */
73
+ sort?: 'asc' | 'desc';
74
+ }
75
+ /**
76
+ * Result of a version query with pagination info.
77
+ */
78
+ export interface VersionQueryResult<T = Record<string, unknown>> {
79
+ /** Array of versions */
80
+ docs: DocumentVersionParsed<T>[];
81
+ /** Total number of versions */
82
+ totalDocs: number;
83
+ /** Total number of pages */
84
+ totalPages: number;
85
+ /** Current page number */
86
+ page: number;
87
+ /** Items per page */
88
+ limit: number;
89
+ /** Whether there is a next page */
90
+ hasNextPage: boolean;
91
+ /** Whether there is a previous page */
92
+ hasPrevPage: boolean;
93
+ }
94
+ /**
95
+ * Options for restoring a version.
96
+ */
97
+ export interface RestoreVersionOptions {
98
+ /** The version ID to restore */
99
+ versionId: string;
100
+ /** The document ID that the version must belong to (validated when provided) */
101
+ docId?: string;
102
+ /** Whether to publish the restored version immediately (default: false) */
103
+ publish?: boolean;
104
+ }
105
+ /**
106
+ * Options for creating a version.
107
+ */
108
+ export interface CreateVersionOptions {
109
+ /** Status of the new version */
110
+ status?: DocumentStatus;
111
+ /** Whether this is an autosave version */
112
+ autosave?: boolean;
113
+ }
114
+ /**
115
+ * Options for publishing a document.
116
+ */
117
+ export interface PublishOptions {
118
+ /** Scheduled publish date (ISO string). If provided, document will be scheduled for future publishing. */
119
+ scheduledPublishAt?: string;
120
+ }
121
+ /**
122
+ * Options for scheduling a document publish.
123
+ */
124
+ export interface SchedulePublishOptions {
125
+ /** ISO date string for when the document should be published. */
126
+ publishAt: string;
127
+ }
128
+ /**
129
+ * Result of a schedule publish operation.
130
+ */
131
+ export interface SchedulePublishResult {
132
+ /** Document ID */
133
+ id: string;
134
+ /** Scheduled publish date (ISO string) */
135
+ scheduledPublishAt: string;
136
+ }
137
+ /**
138
+ * Event data passed to version-related hooks.
139
+ */
140
+ export interface VersionHookArgs {
141
+ /** The version being operated on */
142
+ version: DocumentVersion;
143
+ /** The parent document ID */
144
+ parentId: string;
145
+ /** The collection slug */
146
+ collection: string;
147
+ /** The operation being performed */
148
+ operation: 'create' | 'restore' | 'publish' | 'unpublish' | 'delete';
149
+ }