turbo-rails 2.0.20 → 2.0.22

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.
@@ -1,68 +1,7 @@
1
1
  /*!
2
- Turbo 8.0.19
3
- Copyright © 2025 37signals LLC
2
+ Turbo 8.0.21
3
+ Copyright © 2026 37signals LLC
4
4
  */
5
- (function(prototype) {
6
- if (typeof prototype.requestSubmit == "function") return;
7
- prototype.requestSubmit = function(submitter) {
8
- if (submitter) {
9
- validateSubmitter(submitter, this);
10
- submitter.click();
11
- } else {
12
- submitter = document.createElement("input");
13
- submitter.type = "submit";
14
- submitter.hidden = true;
15
- this.appendChild(submitter);
16
- submitter.click();
17
- this.removeChild(submitter);
18
- }
19
- };
20
- function validateSubmitter(submitter, form) {
21
- submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
22
- submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
23
- submitter.form == form || raise(DOMException, "The specified element is not owned by this form element", "NotFoundError");
24
- }
25
- function raise(errorConstructor, message, name) {
26
- throw new errorConstructor("Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".", name);
27
- }
28
- })(HTMLFormElement.prototype);
29
-
30
- const submittersByForm = new WeakMap;
31
-
32
- function findSubmitterFromClickTarget(target) {
33
- const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
34
- const candidate = element ? element.closest("input, button") : null;
35
- return candidate?.type == "submit" ? candidate : null;
36
- }
37
-
38
- function clickCaptured(event) {
39
- const submitter = findSubmitterFromClickTarget(event.target);
40
- if (submitter && submitter.form) {
41
- submittersByForm.set(submitter.form, submitter);
42
- }
43
- }
44
-
45
- (function() {
46
- if ("submitter" in Event.prototype) return;
47
- let prototype = window.Event.prototype;
48
- if ("SubmitEvent" in window) {
49
- const prototypeOfSubmitEvent = window.SubmitEvent.prototype;
50
- if (/Apple Computer/.test(navigator.vendor) && !("submitter" in prototypeOfSubmitEvent)) {
51
- prototype = prototypeOfSubmitEvent;
52
- } else {
53
- return;
54
- }
55
- }
56
- addEventListener("click", clickCaptured, true);
57
- Object.defineProperty(prototype, "submitter", {
58
- get() {
59
- if (this.type == "submit" && this.target instanceof HTMLFormElement) {
60
- return submittersByForm.get(this.target);
61
- }
62
- }
63
- });
64
- })();
65
-
66
5
  const FrameLoadingStyle = {
67
6
  eager: "eager",
68
7
  lazy: "lazy"
@@ -240,10 +179,6 @@ function nextEventLoopTick() {
240
179
  return new Promise((resolve => setTimeout((() => resolve()), 0)));
241
180
  }
242
181
 
243
- function nextMicrotask() {
244
- return Promise.resolve();
245
- }
246
-
247
182
  function parseHTMLDocument(html = "") {
248
183
  return (new DOMParser).parseFromString(html, "text/html");
249
184
  }
@@ -273,7 +208,7 @@ function uuid() {
273
208
  } else if (i == 19) {
274
209
  return (Math.floor(Math.random() * 4) + 8).toString(16);
275
210
  } else {
276
- return Math.floor(Math.random() * 15).toString(16);
211
+ return Math.floor(Math.random() * 16).toString(16);
277
212
  }
278
213
  })).join("");
279
214
  }
@@ -411,15 +346,13 @@ function doesNotTargetIFrame(name) {
411
346
  function findLinkFromClickTarget(target) {
412
347
  const link = findClosestRecursively(target, "a[href], a[xlink\\:href]");
413
348
  if (!link) return null;
349
+ if (link.href.startsWith("#")) return null;
414
350
  if (link.hasAttribute("download")) return null;
415
- if (link.hasAttribute("target") && link.target !== "_self") return null;
351
+ const linkTarget = link.getAttribute("target");
352
+ if (linkTarget && linkTarget !== "_self") return null;
416
353
  return link;
417
354
  }
418
355
 
419
- function getLocationForLink(link) {
420
- return expandURL(link.getAttribute("href") || "");
421
- }
422
-
423
356
  function debounce(fn, delay) {
424
357
  let timeoutId = null;
425
358
  return (...args) => {
@@ -500,6 +433,10 @@ function locationIsVisitable(location, rootLocation) {
500
433
  return isPrefixedBy(location, rootLocation) && !config.drive.unvisitableExtensions.has(getExtension(location));
501
434
  }
502
435
 
436
+ function getLocationForLink(link) {
437
+ return expandURL(link.getAttribute("href") || "");
438
+ }
439
+
503
440
  function getRequestURL(url) {
504
441
  const anchor = getAnchor(url);
505
442
  return anchor != null ? url.href.slice(0, -(anchor.length + 1)) : url.href;
@@ -871,34 +808,94 @@ function importStreamElements(fragment) {
871
808
  return fragment;
872
809
  }
873
810
 
811
+ const identity = key => key;
812
+
813
+ class LRUCache {
814
+ keys=[];
815
+ entries={};
816
+ #toCacheKey;
817
+ constructor(size, toCacheKey = identity) {
818
+ this.size = size;
819
+ this.#toCacheKey = toCacheKey;
820
+ }
821
+ has(key) {
822
+ return this.#toCacheKey(key) in this.entries;
823
+ }
824
+ get(key) {
825
+ if (this.has(key)) {
826
+ const entry = this.read(key);
827
+ this.touch(key);
828
+ return entry;
829
+ }
830
+ }
831
+ put(key, entry) {
832
+ this.write(key, entry);
833
+ this.touch(key);
834
+ return entry;
835
+ }
836
+ clear() {
837
+ for (const key of Object.keys(this.entries)) {
838
+ this.evict(key);
839
+ }
840
+ }
841
+ read(key) {
842
+ return this.entries[this.#toCacheKey(key)];
843
+ }
844
+ write(key, entry) {
845
+ this.entries[this.#toCacheKey(key)] = entry;
846
+ }
847
+ touch(key) {
848
+ key = this.#toCacheKey(key);
849
+ const index = this.keys.indexOf(key);
850
+ if (index > -1) this.keys.splice(index, 1);
851
+ this.keys.unshift(key);
852
+ this.trim();
853
+ }
854
+ trim() {
855
+ for (const key of this.keys.splice(this.size)) {
856
+ this.evict(key);
857
+ }
858
+ }
859
+ evict(key) {
860
+ delete this.entries[key];
861
+ }
862
+ }
863
+
874
864
  const PREFETCH_DELAY = 100;
875
865
 
876
- class PrefetchCache {
866
+ class PrefetchCache extends LRUCache {
877
867
  #prefetchTimeout=null;
878
- #prefetched=null;
879
- get(url) {
880
- if (this.#prefetched && this.#prefetched.url === url && this.#prefetched.expire > Date.now()) {
881
- return this.#prefetched.request;
882
- }
868
+ #maxAges={};
869
+ constructor(size = 1, prefetchDelay = PREFETCH_DELAY) {
870
+ super(size, toCacheKey);
871
+ this.prefetchDelay = prefetchDelay;
883
872
  }
884
- setLater(url, request, ttl) {
885
- this.clear();
873
+ putLater(url, request, ttl) {
886
874
  this.#prefetchTimeout = setTimeout((() => {
887
875
  request.perform();
888
- this.set(url, request, ttl);
876
+ this.put(url, request, ttl);
889
877
  this.#prefetchTimeout = null;
890
- }), PREFETCH_DELAY);
878
+ }), this.prefetchDelay);
891
879
  }
892
- set(url, request, ttl) {
893
- this.#prefetched = {
894
- url: url,
895
- request: request,
896
- expire: new Date((new Date).getTime() + ttl)
897
- };
880
+ put(url, request, ttl = cacheTtl) {
881
+ super.put(url, request);
882
+ this.#maxAges[toCacheKey(url)] = new Date((new Date).getTime() + ttl);
898
883
  }
899
884
  clear() {
885
+ super.clear();
900
886
  if (this.#prefetchTimeout) clearTimeout(this.#prefetchTimeout);
901
- this.#prefetched = null;
887
+ }
888
+ evict(key) {
889
+ super.evict(key);
890
+ delete this.#maxAges[key];
891
+ }
892
+ has(key) {
893
+ if (super.has(key)) {
894
+ const maxAge = this.#maxAges[toCacheKey(key)];
895
+ return maxAge && maxAge > Date.now();
896
+ } else {
897
+ return false;
898
+ }
902
899
  }
903
900
  }
904
901
 
@@ -1723,8 +1720,12 @@ var Idiomorph = function() {
1723
1720
  }
1724
1721
  function morphOuterHTML(ctx, oldNode, newNode) {
1725
1722
  const oldParent = normalizeParent(oldNode);
1723
+ let childNodes = Array.from(oldParent.childNodes);
1724
+ const index = childNodes.indexOf(oldNode);
1725
+ const rightMargin = childNodes.length - (index + 1);
1726
1726
  morphChildren(ctx, oldParent, newNode, oldNode, oldNode.nextSibling);
1727
- return Array.from(oldParent.childNodes);
1727
+ childNodes = Array.from(oldParent.childNodes);
1728
+ return childNodes.slice(index, childNodes.length - rightMargin);
1728
1729
  }
1729
1730
  function saveAndRestoreFocus(ctx, fn) {
1730
1731
  if (!ctx.config.restoreFocus) return fn();
@@ -1734,8 +1735,8 @@ var Idiomorph = function() {
1734
1735
  }
1735
1736
  const {id: activeElementId, selectionStart: selectionStart, selectionEnd: selectionEnd} = activeElement;
1736
1737
  const results = fn();
1737
- if (activeElementId && activeElementId !== document.activeElement?.getAttribute("id")) {
1738
- activeElement = ctx.target.querySelector(`[id="${activeElementId}"]`);
1738
+ if (activeElementId && activeElementId !== document.activeElement?.id) {
1739
+ activeElement = ctx.target.querySelector(`#${activeElementId}`);
1739
1740
  activeElement?.focus();
1740
1741
  }
1741
1742
  if (activeElement && !activeElement.selectionEnd && selectionEnd) {
@@ -1762,14 +1763,11 @@ var Idiomorph = function() {
1762
1763
  continue;
1763
1764
  }
1764
1765
  }
1765
- if (newChild instanceof Element) {
1766
- const newChildId = newChild.getAttribute("id");
1767
- if (ctx.persistentIds.has(newChildId)) {
1768
- const movedChild = moveBeforeById(oldParent, newChildId, insertionPoint, ctx);
1769
- morphNode(movedChild, newChild, ctx);
1770
- insertionPoint = movedChild.nextSibling;
1771
- continue;
1772
- }
1766
+ if (newChild instanceof Element && ctx.persistentIds.has(newChild.id)) {
1767
+ const movedChild = moveBeforeById(oldParent, newChild.id, insertionPoint, ctx);
1768
+ morphNode(movedChild, newChild, ctx);
1769
+ insertionPoint = movedChild.nextSibling;
1770
+ continue;
1773
1771
  }
1774
1772
  const insertedNode = createNode(oldParent, newChild, insertionPoint, ctx);
1775
1773
  if (insertedNode) {
@@ -1821,7 +1819,7 @@ var Idiomorph = function() {
1821
1819
  softMatch = undefined;
1822
1820
  }
1823
1821
  }
1824
- if (ctx.activeElementAndParents.includes(cursor)) break;
1822
+ if (cursor.contains(document.activeElement)) break;
1825
1823
  cursor = cursor.nextSibling;
1826
1824
  }
1827
1825
  return softMatch || null;
@@ -1840,7 +1838,7 @@ var Idiomorph = function() {
1840
1838
  function isSoftMatch(oldNode, newNode) {
1841
1839
  const oldElt = oldNode;
1842
1840
  const newElt = newNode;
1843
- return oldElt.nodeType === newElt.nodeType && oldElt.tagName === newElt.tagName && (!oldElt.getAttribute?.("id") || oldElt.getAttribute?.("id") === newElt.getAttribute?.("id"));
1841
+ return oldElt.nodeType === newElt.nodeType && oldElt.tagName === newElt.tagName && (!oldElt.id || oldElt.id === newElt.id);
1844
1842
  }
1845
1843
  return findBestMatch;
1846
1844
  }();
@@ -1863,13 +1861,13 @@ var Idiomorph = function() {
1863
1861
  return cursor;
1864
1862
  }
1865
1863
  function moveBeforeById(parentNode, id, after, ctx) {
1866
- const target = ctx.target.getAttribute?.("id") === id && ctx.target || ctx.target.querySelector(`[id="${id}"]`) || ctx.pantry.querySelector(`[id="${id}"]`);
1864
+ const target = ctx.target.querySelector(`#${id}`) || ctx.pantry.querySelector(`#${id}`);
1867
1865
  removeElementFromAncestorsIdMaps(target, ctx);
1868
1866
  moveBefore(parentNode, target, after);
1869
1867
  return target;
1870
1868
  }
1871
1869
  function removeElementFromAncestorsIdMaps(element, ctx) {
1872
- const id = element.getAttribute("id");
1870
+ const id = element.id;
1873
1871
  while (element = element.parentNode) {
1874
1872
  let idSet = ctx.idMap.get(element);
1875
1873
  if (idSet) {
@@ -2113,7 +2111,6 @@ var Idiomorph = function() {
2113
2111
  idMap: idMap,
2114
2112
  persistentIds: persistentIds,
2115
2113
  pantry: createPantry(),
2116
- activeElementAndParents: createActiveElementAndParents(oldNode),
2117
2114
  callbacks: mergedConfig.callbacks,
2118
2115
  head: mergedConfig.head
2119
2116
  };
@@ -2131,29 +2128,16 @@ var Idiomorph = function() {
2131
2128
  document.body.insertAdjacentElement("afterend", pantry);
2132
2129
  return pantry;
2133
2130
  }
2134
- function createActiveElementAndParents(oldNode) {
2135
- let activeElementAndParents = [];
2136
- let elt = document.activeElement;
2137
- if (elt?.tagName !== "BODY" && oldNode.contains(elt)) {
2138
- while (elt) {
2139
- activeElementAndParents.push(elt);
2140
- if (elt === oldNode) break;
2141
- elt = elt.parentElement;
2142
- }
2143
- }
2144
- return activeElementAndParents;
2145
- }
2146
2131
  function findIdElements(root) {
2147
2132
  let elements = Array.from(root.querySelectorAll("[id]"));
2148
- if (root.getAttribute?.("id")) {
2133
+ if (root.id) {
2149
2134
  elements.push(root);
2150
2135
  }
2151
2136
  return elements;
2152
2137
  }
2153
2138
  function populateIdMapWithTree(idMap, persistentIds, root, elements) {
2154
2139
  for (const elt of elements) {
2155
- const id = elt.getAttribute("id");
2156
- if (persistentIds.has(id)) {
2140
+ if (persistentIds.has(elt.id)) {
2157
2141
  let current = elt;
2158
2142
  while (current) {
2159
2143
  let idSet = idMap.get(current);
@@ -2161,7 +2145,7 @@ var Idiomorph = function() {
2161
2145
  idSet = new Set;
2162
2146
  idMap.set(current, idSet);
2163
2147
  }
2164
- idSet.add(id);
2148
+ idSet.add(elt.id);
2165
2149
  if (current === root) break;
2166
2150
  current = current.parentElement;
2167
2151
  }
@@ -2224,7 +2208,7 @@ var Idiomorph = function() {
2224
2208
  return newContent;
2225
2209
  } else if (newContent instanceof Node) {
2226
2210
  if (newContent.parentNode) {
2227
- return new SlicedParentNode(newContent);
2211
+ return createDuckTypedParent(newContent);
2228
2212
  } else {
2229
2213
  const dummyParent = document.createElement("div");
2230
2214
  dummyParent.append(newContent);
@@ -2238,43 +2222,19 @@ var Idiomorph = function() {
2238
2222
  return dummyParent;
2239
2223
  }
2240
2224
  }
2241
- class SlicedParentNode {
2242
- constructor(node) {
2243
- this.originalNode = node;
2244
- this.realParentNode = node.parentNode;
2245
- this.previousSibling = node.previousSibling;
2246
- this.nextSibling = node.nextSibling;
2247
- }
2248
- get childNodes() {
2249
- const nodes = [];
2250
- let cursor = this.previousSibling ? this.previousSibling.nextSibling : this.realParentNode.firstChild;
2251
- while (cursor && cursor != this.nextSibling) {
2252
- nodes.push(cursor);
2253
- cursor = cursor.nextSibling;
2225
+ function createDuckTypedParent(newContent) {
2226
+ return {
2227
+ childNodes: [ newContent ],
2228
+ querySelectorAll: s => {
2229
+ const elements = newContent.querySelectorAll(s);
2230
+ return newContent.matches(s) ? [ newContent, ...elements ] : elements;
2231
+ },
2232
+ insertBefore: (n, r) => newContent.parentNode.insertBefore(n, r),
2233
+ moveBefore: (n, r) => newContent.parentNode.moveBefore(n, r),
2234
+ get __idiomorphRoot() {
2235
+ return newContent;
2254
2236
  }
2255
- return nodes;
2256
- }
2257
- querySelectorAll(selector) {
2258
- return this.childNodes.reduce(((results, node) => {
2259
- if (node instanceof Element) {
2260
- if (node.matches(selector)) results.push(node);
2261
- const nodeList = node.querySelectorAll(selector);
2262
- for (let i = 0; i < nodeList.length; i++) {
2263
- results.push(nodeList[i]);
2264
- }
2265
- }
2266
- return results;
2267
- }), []);
2268
- }
2269
- insertBefore(node, referenceNode) {
2270
- return this.realParentNode.insertBefore(node, referenceNode);
2271
- }
2272
- moveBefore(node, referenceNode) {
2273
- return this.realParentNode.moveBefore(node, referenceNode);
2274
- }
2275
- get __idiomorphRoot() {
2276
- return this.originalNode;
2277
- }
2237
+ };
2278
2238
  }
2279
2239
  function parseContent(newContent) {
2280
2240
  let parser = new DOMParser;
@@ -2629,11 +2589,17 @@ class PageSnapshot extends Snapshot {
2629
2589
  for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
2630
2590
  clonedPasswordInput.value = "";
2631
2591
  }
2592
+ for (const clonedNoscriptElement of clonedElement.querySelectorAll("noscript")) {
2593
+ clonedNoscriptElement.remove();
2594
+ }
2632
2595
  return new PageSnapshot(this.documentElement, clonedElement, this.headSnapshot);
2633
2596
  }
2634
2597
  get lang() {
2635
2598
  return this.documentElement.getAttribute("lang");
2636
2599
  }
2600
+ get dir() {
2601
+ return this.documentElement.getAttribute("dir");
2602
+ }
2637
2603
  get headElement() {
2638
2604
  return this.headSnapshot.element;
2639
2605
  }
@@ -2657,11 +2623,11 @@ class PageSnapshot extends Snapshot {
2657
2623
  const viewTransitionEnabled = this.getSetting("view-transition") === "true" || this.headSnapshot.getMetaValue("view-transition") === "same-origin";
2658
2624
  return viewTransitionEnabled && !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2659
2625
  }
2660
- get shouldMorphPage() {
2661
- return this.getSetting("refresh-method") === "morph";
2626
+ get refreshMethod() {
2627
+ return this.getSetting("refresh-method");
2662
2628
  }
2663
- get shouldPreserveScrollPosition() {
2664
- return this.getSetting("refresh-scroll") === "preserve";
2629
+ get refreshScroll() {
2630
+ return this.getSetting("refresh-scroll");
2665
2631
  }
2666
2632
  getSetting(name) {
2667
2633
  return this.headSnapshot.getMetaValue(`turbo-${name}`);
@@ -2694,7 +2660,8 @@ const defaultOptions = {
2694
2660
  willRender: true,
2695
2661
  updateHistory: true,
2696
2662
  shouldCacheSnapshot: true,
2697
- acceptsStreamResponse: false
2663
+ acceptsStreamResponse: false,
2664
+ refresh: {}
2698
2665
  };
2699
2666
 
2700
2667
  const TimingMetric = {
@@ -2739,7 +2706,7 @@ class Visit {
2739
2706
  this.delegate = delegate;
2740
2707
  this.location = location;
2741
2708
  this.restorationIdentifier = restorationIdentifier || uuid();
2742
- const {action: action, historyChanged: historyChanged, referrer: referrer, snapshot: snapshot, snapshotHTML: snapshotHTML, response: response, visitCachedSnapshot: visitCachedSnapshot, willRender: willRender, updateHistory: updateHistory, shouldCacheSnapshot: shouldCacheSnapshot, acceptsStreamResponse: acceptsStreamResponse, direction: direction} = {
2709
+ const {action: action, historyChanged: historyChanged, referrer: referrer, snapshot: snapshot, snapshotHTML: snapshotHTML, response: response, visitCachedSnapshot: visitCachedSnapshot, willRender: willRender, updateHistory: updateHistory, shouldCacheSnapshot: shouldCacheSnapshot, acceptsStreamResponse: acceptsStreamResponse, direction: direction, refresh: refresh} = {
2743
2710
  ...defaultOptions,
2744
2711
  ...options
2745
2712
  };
@@ -2749,7 +2716,6 @@ class Visit {
2749
2716
  this.snapshot = snapshot;
2750
2717
  this.snapshotHTML = snapshotHTML;
2751
2718
  this.response = response;
2752
- this.isSamePage = this.delegate.locationWithActionIsSamePage(this.location, this.action);
2753
2719
  this.isPageRefresh = this.view.isPageRefresh(this);
2754
2720
  this.visitCachedSnapshot = visitCachedSnapshot;
2755
2721
  this.willRender = willRender;
@@ -2758,6 +2724,7 @@ class Visit {
2758
2724
  this.shouldCacheSnapshot = shouldCacheSnapshot;
2759
2725
  this.acceptsStreamResponse = acceptsStreamResponse;
2760
2726
  this.direction = direction || Direction[action];
2727
+ this.refresh = refresh;
2761
2728
  }
2762
2729
  get adapter() {
2763
2730
  return this.delegate.adapter;
@@ -2771,9 +2738,6 @@ class Visit {
2771
2738
  get restorationData() {
2772
2739
  return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
2773
2740
  }
2774
- get silent() {
2775
- return this.isSamePage;
2776
- }
2777
2741
  start() {
2778
2742
  if (this.state == VisitState.initialized) {
2779
2743
  this.recordTimingMetric(TimingMetric.visitStart);
@@ -2892,7 +2856,7 @@ class Visit {
2892
2856
  const isPreview = this.shouldIssueRequest();
2893
2857
  this.render((async () => {
2894
2858
  this.cacheSnapshot();
2895
- if (this.isSamePage || this.isPageRefresh) {
2859
+ if (this.isPageRefresh) {
2896
2860
  this.adapter.visitRendered(this);
2897
2861
  } else {
2898
2862
  if (this.view.renderPromise) await this.view.renderPromise;
@@ -2916,16 +2880,6 @@ class Visit {
2916
2880
  this.followedRedirect = true;
2917
2881
  }
2918
2882
  }
2919
- goToSamePageAnchor() {
2920
- if (this.isSamePage) {
2921
- this.render((async () => {
2922
- this.cacheSnapshot();
2923
- this.performScroll();
2924
- this.changeHistory();
2925
- this.adapter.visitRendered(this);
2926
- }));
2927
- }
2928
- }
2929
2883
  prepareRequest(request) {
2930
2884
  if (this.acceptsStreamResponse) {
2931
2885
  request.acceptResponseType(StreamMessage.contentType);
@@ -2984,9 +2938,6 @@ class Visit {
2984
2938
  } else {
2985
2939
  this.scrollToAnchor() || this.view.scrollToTop();
2986
2940
  }
2987
- if (this.isSamePage) {
2988
- this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
2989
- }
2990
2941
  this.scrolled = true;
2991
2942
  }
2992
2943
  }
@@ -3016,9 +2967,7 @@ class Visit {
3016
2967
  return typeof this.response == "object";
3017
2968
  }
3018
2969
  shouldIssueRequest() {
3019
- if (this.isSamePage) {
3020
- return false;
3021
- } else if (this.action == "restore") {
2970
+ if (this.action == "restore") {
3022
2971
  return !this.hasCachedSnapshot();
3023
2972
  } else {
3024
2973
  return this.willRender;
@@ -3073,7 +3022,6 @@ class BrowserAdapter {
3073
3022
  this.redirectedToLocation = null;
3074
3023
  visit.loadCachedSnapshot();
3075
3024
  visit.issueRequest();
3076
- visit.goToSamePageAnchor();
3077
3025
  }
3078
3026
  visitRequestStarted(visit) {
3079
3027
  this.progressBar.setValue(0);
@@ -3167,7 +3115,6 @@ class BrowserAdapter {
3167
3115
 
3168
3116
  class CacheObserver {
3169
3117
  selector="[data-turbo-temporary]";
3170
- deprecatedSelector="[data-turbo-cache=false]";
3171
3118
  started=false;
3172
3119
  start() {
3173
3120
  if (!this.started) {
@@ -3187,14 +3134,7 @@ class CacheObserver {
3187
3134
  }
3188
3135
  };
3189
3136
  get temporaryElements() {
3190
- return [ ...document.querySelectorAll(this.selector), ...this.temporaryElementsWithDeprecation ];
3191
- }
3192
- get temporaryElementsWithDeprecation() {
3193
- const elements = document.querySelectorAll(this.deprecatedSelector);
3194
- if (elements.length) {
3195
- console.warn(`The ${this.deprecatedSelector} selector is deprecated and will be removed in a future version. Use ${this.selector} instead.`);
3196
- }
3197
- return [ ...elements ];
3137
+ return [ ...document.querySelectorAll(this.selector) ];
3198
3138
  }
3199
3139
  }
3200
3140
 
@@ -3262,7 +3202,6 @@ class History {
3262
3202
  restorationIdentifier=uuid();
3263
3203
  restorationData={};
3264
3204
  started=false;
3265
- pageLoaded=false;
3266
3205
  currentIndex=0;
3267
3206
  constructor(delegate) {
3268
3207
  this.delegate = delegate;
@@ -3270,7 +3209,6 @@ class History {
3270
3209
  start() {
3271
3210
  if (!this.started) {
3272
3211
  addEventListener("popstate", this.onPopState, false);
3273
- addEventListener("load", this.onPageLoad, false);
3274
3212
  this.currentIndex = history.state?.turbo?.restorationIndex || 0;
3275
3213
  this.started = true;
3276
3214
  this.replace(new URL(window.location.href));
@@ -3279,7 +3217,6 @@ class History {
3279
3217
  stop() {
3280
3218
  if (this.started) {
3281
3219
  removeEventListener("popstate", this.onPopState, false);
3282
- removeEventListener("load", this.onPageLoad, false);
3283
3220
  this.started = false;
3284
3221
  }
3285
3222
  }
@@ -3325,28 +3262,19 @@ class History {
3325
3262
  }
3326
3263
  }
3327
3264
  onPopState=event => {
3328
- if (this.shouldHandlePopState()) {
3329
- const {turbo: turbo} = event.state || {};
3330
- if (turbo) {
3331
- this.location = new URL(window.location.href);
3332
- const {restorationIdentifier: restorationIdentifier, restorationIndex: restorationIndex} = turbo;
3333
- this.restorationIdentifier = restorationIdentifier;
3334
- const direction = restorationIndex > this.currentIndex ? "forward" : "back";
3335
- this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location, restorationIdentifier, direction);
3336
- this.currentIndex = restorationIndex;
3337
- }
3265
+ const {turbo: turbo} = event.state || {};
3266
+ this.location = new URL(window.location.href);
3267
+ if (turbo) {
3268
+ const {restorationIdentifier: restorationIdentifier, restorationIndex: restorationIndex} = turbo;
3269
+ this.restorationIdentifier = restorationIdentifier;
3270
+ const direction = restorationIndex > this.currentIndex ? "forward" : "back";
3271
+ this.delegate.historyPoppedToLocationWithRestorationIdentifierAndDirection(this.location, restorationIdentifier, direction);
3272
+ this.currentIndex = restorationIndex;
3273
+ } else {
3274
+ this.currentIndex++;
3275
+ this.delegate.historyPoppedWithEmptyState(this.location);
3338
3276
  }
3339
3277
  };
3340
- onPageLoad=async _event => {
3341
- await nextMicrotask();
3342
- this.pageLoaded = true;
3343
- };
3344
- shouldHandlePopState() {
3345
- return this.pageIsLoaded();
3346
- }
3347
- pageIsLoaded() {
3348
- return this.pageLoaded || document.readyState == "complete";
3349
- }
3350
3278
  }
3351
3279
 
3352
3280
  class LinkPrefetchObserver {
@@ -3402,7 +3330,7 @@ class LinkPrefetchObserver {
3402
3330
  this.#prefetchedLink = link;
3403
3331
  const fetchRequest = new FetchRequest(this, FetchMethod.get, location, new URLSearchParams, target);
3404
3332
  fetchRequest.fetchOptions.priority = "low";
3405
- prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl);
3333
+ prefetchCache.putLater(location, fetchRequest, this.#cacheTtl);
3406
3334
  }
3407
3335
  }
3408
3336
  };
@@ -3415,7 +3343,7 @@ class LinkPrefetchObserver {
3415
3343
  };
3416
3344
  #tryToUsePrefetchedRequest=event => {
3417
3345
  if (event.target.tagName !== "FORM" && event.detail.fetchOptions.method === "GET") {
3418
- const cached = prefetchCache.get(event.detail.url.toString());
3346
+ const cached = prefetchCache.get(event.detail.url);
3419
3347
  if (cached) {
3420
3348
  event.detail.fetchRequest = cached;
3421
3349
  }
@@ -3564,7 +3492,7 @@ class Navigator {
3564
3492
  } else {
3565
3493
  await this.view.renderPage(snapshot, false, true, this.currentVisit);
3566
3494
  }
3567
- if (!snapshot.shouldPreserveScrollPosition) {
3495
+ if (snapshot.refreshScroll !== "preserve") {
3568
3496
  this.view.scrollToTop();
3569
3497
  }
3570
3498
  this.view.clearSnapshotCache();
@@ -3592,13 +3520,7 @@ class Navigator {
3592
3520
  delete this.currentVisit;
3593
3521
  }
3594
3522
  locationWithActionIsSamePage(location, action) {
3595
- const anchor = getAnchor(location);
3596
- const currentAnchor = getAnchor(this.view.lastRenderedLocation);
3597
- const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
3598
- return action !== "replace" && getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) && (isRestorationToTop || anchor != null && anchor !== currentAnchor);
3599
- }
3600
- visitScrolledToSamePageLocation(oldURL, newURL) {
3601
- this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
3523
+ return false;
3602
3524
  }
3603
3525
  get location() {
3604
3526
  return this.history.location;
@@ -3929,12 +3851,17 @@ class PageRenderer extends Renderer {
3929
3851
  }
3930
3852
  #setLanguage() {
3931
3853
  const {documentElement: documentElement} = this.currentSnapshot;
3932
- const {lang: lang} = this.newSnapshot;
3854
+ const {dir: dir, lang: lang} = this.newSnapshot;
3933
3855
  if (lang) {
3934
3856
  documentElement.setAttribute("lang", lang);
3935
3857
  } else {
3936
3858
  documentElement.removeAttribute("lang");
3937
3859
  }
3860
+ if (dir) {
3861
+ documentElement.setAttribute("dir", dir);
3862
+ } else {
3863
+ documentElement.removeAttribute("dir");
3864
+ }
3938
3865
  }
3939
3866
  async mergeHead() {
3940
3867
  const mergedHeadElements = this.mergeProvisionalElements();
@@ -4014,8 +3941,14 @@ class PageRenderer extends Renderer {
4014
3941
  }
4015
3942
  activateNewBody() {
4016
3943
  document.adoptNode(this.newElement);
3944
+ this.removeNoscriptElements();
4017
3945
  this.activateNewBodyScriptElements();
4018
3946
  }
3947
+ removeNoscriptElements() {
3948
+ for (const noscriptElement of this.newElement.querySelectorAll("noscript")) {
3949
+ noscriptElement.remove();
3950
+ }
3951
+ }
4019
3952
  activateNewBodyScriptElements() {
4020
3953
  for (const inertScriptElement of this.newBodyScriptElements) {
4021
3954
  const activatedScriptElement = activateScriptElement(inertScriptElement);
@@ -4079,47 +4012,12 @@ class MorphingPageRenderer extends PageRenderer {
4079
4012
  }
4080
4013
  }
4081
4014
 
4082
- class SnapshotCache {
4083
- keys=[];
4084
- snapshots={};
4015
+ class SnapshotCache extends LRUCache {
4085
4016
  constructor(size) {
4086
- this.size = size;
4087
- }
4088
- has(location) {
4089
- return toCacheKey(location) in this.snapshots;
4090
- }
4091
- get(location) {
4092
- if (this.has(location)) {
4093
- const snapshot = this.read(location);
4094
- this.touch(location);
4095
- return snapshot;
4096
- }
4097
- }
4098
- put(location, snapshot) {
4099
- this.write(location, snapshot);
4100
- this.touch(location);
4101
- return snapshot;
4102
- }
4103
- clear() {
4104
- this.snapshots = {};
4105
- }
4106
- read(location) {
4107
- return this.snapshots[toCacheKey(location)];
4108
- }
4109
- write(location, snapshot) {
4110
- this.snapshots[toCacheKey(location)] = snapshot;
4111
- }
4112
- touch(location) {
4113
- const key = toCacheKey(location);
4114
- const index = this.keys.indexOf(key);
4115
- if (index > -1) this.keys.splice(index, 1);
4116
- this.keys.unshift(key);
4117
- this.trim();
4017
+ super(size, toCacheKey);
4118
4018
  }
4119
- trim() {
4120
- for (const key of this.keys.splice(this.size)) {
4121
- delete this.snapshots[key];
4122
- }
4019
+ get snapshots() {
4020
+ return this.entries;
4123
4021
  }
4124
4022
  }
4125
4023
 
@@ -4131,7 +4029,7 @@ class PageView extends View {
4131
4029
  return this.snapshot.prefersViewTransitions && newSnapshot.prefersViewTransitions;
4132
4030
  }
4133
4031
  renderPage(snapshot, isPreview = false, willRender = true, visit) {
4134
- const shouldMorphPage = this.isPageRefresh(visit) && this.snapshot.shouldMorphPage;
4032
+ const shouldMorphPage = this.isPageRefresh(visit) && (visit?.refresh?.method || this.snapshot.refreshMethod) === "morph";
4135
4033
  const rendererClass = shouldMorphPage ? MorphingPageRenderer : PageRenderer;
4136
4034
  const renderer = new rendererClass(this.snapshot, snapshot, isPreview, willRender);
4137
4035
  if (!renderer.shouldRender) {
@@ -4166,7 +4064,7 @@ class PageView extends View {
4166
4064
  return !visit || this.lastRenderedLocation.pathname === visit.location.pathname && visit.action === "replace";
4167
4065
  }
4168
4066
  shouldPreserveScrollPosition(visit) {
4169
- return this.isPageRefresh(visit) && this.snapshot.shouldPreserveScrollPosition;
4067
+ return this.isPageRefresh(visit) && (visit?.refresh?.scroll || this.snapshot.refreshScroll) === "preserve";
4170
4068
  }
4171
4069
  get snapshot() {
4172
4070
  return PageSnapshot.fromElement(this.element);
@@ -4319,13 +4217,21 @@ class Session {
4319
4217
  this.navigator.proposeVisit(expandURL(location), options);
4320
4218
  }
4321
4219
  }
4322
- refresh(url, requestId) {
4220
+ refresh(url, options = {}) {
4221
+ options = typeof options === "string" ? {
4222
+ requestId: options
4223
+ } : options;
4224
+ const {method: method, requestId: requestId, scroll: scroll} = options;
4323
4225
  const isRecentRequest = requestId && this.recentRequests.has(requestId);
4324
4226
  const isCurrentUrl = url === document.baseURI;
4325
4227
  if (!isRecentRequest && !this.navigator.currentVisit && isCurrentUrl) {
4326
4228
  this.visit(url, {
4327
4229
  action: "replace",
4328
- shouldCacheSnapshot: false
4230
+ shouldCacheSnapshot: false,
4231
+ refresh: {
4232
+ method: method,
4233
+ scroll: scroll
4234
+ }
4329
4235
  });
4330
4236
  }
4331
4237
  }
@@ -4401,6 +4307,11 @@ class Session {
4401
4307
  });
4402
4308
  }
4403
4309
  }
4310
+ historyPoppedWithEmptyState(location) {
4311
+ this.history.replace(location);
4312
+ this.view.lastRenderedLocation = location;
4313
+ this.view.cacheSnapshot();
4314
+ }
4404
4315
  scrollPositionChanged(position) {
4405
4316
  this.history.updateRestorationData({
4406
4317
  scrollPosition: position
@@ -4425,7 +4336,7 @@ class Session {
4425
4336
  });
4426
4337
  }
4427
4338
  allowsVisitingLocationWithAction(location, action) {
4428
- return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
4339
+ return this.applicationAllowsVisitingLocation(location);
4429
4340
  }
4430
4341
  visitProposedToLocation(location, options) {
4431
4342
  extendURLWithDeprecatedProperties(location);
@@ -4437,21 +4348,13 @@ class Session {
4437
4348
  this.view.markVisitDirection(visit.direction);
4438
4349
  }
4439
4350
  extendURLWithDeprecatedProperties(visit.location);
4440
- if (!visit.silent) {
4441
- this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
4442
- }
4351
+ this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
4443
4352
  }
4444
4353
  visitCompleted(visit) {
4445
4354
  this.view.unmarkVisitDirection();
4446
4355
  clearBusyState(document.documentElement);
4447
4356
  this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
4448
4357
  }
4449
- locationWithActionIsSamePage(location, action) {
4450
- return this.navigator.locationWithActionIsSamePage(location, action);
4451
- }
4452
- visitScrolledToSamePageLocation(oldURL, newURL) {
4453
- this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
4454
- }
4455
4358
  willSubmitForm(form, submitter) {
4456
4359
  const action = getAction$1(form, submitter);
4457
4360
  return this.submissionIsNavigatable(form, submitter) && locationIsVisitable(expandURL(action), this.snapshot.rootLocation);
@@ -4473,9 +4376,7 @@ class Session {
4473
4376
  this.renderStreamMessage(message);
4474
4377
  }
4475
4378
  viewWillCacheSnapshot() {
4476
- if (!this.navigator.currentVisit?.silent) {
4477
- this.notifyApplicationBeforeCachingSnapshot();
4478
- }
4379
+ this.notifyApplicationBeforeCachingSnapshot();
4479
4380
  }
4480
4381
  allowsImmediateRender({element: element}, options) {
4481
4382
  const event = this.notifyApplicationBeforeRender(element, options);
@@ -4562,12 +4463,6 @@ class Session {
4562
4463
  }
4563
4464
  });
4564
4465
  }
4565
- notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL) {
4566
- dispatchEvent(new HashChangeEvent("hashchange", {
4567
- oldURL: oldURL.toString(),
4568
- newURL: newURL.toString()
4569
- }));
4570
- }
4571
4466
  notifyApplicationAfterFrameLoad(frame) {
4572
4467
  return dispatch("turbo:frame-load", {
4573
4468
  target: frame
@@ -4633,7 +4528,7 @@ const deprecatedLocationPropertyDescriptors = {
4633
4528
 
4634
4529
  const session = new Session(recentRequests);
4635
4530
 
4636
- const {cache: cache, navigator: navigator$1} = session;
4531
+ const {cache: cache, navigator: sessionNavigator} = session;
4637
4532
 
4638
4533
  function start() {
4639
4534
  session.start();
@@ -4659,11 +4554,6 @@ function renderStreamMessage(message) {
4659
4554
  session.renderStreamMessage(message);
4660
4555
  }
4661
4556
 
4662
- function clearCache() {
4663
- console.warn("Please replace `Turbo.clearCache()` with `Turbo.cache.clear()`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
4664
- session.clearCache();
4665
- }
4666
-
4667
4557
  function setProgressBarDelay(delay) {
4668
4558
  console.warn("Please replace `Turbo.setProgressBarDelay(delay)` with `Turbo.config.drive.progressBarDelay = delay`. The top-level function is deprecated and will be removed in a future version of Turbo.`");
4669
4559
  config.drive.progressBarDelay = delay;
@@ -4689,21 +4579,20 @@ function morphTurboFrameElements(currentFrame, newFrame) {
4689
4579
 
4690
4580
  var Turbo = Object.freeze({
4691
4581
  __proto__: null,
4692
- navigator: navigator$1,
4693
- session: session,
4694
- cache: cache,
4695
4582
  PageRenderer: PageRenderer,
4696
4583
  PageSnapshot: PageSnapshot,
4697
4584
  FrameRenderer: FrameRenderer,
4698
4585
  fetch: fetchWithTurboHeaders,
4699
4586
  config: config,
4587
+ session: session,
4588
+ cache: cache,
4589
+ navigator: sessionNavigator,
4700
4590
  start: start,
4701
4591
  registerAdapter: registerAdapter,
4702
4592
  visit: visit,
4703
4593
  connectStreamSource: connectStreamSource,
4704
4594
  disconnectStreamSource: disconnectStreamSource,
4705
4595
  renderStreamMessage: renderStreamMessage,
4706
- clearCache: clearCache,
4707
4596
  setProgressBarDelay: setProgressBarDelay,
4708
4597
  setConfirmMethod: setConfirmMethod,
4709
4598
  setFormMode: setFormMode,
@@ -4753,15 +4642,23 @@ class FrameController {
4753
4642
  this.formLinkClickObserver.stop();
4754
4643
  this.linkInterceptor.stop();
4755
4644
  this.formSubmitObserver.stop();
4645
+ if (!this.element.hasAttribute("recurse")) {
4646
+ this.#currentFetchRequest?.cancel();
4647
+ }
4756
4648
  }
4757
4649
  }
4758
4650
  disabledChanged() {
4759
- if (this.loadingStyle == FrameLoadingStyle.eager) {
4651
+ if (this.disabled) {
4652
+ this.#currentFetchRequest?.cancel();
4653
+ } else if (this.loadingStyle == FrameLoadingStyle.eager) {
4760
4654
  this.#loadSourceURL();
4761
4655
  }
4762
4656
  }
4763
4657
  sourceURLChanged() {
4764
4658
  if (this.#isIgnoringChangesTo("src")) return;
4659
+ if (!this.sourceURL) {
4660
+ this.#currentFetchRequest?.cancel();
4661
+ }
4765
4662
  if (this.element.isConnected) {
4766
4663
  this.complete = false;
4767
4664
  }
@@ -4839,11 +4736,12 @@ class FrameController {
4839
4736
  }
4840
4737
  this.formSubmission = new FormSubmission(this, element, submitter);
4841
4738
  const {fetchRequest: fetchRequest} = this.formSubmission;
4842
- this.prepareRequest(fetchRequest);
4739
+ const frame = this.#findFrameElement(element, submitter);
4740
+ this.prepareRequest(fetchRequest, frame);
4843
4741
  this.formSubmission.start();
4844
4742
  }
4845
- prepareRequest(request) {
4846
- request.headers["Turbo-Frame"] = this.id;
4743
+ prepareRequest(request, frame = this) {
4744
+ request.headers["Turbo-Frame"] = frame.id;
4847
4745
  if (this.currentNavigationElement?.hasAttribute("data-turbo-stream")) {
4848
4746
  request.acceptResponseType(StreamMessage.contentType);
4849
4747
  }
@@ -5037,7 +4935,8 @@ class FrameController {
5037
4935
  }
5038
4936
  #findFrameElement(element, submitter) {
5039
4937
  const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
5040
- return getFrameElementById(id) ?? this.element;
4938
+ const target = this.#getFrameElementById(id);
4939
+ return target instanceof FrameElement ? target : this.element;
5041
4940
  }
5042
4941
  async extractForeignFrameElement(container) {
5043
4942
  let element;
@@ -5071,9 +4970,11 @@ class FrameController {
5071
4970
  return false;
5072
4971
  }
5073
4972
  if (id) {
5074
- const frameElement = getFrameElementById(id);
4973
+ const frameElement = this.#getFrameElementById(id);
5075
4974
  if (frameElement) {
5076
4975
  return !frameElement.disabled;
4976
+ } else if (id == "_parent") {
4977
+ return false;
5077
4978
  }
5078
4979
  }
5079
4980
  if (!session.elementIsNavigatable(element)) {
@@ -5087,8 +4988,11 @@ class FrameController {
5087
4988
  get id() {
5088
4989
  return this.element.id;
5089
4990
  }
4991
+ get disabled() {
4992
+ return this.element.disabled;
4993
+ }
5090
4994
  get enabled() {
5091
- return !this.element.disabled;
4995
+ return !this.disabled;
5092
4996
  }
5093
4997
  get sourceURL() {
5094
4998
  if (this.element.src) {
@@ -5137,13 +5041,12 @@ class FrameController {
5137
5041
  callback();
5138
5042
  delete this.currentNavigationElement;
5139
5043
  }
5140
- }
5141
-
5142
- function getFrameElementById(id) {
5143
- if (id != null) {
5144
- const element = document.getElementById(id);
5145
- if (element instanceof FrameElement) {
5146
- return element;
5044
+ #getFrameElementById(id) {
5045
+ if (id != null) {
5046
+ const element = id === "_parent" ? this.element.parentElement.closest("turbo-frame") : document.getElementById(id);
5047
+ if (element instanceof FrameElement) {
5048
+ return element;
5049
+ }
5147
5050
  }
5148
5051
  }
5149
5052
  }
@@ -5167,6 +5070,7 @@ function activateElement(element, currentURL) {
5167
5070
 
5168
5071
  const StreamActions = {
5169
5072
  after() {
5073
+ this.removeDuplicateTargetSiblings();
5170
5074
  this.targetElements.forEach((e => e.parentElement?.insertBefore(this.templateContent, e.nextSibling)));
5171
5075
  },
5172
5076
  append() {
@@ -5174,6 +5078,7 @@ const StreamActions = {
5174
5078
  this.targetElements.forEach((e => e.append(this.templateContent)));
5175
5079
  },
5176
5080
  before() {
5081
+ this.removeDuplicateTargetSiblings();
5177
5082
  this.targetElements.forEach((e => e.parentElement?.insertBefore(this.templateContent, e)));
5178
5083
  },
5179
5084
  prepend() {
@@ -5205,7 +5110,14 @@ const StreamActions = {
5205
5110
  }));
5206
5111
  },
5207
5112
  refresh() {
5208
- session.refresh(this.baseURI, this.requestId);
5113
+ const method = this.getAttribute("method");
5114
+ const requestId = this.requestId;
5115
+ const scroll = this.getAttribute("scroll");
5116
+ session.refresh(this.baseURI, {
5117
+ method: method,
5118
+ requestId: requestId,
5119
+ scroll: scroll
5120
+ });
5209
5121
  }
5210
5122
  };
5211
5123
 
@@ -5244,6 +5156,14 @@ class StreamElement extends HTMLElement {
5244
5156
  const newChildrenIds = [ ...this.templateContent?.children || [] ].filter((c => !!c.getAttribute("id"))).map((c => c.getAttribute("id")));
5245
5157
  return existingChildren.filter((c => newChildrenIds.includes(c.getAttribute("id"))));
5246
5158
  }
5159
+ removeDuplicateTargetSiblings() {
5160
+ this.duplicateSiblings.forEach((c => c.remove()));
5161
+ }
5162
+ get duplicateSiblings() {
5163
+ const existingChildren = this.targetElements.flatMap((e => [ ...e.parentElement.children ])).filter((c => !!c.id));
5164
+ const newChildrenIds = [ ...this.templateContent?.children || [] ].filter((c => !!c.id)).map((c => c.id));
5165
+ return existingChildren.filter((c => newChildrenIds.includes(c.id)));
5166
+ }
5247
5167
  get performAction() {
5248
5168
  if (this.action) {
5249
5169
  const actionFunction = StreamActions[this.action];
@@ -5354,10 +5274,10 @@ if (customElements.get("turbo-stream-source") === undefined) {
5354
5274
  }
5355
5275
 
5356
5276
  (() => {
5357
- let element = document.currentScript;
5358
- if (!element) return;
5359
- if (element.hasAttribute("data-turbo-suppress-warning")) return;
5360
- element = element.parentElement;
5277
+ const scriptElement = document.currentScript;
5278
+ if (!scriptElement) return;
5279
+ if (scriptElement.hasAttribute("data-turbo-suppress-warning")) return;
5280
+ let element = scriptElement.parentElement;
5361
5281
  while (element) {
5362
5282
  if (element == document.body) {
5363
5283
  return console.warn(unindent`
@@ -5369,7 +5289,7 @@ if (customElements.get("turbo-stream-source") === undefined) {
5369
5289
 
5370
5290
  ——
5371
5291
  Suppress this warning by adding a "data-turbo-suppress-warning" attribute to: %s
5372
- `, element.outerHTML);
5292
+ `, scriptElement.outerHTML);
5373
5293
  }
5374
5294
  element = element.parentElement;
5375
5295
  }
@@ -5397,7 +5317,6 @@ var Turbo$1 = Object.freeze({
5397
5317
  StreamElement: StreamElement,
5398
5318
  StreamSourceElement: StreamSourceElement,
5399
5319
  cache: cache,
5400
- clearCache: clearCache,
5401
5320
  config: config,
5402
5321
  connectStreamSource: connectStreamSource,
5403
5322
  disconnectStreamSource: disconnectStreamSource,
@@ -5409,7 +5328,7 @@ var Turbo$1 = Object.freeze({
5409
5328
  morphChildren: morphChildren,
5410
5329
  morphElements: morphElements,
5411
5330
  morphTurboFrameElements: morphTurboFrameElements,
5412
- navigator: navigator$1,
5331
+ navigator: sessionNavigator,
5413
5332
  registerAdapter: registerAdapter,
5414
5333
  renderStreamMessage: renderStreamMessage,
5415
5334
  session: session,