olova-router 1.0.19 → 1.0.21

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/dist/index.js CHANGED
@@ -33,6 +33,913 @@ var Link = defineComponent((incomingProps) => {
33
33
  });
34
34
  var Link_default = Link;
35
35
 
36
+ // src/breadcrumbs.ts
37
+ function generateBreadcrumbs(location, labelMap) {
38
+ const breadcrumbs = [];
39
+ const segments = location.path.split("/").filter(Boolean);
40
+ breadcrumbs.push({
41
+ label: labelMap?.home ?? "Home",
42
+ path: "/",
43
+ name: "home"
44
+ });
45
+ let currentPath = "";
46
+ for (const segment of segments) {
47
+ currentPath += `/${segment}`;
48
+ const label = labelMap?.[segment] ?? segment.charAt(0).toUpperCase() + segment.slice(1);
49
+ breadcrumbs.push({
50
+ label,
51
+ path: currentPath
52
+ });
53
+ }
54
+ return breadcrumbs;
55
+ }
56
+ function generateBreadcrumbsFromMatches(matches, labelMap) {
57
+ const breadcrumbs = [];
58
+ for (const match of matches) {
59
+ const label = labelMap?.[match.name ?? ""] ?? match.name ?? match.path;
60
+ breadcrumbs.push({
61
+ label,
62
+ path: match.path,
63
+ name: match.name
64
+ });
65
+ }
66
+ return breadcrumbs;
67
+ }
68
+
69
+ // src/guards.ts
70
+ function createConditionalGuard(condition, redirectTo) {
71
+ return async (context) => {
72
+ const allowed = await condition(context);
73
+ if (!allowed && redirectTo) {
74
+ return redirectTo;
75
+ }
76
+ return allowed;
77
+ };
78
+ }
79
+ function createParamGuard(paramName, validator, redirectTo) {
80
+ return async (context) => {
81
+ const value = context.params[paramName];
82
+ if (!value) {
83
+ return redirectTo ? redirectTo : false;
84
+ }
85
+ const valid = await validator(value);
86
+ if (!valid && redirectTo) {
87
+ return redirectTo;
88
+ }
89
+ return valid;
90
+ };
91
+ }
92
+ function createQueryGuard(queryKey, validator, redirectTo) {
93
+ return async (context) => {
94
+ const value = context.query[queryKey];
95
+ const valid = await validator(value);
96
+ if (!valid && redirectTo) {
97
+ return redirectTo;
98
+ }
99
+ return valid;
100
+ };
101
+ }
102
+ function createCompositeGuard(guards) {
103
+ return async (context) => {
104
+ for (const guard of guards) {
105
+ const result = await guard(context);
106
+ if (result !== true && result !== void 0) {
107
+ return result;
108
+ }
109
+ }
110
+ return true;
111
+ };
112
+ }
113
+ function createDebounceGuard(guard, delayMs = 300) {
114
+ let timeoutId = null;
115
+ let lastContext = null;
116
+ return async (context) => {
117
+ lastContext = context;
118
+ return new Promise((resolve) => {
119
+ if (timeoutId) {
120
+ clearTimeout(timeoutId);
121
+ }
122
+ timeoutId = setTimeout(async () => {
123
+ if (lastContext === context) {
124
+ const result = await guard(context);
125
+ resolve(result);
126
+ }
127
+ }, delayMs);
128
+ });
129
+ };
130
+ }
131
+
132
+ // src/history.ts
133
+ function createRouterHistory(maxSize = 50) {
134
+ const entries = [];
135
+ let current = -1;
136
+ return {
137
+ entries,
138
+ current,
139
+ push(path) {
140
+ entries.splice(current + 1);
141
+ entries.push({ path, timestamp: Date.now() });
142
+ if (entries.length > maxSize) {
143
+ entries.shift();
144
+ } else {
145
+ current++;
146
+ }
147
+ },
148
+ back() {
149
+ if (current > 0) {
150
+ current--;
151
+ return entries[current];
152
+ }
153
+ return null;
154
+ },
155
+ forward() {
156
+ if (current < entries.length - 1) {
157
+ current++;
158
+ return entries[current];
159
+ }
160
+ return null;
161
+ },
162
+ go(delta) {
163
+ const next = current + delta;
164
+ if (next >= 0 && next < entries.length) {
165
+ current = next;
166
+ return entries[current];
167
+ }
168
+ return null;
169
+ },
170
+ clear() {
171
+ entries.length = 0;
172
+ current = -1;
173
+ },
174
+ getHistory() {
175
+ return [...entries];
176
+ }
177
+ };
178
+ }
179
+
180
+ // src/middleware.ts
181
+ function createMiddlewareChain() {
182
+ const middlewares = [];
183
+ return {
184
+ use(middleware) {
185
+ middlewares.push(middleware);
186
+ return this;
187
+ },
188
+ async execute(context) {
189
+ for (const middleware of middlewares) {
190
+ const result = await middleware(context);
191
+ if (result !== void 0 && result !== true) {
192
+ return result;
193
+ }
194
+ }
195
+ return true;
196
+ }
197
+ };
198
+ }
199
+ function createAuthMiddleware(isAuthenticated, redirectTo = "/login") {
200
+ return (context) => {
201
+ if (!isAuthenticated()) {
202
+ return redirectTo;
203
+ }
204
+ return true;
205
+ };
206
+ }
207
+ function createRoleMiddleware(userRole, allowedRoles, redirectTo = "/unauthorized") {
208
+ return (context) => {
209
+ const role = userRole();
210
+ if (!role || !allowedRoles.includes(role)) {
211
+ return redirectTo;
212
+ }
213
+ return true;
214
+ };
215
+ }
216
+ function createAnalyticsMiddleware(trackPageView) {
217
+ return (context) => {
218
+ trackPageView(context.to.path);
219
+ return true;
220
+ };
221
+ }
222
+ function createScrollMiddleware(scrollToTop2 = true) {
223
+ return () => {
224
+ if (scrollToTop2 && typeof window !== "undefined") {
225
+ window.scrollTo({ top: 0, left: 0, behavior: "auto" });
226
+ }
227
+ return true;
228
+ };
229
+ }
230
+ function lazyComponent(loader) {
231
+ let component = null;
232
+ let loading = false;
233
+ let error = null;
234
+ return defineComponent(() => {
235
+ if (!component && !loading && !error) {
236
+ loading = true;
237
+ loader().then((module) => {
238
+ component = module.default;
239
+ loading = false;
240
+ }).catch((err) => {
241
+ error = err;
242
+ loading = false;
243
+ });
244
+ }
245
+ if (error) {
246
+ return {
247
+ template: '<div class="router-error">Failed to load component</div>',
248
+ textBindings: [],
249
+ htmlBindings: [],
250
+ attrBindings: [],
251
+ eventBindings: [],
252
+ slotBindings: [],
253
+ componentBindings: []
254
+ };
255
+ }
256
+ if (loading || !component) {
257
+ return {
258
+ template: '<div class="router-loading">Loading...</div>',
259
+ textBindings: [],
260
+ htmlBindings: [],
261
+ attrBindings: [],
262
+ eventBindings: [],
263
+ slotBindings: [],
264
+ componentBindings: []
265
+ };
266
+ }
267
+ return {
268
+ template: "__O_COMP_lazy_component__",
269
+ textBindings: [],
270
+ htmlBindings: [],
271
+ attrBindings: [],
272
+ eventBindings: [],
273
+ slotBindings: [],
274
+ componentBindings: [
275
+ {
276
+ id: "lazy_component",
277
+ getComponent: () => component,
278
+ getProps: () => ({})
279
+ }
280
+ ]
281
+ };
282
+ });
283
+ }
284
+ function lazyComponentWithFallback(loader, fallback, errorComponent) {
285
+ let component = null;
286
+ let loading = false;
287
+ let error = null;
288
+ return defineComponent(() => {
289
+ if (!component && !loading && !error) {
290
+ loading = true;
291
+ loader().then((module) => {
292
+ component = module.default;
293
+ loading = false;
294
+ }).catch((err) => {
295
+ error = err;
296
+ loading = false;
297
+ });
298
+ }
299
+ if (error && errorComponent) {
300
+ return {
301
+ template: "__O_COMP_error_component__",
302
+ textBindings: [],
303
+ htmlBindings: [],
304
+ attrBindings: [],
305
+ eventBindings: [],
306
+ slotBindings: [],
307
+ componentBindings: [
308
+ {
309
+ id: "error_component",
310
+ getComponent: () => errorComponent,
311
+ getProps: () => ({ error })
312
+ }
313
+ ]
314
+ };
315
+ }
316
+ if (loading || !component) {
317
+ return {
318
+ template: "__O_COMP_fallback_component__",
319
+ textBindings: [],
320
+ htmlBindings: [],
321
+ attrBindings: [],
322
+ eventBindings: [],
323
+ slotBindings: [],
324
+ componentBindings: [
325
+ {
326
+ id: "fallback_component",
327
+ getComponent: () => fallback,
328
+ getProps: () => ({})
329
+ }
330
+ ]
331
+ };
332
+ }
333
+ return {
334
+ template: "__O_COMP_lazy_component__",
335
+ textBindings: [],
336
+ htmlBindings: [],
337
+ attrBindings: [],
338
+ eventBindings: [],
339
+ slotBindings: [],
340
+ componentBindings: [
341
+ {
342
+ id: "lazy_component",
343
+ getComponent: () => component,
344
+ getProps: () => ({})
345
+ }
346
+ ]
347
+ };
348
+ });
349
+ }
350
+
351
+ // src/prefetch.ts
352
+ var prefetchedRoutes = /* @__PURE__ */ new Set();
353
+ var prefetchQueue = [];
354
+ var isPrefetching = false;
355
+ function stringifyTarget(target) {
356
+ if (typeof target === "string") {
357
+ return target;
358
+ }
359
+ const path = target.path ?? "/";
360
+ const query = target.query ? Object.entries(target.query).flatMap(([key, value]) => {
361
+ if (value === null || value === void 0) {
362
+ return [];
363
+ }
364
+ if (Array.isArray(value)) {
365
+ return value.map((v) => `${key}=${encodeURIComponent(String(v))}`);
366
+ }
367
+ return `${key}=${encodeURIComponent(String(value))}`;
368
+ }).join("&") : "";
369
+ const hash = target.hash ? `#${target.hash}` : "";
370
+ return `${path}${query ? `?${query}` : ""}${hash}`;
371
+ }
372
+ async function processPrefetchQueue() {
373
+ if (isPrefetching || prefetchQueue.length === 0) {
374
+ return;
375
+ }
376
+ isPrefetching = true;
377
+ while (prefetchQueue.length > 0) {
378
+ const item = prefetchQueue.shift();
379
+ if (!item) continue;
380
+ const { href } = item;
381
+ if (prefetchedRoutes.has(href)) {
382
+ continue;
383
+ }
384
+ try {
385
+ await fetch(href, { method: "HEAD" });
386
+ prefetchedRoutes.add(href);
387
+ } catch {
388
+ }
389
+ }
390
+ isPrefetching = false;
391
+ }
392
+ function prefetchRoute(to, options = {}) {
393
+ if (typeof window === "undefined") {
394
+ return;
395
+ }
396
+ const href = stringifyTarget(to);
397
+ if (prefetchedRoutes.has(href)) {
398
+ return;
399
+ }
400
+ const priority = options.priority ?? "low";
401
+ prefetchQueue.push({ href, priority });
402
+ prefetchQueue.sort((a, b) => {
403
+ if (!a || !b) return 0;
404
+ if (a.priority === "high" && b.priority === "low") return -1;
405
+ if (a.priority === "low" && b.priority === "high") return 1;
406
+ return 0;
407
+ });
408
+ processPrefetchQueue();
409
+ }
410
+ function isPrefetched(to) {
411
+ const href = stringifyTarget(to);
412
+ return prefetchedRoutes.has(href);
413
+ }
414
+ function clearPrefetchCache() {
415
+ prefetchedRoutes.clear();
416
+ prefetchQueue.length = 0;
417
+ }
418
+
419
+ // src/search-params.ts
420
+ var searchParamsCache = /* @__PURE__ */ new Map();
421
+ function parseSearchParams(search) {
422
+ const params = new URLSearchParams(search);
423
+ const query = {};
424
+ for (const [key, value] of params.entries()) {
425
+ const existing = query[key];
426
+ if (existing === void 0) {
427
+ query[key] = value;
428
+ continue;
429
+ }
430
+ query[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
431
+ }
432
+ return query;
433
+ }
434
+ function useSearchParams() {
435
+ if (typeof window === "undefined") {
436
+ return {
437
+ get: () => null,
438
+ getAll: () => [],
439
+ has: () => false,
440
+ keys: () => [],
441
+ values: () => [],
442
+ entries: () => [],
443
+ toString: () => ""
444
+ };
445
+ }
446
+ const search = window.location.search;
447
+ const cached = searchParamsCache.get(search);
448
+ if (cached) {
449
+ return {
450
+ get: (name) => cached.params.get(name),
451
+ getAll: (name) => cached.params.getAll(name),
452
+ has: (name) => cached.params.has(name),
453
+ keys: () => Array.from(cached.params.keys()),
454
+ values: () => Array.from(cached.params.values()),
455
+ entries: () => Array.from(cached.params.entries()),
456
+ toString: () => cached.params.toString()
457
+ };
458
+ }
459
+ const params = new URLSearchParams(search);
460
+ const query = parseSearchParams(search);
461
+ searchParamsCache.set(search, { params, query });
462
+ return {
463
+ get: (name) => params.get(name),
464
+ getAll: (name) => params.getAll(name),
465
+ has: (name) => params.has(name),
466
+ keys: () => Array.from(params.keys()),
467
+ values: () => Array.from(params.values()),
468
+ entries: () => Array.from(params.entries()),
469
+ toString: () => params.toString()
470
+ };
471
+ }
472
+ function usePathname() {
473
+ if (typeof window === "undefined") {
474
+ return "/";
475
+ }
476
+ return window.location.pathname;
477
+ }
478
+ function useHash() {
479
+ if (typeof window === "undefined") {
480
+ return "";
481
+ }
482
+ return window.location.hash.replace(/^#/, "");
483
+ }
484
+ function clearSearchParamsCache() {
485
+ searchParamsCache.clear();
486
+ }
487
+ var transitionState = state("idle");
488
+ var isPendingNavigation = state(false);
489
+ var currentTransitionError = state(null);
490
+ function useTransition() {
491
+ return {
492
+ isPending: isPendingNavigation.value,
493
+ state: transitionState.value,
494
+ error: currentTransitionError.value
495
+ };
496
+ }
497
+ function startTransition(callback, options = {}) {
498
+ isPendingNavigation.value = true;
499
+ transitionState.value = "pending";
500
+ options.onStart?.();
501
+ try {
502
+ const result = callback();
503
+ if (result instanceof Promise) {
504
+ result.then(() => {
505
+ isPendingNavigation.value = false;
506
+ transitionState.value = "success";
507
+ options.onSuccess?.();
508
+ }).catch((error) => {
509
+ isPendingNavigation.value = false;
510
+ transitionState.value = "error";
511
+ currentTransitionError.value = error;
512
+ options.onError?.(error);
513
+ });
514
+ } else {
515
+ isPendingNavigation.value = false;
516
+ transitionState.value = "success";
517
+ options.onSuccess?.();
518
+ }
519
+ } catch (error) {
520
+ isPendingNavigation.value = false;
521
+ transitionState.value = "error";
522
+ const err = error instanceof Error ? error : new Error(String(error));
523
+ currentTransitionError.value = err;
524
+ options.onError?.(err);
525
+ }
526
+ }
527
+ function resetTransition() {
528
+ isPendingNavigation.value = false;
529
+ transitionState.value = "idle";
530
+ currentTransitionError.value = null;
531
+ }
532
+
533
+ // src/error-boundary.ts
534
+ var errorBoundaryState = {
535
+ hasError: false,
536
+ error: null
537
+ };
538
+ function captureError(error) {
539
+ errorBoundaryState = {
540
+ hasError: true,
541
+ error
542
+ };
543
+ }
544
+ function clearError() {
545
+ errorBoundaryState = {
546
+ hasError: false,
547
+ error: null
548
+ };
549
+ }
550
+ function getErrorBoundaryState() {
551
+ return { ...errorBoundaryState };
552
+ }
553
+ function hasError() {
554
+ return errorBoundaryState.hasError;
555
+ }
556
+ function getError() {
557
+ return errorBoundaryState.error;
558
+ }
559
+
560
+ // src/metadata.ts
561
+ var metadataRegistry = /* @__PURE__ */ new Map();
562
+ var metadataCache = /* @__PURE__ */ new Map();
563
+ function registerRouteMetadata(path, metadata) {
564
+ metadataRegistry.set(path, metadata);
565
+ }
566
+ async function getRouteMetadata(location) {
567
+ const cached = metadataCache.get(location.path);
568
+ if (cached) {
569
+ return cached;
570
+ }
571
+ const metadata = metadataRegistry.get(location.path);
572
+ if (!metadata) {
573
+ return {};
574
+ }
575
+ const resolved = typeof metadata === "function" ? await metadata(location) : metadata;
576
+ metadataCache.set(location.path, resolved);
577
+ return resolved;
578
+ }
579
+ function applyMetadata(metadata) {
580
+ if (typeof document === "undefined") {
581
+ return;
582
+ }
583
+ if (metadata.title) {
584
+ document.title = metadata.title;
585
+ }
586
+ const metaTags = [
587
+ { name: "description", content: metadata.description },
588
+ { name: "keywords", content: Array.isArray(metadata.keywords) ? metadata.keywords.join(", ") : metadata.keywords },
589
+ { name: "author", content: metadata.author },
590
+ { name: "robots", content: metadata.robots },
591
+ { name: "viewport", content: metadata.viewport },
592
+ { name: "og:title", content: metadata.ogTitle },
593
+ { name: "og:description", content: metadata.ogDescription },
594
+ { name: "og:image", content: metadata.ogImage },
595
+ { name: "og:url", content: metadata.ogUrl },
596
+ { name: "og:type", content: metadata.ogType },
597
+ { name: "twitter:card", content: metadata.twitterCard },
598
+ { name: "twitter:title", content: metadata.twitterTitle },
599
+ { name: "twitter:description", content: metadata.twitterDescription },
600
+ { name: "twitter:image", content: metadata.twitterImage },
601
+ { name: "twitter:creator", content: metadata.twitterCreator }
602
+ ];
603
+ for (const tag of metaTags) {
604
+ if (!tag.content) continue;
605
+ let element = document.querySelector(`meta[name="${tag.name}"]`);
606
+ if (!element) {
607
+ element = document.createElement("meta");
608
+ element.setAttribute("name", tag.name);
609
+ document.head.appendChild(element);
610
+ }
611
+ element.setAttribute("content", String(tag.content));
612
+ }
613
+ if (metadata.canonical) {
614
+ let canonical = document.querySelector('link[rel="canonical"]');
615
+ if (!canonical) {
616
+ canonical = document.createElement("link");
617
+ canonical.setAttribute("rel", "canonical");
618
+ document.head.appendChild(canonical);
619
+ }
620
+ canonical.setAttribute("href", metadata.canonical);
621
+ }
622
+ if (metadata.charset) {
623
+ let charsetMeta = document.querySelector("meta[charset]");
624
+ if (!charsetMeta) {
625
+ charsetMeta = document.createElement("meta");
626
+ document.head.insertBefore(charsetMeta, document.head.firstChild);
627
+ }
628
+ charsetMeta.setAttribute("charset", String(metadata.charset));
629
+ }
630
+ }
631
+ function clearMetadataCache() {
632
+ metadataCache.clear();
633
+ }
634
+
635
+ // src/dynamic-routes.ts
636
+ function parseRoutePattern(path) {
637
+ const segments = path.split("/").filter(Boolean);
638
+ const paramNames = [];
639
+ let isDynamic = false;
640
+ let isCatchAll = false;
641
+ let isOptionalCatchAll = false;
642
+ for (const segment of segments) {
643
+ if (segment.startsWith("[[...") && segment.endsWith("]]")) {
644
+ isOptionalCatchAll = true;
645
+ paramNames.push(segment.slice(5, -2));
646
+ isDynamic = true;
647
+ } else if (segment.startsWith("[...") && segment.endsWith("]")) {
648
+ isCatchAll = true;
649
+ paramNames.push(segment.slice(4, -1));
650
+ isDynamic = true;
651
+ } else if (segment.startsWith("[") && segment.endsWith("]")) {
652
+ paramNames.push(segment.slice(1, -1));
653
+ isDynamic = true;
654
+ }
655
+ }
656
+ return {
657
+ pattern: path,
658
+ isDynamic,
659
+ isCatchAll,
660
+ isOptionalCatchAll,
661
+ paramNames
662
+ };
663
+ }
664
+ function matchOptionalCatchAll(pattern, pathname) {
665
+ const patternSegments = pattern.split("/").filter(Boolean);
666
+ const pathSegments = pathname.split("/").filter(Boolean);
667
+ const params = {};
668
+ let catchAllParam = null;
669
+ let catchAllValues = [];
670
+ for (let i = 0; i < patternSegments.length; i++) {
671
+ const segment = patternSegments[i];
672
+ if (segment.startsWith("[[...") && segment.endsWith("]]")) {
673
+ catchAllParam = segment.slice(5, -2);
674
+ catchAllValues = pathSegments.slice(i);
675
+ break;
676
+ }
677
+ if (segment.startsWith("[") && segment.endsWith("]")) {
678
+ const paramName = segment.slice(1, -1);
679
+ if (i < pathSegments.length) {
680
+ params[paramName] = decodeURIComponent(pathSegments[i]);
681
+ } else {
682
+ return null;
683
+ }
684
+ } else if (segment !== pathSegments[i]) {
685
+ return null;
686
+ }
687
+ }
688
+ if (catchAllParam) {
689
+ if (catchAllValues.length > 0) {
690
+ params[catchAllParam] = catchAllValues.map(decodeURIComponent).join("/");
691
+ }
692
+ }
693
+ return params;
694
+ }
695
+ function matchCatchAll(pattern, pathname) {
696
+ const patternSegments = pattern.split("/").filter(Boolean);
697
+ const pathSegments = pathname.split("/").filter(Boolean);
698
+ const params = {};
699
+ let catchAllParam = null;
700
+ let catchAllValues = [];
701
+ for (let i = 0; i < patternSegments.length; i++) {
702
+ const segment = patternSegments[i];
703
+ if (segment.startsWith("[...") && segment.endsWith("]")) {
704
+ catchAllParam = segment.slice(4, -1);
705
+ catchAllValues = pathSegments.slice(i);
706
+ break;
707
+ }
708
+ if (segment.startsWith("[") && segment.endsWith("]")) {
709
+ const paramName = segment.slice(1, -1);
710
+ if (i < pathSegments.length) {
711
+ params[paramName] = decodeURIComponent(pathSegments[i]);
712
+ } else {
713
+ return null;
714
+ }
715
+ } else if (segment !== pathSegments[i]) {
716
+ return null;
717
+ }
718
+ }
719
+ if (catchAllParam && catchAllValues.length === 0) {
720
+ return null;
721
+ }
722
+ if (catchAllParam) {
723
+ params[catchAllParam] = catchAllValues.map(decodeURIComponent).join("/");
724
+ }
725
+ return params;
726
+ }
727
+ function matchDynamicRoute(pattern, pathname) {
728
+ const parsed = parseRoutePattern(pattern);
729
+ if (parsed.isOptionalCatchAll) {
730
+ return matchOptionalCatchAll(pattern, pathname);
731
+ }
732
+ if (parsed.isCatchAll) {
733
+ return matchCatchAll(pattern, pathname);
734
+ }
735
+ const patternSegments = pattern.split("/").filter(Boolean);
736
+ const pathSegments = pathname.split("/").filter(Boolean);
737
+ if (patternSegments.length !== pathSegments.length) {
738
+ return null;
739
+ }
740
+ const params = {};
741
+ for (let i = 0; i < patternSegments.length; i++) {
742
+ const segment = patternSegments[i];
743
+ if (segment.startsWith("[") && segment.endsWith("]")) {
744
+ const paramName = segment.slice(1, -1);
745
+ params[paramName] = decodeURIComponent(pathSegments[i]);
746
+ } else if (segment !== pathSegments[i]) {
747
+ return null;
748
+ }
749
+ }
750
+ return params;
751
+ }
752
+ function buildDynamicPath(pattern, params) {
753
+ const segments = pattern.split("/").map((segment) => {
754
+ if (segment.startsWith("[[...") && segment.endsWith("]]")) {
755
+ const paramName = segment.slice(5, -2);
756
+ const value = params[paramName];
757
+ return value ? encodeURIComponent(String(value)) : "";
758
+ }
759
+ if (segment.startsWith("[...") && segment.endsWith("]")) {
760
+ const paramName = segment.slice(4, -1);
761
+ const value = params[paramName];
762
+ return value ? encodeURIComponent(String(value)) : "";
763
+ }
764
+ if (segment.startsWith("[") && segment.endsWith("]")) {
765
+ const paramName = segment.slice(1, -1);
766
+ const value = params[paramName];
767
+ return value ? encodeURIComponent(String(value)) : segment;
768
+ }
769
+ return segment;
770
+ });
771
+ return "/" + segments.filter(Boolean).join("/");
772
+ }
773
+
774
+ // src/route-groups.ts
775
+ var routeGroups = /* @__PURE__ */ new Map();
776
+ function createRouteGroup(name, routes, options) {
777
+ const group = {
778
+ name,
779
+ routes,
780
+ layout: options?.layout,
781
+ metadata: options?.metadata
782
+ };
783
+ routeGroups.set(name, group);
784
+ return group;
785
+ }
786
+ function getRouteGroup(name) {
787
+ return routeGroups.get(name);
788
+ }
789
+ function getAllRouteGroups() {
790
+ return Array.from(routeGroups.values());
791
+ }
792
+ function removeRouteGroup(name) {
793
+ return routeGroups.delete(name);
794
+ }
795
+ function mergeRouteGroups(...groupNames) {
796
+ const merged = [];
797
+ for (const groupName of groupNames) {
798
+ const group = routeGroups.get(groupName);
799
+ if (group) {
800
+ merged.push(...group.routes);
801
+ }
802
+ }
803
+ return merged;
804
+ }
805
+ function clearRouteGroups() {
806
+ routeGroups.clear();
807
+ }
808
+ function createParallelRoutes(config) {
809
+ return defineComponent((incomingProps) => {
810
+ const props = incomingProps;
811
+ const slots = props.slots ?? config.slots;
812
+ props.defaultSlots ?? config.defaultSlots ?? {};
813
+ const slotEntries = Object.entries(slots);
814
+ const componentBindings = slotEntries.map(([slotName, component]) => ({
815
+ id: `slot_${slotName}`,
816
+ getComponent: () => component,
817
+ getProps: () => ({})
818
+ }));
819
+ return {
820
+ template: "__O_COMP_parallel_routes__",
821
+ textBindings: [],
822
+ htmlBindings: [],
823
+ attrBindings: [],
824
+ eventBindings: [],
825
+ slotBindings: slotEntries.map(([slotName]) => ({
826
+ id: `slot_${slotName}`,
827
+ name: slotName
828
+ })),
829
+ componentBindings
830
+ };
831
+ });
832
+ }
833
+ function useSlot(slotName) {
834
+ return null;
835
+ }
836
+ function renderSlot(slot) {
837
+ return defineComponent(() => ({
838
+ template: "__O_COMP_slot__",
839
+ textBindings: [],
840
+ htmlBindings: [],
841
+ attrBindings: [],
842
+ eventBindings: [],
843
+ slotBindings: [],
844
+ componentBindings: [
845
+ {
846
+ id: "slot_content",
847
+ getComponent: () => slot.component,
848
+ getProps: () => slot.props ?? {}
849
+ }
850
+ ]
851
+ }));
852
+ }
853
+ function conditionalSlot(condition, trueSlot, falseSlot) {
854
+ return condition ? trueSlot : falseSlot ?? defineComponent(() => ({
855
+ template: "",
856
+ textBindings: [],
857
+ htmlBindings: [],
858
+ attrBindings: [],
859
+ eventBindings: [],
860
+ slotBindings: [],
861
+ componentBindings: []
862
+ }));
863
+ }
864
+
865
+ // src/intercepting-routes.ts
866
+ var interceptRules = /* @__PURE__ */ new Map();
867
+ function levelToDepth(level) {
868
+ switch (level) {
869
+ case "same":
870
+ return 0;
871
+ case "parent":
872
+ return 1;
873
+ case "grandparent":
874
+ return 2;
875
+ case "root":
876
+ return Infinity;
877
+ default:
878
+ return 0;
879
+ }
880
+ }
881
+ function getSegmentDepth(path) {
882
+ return path.split("/").filter(Boolean).length;
883
+ }
884
+ function matchPattern(pattern, pathname) {
885
+ const patternSegments = pattern.split("/").filter(Boolean);
886
+ const pathSegments = pathname.split("/").filter(Boolean);
887
+ if (patternSegments.length > pathSegments.length) {
888
+ return false;
889
+ }
890
+ for (let i = 0; i < patternSegments.length; i++) {
891
+ const segment = patternSegments[i];
892
+ if (segment === "*") {
893
+ return true;
894
+ }
895
+ if (segment.startsWith("[") && segment.endsWith("]")) {
896
+ continue;
897
+ }
898
+ if (segment !== pathSegments[i]) {
899
+ return false;
900
+ }
901
+ }
902
+ return true;
903
+ }
904
+ function registerInterceptRule(id, rule) {
905
+ interceptRules.set(id, rule);
906
+ }
907
+ function getInterceptRule(id) {
908
+ return interceptRules.get(id);
909
+ }
910
+ function findInterceptor(pathname, currentPath) {
911
+ const currentDepth = getSegmentDepth(currentPath);
912
+ for (const rule of interceptRules.values()) {
913
+ if (!matchPattern(rule.pattern, pathname)) {
914
+ continue;
915
+ }
916
+ const interceptorDepth = getSegmentDepth(rule.interceptor);
917
+ const levelDepth = levelToDepth(rule.level);
918
+ if (rule.level === "same" && interceptorDepth === currentDepth) {
919
+ return rule;
920
+ }
921
+ if (rule.level === "parent" && interceptorDepth === currentDepth - levelDepth) {
922
+ return rule;
923
+ }
924
+ if (rule.level === "grandparent" && interceptorDepth === currentDepth - levelDepth) {
925
+ return rule;
926
+ }
927
+ if (rule.level === "root" && interceptorDepth === 0) {
928
+ return rule;
929
+ }
930
+ }
931
+ return null;
932
+ }
933
+ function removeInterceptRule(id) {
934
+ return interceptRules.delete(id);
935
+ }
936
+ function clearInterceptRules() {
937
+ interceptRules.clear();
938
+ }
939
+ function getAllInterceptRules() {
940
+ return Array.from(interceptRules.values());
941
+ }
942
+
36
943
  // src/index.ts
37
944
  var ROUTER_CONTEXT = /* @__PURE__ */ Symbol.for("olova.router");
38
945
  var OUTLET_CONTEXT = /* @__PURE__ */ Symbol.for("olova.router.outlet");
@@ -363,7 +1270,7 @@ function resolveTarget(to, mode, base, named) {
363
1270
  hash: to.hash ? `#${String(to.hash).replace(/^#/, "")}` : ""
364
1271
  };
365
1272
  }
366
- function stringifyTarget(target, base) {
1273
+ function stringifyTarget2(target, base) {
367
1274
  if (target.mode === "hash") {
368
1275
  const root = base === "/" ? "" : base;
369
1276
  return `${root}/#${target.path}${target.search}`;
@@ -705,7 +1612,7 @@ var Router = defineComponent((incomingProps) => {
705
1612
  push(to, options = {}) {
706
1613
  const parsed = resolveTarget(to, props.mode ?? "auto", base, config.named);
707
1614
  const next = resolveRoute(config, parsed, currentRoute.location, base);
708
- const href = stringifyTarget(parsed, base);
1615
+ const href = stringifyTarget2(parsed, base);
709
1616
  const method = options.replace ? "replaceState" : "pushState";
710
1617
  if (parsed.mode === "hash") {
711
1618
  const targetHash = href.includes("#") ? href.slice(href.indexOf("#")) : "";
@@ -737,7 +1644,7 @@ var Router = defineComponent((incomingProps) => {
737
1644
  syncFromWindow(false);
738
1645
  },
739
1646
  resolve(to) {
740
- return stringifyTarget(resolveTarget(to, props.mode ?? "auto", base, config.named), base);
1647
+ return stringifyTarget2(resolveTarget(to, props.mode ?? "auto", base, config.named), base);
741
1648
  },
742
1649
  href(to) {
743
1650
  return this.resolve(to);
@@ -756,7 +1663,7 @@ var Router = defineComponent((incomingProps) => {
756
1663
  const next = resolveRoute(config, target, currentRoute.location ?? null, base);
757
1664
  setCurrentRoute(next);
758
1665
  if (replace) {
759
- const href = stringifyTarget(target, base);
1666
+ const href = stringifyTarget2(target, base);
760
1667
  window.history.replaceState({}, "", href);
761
1668
  }
762
1669
  applyScroll(target.hash, props.scroll ?? true);
@@ -818,6 +1725,6 @@ var BrowserRouter = createRouterVariant("history");
818
1725
  var HashRouter = createRouterVariant("hash");
819
1726
  var src_default = Router;
820
1727
 
821
- export { BrowserRouter, HashRouter, Link_default as Link, Outlet, src_default as default, isActiveHref, link, matchPath, navigate, resolveHref, useParams, useQuery, useRoute, useRouter };
1728
+ export { BrowserRouter, HashRouter, Link_default as Link, Outlet, applyMetadata, buildDynamicPath, captureError, clearError, clearInterceptRules, clearMetadataCache, clearPrefetchCache, clearRouteGroups, clearSearchParamsCache, conditionalSlot, createAnalyticsMiddleware, createAuthMiddleware, createCompositeGuard, createConditionalGuard, createDebounceGuard, createMiddlewareChain, createParallelRoutes, createParamGuard, createQueryGuard, createRoleMiddleware, createRouteGroup, createRouterHistory, createScrollMiddleware, src_default as default, findInterceptor, generateBreadcrumbs, generateBreadcrumbsFromMatches, getAllInterceptRules, getAllRouteGroups, getError, getErrorBoundaryState, getInterceptRule, getRouteGroup, getRouteMetadata, hasError, isActiveHref, isPrefetched, lazyComponent, lazyComponentWithFallback, link, matchDynamicRoute, matchPath, mergeRouteGroups, navigate, parseRoutePattern, prefetchRoute, registerInterceptRule, registerRouteMetadata, removeInterceptRule, removeRouteGroup, renderSlot, resetTransition, resolveHref, startTransition, useHash, useParams, usePathname, useQuery, useRoute, useRouter, useSearchParams, useSlot, useTransition };
822
1729
  //# sourceMappingURL=index.js.map
823
1730
  //# sourceMappingURL=index.js.map