@typeroute/router 0.7.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.
@@ -0,0 +1,213 @@
1
+ import * as react from "react";
2
+ import { AnchorHTMLAttributes, CSSProperties, ComponentType, ReactNode, RefAttributes } from "react";
3
+ import { RouteParams } from "regexparam";
4
+ import { EmptyObject, Merge, Simplify } from "type-fest";
5
+ import { StandardSchemaV1 } from "@standard-schema/spec";
6
+
7
+ //#region src/utils/types.d.ts
8
+ type ParsePattern<P extends string> = Simplify<RouteParams<P>>;
9
+ type NormalizePath<P extends string> = RemoveTrailingSlash<DedupSlashes<`/${P}`>>;
10
+ type DedupSlashes<P extends string> = P extends `${infer Prefix}//${infer Rest}` ? `${Prefix}${DedupSlashes<`/${Rest}`>}` : P;
11
+ type RemoveTrailingSlash<P extends string> = P extends `${infer Prefix}/` ? Prefix extends "" ? "/" : Prefix : P;
12
+ type MaybeKey<K extends string, T> = T extends EmptyObject ? { [P in K]?: EmptyObject } : {} extends T ? { [P in K]?: T } : { [P in K]: T };
13
+ type OptionalOnUndefined<T extends object> = Simplify<{ [K in keyof T as undefined extends T[K] ? never : K]: T[K] } & { [K in keyof T as undefined extends T[K] ? K : never]?: T[K] }>;
14
+ //#endregion
15
+ //#region src/types.d.ts
16
+ interface Register {}
17
+ type RouteList = Register extends {
18
+ routes: infer RouteList extends ReadonlyArray<Route>;
19
+ } ? RouteList : ReadonlyArray<Route>;
20
+ type Handle = Register extends {
21
+ handle: infer Handle;
22
+ } ? Handle : any;
23
+ interface Middleware<S extends {} = any> {
24
+ use: <S2 extends {}>(middleware: Middleware<S2>) => Middleware<Merge<S, OptionalOnUndefined<S2>>>;
25
+ search: <S2 extends {}>(validate: Validator<S, S2>) => Middleware<Merge<S, OptionalOnUndefined<S2>>>;
26
+ handle: (handle: Handle) => Middleware<S>;
27
+ preload: (preload: (context: PreloadContext<{}, S>) => Promise<any>) => Middleware<S>;
28
+ component: (component: ComponentType) => Middleware<S>;
29
+ lazy: (loader: ComponentLoader) => Middleware<S>;
30
+ suspense: (fallback: ComponentType) => Middleware<S>;
31
+ error: (fallback: ComponentType<{
32
+ error: unknown;
33
+ }>) => Middleware<S>;
34
+ }
35
+ type Validator<S extends {}, S2 extends {}> = ((search: S & Record<string, unknown>) => S2) | StandardSchemaV1<Record<string, unknown>, S2>;
36
+ interface PreloadContext<Ps extends {} = any, S extends {} = any> {
37
+ params: Ps;
38
+ search: S;
39
+ }
40
+ interface RouterOptions {
41
+ routes: RouteList;
42
+ basePath?: string;
43
+ history?: HistoryLike;
44
+ ssrContext?: SSRContext;
45
+ defaultLinkOptions?: LinkOptions;
46
+ }
47
+ type Pattern = RouteList[number]["_"]["pattern"];
48
+ type GetRoute<P extends Pattern> = Extract<RouteList[number], {
49
+ _: {
50
+ pattern: P;
51
+ };
52
+ }>;
53
+ type Params<P extends Pattern> = GetRoute<P>["_types"]["params"];
54
+ type Search<P extends Pattern> = GetRoute<P>["_types"]["search"];
55
+ type MatchOptions<P extends Pattern> = {
56
+ from: P | GetRoute<P>;
57
+ strict?: boolean;
58
+ params?: Partial<Params<P>>;
59
+ };
60
+ type Match<P extends Pattern = Pattern> = {
61
+ route: GetRoute<P>;
62
+ params: Params<P>;
63
+ };
64
+ type NavigateOptions<P extends Pattern> = {
65
+ to: P | GetRoute<P>;
66
+ replace?: boolean;
67
+ state?: any;
68
+ } & MaybeKey<"params", Params<P>> & MaybeKey<"search", Search<P>>;
69
+ interface LinkOptions {
70
+ strict?: boolean;
71
+ preload?: "intent" | "render" | "viewport" | false;
72
+ preloadDelay?: number;
73
+ style?: CSSProperties;
74
+ className?: string;
75
+ activeStyle?: CSSProperties;
76
+ activeClassName?: string;
77
+ }
78
+ type SSRContext = {
79
+ redirect?: string;
80
+ statusCode?: number;
81
+ };
82
+ interface HistoryLike {
83
+ location: () => HistoryLocation;
84
+ go: (delta: number) => void;
85
+ push: (options: HistoryPushOptions) => void;
86
+ subscribe: (listener: () => void) => () => void;
87
+ }
88
+ interface HistoryLocation {
89
+ path: string;
90
+ search: Record<string, unknown>;
91
+ state: any;
92
+ }
93
+ interface HistoryPushOptions {
94
+ url: string;
95
+ replace?: boolean;
96
+ state?: any;
97
+ }
98
+ type Updater<T extends object> = Partial<T> | ((prev: T) => Partial<T>);
99
+ type ComponentLoader = () => Promise<ComponentType | {
100
+ default: ComponentType;
101
+ }>;
102
+ //#endregion
103
+ //#region src/route.d.ts
104
+ declare function route<P extends string>(pattern: P): Route<NormalizePath<P>, ParsePattern<NormalizePath<P>>, {}>;
105
+ declare function middleware(): Middleware<{}>;
106
+ declare class Route<P extends string = string, Ps extends {} = any, S extends {} = any> implements Middleware<S> {
107
+ readonly _: {
108
+ pattern: P;
109
+ keys: string[];
110
+ regex: RegExp;
111
+ loose: RegExp;
112
+ weights: number[];
113
+ validate: (search: Record<string, unknown>) => S;
114
+ handles: Handle[];
115
+ components: ComponentType[];
116
+ preloads: ((context: PreloadContext) => Promise<any>)[];
117
+ p?: Route;
118
+ };
119
+ readonly _types: {
120
+ params: Ps;
121
+ search: S;
122
+ };
123
+ constructor(_: typeof this._);
124
+ route: <P2 extends string>(pattern: P2) => Route<NormalizePath<`${P}/${P2}`>, ParsePattern<NormalizePath<`${P}/${P2}`>>, S>;
125
+ use: <S2 extends {}>(middleware: Middleware<S2>) => Route<P, Ps, Merge<S, OptionalOnUndefined<S2>>>;
126
+ search: <S2 extends {}>(validate: Validator<S, S2>) => Route<P, Ps, Merge<S, OptionalOnUndefined<S2>>>;
127
+ handle: (handle: Handle) => Route<P, Ps, S>;
128
+ preload: (preload: (context: PreloadContext<Ps, S>) => Promise<any>) => Route<P, Ps, S>;
129
+ component: (component: ComponentType) => Route<P, Ps, S>;
130
+ lazy: (loader: ComponentLoader) => Route<P, Ps, S>;
131
+ suspense: (fallback: ComponentType) => Route<P, Ps, S>;
132
+ error: (fallback: ComponentType<{
133
+ error: unknown;
134
+ }>) => Route<P, Ps, S>;
135
+ toString: () => P;
136
+ }
137
+ //#endregion
138
+ //#region src/router/router.d.ts
139
+ declare class Router {
140
+ readonly routes: RouteList;
141
+ readonly basePath: string;
142
+ readonly history: HistoryLike;
143
+ readonly ssrContext?: SSRContext;
144
+ readonly defaultLinkOptions?: LinkOptions;
145
+ private readonly _;
146
+ constructor(options: RouterOptions);
147
+ getRoute: <P extends Pattern>(pattern: P | GetRoute<P>) => GetRoute<P>;
148
+ match: <P extends Pattern>(path: string, options: MatchOptions<P>) => Match<P> | null;
149
+ matchAll: (path: string) => Match | null;
150
+ createUrl: <P extends Pattern>(options: NavigateOptions<P>) => string;
151
+ preload: <P extends Pattern>(options: NavigateOptions<P>) => Promise<void>;
152
+ navigate: <P extends Pattern>(options: NavigateOptions<P> | HistoryPushOptions | number) => void;
153
+ }
154
+ //#endregion
155
+ //#region src/router/browser-history.d.ts
156
+ declare class BrowserHistory implements HistoryLike {
157
+ private _?;
158
+ protected _loc: (path: string, search: string) => HistoryLocation;
159
+ constructor();
160
+ location: () => HistoryLocation;
161
+ go: (delta: number) => void;
162
+ push: (options: HistoryPushOptions) => void;
163
+ subscribe: (listener: () => void) => () => void;
164
+ }
165
+ //#endregion
166
+ //#region src/router/memory-history.d.ts
167
+ declare class MemoryHistory implements HistoryLike {
168
+ private stack;
169
+ private index;
170
+ private listeners;
171
+ constructor(url?: string);
172
+ location: () => HistoryLocation;
173
+ go: (delta: number) => void;
174
+ push: (options: HistoryPushOptions) => void;
175
+ subscribe: (listener: () => void) => () => void;
176
+ }
177
+ //#endregion
178
+ //#region src/router/hash-history.d.ts
179
+ declare class HashHistory extends BrowserHistory {
180
+ location: () => HistoryLocation;
181
+ push: (options: HistoryPushOptions) => void;
182
+ }
183
+ //#endregion
184
+ //#region src/react/components.d.ts
185
+ type RouterRootProps = RouterOptions | {
186
+ router: Router;
187
+ };
188
+ declare function RouterRoot(props: RouterRootProps): ReactNode;
189
+ declare function Outlet(): ReactNode;
190
+ type NavigateProps<P extends Pattern> = NavigateOptions<P>;
191
+ declare function Navigate<P extends Pattern>(props: NavigateProps<P>): null;
192
+ type LinkProps<P extends Pattern> = NavigateOptions<P> & LinkOptions & AnchorHTMLAttributes<HTMLAnchorElement> & RefAttributes<HTMLAnchorElement> & {
193
+ asChild?: boolean;
194
+ };
195
+ declare function Link<P extends Pattern>(props: LinkProps<P>): ReactNode;
196
+ //#endregion
197
+ //#region src/react/hooks.d.ts
198
+ declare function useRouter(): Router;
199
+ declare function useLocation(): HistoryLocation;
200
+ declare function useMatch<P extends Pattern>(options: MatchOptions<P>): Match<P> | null;
201
+ declare function useOutlet(): ReactNode;
202
+ declare function useNavigate(): <P extends Pattern>(options: number | HistoryPushOptions | NavigateOptions<P>) => void;
203
+ declare function useHandles(): Handle[];
204
+ declare function useParams<P extends Pattern>(from: P | GetRoute<P>): Params<P>;
205
+ declare function useSearch<P extends Pattern>(from: P | GetRoute<P>): readonly [Search<P>, (update: Updater<Search<P>>, replace?: boolean) => void];
206
+ //#endregion
207
+ //#region src/react/contexts.d.ts
208
+ declare const RouterContext: react.Context<Router | null>;
209
+ declare const LocationContext: react.Context<HistoryLocation | null>;
210
+ declare const MatchContext: react.Context<Match | null>;
211
+ declare const OutletContext: react.Context<ReactNode>;
212
+ //#endregion
213
+ export { BrowserHistory, ComponentLoader, GetRoute, Handle, HashHistory, HistoryLike, HistoryLocation, HistoryPushOptions, Link, LinkOptions, LinkProps, LocationContext, Match, MatchContext, MatchOptions, MemoryHistory, Middleware, Navigate, NavigateOptions, NavigateProps, Outlet, OutletContext, Params, Pattern, PreloadContext, Register, Route, RouteList, Router, RouterContext, RouterOptions, RouterRoot, RouterRootProps, SSRContext, Search, Updater, Validator, middleware, route, useHandles, useLocation, useMatch, useNavigate, useOutlet, useParams, useRouter, useSearch };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{Component as e,Suspense as t,cloneElement as n,createContext as r,isValidElement as i,lazy as a,memo as o,useCallback as s,useContext as c,useEffect as l,useInsertionEffect as u,useLayoutEffect as d,useMemo as f,useRef as p,useState as m,useSyncExternalStore as h}from"react";import{inject as g,parse as _}from"regexparam";import{jsx as v}from"react/jsx-runtime";function y(e){return`/${e}`.replaceAll(/\/+/g,`/`).replace(/(.+)\/$/,`$1`)}function b(e){let{keys:t,pattern:n}=_(e);return{pattern:e,keys:t,regex:n,loose:_(e,!0).pattern,weights:e.split(`/`).slice(1).map(e=>e.includes(`*`)?0:e.includes(`:`)?1:2)}}function x(e){return typeof e==`function`?e:t=>{let n=e[`~standard`].validate(t);if(n instanceof Promise)throw Error(`[TypeRoute] Validation can't be async`);if(n.issues)throw Error(`[TypeRoute] Validation failed`,{cause:n.issues});return n.value}}function S(e){return Object.entries(e).filter(([e,t])=>t!==void 0).map(([e,t])=>`${e}=${encodeURIComponent(w(t))}`).join(`&`)}function C(e){let t=new URLSearchParams(e);return Object.fromEntries([...t.entries()].map(([e,t])=>(t=decodeURIComponent(t),[e,T(t)?JSON.parse(t):t])))}function w(e){return typeof e==`string`&&!T(e)?e:JSON.stringify(e)}function T(e){try{return JSON.parse(e),!0}catch{return!1}}function E(e,t){return y(`${t}/${e}`)}function D(e,t){return(e===t||e.startsWith(`${t}/`))&&(e=e.slice(t.length)||`/`),e}function O(e,t){return[e,S(t)].filter(Boolean).join(`?`)}function k(e){let{pathname:t,search:n}=new URL(e,`http://w`);return{path:t,search:C(n)}}function A({keys:e,regex:t,loose:n},r,i,a){let o=(r?t:n).exec(D(i,a));if(!o)return null;let s={};return e.forEach((e,t)=>{let n=o[t+1];n&&(s[e]=n)}),s}function j(e){return[...e].sort((e,t)=>{let n=e.route._.weights,r=t.route._.weights,i=Math.max(n.length,r.length);for(let e=0;e<i;e++){let t=n[e]??-1,i=r[e]??-1;if(t!==i)return i-t}return 0})}const M=r(null),N=r(null),P=r(null),F=r(null);function I(){let e=c(M);if(e)return e;throw Error(`[TypeRoute] useRouter must be within a router context`)}function L(){let e=c(N);if(e)return e;throw Error(`[TypeRoute] useLocation must be within a router context`)}function R(e){let t=I(),{path:n}=L();return f(()=>t.match(n,e),[t,n,e.from,e.strict,e.params])}function z(){return c(F)}function B(){return I().navigate}function V(){let e=c(P);return f(()=>e?.route._.handles??[],[e])}function H(e){let t=R({from:e});if(t)return t.params;throw Error(`[TypeRoute] Can't read params for non-matching route ${e}`)}function U(e){let t=I(),{search:n,path:r}=L(),i=t.getRoute(e),a=f(()=>i._.validate(n),[i,n]);return[a,Z((e,n)=>{e=typeof e==`function`?e(a):e;let i=O(r,{...a,...e});t.navigate({url:i,replace:n})})]}var W=class{_;_loc=(e,t)=>{let{state:n}=history,[r,i]=this._??[];return i?.path===e&&r===t&&i.state===n?i:(this._=[t,{path:e,search:C(t),state:n}])[1]};constructor(){if(!window[G]){for(let e of[K,q]){let t=history[e];history[e]=function(...n){t.apply(this,n);let r=new Event(e);dispatchEvent(r)}}window[G]=1}}location=()=>this._loc(location.pathname,location.search);go=e=>history.go(e);push=e=>{let{url:t,replace:n,state:r}=e;history[n?q:K](r,``,t)};subscribe=e=>(J.forEach(t=>window.addEventListener(t,e)),()=>{J.forEach(t=>window.removeEventListener(t,e))})};const G=Symbol.for(`wmp01`),K=`pushState`,q=`replaceState`,J=[`popstate`,K,q,`hashchange`];var Y=class{routes;basePath;history;ssrContext;defaultLinkOptions;_;constructor(e){let{routes:t,basePath:n=`/`,history:r,ssrContext:i,defaultLinkOptions:a}=e;this.routes=t,this.basePath=y(n),this.history=r??new W,this.ssrContext=i,this.defaultLinkOptions=a,this._={routeMap:new Map(t.map(e=>[e._.pattern,e]))}}getRoute=e=>{if(typeof e!=`string`)return e;let t=this._.routeMap.get(e);if(!t)throw Error(`[TypeRoute] Route not found for ${e}`);return t};match=(e,t)=>{let{from:n,strict:r,params:i}=t,a=this.getRoute(n),o=A(a._,r,e,this.basePath);return o&&(!i||Object.keys(i).every(e=>i[e]===o[e]))?{route:a,params:o}:null};matchAll=e=>j(this.routes.map(t=>this.match(e,{from:t,strict:!0})).filter(e=>!!e))[0]??null;createUrl=e=>{let{to:t,params:n={},search:r={}}=e,{pattern:i}=this.getRoute(t)._;return O(E(g(i,n),this.basePath),r)};preload=async e=>{let{to:t,params:n={},search:r={}}=e,{preloads:i}=this.getRoute(t)._;await Promise.all(i.map(e=>e({params:n,search:r})))};navigate=e=>{if(typeof e==`number`)this.history.go(e);else if(`url`in e)this.history.push(e);else{let{replace:t,state:n}=e;this.history.push({url:this.createUrl(e),replace:t,state:n})}}},X=class{stack=[];index=0;listeners=new Set;constructor(e=`/`){this.stack.push({...k(e),state:void 0})}location=()=>this.stack[this.index];go=e=>{let t=this.index+e;this.stack[t]&&(this.index=t,this.listeners.forEach(e=>e()))};push=e=>{let{url:t,replace:n,state:r}=e,i={...k(t),state:r};this.stack=this.stack.slice(0,this.index+1),n?this.stack[this.index]=i:this.index=this.stack.push(i)-1,this.listeners.forEach(e=>e())};subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)})},ee=class extends W{location=()=>{let{pathname:e,search:t}=new URL(location.hash.slice(1),`http://w`);return this._loc(e,t)};push=e=>{let{url:t,replace:n,state:r}=e;history[n?`replaceState`:`pushState`](r,``,`#${t}`)}};function te(e){let[t]=m(()=>`router`in e?e.router:new Y(e)),{subscribe:n,location:r}=t.history,i=h(n,r,r),a=f(()=>t.matchAll(i.path),[t,i.path]);return a||console.error(`[TypeRoute] No matching route for path`,i.path),f(()=>v(M.Provider,{value:t,children:v(N.Provider,{value:i,children:v(P.Provider,{value:a,children:a?.route._.components.reduceRight((e,t)=>v(F.Provider,{value:e,children:v(t,{})}),null)})})}),[t,i,a])}function ne(){return z()}function re(e){let t=I();return d(()=>t.navigate(e),[]),t.ssrContext&&(t.ssrContext.redirect=t.createUrl(e)),null}function ie(e){let t=I(),{to:r,replace:a,state:o,params:c,search:u,strict:d,preload:m,preloadDelay:h=50,style:g,className:_,activeStyle:y,activeClassName:b,asChild:x,children:S,...C}={...t.defaultLinkOptions,...e},w=p(null),T=p(null),E=t.createUrl(e),D=!!R({from:r,strict:d,params:c}),O=Z(()=>t.preload(e)),k=s(()=>{clearTimeout(T.current)},[]),A=s(()=>{k(),T.current=setTimeout(O,h)},[h,k]),j=f(()=>({"data-active":D,style:{...g,...D&&y},className:[_,D&&b].filter(Boolean).join(` `)||void 0}),[D,g,_,y,b]);l(()=>{if(m===`render`)A();else if(m===`viewport`&&w.current){let e=new IntersectionObserver(e=>e.forEach(e=>{e.isIntersecting?A():k()}));return e.observe(w.current),()=>{e.disconnect(),k()}}return k},[m,A,k]);let M=e=>{C.onClick?.(e),!(e.ctrlKey||e.metaKey||e.shiftKey||e.altKey||e.button!==0||e.defaultPrevented)&&(e.preventDefault(),t.navigate({url:E,replace:a,state:o}))},N=e=>{C.onFocus?.(e),m===`intent`&&!e.defaultPrevented&&A()},P=e=>{C.onBlur?.(e),m===`intent`&&k()},F=e=>{C.onPointerEnter?.(e),m===`intent`&&!e.defaultPrevented&&A()},L=e=>{C.onPointerLeave?.(e),m===`intent`&&k()},z={...C,...j,ref:ae(w,C.ref),href:E,onClick:M,onFocus:N,onBlur:P,onPointerEnter:F,onPointerLeave:L};return x&&i(S)?n(S,z):v(`a`,{...z,children:S})}function ae(e,t){return t?n=>{e.current=n;let r=typeof t==`function`?t(n):void(t.current=n);return r&&(()=>{e.current=null,r()})}:e}function Z(e){let t=p(e);return u(()=>{t.current=e},[e]),p(((...e)=>t.current(...e))).current}function oe(e){return()=>v(t,{fallback:v(e,{}),children:z()})}function se(t){class n extends e{constructor(e){super(e),this.state={children:e.children,error:null}}static getDerivedStateFromError(e){return{error:[e]}}static getDerivedStateFromProps(e,t){return e.children===t.children?t:{children:e.children,error:null}}render(){return this.state.error?v(t,{error:this.state.error[0]}):this.props.children}}return()=>v(n,{children:z()})}function Q(e){return new $({...b(y(e)),validate:e=>e,handles:[],components:[],preloads:[]})}function ce(){return Q(``)}var $=class e{_;_types;constructor(e){this._=e}route=t=>new e({...this._,...b(y(`${this._.pattern}/${t}`)),p:this});use=t=>{let{_:n}=t;return new e({...this._,handles:[...this._.handles,...n.handles],components:[...this._.components,...n.components],preloads:[...this._.preloads,...n.preloads]}).search(n.validate)};search=t=>(t=x(t),new e({...this._,validate:e=>{let n=this._.validate(e);return{...n,...t({...e,...n})}}}));handle=t=>new e({...this._,handles:[...this._.handles,t]});preload=t=>new e({...this._,preloads:[...this._.preloads,e=>t({params:e.params,search:this._.validate(e.search)})]});component=t=>new e({...this._,components:[...this._.components,o(t)]});lazy=e=>{let t=a(async()=>{let t=await e();return`default`in t?t:{default:t}});return this.preload(e).component(t)};suspense=e=>this.component(oe(e));error=e=>this.component(se(e));toString=()=>this._.pattern};export{W as BrowserHistory,ee as HashHistory,ie as Link,N as LocationContext,P as MatchContext,X as MemoryHistory,re as Navigate,ne as Outlet,F as OutletContext,$ as Route,Y as Router,M as RouterContext,te as RouterRoot,ce as middleware,Q as route,V as useHandles,L as useLocation,R as useMatch,B as useNavigate,z as useOutlet,H as useParams,I as useRouter,U as useSearch};
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@typeroute/router",
3
+ "version": "0.7.0",
4
+ "license": "MIT",
5
+ "author": "strblr",
6
+ "description": "Type-safe React router that just works - simple setup, full autocomplete, 4kB gzipped",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "default": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/strblr/typeroute.git"
19
+ },
20
+ "homepage": "https://typeroute.com",
21
+ "bugs": "https://github.com/strblr/typeroute/issues",
22
+ "keywords": [
23
+ "react",
24
+ "router",
25
+ "typescript",
26
+ "type",
27
+ "typed",
28
+ "strongly-typed",
29
+ "type-safe",
30
+ "navigate",
31
+ "navigation",
32
+ "route",
33
+ "history",
34
+ "search",
35
+ "params",
36
+ "simple",
37
+ "lightweight"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsc --noEmit && tsdown",
41
+ "dev": "tsdown --watch",
42
+ "prepublishOnly": "bun run build && cp ../../README.md README.md",
43
+ "postpublish": "rm -f README.md"
44
+ },
45
+ "devDependencies": {
46
+ "@types/bun": "^1.3.8",
47
+ "@types/react": "^19.2.11",
48
+ "tsdown": "^0.20.2",
49
+ "typescript": "^5.9.3"
50
+ },
51
+ "peerDependencies": {
52
+ "react": ">=18"
53
+ },
54
+ "dependencies": {
55
+ "@standard-schema/spec": "^1.1.0",
56
+ "regexparam": "^3.0.0",
57
+ "type-fest": "5.4.1"
58
+ }
59
+ }