@hua-labs/hua-ux 0.1.0-alpha.0.1

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 (210) hide show
  1. package/README.md +839 -0
  2. package/dist/framework/a11y/components/LiveRegion.d.ts +64 -0
  3. package/dist/framework/a11y/components/LiveRegion.d.ts.map +1 -0
  4. package/dist/framework/a11y/components/LiveRegion.js +43 -0
  5. package/dist/framework/a11y/components/SkipToContent.d.ts +62 -0
  6. package/dist/framework/a11y/components/SkipToContent.d.ts.map +1 -0
  7. package/dist/framework/a11y/components/SkipToContent.js +60 -0
  8. package/dist/framework/a11y/hooks/useFocusManagement.d.ts +60 -0
  9. package/dist/framework/a11y/hooks/useFocusManagement.d.ts.map +1 -0
  10. package/dist/framework/a11y/hooks/useFocusManagement.js +71 -0
  11. package/dist/framework/a11y/hooks/useFocusTrap.d.ts +64 -0
  12. package/dist/framework/a11y/hooks/useFocusTrap.d.ts.map +1 -0
  13. package/dist/framework/a11y/hooks/useFocusTrap.js +185 -0
  14. package/dist/framework/a11y/hooks/useLiveRegion.d.ts +56 -0
  15. package/dist/framework/a11y/hooks/useLiveRegion.d.ts.map +1 -0
  16. package/dist/framework/a11y/hooks/useLiveRegion.js +60 -0
  17. package/dist/framework/a11y/index.d.ts +16 -0
  18. package/dist/framework/a11y/index.d.ts.map +1 -0
  19. package/dist/framework/a11y/index.js +11 -0
  20. package/dist/framework/branding/context.d.ts +52 -0
  21. package/dist/framework/branding/context.d.ts.map +1 -0
  22. package/dist/framework/branding/context.js +96 -0
  23. package/dist/framework/branding/css-vars.d.ts +34 -0
  24. package/dist/framework/branding/css-vars.d.ts.map +1 -0
  25. package/dist/framework/branding/css-vars.js +95 -0
  26. package/dist/framework/branding/tailwind-config.d.ts +38 -0
  27. package/dist/framework/branding/tailwind-config.d.ts.map +1 -0
  28. package/dist/framework/branding/tailwind-config.js +66 -0
  29. package/dist/framework/components/BrandedButton.d.ts +53 -0
  30. package/dist/framework/components/BrandedButton.d.ts.map +1 -0
  31. package/dist/framework/components/BrandedButton.js +40 -0
  32. package/dist/framework/components/BrandedCard.d.ts +52 -0
  33. package/dist/framework/components/BrandedCard.d.ts.map +1 -0
  34. package/dist/framework/components/BrandedCard.js +73 -0
  35. package/dist/framework/components/ErrorBoundary.d.ts +92 -0
  36. package/dist/framework/components/ErrorBoundary.d.ts.map +1 -0
  37. package/dist/framework/components/ErrorBoundary.js +121 -0
  38. package/dist/framework/components/HuaUxLayout.d.ts +29 -0
  39. package/dist/framework/components/HuaUxLayout.d.ts.map +1 -0
  40. package/dist/framework/components/HuaUxLayout.js +32 -0
  41. package/dist/framework/components/HuaUxPage.d.ts +48 -0
  42. package/dist/framework/components/HuaUxPage.d.ts.map +1 -0
  43. package/dist/framework/components/HuaUxPage.js +105 -0
  44. package/dist/framework/components/Providers.d.ts +17 -0
  45. package/dist/framework/components/Providers.d.ts.map +1 -0
  46. package/dist/framework/components/Providers.js +72 -0
  47. package/dist/framework/components/WelcomePage.d.ts +44 -0
  48. package/dist/framework/components/WelcomePage.d.ts.map +1 -0
  49. package/dist/framework/components/WelcomePage.js +80 -0
  50. package/dist/framework/config/index.d.ts +182 -0
  51. package/dist/framework/config/index.d.ts.map +1 -0
  52. package/dist/framework/config/index.js +329 -0
  53. package/dist/framework/config/merge.d.ts +26 -0
  54. package/dist/framework/config/merge.d.ts.map +1 -0
  55. package/dist/framework/config/merge.js +160 -0
  56. package/dist/framework/config/schema.d.ts +25 -0
  57. package/dist/framework/config/schema.d.ts.map +1 -0
  58. package/dist/framework/config/schema.js +122 -0
  59. package/dist/framework/hooks/useMotion.d.ts +45 -0
  60. package/dist/framework/hooks/useMotion.d.ts.map +1 -0
  61. package/dist/framework/hooks/useMotion.js +40 -0
  62. package/dist/framework/index.d.ts +37 -0
  63. package/dist/framework/index.d.ts.map +1 -0
  64. package/dist/framework/index.js +42 -0
  65. package/dist/framework/license/errors.d.ts +15 -0
  66. package/dist/framework/license/errors.d.ts.map +1 -0
  67. package/dist/framework/license/errors.js +52 -0
  68. package/dist/framework/license/index.d.ts +70 -0
  69. package/dist/framework/license/index.d.ts.map +1 -0
  70. package/dist/framework/license/index.js +124 -0
  71. package/dist/framework/license/loader.d.ts +26 -0
  72. package/dist/framework/license/loader.d.ts.map +1 -0
  73. package/dist/framework/license/loader.js +137 -0
  74. package/dist/framework/license/types.d.ts +67 -0
  75. package/dist/framework/license/types.d.ts.map +1 -0
  76. package/dist/framework/license/types.js +18 -0
  77. package/dist/framework/loading/components/SkeletonGroup.d.ts +44 -0
  78. package/dist/framework/loading/components/SkeletonGroup.d.ts.map +1 -0
  79. package/dist/framework/loading/components/SkeletonGroup.js +34 -0
  80. package/dist/framework/loading/components/SuspenseWrapper.d.ts +58 -0
  81. package/dist/framework/loading/components/SuspenseWrapper.d.ts.map +1 -0
  82. package/dist/framework/loading/components/SuspenseWrapper.js +40 -0
  83. package/dist/framework/loading/hoc/withSuspense.d.ts +46 -0
  84. package/dist/framework/loading/hoc/withSuspense.d.ts.map +1 -0
  85. package/dist/framework/loading/hoc/withSuspense.js +54 -0
  86. package/dist/framework/loading/hooks/useDelayedLoading.d.ts +56 -0
  87. package/dist/framework/loading/hooks/useDelayedLoading.d.ts.map +1 -0
  88. package/dist/framework/loading/hooks/useDelayedLoading.js +97 -0
  89. package/dist/framework/loading/hooks/useLoadingState.d.ts +69 -0
  90. package/dist/framework/loading/hooks/useLoadingState.d.ts.map +1 -0
  91. package/dist/framework/loading/hooks/useLoadingState.js +59 -0
  92. package/dist/framework/loading/index.d.ts +16 -0
  93. package/dist/framework/loading/index.d.ts.map +1 -0
  94. package/dist/framework/loading/index.js +13 -0
  95. package/dist/framework/middleware/i18n.d.ts +90 -0
  96. package/dist/framework/middleware/i18n.d.ts.map +1 -0
  97. package/dist/framework/middleware/i18n.js +99 -0
  98. package/dist/framework/plugins/index.d.ts +8 -0
  99. package/dist/framework/plugins/index.d.ts.map +1 -0
  100. package/dist/framework/plugins/index.js +6 -0
  101. package/dist/framework/plugins/registry.d.ts +95 -0
  102. package/dist/framework/plugins/registry.d.ts.map +1 -0
  103. package/dist/framework/plugins/registry.js +160 -0
  104. package/dist/framework/plugins/types.d.ts +97 -0
  105. package/dist/framework/plugins/types.d.ts.map +1 -0
  106. package/dist/framework/plugins/types.js +6 -0
  107. package/dist/framework/seo/geo/examples.d.ts +87 -0
  108. package/dist/framework/seo/geo/examples.d.ts.map +1 -0
  109. package/dist/framework/seo/geo/examples.js +295 -0
  110. package/dist/framework/seo/geo/generateGEOMetadata.d.ts +107 -0
  111. package/dist/framework/seo/geo/generateGEOMetadata.d.ts.map +1 -0
  112. package/dist/framework/seo/geo/generateGEOMetadata.js +404 -0
  113. package/dist/framework/seo/geo/index.d.ts +19 -0
  114. package/dist/framework/seo/geo/index.d.ts.map +1 -0
  115. package/dist/framework/seo/geo/index.js +21 -0
  116. package/dist/framework/seo/geo/presets.d.ts +52 -0
  117. package/dist/framework/seo/geo/presets.d.ts.map +1 -0
  118. package/dist/framework/seo/geo/presets.js +47 -0
  119. package/dist/framework/seo/geo/structuredData.d.ts +187 -0
  120. package/dist/framework/seo/geo/structuredData.d.ts.map +1 -0
  121. package/dist/framework/seo/geo/structuredData.js +354 -0
  122. package/dist/framework/seo/geo/test-utils.d.ts +78 -0
  123. package/dist/framework/seo/geo/test-utils.d.ts.map +1 -0
  124. package/dist/framework/seo/geo/test-utils.js +139 -0
  125. package/dist/framework/seo/geo/types.d.ts +225 -0
  126. package/dist/framework/seo/geo/types.d.ts.map +1 -0
  127. package/dist/framework/seo/geo/types.js +51 -0
  128. package/dist/framework/types/index.d.ts +577 -0
  129. package/dist/framework/types/index.d.ts.map +1 -0
  130. package/dist/framework/types/index.js +6 -0
  131. package/dist/framework/utils/data-fetching.d.ts +45 -0
  132. package/dist/framework/utils/data-fetching.d.ts.map +1 -0
  133. package/dist/framework/utils/data-fetching.js +74 -0
  134. package/dist/framework/utils/file-structure.d.ts +29 -0
  135. package/dist/framework/utils/file-structure.d.ts.map +1 -0
  136. package/dist/framework/utils/file-structure.js +72 -0
  137. package/dist/framework/utils/metadata.d.ts +109 -0
  138. package/dist/framework/utils/metadata.d.ts.map +1 -0
  139. package/dist/framework/utils/metadata.js +105 -0
  140. package/dist/index.d.ts +15 -0
  141. package/dist/index.d.ts.map +1 -0
  142. package/dist/index.js +21 -0
  143. package/dist/presets/index.d.ts +8 -0
  144. package/dist/presets/index.d.ts.map +1 -0
  145. package/dist/presets/index.js +7 -0
  146. package/dist/presets/marketing.d.ts +41 -0
  147. package/dist/presets/marketing.d.ts.map +1 -0
  148. package/dist/presets/marketing.js +81 -0
  149. package/dist/presets/product.d.ts +41 -0
  150. package/dist/presets/product.d.ts.map +1 -0
  151. package/dist/presets/product.js +74 -0
  152. package/package.json +91 -0
  153. package/src/framework/README.md +329 -0
  154. package/src/framework/__tests__/branding/css-vars.test.ts +147 -0
  155. package/src/framework/__tests__/components/ErrorBoundary.test.tsx +146 -0
  156. package/src/framework/__tests__/config/defineConfig.test.ts +138 -0
  157. package/src/framework/__tests__/hooks/useMotion.test.ts +105 -0
  158. package/src/framework/__tests__/seo/geo/generateGEOMetadata.test.ts +207 -0
  159. package/src/framework/__tests__/seo/geo/structuredData.test.ts +262 -0
  160. package/src/framework/a11y/components/LiveRegion.tsx +89 -0
  161. package/src/framework/a11y/components/SkipToContent.tsx +103 -0
  162. package/src/framework/a11y/hooks/useFocusManagement.ts +125 -0
  163. package/src/framework/a11y/hooks/useFocusTrap.ts +239 -0
  164. package/src/framework/a11y/hooks/useLiveRegion.ts +95 -0
  165. package/src/framework/a11y/index.ts +17 -0
  166. package/src/framework/branding/context.tsx +135 -0
  167. package/src/framework/branding/css-vars.ts +110 -0
  168. package/src/framework/branding/tailwind-config.ts +90 -0
  169. package/src/framework/components/BrandedButton.tsx +94 -0
  170. package/src/framework/components/BrandedCard.tsx +87 -0
  171. package/src/framework/components/ErrorBoundary.tsx +215 -0
  172. package/src/framework/components/HuaUxLayout.tsx +36 -0
  173. package/src/framework/components/HuaUxPage.tsx +138 -0
  174. package/src/framework/components/Providers.tsx +98 -0
  175. package/src/framework/components/WelcomePage.tsx +207 -0
  176. package/src/framework/config/index.ts +349 -0
  177. package/src/framework/config/merge.ts +190 -0
  178. package/src/framework/config/schema.ts +140 -0
  179. package/src/framework/hooks/useMotion.ts +57 -0
  180. package/src/framework/index.ts +122 -0
  181. package/src/framework/license/errors.ts +63 -0
  182. package/src/framework/license/index.ts +137 -0
  183. package/src/framework/license/loader.ts +158 -0
  184. package/src/framework/license/types.ts +95 -0
  185. package/src/framework/loading/components/SkeletonGroup.tsx +70 -0
  186. package/src/framework/loading/components/SuspenseWrapper.tsx +88 -0
  187. package/src/framework/loading/hoc/withSuspense.tsx +96 -0
  188. package/src/framework/loading/hooks/useDelayedLoading.ts +127 -0
  189. package/src/framework/loading/hooks/useLoadingState.ts +103 -0
  190. package/src/framework/loading/index.ts +19 -0
  191. package/src/framework/middleware/i18n.ts +161 -0
  192. package/src/framework/middleware/index.ts +7 -0
  193. package/src/framework/plugins/index.ts +13 -0
  194. package/src/framework/plugins/registry.ts +186 -0
  195. package/src/framework/plugins/types.ts +106 -0
  196. package/src/framework/seo/geo/examples.tsx +415 -0
  197. package/src/framework/seo/geo/generateGEOMetadata.ts +441 -0
  198. package/src/framework/seo/geo/index.ts +61 -0
  199. package/src/framework/seo/geo/presets.ts +58 -0
  200. package/src/framework/seo/geo/structuredData.ts +422 -0
  201. package/src/framework/seo/geo/test-utils.ts +179 -0
  202. package/src/framework/seo/geo/types.ts +315 -0
  203. package/src/framework/types/index.ts +623 -0
  204. package/src/framework/utils/data-fetching.ts +95 -0
  205. package/src/framework/utils/file-structure.ts +88 -0
  206. package/src/framework/utils/metadata.ts +152 -0
  207. package/src/index.ts +31 -0
  208. package/src/presets/index.ts +8 -0
  209. package/src/presets/marketing.ts +88 -0
  210. package/src/presets/product.ts +81 -0
@@ -0,0 +1,107 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - GEO Metadata Generator
3
+ *
4
+ * Generate AI-friendly metadata for Generative Engine Optimization (GEO)
5
+ * ChatGPT, Claude, Gemini, Perplexity가 hua-ux를 잘 찾고 추천하도록 메타데이터 생성
6
+ */
7
+ import type { GEOConfig, GEOMetadata } from './types';
8
+ /**
9
+ * Convert meta tags array to object
10
+ *
11
+ * 메타 태그 배열을 객체로 변환 (Next.js metadata API에서 사용)
12
+ *
13
+ * @param meta - Array of meta tags
14
+ * @returns Object with meta tag names as keys
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const metaObj = metaToObject(geoMeta.meta);
19
+ * return {
20
+ * title: 'My App',
21
+ * description: metaObj.description,
22
+ * keywords: metaObj.keywords,
23
+ * };
24
+ * ```
25
+ */
26
+ export declare function metaToObject(meta: GEOMetadata['meta']): Record<string, string>;
27
+ /**
28
+ * Convert Open Graph tags array to object
29
+ *
30
+ * Open Graph 태그 배열을 객체로 변환
31
+ *
32
+ * @param og - Array of Open Graph tags
33
+ * @returns Object with Open Graph properties as keys
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * const ogObj = openGraphToObject(geoMeta.openGraph);
38
+ * return {
39
+ * openGraph: {
40
+ * title: ogObj['og:title'],
41
+ * description: ogObj['og:description'],
42
+ * },
43
+ * };
44
+ * ```
45
+ */
46
+ export declare function openGraphToObject(og: GEOMetadata['openGraph']): Record<string, string>;
47
+ export declare function generateGEOMetadata(config: GEOConfig): GEOMetadata;
48
+ /**
49
+ * Render JSON-LD for Next.js Script component
50
+ *
51
+ * XSS 보안을 위해 위험한 문자를 이스케이프합니다.
52
+ * Escapes dangerous characters for XSS security.
53
+ *
54
+ * Next.js에서 사용할 수 있는 JSON-LD script 태그 생성
55
+ *
56
+ * @param jsonLd - JSON-LD structured data
57
+ * @param id - Optional script ID (default: auto-generated)
58
+ * @returns Props for Next.js Script component with:
59
+ * - `id`: Unique script ID
60
+ * - `type`: 'application/ld+json'
61
+ * - `dangerouslySetInnerHTML.__html`: Escaped JSON string
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * import Script from 'next/script';
66
+ * import { renderJSONLD } from '@hua-labs/hua-ux/framework';
67
+ *
68
+ * const geoMeta = generateGEOMetadata({ ... });
69
+ *
70
+ * export default function Page() {
71
+ * return (
72
+ * <>
73
+ * <Script {...renderJSONLD(geoMeta.jsonLd[0])} />
74
+ * <main>...</main>
75
+ * </>
76
+ * );
77
+ * }
78
+ * ```
79
+ */
80
+ export declare function renderJSONLD(jsonLd: unknown, id?: string): {
81
+ id: string;
82
+ type: string;
83
+ dangerouslySetInnerHTML: {
84
+ __html: string;
85
+ };
86
+ };
87
+ /**
88
+ * Create AI-friendly context description
89
+ *
90
+ * AI가 맥락을 이해하기 쉽도록 풍부한 설명 생성
91
+ *
92
+ * @param config - GEO configuration
93
+ * @returns AI-friendly context string
94
+ *
95
+ * @example
96
+ * ```tsx
97
+ * const context = createAIContext({
98
+ * name: 'hua-ux',
99
+ * description: 'Privacy-first UX framework',
100
+ * features: ['i18n', 'Motion', 'Accessibility'],
101
+ * useCases: ['Multilingual apps', 'Accessible UX'],
102
+ * });
103
+ * // Returns: "hua-ux is a Privacy-first UX framework. Key features include: i18n, Motion, Accessibility. Common use cases: Multilingual apps, Accessible UX."
104
+ * ```
105
+ */
106
+ export declare function createAIContext(config: GEOConfig): string;
107
+ //# sourceMappingURL=generateGEOMetadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateGEOMetadata.d.ts","sourceRoot":"","sources":["../../../../src/framework/seo/geo/generateGEOMetadata.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA0HtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE9E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,WAAW,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGtF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAiIlE;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG;IAC1D,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C,CA6BA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CA0CzD"}
@@ -0,0 +1,404 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - GEO Metadata Generator
3
+ *
4
+ * Generate AI-friendly metadata for Generative Engine Optimization (GEO)
5
+ * ChatGPT, Claude, Gemini, Perplexity가 hua-ux를 잘 찾고 추천하도록 메타데이터 생성
6
+ */
7
+ import { META_NAMES, OG_PROPERTIES } from './types';
8
+ import { generateSoftwareApplicationLD } from './structuredData';
9
+ /**
10
+ * Generate GEO (Generative Engine Optimization) Metadata
11
+ *
12
+ * AI 검색 엔진이 소프트웨어를 정확하게 이해하고 추천할 수 있도록
13
+ * 구조화된 메타데이터를 생성합니다.
14
+ *
15
+ * Generate structured metadata that helps AI search engines (ChatGPT, Claude,
16
+ * Gemini, Perplexity) accurately understand and recommend your software.
17
+ *
18
+ * @param config - GEO configuration
19
+ * @returns Object containing:
20
+ * - `meta`: Array of HTML meta tags with name and content
21
+ * - `jsonLd`: Array of Schema.org JSON-LD structured data objects
22
+ * - `openGraph`: Array of Open Graph meta tags (optional)
23
+ * - `twitter`: Array of Twitter Card meta tags (optional)
24
+ *
25
+ * @throws {Error} If config.name or config.description is empty
26
+ * @throws {Error} If config.url is provided but invalid
27
+ * @throws {Error} If config.codeRepository is provided but invalid
28
+ * @throws {Error} If config.documentationUrl is provided but invalid
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * // Basic usage
33
+ * const geoMetadata = generateGEOMetadata({
34
+ * name: 'hua-ux',
35
+ * description: 'Privacy-first UX framework for Next.js with built-in i18n, motion, and accessibility',
36
+ * version: '1.0.0',
37
+ * applicationCategory: ['UX Framework', 'Developer Tool'],
38
+ * programmingLanguage: ['TypeScript', 'React', 'Next.js'],
39
+ * features: [
40
+ * 'Privacy-first architecture',
41
+ * 'Built-in internationalization (i18n)',
42
+ * 'Motion animations with hua-motion',
43
+ * 'WCAG 2.1 compliant accessibility',
44
+ * 'Automatic error handling',
45
+ * 'Loading state optimization',
46
+ * ],
47
+ * useCases: [
48
+ * 'Building multilingual Next.js applications',
49
+ * 'Creating accessible web applications',
50
+ * 'Rapid prototyping with AI-friendly documentation',
51
+ * ],
52
+ * keywords: [
53
+ * 'nextjs',
54
+ * 'react',
55
+ * 'ux',
56
+ * 'i18n',
57
+ * 'internationalization',
58
+ * 'accessibility',
59
+ * 'a11y',
60
+ * 'motion',
61
+ * 'animation',
62
+ * 'privacy',
63
+ * ],
64
+ * codeRepository: 'https://github.com/hua-labs/hua',
65
+ * documentationUrl: 'https://hua-labs.dev/docs/hua-ux',
66
+ * license: 'MIT',
67
+ * author: {
68
+ * name: 'hua-labs',
69
+ * url: 'https://hua-labs.dev',
70
+ * },
71
+ * });
72
+ * ```
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * // Use with Next.js metadata
77
+ * import { generateGEOMetadata } from '@hua-labs/hua-ux/framework';
78
+ *
79
+ * export async function generateMetadata() {
80
+ * const geoMeta = generateGEOMetadata({
81
+ * name: 'My App',
82
+ * description: 'Built with hua-ux',
83
+ * features: ['i18n', 'Dark mode', 'Responsive'],
84
+ * });
85
+ *
86
+ * return {
87
+ * title: 'My App',
88
+ * description: geoMeta.meta.find(m => m.name === 'description')?.content,
89
+ * // Add JSON-LD to page
90
+ * other: {
91
+ * 'script:ld+json': JSON.stringify(geoMeta.jsonLd),
92
+ * },
93
+ * };
94
+ * }
95
+ * ```
96
+ */
97
+ /**
98
+ * Validate URL format
99
+ */
100
+ function isValidUrl(url) {
101
+ try {
102
+ new URL(url);
103
+ return true;
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ }
109
+ /**
110
+ * Normalize array (single value or array to array)
111
+ */
112
+ function normalizeToArray(value) {
113
+ if (value === undefined)
114
+ return [];
115
+ return Array.isArray(value) ? value : [value];
116
+ }
117
+ /**
118
+ * Join non-empty array values
119
+ */
120
+ function joinNonEmpty(values, separator) {
121
+ if (!values || values.length === 0)
122
+ return undefined;
123
+ const nonEmpty = values.filter(v => v && v.trim().length > 0);
124
+ return nonEmpty.length > 0 ? nonEmpty.join(separator) : undefined;
125
+ }
126
+ /**
127
+ * Convert meta tags array to object
128
+ *
129
+ * 메타 태그 배열을 객체로 변환 (Next.js metadata API에서 사용)
130
+ *
131
+ * @param meta - Array of meta tags
132
+ * @returns Object with meta tag names as keys
133
+ *
134
+ * @example
135
+ * ```tsx
136
+ * const metaObj = metaToObject(geoMeta.meta);
137
+ * return {
138
+ * title: 'My App',
139
+ * description: metaObj.description,
140
+ * keywords: metaObj.keywords,
141
+ * };
142
+ * ```
143
+ */
144
+ export function metaToObject(meta) {
145
+ return Object.fromEntries(meta.map(m => [m.name, m.content]));
146
+ }
147
+ /**
148
+ * Convert Open Graph tags array to object
149
+ *
150
+ * Open Graph 태그 배열을 객체로 변환
151
+ *
152
+ * @param og - Array of Open Graph tags
153
+ * @returns Object with Open Graph properties as keys
154
+ *
155
+ * @example
156
+ * ```tsx
157
+ * const ogObj = openGraphToObject(geoMeta.openGraph);
158
+ * return {
159
+ * openGraph: {
160
+ * title: ogObj['og:title'],
161
+ * description: ogObj['og:description'],
162
+ * },
163
+ * };
164
+ * ```
165
+ */
166
+ export function openGraphToObject(og) {
167
+ if (!og)
168
+ return {};
169
+ return Object.fromEntries(og.map(o => [o.property, o.content]));
170
+ }
171
+ export function generateGEOMetadata(config) {
172
+ // Input validation
173
+ if (!config.name || config.name.trim().length === 0) {
174
+ throw new Error('GEOConfig.name is required and cannot be empty');
175
+ }
176
+ if (!config.description || config.description.trim().length === 0) {
177
+ throw new Error('GEOConfig.description is required and cannot be empty');
178
+ }
179
+ if (config.description.length > 160) {
180
+ console.warn(`[GEO] Description is ${config.description.length} characters. ` +
181
+ `Consider keeping it under 160 for better AI parsing.`);
182
+ }
183
+ if (config.url && !isValidUrl(config.url)) {
184
+ throw new Error(`GEOConfig.url must be a valid URL: ${config.url}`);
185
+ }
186
+ if (config.codeRepository && !isValidUrl(config.codeRepository)) {
187
+ throw new Error(`GEOConfig.codeRepository must be a valid URL: ${config.codeRepository}`);
188
+ }
189
+ if (config.documentationUrl && !isValidUrl(config.documentationUrl)) {
190
+ throw new Error(`GEOConfig.documentationUrl must be a valid URL: ${config.documentationUrl}`);
191
+ }
192
+ // Generate meta tags
193
+ const meta = [
194
+ {
195
+ name: META_NAMES.DESCRIPTION,
196
+ content: config.description,
197
+ },
198
+ ];
199
+ // Add keywords meta tag (filter empty values)
200
+ const keywordsContent = joinNonEmpty(config.keywords, ', ');
201
+ if (keywordsContent) {
202
+ meta.push({
203
+ name: META_NAMES.KEYWORDS,
204
+ content: keywordsContent,
205
+ });
206
+ }
207
+ // Add software-specific meta tags
208
+ if (config.version) {
209
+ meta.push({
210
+ name: META_NAMES.SOFTWARE_VERSION,
211
+ content: config.version,
212
+ });
213
+ }
214
+ const categories = normalizeToArray(config.applicationCategory);
215
+ const categoryContent = joinNonEmpty(categories, ', ');
216
+ if (categoryContent) {
217
+ meta.push({
218
+ name: META_NAMES.SOFTWARE_CATEGORY,
219
+ content: categoryContent,
220
+ });
221
+ }
222
+ const languages = normalizeToArray(config.programmingLanguage);
223
+ const languageContent = joinNonEmpty(languages, ', ');
224
+ if (languageContent) {
225
+ meta.push({
226
+ name: META_NAMES.SOFTWARE_LANGUAGE,
227
+ content: languageContent,
228
+ });
229
+ }
230
+ // Generate JSON-LD structured data
231
+ const jsonLd = [generateSoftwareApplicationLD(config)];
232
+ // Generate Open Graph tags
233
+ const openGraph = [
234
+ {
235
+ property: OG_PROPERTIES.TITLE,
236
+ content: config.name,
237
+ },
238
+ {
239
+ property: OG_PROPERTIES.DESCRIPTION,
240
+ content: config.description,
241
+ },
242
+ {
243
+ property: OG_PROPERTIES.TYPE,
244
+ content: 'website',
245
+ },
246
+ ];
247
+ if (config.url) {
248
+ openGraph.push({
249
+ property: OG_PROPERTIES.URL,
250
+ content: config.url,
251
+ });
252
+ }
253
+ // Add Open Graph article tags for software
254
+ if (config.author) {
255
+ openGraph.push({
256
+ property: OG_PROPERTIES.SITE_NAME,
257
+ content: config.author.name,
258
+ });
259
+ }
260
+ // Generate Twitter Card tags
261
+ const twitter = [
262
+ {
263
+ name: 'twitter:card',
264
+ content: 'summary_large_image',
265
+ },
266
+ {
267
+ name: 'twitter:title',
268
+ content: config.name,
269
+ },
270
+ {
271
+ name: 'twitter:description',
272
+ content: config.description,
273
+ },
274
+ ];
275
+ return {
276
+ meta,
277
+ jsonLd,
278
+ openGraph,
279
+ twitter,
280
+ version: '1.0.0', // Schema version for future migrations and debugging
281
+ };
282
+ }
283
+ /**
284
+ * JSON-LD stringification cache
285
+ * 동일한 객체를 여러 번 렌더링할 때 성능 최적화를 위한 캐시
286
+ */
287
+ const stringifiedCache = new WeakMap();
288
+ /**
289
+ * Render JSON-LD for Next.js Script component
290
+ *
291
+ * XSS 보안을 위해 위험한 문자를 이스케이프합니다.
292
+ * Escapes dangerous characters for XSS security.
293
+ *
294
+ * Next.js에서 사용할 수 있는 JSON-LD script 태그 생성
295
+ *
296
+ * @param jsonLd - JSON-LD structured data
297
+ * @param id - Optional script ID (default: auto-generated)
298
+ * @returns Props for Next.js Script component with:
299
+ * - `id`: Unique script ID
300
+ * - `type`: 'application/ld+json'
301
+ * - `dangerouslySetInnerHTML.__html`: Escaped JSON string
302
+ *
303
+ * @example
304
+ * ```tsx
305
+ * import Script from 'next/script';
306
+ * import { renderJSONLD } from '@hua-labs/hua-ux/framework';
307
+ *
308
+ * const geoMeta = generateGEOMetadata({ ... });
309
+ *
310
+ * export default function Page() {
311
+ * return (
312
+ * <>
313
+ * <Script {...renderJSONLD(geoMeta.jsonLd[0])} />
314
+ * <main>...</main>
315
+ * </>
316
+ * );
317
+ * }
318
+ * ```
319
+ */
320
+ export function renderJSONLD(jsonLd, id) {
321
+ let jsonString;
322
+ // 캐시된 문자열이 있으면 재사용 (성능 최적화)
323
+ // Reuse cached string if available (performance optimization)
324
+ if (typeof jsonLd === 'object' && jsonLd !== null) {
325
+ if (stringifiedCache.has(jsonLd)) {
326
+ jsonString = stringifiedCache.get(jsonLd);
327
+ }
328
+ else {
329
+ // XSS 보안: </script> 태그가 JSON 문자열에 포함되어도 안전하게 이스케이프
330
+ // XSS Security: Escape dangerous characters even if </script> appears in JSON string
331
+ jsonString = JSON.stringify(jsonLd)
332
+ .replace(/</g, '\\u003c')
333
+ .replace(/>/g, '\\u003e')
334
+ .replace(/&/g, '\\u0026');
335
+ stringifiedCache.set(jsonLd, jsonString);
336
+ }
337
+ }
338
+ else {
339
+ // 원시 타입은 캐싱하지 않음
340
+ jsonString = String(jsonLd);
341
+ }
342
+ return {
343
+ id: id || `jsonld-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
344
+ type: 'application/ld+json',
345
+ dangerouslySetInnerHTML: {
346
+ __html: jsonString,
347
+ },
348
+ };
349
+ }
350
+ /**
351
+ * Create AI-friendly context description
352
+ *
353
+ * AI가 맥락을 이해하기 쉽도록 풍부한 설명 생성
354
+ *
355
+ * @param config - GEO configuration
356
+ * @returns AI-friendly context string
357
+ *
358
+ * @example
359
+ * ```tsx
360
+ * const context = createAIContext({
361
+ * name: 'hua-ux',
362
+ * description: 'Privacy-first UX framework',
363
+ * features: ['i18n', 'Motion', 'Accessibility'],
364
+ * useCases: ['Multilingual apps', 'Accessible UX'],
365
+ * });
366
+ * // Returns: "hua-ux is a Privacy-first UX framework. Key features include: i18n, Motion, Accessibility. Common use cases: Multilingual apps, Accessible UX."
367
+ * ```
368
+ */
369
+ export function createAIContext(config) {
370
+ const parts = [];
371
+ // Basic description
372
+ parts.push(`${config.name} is a ${config.description}`);
373
+ // Features (filter empty values)
374
+ const featureContent = joinNonEmpty(config.features, ', ');
375
+ if (featureContent) {
376
+ parts.push(`Key features include: ${featureContent}`);
377
+ }
378
+ // Use cases (filter empty values)
379
+ const useCaseContent = joinNonEmpty(config.useCases, ', ');
380
+ if (useCaseContent) {
381
+ parts.push(`Common use cases: ${useCaseContent}`);
382
+ }
383
+ // Programming language
384
+ const languages = normalizeToArray(config.programmingLanguage);
385
+ const languageContent = joinNonEmpty(languages, ', ');
386
+ if (languageContent) {
387
+ parts.push(`Built with: ${languageContent}`);
388
+ }
389
+ // Technology stack
390
+ const techStack = normalizeToArray(config.technologyStack);
391
+ const techStackContent = joinNonEmpty(techStack, ', ');
392
+ if (techStackContent) {
393
+ parts.push(`Technology stack: ${techStackContent}`);
394
+ }
395
+ // Requirements (filter empty values)
396
+ const requirementsContent = joinNonEmpty(config.softwareRequirements, ', ');
397
+ if (requirementsContent) {
398
+ parts.push(`Requires: ${requirementsContent}`);
399
+ }
400
+ const result = parts.join('. ');
401
+ // 중복 마침표 방지 (이미 마침표로 끝나면 추가하지 않음)
402
+ // Prevent double periods (don't add if already ends with period)
403
+ return result.endsWith('.') ? result : result + '.';
404
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - GEO (Generative Engine Optimization)
3
+ *
4
+ * Make your application discoverable and recommendable by AI search engines
5
+ * (ChatGPT, Claude, Gemini, Perplexity)
6
+ *
7
+ * ChatGPT, Claude, Gemini, Perplexity 같은 AI 검색 엔진이
8
+ * 당신의 애플리케이션을 잘 찾고 추천하도록 최적화
9
+ */
10
+ export { generateGEOMetadata, renderJSONLD, createAIContext, metaToObject, openGraphToObject, } from './generateGEOMetadata';
11
+ export { generateSoftwareApplicationLD, generateFAQPageLD, generateTechArticleLD, generateHowToLD, generateCodeLD, generateVideoLD, generateOrganizationLD, } from './structuredData';
12
+ export { GEO_PRESETS } from './presets';
13
+ export type { GEOPreset } from './presets';
14
+ export { validateGEOMetadata, prettyPrintGEOMetadata, compareGEOMetadata, } from './test-utils';
15
+ export type { GEOValidationResult } from './test-utils';
16
+ export { META_NAMES, OG_PROPERTIES } from './types';
17
+ export type { GEOConfig, GEOMetadata, StructuredData, SoftwareApplicationType, SoftwareCategory, ProgrammingLanguage, TechnologyStack, RequiredGEOConfig, OptionalGEOConfig, GEOConfigInput, } from './types';
18
+ export { isValidGEOConfig } from './types';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/framework/seo/geo/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,6BAA6B,EAC7B,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,eAAe,EACf,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGpD,YAAY,EACV,SAAS,EACT,WAAW,EACX,cAAc,EACd,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - GEO (Generative Engine Optimization)
3
+ *
4
+ * Make your application discoverable and recommendable by AI search engines
5
+ * (ChatGPT, Claude, Gemini, Perplexity)
6
+ *
7
+ * ChatGPT, Claude, Gemini, Perplexity 같은 AI 검색 엔진이
8
+ * 당신의 애플리케이션을 잘 찾고 추천하도록 최적화
9
+ */
10
+ // Main GEO function
11
+ export { generateGEOMetadata, renderJSONLD, createAIContext, metaToObject, openGraphToObject, } from './generateGEOMetadata';
12
+ // Structured data helpers
13
+ export { generateSoftwareApplicationLD, generateFAQPageLD, generateTechArticleLD, generateHowToLD, generateCodeLD, generateVideoLD, generateOrganizationLD, } from './structuredData';
14
+ // Presets
15
+ export { GEO_PRESETS } from './presets';
16
+ // Test utilities
17
+ export { validateGEOMetadata, prettyPrintGEOMetadata, compareGEOMetadata, } from './test-utils';
18
+ // Constants
19
+ export { META_NAMES, OG_PROPERTIES } from './types';
20
+ // Type guards
21
+ export { isValidGEOConfig } from './types';
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - GEO Presets
3
+ *
4
+ * Pre-configured GEO presets for common use cases
5
+ * 일반적인 사용 사례를 위한 사전 구성된 GEO 프리셋
6
+ */
7
+ /**
8
+ * GEO Presets
9
+ * 일반적인 소프트웨어 타입을 위한 사전 구성된 설정
10
+ */
11
+ export declare const GEO_PRESETS: {
12
+ /**
13
+ * Next.js Framework preset
14
+ * Next.js 프레임워크용 프리셋
15
+ */
16
+ readonly NEXTJS_FRAMEWORK: {
17
+ readonly applicationType: "DeveloperApplication";
18
+ readonly programmingLanguage: ["TypeScript"];
19
+ readonly technologyStack: ["Next.js", "React"];
20
+ readonly applicationCategory: "Developer Tool";
21
+ };
22
+ /**
23
+ * UI Library preset
24
+ * UI 라이브러리용 프리셋
25
+ */
26
+ readonly UI_LIBRARY: {
27
+ readonly applicationType: "DeveloperApplication";
28
+ readonly applicationCategory: "Component Library";
29
+ };
30
+ /**
31
+ * React Application preset
32
+ * React 애플리케이션용 프리셋
33
+ */
34
+ readonly REACT_APP: {
35
+ readonly applicationType: "WebApplication";
36
+ readonly programmingLanguage: ["TypeScript", "JavaScript"];
37
+ readonly technologyStack: ["React"];
38
+ };
39
+ /**
40
+ * NPM Package preset
41
+ * NPM 패키지용 프리셋
42
+ */
43
+ readonly NPM_PACKAGE: {
44
+ readonly applicationType: "DeveloperApplication";
45
+ readonly applicationCategory: "Developer Tool";
46
+ };
47
+ };
48
+ /**
49
+ * Type helper for preset values
50
+ */
51
+ export type GEOPreset = typeof GEO_PRESETS[keyof typeof GEO_PRESETS];
52
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../../../src/framework/seo/geo/presets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;GAGG;AACH,eAAO,MAAM,WAAW;IACtB;;;OAGG;;;;;;;IAQH;;;OAGG;;;;;IAMH;;;OAGG;;;;;;IAOH;;;OAGG;;;;;CAKkD,CAAC;AAExD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,WAAW,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - GEO Presets
3
+ *
4
+ * Pre-configured GEO presets for common use cases
5
+ * 일반적인 사용 사례를 위한 사전 구성된 GEO 프리셋
6
+ */
7
+ /**
8
+ * GEO Presets
9
+ * 일반적인 소프트웨어 타입을 위한 사전 구성된 설정
10
+ */
11
+ export const GEO_PRESETS = {
12
+ /**
13
+ * Next.js Framework preset
14
+ * Next.js 프레임워크용 프리셋
15
+ */
16
+ NEXTJS_FRAMEWORK: {
17
+ applicationType: 'DeveloperApplication',
18
+ programmingLanguage: ['TypeScript'],
19
+ technologyStack: ['Next.js', 'React'],
20
+ applicationCategory: 'Developer Tool',
21
+ },
22
+ /**
23
+ * UI Library preset
24
+ * UI 라이브러리용 프리셋
25
+ */
26
+ UI_LIBRARY: {
27
+ applicationType: 'DeveloperApplication',
28
+ applicationCategory: 'Component Library',
29
+ },
30
+ /**
31
+ * React Application preset
32
+ * React 애플리케이션용 프리셋
33
+ */
34
+ REACT_APP: {
35
+ applicationType: 'WebApplication',
36
+ programmingLanguage: ['TypeScript', 'JavaScript'],
37
+ technologyStack: ['React'],
38
+ },
39
+ /**
40
+ * NPM Package preset
41
+ * NPM 패키지용 프리셋
42
+ */
43
+ NPM_PACKAGE: {
44
+ applicationType: 'DeveloperApplication',
45
+ applicationCategory: 'Developer Tool',
46
+ },
47
+ };