@vesk/runtime 0.2.94 → 0.2.95

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/hydrate.d.ts CHANGED
@@ -17,16 +17,30 @@ export interface ClaimByKeyOptions {
17
17
  */
18
18
  skipK?: number;
19
19
  }
20
+ /**
21
+ * Minimal compiler-generated shape descriptor for a structural claim. It never
22
+ * touches the DOM; the client program carries it. Only stable, source-authored
23
+ * static class tokens are validated — dynamic values are deliberately ignored
24
+ * because SSR text/attributes may legitimately differ at first paint.
25
+ */
26
+ export interface HydrateClaimDescriptor {
27
+ class?: string;
28
+ }
29
+ export interface StructuralRegionBranchDescriptor {
30
+ tag?: string;
31
+ descriptor?: HydrateClaimDescriptor | null;
32
+ count: number;
33
+ }
20
34
  export interface HydrateWalker {
21
35
  root: HTMLElement | null;
22
36
  done(): boolean;
23
- nextElement(tag?: string, skipK?: number): Element;
37
+ nextElement(tag?: string, skipK?: number, descriptor?: HydrateClaimDescriptor): Element;
24
38
  /**
25
39
  * Claim an element like `nextElement` but without stripping its direct text
26
40
  * children. Used by static-component stubs whose SSR content is preserved
27
41
  * as-is (no client-side re-creation of text nodes).
28
42
  */
29
- claimOnly?(tag?: string): Element;
43
+ claimOnly?(tag?: string, skipK?: number, descriptor?: HydrateClaimDescriptor): Element;
30
44
  /**
31
45
  * Drop any remaining markers whose comment node is no longer attached to
32
46
  * the document. Self-claiming runtime components (Link/NavLink/Md/Form/
@@ -85,6 +99,13 @@ export interface HydrateWalker {
85
99
  */
86
100
  injectSkipK?(n: number): void;
87
101
  takeSkipK?(): number;
102
+ /**
103
+ * Markerless structural branch reconciliation: identify the SSR branch
104
+ * sitting at the region's cursor slot by its static descriptor, and
105
+ * discard that branch's SSR residue when the client selected a different
106
+ * branch (or no branch). Returns true when a branch was discarded.
107
+ */
108
+ discardStructuralRegion?(branches: StructuralRegionBranchDescriptor[], selected: number, skipK?: number): boolean;
88
109
  }
89
110
  interface HydrateIdleOptions {
90
111
  chunkSize?: number;
@@ -105,7 +126,7 @@ interface HydrateCancel extends HydrateCancelBase {
105
126
  */
106
127
  export declare function reactiveProps<T extends Record<string, unknown>>(props: T): T;
107
128
  export declare function setHydrateDevMode(enabled: boolean): void;
108
- export type HydrationIssueKind = 'tag-mismatch' | 'exhausted' | 'leftover-marker' | 'twin' | 'key-reorder' | 'marker-skew' | 'untyped-marker';
129
+ export type HydrationIssueKind = 'tag-mismatch' | 'descriptor-mismatch' | 'exhausted' | 'leftover-marker' | 'twin' | 'key-reorder' | 'marker-skew' | 'untyped-marker';
109
130
  export interface HydrationIssue {
110
131
  kind: HydrationIssueKind;
111
132
  detail: string;
@@ -1 +1 @@
1
- {"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../src/hydrate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../src/hydrate.ts"],"names":[],"mappings":"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"}
package/dist/hydrate.js CHANGED
@@ -874,16 +874,73 @@ class WalkerEngine {
874
874
  // markerless design walks PLAIN server HTML: a per-parent stack of element
875
875
  // lists, claimed positionally by `skipK` (the number of source siblings the
876
876
  // caller already consumed — static residues it retrieved from `__vsk_ssrEls`)
877
- // and by tag. The compiler emits `nextElement(tag, skipK)` claims with the
878
- // descriptor implicit in the tag; divergence follows §2.2 step 4: a tag
879
- // mismatch consumes exactly one slot, reports, detaches the provably-owned
880
- // direct-child ghost, and hands back a fresh element that the caller mounts.
881
- // The SSR node is never bound.
877
+ // by tag and, at component/region roots, an optional compiler-generated static
878
+ // class descriptor. The compiler emits `nextElement(tag, skipK, descriptor)`
879
+ // claims; divergence follows §2.2 step 4: a tag or descriptor mismatch consumes
880
+ // exactly one slot, reports, detaches the provably-owned direct-child ghost,
881
+ // and hands back a fresh element that the caller mounts. The SSR node is never
882
+ // bound.
882
883
  //
883
884
  // The same engine backs `createHydrateChildWalker`, so runtime components that
884
885
  // claim offspring positionally (loading stubs, slot content) keep working with
885
886
  // identical semantics.
886
887
  // ---------------------------------------------------------------------------
888
+ /**
889
+ * Split a class attribute into tokens without a regex (runtime helpers follow
890
+ * the tokenizer style used by md.ts). Only used for the descriptor's static
891
+ * class fingerprint.
892
+ */
893
+ function classTokens(value) {
894
+ const out = [];
895
+ let token = '';
896
+ for (let i = 0; i < value.length; i++) {
897
+ const ch = value[i];
898
+ if (ch === ' ' || ch === '\n' || ch === '\t' || ch === '\r' || ch === '\f') {
899
+ if (token) {
900
+ out.push(token);
901
+ token = '';
902
+ }
903
+ }
904
+ else {
905
+ token += ch;
906
+ }
907
+ }
908
+ if (token)
909
+ out.push(token);
910
+ return out;
911
+ }
912
+ function descriptorMatches(el, descriptor) {
913
+ if (!descriptor || !descriptor.class)
914
+ return true;
915
+ const wanted = classTokens(descriptor.class);
916
+ if (wanted.length === 0)
917
+ return true;
918
+ const actual = el.classList;
919
+ if (actual) {
920
+ for (const token of wanted) {
921
+ if (!actual.contains(token))
922
+ return false;
923
+ }
924
+ return true;
925
+ }
926
+ // Minimal DOM shims (tests, linkedom variants) may expose className or only
927
+ // the class attribute instead of a live classList.
928
+ const raw = el.getAttribute('class') ?? el.className ?? '';
929
+ const present = classTokens(String(raw));
930
+ for (const token of wanted) {
931
+ if (!present.includes(token))
932
+ return false;
933
+ }
934
+ return true;
935
+ }
936
+ function structuralBranchMatches(el, branch) {
937
+ const actualTag = el.tagName.toLowerCase();
938
+ if (branch.tag && actualTag !== branch.tag)
939
+ return false;
940
+ if (branch.descriptor)
941
+ return descriptorMatches(el, branch.descriptor);
942
+ return !!branch.tag;
943
+ }
887
944
  class StructuralWalker {
888
945
  root;
889
946
  // Snapshot taken at construction, so fresh nodes mounted during the render
@@ -975,7 +1032,7 @@ class StructuralWalker {
975
1032
  this.inheritOffset = 0;
976
1033
  return n;
977
1034
  }
978
- claimAt(tag, skipK, raw) {
1035
+ claimAt(tag, skipK, raw, descriptor) {
979
1036
  if (globalThis.__vesk_hydrate_debug) {
980
1037
  // eslint-disable-next-line no-console
981
1038
  console.error('[hyd-dbg] claimAt', tag, 'skip=', skipK, 'idx=', this.idx, 'els=', this.els.length);
@@ -995,17 +1052,21 @@ class StructuralWalker {
995
1052
  target = this.idx;
996
1053
  while (target < this.els.length) {
997
1054
  const el = this.els[target];
998
- if (tag && el.tagName.toLowerCase() !== tag) {
1055
+ const actualTag = el.tagName.toLowerCase();
1056
+ const tagMismatch = !!tag && actualTag !== tag;
1057
+ const descriptorMismatch = !tagMismatch && !descriptorMatches(el, descriptor);
1058
+ if (tagMismatch || descriptorMismatch) {
999
1059
  // §2.2 step 4: descriptor failed. Consume exactly ONE slot and
1000
1060
  // hand back a fresh element. The direct-child SSR ghost is
1001
1061
  // detached below; nested recovery remains owned by its child walker.
1002
1062
  this.idx = this.advancePastSkippable(target + 1);
1063
+ const expectedTag = tag || 'div';
1003
1064
  const ghost = {
1004
1065
  state: 'ghost',
1005
1066
  el,
1006
1067
  owner: this.root,
1007
- expectedTag: tag || 'div',
1008
- actualTag: el.tagName.toLowerCase(),
1068
+ expectedTag,
1069
+ actualTag,
1009
1070
  detached: false,
1010
1071
  };
1011
1072
  recordClaim(this.root, ghost);
@@ -1021,16 +1082,20 @@ class StructuralWalker {
1021
1082
  // The final sweep will report/repair a still-owned ghost.
1022
1083
  }
1023
1084
  }
1024
- const fresh = document.createElement(tag || 'div');
1085
+ const fresh = document.createElement(expectedTag);
1025
1086
  recordClaim(this.root, {
1026
1087
  state: 'fresh',
1027
1088
  el: fresh,
1028
1089
  owner: this.root,
1029
- expectedTag: tag || 'div',
1090
+ expectedTag,
1030
1091
  actualTag: fresh.tagName.toLowerCase(),
1031
1092
  detached: false,
1032
1093
  });
1033
- reportMiss('tag-mismatch', `descriptor wanted <${tag}> but the SSR slot holds <${el.tagName.toLowerCase()}>; consumed one slot, detached the local ghost, and rendered fresh.`, false);
1094
+ const actualClass = el.getAttribute('class') || '';
1095
+ const detail = descriptorMismatch
1096
+ ? `descriptor wanted <${expectedTag} class="${descriptor?.class || ''}"> but the SSR slot holds <${actualTag} class="${actualClass}">; consumed one slot, detached the local ghost, and rendered fresh.`
1097
+ : `descriptor wanted <${tag}> but the SSR slot holds <${actualTag}>; consumed one slot, detached the local ghost, and rendered fresh.`;
1098
+ reportMiss(descriptorMismatch ? 'descriptor-mismatch' : 'tag-mismatch', detail, false);
1034
1099
  return fresh;
1035
1100
  }
1036
1101
  if (this.adopted.has(el)) {
@@ -1064,11 +1129,76 @@ class StructuralWalker {
1064
1129
  false);
1065
1130
  return fresh;
1066
1131
  }
1067
- nextElement(tag, skipK = 0) {
1068
- return this.claimAt(tag, skipK, false);
1132
+ nextElement(tag, skipK = 0, descriptor) {
1133
+ return this.claimAt(tag, skipK, false, descriptor);
1134
+ }
1135
+ claimOnly(tag, skipK = 0, descriptor) {
1136
+ return this.claimAt(tag, skipK, true, descriptor);
1069
1137
  }
1070
- claimOnly(tag, skipK = 0) {
1071
- return this.claimAt(tag, skipK, true);
1138
+ discardStructuralRegion(branches, selected, skipK = 0) {
1139
+ if (!branches || branches.length === 0)
1140
+ return false;
1141
+ let offset = skipK || 0;
1142
+ if (!offset)
1143
+ offset = this.inheritOffset;
1144
+ let target = this.advancePastSkippable(this.idx + offset);
1145
+ if (target > this.els.length)
1146
+ target = this.idx;
1147
+ target = this.advancePastSkippable(target);
1148
+ while (target < this.els.length) {
1149
+ const el = this.els[target];
1150
+ if (this.adopted.has(el)) {
1151
+ target = this.advancePastSkippable(target + 1);
1152
+ continue;
1153
+ }
1154
+ let matchCount = 0;
1155
+ let matchIdx = -1;
1156
+ for (let i = 0; i < branches.length; i++) {
1157
+ if (structuralBranchMatches(el, branches[i])) {
1158
+ matchCount++;
1159
+ matchIdx = i;
1160
+ }
1161
+ }
1162
+ if (matchCount !== 1 || matchIdx === selected)
1163
+ return false;
1164
+ const count = branches[matchIdx].count;
1165
+ if (!count || count < 1)
1166
+ return false;
1167
+ this.inheritOffset = 0;
1168
+ let p = target;
1169
+ let removed = 0;
1170
+ while (removed < count && p < this.els.length) {
1171
+ const candidate = this.els[p];
1172
+ if (this.adopted.has(candidate)) {
1173
+ p = this.advancePastSkippable(p + 1);
1174
+ continue;
1175
+ }
1176
+ const actualTag = candidate.tagName.toLowerCase();
1177
+ const ghost = {
1178
+ state: 'ghost',
1179
+ el: candidate,
1180
+ owner: this.root,
1181
+ expectedTag: branches[matchIdx].tag || actualTag,
1182
+ actualTag,
1183
+ detached: false,
1184
+ };
1185
+ recordClaim(this.root, ghost);
1186
+ if (this.root && candidate.parentNode === this.root) {
1187
+ try {
1188
+ candidate.remove();
1189
+ ghost.detached = true;
1190
+ }
1191
+ catch {
1192
+ }
1193
+ }
1194
+ p = this.advancePastSkippable(p + 1);
1195
+ removed++;
1196
+ }
1197
+ this.idx = p;
1198
+ reportMiss('descriptor-mismatch', `structural region reconciliation removed SSR branch ${matchIdx}; the client selected branch ${selected}.`, false);
1199
+ return true;
1200
+ }
1201
+ return false;
1072
1202
  }
1073
1203
  subWalker(rootEl) {
1074
1204
  if (globalThis.__vesk_hydrate_debug) {
@@ -1,5 +1,5 @@
1
1
  import type { Tracked } from '@vesk/runtime/src/ripple-runtime';
2
- export interface Resource<T> extends PromiseLike<T> {
2
+ export interface Resource<T> extends Promise<T> {
3
3
  loading: boolean;
4
4
  error: unknown;
5
5
  data: T | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
package/dist/resource.js CHANGED
@@ -212,8 +212,11 @@ function resolveFetchUrl(url) {
212
212
  if (/^[a-z][a-z0-9+.-]*:/i.test(url) || url.startsWith('//'))
213
213
  return url;
214
214
  const ctx = g().__vesk_request;
215
+ // Bind the resolver: on the server the request context is often a
216
+ // `VeskRequest`, whose `resolveUrl` is a prototype method that reads
217
+ // `this.origin`. Calling it unbound throws "reading 'origin' of undefined".
215
218
  const resolver = ctx && typeof ctx.resolveUrl === 'function'
216
- ? ctx.resolveUrl
219
+ ? ctx.resolveUrl.bind(ctx)
217
220
  : null;
218
221
  if (resolver)
219
222
  return resolver(url);
@@ -559,20 +562,25 @@ export function mutate(key, data) {
559
562
  registry.forEach(h => startRequest(h, true));
560
563
  }
561
564
  }
565
+ class ResourcePromise extends Promise {
566
+ read;
567
+ constructor(read) {
568
+ super(() => { });
569
+ this.read = read;
570
+ }
571
+ then(onfulfilled, onrejected) {
572
+ return this.read().then(onfulfilled, onrejected);
573
+ }
574
+ catch(onrejected) {
575
+ return this.read().catch(onrejected);
576
+ }
577
+ finally(onfinally) {
578
+ return this.read().finally(onfinally);
579
+ }
580
+ }
562
581
  function createResourceAccessor(handle) {
563
582
  const state = handle.state;
564
- const accessor = {};
565
- Object.defineProperty(accessor, 'loading', {
566
- get() { return get(state).loading; },
567
- });
568
- Object.defineProperty(accessor, 'error', {
569
- get() { return get(state).error; },
570
- });
571
- Object.defineProperty(accessor, 'data', {
572
- get() { return get(state).data; },
573
- });
574
- accessor._state = state;
575
- const toData = async () => {
583
+ const read = async () => {
576
584
  for (;;) {
577
585
  const s = get(state);
578
586
  if (!s.loading) {
@@ -583,9 +591,17 @@ function createResourceAccessor(handle) {
583
591
  await handle.settled;
584
592
  }
585
593
  };
586
- accessor.then = (onFulfilled, onRejected) => toData().then(onFulfilled, onRejected);
587
- accessor.catch = (onRejected) => toData().catch(onRejected);
588
- accessor.finally = (onFinally) => toData().finally(onFinally);
594
+ const accessor = new ResourcePromise(read);
595
+ Object.defineProperty(accessor, 'loading', {
596
+ get() { return get(state).loading; },
597
+ });
598
+ Object.defineProperty(accessor, 'error', {
599
+ get() { return get(state).error; },
600
+ });
601
+ Object.defineProperty(accessor, 'data', {
602
+ get() { return get(state).data; },
603
+ });
604
+ accessor._state = state;
589
605
  accessor.refresh = () => revalidate(handle);
590
606
  accessor.abort = () => {
591
607
  if (!isServer())
package/dist/router.d.ts CHANGED
@@ -82,6 +82,7 @@ type GuardFn = (to: string, from: string) => false | string | void | Promise<fal
82
82
  export declare function createRouter(routes: RouteNode[] | Record<string, Function>, options?: RouterOptions): RouterInstance;
83
83
  interface FileRouterOptions extends RouterOptions {
84
84
  middleware?: Function | Function[];
85
+ serverMiddleware?: boolean;
85
86
  render?: (router: RouterInstance, match: RouteMatch, container: HTMLElement) => void;
86
87
  }
87
88
  interface FileRouterInstance extends RouterInstance {
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
package/dist/router.js CHANGED
@@ -1046,6 +1046,21 @@ async function renderErrorPage(router, match, container, error, navToken) {
1046
1046
  container.innerHTML = dom;
1047
1047
  }
1048
1048
  }
1049
+ /**
1050
+ * Marks the initial-hydration lifecycle on `globalThis.__vesk_hydrated`.
1051
+ * `window.__vesk_router` is assigned before the layout/page hydration runs, and
1052
+ * claiming a large layout chain can take seconds on low-end devices — so tools
1053
+ * (e2e harnesses, boot scripts) that need "handlers are attached and the SSR
1054
+ * DOM is claimed" must wait for this flag, not the router. Set false while the
1055
+ * hydration promise is in flight, true once it settles (resolved or rejected —
1056
+ * the error path still renders an interactive error component).
1057
+ */
1058
+ function trackHydration(promise) {
1059
+ const g = globalThis;
1060
+ g.__vesk_hydrated = false;
1061
+ promise.then(() => { g.__vesk_hydrated = true; }, () => { g.__vesk_hydrated = true; });
1062
+ return promise;
1063
+ }
1049
1064
  async function hydrateInitial(router, match, container, strategy) {
1050
1065
  // Token at hydration start (usually 0 — before any client navigation).
1051
1066
  // Every container paint below passes it to its async renderer so a
@@ -1444,15 +1459,17 @@ export function createRouter(routes, options = {}) {
1444
1459
  const match = matchRoute(this.routeTree, url.pathname);
1445
1460
  if (match) {
1446
1461
  match.pathname = url.pathname;
1447
- hydrateInitial(this, match, container, hydrateStrategy);
1462
+ trackHydration(hydrateInitial(this, match, container, hydrateStrategy));
1448
1463
  this._currentMatch = match;
1449
1464
  }
1450
1465
  else {
1451
1466
  this.navigate(path, { replace: true });
1467
+ globalThis.__vesk_hydrated = true;
1452
1468
  }
1453
1469
  }
1454
1470
  else {
1455
1471
  this.navigate(path, { replace: true });
1472
+ globalThis.__vesk_hydrated = true;
1456
1473
  }
1457
1474
  return this;
1458
1475
  },
@@ -1497,7 +1514,15 @@ export function createRouter(routes, options = {}) {
1497
1514
  const updateUrl = () => {
1498
1515
  const fullPath = url.pathname + url.search;
1499
1516
  const target = mode ? (fullPath.startsWith('/') ? '#' + fullPath : '#' + fullPath) : fullPath;
1500
- if (!opts.replace) {
1517
+ // Re-check the live location here, not just at navigate()
1518
+ // entry: an async middleware/guard chain defers this push, so
1519
+ // a second navigation started before the first one lands
1520
+ // would otherwise push the same URL twice.
1521
+ const liveFull = window.location.pathname + window.location.search + window.location.hash;
1522
+ if (!opts.replace && target === liveFull) {
1523
+ // already current — do not duplicate the history entry
1524
+ }
1525
+ else if (!opts.replace) {
1501
1526
  window.history.pushState({ path: url.pathname }, '', target);
1502
1527
  }
1503
1528
  else {
@@ -1710,6 +1735,46 @@ export function createFileRouter(routeTree, options = {}) {
1710
1735
  const renderFn = options.render || renderMatch;
1711
1736
  const hydrateStrategy = options.hydrate || 'full';
1712
1737
  const hashMode = options.hash === true;
1738
+ const serverMiddlewareCache = new Map();
1739
+ let serverMiddlewareDisabled = false;
1740
+ const runServerMiddleware = async (ctx, next) => {
1741
+ if (options.serverMiddleware !== true || typeof window === 'undefined' || serverMiddlewareDisabled)
1742
+ return next();
1743
+ const path = String(ctx.url || '/');
1744
+ const cached = serverMiddlewareCache.get(path);
1745
+ if (cached && Date.now() - cached.at < 2500) {
1746
+ if (cached.body && typeof cached.body.redirect === 'string' && cached.body.redirect) {
1747
+ return { redirect: cached.body.redirect };
1748
+ }
1749
+ if (cached.body && typeof cached.body.rewrite === 'string' && cached.body.rewrite)
1750
+ return next(cached.body.rewrite);
1751
+ return next();
1752
+ }
1753
+ const endpoint = globalThis.__veskServerMiddlewareEndpoint ?? '/_vesk/middleware';
1754
+ try {
1755
+ const res = await fetch(`${endpoint}?path=${encodeURIComponent(path)}`, {
1756
+ credentials: 'include',
1757
+ headers: { Accept: 'application/json' },
1758
+ cache: 'no-store',
1759
+ });
1760
+ if (res.status === 404) {
1761
+ serverMiddlewareDisabled = true;
1762
+ return next();
1763
+ }
1764
+ const body = await res.json().catch(() => null);
1765
+ serverMiddlewareCache.set(path, { at: Date.now(), body });
1766
+ if (body && typeof body.redirect === 'string' && body.redirect) {
1767
+ // Returning this object is honored by the router's middleware chain.
1768
+ return { redirect: body.redirect };
1769
+ }
1770
+ if (body && typeof body.rewrite === 'string' && body.rewrite)
1771
+ return next(body.rewrite);
1772
+ return next();
1773
+ }
1774
+ catch {
1775
+ return next();
1776
+ }
1777
+ };
1713
1778
  const router = {
1714
1779
  _hydrateStrategy: hydrateStrategy,
1715
1780
  routeTree,
@@ -1847,15 +1912,17 @@ export function createFileRouter(routeTree, options = {}) {
1847
1912
  const match = matchRoute(routeTree, path);
1848
1913
  if (match) {
1849
1914
  match.pathname = path;
1850
- hydrateInitial(router, match, container, hydrateStrategy);
1915
+ trackHydration(hydrateInitial(router, match, container, hydrateStrategy));
1851
1916
  router._currentMatch = match;
1852
1917
  }
1853
1918
  else {
1854
1919
  router.navigate(path, { replace: true });
1920
+ globalThis.__vesk_hydrated = true;
1855
1921
  }
1856
1922
  }
1857
1923
  else {
1858
1924
  router.navigate(path, { replace: true });
1925
+ globalThis.__vesk_hydrated = true;
1859
1926
  }
1860
1927
  return router;
1861
1928
  },
@@ -1924,12 +1991,23 @@ export function createFileRouter(routeTree, options = {}) {
1924
1991
  navDebug('navigate', url.pathname, 'token=' + router._navToken, 'pendingChunks', hasPendingChunks(match.matchChain));
1925
1992
  const navToken = router._navToken;
1926
1993
  loadingStart();
1927
- const middlewareFns = Array.isArray(middleware) ? middleware : (middleware ? [middleware] : []);
1994
+ const middlewareFns = [
1995
+ ...(options.serverMiddleware === true ? [runServerMiddleware] : []),
1996
+ ...(Array.isArray(middleware) ? middleware : (middleware ? [middleware] : [])),
1997
+ ];
1928
1998
  let firstRenderFailed = false;
1929
1999
  const updateUrl = () => {
1930
2000
  const fullUrl = url.pathname + url.search;
1931
2001
  const target = mode ? (fullUrl.startsWith('/') ? '#' + fullUrl : '#' + fullUrl) : fullUrl;
1932
- if (!opts.replace) {
2002
+ // Re-check the live location here, not just at navigate()
2003
+ // entry: an async middleware/guard chain defers this push, so
2004
+ // a second navigation started before the first one lands
2005
+ // would otherwise push the same URL twice.
2006
+ const liveFull = window.location.pathname + window.location.search + window.location.hash;
2007
+ if (!opts.replace && target === liveFull) {
2008
+ // already current — do not duplicate the history entry
2009
+ }
2010
+ else if (!opts.replace) {
1933
2011
  window.history.pushState({ path: fullUrl }, '', target);
1934
2012
  }
1935
2013
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/runtime",
3
- "version": "0.2.94",
3
+ "version": "0.2.95",
4
4
  "type": "module",
5
5
  "description": "Vesk runtime — reactivity, blocks, DOM operations, hydration",
6
6
  "main": "./dist/index-client.js",
@@ -80,7 +80,7 @@
80
80
  }
81
81
  },
82
82
  "dependencies": {
83
- "@vesk/compiler": "^0.2.94"
83
+ "@vesk/compiler": "^0.2.95"
84
84
  },
85
85
  "scripts": {
86
86
  "typecheck": "tsc --noEmit",