@spfn/cms 0.1.0-alpha.66 → 0.1.0-alpha.69

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.
Files changed (31) hide show
  1. package/README.md +27 -459
  2. package/dist/api.d.ts +6 -4
  3. package/dist/api.js +36 -18
  4. package/dist/api.js.map +1 -1
  5. package/dist/client.js +36 -18
  6. package/dist/client.js.map +1 -1
  7. package/dist/lib/contracts/labels.d.ts +14 -13
  8. package/dist/lib/contracts/labels.js +10 -8
  9. package/dist/lib/contracts/labels.js.map +1 -1
  10. package/dist/lib/contracts/published-cache.d.ts +26 -4
  11. package/dist/lib/contracts/published-cache.js +19 -2
  12. package/dist/lib/contracts/published-cache.js.map +1 -1
  13. package/dist/lib/contracts/values.d.ts +4 -6
  14. package/dist/lib/contracts/values.js +2 -2
  15. package/dist/lib/contracts/values.js.map +1 -1
  16. package/dist/server/routes/labels/[id]/index.js +10 -8
  17. package/dist/server/routes/labels/[id]/index.js.map +1 -1
  18. package/dist/server/routes/labels/by-key/[key]/index.js +10 -8
  19. package/dist/server/routes/labels/by-key/[key]/index.js.map +1 -1
  20. package/dist/server/routes/labels/index.js +135 -10
  21. package/dist/server/routes/labels/index.js.map +1 -1
  22. package/dist/server/routes/published-cache/index.d.ts +1 -0
  23. package/dist/server/routes/published-cache/index.js +42 -1
  24. package/dist/server/routes/published-cache/index.js.map +1 -1
  25. package/dist/server/routes/values/[labelId]/[version]/index.js +2 -2
  26. package/dist/server/routes/values/[labelId]/[version]/index.js.map +1 -1
  27. package/dist/server/routes/values/[labelId]/index.js +2 -2
  28. package/dist/server/routes/values/[labelId]/index.js.map +1 -1
  29. package/dist/server.js +17 -1
  30. package/dist/server.js.map +1 -1
  31. package/package.json +3 -3
package/README.md CHANGED
@@ -4,94 +4,26 @@ Content Management System for Next.js with JSON-based labels and automatic datab
4
4
 
5
5
  ## Features
6
6
 
7
- - 📁 **JSON file-based labels** - Simple file structure for label management
8
- - 🔄 **Auto-sync to database** on server startup and during development
9
- - 🌐 **Multi-language support** (i18n)
10
- - 🍪 **Cookie-based locale management** - Automatic locale detection and persistence
11
- - 📦 **Folder-based structure** for better organization
12
- - 🔥 **Hot reload** during development
13
- - 💾 **Published cache** for optimal performance
14
- - ⚡ **Server Actions** for client-side locale management
15
- - 🛠️ **Built on Drizzle ORM**
7
+ - 📁 JSON file-based labels
8
+ - 🔄 Auto-sync to database
9
+ - 🌐 50+ languages support
10
+ - 🍪 Cookie-based locale management
11
+ - 🔥 Hot reload during development
12
+ - 💾 Published cache (17x faster)
13
+ - 📝 Draft system & version control
16
14
 
17
15
  ## Installation
18
16
 
19
- ### Recommended: Using Superfunction CLI (Automatic Database Setup)
20
-
21
17
  ```bash
22
18
  pnpm spfn add @spfn/cms
23
19
  ```
24
20
 
25
- This command will:
26
- 1. ✅ Install the package
27
- 2. ✅ Discover CMS database schemas automatically
28
- 3. ✅ Generate migrations for 6 CMS tables
29
- 4. ✅ Apply migrations to your database
30
- 5. ✅ Show setup guide
31
-
32
- **Tables created:**
33
- - `cms_labels` - Label definitions (10 columns, 2 indexes)
34
- - `cms_label_values` - Label values per locale (7 columns, 2 indexes, 1 FK)
35
- - `cms_label_versions` - Version history (9 columns, 2 indexes, 1 FK)
36
- - `cms_draft_cache` - Draft content cache (6 columns, 2 indexes)
37
- - `cms_published_cache` - Published content cache (7 columns, 1 index)
38
- - `cms_audit_logs` - Change audit trail (8 columns, 4 indexes, 1 FK)
39
-
40
- ### Manual Installation
41
-
42
- ```bash
43
- pnpm add @spfn/cms
44
- ```
45
-
46
- Then run database migrations:
47
-
48
- ```bash
49
- pnpm spfn db generate # Generate migrations
50
- pnpm spfn db migrate # Apply migrations
51
- ```
52
-
53
- **Note:** Manual installation requires that you have `DATABASE_URL` configured in your `.env.local` file.
54
-
55
21
  ## Quick Start
56
22
 
57
23
  ### 1. Create Label Files
58
24
 
59
- Create JSON files organized by sections and categories:
60
-
61
- ```
62
- src/lib/labels/
63
- layout/ ← Section name
64
- nav.json ← Category
65
- footer.json
66
- home/
67
- hero.json
68
- features.json
69
- ```
70
-
71
- **Example:** `src/lib/labels/layout/nav.json`
72
-
73
- ```json
74
- {
75
- "about": {
76
- "key": "layout.nav.about",
77
- "defaultValue": "About",
78
- "description": "Navigation link for About page"
79
- },
80
- "services": {
81
- "key": "layout.nav.services",
82
- "defaultValue": "Services",
83
- "description": "Navigation link for Services page"
84
- },
85
- "team": {
86
- "key": "layout.nav.team",
87
- "defaultValue": "Team"
88
- }
89
- }
90
- ```
91
-
92
- **Multi-language example:** `src/lib/labels/home/hero.json`
93
-
94
25
  ```json
26
+ // src/lib/labels/home/hero.json
95
27
  {
96
28
  "title": {
97
29
  "key": "home.hero.title",
@@ -99,67 +31,24 @@ src/lib/labels/
99
31
  "ko": "혁신적인 솔루션",
100
32
  "en": "Innovative Solutions"
101
33
  }
102
- },
103
- "subtitle": {
104
- "key": "home.hero.subtitle",
105
- "defaultValue": {
106
- "ko": "비즈니스 성장을 위한 최고의 파트너",
107
- "en": "Your Best Partner for Business Growth"
108
- }
109
- }
110
- }
111
- ```
112
-
113
- **Variable substitution:** `src/lib/labels/layout/footer.json`
114
-
115
- ```json
116
- {
117
- "copyright": {
118
- "key": "layout.footer.copyright",
119
- "defaultValue": "© {year} Company. All rights reserved."
120
34
  }
121
35
  }
122
36
  ```
123
37
 
124
- ### 2. Enable Auto-Sync on Server Startup
125
-
126
- Configure `src/server/server.config.ts`:
38
+ ### 2. Enable Auto-Sync
127
39
 
128
40
  ```typescript
129
- import type { ServerConfig } from '@spfn/core/server';
41
+ // src/server/server.config.ts
130
42
  import { initLabelSync } from '@spfn/cms/server';
131
- import { DEFAULT_LABELS_DIR } from '@spfn/cms';
132
43
 
133
44
  export default {
134
45
  beforeRoutes: async (app) => {
135
- await initLabelSync({
136
- verbose: true,
137
- // labelsDir: DEFAULT_LABELS_DIR // Optional, this is the default
138
- });
46
+ await initLabelSync({ verbose: true });
139
47
  },
140
48
  } satisfies ServerConfig;
141
49
  ```
142
50
 
143
- ### 3. Enable Auto-Sync During Development
144
-
145
- Your `.spfnrc.json` should include:
146
-
147
- ```json
148
- {
149
- "codegen": {
150
- "generators": [
151
- {
152
- "name": "@spfn/cms:label-sync",
153
- "enabled": true
154
- }
155
- ]
156
- }
157
- }
158
- ```
159
-
160
- This is automatically configured when you run `pnpm spfn add @spfn/cms`.
161
-
162
- ### 4. Use Labels in Your App
51
+ ### 3. Use in Your App
163
52
 
164
53
  **Server Component:**
165
54
 
@@ -167,22 +56,11 @@ This is automatically configured when you run `pnpm spfn add @spfn/cms`.
167
56
  import { getSection } from '@spfn/cms/server';
168
57
 
169
58
  export default async function HomePage() {
170
- const { t } = await getSection('layout', 'ko');
171
-
172
- return <h1>{t('nav.team')}</h1>;
59
+ const { t } = await getSection('home');
60
+ return <h1>{t('hero.title')}</h1>;
173
61
  }
174
62
  ```
175
63
 
176
- **With variable substitution:**
177
-
178
- ```typescript
179
- const { t } = await getSection('layout');
180
- const copyright = t('footer.copyright', undefined, {
181
- year: new Date().getFullYear()
182
- });
183
- // → "© 2025 Company. All rights reserved."
184
- ```
185
-
186
64
  **Client Component:**
187
65
 
188
66
  ```typescript
@@ -190,344 +68,34 @@ const copyright = t('footer.copyright', undefined, {
190
68
  import { useSection } from '@spfn/cms/client';
191
69
 
192
70
  export default function Nav() {
193
- const { t, loading } = useSection('layout', { autoLoad: true });
194
-
195
- if (loading) return <div>Loading...</div>;
196
-
197
- return (
198
- <nav>
199
- <a>{t('nav.about')}</a>
200
- <a>{t('nav.services')}</a>
201
- </nav>
202
- );
203
- }
204
- ```
205
-
206
- ## File Structure
207
-
208
- ```
209
- src/lib/labels/
210
- layout/ # Section: layout
211
- nav.json # Category: nav
212
- footer.json # Category: footer
213
- home/ # Section: home
214
- hero.json # Category: hero
215
- features.json # Category: features
216
- ```
217
-
218
- **How it maps:**
219
- - Folder name = Section name
220
- - JSON file name = Category name (for organization only)
221
- - Inside JSON: `key` field defines the actual label key
222
-
223
- Example:
224
- ```
225
- src/lib/labels/layout/nav.json:
226
- key: "layout.nav.team" → t('nav.team') in code
227
- ```
228
-
229
- ## JSON Label Format
230
-
231
- ```typescript
232
- {
233
- "labelName": {
234
- "key": "section.category.name", // Required: Unique identifier
235
- "defaultValue": "Text" | {...}, // Required: String or i18n object
236
- "description": "Optional description" // Optional: For documentation
237
- }
71
+ const { t } = useSection('layout', { autoLoad: true });
72
+ return <nav><a>{t('nav.about')}</a></nav>;
238
73
  }
239
74
  ```
240
75
 
241
- **Single language:**
242
- ```json
243
- {
244
- "welcome": {
245
- "key": "home.welcome",
246
- "defaultValue": "Welcome"
247
- }
248
- }
249
- ```
250
-
251
- **Multi-language:**
252
- ```json
253
- {
254
- "welcome": {
255
- "key": "home.welcome",
256
- "defaultValue": {
257
- "ko": "환영합니다",
258
- "en": "Welcome",
259
- "ja": "ようこそ"
260
- }
261
- }
262
- }
263
- ```
76
+ ## Documentation
264
77
 
265
- **Variable placeholders:**
266
- ```json
267
- {
268
- "greeting": {
269
- "key": "home.greeting",
270
- "defaultValue": "Hello, {name}!"
271
- }
272
- }
273
- ```
78
+ 📖 **[Full Documentation](../../docs/ecosystem/cms/index.md)**
274
79
 
275
- Usage:
276
- ```typescript
277
- t('greeting', undefined, { name: 'John' })
278
- // "Hello, John!"
279
- ```
80
+ - [Getting Started](../../docs/ecosystem/cms/getting-started.md) - Setup and configuration
81
+ - [Label Sync Guide](../../docs/ecosystem/cms/label-sync.md) - Auto-sync options
82
+ - [Advanced Features](../../docs/ecosystem/cms/advanced-features.md) - Breakpoints, value types, Draft Mode
83
+ - [Locale Management](../../docs/ecosystem/cms/locale-management.md) - 50+ languages guide
84
+ - [API Reference](../../docs/ecosystem/cms/api-reference.md) - Complete API docs
85
+ - [Draft & Versioning](../../docs/ecosystem/cms/draft-versioning.md) - Version control & audit logs
280
86
 
281
87
  ## Configuration
282
88
 
283
- ### Default Paths
284
-
285
- The default label directory path is defined as a constant:
286
-
287
- ```typescript
288
- import { DEFAULT_LABELS_DIR } from '@spfn/cms';
289
-
290
- console.log(DEFAULT_LABELS_DIR); // 'src/lib/labels'
291
- ```
292
-
293
- This constant is used throughout the CMS package and can be referenced in your code to avoid hardcoding paths.
294
-
295
- ### Environment Variables
296
-
297
- Configure CMS behavior via environment variables in `.env.local`:
298
-
299
89
  ```bash
300
- # Default locale (default: 'en')
90
+ # .env.local
301
91
  SPFN_CMS_DEFAULT_LOCALE=ko
302
-
303
- # Supported locales, comma-separated (default: 'en,ko')
304
92
  SPFN_CMS_SUPPORTED_LOCALES=en,ko,ja
305
-
306
- # Auto-detect browser language (default: true)
307
93
  SPFN_CMS_DETECT_BROWSER_LANGUAGE=true
308
94
  ```
309
95
 
310
- ### Runtime Configuration
311
-
312
- Override configuration at runtime (mainly for testing):
313
-
314
- ```typescript
315
- import { configureCms, getCmsConfig } from '@spfn/cms';
316
-
317
- // Get current configuration
318
- const config = getCmsConfig();
319
- console.log(config.defaultLocale); // 'ko'
320
- console.log(config.supportedLocales); // ['ko', 'en']
321
-
322
- // Update configuration
323
- configureCms({
324
- defaultLocale: 'en',
325
- supportedLocales: ['en', 'ko', 'ja'],
326
- detectBrowserLanguage: false
327
- });
328
- ```
329
-
330
- ## Locale Management
331
-
332
- ### Automatic Locale Detection
333
-
334
- The CMS automatically manages user locale with the following priority:
335
-
336
- 1. **Cookie** - User's explicitly selected locale (persisted)
337
- 2. **Browser Language** - Auto-detected from `Accept-Language` header (if enabled)
338
- 3. **Default Locale** - System default from environment variables
339
-
340
- ### Server Actions (`@spfn/cms/actions`)
341
-
342
- Use Server Actions for locale management in both server and client components:
343
-
344
- **Get current locale:**
96
+ ## Development Status
345
97
 
346
- ```typescript
347
- // Server Component
348
- import { getLocale } from '@spfn/cms/actions';
349
-
350
- export default async function RootLayout({ children }) {
351
- const locale = await getLocale();
352
-
353
- return <html lang={locale}>{children}</html>;
354
- }
355
- ```
356
-
357
- ```typescript
358
- // Client Component
359
- 'use client';
360
- import { getLocale } from '@spfn/cms/client';
361
- import { useEffect, useState } from 'react';
362
-
363
- export default function LanguageSwitcher() {
364
- const [locale, setLocale] = useState('');
365
-
366
- useEffect(() => {
367
- getLocale().then(setLocale);
368
- }, []);
369
-
370
- return <div>Current: {locale}</div>;
371
- }
372
- ```
373
-
374
- **Change locale:**
375
-
376
- ```typescript
377
- import { setLocale } from '@spfn/cms/actions';
378
-
379
- async function changeLanguage(newLocale: string) {
380
- await setLocale(newLocale);
381
- window.location.reload(); // Reload to apply changes
382
- }
383
- ```
384
-
385
- **Get supported locales:**
386
-
387
- ```typescript
388
- import { getLocales } from '@spfn/cms/actions';
389
-
390
- const locales = await getLocales(); // ['ko', 'en', 'ja']
391
- ```
392
-
393
- ### Auto-detect Locale in Server Components
394
-
395
- When `locale` is not specified, `getSection()` automatically uses the detected locale:
396
-
397
- ```typescript
398
- import { getSection } from '@spfn/cms/server';
399
-
400
- // Auto-detects locale from cookie → browser → default
401
- const { t } = await getSection('home');
402
-
403
- // Or explicitly specify locale
404
- const { t: tEn } = await getSection('home', 'en');
405
- ```
406
-
407
- ## Documentation
408
-
409
- ### Getting Started
410
- - **[Label Auto-Sync Guide](./LABEL_SYNC_GUIDE.md)** - Detailed sync configuration and options
411
- - **[Examples](./examples/)** - Usage examples
412
-
413
- ### Advanced Guides
414
- - **[Advanced Features](./ADVANCED_FEATURES.md)** - Breakpoints, value types, InitCms, Draft Mode
415
- - **[Locale Management](./LOCALE_GUIDE.md)** - Complete locale guide with 50+ languages
416
- - **[API Reference](./API_REFERENCE.md)** - Complete API documentation and repository functions
417
- - **[Draft & Versioning](./DRAFT_AND_VERSIONING.md)** - Draft system, version control, audit logs
418
-
419
- ## Architecture
420
-
421
- ```
422
- JSON Files (src/lib/labels/**/*.json)
423
-
424
- loadLabelsFromJson()
425
-
426
- ┌─────────────────────┐
427
- │ LabelSyncGenerator │ ← File watcher (development)
428
- │ initLabelSync() │ ← Server startup
429
- └─────────────────────┘
430
-
431
- syncAll()
432
-
433
- ┌─────────────────────┐
434
- │ PostgreSQL DB │
435
- │ - cms_labels │
436
- │ - published_cache │ ⭐ Used by API
437
- └─────────────────────┘
438
- ↓ HTTP API
439
- ┌─────────────────────┐
440
- │ Application │
441
- │ - getSection() │
442
- │ - useSection() │
443
- └─────────────────────┘
444
- ```
445
-
446
- ## API Reference
447
-
448
- ### Common API (`@spfn/cms`)
449
-
450
- **Configuration:**
451
- - `getCmsConfig()` - Get current CMS configuration
452
- - `configureCms(config)` - Update configuration (runtime)
453
- - `resetCmsConfig()` - Reset configuration to defaults
454
-
455
- **Constants:**
456
- - `DEFAULT_LABELS_DIR` - Default label directory path (`'src/lib/labels'`)
457
- - `LOCALE_COOKIE_KEY` - Locale cookie key constant
458
- - Locale helpers: `getLocaleInfo()`, `getSupportedLocales()`, `getFlag()`, `getDialCode()`, `isRTL()`
459
-
460
- **Types:**
461
- - `SectionData`, `SectionAPI`, `CmsConfig`, `LocaleInfo`, `SupportedLocale`
462
-
463
- ### Server-side API (`@spfn/cms/server`)
464
-
465
- **Server Components:**
466
- - `getSection(section, locale?)` - Get section labels (auto-detects locale if not specified)
467
- - `getSections(sections, locale?)` - Get multiple sections (auto-detects locale if not specified)
468
-
469
- **Backend/Sync:**
470
- - `initLabelSync(options?)` - Sync labels on server startup
471
- - `syncAll(sections, options?)` - Sync all sections
472
- - `syncSection(definition, options?)` - Sync specific section
473
- - `loadLabelsFromJson(labelsDir)` - Load labels from JSON files
474
-
475
- **Repositories & Entities:**
476
- - All repository and entity exports
477
-
478
- **Codegen:**
479
- - `createLabelSyncGenerator(config?)` - Generator factory
480
- - `LabelSyncGenerator` - Generator class
481
-
482
- **Locale (Server Actions):**
483
- - `getLocale()` - Get current locale (cookie → browser → default)
484
- - `setLocale(locale)` - Set locale (saves to cookie)
485
- - `getLocales()` - Get supported locale list
486
- - `getLocaleWithInfo()`, `getLocalesWithInfo()`, `isValidLocale()`
487
-
488
- ### Server Actions API (`@spfn/cms/actions`)
489
-
490
- *Alias for `@spfn/cms/server` locale functions - available for both server and client components*
491
-
492
- ### Client-side API (`@spfn/cms/client`)
493
-
494
- - `useSection(section, options?)` - Section labels hook
495
- - `useSections(sections)` - Multiple sections hook
496
- - `useCmsStore()` - CMS store hook
497
- - `InitCms` - Client initialization component
498
-
499
- ### Management API (`@spfn/cms/api`)
500
-
501
- ⚠️ **Admin only** - Use with proper authentication
502
-
503
- - `cmsApi.cmsLabels.get(options?)` - List labels with filters
504
- - `cmsApi.cmsLabels.getById(options)` - Get label by ID
505
- - `cmsApi.cmsLabels.post(options)` - Create new label
506
- - `cmsApi.cmsLabels.update(options)` - Update label
507
- - `cmsApi.cmsLabels.delete(options)` - Delete label
508
- - `cmsApi.cmsLabelsByKey` - Get labels by key
509
- - `cmsApi.cmsValues` - Manage label values
510
- - `cmsApi.cmsPublishedCache.get(options)` - Get published cache
511
-
512
- ## Development Workflow
513
-
514
- 1. **Create/Edit JSON files** in `src/lib/labels/`
515
- 2. **Auto-sync happens** (if dev server is running)
516
- 3. **Labels immediately available** via `getSection()` or `useSection()`
517
-
518
- **Example:**
519
-
520
- ```bash
521
- # Terminal 1: Start dev server
522
- pnpm dev
523
-
524
- # Terminal 2: Edit label file
525
- echo '{"test": {"key": "layout.test", "defaultValue": "Test"}}' > src/lib/labels/layout/test.json
526
-
527
- # Auto-sync triggers
528
- # ✅ Label sync completed
529
- # Created: 1
530
- ```
98
+ This package is currently in alpha. APIs may change.
531
99
 
532
100
  ## License
533
101
 
package/dist/api.d.ts CHANGED
@@ -11,7 +11,7 @@ import '@sinclair/typebox';
11
11
  * Generated by @spfn/core codegen
12
12
  * DO NOT EDIT MANUALLY
13
13
  *
14
- * @generated 2025-11-01T16:47:27.111Z
14
+ * @generated 2025-11-02T11:18:13.331Z
15
15
  */
16
16
 
17
17
  type SaveValuesResponse = InferContract<typeof saveValuesContract>['response'];
@@ -64,7 +64,7 @@ declare const CmsValues: {
64
64
  * Generated by @spfn/core codegen
65
65
  * DO NOT EDIT MANUALLY
66
66
  *
67
- * @generated 2025-11-01T16:47:27.111Z
67
+ * @generated 2025-11-02T11:18:13.331Z
68
68
  */
69
69
 
70
70
  type GetPublishedCacheResponse = InferContract<typeof getPublishedCacheContract>['response'];
@@ -97,7 +97,7 @@ declare const CmsPublishedCache: {
97
97
  * Generated by @spfn/core codegen
98
98
  * DO NOT EDIT MANUALLY
99
99
  *
100
- * @generated 2025-11-01T16:47:27.110Z
100
+ * @generated 2025-11-02T11:18:13.331Z
101
101
  */
102
102
 
103
103
  type GetLabelByKeyResponse = InferContract<typeof getLabelByKeyContract>['response'];
@@ -132,7 +132,7 @@ declare const CmsLabelsByKey: {
132
132
  * Generated by @spfn/core codegen
133
133
  * DO NOT EDIT MANUALLY
134
134
  *
135
- * @generated 2025-11-01T16:47:27.109Z
135
+ * @generated 2025-11-02T11:18:13.330Z
136
136
  */
137
137
 
138
138
  type GetLabelsResponse = InferContract<typeof getLabelsContract>['response'];
@@ -157,6 +157,7 @@ declare const CmsLabels: {
157
157
  query?: GetLabelsQuery;
158
158
  }) => Promise<{
159
159
  labels: {
160
+ defaultValue?: any;
160
161
  section: string;
161
162
  id: number;
162
163
  key: string;
@@ -245,6 +246,7 @@ declare const cmsApi: {
245
246
  query?: GetLabelsQuery;
246
247
  }) => Promise<{
247
248
  labels: {
249
+ defaultValue?: any;
248
250
  section: string;
249
251
  id: number;
250
252
  key: string;