@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/LICENSE +5 -0
- package/README.md +750 -0
- package/dist/index.d.mts +1290 -0
- package/dist/index.d.ts +1290 -0
- package/dist/index.js +1070 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1021 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.d.mts +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +1102 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +1089 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +80 -0
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.
|