weifuwu 0.16.6 → 0.17.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/README.md +223 -30
- package/dist/client-router.d.ts +11 -7
- package/dist/client-state.d.ts +22 -0
- package/dist/head.d.ts +6 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +249 -113
- package/dist/preferences.d.ts +14 -0
- package/dist/react.d.ts +5 -2
- package/dist/react.js +315 -41
- package/dist/tsx-context.d.ts +5 -6
- package/dist/tsx-instance.d.ts +2 -2
- package/dist/tsx.d.ts +2 -2
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,6 @@ description: Web-standard HTTP framework for Node.js — (req, ctx) => Response
|
|
|
14
14
|
- [React SSR (tsx)](#react-ssr-tsx)
|
|
15
15
|
- [PostgreSQL](#postgresql)
|
|
16
16
|
- [Auth](#auth)
|
|
17
|
-
- [Security](#security)
|
|
18
17
|
- [WebSocket & Real-time](#websocket--real-time)
|
|
19
18
|
- [AI](#ai)
|
|
20
19
|
- [Data Layer](#data-layer)
|
|
@@ -26,7 +25,7 @@ description: Web-standard HTTP framework for Node.js — (req, ctx) => Response
|
|
|
26
25
|
- [Opencode](#opencode)
|
|
27
26
|
- [Deploy](#deploy)
|
|
28
27
|
- [Health check](#health-check)
|
|
29
|
-
- [
|
|
28
|
+
- [Preferences](#preferences)
|
|
30
29
|
- [Email](#email)
|
|
31
30
|
- [Server-Sent Events](#server-sent-events)
|
|
32
31
|
- [Utility functions](#utility-functions)
|
|
@@ -44,17 +43,42 @@ import { serve } from 'weifuwu'
|
|
|
44
43
|
serve((req, ctx) => new Response('Hello, World!'), { port: 3000 })
|
|
45
44
|
```
|
|
46
45
|
|
|
47
|
-
###
|
|
46
|
+
### Full-stack SSR in one file
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { serve, Router, tsx, preferences } from 'weifuwu'
|
|
50
|
+
|
|
51
|
+
const app = new Router()
|
|
52
|
+
app.use(preferences({
|
|
53
|
+
dir: './locales', // i18n (0 extra deps)
|
|
54
|
+
theme: {}, // dark mode (0 extra deps)
|
|
55
|
+
}))
|
|
56
|
+
app.use('/', await tsx({ dir: './ui' }))
|
|
57
|
+
|
|
58
|
+
serve(app.handler(), { port: 3000 })
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Your tsx pages can use it directly:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { Head, useCtx, useData, createStore } from 'weifuwu/react'
|
|
48
65
|
|
|
49
|
-
|
|
66
|
+
export default function Page() {
|
|
67
|
+
const { t, theme } = useCtx() // i18n + theme
|
|
68
|
+
const { data } = useData('/api/list') // data fetching
|
|
69
|
+
return <h1>{t('hello')} / {theme}</h1>
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Zero extra dependencies** — no zustand, react-query, next-intl, next-themes, react-hot-toast needed.
|
|
74
|
+
|
|
75
|
+
### weifuwu init
|
|
50
76
|
|
|
51
77
|
```bash
|
|
52
78
|
npx weifuwu init my-app
|
|
53
79
|
cd my-app && npm install && npm run dev
|
|
54
80
|
```
|
|
55
81
|
|
|
56
|
-
Creates `app.ts` with `tsx()`, `router.ws()`, `/api/ping` API route, and `ui/pages/layout.tsx` + `page.tsx`.
|
|
57
|
-
|
|
58
82
|
---
|
|
59
83
|
|
|
60
84
|
## serve() — HTTP server
|
|
@@ -183,10 +207,11 @@ app.get('/admin', middleware, handler) // route-level
|
|
|
183
207
|
| `compress(options?)` | Brotli / Gzip / Deflate compression |
|
|
184
208
|
| `validate(schemas)` | Zod validation (body, query, params) |
|
|
185
209
|
| `upload(options?)` | Multipart file upload |
|
|
186
|
-
| `
|
|
210
|
+
| `preferences(options?)` | Locale + theme detection, `ctx.t()`, `ctx.prefs`, `ctx.setPref()` |
|
|
187
211
|
| `seoMiddleware(options?)` | `X-Robots-Tag` header — string or path-based function |
|
|
188
212
|
| `helmet(options?)` | Security headers — CSP, HSTS, X-Frame-Options, etc. |
|
|
189
213
|
| `requestId(options?)` | `X-Request-ID` header + `ctx.requestId` |
|
|
214
|
+
| `health(options?)` | `GET /health` endpoint with custom checks |
|
|
190
215
|
|
|
191
216
|
### auth
|
|
192
217
|
|
|
@@ -332,6 +357,8 @@ app.use(helmet({
|
|
|
332
357
|
}))
|
|
333
358
|
```
|
|
334
359
|
|
|
360
|
+
13 security headers by default: `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Strict-Transport-Security`, `Content-Security-Policy`, `Referrer-Policy`, `Permissions-Policy`, `Cross-Origin-Opener-Policy`, `Cross-Origin-Resource-Policy`, `Cross-Origin-Embedder-Policy`, `X-DNS-Prefetch-Control`, `X-Download-Options`, `X-Permitted-Cross-Domain-Policies`.
|
|
361
|
+
|
|
335
362
|
### requestId
|
|
336
363
|
|
|
337
364
|
```ts
|
|
@@ -341,14 +368,11 @@ app.use(requestId())
|
|
|
341
368
|
// Sets X-Request-ID header on responses, available as ctx.requestId
|
|
342
369
|
```
|
|
343
370
|
|
|
344
|
-
13 security headers set by default with `helmet()`: `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Strict-Transport-Security`, `Content-Security-Policy`, `Referrer-Policy`, `Permissions-Policy`, `Cross-Origin-Openner-Policy`, `Cross-Origin-Resource-Policy`, `Cross-Origin-Embedder-Policy`, `X-DNS-Prefetch-Control`, `X-Download-Options`, `X-Permitted-Cross-Domain-Policies`.
|
|
345
|
-
|
|
346
371
|
---
|
|
347
372
|
|
|
348
373
|
## React SSR (tsx)
|
|
349
374
|
|
|
350
375
|
```ts
|
|
351
|
-
import { serve, Router } from 'weifuwu'
|
|
352
376
|
import { serve, Router, tsx } from 'weifuwu'
|
|
353
377
|
|
|
354
378
|
const app = new Router()
|
|
@@ -378,12 +402,25 @@ ui/
|
|
|
378
402
|
|
|
379
403
|
### page.tsx — page component
|
|
380
404
|
|
|
405
|
+
Components receive `{ params, query }` from routing and can use hooks for
|
|
406
|
+
context, data fetching, state, URL sync, and meta tags (see [exports table](#react-exports-weifuwureact)):
|
|
407
|
+
|
|
381
408
|
```tsx
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
409
|
+
import { Head, useCtx, useData, createStore, useQueryState } from 'weifuwu/react'
|
|
410
|
+
|
|
411
|
+
const useFilters = createStore({ category: '' })
|
|
412
|
+
|
|
413
|
+
export default function Page({ params, query }: { params: { slug: string }; query: Record<string, string> }) {
|
|
414
|
+
const { t, locale, theme } = useCtx() // i18n + theme + prefs
|
|
415
|
+
const [page, setPage] = useQueryState('page', '1') // URL sync
|
|
416
|
+
const { data, loading, mutate } = useData(`/api/posts?page=${page}`) // data fetching
|
|
417
|
+
|
|
418
|
+
return (
|
|
419
|
+
<>
|
|
420
|
+
<Head><title>{t('page.title')}</title></Head>
|
|
421
|
+
{loading ? <Skeleton /> : data.posts.map(p => <Card key={p.id} />)}
|
|
422
|
+
</>
|
|
423
|
+
)
|
|
387
424
|
}
|
|
388
425
|
```
|
|
389
426
|
|
|
@@ -422,6 +459,8 @@ export default function RootLayout({ children, req, ctx }: {
|
|
|
422
459
|
|
|
423
460
|
**Nested layouts** (`pages/blog/layout.tsx`) — receives only `{ children }`.
|
|
424
461
|
|
|
462
|
+
Page components access preferences, i18n, and theme via `useCtx()` — see [Preferences](#preferences).
|
|
463
|
+
|
|
425
464
|
### route.ts — API (co-located with page)
|
|
426
465
|
|
|
427
466
|
```ts
|
|
@@ -446,6 +485,17 @@ const apiUrl = process.env.WEIFUWU_PUBLIC_API_URL
|
|
|
446
485
|
|
|
447
486
|
The hydration bundle also injects `self.process = { env: {} }` as a safety net so any `process.env.*` reference in bundled dependencies won't throw.
|
|
448
487
|
|
|
488
|
+
### Streaming SSR
|
|
489
|
+
|
|
490
|
+
HTML is streamed via `TransformStream` — the browser starts rendering before the full page is ready. `<head>` content (theme blocking script, locale data, CSS) is injected at the `</head>` boundary and sent immediately.
|
|
491
|
+
|
|
492
|
+
### Persistent layout
|
|
493
|
+
|
|
494
|
+
The client hydration bundle creates a persistent `App` root that wraps all pages. Client-side navigation via `<Link>` (or `navigate()`) replaces the page component in-place instead of unmounting and re-hydrating. This means:
|
|
495
|
+
- `TsxContext.Provider` stays alive across navigations
|
|
496
|
+
- `createStore()` state persists (no cache-busting on bundle imports)
|
|
497
|
+
- Faster navigation — React only re-renders the page component
|
|
498
|
+
|
|
449
499
|
### Client-side hooks
|
|
450
500
|
|
|
451
501
|
#### useWebsocket — auto-reconnecting WebSocket
|
|
@@ -490,13 +540,14 @@ Auto-serializes JSON, auto-reads `_csrf` cookie and sends as `X-CSRF-Token`. Ret
|
|
|
490
540
|
### Client-side navigation
|
|
491
541
|
|
|
492
542
|
```tsx
|
|
493
|
-
import { Link, useNavigate } from 'weifuwu/react'
|
|
543
|
+
import { Link, useNavigate, useNavigating } from 'weifuwu/react'
|
|
494
544
|
|
|
495
545
|
function Nav() {
|
|
496
546
|
const navigate = useNavigate()
|
|
547
|
+
const loading = useNavigating()
|
|
497
548
|
return (
|
|
498
|
-
<nav>
|
|
499
|
-
<Link href="/about">About</Link>
|
|
549
|
+
<nav className={loading ? 'opacity-50' : ''}>
|
|
550
|
+
<Link href="/about" prefetch>About</Link>
|
|
500
551
|
<button onClick={() => navigate('/contact')}>Contact</button>
|
|
501
552
|
</nav>
|
|
502
553
|
)
|
|
@@ -505,6 +556,126 @@ function Nav() {
|
|
|
505
556
|
|
|
506
557
|
`navigate(href)` fetches the target via SSR, extracts `__weifuwu_root` content and `__WEIFUWU_PROPS`, replaces in-place, then imports the new hydration bundle. `load.ts` runs on the server for every navigation. Initial load is full SSR; subsequent navigations are client-side.
|
|
507
558
|
|
|
559
|
+
- `<Link prefetch>` — pre-fetches page data on hover / when entering viewport (200px margin)
|
|
560
|
+
- `useNavigating()` — reactive boolean, `true` while navigation is in-flight
|
|
561
|
+
- `isNavigating()` / `onNavigate(fn)` — non-hook alternatives
|
|
562
|
+
- Scroll position is saved before navigation and restored after the new page renders
|
|
563
|
+
|
|
564
|
+
### Head — per-page meta tags
|
|
565
|
+
|
|
566
|
+
```tsx
|
|
567
|
+
import { Head } from 'weifuwu/react'
|
|
568
|
+
|
|
569
|
+
export default function Page() {
|
|
570
|
+
return (
|
|
571
|
+
<>
|
|
572
|
+
<Head>
|
|
573
|
+
<title>My Page - App</title>
|
|
574
|
+
<meta name="description" content="Page description" />
|
|
575
|
+
<meta property="og:title" content="My Page" />
|
|
576
|
+
</Head>
|
|
577
|
+
<h1>Content</h1>
|
|
578
|
+
</>
|
|
579
|
+
)
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
During SSR, the `<Head>` content is extracted from the body and merged into `<head>`. On client-side navigation via `<Link>`, title and meta tags are updated automatically.
|
|
584
|
+
|
|
585
|
+
### Flash messages
|
|
586
|
+
|
|
587
|
+
```ts
|
|
588
|
+
// Server: set a flash message before redirect
|
|
589
|
+
router.post('/post', (req, ctx) => {
|
|
590
|
+
return ctx.setPref('flash', JSON.stringify({
|
|
591
|
+
type: 'success', message: 'Published!'
|
|
592
|
+
})) // → 302 with Set-Cookie: flash=...
|
|
593
|
+
})
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
```tsx
|
|
597
|
+
// Client: display flash from preferences
|
|
598
|
+
function Toast() {
|
|
599
|
+
const { prefs } = useCtx()
|
|
600
|
+
const flash = prefs?.flash ? JSON.parse(prefs.flash) : null
|
|
601
|
+
if (!flash) return null
|
|
602
|
+
return <div className={`toast ${flash.type}`}>{flash.message}</div>
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
Flash is read once from the cookie, then automatically cleared on the response. After page refresh the flash is gone.
|
|
607
|
+
|
|
608
|
+
### Client-side state management
|
|
609
|
+
|
|
610
|
+
#### createStore — shared state (replaces Zustand)
|
|
611
|
+
|
|
612
|
+
```tsx
|
|
613
|
+
import { createStore } from 'weifuwu/react'
|
|
614
|
+
|
|
615
|
+
const useStore = createStore({ count: 0, items: [] as string[] })
|
|
616
|
+
|
|
617
|
+
function Counter() {
|
|
618
|
+
const count = useStore(s => s.count) // selector
|
|
619
|
+
const { setState, getState } = useStore() // full state + API
|
|
620
|
+
return <button onClick={() => setState({ count: count + 1 })}>{count}</button>
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function List() {
|
|
624
|
+
const items = useStore(s => s.items)
|
|
625
|
+
return items.map(i => <div>{i}</div>)
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Outside components:
|
|
629
|
+
useStore.getState()
|
|
630
|
+
useStore.setState({ count: 1 })
|
|
631
|
+
useStore.subscribe(() => {})
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
Uses `useSyncExternalStore` internally. No context provider needed. State persists across client-side navigations (no cache-busting on bundle imports).
|
|
635
|
+
|
|
636
|
+
#### useData — data fetching (replaces React Query / SWR)
|
|
637
|
+
|
|
638
|
+
```tsx
|
|
639
|
+
import { useData } from 'weifuwu/react'
|
|
640
|
+
|
|
641
|
+
// Client-only fetch (shows loading skeleton on first load)
|
|
642
|
+
function PostList() {
|
|
643
|
+
const { data, error, loading, mutate } = useData('/api/posts')
|
|
644
|
+
if (loading) return <Skeleton />
|
|
645
|
+
return <div>{data.posts.map(p => <PostCard key={p.id} post={p} />)}</div>
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// With SSR fallback — data from load.ts, client takes over after hydration
|
|
649
|
+
function PostList({ load }: { load: { posts: Post[] } }) {
|
|
650
|
+
const { data, mutate } = useData('/api/posts', { fallback: load })
|
|
651
|
+
return <div>{data.posts.map(p => <PostCard key={p.id} post={p} />)}</div>
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
In-memory cache with 60s TTL, concurrent request dedup. `mutate(data)` for optimistic updates, `mutate()` for revalidation.
|
|
656
|
+
|
|
657
|
+
#### useQueryState — URL query params
|
|
658
|
+
|
|
659
|
+
```tsx
|
|
660
|
+
import { useQueryState } from 'weifuwu/react'
|
|
661
|
+
|
|
662
|
+
function SearchPage() {
|
|
663
|
+
const [q, setQ] = useQueryState('q', '')
|
|
664
|
+
const [page, setPage] = useQueryState('page', '1')
|
|
665
|
+
const { data } = useData(`/api/search?q=${q}&page=${page}`)
|
|
666
|
+
|
|
667
|
+
return (
|
|
668
|
+
<>
|
|
669
|
+
<input value={q} onChange={e => { setQ(e.target.value); setPage('1') }} />
|
|
670
|
+
<Results items={data?.items} />
|
|
671
|
+
<Pagination page={Number(page)} onChange={setPage} />
|
|
672
|
+
</>
|
|
673
|
+
)
|
|
674
|
+
}
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Synced with `window.location.search` via `useSyncExternalStore`. Back/forward navigation updates the state. Changes use `history.replaceState` and dispatch a synthetic `popstate` for reactivity.
|
|
678
|
+
|
|
508
679
|
### Development mode
|
|
509
680
|
|
|
510
681
|
Auto-detected when `NODE_ENV !== 'production'`. File watching (`chokidar`), single-file recompilation, WebSocket live reload (`/__weifuwu/livereload`), Tailwind CSS v4 auto-compilation.
|
|
@@ -990,18 +1161,31 @@ app.use(health({
|
|
|
990
1161
|
|
|
991
1162
|
---
|
|
992
1163
|
|
|
993
|
-
##
|
|
1164
|
+
## Preferences
|
|
994
1165
|
|
|
995
1166
|
```ts
|
|
996
|
-
import {
|
|
1167
|
+
import { preferences } from 'weifuwu'
|
|
997
1168
|
|
|
998
|
-
app.use(
|
|
1169
|
+
app.use(preferences({
|
|
1170
|
+
dir: './locales', // translation directory (optional)
|
|
1171
|
+
locale: { default: 'en' }, // locale detection
|
|
1172
|
+
theme: { default: 'system' }, // 'light' | 'dark' | 'system'
|
|
1173
|
+
}))
|
|
999
1174
|
|
|
1000
1175
|
// In handlers: ctx.t('greeting') → "Hello"
|
|
1001
|
-
//
|
|
1176
|
+
// ctx.locale → "en"
|
|
1177
|
+
// ctx.theme → "light"
|
|
1178
|
+
// ctx.prefs → { locale: 'en', theme: 'light' }
|
|
1179
|
+
// ctx.setPref('locale', 'zh') → 302 + cookie
|
|
1180
|
+
// ctx.setPref('flash', '{"type":"success","message":"Done"}') → flash message
|
|
1181
|
+
|
|
1182
|
+
// In tsx components:
|
|
1183
|
+
const { t, locale, theme } = useCtx()
|
|
1002
1184
|
```
|
|
1003
1185
|
|
|
1004
|
-
Locale detection: `Accept-Language`
|
|
1186
|
+
Locale detection priority: cookie → `Accept-Language` → default.
|
|
1187
|
+
Theme detection: cookie → default (`'system'`).
|
|
1188
|
+
Flash messages: set via `ctx.setPref('flash', ...)` → auto-read from cookie → cleared after rendering.
|
|
1005
1189
|
|
|
1006
1190
|
---
|
|
1007
1191
|
|
|
@@ -1045,13 +1229,22 @@ app.get('/stream', (req, ctx) => createSSEStream(events()))
|
|
|
1045
1229
|
| `formatSSE(event, data)` | Format SSE event string |
|
|
1046
1230
|
| `formatSSEData(data)` | Format SSE data string |
|
|
1047
1231
|
| `runWorkflow(options)` | DAG execution engine as AI SDK Tool |
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
|
1052
|
-
|
|
1053
|
-
| `
|
|
1054
|
-
| `
|
|
1232
|
+
|
|
1233
|
+
### React exports (`weifuwu/react`)
|
|
1234
|
+
|
|
1235
|
+
| Hook / Component | Description |
|
|
1236
|
+
|-----------------|-------------|
|
|
1237
|
+
| `useCtx()` | Unified context — `{ prefs, locale, theme, t, params, query }` (requires `preferences` middleware) |
|
|
1238
|
+
| `createStore(initial)` | Zustand-compatible shared state — `getState`, `setState`, `subscribe` |
|
|
1239
|
+
| `useData(url, opts?)` | SWR-style data fetching — cache, dedup, mutate, fallback |
|
|
1240
|
+
| `useQueryState(key, default)` | URL query param sync — `?page=1` via `useSyncExternalStore` |
|
|
1241
|
+
| `useAction(url, opts?)` | Async form submission — `{ submit, data, error, pending }` |
|
|
1242
|
+
| `useWebsocket(url, opts?)` | Auto-reconnecting WebSocket — `{ send, lastMessage, readyState }` |
|
|
1243
|
+
| `useNavigate()` | Client-side navigation callback |
|
|
1244
|
+
| `useNavigating()` | Reactive loading state during navigation |
|
|
1245
|
+
| `navigate(href)` | Client-side page navigation (imperative) |
|
|
1246
|
+
| `Link` | `<Link href prefetch>` — prefetch on hover/visible |
|
|
1247
|
+
| `Head` | `<Head>` — per-page `<title>` / `<meta>` merged into `<head>` |
|
|
1055
1248
|
|
|
1056
1249
|
### AI SDK re-exports
|
|
1057
1250
|
|
package/dist/client-router.d.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
export declare function isNavigating(): boolean;
|
|
2
|
+
export declare function onNavigate(fn: (v: boolean) => void): () => void;
|
|
1
3
|
export declare function navigate(href: string): Promise<void>;
|
|
2
4
|
export declare function useNavigate(): (href: string) => Promise<void>;
|
|
5
|
+
export declare function useNavigating(): boolean;
|
|
3
6
|
interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
4
7
|
href: string;
|
|
5
8
|
children: React.ReactNode;
|
|
9
|
+
prefetch?: boolean;
|
|
6
10
|
}
|
|
7
|
-
export declare function Link({ href, children, onClick, ...props }: LinkProps): import("react").DetailedReactHTMLElement<{
|
|
11
|
+
export declare function Link({ href, children, onClick, prefetch, ...props }: LinkProps): import("react").DetailedReactHTMLElement<{
|
|
12
|
+
slot?: string | undefined | undefined;
|
|
13
|
+
style?: import("react").CSSProperties | undefined;
|
|
14
|
+
title?: string | undefined | undefined;
|
|
15
|
+
dir?: string | undefined | undefined;
|
|
16
|
+
property?: string | undefined | undefined;
|
|
8
17
|
download?: any;
|
|
9
18
|
hrefLang?: string | undefined | undefined;
|
|
10
19
|
media?: string | undefined | undefined;
|
|
@@ -22,18 +31,14 @@ export declare function Link({ href, children, onClick, ...props }: LinkProps):
|
|
|
22
31
|
className?: string | undefined | undefined;
|
|
23
32
|
contentEditable?: (boolean | "true" | "false") | "inherit" | "plaintext-only" | undefined;
|
|
24
33
|
contextMenu?: string | undefined | undefined;
|
|
25
|
-
dir?: string | undefined | undefined;
|
|
26
34
|
draggable?: (boolean | "true" | "false") | undefined;
|
|
27
35
|
enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined | undefined;
|
|
28
36
|
hidden?: boolean | undefined | undefined;
|
|
29
37
|
id?: string | undefined | undefined;
|
|
30
38
|
lang?: string | undefined | undefined;
|
|
31
39
|
nonce?: string | undefined | undefined;
|
|
32
|
-
slot?: string | undefined | undefined;
|
|
33
40
|
spellCheck?: (boolean | "true" | "false") | undefined;
|
|
34
|
-
style?: import("react").CSSProperties | undefined;
|
|
35
41
|
tabIndex?: number | undefined | undefined;
|
|
36
|
-
title?: string | undefined | undefined;
|
|
37
42
|
translate?: "yes" | "no" | undefined | undefined;
|
|
38
43
|
radioGroup?: string | undefined | undefined;
|
|
39
44
|
role?: import("react").AriaRole | undefined;
|
|
@@ -42,7 +47,6 @@ export declare function Link({ href, children, onClick, ...props }: LinkProps):
|
|
|
42
47
|
datatype?: string | undefined | undefined;
|
|
43
48
|
inlist?: any;
|
|
44
49
|
prefix?: string | undefined | undefined;
|
|
45
|
-
property?: string | undefined | undefined;
|
|
46
50
|
rel?: string | undefined | undefined;
|
|
47
51
|
resource?: string | undefined | undefined;
|
|
48
52
|
rev?: string | undefined | undefined;
|
|
@@ -230,7 +234,7 @@ export declare function Link({ href, children, onClick, ...props }: LinkProps):
|
|
|
230
234
|
onDropCapture?: import("react").DragEventHandler<HTMLAnchorElement> | undefined;
|
|
231
235
|
onMouseDown?: import("react").MouseEventHandler<HTMLAnchorElement> | undefined;
|
|
232
236
|
onMouseDownCapture?: import("react").MouseEventHandler<HTMLAnchorElement> | undefined;
|
|
233
|
-
onMouseEnter
|
|
237
|
+
onMouseEnter: import("react").MouseEventHandler<HTMLAnchorElement>;
|
|
234
238
|
onMouseLeave?: import("react").MouseEventHandler<HTMLAnchorElement> | undefined;
|
|
235
239
|
onMouseMove?: import("react").MouseEventHandler<HTMLAnchorElement> | undefined;
|
|
236
240
|
onMouseMoveCapture?: import("react").MouseEventHandler<HTMLAnchorElement> | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type SetPartial<T> = Partial<T> | ((prev: T) => Partial<T>);
|
|
2
|
+
export interface StoreApi<T> {
|
|
3
|
+
(): T;
|
|
4
|
+
<S>(selector: (state: T) => S): S;
|
|
5
|
+
getState: () => T;
|
|
6
|
+
setState: (partial: SetPartial<T>) => void;
|
|
7
|
+
subscribe: (listener: () => void) => () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function createStore<T extends Record<string, unknown>>(initial: T): StoreApi<T>;
|
|
10
|
+
interface UseDataResult<T> {
|
|
11
|
+
data: T | undefined;
|
|
12
|
+
error: Error | undefined;
|
|
13
|
+
loading: boolean;
|
|
14
|
+
mutate: (data?: T) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
interface UseDataOptions<T> {
|
|
17
|
+
fallback?: T;
|
|
18
|
+
ttl?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function useData<T = unknown>(url: string | null, options?: UseDataOptions<T>): UseDataResult<T>;
|
|
21
|
+
export declare function useQueryState(key: string, defaultValue?: string): [string, (val: string | ((prev: string) => string)) => void];
|
|
22
|
+
export {};
|
package/dist/head.d.ts
ADDED
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,
|
|
7
|
+
export { tsx, TsxContext, useCtx } 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';
|
|
@@ -54,8 +54,8 @@ export { opencode } from './opencode/index.ts';
|
|
|
54
54
|
export type { OpencodeOptions, OpencodeModule, SkillDef, OpencodePermissions, Session as OpencodeSession, } from './opencode/types.ts';
|
|
55
55
|
export { health } from './health.ts';
|
|
56
56
|
export type { HealthOptions } from './health.ts';
|
|
57
|
-
export {
|
|
58
|
-
export type {
|
|
57
|
+
export { preferences } from './preferences.ts';
|
|
58
|
+
export type { PrefOptions } from './preferences.ts';
|
|
59
59
|
export { seo, seoMiddleware, seoTags } from './seo.ts';
|
|
60
60
|
export type { SeoOptions, RobotsRule, SitemapUrl, SitemapConfig, SeoHeadersConfig, SeoTagsConfig, } from './seo.ts';
|
|
61
61
|
export { mailer } from './mailer.ts';
|