@spfn/cms 0.1.0-alpha.9 → 0.2.0-beta.10

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 (171) hide show
  1. package/README.md +436 -342
  2. package/dist/actions.d.ts +12 -6
  3. package/dist/actions.js +25 -10
  4. package/dist/actions.js.map +1 -1
  5. package/dist/config.d.ts +40 -0
  6. package/dist/config.js +39 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/errors.d.ts +149 -0
  9. package/dist/errors.js +164 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/index.d.ts +199 -20
  12. package/dist/index.js +202 -23
  13. package/dist/index.js.map +1 -1
  14. package/dist/server.d.ts +99 -81
  15. package/dist/server.js +842 -256
  16. package/dist/server.js.map +1 -1
  17. package/migrations/0000_medical_ozymandias.sql +54 -0
  18. package/migrations/meta/0000_snapshot.json +336 -0
  19. package/migrations/meta/_journal.json +13 -0
  20. package/package.json +56 -46
  21. package/dist/actions.d.ts.map +0 -1
  22. package/dist/client.d.ts +0 -138
  23. package/dist/client.d.ts.map +0 -1
  24. package/dist/client.js +0 -62
  25. package/dist/client.js.map +0 -1
  26. package/dist/cms.config.d.ts +0 -77
  27. package/dist/cms.config.d.ts.map +0 -1
  28. package/dist/cms.config.js +0 -111
  29. package/dist/cms.config.js.map +0 -1
  30. package/dist/entities/cms-audit-logs.d.ts +0 -213
  31. package/dist/entities/cms-audit-logs.d.ts.map +0 -1
  32. package/dist/entities/cms-audit-logs.js +0 -103
  33. package/dist/entities/cms-audit-logs.js.map +0 -1
  34. package/dist/entities/cms-draft-cache.d.ts +0 -188
  35. package/dist/entities/cms-draft-cache.d.ts.map +0 -1
  36. package/dist/entities/cms-draft-cache.js +0 -112
  37. package/dist/entities/cms-draft-cache.js.map +0 -1
  38. package/dist/entities/cms-label-values.d.ts +0 -192
  39. package/dist/entities/cms-label-values.d.ts.map +0 -1
  40. package/dist/entities/cms-label-values.js +0 -105
  41. package/dist/entities/cms-label-values.js.map +0 -1
  42. package/dist/entities/cms-label-versions.d.ts +0 -207
  43. package/dist/entities/cms-label-versions.d.ts.map +0 -1
  44. package/dist/entities/cms-label-versions.js +0 -80
  45. package/dist/entities/cms-label-versions.js.map +0 -1
  46. package/dist/entities/cms-labels.d.ts +0 -189
  47. package/dist/entities/cms-labels.d.ts.map +0 -1
  48. package/dist/entities/cms-labels.js +0 -48
  49. package/dist/entities/cms-labels.js.map +0 -1
  50. package/dist/entities/cms-published-cache.d.ts +0 -199
  51. package/dist/entities/cms-published-cache.d.ts.map +0 -1
  52. package/dist/entities/cms-published-cache.js +0 -103
  53. package/dist/entities/cms-published-cache.js.map +0 -1
  54. package/dist/entities/index.d.ts +0 -10
  55. package/dist/entities/index.d.ts.map +0 -1
  56. package/dist/entities/index.js +0 -10
  57. package/dist/entities/index.js.map +0 -1
  58. package/dist/generators/index.d.ts +0 -19
  59. package/dist/generators/index.d.ts.map +0 -1
  60. package/dist/generators/index.js +0 -19
  61. package/dist/generators/index.js.map +0 -1
  62. package/dist/generators/label-sync-generator.d.ts +0 -33
  63. package/dist/generators/label-sync-generator.d.ts.map +0 -1
  64. package/dist/generators/label-sync-generator.js +0 -86
  65. package/dist/generators/label-sync-generator.js.map +0 -1
  66. package/dist/helpers/locale.actions.d.ts +0 -132
  67. package/dist/helpers/locale.actions.d.ts.map +0 -1
  68. package/dist/helpers/locale.actions.js +0 -210
  69. package/dist/helpers/locale.actions.js.map +0 -1
  70. package/dist/helpers/locale.constants.d.ts +0 -10
  71. package/dist/helpers/locale.constants.d.ts.map +0 -1
  72. package/dist/helpers/locale.constants.js +0 -10
  73. package/dist/helpers/locale.constants.js.map +0 -1
  74. package/dist/helpers/locale.d.ts +0 -17
  75. package/dist/helpers/locale.d.ts.map +0 -1
  76. package/dist/helpers/locale.js +0 -20
  77. package/dist/helpers/locale.js.map +0 -1
  78. package/dist/helpers/sync.d.ts +0 -41
  79. package/dist/helpers/sync.d.ts.map +0 -1
  80. package/dist/helpers/sync.js +0 -309
  81. package/dist/helpers/sync.js.map +0 -1
  82. package/dist/index.d.ts.map +0 -1
  83. package/dist/init.d.ts +0 -31
  84. package/dist/init.d.ts.map +0 -1
  85. package/dist/init.js +0 -36
  86. package/dist/init.js.map +0 -1
  87. package/dist/labels/helpers.d.ts +0 -31
  88. package/dist/labels/helpers.d.ts.map +0 -1
  89. package/dist/labels/helpers.js +0 -60
  90. package/dist/labels/helpers.js.map +0 -1
  91. package/dist/labels/index.d.ts +0 -7
  92. package/dist/labels/index.d.ts.map +0 -1
  93. package/dist/labels/index.js +0 -7
  94. package/dist/labels/index.js.map +0 -1
  95. package/dist/repositories/cms-draft-cache.repository.d.ts +0 -62
  96. package/dist/repositories/cms-draft-cache.repository.d.ts.map +0 -1
  97. package/dist/repositories/cms-draft-cache.repository.js +0 -56
  98. package/dist/repositories/cms-draft-cache.repository.js.map +0 -1
  99. package/dist/repositories/cms-label-values.repository.d.ts +0 -32
  100. package/dist/repositories/cms-label-values.repository.d.ts.map +0 -1
  101. package/dist/repositories/cms-label-values.repository.js +0 -72
  102. package/dist/repositories/cms-label-values.repository.js.map +0 -1
  103. package/dist/repositories/cms-labels.repository.d.ts +0 -53
  104. package/dist/repositories/cms-labels.repository.d.ts.map +0 -1
  105. package/dist/repositories/cms-labels.repository.js +0 -77
  106. package/dist/repositories/cms-labels.repository.js.map +0 -1
  107. package/dist/repositories/cms-published-cache.repository.d.ts +0 -53
  108. package/dist/repositories/cms-published-cache.repository.d.ts.map +0 -1
  109. package/dist/repositories/cms-published-cache.repository.js +0 -54
  110. package/dist/repositories/cms-published-cache.repository.js.map +0 -1
  111. package/dist/repositories/index.d.ts +0 -8
  112. package/dist/repositories/index.d.ts.map +0 -1
  113. package/dist/repositories/index.js +0 -9
  114. package/dist/repositories/index.js.map +0 -1
  115. package/dist/routes/labels/[id]/contract.d.ts +0 -68
  116. package/dist/routes/labels/[id]/contract.d.ts.map +0 -1
  117. package/dist/routes/labels/[id]/contract.js +0 -84
  118. package/dist/routes/labels/[id]/contract.js.map +0 -1
  119. package/dist/routes/labels/[id]/index.d.ts +0 -10
  120. package/dist/routes/labels/[id]/index.d.ts.map +0 -1
  121. package/dist/routes/labels/[id]/index.js +0 -96
  122. package/dist/routes/labels/[id]/index.js.map +0 -1
  123. package/dist/routes/labels/by-key/[key]/contract.d.ts +0 -24
  124. package/dist/routes/labels/by-key/[key]/contract.d.ts.map +0 -1
  125. package/dist/routes/labels/by-key/[key]/contract.js +0 -28
  126. package/dist/routes/labels/by-key/[key]/contract.js.map +0 -1
  127. package/dist/routes/labels/by-key/[key]/index.d.ts +0 -8
  128. package/dist/routes/labels/by-key/[key]/index.d.ts.map +0 -1
  129. package/dist/routes/labels/by-key/[key]/index.js +0 -32
  130. package/dist/routes/labels/by-key/[key]/index.js.map +0 -1
  131. package/dist/routes/labels/contract.d.ts +0 -59
  132. package/dist/routes/labels/contract.d.ts.map +0 -1
  133. package/dist/routes/labels/contract.js +0 -75
  134. package/dist/routes/labels/contract.js.map +0 -1
  135. package/dist/routes/labels/index.d.ts +0 -10
  136. package/dist/routes/labels/index.d.ts.map +0 -1
  137. package/dist/routes/labels/index.js +0 -73
  138. package/dist/routes/labels/index.js.map +0 -1
  139. package/dist/routes/published-cache/contract.d.ts +0 -25
  140. package/dist/routes/published-cache/contract.d.ts.map +0 -1
  141. package/dist/routes/published-cache/contract.js +0 -35
  142. package/dist/routes/published-cache/contract.js.map +0 -1
  143. package/dist/routes/published-cache/index.d.ts +0 -8
  144. package/dist/routes/published-cache/index.d.ts.map +0 -1
  145. package/dist/routes/published-cache/index.js +0 -33
  146. package/dist/routes/published-cache/index.js.map +0 -1
  147. package/dist/routes/values/[labelId]/[version]/contract.d.ts +0 -29
  148. package/dist/routes/values/[labelId]/[version]/contract.d.ts.map +0 -1
  149. package/dist/routes/values/[labelId]/[version]/contract.js +0 -33
  150. package/dist/routes/values/[labelId]/[version]/contract.js.map +0 -1
  151. package/dist/routes/values/[labelId]/[version]/index.d.ts +0 -8
  152. package/dist/routes/values/[labelId]/[version]/index.d.ts.map +0 -1
  153. package/dist/routes/values/[labelId]/[version]/index.js +0 -45
  154. package/dist/routes/values/[labelId]/[version]/index.js.map +0 -1
  155. package/dist/routes/values/[labelId]/contract.d.ts +0 -38
  156. package/dist/routes/values/[labelId]/contract.d.ts.map +0 -1
  157. package/dist/routes/values/[labelId]/contract.js +0 -59
  158. package/dist/routes/values/[labelId]/contract.js.map +0 -1
  159. package/dist/routes/values/[labelId]/index.d.ts +0 -8
  160. package/dist/routes/values/[labelId]/index.d.ts.map +0 -1
  161. package/dist/routes/values/[labelId]/index.js +0 -42
  162. package/dist/routes/values/[labelId]/index.js.map +0 -1
  163. package/dist/server.d.ts.map +0 -1
  164. package/dist/store.d.ts +0 -87
  165. package/dist/store.d.ts.map +0 -1
  166. package/dist/store.js +0 -205
  167. package/dist/store.js.map +0 -1
  168. package/dist/types.d.ts +0 -74
  169. package/dist/types.d.ts.map +0 -1
  170. package/dist/types.js +0 -7
  171. package/dist/types.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -1,20 +1,199 @@
1
- import "server-only";
2
- /**
3
- * @spfn/cms
4
- *
5
- * Backend + Server Components
6
- * 백엔드 서버 컴포넌트 전용 (서버에서만 실행)
7
- *
8
- * For client components, use: import { ... } from '@spfn/cms/client'
9
- */
10
- export { getCmsConfig, configureCms, resetCmsConfig } from './cms.config.js';
11
- export type { CmsConfig } from './cms.config.js';
12
- export * from './server.js';
13
- export type { SectionData, SectionAPI } from './server.js';
14
- export * from './repositories/index.js';
15
- export * from './entities/index.js';
16
- export { syncSection, syncAll, initLabelSync, loadLabelsFromJson } from './helpers/sync.js';
17
- export * from './labels/index.js';
18
- export { createLabelSyncGenerator, LabelSyncGenerator } from './generators/label-sync-generator.js';
19
- export type * from './types.js';
20
- //# sourceMappingURL=index.d.ts.map
1
+ import * as _spfn_core_nextjs from '@spfn/core/nextjs';
2
+ import * as _spfn_core_route from '@spfn/core/route';
3
+ import * as _sinclair_typebox from '@sinclair/typebox';
4
+
5
+ /**
6
+ * Bind locale to labels, returning locale-specific values
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const labelsDefinition = defineLabels({
11
+ * home: {
12
+ * title: { en: "Home", ko: "홈" }
13
+ * }
14
+ * });
15
+ *
16
+ * const labels = bindLocale(labelsDefinition, 'ko');
17
+ * labels.home.title // "홈"
18
+ * ```
19
+ */
20
+ /**
21
+ * Type that converts locale records to strings
22
+ */
23
+ type BoundLabels<T> = {
24
+ [K in keyof T]: T[K] extends Record<string, any> ? IsLocaleRecord<T[K]> extends true ? string : BoundLabels<T[K]> : T[K];
25
+ };
26
+ /**
27
+ * Extract section keys from label definition
28
+ */
29
+ type SectionKeys<T> = Extract<keyof T, string>;
30
+ /**
31
+ * Get content of a single section (without section name wrapper)
32
+ */
33
+ type BoundLabelSection<T, K extends SectionKeys<T>> = BoundLabels<T>[K];
34
+ /**
35
+ * Pick specific sections from bound labels (for multiple sections)
36
+ */
37
+ type BoundLabelsSections<T, K extends SectionKeys<T>> = Pick<BoundLabels<T>, K>;
38
+ /**
39
+ * Check if object is a locale record (has string values only)
40
+ */
41
+ type IsLocaleRecord<T> = T extends Record<string, string> ? true : false;
42
+
43
+ /**
44
+ * Defines a type-safe label configuration.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * export const labelConfig = defineLabelConfig({
49
+ * locales: ['en', 'ar'] as const,
50
+ * defaultLocale: 'en',
51
+ * fallbackLocale: 'en', // Optional
52
+ * });
53
+ *
54
+ * export type LabelConfig = typeof labelConfig;
55
+ * export type AppLocale = typeof labelConfig.locales[number]; // 'en' | 'ar'
56
+ * ```
57
+ */
58
+ declare function defineLabelConfig<const TLocales extends readonly string[]>(config: {
59
+ locales: TLocales;
60
+ defaultLocale: TLocales[number];
61
+ fallbackLocale?: TLocales[number];
62
+ useBrowserLanguage?: boolean;
63
+ }): {
64
+ locales: TLocales;
65
+ defaultLocale: TLocales[number];
66
+ fallbackLocale?: TLocales[number];
67
+ useBrowserLanguage?: boolean;
68
+ };
69
+ /**
70
+ * Define nested label structure (tRPC-style)
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * export const labels = defineLabels({
75
+ * home: {
76
+ * slogan: { en: "Welcome", ko: "환영합니다" },
77
+ * hero: {
78
+ * title: { en: "Hello", ko: "안녕하세요" }
79
+ * }
80
+ * },
81
+ * about: {
82
+ * title: { en: "About Us", ko: "회사 소개" }
83
+ * }
84
+ * });
85
+ *
86
+ * // Usage
87
+ * labels.home.slogan;
88
+ * labels.home.hero.title;
89
+ * labels.about.title;
90
+ * ```
91
+ */
92
+ declare function defineLabels<const T>(labels: T): T;
93
+ declare function format(template: string, vars: Record<string, string | number>): string;
94
+
95
+ /**
96
+ * Create CMS client with API, label getters, and format utility
97
+ *
98
+ * @param labelsDefinition - Labels defined using defineLabels()
99
+ * @param config - Label config from defineLabelConfig()
100
+ * @returns API client, getLabel (single), getLabels (multiple), and format utility
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * // labels.ts - Setup once
105
+ * import { getLocale } from '@spfn/cms/actions';
106
+ *
107
+ * export const { api, getLabel, getLabels, format } = createCmsClient(labelsDefinition, {
108
+ * defaultLocale: 'ko',
109
+ * getLocale: () => getLocale('ko'),
110
+ * });
111
+ *
112
+ * // Single section - direct access
113
+ * const label = await getLabel('home');
114
+ * label.hero.title // "Hello" (no section name!)
115
+ *
116
+ * // Multiple sections - with section names
117
+ * const labels = await getLabels(['home', 'about']);
118
+ * labels.home.hero.title // "Hello"
119
+ * labels.about.title // "About Us"
120
+ *
121
+ * // With template variables
122
+ * const greeting = label.hero.greeting; // "Hello {name}"
123
+ * format(greeting, { name: "John" }); // "Hello John"
124
+ * ```
125
+ */
126
+ declare function createCmsClient<T>(labelsDefinition: T, config: {
127
+ defaultLocale: string;
128
+ fallbackLocale?: string;
129
+ getLocale: () => Promise<string>;
130
+ }): {
131
+ api: _spfn_core_nextjs.Client<_spfn_core_route.Router<{
132
+ getLabelCache: _spfn_core_route.RouteDef<{
133
+ query: _sinclair_typebox.TObject<{
134
+ sections: _sinclair_typebox.TArray<_sinclair_typebox.TString>;
135
+ locale: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
136
+ }>;
137
+ }, {}, Record<string, any>>;
138
+ getSectionLabels: _spfn_core_route.RouteDef<{
139
+ params: _sinclair_typebox.TObject<{
140
+ section: _sinclair_typebox.TString;
141
+ }>;
142
+ query: _sinclair_typebox.TObject<{
143
+ locales: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
144
+ }>;
145
+ }, {}, {
146
+ section: string;
147
+ locales: string[];
148
+ labels: Array<{
149
+ id: number;
150
+ key: string;
151
+ description: string | null;
152
+ defaultValue: Record<string, string>;
153
+ draft: Record<string, string> | null;
154
+ published: Record<string, string> | null;
155
+ hasDraft: boolean;
156
+ }>;
157
+ }>;
158
+ saveSectionDraft: _spfn_core_route.RouteDef<{
159
+ params: _sinclair_typebox.TObject<{
160
+ section: _sinclair_typebox.TString;
161
+ }>;
162
+ body: _sinclair_typebox.TObject<{
163
+ labels: _sinclair_typebox.TArray<_sinclair_typebox.TObject<{
164
+ id: _sinclair_typebox.TNumber;
165
+ values: _sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TString>;
166
+ }>>;
167
+ }>;
168
+ }, {}, {
169
+ updated: number;
170
+ success: boolean;
171
+ }>;
172
+ publishSection: _spfn_core_route.RouteDef<{
173
+ params: _sinclair_typebox.TObject<{
174
+ section: _sinclair_typebox.TString;
175
+ }>;
176
+ body: _sinclair_typebox.TObject<{
177
+ locales: _sinclair_typebox.TArray<_sinclair_typebox.TString>;
178
+ }>;
179
+ }, {}, {
180
+ published: number;
181
+ version: number;
182
+ labels: string[];
183
+ success: boolean;
184
+ }>;
185
+ resetSectionDraft: _spfn_core_route.RouteDef<{
186
+ params: _sinclair_typebox.TObject<{
187
+ section: _sinclair_typebox.TString;
188
+ }>;
189
+ }, {}, {
190
+ reset: number;
191
+ success: boolean;
192
+ }>;
193
+ }>>;
194
+ getLabel: <K extends SectionKeys<T>>(section: K) => Promise<BoundLabelSection<T, K>>;
195
+ getLabels: <K extends SectionKeys<T>>(sections: readonly K[]) => Promise<BoundLabelsSections<T, K>>;
196
+ format: typeof format;
197
+ };
198
+
199
+ export { type BoundLabelSection, type BoundLabels, type BoundLabelsSections, type SectionKeys, createCmsClient, defineLabelConfig, defineLabels, format };
package/dist/index.js CHANGED
@@ -1,24 +1,203 @@
1
- import "server-only";
2
- /**
3
- * @spfn/cms
4
- *
5
- * Backend + Server Components
6
- * 백엔드 및 서버 컴포넌트 전용 (서버에서만 실행)
7
- *
8
- * For client components, use: import { ... } from '@spfn/cms/client'
9
- */
10
- // Configuration API
11
- export { getCmsConfig, configureCms, resetCmsConfig } from './cms.config.js';
12
- // Server Components API (React Server Components)
13
- export * from './server.js';
14
- // Backend: Repositories (DB access)
15
- export * from './repositories/index.js';
16
- // Backend: Entities (DB schemas)
17
- export * from './entities/index.js';
18
- // Backend: Sync utilities (server startup, CLI scripts)
19
- export { syncSection, syncAll, initLabelSync, loadLabelsFromJson } from './helpers/sync.js';
20
- // Backend: Label helpers (for processing JSON labels)
21
- export * from './labels/index.js';
22
- // Backend: Codegen generators (for development)
23
- export { createLabelSyncGenerator, LabelSyncGenerator } from './generators/label-sync-generator.js';
1
+ // src/index.ts
2
+ import { createApi } from "@spfn/core/nextjs";
3
+ import { logger } from "@spfn/core/logger";
4
+
5
+ // src/lib/bind-locale.ts
6
+ function isLocaleRecord(obj) {
7
+ if (!obj || typeof obj !== "object") {
8
+ return false;
9
+ }
10
+ const values = Object.values(obj);
11
+ if (values.length === 0) {
12
+ return false;
13
+ }
14
+ return values.every((v) => typeof v === "string");
15
+ }
16
+ function bindLocale(labels, locale, fallbackLocale) {
17
+ return createProxy(labels, locale, fallbackLocale);
18
+ }
19
+ function createProxy(obj, locale, fallbackLocale) {
20
+ return new Proxy(obj, {
21
+ get(target, prop) {
22
+ const value = target[prop];
23
+ if (value === void 0) {
24
+ return void 0;
25
+ }
26
+ if (isLocaleRecord(value)) {
27
+ if (value[locale] !== void 0) {
28
+ return value[locale];
29
+ }
30
+ if (fallbackLocale && value[fallbackLocale] !== void 0) {
31
+ return value[fallbackLocale];
32
+ }
33
+ const firstLocale = Object.keys(value)[0];
34
+ return value[firstLocale];
35
+ }
36
+ if (typeof value === "object" && value !== null) {
37
+ return createProxy(value, locale, fallbackLocale);
38
+ }
39
+ return value;
40
+ }
41
+ });
42
+ }
43
+
44
+ // src/lib/helpers.ts
45
+ function setNestedValue(target, path, value) {
46
+ const parts = path.split(".");
47
+ let current = target;
48
+ for (let i = 0; i < parts.length - 1; i++) {
49
+ const part = parts[i];
50
+ if (!current[part]) {
51
+ current[part] = {};
52
+ }
53
+ current = current[part];
54
+ }
55
+ const lastPart = parts[parts.length - 1];
56
+ current[lastPart] = value;
57
+ }
58
+
59
+ // src/lib/define-labels.ts
60
+ function defineLabelConfig(config) {
61
+ return config;
62
+ }
63
+ function defineLabels(labels) {
64
+ return labels;
65
+ }
66
+ function format(template, vars) {
67
+ return template.replace(/\{(\w+)}/g, (match, key) => {
68
+ const value = vars[key];
69
+ return value !== void 0 ? String(value) : match;
70
+ });
71
+ }
72
+
73
+ // src/index.ts
74
+ var cmsLogger = logger.child("@spfn/cms");
75
+ var api = createApi();
76
+ function createCmsClient(labelsDefinition, config) {
77
+ async function getLabel(section) {
78
+ const locale = await config.getLocale();
79
+ cmsLogger.debug("getLabel called", {
80
+ section,
81
+ locale,
82
+ defaultLocale: config.defaultLocale,
83
+ fallbackLocale: config.fallbackLocale
84
+ });
85
+ const cache = await api.getLabelCache.call({
86
+ query: {
87
+ sections: [section],
88
+ locale
89
+ }
90
+ });
91
+ const filteredLabels = {};
92
+ if (section in labelsDefinition) {
93
+ filteredLabels[section] = labelsDefinition[section];
94
+ }
95
+ const defaults = bindLocale(filteredLabels, locale, config.fallbackLocale);
96
+ const merged = deepMergeCache(defaults, cache, locale);
97
+ return merged[section];
98
+ }
99
+ async function getLabels(sections) {
100
+ const locale = await config.getLocale();
101
+ cmsLogger.debug("getLabels called", {
102
+ sections,
103
+ locale,
104
+ defaultLocale: config.defaultLocale,
105
+ fallbackLocale: config.fallbackLocale,
106
+ availableDefinitionKeys: Object.keys(labelsDefinition)
107
+ });
108
+ const cache = await api.getLabelCache.call({
109
+ query: {
110
+ sections: [...sections],
111
+ locale
112
+ }
113
+ });
114
+ cmsLogger.debug("Fetched from cache", {
115
+ cacheKeys: Object.keys(cache),
116
+ cacheEntryCount: Object.keys(cache).length,
117
+ cacheStructure: Object.entries(cache).map(([key, value]) => ({
118
+ section: key,
119
+ isObject: typeof value === "object",
120
+ isNull: value === null,
121
+ contentKeys: value && typeof value === "object" ? Object.keys(value) : []
122
+ }))
123
+ });
124
+ const filteredLabels = {};
125
+ for (const section of sections) {
126
+ if (section in labelsDefinition) {
127
+ filteredLabels[section] = labelsDefinition[section];
128
+ }
129
+ }
130
+ cmsLogger.debug("Filtered sections", {
131
+ requestedSections: sections,
132
+ filteredSections: Object.keys(filteredLabels),
133
+ filteredLabelsStructure: Object.entries(filteredLabels).map(([key, value]) => ({
134
+ section: key,
135
+ hasValue: !!value,
136
+ isObject: typeof value === "object",
137
+ nestedKeys: value && typeof value === "object" ? Object.keys(value) : []
138
+ }))
139
+ });
140
+ const defaults = bindLocale(filteredLabels, locale, config.fallbackLocale);
141
+ cmsLogger.debug("Generated defaults with locale binding", {
142
+ defaultsKeys: Object.keys(defaults)
143
+ });
144
+ const merged = deepMergeCache(defaults, cache, locale);
145
+ cmsLogger.debug("Merged cache and defaults", {
146
+ mergedKeys: Object.keys(merged)
147
+ });
148
+ return merged;
149
+ }
150
+ return { api, getLabel, getLabels, format };
151
+ }
152
+ function deepMergeCache(defaults, cache, locale) {
153
+ const result = { ...defaults };
154
+ cmsLogger.debug("deepMergeCache: Starting merge", {
155
+ cacheEntries: Object.keys(cache).length,
156
+ locale
157
+ });
158
+ for (const [section, content] of Object.entries(cache)) {
159
+ if (!content || typeof content !== "object") {
160
+ cmsLogger.debug("deepMergeCache: Skipping invalid content", { section });
161
+ continue;
162
+ }
163
+ const contentKeys = Object.keys(content);
164
+ cmsLogger.debug("deepMergeCache: Processing section", {
165
+ section,
166
+ labelCount: contentKeys.length
167
+ });
168
+ for (const [flatKey, value] of Object.entries(content)) {
169
+ let extractedValue;
170
+ if (value && typeof value === "object" && "content" in value) {
171
+ extractedValue = value.content;
172
+ cmsLogger.debug("deepMergeCache: Extracted from content field", {
173
+ flatKey,
174
+ hasContent: true
175
+ });
176
+ } else if (value && typeof value === "object" && locale in value) {
177
+ extractedValue = value[locale];
178
+ cmsLogger.debug("deepMergeCache: Extracted from locale field", {
179
+ flatKey,
180
+ locale
181
+ });
182
+ } else {
183
+ extractedValue = value;
184
+ cmsLogger.debug("deepMergeCache: Using raw value", {
185
+ flatKey,
186
+ valueType: typeof value
187
+ });
188
+ }
189
+ setNestedValue(result, flatKey, extractedValue);
190
+ }
191
+ }
192
+ cmsLogger.debug("deepMergeCache: Merge completed", {
193
+ resultKeys: Object.keys(result)
194
+ });
195
+ return result;
196
+ }
197
+ export {
198
+ createCmsClient,
199
+ defineLabelConfig,
200
+ defineLabels,
201
+ format
202
+ };
24
203
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAErB;;;;;;;GAOG;AAEH,oBAAoB;AACpB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAG7E,kDAAkD;AAClD,cAAc,aAAa,CAAC;AAG5B,oCAAoC;AACpC,cAAc,yBAAyB,CAAC;AAExC,iCAAiC;AACjC,cAAc,qBAAqB,CAAC;AAEpC,wDAAwD;AACxD,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5F,sDAAsD;AACtD,cAAc,mBAAmB,CAAC;AAElC,gDAAgD;AAChD,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC"}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/bind-locale.ts","../src/lib/helpers.ts","../src/lib/define-labels.ts"],"sourcesContent":["import { createApi } from \"@spfn/core/nextjs\";\nimport { logger } from \"@spfn/core/logger\";\nimport { type AppRouter } from './server/routes/index';\nimport { bindLocale, type SectionKeys, type BoundLabelSection, type BoundLabelsSections } from './lib/bind-locale';\nimport { setNestedValue } from './lib/helpers';\nimport { format, defineLabelConfig, defineLabels } from './lib/define-labels';\n\nconst cmsLogger = logger.child('@spfn/cms');\n\n/**\n * Default API client (for backward compatibility or when not using labels)\n */\nconst api = createApi<AppRouter>();\n\n/**\n * Create CMS client with API, label getters, and format utility\n *\n * @param labelsDefinition - Labels defined using defineLabels()\n * @param config - Label config from defineLabelConfig()\n * @returns API client, getLabel (single), getLabels (multiple), and format utility\n *\n * @example\n * ```typescript\n * // labels.ts - Setup once\n * import { getLocale } from '@spfn/cms/actions';\n *\n * export const { api, getLabel, getLabels, format } = createCmsClient(labelsDefinition, {\n * defaultLocale: 'ko',\n * getLocale: () => getLocale('ko'),\n * });\n *\n * // Single section - direct access\n * const label = await getLabel('home');\n * label.hero.title // \"Hello\" (no section name!)\n *\n * // Multiple sections - with section names\n * const labels = await getLabels(['home', 'about']);\n * labels.home.hero.title // \"Hello\"\n * labels.about.title // \"About Us\"\n *\n * // With template variables\n * const greeting = label.hero.greeting; // \"Hello {name}\"\n * format(greeting, { name: \"John\" }); // \"Hello John\"\n * ```\n */\nexport function createCmsClient<T>(\n labelsDefinition: T,\n config: {\n defaultLocale: string;\n fallbackLocale?: string;\n getLocale: () => Promise<string>;\n }\n)\n{\n /**\n * Get a single section's labels (without section name wrapper)\n *\n * @param section - Section name to fetch\n * @returns Labels for the section, directly accessible\n *\n * @example\n * ```typescript\n * const label = await getLabel('signup');\n * label.title // Direct access\n * label.userName\n * ```\n */\n async function getLabel<K extends SectionKeys<T>>(section: K): Promise<BoundLabelSection<T, K>>\n {\n // Auto-detect locale from cookie via injected getLocale\n const locale = await config.getLocale();\n\n cmsLogger.debug('getLabel called', {\n section,\n locale,\n defaultLocale: config.defaultLocale,\n fallbackLocale: config.fallbackLocale,\n });\n\n // 1. Fetch from published_cache\n const cache = await api.getLabelCache.call({\n query: {\n sections: [section as string],\n locale\n }\n });\n\n // 2. Filter only requested section\n const filteredLabels: any = {};\n if (section in (labelsDefinition as any))\n {\n filteredLabels[section] = (labelsDefinition as any)[section];\n }\n\n // 3. Generate defaults with locale binding\n const defaults = bindLocale(filteredLabels, locale, config.fallbackLocale);\n\n // 4. Merge: cache takes priority, fallback to defaults\n const merged = deepMergeCache(defaults, cache, locale);\n\n // 5. Return only the section content (without section name)\n return merged[section] as BoundLabelSection<T, K>;\n }\n\n /**\n * Get multiple sections' labels (with section names as keys)\n *\n * @param sections - Array of section names to fetch\n * @returns Object with section names as keys\n *\n * @example\n * ```typescript\n * const labels = await getLabels(['home', 'about']);\n * labels.home.title\n * labels.about.description\n * ```\n */\n async function getLabels<K extends SectionKeys<T>>(sections: readonly K[]): Promise<BoundLabelsSections<T, K>>\n {\n // Auto-detect locale from cookie via injected getLocale\n const locale = await config.getLocale();\n\n cmsLogger.debug('getLabels called', {\n sections,\n locale,\n defaultLocale: config.defaultLocale,\n fallbackLocale: config.fallbackLocale,\n availableDefinitionKeys: Object.keys(labelsDefinition as any),\n });\n\n // 1. Fetch from published_cache\n const cache = await api.getLabelCache.call({\n query: {\n sections: [...sections] as unknown as string[],\n locale\n }\n });\n\n cmsLogger.debug('Fetched from cache', {\n cacheKeys: Object.keys(cache),\n cacheEntryCount: Object.keys(cache).length,\n cacheStructure: Object.entries(cache).map(([key, value]) => ({\n section: key,\n isObject: typeof value === 'object',\n isNull: value === null,\n contentKeys: value && typeof value === 'object' ? Object.keys(value) : [],\n })),\n });\n\n // 2. Filter only requested sections (performance optimization)\n const filteredLabels: any = {};\n for (const section of sections)\n {\n if (section in (labelsDefinition as any))\n {\n filteredLabels[section] = (labelsDefinition as any)[section];\n }\n }\n\n cmsLogger.debug('Filtered sections', {\n requestedSections: sections,\n filteredSections: Object.keys(filteredLabels),\n filteredLabelsStructure: Object.entries(filteredLabels).map(([key, value]) => ({\n section: key,\n hasValue: !!value,\n isObject: typeof value === 'object',\n nestedKeys: value && typeof value === 'object' ? Object.keys(value) : [],\n })),\n });\n\n // 3. Generate defaults with locale binding (only for requested sections)\n const defaults = bindLocale(filteredLabels, locale, config.fallbackLocale);\n\n cmsLogger.debug('Generated defaults with locale binding', {\n defaultsKeys: Object.keys(defaults),\n });\n\n // 4. Merge: cache takes priority, fallback to defaults\n const merged = deepMergeCache(defaults, cache, locale);\n\n cmsLogger.debug('Merged cache and defaults', {\n mergedKeys: Object.keys(merged),\n });\n\n return merged as BoundLabelsSections<T, K>;\n }\n\n return { api, getLabel, getLabels, format };\n}\n\n/**\n * Deep merge cache into defaults\n */\nfunction deepMergeCache(defaults: any, cache: Record<string, any>, locale: string): any\n{\n const result = { ...defaults };\n\n cmsLogger.debug('deepMergeCache: Starting merge', {\n cacheEntries: Object.keys(cache).length,\n locale,\n });\n\n for (const [section, content] of Object.entries(cache))\n {\n if (!content || typeof content !== 'object')\n {\n cmsLogger.debug('deepMergeCache: Skipping invalid content', { section });\n continue;\n }\n\n const contentKeys = Object.keys(content);\n cmsLogger.debug('deepMergeCache: Processing section', {\n section,\n labelCount: contentKeys.length,\n });\n\n for (const [flatKey, value] of Object.entries(content))\n {\n // Extract locale-specific value from LabelValue format\n let extractedValue: any;\n\n if (value && typeof value === 'object' && 'content' in value)\n {\n extractedValue = (value as any).content;\n cmsLogger.debug('deepMergeCache: Extracted from content field', {\n flatKey,\n hasContent: true,\n });\n }\n else if (value && typeof value === 'object' && locale in value)\n {\n extractedValue = (value as any)[locale];\n cmsLogger.debug('deepMergeCache: Extracted from locale field', {\n flatKey,\n locale,\n });\n }\n else\n {\n extractedValue = value;\n cmsLogger.debug('deepMergeCache: Using raw value', {\n flatKey,\n valueType: typeof value,\n });\n }\n\n // Set value using helper function\n setNestedValue(result, flatKey, extractedValue);\n }\n }\n\n cmsLogger.debug('deepMergeCache: Merge completed', {\n resultKeys: Object.keys(result),\n });\n\n return result;\n}\n\n/**\n * Re-export format utility for standalone use\n *\n * @example\n * ```typescript\n * import { format } from '@spfn/cms/api-client';\n *\n * const text = \"Hello {name}, you have {count} messages\";\n * format(text, { name: \"John\", count: 5 });\n * // \"Hello John, you have 5 messages\"\n * ```\n */\nexport { format, defineLabelConfig, defineLabels };\n\n/**\n * Re-export types for external use\n */\nexport type { BoundLabels, SectionKeys, BoundLabelSection, BoundLabelsSections } from './lib/bind-locale';","/**\n * Bind locale to labels, returning locale-specific values\n *\n * @example\n * ```ts\n * const labelsDefinition = defineLabels({\n * home: {\n * title: { en: \"Home\", ko: \"홈\" }\n * }\n * });\n *\n * const labels = bindLocale(labelsDefinition, 'ko');\n * labels.home.title // \"홈\"\n * ```\n */\n\n/**\n * Type that converts locale records to strings\n */\nexport type BoundLabels<T> = {\n [K in keyof T]: T[K] extends Record<string, any>\n ? IsLocaleRecord<T[K]> extends true\n ? string\n : BoundLabels<T[K]>\n : T[K];\n};\n\n/**\n * Extract section keys from label definition\n */\nexport type SectionKeys<T> = Extract<keyof T, string>;\n\n/**\n * Get content of a single section (without section name wrapper)\n */\nexport type BoundLabelSection<T, K extends SectionKeys<T>> = BoundLabels<T>[K];\n\n/**\n * Pick specific sections from bound labels (for multiple sections)\n */\nexport type BoundLabelsSections<T, K extends SectionKeys<T>> = Pick<BoundLabels<T>, K>;\n\n/**\n * Check if object is a locale record (has string values only)\n */\ntype IsLocaleRecord<T> = T extends Record<string, string> ? true : false;\n\n/**\n * Check if an object is a locale record at runtime\n */\nfunction isLocaleRecord(obj: any): boolean\n{\n if (!obj || typeof obj !== 'object')\n {\n return false;\n }\n\n const values = Object.values(obj);\n\n // Empty object is not a locale record\n if (values.length === 0)\n {\n return false;\n }\n\n // All values must be strings\n return values.every(v => typeof v === 'string');\n}\n\n/**\n * Bind a locale to label definitions, returning locale-specific values\n *\n * @param labels - Label definitions with locale records\n * @param locale - Locale to bind (e.g., 'en', 'ko')\n * @param fallbackLocale - Optional fallback locale if value not found\n * @returns Labels with locale-specific string values\n *\n * @example\n * ```typescript\n * const labelsDefinition = defineLabels({\n * home: {\n * title: { en: \"Home\", ko: \"홈\" },\n * hero: {\n * title: { en: \"Welcome\", ko: \"환영합니다\" }\n * }\n * }\n * });\n *\n * const labels = bindLocale(labelsDefinition, 'ko');\n * labels.home.title // \"홈\"\n * labels.home.hero.title // \"환영합니다\"\n *\n * // With fallback\n * const labelsEn = bindLocale(labelsDefinition, 'en', 'ko');\n * ```\n */\nexport function bindLocale<T>(\n labels: T,\n locale: string,\n fallbackLocale?: string\n): BoundLabels<T>\n{\n return createProxy(labels, locale, fallbackLocale) as BoundLabels<T>;\n}\n\n/**\n * Create a proxy that intercepts property access and returns locale-specific values\n */\nfunction createProxy(obj: any, locale: string, fallbackLocale?: string): any\n{\n return new Proxy(obj, {\n get(target, prop)\n {\n const value = target[prop];\n\n // If value doesn't exist, return undefined\n if (value === undefined)\n {\n return undefined;\n }\n\n // If this is a locale record, return the locale value\n if (isLocaleRecord(value))\n {\n // Try to get the requested locale\n if (value[locale] !== undefined)\n {\n return value[locale];\n }\n\n // Fallback to fallbackLocale if specified\n if (fallbackLocale && value[fallbackLocale] !== undefined)\n {\n return value[fallbackLocale];\n }\n\n // If locale not found, return first available locale\n const firstLocale = Object.keys(value)[0];\n return value[firstLocale];\n }\n\n // If this is a nested object, wrap it in a proxy\n if (typeof value === 'object' && value !== null)\n {\n return createProxy(value, locale, fallbackLocale);\n }\n\n // Otherwise return as-is\n return value;\n },\n });\n}","/**\n * CMS Helper Functions\n */\n\nexport type FlatLabel = Record<string, Record<string, string>>;\n\nexport type FlatLabelItem = {\n values: Record<string, string>;\n description?: string;\n};\n\nexport type FlatLabelWithDescription = Record<string, FlatLabelItem>;\n\n/**\n * Flatten nested label structure into dot notation\n *\n * @param labels - Nested label object\n * @param prefix - Key prefix for recursion\n * @returns Flattened label structure with description\n *\n * @example\n * ```typescript\n * const nested = {\n * home: {\n * hero: {\n * title: {\n * en: \"Welcome\",\n * ko: \"환영합니다\",\n * $description: \"Main hero title\"\n * }\n * }\n * }\n * };\n *\n * const flat = flattenLabels(nested);\n * // {\n * // \"home.hero.title\": {\n * // values: { en: \"Welcome\", ko: \"환영합니다\" },\n * // description: \"Main hero title\"\n * // }\n * // }\n * ```\n */\nexport function flattenLabels<T extends Record<string, any>>(\n labels: T,\n prefix = ''\n): FlatLabelWithDescription\n{\n const result: FlatLabelWithDescription = {};\n\n if (!labels || typeof labels !== 'object')\n {\n return result;\n }\n\n const obj = labels as Record<string, unknown>;\n\n for (const [key, value] of Object.entries(obj))\n {\n const newKey = prefix ? `${prefix}.${key}` : key;\n\n if (!value || typeof value !== 'object')\n {\n continue;\n }\n\n const valueObj = value as Record<string, unknown>;\n\n // Check if this is a leaf node (locale values: { en: \"...\", ko: \"...\", $description?: \"...\" })\n // All values must be strings (including $description)\n const isLeaf = Object.values(valueObj).every(v => typeof v === 'string');\n\n if (isLeaf)\n {\n const { $description, ...localeValues } = valueObj as Record<string, string>;\n\n result[newKey] = {\n values: localeValues,\n description: $description,\n };\n }\n else\n {\n // Recursively flatten nested structure\n Object.assign(result, flattenLabels(value, newKey));\n }\n }\n\n return result;\n}\n\n/**\n * Set a value in nested object using dot notation path\n *\n * @param target - Target object to modify\n * @param path - Dot notation path (e.g., \"home.hero.title\")\n * @param value - Value to set\n *\n * @example\n * ```typescript\n * const obj = {};\n * setNestedValue(obj, \"home.hero.title\", \"Welcome\");\n * // obj = { home: { hero: { title: \"Welcome\" } } }\n * ```\n */\nexport function setNestedValue(target: any, path: string, value: any): void\n{\n const parts = path.split('.');\n let current = target;\n\n for (let i = 0; i < parts.length - 1; i++)\n {\n const part = parts[i];\n if (!current[part])\n {\n current[part] = {};\n }\n current = current[part];\n }\n\n // Set the leaf value\n const lastPart = parts[parts.length - 1];\n current[lastPart] = value;\n}\n\n/**\n * Unflatten dot notation keys back to nested structure\n *\n * @param flat - Flattened label object\n * @returns Nested label structure\n *\n * @example\n * ```typescript\n * const flat = {\n * \"home.hero.title\": { en: \"Welcome\", ko: \"환영합니다\" },\n * \"home.hero.subtitle\": { en: \"Subtitle\", ko: \"부제목\" }\n * };\n *\n * const nested = unflattenLabels(flat);\n * // {\n * // home: {\n * // hero: {\n * // title: { en: \"Welcome\", ko: \"환영합니다\" },\n * // subtitle: { en: \"Subtitle\", ko: \"부제목\" }\n * // }\n * // }\n * // }\n * ```\n */\nexport function unflattenLabels(flat: FlatLabel): Record<string, any>\n{\n const result: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(flat))\n {\n setNestedValue(result, key, value);\n }\n\n return result;\n}","/**\n * Defines a type-safe label configuration.\n *\n * @example\n * ```ts\n * export const labelConfig = defineLabelConfig({\n * locales: ['en', 'ar'] as const,\n * defaultLocale: 'en',\n * fallbackLocale: 'en', // Optional\n * });\n *\n * export type LabelConfig = typeof labelConfig;\n * export type AppLocale = typeof labelConfig.locales[number]; // 'en' | 'ar'\n * ```\n */\nexport function defineLabelConfig<const TLocales extends readonly string[]>(config: {\n locales: TLocales;\n defaultLocale: TLocales[number];\n fallbackLocale?: TLocales[number];\n useBrowserLanguage?: boolean;\n})\n{\n return config;\n}\n\n/**\n * Define nested label structure (tRPC-style)\n *\n * @example\n * ```ts\n * export const labels = defineLabels({\n * home: {\n * slogan: { en: \"Welcome\", ko: \"환영합니다\" },\n * hero: {\n * title: { en: \"Hello\", ko: \"안녕하세요\" }\n * }\n * },\n * about: {\n * title: { en: \"About Us\", ko: \"회사 소개\" }\n * }\n * });\n *\n * // Usage\n * labels.home.slogan;\n * labels.home.hero.title;\n * labels.about.title;\n * ```\n */\nexport function defineLabels<const T>(labels: T)\n{\n return labels;\n}\n\nexport function format(template: string, vars: Record<string, string | number>): string\n{\n return template.replace(/\\{(\\w+)}/g, (match, key) =>\n {\n const value = vars[key];\n return value !== undefined ? String(value) : match;\n });\n}"],"mappings":"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","names":[]}