sibujs 1.0.6 → 1.0.7

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/plugins.cjs CHANGED
@@ -1290,14 +1290,17 @@ var NavigationController = class {
1290
1290
  var RouteMatcher = class {
1291
1291
  constructor(routes) {
1292
1292
  this.routeTrie = /* @__PURE__ */ new Map();
1293
+ this.parentChain = /* @__PURE__ */ new Map();
1293
1294
  this.namedRoutes = /* @__PURE__ */ new Map();
1294
1295
  this.compiledPatterns = new LRUCache(50);
1295
1296
  this.buildIndex(routes);
1296
1297
  }
1297
- buildIndex(routes, parentPath = "") {
1298
+ buildIndex(routes, parentPath = "", ancestors = []) {
1298
1299
  for (const route2 of routes) {
1299
1300
  const fullPath = parentPath + route2.path;
1301
+ const chain = [...ancestors, route2];
1300
1302
  this.routeTrie.set(fullPath, route2);
1303
+ this.parentChain.set(fullPath, chain);
1301
1304
  if (route2.name) {
1302
1305
  this.namedRoutes.set(route2.name, route2);
1303
1306
  }
@@ -1305,22 +1308,23 @@ var RouteMatcher = class {
1305
1308
  const aliases = Array.isArray(route2.alias) ? route2.alias : [route2.alias];
1306
1309
  for (const alias of aliases) {
1307
1310
  this.routeTrie.set(parentPath + alias, route2);
1311
+ this.parentChain.set(parentPath + alias, chain);
1308
1312
  }
1309
1313
  }
1310
1314
  if (route2.children?.length) {
1311
- this.buildIndex(route2.children, fullPath);
1315
+ this.buildIndex(route2.children, fullPath, chain);
1312
1316
  }
1313
1317
  }
1314
1318
  }
1315
1319
  match(path2) {
1316
1320
  const exactMatch = this.routeTrie.get(path2);
1317
1321
  if (exactMatch) {
1318
- return { route: exactMatch, params: {}, matched: [exactMatch] };
1322
+ return { route: exactMatch, params: {}, matched: this.parentChain.get(path2) || [exactMatch] };
1319
1323
  }
1320
1324
  for (const [routePath, route2] of this.routeTrie) {
1321
1325
  const match = this.matchPattern(path2, routePath);
1322
1326
  if (match) {
1323
- return { route: route2, params: match.params, matched: [route2] };
1327
+ return { route: route2, params: match.params, matched: this.parentChain.get(routePath) || [route2] };
1324
1328
  }
1325
1329
  }
1326
1330
  return null;
@@ -1378,22 +1382,27 @@ var RouteMatcher = class {
1378
1382
  }
1379
1383
  rebuild(routes) {
1380
1384
  this.routeTrie.clear();
1385
+ this.parentChain.clear();
1381
1386
  this.namedRoutes.clear();
1382
1387
  this.compiledPatterns.clear();
1383
1388
  this.buildIndex(routes);
1384
1389
  }
1385
1390
  addRoute(route2, parentPath = "") {
1386
1391
  const fullPath = parentPath + route2.path;
1392
+ const parentAncestors = this.parentChain.get(parentPath) || [];
1393
+ const chain = [...parentAncestors, route2];
1387
1394
  this.routeTrie.set(fullPath, route2);
1395
+ this.parentChain.set(fullPath, chain);
1388
1396
  if (route2.name) {
1389
1397
  this.namedRoutes.set(route2.name, route2);
1390
1398
  }
1391
1399
  if (route2.children?.length) {
1392
- this.buildIndex(route2.children, fullPath);
1400
+ this.buildIndex(route2.children, fullPath, chain);
1393
1401
  }
1394
1402
  }
1395
1403
  removeRoute(path2) {
1396
1404
  this.routeTrie.delete(path2);
1405
+ this.parentChain.delete(path2);
1397
1406
  this.compiledPatterns.clear();
1398
1407
  for (const [name, route2] of this.namedRoutes) {
1399
1408
  if (route2.path === path2) {
@@ -1631,16 +1640,16 @@ var _SibuRouter = class _SibuRouter {
1631
1640
  }
1632
1641
  initialize() {
1633
1642
  if (this.options.mode === "history") {
1634
- const popstateHandler = () => this.handleLocationChange();
1643
+ const popstateHandler = () => this.handleLocationChange(true);
1635
1644
  window.addEventListener("popstate", popstateHandler);
1636
1645
  this.cleanup.push(() => window.removeEventListener("popstate", popstateHandler));
1637
1646
  } else {
1638
- const hashHandler = () => this.handleLocationChange();
1647
+ const hashHandler = () => this.handleLocationChange(true);
1639
1648
  window.addEventListener("hashchange", hashHandler);
1640
1649
  this.cleanup.push(() => window.removeEventListener("hashchange", hashHandler));
1641
1650
  }
1642
1651
  queueMicrotask(() => {
1643
- this.handleLocationChange();
1652
+ this.handleLocationChange(true);
1644
1653
  this.isReadySetter(true);
1645
1654
  });
1646
1655
  }
@@ -1654,10 +1663,11 @@ var _SibuRouter = class _SibuRouter {
1654
1663
  matched: []
1655
1664
  };
1656
1665
  }
1657
- handleLocationChange() {
1666
+ handleLocationChange(skipHistory = true) {
1658
1667
  const path2 = this.getCurrentPath();
1659
- const context = this.createRouteContext(path2);
1660
- this.currentRouteSetter(context);
1668
+ this.navigate(path2, { replace: true, skipHistory }).catch((err) => {
1669
+ console.error("[Router] Error during location change navigation:", err);
1670
+ });
1661
1671
  }
1662
1672
  getCurrentPath() {
1663
1673
  const { mode, base: base2 } = this.options;
@@ -1677,7 +1687,7 @@ var _SibuRouter = class _SibuRouter {
1677
1687
  const match = this.matcher.match(path2 || "/");
1678
1688
  const params = match?.params || {};
1679
1689
  const meta2 = match?.route.meta || {};
1680
- const matched = match ? [match.route] : [];
1690
+ const matched = match?.matched || [];
1681
1691
  return {
1682
1692
  path: path2 || "/",
1683
1693
  params,
@@ -1728,16 +1738,18 @@ var _SibuRouter = class _SibuRouter {
1728
1738
  const match = this.matcher.match(to.path);
1729
1739
  if (match) {
1730
1740
  const { route: route2 } = match;
1731
- if ("beforeEnter" in route2 && route2.beforeEnter) {
1732
- const guards = Array.isArray(route2.beforeEnter) ? route2.beforeEnter : [route2.beforeEnter];
1733
- for (const guard of guards) {
1734
- if (signal2.aborted) throw new Error("Navigation aborted");
1735
- const result = await guard(to, from);
1736
- if (result !== true) {
1737
- if (typeof result === "string") {
1738
- return this.performNavigation(this.createRouteContext(result), from, options, signal2, depth + 1);
1741
+ for (const matchedRoute of match.matched) {
1742
+ if ("beforeEnter" in matchedRoute && matchedRoute.beforeEnter) {
1743
+ const guards = Array.isArray(matchedRoute.beforeEnter) ? matchedRoute.beforeEnter : [matchedRoute.beforeEnter];
1744
+ for (const guard of guards) {
1745
+ if (signal2.aborted) throw new Error("Navigation aborted");
1746
+ const result = await guard(to, from);
1747
+ if (result !== true) {
1748
+ if (typeof result === "string") {
1749
+ return this.performNavigation(this.createRouteContext(result), from, options, signal2, depth + 1);
1750
+ }
1751
+ throw new NavigationFailureError("aborted", from, to);
1739
1752
  }
1740
- throw new NavigationFailureError("aborted", from, to);
1741
1753
  }
1742
1754
  }
1743
1755
  }
@@ -1758,7 +1770,9 @@ var _SibuRouter = class _SibuRouter {
1758
1770
  }
1759
1771
  throw new NavigationFailureError("aborted", from, to);
1760
1772
  }
1761
- this.updateHistory(to, options);
1773
+ if (!options.skipHistory) {
1774
+ this.updateHistory(to, options);
1775
+ }
1762
1776
  this.currentRouteSetter(to);
1763
1777
  this.guards.runAfterEach(to, from);
1764
1778
  this.handleScrollBehavior(to, from);
@@ -1993,6 +2007,7 @@ function Route() {
1993
2007
  let errorNode = null;
1994
2008
  let isUpdating = false;
1995
2009
  let currentPath = "";
2010
+ let currentTopRoute = null;
1996
2011
  const cleanupNodes = () => {
1997
2012
  [currentNode, loadingNode, errorNode].forEach((node) => {
1998
2013
  if (node?.parentNode) {
@@ -2062,16 +2077,22 @@ function Route() {
2062
2077
  return;
2063
2078
  }
2064
2079
  const route2 = globalRouter.currentRoute;
2065
- if (route2.path === currentPath && currentNode) return;
2066
- isUpdating = true;
2067
- currentPath = route2.path;
2068
2080
  try {
2069
2081
  const match = globalRouter["matcher"].match(route2.path);
2070
2082
  if (!match) {
2083
+ currentPath = route2.path;
2084
+ currentTopRoute = null;
2071
2085
  cleanupNodes();
2072
2086
  return;
2073
2087
  }
2074
- const { route: routeDef } = match;
2088
+ const routeDef = match.matched[0] || match.route;
2089
+ if (routeDef === currentTopRoute && currentNode) {
2090
+ currentPath = route2.path;
2091
+ return;
2092
+ }
2093
+ isUpdating = true;
2094
+ currentPath = route2.path;
2095
+ currentTopRoute = routeDef;
2075
2096
  if ("redirect" in routeDef) {
2076
2097
  const redirectPath = typeof routeDef.redirect === "function" ? routeDef.redirect(route2) : routeDef.redirect;
2077
2098
  queueMicrotask(() => globalRouter?.navigate(redirectPath));
@@ -2422,7 +2443,8 @@ function Outlet() {
2422
2443
  const childRoute = route2.matched[route2.matched.length - 1];
2423
2444
  if (!childRoute || !("component" in childRoute)) return;
2424
2445
  try {
2425
- const component = await globalRouter.loadComponent(childRoute, route2.path);
2446
+ const cacheKey = `${route2.path}\0${childRoute.path}`;
2447
+ const component = await globalRouter.loadComponent(childRoute, cacheKey);
2426
2448
  const node = component();
2427
2449
  if (node && anchor.parentNode) {
2428
2450
  if (currentNode?.parentNode) {
@@ -140,6 +140,7 @@ declare class SibuRouter {
140
140
  navigate(to: NavigationTarget, options?: {
141
141
  replace?: boolean;
142
142
  state?: unknown;
143
+ skipHistory?: boolean;
143
144
  }): Promise<NavigationResult>;
144
145
  private static readonly MAX_REDIRECT_DEPTH;
145
146
  private performNavigation;
package/dist/plugins.d.ts CHANGED
@@ -140,6 +140,7 @@ declare class SibuRouter {
140
140
  navigate(to: NavigationTarget, options?: {
141
141
  replace?: boolean;
142
142
  state?: unknown;
143
+ skipHistory?: boolean;
143
144
  }): Promise<NavigationResult>;
144
145
  private static readonly MAX_REDIRECT_DEPTH;
145
146
  private performNavigation;
package/dist/plugins.js CHANGED
@@ -154,14 +154,17 @@ var NavigationController = class {
154
154
  var RouteMatcher = class {
155
155
  constructor(routes) {
156
156
  this.routeTrie = /* @__PURE__ */ new Map();
157
+ this.parentChain = /* @__PURE__ */ new Map();
157
158
  this.namedRoutes = /* @__PURE__ */ new Map();
158
159
  this.compiledPatterns = new LRUCache(50);
159
160
  this.buildIndex(routes);
160
161
  }
161
- buildIndex(routes, parentPath = "") {
162
+ buildIndex(routes, parentPath = "", ancestors = []) {
162
163
  for (const route2 of routes) {
163
164
  const fullPath = parentPath + route2.path;
165
+ const chain = [...ancestors, route2];
164
166
  this.routeTrie.set(fullPath, route2);
167
+ this.parentChain.set(fullPath, chain);
165
168
  if (route2.name) {
166
169
  this.namedRoutes.set(route2.name, route2);
167
170
  }
@@ -169,22 +172,23 @@ var RouteMatcher = class {
169
172
  const aliases = Array.isArray(route2.alias) ? route2.alias : [route2.alias];
170
173
  for (const alias of aliases) {
171
174
  this.routeTrie.set(parentPath + alias, route2);
175
+ this.parentChain.set(parentPath + alias, chain);
172
176
  }
173
177
  }
174
178
  if (route2.children?.length) {
175
- this.buildIndex(route2.children, fullPath);
179
+ this.buildIndex(route2.children, fullPath, chain);
176
180
  }
177
181
  }
178
182
  }
179
183
  match(path) {
180
184
  const exactMatch = this.routeTrie.get(path);
181
185
  if (exactMatch) {
182
- return { route: exactMatch, params: {}, matched: [exactMatch] };
186
+ return { route: exactMatch, params: {}, matched: this.parentChain.get(path) || [exactMatch] };
183
187
  }
184
188
  for (const [routePath, route2] of this.routeTrie) {
185
189
  const match = this.matchPattern(path, routePath);
186
190
  if (match) {
187
- return { route: route2, params: match.params, matched: [route2] };
191
+ return { route: route2, params: match.params, matched: this.parentChain.get(routePath) || [route2] };
188
192
  }
189
193
  }
190
194
  return null;
@@ -242,22 +246,27 @@ var RouteMatcher = class {
242
246
  }
243
247
  rebuild(routes) {
244
248
  this.routeTrie.clear();
249
+ this.parentChain.clear();
245
250
  this.namedRoutes.clear();
246
251
  this.compiledPatterns.clear();
247
252
  this.buildIndex(routes);
248
253
  }
249
254
  addRoute(route2, parentPath = "") {
250
255
  const fullPath = parentPath + route2.path;
256
+ const parentAncestors = this.parentChain.get(parentPath) || [];
257
+ const chain = [...parentAncestors, route2];
251
258
  this.routeTrie.set(fullPath, route2);
259
+ this.parentChain.set(fullPath, chain);
252
260
  if (route2.name) {
253
261
  this.namedRoutes.set(route2.name, route2);
254
262
  }
255
263
  if (route2.children?.length) {
256
- this.buildIndex(route2.children, fullPath);
264
+ this.buildIndex(route2.children, fullPath, chain);
257
265
  }
258
266
  }
259
267
  removeRoute(path) {
260
268
  this.routeTrie.delete(path);
269
+ this.parentChain.delete(path);
261
270
  this.compiledPatterns.clear();
262
271
  for (const [name, route2] of this.namedRoutes) {
263
272
  if (route2.path === path) {
@@ -495,16 +504,16 @@ var _SibuRouter = class _SibuRouter {
495
504
  }
496
505
  initialize() {
497
506
  if (this.options.mode === "history") {
498
- const popstateHandler = () => this.handleLocationChange();
507
+ const popstateHandler = () => this.handleLocationChange(true);
499
508
  window.addEventListener("popstate", popstateHandler);
500
509
  this.cleanup.push(() => window.removeEventListener("popstate", popstateHandler));
501
510
  } else {
502
- const hashHandler = () => this.handleLocationChange();
511
+ const hashHandler = () => this.handleLocationChange(true);
503
512
  window.addEventListener("hashchange", hashHandler);
504
513
  this.cleanup.push(() => window.removeEventListener("hashchange", hashHandler));
505
514
  }
506
515
  queueMicrotask(() => {
507
- this.handleLocationChange();
516
+ this.handleLocationChange(true);
508
517
  this.isReadySetter(true);
509
518
  });
510
519
  }
@@ -518,10 +527,11 @@ var _SibuRouter = class _SibuRouter {
518
527
  matched: []
519
528
  };
520
529
  }
521
- handleLocationChange() {
530
+ handleLocationChange(skipHistory = true) {
522
531
  const path = this.getCurrentPath();
523
- const context = this.createRouteContext(path);
524
- this.currentRouteSetter(context);
532
+ this.navigate(path, { replace: true, skipHistory }).catch((err) => {
533
+ console.error("[Router] Error during location change navigation:", err);
534
+ });
525
535
  }
526
536
  getCurrentPath() {
527
537
  const { mode, base } = this.options;
@@ -541,7 +551,7 @@ var _SibuRouter = class _SibuRouter {
541
551
  const match = this.matcher.match(path || "/");
542
552
  const params = match?.params || {};
543
553
  const meta = match?.route.meta || {};
544
- const matched = match ? [match.route] : [];
554
+ const matched = match?.matched || [];
545
555
  return {
546
556
  path: path || "/",
547
557
  params,
@@ -592,16 +602,18 @@ var _SibuRouter = class _SibuRouter {
592
602
  const match = this.matcher.match(to.path);
593
603
  if (match) {
594
604
  const { route: route2 } = match;
595
- if ("beforeEnter" in route2 && route2.beforeEnter) {
596
- const guards = Array.isArray(route2.beforeEnter) ? route2.beforeEnter : [route2.beforeEnter];
597
- for (const guard of guards) {
598
- if (signal2.aborted) throw new Error("Navigation aborted");
599
- const result = await guard(to, from);
600
- if (result !== true) {
601
- if (typeof result === "string") {
602
- return this.performNavigation(this.createRouteContext(result), from, options, signal2, depth + 1);
605
+ for (const matchedRoute of match.matched) {
606
+ if ("beforeEnter" in matchedRoute && matchedRoute.beforeEnter) {
607
+ const guards = Array.isArray(matchedRoute.beforeEnter) ? matchedRoute.beforeEnter : [matchedRoute.beforeEnter];
608
+ for (const guard of guards) {
609
+ if (signal2.aborted) throw new Error("Navigation aborted");
610
+ const result = await guard(to, from);
611
+ if (result !== true) {
612
+ if (typeof result === "string") {
613
+ return this.performNavigation(this.createRouteContext(result), from, options, signal2, depth + 1);
614
+ }
615
+ throw new NavigationFailureError("aborted", from, to);
603
616
  }
604
- throw new NavigationFailureError("aborted", from, to);
605
617
  }
606
618
  }
607
619
  }
@@ -622,7 +634,9 @@ var _SibuRouter = class _SibuRouter {
622
634
  }
623
635
  throw new NavigationFailureError("aborted", from, to);
624
636
  }
625
- this.updateHistory(to, options);
637
+ if (!options.skipHistory) {
638
+ this.updateHistory(to, options);
639
+ }
626
640
  this.currentRouteSetter(to);
627
641
  this.guards.runAfterEach(to, from);
628
642
  this.handleScrollBehavior(to, from);
@@ -857,6 +871,7 @@ function Route() {
857
871
  let errorNode = null;
858
872
  let isUpdating = false;
859
873
  let currentPath = "";
874
+ let currentTopRoute = null;
860
875
  const cleanupNodes = () => {
861
876
  [currentNode, loadingNode, errorNode].forEach((node) => {
862
877
  if (node?.parentNode) {
@@ -926,16 +941,22 @@ function Route() {
926
941
  return;
927
942
  }
928
943
  const route2 = globalRouter.currentRoute;
929
- if (route2.path === currentPath && currentNode) return;
930
- isUpdating = true;
931
- currentPath = route2.path;
932
944
  try {
933
945
  const match = globalRouter["matcher"].match(route2.path);
934
946
  if (!match) {
947
+ currentPath = route2.path;
948
+ currentTopRoute = null;
935
949
  cleanupNodes();
936
950
  return;
937
951
  }
938
- const { route: routeDef } = match;
952
+ const routeDef = match.matched[0] || match.route;
953
+ if (routeDef === currentTopRoute && currentNode) {
954
+ currentPath = route2.path;
955
+ return;
956
+ }
957
+ isUpdating = true;
958
+ currentPath = route2.path;
959
+ currentTopRoute = routeDef;
939
960
  if ("redirect" in routeDef) {
940
961
  const redirectPath = typeof routeDef.redirect === "function" ? routeDef.redirect(route2) : routeDef.redirect;
941
962
  queueMicrotask(() => globalRouter?.navigate(redirectPath));
@@ -1286,7 +1307,8 @@ function Outlet() {
1286
1307
  const childRoute = route2.matched[route2.matched.length - 1];
1287
1308
  if (!childRoute || !("component" in childRoute)) return;
1288
1309
  try {
1289
- const component = await globalRouter.loadComponent(childRoute, route2.path);
1310
+ const cacheKey = `${route2.path}\0${childRoute.path}`;
1311
+ const component = await globalRouter.loadComponent(childRoute, cacheKey);
1290
1312
  const node = component();
1291
1313
  if (node && anchor.parentNode) {
1292
1314
  if (currentNode?.parentNode) {
@@ -0,0 +1,36 @@
1
+ import {
2
+ collectStream,
3
+ deserializeState,
4
+ hydrate,
5
+ hydrateIslands,
6
+ hydrateProgressively,
7
+ island,
8
+ renderToDocument,
9
+ renderToReadableStream,
10
+ renderToStream,
11
+ renderToString,
12
+ renderToSuspenseStream,
13
+ resetSSRState,
14
+ serializeState,
15
+ ssrSuspense,
16
+ suspenseSwapScript
17
+ } from "./chunk-7TQKR4PP.js";
18
+ import "./chunk-4MYMUBRS.js";
19
+ import "./chunk-MLKGABMK.js";
20
+ export {
21
+ collectStream,
22
+ deserializeState,
23
+ hydrate,
24
+ hydrateIslands,
25
+ hydrateProgressively,
26
+ island,
27
+ renderToDocument,
28
+ renderToReadableStream,
29
+ renderToStream,
30
+ renderToString,
31
+ renderToSuspenseStream,
32
+ resetSSRState,
33
+ serializeState,
34
+ ssrSuspense,
35
+ suspenseSwapScript
36
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sibujs",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "A lightweight, function-based frontend framework that combines the best of React, Svelte, and Vue — with zero VDOM and maximum simplicity. Designed for developers who want fine-grained reactivity and full control without compilation or magic.",
5
5
  "keywords": [
6
6
  "frontend",