@iyulab/router 0.7.6 → 0.9.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/CHANGELOG.md +27 -0
- package/README.md +79 -213
- package/dist/index.d.ts +75 -7
- package/dist/index.js +124 -63
- package/dist/react.d.ts +11 -3
- package/dist/react.js +1 -1
- package/dist/{share-sbAElOI7.js → share-CUGwxZKa.js} +12 -7
- package/package.json +1 -1
- package/skills/iyulab-router/SKILL.md +28 -6
- package/skills/iyulab-router/references/components.md +43 -0
- package/skills/iyulab-router/references/events-and-errors.md +39 -0
- package/skills/iyulab-router/references/guards-and-metadata.md +50 -0
- package/skills/iyulab-router/references/routing-basics.md +40 -0
- package/skills/iyulab-router/references/url-pattern.md +32 -0
- package/skills/iyulab-router/references/REFERENCE.md +0 -175
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.0] - 2026-04-08
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `NavigateOptions` — new optional second parameter for `go()` with `isRedirect`, `replace`, and `state` fields
|
|
7
|
+
- `replace` option — navigate without pushing a new browser history entry (`replaceState`)
|
|
8
|
+
- `state` option — attach custom state object to `history.pushState` / `replaceState`
|
|
9
|
+
- `AccessDeniedError` — new error class (HTTP 403) thrown when an `enter` guard returns `false`; renders an error page with `ACCESS_DENIED` code
|
|
10
|
+
- Redirect cycle detection — logs an error and halts routing if the same URL is visited more than once within a single navigation chain
|
|
11
|
+
- `UErrorPage` now accepts an optional `RouteError` in its constructor for direct instantiation
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Breaking:** `enter` returning `false` now throws `AccessDeniedError` and renders a 403 error page instead of silently aborting navigation
|
|
15
|
+
- Redirect navigations (`isRedirect: true`) use `replaceState` — intermediate redirect URLs are no longer pushed onto the browser history stack
|
|
16
|
+
- Global `enter` guard is skipped on redirect hops — runs only once per user-initiated navigation
|
|
17
|
+
- Route-level `enter` hooks are deduplicated within a redirect chain — each route's `enter` executes at most once per navigation cycle
|
|
18
|
+
- `document.title` is now updated in a `finally` block — title is set regardless of whether routing succeeds or fails
|
|
19
|
+
|
|
20
|
+
## [0.8.0] - 2026-04-08
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Added navigation guards via `enter` hooks at both router level (`RouterConfig.enter`) and route level (`RouteConfig.enter`) with redirect/cancel flow support
|
|
24
|
+
- Added `rel` attribute support to `<u-link>` for secure external navigation patterns (for example `noopener noreferrer`)
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- **Breaking:** Renamed route metadata fields from `meta` to `metadata` (`RouteConfig.metadata`, `RouteContext.metadata`)
|
|
28
|
+
- Updated nested outlet resolution to prefer child outlet discovery inside the current outlet, improving deep nested route rendering behavior
|
|
29
|
+
|
|
3
30
|
## [0.7.6] - 2026-04-02
|
|
4
31
|
|
|
5
32
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @iyulab/router
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Client-side SPA router for Lit and React with URLPattern matching, nested routes, and route lifecycle events.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,270 +10,136 @@ npm install @iyulab/router
|
|
|
10
10
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
|
-
### Basic Setup
|
|
14
|
-
|
|
15
13
|
```typescript
|
|
16
14
|
import { Router } from '@iyulab/router';
|
|
17
15
|
import { html } from 'lit';
|
|
18
16
|
|
|
19
17
|
const router = new Router({
|
|
18
|
+
root: document.body,
|
|
20
19
|
basepath: '/',
|
|
21
20
|
routes: [
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
render: () => html`<home-page></home-page>`
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
path: '/user/:id', // URLPattern route
|
|
28
|
-
render: (routeInfo) => html`<user-page .userId=${routeInfo.params.id}></user-page>`
|
|
29
|
-
}
|
|
21
|
+
{ index: true, render: () => html`<home-page></home-page>` },
|
|
22
|
+
{ path: '/users/:id', render: (ctx) => html`<user-page .id=${ctx.params.id}></user-page>` },
|
|
30
23
|
],
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
### Mixed Framework Support
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
import React from 'react';
|
|
38
|
-
|
|
39
|
-
const routes = [
|
|
40
|
-
// Lit component
|
|
41
|
-
{
|
|
42
|
-
path: '/lit-page',
|
|
43
|
-
render: (routeInfo) => {
|
|
44
|
-
return html`<my-lit-component .routeInfo=${routeInfo}></my-lit-component>`
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
// React component
|
|
48
|
-
{
|
|
49
|
-
path: '/react-page',
|
|
50
|
-
render: (routeInfo) => {
|
|
51
|
-
return ( <MyComponent></MyComponent> )
|
|
52
|
-
}
|
|
24
|
+
fallback: {
|
|
25
|
+
render: (ctx) => html`<error-page .error=${ctx.error}></error-page>`,
|
|
53
26
|
},
|
|
54
|
-
|
|
55
|
-
{
|
|
56
|
-
path: '/element-page',
|
|
57
|
-
render: (routeInfo) => {
|
|
58
|
-
const element = document.createElement('my-element');
|
|
59
|
-
element.data = routeInfo.params;
|
|
60
|
-
return element;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
];
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Nested Routes
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
import { RouteConfig } from '@iyulab/router';
|
|
27
|
+
});
|
|
70
28
|
|
|
71
|
-
|
|
72
|
-
{
|
|
73
|
-
path: '/dashboard',
|
|
74
|
-
render: () => html`<dashboard-layout><u-outlet></u-outlet></dashboard-layout>`,
|
|
75
|
-
children: [
|
|
76
|
-
{
|
|
77
|
-
index: true, // Matches '/dashboard'
|
|
78
|
-
render: () => html`<dashboard-home></dashboard-home>`
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
path: 'settings', // Matches '/dashboard/settings'
|
|
82
|
-
render: () => html`<dashboard-settings></dashboard-settings>`
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
];
|
|
29
|
+
router.go('/users/1');
|
|
87
30
|
```
|
|
88
31
|
|
|
89
32
|
## Skills Usage
|
|
90
33
|
|
|
91
|
-
Install the `iyulab-router` skill
|
|
92
|
-
|
|
93
|
-
**Using GitHub shorthand:**
|
|
34
|
+
Install the `iyulab-router` skill for agent-friendly package guidance.
|
|
94
35
|
|
|
95
36
|
```bash
|
|
96
37
|
npx skills add iyulab/node-router
|
|
97
38
|
```
|
|
98
39
|
|
|
99
|
-
**Using local path (after `npm install`):**
|
|
100
|
-
|
|
101
40
|
```bash
|
|
102
41
|
npx skills add ./node_modules/@iyulab/router
|
|
103
42
|
```
|
|
104
43
|
|
|
105
|
-
##
|
|
44
|
+
## Route Guards
|
|
106
45
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
import { LitElement, html } from 'lit';
|
|
111
|
-
import { customElement } from 'lit/decorators.js';
|
|
112
|
-
|
|
113
|
-
import "@iyulab/router";
|
|
114
|
-
|
|
115
|
-
@customElement('app-root')
|
|
116
|
-
export class AppRoot extends LitElement {
|
|
117
|
-
render() {
|
|
118
|
-
return html`
|
|
119
|
-
<nav>
|
|
120
|
-
<u-link href="/">Home</u-link>
|
|
121
|
-
<u-link href="/about">About</u-link>
|
|
122
|
-
<u-link href="/user/123">User Profile</u-link>
|
|
123
|
-
</nav>
|
|
124
|
-
<main>
|
|
125
|
-
<u-outlet></u-outlet>
|
|
126
|
-
</main>
|
|
127
|
-
`;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### Using with React Components
|
|
133
|
-
|
|
134
|
-
```tsx
|
|
135
|
-
import React from 'react';
|
|
136
|
-
import { UOutlet, ULink } from '@iyulab/router/react';
|
|
137
|
-
|
|
138
|
-
export function AppRoot() {
|
|
139
|
-
return (
|
|
140
|
-
<div>
|
|
141
|
-
<nav>
|
|
142
|
-
<ULink href="/">Home</ULink>
|
|
143
|
-
<ULink href="/about">About</ULink>
|
|
144
|
-
<ULink href="/user/123">User Profile</ULink>
|
|
145
|
-
</nav>
|
|
146
|
-
<main>
|
|
147
|
-
<UOutlet />
|
|
148
|
-
</main>
|
|
149
|
-
</div>
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Error Handling
|
|
155
|
-
|
|
156
|
-
The router provides comprehensive error handling through `FallbackRouteContext`. When a routing error occurs, the fallback render function receives a context with full error information:
|
|
46
|
+
Use `enter` to run guard logic before rendering.
|
|
157
47
|
|
|
158
48
|
```typescript
|
|
159
49
|
const router = new Router({
|
|
160
50
|
root: document.body,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return html`<error-page .message=${message}></error-page>`;
|
|
174
|
-
}
|
|
175
|
-
return html`<error-page .error=${ctx.error}></error-page>`;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
51
|
+
enter: (ctx) => {
|
|
52
|
+
if (!isAuthenticated() && ctx.pathname !== '/login') return '/login';
|
|
53
|
+
return true;
|
|
54
|
+
},
|
|
55
|
+
routes: [
|
|
56
|
+
{ path: '/login', render: () => html`<login-page></login-page>` },
|
|
57
|
+
{
|
|
58
|
+
path: '/admin',
|
|
59
|
+
enter: () => hasRole('admin') || '/forbidden',
|
|
60
|
+
render: () => html`<admin-page></admin-page>`,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
178
63
|
});
|
|
179
64
|
```
|
|
180
65
|
|
|
181
|
-
|
|
182
|
-
- `
|
|
183
|
-
- `
|
|
184
|
-
- `
|
|
66
|
+
`enter` return values:
|
|
67
|
+
- `true` (or `undefined`): continue
|
|
68
|
+
- `false`: cancel navigation
|
|
69
|
+
- `string`: redirect to that path
|
|
185
70
|
|
|
186
71
|
## Route Metadata
|
|
187
72
|
|
|
188
|
-
|
|
73
|
+
Attach metadata to routes using `metadata`. The router merges metadata from parent to child and exposes it on `ctx.metadata`.
|
|
189
74
|
|
|
190
75
|
```typescript
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
76
|
+
const routes = [
|
|
77
|
+
{
|
|
78
|
+
path: '/dashboard',
|
|
79
|
+
metadata: { requiresAuth: true, section: 'dashboard' },
|
|
80
|
+
render: () => html`<dashboard-layout><u-outlet></u-outlet></dashboard-layout>`,
|
|
81
|
+
children: [
|
|
82
|
+
{
|
|
83
|
+
path: 'settings',
|
|
84
|
+
metadata: { tab: 'settings' },
|
|
85
|
+
render: (ctx) => html`<settings-page .metadata=${ctx.metadata}></settings-page>`,
|
|
201
86
|
},
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
meta: { requiresAuth: true, role: 'superadmin' },
|
|
206
|
-
render: (ctx) => {
|
|
207
|
-
// ctx.meta === { requiresAuth: true, layout: 'admin', role: 'superadmin' }
|
|
208
|
-
return html`<admin-settings></admin-settings>`;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
]
|
|
212
|
-
}
|
|
213
|
-
]
|
|
214
|
-
});
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
];
|
|
215
90
|
```
|
|
216
91
|
|
|
217
|
-
|
|
92
|
+
## Nested Routes
|
|
218
93
|
|
|
219
|
-
|
|
94
|
+
Parent routes must render `<u-outlet>` to host child route content.
|
|
220
95
|
|
|
221
|
-
|
|
96
|
+
```typescript
|
|
97
|
+
const routes = [
|
|
98
|
+
{
|
|
99
|
+
path: '/nested',
|
|
100
|
+
render: () => html`<nested-layout><u-outlet></u-outlet></nested-layout>`,
|
|
101
|
+
children: [
|
|
102
|
+
{ index: true, render: () => html`<nested-home></nested-home>` },
|
|
103
|
+
{ path: 'lit', render: () => html`<nested-lit></nested-lit>` },
|
|
104
|
+
{ path: 'react', render: () => <NestedReact /> },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
```
|
|
222
109
|
|
|
223
|
-
|
|
224
|
-
|-------|------|-------------|
|
|
225
|
-
| `route-begin` | `RouteBeginEvent` | Fired when navigation starts |
|
|
226
|
-
| `route-progress` | `RouteProgressEvent` | Fired during async loading (0–100) |
|
|
227
|
-
| `route-done` | `RouteDoneEvent` | Fired when navigation completes successfully |
|
|
228
|
-
| `route-error` | `RouteErrorEvent` | Fired when a routing error occurs |
|
|
110
|
+
## Link and Outlet Components
|
|
229
111
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
window.addEventListener('route-progress', (e: RouteProgressEvent) => {
|
|
233
|
-
progressBar.value = e.progress;
|
|
234
|
-
});
|
|
112
|
+
- `<u-link>`: SPA-aware anchor element
|
|
113
|
+
- `<u-outlet>`: render target for matched route output
|
|
235
114
|
|
|
236
|
-
|
|
237
|
-
window.addEventListener('route-begin', (e: RouteBeginEvent) => {
|
|
238
|
-
console.log('Navigating to:', e.context.pathname);
|
|
239
|
-
});
|
|
115
|
+
`<u-link>` supports `href`, `target`, and `rel`.
|
|
240
116
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
117
|
+
```html
|
|
118
|
+
<u-link href="/docs">Docs</u-link>
|
|
119
|
+
<u-link href="https://example.com" target="_blank" rel="noopener noreferrer">External</u-link>
|
|
120
|
+
```
|
|
244
121
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
122
|
+
React wrappers:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { ULink, UOutlet } from '@iyulab/router/react';
|
|
248
126
|
```
|
|
249
127
|
|
|
250
|
-
##
|
|
128
|
+
## Route Events
|
|
129
|
+
|
|
130
|
+
The router dispatches events on `window`:
|
|
251
131
|
|
|
252
|
-
|
|
132
|
+
- `route-begin`
|
|
133
|
+
- `route-progress`
|
|
134
|
+
- `route-done`
|
|
135
|
+
- `route-error`
|
|
253
136
|
|
|
254
137
|
```typescript
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// Optional parameter
|
|
260
|
-
{ path: '/posts/:category?', render: (ctx) => {
|
|
261
|
-
const category = ctx.params.category || 'all';
|
|
262
|
-
return html`<posts-page .category=${category}></posts-page>`;
|
|
263
|
-
}},
|
|
264
|
-
|
|
265
|
-
// Wildcard (catch-all)
|
|
266
|
-
{ path: '/docs/:path*', render: (ctx) => html`<docs-page .path=${ctx.params.path}></docs-page>` },
|
|
267
|
-
|
|
268
|
-
// Multiple parameters
|
|
269
|
-
{ path: '/org/:orgId/repo/:repoId', render: (ctx) => {
|
|
270
|
-
return html`<repo-page .orgId=${ctx.params.orgId} .repoId=${ctx.params.repoId}></repo-page>`;
|
|
271
|
-
}}
|
|
272
|
-
];
|
|
138
|
+
window.addEventListener('route-progress', (e) => {
|
|
139
|
+
console.log(e.progress);
|
|
140
|
+
});
|
|
273
141
|
```
|
|
274
142
|
|
|
275
|
-
When URL parameters change (e.g., navigating from `/user/1` to `/user/2`), leaf routes (without children) automatically re-render since `force` defaults to `true`. For parent routes with children, set `force: true` explicitly if re-rendering is needed on parameter changes.
|
|
276
|
-
|
|
277
143
|
## License
|
|
278
144
|
|
|
279
|
-
MIT License
|
|
145
|
+
MIT License. See [LICENSE](LICENSE).
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ import { LitElement } from 'lit';
|
|
|
3
3
|
import { PropertyValues } from 'lit';
|
|
4
4
|
import { TemplateResult } from 'lit-html';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* enter 가드가 false를 반환하여 접근이 거부되었을 때 발생하는 에러
|
|
8
|
+
*/
|
|
9
|
+
export declare class AccessDeniedError extends RouteError {
|
|
10
|
+
constructor(path: string);
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
/**
|
|
7
14
|
* 공통 라우트 속성
|
|
8
15
|
*/
|
|
@@ -47,6 +54,17 @@ declare interface BaseRouteConfig {
|
|
|
47
54
|
* ```
|
|
48
55
|
*/
|
|
49
56
|
render?: (ctx: RouteContext) => Promise<unknown> | unknown;
|
|
57
|
+
/**
|
|
58
|
+
* 이 라우트 진입 전에 호출되는 enter 함수입니다.
|
|
59
|
+
* - `string` 반환: 해당 경로로 redirect
|
|
60
|
+
* - `false` 반환: 네비게이션 취소
|
|
61
|
+
* - `true` 반환: 통과
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* { path: '/admin', enter: (ctx) => ctx.metadata.role === 'admin' || '/forbidden' }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
enter?: (ctx: RouteContext) => Promise<string | boolean> | string | boolean;
|
|
50
68
|
/**
|
|
51
69
|
* 라우터 URL 변경시 렌더링을 강제할지 여부
|
|
52
70
|
* - 기본값으로 children을 가질때 false로 설정되며, children이 없을 경우 true로 설정됩니다.
|
|
@@ -63,10 +81,10 @@ declare interface BaseRouteConfig {
|
|
|
63
81
|
* - 인증, SEO, 분석 등의 용도로 사용할 수 있습니다.
|
|
64
82
|
* @example
|
|
65
83
|
* ```typescript
|
|
66
|
-
* { path: '/admin',
|
|
84
|
+
* { path: '/admin', metadata: { requiresAuth: true, role: 'admin' } }
|
|
67
85
|
* ```
|
|
68
86
|
*/
|
|
69
|
-
|
|
87
|
+
metadata?: Record<string, unknown>;
|
|
70
88
|
}
|
|
71
89
|
|
|
72
90
|
/**
|
|
@@ -124,6 +142,32 @@ declare interface IndexRouteConfig extends BaseRouteConfig {
|
|
|
124
142
|
index: true;
|
|
125
143
|
}
|
|
126
144
|
|
|
145
|
+
/**
|
|
146
|
+
* go() 메서드에 전달할 네비게이션 옵션
|
|
147
|
+
*/
|
|
148
|
+
export declare interface NavigateOptions {
|
|
149
|
+
/**
|
|
150
|
+
* 리다이렉트로 인한 네비게이션 여부.
|
|
151
|
+
* - `true`이면 히스토리에 새 항목을 추가하지 않고 현재 항목을 교체합니다(replaceState).
|
|
152
|
+
* - 뒤로가기 버튼이 리다이렉트 경유지를 건너뛰게 됩니다.
|
|
153
|
+
* - 리다이렉트 사이클 감지에 사용됩니다.
|
|
154
|
+
* @default false
|
|
155
|
+
*/
|
|
156
|
+
isRedirect?: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* 히스토리에 새 항목을 추가하지 않고 현재 항목을 교체합니다(replaceState).
|
|
159
|
+
* - `isRedirect`와 달리 리다이렉트 체인 추적에는 영향을 주지 않습니다.
|
|
160
|
+
* @default false
|
|
161
|
+
*/
|
|
162
|
+
replace?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* pushState / replaceState 호출 시 함께 저장할 커스텀 상태 객체.
|
|
165
|
+
* - `history.state`로 다시 읽을 수 있습니다.
|
|
166
|
+
* @example { from: '/login', referrer: 'email-link' }
|
|
167
|
+
*/
|
|
168
|
+
state?: Record<string, unknown>;
|
|
169
|
+
}
|
|
170
|
+
|
|
127
171
|
declare interface NonIndexRouteConfig extends BaseRouteConfig {
|
|
128
172
|
/**
|
|
129
173
|
* 인덱스 라우트가 아님을 나타냅니다.
|
|
@@ -156,8 +200,6 @@ declare interface RenderOption {
|
|
|
156
200
|
id?: string;
|
|
157
201
|
/** 강제 렌더링 여부 */
|
|
158
202
|
force?: boolean;
|
|
159
|
-
/** 렌더링할 값 */
|
|
160
|
-
value: unknown;
|
|
161
203
|
}
|
|
162
204
|
|
|
163
205
|
/**
|
|
@@ -244,7 +286,7 @@ export declare interface RouteContext {
|
|
|
244
286
|
* 매칭된 라우트 체인의 병합된 메타데이터
|
|
245
287
|
* - 부모 라우트에서 자식 라우트 순서로 병합됩니다.
|
|
246
288
|
*/
|
|
247
|
-
|
|
289
|
+
metadata: Record<string, unknown>;
|
|
248
290
|
}
|
|
249
291
|
|
|
250
292
|
/**
|
|
@@ -316,6 +358,8 @@ export declare class Router {
|
|
|
316
358
|
private readonly _basepath;
|
|
317
359
|
private readonly _routes;
|
|
318
360
|
private readonly _fallback?;
|
|
361
|
+
private readonly _enter?;
|
|
362
|
+
private readonly _tracker;
|
|
319
363
|
/** 현재 라우팅 요청 ID */
|
|
320
364
|
private _requestID?;
|
|
321
365
|
/** 현재 라우팅 정보 */
|
|
@@ -332,8 +376,9 @@ export declare class Router {
|
|
|
332
376
|
/**
|
|
333
377
|
* 지정한 경로의 클라이언트 라우팅을 수행합니다. 상대경로일 경우 basepath와 조합되어 이동합니다.
|
|
334
378
|
* @param href 이동할 경로
|
|
379
|
+
* @param options 네비게이션 옵션
|
|
335
380
|
*/
|
|
336
|
-
go(href: string): Promise<
|
|
381
|
+
go(href: string, options?: NavigateOptions): Promise<undefined>;
|
|
337
382
|
/** 브라우저 히스토리 이벤트가 발생시 라우팅 처리 */
|
|
338
383
|
private handleWindowPopstate;
|
|
339
384
|
/** 클릭 이벤트에서 라우터로 처리할 앵커를 찾아 클라이언트 라우팅 수행 */
|
|
@@ -361,6 +406,19 @@ export declare interface RouterConfig {
|
|
|
361
406
|
* - 라우트는 렌더링할 엘리먼트 또는 컴포넌트를 지정합니다.
|
|
362
407
|
*/
|
|
363
408
|
routes?: RouteConfig[];
|
|
409
|
+
/**
|
|
410
|
+
* 모든 라우트 전환 전에 호출되는 글로벌 enter 함수입니다.
|
|
411
|
+
* - `string` 반환: 해당 경로로 redirect
|
|
412
|
+
* - `false` 반환: 네비게이션 취소
|
|
413
|
+
* - `true` 반환: 통과
|
|
414
|
+
* @example
|
|
415
|
+
* ```typescript
|
|
416
|
+
* enter: async (ctx) => {
|
|
417
|
+
* if (!isAuthenticated() && ctx.pathname !== '/login') return '/login';
|
|
418
|
+
* }
|
|
419
|
+
* ```
|
|
420
|
+
*/
|
|
421
|
+
enter?: (ctx: RouteContext) => Promise<string | boolean> | string | boolean;
|
|
364
422
|
/**
|
|
365
423
|
* 라우트 매칭 실패 또는 오류 발생 시 대체 라우트 설정
|
|
366
424
|
* - 지정된 설정이 없을 경우, 기본 오류 페이지가 렌더링됩니다.
|
|
@@ -395,6 +453,16 @@ export declare class ULink extends LitElement {
|
|
|
395
453
|
* - `_top`: 최상위 프레임에서 링크 열기
|
|
396
454
|
*/
|
|
397
455
|
target?: string;
|
|
456
|
+
/**
|
|
457
|
+
* 링크 관계 rel 속성
|
|
458
|
+
*
|
|
459
|
+
* - `noopener`: target이 _blank인 경우 보안 강화 (window.opener 차단)
|
|
460
|
+
* - `noreferrer`: target이 _blank인 경우 보안 강화 + Referer 헤더 제거
|
|
461
|
+
* - `external`: 외부 링크임을 명시 (SEO/접근성에 도움)
|
|
462
|
+
* - `nofollow`: 검색 엔진이 링크를 따라가지 않도록 지시 (SEO에 영향)
|
|
463
|
+
* - 그 외 rel 값도 그대로 전달됩니다.
|
|
464
|
+
*/
|
|
465
|
+
rel?: string;
|
|
398
466
|
/**
|
|
399
467
|
* 링크 대상 URL, 다음 사항에 따라 SPA 라우팅 또는 브라우저 네비게이션이 결정됩니다.
|
|
400
468
|
*
|
|
@@ -436,7 +504,7 @@ export declare class UOutlet extends HTMLElement {
|
|
|
436
504
|
/**
|
|
437
505
|
* 주어진 렌더링 옵션에 따라 컨텐츠를 렌더링합니다.
|
|
438
506
|
*/
|
|
439
|
-
render(
|
|
507
|
+
render(value: unknown, options?: RenderOption): Promise<void>;
|
|
440
508
|
/**
|
|
441
509
|
* 기존 DOM을 삭제하여, 초기 상태로 되돌립니다.
|
|
442
510
|
*/
|