import { ReactiveController } from 'lit';
import { ReactiveControllerHost } from 'lit';
/**
* enter 가드가 false를 반환하여 접근이 거부되었을 때 발생하는 에러
*/
export declare class AccessDeniedError extends RouteError {
constructor(path: string);
}
/**
* 공통 라우트 속성
*/
declare interface BaseRouteConfig {
/**
* 라우터에서 사용하는 식별자
*/
id?: string;
/**
* 브라우저의 타이틀이 설정에 따라 변경됩니다.
*/
title?: string;
/**
* 라우터 경로는 string 또는 URLPattern을 사용할 수 있습니다.
* string일 경우 자동으로 URLPattern으로 변환됩니다.
* @default '/'
* @example
* - "/user/:id/:name"
* - "/user/:id/:name?"
* - "/user/:id/:name*"
* - "/user/:id/:name+"
* @link
* https://developer.mozilla.org/en-US/docs/Web/API/URLPattern
*/
path?: string | URLPattern;
/**
* 라우트 정보를 받아 렌더링 결과를 반환합니다.
* @param ctx 현재 라우팅 정보 및, 진행 상태 콜백을 포함하는 Context 객체가 인자로 전달됩니다.
* @example
* ```typescript
* const route = {
* path: '/user:id',
* render: async (ctx) => {
* // 사용자 정보를 비동기로 가져오는 예시
* const userId = ctx.params.id;
* ctx.progress(30);
* const userData = await fetchUserData(userId);
* ctx.progress(70);
* return html``;
* }
* }
* ```
* @remarks
* `render`는 Lit `TemplateResult`, `HTMLElement`, **또는 React 엘리먼트**를 반환할 수 있습니다.
* React 엘리먼트를 직접 반환하면 내부 ``이 `createRoot`/`root.unmount()`를
* 자동으로 관리합니다 — React 루트를 직접 만들어 컨테이너 `HTMLElement`로 감싸 반환하면
* 이 자동 해제 지점을 우회하게 되어 라우트 전환마다 unmount가 호출되지 않는 누수가 생깁니다.
* @example
* ```tsx
* const route = {
* path: '/users/:id',
* render: (ctx) => , // React 엘리먼트를 직접 반환
* };
* ```
*/
render?: (ctx: RouteContext) => Promise | unknown;
/**
* 이 라우트 진입 전에 호출되는 enter 함수입니다.
* - `string` 반환: 해당 경로로 redirect
* - `false` 반환: 네비게이션 취소
* - `true` 반환: 통과
* @example
* ```typescript
* { path: '/admin', enter: (ctx) => ctx.metadata.role === 'admin' || '/forbidden' }
* ```
*/
enter?: (ctx: RouteContext) => Promise | string | boolean;
/**
* 이 라우트의 화면을 떠나기 전에 호출되는 leave 함수입니다 — `enter` 의 대칭입니다.
* - `false` 반환: 이동 취소(화면과 주소가 그대로 남습니다)
* - `true` 반환: 통과
*
* 화면을 바꾸는 이동(경로나 쿼리가 다른 곳)마다 부릅니다 — 링크 · `go()` · `navigate()` · 라우터를 거치는 선택 채널 ·
* 뒤로·앞으로 가기. 같은 주소와 해시만 다른 이동, 리다이렉트 경유지, 문서를 떠나는 이동(새로 고침 · 다른 사이트 ·
* 탭 닫기 — 그것은 `beforeunload` 의 자리)에는 부르지 않습니다. 뒤로·앞으로 가기는 묻기 전에 원래 항목으로 되돌리고,
* 통과하면 다시 그 항목으로 갑니다(History API 에는 «취소» 가 없습니다).
* 지금 화면의 라우트 사슬에서 자식부터 부모 순으로 부르고, 하나라도 `false` 면 멈춥니다. 던지면 취소로 봅니다.
* @example
* ```typescript
* { path: '/orders/:id', leave: () => !form.dirty || confirm('Discard your changes?') }
* ```
*/
leave?: (ctx: RouteLeaveContext) => Promise | boolean;
/**
* 이 라우트의 콘텐츠를 **언제 새로 만들 것인가**를 정하는 식별 키입니다.
*
* 네비게이션마다 `key(ctx)`를 계산해 직전 값과 비교합니다.
* - 키가 **바뀌면** 기존 콘텐츠를 내리고 새로 마운트합니다.
* - 키가 **같으면** 콘텐츠를 유지한 채 `render(ctx)`의 결과로 **제자리 갱신**합니다 —
* Lit 템플릿은 같은 파트에 다시 렌더(DOM·요소 상태 유지, 바인딩만 갱신), React 엘리먼트는
* 같은 root에 다시 렌더(컴포넌트 상태 유지, props만 갱신), `HTMLElement`는 기존 인스턴스를
* 그대로 둡니다(조정할 수단이 없습니다). 새 `ctx`는 따로 전달되지 않고 `render(ctx)`를
* 통해 도달합니다.
*
* 기본값: 자식 라우트가 없으면 `ctx => ctx.href`(URL이 조금이라도 바뀌면 새로), 자식 라우트가
* 있으면 상수(레이아웃은 유지하고 자식만 바뀝니다).
*
* @example
* ```typescript
* // 쿼리스트링만 바뀌면 페이지를 유지하고 prop만 갱신 — 목록 상태·스크롤이 살아남습니다
* { path: '/orders', key: ctx => ctx.pathname,
* render: ctx => html`` }
* // params가 바뀌어도 유지
* { path: '/orders/:id', key: () => 'orders', render: ctx => html`...` }
* ```
*/
key?: (ctx: RouteContext) => string;
/**
* 경로 매칭시 대소문자 구분 여부
* @default false
*/
ignoreCase?: boolean;
/**
* 라우트에 연결할 메타데이터
* - 인증, SEO, 분석 등의 용도로 사용할 수 있습니다.
* @example
* ```typescript
* { path: '/admin', metadata: { requiresAuth: true, role: 'admin' } }
* ```
*/
metadata?: Record;
}
/**
* 컨텐츠 로드시 나타나는 에러
*/
export declare class ContentLoadError extends RouteError {
constructor(original?: Error | any);
}
/**
* 컨텐츠 렌더링시 발생하는 에러
*/
export declare class ContentRenderError extends RouteError {
constructor(original?: Error | any);
}
/**
* 지금 위치의 라우트 경로(경로 + 쿼리 + 해시) — `RouteContext.path` 와 같은 형식이다.
*
* 라우터 인스턴스 없이 읽는다(모드는 라우터가 적은 히스토리 상태에서). 라우터 이벤트(`route-begin`)를 듣는 위젯은
* 그 이벤트가 **자기가 붙기 전에** 지나갔을 수 있다 — 라우트 결과물로 그려지는 셸이 그렇다. 붙을 때 이것으로 시작한다.
*/
export declare function currentRoutePath(): string;
export declare interface FallbackRouteConfig {
/**
* 브라우저의 타이틀이 설정에 따라 변경됩니다.
* - 함수면 실패마다 그 컨텍스트로 부른다 — 오류에 따라 제목이 달라야 할 때(403 · 404 · 그 밖).
* 고정 문자열은 모든 실패에 같은 제목을 준다.
* - 비거나 빈 문자열을 돌려주면 오류 메시지가 제목이 된다(종전 동작).
* @example
* ```typescript
* title: (ctx) => ctx.error.code === 404 ? 'Page not found' : 'Something went wrong'
* ```
*/
title?: string | ((ctx: FallbackRouteContext) => string | undefined);
/**
* 라우팅 실패 시 표시할 렌더링 결과를 반환합니다.
* - 오류가 발생할 경우 또는 렌더링 결과가 false일 경우 호출됩니다.
* @param ctx 현재 라우팅 정보 및 오류 정보를 포함하는 Context 객체가 인자로 전달됩니다.
* @example
* ```typescript
* const fallbackRoute = {
* title: 'Not Found',
* render: (ctx) => {
* if (ctx.error) {
* return html``;
* }
* return html``;
* }
* }
* ```
*/
render?: (ctx: FallbackRouteContext) => Promise | unknown;
}
export declare interface FallbackRouteContext extends RouteContext {
/**
* 라우팅 에러 정보
* - 라우팅 중 발생한 에러 정보를 포함합니다.
*/
error: RouteError;
}
declare interface IndexRouteConfig extends BaseRouteConfig {
/**
* 현재 경로의 인덱스 라우트임을 나타냅니다.
* - 인덱스 라우트는 부모 경로와 동일한 경로를 가지며, path는 자동으로 설정됩니다.
*/
index: true;
}
/** 이동 하나의 `leave` 판정 — 가드가 Promise 를 돌려주면 `pending`. */
declare type LeaveOutcome = 'allowed' | 'blocked' | 'pending';
/**
* 라우터 인스턴스 없이 클라이언트 이동을 요청한다 — 셸 · 링크 · 인증 만료 처리처럼 라우터를 쥐지 않은 코드의 자리다.
*
* 떠 있는 라우터가 요청을 받아 `go(href, options)` 한다(새 기록 항목 — `options.replace` 면 교체). 받을 라우터가 없으면
* 문서 이동으로 간다. ⚠`history.pushState` + 합성 `popstate` 로 흉내 내지 말 것 — 그 `popstate` 는 앱이 듣는
* «뒤로 가기» 와 구별되지 않고, 상태 없이 쓴 항목은 라우터의 기록 상태(모드 · 항목 키)를 지운다.
*/
export declare function navigate(href: string, options?: NavigateOptions): void;
/**
* go() 메서드에 전달할 네비게이션 옵션
*/
export declare interface NavigateOptions {
/**
* 리다이렉트로 인한 네비게이션 여부.
* - `true`이면 히스토리에 새 항목을 추가하지 않고 현재 항목을 교체합니다(replaceState).
* - 뒤로가기 버튼이 리다이렉트 경유지를 건너뛰게 됩니다.
* - 리다이렉트 사이클 감지에 사용됩니다.
* @default false
*/
isRedirect?: boolean;
/**
* 히스토리에 새 항목을 추가하지 않고 현재 항목을 교체합니다(replaceState).
* - `isRedirect`와 달리 리다이렉트 체인 추적에는 영향을 주지 않습니다.
* @default false
*/
replace?: boolean;
/**
* pushState / replaceState 호출 시 함께 저장할 커스텀 상태 객체.
* - `history.state`로 다시 읽을 수 있습니다 — 뒤로·앞으로 가기나 새로 고침으로 그 기록 항목에 돌아와도 남습니다.
* - 같은 주소로 다시 이동하면 기존 상태 위에 얹힙니다. 다른 주소로의 교체(`replace` · 리다이렉트)는 잇지 않습니다.
* @example { from: '/login', referrer: 'email-link' }
*/
state?: Record;
}
declare interface NonIndexRouteConfig extends BaseRouteConfig {
/**
* 인덱스 라우트가 아님을 나타냅니다.
*/
index?: false;
/**
* 하위 라우트 설정, 재귀적으로 RouteConfig 배열을 가질 수 있습니다.
* - 하위 라우트가 있는 경우, 부모 라우트의 경로를 기준으로 매칭됩니다.
*/
children?: RouteConfig[];
}
/**
* 페이지를 찾을 수 없을 때 발생하는 에러
*/
export declare class NotFoundError extends RouteError {
constructor(path: string, original?: Error | any);
}
/**
* u-outlet 요소를 찾을 수 없을 때 발생하는 에러
*/
export declare class OutletMissingError extends RouteError {
constructor();
}
/** 렌더링 옵션 */
declare interface RenderOption {
/** 교차 렌더링 방지 ID — 어느 라우트의 콘텐츠인가 */
id?: string;
/**
* 라우트의 식별 키(`RouteConfig.key` 의 결과). 같은 라우트 + 같은 키면 기존 콘텐츠를
* 유지한 채 제자리 갱신하고, 바뀌면 내리고 새로 마운트한다.
*/
key?: string;
}
/**
* 라우트 시작 이벤트
*/
export declare class RouteBeginEvent extends RouteEvent {
constructor(context: RouteContext);
}
export declare type RouteConfig = IndexRouteConfig | NonIndexRouteConfig;
/**
* 라우터 정보
*/
export declare interface RouteContext {
/**
* 전체 URL 정보
* - 도메인 이름을 포함한 URL의 전체 경로입니다.
* @example https://www.iyulab.com/home/user/1?name=iyu#profile
*/
href: string;
/**
* URL 도메인 이름
* - URL의 도메인 이름입니다.
* @example https://www.iyulab.com
*/
origin: string;
/**
* 라우터 URL 기본 경로
* - 라우터의 현재 basepath입니다.
* @example
* if basepath is /app/:id
* set basepath to /app/123
*/
basepath: string;
/**
* 라우터 URL 전체 경로
* - 도메인 이름을 제외한 URL의 전체 경로입니다.
* @example /home/user/1?name=iyu#profile
*/
path: string;
/**
* 라우터 URL 절대 경로
* - 쿼리스트링과 해시를 제외한 URL 경로입니다.
* @example /home/user/1
*/
pathname: string;
/**
* 라우터 URL 파라미터
* - URLPattern을 사용하여 파싱된 파라미터입니다.
* @example 만약 URL이 /user/:id/:name 일경우
* ```typescript
* const id = params.id;
* const name = params.name;
* ```
*/
params: {
[key: string]: string | undefined;
};
/**
* 라우터 URL 쿼리스트링
* - URLSearchParams를 사용하여 파싱된 쿼리스트링입니다.
* @example 만약 URL이 ?page=1&size=10 일경우
* ```typescript
* const page = query.get('page');
* const size = query.get('size');
* ```
*/
query: URLSearchParams;
/**
* 라우터 URL 해시
* - URL의 해시값입니다. 해시값은 하나만 사용할 수 있습니다.
* - #profile#user 두개 이상의 해시값은 하나로 취급합니다.
* @example #profile
*/
hash?: string;
/**
* 현재 라우팅의 진행 상태를 업데이트하여, window 객체의 'route-progress' 이벤트를 트리거합니다.
* 여러 라우팅이 호출되는 경우, 가장 최근의 라우팅의 진행 상태만 반영되며, 나머지는 무시됩니다.
* @param value 진행 상태 값 (0~100)
*/
progress: (value: number) => void;
/**
* 매칭된 라우트 체인의 병합된 메타데이터
* - 부모 라우트에서 자식 라우트 순서로 병합됩니다.
*/
metadata: Record;
/**
* 이 화면에 어떻게 왔는가 — 이동 종류와 그 기록 항목.
* - 화면이 «돌아왔는가»(`traverse`)와 «새로 왔는가»(`push`)를 가르는 자리다 — 스크롤·선택을 되돌릴지 정하는 쪽이 읽는다.
*/
navigation: RouteNavigation;
}
/**
* 라우트 완료 이벤트
*/
export declare class RouteDoneEvent extends RouteEvent {
constructor(context: RouteContext);
}
/**
* 라우팅 에러 정보
*/
export declare class RouteError extends Error {
/**
* 에러 코드
* - HTTP 상태 코드 또는 커스텀 에러 코드
* @example 404, 500, 'ROUTE_NOT_FOUND'
*/
code: number | string;
/**
* 원본 에러 객체
* - 원본 Error 객체 또는 예외 정보
*/
original?: Error | any;
/**
* 에러 발생 시간
* - 에러가 발생한 시간 (ISO 8601 형식)
*/
timestamp: string;
constructor(code: number | string, message: string, original?: Error | any);
}
/**
* 라우트 에러 이벤트
*/
export declare class RouteErrorEvent extends RouteEvent {
/** 에러 정보 */
readonly error: RouteError;
constructor(context: RouteContext, error: RouteError);
}
/** 라우터 이벤트 기본 클래스 */
declare abstract class RouteEvent extends Event {
/** 라우팅 정보 */
readonly context: RouteContext;
/** 이벤트 발생 시간 */
readonly timestamp: string;
constructor(type: string, context: RouteContext, cancelable?: boolean);
/** 이벤트가 취소되었는지 확인 */
get cancelled(): boolean;
/** 이벤트 취소 */
cancel(): void;
}
/**
* `leave` 가드가 받는 것 — 지금 화면(`from`)과 가려는 곳(`to`).
*
* `to` 는 주소만 푼 문맥이다 — 아직 라우트를 맞추지 않았으므로 `params`·`metadata` 는 비어 있고 `navigation.type` 은
* 그 이동의 종류다(`push` · `replace` · `traverse`).
*/
export declare interface RouteLeaveContext {
/** 지금 그려진 화면의 문맥 */
from: RouteContext;
/** 가려는 곳 — 주소만 푼 문맥 */
to: RouteContext;
}
export declare interface RouteNavigation {
/** 이동 종류 */
type: RouteNavigationType;
/**
* 기록 항목의 키 — 그 항목에 돌아오면(`traverse` · `reload`) 같은 값이다. `replace` 는 같은 칸이라 키를 잇는다
* (Navigation API 의 `key` 와 같은 뜻). 항목별로 무언가(스크롤 · 선택)를 기억할 때 이것으로 묶는다.
*/
entryKey: string;
}
/**
* 이동 종류 — 플랫폼 Navigation API 의 `NavigationType` 과 같은 어휘다.
* - `push`: 새 기록 항목(링크 · `go()` · 첫 방문)
* - `replace`: 지금 항목을 바꿈(`replace` · 리다이렉트 · 같은 주소로의 이동)
* - `reload`: 새로 고침으로 들어온 첫 라우팅
* - `traverse`: 뒤로·앞으로 가기로 이미 있던 항목에 돌아옴
*/
export declare type RouteNavigationType = 'push' | 'replace' | 'reload' | 'traverse';
/**
* 라우트 진행 이벤트
*/
export declare class RouteProgressEvent extends RouteEvent {
/** 진행 상태 값 (0~100) */
readonly progress: number;
constructor(context: RouteContext, progress: number);
}
/**
* `lit-element`, `react`를 지원하는 SPA 클라이언트 라우터 객체입니다.
*/
export declare class Router {
private readonly _rootElement;
private readonly _basepath;
private readonly _routes;
private readonly _fallback?;
private readonly _enter?;
private readonly _leave?;
private readonly _tracker;
private readonly _mode;
/** 현재 라우팅 요청 ID */
private _requestID?;
/** 현재 라우팅 정보 */
private _context?;
/**
* fallback 이 제목을 바꾸기 직전의 문서 제목 — 다음 성공 라우트가 제목을 갖지 않으면 이것으로 돌아간다.
* 제목 없는 라우트는 문서 제목을 그대로 두므로, 없으면 실패 제목(«Page not found»)이 회복 뒤에도 남는다.
*/
private _titleBeforeFallback?;
/** `RouterConfig.scrollRestoration` 을 켰을 때 — 기록 항목별 스크롤 기억. */
private _scroll?;
/** 지금 화면의 라우트 사슬(부모 → 자식) — `leave` 가드를 부를 대상. 대체 화면이면 비어 있다. */
private _activeRoutes;
/**
* 지금 기록 항목의 순번 — 새 항목마다 하나씩 는다(`history.state.entryIndex`). 뒤로·앞으로 가기가 몇 칸을 건넜는지
* 알아야 `leave` 가 막은 이동을 원래 항목으로 되돌릴 수 있다(History API 는 그 수를 주지 않는다).
*/
private _entryIndex;
/** 가드가 되돌린 이동의 `popstate` 를 기다리는 중이면 — 그 이벤트는 라우팅하지 않고 이것을 부른다. */
private _reverted?;
/** 가드를 통과해 다시 건너는 이동 — 그 `popstate` 는 묻지 않고 라우팅한다. */
private _approvedTraverse;
/** 마지막 이동의 `leave` 판정 — 이동 요청(`navigate()` · 선택 채널)에 동기로 돌려준다. */
private _leaveOutcome;
constructor(config: RouterConfig);
/** 객체를 정리하고 이벤트 리스너를 제거합니다. */
destroy(): void;
/** 라우터의 기본 경로 반환 */
get basepath(): string;
/** 라우트를 주소의 어디에서 읽는가(`RouterConfig.mode`). */
get mode(): RouterMode;
/** 등록된 라우트 정보 반환 */
get routes(): RouteConfig[];
/** 현재 라우팅 정보 반환 */
get context(): RouteContext | undefined;
/**
* 지정한 경로의 클라이언트 라우팅을 수행합니다. 상대경로일 경우 basepath와 조합되어 이동합니다.
* @param href 이동할 경로
* @param options 네비게이션 옵션
* @param routes (internal) 호출자가 이미 계산한 라우트 매칭 결과가 있으면 재사용합니다.
* `handleRootElementClick`이 가로채기 여부 판단을 위해 미리 계산한 결과를 전달해
* 동일 pathname에 대한 getRoutes 중복 호출을 피하는 용도입니다. 외부에서 사용하지 마세요.
*/
go(href: string, options?: NavigateOptions, routes?: RouteConfig[]): Promise;
/**
* 이동 본체. `type` 은 브라우저가 이미 옮긴 항목에 들어갈 때(뒤로·앞으로 가기 · 첫 로드) 호출자가 준다 — 없으면
* 히스토리를 어떻게 쓰는가로 정한다(`replace` 면 교체, 아니면 새 항목).
*/
private run;
/**
* 화면을 떠나도 되는가 — 지금 화면의 라우트 `leave`(자식 → 부모) 다음 글로벌 `leave`. 하나라도 `false` 거나 던지면
* 떠나지 않는다. 화면을 바꾸지 않는 이동(해시만 다름)과 첫 라우팅에는 묻지 않는다.
* 모든 가드가 바로 답하면 `boolean`, 하나라도 Promise 를 돌려주면 `Promise`.
*/
private leaveDecision;
/** 이 이동이 물어야 할 가드 — 지금 화면의 라우트(자식 → 부모) 다음 글로벌. 화면을 떠나지 않거나 첫 라우팅이면 없다. */
private leaveGuardsFor;
/** 브라우저 히스토리 이벤트가 발생시 라우팅 처리 */
private handleWindowPopstate;
/**
* 뒤로·앞으로 가기를 `leave` 에 묻는다 — 브라우저가 이미 항목을 옮겼으므로 먼저 원래 항목으로 되돌리고 묻는다
* (묻는 동안 주소가 떠나는 곳을 가리키지 않게). 통과하면 같은 칸 수만큼 다시 건너고(`false` 를 돌려 이번 이벤트는
* 라우팅하지 않는다 — 다시 건넌 이동이 라우팅한다), 막히면 원래 항목에 머문다.
* 건넌 칸 수를 모르면(라우터가 쓰지 않은 항목) 되돌리지 않고 묻고, 막히면 떠나던 화면의 주소를 새 항목으로 다시 쌓는다.
*/
private confirmTraverse;
/** 라우터 인스턴스 없이 낸 이동 요청(`navigate()` · `u-link`)을 받는다 — 먼저 받은 라우터 하나가 처리한다. */
private handleNavigateRequest;
/** 클릭 이벤트에서 라우터로 처리할 앵커를 찾아 클라이언트 라우팅 수행 */
private handleRootElementClick;
}
/**
* 라우터 설정
*/
export declare interface RouterConfig {
/**
* 라우터가 연결될 최상위 엘리먼트
*/
root: HTMLElement;
/**
* 라우터의 기본 경로
* - 라우터의 기본 경로는 URL의 시작점입니다.
* - URLPattern을 사용하여 경로를 탐색합니다.
* @default '/'
*/
basepath?: string;
/**
* 라우트를 주소의 어디에서 읽는가.
* - `'history'`: 경로(`/app/orders/1`). 서버가 앱의 모든 경로에 같은 문서를 돌려줘야 새로고침·딥링크가 동작한다.
* - `'hash'`: `#` 뒤(`/app/#/orders/1`). 서버는 문서 하나만 서빙하면 된다 — 정적 호스팅에서 서버 설정 없이 딥링크가
* 동작한다. `basepath` 와 라우트 경로는 `#` 뒤에 적용된다. 라우트 안의 조각 식별자(`#section`)는 쓸 수 없다.
* @default 'history'
*/
mode?: 'history' | 'hash';
/**
* 라우트 설정
* - 라우트는 URLPattern을 사용하여 경로를 탐색합니다.
* - 라우트는 렌더링할 엘리먼트 또는 컴포넌트를 지정합니다.
*/
routes?: RouteConfig[];
/**
* 모든 라우트 전환 전에 호출되는 글로벌 enter 함수입니다.
* - `string` 반환: 해당 경로로 redirect
* - `false` 반환: 네비게이션 취소
* - `true` 반환: 통과
* @example
* ```typescript
* enter: async (ctx) => {
* if (!isAuthenticated() && ctx.pathname !== '/login') return '/login';
* }
* ```
*/
enter?: (ctx: RouteContext) => Promise | string | boolean;
/**
* 모든 라우트 전환 전에 호출되는 글로벌 leave 함수입니다 — 라우트별 `leave` 뒤에 부릅니다.
* `false` 면 이동을 취소합니다. 언제 부르는가는 `RouteConfig.leave` 와 같습니다.
*/
leave?: (ctx: RouteLeaveContext) => Promise | boolean;
/**
* 라우트 매칭 실패 또는 오류 발생 시 대체 라우트 설정
* - 지정된 설정이 없을 경우, 기본 오류 페이지가 렌더링됩니다.
*/
fallback?: FallbackRouteConfig;
/**
* `a` 태그 클릭 시 클라이언트 라우팅을 수행할지 여부를 설정합니다.
* @default true
*/
useIntercept?: boolean;
/**
* 초기 로드 시 현재 URL로 라우팅을 자동으로 수행할지 여부를 설정합니다.
* @default true
*/
initialLoad?: boolean;
/**
* 기록 항목마다 스크롤 위치를 기억하고 되돌린다.
* - 뒤로·앞으로 가기와 새로 고침으로 항목에 돌아오면, 화면이 선 뒤 그 항목에서 보던 자리로 — 데이터를 받은 뒤 높이가
* 생기는 화면은 내용이 그 위치까지 자랄 때까지 잠시 기다린다(사람이 먼저 굴리면 멈춘다).
* - 새 이동은 맨 위로(주소에 해시가 있으면 그 요소로) · 교체(`replace`)는 그대로.
* - `container` 는 스크롤하는 상자 — 셸이 문서가 아니라 안쪽 상자를 굴리면 그것을 준다(기본 문서). 켜면
* `history.scrollRestoration` 을 `'manual'` 로 둔다(브라우저의 문서 복원과 겹치지 않게).
* @default false
* @example
* ```typescript
* scrollRestoration: { container: () => document.querySelector('main') }
* ```
*/
scrollRestoration?: boolean | {
container?: () => Element | null | undefined;
};
}
/**
* 라우팅 위치의 원천.
* - `history`: 주소의 경로(`/app/orders/1`)가 곧 라우트다. 서버가 모든 경로에 앱을 돌려줘야 새로고침·딥링크가 동작한다.
* - `hash`: `#` 뒤(`/app/#/orders/1`)가 라우트다. 서버는 문서 하나만 서빙하면 된다(정적 호스팅).
*/
declare type RouterMode = 'history' | 'hash';
export declare interface RouterNavigateRequest {
href: string;
options?: NavigateOptions;
/* Excluded from this release type: leave */
}
/**
* 이름 있는 선택 채널을 얻는다 — 같은 이름이면 같은 채널이다(선택 사항은 처음 만든 쪽의 것).
*
* ```ts
* const order = selection('order', { query: 'order' });
* order.subscribe((id, signal) => id && load(id, { signal })); // 늦은 응답은 signal 로 버린다
* order.set('G-7'); // 주소 ?order=G-7 · 구독자 전부
* ```
*/
export declare function selection(name: string, options?: SelectionOptions): SelectionChannel;
/**
* 이름 있는 선택 채널 — 여러 뷰(목록 · 곁 패널 · 열린 탭)가 «지금 고른 것» 하나를 함께 따라간다.
*
* 이동이 «어느 화면인가» 라면 선택은 «그 화면 안에서 지금 무엇을 보는가» 다. 같은 이름의 채널은 문서에 하나뿐이라,
* 서로 모르는 모듈이 이름으로 같은 선택을 공유한다.
*/
export declare interface SelectionChannel {
readonly name: string;
/** 지금 값(선택 없음이면 `undefined`). */
readonly value: T | undefined;
/** 지금 값이 유효한 동안의 신호 — 이 값으로 시작한 비동기 작업에 넘긴다. */
readonly signal: AbortSignal;
/** 선택을 바꾼다. `query` 가 있으면 주소에도 남긴다(`history`). */
set(value: T | undefined): void;
/** 바뀔 때마다 부른다. `immediate` 면 지금 값으로 한 번 먼저. 해지 함수를 돌려준다. */
subscribe(listener: SelectionListener, options?: {
immediate?: boolean;
}): () => void;
/** 고정할 수 있는 구독 — 뷰마다 «따라가기 / 고정». 지금 값으로 한 번 먼저 부른다. */
follow(listener: SelectionListener): SelectionFollower;
}
/**
* Lit 요소에서 선택 채널을 따라가는 컨트롤러 — 연결된 동안 구독하고 값이 바뀌면 다시 그린다.
*
* ```ts
* private order = new SelectionController(this, selection('order', { query: 'order' }));
* render() { return html`${this.order.value ?? 'none'}`; }
* ```
*/
export declare class SelectionController implements ReactiveController {
private readonly host;
readonly channel: SelectionChannel;
private follower?;
private _value;
private _signal;
constructor(host: ReactiveControllerHost, channel: SelectionChannel);
/** 이 뷰가 보는 값 — 고정 중이면 고정한 때의 값. */
get value(): T | undefined;
get signal(): AbortSignal;
get pinned(): boolean;
pin(): void;
unpin(): void;
hostConnected(): void;
hostDisconnected(): void;
}
/** 한 뷰의 따라가기 — 고정하면 채널이 바뀌어도 그 뷰는 멈춰 있고, 풀면 지금 값으로 다시 맞춘다. */
export declare interface SelectionFollower {
readonly pinned: boolean;
pin(): void;
unpin(): void;
dispose(): void;
}
/** 구독자 — 값과, 그 값이 유효한 동안만 살아 있는 신호(다음 선택이 오면 중단된다 — 늦은 응답 버리기). */
export declare type SelectionListener = (value: T | undefined, signal: AbortSignal) => void;
/**
* 선택 채널의 선택 사항.
*/
export declare interface SelectionOptions {
/**
* 주소의 쿼리 키 — 주면 값이 주소에 남는다(딥링크 · 공유 · 뒤로 가기). 주소가 원천이 되어, 다른 탭에서 연 링크나
* Back 으로 돌아온 항목의 값이 채널에 들어온다. 주지 않으면 이 문서 안의 값일 뿐이다.
*/
query?: string;
/** 쿼리 글자 → 값. 기본은 글자 그대로. `undefined` 를 돌려주면 «선택 없음». */
parse?: (raw: string) => T | undefined;
/** 값 → 쿼리 글자. 기본 `String(value)`. */
format?: (value: T) => string;
/**
* 선택이 바뀔 때 주소를 어떻게 남기는가 — `'replace'`(기본: 선택을 바꿀 때마다 기록이 쌓이지 않는다) ·
* `'push'`(선택 하나가 기록 항목 하나 — Back 이 앞 선택으로 돌아간다).
*/
history?: 'replace' | 'push';
}
/**
* - 클라이언트 라우팅을 지원하는 링크 엘리먼트입니다.
* - 내부 링크는 클라이언트 라우팅을 수행하고, 외부 링크는 브라우저 기본 네비게이션을 사용합니다.
* - Ctrl/Meta/Shift/Alt, 중클릭/우클릭 등은 브라우저 기본 동작(새 탭, 컨텍스트 메뉴 등)을 그대로 유지합니다.
*
* Lit 없이 쓰는 표준 커스텀 엘리먼트다 — 라우터가 Lit 을 필수 의존으로 끌고 오지 않도록(React·요소만 쓰는 앱).
* 값이 바뀌면 그 자리에서 내부 `` 를 다시 맞춘다(비동기 갱신 주기가 없다).
*/
export declare class ULink extends HTMLElement {
static readonly observedAttributes: string[];
/** 외부 링크 여부 */
private isExternal;
private _href?;
private _target?;
private _rel?;
private _navigate?;
private readonly anchor;
constructor();
/**
* 링크 대상 target 속성
*
* - `_self`: 현재 창에서 링크 열기 (기본값)
* - `_blank`: 새 탭/창에서 링크 열기
* - `_parent`: 부모 프레임에서 링크 열기
* - `_top`: 최상위 프레임에서 링크 열기
*/
get target(): string | undefined;
set target(value: string | undefined);
/**
* 링크 관계 rel 속성
*
* - `noopener`: target이 _blank인 경우 보안 강화 (window.opener 차단)
* - `noreferrer`: target이 _blank인 경우 보안 강화 + Referer 헤더 제거
* - `external`: 외부 링크임을 명시 (SEO/접근성에 도움)
* - `nofollow`: 검색 엔진이 링크를 따라가지 않도록 지시 (SEO에 영향)
* - 그 외 rel 값도 그대로 전달됩니다.
*/
get rel(): string | undefined;
set rel(value: string | undefined);
/**
* 링크 대상 URL, 다음 사항에 따라 SPA 라우팅 또는 브라우저 네비게이션이 결정됩니다.
*
* - 속성을 정의하지 않으면 설정에서 지정한 `basepath`로 SPA 라우팅합니다.
* - http(s)로 시작하면 외부 링크로 간주하고 브라우저 네비게이션을 사용합니다.
* - 절대경로(/...)의 경우 `basepath`로 시작하면 SPA 라우팅합니다, 이외 브라우저 네비게이션을 사용합니다.
* - 상대경로는 (basepath + 상대경로)로 결합하여 SPA 라우팅합니다.
* - ?로 시작하면 현재 경로에 쿼리스트링을 추가하여 SPA 라우팅합니다.
* - #으로 시작하면 브라우저 기본 동작을 사용합니다.
*/
get href(): string | undefined;
set href(value: string | undefined);
/**
* 이 링크를 라우터가 처리할지, 브라우저의 문서 이동에 맡길지.
*
* - `router`(기본): 종전 동작 그대로 — 같은 오리진이면 SPA 이동, 아니면 브라우저에 맡긴다.
* - `document`: 라우터가 **가로채지 않는다.** 같은 오리진이지만 SPA 라우트가 아닌 경로
* (정적 문서 사이트, 서버 렌더 페이지, 파일 다운로드 엔드포인트, 인증 리다이렉트)를 가리킬 때 쓴다.
*
* ⚠**「외부 오리진」이 아니라 「다른 문서」다.** 종전에는 이 구분이 **오리진 비교 하나**로만
* 결정돼서, 같은 오리진의 비-SPA 경로를 가리킬 수단이 없었다 — 라우터가 클릭을 가로채고
* 등록되지 않은 라우트이므로 화면이 not-found 로 떨어졌다. 빠져나갈 길이 셋뿐이었고
* (다른 오리진 · `target="_blank"` · `#` 프래그먼트) 셋 다 요구와 다르다:
* 같은 오리진이어야 하고(쿠키·세션·역방향 프록시), **같은 탭**이어야 하며, 다른 문서다.
*
* ```html
* Help
* ```
*
* ⚠**자동 판정을 넓히지 않는다.** 「등록된 라우트와 대조해 미등록이면 문서 이동」도 가능하지만
* 라우트가 늦게 등록되면 판정이 **시점에 의존**하게 된다. 명시 선언이 예측 가능하다.
*/
get navigate(): "router" | "document" | undefined;
set navigate(value: "router" | "document" | undefined);
connectedCallback(): void;
disconnectedCallback(): void;
private handleRouterState;
/**
* 호스트에 세팅된 `aria-current`/`aria-label`은 실제 접근 가능한(포커스 대상)
* 엘리먼트가 아니라 — 그 안쪽 shadow DOM 의 네이티브 ``다. 섀도우 경계를
* 넘지 않으므로 접근성 트리에 자동 반영되지 않는다(실측 — 속성은
* 붙어 있는데 접근성 트리의 `aria-current`는 계속 비어 있음). 그래서 `sync()`가 이
* 값을 읽어 내부 ``에 직접 옮기고, 두 속성을 `observedAttributes`에 둬 연결 후의
* 변경도 반영한다(목록에 없는 속성은 `attributeChangedCallback` 자체가 호출되지 않는다).
*/
attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
/** 내부 `` 를 지금 값으로 맞춘다. */
private sync;
private compute;
/**
* 클릭 가로채기 핸들러
* - 좌클릭(0) + 보조키 없음(ctrl/meta/shift/alt 없음) + target이 _self일 때만 SPA 라우팅 고려
* - 그 외(중클릭/우클릭/보조키/target=_blank 등)는 브라우저 기본 동작 유지
*/
private handleClick;
/** basepath를 state에서 꺼내는 헬퍼 */
private getBasepath;
}
/**
* LitElement 또는 React 컴포넌트를 렌더링해주는 웹컴포넌트 입니다.
*/
export declare class UOutlet extends HTMLElement {
/** 교차 렌더링 방지 id */
private routeId?;
/** 마지막으로 렌더한 라우트의 식별 키 */
private routeKey?;
/** 마운트된 콘텐츠의 종류 */
private kind?;
/** 실제 렌더링 컨텐츠 */
private root?;
/** 진행 중인 render — 다음 render 는 이것이 끝난 뒤 판정한다 */
private pending?;
connectedCallback(): void;
/**
* 주어진 렌더링 옵션에 따라 컨텐츠를 렌더링합니다.
*
* 같은 라우트(`id`)에 같은 키(`key`)로 다시 불리면 **제자리 갱신**한다 — Lit 템플릿은
* 같은 파트에 다시 렌더(요소·상태 유지, 바인딩만 갱신), React 엘리먼트는 같은 root 에 다시
* 렌더(컴포넌트 상태 유지), `HTMLElement` 는 기존 인스턴스를 그대로 둔다. 매번 `reset()`
* 을 먼저 부르던 종전 동작이 «쿼리스트링만 바뀌어도 페이지가 재마운트되는» 원인이었다
* (Lit 의 `render` 도 React 의 `root.render` 도 같은 컨테이너에 다시 부르면 조정한다).
*/
render(value: unknown, options?: RenderOption): Promise;
private mount;
/**
* 방금 그린 최상위 요소들의 첫 렌더를 기다린다 — `render()` 가 끝났다는 것은 «내용이 그려졌다» 여야
* `route-done` 을 듣는 쪽(포커스 배치 등)이 화면을 읽을 수 있다. Lit 요소는 연결된 뒤 자기 템플릿을 비동기로 그린다.
*/
private childrenRendered;
/**
* 기존 DOM을 삭제하여, 초기 상태로 되돌립니다.
*/
reset(): void;
}
export { }
declare global {
interface HTMLElementTagNameMap {
'u-outlet': UOutlet;
}
}
declare global {
interface WindowEventMap {
'u-router-navigate': CustomEvent;
}
}
/**
* 전역 WindowEventMap에 라우터 이벤트 타입
*/
declare global {
interface WindowEventMap {
'route-begin': RouteBeginEvent;
'route-progress': RouteProgressEvent;
'route-done': RouteDoneEvent;
'route-error': RouteErrorEvent;
}
}