@pyreon/router 0.24.5 → 0.24.6
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/package.json +4 -6
- package/src/components.tsx +0 -650
- package/src/env.d.ts +0 -6
- package/src/index.ts +0 -106
- package/src/loader.ts +0 -200
- package/src/manifest.ts +0 -399
- package/src/match.ts +0 -921
- package/src/not-found.ts +0 -75
- package/src/redirect.ts +0 -63
- package/src/router.ts +0 -1424
- package/src/scroll.ts +0 -93
- package/src/tests/integration.test.tsx +0 -298
- package/src/tests/loader.test.ts +0 -1024
- package/src/tests/manifest-snapshot.test.ts +0 -101
- package/src/tests/match.test.ts +0 -782
- package/src/tests/native-markers.test.ts +0 -18
- package/src/tests/redirect.test.ts +0 -96
- package/src/tests/router.browser.test.tsx +0 -509
- package/src/tests/router.test.ts +0 -5498
- package/src/tests/routerlink-reactive-to.browser.test.tsx +0 -158
- package/src/tests/scroll.test.ts +0 -31
- package/src/tests/setup.ts +0 -3
- package/src/types.ts +0 -517
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
renderApiReferenceEntries,
|
|
3
|
-
renderLlmsFullSection,
|
|
4
|
-
renderLlmsTxtLine,
|
|
5
|
-
} from '@pyreon/manifest'
|
|
6
|
-
import routerManifest from '../manifest'
|
|
7
|
-
|
|
8
|
-
describe('gen-docs — router snapshot', () => {
|
|
9
|
-
it('renders @pyreon/router to its expected llms.txt bullet', () => {
|
|
10
|
-
expect(renderLlmsTxtLine(routerManifest)).toMatchInlineSnapshot(`"- @pyreon/router — hash+history+SSR, context-based, prefetching, guards, loaders, useIsActive, View Transitions, middleware, typed search params. \`await router.push()\` resolves after \`updateCallbackDone\` (DOM commit), NOT after animation finishes. It does NOT wait for \`.finished\` (~200-300ms). \`.ready\` and \`.finished\` get empty \`.catch()\` handlers so \`AbortError: Transition was skipped\` rejections (from interrupted transitions) do not leak as unhandled promise rejections."`)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('renders @pyreon/router to its expected llms-full.txt section — full body snapshot', () => {
|
|
14
|
-
expect(renderLlmsFullSection(routerManifest)).toMatchInlineSnapshot(`
|
|
15
|
-
"## @pyreon/router — Router
|
|
16
|
-
|
|
17
|
-
Type-safe client-side router for Pyreon with nested routes, per-route and global navigation guards, data loaders, middleware chain, View Transitions API integration, and typed search params. Context-based (\`RouterContext\`) with hash and history mode support. Route params are inferred from path strings (\`"/user/:id"\` yields \`{ id: string }\`). Named routes enable typed programmatic navigation. SSR-compatible with server-side route resolution. Hash mode uses \`history.pushState\` (not \`window.location.hash\`) to avoid double-update. \`await router.push()\` resolves after the View Transition \`updateCallbackDone\` (DOM commit), not after animation completion.
|
|
18
|
-
|
|
19
|
-
\`\`\`typescript
|
|
20
|
-
import { createRouter, RouterProvider, RouterView, RouterLink, useRouter, useRoute, useIsActive, useTypedSearchParams, useTransition, useLoaderData, useMiddlewareData } from "@pyreon/router"
|
|
21
|
-
import { mount } from "@pyreon/runtime-dom"
|
|
22
|
-
|
|
23
|
-
// Define routes with typed params, guards, loaders, and middleware
|
|
24
|
-
const router = createRouter({
|
|
25
|
-
routes: [
|
|
26
|
-
{ path: "/", component: Home, name: "home" },
|
|
27
|
-
{ path: "/user/:id", component: User, name: "user",
|
|
28
|
-
loader: ({ params }) => fetchUser(params.id),
|
|
29
|
-
meta: { title: "User Profile" } },
|
|
30
|
-
{ path: "/admin", component: AdminLayout,
|
|
31
|
-
beforeEnter: (to, from) => isAdmin() || "/login",
|
|
32
|
-
children: [
|
|
33
|
-
{ path: "users", component: AdminUsers },
|
|
34
|
-
{ path: "settings", component: AdminSettings },
|
|
35
|
-
] },
|
|
36
|
-
{ path: "/settings", redirect: "/admin/settings" },
|
|
37
|
-
{ path: "(.*)", component: NotFound },
|
|
38
|
-
],
|
|
39
|
-
middleware: [authMiddleware, loggerMiddleware],
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
// Mount with RouterProvider
|
|
43
|
-
mount(
|
|
44
|
-
<RouterProvider router={router}>
|
|
45
|
-
<nav>
|
|
46
|
-
<RouterLink to="/" activeClass="nav-active">Home</RouterLink>
|
|
47
|
-
<RouterLink to={{ name: "user", params: { id: "42" } }}>Profile</RouterLink>
|
|
48
|
-
</nav>
|
|
49
|
-
<RouterView />
|
|
50
|
-
</RouterProvider>,
|
|
51
|
-
document.getElementById("app")!
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
// Inside a component — hooks
|
|
55
|
-
const User = () => {
|
|
56
|
-
const route = useRoute<"/user/:id">()
|
|
57
|
-
const data = useLoaderData<UserData>()
|
|
58
|
-
const router = useRouter()
|
|
59
|
-
const isAdmin = useIsActive("/admin")
|
|
60
|
-
const isTransitioning = useTransition()
|
|
61
|
-
const params = useTypedSearchParams({ tab: "string", page: "number" })
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<div>
|
|
65
|
-
<h1>{data.name} (ID: {route().params.id})</h1>
|
|
66
|
-
<Show when={isTransitioning()}>
|
|
67
|
-
<ProgressBar />
|
|
68
|
-
</Show>
|
|
69
|
-
<button onClick={() => router.push("/")}>Go Home</button>
|
|
70
|
-
</div>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
\`\`\`
|
|
74
|
-
|
|
75
|
-
> **View Transitions — what push() awaits**: \`await router.push()\` resolves after \`updateCallbackDone\` (DOM commit), NOT after animation finishes. It does NOT wait for \`.finished\` (~200-300ms). \`.ready\` and \`.finished\` get empty \`.catch()\` handlers so \`AbortError: Transition was skipped\` rejections (from interrupted transitions) do not leak as unhandled promise rejections.
|
|
76
|
-
>
|
|
77
|
-
> **Hash mode uses pushState**: Hash mode uses \`history.pushState\` — NOT \`window.location.hash\` assignment — to avoid double-update from the hashchange event. Reading \`location.hash\` directly will not reflect router state; use \`useRoute()\` instead.
|
|
78
|
-
>
|
|
79
|
-
> **Imperative navigation in render body**: \`router.push()\` or \`navigate()\` called synchronously in the component function body causes an infinite render loop. Wrap in \`onMount\`, event handlers, \`effect\`, or any deferred execution context. The \`pyreon/no-imperative-navigate-in-render\` lint rule catches this.
|
|
80
|
-
>
|
|
81
|
-
> **Hook ordering with View Transitions**: \`afterEach\` hooks and scroll restoration fire AFTER the View Transition callback completes — not before. This means hooks see the NEW route state, which is the correct per-spec behavior but a subtle change from pre-VT versions.
|
|
82
|
-
>
|
|
83
|
-
> **For uses by, not key**: \`<For>\` in route lists uses \`by\` not \`key\`. \`<For each={routes()} key={r => r.path}>\` silently passes the key to VNode reconciliation instead of the list reconciler. Use \`by={r => r.path}\`.
|
|
84
|
-
"
|
|
85
|
-
`)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('renders @pyreon/router to MCP api-reference entries — one per api[] item', () => {
|
|
89
|
-
const record = renderApiReferenceEntries(routerManifest)
|
|
90
|
-
expect(Object.keys(record).length).toBe(18)
|
|
91
|
-
expect(Object.keys(record)).toContain('router/createRouter')
|
|
92
|
-
// PR-B added redirect/isRedirectError/getRedirectInfo entries.
|
|
93
|
-
expect(Object.keys(record)).toContain('router/redirect')
|
|
94
|
-
expect(Object.keys(record)).toContain('router/isRedirectError')
|
|
95
|
-
expect(Object.keys(record)).toContain('router/getRedirectInfo')
|
|
96
|
-
// Spot-check the flagship API — createRouter is the factory
|
|
97
|
-
const createRouter = record['router/createRouter']!
|
|
98
|
-
expect(createRouter.notes).toContain('routes')
|
|
99
|
-
expect(createRouter.mistakes?.split('\n').length).toBeGreaterThan(2)
|
|
100
|
-
})
|
|
101
|
-
})
|