@spfn/cms 0.1.0-alpha.5 → 0.1.0-alpha.60

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 (176) hide show
  1. package/dist/actions.d.ts +140 -6
  2. package/dist/actions.js +97 -10
  3. package/dist/actions.js.map +1 -1
  4. package/dist/client.d.ts +43 -9
  5. package/dist/client.js +406 -56
  6. package/dist/client.js.map +1 -1
  7. package/dist/contracts/labels.d.ts +149 -0
  8. package/dist/contracts/labels.js +166 -0
  9. package/dist/contracts/labels.js.map +1 -0
  10. package/dist/contracts/published-cache.d.ts +25 -0
  11. package/dist/contracts/published-cache.js +32 -0
  12. package/dist/contracts/published-cache.js.map +1 -0
  13. package/dist/contracts/values.d.ts +69 -0
  14. package/dist/contracts/values.js +100 -0
  15. package/dist/contracts/values.js.map +1 -0
  16. package/dist/entities/cms-audit-logs.d.ts +15 -70
  17. package/dist/entities/cms-audit-logs.js +75 -100
  18. package/dist/entities/cms-audit-logs.js.map +1 -1
  19. package/dist/entities/cms-draft-cache.d.ts +13 -73
  20. package/dist/entities/cms-draft-cache.js +35 -109
  21. package/dist/entities/cms-draft-cache.js.map +1 -1
  22. package/dist/entities/cms-label-values.d.ts +14 -65
  23. package/dist/entities/cms-label-values.js +78 -102
  24. package/dist/entities/cms-label-values.js.map +1 -1
  25. package/dist/entities/cms-label-versions.d.ts +16 -49
  26. package/dist/entities/cms-label-versions.js +73 -77
  27. package/dist/entities/cms-label-versions.js.map +1 -1
  28. package/dist/entities/cms-labels.d.ts +17 -14
  29. package/dist/entities/cms-labels.js +39 -45
  30. package/dist/entities/cms-labels.js.map +1 -1
  31. package/dist/entities/cms-published-cache.d.ts +14 -69
  32. package/dist/entities/cms-published-cache.js +33 -100
  33. package/dist/entities/cms-published-cache.js.map +1 -1
  34. package/dist/entities/index.d.ts +7 -10
  35. package/dist/entities/index.js +217 -9
  36. package/dist/entities/index.js.map +1 -1
  37. package/dist/generators/index.d.ts +8 -7
  38. package/dist/generators/index.js +657 -17
  39. package/dist/generators/index.js.map +1 -1
  40. package/dist/index.d.ts +134 -20
  41. package/dist/index.js +1115 -23
  42. package/dist/index.js.map +1 -1
  43. package/dist/{generators/label-sync-generator.d.ts → label-sync-generator-lQrcVfja.d.ts} +9 -6
  44. package/dist/labels/index.d.ts +31 -4
  45. package/dist/labels/index.js +31 -6
  46. package/dist/labels/index.js.map +1 -1
  47. package/dist/repositories/index.d.ts +205 -6
  48. package/dist/repositories/index.js +435 -8
  49. package/dist/repositories/index.js.map +1 -1
  50. package/dist/routes/labels/[id]/index.js +499 -89
  51. package/dist/routes/labels/[id]/index.js.map +1 -1
  52. package/dist/routes/labels/{[id] → _id_}/index.d.ts +5 -3
  53. package/dist/routes/labels/by-key/[key]/index.js +453 -29
  54. package/dist/routes/labels/by-key/[key]/index.js.map +1 -1
  55. package/dist/routes/labels/by-key/_key_/index.d.ts +10 -0
  56. package/dist/routes/labels/index.d.ts +5 -3
  57. package/dist/routes/labels/index.js +488 -68
  58. package/dist/routes/labels/index.js.map +1 -1
  59. package/dist/routes/published-cache/index.d.ts +5 -3
  60. package/dist/routes/published-cache/index.js +325 -30
  61. package/dist/routes/published-cache/index.js.map +1 -1
  62. package/dist/routes/values/[labelId]/[version]/index.js +467 -41
  63. package/dist/routes/values/[labelId]/[version]/index.js.map +1 -1
  64. package/dist/routes/values/[labelId]/index.js +463 -39
  65. package/dist/routes/values/[labelId]/index.js.map +1 -1
  66. package/dist/routes/values/_labelId_/_version_/index.d.ts +10 -0
  67. package/dist/routes/values/_labelId_/index.d.ts +10 -0
  68. package/dist/server.d.ts +17 -7
  69. package/dist/server.js +263 -244
  70. package/dist/server.js.map +1 -1
  71. package/dist/store.d.ts +8 -14
  72. package/dist/store.js +396 -198
  73. package/dist/store.js.map +1 -1
  74. package/dist/types.d.ts +14 -7
  75. package/dist/types.js +0 -6
  76. package/dist/types.js.map +1 -1
  77. package/migrations/0000_condemned_centennial.sql +89 -0
  78. package/migrations/meta/0000_snapshot.json +687 -0
  79. package/migrations/meta/_journal.json +13 -0
  80. package/package.json +33 -16
  81. package/dist/actions.d.ts.map +0 -1
  82. package/dist/client.d.ts.map +0 -1
  83. package/dist/cms.config.d.ts +0 -77
  84. package/dist/cms.config.d.ts.map +0 -1
  85. package/dist/cms.config.js +0 -111
  86. package/dist/cms.config.js.map +0 -1
  87. package/dist/entities/cms-audit-logs.d.ts.map +0 -1
  88. package/dist/entities/cms-draft-cache.d.ts.map +0 -1
  89. package/dist/entities/cms-label-values.d.ts.map +0 -1
  90. package/dist/entities/cms-label-versions.d.ts.map +0 -1
  91. package/dist/entities/cms-labels.d.ts.map +0 -1
  92. package/dist/entities/cms-published-cache.d.ts.map +0 -1
  93. package/dist/entities/index.d.ts.map +0 -1
  94. package/dist/generators/index.d.ts.map +0 -1
  95. package/dist/generators/label-sync-generator.d.ts.map +0 -1
  96. package/dist/generators/label-sync-generator.js +0 -87
  97. package/dist/generators/label-sync-generator.js.map +0 -1
  98. package/dist/helpers/locale.actions.d.ts +0 -132
  99. package/dist/helpers/locale.actions.d.ts.map +0 -1
  100. package/dist/helpers/locale.actions.js +0 -210
  101. package/dist/helpers/locale.actions.js.map +0 -1
  102. package/dist/helpers/locale.constants.d.ts +0 -10
  103. package/dist/helpers/locale.constants.d.ts.map +0 -1
  104. package/dist/helpers/locale.constants.js +0 -10
  105. package/dist/helpers/locale.constants.js.map +0 -1
  106. package/dist/helpers/locale.d.ts +0 -17
  107. package/dist/helpers/locale.d.ts.map +0 -1
  108. package/dist/helpers/locale.js +0 -20
  109. package/dist/helpers/locale.js.map +0 -1
  110. package/dist/helpers/sync.d.ts +0 -41
  111. package/dist/helpers/sync.d.ts.map +0 -1
  112. package/dist/helpers/sync.js +0 -309
  113. package/dist/helpers/sync.js.map +0 -1
  114. package/dist/index.d.ts.map +0 -1
  115. package/dist/init.d.ts +0 -31
  116. package/dist/init.d.ts.map +0 -1
  117. package/dist/init.js +0 -36
  118. package/dist/init.js.map +0 -1
  119. package/dist/labels/helpers.d.ts +0 -31
  120. package/dist/labels/helpers.d.ts.map +0 -1
  121. package/dist/labels/helpers.js +0 -60
  122. package/dist/labels/helpers.js.map +0 -1
  123. package/dist/labels/index.d.ts.map +0 -1
  124. package/dist/repositories/cms-draft-cache.repository.d.ts +0 -62
  125. package/dist/repositories/cms-draft-cache.repository.d.ts.map +0 -1
  126. package/dist/repositories/cms-draft-cache.repository.js +0 -56
  127. package/dist/repositories/cms-draft-cache.repository.js.map +0 -1
  128. package/dist/repositories/cms-label-values.repository.d.ts +0 -32
  129. package/dist/repositories/cms-label-values.repository.d.ts.map +0 -1
  130. package/dist/repositories/cms-label-values.repository.js +0 -72
  131. package/dist/repositories/cms-label-values.repository.js.map +0 -1
  132. package/dist/repositories/cms-labels.repository.d.ts +0 -53
  133. package/dist/repositories/cms-labels.repository.d.ts.map +0 -1
  134. package/dist/repositories/cms-labels.repository.js +0 -77
  135. package/dist/repositories/cms-labels.repository.js.map +0 -1
  136. package/dist/repositories/cms-published-cache.repository.d.ts +0 -53
  137. package/dist/repositories/cms-published-cache.repository.d.ts.map +0 -1
  138. package/dist/repositories/cms-published-cache.repository.js +0 -54
  139. package/dist/repositories/cms-published-cache.repository.js.map +0 -1
  140. package/dist/repositories/index.d.ts.map +0 -1
  141. package/dist/routes/labels/[id]/contract.d.ts +0 -68
  142. package/dist/routes/labels/[id]/contract.d.ts.map +0 -1
  143. package/dist/routes/labels/[id]/contract.js +0 -84
  144. package/dist/routes/labels/[id]/contract.js.map +0 -1
  145. package/dist/routes/labels/[id]/index.d.ts.map +0 -1
  146. package/dist/routes/labels/by-key/[key]/contract.d.ts +0 -24
  147. package/dist/routes/labels/by-key/[key]/contract.d.ts.map +0 -1
  148. package/dist/routes/labels/by-key/[key]/contract.js +0 -28
  149. package/dist/routes/labels/by-key/[key]/contract.js.map +0 -1
  150. package/dist/routes/labels/by-key/[key]/index.d.ts +0 -8
  151. package/dist/routes/labels/by-key/[key]/index.d.ts.map +0 -1
  152. package/dist/routes/labels/contract.d.ts +0 -59
  153. package/dist/routes/labels/contract.d.ts.map +0 -1
  154. package/dist/routes/labels/contract.js +0 -75
  155. package/dist/routes/labels/contract.js.map +0 -1
  156. package/dist/routes/labels/index.d.ts.map +0 -1
  157. package/dist/routes/published-cache/contract.d.ts +0 -25
  158. package/dist/routes/published-cache/contract.d.ts.map +0 -1
  159. package/dist/routes/published-cache/contract.js +0 -35
  160. package/dist/routes/published-cache/contract.js.map +0 -1
  161. package/dist/routes/published-cache/index.d.ts.map +0 -1
  162. package/dist/routes/values/[labelId]/[version]/contract.d.ts +0 -29
  163. package/dist/routes/values/[labelId]/[version]/contract.d.ts.map +0 -1
  164. package/dist/routes/values/[labelId]/[version]/contract.js +0 -33
  165. package/dist/routes/values/[labelId]/[version]/contract.js.map +0 -1
  166. package/dist/routes/values/[labelId]/[version]/index.d.ts +0 -8
  167. package/dist/routes/values/[labelId]/[version]/index.d.ts.map +0 -1
  168. package/dist/routes/values/[labelId]/contract.d.ts +0 -38
  169. package/dist/routes/values/[labelId]/contract.d.ts.map +0 -1
  170. package/dist/routes/values/[labelId]/contract.js +0 -59
  171. package/dist/routes/values/[labelId]/contract.js.map +0 -1
  172. package/dist/routes/values/[labelId]/index.d.ts +0 -8
  173. package/dist/routes/values/[labelId]/index.d.ts.map +0 -1
  174. package/dist/server.d.ts.map +0 -1
  175. package/dist/store.d.ts.map +0 -1
  176. package/dist/types.d.ts.map +0 -1
package/dist/actions.d.ts CHANGED
@@ -1,9 +1,143 @@
1
1
  /**
2
- * @spfn/cms/actions
2
+ * 현재 locale 가져오기 (Server Action)
3
3
  *
4
- * Server Actions
5
- * 서버/클라이언트 컴포넌트 양쪽에서 사용 가능한 Server Actions
4
+ * 서버/클라이언트 컴포넌트 모두에서 사용 가능
5
+ *
6
+ * 우선순위:
7
+ * 1. 쿠키 (사용자가 명시적으로 선택한 언어)
8
+ * 2. 브라우저 언어 감지 (설정에서 활성화된 경우)
9
+ * 3. 시스템 기본 언어 (CMS 설정)
10
+ *
11
+ * @returns 현재 locale (예: 'ko', 'en')
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * // Server Component
16
+ * import { getLocale } from '@spfn/cms';
17
+ *
18
+ * export default async function Page()
19
+ * {
20
+ * const locale = await getLocale();
21
+ * return <div>Current locale: {locale}</div>;
22
+ * }
23
+ * ```
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * // Client Component
28
+ * 'use client';
29
+ * import { getLocale } from '@spfn/cms/client';
30
+ *
31
+ * export default function LanguageSwitcher()
32
+ * {
33
+ * const [locale, setLocale] = useState('');
34
+ *
35
+ * useEffect(() => {
36
+ * getLocale().then(setLocale);
37
+ * }, []);
38
+ *
39
+ * return <div>Current locale: {locale}</div>;
40
+ * }
41
+ * ```
42
+ */
43
+ declare function getLocale(): Promise<string>;
44
+ /**
45
+ * Locale 설정하기 (Server Action)
46
+ *
47
+ * 서버/클라이언트 컴포넌트 모두에서 사용 가능
48
+ * 쿠키에 locale을 저장합니다.
49
+ *
50
+ * @param locale - 설정할 locale (예: 'ko', 'en')
51
+ * @throws {Error} 지원하지 않는 locale인 경우
52
+ *
53
+ * @example
54
+ * ```tsx
55
+ * // Server Component (Server Action)
56
+ * import { setLocale } from '@spfn/cms';
57
+ *
58
+ * export default async function Page()
59
+ * {
60
+ * await setLocale('en');
61
+ * return <div>Locale changed</div>;
62
+ * }
63
+ * ```
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * // Client Component (Server Action)
68
+ * 'use client';
69
+ * import { setLocale } from '@spfn/cms/client';
70
+ *
71
+ * export default function LanguageSwitcher()
72
+ * {
73
+ * const handleChange = async (newLocale: string) =>
74
+ * {
75
+ * await setLocale(newLocale);
76
+ * window.location.reload(); // 페이지 새로고침
77
+ * };
78
+ *
79
+ * return (
80
+ * <button onClick={() => handleChange('en')}>
81
+ * Switch to English
82
+ * </button>
83
+ * );
84
+ * }
85
+ * ```
86
+ */
87
+ declare function setLocale(locale: string): Promise<void>;
88
+ /**
89
+ * 지원하는 locale 목록 가져오기 (Server Action)
90
+ *
91
+ * 서버/클라이언트 컴포넌트 모두에서 사용 가능
92
+ *
93
+ * @returns 지원하는 locale 배열 (예: ['ko', 'en', 'ja'])
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * // Server Component
98
+ * import { getLocales } from '@spfn/cms';
99
+ *
100
+ * export default async function Page()
101
+ * {
102
+ * const locales = await getLocales();
103
+ * return <div>Supported: {locales.join(', ')}</div>;
104
+ * }
105
+ * ```
106
+ *
107
+ * @example
108
+ * ```tsx
109
+ * // Client Component
110
+ * 'use client';
111
+ * import { getLocales } from '@spfn/cms/client';
112
+ *
113
+ * export default function LanguageSwitcher()
114
+ * {
115
+ * const [locales, setLocales] = useState<string[]>([]);
116
+ *
117
+ * useEffect(() => {
118
+ * getLocales().then(setLocales);
119
+ * }, []);
120
+ *
121
+ * return (
122
+ * <div>
123
+ * {locales.map(locale => (
124
+ * <button key={locale}>{locale}</button>
125
+ * ))}
126
+ * </div>
127
+ * );
128
+ * }
129
+ * ```
130
+ */
131
+ declare function getLocales(): Promise<string[]>;
132
+
133
+ /**
134
+ * Locale Constants
135
+ *
136
+ * Server/Client 양쪽에서 사용 가능한 locale 관련 상수
137
+ */
138
+ /**
139
+ * Locale 쿠키 키
6
140
  */
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
141
+ declare const LOCALE_COOKIE_KEY = "spfn-locale";
142
+
143
+ export { LOCALE_COOKIE_KEY, getLocale, getLocales, setLocale };
package/dist/actions.js CHANGED
@@ -1,11 +1,98 @@
1
- /**
2
- * @spfn/cms/actions
3
- *
4
- * Server Actions
5
- * 서버/클라이언트 컴포넌트 양쪽에서 사용 가능한 Server Actions
6
- */
7
- // Locale Server Actions
8
- export { getLocale, setLocale, getLocales, } from './helpers/locale.actions.js';
9
- // Locale Constants
10
- export { LOCALE_COOKIE_KEY } from './helpers/locale.constants.js';
1
+ // src/helpers/locale.actions.ts
2
+ import { cookies, headers } from "next/headers.js";
3
+
4
+ // src/cms.config.ts
5
+ function getEnvVar(key, defaultValue) {
6
+ return process.env[key] || defaultValue;
7
+ }
8
+ function getEnvBoolean(key, defaultValue) {
9
+ const value = process.env[key];
10
+ if (value === void 0) return defaultValue;
11
+ return value === "true" || value === "1";
12
+ }
13
+ function loadConfigFromEnv() {
14
+ const defaultLocale = getEnvVar("SPFN_CMS_DEFAULT_LOCALE", "en");
15
+ const supportedLocalesStr = getEnvVar("SPFN_CMS_SUPPORTED_LOCALES", "en,ko");
16
+ const detectBrowserLanguage2 = getEnvBoolean("SPFN_CMS_DETECT_BROWSER_LANGUAGE", true);
17
+ const supportedLocales = supportedLocalesStr.split(",").map((locale) => locale.trim()).filter((locale) => locale.length > 0);
18
+ if (!supportedLocales.includes(defaultLocale)) {
19
+ supportedLocales.unshift(defaultLocale);
20
+ }
21
+ return {
22
+ defaultLocale,
23
+ supportedLocales,
24
+ detectBrowserLanguage: detectBrowserLanguage2
25
+ };
26
+ }
27
+ var currentConfig = loadConfigFromEnv();
28
+ function getCmsConfig() {
29
+ return currentConfig;
30
+ }
31
+
32
+ // src/helpers/locale.constants.ts
33
+ var LOCALE_COOKIE_KEY = "spfn-locale";
34
+
35
+ // src/helpers/locale.actions.ts
36
+ async function detectBrowserLanguage() {
37
+ try {
38
+ const headersList = await headers();
39
+ const acceptLanguage = headersList.get("accept-language");
40
+ if (!acceptLanguage) {
41
+ return null;
42
+ }
43
+ const languages = acceptLanguage.split(",").map((lang) => {
44
+ const [code] = lang.split(";");
45
+ return code.split("-")[0].trim();
46
+ });
47
+ const config = getCmsConfig();
48
+ for (const lang of languages) {
49
+ if (config.supportedLocales.includes(lang)) {
50
+ return lang;
51
+ }
52
+ }
53
+ return null;
54
+ } catch (error) {
55
+ return null;
56
+ }
57
+ }
58
+ async function getLocale() {
59
+ const config = getCmsConfig();
60
+ const cookieStore = await cookies();
61
+ const cookieLocale = cookieStore.get(LOCALE_COOKIE_KEY)?.value;
62
+ if (cookieLocale && config.supportedLocales.includes(cookieLocale)) {
63
+ return cookieLocale;
64
+ }
65
+ if (config.detectBrowserLanguage) {
66
+ const browserLang = await detectBrowserLanguage();
67
+ if (browserLang) {
68
+ return browserLang;
69
+ }
70
+ }
71
+ return config.defaultLocale;
72
+ }
73
+ async function setLocale(locale) {
74
+ const config = getCmsConfig();
75
+ if (!config.supportedLocales.includes(locale)) {
76
+ throw new Error(
77
+ `Unsupported locale: ${locale}. Supported locales: ${config.supportedLocales.join(", ")}`
78
+ );
79
+ }
80
+ const cookieStore = await cookies();
81
+ cookieStore.set(LOCALE_COOKIE_KEY, locale, {
82
+ path: "/",
83
+ maxAge: 60 * 60 * 24 * 365,
84
+ // 1년
85
+ sameSite: "lax"
86
+ });
87
+ }
88
+ async function getLocales() {
89
+ const config = getCmsConfig();
90
+ return config.supportedLocales;
91
+ }
92
+ export {
93
+ LOCALE_COOKIE_KEY,
94
+ getLocale,
95
+ getLocales,
96
+ setLocale
97
+ };
11
98
  //# sourceMappingURL=actions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAwB;AACxB,OAAO,EACH,SAAS,EACT,SAAS,EACT,UAAU,GACb,MAAM,6BAA6B,CAAC;AAErC,mBAAmB;AACnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"sources":["../src/helpers/locale.actions.ts","../src/cms.config.ts","../src/helpers/locale.constants.ts"],"sourcesContent":["\"use server\";\n\n/**\n * Locale Management Server Actions\n *\n * Server Actions으로 구현된 locale 관리 함수\n * - 서버 컴포넌트: 일반 함수 호출로 동작\n * - 클라이언트 컴포넌트: Server Action으로 자동 처리\n */\n\nimport { cookies, headers } from 'next/headers.js';\nimport { getCmsConfig } from '../cms.config.js';\nimport { LOCALE_COOKIE_KEY } from './locale.constants.js';\n\n/**\n * 브라우저 언어 감지\n *\n * Accept-Language 헤더에서 지원하는 언어를 찾습니다.\n *\n * @returns 감지된 언어 코드 또는 null\n */\nasync function detectBrowserLanguage(): Promise<string | null>\n{\n try\n {\n const headersList = await headers();\n const acceptLanguage = headersList.get('accept-language');\n\n if (!acceptLanguage)\n {\n return null;\n }\n\n // \"ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7\" 형식 파싱\n const languages = acceptLanguage\n .split(',')\n .map(lang =>\n {\n const [code] = lang.split(';');\n return code.split('-')[0].trim();\n });\n\n const config = getCmsConfig();\n\n // 지원하는 언어 중 첫 번째 매칭되는 언어 반환\n for (const lang of languages)\n {\n if (config.supportedLocales.includes(lang))\n {\n return lang;\n }\n }\n\n return null;\n }\n catch (error)\n {\n // 헤더 접근 실패 시\n return null;\n }\n}\n\n/**\n * 현재 locale 가져오기 (Server Action)\n *\n * 서버/클라이언트 컴포넌트 모두에서 사용 가능\n *\n * 우선순위:\n * 1. 쿠키 (사용자가 명시적으로 선택한 언어)\n * 2. 브라우저 언어 감지 (설정에서 활성화된 경우)\n * 3. 시스템 기본 언어 (CMS 설정)\n *\n * @returns 현재 locale (예: 'ko', 'en')\n *\n * @example\n * ```tsx\n * // Server Component\n * import { getLocale } from '@spfn/cms';\n *\n * export default async function Page()\n * {\n * const locale = await getLocale();\n * return <div>Current locale: {locale}</div>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Client Component\n * 'use client';\n * import { getLocale } from '@spfn/cms/client';\n *\n * export default function LanguageSwitcher()\n * {\n * const [locale, setLocale] = useState('');\n *\n * useEffect(() => {\n * getLocale().then(setLocale);\n * }, []);\n *\n * return <div>Current locale: {locale}</div>;\n * }\n * ```\n */\nexport async function getLocale(): Promise<string>\n{\n const config = getCmsConfig();\n\n // 1순위: 쿠키 (사용자가 명시적으로 선택한 언어)\n const cookieStore = await cookies();\n const cookieLocale = cookieStore.get(LOCALE_COOKIE_KEY)?.value;\n\n if (cookieLocale && config.supportedLocales.includes(cookieLocale))\n {\n return cookieLocale;\n }\n\n // 2순위: 브라우저 언어 감지 (설정에서 활성화된 경우)\n if (config.detectBrowserLanguage)\n {\n const browserLang = await detectBrowserLanguage();\n if (browserLang)\n {\n return browserLang;\n }\n }\n\n // 3순위: 시스템 기본 언어\n return config.defaultLocale;\n}\n\n/**\n * Locale 설정하기 (Server Action)\n *\n * 서버/클라이언트 컴포넌트 모두에서 사용 가능\n * 쿠키에 locale을 저장합니다.\n *\n * @param locale - 설정할 locale (예: 'ko', 'en')\n * @throws {Error} 지원하지 않는 locale인 경우\n *\n * @example\n * ```tsx\n * // Server Component (Server Action)\n * import { setLocale } from '@spfn/cms';\n *\n * export default async function Page()\n * {\n * await setLocale('en');\n * return <div>Locale changed</div>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Client Component (Server Action)\n * 'use client';\n * import { setLocale } from '@spfn/cms/client';\n *\n * export default function LanguageSwitcher()\n * {\n * const handleChange = async (newLocale: string) =>\n * {\n * await setLocale(newLocale);\n * window.location.reload(); // 페이지 새로고침\n * };\n *\n * return (\n * <button onClick={() => handleChange('en')}>\n * Switch to English\n * </button>\n * );\n * }\n * ```\n */\nexport async function setLocale(locale: string): Promise<void>\n{\n const config = getCmsConfig();\n\n // 유효성 검사\n if (!config.supportedLocales.includes(locale))\n {\n throw new Error(\n `Unsupported locale: ${locale}. Supported locales: ${config.supportedLocales.join(', ')}`\n );\n }\n\n const cookieStore = await cookies();\n\n cookieStore.set(LOCALE_COOKIE_KEY, locale, {\n path: '/',\n maxAge: 60 * 60 * 24 * 365, // 1년\n sameSite: 'lax',\n });\n}\n\n/**\n * 지원하는 locale 목록 가져오기 (Server Action)\n *\n * 서버/클라이언트 컴포넌트 모두에서 사용 가능\n *\n * @returns 지원하는 locale 배열 (예: ['ko', 'en', 'ja'])\n *\n * @example\n * ```tsx\n * // Server Component\n * import { getLocales } from '@spfn/cms';\n *\n * export default async function Page()\n * {\n * const locales = await getLocales();\n * return <div>Supported: {locales.join(', ')}</div>;\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Client Component\n * 'use client';\n * import { getLocales } from '@spfn/cms/client';\n *\n * export default function LanguageSwitcher()\n * {\n * const [locales, setLocales] = useState<string[]>([]);\n *\n * useEffect(() => {\n * getLocales().then(setLocales);\n * }, []);\n *\n * return (\n * <div>\n * {locales.map(locale => (\n * <button key={locale}>{locale}</button>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport async function getLocales(): Promise<string[]>\n{\n const config = getCmsConfig();\n return config.supportedLocales;\n}","/**\n * CMS Configuration Module\n *\n * 환경변수 기반 CMS 설정 관리\n * - SPFN_CMS_DEFAULT_LOCALE: 기본 언어 (기본값: 'ko')\n * - SPFN_CMS_SUPPORTED_LOCALES: 지원 언어 목록, 쉼표로 구분 (기본값: 'ko,en')\n * - SPFN_CMS_DETECT_BROWSER_LANGUAGE: 브라우저 언어 자동 감지 (기본값: 'false')\n */\n\n/**\n * CMS 설정 타입\n */\nexport interface CmsConfig\n{\n /**\n * 기본 언어 코드\n * @example 'ko', 'en', 'ja'\n */\n defaultLocale: string;\n\n /**\n * 지원하는 언어 목록\n * @example ['ko', 'en', 'ja']\n */\n supportedLocales: string[];\n\n /**\n * 브라우저 언어 자동 감지 여부\n * @default false\n */\n detectBrowserLanguage: boolean;\n}\n\n/**\n * 환경변수 읽기 헬퍼\n */\nfunction getEnvVar(key: string, defaultValue: string): string\n{\n return process.env[key] || defaultValue;\n}\n\n/**\n * 환경변수에서 boolean 읽기\n */\nfunction getEnvBoolean(key: string, defaultValue: boolean): boolean\n{\n const value = process.env[key];\n if (value === undefined) return defaultValue;\n return value === 'true' || value === '1';\n}\n\n/**\n * 환경변수에서 설정 로드\n */\nfunction loadConfigFromEnv(): CmsConfig\n{\n const defaultLocale = getEnvVar('SPFN_CMS_DEFAULT_LOCALE', 'en');\n const supportedLocalesStr = getEnvVar('SPFN_CMS_SUPPORTED_LOCALES', 'en,ko');\n const detectBrowserLanguage = getEnvBoolean('SPFN_CMS_DETECT_BROWSER_LANGUAGE', true);\n\n const supportedLocales = supportedLocalesStr\n .split(',')\n .map(locale => locale.trim())\n .filter(locale => locale.length > 0);\n\n // 기본 언어가 지원 목록에 없으면 추가\n if (!supportedLocales.includes(defaultLocale))\n {\n supportedLocales.unshift(defaultLocale);\n }\n\n return {\n defaultLocale,\n supportedLocales,\n detectBrowserLanguage,\n };\n}\n\n/**\n * 현재 설정 (환경변수에서 초기화)\n */\nlet currentConfig: CmsConfig = loadConfigFromEnv();\n\n/**\n * CMS 설정 조회\n *\n * @returns 현재 CMS 설정\n *\n * @example\n * ```tsx\n * import { getCmsConfig } from '@spfn/cms';\n *\n * const config = getCmsConfig();\n * console.log(config.defaultLocale); // 'ko'\n * console.log(config.supportedLocales); // ['ko', 'en']\n * ```\n */\nexport function getCmsConfig(): Readonly<CmsConfig>\n{\n return currentConfig;\n}\n\n/**\n * CMS 설정 변경 (런타임 오버라이드)\n *\n * 환경변수 설정을 런타임에 오버라이드합니다.\n * 주로 테스트나 특수한 경우에 사용됩니다.\n *\n * @param config - 변경할 설정 (부분 업데이트 가능)\n *\n * @example\n * ```tsx\n * import { configureCms } from '@spfn/cms';\n *\n * // 앱 초기화 시 (선택적)\n * configureCms({\n * defaultLocale: 'en',\n * supportedLocales: ['en', 'ko', 'ja'],\n * detectBrowserLanguage: true,\n * });\n * ```\n */\nexport function configureCms(config: Partial<CmsConfig>): void\n{\n currentConfig = {\n ...currentConfig,\n ...config,\n };\n\n // 기본 언어가 지원 목록에 있는지 확인\n if (config.defaultLocale && !currentConfig.supportedLocales.includes(config.defaultLocale))\n {\n console.warn(\n `[CMS Config] Default locale '${config.defaultLocale}' not in supported locales, adding automatically.`,\n `Supported locales: [${currentConfig.supportedLocales.join(', ')}]`\n );\n\n currentConfig.supportedLocales.unshift(config.defaultLocale);\n }\n}\n\n/**\n * 설정 초기화 (환경변수에서 재로드)\n *\n * @example\n * ```tsx\n * import { resetCmsConfig } from '@spfn/cms';\n *\n * // 환경변수 설정으로 되돌리기\n * resetCmsConfig();\n * ```\n */\nexport function resetCmsConfig(): void\n{\n currentConfig = loadConfigFromEnv();\n}","/**\n * Locale Constants\n *\n * Server/Client 양쪽에서 사용 가능한 locale 관련 상수\n */\n\n/**\n * Locale 쿠키 키\n */\nexport const LOCALE_COOKIE_KEY = 'spfn-locale';"],"mappings":";AAUA,SAAS,SAAS,eAAe;;;AC0BjC,SAAS,UAAU,KAAa,cAChC;AACI,SAAO,QAAQ,IAAI,GAAG,KAAK;AAC/B;AAKA,SAAS,cAAc,KAAa,cACpC;AACI,QAAM,QAAQ,QAAQ,IAAI,GAAG;AAC7B,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO,UAAU,UAAU,UAAU;AACzC;AAKA,SAAS,oBACT;AACI,QAAM,gBAAgB,UAAU,2BAA2B,IAAI;AAC/D,QAAM,sBAAsB,UAAU,8BAA8B,OAAO;AAC3E,QAAMA,yBAAwB,cAAc,oCAAoC,IAAI;AAEpF,QAAM,mBAAmB,oBACpB,MAAM,GAAG,EACT,IAAI,YAAU,OAAO,KAAK,CAAC,EAC3B,OAAO,YAAU,OAAO,SAAS,CAAC;AAGvC,MAAI,CAAC,iBAAiB,SAAS,aAAa,GAC5C;AACI,qBAAiB,QAAQ,aAAa;AAAA,EAC1C;AAEA,SAAO;AAAA,IACH;AAAA,IACA;AAAA,IACA,uBAAAA;AAAA,EACJ;AACJ;AAKA,IAAI,gBAA2B,kBAAkB;AAgB1C,SAAS,eAChB;AACI,SAAO;AACX;;;AC3FO,IAAM,oBAAoB;;;AFYjC,eAAe,wBACf;AACI,MACA;AACI,UAAM,cAAc,MAAM,QAAQ;AAClC,UAAM,iBAAiB,YAAY,IAAI,iBAAiB;AAExD,QAAI,CAAC,gBACL;AACI,aAAO;AAAA,IACX;AAGA,UAAM,YAAY,eACb,MAAM,GAAG,EACT,IAAI,UACL;AACI,YAAM,CAAC,IAAI,IAAI,KAAK,MAAM,GAAG;AAC7B,aAAO,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK;AAAA,IACnC,CAAC;AAEL,UAAM,SAAS,aAAa;AAG5B,eAAW,QAAQ,WACnB;AACI,UAAI,OAAO,iBAAiB,SAAS,IAAI,GACzC;AACI,eAAO;AAAA,MACX;AAAA,IACJ;AAEA,WAAO;AAAA,EACX,SACO,OACP;AAEI,WAAO;AAAA,EACX;AACJ;AA4CA,eAAsB,YACtB;AACI,QAAM,SAAS,aAAa;AAG5B,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,eAAe,YAAY,IAAI,iBAAiB,GAAG;AAEzD,MAAI,gBAAgB,OAAO,iBAAiB,SAAS,YAAY,GACjE;AACI,WAAO;AAAA,EACX;AAGA,MAAI,OAAO,uBACX;AACI,UAAM,cAAc,MAAM,sBAAsB;AAChD,QAAI,aACJ;AACI,aAAO;AAAA,IACX;AAAA,EACJ;AAGA,SAAO,OAAO;AAClB;AA6CA,eAAsB,UAAU,QAChC;AACI,QAAM,SAAS,aAAa;AAG5B,MAAI,CAAC,OAAO,iBAAiB,SAAS,MAAM,GAC5C;AACI,UAAM,IAAI;AAAA,MACN,uBAAuB,MAAM,wBAAwB,OAAO,iBAAiB,KAAK,IAAI,CAAC;AAAA,IAC3F;AAAA,EACJ;AAEA,QAAM,cAAc,MAAM,QAAQ;AAElC,cAAY,IAAI,mBAAmB,QAAQ;AAAA,IACvC,MAAM;AAAA,IACN,QAAQ,KAAK,KAAK,KAAK;AAAA;AAAA,IACvB,UAAU;AAAA,EACd,CAAC;AACL;AA6CA,eAAsB,aACtB;AACI,QAAM,SAAS,aAAa;AAC5B,SAAO,OAAO;AAClB;","names":["detectBrowserLanguage"]}
package/dist/client.d.ts CHANGED
@@ -1,17 +1,52 @@
1
+ import { InferContract } from '@spfn/core';
2
+ import { getLabelsContract, getLabelContract, createLabelContract, updateLabelContract, deleteLabelContract } from './contracts/labels.js';
3
+ import { getPublishedCacheContract } from './contracts/published-cache.js';
4
+ export { useCmsStore, useSection, useSections } from './store.js';
5
+ import { SectionAPI } from './server.js';
6
+ import '@sinclair/typebox';
7
+ import 'zustand';
8
+
9
+ /**
10
+ * CMS Store Initializer
11
+ *
12
+ * 서버 컴포넌트에서 로드한 섹션 데이터를 클라이언트 store에 초기화
13
+ */
14
+
15
+ interface InitCmsProps {
16
+ /**
17
+ * 서버에서 로드한 섹션 데이터
18
+ * { home: { t, data }, ... }
19
+ */
20
+ sections: Record<string, SectionAPI>;
21
+ }
22
+ /**
23
+ * CMS Store 초기화 컴포넌트
24
+ *
25
+ * 서버 컴포넌트에서 로드한 데이터를 클라이언트 store에 주입
26
+ * 이후 하위 클라이언트 컴포넌트에서 useSection() 사용 가능
27
+ *
28
+ * @param props - InitCmsProps
29
+ * @param props.sections - 서버에서 로드한 섹션 데이터
30
+ *
31
+ * @example
32
+ * // Server Component
33
+ * const home = await getSection('home');
34
+ * <InitCms sections={{ home }} />
35
+ * // 이후 하위 클라이언트 컴포넌트에서 useSection('home') 사용 가능
36
+ */
37
+ declare function InitCms({ sections }: InitCmsProps): null;
38
+
1
39
  /**
2
40
  * @spfn/cms/client
3
41
  *
4
42
  * Client Components Only
5
43
  * 클라이언트 컴포넌트 전용 (브라우저에서 실행)
6
44
  */
7
- import type { InferContract } from '@spfn/core';
8
- import { getLabelsContract, createLabelContract } from './routes/labels/contract';
9
- import { getLabelContract, updateLabelContract, deleteLabelContract } from './routes/labels/[id]/contract';
10
- import { getPublishedCacheContract } from './routes/published-cache/contract';
45
+
11
46
  /**
12
47
  * CMS API Client
13
48
  */
14
- export declare const cmsApi: {
49
+ declare const cmsApi: {
15
50
  /**
16
51
  * Labels API
17
52
  */
@@ -132,7 +167,6 @@ export declare const cmsApi: {
132
167
  /**
133
168
  * Type exports
134
169
  */
135
- export type CmsApi = typeof cmsApi;
136
- export { useCmsStore, useSection, useSections } from './store';
137
- export { InitCms } from './init';
138
- //# sourceMappingURL=client.d.ts.map
170
+ type CmsApi = typeof cmsApi;
171
+
172
+ export { type CmsApi, InitCms, cmsApi };