@pithyjs/router 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +621 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/router/core/config.d.ts +22 -0
- package/dist/router/core/config.d.ts.map +1 -0
- package/dist/router/core/config.js +54 -0
- package/dist/router/core/config.js.map +1 -0
- package/dist/router/core/constants.d.ts +89 -0
- package/dist/router/core/constants.d.ts.map +1 -0
- package/dist/router/core/constants.js +95 -0
- package/dist/router/core/constants.js.map +1 -0
- package/dist/router/core/history.d.ts +54 -0
- package/dist/router/core/history.d.ts.map +1 -0
- package/dist/router/core/history.js +89 -0
- package/dist/router/core/history.js.map +1 -0
- package/dist/router/core/matcher.d.ts +43 -0
- package/dist/router/core/matcher.d.ts.map +1 -0
- package/dist/router/core/matcher.js +242 -0
- package/dist/router/core/matcher.js.map +1 -0
- package/dist/router/core/params.d.ts +88 -0
- package/dist/router/core/params.d.ts.map +1 -0
- package/dist/router/core/params.js +150 -0
- package/dist/router/core/params.js.map +1 -0
- package/dist/router/core/pipeline.d.ts +90 -0
- package/dist/router/core/pipeline.d.ts.map +1 -0
- package/dist/router/core/pipeline.js +323 -0
- package/dist/router/core/pipeline.js.map +1 -0
- package/dist/router/core/route-tree.d.ts +76 -0
- package/dist/router/core/route-tree.d.ts.map +1 -0
- package/dist/router/core/route-tree.js +143 -0
- package/dist/router/core/route-tree.js.map +1 -0
- package/dist/router/core/router.d.ts +229 -0
- package/dist/router/core/router.d.ts.map +1 -0
- package/dist/router/core/router.js +762 -0
- package/dist/router/core/router.js.map +1 -0
- package/dist/router/core/types.d.ts +387 -0
- package/dist/router/core/types.d.ts.map +1 -0
- package/dist/router/core/types.js +22 -0
- package/dist/router/core/types.js.map +1 -0
- package/dist/router/core/utils.d.ts +66 -0
- package/dist/router/core/utils.d.ts.map +1 -0
- package/dist/router/core/utils.js +152 -0
- package/dist/router/core/utils.js.map +1 -0
- package/dist/router/features/coordinator.d.ts +75 -0
- package/dist/router/features/coordinator.d.ts.map +1 -0
- package/dist/router/features/coordinator.js +203 -0
- package/dist/router/features/coordinator.js.map +1 -0
- package/dist/router/features/errors/error-codes.d.ts +107 -0
- package/dist/router/features/errors/error-codes.d.ts.map +1 -0
- package/dist/router/features/errors/error-codes.js +247 -0
- package/dist/router/features/errors/error-codes.js.map +1 -0
- package/dist/router/features/errors/error-resolution.d.ts +31 -0
- package/dist/router/features/errors/error-resolution.d.ts.map +1 -0
- package/dist/router/features/errors/error-resolution.js +47 -0
- package/dist/router/features/errors/error-resolution.js.map +1 -0
- package/dist/router/features/errors/error-types.d.ts +60 -0
- package/dist/router/features/errors/error-types.d.ts.map +1 -0
- package/dist/router/features/errors/error-types.js +57 -0
- package/dist/router/features/errors/error-types.js.map +1 -0
- package/dist/router/features/errors/global-error-fallback.d.ts +33 -0
- package/dist/router/features/errors/global-error-fallback.d.ts.map +1 -0
- package/dist/router/features/errors/global-error-fallback.js +182 -0
- package/dist/router/features/errors/global-error-fallback.js.map +1 -0
- package/dist/router/features/errors/index.d.ts +15 -0
- package/dist/router/features/errors/index.d.ts.map +1 -0
- package/dist/router/features/errors/index.js +18 -0
- package/dist/router/features/errors/index.js.map +1 -0
- package/dist/router/features/errors/router-errors.d.ts +44 -0
- package/dist/router/features/errors/router-errors.d.ts.map +1 -0
- package/dist/router/features/errors/router-errors.js +53 -0
- package/dist/router/features/errors/router-errors.js.map +1 -0
- package/dist/router/features/guards/engine.d.ts +40 -0
- package/dist/router/features/guards/engine.d.ts.map +1 -0
- package/dist/router/features/guards/engine.js +67 -0
- package/dist/router/features/guards/engine.js.map +1 -0
- package/dist/router/features/guards/index.d.ts +52 -0
- package/dist/router/features/guards/index.d.ts.map +1 -0
- package/dist/router/features/guards/index.js +95 -0
- package/dist/router/features/guards/index.js.map +1 -0
- package/dist/router/features/loaders/cache.d.ts +60 -0
- package/dist/router/features/loaders/cache.d.ts.map +1 -0
- package/dist/router/features/loaders/cache.js +80 -0
- package/dist/router/features/loaders/cache.js.map +1 -0
- package/dist/router/features/loaders/engine.d.ts +17 -0
- package/dist/router/features/loaders/engine.d.ts.map +1 -0
- package/dist/router/features/loaders/engine.js +27 -0
- package/dist/router/features/loaders/engine.js.map +1 -0
- package/dist/router/features/loaders/index.d.ts +49 -0
- package/dist/router/features/loaders/index.d.ts.map +1 -0
- package/dist/router/features/loaders/index.js +112 -0
- package/dist/router/features/loaders/index.js.map +1 -0
- package/dist/router/features/loading-ui-manager.d.ts +69 -0
- package/dist/router/features/loading-ui-manager.d.ts.map +1 -0
- package/dist/router/features/loading-ui-manager.js +209 -0
- package/dist/router/features/loading-ui-manager.js.map +1 -0
- package/dist/router/features/module-loader.d.ts +50 -0
- package/dist/router/features/module-loader.d.ts.map +1 -0
- package/dist/router/features/module-loader.js +121 -0
- package/dist/router/features/module-loader.js.map +1 -0
- package/dist/router/features/outlet/errors.d.ts +24 -0
- package/dist/router/features/outlet/errors.d.ts.map +1 -0
- package/dist/router/features/outlet/errors.js +96 -0
- package/dist/router/features/outlet/errors.js.map +1 -0
- package/dist/router/features/outlet/index.d.ts +45 -0
- package/dist/router/features/outlet/index.d.ts.map +1 -0
- package/dist/router/features/outlet/index.js +95 -0
- package/dist/router/features/outlet/index.js.map +1 -0
- package/dist/router/features/outlet/mount.d.ts +38 -0
- package/dist/router/features/outlet/mount.d.ts.map +1 -0
- package/dist/router/features/outlet/mount.js +301 -0
- package/dist/router/features/outlet/mount.js.map +1 -0
- package/dist/router/features/outlet/segment-chain.d.ts +16 -0
- package/dist/router/features/outlet/segment-chain.d.ts.map +1 -0
- package/dist/router/features/outlet/segment-chain.js +62 -0
- package/dist/router/features/outlet/segment-chain.js.map +1 -0
- package/dist/router/features/prefetch/index.d.ts +97 -0
- package/dist/router/features/prefetch/index.d.ts.map +1 -0
- package/dist/router/features/prefetch/index.js +345 -0
- package/dist/router/features/prefetch/index.js.map +1 -0
- package/dist/router/features/ux/focus.d.ts +62 -0
- package/dist/router/features/ux/focus.d.ts.map +1 -0
- package/dist/router/features/ux/focus.js +136 -0
- package/dist/router/features/ux/focus.js.map +1 -0
- package/dist/router/features/ux/live-region.d.ts +81 -0
- package/dist/router/features/ux/live-region.d.ts.map +1 -0
- package/dist/router/features/ux/live-region.js +166 -0
- package/dist/router/features/ux/live-region.js.map +1 -0
- package/dist/router/features/ux/scroll-focus.d.ts +105 -0
- package/dist/router/features/ux/scroll-focus.d.ts.map +1 -0
- package/dist/router/features/ux/scroll-focus.js +111 -0
- package/dist/router/features/ux/scroll-focus.js.map +1 -0
- package/dist/router/features/ux/scroll.d.ts +71 -0
- package/dist/router/features/ux/scroll.d.ts.map +1 -0
- package/dist/router/features/ux/scroll.js +137 -0
- package/dist/router/features/ux/scroll.js.map +1 -0
- package/dist/router/features/ux/transitions.d.ts +120 -0
- package/dist/router/features/ux/transitions.d.ts.map +1 -0
- package/dist/router/features/ux/transitions.js +235 -0
- package/dist/router/features/ux/transitions.js.map +1 -0
- package/dist/router/integration/directive-binder.d.ts +20 -0
- package/dist/router/integration/directive-binder.d.ts.map +1 -0
- package/dist/router/integration/directive-binder.js +139 -0
- package/dist/router/integration/directive-binder.js.map +1 -0
- package/dist/router/integration/directives.d.ts +42 -0
- package/dist/router/integration/directives.d.ts.map +1 -0
- package/dist/router/integration/directives.js +109 -0
- package/dist/router/integration/directives.js.map +1 -0
- package/dist/router/integration/history-adapter.d.ts +76 -0
- package/dist/router/integration/history-adapter.d.ts.map +1 -0
- package/dist/router/integration/history-adapter.js +157 -0
- package/dist/router/integration/history-adapter.js.map +1 -0
- package/dist/router/integration/hmr.d.ts +83 -0
- package/dist/router/integration/hmr.d.ts.map +1 -0
- package/dist/router/integration/hmr.js +160 -0
- package/dist/router/integration/hmr.js.map +1 -0
- package/dist/router/integration/link-interceptor.d.ts +49 -0
- package/dist/router/integration/link-interceptor.d.ts.map +1 -0
- package/dist/router/integration/link-interceptor.js +94 -0
- package/dist/router/integration/link-interceptor.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PithyJS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
# @pithyjs/router
|
|
2
|
+
|
|
3
|
+
File-based client-side router for PithyJS with guards, loaders, view transitions, and fine-grained reactivity.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **File-Based Routing** - Automatic route generation from file structure
|
|
8
|
+
- **Zero-Flash Navigation** - Guards execute before DOM/history mutations
|
|
9
|
+
- **Data Loading** - Loaders with SWR caching and loading states
|
|
10
|
+
- **Two-Phase Mounting** - Atomic DOM updates with diff-based rendering
|
|
11
|
+
- **Error Boundaries** - Parent chain resolution for error components
|
|
12
|
+
- **Intelligent Prefetching** - Hover, visible, and intent-based strategies
|
|
13
|
+
- **View Transitions** - Smooth animations with View Transitions API
|
|
14
|
+
- **Accessibility First** - Focus management, scroll restoration, screen reader announcements
|
|
15
|
+
- **HMR Support** - Hot module replacement for development
|
|
16
|
+
- **TypeScript** - Full type safety with strict mode
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
<!-- @codex:auto install="@pithyjs/router" -->
|
|
21
|
+
```bash
|
|
22
|
+
npm install @pithyjs/router
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @pithyjs/router
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
yarn add @pithyjs/router
|
|
31
|
+
```
|
|
32
|
+
<!-- @codex:end -->
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### 1. Define Routes
|
|
37
|
+
|
|
38
|
+
Create route files in your `src/routes/` directory:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
src/routes/
|
|
42
|
+
├── +page.ts # /
|
|
43
|
+
├── +layout.ts # Root layout
|
|
44
|
+
├── about/
|
|
45
|
+
│ └── +page.ts # /about
|
|
46
|
+
└── products/
|
|
47
|
+
├── +layout.ts # Products layout
|
|
48
|
+
├── +page.ts # /products
|
|
49
|
+
├── +loader.ts # Products data loader
|
|
50
|
+
├── [id]/
|
|
51
|
+
│ ├── +page.ts # /products/:id
|
|
52
|
+
│ ├── +loader.ts # Product details loader
|
|
53
|
+
│ └── +error.ts # Product error boundary
|
|
54
|
+
└── +config.ts # Products route config
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. Create Router
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// src/main.ts
|
|
61
|
+
import { createRouter } from '@pithyjs/router';
|
|
62
|
+
import { setupLinkInterceptor } from '@pithyjs/router/integration';
|
|
63
|
+
import routes from './routes'; // Generated by Vite plugin
|
|
64
|
+
|
|
65
|
+
const router = createRouter(routes, {
|
|
66
|
+
basePath: '/',
|
|
67
|
+
loadingPolicy: 'replace'
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Enable client-side navigation for all links
|
|
71
|
+
setupLinkInterceptor(router);
|
|
72
|
+
|
|
73
|
+
// Mount root outlet
|
|
74
|
+
const app = document.getElementById('app');
|
|
75
|
+
if (app) {
|
|
76
|
+
router.mount(app);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Expose router for dev tools
|
|
80
|
+
if (import.meta.env.DEV) {
|
|
81
|
+
(globalThis as any).router = router;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3. Configure Vite Plugin
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// vite.config.ts
|
|
89
|
+
import { defineConfig } from 'vite';
|
|
90
|
+
import { pithyRouter } from '@pithyjs/vite-plugin-router';
|
|
91
|
+
|
|
92
|
+
export default defineConfig({
|
|
93
|
+
plugins: [
|
|
94
|
+
pithyRouter({
|
|
95
|
+
routesDir: 'src/routes',
|
|
96
|
+
outputFile: 'src/routes.ts'
|
|
97
|
+
})
|
|
98
|
+
]
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Route File Conventions
|
|
103
|
+
|
|
104
|
+
| File | Purpose |
|
|
105
|
+
|------|---------|
|
|
106
|
+
| `+page.ts` | Page component |
|
|
107
|
+
| `+layout.ts` | Layout wrapper with `<div @routerOutlet>` |
|
|
108
|
+
| `+loader.ts` | Data loader (runs before page render) |
|
|
109
|
+
| `+error.ts` | Error boundary component |
|
|
110
|
+
| `+config.ts` | Route configuration (guards, cache options) |
|
|
111
|
+
| `[param]/` | Dynamic route segment |
|
|
112
|
+
| `(group)/` | Route group (doesn't affect URL) |
|
|
113
|
+
|
|
114
|
+
## Core Concepts
|
|
115
|
+
|
|
116
|
+
### Guards (Authorization)
|
|
117
|
+
|
|
118
|
+
Guards execute **before** navigation to protect routes:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// guards/auth.ts
|
|
122
|
+
import type { GuardFunction } from '@pithyjs/router';
|
|
123
|
+
|
|
124
|
+
export const authGuard: GuardFunction = async ({ to, signal }) => {
|
|
125
|
+
const isAuthenticated = await checkAuth(signal);
|
|
126
|
+
|
|
127
|
+
if (!isAuthenticated) {
|
|
128
|
+
return `/login?returnUrl=${encodeURIComponent(to.path)}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return true; // Allow navigation
|
|
132
|
+
};
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// products/+config.ts
|
|
137
|
+
import { authGuard } from '../guards/auth';
|
|
138
|
+
|
|
139
|
+
export const guards = [authGuard];
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Loaders (Data Fetching)
|
|
143
|
+
|
|
144
|
+
Loaders fetch data before rendering:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// products/[id]/+loader.ts
|
|
148
|
+
import type { LoaderFunction } from '@pithyjs/router';
|
|
149
|
+
|
|
150
|
+
export const loader: LoaderFunction = async ({ params, signal }) => {
|
|
151
|
+
const response = await fetch(`/api/products/${params.id}`, { signal });
|
|
152
|
+
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
throw new Error(`HTTP ${response.status}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return response.json();
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// products/[id]/+page.ts
|
|
163
|
+
import { html } from '@pithyjs/core';
|
|
164
|
+
|
|
165
|
+
export default function ProductPage({ id, loaderData }) {
|
|
166
|
+
const product = loaderData;
|
|
167
|
+
|
|
168
|
+
return html`
|
|
169
|
+
<div>
|
|
170
|
+
<h1>{{ product.name }}</h1>
|
|
171
|
+
<p>{{ product.description }}</p>
|
|
172
|
+
<p>Price: {{ product.price }}</p>
|
|
173
|
+
</div>
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Layouts
|
|
179
|
+
|
|
180
|
+
Layouts wrap child pages:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// products/+layout.ts
|
|
184
|
+
import { html } from '@pithyjs/core';
|
|
185
|
+
|
|
186
|
+
export default function ProductsLayout() {
|
|
187
|
+
return html`
|
|
188
|
+
<div class="products-layout">
|
|
189
|
+
<aside>
|
|
190
|
+
<nav>
|
|
191
|
+
<a href="/products" @routerLink>All Products</a>
|
|
192
|
+
<a href="/products/featured" @routerLink>Featured</a>
|
|
193
|
+
</nav>
|
|
194
|
+
</aside>
|
|
195
|
+
|
|
196
|
+
<main>
|
|
197
|
+
<!-- Child pages render here -->
|
|
198
|
+
<div @routerOutlet></div>
|
|
199
|
+
</main>
|
|
200
|
+
</div>
|
|
201
|
+
`;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Error Boundaries
|
|
206
|
+
|
|
207
|
+
Error components handle errors in child routes:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// products/+error.ts
|
|
211
|
+
import { html } from '@pithyjs/core';
|
|
212
|
+
|
|
213
|
+
export default function ProductError({ error, retry }: { error: Error; retry: () => void }) {
|
|
214
|
+
return html`<div class="error"><h1>{{ error.message }}</h1><button @click="retry()">Retry</button></div>`;
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Navigation
|
|
219
|
+
|
|
220
|
+
### Declarative Navigation
|
|
221
|
+
|
|
222
|
+
```html
|
|
223
|
+
<!-- Basic link -->
|
|
224
|
+
<a href="/products" @routerLink>Products</a>
|
|
225
|
+
|
|
226
|
+
<!-- With active state -->
|
|
227
|
+
<a href="/products" routerActive="active" @routerLink>Products</a>
|
|
228
|
+
|
|
229
|
+
<!-- With prefetch -->
|
|
230
|
+
<a href="/products" data-prefetch="hover" @routerLink>Products</a>
|
|
231
|
+
|
|
232
|
+
<!-- Button navigation -->
|
|
233
|
+
<button href="/dashboard" @routerLink>Go to Dashboard</button>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Programmatic Navigation
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { createRouter } from '@pithyjs/router';
|
|
240
|
+
|
|
241
|
+
const router = createRouter(routes);
|
|
242
|
+
|
|
243
|
+
// Basic navigation
|
|
244
|
+
router.navigate('/products');
|
|
245
|
+
|
|
246
|
+
// With options
|
|
247
|
+
router.navigate('/products/123', {
|
|
248
|
+
replace: true, // Replace history entry
|
|
249
|
+
skipScroll: true, // Don't scroll to top
|
|
250
|
+
state: { from: 'search' }
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// History navigation
|
|
254
|
+
router.back();
|
|
255
|
+
router.forward();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Configuration
|
|
259
|
+
|
|
260
|
+
### Router Options
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const router = createRouter(routes, {
|
|
264
|
+
// Core options
|
|
265
|
+
basePath: '/', // Base path for all routes
|
|
266
|
+
loadingPolicy: 'replace', // 'replace' | 'overlay' | 'progress-only'
|
|
267
|
+
popstateLoading: 'none', // Silent loaders for back/forward
|
|
268
|
+
|
|
269
|
+
// Cache options (default for all routes)
|
|
270
|
+
cache: {
|
|
271
|
+
strategy: 'swr', // 'swr' | 'cache-first' | 'no-store'
|
|
272
|
+
ttl: 300000 // 5 minutes
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
// UX options
|
|
276
|
+
scroll: {
|
|
277
|
+
enableAutoScroll: true,
|
|
278
|
+
scrollBehavior: 'smooth',
|
|
279
|
+
anchorScrollDelay: 100
|
|
280
|
+
},
|
|
281
|
+
focus: {
|
|
282
|
+
enableAutoFocus: true,
|
|
283
|
+
focusTarget: '[data-focus-target]'
|
|
284
|
+
},
|
|
285
|
+
transitions: {
|
|
286
|
+
enabled: true,
|
|
287
|
+
defaultTransition: 'fade',
|
|
288
|
+
duration: 400
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// Prefetch options
|
|
292
|
+
prefetch: {
|
|
293
|
+
hoverDelay: 200,
|
|
294
|
+
visibleMargin: '200px'
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Route-Level Configuration
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// +config.ts
|
|
303
|
+
import { authGuard, adminGuard } from '../guards';
|
|
304
|
+
import { lazy } from '@pithyjs/core';
|
|
305
|
+
|
|
306
|
+
export const guards = [authGuard, adminGuard];
|
|
307
|
+
|
|
308
|
+
export const cache = {
|
|
309
|
+
strategy: 'swr' as const,
|
|
310
|
+
ttl: 60000 // 1 minute
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// Lazy components used in this route
|
|
314
|
+
const Chart = lazy(() => import('./Chart.pithy'), { key: 'lazy-chart' });
|
|
315
|
+
const Table = lazy(() => import('./Table.pithy'), { key: 'lazy-table' });
|
|
316
|
+
|
|
317
|
+
// Prefetch these chunks when route is prefetched
|
|
318
|
+
export const prefetchChunks = [
|
|
319
|
+
() => Chart.load(),
|
|
320
|
+
() => Table.load()
|
|
321
|
+
];
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Advanced Features
|
|
325
|
+
|
|
326
|
+
### Prefetching
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
// Manual prefetch (also prefetches lazy chunks defined in +config.ts)
|
|
330
|
+
await router.prefetchRoute('/products');
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Directive-based (automatic):
|
|
334
|
+
|
|
335
|
+
```html
|
|
336
|
+
<a href="/products" @routerLink data-prefetch="hover">Products</a>
|
|
337
|
+
<a href="/about" @routerLink data-prefetch="visible">About</a>
|
|
338
|
+
<a href="/dashboard" @routerLink data-prefetch="intent">Dashboard</a>
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
When prefetching a route, the router also executes any `prefetchChunks` defined in the route's `+config.ts`. This enables prefetching lazy-loaded components along with route data.
|
|
342
|
+
|
|
343
|
+
### View Transitions
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// Enable globally
|
|
347
|
+
const router = createRouter(routes, {
|
|
348
|
+
transitions: {
|
|
349
|
+
enabled: true,
|
|
350
|
+
defaultTransition: 'fade',
|
|
351
|
+
duration: 300
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
```html
|
|
357
|
+
<!-- Custom transition name -->
|
|
358
|
+
<div data-transition-name="hero">
|
|
359
|
+
<img src="/hero.jpg" alt="Hero">
|
|
360
|
+
</div>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Scroll Restoration
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Automatic scroll restoration for back/forward
|
|
367
|
+
router.navigate('/products'); // Scrolls to top
|
|
368
|
+
|
|
369
|
+
// Manual control
|
|
370
|
+
router.navigate('/products#section-2'); // Scrolls to anchor
|
|
371
|
+
|
|
372
|
+
router.navigate('/products', {
|
|
373
|
+
skipScroll: true // Don't auto-scroll
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## API Reference
|
|
378
|
+
|
|
379
|
+
| API | Signature | Stability | Description |
|
|
380
|
+
| --- | --- | --- | --- |
|
|
381
|
+
| Router | `class Router` | stable | Core router class with navigation transaction state machine |
|
|
382
|
+
| state | `() => Signal<RouterState>` | stable | Get current router state as a reactive signal |
|
|
383
|
+
| path | `() => () => string` | stable | Get current path as a computed reactive signal |
|
|
384
|
+
| params | `() => () => RouteParams` | stable | Get current route params as a computed reactive signal |
|
|
385
|
+
| query | `() => () => QueryParams` | stable | Get current query params as a computed reactive signal |
|
|
386
|
+
| loaderData | `() => () => ResourceState \| null` | stable | Get current loader data as a computed reactive signal |
|
|
387
|
+
| navigate | `(path: string, options?: NavigationOptions) => Promise<boolean>` | stable | Navigate to a path with two-phase commit via the pipeline |
|
|
388
|
+
| back | `() => void` | stable | Go back one entry in browser history |
|
|
389
|
+
| forward | `() => void` | stable | Go forward one entry in browser history |
|
|
390
|
+
| mount | `(rootElement: HTMLElement) => void` | stable | Mount router to a DOM element and activate outlet routing |
|
|
391
|
+
| getActiveNavId | `() => NavigationId` | experimental | Get the active navigation ID for outlet manager integration |
|
|
392
|
+
| isNavActive | `(navId: NavigationId) => boolean` | experimental | Check if a navigation ID is still active |
|
|
393
|
+
| prefetchRoute | `(href: string, signal: AbortSignal) => Promise<void>` | experimental | Prefetch route components using shared module cache |
|
|
394
|
+
| updateRoutes | `(newRoutes: RouteNode[], options?: { source?: string }) => Promise<void>` | stable | Hot-update routes for HMR, preserving router state |
|
|
395
|
+
| prefetchLoader | `(href: string, signal: AbortSignal) => Promise<void>` | experimental | Prefetch route loader data with guard checks |
|
|
396
|
+
| getRouteCacheTTL | `(href: string) => Promise<number \| undefined>` | experimental | Get the configured cache TTL for a specific route |
|
|
397
|
+
| dispose | `() => void` | stable | Dispose router and clean up all listeners, outlets, and navigations |
|
|
398
|
+
| createRouter | `(routes: RouteNode[], config?: RouterConfig) => Router` | stable | Create a router instance with HMR hot-singleton reuse |
|
|
399
|
+
|
|
400
|
+
### Matcher
|
|
401
|
+
|
|
402
|
+
| API | Signature | Stability | Description |
|
|
403
|
+
| --- | --- | --- | --- |
|
|
404
|
+
| parseRoutePattern | `(pattern: string) => RouteSegment[]` | stable | Parse a route pattern into typed segments (static, dynamic, catch-all, group) |
|
|
405
|
+
| calculateRouteScore | `(segments: RouteSegment[]) => number` | stable | Calculate route precedence score for conflict resolution |
|
|
406
|
+
| normalizePath | `(path: string, policy?: TrailingSlashPolicy) => string` | stable | Normalize path according to trailing slash policy |
|
|
407
|
+
| extractParams | `(pathSegments: string[], routeSegments: RouteSegment[], decodeParams?: boolean) => RouteParams` | stable | Extract parameters from a matched path using route segments |
|
|
408
|
+
| matchRoute | `(path: string, pattern: string, config?: RouterConfig) => RouteMatch \| null` | stable | Check if a path matches a route pattern and return match details |
|
|
409
|
+
| findBestMatch | `(path: string, patterns: string[], config?: RouterConfig) => RouteMatch \| null` | stable | Find the best matching route by highest precedence score |
|
|
410
|
+
|
|
411
|
+
### Route Tree
|
|
412
|
+
|
|
413
|
+
| API | Signature | Stability | Description |
|
|
414
|
+
| --- | --- | --- | --- |
|
|
415
|
+
| flattenRouteTree | `(routes: RouteNode[], parent?: RouteNode, ancestors?: RouteNode[]) => FlatRouteEntry[]` | experimental | Flatten hierarchical route tree into flat list with parent references |
|
|
416
|
+
| buildRouteMap | `(flatRoutes: FlatRouteEntry[]) => Map<string, FlatRouteEntry>` | experimental | Build route map from flattened routes for quick lookup by ID |
|
|
417
|
+
| getAncestorChain | `(routeId: string, routeMap: Map<string, FlatRouteEntry>) => RouteNode[]` | experimental | Get full ancestor chain for a route, ordered root to leaf |
|
|
418
|
+
| findMatchingEntry | `(path: string, flatRoutes: FlatRouteEntry[]) => FlatRouteEntry \| null` | experimental | Find matching route entry from flattened routes for a given path |
|
|
419
|
+
| extractAncestorParams | `(path: string, ancestors: RouteNode[]) => RouteParams[]` | experimental | Extract parameters for each ancestor by matching path progressively |
|
|
420
|
+
| findRouteByPattern | `(routes: RouteNode[], pattern: string) => RouteNode \| null` | stable | Find a route node by exact pattern match, searching recursively |
|
|
421
|
+
| collectPatterns | `(routes: RouteNode[]) => string[]` | stable | Collect all route patterns from a nested tree into a flat array |
|
|
422
|
+
| mergeAncestorParams | `(paramsByLevel: RouteParams[]) => RouteParams[]` | experimental | Merge ancestor params cumulatively; child params override parent |
|
|
423
|
+
|
|
424
|
+
### Guards
|
|
425
|
+
|
|
426
|
+
| API | Signature | Stability | Description |
|
|
427
|
+
| --- | --- | --- | --- |
|
|
428
|
+
| RedirectConfig | `interface RedirectConfig` | stable | Redirect configuration for guard results with optional history replacement |
|
|
429
|
+
| GuardManager | `class GuardManager` | stable | Manages route guards with zero-flash navigation and cancellation |
|
|
430
|
+
| executeGuards | `(guards: GuardFunction[], context: GuardContext) => Promise<GuardExecutionResult>` | stable | Execute guards in parallel with proper cancellation support |
|
|
431
|
+
| cancelGuards | `(navId: NavigationId) => void` | stable | Cancel all active guards for a navigation |
|
|
432
|
+
| AuthProvider | `interface AuthProvider` | stable | Authentication provider interface for user state and permissions |
|
|
433
|
+
|
|
434
|
+
### Loaders
|
|
435
|
+
|
|
436
|
+
| API | Signature | Stability | Description |
|
|
437
|
+
| --- | --- | --- | --- |
|
|
438
|
+
| CacheOptions | `interface CacheOptions` | stable | Cache configuration with TTL and strategy for route loaders |
|
|
439
|
+
| LoaderManager | `class LoaderManager` | stable | Manages route loaders with caching and cancellation support |
|
|
440
|
+
| executeLoader | `<T>(routeId: string, loader: LoaderFunction<T>, context: LoaderContext, options?: CacheOptions) => Promise<T>` | stable | Execute a loader with caching and cancellation support |
|
|
441
|
+
| executeRouteLoader | `<T>(routeId: string, loader: LoaderFunction<T>, context: LoaderContext, options?: CacheOptions) => Promise<T>` | stable | Execute a route loader with caching and cancellation via global manager |
|
|
442
|
+
| getResourceCache | `() => ResourceCache` | stable | Get the global resource cache instance |
|
|
443
|
+
|
|
444
|
+
### Prefetching
|
|
445
|
+
|
|
446
|
+
| API | Signature | Stability | Description |
|
|
447
|
+
| --- | --- | --- | --- |
|
|
448
|
+
| PrefetchStrategy | `type PrefetchStrategy` | stable | Strategy options controlling when routes are prefetched |
|
|
449
|
+
| PrefetchConfig | `interface PrefetchConfig` | stable | Fine-tune prefetch behavior with delays, thresholds, and caching |
|
|
450
|
+
| PrefetchManager | `class PrefetchManager` | stable | Intelligent resource prefetching with enhanced heuristics |
|
|
451
|
+
| registerLink | `(element: HTMLElement, strategy: PrefetchStrategy) => void` | stable | Register a link element for prefetching with a given strategy |
|
|
452
|
+
| unregisterLink | `(element: HTMLElement) => void` | stable | Unregister a link and clean up its prefetch listeners |
|
|
453
|
+
| prefetchRoute | `(href: string, strategy: PrefetchStrategy) => Promise<void>` | stable | Prefetch a route with priority handling and deduplication |
|
|
454
|
+
| cancelPrefetch | `(href: string) => void` | stable | Cancel an in-flight prefetch for a specific route |
|
|
455
|
+
| dispose | `() => void` | stable | Clean up all prefetch resources, requests, and timers |
|
|
456
|
+
| getPrefetchManager | `() => PrefetchManager` | stable | Get the lazy-initialized global prefetch manager |
|
|
457
|
+
|
|
458
|
+
### Error Handling
|
|
459
|
+
|
|
460
|
+
| API | Signature | Stability | Description |
|
|
461
|
+
| --- | --- | --- | --- |
|
|
462
|
+
| ErrorComponentProps | `interface ErrorComponentProps` | stable | Props passed to error boundary components |
|
|
463
|
+
| GlobalErrorFallback | `(props: ErrorComponentProps) => Component` | stable | Default error UI when no route-specific error.ts exists |
|
|
464
|
+
| setRouteError | `(routeId: string, err: RouterError) => void` | stable | Set an error for a route, triggering reactive outlet update |
|
|
465
|
+
| clearRouteError | `(routeId: string) => void` | stable | Clear error for a specific route on retry or navigation |
|
|
466
|
+
| clearAllRouteErrors | `() => void` | stable | Clear all route errors on successful navigation |
|
|
467
|
+
| resolveErrorImport | `(node: RouteNode, getParent: ...) => (() => Promise<unknown>) \| null` | internal | Resolve error component by walking parent chain |
|
|
468
|
+
| hasErrorComponent | `(node: RouteNode) => boolean` | internal | Check if a route node has an error component |
|
|
469
|
+
|
|
470
|
+
### Router Class
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
class Router {
|
|
474
|
+
state(): Signal<RouterState>
|
|
475
|
+
path(): () => string
|
|
476
|
+
params(): () => RouteParams
|
|
477
|
+
query(): () => QueryParams
|
|
478
|
+
loaderData(): () => ResourceState | null
|
|
479
|
+
navigate(path: string, options?: NavigationOptions): Promise<boolean>
|
|
480
|
+
back(): void
|
|
481
|
+
forward(): void
|
|
482
|
+
mount(rootElement: HTMLElement): void
|
|
483
|
+
prefetchRoute(href: string, signal: AbortSignal): Promise<void>
|
|
484
|
+
prefetchLoader(href: string, signal: AbortSignal): Promise<void>
|
|
485
|
+
updateRoutes(newRoutes: RouteNode[], options?: { source?: string }): Promise<void>
|
|
486
|
+
dispose(): void
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Types
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
interface RouterConfig {
|
|
494
|
+
basePath?: string;
|
|
495
|
+
loadingPolicy?: 'replace' | 'overlay' | 'progress-only';
|
|
496
|
+
cache?: CacheOptions;
|
|
497
|
+
scroll?: ScrollConfig;
|
|
498
|
+
focus?: FocusConfig;
|
|
499
|
+
transitions?: ViewTransitionConfig;
|
|
500
|
+
// ... more options
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
interface NavigationOptions {
|
|
504
|
+
replace?: boolean;
|
|
505
|
+
skipScroll?: boolean;
|
|
506
|
+
state?: any;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
type GuardFunction = (context: GuardContext) =>
|
|
510
|
+
| boolean
|
|
511
|
+
| string
|
|
512
|
+
| RedirectConfig
|
|
513
|
+
| Promise<boolean | string | RedirectConfig>;
|
|
514
|
+
|
|
515
|
+
type LoaderFunction<T = any> = (context: LoaderContext) =>
|
|
516
|
+
| T
|
|
517
|
+
| Promise<T>;
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## Documentation
|
|
521
|
+
|
|
522
|
+
- **[Implementation Overview](src/router/README.md)** - Architecture and navigation flow
|
|
523
|
+
- **[Core Engine](src/router/core/README.md)** - Router engine and pipeline
|
|
524
|
+
- **[Features](src/router/features/README.md)** - Feature modules overview
|
|
525
|
+
- [Error Handling](src/router/features/errors/README.md)
|
|
526
|
+
- [Route Guards](src/router/features/guards/README.md)
|
|
527
|
+
- [Data Loaders](src/router/features/loaders/README.md)
|
|
528
|
+
- [Outlet System](src/router/features/outlet/README.md)
|
|
529
|
+
- [Prefetch System](src/router/features/prefetch/README.md)
|
|
530
|
+
- [UX Features](src/router/features/ux/README.md)
|
|
531
|
+
- **[Integration Layer](src/router/integration/README.md)** - Directives, HMR, link interception
|
|
532
|
+
- **[Error Handling Guide](ERROR_HANDLING.md)** - Complete error handling documentation
|
|
533
|
+
|
|
534
|
+
## Examples
|
|
535
|
+
|
|
536
|
+
### Basic Navigation
|
|
537
|
+
|
|
538
|
+
```typescript
|
|
539
|
+
import { createRouter } from '@pithyjs/router';
|
|
540
|
+
import { effect } from '@pithyjs/signals';
|
|
541
|
+
|
|
542
|
+
const router = createRouter(routes);
|
|
543
|
+
|
|
544
|
+
// Navigate and check result
|
|
545
|
+
const success = await router.navigate('/about');
|
|
546
|
+
console.log(success); // true
|
|
547
|
+
|
|
548
|
+
// Reactive state — updates automatically on navigation
|
|
549
|
+
const state = router.state();
|
|
550
|
+
effect(() => {
|
|
551
|
+
console.log('Current path:', state().path);
|
|
552
|
+
console.log('Params:', state().params);
|
|
553
|
+
console.log('Query:', state().query);
|
|
554
|
+
});
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Dynamic Route Parameters
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
// Given route pattern: /products/:id
|
|
561
|
+
await router.navigate('/products/laptop-123');
|
|
562
|
+
|
|
563
|
+
const state = router.state();
|
|
564
|
+
console.log(state().params); // { id: 'laptop-123' }
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Guarded Navigation
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
// Guard blocks unauthorized access and redirects
|
|
571
|
+
import type { GuardFunction } from '@pithyjs/router';
|
|
572
|
+
|
|
573
|
+
const authGuard: GuardFunction = async ({ to }) => {
|
|
574
|
+
const user = await getUser();
|
|
575
|
+
if (!user) return `/login?returnUrl=${to.path}`;
|
|
576
|
+
return true;
|
|
577
|
+
};
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
See the [tests/demo-app/](tests/demo-app/) for a complete example application demonstrating:
|
|
581
|
+
|
|
582
|
+
- File-based routing
|
|
583
|
+
- Guards and loaders
|
|
584
|
+
- Error boundaries
|
|
585
|
+
- Nested layouts
|
|
586
|
+
- View transitions
|
|
587
|
+
- Prefetching
|
|
588
|
+
|
|
589
|
+
## Testing
|
|
590
|
+
|
|
591
|
+
The router package has 18 test files with 281+ passing tests. The table below highlights the 15 files that include `@codex:example` markers:
|
|
592
|
+
|
|
593
|
+
| Module | Test File | Markers |
|
|
594
|
+
| --- | --- | --- |
|
|
595
|
+
| Navigation & state | `navigation.test.ts` | 5 |
|
|
596
|
+
| Guards | `guards.test.ts` | 3 |
|
|
597
|
+
| Loaders & caching | `loader.test.ts` | 2 |
|
|
598
|
+
| Route tree | `route-tree.test.ts` | 3 |
|
|
599
|
+
| Outlet mounting | `outlet.test.ts` | 2 |
|
|
600
|
+
| Error resolution | `error-resolution.test.ts` | 2 |
|
|
601
|
+
| Error types | `router-error.test.ts` | 1 |
|
|
602
|
+
| Configuration | `config.test.ts` | 2 |
|
|
603
|
+
| Prefetching | `prefetch.test.ts` | 1 |
|
|
604
|
+
| Scroll & focus | `ux.test.ts` | 2 |
|
|
605
|
+
| Live region a11y | `live-region.test.ts` | 1 |
|
|
606
|
+
| HMR state preservation | `hmr.test.ts` | 1 |
|
|
607
|
+
| Integration flow | `integration.test.ts` | 1 |
|
|
608
|
+
| Directive binding | `directive-binder.test.ts` | 1 |
|
|
609
|
+
| Router dispose | `router-dispose.test.ts` | 1 |
|
|
610
|
+
|
|
611
|
+
```bash
|
|
612
|
+
cd packages/router && pnpm test
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
## License
|
|
616
|
+
|
|
617
|
+
MIT
|
|
618
|
+
|
|
619
|
+
## Contributing
|
|
620
|
+
|
|
621
|
+
See the main [DEVELOPMENT.md](../../docs/DEVELOPMENT.md) for development guidelines.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** @codex
|
|
2
|
+
{
|
|
3
|
+
"id": "pithy.router",
|
|
4
|
+
"title": "PithyJS Router Core",
|
|
5
|
+
"category": "feature"
|
|
6
|
+
}
|
|
7
|
+
*/
|
|
8
|
+
export * from './router/core/types';
|
|
9
|
+
export * from './router/core/matcher';
|
|
10
|
+
export * from './router/core/route-tree';
|
|
11
|
+
export * from './router/core/params';
|
|
12
|
+
export * from './router/core/utils';
|
|
13
|
+
export * from './router/core/router';
|
|
14
|
+
export * from './router/core/config';
|
|
15
|
+
export * from './router/core/history';
|
|
16
|
+
export * from './router/integration/directives';
|
|
17
|
+
export { setGlobalRouter, getGlobalRouter, } from './router/integration/directive-binder';
|
|
18
|
+
export * from './router/features/loaders';
|
|
19
|
+
export * from './router/features/guards';
|
|
20
|
+
export * from './router/features/prefetch';
|
|
21
|
+
export * from './router/integration/hmr';
|
|
22
|
+
export * from './router/integration/link-interceptor';
|
|
23
|
+
export * from './router/features/ux/scroll-focus';
|
|
24
|
+
export * from './router/features/ux/transitions';
|
|
25
|
+
export * from './router/features/errors';
|
|
26
|
+
export { OutletManager, type RouterInstance } from './router/features/outlet';
|
|
27
|
+
export { buildSegmentChain } from './router/features/outlet/segment-chain';
|
|
28
|
+
export { BrowserHistoryAdapter, MemoryHistoryAdapter, } from './router/integration/history-adapter';
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAGF,cAAc,qBAAqB,CAAC;AAGpC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,0BAA0B,CAAC;AAGzC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,iCAAiC,CAAC;AAGhD,OAAO,EACL,eAAe,EACf,eAAe,GAChB,MAAM,uCAAuC,CAAC;AAG/C,cAAc,2BAA2B,CAAC;AAG1C,cAAc,0BAA0B,CAAC;AAGzC,cAAc,4BAA4B,CAAC;AAG3C,cAAc,0BAA0B,CAAC;AAGzC,cAAc,uCAAuC,CAAC;AAGtD,cAAc,mCAAmC,CAAC;AAGlD,cAAc,kCAAkC,CAAC;AAGjD,cAAc,0BAA0B,CAAC;AAGzC,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAG3E,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,sCAAsC,CAAC"}
|