@hyeonqyu/typed-router-next 1.5.3 → 1.5.4

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.d.mts CHANGED
@@ -40,7 +40,7 @@ declare const createAppRoutes: <TMetadata extends BaseMetadata, TContext>() => <
40
40
  } | undefined) => void;
41
41
  };
42
42
  useTypedPathname: () => RoutePathname<TMetadata, TContext, TRouteTree>;
43
- useTypedSearchParams: <T extends RoutePathname<TMetadata, TContext, TRouteTree>>(_pathname: T, _options?: {
43
+ useTypedSearchParams: <T extends RoutePathname<TMetadata, TContext, TRouteTree>>(pathname: T, _options?: {
44
44
  onError?: "throw" | "default" | "raw";
45
45
  }) => _hyeonqyu_typed_router_core.ExtractSearchParams<_hyeonqyu_typed_router_core.GetRouteNode<TRouteTree, T>>;
46
46
  getCurrentRouteNode: (pathname: RoutePathname<TMetadata, TContext, TRouteTree>) => RouteNode<TMetadata, TContext>;
package/dist/index.d.ts CHANGED
@@ -40,7 +40,7 @@ declare const createAppRoutes: <TMetadata extends BaseMetadata, TContext>() => <
40
40
  } | undefined) => void;
41
41
  };
42
42
  useTypedPathname: () => RoutePathname<TMetadata, TContext, TRouteTree>;
43
- useTypedSearchParams: <T extends RoutePathname<TMetadata, TContext, TRouteTree>>(_pathname: T, _options?: {
43
+ useTypedSearchParams: <T extends RoutePathname<TMetadata, TContext, TRouteTree>>(pathname: T, _options?: {
44
44
  onError?: "throw" | "default" | "raw";
45
45
  }) => _hyeonqyu_typed_router_core.ExtractSearchParams<_hyeonqyu_typed_router_core.GetRouteNode<TRouteTree, T>>;
46
46
  getCurrentRouteNode: (pathname: RoutePathname<TMetadata, TContext, TRouteTree>) => RouteNode<TMetadata, TContext>;
package/dist/index.js CHANGED
@@ -43,18 +43,26 @@ var createTypedRouter = /* @__PURE__ */ __name(() => {
43
43
  };
44
44
  }, "createTypedRouter");
45
45
  var createTypedSearchParams = /* @__PURE__ */ __name(() => {
46
- return (_pathname, _options) => {
46
+ return (pathname, _options) => {
47
47
  const searchParams = navigation.useSearchParams();
48
- const rawParams = {};
48
+ const pathParams = navigation.useParams();
49
+ const queryParams = {};
49
50
  searchParams.forEach((value, key) => {
50
- const existing = rawParams[key];
51
+ const existing = queryParams[key];
51
52
  if (existing !== void 0) {
52
- rawParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
53
+ queryParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
53
54
  } else {
54
- rawParams[key] = value;
55
+ queryParams[key] = value;
55
56
  }
56
57
  });
57
- return rawParams;
58
+ const dynamicKeys = typedRouterCore.extractDynamicSegmentKeys(pathname);
59
+ const routeParams = {};
60
+ dynamicKeys.forEach((key) => {
61
+ if (pathParams[key] !== void 0) {
62
+ routeParams[key] = pathParams[key];
63
+ }
64
+ });
65
+ return { ...queryParams, ...routeParams };
58
66
  };
59
67
  }, "createTypedSearchParams");
60
68
  var createTypedLink = /* @__PURE__ */ __name(() => {
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","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"]}
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","useParams","extractDynamicSegmentKeys","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 { extractDynamicSegmentKeys, type SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport { useParams, 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 const pathParams = useParams();\n\n type ExpectedParams = SearchParamsForPath<TRouteTree, T>;\n\n const queryParams: Record<string, unknown> = {};\n searchParams.forEach((value, key) => {\n const existing = queryParams[key];\n if (existing !== undefined) {\n // Handle multiple values for the same key\n queryParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];\n } else {\n queryParams[key] = value;\n }\n });\n\n const dynamicKeys = extractDynamicSegmentKeys(pathname as string);\n\n const routeParams: Record<string, unknown> = {};\n dynamicKeys.forEach((key) => {\n if (pathParams[key] !== undefined) {\n routeParams[key] = pathParams[key];\n }\n });\n\n return { ...queryParams, ...routeParams } 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,7 +1,7 @@
1
- import { getSafely, replaceDynamicSegments, toSearchParamsString } from '@hyeonqyu/typed-router-core';
1
+ import { getSafely, replaceDynamicSegments, toSearchParamsString, extractDynamicSegmentKeys } 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
- import { useRouter, usePathname, useSearchParams } from 'next/navigation';
4
+ import { useRouter, usePathname, useSearchParams, useParams } from 'next/navigation';
5
5
  import Link from 'next/link';
6
6
  import { forwardRef } from 'react';
7
7
  import { jsx } from 'react/jsx-runtime';
@@ -38,18 +38,26 @@ var createTypedRouter = /* @__PURE__ */ __name(() => {
38
38
  };
39
39
  }, "createTypedRouter");
40
40
  var createTypedSearchParams = /* @__PURE__ */ __name(() => {
41
- return (_pathname, _options) => {
41
+ return (pathname, _options) => {
42
42
  const searchParams = useSearchParams();
43
- const rawParams = {};
43
+ const pathParams = useParams();
44
+ const queryParams = {};
44
45
  searchParams.forEach((value, key) => {
45
- const existing = rawParams[key];
46
+ const existing = queryParams[key];
46
47
  if (existing !== void 0) {
47
- rawParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
48
+ queryParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
48
49
  } else {
49
- rawParams[key] = value;
50
+ queryParams[key] = value;
50
51
  }
51
52
  });
52
- return rawParams;
53
+ const dynamicKeys = extractDynamicSegmentKeys(pathname);
54
+ const routeParams = {};
55
+ dynamicKeys.forEach((key) => {
56
+ if (pathParams[key] !== void 0) {
57
+ routeParams[key] = pathParams[key];
58
+ }
59
+ });
60
+ return { ...queryParams, ...routeParams };
53
61
  };
54
62
  }, "createTypedSearchParams");
55
63
  var createTypedLink = /* @__PURE__ */ __name(() => {
@@ -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":["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"]}
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 { extractDynamicSegmentKeys, type SearchParamsForPath } from '@hyeonqyu/typed-router-core';\nimport { useParams, 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 const pathParams = useParams();\n\n type ExpectedParams = SearchParamsForPath<TRouteTree, T>;\n\n const queryParams: Record<string, unknown> = {};\n searchParams.forEach((value, key) => {\n const existing = queryParams[key];\n if (existing !== undefined) {\n // Handle multiple values for the same key\n queryParams[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];\n } else {\n queryParams[key] = value;\n }\n });\n\n const dynamicKeys = extractDynamicSegmentKeys(pathname as string);\n\n const routeParams: Record<string, unknown> = {};\n dynamicKeys.forEach((key) => {\n if (pathParams[key] !== undefined) {\n routeParams[key] = pathParams[key];\n }\n });\n\n return { ...queryParams, ...routeParams } 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.3",
3
+ "version": "1.5.4",
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.3"
53
+ "@hyeonqyu/typed-router-core": "^1.5.4"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/react": "^19.2.5",