@bernierllc/content-management-suite 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/README.md ADDED
@@ -0,0 +1,750 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ # Content Management Suite
10
+
11
+ A comprehensive content management suite with editorial workflows, content types, and admin UI built with TypeScript and Tamagui.
12
+
13
+ ## Features
14
+
15
+ - **🎯 Editorial Workflows**: Configurable content workflows with stages, transitions, and permissions
16
+ - **📝 Content Types**: Pluggable content type system supporting text, images, audio, and video
17
+ - **⚡ Auto-save**: Intelligent auto-save with backoff retry and debounce
18
+ - **🗑️ Soft Delete**: Configurable soft delete with user visibility options
19
+ - **🎨 Modern UI**: Tamagui-based components with responsive design
20
+ - **🔧 Configuration**: File + database override + environment variable configuration
21
+ - **🔌 Plugin System**: Extensible plugin architecture for custom functionality
22
+ - **🛡️ Security**: JWT authentication, role-based permissions, and rate limiting
23
+ - **📊 Analytics**: Built-in analytics and metrics collection
24
+ - **🌐 API**: RESTful API with comprehensive endpoints
25
+ - **📱 Admin UI**: NeverAdmin integration for workflow configuration
26
+ - **🔍 Search**: Full-text search across all content types
27
+ - **📈 Performance**: Caching, compression, and optimization
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm install @bernierllc/content-management-suite
33
+ ```
34
+
35
+ ## Quick Start
36
+
37
+ ```typescript
38
+ import { createContentManagementSuite } from '@bernierllc/content-management-suite';
39
+
40
+ const suite = createContentManagementSuite({
41
+ config: {
42
+ server: {
43
+ port: 3000,
44
+ host: 'localhost'
45
+ }
46
+ }
47
+ });
48
+
49
+ await suite.start();
50
+ console.log('Content Management Suite started on http://localhost:3000');
51
+ ```
52
+
53
+ ## Configuration
54
+
55
+ The suite supports comprehensive configuration through multiple sources:
56
+
57
+ ### 1. File Configuration
58
+
59
+ Create a `content-management.config.json` file:
60
+
61
+ ```json
62
+ {
63
+ "server": {
64
+ "port": 3000,
65
+ "host": "localhost",
66
+ "cors": {
67
+ "origin": "*",
68
+ "credentials": true
69
+ }
70
+ },
71
+ "database": {
72
+ "type": "sqlite",
73
+ "name": "content_management"
74
+ },
75
+ "content": {
76
+ "defaultWorkflow": {
77
+ "id": "standard",
78
+ "name": "Standard Workflow",
79
+ "stages": [
80
+ {
81
+ "id": "write",
82
+ "name": "Write",
83
+ "order": 1,
84
+ "isPublishStage": false,
85
+ "allowsScheduling": false,
86
+ "permissions": ["content.edit"]
87
+ },
88
+ {
89
+ "id": "publish",
90
+ "name": "Publish",
91
+ "order": 2,
92
+ "isPublishStage": true,
93
+ "allowsScheduling": true,
94
+ "permissions": ["content.publish"]
95
+ }
96
+ ]
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ### 2. Environment Variables
103
+
104
+ ```bash
105
+ # Server
106
+ CONTENT_MANAGEMENT_PORT=3000
107
+ CONTENT_MANAGEMENT_HOST=localhost
108
+
109
+ # Database
110
+ CONTENT_MANAGEMENT_DATABASE_TYPE=postgresql
111
+ CONTENT_MANAGEMENT_DATABASE_URL=postgresql://user:password@localhost:5432/content_management
112
+
113
+ # Security
114
+ CONTENT_MANAGEMENT_JWT_SECRET=your-secret-key
115
+ CONTENT_MANAGEMENT_JWT_EXPIRES_IN=24h
116
+
117
+ # Integrations
118
+ CONTENT_MANAGEMENT_NEVERADMIN_ENABLED=true
119
+ CONTENT_MANAGEMENT_NEVERADMIN_URL=https://admin.myapp.com
120
+ CONTENT_MANAGEMENT_NEVERADMIN_API_KEY=your-api-key
121
+ ```
122
+
123
+ ### 3. Database Override
124
+
125
+ Configuration can be overridden through the database for runtime changes:
126
+
127
+ ```typescript
128
+ await suite.updateConfig({
129
+ ui: {
130
+ theme: {
131
+ mode: 'dark'
132
+ }
133
+ }
134
+ });
135
+ ```
136
+
137
+ ## Content Types
138
+
139
+ The suite comes with four built-in content types:
140
+
141
+ ### Text Content
142
+
143
+ ```typescript
144
+ import { TextContentType } from '@bernierllc/content-management-suite';
145
+
146
+ // Register text content type
147
+ suite.registerContentType(TextContentType);
148
+
149
+ // Create text content
150
+ const textContent = {
151
+ title: 'My Blog Post',
152
+ slug: 'my-blog-post',
153
+ body: '<p>Hello world!</p>',
154
+ excerpt: 'A brief description',
155
+ tags: ['blog', 'example'],
156
+ categories: ['general'],
157
+ author: {
158
+ id: 'user-1',
159
+ name: 'John Doe',
160
+ email: 'john@example.com'
161
+ }
162
+ };
163
+ ```
164
+
165
+ ### Image Content
166
+
167
+ ```typescript
168
+ import { ImageContentType } from '@bernierllc/content-management-suite';
169
+
170
+ // Register image content type
171
+ suite.registerContentType(ImageContentType);
172
+
173
+ // Create image content
174
+ const imageContent = {
175
+ title: 'Beautiful Landscape',
176
+ slug: 'beautiful-landscape',
177
+ description: 'A stunning mountain landscape',
178
+ altText: 'Mountain landscape with sunset',
179
+ imageUrl: 'https://example.com/images/landscape.jpg',
180
+ tags: ['landscape', 'nature'],
181
+ categories: ['photography'],
182
+ author: {
183
+ id: 'user-1',
184
+ name: 'John Doe',
185
+ email: 'john@example.com'
186
+ }
187
+ };
188
+ ```
189
+
190
+ ### Audio Content
191
+
192
+ ```typescript
193
+ import { AudioContentType } from '@bernierllc/content-management-suite';
194
+
195
+ // Register audio content type
196
+ suite.registerContentType(AudioContentType);
197
+
198
+ // Create audio content
199
+ const audioContent = {
200
+ title: 'My Podcast Episode',
201
+ slug: 'my-podcast-episode',
202
+ description: 'A great podcast episode',
203
+ audioUrl: 'https://example.com/audio/episode.mp3',
204
+ duration: 1800, // 30 minutes
205
+ metadata: {
206
+ artist: 'John Doe',
207
+ album: 'Tech Talk Podcast',
208
+ year: 2023
209
+ },
210
+ tags: ['podcast', 'technology'],
211
+ categories: ['podcast'],
212
+ author: {
213
+ id: 'user-1',
214
+ name: 'John Doe',
215
+ email: 'john@example.com'
216
+ }
217
+ };
218
+ ```
219
+
220
+ ### Video Content
221
+
222
+ ```typescript
223
+ import { VideoContentType } from '@bernierllc/content-management-suite';
224
+
225
+ // Register video content type
226
+ suite.registerContentType(VideoContentType);
227
+
228
+ // Create video content
229
+ const videoContent = {
230
+ title: 'My Video Tutorial',
231
+ slug: 'my-video-tutorial',
232
+ description: 'A comprehensive tutorial',
233
+ videoUrl: 'https://example.com/videos/tutorial.mp4',
234
+ duration: 1800, // 30 minutes
235
+ dimensions: {
236
+ width: 1920,
237
+ height: 1080
238
+ },
239
+ metadata: {
240
+ director: 'John Doe',
241
+ genre: 'Educational',
242
+ year: 2023
243
+ },
244
+ tags: ['tutorial', 'video'],
245
+ categories: ['tutorial'],
246
+ author: {
247
+ id: 'user-1',
248
+ name: 'John Doe',
249
+ email: 'john@example.com'
250
+ }
251
+ };
252
+ ```
253
+
254
+ ## Editorial Workflows
255
+
256
+ Configure custom editorial workflows:
257
+
258
+ ```typescript
259
+ const customWorkflow = {
260
+ id: 'editorial',
261
+ name: 'Editorial Workflow',
262
+ description: 'Multi-stage editorial workflow',
263
+ stages: [
264
+ {
265
+ id: 'write',
266
+ name: 'Write',
267
+ order: 1,
268
+ isPublishStage: false,
269
+ allowsScheduling: false,
270
+ permissions: ['content.edit']
271
+ },
272
+ {
273
+ id: 'edit',
274
+ name: 'Edit',
275
+ order: 2,
276
+ isPublishStage: false,
277
+ allowsScheduling: false,
278
+ permissions: ['content.edit']
279
+ },
280
+ {
281
+ id: 'review',
282
+ name: 'Review',
283
+ order: 3,
284
+ isPublishStage: false,
285
+ allowsScheduling: false,
286
+ permissions: ['content.review']
287
+ },
288
+ {
289
+ id: 'publish',
290
+ name: 'Publish',
291
+ order: 4,
292
+ isPublishStage: true,
293
+ allowsScheduling: true,
294
+ permissions: ['content.publish']
295
+ }
296
+ ],
297
+ transitions: [
298
+ {
299
+ id: 'write-to-edit',
300
+ from: 'write',
301
+ to: 'edit',
302
+ permissions: ['content.edit']
303
+ },
304
+ {
305
+ id: 'edit-to-review',
306
+ from: 'edit',
307
+ to: 'review',
308
+ permissions: ['content.review']
309
+ },
310
+ {
311
+ id: 'review-to-publish',
312
+ from: 'review',
313
+ to: 'publish',
314
+ permissions: ['content.publish']
315
+ }
316
+ ]
317
+ };
318
+
319
+ await suite.workflowService.createWorkflow(customWorkflow);
320
+ ```
321
+
322
+ ## API Endpoints
323
+
324
+ ### Content Management
325
+
326
+ - `GET /api/content` - List content
327
+ - `GET /api/content/:id` - Get content by ID
328
+ - `POST /api/content` - Create content
329
+ - `PUT /api/content/:id` - Update content
330
+ - `DELETE /api/content/:id` - Delete content
331
+ - `POST /api/content/:id/publish` - Publish content
332
+ - `POST /api/content/:id/schedule` - Schedule content
333
+ - `POST /api/content/:id/unpublish` - Unpublish content
334
+
335
+ ### Workflow Management
336
+
337
+ - `GET /api/workflows` - List workflows
338
+ - `GET /api/workflows/:id` - Get workflow by ID
339
+ - `POST /api/workflows` - Create workflow
340
+ - `PUT /api/workflows/:id` - Update workflow
341
+ - `DELETE /api/workflows/:id` - Delete workflow
342
+
343
+ ### Content Type Management
344
+
345
+ - `GET /api/content-types` - List content types
346
+ - `GET /api/content-types/:id` - Get content type by ID
347
+ - `POST /api/content-types` - Create content type
348
+ - `PUT /api/content-types/:id` - Update content type
349
+ - `DELETE /api/content-types/:id` - Delete content type
350
+
351
+ ### Configuration Management
352
+
353
+ - `GET /api/config` - Get configuration
354
+ - `PUT /api/config` - Update configuration
355
+
356
+ ### User Management
357
+
358
+ - `GET /api/users` - List users
359
+ - `GET /api/users/:id` - Get user by ID
360
+ - `POST /api/users` - Create user
361
+ - `PUT /api/users/:id` - Update user
362
+ - `DELETE /api/users/:id` - Delete user
363
+
364
+ ## UI Components
365
+
366
+ ### Content Editor
367
+
368
+ ```typescript
369
+ import { ContentEditor } from '@bernierllc/content-management-suite';
370
+
371
+ <ContentEditor
372
+ content={content}
373
+ onChange={(content) => console.log('Content changed:', content)}
374
+ onSave={(content) => console.log('Save:', content)}
375
+ onPublish={(content) => console.log('Publish:', content)}
376
+ onSchedule={(content, date) => console.log('Schedule:', content, date)}
377
+ showToolbar={true}
378
+ showStatusBar={true}
379
+ showWordCount={true}
380
+ showCharacterCount={true}
381
+ placeholder="Start writing your content..."
382
+ maxCharacters={10000}
383
+ targetWordCount={1000}
384
+ />
385
+ ```
386
+
387
+ ### Workflow Components
388
+
389
+ ```typescript
390
+ import {
391
+ WorkflowStepper,
392
+ StageActionButtons,
393
+ WorkflowTimeline,
394
+ WorkflowAdminConfig
395
+ } from '@bernierllc/content-management-suite';
396
+
397
+ // Workflow stepper
398
+ <WorkflowStepper
399
+ workflow={workflow}
400
+ currentStage={currentStage}
401
+ onStageChange={(stage) => console.log('Stage changed:', stage)}
402
+ />
403
+
404
+ // Stage action buttons
405
+ <StageActionButtons
406
+ stage={currentStage}
407
+ content={content}
408
+ onAction={(action) => console.log('Action:', action)}
409
+ />
410
+
411
+ // Workflow timeline
412
+ <WorkflowTimeline
413
+ workflow={workflow}
414
+ content={content}
415
+ onStageClick={(stage) => console.log('Stage clicked:', stage)}
416
+ />
417
+
418
+ // Workflow admin configuration
419
+ <WorkflowAdminConfig
420
+ workflow={workflow}
421
+ onChange={(workflow) => console.log('Workflow changed:', workflow)}
422
+ onSave={(workflow) => console.log('Workflow saved:', workflow)}
423
+ />
424
+ ```
425
+
426
+ ### Content List
427
+
428
+ ```typescript
429
+ import { ContentList } from '@bernierllc/content-management-suite';
430
+
431
+ <ContentList
432
+ content={contentList}
433
+ onContentSelect={(content) => console.log('Content selected:', content)}
434
+ onContentEdit={(content) => console.log('Edit content:', content)}
435
+ onContentDelete={(content) => console.log('Delete content:', content)}
436
+ onContentPublish={(content) => console.log('Publish content:', content)}
437
+ view="table" // table, list, grid, kanban
438
+ showSearch={true}
439
+ showFilters={true}
440
+ showSorting={true}
441
+ showPagination={true}
442
+ pageSize={20}
443
+ />
444
+ ```
445
+
446
+ ## Plugin System
447
+
448
+ Create custom plugins:
449
+
450
+ ```typescript
451
+ const analyticsPlugin = {
452
+ name: 'analytics',
453
+ version: '1.0.0',
454
+ description: 'Analytics plugin for content management',
455
+ dependencies: ['@bernierllc/analytics'],
456
+ install: async (suite) => {
457
+ console.log('Installing analytics plugin');
458
+ // Initialize analytics
459
+ },
460
+ uninstall: async (suite) => {
461
+ console.log('Uninstalling analytics plugin');
462
+ // Cleanup analytics
463
+ },
464
+ enable: async (suite) => {
465
+ console.log('Enabling analytics plugin');
466
+ // Enable analytics tracking
467
+ },
468
+ disable: async (suite) => {
469
+ console.log('Disabling analytics plugin');
470
+ // Disable analytics tracking
471
+ }
472
+ };
473
+
474
+ suite.registerPlugin(analyticsPlugin);
475
+ ```
476
+
477
+ ## Middleware System
478
+
479
+ Add custom middleware:
480
+
481
+ ```typescript
482
+ suite.addMiddleware({
483
+ name: 'request-logger',
484
+ handler: (req, res, next) => {
485
+ console.log(`${req.method} ${req.path} - ${new Date().toISOString()}`);
486
+ next();
487
+ },
488
+ order: 1
489
+ });
490
+ ```
491
+
492
+ ## Hook System
493
+
494
+ Add custom hooks:
495
+
496
+ ```typescript
497
+ suite.addHook({
498
+ name: 'content:created',
499
+ handler: async (content) => {
500
+ console.log('New content created:', content.id);
501
+ // Send notification, update analytics, etc.
502
+ },
503
+ priority: 1
504
+ });
505
+ ```
506
+
507
+ ## Security
508
+
509
+ ### JWT Authentication
510
+
511
+ ```typescript
512
+ const suite = createContentManagementSuite({
513
+ config: {
514
+ security: {
515
+ jwt: {
516
+ secret: 'your-super-secret-jwt-key',
517
+ expiresIn: '24h',
518
+ issuer: 'content-management-suite'
519
+ }
520
+ }
521
+ }
522
+ });
523
+ ```
524
+
525
+ ### Role-Based Permissions
526
+
527
+ ```typescript
528
+ const suite = createContentManagementSuite({
529
+ config: {
530
+ security: {
531
+ permissions: {
532
+ enabled: true,
533
+ defaultRole: 'user',
534
+ roles: [
535
+ {
536
+ name: 'admin',
537
+ permissions: ['*'],
538
+ description: 'Full administrative access'
539
+ },
540
+ {
541
+ name: 'editor',
542
+ permissions: ['content.edit', 'content.publish', 'content.schedule'],
543
+ description: 'Content editing and publishing'
544
+ },
545
+ {
546
+ name: 'author',
547
+ permissions: ['content.edit'],
548
+ description: 'Content creation and editing'
549
+ },
550
+ {
551
+ name: 'user',
552
+ permissions: ['content.view'],
553
+ description: 'Content viewing only'
554
+ }
555
+ ]
556
+ }
557
+ }
558
+ }
559
+ });
560
+ ```
561
+
562
+ ## Performance
563
+
564
+ ### Caching
565
+
566
+ ```typescript
567
+ const suite = createContentManagementSuite({
568
+ config: {
569
+ performance: {
570
+ cache: {
571
+ enabled: true,
572
+ ttl: 300, // 5 minutes
573
+ maxSize: 1000
574
+ }
575
+ }
576
+ }
577
+ });
578
+ ```
579
+
580
+ ### Compression
581
+
582
+ ```typescript
583
+ const suite = createContentManagementSuite({
584
+ config: {
585
+ performance: {
586
+ compression: {
587
+ enabled: true,
588
+ level: 6
589
+ }
590
+ }
591
+ }
592
+ });
593
+ ```
594
+
595
+ ### Rate Limiting
596
+
597
+ ```typescript
598
+ const suite = createContentManagementSuite({
599
+ config: {
600
+ performance: {
601
+ rateLimit: {
602
+ enabled: true,
603
+ windowMs: 15 * 60 * 1000, // 15 minutes
604
+ max: 100
605
+ }
606
+ }
607
+ }
608
+ });
609
+ ```
610
+
611
+ ## Logging
612
+
613
+ ```typescript
614
+ const suite = createContentManagementSuite({
615
+ config: {
616
+ logging: {
617
+ level: 'info',
618
+ format: 'json',
619
+ file: {
620
+ enabled: true,
621
+ path: './logs',
622
+ maxSize: '10MB',
623
+ maxFiles: 5
624
+ },
625
+ console: {
626
+ enabled: true,
627
+ colorize: true
628
+ }
629
+ }
630
+ }
631
+ });
632
+ ```
633
+
634
+ ## Integrations
635
+
636
+ ### NeverAdmin Integration
637
+
638
+ ```typescript
639
+ const suite = createContentManagementSuite({
640
+ config: {
641
+ integrations: {
642
+ neverAdmin: {
643
+ enabled: true,
644
+ url: 'https://admin.myapp.com',
645
+ apiKey: 'your-api-key',
646
+ syncInterval: 300000 // 5 minutes
647
+ }
648
+ }
649
+ }
650
+ });
651
+ ```
652
+
653
+ ### NeverHub Integration
654
+
655
+ ```typescript
656
+ const suite = createContentManagementSuite({
657
+ config: {
658
+ integrations: {
659
+ neverHub: {
660
+ enabled: true,
661
+ url: 'https://hub.myapp.com',
662
+ apiKey: 'your-api-key',
663
+ packageDiscovery: true
664
+ }
665
+ }
666
+ }
667
+ });
668
+ ```
669
+
670
+ ## Error Handling
671
+
672
+ ```typescript
673
+ suite.addHook({
674
+ name: 'error',
675
+ handler: async (error) => {
676
+ console.error('Suite error:', error);
677
+ // Send to error tracking service
678
+ }
679
+ });
680
+ ```
681
+
682
+ ## Graceful Shutdown
683
+
684
+ ```typescript
685
+ process.on('SIGINT', async () => {
686
+ console.log('Received SIGINT, shutting down gracefully...');
687
+ try {
688
+ await suite.stop();
689
+ console.log('Suite stopped successfully');
690
+ process.exit(0);
691
+ } catch (error) {
692
+ console.error('Error during shutdown:', error);
693
+ process.exit(1);
694
+ }
695
+ });
696
+ ```
697
+
698
+ ## Development vs Production
699
+
700
+ ```typescript
701
+ const isDevelopment = process.env.NODE_ENV === 'development';
702
+
703
+ const suite = createContentManagementSuite({
704
+ config: {
705
+ server: {
706
+ port: isDevelopment ? 3000 : 80,
707
+ host: isDevelopment ? 'localhost' : '0.0.0.0'
708
+ },
709
+ logging: {
710
+ level: isDevelopment ? 'debug' : 'info',
711
+ format: isDevelopment ? 'text' : 'json'
712
+ },
713
+ security: {
714
+ jwt: {
715
+ secret: isDevelopment ? 'dev-secret' : process.env.JWT_SECRET
716
+ }
717
+ }
718
+ }
719
+ });
720
+ ```
721
+
722
+ ## Testing
723
+
724
+ ```bash
725
+ # Run unit tests
726
+ npm test
727
+
728
+ # Run Playwright tests
729
+ npm run test:playwright
730
+
731
+ # Run all tests
732
+ npm run test:all
733
+ ```
734
+
735
+ ## Examples
736
+
737
+ See the `examples.ts` file for comprehensive usage examples:
738
+
739
+ - Basic setup
740
+ - Advanced configuration
741
+ - Custom content types
742
+ - Plugin system
743
+ - Error handling
744
+ - API usage
745
+ - Graceful shutdown
746
+ - Environment-specific setup
747
+
748
+ ## License
749
+
750
+ Copyright (c) 2025 Bernier LLC. Licensed under limited-use license.