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; } }