@iyulab/router 0.4.0 → 0.5.1

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.
@@ -29,7 +29,7 @@ declare interface BaseRouteConfig {
29
29
  * }
30
30
  * ```
31
31
  */
32
- render: (info: RouteInfo) => HTMLElement | ReactElement | TemplateResult<1>;
32
+ render?: (info: RouteInfo) => Promise<RenderResult> | RenderResult;
33
33
  /**
34
34
  * 중첩 라우트
35
35
  */
@@ -42,6 +42,20 @@ declare interface BaseRouteConfig {
42
42
  force?: boolean;
43
43
  }
44
44
 
45
+ /**
46
+ * 컨텐츠 로드시 나타나는 에러
47
+ */
48
+ export declare class ContentLoadError extends RouteError {
49
+ constructor(original?: Error | any);
50
+ }
51
+
52
+ /**
53
+ * 컨텐츠 렌더링시 발생하는 에러
54
+ */
55
+ export declare class ContentRenderError extends RouteError {
56
+ constructor(original?: Error | any);
57
+ }
58
+
45
59
  /**
46
60
  * 인덱스 라우트 타입
47
61
  */
@@ -102,7 +116,7 @@ export declare class Link extends LitElement {
102
116
  /**
103
117
  * 페이지를 찾을 수 없을 때 발생하는 에러
104
118
  */
105
- export declare class NotFoundRouteError extends RouteError {
119
+ export declare class NotFoundError extends RouteError {
106
120
  constructor(path: string, original?: Error | any);
107
121
  }
108
122
 
@@ -128,6 +142,13 @@ export declare class Outlet extends LitElement {
128
142
  clear(): void;
129
143
  }
130
144
 
145
+ /**
146
+ * u-outlet 요소를 찾을 수 없을 때 발생하는 에러
147
+ */
148
+ export declare class OutletMissingError extends RouteError {
149
+ constructor();
150
+ }
151
+
131
152
  /**
132
153
  * 경로 라우트 타입
133
154
  */
@@ -150,9 +171,11 @@ declare interface PathRouteConfig extends BaseRouteConfig {
150
171
  declare interface RenderOption {
151
172
  id?: string;
152
173
  force?: boolean;
153
- content: HTMLElement | ReactElement | TemplateResult<1>;
174
+ content: RenderResult;
154
175
  }
155
176
 
177
+ declare type RenderResult = HTMLElement | ReactElement | TemplateResult<1>;
178
+
156
179
  /**
157
180
  * 라우트 시작 이벤트
158
181
  */
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { LitElement, html, css, render } from "lit";
3
3
  import { state, property, customElement } from "lit/decorators.js";
4
4
  import { createRoot } from "react-dom/client";
5
+ import { unsafeSVG } from "lit/directives/unsafe-svg.js";
5
6
  const e = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), n = /* @__PURE__ */ new WeakMap(), t = (e2, t2, o2, l, a) => {
6
7
  const s = a?.[t2];
7
8
  void 0 === s ? (e2[t2] = o2, null == o2 && t2 in HTMLElement.prototype && e2.removeAttribute(t2)) : o2 !== l && ((e3, t3, o3) => {
@@ -270,13 +271,24 @@ class RouteError extends Error {
270
271
  }
271
272
  }
272
273
  }
273
- class NotFoundRouteError extends RouteError {
274
+ class NotFoundError extends RouteError {
274
275
  constructor(path, original) {
275
276
  super(404, `Page not found: ${path}`, original);
276
- this.name = "NotFoundError";
277
- if (Error.captureStackTrace) {
278
- Error.captureStackTrace(this, NotFoundRouteError);
279
- }
277
+ }
278
+ }
279
+ class OutletMissingError extends RouteError {
280
+ constructor() {
281
+ super("OUTLET_MISSING", "Router outlet element not found. Add <u-outlet> to your template.");
282
+ }
283
+ }
284
+ class ContentLoadError extends RouteError {
285
+ constructor(original) {
286
+ super("CONTENT_LOAD_FAILED", "Failed to load route content. Check browser console for details.", original);
287
+ }
288
+ }
289
+ class ContentRenderError extends RouteError {
290
+ constructor(original) {
291
+ super("CONTENT_RENDER_FAILED", "Failed to render route component. Check browser console for details.", original);
280
292
  }
281
293
  }
282
294
  class RouteEvent extends Event {
@@ -312,262 +324,61 @@ class RouteErrorEvent extends RouteEvent {
312
324
  this.error = error;
313
325
  }
314
326
  }
327
+ const __vite_glob_0_0 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M15 8a6.97 6.97 0 0 0-1.71-4.584l-9.874 9.875A7 7 0 0 0 15 8M2.71 12.584l9.874-9.875a7 7 0 0 0-9.874 9.874ZM16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0"/>\n</svg>';
328
+ const __vite_glob_0_1 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5l2.404.961L10.404 2zm3.564 1.426L5.596 5 8 5.961 14.154 3.5zm3.25 1.7-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464z"/>\n</svg>';
329
+ const __vite_glob_0_2 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016.15.15 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.2.2 0 0 1-.054.06.1.1 0 0 1-.066.017H1.146a.1.1 0 0 1-.066-.017.2.2 0 0 1-.054-.06.18.18 0 0 1 .002-.183L7.884 2.073a.15.15 0 0 1 .054-.057m1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767z"/>\n <path d="M7.002 12a1 1 0 1 1 2 0 1 1 0 0 1-2 0M7.1 5.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0z"/>\n</svg>';
330
+ const __vite_glob_0_3 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M8 5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3m4 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3M5.5 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m.5 6a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3"/>\n <path d="M16 8c0 3.15-1.866 2.585-3.567 2.07C11.42 9.763 10.465 9.473 10 10c-.603.683-.475 1.819-.351 2.92C9.826 14.495 9.996 16 8 16a8 8 0 1 1 8-8m-8 7c.611 0 .654-.171.655-.176.078-.146.124-.464.07-1.119-.014-.168-.037-.37-.061-.591-.052-.464-.112-1.005-.118-1.462-.01-.707.083-1.61.704-2.314.369-.417.845-.578 1.272-.618.404-.038.812.026 1.16.104.343.077.702.186 1.025.284l.028.008c.346.105.658.199.953.266.653.148.904.083.991.024C14.717 9.38 15 9.161 15 8a7 7 0 1 0-7 7"/>\n</svg>';
331
+ const __vite_glob_0_4 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M11 5a3 3 0 1 1-6 0 3 3 0 0 1 6 0M8 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4m0 5.996V14H3s-1 0-1-1 1-4 6-4q.845.002 1.544.107a4.5 4.5 0 0 0-.803.918A11 11 0 0 0 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664zM9 13a1 1 0 0 1 1-1v-1a2 2 0 1 1 4 0v1a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1zm3-3a1 1 0 0 0-1 1v1h2v-1a1 1 0 0 0-1-1"/>\n</svg>';
332
+ const __vite_glob_0_5 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>\n</svg>';
333
+ const __vite_glob_0_6 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M8.5 5.6a.5.5 0 1 0-1 0v2.9h-3a.5.5 0 0 0 0 1H8a.5.5 0 0 0 .5-.5z"/>\n <path d="M6.5 1A.5.5 0 0 1 7 .5h2a.5.5 0 0 1 0 1v.57c1.36.196 2.594.78 3.584 1.64l.012-.013.354-.354-.354-.353a.5.5 0 0 1 .707-.708l1.414 1.415a.5.5 0 1 1-.707.707l-.353-.354-.354.354-.013.012A7 7 0 1 1 7 2.071V1.5a.5.5 0 0 1-.5-.5M8 3a6 6 0 1 0 .001 12A6 6 0 0 0 8 3"/>\n</svg>';
334
+ const __vite_glob_0_7 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M10.706 3.294A12.6 12.6 0 0 0 8 3C5.259 3 2.723 3.882.663 5.379a.485.485 0 0 0-.048.736.52.52 0 0 0 .668.05A11.45 11.45 0 0 1 8 4q.946 0 1.852.148zM8 6c-1.905 0-3.68.56-5.166 1.526a.48.48 0 0 0-.063.745.525.525 0 0 0 .652.065 8.45 8.45 0 0 1 3.51-1.27zm2.596 1.404.785-.785q.947.362 1.785.907a.482.482 0 0 1 .063.745.525.525 0 0 1-.652.065 8.5 8.5 0 0 0-1.98-.932zM8 10l.933-.933a6.5 6.5 0 0 1 2.013.637c.285.145.326.524.1.75l-.015.015a.53.53 0 0 1-.611.09A5.5 5.5 0 0 0 8 10m4.905-4.905.747-.747q.886.451 1.685 1.03a.485.485 0 0 1 .047.737.52.52 0 0 1-.668.05 11.5 11.5 0 0 0-1.811-1.07M9.02 11.78c.238.14.236.464.04.66l-.707.706a.5.5 0 0 1-.707 0l-.707-.707c-.195-.195-.197-.518.04-.66A2 2 0 0 1 8 11.5c.374 0 .723.102 1.021.28zm4.355-9.905a.53.53 0 0 1 .75.75l-10.75 10.75a.53.53 0 0 1-.75-.75z"/>\n</svg>';
335
+ const __vite_glob_0_8 = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M16 4.5a4.5 4.5 0 0 1-1.703 3.526L13 5l2.959-1.11q.04.3.041.61"/>\n <path d="M11.5 9c.653 0 1.273-.139 1.833-.39L12 5.5 11 3l3.826-1.53A4.5 4.5 0 0 0 7.29 6.092l-6.116 5.096a2.583 2.583 0 1 0 3.638 3.638L9.908 8.71A4.5 4.5 0 0 0 11.5 9m-1.292-4.361-.596.893.809-.27a.25.25 0 0 1 .287.377l-.596.893.809-.27.158.475-1.5.5a.25.25 0 0 1-.287-.376l.596-.893-.809.27a.25.25 0 0 1-.287-.377l.596-.893-.809.27-.158-.475 1.5-.5a.25.25 0 0 1 .287.376M3 14a1 1 0 1 1 0-2 1 1 0 0 1 0 2"/>\n</svg>';
315
336
  const styles = css`
316
337
  :host {
317
338
  display: flex;
339
+ flex-direction: column;
318
340
  justify-content: center;
319
341
  align-items: center;
320
342
  min-height: 100vh;
321
343
  width: 100%;
322
- padding: 2rem;
323
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
324
- background: var(--route-error-background, linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%));
325
- color: var(--route-error-color, #2d3748);
326
- line-height: 1.6;
327
- }
328
-
329
- .container {
330
- max-width: 520px;
331
- margin: 0 auto;
332
344
  text-align: center;
333
- background: var(--route-error-container-bg, rgba(255, 255, 255, 0.95));
334
- backdrop-filter: blur(10px);
335
- border-radius: var(--route-error-border-radius, 24px);
336
- padding: 3rem 2rem;
337
- box-shadow: var(--route-error-box-shadow, 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04));
338
- border: 1px solid var(--route-error-border, rgba(255, 255, 255, 0.2));
339
- animation: slideUp 0.6s ease-out;
340
- }
341
-
342
- @keyframes slideUp {
343
- from {
344
- opacity: 0;
345
- transform: translateY(30px);
346
- }
347
- to {
348
- opacity: 1;
349
- transform: translateY(0);
350
- }
345
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
346
+ overflow: auto;
347
+ user-select: none;
351
348
  }
352
349
 
353
350
  .icon {
354
- font-size: 5rem;
355
- margin-bottom: 1.5rem;
356
- animation: bounce 0.8s ease-out 0.2s both;
357
- filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
358
- }
359
-
360
- @keyframes bounce {
361
- 0%, 20%, 53%, 80%, 100% {
362
- transform: translate3d(0, 0, 0);
363
- }
364
- 40%, 43% {
365
- transform: translate3d(0, -10px, 0);
366
- }
367
- 70% {
368
- transform: translate3d(0, -5px, 0);
369
- }
370
- 90% {
371
- transform: translate3d(0, -2px, 0);
372
- }
351
+ display: contents;
352
+ font-size: 6rem;
353
+ color: var(--route-error-color, #4a5568);
354
+ opacity: 0.85;
373
355
  }
374
356
 
375
357
  .code {
376
358
  font-size: 2rem;
377
359
  font-weight: 700;
378
- margin-bottom: 1rem;
379
- color: var(--route-error-code-color, #4a5568);
380
- letter-spacing: -0.025em;
360
+ margin: 1rem 0;
361
+ color: var(--route-error-code-color, #1a202c);
362
+ letter-spacing: -0.5px;
381
363
  }
382
364
 
383
365
  .message {
384
- font-size: 1.125rem;
385
- margin-bottom: 2.5rem;
366
+ font-size: 1rem;
386
367
  color: var(--route-error-message-color, #718096);
387
- font-weight: 400;
388
- max-width: 400px;
389
- margin-left: auto;
390
- margin-right: auto;
391
- }
392
-
393
- .actions {
394
- display: flex;
395
- gap: 1rem;
396
- justify-content: center;
397
- flex-wrap: wrap;
398
- }
399
-
400
- .button {
401
- position: relative;
402
- padding: 0.875rem 2rem;
403
- border: none;
404
- border-radius: 12px;
405
- font-size: 0.95rem;
406
- font-weight: 600;
407
- cursor: pointer;
408
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
409
- text-decoration: none;
410
- display: inline-flex;
411
- align-items: center;
412
- justify-content: center;
413
- gap: 0.5rem;
414
- font-family: inherit;
415
- min-width: 120px;
416
- overflow: hidden;
417
- user-select: none;
418
- -webkit-tap-highlight-color: transparent;
419
- }
420
-
421
- .button:first-child {
422
- background: var(--route-error-primary-button-bg, linear-gradient(135deg, #667eea 0%, #764ba2 100%));
423
- color: var(--route-error-primary-button-color, white);
424
- box-shadow: 0 4px 15px 0 rgba(102, 126, 234, 0.4);
425
- }
426
-
427
- .button:first-child:hover {
428
- transform: translateY(-2px);
429
- box-shadow: 0 8px 25px 0 rgba(102, 126, 234, 0.5);
430
- }
431
-
432
- .button:first-child:active {
433
- transform: translateY(0);
434
- }
435
-
436
- .button:last-child {
437
- background: var(--route-error-secondary-button-bg, rgba(255, 255, 255, 0.9));
438
- color: var(--route-error-secondary-button-color, #4a5568);
439
- border: 2px solid var(--route-error-secondary-button-border, rgba(74, 85, 104, 0.2));
440
- backdrop-filter: blur(10px);
441
- }
442
-
443
- .button:last-child:hover {
444
- background: var(--route-error-secondary-button-hover-bg, rgba(255, 255, 255, 1));
445
- border-color: var(--route-error-secondary-button-hover-border, rgba(74, 85, 104, 0.4));
446
- transform: translateY(-1px);
447
- }
448
-
449
- .button:focus-visible {
450
- outline: 2px solid var(--route-error-focus-color, #667eea);
451
- outline-offset: 2px;
452
- }
453
-
454
- .button::before {
455
- content: '';
456
- position: absolute;
457
- top: 0;
458
- left: -100%;
459
- width: 100%;
460
- height: 100%;
461
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
462
- transition: left 0.5s;
463
- }
464
-
465
- .button:hover::before {
466
- left: 100%;
467
- }
468
-
469
- @media (max-width: 640px) {
470
- :host {
471
- padding: 1rem;
472
- }
473
-
474
- .container {
475
- padding: 2rem 1.5rem;
476
- border-radius: 20px;
477
- }
478
-
479
- .icon {
480
- font-size: 4rem;
481
- }
482
-
483
- .code {
484
- font-size: 1.75rem;
485
- }
486
-
487
- .message {
488
- font-size: 1rem;
489
- margin-bottom: 2rem;
490
- }
491
-
492
- .actions {
493
- flex-direction: column;
494
- align-items: center;
495
- gap: 0.75rem;
496
- }
497
-
498
- .button {
499
- width: 100%;
500
- max-width: 280px;
501
- padding: 1rem 2rem;
502
- }
503
- }
504
-
505
- @media (max-width: 480px) {
506
- .container {
507
- margin: 1rem;
508
- padding: 1.5rem 1rem;
509
- }
510
-
511
- .icon {
512
- font-size: 3.5rem;
513
- }
368
+ max-width: 600px;
369
+ line-height: 1.6;
514
370
  }
515
371
 
516
372
  @media (prefers-color-scheme: dark) {
517
- :host {
518
- background: var(--route-error-dark-background, linear-gradient(135deg, #1a202c 0%, #2d3748 100%));
519
- color: var(--route-error-dark-color, #e2e8f0);
520
- }
521
-
522
- .container {
523
- background: var(--route-error-dark-container-bg, rgba(45, 55, 72, 0.95));
524
- border: 1px solid var(--route-error-dark-border, rgba(255, 255, 255, 0.1));
373
+ .icon {
374
+ color: var(--route-error-dark-color, #a0aec0);
375
+ opacity: 0.9;
525
376
  }
526
-
527
377
  .code {
528
378
  color: var(--route-error-dark-code-color, #f7fafc);
529
379
  }
530
-
531
380
  .message {
532
- color: var(--route-error-dark-message-color, #a0aec0);
533
- }
534
-
535
- .button:first-child {
536
- background: var(--route-error-dark-primary-button-bg, linear-gradient(135deg, #553c9a 0%, #764ba2 100%));
537
- box-shadow: 0 4px 15px 0 rgba(85, 60, 154, 0.4);
538
- }
539
-
540
- .button:first-child:hover {
541
- box-shadow: 0 8px 25px 0 rgba(85, 60, 154, 0.5);
542
- }
543
-
544
- .button:last-child {
545
- background: var(--route-error-dark-secondary-button-bg, rgba(74, 85, 104, 0.3));
546
- color: var(--route-error-dark-secondary-button-color, #e2e8f0);
547
- border: 2px solid var(--route-error-dark-secondary-button-border, rgba(226, 232, 240, 0.2));
548
- }
549
-
550
- .button:last-child:hover {
551
- background: var(--route-error-dark-secondary-button-hover-bg, rgba(74, 85, 104, 0.5));
552
- border-color: var(--route-error-dark-secondary-button-hover-border, rgba(226, 232, 240, 0.4));
553
- }
554
- }
555
-
556
- @media (prefers-reduced-motion: reduce) {
557
- .container {
558
- animation: none;
559
- }
560
-
561
- .icon {
562
- animation: none;
563
- }
564
-
565
- .button::before {
566
- display: none;
567
- }
568
-
569
- .button {
570
- transition: none;
381
+ color: var(--route-error-dark-message-color, #cbd5e0);
571
382
  }
572
383
  }
573
384
  `;
@@ -581,34 +392,19 @@ var __decorateClass = (decorators, target, key, kind) => {
581
392
  if (kind && result) __defProp(target, key, result);
582
393
  return result;
583
394
  };
395
+ const icons = Object.entries(/* @__PURE__ */ Object.assign({ "../assets/ban.svg": __vite_glob_0_0, "../assets/box-seam.svg": __vite_glob_0_1, "../assets/exclamation-triangle.svg": __vite_glob_0_2, "../assets/palette.svg": __vite_glob_0_3, "../assets/person-lock.svg": __vite_glob_0_4, "../assets/search.svg": __vite_glob_0_5, "../assets/stopwatch.svg": __vite_glob_0_6, "../assets/wifi-off.svg": __vite_glob_0_7, "../assets/wrench-adjustable.svg": __vite_glob_0_8 })).reduce((acc, [path, content]) => {
396
+ const name = path.split("/").pop()?.replace(".svg", "") || "";
397
+ acc[name] = content;
398
+ return acc;
399
+ }, {});
584
400
  let ErrorPage = class extends LitElement {
585
401
  render() {
586
402
  const error = this.error || this.getDefaultError();
587
403
  const icon = this.getErrorIcon(error.code);
588
404
  return html`
589
- <div class="container" role="alert" aria-live="polite">
590
- <div class="icon" aria-hidden="true">${icon}</div>
591
- <div class="code" aria-label="Error code">${error.code}</div>
592
- <div class="message">${error.message}</div>
593
-
594
- <div class="actions">
595
- <button
596
- class="button"
597
- @click=${this.handleGoBack}
598
- title="Go back to previous page"
599
- aria-label="Go back to previous page">
600
- ← Go Back
601
- </button>
602
-
603
- <button
604
- class="button"
605
- @click=${this.handleRefresh}
606
- title="Refresh the current page"
607
- aria-label="Refresh the current page">
608
- 🔄 Refresh
609
- </button>
610
- </div>
611
- </div>
405
+ <div class="icon">${icon}</div>
406
+ <div class="code">${error.code}</div>
407
+ <div class="message">${error.message}</div>
612
408
  `;
613
409
  }
614
410
  /** 기본 에러 정보 반환 */
@@ -617,31 +413,31 @@ let ErrorPage = class extends LitElement {
617
413
  }
618
414
  /** 에러 코드에 따른 기본 아이콘 반환 */
619
415
  getErrorIcon(code) {
416
+ const codeStr = String(code);
620
417
  const numericCode = typeof code === "string" ? parseInt(code) : code;
418
+ switch (codeStr) {
419
+ case "OUTLET_NOT_FOUND":
420
+ return unsafeSVG(icons["box-seam"] || "📦");
421
+ case "CONTENT_LOAD_FAILED":
422
+ return unsafeSVG(icons["wifi-off"] || "📡");
423
+ case "RENDER_FAILED":
424
+ return unsafeSVG(icons["palette"] || "🎨");
425
+ }
621
426
  switch (numericCode) {
622
427
  case 404:
623
- return "🔍";
428
+ return unsafeSVG(icons["search"] || "🔍");
624
429
  case 403:
625
- return "🔒";
430
+ return unsafeSVG(icons["ban"] || "🚫");
626
431
  case 401:
627
- return "🔑";
432
+ return unsafeSVG(icons["person-lock"] || "🔐");
628
433
  case 429:
629
- return "⏱️";
434
+ return unsafeSVG(icons["stopwatch"] || "⏱️");
630
435
  case 503:
631
- return "🛠️";
632
- case 500:
436
+ return unsafeSVG(icons["wrench-adjustable"] || "🛠️");
633
437
  default:
634
- return "⚠️";
438
+ return unsafeSVG(icons["exclamation-triangle"] || "⚠️");
635
439
  }
636
440
  }
637
- /** 뒤로가기 */
638
- handleGoBack() {
639
- window.history.back();
640
- }
641
- /** 새로고침 */
642
- handleRefresh() {
643
- window.location.reload();
644
- }
645
441
  };
646
442
  ErrorPage.styles = styles;
647
443
  __decorateClass([
@@ -675,7 +471,7 @@ function findOutlet(element) {
675
471
  function findOutletOrThrow(element) {
676
472
  const outlet = findOutlet(element);
677
473
  if (!outlet) {
678
- throw new Error("No Outlet component found in the root element.");
474
+ throw new OutletMissingError();
679
475
  }
680
476
  return outlet;
681
477
  }
@@ -817,6 +613,7 @@ class Router {
817
613
  this._requestID = requestID;
818
614
  const routeInfo = parseUrl(href, this._basepath);
819
615
  if (routeInfo.href === this._routeInfo?.href) return;
616
+ let outlet = void 0;
820
617
  try {
821
618
  if (this._requestID !== requestID) return;
822
619
  window.dispatchEvent(new RouteBeginEvent(routeInfo));
@@ -827,16 +624,26 @@ class Router {
827
624
  }
828
625
  this._routeInfo = routeInfo;
829
626
  window.route = routeInfo;
830
- if (this._requestID !== requestID) return;
627
+ outlet = findOutletOrThrow(this._rootElement);
628
+ let title = void 0;
629
+ let content = null;
630
+ let element = null;
831
631
  if (routes.length === 0) {
832
- throw new NotFoundRouteError(routeInfo.href);
632
+ throw new NotFoundError(routeInfo.href);
833
633
  }
834
- let outlet = findOutletOrThrow(this._rootElement);
835
- let title = void 0;
836
634
  for (const route of routes) {
837
635
  if (this._requestID !== requestID) return;
838
- const content = route.render(routeInfo);
839
- const element = await outlet.renderContent({ id: route.id, content, force: route.force });
636
+ if (!route.render) continue;
637
+ try {
638
+ content = await route.render(routeInfo);
639
+ } catch (LoadError) {
640
+ throw new ContentLoadError(LoadError);
641
+ }
642
+ try {
643
+ element = await outlet.renderContent({ id: route.id, content, force: route.force });
644
+ } catch (renderError) {
645
+ throw new ContentRenderError(renderError);
646
+ }
840
647
  outlet = findOutlet(element) || outlet;
841
648
  title = route.title || title;
842
649
  }
@@ -849,29 +656,36 @@ class Router {
849
656
  }
850
657
  window.dispatchEvent(new RouteDoneEvent(routeInfo));
851
658
  } catch (error) {
852
- const routeError = new RouteError(
659
+ const routeError = error instanceof RouteError ? error : new RouteError(
853
660
  error.status || error.code || "UNKNOWN_ERROR",
854
661
  error.message || "An unexpected error occurred",
855
662
  error
856
663
  );
857
664
  window.dispatchEvent(new RouteErrorEvent(routeError, routeInfo));
858
- console.error("Routing error:", error);
665
+ console.error("Routing error:", error.original || error);
859
666
  try {
860
- const errorEl = new ErrorPage();
861
- errorEl.error = routeError;
862
- document.body.innerHTML = "";
863
- document.body.appendChild(errorEl);
667
+ const errorPage = new ErrorPage();
668
+ errorPage.error = error;
669
+ if (outlet) {
670
+ outlet.renderContent({ id: "#", content: errorPage, force: true });
671
+ } else {
672
+ document.body.innerHTML = "";
673
+ document.body.appendChild(errorPage);
674
+ }
864
675
  } catch (pageError) {
865
676
  console.error("Failed to render error component:", pageError);
866
- console.error("Original error:", error);
677
+ console.error("Original error:", routeError.original || routeError);
867
678
  }
868
679
  }
869
680
  }
870
681
  }
871
682
  export {
683
+ ContentLoadError,
684
+ ContentRenderError,
872
685
  Link,
873
- NotFoundRouteError,
686
+ NotFoundError,
874
687
  Outlet,
688
+ OutletMissingError,
875
689
  RouteBeginEvent,
876
690
  RouteDoneEvent,
877
691
  RouteError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iyulab/router",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "A modern client-side router for web applications with support for Lit and React components",
5
5
  "keywords": [
6
6
  "lit",
@@ -24,12 +24,11 @@
24
24
  "LICENSE"
25
25
  ],
26
26
  "type": "module",
27
- "types": "dist/main.d.ts",
27
+ "types": "dist/index.d.ts",
28
28
  "exports": {
29
29
  ".": {
30
- "types": "./dist/main.d.ts",
31
- "import": "./dist/main.js",
32
- "require": "./dist/main.cjs.js"
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
33
32
  }
34
33
  },
35
34
  "scripts": {
@@ -42,10 +41,9 @@
42
41
  },
43
42
  "devDependencies": {
44
43
  "@lit/react": "^1.0.8",
45
- "@types/node": "^24.10.0",
46
- "@types/react": "^19.2.2",
47
- "@types/react-dom": "^19.2.2",
48
- "tslib": "^2.8.1",
44
+ "@types/node": "^24.10.1",
45
+ "@types/react": "^19.2.4",
46
+ "@types/react-dom": "^19.2.3",
49
47
  "typescript": "^5.9.3",
50
48
  "vite": "^7.2.2",
51
49
  "vite-plugin-dts": "^4.5.4"