jotai-iten 0.4.2 → 0.5.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 CHANGED
@@ -67,9 +67,7 @@ The current route stays mounted until the target route is ready. Loader failures
67
67
  // router.ts
68
68
  import {
69
69
  createRouter,
70
- createRoute,
71
70
  defineRoutes,
72
- type InferRoutes,
73
71
  route,
74
72
  } from 'jotai-iten'
75
73
 
@@ -79,12 +77,9 @@ const routes = defineRoutes({
79
77
  settings: route(),
80
78
  })
81
79
 
82
- type Routes = InferRoutes<typeof routes>
83
-
84
- export const toRoute = createRoute(routes)
85
-
86
- export const router = createRouter<Routes, unknown>({
87
- initial: toRoute({ name: 'home' }),
80
+ export const router = createRouter({
81
+ routes,
82
+ initial: { name: 'home' },
88
83
  })
89
84
 
90
85
  export const {
@@ -100,7 +95,7 @@ export const {
100
95
 
101
96
  ```tsx
102
97
  // App.tsx
103
- import { Navigate, Route, Switch, toRoute } from './router'
98
+ import { Navigate, Route, Switch, router } from './router'
104
99
 
105
100
  export function App() {
106
101
  return (
@@ -108,7 +103,7 @@ export function App() {
108
103
  <Route name="home">{() => <HomeView />}</Route>
109
104
  <Route name="detail">{({ id }) => <DetailView id={id} />}</Route>
110
105
  <Route name="settings">{() => <SettingsView />}</Route>
111
- <Navigate to={toRoute({ name: 'home' })} />
106
+ <Navigate to={router.to({ name: 'home' })} />
112
107
  </Switch>
113
108
  )
114
109
  }
@@ -125,9 +120,7 @@ Use `jotai-iten/headless` when you want atoms and hooks but do not need `Route`,
125
120
  ```ts
126
121
  import {
127
122
  createHeadlessRouter,
128
- createRoute,
129
123
  defineRoutes,
130
- type InferRoutes,
131
124
  route,
132
125
  } from 'jotai-iten/headless'
133
126
 
@@ -136,12 +129,9 @@ const routes = defineRoutes({
136
129
  detail: route<{ id: string }>(),
137
130
  })
138
131
 
139
- type Routes = InferRoutes<typeof routes>
140
-
141
- export const toRoute = createRoute(routes)
142
-
143
- export const router = createHeadlessRouter<Routes, unknown>({
144
- initial: toRoute({ name: 'home' }),
132
+ export const router = createHeadlessRouter({
133
+ routes,
134
+ initial: { name: 'home' },
145
135
  })
146
136
 
147
137
  export const { atoms, useNavigate, useRoute, useCurrentRoute } = router
@@ -153,11 +143,18 @@ export const { atoms, useNavigate, useRoute, useCurrentRoute } = router
153
143
 
154
144
  ## Creating Routes
155
145
 
156
- Use `defineRoutes` plus `createRoute` instead of hand-written object literals. The `routes` object is the runtime source of truth for names, and `InferRoutes` derives the router type.
146
+ Use `defineRoutes` with `createRouter` or `createHeadlessRouter` for inferred route maps. The returned router exposes `router.to(...)` for typed route targets.
157
147
 
158
148
  ```ts
159
- const toRoute = createRoute(routes)
149
+ router.to({ name: 'home' })
150
+ router.to({ name: 'detail', params: { id: '42' } })
151
+ ```
152
+
153
+ Use `InferRoutes` plus `createRoute` when route types or factories need to live independently from a router instance.
160
154
 
155
+ ```ts
156
+ type Routes = InferRoutes<typeof routes>
157
+ const toRoute = createRoute(routes)
161
158
  toRoute({ name: 'home' })
162
159
  toRoute({ name: 'detail', params: { id: '42' } })
163
160
  ```
@@ -171,8 +168,9 @@ For no-param routes, call `route()` without a type argument. Avoid `route<Record
171
168
  Loaders run before the target route commits.
172
169
 
173
170
  ```ts
174
- export const router = createRouter<Routes, unknown>({
175
- initial: toRoute({ name: 'home' }),
171
+ export const router = createRouter({
172
+ routes,
173
+ initial: { name: 'home' },
176
174
  queryClient,
177
175
  routeConfig: {
178
176
  detail: {
@@ -205,12 +203,13 @@ The `queryClient` only needs an `ensureQueryData` method. TanStack Query works,
205
203
  ```ts
206
204
  import { authAtom } from './atoms'
207
205
 
208
- const router = createRouter<Routes, unknown>({
209
- initial: toRoute({ name: 'home' }),
206
+ const router = createRouter({
207
+ routes,
208
+ initial: { name: 'home' },
210
209
  routeConfig: {
211
210
  detail: {
212
- beforeLoad: ({ get }) => {
213
- if (!get(authAtom).userId) return toRoute({ name: 'home' })
211
+ beforeLoad: ({ get, to }) => {
212
+ if (!get(authAtom).userId) return to({ name: 'home' })
214
213
  },
215
214
  loader: async ({ params, queryClient }) => {
216
215
  await queryClient.ensureQueryData(detailQuery(params.id))
@@ -229,8 +228,9 @@ Redirect loops are capped by `iten-core` and become retryable errors instead of
229
228
  Use `context` to compute shared values once per navigation. It can be a static object or a function that reads atoms.
230
229
 
231
230
  ```ts
232
- const router = createRouter<Routes, { userId: string | null }>({
233
- initial: toRoute({ name: 'home' }),
231
+ const router = createRouter({
232
+ routes,
233
+ initial: { name: 'home' },
234
234
  context: ({ get }) => ({ userId: get(authAtom).userId }),
235
235
  routeConfig: {
236
236
  detail: {
@@ -284,7 +284,7 @@ Renders the first matching child. Use `<Navigate>` as a fallback.
284
284
  <Switch>
285
285
  <Route name="home">{() => <HomeView />}</Route>
286
286
  <Route name="detail">{({ id }) => <DetailView id={id} />}</Route>
287
- <Navigate to={toRoute({ name: 'home' })} />
287
+ <Navigate to={router.to({ name: 'home' })} />
288
288
  </Switch>
289
289
  ```
290
290
 
@@ -293,7 +293,7 @@ Renders the first matching child. Use `<Navigate>` as a fallback.
293
293
  Typed navigation with route-specific loading state.
294
294
 
295
295
  ```tsx
296
- <Link to={toRoute({ name: 'detail', params: { id: item.id } })}>
296
+ <Link to={router.to({ name: 'detail', params: { id: item.id } })}>
297
297
  {({ isLoading }) => (isLoading ? 'Loading...' : 'Open')}
298
298
  </Link>
299
299
  ```
@@ -303,7 +303,7 @@ Typed navigation with route-specific loading state.
303
303
  Redirects on mount.
304
304
 
305
305
  ```tsx
306
- {!isAuthenticated && <Navigate to={toRoute({ name: 'home' })} />}
306
+ {!isAuthenticated && <Navigate to={router.to({ name: 'home' })} />}
307
307
  ```
308
308
 
309
309
  ---
@@ -333,7 +333,7 @@ function Header() {
333
333
  <button
334
334
  type="button"
335
335
  aria-current={isActive ? 'page' : undefined}
336
- onClick={() => void navigate({ target: toRoute({ name: 'settings' }) })}
336
+ onClick={() => void navigate({ target: router.to({ name: 'settings' }) })}
337
337
  >
338
338
  {isLoading ? 'Loading...' : 'Settings'}
339
339
  </button>
@@ -428,11 +428,8 @@ const modals = defineRoutes({
428
428
  imagePicker: route<{ onSelect: (uri: string) => void }>(),
429
429
  })
430
430
 
431
- type Modals = InferRoutes<typeof modals>
432
-
433
- export const modalRoute = createRoute(modals)
434
-
435
- export const modalRouter = createRouter<Modals, unknown>({
431
+ export const modalRouter = createRouter({
432
+ routes: modals,
436
433
  initial: null,
437
434
  })
438
435
  ```
@@ -490,7 +487,7 @@ const urlSync = createUrlSync<Routes>({
490
487
  router,
491
488
  parse: ({ url }) => {
492
489
  const id = url.searchParams.get('id')
493
- return id ? toRoute({ name: 'detail', params: { id } }) : toRoute({ name: 'home' })
490
+ return id ? router.to({ name: 'detail', params: { id } }) : router.to({ name: 'home' })
494
491
  },
495
492
  format: ({ route, url }) => {
496
493
  const next = new URL(url)
@@ -4,7 +4,7 @@ import { LinkComponent } from "./components/Link.cjs";
4
4
  import { NavigateComponent } from "./components/Navigate.cjs";
5
5
  import { RouteComponent } from "./components/Route.cjs";
6
6
  import { SwitchComponent } from "./components/Switch.cjs";
7
- import { RouteMapDef } from "iten-core";
7
+ import { InferRoutes, RouteDefinitions, RouteMapDef } from "iten-core";
8
8
 
9
9
  //#region src/create-router.d.ts
10
10
  type JotaiRouter<M extends RouteMapDef, _Ctx = unknown> = HeadlessJotaiRouter<M> & {
@@ -13,6 +13,9 @@ type JotaiRouter<M extends RouteMapDef, _Ctx = unknown> = HeadlessJotaiRouter<M>
13
13
  Link: LinkComponent<M>;
14
14
  Navigate: NavigateComponent<M>;
15
15
  };
16
+ declare function createRouter<const Defs extends RouteDefinitions, Ctx = unknown>(config: JotaiRouterConfig<InferRoutes<Defs>, Ctx> & {
17
+ routes: Defs;
18
+ }): JotaiRouter<InferRoutes<Defs>, Ctx>;
16
19
  declare function createRouter<M extends RouteMapDef, Ctx = unknown>(config: JotaiRouterConfig<M, Ctx>): JotaiRouter<M, Ctx>;
17
20
  //#endregion
18
21
  export { JotaiRouter, createRouter };
@@ -4,7 +4,7 @@ import { LinkComponent } from "./components/Link.js";
4
4
  import { NavigateComponent } from "./components/Navigate.js";
5
5
  import { RouteComponent } from "./components/Route.js";
6
6
  import { SwitchComponent } from "./components/Switch.js";
7
- import { RouteMapDef } from "iten-core";
7
+ import { InferRoutes, RouteDefinitions, RouteMapDef } from "iten-core";
8
8
 
9
9
  //#region src/create-router.d.ts
10
10
  type JotaiRouter<M extends RouteMapDef, _Ctx = unknown> = HeadlessJotaiRouter<M> & {
@@ -13,6 +13,9 @@ type JotaiRouter<M extends RouteMapDef, _Ctx = unknown> = HeadlessJotaiRouter<M>
13
13
  Link: LinkComponent<M>;
14
14
  Navigate: NavigateComponent<M>;
15
15
  };
16
+ declare function createRouter<const Defs extends RouteDefinitions, Ctx = unknown>(config: JotaiRouterConfig<InferRoutes<Defs>, Ctx> & {
17
+ routes: Defs;
18
+ }): JotaiRouter<InferRoutes<Defs>, Ctx>;
16
19
  declare function createRouter<M extends RouteMapDef, Ctx = unknown>(config: JotaiRouterConfig<M, Ctx>): JotaiRouter<M, Ctx>;
17
20
  //#endregion
18
21
  export { JotaiRouter, createRouter };
@@ -16,7 +16,8 @@ function createHeadlessRouter(config) {
16
16
  const wrapped = beforeLoad ? (input) => beforeLoad({
17
17
  params: input.params,
18
18
  get: _currentGet,
19
- context: input.context
19
+ context: input.context,
20
+ to: input.to
20
21
  }) : void 0;
21
22
  return [key, {
22
23
  ...rest,
@@ -25,6 +26,7 @@ function createHeadlessRouter(config) {
25
26
  })) : void 0;
26
27
  const core = (0, iten_core.createItenCore)({
27
28
  initial: config.initial,
29
+ routes: config.routes,
28
30
  routeConfig: coreRouteConfig,
29
31
  maxHistoryLength: config.maxHistoryLength,
30
32
  queryClient: config.queryClient,
@@ -47,6 +49,7 @@ function createHeadlessRouter(config) {
47
49
  };
48
50
  return {
49
51
  atoms,
52
+ to: core.to,
50
53
  ...require_hooks.makeHooks(atoms)
51
54
  };
52
55
  }
@@ -1,6 +1,6 @@
1
1
  import { JotaiRouteConfig, JotaiRouterConfig, NoParams, QueryClientLike, RouteConfigMap, RouteMap as RouteMap$1, RouteMapDef as RouteMapDef$1, RouteMatcher, RouteName, RouteOf, RouteParams, RouteUnion as RouteUnion$1, RouterError as RouterError$1, RouterState as RouterState$1, UseRouteResult } from "./types.cjs";
2
2
  import { HookBundle } from "./hooks.cjs";
3
- import { NavigateInput, RouteMapDef, RouterState } from "iten-core";
3
+ import { InferRoutes, NavigateInput, RouteDefinitions, RouteFactory, RouteMapDef, RouterState } from "iten-core";
4
4
  import { Atom, WritableAtom } from "jotai";
5
5
 
6
6
  //#region src/headless-core.d.ts
@@ -11,7 +11,11 @@ type RouterAtoms<M extends RouteMapDef> = {
11
11
  };
12
12
  type HeadlessJotaiRouter<M extends RouteMapDef> = HookBundle<M> & {
13
13
  atoms: RouterAtoms<M>;
14
+ to: RouteFactory<M>;
14
15
  };
16
+ declare function createHeadlessRouter<const Defs extends RouteDefinitions, Ctx = unknown>(config: JotaiRouterConfig<InferRoutes<Defs>, Ctx> & {
17
+ routes: Defs;
18
+ }): HeadlessJotaiRouter<InferRoutes<Defs>>;
15
19
  declare function createHeadlessRouter<M extends RouteMapDef, Ctx = unknown>(config: JotaiRouterConfig<M, Ctx>): HeadlessJotaiRouter<M>;
16
20
  //#endregion
17
21
  export { HeadlessJotaiRouter, RouterAtoms, createHeadlessRouter };
@@ -1,6 +1,6 @@
1
1
  import { JotaiRouteConfig, JotaiRouterConfig, NoParams, QueryClientLike, RouteConfigMap, RouteMap as RouteMap$1, RouteMapDef as RouteMapDef$1, RouteMatcher, RouteName, RouteOf, RouteParams, RouteUnion as RouteUnion$1, RouterError as RouterError$1, RouterState as RouterState$1, UseRouteResult } from "./types.js";
2
2
  import { HookBundle } from "./hooks.js";
3
- import { NavigateInput, RouteMapDef, RouterState } from "iten-core";
3
+ import { InferRoutes, NavigateInput, RouteDefinitions, RouteFactory, RouteMapDef, RouterState } from "iten-core";
4
4
  import { Atom, WritableAtom } from "jotai";
5
5
 
6
6
  //#region src/headless-core.d.ts
@@ -11,7 +11,11 @@ type RouterAtoms<M extends RouteMapDef> = {
11
11
  };
12
12
  type HeadlessJotaiRouter<M extends RouteMapDef> = HookBundle<M> & {
13
13
  atoms: RouterAtoms<M>;
14
+ to: RouteFactory<M>;
14
15
  };
16
+ declare function createHeadlessRouter<const Defs extends RouteDefinitions, Ctx = unknown>(config: JotaiRouterConfig<InferRoutes<Defs>, Ctx> & {
17
+ routes: Defs;
18
+ }): HeadlessJotaiRouter<InferRoutes<Defs>>;
15
19
  declare function createHeadlessRouter<M extends RouteMapDef, Ctx = unknown>(config: JotaiRouterConfig<M, Ctx>): HeadlessJotaiRouter<M>;
16
20
  //#endregion
17
21
  export { HeadlessJotaiRouter, RouterAtoms, createHeadlessRouter };
@@ -16,7 +16,8 @@ function createHeadlessRouter(config) {
16
16
  const wrapped = beforeLoad ? (input) => beforeLoad({
17
17
  params: input.params,
18
18
  get: _currentGet,
19
- context: input.context
19
+ context: input.context,
20
+ to: input.to
20
21
  }) : void 0;
21
22
  return [key, {
22
23
  ...rest,
@@ -25,6 +26,7 @@ function createHeadlessRouter(config) {
25
26
  })) : void 0;
26
27
  const core = createItenCore({
27
28
  initial: config.initial,
29
+ routes: config.routes,
28
30
  routeConfig: coreRouteConfig,
29
31
  maxHistoryLength: config.maxHistoryLength,
30
32
  queryClient: config.queryClient,
@@ -47,6 +49,7 @@ function createHeadlessRouter(config) {
47
49
  };
48
50
  return {
49
51
  atoms,
52
+ to: core.to,
50
53
  ...makeHooks(atoms)
51
54
  };
52
55
  }
package/dist/types.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { InferRoutes, NoParams, QueryClientLike, RouteConfig, RouteConfigMap, RouteDefinition, RouteDefinitions, RouteMap as RouteMap$1, RouteMapDef as RouteMapDef$1, RouteMatcher, RouteName, RouteOf, RouteParams, RouteUnion as RouteUnion$1, RouterError as RouterError$1, RouterState as RouterState$1 } from "iten-core";
1
+ import { InferRoutes as InferRoutes$1, NoParams, QueryClientLike, RouteConfig, RouteConfigMap, RouteDefinition, RouteDefinitions as RouteDefinitions$1, RouteFactory as RouteFactory$1, RouteMap as RouteMap$1, RouteMapDef as RouteMapDef$1, RouteMatcher, RouteName, RouteOf, RouteParams, RouteUnion as RouteUnion$1, RouterError as RouterError$1, RouterState as RouterState$1 } from "iten-core";
2
2
  import { Getter } from "jotai";
3
3
 
4
4
  //#region src/types.d.ts
@@ -8,10 +8,12 @@ type JotaiRouteConfig<M extends RouteMapDef$1, K extends keyof M, Ctx = unknown>
8
8
  params: M[K];
9
9
  get: Getter;
10
10
  context: Ctx;
11
+ to: RouteFactory$1<M>;
11
12
  }) => RouteUnion$1<M> | undefined | Promise<RouteUnion$1<M> | undefined>;
12
13
  }>;
13
14
  type JotaiRouterConfig<M extends RouteMapDef$1, Ctx = unknown> = {
14
15
  initial: RouteUnion$1<M> | null;
16
+ routes?: RouteDefinitions$1 | undefined;
15
17
  routeConfig?: { [K in keyof M]?: JotaiRouteConfig<M, K, Ctx> };
16
18
  maxHistoryLength?: number | undefined;
17
19
  context?: Ctx | ((input: {
@@ -27,4 +29,4 @@ type UseRouteResult<M extends RouteMapDef$1, K extends keyof M> = {
27
29
  params: null;
28
30
  };
29
31
  //#endregion
30
- export { type InferRoutes, JotaiRouteConfig, JotaiRouterConfig, type NoParams, type QueryClientLike, type RouteConfigMap, type RouteDefinition, type RouteDefinitions, type RouteMap$1 as RouteMap, type RouteMapDef$1 as RouteMapDef, type RouteMatcher, type RouteName, type RouteOf, type RouteParams, type RouteUnion$1 as RouteUnion, type RouterError$1 as RouterError, type RouterState$1 as RouterState, UseRouteResult };
32
+ export { type InferRoutes$1 as InferRoutes, JotaiRouteConfig, JotaiRouterConfig, type NoParams, type QueryClientLike, type RouteConfigMap, type RouteDefinition, type RouteDefinitions$1 as RouteDefinitions, type RouteMap$1 as RouteMap, type RouteMapDef$1 as RouteMapDef, type RouteMatcher, type RouteName, type RouteOf, type RouteParams, type RouteUnion$1 as RouteUnion, type RouterError$1 as RouterError, type RouterState$1 as RouterState, UseRouteResult };
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { InferRoutes, NoParams, QueryClientLike, RouteConfig, RouteConfigMap, RouteDefinition, RouteDefinitions, RouteMap as RouteMap$1, RouteMapDef as RouteMapDef$1, RouteMatcher, RouteName, RouteOf, RouteParams, RouteUnion as RouteUnion$1, RouterError as RouterError$1, RouterState as RouterState$1 } from "iten-core";
1
+ import { InferRoutes as InferRoutes$1, NoParams, QueryClientLike, RouteConfig, RouteConfigMap, RouteDefinition, RouteDefinitions as RouteDefinitions$1, RouteFactory as RouteFactory$1, RouteMap as RouteMap$1, RouteMapDef as RouteMapDef$1, RouteMatcher, RouteName, RouteOf, RouteParams, RouteUnion as RouteUnion$1, RouterError as RouterError$1, RouterState as RouterState$1 } from "iten-core";
2
2
  import { Getter } from "jotai";
3
3
 
4
4
  //#region src/types.d.ts
@@ -8,10 +8,12 @@ type JotaiRouteConfig<M extends RouteMapDef$1, K extends keyof M, Ctx = unknown>
8
8
  params: M[K];
9
9
  get: Getter;
10
10
  context: Ctx;
11
+ to: RouteFactory$1<M>;
11
12
  }) => RouteUnion$1<M> | undefined | Promise<RouteUnion$1<M> | undefined>;
12
13
  }>;
13
14
  type JotaiRouterConfig<M extends RouteMapDef$1, Ctx = unknown> = {
14
15
  initial: RouteUnion$1<M> | null;
16
+ routes?: RouteDefinitions$1 | undefined;
15
17
  routeConfig?: { [K in keyof M]?: JotaiRouteConfig<M, K, Ctx> };
16
18
  maxHistoryLength?: number | undefined;
17
19
  context?: Ctx | ((input: {
@@ -27,4 +29,4 @@ type UseRouteResult<M extends RouteMapDef$1, K extends keyof M> = {
27
29
  params: null;
28
30
  };
29
31
  //#endregion
30
- export { type InferRoutes, JotaiRouteConfig, JotaiRouterConfig, type NoParams, type QueryClientLike, type RouteConfigMap, type RouteDefinition, type RouteDefinitions, type RouteMap$1 as RouteMap, type RouteMapDef$1 as RouteMapDef, type RouteMatcher, type RouteName, type RouteOf, type RouteParams, type RouteUnion$1 as RouteUnion, type RouterError$1 as RouterError, type RouterState$1 as RouterState, UseRouteResult };
32
+ export { type InferRoutes$1 as InferRoutes, JotaiRouteConfig, JotaiRouterConfig, type NoParams, type QueryClientLike, type RouteConfigMap, type RouteDefinition, type RouteDefinitions$1 as RouteDefinitions, type RouteMap$1 as RouteMap, type RouteMapDef$1 as RouteMapDef, type RouteMatcher, type RouteName, type RouteOf, type RouteParams, type RouteUnion$1 as RouteUnion, type RouterError$1 as RouterError, type RouterState$1 as RouterState, UseRouteResult };
package/dist/url.cjs CHANGED
@@ -5,6 +5,7 @@ let iten_core_url = require("iten-core/url");
5
5
  function createUrlSync({ router, store = (0, jotai.getDefaultStore)(), ...input }) {
6
6
  const coreLike = {
7
7
  getState: () => store.get(router.atoms.state),
8
+ to: router.to,
8
9
  navigate: (navigateInput) => store.set(router.atoms.navigate, navigateInput),
9
10
  goBack: () => {
10
11
  store.set(router.atoms.goBack);
package/dist/url.mjs CHANGED
@@ -4,6 +4,7 @@ import { createUrlSync as createUrlSync$1 } from "iten-core/url";
4
4
  function createUrlSync({ router, store = getDefaultStore(), ...input }) {
5
5
  const coreLike = {
6
6
  getState: () => store.get(router.atoms.state),
7
+ to: router.to,
7
8
  navigate: (navigateInput) => store.set(router.atoms.navigate, navigateInput),
8
9
  goBack: () => {
9
10
  store.set(router.atoms.goBack);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jotai-iten",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Jotai adapter for iten — ultralight in-memory router for embedded JS apps",
5
5
  "keywords": [
6
6
  "jotai",
@@ -108,7 +108,7 @@
108
108
  }
109
109
  },
110
110
  "dependencies": {
111
- "iten-core": "^0.4.2"
111
+ "iten-core": "^0.5.0"
112
112
  },
113
113
  "publishConfig": {
114
114
  "access": "public",
@@ -1,4 +1,4 @@
1
- import type { RouteMapDef } from 'iten-core'
1
+ import type { InferRoutes, RouteDefinitions, RouteMapDef } from 'iten-core'
2
2
  import type { LinkComponent } from './components/Link.tsx'
3
3
  import { makeLink } from './components/Link.tsx'
4
4
  import type { NavigateComponent } from './components/Navigate.tsx'
@@ -18,6 +18,12 @@ export type JotaiRouter<M extends RouteMapDef, _Ctx = unknown> = HeadlessJotaiRo
18
18
  Navigate: NavigateComponent<M>
19
19
  }
20
20
 
21
+ export function createRouter<const Defs extends RouteDefinitions, Ctx = unknown>(
22
+ config: JotaiRouterConfig<InferRoutes<Defs>, Ctx> & { routes: Defs },
23
+ ): JotaiRouter<InferRoutes<Defs>, Ctx>
24
+ export function createRouter<M extends RouteMapDef, Ctx = unknown>(
25
+ config: JotaiRouterConfig<M, Ctx>,
26
+ ): JotaiRouter<M, Ctx>
21
27
  export function createRouter<M extends RouteMapDef, Ctx = unknown>(
22
28
  config: JotaiRouterConfig<M, Ctx>,
23
29
  ): JotaiRouter<M, Ctx> {
@@ -1,4 +1,13 @@
1
- import type { NavigateInput, RouteConfigMap, RouteMapDef, RouterState, RouteUnion } from 'iten-core'
1
+ import type {
2
+ InferRoutes,
3
+ NavigateInput,
4
+ RouteConfigMap,
5
+ RouteDefinitions,
6
+ RouteFactory,
7
+ RouteMapDef,
8
+ RouterState,
9
+ RouteUnion,
10
+ } from 'iten-core'
2
11
  import { createItenCore } from 'iten-core'
3
12
  import type { Atom, Getter, WritableAtom } from 'jotai'
4
13
  import { atom } from 'jotai'
@@ -40,8 +49,15 @@ export type RouterAtoms<M extends RouteMapDef> = {
40
49
 
41
50
  export type HeadlessJotaiRouter<M extends RouteMapDef> = HookBundle<M> & {
42
51
  atoms: RouterAtoms<M>
52
+ to: RouteFactory<M>
43
53
  }
44
54
 
55
+ export function createHeadlessRouter<const Defs extends RouteDefinitions, Ctx = unknown>(
56
+ config: JotaiRouterConfig<InferRoutes<Defs>, Ctx> & { routes: Defs },
57
+ ): HeadlessJotaiRouter<InferRoutes<Defs>>
58
+ export function createHeadlessRouter<M extends RouteMapDef, Ctx = unknown>(
59
+ config: JotaiRouterConfig<M, Ctx>,
60
+ ): HeadlessJotaiRouter<M>
45
61
  export function createHeadlessRouter<M extends RouteMapDef, Ctx = unknown>(
46
62
  config: JotaiRouterConfig<M, Ctx>,
47
63
  ): HeadlessJotaiRouter<M> {
@@ -64,17 +80,19 @@ export function createHeadlessRouter<M extends RouteMapDef, Ctx = unknown>(
64
80
  if (!route) return [key, route]
65
81
  const { beforeLoad, ...rest } = route as JotaiRouteConfig<M, keyof M, Ctx>
66
82
  const wrapped = beforeLoad
67
- ? (input: { params: M[keyof M]; context: Ctx }) =>
83
+ ? (input: { params: M[keyof M]; context: Ctx; to: RouteFactory<M> }) =>
68
84
  (
69
85
  beforeLoad as (input: {
70
86
  params: M[keyof M]
71
87
  get: Getter
72
88
  context: Ctx
89
+ to: RouteFactory<M>
73
90
  }) => RouteUnion<M> | undefined | Promise<RouteUnion<M> | undefined>
74
91
  )({
75
92
  params: input.params,
76
93
  get: _currentGet,
77
94
  context: input.context,
95
+ to: input.to,
78
96
  })
79
97
  : undefined
80
98
  return [key, { ...rest, beforeLoad: wrapped }]
@@ -84,6 +102,7 @@ export function createHeadlessRouter<M extends RouteMapDef, Ctx = unknown>(
84
102
 
85
103
  const core = createItenCore<M, Ctx>({
86
104
  initial: config.initial,
105
+ routes: config.routes,
87
106
  routeConfig: coreRouteConfig,
88
107
  maxHistoryLength: config.maxHistoryLength,
89
108
  queryClient: config.queryClient,
@@ -121,6 +140,7 @@ export function createHeadlessRouter<M extends RouteMapDef, Ctx = unknown>(
121
140
 
122
141
  return {
123
142
  atoms,
143
+ to: core.to,
124
144
  ...makeHooks<M>(atoms),
125
145
  }
126
146
  }
package/src/types.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
  RouteConfigMap,
7
7
  RouteDefinition,
8
8
  RouteDefinitions,
9
+ RouteFactory,
9
10
  RouteMap,
10
11
  RouteMapDef,
11
12
  RouteMatcher,
@@ -25,6 +26,7 @@ export type {
25
26
  RouteConfigMap,
26
27
  RouteDefinition,
27
28
  RouteDefinitions,
29
+ RouteFactory,
28
30
  RouteMap,
29
31
  RouteMapDef,
30
32
  RouteMatcher,
@@ -44,12 +46,14 @@ export type JotaiRouteConfig<M extends RouteMapDef, K extends keyof M, Ctx = unk
44
46
  params: M[K]
45
47
  get: Getter
46
48
  context: Ctx
49
+ to: RouteFactory<M>
47
50
  }) => RouteUnion<M> | undefined | Promise<RouteUnion<M> | undefined>
48
51
  }
49
52
  >
50
53
 
51
54
  export type JotaiRouterConfig<M extends RouteMapDef, Ctx = unknown> = {
52
55
  initial: RouteUnion<M> | null
56
+ routes?: RouteDefinitions | undefined
53
57
  routeConfig?: { [K in keyof M]?: JotaiRouteConfig<M, K, Ctx> }
54
58
  maxHistoryLength?: number | undefined
55
59
  context?: Ctx | ((input: { get: Getter }) => Ctx)
package/src/url.ts CHANGED
@@ -36,6 +36,7 @@ export function createUrlSync<M extends RouteMapDef>({
36
36
  }: CreateUrlSyncInput<M>): UrlSync<M> {
37
37
  const coreLike: ItenCore<M> = {
38
38
  getState: () => store.get(router.atoms.state) as RouterState<M>,
39
+ to: router.to,
39
40
  navigate: (navigateInput: NavigateInput<M>) => store.set(router.atoms.navigate, navigateInput),
40
41
  goBack: () => {
41
42
  void store.set(router.atoms.goBack)