@vesk/runtime 0.2.28 → 0.2.29

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
@@ -30,6 +30,16 @@ export interface HydrateWalker {
30
30
  */
31
31
  retireDetached?(): void;
32
32
  subWalker(rootEl: HTMLElement): HydrateWalker;
33
+ /**
34
+ * Transfer ownership of an explicit marker subset out of this walk: the
35
+ * listed markers are marked claimed here (the cursor skips them) so a
36
+ * scoped walker built over the same comments owns them exclusively.
37
+ * Used by the layout slot contract (`layout.ts`), which scopes page
38
+ * claims to SSR slot boundaries instead of sharing the positional cursor
39
+ * with nav/footer claims. Optional — walkers that cannot transfer leave
40
+ * the markers in place and the caller falls back to the shared walk.
41
+ */
42
+ takeMarkers?(comments: Comment[]): void;
33
43
  /**
34
44
  * Claim the SSR element of a keyed list item whose `data-vsk-key` matches
35
45
  * `key`. Consumes the item's root marker and stamps the element, but leaves
@@ -79,6 +89,7 @@ export type HydrationMismatchHandler = (issue: HydrationIssue) => void;
79
89
  export declare function setHydrateStrict(enabled: boolean): void;
80
90
  export declare function isHydrateStrict(): boolean;
81
91
  export declare function onHydrationMismatch(handler: HydrationMismatchHandler | null): void;
92
+ export declare function reportHydrationIssue(issue: HydrationIssue): void;
82
93
  /**
83
94
  * Structured audit of the most recent full hydration pass over `container`.
84
95
  * Counts unclaimed `<!--vsk-->` markers (SSR regions the client never
@@ -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
@@ -62,6 +62,16 @@ export function isHydrateStrict() {
62
62
  export function onHydrationMismatch(handler) {
63
63
  __mismatchHandler = handler;
64
64
  }
65
+ export function reportHydrationIssue(issue) {
66
+ if (!__mismatchHandler)
67
+ return;
68
+ try {
69
+ __mismatchHandler(issue);
70
+ }
71
+ catch {
72
+ // Telemetry must never break rendering.
73
+ }
74
+ }
65
75
  function devWarn(message) {
66
76
  if (__hydrateDevMode && typeof console !== 'undefined') {
67
77
  console.warn('[vesk-hydrate] ' + message);
@@ -613,6 +623,25 @@ class WalkerEngine {
613
623
  this.idx = this.markers.length;
614
624
  return new WalkerEngine(rootEl, owned.map((m) => m.comment));
615
625
  }
626
+ // Ownership transfer for an explicit marker subset (layout slot contract).
627
+ // The listed markers are marked claimed here without advancing the cursor:
628
+ // positional claims skip them, and a scoped walker built over the same
629
+ // comments owns them exclusively. Exactly one engine owns each marker.
630
+ takeMarkers(comments) {
631
+ let owned;
632
+ try {
633
+ owned = new Set(comments);
634
+ }
635
+ catch {
636
+ return;
637
+ }
638
+ if (owned.size === 0)
639
+ return;
640
+ for (const tm of this.markers) {
641
+ if (tm.state !== 'claimed' && owned.has(tm.comment))
642
+ tm.state = 'claimed';
643
+ }
644
+ }
616
645
  // Retire every unclaimed marker in the whole walk that points at `el`.
617
646
  // Used after a cross-position adopt (relocate): the adopted node moves to
618
647
  // the cursor, so alias markers stacked at its old station would otherwise
@@ -764,6 +793,10 @@ export function createHydrateChildWalker(parentEl) {
764
793
  retireDetached() {
765
794
  // Child-walkers hold no marker list; nothing to sweep.
766
795
  },
796
+ takeMarkers(_comments) {
797
+ // Child-walkers hold no marker list; nothing to transfer. The
798
+ // layout slot contract falls back to the shared walk.
799
+ },
767
800
  };
768
801
  }
769
802
  export function hydrate(container, componentFn, props) {
@@ -1,6 +1,8 @@
1
1
  export { track, get, set, untrack, peek_tracked as peek, derived, flush_sync as flushSync, tick, schedule_update, queue_microtask, active_block, set_active_block, set_active_component, is_mutating_allowed, tracking, teardown, run_block, run_teardown, create_component_ctx, push_component, pop_component, with_block, with_scope, scope, safe_scope, set_tracking, set_active_reaction, is_block_dirty, destroy_non_branch_children, disable_scoped_flush, on_destroy } from '@vesk/runtime/src/ripple-runtime';
2
2
  export { effect, user_effect, block, branch, root, render, pre_effect, destroy_block, destroy_block_children, pause_block, resume_block, is_destroyed, unlink_block, create_try_block, boundary_fn_running_block } from '@vesk/runtime/src/ripple-blocks';
3
- export { hydrate, hydrateViewport, hydrateIdle, hydrateOnInteraction, needsHydration, hydrationCount, createHydrateWalker, collectVskMarkers, reactiveProps, hydrateInitial, assertFullyHydrated, auditHydration, setHydrateDevMode, setHydrateStrict, isHydrateStrict, onHydrationMismatch, bumpNavEpoch, isVskMarkerText, parseVskMarker } from '@vesk/runtime/src/hydrate';
3
+ export { hydrate, hydrateViewport, hydrateIdle, hydrateOnInteraction, needsHydration, hydrationCount, createHydrateWalker, collectVskMarkers, reactiveProps, hydrateInitial, assertFullyHydrated, auditHydration, setHydrateDevMode, setHydrateStrict, isHydrateStrict, onHydrationMismatch, reportHydrationIssue, bumpNavEpoch, isVskMarkerText, parseVskMarker } from '@vesk/runtime/src/hydrate';
4
+ export { createLayoutSlot, trackSlotContent, auditLayoutSlots, LAYOUT_SLOT_OPEN, LAYOUT_SLOT_CLOSE } from '@vesk/runtime/src/layout';
5
+ export type { LayoutSlot, LayoutSlotAudit, SlotIdentity } from '@vesk/runtime/src/layout';
4
6
  export type { HydrationReport, HydrationIssue, HydrationIssueKind, HydrationMismatchHandler, DeferredHydrationOptions, IdleHydrationOptions, InteractionHydrationOptions, ViewportHydrationHandle } from '@vesk/runtime/src/hydrate';
5
7
  export { createRouter, createFileRouter, Outlet, Link, NavLink, useNavigate, useParams, usePathname, useSearchParams, useRouter, buildRouteTree, defineRoute, Redirect, redirect, permanentRedirect, notFound, NotFoundError, ensureChunk, matchRoute } from '@vesk/runtime/src/router';
6
8
  export { getNetworkState, watchNetwork } from '@vesk/runtime/src/network';
@@ -1 +1 @@
1
- {"version":3,"file":"index-client.d.ts","sourceRoot":"","sources":["../src/index-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index-client.d.ts","sourceRoot":"","sources":["../src/index-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,YAAY,IAAI,IAAI,EAAE,OAAO,EAAE,UAAU,IAAI,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AACtf,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,sBAAsB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AAC1P,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,oBAAoB,EAAE,cAAc,EAAE,cAAc,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACpY,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACrI,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC1F,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACrO,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,EAAE,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACxR,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC1E,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC3G,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACrH,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACxK,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC7G,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,+BAA+B,EAAE,qCAAqC,EAAE,MAAM,qCAAqC,CAAC;AACrP,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAClI,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC"}
@@ -1,6 +1,7 @@
1
1
  export { track, get, set, untrack, peek_tracked as peek, derived, flush_sync as flushSync, tick, schedule_update, queue_microtask, active_block, set_active_block, set_active_component, is_mutating_allowed, tracking, teardown, run_block, run_teardown, create_component_ctx, push_component, pop_component, with_block, with_scope, scope, safe_scope, set_tracking, set_active_reaction, is_block_dirty, destroy_non_branch_children, disable_scoped_flush, on_destroy } from '@vesk/runtime/src/ripple-runtime';
2
2
  export { effect, user_effect, block, branch, root, render, pre_effect, destroy_block, destroy_block_children, pause_block, resume_block, is_destroyed, unlink_block, create_try_block, boundary_fn_running_block } from '@vesk/runtime/src/ripple-blocks';
3
- export { hydrate, hydrateViewport, hydrateIdle, hydrateOnInteraction, needsHydration, hydrationCount, createHydrateWalker, collectVskMarkers, reactiveProps, hydrateInitial, assertFullyHydrated, auditHydration, setHydrateDevMode, setHydrateStrict, isHydrateStrict, onHydrationMismatch, bumpNavEpoch, isVskMarkerText, parseVskMarker } from '@vesk/runtime/src/hydrate';
3
+ export { hydrate, hydrateViewport, hydrateIdle, hydrateOnInteraction, needsHydration, hydrationCount, createHydrateWalker, collectVskMarkers, reactiveProps, hydrateInitial, assertFullyHydrated, auditHydration, setHydrateDevMode, setHydrateStrict, isHydrateStrict, onHydrationMismatch, reportHydrationIssue, bumpNavEpoch, isVskMarkerText, parseVskMarker } from '@vesk/runtime/src/hydrate';
4
+ export { createLayoutSlot, trackSlotContent, auditLayoutSlots, LAYOUT_SLOT_OPEN, LAYOUT_SLOT_CLOSE } from '@vesk/runtime/src/layout';
4
5
  export { createRouter, createFileRouter, Outlet, Link, NavLink, useNavigate, useParams, usePathname, useSearchParams, useRouter, buildRouteTree, defineRoute, Redirect, redirect, permanentRedirect, notFound, NotFoundError, ensureChunk, matchRoute } from '@vesk/runtime/src/router';
5
6
  export { getNetworkState, watchNetwork } from '@vesk/runtime/src/network';
6
7
  export { bindValue, bindChecked, bindGroup } from '@vesk/runtime/src/bindings';
@@ -0,0 +1,59 @@
1
+ import type { HydrateWalker } from '@vesk/runtime/src/hydrate';
2
+ /** SSR comment text opening a layout page-slot region. */
3
+ export declare const LAYOUT_SLOT_OPEN = "vsk-slot";
4
+ /** SSR comment text closing a layout page-slot region (also the async anchor). */
5
+ export declare const LAYOUT_SLOT_CLOSE = "vsk-slot-end";
6
+ export interface LayoutSlot {
7
+ /** Walker scoped to the slot's SSR markers (or the shared walker when unscopable). */
8
+ walker: HydrateWalker;
9
+ /** True when the walker is scoped to slot boundaries (not the shared cursor). */
10
+ scoped: boolean;
11
+ /**
12
+ * Wire an async child hydrator result: resolved slot content is inserted
13
+ * at its SSR position (before the slot-end boundary). Returns a promise
14
+ * for the layout's `__pendingChild` chain.
15
+ */
16
+ track(promise: unknown): unknown;
17
+ }
18
+ /**
19
+ * Slot boundary identity. Server codegen stamps every pair with a unique id
20
+ * (`vsk-slot:s7` … `vsk-slot-end:s7`); pre-boundary output uses the bare texts
21
+ * (legacy, id `''`). Open/close are paired by EXACT id — never positionally —
22
+ * so nested layouts and sibling slots cannot mis-scope each other. No regex:
23
+ * prefix strip only.
24
+ */
25
+ export interface SlotIdentity {
26
+ /** '' for legacy bare boundaries, otherwise the stamped id. */
27
+ id: string;
28
+ }
29
+ /**
30
+ * Anchor an async slot child: when the hydrator promise resolves, detached
31
+ * slot content is inserted BEFORE the slot-end boundary (its SSR position).
32
+ * When the boundary is gone (a navigation replaced the DOM while suspended),
33
+ * the late content is dropped — hydrating replaced DOM is how duplicates are
34
+ * born. Resolution value and errors pass through untouched.
35
+ */
36
+ export declare function trackSlotContent(promise: unknown, anchor: Comment | null): unknown;
37
+ /**
38
+ * Build the slot scope for a layout `{children}` invocation.
39
+ *
40
+ * Transfers the whole slot range — depth-0 markers plus nested-slot
41
+ * interiors — out of the shared walk (`takeMarkers`), then hands back a
42
+ * walker scoped to the depth-0 markers only. Nav/footer claims can no longer
43
+ * drift into page markers and page claims can no longer steal footer markers,
44
+ * including across `await` suspensions (scope never depends on claim timing)
45
+ * and across nesting levels (an inner scope re-scans the live DOM and owns
46
+ * its interior exclusively). Falls back to the shared walker (legacy
47
+ * behavior) when boundaries are absent, unbalanced, already consumed, or the
48
+ * walker cannot transfer — a fallback degrades to today's semantics, never to
49
+ * a wrong scope.
50
+ */
51
+ export declare function createLayoutSlot(walker: HydrateWalker, parentEl: Element | null): LayoutSlot;
52
+ export interface LayoutSlotAudit {
53
+ ok: boolean;
54
+ unbalanced: number;
55
+ leftoverInSlot: number;
56
+ }
57
+ /** Verify slot boundary balance and slot-region claim completeness. */
58
+ export declare function auditLayoutSlots(container: HTMLElement): LayoutSlotAudit;
59
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAqC/D,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAC3C,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAEhD,MAAM,WAAW,UAAU;IAC1B,sFAAsF;IACtF,MAAM,EAAE,aAAa,CAAC;IACtB,iFAAiF;IACjF,MAAM,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;CACjC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC5B,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAC;CACX;AAyOD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAuBlF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,OAAO,GAAG,IAAI,GACtB,UAAU,CA4CZ;AAcD,MAAM,WAAW,eAAe;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACvB;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,WAAW,GAAG,eAAe,CA8DxE"}
package/dist/layout.js ADDED
@@ -0,0 +1,456 @@
1
+ import { createHydrateWalker, isVskMarkerText, reportHydrationIssue, } from '@vesk/runtime/src/hydrate';
2
+ // ---------------------------------------------------------------------------
3
+ // Dedicated layout contract (Layout hydration ownership).
4
+ //
5
+ // A layout composes three independently-authored regions — chrome/nav, the
6
+ // page slot, footer — that SSR serializes as undifferentiated siblings inside
7
+ // one parent element. Hydrating all three off a single shared positional
8
+ // cursor means ANY single claim miss (a self-claiming Link in the nav, an
9
+ // async suspension in the page, a conditional) shifts every later claim into
10
+ // fresh-node fallback: fresh nodes append at region tails, so one miss twins
11
+ // the footer, and a late-resolving async page lands below the footer (or the
12
+ // page claims first and the layout stacks under it).
13
+ //
14
+ // Containment scoping (`subWalker(enclosingEl)`) is not the answer either: it
15
+ // bulk-transfers sibling markers rendered AFTER the slot (the footer) into
16
+ // the page's walk. The missing piece is boundary-delimited scoping, owned
17
+ // here:
18
+ //
19
+ // * SSR wraps every `{props.children}` region in `<!--vsk-slot-->` …
20
+ // `<!--vsk-slot-end-->` (server codegen). The texts deliberately lack the
21
+ // `vsk:` prefix so the generic marker machinery (walkers, canary, audit)
22
+ // ignores them; only this module reads them.
23
+ // * `createLayoutSlot` transfers the slot range out of the shared walk and
24
+ // hands back a walker scoped to the depth-0 markers. Nav/footer claims can
25
+ // no longer drift into page markers and page claims can no longer steal
26
+ // footer markers — including across `await` suspensions, because scope no
27
+ // longer depends on claim timing, and across nesting (nested-slot
28
+ // interiors are neutralized in the ancestor walk and re-scoped by the
29
+ // inner layout from the live DOM).
30
+ // * `track` anchors async slot content: a suspending child resolves to
31
+ // nodes inserted BEFORE the slot-end boundary at its SSR position — never
32
+ // appended at the tail, never dropped. A navigation that replaced the DOM
33
+ // first (slot-end detached) drops the late content instead of duplicating
34
+ // it into the new page.
35
+ // ---------------------------------------------------------------------------
36
+ /** SSR comment text opening a layout page-slot region. */
37
+ export const LAYOUT_SLOT_OPEN = 'vsk-slot';
38
+ /** SSR comment text closing a layout page-slot region (also the async anchor). */
39
+ export const LAYOUT_SLOT_CLOSE = 'vsk-slot-end';
40
+ function slotIdentity(text, prefix) {
41
+ if (text === prefix)
42
+ return { id: '' };
43
+ if (typeof text !== 'string')
44
+ return null;
45
+ const want = prefix + ':';
46
+ if (text.length > want.length && text.startsWith(want))
47
+ return { id: text.slice(want.length) };
48
+ return null;
49
+ }
50
+ function slotOpenId(text) {
51
+ return slotIdentity(text, LAYOUT_SLOT_OPEN);
52
+ }
53
+ function slotCloseId(text) {
54
+ return slotIdentity(text, LAYOUT_SLOT_CLOSE);
55
+ }
56
+ function slotUsed(node) {
57
+ try {
58
+ return node.__vsk_slot_used === true;
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ function markSlotUsed(node) {
65
+ try {
66
+ node.__vsk_slot_used = true;
67
+ }
68
+ catch {
69
+ // Expando unsupported — the pair may be re-scanned, which degrades to
70
+ // the legacy shared-walker path, never to a wrong scope.
71
+ }
72
+ }
73
+ /**
74
+ * Locate the unconsumed slot boundary pair that are direct children of
75
+ * `parentEl` and collect the `vsk:` markers between them in document order.
76
+ *
77
+ * Open/close pair by EXACT boundary id (legacy bare boundaries pair
78
+ * positionally, as before). An id-stamped open with no same-id close — or a
79
+ * legacy open facing only id-stamped closes (version skew) — refuses to scope
80
+ * (null) rather than risk a wrong range. Nested layout slots inside the range
81
+ * are skipped via a depth counter, so an outer slot never captures an inner
82
+ * layout's page markers (each layout scopes its own pair when its slot runs).
83
+ * Returns null when the SSR output predates slot boundaries (legacy) or the
84
+ * pair is unbalanced.
85
+ */
86
+ function findSlotRange(parentEl) {
87
+ let open = null;
88
+ let openId = null;
89
+ let close = null;
90
+ try {
91
+ // Walk the entire subtree of parentEl (not just direct children) so a
92
+ // layout whose slot lives inside a nested <main> (DocsLayout:
93
+ // outer <div> → <div.flex> → <main>) is still found. The old
94
+ // direct-children scan missed those and fell back to the shared walk
95
+ // (257-marker hydration drift on /docs).
96
+ const walker = document.createTreeWalker(parentEl, 128, {
97
+ acceptNode: (node) => {
98
+ const t = node.textContent;
99
+ if (slotOpenId(t) !== null || slotCloseId(t) !== null)
100
+ return 1;
101
+ return 2;
102
+ },
103
+ });
104
+ // Nesting depth while seeking the close: a nested slot pair inside
105
+ // our range must not donate its close to us. Bare boundaries
106
+ // (`vsk-slot` … `vsk-slot-end`) carry no id, so without depth the
107
+ // first inner close would prematurely end the outer range and
108
+ // mis-scope the page. Distinct ids pair by exact id AND depth.
109
+ let nest = 0;
110
+ while (walker.nextNode()) {
111
+ const c = walker.currentNode;
112
+ const t = c.textContent;
113
+ if (open === null) {
114
+ const id = slotOpenId(t);
115
+ if (id !== null && !slotUsed(c)) {
116
+ open = c;
117
+ openId = id;
118
+ }
119
+ }
120
+ else {
121
+ if (slotOpenId(t) !== null && !slotUsed(c)) {
122
+ nest++;
123
+ continue;
124
+ }
125
+ const id = slotCloseId(t);
126
+ if (id === null || slotUsed(c))
127
+ continue;
128
+ if (id.id === openId.id && nest === 0) {
129
+ // Ensure close is after open in document order and still
130
+ // inside parentEl (TreeWalker guarantees this, but verify
131
+ // via isConnected/contains for detached-navigation safety).
132
+ try {
133
+ if (!parentEl.contains?.(c))
134
+ continue;
135
+ }
136
+ catch { }
137
+ close = c;
138
+ break;
139
+ }
140
+ if (nest > 0)
141
+ nest--;
142
+ }
143
+ }
144
+ }
145
+ catch {
146
+ return null;
147
+ }
148
+ if (!open || !close)
149
+ return null;
150
+ // Document-order walk from `open` to `close`, collecting vsk markers at
151
+ // nesting depth 0 for the slot walker, and every vsk marker in range for
152
+ // ancestor neutralization (see `SlotRange.all`). A nested `vsk-slot` …
153
+ // `vsk-slot-end` pair (inner layout) increments depth so its interior
154
+ // markers stay owned by the inner scope.
155
+ const markers = [];
156
+ const all = [];
157
+ let depth = 0;
158
+ let node = null;
159
+ try {
160
+ node = open.nextSibling;
161
+ }
162
+ catch {
163
+ return null;
164
+ }
165
+ let guard = 0;
166
+ while (node !== null && node !== close && guard++ < 100000) {
167
+ let next = null;
168
+ let descend = null;
169
+ try {
170
+ const n = node;
171
+ next = n.nextSibling;
172
+ descend = n.firstChild;
173
+ if (n.nodeType === 8) {
174
+ if (slotOpenId(n.textContent) !== null) {
175
+ depth++;
176
+ }
177
+ else if (slotCloseId(n.textContent) !== null) {
178
+ if (depth === 0)
179
+ break; // unbalanced: a close with no open — stop, don't over-capture
180
+ depth--;
181
+ }
182
+ else if (isVskMarkerText(n.textContent)) {
183
+ all.push(n);
184
+ // Depth-0 only: nested `vsk-slot` interiors stay owned by
185
+ // the inner layout's own scope (see the nested test in
186
+ // layout.test.ts). The ancestor walk is neutralized for
187
+ // the whole range via `all` + `takeMarkers` regardless.
188
+ if (depth === 0)
189
+ markers.push(n);
190
+ }
191
+ }
192
+ }
193
+ catch {
194
+ return null;
195
+ }
196
+ // Preorder: descend first, else advance; climb past exhausted parents.
197
+ if (descend !== null && descend !== undefined) {
198
+ node = descend;
199
+ }
200
+ else {
201
+ let cur = node;
202
+ let nxt = next;
203
+ while (nxt === null || nxt === undefined) {
204
+ let parent = null;
205
+ try {
206
+ parent = cur.parentNode;
207
+ }
208
+ catch {
209
+ return null;
210
+ }
211
+ if (parent === null || parent === undefined || parent === parentEl)
212
+ break;
213
+ if (parent === close) {
214
+ nxt = null;
215
+ cur = null;
216
+ break;
217
+ }
218
+ try {
219
+ nxt = parent.nextSibling;
220
+ }
221
+ catch {
222
+ return null;
223
+ }
224
+ cur = parent;
225
+ }
226
+ if (cur === null)
227
+ break;
228
+ node = nxt;
229
+ }
230
+ }
231
+ if (node !== close)
232
+ return null; // never reached the close — unbalanced, refuse to scope
233
+ markSlotUsed(open);
234
+ markSlotUsed(close);
235
+ return { markers, all, end: close };
236
+ }
237
+ function insertDetachedSlotContent(value, anchor) {
238
+ let parent = null;
239
+ try {
240
+ parent = anchor.parentNode;
241
+ if (!parent)
242
+ return;
243
+ }
244
+ catch {
245
+ return;
246
+ }
247
+ const nodes = [];
248
+ if (value && typeof value === 'object') {
249
+ const v = value;
250
+ if (v.nodeType === 11 || v.nodeType === 1)
251
+ nodes.push(v);
252
+ else if (typeof v.length === 'number') {
253
+ for (let i = 0; i < v.length; i++) {
254
+ const n = v[i];
255
+ if (n && (n.nodeType === 11 || n.nodeType === 1))
256
+ nodes.push(n);
257
+ }
258
+ }
259
+ else
260
+ return;
261
+ }
262
+ else
263
+ return;
264
+ for (const n of nodes) {
265
+ if (n === anchor)
266
+ continue;
267
+ let attached = null;
268
+ try {
269
+ attached = n.parentNode;
270
+ }
271
+ catch {
272
+ continue;
273
+ }
274
+ // Only place detached content. Attached nodes were claimed in place —
275
+ // moving them would reorder live DOM on a hunch.
276
+ if (attached !== null && attached !== undefined)
277
+ continue;
278
+ try {
279
+ parent.insertBefore(n, anchor);
280
+ }
281
+ catch {
282
+ // A failed insert must not strand the render; the audit reports it.
283
+ }
284
+ }
285
+ }
286
+ /**
287
+ * Anchor an async slot child: when the hydrator promise resolves, detached
288
+ * slot content is inserted BEFORE the slot-end boundary (its SSR position).
289
+ * When the boundary is gone (a navigation replaced the DOM while suspended),
290
+ * the late content is dropped — hydrating replaced DOM is how duplicates are
291
+ * born. Resolution value and errors pass through untouched.
292
+ */
293
+ export function trackSlotContent(promise, anchor) {
294
+ if (!promise || typeof promise.then !== 'function' || !anchor)
295
+ return promise;
296
+ return promise.then((value) => {
297
+ let live = false;
298
+ try {
299
+ live = !!anchor.parentNode && anchor.isConnected !== false;
300
+ }
301
+ catch {
302
+ live = false;
303
+ }
304
+ if (live)
305
+ insertDetachedSlotContent(value, anchor);
306
+ else {
307
+ reportHydrationIssue({
308
+ kind: 'leftover-marker',
309
+ detail: 'async layout slot resolved after its SSR boundary detached (navigation replaced the DOM); late slot content dropped instead of duplicated.',
310
+ });
311
+ }
312
+ return value;
313
+ }, (error) => {
314
+ throw error;
315
+ });
316
+ }
317
+ /**
318
+ * Build the slot scope for a layout `{children}` invocation.
319
+ *
320
+ * Transfers the whole slot range — depth-0 markers plus nested-slot
321
+ * interiors — out of the shared walk (`takeMarkers`), then hands back a
322
+ * walker scoped to the depth-0 markers only. Nav/footer claims can no longer
323
+ * drift into page markers and page claims can no longer steal footer markers,
324
+ * including across `await` suspensions (scope never depends on claim timing)
325
+ * and across nesting levels (an inner scope re-scans the live DOM and owns
326
+ * its interior exclusively). Falls back to the shared walker (legacy
327
+ * behavior) when boundaries are absent, unbalanced, already consumed, or the
328
+ * walker cannot transfer — a fallback degrades to today's semantics, never to
329
+ * a wrong scope.
330
+ */
331
+ export function createLayoutSlot(walker, parentEl) {
332
+ const fallback = {
333
+ walker,
334
+ scoped: false,
335
+ track: (promise) => trackSlotContent(promise, null),
336
+ };
337
+ if (!walker || !parentEl)
338
+ return fallback;
339
+ let range = null;
340
+ try {
341
+ range = findSlotRange(parentEl);
342
+ }
343
+ catch {
344
+ return fallback;
345
+ }
346
+ if (!range) {
347
+ reportHydrationIssue({
348
+ kind: 'untyped-marker',
349
+ detail: 'layout slot has no SSR slot boundaries (legacy output or unbalanced markers); hydrating the page on the shared walker — ordering is best-effort.',
350
+ });
351
+ return fallback;
352
+ }
353
+ if (typeof walker.takeMarkers !== 'function') {
354
+ reportHydrationIssue({
355
+ kind: 'untyped-marker',
356
+ detail: 'layout slot walker cannot transfer markers; hydrating the page on the shared walker — ordering is best-effort.',
357
+ });
358
+ return { walker, scoped: false, track: (promise) => trackSlotContent(promise, range.end) };
359
+ }
360
+ try {
361
+ walker.takeMarkers(range.all);
362
+ }
363
+ catch {
364
+ return fallback;
365
+ }
366
+ let slotWalker;
367
+ try {
368
+ slotWalker = createHydrateWalker(parentEl, range.markers);
369
+ }
370
+ catch {
371
+ return fallback;
372
+ }
373
+ const end = range.end;
374
+ return {
375
+ walker: slotWalker,
376
+ scoped: true,
377
+ track: (promise) => trackSlotContent(promise, end),
378
+ };
379
+ }
380
+ // ---------------------------------------------------------------------------
381
+ // Strict slot audit: every slot boundary pair must be balanced, and no `vsk:`
382
+ // marker may survive inside a slot range after hydration — a leftover there is
383
+ // page content the client never claimed (the twin/duplication surface).
384
+ // Runs from the router after the layout chain hydrates (full strategy only;
385
+ // deferred strategies hydrate the slot later by design).
386
+ // ---------------------------------------------------------------------------
387
+ const _SHOW_COMMENT = 128;
388
+ const _FILTER_ACCEPT = 1;
389
+ const _FILTER_SKIP = 2;
390
+ /** Verify slot boundary balance and slot-region claim completeness. */
391
+ export function auditLayoutSlots(container) {
392
+ let unbalanced = 0;
393
+ let leftoverInSlot = 0;
394
+ try {
395
+ const walker = document.createTreeWalker(container, _SHOW_COMMENT, {
396
+ acceptNode: (node) => {
397
+ const t = node.textContent;
398
+ if (slotOpenId(t) !== null || slotCloseId(t) !== null || isVskMarkerText(t))
399
+ return _FILTER_ACCEPT;
400
+ return _FILTER_SKIP;
401
+ },
402
+ });
403
+ // Open-id stack: id-stamped pairs must close in LIFO order with the
404
+ // exact id; legacy bare boundaries pair blindly (as before). Any id
405
+ // mismatch names the diverging slot instead of silently mis-scoping.
406
+ const stack = [];
407
+ while (walker.nextNode()) {
408
+ const c = walker.currentNode;
409
+ const t = c.textContent;
410
+ const open = slotOpenId(t);
411
+ if (open !== null) {
412
+ stack.push(open.id);
413
+ continue;
414
+ }
415
+ const close = slotCloseId(t);
416
+ if (close !== null) {
417
+ if (stack.length === 0) {
418
+ unbalanced++;
419
+ reportHydrationIssue({
420
+ kind: 'marker-skew',
421
+ detail: 'layout slot close without a matching open — SSR/client slot structure diverges; slot content may misorder.',
422
+ });
423
+ continue;
424
+ }
425
+ const top = stack.pop();
426
+ if (top !== '' && close.id !== '' && top !== close.id) {
427
+ unbalanced++;
428
+ reportHydrationIssue({
429
+ kind: 'marker-skew',
430
+ detail: `layout slot id mismatch: open "${top}" closed by "${close.id}" — SSR/client slot structure diverges; slot content may misorder.`,
431
+ });
432
+ }
433
+ continue;
434
+ }
435
+ if (stack.length > 0 && isVskMarkerText(t))
436
+ leftoverInSlot++;
437
+ }
438
+ if (stack.length > 0) {
439
+ unbalanced++;
440
+ reportHydrationIssue({
441
+ kind: 'marker-skew',
442
+ detail: 'layout slot open without a matching close — SSR/client slot structure diverges; slot content may misorder.',
443
+ });
444
+ }
445
+ if (leftoverInSlot > 0) {
446
+ reportHydrationIssue({
447
+ kind: 'leftover-marker',
448
+ detail: `${leftoverInSlot} hydration marker(s) survived inside a layout slot region; the page rendered differently than SSR — duplication risk.`,
449
+ });
450
+ }
451
+ }
452
+ catch {
453
+ // Audit must never break rendering.
454
+ }
455
+ return { ok: unbalanced === 0 && leftoverInSlot === 0, unbalanced, leftoverInSlot };
456
+ }
@@ -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
@@ -1,6 +1,7 @@
1
1
  import { get, set, scope, set_active_block } from '@vesk/runtime/src/ripple-runtime';
2
2
  import { root } from '@vesk/runtime/src/ripple-blocks';
3
3
  import { createHydrateWalker, hydrateViewport, hydrateIdle, hydrateOnInteraction, bumpNavEpoch } from '@vesk/runtime/src/hydrate';
4
+ import { auditLayoutSlots } from '@vesk/runtime/src/layout';
4
5
  import { matchRoute, flattenLayoutChain, buildTreeFromMap } from '@vesk/runtime/src/router-match';
5
6
  import { setIsHydrating, _state, _scrollPositions, _isPopStateNavigation, setIsPopStateNavigation, setCurrentRouter, showLoadingInContainer, handleScroll, applyHead, findLoadingComponent, findErrorComponent, findNotFoundComponent, findOfflineComponent, findNetworkComponent, getCurrentRouter, Outlet, Link, NavLink, useNavigate, useParams, usePathname, useSearchParams, useRouter, Redirect, redirect, permanentRedirect, NotFoundError, notFound, } from '@vesk/runtime/src/router-components';
6
7
  import { getNetworkState, watchNetwork } from '@vesk/runtime/src/network';
@@ -1137,6 +1138,18 @@ async function hydrateInitial(router, match, container, strategy) {
1137
1138
  }
1138
1139
  const walker = createHydrateWalker(container);
1139
1140
  await runInBlockWindow(() => renderLayoutChain(0)(walker));
1141
+ // Strict slot audit: with boundary-delimited slot walkers, any marker
1142
+ // surviving inside a slot region (or an unbalanced boundary pair) is a
1143
+ // page/layout divergence — the duplication surface. Deferred strategies
1144
+ // hydrate the slot later by design, so only the full strategy audits.
1145
+ if (!strategy || strategy === 'full') {
1146
+ try {
1147
+ auditLayoutSlots(container);
1148
+ }
1149
+ catch {
1150
+ // Audit must never break rendering.
1151
+ }
1152
+ }
1140
1153
  setIsHydrating(false);
1141
1154
  }
1142
1155
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/runtime",
3
- "version": "0.2.28",
3
+ "version": "0.2.29",
4
4
  "type": "module",
5
5
  "description": "Vesk runtime — reactivity, blocks, DOM operations, hydration",
6
6
  "main": "./dist/index-client.js",
@@ -51,7 +51,7 @@
51
51
  }
52
52
  },
53
53
  "dependencies": {
54
- "@vesk/compiler": "^0.2.28"
54
+ "@vesk/compiler": "^0.2.29"
55
55
  },
56
56
  "scripts": {
57
57
  "typecheck": "tsc --noEmit",