weifuwu 0.17.24 → 0.17.26

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
@@ -439,15 +439,16 @@ ui/
439
439
 
440
440
  ```tsx
441
441
  // page.tsx
442
- export default function Page({ params, query }: { params: { slug: string }; query: Record<string, string> }) {
443
- const { t } = useCtx()
444
- return <h1>{t('title')}</h1>
442
+ export default function Page() {
443
+ const { t } = useLocale()
444
+ const data = useLoaderData()
445
+ return <h1>{t('title') ?? data.title}</h1>
445
446
  }
446
447
  ```
447
448
 
448
449
  ```tsx
449
450
  // layout.tsx
450
- export default function RootLayout({ children, req }: { children: React.ReactNode; req: Request }) {
451
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
451
452
  return <html><head/><body><main>{children}</main></body></html>
452
453
  }
453
454
  ```
@@ -479,8 +480,8 @@ const loading = useNavigating() // reactive loading state
479
480
  ### Client-side hooks
480
481
 
481
482
  ```tsx
482
- import { useWebsocket, useAction, useData, useQueryState, createStore, Head, setCtx } from 'weifuwu/react'
483
- import { useLocale, useTheme, applyTheme, addInterceptor } from 'weifuwu/react'
483
+ import { useWebsocket, useAction, useFetch, useQueryState, createStore, Head } from 'weifuwu/react'
484
+ import { useLocale, useTheme, applyTheme, addInterceptor, useLoaderData } from 'weifuwu/react'
484
485
 
485
486
  // WebSocket — auto-reconnecting
486
487
  const { send, lastMessage, readyState } = useWebsocket('/ws/chat', { onMessage: (d) => console.log(d), reconnect: { maxRetries: 10, delay: 3000 } })
@@ -490,7 +491,7 @@ const { submit, data, error, pending } = useAction('/api/feedback', { method: 'P
490
491
  // Auto-reads _csrf cookie, sends as X-CSRF-Token
491
492
 
492
493
  // Data fetching — cache + dedup + mutate
493
- const { data, error, loading, mutate } = useData('/api/posts', { fallback: loadData })
494
+ const { data, error, loading, mutate } = useFetch('/api/posts', { fallback: loadData })
494
495
 
495
496
  // URL query state
496
497
  const [q, setQ] = useQueryState('q', '')
@@ -503,8 +504,6 @@ const count = useStore(s => s.count)
503
504
  // Per-page meta tags
504
505
  <Head><title>Page Title</title><meta name="description" content="..." /></Head>
505
506
 
506
- // Update context (locale switch etc.)
507
- setCtx({ locale: 'en', prefs: { locale: 'en' } })
508
507
  ```
509
508
 
510
509
  ### Locale & Theme
@@ -548,6 +547,16 @@ function ThemeToggle() {
548
547
 
549
548
  **`applyTheme(theme)`** — DOM-only theme application. Sets `data-theme` on `<html>`, registers `matchMedia` listener for `'system'`. Used by the interceptor; exported for custom scenarios.
550
549
 
550
+ **`useLoaderData()`** — Returns the data returned by `load.ts`. Update-triggered; re-renders on SPA navigation.
551
+
552
+ ```tsx
553
+ import { useLoaderData } from 'weifuwu/react'
554
+ function Page() {
555
+ const data = useLoaderData<{ post: { title: string } }>()
556
+ return <h1>{data.post.title}</h1>
557
+ }
558
+ ```
559
+
551
560
  **`addInterceptor(fn)`** — Register a URL interceptor. Interceptors run before SPA navigation; if one returns `true`, `navigate()` skips the fetch-and-swap.
552
561
 
553
562
  ```ts
@@ -7,16 +7,16 @@ export interface StoreApi<T> {
7
7
  subscribe: (listener: () => void) => () => void;
8
8
  }
9
9
  export declare function createStore<T extends Record<string, unknown>>(initial: T): StoreApi<T>;
10
- interface UseDataResult<T> {
10
+ interface UseFetchResult<T> {
11
11
  data: T | undefined;
12
12
  error: Error | undefined;
13
13
  loading: boolean;
14
14
  mutate: (data?: T) => Promise<void>;
15
15
  }
16
- interface UseDataOptions<T> {
16
+ interface UseFetchOptions<T> {
17
17
  fallback?: T;
18
18
  ttl?: number;
19
19
  }
20
- export declare function useData<T = unknown>(url: string | null, options?: UseDataOptions<T>): UseDataResult<T>;
20
+ export declare function useFetch<T = unknown>(url: string | null, options?: UseFetchOptions<T>): UseFetchResult<T>;
21
21
  export declare function useQueryState(key: string, defaultValue?: string): [string, (val: string | ((prev: string) => string)) => void];
22
22
  export {};
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { serve, createTestServer } from './serve.ts';
4
4
  export type { ServeOptions, Server } from './serve.ts';
5
5
  export { Router } from './router.ts';
6
6
  export type { WebSocketHandler } from './router.ts';
7
- export { tsx, TsxContext, useCtx, setCtx } from './tsx.ts';
7
+ export { tsx, TsxContext } from './tsx.ts';
8
8
  export type { TsxOptions } from './tsx.ts';
9
9
  export { auth, cors, logger } from './middleware.ts';
10
10
  export type { AuthOptions, CORSOptions, LoggerOptions } from './middleware.ts';
package/dist/index.js CHANGED
@@ -575,10 +575,9 @@ import { AsyncLocalStorage } from "node:async_hooks";
575
575
  import chokidar from "chokidar";
576
576
 
577
577
  // tsx-context.ts
578
- import { useSyncExternalStore, createContext } from "react";
579
- var fallbackT = (key, _params, fallback) => fallback ?? key;
580
- var DEFAULT_CTX = { params: {}, query: {}, parsed: {}, prefs: {}, env: {}, t: fallbackT, user: {} };
581
- var KEY = "__WEIFUWU_CTX";
578
+ import { createContext } from "react";
579
+ var DEFAULT_CTX = { params: {}, query: {}, parsed: {}, prefs: {}, loaderData: {}, env: {}, user: {} };
580
+ var KEY = "__WEIFUWU_CTX_STORE";
582
581
  function getStore() {
583
582
  if (typeof globalThis !== "undefined" && globalThis[KEY]) {
584
583
  return globalThis[KEY];
@@ -595,14 +594,6 @@ function getStore() {
595
594
  return s;
596
595
  }
597
596
  var store = getStore();
598
- var subscribe = (cb) => {
599
- store._listeners.add(cb);
600
- return () => {
601
- store._listeners.delete(cb);
602
- };
603
- };
604
- var getSnapshot = () => store._snapshot;
605
- var getServerSnapshot = getSnapshot;
606
597
  function __registerAls(getStore2) {
607
598
  store._alsGetStore = getStore2;
608
599
  }
@@ -611,28 +602,6 @@ function setCtx(value) {
611
602
  store._snapshot = { params: store._ctx.params, query: store._ctx.query, user: store._ctx.user, parsed: store._ctx.parsed, prefs: store._ctx.prefs, env: store._ctx.env };
612
603
  store._listeners.forEach((fn) => fn());
613
604
  }
614
- function _buildT() {
615
- const messages2 = typeof window !== "undefined" ? window.__LOCALE_DATA__ : globalThis.__LOCALE_DATA__;
616
- if (!messages2) return fallbackT;
617
- return (key, params, fallback) => {
618
- const msg = key.split(".").reduce((o, k) => o?.[k], messages2);
619
- if (msg === void 0 || msg === null) return fallback ?? key;
620
- if (!params) return String(msg);
621
- let result = String(msg);
622
- for (const [k, v] of Object.entries(params)) result = result.replace(`{${k}}`, v);
623
- return result;
624
- };
625
- }
626
- function _readCtx() {
627
- const alsStore = store._alsGetStore?.();
628
- const base = alsStore ?? store._ctx;
629
- const data = typeof window !== "undefined" ? window.__WEIFUWU_CTX : null;
630
- return { ...base, ...data, t: _buildT() };
631
- }
632
- function useCtx() {
633
- useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
634
- return _readCtx();
635
- }
636
605
  var TsxContext = createContext(DEFAULT_CTX);
637
606
 
638
607
  // tsx-instance.ts
@@ -929,7 +898,7 @@ var TsxInstance = class {
929
898
  user: ctx.user ?? {},
930
899
  parsed: ctx.parsed ?? {},
931
900
  prefs: ctx.prefs ?? {},
932
- t: ctx.t ?? ((key) => key),
901
+ loaderData: {},
933
902
  env: ctx.env ?? {}
934
903
  };
935
904
  return als.run(ctxValue, async () => {
@@ -1032,24 +1001,27 @@ ${src}`;
1032
1001
  async buildClientBundle(entryPath, layoutPaths, pagesDir) {
1033
1002
  try {
1034
1003
  const layoutImports = layoutPaths.map((p) => `import${JSON.stringify(p)};`).join("");
1004
+ const _sc = `(function(){var k='__WEIFUWU_CTX_STORE';var s=typeof globalThis!='undefined'&&globalThis[k];if(!s)return function(){};return function(v){s._ctx={...s._ctx,...v};s._snapshot={params:s._ctx.params,query:s._ctx.query,user:s._ctx.user,parsed:s._ctx.parsed,prefs:s._ctx.prefs,env:s._ctx.env};s._listeners.forEach(function(fn){fn()})}})()`;
1035
1005
  const code = [
1036
1006
  layoutImports,
1037
1007
  `import{hydrateRoot}from'react-dom/client';`,
1038
1008
  `import{createElement,useState,useEffect}from'react';`,
1039
1009
  `import{TsxContext}from'weifuwu/react';`,
1040
1010
  `import P from${JSON.stringify(entryPath)};`,
1011
+ `var setCtx=${_sc};`,
1041
1012
  `const c=document.getElementById('__weifuwu_root');`,
1013
+ `if(window.__WEIFUWU_PROPS)setCtx({loaderData:window.__WEIFUWU_PROPS});`,
1042
1014
  `if(!window.__WFW_ROOT){`,
1043
1015
  `function App(){`,
1044
- `const[p,setP]=useState({C:P,props:window.__WEIFUWU_PROPS});`,
1045
- `useEffect(()=>{window.__WFW_SET_PAGE=(C,props)=>setP({C,props})},[]);`,
1046
- `const ctx=window.__WEIFUWU_CTX||{params:{},query:{}};`,
1016
+ `const[p,setP]=useState({C:P});`,
1017
+ `useEffect(()=>{window.__WFW_SET_PAGE=(C)=>{setCtx({loaderData:window.__WEIFUWU_PROPS});setP({C})}},[]);`,
1018
+ `const ctx=window.__WEIFUWU_CTX||{};`,
1047
1019
  `return createElement(TsxContext.Provider,{value:ctx},`,
1048
- `createElement(p.C,p.props))`,
1020
+ `createElement(p.C,null))`,
1049
1021
  `}`,
1050
1022
  `window.__WFW_ROOT=hydrateRoot(c,createElement(App));`,
1051
1023
  `}else{`,
1052
- `window.__WFW_SET_PAGE?.(P,window.__WEIFUWU_PROPS);`,
1024
+ `window.__WFW_SET_PAGE?.(P);`,
1053
1025
  `}`
1054
1026
  ].join("");
1055
1027
  const publicEnv = {};
@@ -1119,28 +1091,27 @@ ${src}`;
1119
1091
  const pageMod = this.pageModules.get(entryPath);
1120
1092
  if (!pageMod) return new Response("", { status: 500 });
1121
1093
  const Component = pageMod.default;
1094
+ const loadMod = loadPath ? this.loadModules.get(loadPath) : void 0;
1095
+ const loadFn = loadMod?.default;
1096
+ const loadProps = loadFn ? await loadFn({ params: ctx.params, query: ctx.query }) : {};
1122
1097
  const ctxValue = {
1123
1098
  params: ctx.params,
1124
1099
  query: ctx.query,
1125
1100
  user: ctx.user ?? {},
1126
1101
  parsed: ctx.parsed ?? {},
1127
1102
  prefs: ctx.prefs ?? {},
1128
- t: ctx.t ?? ((key) => key),
1103
+ loaderData: loadProps,
1129
1104
  env: ctx.env ?? {}
1130
1105
  };
1131
1106
  return als.run(ctxValue, async () => {
1132
1107
  setCtx(ctxValue);
1133
- const loadMod = loadPath ? this.loadModules.get(loadPath) : void 0;
1134
- const loadFn = loadMod?.default;
1135
- const loadProps = loadFn ? await loadFn({ params: ctx.params, query: ctx.query }) : {};
1136
- const allProps = { ...loadProps, params: ctx.params, query: ctx.query };
1137
1108
  let element = createElement(
1138
1109
  TsxContext.Provider,
1139
1110
  { value: ctxValue },
1140
1111
  createElement(
1141
1112
  "div",
1142
1113
  { id: "__weifuwu_root" },
1143
- createElement(Component, allProps)
1114
+ createElement(Component, null)
1144
1115
  )
1145
1116
  );
1146
1117
  if (layoutPaths.length === 0) {
@@ -1165,7 +1136,7 @@ ${src}`;
1165
1136
  const isRoot = i === 0;
1166
1137
  element = createElement(
1167
1138
  Layout,
1168
- isRoot ? { children: element, req } : { children: element }
1139
+ { children: element }
1169
1140
  );
1170
1141
  }
1171
1142
  }
@@ -1177,7 +1148,7 @@ ${src}`;
1177
1148
  compiledTailwindCss: this.compiledTailwindCss,
1178
1149
  isDev,
1179
1150
  bundle,
1180
- allProps
1151
+ loaderData: loadProps
1181
1152
  });
1182
1153
  });
1183
1154
  };
@@ -1456,12 +1427,13 @@ function buildHeadPayload(opts) {
1456
1427
  return result;
1457
1428
  }
1458
1429
  function buildBodyScripts(opts) {
1459
- if (!opts.bundle) return "";
1460
1430
  const parts = [];
1461
- if (opts.allProps) {
1462
- parts.push(`<script>window.__WEIFUWU_PROPS=${JSON.stringify(opts.allProps)}</script>`);
1431
+ if (opts.loaderData && Object.keys(opts.loaderData).length > 0) {
1432
+ parts.push(`<script>window.__WEIFUWU_PROPS=${JSON.stringify(opts.loaderData)}</script>`);
1433
+ }
1434
+ if (opts.bundle) {
1435
+ parts.push(`<script type="module" src="${opts.base}${opts.bundle.url}"></script>`);
1463
1436
  }
1464
- parts.push(`<script type="module" src="${opts.base}${opts.bundle.url}"></script>`);
1465
1437
  return parts.join("\n");
1466
1438
  }
1467
1439
 
@@ -7235,6 +7207,7 @@ function preferences(options) {
7235
7207
  globalThis.__LOCALE_DATA__ = msgs;
7236
7208
  ctx.parsed = { ...ctx.parsed, __localeData: msgs };
7237
7209
  }
7210
+ ;
7238
7211
  ctx.setPref = (name, value) => {
7239
7212
  const cookieOpts = [`${name}=${encodeURIComponent(value)}`, "Path=/", "SameSite=Lax"];
7240
7213
  const referer = req.headers.get("referer") || "/";
@@ -8665,7 +8638,6 @@ export {
8665
8638
  serve,
8666
8639
  serveStatic,
8667
8640
  setCookie,
8668
- setCtx,
8669
8641
  smoothStream,
8670
8642
  streamObject,
8671
8643
  streamText,
@@ -8673,7 +8645,6 @@ export {
8673
8645
  tool2 as tool,
8674
8646
  tsx,
8675
8647
  upload,
8676
- useCtx,
8677
8648
  user,
8678
8649
  validate
8679
8650
  };
package/dist/react.d.ts CHANGED
@@ -3,9 +3,9 @@ export type { UseWebsocketOptions, UseWebsocketReturn } from './use-websocket.ts
3
3
  export { useAction } from './use-action.ts';
4
4
  export type { UseActionOptions, UseActionReturn } from './use-action.ts';
5
5
  export { Link, useNavigate, navigate, useNavigating, addInterceptor } from './client-router.ts';
6
- export { TsxContext, useCtx, setCtx } from './tsx-context.ts';
6
+ export { TsxContext, useLoaderData } from './tsx-context.ts';
7
7
  export { Head } from './head.tsx';
8
- export { createStore, useData, useQueryState } from './client-state.ts';
8
+ export { createStore, useFetch, useQueryState } from './client-state.ts';
9
9
  export type { StoreApi } from './client-state.ts';
10
10
  export { useLocale } from './client-locale.ts';
11
11
  export { useTheme, applyTheme } from './client-theme.ts';
package/dist/react.js CHANGED
@@ -331,10 +331,9 @@ async function prefetchPage(href) {
331
331
  }
332
332
 
333
333
  // tsx-context.ts
334
- import { useSyncExternalStore, createContext } from "react";
335
- var fallbackT = (key, _params, fallback) => fallback ?? key;
336
- var DEFAULT_CTX = { params: {}, query: {}, parsed: {}, prefs: {}, env: {}, t: fallbackT, user: {} };
337
- var KEY = "__WEIFUWU_CTX";
334
+ import { createContext } from "react";
335
+ var DEFAULT_CTX = { params: {}, query: {}, parsed: {}, prefs: {}, loaderData: {}, env: {}, user: {} };
336
+ var KEY = "__WEIFUWU_CTX_STORE";
338
337
  function getStore() {
339
338
  if (typeof globalThis !== "undefined" && globalThis[KEY]) {
340
339
  return globalThis[KEY];
@@ -351,40 +350,22 @@ function getStore() {
351
350
  return s;
352
351
  }
353
352
  var store = getStore();
354
- var subscribe = (cb) => {
355
- store._listeners.add(cb);
356
- return () => {
357
- store._listeners.delete(cb);
358
- };
359
- };
360
- var getSnapshot = () => store._snapshot;
361
- var getServerSnapshot = getSnapshot;
362
353
  function setCtx(value) {
363
354
  store._ctx = { ...store._ctx, ...value };
364
355
  store._snapshot = { params: store._ctx.params, query: store._ctx.query, user: store._ctx.user, parsed: store._ctx.parsed, prefs: store._ctx.prefs, env: store._ctx.env };
365
356
  store._listeners.forEach((fn) => fn());
366
357
  }
367
- function _buildT() {
368
- const messages = typeof window !== "undefined" ? window.__LOCALE_DATA__ : globalThis.__LOCALE_DATA__;
369
- if (!messages) return fallbackT;
370
- return (key, params, fallback) => {
371
- const msg = key.split(".").reduce((o, k) => o?.[k], messages);
372
- if (msg === void 0 || msg === null) return fallback ?? key;
373
- if (!params) return String(msg);
374
- let result = String(msg);
375
- for (const [k, v] of Object.entries(params)) result = result.replace(`{${k}}`, v);
376
- return result;
377
- };
378
- }
379
- function _readCtx() {
358
+ function useCtx() {
380
359
  const alsStore = store._alsGetStore?.();
381
360
  const base = alsStore ?? store._ctx;
382
361
  const data = typeof window !== "undefined" ? window.__WEIFUWU_CTX : null;
383
- return { ...base, ...data, t: _buildT() };
362
+ return { ...base, ...data };
384
363
  }
385
- function useCtx() {
386
- useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
387
- return _readCtx();
364
+ function useLoaderData() {
365
+ const alsStore = store._alsGetStore?.();
366
+ const base = alsStore ?? store._ctx;
367
+ const data = typeof window !== "undefined" ? window.__WEIFUWU_CTX : null;
368
+ return { ...base, ...data }.loaderData;
388
369
  }
389
370
  var TsxContext = createContext(DEFAULT_CTX);
390
371
 
@@ -405,25 +386,25 @@ function createStore(initial) {
405
386
  state = { ...state, ...next };
406
387
  listeners.forEach((fn) => fn());
407
388
  };
408
- const subscribe2 = (listener) => {
389
+ const subscribe = (listener) => {
409
390
  listeners.add(listener);
410
391
  return () => {
411
392
  listeners.delete(listener);
412
393
  };
413
394
  };
414
395
  const useStore = ((selector) => useSyncExternalStore2(
415
- subscribe2,
396
+ subscribe,
416
397
  () => selector ? selector(state) : state
417
398
  ));
418
399
  useStore.getState = getState;
419
400
  useStore.setState = setState;
420
- useStore.subscribe = subscribe2;
401
+ useStore.subscribe = subscribe;
421
402
  return useStore;
422
403
  }
423
404
  var dataCache = /* @__PURE__ */ new Map();
424
405
  var inflight = /* @__PURE__ */ new Map();
425
406
  var CACHE_TTL = 6e4;
426
- function useData(url, options) {
407
+ function useFetch(url, options) {
427
408
  const ttl = options?.ttl ?? CACHE_TTL;
428
409
  const [state, setState] = useState4({
429
410
  data: options?.fallback,
@@ -497,7 +478,7 @@ function notifyQueryListeners() {
497
478
  window.dispatchEvent(new PopStateEvent("popstate"));
498
479
  }
499
480
  function useQueryState(key, defaultValue = "") {
500
- function getSnapshot2() {
481
+ function getSnapshot() {
501
482
  if (typeof window === "undefined") return defaultValue;
502
483
  const params = new URLSearchParams(window.location.search);
503
484
  return params.get(key) ?? defaultValue;
@@ -509,12 +490,12 @@ function useQueryState(key, defaultValue = "") {
509
490
  window.addEventListener("popstate", cb);
510
491
  return () => window.removeEventListener("popstate", cb);
511
492
  },
512
- getSnapshot2,
493
+ getSnapshot,
513
494
  () => defaultValue
514
495
  );
515
496
  const setValue = useCallback4((val) => {
516
497
  if (typeof window === "undefined") return;
517
- const resolved = typeof val === "function" ? val(getSnapshot2()) : val;
498
+ const resolved = typeof val === "function" ? val(getSnapshot()) : val;
518
499
  const url = new URL(window.location.href);
519
500
  if (resolved === defaultValue || resolved === "") {
520
501
  url.searchParams.delete(key);
@@ -528,6 +509,18 @@ function useQueryState(key, defaultValue = "") {
528
509
  }
529
510
 
530
511
  // client-locale.ts
512
+ function buildT() {
513
+ const messages = typeof window !== "undefined" ? window.__LOCALE_DATA__ : globalThis.__LOCALE_DATA__;
514
+ if (!messages) return (key, _p, fb) => fb ?? key;
515
+ return (key, params, fallback) => {
516
+ const msg = key.split(".").reduce((o, k) => o?.[k], messages);
517
+ if (msg === void 0 || msg === null) return fallback ?? key;
518
+ if (!params) return String(msg);
519
+ let result = String(msg);
520
+ for (const [k, v] of Object.entries(params)) result = result.replace(`{${k}}`, v);
521
+ return result;
522
+ };
523
+ }
531
524
  addInterceptor(async (url) => {
532
525
  const m = url.pathname.match(/^\/__lang\/(\w+)$/);
533
526
  if (!m) return false;
@@ -551,11 +544,12 @@ function useLocale() {
551
544
  return {
552
545
  locale: ctx.prefs.locale,
553
546
  setLocale: (locale) => navigate("/__lang/" + locale),
554
- t: ctx.t
547
+ t: buildT()
555
548
  };
556
549
  }
557
550
 
558
551
  // client-theme.ts
552
+ import { useEffect as useEffect4 } from "react";
559
553
  function resolveTheme(theme) {
560
554
  if (theme === "system") {
561
555
  if (typeof window === "undefined") return "light";
@@ -601,6 +595,9 @@ addInterceptor(async (url) => {
601
595
  function useTheme() {
602
596
  const ctx = useCtx();
603
597
  const theme = ctx.prefs.theme ?? "system";
598
+ useEffect4(() => {
599
+ applyTheme(theme);
600
+ }, [theme]);
604
601
  return {
605
602
  theme,
606
603
  resolvedTheme: resolveTheme(theme),
@@ -615,10 +612,9 @@ export {
615
612
  applyTheme,
616
613
  createStore,
617
614
  navigate,
618
- setCtx,
619
615
  useAction,
620
- useCtx,
621
- useData,
616
+ useFetch,
617
+ useLoaderData,
622
618
  useLocale,
623
619
  useNavigate,
624
620
  useNavigating,
@@ -1,4 +1,4 @@
1
- export interface CtxValue {
1
+ export interface PageContext {
2
2
  params: Record<string, string>;
3
3
  query: Record<string, string>;
4
4
  user: {
@@ -6,11 +6,13 @@ export interface CtxValue {
6
6
  };
7
7
  parsed: Record<string, unknown>;
8
8
  prefs: Record<string, string>;
9
- t: (key: string, params?: Record<string, string>, fallback?: string) => string;
9
+ loaderData: Record<string, unknown>;
10
10
  env: Record<string, string>;
11
11
  }
12
12
  /** @internal Injected by tsx-instance.ts for async-safe context isolation */
13
- export declare function __registerAls(getStore: () => CtxValue | undefined): void;
14
- export declare function setCtx(value: Partial<CtxValue>): void;
15
- export declare function useCtx(): CtxValue;
16
- export declare const TsxContext: import("react").Context<CtxValue>;
13
+ export declare function __registerAls(getStore: () => PageContext | undefined): void;
14
+ declare function setCtx(value: Partial<PageContext>): void;
15
+ declare function useCtx(): PageContext;
16
+ export declare function useLoaderData<T = Record<string, unknown>>(): T;
17
+ export declare const TsxContext: import("react").Context<PageContext>;
18
+ export { useCtx, setCtx };
@@ -1,6 +1,6 @@
1
1
  import { Router } from './router.ts';
2
- import { TsxContext, useCtx, setCtx } from './tsx-context.ts';
3
- export { TsxContext, useCtx, setCtx };
2
+ import { TsxContext } from './tsx-context.ts';
3
+ export { TsxContext };
4
4
  export interface TsxOptions {
5
5
  dir: string;
6
6
  }
package/dist/tsx.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { TsxContext, useCtx, setCtx } from './tsx-instance.ts';
1
+ import { TsxContext } from './tsx-instance.ts';
2
2
  import type { TsxOptions } from './tsx-instance.ts';
3
3
  import type { Router } from './router.ts';
4
- export { TsxContext, useCtx, setCtx };
4
+ export { TsxContext };
5
5
  export type { TsxOptions };
6
6
  export declare function tsx(options: TsxOptions): Promise<Router & {
7
7
  stop: () => void;
package/dist/types.d.ts CHANGED
@@ -4,12 +4,8 @@ export interface Context {
4
4
  user?: unknown;
5
5
  parsed?: Record<string, unknown>;
6
6
  mountPath?: string;
7
- locale?: string;
8
7
  t?: (key: string, params?: Record<string, string>, fallback?: string) => string;
9
- requestId?: string;
10
8
  prefs?: Record<string, string>;
11
- theme?: string;
12
- setPref?: (name: string, value: string) => Response;
13
9
  env?: Record<string, string>;
14
10
  }
15
11
  export type Handler = (req: Request, ctx: Context) => Response | Promise<Response>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "weifuwu",
3
- "version": "0.17.24",
3
+ "version": "0.17.26",
4
4
  "description": "Web-standard HTTP framework for Node.js — (req, ctx) => Response",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",