@korioinc/next-core 2.0.44 → 2.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,99 +1,298 @@
1
1
  # @korioinc/next-core
2
2
 
3
- Core utilities and components for Next.js applications.
3
+ Core runtime utilities, providers, and components for Korio Next.js apps.
4
+
5
+ The package is organized around subpath exports. Most modules depend on
6
+ `@korioinc/next-conf` for app-level configuration, so apps should set up
7
+ `next-kit.config.ts` and wrap `next.config.ts` with `withNextKitConf`.
4
8
 
5
9
  ## Installation
6
10
 
7
11
  ```bash
8
- pnpm add @korioinc/next-core
12
+ pnpm add @korioinc/next-core @korioinc/next-conf
9
13
  ```
10
14
 
11
- ## Features
15
+ ## Requirements
16
+
17
+ - Next.js `>=16.2.6`
18
+ - React and React DOM `>=19.2.4`
19
+ - Tailwind CSS `>=4.0.0`
20
+ - `@lingui/core` and `@lingui/react` `>=6.0.1`
21
+ - `@headlessui/react` `>=2.2.0`
22
+
23
+ ## Required app setup
24
+
25
+ Create `next-kit.config.ts`:
12
26
 
13
- - **Authentication**: Complete auth system with JWT support
14
- - **Internationalization**: i18n routing and locale management
15
- - **Components**: Reusable UI components (Theme switcher, Locale switcher)
16
- - **Ads Management**: Ad container components and configurations
17
- - **API Client**: Configured fetch client with interceptors
18
- - **Navigation**: Type-safe navigation system
19
- - **Local Storage**: Enhanced local storage management
20
- - **Utilities**: Common utility functions
27
+ ```typescript
28
+ import type { NextCoreConfig } from '@korioinc/next-conf';
29
+
30
+ const config: NextCoreConfig = {
31
+ api: {
32
+ baseUrl: process.env.API_BASE_URL,
33
+ timeout: 5000,
34
+ headers: {},
35
+ },
36
+ cookie: {
37
+ accessTokenName: '_at',
38
+ domain: '.localhost',
39
+ maxAge: 7 * 24 * 60 * 60,
40
+ },
41
+ auth: {
42
+ restrictedPaths: ['/accounts/login', '/accounts/register'],
43
+ requiredPaths: ['/accounts/profile', '/accounts/settings'],
44
+ loginUrl: '/accounts/login',
45
+ homeUrl: '/',
46
+ },
47
+ analytics: {
48
+ gaId: process.env.NEXT_PUBLIC_GA_ID,
49
+ gtmId: process.env.NEXT_PUBLIC_GTM_ID,
50
+ adsensePublisherId: process.env.NEXT_PUBLIC_ADSENSE_PUBLISHER_ID,
51
+ },
52
+ };
21
53
 
22
- ## Usage
54
+ export default config;
55
+ ```
23
56
 
24
- ### Authentication
57
+ Wrap `next.config.ts`:
25
58
 
26
59
  ```typescript
60
+ import type { NextConfig } from 'next';
61
+
62
+ import { withNextKitConf } from '@korioinc/next-conf/plugin';
63
+
64
+ const nextConfig: NextConfig = {
65
+ turbopack: {
66
+ rules: {
67
+ '*.po': {
68
+ loaders: ['@lingui/loader'],
69
+ as: '*.js',
70
+ },
71
+ },
72
+ },
73
+ };
74
+
75
+ export default withNextKitConf(nextConfig);
76
+ ```
77
+
78
+ `withNextKitConf` exposes the app config, `lingui.config`, and the generated
79
+ Lingui loader to this package.
80
+
81
+ ## Public entrypoints
82
+
83
+ | Entrypoint | Main exports |
84
+ | ----------------------------------- | ------------------------------------------------------------------------------------------ |
85
+ | `@korioinc/next-core/ads` | `GoogleAdSense`, `AdContainer`, `MockAdDisplay`, ad types and config |
86
+ | `@korioinc/next-core/auth` | Server auth helpers, routing helper, client provider, permission utilities, auth types |
87
+ | `@korioinc/next-core/auth/server` | `getAuthData`, `getCurrentUser`, `isAuthenticated`, permission checks, `handleAuthRouting` |
88
+ | `@korioinc/next-core/auth/client` | `AuthContextProvider`, `useAuthContext`, client token and permission helpers |
89
+ | `@korioinc/next-core/navigation` | Navigation types, permission filtering, `NavigationBuilder` |
90
+ | `@korioinc/next-core/api-client` | `requestApi`, `ApiResponse` |
91
+ | `@korioinc/next-core/i18n/*` | Routing, Lingui setup, Lingui initialization, client provider |
92
+ | `@korioinc/next-core/utils` | `cn`, `isBrowser`, `getBaseUrl`, sitemap helpers |
93
+ | `@korioinc/next-core/local-storage` | `LocalStorageManager`, `createStorageInstance`, `storage`, Valtio persistence helpers |
94
+ | `@korioinc/next-core/components` | `LocaleSwitcher`, `ThemeSwitcher`, `Tooltip`, `HeadManifest`, `JsonLd` |
95
+ | `@korioinc/next-core/theme` | Client `ThemeProvider`, `useTheme`, theme types |
96
+ | `@korioinc/next-core/theme/server` | Server `ThemeProvider`, theme types |
97
+ | `@korioinc/next-core/styles/*` | Built CSS assets, including `styles/globals.css` |
98
+ | `@korioinc/next-core/types/*` | Published ambient type files |
99
+
100
+ ## Authentication
101
+
102
+ Server helpers read the access token cookie configured in `next-kit.config.ts`
103
+ and cache auth data per React request.
104
+
105
+ ```tsx
106
+ import type { ReactNode } from 'react';
107
+
108
+ import { AuthContextProvider } from '@korioinc/next-core/auth/client';
109
+ import { getAuthData } from '@korioinc/next-core/auth/server';
110
+
111
+ export default async function Layout({ children }: { children: ReactNode }) {
112
+ const authData = await getAuthData();
113
+
114
+ return <AuthContextProvider initialAuthData={authData}>{children}</AuthContextProvider>;
115
+ }
116
+ ```
117
+
118
+ Client components can read auth state and permission helpers from the provider:
119
+
120
+ ```tsx
121
+ 'use client';
122
+
27
123
  import { useAuthContext } from '@korioinc/next-core/auth/client';
28
- import { getCurrentUser } from '@korioinc/next-core/auth/server';
29
124
 
30
- // Client-side
31
- const { isLogin, user, login, logout } = useAuthContext();
125
+ export function AccountButton() {
126
+ const { isLogin, user, hasPermission, logout } = useAuthContext();
127
+
128
+ if (!isLogin) return null;
32
129
 
33
- // Server-side
34
- const user = await getCurrentUser();
130
+ return (
131
+ <button disabled={!hasPermission('update')} onClick={logout}>
132
+ {user?.name}
133
+ </button>
134
+ );
135
+ }
35
136
  ```
36
137
 
37
- ### Internationalization
138
+ ## Routing and i18n
139
+
140
+ Use auth and locale routing together from the Next.js request entrypoint:
38
141
 
39
142
  ```typescript
143
+ import type { NextFetchEvent, NextRequest } from 'next/server';
144
+ import { NextResponse } from 'next/server';
145
+
146
+ import { handleAuthRouting } from '@korioinc/next-core/auth/server';
40
147
  import { handleLocaleRouting } from '@korioinc/next-core/i18n/routing';
41
148
 
42
- // In middleware.ts
43
- export default function middleware(request: NextRequest) {
149
+ export default async function proxy(request: NextRequest, _event: NextFetchEvent): Promise<NextResponse> {
150
+ const authResponse = await handleAuthRouting(request);
151
+ if (authResponse) return authResponse;
152
+
44
153
  return handleLocaleRouting(request);
45
154
  }
46
155
  ```
47
156
 
48
- ### Components
157
+ Server components can initialize Lingui and pass loaded messages to the client:
49
158
 
50
- ```typescript
51
- import { ThemeSwitcher, LocaleSwitcher } from '@korioinc/next-core/components';
52
- import { ThemeProvider } from '@korioinc/next-core/theme/server';
159
+ ```tsx
160
+ import type { PropsWithChildren } from 'react';
161
+
162
+ import { PageLocaleParam, initLingui } from '@korioinc/next-core/i18n/init-lingui';
163
+ import { allMessages } from '@korioinc/next-core/i18n/lingui.setup';
164
+ import { LinguiClientProvider } from '@korioinc/next-core/i18n/providers/lingui-client-provider';
165
+
166
+ export default async function LocaleLayout({ children, params }: PropsWithChildren<PageLocaleParam>) {
167
+ const locale = (await params).locale;
168
+ initLingui(locale);
53
169
 
54
- export function Layout({ children }: { children: React.ReactNode }) {
55
170
  return (
56
- <ThemeProvider attribute="class" enableSystem defaultTheme="system">
171
+ <LinguiClientProvider initialLocale={locale} initialMessages={allMessages[locale] ?? {}}>
57
172
  {children}
58
- </ThemeProvider>
173
+ </LinguiClientProvider>
59
174
  );
60
175
  }
176
+ ```
61
177
 
62
- export function Header() {
178
+ Useful routing exports include `getLocales`, `getDefaultLocale`,
179
+ `getPathname`, `getCurrentPathname`, `getCurrentLanguageAlternates`,
180
+ `getFullUrl`, and `getFullUrlByPathname`. Client components can use
181
+ `useToLocalePath` from `@korioinc/next-core/i18n/routing.client`.
182
+
183
+ ## Theme and components
184
+
185
+ Use the server theme provider in layouts when theme state may be read from
186
+ cookies:
187
+
188
+ ```tsx
189
+ import type { ReactNode } from 'react';
190
+
191
+ import { LocaleSwitcher, ThemeSwitcher } from '@korioinc/next-core/components';
192
+ import { ThemeProvider } from '@korioinc/next-core/theme/server';
193
+
194
+ export default function Layout({ children }: { children: ReactNode }) {
63
195
  return (
64
- <header>
65
- <ThemeSwitcher />
66
- <LocaleSwitcher />
67
- </header>
196
+ <ThemeProvider attribute='class' enableSystem defaultTheme='system'>
197
+ <header>
198
+ <ThemeSwitcher />
199
+ <LocaleSwitcher />
200
+ </header>
201
+ {children}
202
+ </ThemeProvider>
68
203
  );
69
204
  }
70
205
  ```
71
206
 
72
- ### Ads
207
+ Import the shared styles from app CSS:
208
+
209
+ ```css
210
+ @import '@korioinc/next-core/styles/globals.css';
211
+ ```
212
+
213
+ ## API client
214
+
215
+ `requestApi` is a server-side fetch helper. It uses `api.baseUrl` and the access
216
+ token cookie from `next-kit.config.ts`, forwards the token as `Authorization`,
217
+ updates the cookie from an `x-refreshed-token` response header, and clears the
218
+ cookie on `401`.
73
219
 
74
220
  ```typescript
75
- import { AdContainer } from '@korioinc/next-core/ads';
221
+ import { requestApi } from '@korioinc/next-core/api-client';
222
+
223
+ type SearchResult = {
224
+ id: string;
225
+ title: string;
226
+ };
227
+
228
+ const results = await requestApi<SearchResult[]>('/search', {
229
+ params: { q: 'korio' },
230
+ next: { revalidate: 60 },
231
+ });
232
+ ```
233
+
234
+ ## Ads
235
+
236
+ ```tsx
237
+ import type { ReactNode } from 'react';
238
+
239
+ import { getConfig } from '@korioinc/next-conf';
240
+ import { AdContainer, GoogleAdSense } from '@korioinc/next-core/ads';
241
+
242
+ export function RootProviders({ children }: { children: ReactNode }) {
243
+ const publisherId = getConfig().analytics?.adsensePublisherId;
76
244
 
77
- export function Page() {
78
245
  return (
79
- <AdContainer
80
- adType="leaderboard"
81
- className="my-4"
82
- />
246
+ <>
247
+ {publisherId && <GoogleAdSense publisherId={publisherId} />}
248
+ {children}
249
+ </>
83
250
  );
84
251
  }
252
+
253
+ export function PageAd() {
254
+ return <AdContainer adType='leaderboard' className='hidden lg:block' />;
255
+ }
85
256
  ```
86
257
 
87
- ## Requirements
258
+ `GoogleAdSense` requires a `publisherId` prop. `AdContainer` reads
259
+ `analytics.adsensePublisherId` from `next-kit.config.ts` when loading production
260
+ ads and renders mock ads for local or development environments. See
261
+ `src/ads/README.md` in this package for available ad types and placement patterns.
88
262
 
89
- - Next.js 15+
90
- - React 19+
91
- - TypeScript 5.9+
263
+ ## Local storage
92
264
 
93
- ## License
265
+ ```typescript
266
+ import { LocalStorageManager, createPersistedStore } from '@korioinc/next-core/local-storage';
94
267
 
95
- MIT
268
+ type Preferences = {
269
+ compact: boolean;
270
+ };
271
+
272
+ const preferences = new LocalStorageManager<Preferences>('preferences');
273
+ await preferences.setItem({ compact: true });
96
274
 
97
- ## Contributing
275
+ const store = createPersistedStore('todo-store', {
276
+ todos: [] as string[],
277
+ });
278
+
279
+ await store.hydrate();
280
+ ```
98
281
 
99
- Contributions are welcome! Please feel free to submit a Pull Request.
282
+ `LocalStorageManager` uses localforage and reads `storage.name`,
283
+ `storage.storeName`, and `storage.driver` from `next-kit.config.ts`.
284
+
285
+ ## Utilities
286
+
287
+ ```typescript
288
+ import { cn, getBaseUrl, getEntry, getUrl, isBrowser } from '@korioinc/next-core/utils';
289
+ ```
290
+
291
+ - `cn` combines `clsx` and `tailwind-merge`
292
+ - `getBaseUrl` uses the app URL environment fallback logic
293
+ - `getEntry` and `getUrl` help build sitemap entries
294
+ - `isBrowser` guards browser-only code paths
295
+
296
+ ## License
297
+
298
+ MIT
@@ -8,6 +8,7 @@ export declare function getLocales(): string[];
8
8
  * Get default locale
9
9
  */
10
10
  export declare function getDefaultLocale(): string;
11
+ export declare function getHreflang(locale: string): string;
11
12
  export declare function getPathLocale(pathname: string): string | undefined;
12
13
  export declare function handleLocaleRouting(request: NextRequest): NextResponse<unknown>;
13
14
  export declare function getRequestLocale(requestHeaders: Headers): string;
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/i18n/routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoC7D;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,EAAE,CAErC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AA4BD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElE;AAiFD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,yBA2EvD;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,OAAO,GAAG,MAAM,CAShE;AAED,wBAAgB,eAAe,CAAC,cAAc,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW9F;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAevG;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,UAY7E;AAsBD,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAY,0BAwB3D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK1D;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAKxE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAuBpF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE3E;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKrE;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CAMvE;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU1D;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAIpF;AAED,KAAK,aAAa,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7C,eAAe,GACf,uBAAuB,GACvB,SAAS,CAAC;AAiDd,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAErF;AAED,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAIxE;AAED,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,MAAM,CAAC,CAKnE"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/i18n/routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoC7D;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,EAAE,CAErC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIlD;AA4BD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElE;AAiFD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,yBA2EvD;AAED,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,OAAO,GAAG,MAAM,CAShE;AAED,wBAAgB,eAAe,CAAC,cAAc,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW9F;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAevG;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,UAY7E;AAsBD,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAY,0BA0B3D;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAK1D;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAKxE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAuBpF;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE3E;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKrE;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC,CAMvE;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU1D;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAIpF;AAED,KAAK,aAAa,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7C,eAAe,GACf,uBAAuB,GACvB,SAAS,CAAC;AAiDd,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,MAAM,CAErF;AAED,wBAAsB,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAIxE;AAED,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,MAAM,CAAC,CAKnE"}
@@ -36,6 +36,10 @@ export function getLocales() {
36
36
  export function getDefaultLocale() {
37
37
  return DEFAULT_FALLBACK();
38
38
  }
39
+ export function getHreflang(locale) {
40
+ const [language, region] = locale.split('-');
41
+ return region ? `${language.toLowerCase()}-${region.toUpperCase()}` : language.toLowerCase();
42
+ }
39
43
  /**
40
44
  * 쿠키를 설정하는 헬퍼 함수
41
45
  */
@@ -245,11 +249,12 @@ export function getLanguageAlternates(pathname = '/') {
245
249
  locales
246
250
  .filter((locale) => locale !== 'pseudo')
247
251
  .forEach((locale) => {
252
+ const hreflang = getHreflang(locale);
248
253
  if (LOCALE_PREFIX === 'as-needed' && locale === defaultFallback) {
249
- alternates[locale] = basePath;
254
+ alternates[hreflang] = basePath;
250
255
  }
251
256
  else {
252
- alternates[locale] = `/${locale}${basePath === '/' ? '' : basePath}`;
257
+ alternates[hreflang] = `/${locale}${basePath === '/' ? '' : basePath}`;
253
258
  }
254
259
  });
255
260
  return alternates;
@@ -1,3 +1,3 @@
1
1
  export { cn, isBrowser, getBaseUrl } from './helpers';
2
- export { getEntry, getUrl } from './sitemap';
2
+ export { getEntry, getLocalizedEntries, getUrl } from './sitemap';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC"}
@@ -1,2 +1,2 @@
1
1
  export { cn, isBrowser, getBaseUrl } from './helpers';
2
- export { getEntry, getUrl } from './sitemap';
2
+ export { getEntry, getLocalizedEntries, getUrl } from './sitemap';
@@ -1,3 +1,4 @@
1
+ import type { MetadataRoute } from 'next';
1
2
  import { getPathname } from '../i18n/routing';
2
3
  type Href = Parameters<typeof getPathname>[0]['href'];
3
4
  export declare function getEntry(href: Href, options?: Record<string, string>): {
@@ -8,6 +9,7 @@ export declare function getEntry(href: Href, options?: Record<string, string>):
8
9
  };
9
10
  };
10
11
  };
12
+ export declare function getLocalizedEntries(routes: readonly Href[]): MetadataRoute.Sitemap;
11
13
  export declare function getUrl(href: Href, locale: string): string;
12
14
  export {};
13
15
  //# sourceMappingURL=sitemap.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../src/utils/sitemap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAQjD,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;;;;;;;EAUxE;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,UAKhD"}
1
+ {"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../src/utils/sitemap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,EAAe,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAgB9D,KAAK,IAAI,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;;;;;;;EAQxE;AAMD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,GAAG,aAAa,CAAC,OAAO,CAWlF;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,UAKhD"}
@@ -1,17 +1,40 @@
1
- import { getPathname } from '../i18n/routing';
1
+ import { getHreflang, getPathname } from '../i18n/routing';
2
2
  import { getBaseUrl } from '../utils/helpers';
3
3
  import linguiConfig from 'lingui.config';
4
4
  const { locales, fallbackLocales } = linguiConfig;
5
5
  const defaultLocale = fallbackLocales.default;
6
+ const indexableLocales = [...locales]
7
+ .filter((locale) => locale !== 'pseudo')
8
+ .sort((a, b) => {
9
+ if (a === defaultLocale)
10
+ return -1;
11
+ if (b === defaultLocale)
12
+ return 1;
13
+ return a.localeCompare(b);
14
+ });
6
15
  export function getEntry(href, options = {}) {
7
16
  return {
8
17
  url: getUrl(href, defaultLocale),
9
18
  alternates: {
10
- languages: Object.fromEntries(locales.filter((locale) => locale !== 'pseudo').map((cur) => [cur, getUrl(href, cur)])),
19
+ languages: Object.fromEntries(indexableLocales.map((cur) => [getHreflang(cur), getUrl(href, cur)])),
11
20
  },
12
21
  ...options,
13
22
  };
14
23
  }
24
+ function getAlternateLanguages(href) {
25
+ return Object.fromEntries(indexableLocales.map((locale) => [getHreflang(locale), getUrl(href, locale)]));
26
+ }
27
+ export function getLocalizedEntries(routes) {
28
+ return routes.flatMap((href) => {
29
+ const languages = getAlternateLanguages(href);
30
+ return indexableLocales.map((locale) => ({
31
+ url: getUrl(href, locale),
32
+ alternates: {
33
+ languages,
34
+ },
35
+ }));
36
+ });
37
+ }
15
38
  export function getUrl(href, locale) {
16
39
  const pathname = getPathname({ locale, href });
17
40
  const host = getBaseUrl();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@korioinc/next-core",
3
- "version": "2.0.44",
3
+ "version": "2.0.45",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./ads": {
@@ -78,10 +78,10 @@
78
78
  "@lingui/react": "^6.0.1",
79
79
  "@tailwindcss/typography": "^0.5.19",
80
80
  "@types/negotiator": "^0.6.4",
81
- "@types/node": "^25.6.2",
81
+ "@types/node": "^25.8.0",
82
82
  "@types/react": "^19.2.14",
83
83
  "@types/react-dom": "^19.2.3",
84
- "eslint": "^10.3.0",
84
+ "eslint": "^10.4.0",
85
85
  "jsdom": "^29.1.1",
86
86
  "react": "^19.2.6",
87
87
  "react-dom": "^19.2.6",
@@ -89,12 +89,12 @@
89
89
  "tsc-alias": "^1.8.17",
90
90
  "tw-animate-css": "^1.4.0",
91
91
  "typescript": "^6.0.3",
92
- "vitest": "^4.1.5",
93
- "@korioinc/next-configs": "2.0.44"
92
+ "vitest": "^4.1.6",
93
+ "@korioinc/next-configs": "2.0.45"
94
94
  },
95
95
  "dependencies": {
96
96
  "@floating-ui/react": "^0.27.19",
97
- "@formatjs/intl-localematcher": "^0.8.6",
97
+ "@formatjs/intl-localematcher": "^0.8.8",
98
98
  "clsx": "^2.1.1",
99
99
  "cookies-next": "^6.1.1",
100
100
  "cosmiconfig": "^9.0.1",
@@ -103,9 +103,9 @@
103
103
  "negotiator": "^1.0.0",
104
104
  "pretendard": "^1.3.9",
105
105
  "schema-dts": "^2.0.0",
106
- "tailwind-merge": "^3.5.0",
106
+ "tailwind-merge": "^3.6.0",
107
107
  "valtio": "^2.3.2",
108
- "@korioinc/next-conf": "2.0.44"
108
+ "@korioinc/next-conf": "2.0.45"
109
109
  },
110
110
  "publishConfig": {
111
111
  "access": "public",