@spfn/cms 0.1.0-alpha.8 → 0.1.0-alpha.81
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 +28 -416
- package/dist/{helpers/locale.actions.d.ts → actions-BEFWwQsh.d.ts} +70 -7
- package/dist/actions.d.ts +2 -9
- package/dist/actions.js +99 -10
- package/dist/actions.js.map +1 -1
- package/dist/api.d.ts +319 -0
- package/dist/api.js +467 -0
- package/dist/api.js.map +1 -0
- package/dist/client.d.ts +135 -127
- package/dist/client.js +1318 -59
- package/dist/client.js.map +1 -1
- package/dist/{types.d.ts → index-Dh5FjWzR.d.ts} +45 -7
- package/dist/index.d.ts +112 -16
- package/dist/index.js +625 -23
- package/dist/index.js.map +1 -1
- package/dist/label-sync-generator-B0EmvtWM.d.ts +32 -0
- package/dist/lib/contracts/labels.d.ts +244 -0
- package/dist/lib/contracts/labels.js +269 -0
- package/dist/lib/contracts/labels.js.map +1 -0
- package/dist/lib/contracts/published-cache.d.ts +48 -0
- package/dist/lib/contracts/published-cache.js +49 -0
- package/dist/lib/contracts/published-cache.js.map +1 -0
- package/dist/lib/contracts/values.d.ts +71 -0
- package/dist/lib/contracts/values.js +104 -0
- package/dist/lib/contracts/values.js.map +1 -0
- package/dist/locale.constants-BNkSdNP1.d.ts +108 -0
- package/dist/{entities → server/entities}/cms-audit-logs.d.ts +15 -70
- package/dist/server/entities/cms-audit-logs.js +78 -0
- package/dist/server/entities/cms-audit-logs.js.map +1 -0
- package/dist/{entities → server/entities}/cms-draft-cache.d.ts +13 -73
- package/dist/server/entities/cms-draft-cache.js +38 -0
- package/dist/server/entities/cms-draft-cache.js.map +1 -0
- package/dist/{entities → server/entities}/cms-label-values.d.ts +16 -67
- package/dist/server/entities/cms-label-values.js +81 -0
- package/dist/server/entities/cms-label-values.js.map +1 -0
- package/dist/{entities → server/entities}/cms-labels.d.ts +17 -14
- package/dist/server/entities/cms-labels.js +42 -0
- package/dist/server/entities/cms-labels.js.map +1 -0
- package/dist/{entities → server/entities}/cms-published-cache.d.ts +14 -69
- package/dist/server/entities/cms-published-cache.js +36 -0
- package/dist/server/entities/cms-published-cache.js.map +1 -0
- package/dist/server/entities/index.d.ts +6 -0
- package/dist/server/entities/index.js +185 -0
- package/dist/server/entities/index.js.map +1 -0
- package/dist/server/generators/index.d.ts +19 -0
- package/dist/server/generators/index.js +731 -0
- package/dist/server/generators/index.js.map +1 -0
- package/dist/server/labels/index.d.ts +1 -0
- package/dist/server/labels/index.js +33 -0
- package/dist/server/labels/index.js.map +1 -0
- package/dist/server/repositories/index.d.ts +212 -0
- package/dist/server/repositories/index.js +418 -0
- package/dist/server/repositories/index.js.map +1 -0
- package/dist/server/routes/labels/[id]/admin/index.js +679 -0
- package/dist/server/routes/labels/[id]/admin/index.js.map +1 -0
- package/dist/server/routes/labels/[id]/index.js +576 -0
- package/dist/server/routes/labels/[id]/index.js.map +1 -0
- package/dist/server/routes/labels/[id]/publish/index.js +720 -0
- package/dist/server/routes/labels/[id]/publish/index.js.map +1 -0
- package/dist/server/routes/labels/[id]/versions/index.js +548 -0
- package/dist/server/routes/labels/[id]/versions/index.js.map +1 -0
- package/dist/server/routes/labels/_id_/admin/index.d.ts +11 -0
- package/dist/{routes/labels/[id] → server/routes/labels/_id_}/index.d.ts +5 -3
- package/dist/server/routes/labels/_id_/publish/index.d.ts +11 -0
- package/dist/server/routes/labels/_id_/versions/index.d.ts +11 -0
- package/dist/server/routes/labels/by-key/[key]/index.js +525 -0
- package/dist/server/routes/labels/by-key/[key]/index.js.map +1 -0
- package/dist/server/routes/labels/by-key/_key_/index.d.ts +10 -0
- package/dist/server/routes/labels/index.d.ts +12 -0
- package/dist/server/routes/labels/index.js +684 -0
- package/dist/server/routes/labels/index.js.map +1 -0
- package/dist/server/routes/published-cache/index.d.ts +11 -0
- package/dist/server/routes/published-cache/index.js +337 -0
- package/dist/server/routes/published-cache/index.js.map +1 -0
- package/dist/server/routes/values/[labelId]/[version]/index.js +457 -0
- package/dist/server/routes/values/[labelId]/[version]/index.js.map +1 -0
- package/dist/server/routes/values/[labelId]/index.js +452 -0
- package/dist/server/routes/values/[labelId]/index.js.map +1 -0
- package/dist/server/routes/values/_labelId_/_version_/index.d.ts +10 -0
- package/dist/server/routes/values/_labelId_/index.d.ts +10 -0
- package/dist/server.d.ts +77 -7
- package/dist/server.js +1747 -247
- package/dist/server.js.map +1 -1
- package/migrations/0000_init.sql +3 -0
- package/migrations/0001_far_lady_vermin.sql +86 -0
- package/migrations/0002_heavy_the_enforcers.sql +2 -0
- package/migrations/0003_rare_runaways.sql +1 -0
- package/migrations/meta/0000_snapshot.json +15 -0
- package/migrations/meta/0001_snapshot.json +687 -0
- package/migrations/meta/0002_snapshot.json +686 -0
- package/migrations/meta/0003_snapshot.json +563 -0
- package/migrations/meta/_journal.json +34 -0
- package/package.json +55 -36
- package/dist/actions.d.ts.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/cms.config.d.ts +0 -77
- package/dist/cms.config.d.ts.map +0 -1
- package/dist/cms.config.js +0 -111
- package/dist/cms.config.js.map +0 -1
- package/dist/entities/cms-audit-logs.d.ts.map +0 -1
- package/dist/entities/cms-audit-logs.js +0 -103
- package/dist/entities/cms-audit-logs.js.map +0 -1
- package/dist/entities/cms-draft-cache.d.ts.map +0 -1
- package/dist/entities/cms-draft-cache.js +0 -112
- package/dist/entities/cms-draft-cache.js.map +0 -1
- package/dist/entities/cms-label-values.d.ts.map +0 -1
- package/dist/entities/cms-label-values.js +0 -105
- package/dist/entities/cms-label-values.js.map +0 -1
- package/dist/entities/cms-label-versions.d.ts +0 -207
- package/dist/entities/cms-label-versions.d.ts.map +0 -1
- package/dist/entities/cms-label-versions.js +0 -80
- package/dist/entities/cms-label-versions.js.map +0 -1
- package/dist/entities/cms-labels.d.ts.map +0 -1
- package/dist/entities/cms-labels.js +0 -48
- package/dist/entities/cms-labels.js.map +0 -1
- package/dist/entities/cms-published-cache.d.ts.map +0 -1
- package/dist/entities/cms-published-cache.js +0 -103
- package/dist/entities/cms-published-cache.js.map +0 -1
- package/dist/entities/index.d.ts +0 -10
- package/dist/entities/index.d.ts.map +0 -1
- package/dist/entities/index.js +0 -10
- package/dist/entities/index.js.map +0 -1
- package/dist/generators/index.d.ts +0 -19
- package/dist/generators/index.d.ts.map +0 -1
- package/dist/generators/index.js +0 -19
- package/dist/generators/index.js.map +0 -1
- package/dist/generators/label-sync-generator.d.ts +0 -33
- package/dist/generators/label-sync-generator.d.ts.map +0 -1
- package/dist/generators/label-sync-generator.js +0 -86
- package/dist/generators/label-sync-generator.js.map +0 -1
- package/dist/helpers/locale.actions.d.ts.map +0 -1
- package/dist/helpers/locale.actions.js +0 -210
- package/dist/helpers/locale.actions.js.map +0 -1
- package/dist/helpers/locale.constants.d.ts +0 -10
- package/dist/helpers/locale.constants.d.ts.map +0 -1
- package/dist/helpers/locale.constants.js +0 -10
- package/dist/helpers/locale.constants.js.map +0 -1
- package/dist/helpers/locale.d.ts +0 -17
- package/dist/helpers/locale.d.ts.map +0 -1
- package/dist/helpers/locale.js +0 -20
- package/dist/helpers/locale.js.map +0 -1
- package/dist/helpers/sync.d.ts +0 -41
- package/dist/helpers/sync.d.ts.map +0 -1
- package/dist/helpers/sync.js +0 -309
- package/dist/helpers/sync.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/init.d.ts +0 -31
- package/dist/init.d.ts.map +0 -1
- package/dist/init.js +0 -36
- package/dist/init.js.map +0 -1
- package/dist/labels/helpers.d.ts +0 -31
- package/dist/labels/helpers.d.ts.map +0 -1
- package/dist/labels/helpers.js +0 -60
- package/dist/labels/helpers.js.map +0 -1
- package/dist/labels/index.d.ts +0 -7
- package/dist/labels/index.d.ts.map +0 -1
- package/dist/labels/index.js +0 -7
- package/dist/labels/index.js.map +0 -1
- package/dist/repositories/cms-draft-cache.repository.d.ts +0 -62
- package/dist/repositories/cms-draft-cache.repository.d.ts.map +0 -1
- package/dist/repositories/cms-draft-cache.repository.js +0 -56
- package/dist/repositories/cms-draft-cache.repository.js.map +0 -1
- package/dist/repositories/cms-label-values.repository.d.ts +0 -32
- package/dist/repositories/cms-label-values.repository.d.ts.map +0 -1
- package/dist/repositories/cms-label-values.repository.js +0 -72
- package/dist/repositories/cms-label-values.repository.js.map +0 -1
- package/dist/repositories/cms-labels.repository.d.ts +0 -53
- package/dist/repositories/cms-labels.repository.d.ts.map +0 -1
- package/dist/repositories/cms-labels.repository.js +0 -77
- package/dist/repositories/cms-labels.repository.js.map +0 -1
- package/dist/repositories/cms-published-cache.repository.d.ts +0 -53
- package/dist/repositories/cms-published-cache.repository.d.ts.map +0 -1
- package/dist/repositories/cms-published-cache.repository.js +0 -54
- package/dist/repositories/cms-published-cache.repository.js.map +0 -1
- package/dist/repositories/index.d.ts +0 -8
- package/dist/repositories/index.d.ts.map +0 -1
- package/dist/repositories/index.js +0 -9
- package/dist/repositories/index.js.map +0 -1
- package/dist/routes/labels/[id]/contract.d.ts +0 -68
- package/dist/routes/labels/[id]/contract.d.ts.map +0 -1
- package/dist/routes/labels/[id]/contract.js +0 -84
- package/dist/routes/labels/[id]/contract.js.map +0 -1
- package/dist/routes/labels/[id]/index.d.ts.map +0 -1
- package/dist/routes/labels/[id]/index.js +0 -96
- package/dist/routes/labels/[id]/index.js.map +0 -1
- package/dist/routes/labels/by-key/[key]/contract.d.ts +0 -24
- package/dist/routes/labels/by-key/[key]/contract.d.ts.map +0 -1
- package/dist/routes/labels/by-key/[key]/contract.js +0 -28
- package/dist/routes/labels/by-key/[key]/contract.js.map +0 -1
- package/dist/routes/labels/by-key/[key]/index.d.ts +0 -8
- package/dist/routes/labels/by-key/[key]/index.d.ts.map +0 -1
- package/dist/routes/labels/by-key/[key]/index.js +0 -32
- package/dist/routes/labels/by-key/[key]/index.js.map +0 -1
- package/dist/routes/labels/contract.d.ts +0 -59
- package/dist/routes/labels/contract.d.ts.map +0 -1
- package/dist/routes/labels/contract.js +0 -75
- package/dist/routes/labels/contract.js.map +0 -1
- package/dist/routes/labels/index.d.ts +0 -10
- package/dist/routes/labels/index.d.ts.map +0 -1
- package/dist/routes/labels/index.js +0 -73
- package/dist/routes/labels/index.js.map +0 -1
- package/dist/routes/published-cache/contract.d.ts +0 -25
- package/dist/routes/published-cache/contract.d.ts.map +0 -1
- package/dist/routes/published-cache/contract.js +0 -35
- package/dist/routes/published-cache/contract.js.map +0 -1
- package/dist/routes/published-cache/index.d.ts +0 -8
- package/dist/routes/published-cache/index.d.ts.map +0 -1
- package/dist/routes/published-cache/index.js +0 -33
- package/dist/routes/published-cache/index.js.map +0 -1
- package/dist/routes/values/[labelId]/[version]/contract.d.ts +0 -29
- package/dist/routes/values/[labelId]/[version]/contract.d.ts.map +0 -1
- package/dist/routes/values/[labelId]/[version]/contract.js +0 -33
- package/dist/routes/values/[labelId]/[version]/contract.js.map +0 -1
- package/dist/routes/values/[labelId]/[version]/index.d.ts +0 -8
- package/dist/routes/values/[labelId]/[version]/index.d.ts.map +0 -1
- package/dist/routes/values/[labelId]/[version]/index.js +0 -45
- package/dist/routes/values/[labelId]/[version]/index.js.map +0 -1
- package/dist/routes/values/[labelId]/contract.d.ts +0 -38
- package/dist/routes/values/[labelId]/contract.d.ts.map +0 -1
- package/dist/routes/values/[labelId]/contract.js +0 -59
- package/dist/routes/values/[labelId]/contract.js.map +0 -1
- package/dist/routes/values/[labelId]/index.d.ts +0 -8
- package/dist/routes/values/[labelId]/index.d.ts.map +0 -1
- package/dist/routes/values/[labelId]/index.js +0 -42
- package/dist/routes/values/[labelId]/index.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/store.d.ts +0 -87
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -205
- package/dist/store.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -7
- package/dist/types.js.map +0 -1
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
|
-
- 📁
|
|
8
|
-
- 🔄
|
|
9
|
-
- 🌐
|
|
10
|
-
- 🍪
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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 SPFN 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/cms/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/cms/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/cms/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,66 +31,24 @@ src/cms/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
34
|
}
|
|
110
35
|
}
|
|
111
36
|
```
|
|
112
37
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
```json
|
|
116
|
-
{
|
|
117
|
-
"copyright": {
|
|
118
|
-
"key": "layout.footer.copyright",
|
|
119
|
-
"defaultValue": "© {year} Company. All rights reserved."
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
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
|
-
|
|
130
|
-
import { initLabelSync } from '@spfn/cms';
|
|
41
|
+
// src/server/server.config.ts
|
|
42
|
+
import { initLabelSync } from '@spfn/cms/server';
|
|
131
43
|
|
|
132
44
|
export default {
|
|
133
45
|
beforeRoutes: async (app) => {
|
|
134
|
-
await initLabelSync({
|
|
135
|
-
verbose: true,
|
|
136
|
-
labelsDir: 'src/cms/labels' // Optional, this is the default
|
|
137
|
-
});
|
|
46
|
+
await initLabelSync({ verbose: true });
|
|
138
47
|
},
|
|
139
48
|
} satisfies ServerConfig;
|
|
140
49
|
```
|
|
141
50
|
|
|
142
|
-
### 3.
|
|
143
|
-
|
|
144
|
-
Your `.spfnrc.json` should include:
|
|
145
|
-
|
|
146
|
-
```json
|
|
147
|
-
{
|
|
148
|
-
"codegen": {
|
|
149
|
-
"generators": [
|
|
150
|
-
{
|
|
151
|
-
"name": "@spfn/cms:label-sync",
|
|
152
|
-
"enabled": true
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
This is automatically configured when you run `pnpm spfn add @spfn/cms`.
|
|
160
|
-
|
|
161
|
-
### 4. Use Labels in Your App
|
|
51
|
+
### 3. Use in Your App
|
|
162
52
|
|
|
163
53
|
**Server Component:**
|
|
164
54
|
|
|
@@ -166,22 +56,11 @@ This is automatically configured when you run `pnpm spfn add @spfn/cms`.
|
|
|
166
56
|
import { getSection } from '@spfn/cms/server';
|
|
167
57
|
|
|
168
58
|
export default async function HomePage() {
|
|
169
|
-
const { t } = await getSection('
|
|
170
|
-
|
|
171
|
-
return <h1>{t('nav.team')}</h1>;
|
|
59
|
+
const { t } = await getSection('home');
|
|
60
|
+
return <h1>{t('hero.title')}</h1>;
|
|
172
61
|
}
|
|
173
62
|
```
|
|
174
63
|
|
|
175
|
-
**With variable substitution:**
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
const { t } = await getSection('layout');
|
|
179
|
-
const copyright = t('footer.copyright', undefined, {
|
|
180
|
-
year: new Date().getFullYear()
|
|
181
|
-
});
|
|
182
|
-
// → "© 2025 Company. All rights reserved."
|
|
183
|
-
```
|
|
184
|
-
|
|
185
64
|
**Client Component:**
|
|
186
65
|
|
|
187
66
|
```typescript
|
|
@@ -189,301 +68,34 @@ const copyright = t('footer.copyright', undefined, {
|
|
|
189
68
|
import { useSection } from '@spfn/cms/client';
|
|
190
69
|
|
|
191
70
|
export default function Nav() {
|
|
192
|
-
const { t
|
|
193
|
-
|
|
194
|
-
if (loading) return <div>Loading...</div>;
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<nav>
|
|
198
|
-
<a>{t('nav.about')}</a>
|
|
199
|
-
<a>{t('nav.services')}</a>
|
|
200
|
-
</nav>
|
|
201
|
-
);
|
|
71
|
+
const { t } = useSection('layout', { autoLoad: true });
|
|
72
|
+
return <nav><a>{t('nav.about')}</a></nav>;
|
|
202
73
|
}
|
|
203
74
|
```
|
|
204
75
|
|
|
205
|
-
##
|
|
206
|
-
|
|
207
|
-
```
|
|
208
|
-
src/cms/labels/
|
|
209
|
-
layout/ # Section: layout
|
|
210
|
-
nav.json # Category: nav
|
|
211
|
-
footer.json # Category: footer
|
|
212
|
-
home/ # Section: home
|
|
213
|
-
hero.json # Category: hero
|
|
214
|
-
features.json # Category: features
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
**How it maps:**
|
|
218
|
-
- Folder name = Section name
|
|
219
|
-
- JSON file name = Category name (for organization only)
|
|
220
|
-
- Inside JSON: `key` field defines the actual label key
|
|
221
|
-
|
|
222
|
-
Example:
|
|
223
|
-
```
|
|
224
|
-
src/cms/labels/layout/nav.json:
|
|
225
|
-
key: "layout.nav.team" → t('nav.team') in code
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## JSON Label Format
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
{
|
|
232
|
-
"labelName": {
|
|
233
|
-
"key": "section.category.name", // Required: Unique identifier
|
|
234
|
-
"defaultValue": "Text" | {...}, // Required: String or i18n object
|
|
235
|
-
"description": "Optional description" // Optional: For documentation
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
```
|
|
76
|
+
## Documentation
|
|
239
77
|
|
|
240
|
-
**
|
|
241
|
-
```json
|
|
242
|
-
{
|
|
243
|
-
"welcome": {
|
|
244
|
-
"key": "home.welcome",
|
|
245
|
-
"defaultValue": "Welcome"
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
```
|
|
78
|
+
📖 **[Full Documentation](../../docs/ecosystem/cms/index.md)**
|
|
249
79
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
"ko": "환영합니다",
|
|
257
|
-
"en": "Welcome",
|
|
258
|
-
"ja": "ようこそ"
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**Variable placeholders:**
|
|
265
|
-
```json
|
|
266
|
-
{
|
|
267
|
-
"greeting": {
|
|
268
|
-
"key": "home.greeting",
|
|
269
|
-
"defaultValue": "Hello, {name}!"
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
Usage:
|
|
275
|
-
```typescript
|
|
276
|
-
t('greeting', undefined, { name: 'John' })
|
|
277
|
-
// → "Hello, John!"
|
|
278
|
-
```
|
|
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
|
|
279
86
|
|
|
280
87
|
## Configuration
|
|
281
88
|
|
|
282
|
-
### Environment Variables
|
|
283
|
-
|
|
284
|
-
Configure CMS behavior via environment variables in `.env.local`:
|
|
285
|
-
|
|
286
89
|
```bash
|
|
287
|
-
#
|
|
90
|
+
# .env.local
|
|
288
91
|
SPFN_CMS_DEFAULT_LOCALE=ko
|
|
289
|
-
|
|
290
|
-
# Supported locales, comma-separated (default: 'en,ko')
|
|
291
92
|
SPFN_CMS_SUPPORTED_LOCALES=en,ko,ja
|
|
292
|
-
|
|
293
|
-
# Auto-detect browser language (default: true)
|
|
294
93
|
SPFN_CMS_DETECT_BROWSER_LANGUAGE=true
|
|
295
94
|
```
|
|
296
95
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
Override configuration at runtime (mainly for testing):
|
|
300
|
-
|
|
301
|
-
```typescript
|
|
302
|
-
import { configureCms, getCmsConfig } from '@spfn/cms';
|
|
303
|
-
|
|
304
|
-
// Get current configuration
|
|
305
|
-
const config = getCmsConfig();
|
|
306
|
-
console.log(config.defaultLocale); // 'ko'
|
|
307
|
-
console.log(config.supportedLocales); // ['ko', 'en']
|
|
308
|
-
|
|
309
|
-
// Update configuration
|
|
310
|
-
configureCms({
|
|
311
|
-
defaultLocale: 'en',
|
|
312
|
-
supportedLocales: ['en', 'ko', 'ja'],
|
|
313
|
-
detectBrowserLanguage: false
|
|
314
|
-
});
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
## Locale Management
|
|
318
|
-
|
|
319
|
-
### Automatic Locale Detection
|
|
320
|
-
|
|
321
|
-
The CMS automatically manages user locale with the following priority:
|
|
322
|
-
|
|
323
|
-
1. **Cookie** - User's explicitly selected locale (persisted)
|
|
324
|
-
2. **Browser Language** - Auto-detected from `Accept-Language` header (if enabled)
|
|
325
|
-
3. **Default Locale** - System default from environment variables
|
|
326
|
-
|
|
327
|
-
### Server Actions (`@spfn/cms/actions`)
|
|
328
|
-
|
|
329
|
-
Use Server Actions for locale management in both server and client components:
|
|
330
|
-
|
|
331
|
-
**Get current locale:**
|
|
332
|
-
|
|
333
|
-
```typescript
|
|
334
|
-
// Server Component
|
|
335
|
-
import { getLocale } from '@spfn/cms/actions';
|
|
336
|
-
|
|
337
|
-
export default async function RootLayout({ children }) {
|
|
338
|
-
const locale = await getLocale();
|
|
339
|
-
|
|
340
|
-
return <html lang={locale}>{children}</html>;
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
// Client Component
|
|
346
|
-
'use client';
|
|
347
|
-
import { getLocale } from '@spfn/cms/actions';
|
|
348
|
-
import { useEffect, useState } from 'react';
|
|
349
|
-
|
|
350
|
-
export default function LanguageSwitcher() {
|
|
351
|
-
const [locale, setLocale] = useState('');
|
|
352
|
-
|
|
353
|
-
useEffect(() => {
|
|
354
|
-
getLocale().then(setLocale);
|
|
355
|
-
}, []);
|
|
356
|
-
|
|
357
|
-
return <div>Current: {locale}</div>;
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
**Change locale:**
|
|
362
|
-
|
|
363
|
-
```typescript
|
|
364
|
-
import { setLocale } from '@spfn/cms/actions';
|
|
365
|
-
|
|
366
|
-
async function changeLanguage(newLocale: string) {
|
|
367
|
-
await setLocale(newLocale);
|
|
368
|
-
window.location.reload(); // Reload to apply changes
|
|
369
|
-
}
|
|
370
|
-
```
|
|
96
|
+
## Development Status
|
|
371
97
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
import { getLocales } from '@spfn/cms/actions';
|
|
376
|
-
|
|
377
|
-
const locales = await getLocales(); // ['ko', 'en', 'ja']
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### Auto-detect Locale in Server Components
|
|
381
|
-
|
|
382
|
-
When `locale` is not specified, `getSection()` automatically uses the detected locale:
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
import { getSection } from '@spfn/cms/server';
|
|
386
|
-
|
|
387
|
-
// Auto-detects locale from cookie → browser → default
|
|
388
|
-
const { t } = await getSection('home');
|
|
389
|
-
|
|
390
|
-
// Or explicitly specify locale
|
|
391
|
-
const { t: tEn } = await getSection('home', 'en');
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
## Documentation
|
|
395
|
-
|
|
396
|
-
- **[Label Auto-Sync Guide](./LABEL_SYNC_GUIDE.md)** - Detailed configuration guide
|
|
397
|
-
- **[Examples](./examples/)** - Usage examples
|
|
398
|
-
|
|
399
|
-
## Architecture
|
|
400
|
-
|
|
401
|
-
```
|
|
402
|
-
JSON Files (src/cms/labels/**/*.json)
|
|
403
|
-
↓
|
|
404
|
-
loadLabelsFromJson()
|
|
405
|
-
↓
|
|
406
|
-
┌─────────────────────┐
|
|
407
|
-
│ LabelSyncGenerator │ ← File watcher (development)
|
|
408
|
-
│ initLabelSync() │ ← Server startup
|
|
409
|
-
└─────────────────────┘
|
|
410
|
-
↓
|
|
411
|
-
syncAll()
|
|
412
|
-
↓
|
|
413
|
-
┌─────────────────────┐
|
|
414
|
-
│ PostgreSQL DB │
|
|
415
|
-
│ - cms_labels │
|
|
416
|
-
│ - published_cache │ ⭐ Used by API
|
|
417
|
-
└─────────────────────┘
|
|
418
|
-
↓ HTTP API
|
|
419
|
-
┌─────────────────────┐
|
|
420
|
-
│ Application │
|
|
421
|
-
│ - getSection() │
|
|
422
|
-
│ - useSection() │
|
|
423
|
-
└─────────────────────┘
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
## API Reference
|
|
427
|
-
|
|
428
|
-
### Server-side API
|
|
429
|
-
|
|
430
|
-
- `getSection(section, locale?)` - Get section labels (auto-detects locale if not specified)
|
|
431
|
-
- `getSections(sections, locale?)` - Get multiple sections (auto-detects locale if not specified)
|
|
432
|
-
- `initLabelSync(options?)` - Sync labels on server startup
|
|
433
|
-
|
|
434
|
-
### Server Actions API (`@spfn/cms/actions`)
|
|
435
|
-
|
|
436
|
-
Available for both server and client components:
|
|
437
|
-
|
|
438
|
-
- `getLocale()` - Get current locale (cookie → browser → default)
|
|
439
|
-
- `setLocale(locale)` - Set locale (saves to cookie)
|
|
440
|
-
- `getLocales()` - Get supported locale list
|
|
441
|
-
- `LOCALE_COOKIE_KEY` - Locale cookie key constant
|
|
442
|
-
|
|
443
|
-
### Configuration API
|
|
444
|
-
|
|
445
|
-
- `getCmsConfig()` - Get current CMS configuration
|
|
446
|
-
- `configureCms(config)` - Update configuration (runtime)
|
|
447
|
-
- `resetCmsConfig()` - Reset configuration to defaults
|
|
448
|
-
|
|
449
|
-
### Client-side API (`@spfn/cms/client`)
|
|
450
|
-
|
|
451
|
-
- `useSection(section, options?)` - Section labels hook
|
|
452
|
-
- `useSections(sections)` - Multiple sections hook
|
|
453
|
-
- `useCmsStore()` - CMS store hook
|
|
454
|
-
- `cmsApi` - CMS API client
|
|
455
|
-
- `InitCms` - Client initialization component
|
|
456
|
-
|
|
457
|
-
### Sync API
|
|
458
|
-
|
|
459
|
-
- `loadLabelsFromJson(labelsDir)` - Load labels from JSON files
|
|
460
|
-
- `syncAll(sections, options?)` - Sync all sections
|
|
461
|
-
- `syncSection(definition, options?)` - Sync specific section
|
|
462
|
-
|
|
463
|
-
### Codegen Integration
|
|
464
|
-
|
|
465
|
-
- `createLabelSyncGenerator(config?)` - Generator factory
|
|
466
|
-
- `LabelSyncGenerator` - Generator class
|
|
467
|
-
|
|
468
|
-
## Development Workflow
|
|
469
|
-
|
|
470
|
-
1. **Create/Edit JSON files** in `src/cms/labels/`
|
|
471
|
-
2. **Auto-sync happens** (if dev server is running)
|
|
472
|
-
3. **Labels immediately available** via `getSection()` or `useSection()`
|
|
473
|
-
|
|
474
|
-
**Example:**
|
|
475
|
-
|
|
476
|
-
```bash
|
|
477
|
-
# Terminal 1: Start dev server
|
|
478
|
-
pnpm dev
|
|
479
|
-
|
|
480
|
-
# Terminal 2: Edit label file
|
|
481
|
-
echo '{"test": {"key": "layout.test", "defaultValue": "Test"}}' > src/cms/labels/layout/test.json
|
|
482
|
-
|
|
483
|
-
# Auto-sync triggers
|
|
484
|
-
# ✅ Label sync completed
|
|
485
|
-
# Created: 1
|
|
486
|
-
```
|
|
98
|
+
This package is currently in alpha. APIs may change.
|
|
487
99
|
|
|
488
100
|
## License
|
|
489
101
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { f as LocaleInfo } from './locale.constants-BNkSdNP1.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* 현재 locale 가져오기 (Server Action)
|
|
3
5
|
*
|
|
@@ -13,7 +15,7 @@
|
|
|
13
15
|
* @example
|
|
14
16
|
* ```tsx
|
|
15
17
|
* // Server Component
|
|
16
|
-
* import { getLocale } from '@spfn/cms';
|
|
18
|
+
* import { getLocale } from '@spfn/cms/actions';
|
|
17
19
|
*
|
|
18
20
|
* export default async function Page()
|
|
19
21
|
* {
|
|
@@ -40,7 +42,7 @@
|
|
|
40
42
|
* }
|
|
41
43
|
* ```
|
|
42
44
|
*/
|
|
43
|
-
|
|
45
|
+
declare function getLocale(): Promise<string>;
|
|
44
46
|
/**
|
|
45
47
|
* Locale 설정하기 (Server Action)
|
|
46
48
|
*
|
|
@@ -53,7 +55,7 @@ export declare function getLocale(): Promise<string>;
|
|
|
53
55
|
* @example
|
|
54
56
|
* ```tsx
|
|
55
57
|
* // Server Component (Server Action)
|
|
56
|
-
* import { setLocale } from '@spfn/cms';
|
|
58
|
+
* import { setLocale } from '@spfn/cms/actions';
|
|
57
59
|
*
|
|
58
60
|
* export default async function Page()
|
|
59
61
|
* {
|
|
@@ -84,7 +86,7 @@ export declare function getLocale(): Promise<string>;
|
|
|
84
86
|
* }
|
|
85
87
|
* ```
|
|
86
88
|
*/
|
|
87
|
-
|
|
89
|
+
declare function setLocale(locale: string): Promise<void>;
|
|
88
90
|
/**
|
|
89
91
|
* 지원하는 locale 목록 가져오기 (Server Action)
|
|
90
92
|
*
|
|
@@ -95,7 +97,7 @@ export declare function setLocale(locale: string): Promise<void>;
|
|
|
95
97
|
* @example
|
|
96
98
|
* ```tsx
|
|
97
99
|
* // Server Component
|
|
98
|
-
* import { getLocales } from '@spfn/cms';
|
|
100
|
+
* import { getLocales } from '@spfn/cms/actions';
|
|
99
101
|
*
|
|
100
102
|
* export default async function Page()
|
|
101
103
|
* {
|
|
@@ -128,5 +130,66 @@ export declare function setLocale(locale: string): Promise<void>;
|
|
|
128
130
|
* }
|
|
129
131
|
* ```
|
|
130
132
|
*/
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
declare function getLocales(): Promise<string[]>;
|
|
134
|
+
/**
|
|
135
|
+
* 현재 locale과 상세 정보 함께 가져오기 (Server Action)
|
|
136
|
+
*
|
|
137
|
+
* locale 코드와 함께 국가 코드, 국기, 전화번호 코드 등의 상세 정보를 반환합니다.
|
|
138
|
+
*
|
|
139
|
+
* @returns Locale 코드와 LocaleInfo 객체
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```tsx
|
|
143
|
+
* // Server Component
|
|
144
|
+
* import { getLocaleWithInfo } from '@spfn/cms/actions';
|
|
145
|
+
*
|
|
146
|
+
* export default async function Page()
|
|
147
|
+
* {
|
|
148
|
+
* const { locale, info } = await getLocaleWithInfo();
|
|
149
|
+
*
|
|
150
|
+
* return (
|
|
151
|
+
* <div>
|
|
152
|
+
* <span>{info?.flag}</span>
|
|
153
|
+
* <span>{info?.nativeName}</span>
|
|
154
|
+
* <span>{info?.dialCode}</span>
|
|
155
|
+
* </div>
|
|
156
|
+
* );
|
|
157
|
+
* }
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
declare function getLocaleWithInfo(): Promise<{
|
|
161
|
+
locale: string;
|
|
162
|
+
info: LocaleInfo | undefined;
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* 지원하는 모든 locale과 상세 정보 가져오기 (Server Action)
|
|
166
|
+
*
|
|
167
|
+
* 시스템이 지원하는 모든 locale의 상세 정보를 배열로 반환합니다.
|
|
168
|
+
* 언어 선택 UI를 만들 때 유용합니다.
|
|
169
|
+
*
|
|
170
|
+
* @returns LocaleInfo 배열
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```tsx
|
|
174
|
+
* // Server Component
|
|
175
|
+
* import { getLocalesWithInfo } from '@spfn/cms/actions';
|
|
176
|
+
*
|
|
177
|
+
* export default async function LanguageSelector()
|
|
178
|
+
* {
|
|
179
|
+
* const locales = await getLocalesWithInfo();
|
|
180
|
+
*
|
|
181
|
+
* return (
|
|
182
|
+
* <select>
|
|
183
|
+
* {locales.map(info => (
|
|
184
|
+
* <option key={info.locale} value={info.locale}>
|
|
185
|
+
* {info.flag} {info.nativeName}
|
|
186
|
+
* </option>
|
|
187
|
+
* ))}
|
|
188
|
+
* </select>
|
|
189
|
+
* );
|
|
190
|
+
* }
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare function getLocalesWithInfo(): Promise<LocaleInfo[]>;
|
|
194
|
+
|
|
195
|
+
export { getLocales as a, getLocaleWithInfo as b, getLocalesWithInfo as c, getLocale as g, setLocale as s };
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,9 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Server Actions
|
|
5
|
-
* 서버/클라이언트 컴포넌트 양쪽에서 사용 가능한 Server Actions
|
|
6
|
-
*/
|
|
7
|
-
export { getLocale, setLocale, getLocales, } from './helpers/locale.actions.js';
|
|
8
|
-
export { LOCALE_COOKIE_KEY } from './helpers/locale.constants.js';
|
|
9
|
-
//# sourceMappingURL=actions.d.ts.map
|
|
1
|
+
export { g as getLocale, a as getLocales, s as setLocale } from './actions-BEFWwQsh.js';
|
|
2
|
+
export { L as LOCALE_COOKIE_KEY } from './locale.constants-BNkSdNP1.js';
|