@hyeonqyu/typed-router-next 1.5.1 → 1.5.3

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/dist/index.js CHANGED
@@ -21,7 +21,8 @@ var createTypedPathname = /* @__PURE__ */ __name(() => {
21
21
  }, "createTypedPathname");
22
22
  var createTypedRouter = /* @__PURE__ */ __name(() => {
23
23
  const getHrefWithSearchParams = /* @__PURE__ */ __name((href, searchParams) => {
24
- return `${href}${typedRouterCore.toSearchParamsString(searchParams ?? {}, { includeQuestionMark: true })}`;
24
+ const { pathname, remainingParams } = typedRouterCore.replaceDynamicSegments(href, searchParams);
25
+ return `${pathname}${typedRouterCore.toSearchParamsString(remainingParams, { includeQuestionMark: true })}`;
25
26
  }, "getHrefWithSearchParams");
26
27
  return () => {
27
28
  const router = navigation.useRouter();
@@ -58,7 +59,18 @@ var createTypedSearchParams = /* @__PURE__ */ __name(() => {
58
59
  }, "createTypedSearchParams");
59
60
  var createTypedLink = /* @__PURE__ */ __name(() => {
60
61
  const TypedLink = react.forwardRef((props, ref) => {
61
- return /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { ref, ...props });
62
+ const { href, ...restProps } = props;
63
+ if (typeof href === "string") {
64
+ return /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { ref, href, ...restProps });
65
+ }
66
+ const { pathname, searchParams, hash } = href;
67
+ const { pathname: replacedPathname, remainingParams } = typedRouterCore.replaceDynamicSegments(
68
+ pathname,
69
+ searchParams
70
+ );
71
+ const queryString = typedRouterCore.toSearchParamsString(remainingParams, { includeQuestionMark: true });
72
+ const finalHref = `${replacedPathname}${queryString}${hash ?? ""}`;
73
+ return /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { ref, href: finalHref, ...restProps });
62
74
  });
63
75
  TypedLink.displayName = "TypedLink";
64
76
  return TypedLink;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pathname.hooks.ts","../src/router.hooks.ts","../src/searchParams.hooks.ts","../src/TypedLink.tsx","../src/index.tsx"],"names":["usePathname","toSearchParamsString","useRouter","useSearchParams","forwardRef","jsx","Link","createAppRoutesCore","getSafely"],"mappings":"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","file":"index.js","sourcesContent":["import { usePathname } from 'next/navigation';\n\nexport const createTypedPathname = <TPathname extends string = string>() => {\n return () => {\n const pathname = usePathname();\n return pathname as TPathname;\n };\n};\n","import { SearchParams, SearchParamsForPath, toSearchParamsString } from '@hyeonqyu/typed-router-core';\nimport { useRouter } from 'next/navigation';\n\ntype NavigateOptions<TSearchParams = SearchParams> = {\n scroll?: boolean;\n searchParams?: TSearchParams;\n};\n\ntype PrefetchOptions<TSearchParams = SearchParams> = Pick<NavigateOptions<TSearchParams>, 'searchParams'>;\n\nexport const createTypedRouter = <TPathname extends string = string, TRouteTree = unknown>() => {\n const getHrefWithSearchParams = (href: TPathname, searchParams?: SearchParams) => {\n return `${href}${toSearchParamsString(searchParams ?? {}, { includeQuestionMark: true })}`;\n };\n\n return () => {\n const router = useRouter();\n\n return {\n back: router.back,\n forward: router.forward,\n refresh: router.refresh,\n push: <TPath extends TPathname>(href: TPath, options?: NavigateOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.push(getHrefWithSearchParams(href, options?.searchParams as SearchParams), options);\n },\n replace: <TPath extends TPathname>(href: TPath, options?: NavigateOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.replace(getHrefWithSearchParams(href, options?.searchParams as SearchParams), options);\n },\n prefetch: <TPath extends TPathname>(href: TPath, options?: PrefetchOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.prefetch(getHrefWithSearchParams(href, options?.searchParams as SearchParams));\n },\n };\n };\n};\n","import type { SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport { useSearchParams } from 'next/navigation';\n\ntype ParseOptions = {\n /**\n * Error handling mode\n * - 'throw': Throw error on validation failure\n * - 'default': Return schema defaults on validation failure\n * - 'raw': Return raw unparsed values on validation failure\n */\n onError?: 'throw' | 'default' | 'raw';\n};\n\nexport const createTypedSearchParams = <TPathname extends string = string, TRouteTree = unknown>() => {\n return <T extends TPathname>(_pathname: T, _options?: ParseOptions) => {\n const searchParams = useSearchParams();\n\n type ExpectedParams = SearchParamsForPath<TRouteTree, T>;\n\n // Convert URLSearchParams to plain object\n const rawParams: Record<string, unknown> = {};\n searchParams.forEach((value, key) => {\n const existing = rawParams[key];\n if (existing !== undefined) {\n // Handle multiple values for the same key\n rawParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];\n } else {\n rawParams[key] = value;\n }\n });\n\n // For now, return raw params as ExpectedParams\n // When schema is available at runtime, we would parse with Zod here\n return rawParams as ExpectedParams;\n };\n};\n","import type { SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport Link from 'next/link';\nimport type { ComponentProps, ComponentRef } from 'react';\nimport { forwardRef } from 'react';\n\ntype NextLinkProps = ComponentProps<typeof Link>;\n\nexport type TypedLinkProps<TPathname extends string = string, TRouteTree = unknown> = Omit<NextLinkProps, 'href'> & {\n href:\n | TPathname\n | (TPathname extends infer TPath\n ? {\n pathname: TPath;\n searchParams?: SearchParamsForPath<TRouteTree, TPath & string>;\n hash?: string;\n }\n : never);\n};\n\nexport const createTypedLink = <TPathname extends string = string, TRouteTree = unknown>() => {\n const TypedLink = forwardRef<ComponentRef<typeof Link>, TypedLinkProps<TPathname, TRouteTree>>((props, ref) => {\n return <Link ref={ref} {...(props as NextLinkProps)} />;\n });\n\n TypedLink.displayName = 'TypedLink';\n\n return TypedLink;\n};\n","import {\n getSafely,\n RouteNode,\n type BaseMetadata,\n type PartialRouteTree,\n type ResolvedRouteTree,\n type RoutePathname,\n type RouteTree,\n} from '@hyeonqyu/typed-router-core';\nimport { createAppRoutes as createAppRoutesCore } from '@hyeonqyu/typed-router-core/routes.utils';\nimport { createTypedPathname } from './pathname.hooks';\nimport { createTypedRouter } from './router.hooks';\nimport { createTypedSearchParams } from './searchParams.hooks';\nimport { createTypedLink } from './TypedLink';\n\nexport const createAppRoutes = <TMetadata extends BaseMetadata, TContext>() => {\n return <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n const { AppRoutesProvider, useAppRoutes, getPathnameFromNode, _types } = createAppRoutesCore<TMetadata, TContext>()(appRoutes);\n\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n type Routes = ResolvedRouteTree<TMetadata, TContext, TRouteTree>;\n\n const TypedLink = createTypedLink<Pathname, TRouteTree>();\n const useTypedRouter = createTypedRouter<Pathname, TRouteTree>();\n const useTypedPathname = createTypedPathname<Pathname>();\n const useTypedSearchParams = createTypedSearchParams<Pathname, TRouteTree>();\n\n const getCurrentRouteNode = (pathname: Pathname) => {\n return getSafely('/', appRoutes, pathname) as RouteNode<TMetadata, TContext>;\n };\n\n const useCurrentRouteNode = () => {\n const pathname = useTypedPathname();\n return getCurrentRouteNode(pathname);\n };\n\n return {\n AppRoutesProvider,\n useAppRoutes: useAppRoutes as () => Routes,\n useCurrentRouteNode,\n TypedLink,\n useTypedRouter,\n useTypedPathname,\n useTypedSearchParams,\n getCurrentRouteNode,\n getPathnameFromNode,\n appRoutes,\n _types: {\n ..._types,\n AppRoutesPathname: {} as Pathname,\n },\n };\n };\n};\n\nexport * from '@hyeonqyu/typed-router-core';\nexport type { TypedLinkProps } from './TypedLink';\n\n"]}
1
+ {"version":3,"sources":["../src/pathname.hooks.ts","../src/router.hooks.ts","../src/searchParams.hooks.ts","../src/TypedLink.tsx","../src/index.tsx"],"names":["usePathname","replaceDynamicSegments","toSearchParamsString","useRouter","useSearchParams","forwardRef","jsx","Link","createAppRoutesCore","getSafely"],"mappings":"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","file":"index.js","sourcesContent":["import { usePathname } from 'next/navigation';\n\nexport const createTypedPathname = <TPathname extends string = string>() => {\n return () => {\n const pathname = usePathname();\n return pathname as TPathname;\n };\n};\n","import { replaceDynamicSegments, SearchParams, SearchParamsForPath, toSearchParamsString } from '@hyeonqyu/typed-router-core';\nimport { useRouter } from 'next/navigation';\n\ntype NavigateOptions<TSearchParams = SearchParams> = {\n scroll?: boolean;\n searchParams?: TSearchParams;\n};\n\ntype PrefetchOptions<TSearchParams = SearchParams> = Pick<NavigateOptions<TSearchParams>, 'searchParams'>;\n\nexport const createTypedRouter = <TPathname extends string = string, TRouteTree = unknown>() => {\n const getHrefWithSearchParams = (href: TPathname, searchParams?: SearchParams) => {\n const { pathname, remainingParams } = replaceDynamicSegments(href as string, searchParams);\n return `${pathname}${toSearchParamsString(remainingParams, { includeQuestionMark: true })}`;\n };\n\n return () => {\n const router = useRouter();\n\n return {\n back: router.back,\n forward: router.forward,\n refresh: router.refresh,\n push: <TPath extends TPathname>(href: TPath, options?: NavigateOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.push(getHrefWithSearchParams(href, options?.searchParams as SearchParams), options);\n },\n replace: <TPath extends TPathname>(href: TPath, options?: NavigateOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.replace(getHrefWithSearchParams(href, options?.searchParams as SearchParams), options);\n },\n prefetch: <TPath extends TPathname>(href: TPath, options?: PrefetchOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.prefetch(getHrefWithSearchParams(href, options?.searchParams as SearchParams));\n },\n };\n };\n};\n","import type { SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport { useSearchParams } from 'next/navigation';\n\ntype ParseOptions = {\n /**\n * Error handling mode\n * - 'throw': Throw error on validation failure\n * - 'default': Return schema defaults on validation failure\n * - 'raw': Return raw unparsed values on validation failure\n */\n onError?: 'throw' | 'default' | 'raw';\n};\n\nexport const createTypedSearchParams = <TPathname extends string = string, TRouteTree = unknown>() => {\n return <T extends TPathname>(_pathname: T, _options?: ParseOptions) => {\n const searchParams = useSearchParams();\n\n type ExpectedParams = SearchParamsForPath<TRouteTree, T>;\n\n // Convert URLSearchParams to plain object\n const rawParams: Record<string, unknown> = {};\n searchParams.forEach((value, key) => {\n const existing = rawParams[key];\n if (existing !== undefined) {\n // Handle multiple values for the same key\n rawParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];\n } else {\n rawParams[key] = value;\n }\n });\n\n // For now, return raw params as ExpectedParams\n // When schema is available at runtime, we would parse with Zod here\n return rawParams as ExpectedParams;\n };\n};\n","import { replaceDynamicSegments, toSearchParamsString, type SearchParams, type SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport Link from 'next/link';\nimport type { ComponentProps, ComponentRef } from 'react';\nimport { forwardRef } from 'react';\n\ntype NextLinkProps = ComponentProps<typeof Link>;\n\nexport type TypedLinkProps<TPathname extends string = string, TRouteTree = unknown> = Omit<NextLinkProps, 'href'> & {\n href:\n | TPathname\n | (TPathname extends infer TPath\n ? {\n pathname: TPath;\n searchParams?: SearchParamsForPath<TRouteTree, TPath & string>;\n hash?: string;\n }\n : never);\n};\n\nexport const createTypedLink = <TPathname extends string = string, TRouteTree = unknown>() => {\n const TypedLink = forwardRef<ComponentRef<typeof Link>, TypedLinkProps<TPathname, TRouteTree>>((props, ref) => {\n const { href, ...restProps } = props;\n\n if (typeof href === 'string') {\n return <Link ref={ref} href={href} {...restProps} />;\n }\n\n const { pathname, searchParams, hash } = href;\n\n const { pathname: replacedPathname, remainingParams } = replaceDynamicSegments(\n pathname as string,\n searchParams as SearchParams,\n );\n\n const queryString = toSearchParamsString(remainingParams, { includeQuestionMark: true });\n const finalHref = `${replacedPathname}${queryString}${hash ?? ''}`;\n\n return <Link ref={ref} href={finalHref} {...restProps} />;\n });\n\n TypedLink.displayName = 'TypedLink';\n\n return TypedLink;\n};\n","import {\n getSafely,\n RouteNode,\n type BaseMetadata,\n type PartialRouteTree,\n type ResolvedRouteTree,\n type RoutePathname,\n type RouteTree,\n} from '@hyeonqyu/typed-router-core';\nimport { createAppRoutes as createAppRoutesCore } from '@hyeonqyu/typed-router-core/routes.utils';\nimport { createTypedPathname } from './pathname.hooks';\nimport { createTypedRouter } from './router.hooks';\nimport { createTypedSearchParams } from './searchParams.hooks';\nimport { createTypedLink } from './TypedLink';\n\nexport const createAppRoutes = <TMetadata extends BaseMetadata, TContext>() => {\n return <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n const { AppRoutesProvider, useAppRoutes, getPathnameFromNode, _types } = createAppRoutesCore<TMetadata, TContext>()(appRoutes);\n\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n type Routes = ResolvedRouteTree<TMetadata, TContext, TRouteTree>;\n\n const TypedLink = createTypedLink<Pathname, TRouteTree>();\n const useTypedRouter = createTypedRouter<Pathname, TRouteTree>();\n const useTypedPathname = createTypedPathname<Pathname>();\n const useTypedSearchParams = createTypedSearchParams<Pathname, TRouteTree>();\n\n const getCurrentRouteNode = (pathname: Pathname) => {\n return getSafely('/', appRoutes, pathname) as RouteNode<TMetadata, TContext>;\n };\n\n const useCurrentRouteNode = () => {\n const pathname = useTypedPathname();\n return getCurrentRouteNode(pathname);\n };\n\n return {\n AppRoutesProvider,\n useAppRoutes: useAppRoutes as () => Routes,\n useCurrentRouteNode,\n TypedLink,\n useTypedRouter,\n useTypedPathname,\n useTypedSearchParams,\n getCurrentRouteNode,\n getPathnameFromNode,\n appRoutes,\n _types: {\n ..._types,\n AppRoutesPathname: {} as Pathname,\n },\n };\n };\n};\n\nexport * from '@hyeonqyu/typed-router-core';\nexport type { TypedLinkProps } from './TypedLink';\n\n"]}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { getSafely, toSearchParamsString } from '@hyeonqyu/typed-router-core';
1
+ import { getSafely, replaceDynamicSegments, toSearchParamsString } from '@hyeonqyu/typed-router-core';
2
2
  export * from '@hyeonqyu/typed-router-core';
3
3
  import { createAppRoutes as createAppRoutes$1 } from '@hyeonqyu/typed-router-core/routes.utils';
4
4
  import { useRouter, usePathname, useSearchParams } from 'next/navigation';
@@ -16,7 +16,8 @@ var createTypedPathname = /* @__PURE__ */ __name(() => {
16
16
  }, "createTypedPathname");
17
17
  var createTypedRouter = /* @__PURE__ */ __name(() => {
18
18
  const getHrefWithSearchParams = /* @__PURE__ */ __name((href, searchParams) => {
19
- return `${href}${toSearchParamsString(searchParams ?? {}, { includeQuestionMark: true })}`;
19
+ const { pathname, remainingParams } = replaceDynamicSegments(href, searchParams);
20
+ return `${pathname}${toSearchParamsString(remainingParams, { includeQuestionMark: true })}`;
20
21
  }, "getHrefWithSearchParams");
21
22
  return () => {
22
23
  const router = useRouter();
@@ -53,7 +54,18 @@ var createTypedSearchParams = /* @__PURE__ */ __name(() => {
53
54
  }, "createTypedSearchParams");
54
55
  var createTypedLink = /* @__PURE__ */ __name(() => {
55
56
  const TypedLink = forwardRef((props, ref) => {
56
- return /* @__PURE__ */ jsx(Link, { ref, ...props });
57
+ const { href, ...restProps } = props;
58
+ if (typeof href === "string") {
59
+ return /* @__PURE__ */ jsx(Link, { ref, href, ...restProps });
60
+ }
61
+ const { pathname, searchParams, hash } = href;
62
+ const { pathname: replacedPathname, remainingParams } = replaceDynamicSegments(
63
+ pathname,
64
+ searchParams
65
+ );
66
+ const queryString = toSearchParamsString(remainingParams, { includeQuestionMark: true });
67
+ const finalHref = `${replacedPathname}${queryString}${hash ?? ""}`;
68
+ return /* @__PURE__ */ jsx(Link, { ref, href: finalHref, ...restProps });
57
69
  });
58
70
  TypedLink.displayName = "TypedLink";
59
71
  return TypedLink;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pathname.hooks.ts","../src/router.hooks.ts","../src/searchParams.hooks.ts","../src/TypedLink.tsx","../src/index.tsx"],"names":["createAppRoutesCore"],"mappings":"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","file":"index.mjs","sourcesContent":["import { usePathname } from 'next/navigation';\n\nexport const createTypedPathname = <TPathname extends string = string>() => {\n return () => {\n const pathname = usePathname();\n return pathname as TPathname;\n };\n};\n","import { SearchParams, SearchParamsForPath, toSearchParamsString } from '@hyeonqyu/typed-router-core';\nimport { useRouter } from 'next/navigation';\n\ntype NavigateOptions<TSearchParams = SearchParams> = {\n scroll?: boolean;\n searchParams?: TSearchParams;\n};\n\ntype PrefetchOptions<TSearchParams = SearchParams> = Pick<NavigateOptions<TSearchParams>, 'searchParams'>;\n\nexport const createTypedRouter = <TPathname extends string = string, TRouteTree = unknown>() => {\n const getHrefWithSearchParams = (href: TPathname, searchParams?: SearchParams) => {\n return `${href}${toSearchParamsString(searchParams ?? {}, { includeQuestionMark: true })}`;\n };\n\n return () => {\n const router = useRouter();\n\n return {\n back: router.back,\n forward: router.forward,\n refresh: router.refresh,\n push: <TPath extends TPathname>(href: TPath, options?: NavigateOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.push(getHrefWithSearchParams(href, options?.searchParams as SearchParams), options);\n },\n replace: <TPath extends TPathname>(href: TPath, options?: NavigateOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.replace(getHrefWithSearchParams(href, options?.searchParams as SearchParams), options);\n },\n prefetch: <TPath extends TPathname>(href: TPath, options?: PrefetchOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.prefetch(getHrefWithSearchParams(href, options?.searchParams as SearchParams));\n },\n };\n };\n};\n","import type { SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport { useSearchParams } from 'next/navigation';\n\ntype ParseOptions = {\n /**\n * Error handling mode\n * - 'throw': Throw error on validation failure\n * - 'default': Return schema defaults on validation failure\n * - 'raw': Return raw unparsed values on validation failure\n */\n onError?: 'throw' | 'default' | 'raw';\n};\n\nexport const createTypedSearchParams = <TPathname extends string = string, TRouteTree = unknown>() => {\n return <T extends TPathname>(_pathname: T, _options?: ParseOptions) => {\n const searchParams = useSearchParams();\n\n type ExpectedParams = SearchParamsForPath<TRouteTree, T>;\n\n // Convert URLSearchParams to plain object\n const rawParams: Record<string, unknown> = {};\n searchParams.forEach((value, key) => {\n const existing = rawParams[key];\n if (existing !== undefined) {\n // Handle multiple values for the same key\n rawParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];\n } else {\n rawParams[key] = value;\n }\n });\n\n // For now, return raw params as ExpectedParams\n // When schema is available at runtime, we would parse with Zod here\n return rawParams as ExpectedParams;\n };\n};\n","import type { SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport Link from 'next/link';\nimport type { ComponentProps, ComponentRef } from 'react';\nimport { forwardRef } from 'react';\n\ntype NextLinkProps = ComponentProps<typeof Link>;\n\nexport type TypedLinkProps<TPathname extends string = string, TRouteTree = unknown> = Omit<NextLinkProps, 'href'> & {\n href:\n | TPathname\n | (TPathname extends infer TPath\n ? {\n pathname: TPath;\n searchParams?: SearchParamsForPath<TRouteTree, TPath & string>;\n hash?: string;\n }\n : never);\n};\n\nexport const createTypedLink = <TPathname extends string = string, TRouteTree = unknown>() => {\n const TypedLink = forwardRef<ComponentRef<typeof Link>, TypedLinkProps<TPathname, TRouteTree>>((props, ref) => {\n return <Link ref={ref} {...(props as NextLinkProps)} />;\n });\n\n TypedLink.displayName = 'TypedLink';\n\n return TypedLink;\n};\n","import {\n getSafely,\n RouteNode,\n type BaseMetadata,\n type PartialRouteTree,\n type ResolvedRouteTree,\n type RoutePathname,\n type RouteTree,\n} from '@hyeonqyu/typed-router-core';\nimport { createAppRoutes as createAppRoutesCore } from '@hyeonqyu/typed-router-core/routes.utils';\nimport { createTypedPathname } from './pathname.hooks';\nimport { createTypedRouter } from './router.hooks';\nimport { createTypedSearchParams } from './searchParams.hooks';\nimport { createTypedLink } from './TypedLink';\n\nexport const createAppRoutes = <TMetadata extends BaseMetadata, TContext>() => {\n return <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n const { AppRoutesProvider, useAppRoutes, getPathnameFromNode, _types } = createAppRoutesCore<TMetadata, TContext>()(appRoutes);\n\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n type Routes = ResolvedRouteTree<TMetadata, TContext, TRouteTree>;\n\n const TypedLink = createTypedLink<Pathname, TRouteTree>();\n const useTypedRouter = createTypedRouter<Pathname, TRouteTree>();\n const useTypedPathname = createTypedPathname<Pathname>();\n const useTypedSearchParams = createTypedSearchParams<Pathname, TRouteTree>();\n\n const getCurrentRouteNode = (pathname: Pathname) => {\n return getSafely('/', appRoutes, pathname) as RouteNode<TMetadata, TContext>;\n };\n\n const useCurrentRouteNode = () => {\n const pathname = useTypedPathname();\n return getCurrentRouteNode(pathname);\n };\n\n return {\n AppRoutesProvider,\n useAppRoutes: useAppRoutes as () => Routes,\n useCurrentRouteNode,\n TypedLink,\n useTypedRouter,\n useTypedPathname,\n useTypedSearchParams,\n getCurrentRouteNode,\n getPathnameFromNode,\n appRoutes,\n _types: {\n ..._types,\n AppRoutesPathname: {} as Pathname,\n },\n };\n };\n};\n\nexport * from '@hyeonqyu/typed-router-core';\nexport type { TypedLinkProps } from './TypedLink';\n\n"]}
1
+ {"version":3,"sources":["../src/pathname.hooks.ts","../src/router.hooks.ts","../src/searchParams.hooks.ts","../src/TypedLink.tsx","../src/index.tsx"],"names":["replaceDynamicSegments","toSearchParamsString","createAppRoutesCore"],"mappings":"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","file":"index.mjs","sourcesContent":["import { usePathname } from 'next/navigation';\n\nexport const createTypedPathname = <TPathname extends string = string>() => {\n return () => {\n const pathname = usePathname();\n return pathname as TPathname;\n };\n};\n","import { replaceDynamicSegments, SearchParams, SearchParamsForPath, toSearchParamsString } from '@hyeonqyu/typed-router-core';\nimport { useRouter } from 'next/navigation';\n\ntype NavigateOptions<TSearchParams = SearchParams> = {\n scroll?: boolean;\n searchParams?: TSearchParams;\n};\n\ntype PrefetchOptions<TSearchParams = SearchParams> = Pick<NavigateOptions<TSearchParams>, 'searchParams'>;\n\nexport const createTypedRouter = <TPathname extends string = string, TRouteTree = unknown>() => {\n const getHrefWithSearchParams = (href: TPathname, searchParams?: SearchParams) => {\n const { pathname, remainingParams } = replaceDynamicSegments(href as string, searchParams);\n return `${pathname}${toSearchParamsString(remainingParams, { includeQuestionMark: true })}`;\n };\n\n return () => {\n const router = useRouter();\n\n return {\n back: router.back,\n forward: router.forward,\n refresh: router.refresh,\n push: <TPath extends TPathname>(href: TPath, options?: NavigateOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.push(getHrefWithSearchParams(href, options?.searchParams as SearchParams), options);\n },\n replace: <TPath extends TPathname>(href: TPath, options?: NavigateOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.replace(getHrefWithSearchParams(href, options?.searchParams as SearchParams), options);\n },\n prefetch: <TPath extends TPathname>(href: TPath, options?: PrefetchOptions<SearchParamsForPath<TRouteTree, TPath>>) => {\n return router.prefetch(getHrefWithSearchParams(href, options?.searchParams as SearchParams));\n },\n };\n };\n};\n","import type { SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport { useSearchParams } from 'next/navigation';\n\ntype ParseOptions = {\n /**\n * Error handling mode\n * - 'throw': Throw error on validation failure\n * - 'default': Return schema defaults on validation failure\n * - 'raw': Return raw unparsed values on validation failure\n */\n onError?: 'throw' | 'default' | 'raw';\n};\n\nexport const createTypedSearchParams = <TPathname extends string = string, TRouteTree = unknown>() => {\n return <T extends TPathname>(_pathname: T, _options?: ParseOptions) => {\n const searchParams = useSearchParams();\n\n type ExpectedParams = SearchParamsForPath<TRouteTree, T>;\n\n // Convert URLSearchParams to plain object\n const rawParams: Record<string, unknown> = {};\n searchParams.forEach((value, key) => {\n const existing = rawParams[key];\n if (existing !== undefined) {\n // Handle multiple values for the same key\n rawParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];\n } else {\n rawParams[key] = value;\n }\n });\n\n // For now, return raw params as ExpectedParams\n // When schema is available at runtime, we would parse with Zod here\n return rawParams as ExpectedParams;\n };\n};\n","import { replaceDynamicSegments, toSearchParamsString, type SearchParams, type SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport Link from 'next/link';\nimport type { ComponentProps, ComponentRef } from 'react';\nimport { forwardRef } from 'react';\n\ntype NextLinkProps = ComponentProps<typeof Link>;\n\nexport type TypedLinkProps<TPathname extends string = string, TRouteTree = unknown> = Omit<NextLinkProps, 'href'> & {\n href:\n | TPathname\n | (TPathname extends infer TPath\n ? {\n pathname: TPath;\n searchParams?: SearchParamsForPath<TRouteTree, TPath & string>;\n hash?: string;\n }\n : never);\n};\n\nexport const createTypedLink = <TPathname extends string = string, TRouteTree = unknown>() => {\n const TypedLink = forwardRef<ComponentRef<typeof Link>, TypedLinkProps<TPathname, TRouteTree>>((props, ref) => {\n const { href, ...restProps } = props;\n\n if (typeof href === 'string') {\n return <Link ref={ref} href={href} {...restProps} />;\n }\n\n const { pathname, searchParams, hash } = href;\n\n const { pathname: replacedPathname, remainingParams } = replaceDynamicSegments(\n pathname as string,\n searchParams as SearchParams,\n );\n\n const queryString = toSearchParamsString(remainingParams, { includeQuestionMark: true });\n const finalHref = `${replacedPathname}${queryString}${hash ?? ''}`;\n\n return <Link ref={ref} href={finalHref} {...restProps} />;\n });\n\n TypedLink.displayName = 'TypedLink';\n\n return TypedLink;\n};\n","import {\n getSafely,\n RouteNode,\n type BaseMetadata,\n type PartialRouteTree,\n type ResolvedRouteTree,\n type RoutePathname,\n type RouteTree,\n} from '@hyeonqyu/typed-router-core';\nimport { createAppRoutes as createAppRoutesCore } from '@hyeonqyu/typed-router-core/routes.utils';\nimport { createTypedPathname } from './pathname.hooks';\nimport { createTypedRouter } from './router.hooks';\nimport { createTypedSearchParams } from './searchParams.hooks';\nimport { createTypedLink } from './TypedLink';\n\nexport const createAppRoutes = <TMetadata extends BaseMetadata, TContext>() => {\n return <TRouteTree extends PartialRouteTree<TMetadata, TContext>>(appRoutes: TRouteTree & RouteTree<TMetadata, TContext, TRouteTree>) => {\n const { AppRoutesProvider, useAppRoutes, getPathnameFromNode, _types } = createAppRoutesCore<TMetadata, TContext>()(appRoutes);\n\n type Pathname = RoutePathname<TMetadata, TContext, TRouteTree>;\n type Routes = ResolvedRouteTree<TMetadata, TContext, TRouteTree>;\n\n const TypedLink = createTypedLink<Pathname, TRouteTree>();\n const useTypedRouter = createTypedRouter<Pathname, TRouteTree>();\n const useTypedPathname = createTypedPathname<Pathname>();\n const useTypedSearchParams = createTypedSearchParams<Pathname, TRouteTree>();\n\n const getCurrentRouteNode = (pathname: Pathname) => {\n return getSafely('/', appRoutes, pathname) as RouteNode<TMetadata, TContext>;\n };\n\n const useCurrentRouteNode = () => {\n const pathname = useTypedPathname();\n return getCurrentRouteNode(pathname);\n };\n\n return {\n AppRoutesProvider,\n useAppRoutes: useAppRoutes as () => Routes,\n useCurrentRouteNode,\n TypedLink,\n useTypedRouter,\n useTypedPathname,\n useTypedSearchParams,\n getCurrentRouteNode,\n getPathnameFromNode,\n appRoutes,\n _types: {\n ..._types,\n AppRoutesPathname: {} as Pathname,\n },\n };\n };\n};\n\nexport * from '@hyeonqyu/typed-router-core';\nexport type { TypedLinkProps } from './TypedLink';\n\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyeonqyu/typed-router-next",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "Type-safe IA-first routing for Next.js",
5
5
  "author": "hyeonQyu <dhk0561@naver.com>",
6
6
  "license": "MIT",
@@ -50,7 +50,7 @@
50
50
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
51
51
  },
52
52
  "dependencies": {
53
- "@hyeonqyu/typed-router-core": "^1.5.1"
53
+ "@hyeonqyu/typed-router-core": "^1.5.3"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/react": "^19.2.5",