@jant/core 0.2.20 → 0.3.0

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/bin/jant.js CHANGED
@@ -7,12 +7,9 @@
7
7
  * swizzle <component> [--wrap|--eject] - Override a theme component
8
8
  */
9
9
 
10
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
11
- import { resolve, dirname } from "path";
12
- import { fileURLToPath } from "url";
10
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
11
+ import { resolve } from "path";
13
12
 
14
- const __filename = fileURLToPath(import.meta.url);
15
- const __dirname = dirname(__filename);
16
13
 
17
14
  // Available components that can be swizzled
18
15
  const SWIZZLABLE_COMPONENTS = {
package/dist/auth.d.ts CHANGED
@@ -14,6 +14,7 @@ export declare function createAuth(d1: D1Database, options: {
14
14
  minPasswordLength: number;
15
15
  };
16
16
  session: {
17
+ expiresIn: number;
17
18
  cookieCache: {
18
19
  enabled: true;
19
20
  maxAge: number;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,wBAAgB,UAAU,CACxB,EAAE,EAAE,UAAU,EACd,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;GA4B7C;AAED,MAAM,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,wBAAgB,UAAU,CACxB,EAAE,EAAE,UAAU,EACd,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;GA6B7C;AAED,MAAM,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC"}
package/dist/auth.js CHANGED
@@ -26,6 +26,7 @@ export function createAuth(d1, options) {
26
26
  minPasswordLength: 8
27
27
  },
28
28
  session: {
29
+ expiresIn: 3600 * 24 * 365 * 10,
29
30
  cookieCache: {
30
31
  enabled: true,
31
32
  maxAge: 60 * 5
@@ -58,7 +58,7 @@ export declare const CreatePostSchema: z.ZodObject<{
58
58
  unlisted: "unlisted";
59
59
  draft: "draft";
60
60
  }>;
61
- sourceUrl: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
61
+ sourceUrl: z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodLiteral<"">]>;
62
62
  sourceName: z.ZodOptional<z.ZodString>;
63
63
  path: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
64
64
  replyToId: z.ZodOptional<z.ZodString>;
@@ -84,7 +84,7 @@ export declare const UpdatePostSchema: z.ZodObject<{
84
84
  unlisted: "unlisted";
85
85
  draft: "draft";
86
86
  }>>;
87
- sourceUrl: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
87
+ sourceUrl: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodLiteral<"">]>>;
88
88
  sourceName: z.ZodOptional<z.ZodOptional<z.ZodString>>;
89
89
  path: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
90
90
  replyToId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
@@ -30,7 +30,7 @@ import { POST_TYPES, VISIBILITY_LEVELS } from "../types.js";
30
30
  title: z.string().optional(),
31
31
  content: z.string(),
32
32
  visibility: VisibilitySchema,
33
- sourceUrl: z.string().url().optional().or(z.literal("")),
33
+ sourceUrl: z.url().optional().or(z.literal("")),
34
34
  sourceName: z.string().optional(),
35
35
  path: z.string().regex(/^[a-z0-9-]*$/).optional().or(z.literal("")),
36
36
  replyToId: z.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jant/core",
3
- "version": "0.2.20",
3
+ "version": "0.3.0",
4
4
  "description": "A modern, open-source microblogging platform built on Cloudflare Workers",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,15 +8,15 @@
8
8
  },
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./dist/index.d.ts",
11
+ "types": "./src/index.ts",
12
12
  "default": "./dist/index.js"
13
13
  },
14
14
  "./theme": {
15
- "types": "./dist/theme/index.d.ts",
15
+ "types": "./src/theme/index.ts",
16
16
  "default": "./dist/theme/index.js"
17
17
  },
18
18
  "./i18n": {
19
- "types": "./dist/i18n/index.d.ts",
19
+ "types": "./src/i18n/index.ts",
20
20
  "default": "./dist/i18n/index.js"
21
21
  },
22
22
  "./preset.css": "./src/preset.css",
@@ -49,6 +49,7 @@
49
49
  "devDependencies": {
50
50
  "@cloudflare/workers-types": "^4.20260131.0",
51
51
  "@lingui/cli": "^5.9.0",
52
+ "@lingui/conf": "^5.9.0",
52
53
  "@lingui/format-po": "^5.9.0",
53
54
  "@lingui/swc-plugin": "^5.10.1",
54
55
  "@swc/cli": "^0.6.0",
package/src/auth.ts CHANGED
@@ -31,6 +31,7 @@ export function createAuth(
31
31
  minPasswordLength: 8,
32
32
  },
33
33
  session: {
34
+ expiresIn: 3600 * 24 * 365 * 10, // 10 years
34
35
  cookieCache: {
35
36
  enabled: true,
36
37
  maxAge: 60 * 5, // 5 minutes
@@ -37,7 +37,7 @@ export const CreatePostSchema = z.object({
37
37
  title: z.string().optional(),
38
38
  content: z.string(),
39
39
  visibility: VisibilitySchema,
40
- sourceUrl: z.string().url().optional().or(z.literal("")),
40
+ sourceUrl: z.url().optional().or(z.literal("")),
41
41
  sourceName: z.string().optional(),
42
42
  path: z
43
43
  .string()
@@ -1,257 +0,0 @@
1
- # i18n Usage Examples
2
-
3
- ## New API: useLingui() Hook
4
-
5
- We now use a React-like hook API that eliminates prop drilling and makes code cleaner.
6
-
7
- ### Basic Pattern
8
-
9
- ```tsx
10
- import { I18nProvider, useLingui } from "@/i18n";
11
-
12
- // 1. Wrap your app in route handler
13
- dashRoute.get("/", async (c) => {
14
- return c.html(
15
- <I18nProvider c={c}>
16
- <MyApp />
17
- </I18nProvider>,
18
- );
19
- });
20
-
21
- // 2. Use useLingui() hook inside components
22
- function MyApp() {
23
- const { t } = useLingui();
24
-
25
- return (
26
- <h1>{t({ message: "Dashboard", comment: "@context: Page title" })}</h1>
27
- );
28
- }
29
- ```
30
-
31
- ### Why the `comment` field?
32
-
33
- The `comment` field provides context for AI translators, improving translation quality:
34
-
35
- ```tsx
36
- // ✅ Good - clear context
37
- t({ message: "Dashboard", comment: "@context: Page title" });
38
- t({ message: "Dashboard", comment: "@context: Navigation link" });
39
-
40
- // ❌ Bad - no context (translator might choose wrong word)
41
- t({ message: "Dashboard" });
42
- ```
43
-
44
- ---
45
-
46
- ## Complete Example
47
-
48
- ```tsx
49
- import { I18nProvider, useLingui, Trans } from "@/i18n";
50
-
51
- // Route handler: wrap in I18nProvider
52
- dashRoute.get("/", async (c) => {
53
- const posts = await c.var.services.posts.list();
54
-
55
- return c.html(
56
- <I18nProvider c={c}>
57
- <Dashboard postCount={posts.length} username="Alice" />
58
- </I18nProvider>,
59
- );
60
- });
61
-
62
- // Component: use useLingui() hook
63
- function Dashboard({
64
- postCount,
65
- username,
66
- }: {
67
- postCount: number;
68
- username: string;
69
- }) {
70
- const { t } = useLingui();
71
-
72
- return (
73
- <div>
74
- {/* 1. Simple translation */}
75
- <h1>{t({ message: "Dashboard", comment: "@context: Page title" })}</h1>
76
-
77
- {/* 2. With variables */}
78
- <p>
79
- {t(
80
- {
81
- message: "Welcome back, {name}!",
82
- comment: "@context: Welcome message",
83
- },
84
- { name: username },
85
- )}
86
- </p>
87
-
88
- {/* 3. With dynamic values */}
89
- <p>
90
- {t(
91
- {
92
- message: "You have {count} posts",
93
- comment: "@context: Post count",
94
- },
95
- { count: postCount },
96
- )}
97
- </p>
98
-
99
- {/* 4. With embedded components */}
100
- <p>
101
- <Trans comment="@context: Help text">
102
- Read the{" "}
103
- <a href="/docs" class="underline">
104
- documentation
105
- </a>{" "}
106
- for help
107
- </Trans>
108
- </p>
109
- </div>
110
- );
111
- }
112
- ```
113
-
114
- ---
115
-
116
- ## Trans Component: Embedded JSX
117
-
118
- The `Trans` component is a simplified implementation that renders children as-is. It's useful for translations with embedded links or formatting.
119
-
120
- ```tsx
121
- import { Trans } from "@/i18n";
122
-
123
- // Simple link
124
- <Trans comment="@context: Website link">
125
- Visit <a href="/" class="text-primary">our website</a>
126
- </Trans>
127
-
128
- // Multiple elements
129
- <Trans comment="@context: Learn more link">
130
- Click <strong class="font-bold">here</strong> to <a href="/learn" class="underline">learn more</a>
131
- </Trans>
132
-
133
- // With formatting
134
- <Trans comment="@context: Welcome message">
135
- Welcome <strong class="font-semibold">back</strong>!
136
- </Trans>
137
- ```
138
-
139
- **Note**: This is a simplified implementation that renders children directly. For complex translations with dynamic placeholders, use the `t()` function instead:
140
-
141
- ```tsx
142
- const { t } = useLingui();
143
-
144
- // For dynamic content, use t() with placeholders
145
- <p>
146
- {t(
147
- {
148
- message: "Visit {linkStart}our website{linkEnd}",
149
- comment: "@context: Website link",
150
- },
151
- {
152
- linkStart: '<a href="/" class="text-primary">',
153
- linkEnd: "</a>",
154
- },
155
- )}
156
- </p>;
157
- ```
158
-
159
- ---
160
-
161
- ## Comparison: Before vs Now
162
-
163
- ### Before (Prop drilling required)
164
-
165
- ```tsx
166
- import { getI18n } from "@/i18n";
167
-
168
- dashRoute.get("/", async (c) => {
169
- const i18n = getI18n(c);
170
-
171
- return c.html(
172
- <Layout>
173
- <MyComponent c={c} /> {/* Must pass c prop */}
174
- </Layout>,
175
- );
176
- });
177
-
178
- function MyComponent({ c }: { c: Context }) {
179
- const i18n = getI18n(c);
180
- const title = i18n._({ message: "Dashboard", comment: "@context: Title" });
181
- const greeting = i18n._(
182
- { message: "Hello {name}", comment: "@context: Greeting" },
183
- { name: "Alice" },
184
- );
185
-
186
- return <h1>{title}</h1>;
187
- }
188
- ```
189
-
190
- ### Now (No prop drilling)
191
-
192
- ```tsx
193
- import { I18nProvider, useLingui } from "@/i18n";
194
-
195
- dashRoute.get("/", async (c) => {
196
- return c.html(
197
- <I18nProvider c={c}>
198
- <Layout>
199
- <MyComponent /> {/* No props needed */}
200
- </Layout>
201
- </I18nProvider>,
202
- );
203
- });
204
-
205
- function MyComponent() {
206
- const { t } = useLingui();
207
- const title = t({ message: "Dashboard", comment: "@context: Title" });
208
- const greeting = t(
209
- { message: "Hello {name}", comment: "@context: Greeting" },
210
- { name: "Alice" },
211
- );
212
-
213
- return <h1>{title}</h1>;
214
- }
215
- ```
216
-
217
- ---
218
-
219
- ## Best Practices
220
-
221
- 1. **Always wrap in I18nProvider**: Wrap your app in `<I18nProvider c={c}>` in route handlers
222
- 2. **Use useLingui() hook**: Call `const { t } = useLingui()` inside components
223
- 3. **Always include comment**: Provide `@context:` comment for better AI translations
224
- 4. **Variables as second param**: `t({ message: "Hello {name}", comment: "..." }, { name })`
225
- 5. **Use Trans for embedded JSX**: Use `<Trans>` for links and formatting
226
- 6. **Extract translations**: Run `pnpm i18n:extract` after adding new strings
227
- 7. **Compile translations**: Run `pnpm i18n:compile` to generate catalog files
228
-
229
- ---
230
-
231
- ## Common Questions
232
-
233
- ### Q: Why can't I use `t("Dashboard")` directly?
234
-
235
- A: Lingui uses a build-time extraction process. The `t()` function expects a message descriptor object that gets transformed by Lingui's macro system during the build. If you pass a plain string, the extraction tool won't be able to find and extract the message for translation.
236
-
237
- You must use the object syntax:
238
-
239
- ```tsx
240
- t({ message: "Dashboard", comment: "@context: Page title" });
241
- ```
242
-
243
- ### Q: Can I use Lingui's official Trans component?
244
-
245
- A: Lingui's `Trans` component is designed for React and requires React Context. Since we use Hono JSX (not React), we provide a simplified `Trans` component that works with our SSR setup. It renders children directly without complex transformation.
246
-
247
- ### Q: Why use useLingui() instead of getI18n(c)?
248
-
249
- A: The `useLingui()` hook provides a cleaner API that eliminates prop drilling. Instead of passing the Hono context `c` to every component, you wrap your app once in `I18nProvider` and all child components can access i18n via the hook.
250
-
251
- ### Q: Is this safe for concurrent requests?
252
-
253
- A: Yes! Each request creates its own i18n instance via `I18nProvider`. The global state is only used during the synchronous rendering phase, so there's no risk of race conditions between concurrent requests.
254
-
255
- ### Q: What if I call useLingui() outside I18nProvider?
256
-
257
- A: You'll get an error: "useLingui() called outside of I18nProvider". This is intentional - the hook must be used within the provider context. Always wrap your app in `<I18nProvider c={c}>` in your route handlers.
@@ -1,310 +0,0 @@
1
- # Jant i18n - React-like API for Hono JSX
2
-
3
- ## ✅ API Overview
4
-
5
- We provide a React-like i18n API that works with Hono JSX SSR!
6
-
7
- ### 1. **I18nProvider** - Like React Context Provider
8
-
9
- ```tsx
10
- import { I18nProvider } from "@/i18n";
11
-
12
- // Wrap your app in route handler
13
- dashRoute.get("/", async (c) => {
14
- return c.html(
15
- <I18nProvider c={c}>
16
- <YourApp />
17
- </I18nProvider>,
18
- );
19
- });
20
- ```
21
-
22
- ### 2. **useLingui()** - Like React hook
23
-
24
- ```tsx
25
- import { useLingui } from "@/i18n";
26
-
27
- function MyComponent() {
28
- // React-like hook API
29
- const { t } = useLingui();
30
-
31
- return (
32
- <div>
33
- {/* Simple and clean */}
34
- <h1>{t({ message: "Dashboard", comment: "@context: Page title" })}</h1>
35
- </div>
36
- );
37
- }
38
- ```
39
-
40
- ### 3. **Trans Component** - For Embedded JSX
41
-
42
- ```tsx
43
- import { Trans } from "@/i18n";
44
-
45
- function MyComponent() {
46
- return (
47
- <Trans comment="@context: Help text">
48
- Read the <a href="/docs">documentation</a>
49
- </Trans>
50
- );
51
- }
52
- ```
53
-
54
- ---
55
-
56
- ## 📝 Complete Example
57
-
58
- ```tsx
59
- /**
60
- * Dashboard Route - React-like i18n API
61
- */
62
-
63
- import { Hono } from "hono";
64
- import { I18nProvider, useLingui, Trans } from "@/i18n";
65
-
66
- export const dashRoute = new Hono();
67
-
68
- // Component: use useLingui() hook
69
- function DashboardContent({ postCount }: { postCount: number }) {
70
- const { t } = useLingui();
71
-
72
- return (
73
- <div>
74
- {/* 1. Simple translation */}
75
- <h1>{t({ message: "Dashboard", comment: "@context: Page title" })}</h1>
76
-
77
- {/* 2. With variables */}
78
- <p>
79
- {t(
80
- {
81
- message: "You have {count} posts",
82
- comment: "@context: Post count message",
83
- },
84
- { count: postCount },
85
- )}
86
- </p>
87
-
88
- {/* 3. With embedded components - use Trans */}
89
- <p>
90
- <Trans comment="@context: Help text">
91
- Read the{" "}
92
- <a href="/docs" class="underline">
93
- documentation
94
- </a>
95
- </Trans>
96
- </p>
97
- </div>
98
- );
99
- }
100
-
101
- // Route handler: wrap in I18nProvider
102
- dashRoute.get("/", async (c) => {
103
- const posts = await c.var.services.posts.list();
104
-
105
- return c.html(
106
- <I18nProvider c={c}>
107
- <DashboardContent postCount={posts.length} />
108
- </I18nProvider>,
109
- );
110
- });
111
- ```
112
-
113
- ---
114
-
115
- ## 🆚 Comparison: Before vs Now
116
-
117
- ### Before (Complex - prop drilling)
118
-
119
- ```tsx
120
- import { getI18n } from "@/i18n";
121
-
122
- dashRoute.get("/", async (c) => {
123
- const i18n = getI18n(c);
124
-
125
- return c.html(
126
- <Layout title={i18n._({ message: "Dashboard", comment: "@context: ..." })}>
127
- <MyComponent c={c} /> {/* Need to pass c prop */}
128
- </Layout>,
129
- );
130
- });
131
-
132
- function MyComponent({ c }: { c: Context }) {
133
- const i18n = getI18n(c);
134
- return <h1>{i18n._({ message: "Hello", comment: "@context: ..." })}</h1>;
135
- }
136
- ```
137
-
138
- ### Now (Clean - no prop drilling)
139
-
140
- ```tsx
141
- import { I18nProvider, useLingui } from "@/i18n";
142
-
143
- dashRoute.get("/", async (c) => {
144
- return c.html(
145
- <I18nProvider c={c}>
146
- <Layout>
147
- <MyComponent /> {/* No need to pass c prop */}
148
- </Layout>
149
- </I18nProvider>,
150
- );
151
- });
152
-
153
- function MyComponent() {
154
- const { t } = useLingui(); // Like React hook
155
- return <h1>{t({ message: "Hello", comment: "@context: ..." })}</h1>;
156
- }
157
- ```
158
-
159
- ---
160
-
161
- ## ⚠️ Important Notes
162
-
163
- ### 1. **Always include `comment` field**
164
-
165
- ```tsx
166
- // ✅ Correct - comment is crucial for AI translation
167
- const { t } = useLingui();
168
- t({ message: "Dashboard", comment: "@context: Page title" });
169
-
170
- // ❌ Wrong - missing comment reduces translation quality
171
- t({ message: "Dashboard" });
172
- ```
173
-
174
- ### 2. **I18nProvider must wrap your app**
175
-
176
- ```tsx
177
- // ✅ Correct - wrap in I18nProvider
178
- c.html(
179
- <I18nProvider c={c}>
180
- <App />
181
- </I18nProvider>,
182
- );
183
-
184
- // ❌ Wrong - useLingui() will throw error
185
- c.html(<App />); // useLingui() inside App won't find i18n context
186
- ```
187
-
188
- ### 3. **useLingui() only works inside components**
189
-
190
- ```tsx
191
- // ✅ Correct - inside JSX component
192
- function MyComponent() {
193
- const { t } = useLingui();
194
- return <div>{t({ message: "Hello", comment: "@context: ..." })}</div>;
195
- }
196
-
197
- // ❌ Wrong - in route handler (outside I18nProvider)
198
- dashRoute.get("/", async (c) => {
199
- const { t } = useLingui(); // Error: not inside I18nProvider
200
- ...
201
- });
202
- ```
203
-
204
- ### 4. **Variables go in second parameter**
205
-
206
- ```tsx
207
- const { t } = useLingui();
208
-
209
- // ✅ Correct - values as second parameter
210
- t(
211
- { message: "Hello {name}", comment: "@context: Greeting" },
212
- { name: "Alice" },
213
- );
214
-
215
- // ❌ Wrong - values inside first parameter (not supported)
216
- t({
217
- message: "Hello {name}",
218
- comment: "@context: Greeting",
219
- values: { name: "Alice" },
220
- });
221
- ```
222
-
223
- ---
224
-
225
- ## 🎯 Best Practices
226
-
227
- 1. **Route handler**: Wrap app in `<I18nProvider c={c}>`
228
- 2. **Inside components**: Use `useLingui()` hook to get `t()` function
229
- 3. **Translation calls**: `t({ message: "...", comment: "@context: ..." })`
230
- 4. **With embedded JSX**: Use `<Trans>` component
231
- 5. **Always include `comment`**: Helps AI understand context for better translations
232
-
233
- ---
234
-
235
- ## 📚 API Reference
236
-
237
- ### `I18nProvider`
238
-
239
- Provides i18n context to all child components. Must wrap your app in route handlers.
240
-
241
- ```tsx
242
- interface I18nProviderProps {
243
- c: Context; // Hono context
244
- children: JSX.Element;
245
- }
246
-
247
- // Usage
248
- <I18nProvider c={c}>
249
- <YourApp />
250
- </I18nProvider>;
251
- ```
252
-
253
- ### `useLingui()`
254
-
255
- Hook to access i18n functionality inside components. Must be used within `I18nProvider`.
256
-
257
- ```tsx
258
- function useLingui(): {
259
- i18n: I18n; // Lingui i18n instance
260
- t: (descriptor: MessageDescriptor, values?: Record<string, any>) => string;
261
- _: (descriptor: MessageDescriptor, values?: Record<string, any>) => string;
262
- };
263
-
264
- // Usage
265
- function MyComponent() {
266
- const { t } = useLingui();
267
- return <h1>{t({ message: "Hello", comment: "@context: Greeting" })}</h1>;
268
- }
269
- ```
270
-
271
- **Note**: `t()` and `_()` are equivalent - use whichever you prefer.
272
-
273
- ### `Trans`
274
-
275
- Component for translations with embedded JSX elements. Simplified implementation that renders children as-is.
276
-
277
- ```tsx
278
- interface TransProps {
279
- comment?: string; // @context comment for translators
280
- id?: string; // Optional message ID
281
- children: JSX.Element; // JSX content with embedded elements
282
- }
283
-
284
- // Usage
285
- <Trans comment="@context: Help text">
286
- Read the <a href="/docs">documentation</a>
287
- </Trans>;
288
- ```
289
-
290
- **Note**: This is a simplified implementation. For complex translations with dynamic content, use `t()` with placeholders instead.
291
-
292
- ---
293
-
294
- ## 🔧 How It Works
295
-
296
- 1. **I18nProvider** sets the global i18n instance during rendering
297
- 2. **useLingui()** reads the current i18n instance from global state
298
- 3. **Single-pass rendering**: Each request renders only once, making global state safe
299
- 4. **Concurrency-safe**: Each request creates a new i18n instance, preventing race conditions
300
-
301
- This implementation mimics React's Context API but is optimized for Hono JSX SSR scenarios.
302
-
303
- ### Why Global State is Safe
304
-
305
- Unlike React (client-side with multiple re-renders), Hono JSX renders once per request on the server:
306
-
307
- - Request arrives → I18nProvider sets global i18n → Components render → Response sent
308
- - Next request → New i18n instance → Components render → Response sent
309
-
310
- Since rendering is synchronous and single-pass, there's no risk of concurrent requests interfering with each other.