@archie/devtools 0.1.13 → 0.1.15

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.
@@ -46,8 +46,22 @@ type InjectOptions = {
46
46
  localScriptUrl?: string;
47
47
  };
48
48
 
49
+ type ArchieRouteFrame = 'phone' | 'tablet' | 'desktop';
50
+ /**
51
+ * Whitelisted subset of a React Router `handle` that is forwarded in
52
+ * ARCHIE_ROUTE_UPDATE. The router's `handle` is free-form (functions, React
53
+ * elements...) and is NOT structured-cloneable in general, so only `surface`
54
+ * (non-empty string) and `frame` ('phone' | 'tablet' | 'desktop') are copied;
55
+ * any other key and any invalid value is dropped. When neither survives the
56
+ * `handle` key is omitted from the entry entirely.
57
+ */
58
+ type ArchieRouteHandle = {
59
+ surface?: string;
60
+ frame?: ArchieRouteFrame;
61
+ };
49
62
  interface ArchieRouteObjectLike {
50
63
  id?: string;
64
+ handle?: unknown;
51
65
  path?: string;
52
66
  index?: boolean;
53
67
  Component?: unknown;
@@ -58,6 +72,7 @@ interface ArchieRouteObjectLike {
58
72
  interface ArchieRouteMatchLike {
59
73
  route: {
60
74
  id?: string;
75
+ handle?: unknown;
61
76
  };
62
77
  pathname: string;
63
78
  params: Record<string, string | undefined>;
@@ -84,6 +99,46 @@ interface ArchieRouterLike {
84
99
  }): Promise<void> | void;
85
100
  subscribe(listener: (state: ArchieRouterStateLike) => void): () => void;
86
101
  }
102
+ /**
103
+ * Route information for the sitemap
104
+ */
105
+ interface ArchieGuardEvidence {
106
+ componentName: string;
107
+ roles?: string[];
108
+ source: 'self' | 'ancestor';
109
+ ancestorId?: string;
110
+ }
111
+ interface RouteInfo {
112
+ id: string | undefined;
113
+ path: string;
114
+ handle?: ArchieRouteHandle;
115
+ hasChildren: boolean;
116
+ parentId: string | null;
117
+ index: boolean;
118
+ isLayout: boolean;
119
+ componentName: string | null;
120
+ guardEvidence: ArchieGuardEvidence[];
121
+ }
122
+ /**
123
+ * Type definition for the route update message
124
+ */
125
+ interface ArchieRouteUpdateMessage {
126
+ type: 'ARCHIE_ROUTE_UPDATE';
127
+ payload: {
128
+ path: string;
129
+ search: string;
130
+ hash: string;
131
+ matches: Array<{
132
+ id: string | undefined;
133
+ pathname: string;
134
+ params: Record<string, string | undefined>;
135
+ handle?: ArchieRouteHandle;
136
+ }>;
137
+ allRoutes: RouteInfo[];
138
+ layoutRoutes: RouteInfo[];
139
+ timestamp: number;
140
+ };
141
+ }
87
142
 
88
143
  interface ArchieDevToolsRuntimeOptions {
89
144
  router: ArchieRouterLike;
@@ -103,4 +158,4 @@ interface DndProviderProps {
103
158
  }
104
159
  declare function DndProvider({ children }: DndProviderProps): react_jsx_runtime.JSX.Element;
105
160
 
106
- export { ArchieDevToolProvider, type ArchieDevToolProviderProps, DndProvider };
161
+ export { ArchieDevToolProvider, type ArchieDevToolProviderProps, type ArchieRouteFrame, type ArchieRouteHandle, type ArchieRouteUpdateMessage, DndProvider, type RouteInfo };
@@ -46,8 +46,22 @@ type InjectOptions = {
46
46
  localScriptUrl?: string;
47
47
  };
48
48
 
49
+ type ArchieRouteFrame = 'phone' | 'tablet' | 'desktop';
50
+ /**
51
+ * Whitelisted subset of a React Router `handle` that is forwarded in
52
+ * ARCHIE_ROUTE_UPDATE. The router's `handle` is free-form (functions, React
53
+ * elements...) and is NOT structured-cloneable in general, so only `surface`
54
+ * (non-empty string) and `frame` ('phone' | 'tablet' | 'desktop') are copied;
55
+ * any other key and any invalid value is dropped. When neither survives the
56
+ * `handle` key is omitted from the entry entirely.
57
+ */
58
+ type ArchieRouteHandle = {
59
+ surface?: string;
60
+ frame?: ArchieRouteFrame;
61
+ };
49
62
  interface ArchieRouteObjectLike {
50
63
  id?: string;
64
+ handle?: unknown;
51
65
  path?: string;
52
66
  index?: boolean;
53
67
  Component?: unknown;
@@ -58,6 +72,7 @@ interface ArchieRouteObjectLike {
58
72
  interface ArchieRouteMatchLike {
59
73
  route: {
60
74
  id?: string;
75
+ handle?: unknown;
61
76
  };
62
77
  pathname: string;
63
78
  params: Record<string, string | undefined>;
@@ -84,6 +99,46 @@ interface ArchieRouterLike {
84
99
  }): Promise<void> | void;
85
100
  subscribe(listener: (state: ArchieRouterStateLike) => void): () => void;
86
101
  }
102
+ /**
103
+ * Route information for the sitemap
104
+ */
105
+ interface ArchieGuardEvidence {
106
+ componentName: string;
107
+ roles?: string[];
108
+ source: 'self' | 'ancestor';
109
+ ancestorId?: string;
110
+ }
111
+ interface RouteInfo {
112
+ id: string | undefined;
113
+ path: string;
114
+ handle?: ArchieRouteHandle;
115
+ hasChildren: boolean;
116
+ parentId: string | null;
117
+ index: boolean;
118
+ isLayout: boolean;
119
+ componentName: string | null;
120
+ guardEvidence: ArchieGuardEvidence[];
121
+ }
122
+ /**
123
+ * Type definition for the route update message
124
+ */
125
+ interface ArchieRouteUpdateMessage {
126
+ type: 'ARCHIE_ROUTE_UPDATE';
127
+ payload: {
128
+ path: string;
129
+ search: string;
130
+ hash: string;
131
+ matches: Array<{
132
+ id: string | undefined;
133
+ pathname: string;
134
+ params: Record<string, string | undefined>;
135
+ handle?: ArchieRouteHandle;
136
+ }>;
137
+ allRoutes: RouteInfo[];
138
+ layoutRoutes: RouteInfo[];
139
+ timestamp: number;
140
+ };
141
+ }
87
142
 
88
143
  interface ArchieDevToolsRuntimeOptions {
89
144
  router: ArchieRouterLike;
@@ -103,4 +158,4 @@ interface DndProviderProps {
103
158
  }
104
159
  declare function DndProvider({ children }: DndProviderProps): react_jsx_runtime.JSX.Element;
105
160
 
106
- export { ArchieDevToolProvider, type ArchieDevToolProviderProps, DndProvider };
161
+ export { ArchieDevToolProvider, type ArchieDevToolProviderProps, type ArchieRouteFrame, type ArchieRouteHandle, type ArchieRouteUpdateMessage, DndProvider, type RouteInfo };
@@ -2503,37 +2503,37 @@ function getInitialState() {
2503
2503
  }
2504
2504
  };
2505
2505
  }
2506
- function reducer(state, action) {
2506
+ function reducer(state2, action) {
2507
2507
  switch (action.type) {
2508
2508
  case Action.DragStart:
2509
2509
  return {
2510
- ...state,
2510
+ ...state2,
2511
2511
  draggable: {
2512
- ...state.draggable,
2512
+ ...state2.draggable,
2513
2513
  initialCoordinates: action.initialCoordinates,
2514
2514
  active: action.active
2515
2515
  }
2516
2516
  };
2517
2517
  case Action.DragMove:
2518
- if (state.draggable.active == null) {
2519
- return state;
2518
+ if (state2.draggable.active == null) {
2519
+ return state2;
2520
2520
  }
2521
2521
  return {
2522
- ...state,
2522
+ ...state2,
2523
2523
  draggable: {
2524
- ...state.draggable,
2524
+ ...state2.draggable,
2525
2525
  translate: {
2526
- x: action.coordinates.x - state.draggable.initialCoordinates.x,
2527
- y: action.coordinates.y - state.draggable.initialCoordinates.y
2526
+ x: action.coordinates.x - state2.draggable.initialCoordinates.x,
2527
+ y: action.coordinates.y - state2.draggable.initialCoordinates.y
2528
2528
  }
2529
2529
  }
2530
2530
  };
2531
2531
  case Action.DragEnd:
2532
2532
  case Action.DragCancel:
2533
2533
  return {
2534
- ...state,
2534
+ ...state2,
2535
2535
  draggable: {
2536
- ...state.draggable,
2536
+ ...state2.draggable,
2537
2537
  active: null,
2538
2538
  initialCoordinates: {
2539
2539
  x: 0,
@@ -2552,12 +2552,12 @@ function reducer(state, action) {
2552
2552
  const {
2553
2553
  id
2554
2554
  } = element;
2555
- const containers = new DroppableContainersMap(state.droppable.containers);
2555
+ const containers = new DroppableContainersMap(state2.droppable.containers);
2556
2556
  containers.set(id, element);
2557
2557
  return {
2558
- ...state,
2558
+ ...state2,
2559
2559
  droppable: {
2560
- ...state.droppable,
2560
+ ...state2.droppable,
2561
2561
  containers
2562
2562
  }
2563
2563
  };
@@ -2568,19 +2568,19 @@ function reducer(state, action) {
2568
2568
  key: key2,
2569
2569
  disabled
2570
2570
  } = action;
2571
- const element = state.droppable.containers.get(id);
2571
+ const element = state2.droppable.containers.get(id);
2572
2572
  if (!element || key2 !== element.key) {
2573
- return state;
2573
+ return state2;
2574
2574
  }
2575
- const containers = new DroppableContainersMap(state.droppable.containers);
2575
+ const containers = new DroppableContainersMap(state2.droppable.containers);
2576
2576
  containers.set(id, {
2577
2577
  ...element,
2578
2578
  disabled
2579
2579
  });
2580
2580
  return {
2581
- ...state,
2581
+ ...state2,
2582
2582
  droppable: {
2583
- ...state.droppable,
2583
+ ...state2.droppable,
2584
2584
  containers
2585
2585
  }
2586
2586
  };
@@ -2590,22 +2590,22 @@ function reducer(state, action) {
2590
2590
  id,
2591
2591
  key: key2
2592
2592
  } = action;
2593
- const element = state.droppable.containers.get(id);
2593
+ const element = state2.droppable.containers.get(id);
2594
2594
  if (!element || key2 !== element.key) {
2595
- return state;
2595
+ return state2;
2596
2596
  }
2597
- const containers = new DroppableContainersMap(state.droppable.containers);
2597
+ const containers = new DroppableContainersMap(state2.droppable.containers);
2598
2598
  containers.delete(id);
2599
2599
  return {
2600
- ...state,
2600
+ ...state2,
2601
2601
  droppable: {
2602
- ...state.droppable,
2602
+ ...state2.droppable,
2603
2603
  containers
2604
2604
  }
2605
2605
  };
2606
2606
  }
2607
2607
  default: {
2608
- return state;
2608
+ return state2;
2609
2609
  }
2610
2610
  }
2611
2611
  }
@@ -2763,7 +2763,7 @@ var DndContext = /* @__PURE__ */ (0, import_react3.memo)(function DndContext2(_r
2763
2763
  ...props
2764
2764
  } = _ref;
2765
2765
  const store = (0, import_react3.useReducer)(reducer, void 0, getInitialState);
2766
- const [state, dispatch] = store;
2766
+ const [state2, dispatch] = store;
2767
2767
  const [dispatchMonitorEvent, registerMonitorListener] = useDndMonitorProvider();
2768
2768
  const [status, setStatus] = (0, import_react3.useState)(Status.Uninitialized);
2769
2769
  const isInitialized = status === Status.Initialized;
@@ -2776,7 +2776,7 @@ var DndContext = /* @__PURE__ */ (0, import_react3.memo)(function DndContext2(_r
2776
2776
  droppable: {
2777
2777
  containers: droppableContainers
2778
2778
  }
2779
- } = state;
2779
+ } = state2;
2780
2780
  const node = activeId != null ? draggableNodes.get(activeId) : null;
2781
2781
  const activeRects = (0, import_react3.useRef)({
2782
2782
  initial: null,
@@ -3937,24 +3937,24 @@ function isAfter(a, b) {
3937
3937
 
3938
3938
  // node_modules/zustand/esm/vanilla.mjs
3939
3939
  var createStoreImpl = (createState) => {
3940
- let state;
3940
+ let state2;
3941
3941
  const listeners = /* @__PURE__ */ new Set();
3942
3942
  const setState = (partial, replace) => {
3943
- const nextState = typeof partial === "function" ? partial(state) : partial;
3944
- if (!Object.is(nextState, state)) {
3945
- const previousState = state;
3946
- state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
3947
- listeners.forEach((listener) => listener(state, previousState));
3943
+ const nextState = typeof partial === "function" ? partial(state2) : partial;
3944
+ if (!Object.is(nextState, state2)) {
3945
+ const previousState = state2;
3946
+ state2 = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state2, nextState);
3947
+ listeners.forEach((listener) => listener(state2, previousState));
3948
3948
  }
3949
3949
  };
3950
- const getState = () => state;
3950
+ const getState = () => state2;
3951
3951
  const getInitialState2 = () => initialState;
3952
3952
  const subscribe = (listener) => {
3953
3953
  listeners.add(listener);
3954
3954
  return () => listeners.delete(listener);
3955
3955
  };
3956
3956
  const api = { setState, getState, getInitialState: getInitialState2, subscribe };
3957
- const initialState = state = createState(setState, getState, api);
3957
+ const initialState = state2 = createState(setState, getState, api);
3958
3958
  return api;
3959
3959
  };
3960
3960
  var createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
@@ -3988,33 +3988,33 @@ var useDndStore = create((set) => ({
3988
3988
  overlayRenderers: /* @__PURE__ */ new Map(),
3989
3989
  dragEndHandlers: /* @__PURE__ */ new Map(),
3990
3990
  setActive: (id, data, element) => set({ activeId: id, activeData: data, activeElement: element ?? null }),
3991
- registerContainer: (container) => set((state) => {
3992
- const next = new Map(state.containers);
3991
+ registerContainer: (container) => set((state2) => {
3992
+ const next = new Map(state2.containers);
3993
3993
  next.set(container.id, container);
3994
3994
  return { containers: next };
3995
3995
  }),
3996
- unregisterContainer: (id) => set((state) => {
3997
- const next = new Map(state.containers);
3996
+ unregisterContainer: (id) => set((state2) => {
3997
+ const next = new Map(state2.containers);
3998
3998
  next.delete(id);
3999
3999
  return { containers: next };
4000
4000
  }),
4001
- registerOverlayRenderer: (type, renderer) => set((state) => {
4002
- const next = new Map(state.overlayRenderers);
4001
+ registerOverlayRenderer: (type, renderer) => set((state2) => {
4002
+ const next = new Map(state2.overlayRenderers);
4003
4003
  next.set(type, renderer);
4004
4004
  return { overlayRenderers: next };
4005
4005
  }),
4006
- unregisterOverlayRenderer: (type) => set((state) => {
4007
- const next = new Map(state.overlayRenderers);
4006
+ unregisterOverlayRenderer: (type) => set((state2) => {
4007
+ const next = new Map(state2.overlayRenderers);
4008
4008
  next.delete(type);
4009
4009
  return { overlayRenderers: next };
4010
4010
  }),
4011
- registerDragEndHandler: (key2, handler) => set((state) => {
4012
- const next = new Map(state.dragEndHandlers);
4011
+ registerDragEndHandler: (key2, handler) => set((state2) => {
4012
+ const next = new Map(state2.dragEndHandlers);
4013
4013
  next.set(key2, handler);
4014
4014
  return { dragEndHandlers: next };
4015
4015
  }),
4016
- unregisterDragEndHandler: (key2) => set((state) => {
4017
- const next = new Map(state.dragEndHandlers);
4016
+ unregisterDragEndHandler: (key2) => set((state2) => {
4017
+ const next = new Map(state2.dragEndHandlers);
4018
4018
  next.delete(key2);
4019
4019
  return { dragEndHandlers: next };
4020
4020
  })
@@ -10271,7 +10271,7 @@ function isPreviewOrigin(origin) {
10271
10271
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
10272
10272
  var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
10273
10273
  function readBuildVersion() {
10274
- return "0.1.13" ? "0.1.13" : null;
10274
+ return "0.1.15" ? "0.1.15" : null;
10275
10275
  }
10276
10276
  var REMOTE_INSPECTOR_VERSION = readBuildVersion();
10277
10277
  var isRemoteInspectorUrlVersioned = REMOTE_INSPECTOR_VERSION !== null;
@@ -10351,6 +10351,163 @@ function injectInspector(opts = {}) {
10351
10351
  document.head.appendChild(script);
10352
10352
  }
10353
10353
 
10354
+ // src/client/route-listener/pageSettle.ts
10355
+ var QUIET_MS = 300;
10356
+ var POLL_MS = 50;
10357
+ var PAGE_QUIET_TIMEOUT_MS = 5e3;
10358
+ var MAX_TRACKED_REQUEST_AGE_MS = 4e3;
10359
+ var IGNORED_URL_RE = /^wss?:|\/@vite\/|\/@react-refresh|\/__vite|\.hot-update\.|\/_next\/webpack-hmr|\/sockjs-node|\/__webpack_hmr/;
10360
+ var LOADING_SELECTOR = [
10361
+ '[aria-busy="true"]',
10362
+ '[role="progressbar"]',
10363
+ '[class*="animate-spin"]',
10364
+ '[class*="animate-pulse"]',
10365
+ '[class*="skeleton" i]'
10366
+ ].join(",");
10367
+ var LOADING_TEXT_RE = /^(loading|cargando)(\.{3}|…)?$/i;
10368
+ var NON_RENDERED_TAGS = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "NOSCRIPT", "TEMPLATE"]);
10369
+ var state = {
10370
+ refs: 0,
10371
+ inflight: /* @__PURE__ */ new Map(),
10372
+ nextId: 0,
10373
+ lastActivityAt: 0
10374
+ };
10375
+ function urlOf(input) {
10376
+ if (typeof input === "string") return input;
10377
+ if (input instanceof URL) return input.href;
10378
+ return input?.url ?? "";
10379
+ }
10380
+ function begin(url) {
10381
+ if (IGNORED_URL_RE.test(url)) return void 0;
10382
+ const id = state.nextId++;
10383
+ state.inflight.set(id, Date.now());
10384
+ state.lastActivityAt = Date.now();
10385
+ return () => {
10386
+ if (state.inflight.delete(id)) state.lastActivityAt = Date.now();
10387
+ };
10388
+ }
10389
+ function patchNetwork() {
10390
+ const originalFetch = window.fetch;
10391
+ const xhrProto = window.XMLHttpRequest?.prototype;
10392
+ const originalOpen = xhrProto?.open;
10393
+ const originalSend = xhrProto?.send;
10394
+ const patchedFetch = function(...args) {
10395
+ const end = begin(urlOf(args[0]));
10396
+ let result;
10397
+ try {
10398
+ result = originalFetch.apply(this, args);
10399
+ } catch (error) {
10400
+ end?.();
10401
+ throw error;
10402
+ }
10403
+ if (end) result.then(end, end);
10404
+ return result;
10405
+ };
10406
+ if (typeof originalFetch === "function") window.fetch = patchedFetch;
10407
+ if (xhrProto && originalOpen && originalSend) {
10408
+ xhrProto.open = function(...args) {
10409
+ this.__archieUrl = urlOf(args[1]);
10410
+ return originalOpen.apply(this, args);
10411
+ };
10412
+ xhrProto.send = function(body) {
10413
+ const end = begin(this.__archieUrl ?? "");
10414
+ if (end) this.addEventListener("loadend", end, { once: true });
10415
+ try {
10416
+ return originalSend.call(this, body);
10417
+ } catch (error) {
10418
+ end?.();
10419
+ throw error;
10420
+ }
10421
+ };
10422
+ }
10423
+ return () => {
10424
+ if (window.fetch === patchedFetch) window.fetch = originalFetch;
10425
+ if (xhrProto && originalOpen && originalSend) {
10426
+ xhrProto.open = originalOpen;
10427
+ xhrProto.send = originalSend;
10428
+ }
10429
+ };
10430
+ }
10431
+ function installPageActivityTracker() {
10432
+ if (state.refs++ === 0) {
10433
+ state.lastActivityAt = Date.now();
10434
+ state.restore = patchNetwork();
10435
+ state.observer = new MutationObserver(() => {
10436
+ state.lastActivityAt = Date.now();
10437
+ });
10438
+ state.observer.observe(document, {
10439
+ subtree: true,
10440
+ childList: true,
10441
+ attributes: true,
10442
+ characterData: true
10443
+ });
10444
+ }
10445
+ let released = false;
10446
+ return () => {
10447
+ if (released) return;
10448
+ released = true;
10449
+ if (--state.refs === 0) {
10450
+ state.restore?.();
10451
+ state.observer?.disconnect();
10452
+ state.restore = void 0;
10453
+ state.observer = void 0;
10454
+ state.inflight.clear();
10455
+ }
10456
+ };
10457
+ }
10458
+ function isVisible(el) {
10459
+ if (el.closest('[hidden],[aria-hidden="true"]')) return false;
10460
+ try {
10461
+ const style = getComputedStyle(el);
10462
+ return style.display !== "none" && style.visibility !== "hidden";
10463
+ } catch {
10464
+ return true;
10465
+ }
10466
+ }
10467
+ function hasLoadingIndicator() {
10468
+ const root = document.body;
10469
+ if (!root) return false;
10470
+ for (const el of Array.from(root.querySelectorAll(LOADING_SELECTOR))) {
10471
+ if (isVisible(el)) return true;
10472
+ }
10473
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
10474
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
10475
+ const parent = node.parentElement;
10476
+ if (!parent || NON_RENDERED_TAGS.has(parent.tagName)) continue;
10477
+ if (LOADING_TEXT_RE.test((node.textContent ?? "").trim()) && isVisible(parent)) {
10478
+ return true;
10479
+ }
10480
+ }
10481
+ return false;
10482
+ }
10483
+ function hasPendingRequests(now) {
10484
+ for (const startedAt of state.inflight.values()) {
10485
+ if (now - startedAt < MAX_TRACKED_REQUEST_AGE_MS) return true;
10486
+ }
10487
+ return false;
10488
+ }
10489
+ function waitForPageQuiet(timeoutMs = PAGE_QUIET_TIMEOUT_MS) {
10490
+ return new Promise((resolve) => {
10491
+ const startedAt = Date.now();
10492
+ const untracked = state.refs === 0;
10493
+ const isQuiet = (now) => !hasPendingRequests(now) && now - (untracked ? startedAt : state.lastActivityAt) >= QUIET_MS && !hasLoadingIndicator();
10494
+ if (isQuiet(startedAt) && !untracked) {
10495
+ resolve(true);
10496
+ return;
10497
+ }
10498
+ const timer = setInterval(() => {
10499
+ const now = Date.now();
10500
+ if (isQuiet(now)) {
10501
+ clearInterval(timer);
10502
+ resolve(true);
10503
+ } else if (now - startedAt >= timeoutMs) {
10504
+ clearInterval(timer);
10505
+ resolve(false);
10506
+ }
10507
+ }, POLL_MS);
10508
+ });
10509
+ }
10510
+
10354
10511
  // src/client/route-listener/domSnapshot.ts
10355
10512
  var STRIP_SELECTOR = [
10356
10513
  "script",
@@ -10433,6 +10590,7 @@ async function captureDomSnapshot() {
10433
10590
  if (document.fonts?.ready) {
10434
10591
  await document.fonts.ready;
10435
10592
  }
10593
+ const settled = await waitForPageQuiet();
10436
10594
  await waitForIdle();
10437
10595
  const inlinedHrefs = /* @__PURE__ */ new Set();
10438
10596
  const css = await collectDocumentCss(inlinedHrefs);
@@ -10470,11 +10628,29 @@ async function captureDomSnapshot() {
10470
10628
  html: "<!doctype html>" + clone.outerHTML,
10471
10629
  path: location.pathname + location.search + location.hash,
10472
10630
  viewport: { width: window.innerWidth, height: window.innerHeight },
10473
- capturedAt: Date.now()
10631
+ capturedAt: Date.now(),
10632
+ settled
10474
10633
  };
10475
10634
  }
10476
10635
 
10477
10636
  // src/client/route-listener/routeListener.ts
10637
+ var ROUTE_FRAMES = ["phone", "tablet", "desktop"];
10638
+ function pickRouteHandle(h) {
10639
+ try {
10640
+ if (typeof h !== "object" || h === null) return void 0;
10641
+ const raw = h;
10642
+ const surface = raw.surface;
10643
+ const frame = raw.frame;
10644
+ const out = {};
10645
+ if (typeof surface === "string" && surface !== "") out.surface = surface;
10646
+ if (typeof frame === "string" && ROUTE_FRAMES.includes(frame)) {
10647
+ out.frame = frame;
10648
+ }
10649
+ return out.surface !== void 0 || out.frame !== void 0 ? out : void 0;
10650
+ } catch {
10651
+ return void 0;
10652
+ }
10653
+ }
10478
10654
  var GUARD_NAME_RE = /guard|protect|require|auth(?!or)/i;
10479
10655
  var GUARD_PRIVATE_RE = /\bprivate|Private(?=[A-Z]|$)/;
10480
10656
  var NON_GUARD_SUFFIX_RE = /(Layout|Provider|Context|Page|Form|Screen|View)$/;
@@ -10544,10 +10720,12 @@ function extractAllRoutes(routes, parentPath = "", parentId = null, inheritedGua
10544
10720
  const isLayout = !route.path && !route.index && hasChildren;
10545
10721
  const own = [];
10546
10722
  collectGuards(route.element, 0, own);
10723
+ const handle = pickRouteHandle(route.handle);
10547
10724
  if (route.path !== void 0 || route.index || isLayout) {
10548
10725
  result.push({
10549
10726
  id: route.id,
10550
10727
  path: route.index ? parentPath || "/" : path,
10728
+ ...handle ? { handle } : {},
10551
10729
  hasChildren,
10552
10730
  parentId,
10553
10731
  index: !!route.index,
@@ -10582,19 +10760,23 @@ function extractAllRoutes(routes, parentPath = "", parentId = null, inheritedGua
10582
10760
  }
10583
10761
  return result;
10584
10762
  }
10585
- function broadcastRouteState(target, state, routes) {
10763
+ function broadcastRouteState(target, state2, routes) {
10586
10764
  try {
10587
10765
  const message = {
10588
10766
  type: "ARCHIE_ROUTE_UPDATE",
10589
10767
  payload: {
10590
- path: state.location.pathname,
10591
- search: state.location.search,
10592
- hash: state.location.hash,
10593
- matches: state.matches.map((m) => ({
10594
- id: m.route.id,
10595
- pathname: m.pathname,
10596
- params: m.params
10597
- })),
10768
+ path: state2.location.pathname,
10769
+ search: state2.location.search,
10770
+ hash: state2.location.hash,
10771
+ matches: state2.matches.map((m) => {
10772
+ const handle = pickRouteHandle(m.route.handle);
10773
+ return {
10774
+ id: m.route.id,
10775
+ pathname: m.pathname,
10776
+ params: m.params,
10777
+ ...handle ? { handle } : {}
10778
+ };
10779
+ }),
10598
10780
  // Layouts stay out of allRoutes so consumers that predate them don't list duplicates.
10599
10781
  allRoutes: routes.filter((r) => !r.isLayout),
10600
10782
  layoutRoutes: routes.filter((r) => r.isLayout),
@@ -10609,7 +10791,7 @@ var SETTLE_QUIET_MS = 400;
10609
10791
  var SETTLE_TIMEOUT_MS = 8e3;
10610
10792
  var SETTLE_POLL_MS = 50;
10611
10793
  function readPackageVersion() {
10612
- return true ? "0.1.13" : "unknown";
10794
+ return true ? "0.1.15" : "unknown";
10613
10795
  }
10614
10796
  var DEFAULT_ARCHIE_COMMAND_ORIGINS = getAllowedOrigins(true);
10615
10797
  function originPatternToRegExp(pattern) {
@@ -10743,11 +10925,12 @@ function setupArchieRouteListener(router, options = {}) {
10743
10925
  return () => {
10744
10926
  };
10745
10927
  }
10928
+ const releaseTracker = installPageActivityTracker();
10746
10929
  broadcastRouteState(target, router.state, extractAllRoutes(router.routes));
10747
10930
  broadcastCapabilities(router, target);
10748
- const unsubscribe = router.subscribe((state) => {
10931
+ const unsubscribe = router.subscribe((state2) => {
10749
10932
  const currentRoutes = extractAllRoutes(router.routes);
10750
- broadcastRouteState(target, state, currentRoutes);
10933
+ broadcastRouteState(target, state2, currentRoutes);
10751
10934
  });
10752
10935
  const handleMessage = (event) => {
10753
10936
  if (event.source !== target) return;
@@ -10779,6 +10962,7 @@ function setupArchieRouteListener(router, options = {}) {
10779
10962
  return () => {
10780
10963
  unsubscribe();
10781
10964
  window.removeEventListener("message", handleMessage);
10965
+ releaseTracker();
10782
10966
  };
10783
10967
  }
10784
10968
 
@@ -2468,37 +2468,37 @@ function getInitialState() {
2468
2468
  }
2469
2469
  };
2470
2470
  }
2471
- function reducer(state, action) {
2471
+ function reducer(state2, action) {
2472
2472
  switch (action.type) {
2473
2473
  case Action.DragStart:
2474
2474
  return {
2475
- ...state,
2475
+ ...state2,
2476
2476
  draggable: {
2477
- ...state.draggable,
2477
+ ...state2.draggable,
2478
2478
  initialCoordinates: action.initialCoordinates,
2479
2479
  active: action.active
2480
2480
  }
2481
2481
  };
2482
2482
  case Action.DragMove:
2483
- if (state.draggable.active == null) {
2484
- return state;
2483
+ if (state2.draggable.active == null) {
2484
+ return state2;
2485
2485
  }
2486
2486
  return {
2487
- ...state,
2487
+ ...state2,
2488
2488
  draggable: {
2489
- ...state.draggable,
2489
+ ...state2.draggable,
2490
2490
  translate: {
2491
- x: action.coordinates.x - state.draggable.initialCoordinates.x,
2492
- y: action.coordinates.y - state.draggable.initialCoordinates.y
2491
+ x: action.coordinates.x - state2.draggable.initialCoordinates.x,
2492
+ y: action.coordinates.y - state2.draggable.initialCoordinates.y
2493
2493
  }
2494
2494
  }
2495
2495
  };
2496
2496
  case Action.DragEnd:
2497
2497
  case Action.DragCancel:
2498
2498
  return {
2499
- ...state,
2499
+ ...state2,
2500
2500
  draggable: {
2501
- ...state.draggable,
2501
+ ...state2.draggable,
2502
2502
  active: null,
2503
2503
  initialCoordinates: {
2504
2504
  x: 0,
@@ -2517,12 +2517,12 @@ function reducer(state, action) {
2517
2517
  const {
2518
2518
  id
2519
2519
  } = element;
2520
- const containers = new DroppableContainersMap(state.droppable.containers);
2520
+ const containers = new DroppableContainersMap(state2.droppable.containers);
2521
2521
  containers.set(id, element);
2522
2522
  return {
2523
- ...state,
2523
+ ...state2,
2524
2524
  droppable: {
2525
- ...state.droppable,
2525
+ ...state2.droppable,
2526
2526
  containers
2527
2527
  }
2528
2528
  };
@@ -2533,19 +2533,19 @@ function reducer(state, action) {
2533
2533
  key: key2,
2534
2534
  disabled
2535
2535
  } = action;
2536
- const element = state.droppable.containers.get(id);
2536
+ const element = state2.droppable.containers.get(id);
2537
2537
  if (!element || key2 !== element.key) {
2538
- return state;
2538
+ return state2;
2539
2539
  }
2540
- const containers = new DroppableContainersMap(state.droppable.containers);
2540
+ const containers = new DroppableContainersMap(state2.droppable.containers);
2541
2541
  containers.set(id, {
2542
2542
  ...element,
2543
2543
  disabled
2544
2544
  });
2545
2545
  return {
2546
- ...state,
2546
+ ...state2,
2547
2547
  droppable: {
2548
- ...state.droppable,
2548
+ ...state2.droppable,
2549
2549
  containers
2550
2550
  }
2551
2551
  };
@@ -2555,22 +2555,22 @@ function reducer(state, action) {
2555
2555
  id,
2556
2556
  key: key2
2557
2557
  } = action;
2558
- const element = state.droppable.containers.get(id);
2558
+ const element = state2.droppable.containers.get(id);
2559
2559
  if (!element || key2 !== element.key) {
2560
- return state;
2560
+ return state2;
2561
2561
  }
2562
- const containers = new DroppableContainersMap(state.droppable.containers);
2562
+ const containers = new DroppableContainersMap(state2.droppable.containers);
2563
2563
  containers.delete(id);
2564
2564
  return {
2565
- ...state,
2565
+ ...state2,
2566
2566
  droppable: {
2567
- ...state.droppable,
2567
+ ...state2.droppable,
2568
2568
  containers
2569
2569
  }
2570
2570
  };
2571
2571
  }
2572
2572
  default: {
2573
- return state;
2573
+ return state2;
2574
2574
  }
2575
2575
  }
2576
2576
  }
@@ -2728,7 +2728,7 @@ var DndContext = /* @__PURE__ */ memo(function DndContext2(_ref) {
2728
2728
  ...props
2729
2729
  } = _ref;
2730
2730
  const store = useReducer(reducer, void 0, getInitialState);
2731
- const [state, dispatch] = store;
2731
+ const [state2, dispatch] = store;
2732
2732
  const [dispatchMonitorEvent, registerMonitorListener] = useDndMonitorProvider();
2733
2733
  const [status, setStatus] = useState2(Status.Uninitialized);
2734
2734
  const isInitialized = status === Status.Initialized;
@@ -2741,7 +2741,7 @@ var DndContext = /* @__PURE__ */ memo(function DndContext2(_ref) {
2741
2741
  droppable: {
2742
2742
  containers: droppableContainers
2743
2743
  }
2744
- } = state;
2744
+ } = state2;
2745
2745
  const node = activeId != null ? draggableNodes.get(activeId) : null;
2746
2746
  const activeRects = useRef2({
2747
2747
  initial: null,
@@ -3902,24 +3902,24 @@ function isAfter(a, b) {
3902
3902
 
3903
3903
  // node_modules/zustand/esm/vanilla.mjs
3904
3904
  var createStoreImpl = (createState) => {
3905
- let state;
3905
+ let state2;
3906
3906
  const listeners = /* @__PURE__ */ new Set();
3907
3907
  const setState = (partial, replace) => {
3908
- const nextState = typeof partial === "function" ? partial(state) : partial;
3909
- if (!Object.is(nextState, state)) {
3910
- const previousState = state;
3911
- state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
3912
- listeners.forEach((listener) => listener(state, previousState));
3908
+ const nextState = typeof partial === "function" ? partial(state2) : partial;
3909
+ if (!Object.is(nextState, state2)) {
3910
+ const previousState = state2;
3911
+ state2 = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state2, nextState);
3912
+ listeners.forEach((listener) => listener(state2, previousState));
3913
3913
  }
3914
3914
  };
3915
- const getState = () => state;
3915
+ const getState = () => state2;
3916
3916
  const getInitialState2 = () => initialState;
3917
3917
  const subscribe = (listener) => {
3918
3918
  listeners.add(listener);
3919
3919
  return () => listeners.delete(listener);
3920
3920
  };
3921
3921
  const api = { setState, getState, getInitialState: getInitialState2, subscribe };
3922
- const initialState = state = createState(setState, getState, api);
3922
+ const initialState = state2 = createState(setState, getState, api);
3923
3923
  return api;
3924
3924
  };
3925
3925
  var createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
@@ -3953,33 +3953,33 @@ var useDndStore = create((set) => ({
3953
3953
  overlayRenderers: /* @__PURE__ */ new Map(),
3954
3954
  dragEndHandlers: /* @__PURE__ */ new Map(),
3955
3955
  setActive: (id, data, element) => set({ activeId: id, activeData: data, activeElement: element ?? null }),
3956
- registerContainer: (container) => set((state) => {
3957
- const next = new Map(state.containers);
3956
+ registerContainer: (container) => set((state2) => {
3957
+ const next = new Map(state2.containers);
3958
3958
  next.set(container.id, container);
3959
3959
  return { containers: next };
3960
3960
  }),
3961
- unregisterContainer: (id) => set((state) => {
3962
- const next = new Map(state.containers);
3961
+ unregisterContainer: (id) => set((state2) => {
3962
+ const next = new Map(state2.containers);
3963
3963
  next.delete(id);
3964
3964
  return { containers: next };
3965
3965
  }),
3966
- registerOverlayRenderer: (type, renderer) => set((state) => {
3967
- const next = new Map(state.overlayRenderers);
3966
+ registerOverlayRenderer: (type, renderer) => set((state2) => {
3967
+ const next = new Map(state2.overlayRenderers);
3968
3968
  next.set(type, renderer);
3969
3969
  return { overlayRenderers: next };
3970
3970
  }),
3971
- unregisterOverlayRenderer: (type) => set((state) => {
3972
- const next = new Map(state.overlayRenderers);
3971
+ unregisterOverlayRenderer: (type) => set((state2) => {
3972
+ const next = new Map(state2.overlayRenderers);
3973
3973
  next.delete(type);
3974
3974
  return { overlayRenderers: next };
3975
3975
  }),
3976
- registerDragEndHandler: (key2, handler) => set((state) => {
3977
- const next = new Map(state.dragEndHandlers);
3976
+ registerDragEndHandler: (key2, handler) => set((state2) => {
3977
+ const next = new Map(state2.dragEndHandlers);
3978
3978
  next.set(key2, handler);
3979
3979
  return { dragEndHandlers: next };
3980
3980
  }),
3981
- unregisterDragEndHandler: (key2) => set((state) => {
3982
- const next = new Map(state.dragEndHandlers);
3981
+ unregisterDragEndHandler: (key2) => set((state2) => {
3982
+ const next = new Map(state2.dragEndHandlers);
3983
3983
  next.delete(key2);
3984
3984
  return { dragEndHandlers: next };
3985
3985
  })
@@ -10236,7 +10236,7 @@ function isPreviewOrigin(origin) {
10236
10236
  var DEFAULT_SCRIPT_ID = "archie-inspector-script";
10237
10237
  var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
10238
10238
  function readBuildVersion() {
10239
- return "0.1.13" ? "0.1.13" : null;
10239
+ return "0.1.15" ? "0.1.15" : null;
10240
10240
  }
10241
10241
  var REMOTE_INSPECTOR_VERSION = readBuildVersion();
10242
10242
  var isRemoteInspectorUrlVersioned = REMOTE_INSPECTOR_VERSION !== null;
@@ -10316,6 +10316,163 @@ function injectInspector(opts = {}) {
10316
10316
  document.head.appendChild(script);
10317
10317
  }
10318
10318
 
10319
+ // src/client/route-listener/pageSettle.ts
10320
+ var QUIET_MS = 300;
10321
+ var POLL_MS = 50;
10322
+ var PAGE_QUIET_TIMEOUT_MS = 5e3;
10323
+ var MAX_TRACKED_REQUEST_AGE_MS = 4e3;
10324
+ var IGNORED_URL_RE = /^wss?:|\/@vite\/|\/@react-refresh|\/__vite|\.hot-update\.|\/_next\/webpack-hmr|\/sockjs-node|\/__webpack_hmr/;
10325
+ var LOADING_SELECTOR = [
10326
+ '[aria-busy="true"]',
10327
+ '[role="progressbar"]',
10328
+ '[class*="animate-spin"]',
10329
+ '[class*="animate-pulse"]',
10330
+ '[class*="skeleton" i]'
10331
+ ].join(",");
10332
+ var LOADING_TEXT_RE = /^(loading|cargando)(\.{3}|…)?$/i;
10333
+ var NON_RENDERED_TAGS = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "NOSCRIPT", "TEMPLATE"]);
10334
+ var state = {
10335
+ refs: 0,
10336
+ inflight: /* @__PURE__ */ new Map(),
10337
+ nextId: 0,
10338
+ lastActivityAt: 0
10339
+ };
10340
+ function urlOf(input) {
10341
+ if (typeof input === "string") return input;
10342
+ if (input instanceof URL) return input.href;
10343
+ return input?.url ?? "";
10344
+ }
10345
+ function begin(url) {
10346
+ if (IGNORED_URL_RE.test(url)) return void 0;
10347
+ const id = state.nextId++;
10348
+ state.inflight.set(id, Date.now());
10349
+ state.lastActivityAt = Date.now();
10350
+ return () => {
10351
+ if (state.inflight.delete(id)) state.lastActivityAt = Date.now();
10352
+ };
10353
+ }
10354
+ function patchNetwork() {
10355
+ const originalFetch = window.fetch;
10356
+ const xhrProto = window.XMLHttpRequest?.prototype;
10357
+ const originalOpen = xhrProto?.open;
10358
+ const originalSend = xhrProto?.send;
10359
+ const patchedFetch = function(...args) {
10360
+ const end = begin(urlOf(args[0]));
10361
+ let result;
10362
+ try {
10363
+ result = originalFetch.apply(this, args);
10364
+ } catch (error) {
10365
+ end?.();
10366
+ throw error;
10367
+ }
10368
+ if (end) result.then(end, end);
10369
+ return result;
10370
+ };
10371
+ if (typeof originalFetch === "function") window.fetch = patchedFetch;
10372
+ if (xhrProto && originalOpen && originalSend) {
10373
+ xhrProto.open = function(...args) {
10374
+ this.__archieUrl = urlOf(args[1]);
10375
+ return originalOpen.apply(this, args);
10376
+ };
10377
+ xhrProto.send = function(body) {
10378
+ const end = begin(this.__archieUrl ?? "");
10379
+ if (end) this.addEventListener("loadend", end, { once: true });
10380
+ try {
10381
+ return originalSend.call(this, body);
10382
+ } catch (error) {
10383
+ end?.();
10384
+ throw error;
10385
+ }
10386
+ };
10387
+ }
10388
+ return () => {
10389
+ if (window.fetch === patchedFetch) window.fetch = originalFetch;
10390
+ if (xhrProto && originalOpen && originalSend) {
10391
+ xhrProto.open = originalOpen;
10392
+ xhrProto.send = originalSend;
10393
+ }
10394
+ };
10395
+ }
10396
+ function installPageActivityTracker() {
10397
+ if (state.refs++ === 0) {
10398
+ state.lastActivityAt = Date.now();
10399
+ state.restore = patchNetwork();
10400
+ state.observer = new MutationObserver(() => {
10401
+ state.lastActivityAt = Date.now();
10402
+ });
10403
+ state.observer.observe(document, {
10404
+ subtree: true,
10405
+ childList: true,
10406
+ attributes: true,
10407
+ characterData: true
10408
+ });
10409
+ }
10410
+ let released = false;
10411
+ return () => {
10412
+ if (released) return;
10413
+ released = true;
10414
+ if (--state.refs === 0) {
10415
+ state.restore?.();
10416
+ state.observer?.disconnect();
10417
+ state.restore = void 0;
10418
+ state.observer = void 0;
10419
+ state.inflight.clear();
10420
+ }
10421
+ };
10422
+ }
10423
+ function isVisible(el) {
10424
+ if (el.closest('[hidden],[aria-hidden="true"]')) return false;
10425
+ try {
10426
+ const style = getComputedStyle(el);
10427
+ return style.display !== "none" && style.visibility !== "hidden";
10428
+ } catch {
10429
+ return true;
10430
+ }
10431
+ }
10432
+ function hasLoadingIndicator() {
10433
+ const root = document.body;
10434
+ if (!root) return false;
10435
+ for (const el of Array.from(root.querySelectorAll(LOADING_SELECTOR))) {
10436
+ if (isVisible(el)) return true;
10437
+ }
10438
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
10439
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
10440
+ const parent = node.parentElement;
10441
+ if (!parent || NON_RENDERED_TAGS.has(parent.tagName)) continue;
10442
+ if (LOADING_TEXT_RE.test((node.textContent ?? "").trim()) && isVisible(parent)) {
10443
+ return true;
10444
+ }
10445
+ }
10446
+ return false;
10447
+ }
10448
+ function hasPendingRequests(now) {
10449
+ for (const startedAt of state.inflight.values()) {
10450
+ if (now - startedAt < MAX_TRACKED_REQUEST_AGE_MS) return true;
10451
+ }
10452
+ return false;
10453
+ }
10454
+ function waitForPageQuiet(timeoutMs = PAGE_QUIET_TIMEOUT_MS) {
10455
+ return new Promise((resolve) => {
10456
+ const startedAt = Date.now();
10457
+ const untracked = state.refs === 0;
10458
+ const isQuiet = (now) => !hasPendingRequests(now) && now - (untracked ? startedAt : state.lastActivityAt) >= QUIET_MS && !hasLoadingIndicator();
10459
+ if (isQuiet(startedAt) && !untracked) {
10460
+ resolve(true);
10461
+ return;
10462
+ }
10463
+ const timer = setInterval(() => {
10464
+ const now = Date.now();
10465
+ if (isQuiet(now)) {
10466
+ clearInterval(timer);
10467
+ resolve(true);
10468
+ } else if (now - startedAt >= timeoutMs) {
10469
+ clearInterval(timer);
10470
+ resolve(false);
10471
+ }
10472
+ }, POLL_MS);
10473
+ });
10474
+ }
10475
+
10319
10476
  // src/client/route-listener/domSnapshot.ts
10320
10477
  var STRIP_SELECTOR = [
10321
10478
  "script",
@@ -10398,6 +10555,7 @@ async function captureDomSnapshot() {
10398
10555
  if (document.fonts?.ready) {
10399
10556
  await document.fonts.ready;
10400
10557
  }
10558
+ const settled = await waitForPageQuiet();
10401
10559
  await waitForIdle();
10402
10560
  const inlinedHrefs = /* @__PURE__ */ new Set();
10403
10561
  const css = await collectDocumentCss(inlinedHrefs);
@@ -10435,11 +10593,29 @@ async function captureDomSnapshot() {
10435
10593
  html: "<!doctype html>" + clone.outerHTML,
10436
10594
  path: location.pathname + location.search + location.hash,
10437
10595
  viewport: { width: window.innerWidth, height: window.innerHeight },
10438
- capturedAt: Date.now()
10596
+ capturedAt: Date.now(),
10597
+ settled
10439
10598
  };
10440
10599
  }
10441
10600
 
10442
10601
  // src/client/route-listener/routeListener.ts
10602
+ var ROUTE_FRAMES = ["phone", "tablet", "desktop"];
10603
+ function pickRouteHandle(h) {
10604
+ try {
10605
+ if (typeof h !== "object" || h === null) return void 0;
10606
+ const raw = h;
10607
+ const surface = raw.surface;
10608
+ const frame = raw.frame;
10609
+ const out = {};
10610
+ if (typeof surface === "string" && surface !== "") out.surface = surface;
10611
+ if (typeof frame === "string" && ROUTE_FRAMES.includes(frame)) {
10612
+ out.frame = frame;
10613
+ }
10614
+ return out.surface !== void 0 || out.frame !== void 0 ? out : void 0;
10615
+ } catch {
10616
+ return void 0;
10617
+ }
10618
+ }
10443
10619
  var GUARD_NAME_RE = /guard|protect|require|auth(?!or)/i;
10444
10620
  var GUARD_PRIVATE_RE = /\bprivate|Private(?=[A-Z]|$)/;
10445
10621
  var NON_GUARD_SUFFIX_RE = /(Layout|Provider|Context|Page|Form|Screen|View)$/;
@@ -10509,10 +10685,12 @@ function extractAllRoutes(routes, parentPath = "", parentId = null, inheritedGua
10509
10685
  const isLayout = !route.path && !route.index && hasChildren;
10510
10686
  const own = [];
10511
10687
  collectGuards(route.element, 0, own);
10688
+ const handle = pickRouteHandle(route.handle);
10512
10689
  if (route.path !== void 0 || route.index || isLayout) {
10513
10690
  result.push({
10514
10691
  id: route.id,
10515
10692
  path: route.index ? parentPath || "/" : path,
10693
+ ...handle ? { handle } : {},
10516
10694
  hasChildren,
10517
10695
  parentId,
10518
10696
  index: !!route.index,
@@ -10547,19 +10725,23 @@ function extractAllRoutes(routes, parentPath = "", parentId = null, inheritedGua
10547
10725
  }
10548
10726
  return result;
10549
10727
  }
10550
- function broadcastRouteState(target, state, routes) {
10728
+ function broadcastRouteState(target, state2, routes) {
10551
10729
  try {
10552
10730
  const message = {
10553
10731
  type: "ARCHIE_ROUTE_UPDATE",
10554
10732
  payload: {
10555
- path: state.location.pathname,
10556
- search: state.location.search,
10557
- hash: state.location.hash,
10558
- matches: state.matches.map((m) => ({
10559
- id: m.route.id,
10560
- pathname: m.pathname,
10561
- params: m.params
10562
- })),
10733
+ path: state2.location.pathname,
10734
+ search: state2.location.search,
10735
+ hash: state2.location.hash,
10736
+ matches: state2.matches.map((m) => {
10737
+ const handle = pickRouteHandle(m.route.handle);
10738
+ return {
10739
+ id: m.route.id,
10740
+ pathname: m.pathname,
10741
+ params: m.params,
10742
+ ...handle ? { handle } : {}
10743
+ };
10744
+ }),
10563
10745
  // Layouts stay out of allRoutes so consumers that predate them don't list duplicates.
10564
10746
  allRoutes: routes.filter((r) => !r.isLayout),
10565
10747
  layoutRoutes: routes.filter((r) => r.isLayout),
@@ -10574,7 +10756,7 @@ var SETTLE_QUIET_MS = 400;
10574
10756
  var SETTLE_TIMEOUT_MS = 8e3;
10575
10757
  var SETTLE_POLL_MS = 50;
10576
10758
  function readPackageVersion() {
10577
- return true ? "0.1.13" : "unknown";
10759
+ return true ? "0.1.15" : "unknown";
10578
10760
  }
10579
10761
  var DEFAULT_ARCHIE_COMMAND_ORIGINS = getAllowedOrigins(true);
10580
10762
  function originPatternToRegExp(pattern) {
@@ -10708,11 +10890,12 @@ function setupArchieRouteListener(router, options = {}) {
10708
10890
  return () => {
10709
10891
  };
10710
10892
  }
10893
+ const releaseTracker = installPageActivityTracker();
10711
10894
  broadcastRouteState(target, router.state, extractAllRoutes(router.routes));
10712
10895
  broadcastCapabilities(router, target);
10713
- const unsubscribe = router.subscribe((state) => {
10896
+ const unsubscribe = router.subscribe((state2) => {
10714
10897
  const currentRoutes = extractAllRoutes(router.routes);
10715
- broadcastRouteState(target, state, currentRoutes);
10898
+ broadcastRouteState(target, state2, currentRoutes);
10716
10899
  });
10717
10900
  const handleMessage = (event) => {
10718
10901
  if (event.source !== target) return;
@@ -10744,6 +10927,7 @@ function setupArchieRouteListener(router, options = {}) {
10744
10927
  return () => {
10745
10928
  unsubscribe();
10746
10929
  window.removeEventListener("message", handleMessage);
10930
+ releaseTracker();
10747
10931
  };
10748
10932
  }
10749
10933
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archie/devtools",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "DevTools for Archie generated applications - Route synchronization and editor communication",
5
5
  "sideEffects": false,
6
6
  "repository": {