@hotwired/turbo 7.2.0-beta.2 → 7.2.0-rc.2

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/README.md CHANGED
@@ -11,4 +11,8 @@ It's all done by sending HTML over the wire. And for those instances when that's
11
11
 
12
12
  Read more on [turbo.hotwired.dev](https://turbo.hotwired.dev).
13
13
 
14
+ ## Contributing
15
+
16
+ Please read [CONTRIBUTING.md](./CONTRIBUTING.md).
17
+
14
18
  © 2021 Basecamp, LLC.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Turbo 7.2.0-beta.2
2
+ Turbo 7.2.0-rc.1
3
3
  Copyright © 2022 Basecamp, LLC
4
4
  */
5
5
  (function () {
@@ -404,6 +404,9 @@ function getAttribute(attributeName, ...elements) {
404
404
  }
405
405
  return null;
406
406
  }
407
+ function hasAttribute(attributeName, ...elements) {
408
+ return elements.some((element) => element && element.hasAttribute(attributeName));
409
+ }
407
410
  function markAsBusy(...elements) {
408
411
  for (const element of elements) {
409
412
  if (element.localName == "turbo-frame") {
@@ -520,7 +523,9 @@ class FetchRequest {
520
523
  }
521
524
  catch (error) {
522
525
  if (error.name !== "AbortError") {
523
- this.delegate.requestErrored(this, error);
526
+ if (this.willDelegateErrorHandling(error)) {
527
+ this.delegate.requestErrored(this, error);
528
+ }
524
529
  throw error;
525
530
  }
526
531
  }
@@ -586,6 +591,14 @@ class FetchRequest {
586
591
  if (event.defaultPrevented)
587
592
  await requestInterception;
588
593
  }
594
+ willDelegateErrorHandling(error) {
595
+ const event = dispatch("turbo:fetch-request-error", {
596
+ target: this.target,
597
+ cancelable: true,
598
+ detail: { request: this, error: error },
599
+ });
600
+ return !event.defaultPrevented;
601
+ }
589
602
  }
590
603
 
591
604
  class AppearanceObserver {
@@ -679,7 +692,7 @@ class FormSubmission {
679
692
  this.fetchRequest = new FetchRequest(this, this.method, this.location, this.body, this.formElement);
680
693
  this.mustRedirect = mustRedirect;
681
694
  }
682
- static confirmMethod(message, _element) {
695
+ static confirmMethod(message, _element, _submitter) {
683
696
  return Promise.resolve(confirm(message));
684
697
  }
685
698
  get method() {
@@ -717,17 +730,11 @@ class FormSubmission {
717
730
  return entries.concat(typeof value == "string" ? [[name, value]] : []);
718
731
  }, []);
719
732
  }
720
- get confirmationMessage() {
721
- var _a;
722
- return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("data-turbo-confirm")) || this.formElement.getAttribute("data-turbo-confirm");
723
- }
724
- get needsConfirmation() {
725
- return this.confirmationMessage !== null;
726
- }
727
733
  async start() {
728
734
  const { initialized, requesting } = FormSubmissionState;
729
- if (this.needsConfirmation) {
730
- const answer = await FormSubmission.confirmMethod(this.confirmationMessage, this.formElement);
735
+ const confirmationMessage = getAttribute("data-turbo-confirm", this.submitter, this.formElement);
736
+ if (typeof confirmationMessage === "string") {
737
+ const answer = await FormSubmission.confirmMethod(confirmationMessage, this.formElement, this.submitter);
731
738
  if (!answer) {
732
739
  return;
733
740
  }
@@ -789,10 +796,6 @@ class FormSubmission {
789
796
  }
790
797
  requestErrored(request, error) {
791
798
  this.result = { success: false, error };
792
- dispatch("turbo:fetch-request-error", {
793
- target: this.formElement,
794
- detail: { request, error },
795
- });
796
799
  this.delegate.formSubmissionErrored(this, error);
797
800
  }
798
801
  requestFinished(_request) {
@@ -809,7 +812,7 @@ class FormSubmission {
809
812
  return !request.isIdempotent && this.mustRedirect;
810
813
  }
811
814
  requestAcceptsTurboStreamResponse(request) {
812
- return !request.isIdempotent || this.formElement.hasAttribute("data-turbo-stream");
815
+ return !request.isIdempotent || hasAttribute("data-turbo-stream", this.submitter, this.formElement);
813
816
  }
814
817
  }
815
818
  function buildFormData(formElement, submitter) {
@@ -875,10 +878,10 @@ class Snapshot {
875
878
  return null;
876
879
  }
877
880
  get permanentElements() {
878
- return [...this.element.querySelectorAll("[id][data-turbo-permanent]")];
881
+ return queryPermanentElementsAll(this.element);
879
882
  }
880
883
  getPermanentElementById(id) {
881
- return this.element.querySelector(`#${id}[data-turbo-permanent]`);
884
+ return getPermanentElementById(this.element, id);
882
885
  }
883
886
  getPermanentElementMapForSnapshot(snapshot) {
884
887
  const permanentElementMap = {};
@@ -892,6 +895,12 @@ class Snapshot {
892
895
  return permanentElementMap;
893
896
  }
894
897
  }
898
+ function getPermanentElementById(node, id) {
899
+ return node.querySelector(`#${id}[data-turbo-permanent]`);
900
+ }
901
+ function queryPermanentElementsAll(node) {
902
+ return node.querySelectorAll("[id][data-turbo-permanent]");
903
+ }
895
904
 
896
905
  class FormSubmitObserver {
897
906
  constructor(delegate, eventTarget) {
@@ -1133,6 +1142,9 @@ class FormLinkClickObserver {
1133
1142
  const turboFrame = link.getAttribute("data-turbo-frame");
1134
1143
  if (turboFrame)
1135
1144
  form.setAttribute("data-turbo-frame", turboFrame);
1145
+ const turboAction = link.getAttribute("data-turbo-action");
1146
+ if (turboAction)
1147
+ form.setAttribute("data-turbo-action", turboAction);
1136
1148
  const turboConfirm = link.getAttribute("data-turbo-confirm");
1137
1149
  if (turboConfirm)
1138
1150
  form.setAttribute("data-turbo-confirm", turboConfirm);
@@ -1141,8 +1153,8 @@ class FormLinkClickObserver {
1141
1153
  form.setAttribute("data-turbo-stream", "");
1142
1154
  this.delegate.submittedFormLinkToLocation(link, location, form);
1143
1155
  document.body.appendChild(form);
1144
- form.requestSubmit();
1145
- form.remove();
1156
+ form.addEventListener("turbo:submit-end", () => form.remove(), { once: true });
1157
+ requestAnimationFrame(() => form.requestSubmit());
1146
1158
  }
1147
1159
  }
1148
1160
 
@@ -1513,19 +1525,19 @@ function elementIsTracked(element) {
1513
1525
  return element.getAttribute("data-turbo-track") == "reload";
1514
1526
  }
1515
1527
  function elementIsScript(element) {
1516
- const tagName = element.tagName.toLowerCase();
1528
+ const tagName = element.localName;
1517
1529
  return tagName == "script";
1518
1530
  }
1519
1531
  function elementIsNoscript(element) {
1520
- const tagName = element.tagName.toLowerCase();
1532
+ const tagName = element.localName;
1521
1533
  return tagName == "noscript";
1522
1534
  }
1523
1535
  function elementIsStylesheet(element) {
1524
- const tagName = element.tagName.toLowerCase();
1536
+ const tagName = element.localName;
1525
1537
  return tagName == "style" || (tagName == "link" && element.getAttribute("rel") == "stylesheet");
1526
1538
  }
1527
1539
  function elementIsMetaElementWithName(element, name) {
1528
- const tagName = element.tagName.toLowerCase();
1540
+ const tagName = element.localName;
1529
1541
  return tagName == "meta" && element.getAttribute("name") == name;
1530
1542
  }
1531
1543
  function elementWithoutNonce(element) {
@@ -1550,7 +1562,20 @@ class PageSnapshot extends Snapshot {
1550
1562
  return new this(body, new HeadSnapshot(head));
1551
1563
  }
1552
1564
  clone() {
1553
- return new PageSnapshot(this.element.cloneNode(true), this.headSnapshot);
1565
+ const clonedElement = this.element.cloneNode(true);
1566
+ const selectElements = this.element.querySelectorAll("select");
1567
+ const clonedSelectElements = clonedElement.querySelectorAll("select");
1568
+ for (const [index, source] of selectElements.entries()) {
1569
+ const clone = clonedSelectElements[index];
1570
+ for (const option of clone.selectedOptions)
1571
+ option.selected = false;
1572
+ for (const option of source.selectedOptions)
1573
+ clone.options[option.index].selected = true;
1574
+ }
1575
+ for (const clonedPasswordInput of clonedElement.querySelectorAll('input[type="password"]')) {
1576
+ clonedPasswordInput.value = "";
1577
+ }
1578
+ return new PageSnapshot(clonedElement, this.headSnapshot);
1554
1579
  }
1555
1580
  get headElement() {
1556
1581
  return this.headSnapshot.element;
@@ -1600,6 +1625,7 @@ const defaultOptions = {
1600
1625
  updateHistory: true,
1601
1626
  shouldCacheSnapshot: true,
1602
1627
  acceptsStreamResponse: false,
1628
+ initiator: document.documentElement,
1603
1629
  };
1604
1630
  var SystemStatusCode;
1605
1631
  (function (SystemStatusCode) {
@@ -1609,7 +1635,6 @@ var SystemStatusCode;
1609
1635
  })(SystemStatusCode || (SystemStatusCode = {}));
1610
1636
  class Visit {
1611
1637
  constructor(delegate, location, restorationIdentifier, options = {}) {
1612
- this.identifier = uuid();
1613
1638
  this.timingMetrics = {};
1614
1639
  this.followedRedirect = false;
1615
1640
  this.historyChanged = false;
@@ -1622,7 +1647,7 @@ class Visit {
1622
1647
  this.location = location;
1623
1648
  this.restorationIdentifier = restorationIdentifier || uuid();
1624
1649
  this.promise = new Promise((resolve, reject) => (this.resolvingFunctions = { resolve, reject }));
1625
- const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, } = Object.assign(Object.assign({}, defaultOptions), options);
1650
+ const { action, historyChanged, referrer, snapshotHTML, response, visitCachedSnapshot, willRender, updateHistory, shouldCacheSnapshot, acceptsStreamResponse, initiator, } = Object.assign(Object.assign({}, defaultOptions), options);
1626
1651
  this.action = action;
1627
1652
  this.historyChanged = historyChanged;
1628
1653
  this.referrer = referrer;
@@ -1635,6 +1660,7 @@ class Visit {
1635
1660
  this.scrolled = !willRender;
1636
1661
  this.shouldCacheSnapshot = shouldCacheSnapshot;
1637
1662
  this.acceptsStreamResponse = acceptsStreamResponse;
1663
+ this.initiator = initiator;
1638
1664
  }
1639
1665
  get adapter() {
1640
1666
  return this.delegate.adapter;
@@ -1702,7 +1728,7 @@ class Visit {
1702
1728
  this.simulateRequest();
1703
1729
  }
1704
1730
  else if (this.shouldIssueRequest() && !this.request) {
1705
- this.request = new FetchRequest(this, FetchMethod.get, this.location);
1731
+ this.request = new FetchRequest(this, FetchMethod.get, this.location, undefined, this.initiator);
1706
1732
  this.request.perform();
1707
1733
  }
1708
1734
  }
@@ -1798,7 +1824,6 @@ class Visit {
1798
1824
  if (this.redirectedToLocation && !this.followedRedirect && ((_a = this.response) === null || _a === void 0 ? void 0 : _a.redirected)) {
1799
1825
  this.adapter.visitProposedToLocation(this.redirectedToLocation, {
1800
1826
  action: "replace",
1801
- willRender: false,
1802
1827
  response: this.response,
1803
1828
  });
1804
1829
  this.followedRedirect = true;
@@ -2208,7 +2233,7 @@ class Navigator {
2208
2233
  this.delegate = delegate;
2209
2234
  }
2210
2235
  proposeVisit(location, options = {}) {
2211
- if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
2236
+ if (this.delegate.allowsVisitingLocation(location, options)) {
2212
2237
  if (locationIsVisitable(location, this.view.snapshot.rootLocation)) {
2213
2238
  return this.delegate.visitProposedToLocation(location, options);
2214
2239
  }
@@ -2416,6 +2441,30 @@ class ScrollObserver {
2416
2441
  }
2417
2442
  }
2418
2443
 
2444
+ class StreamMessageRenderer {
2445
+ render({ fragment }) {
2446
+ Bardo.preservingPermanentElements(this, getPermanentElementMapForFragment(fragment), () => document.documentElement.appendChild(fragment));
2447
+ }
2448
+ enteringBardo(currentPermanentElement, newPermanentElement) {
2449
+ newPermanentElement.replaceWith(currentPermanentElement.cloneNode(true));
2450
+ }
2451
+ leavingBardo() { }
2452
+ }
2453
+ function getPermanentElementMapForFragment(fragment) {
2454
+ const permanentElementsInDocument = queryPermanentElementsAll(document.documentElement);
2455
+ const permanentElementMap = {};
2456
+ for (const permanentElementInDocument of permanentElementsInDocument) {
2457
+ const { id } = permanentElementInDocument;
2458
+ for (const streamElement of fragment.querySelectorAll("turbo-stream")) {
2459
+ const elementInStream = getPermanentElementById(streamElement.templateElement.content, id);
2460
+ if (elementInStream) {
2461
+ permanentElementMap[id] = [permanentElementInDocument, elementInStream];
2462
+ }
2463
+ }
2464
+ }
2465
+ return permanentElementMap;
2466
+ }
2467
+
2419
2468
  class StreamObserver {
2420
2469
  constructor(delegate) {
2421
2470
  this.sources = new Set();
@@ -2776,6 +2825,7 @@ class Session {
2776
2825
  this.streamObserver = new StreamObserver(this);
2777
2826
  this.formLinkClickObserver = new FormLinkClickObserver(this, document.documentElement);
2778
2827
  this.frameRedirector = new FrameRedirector(this, document.documentElement);
2828
+ this.streamMessageRenderer = new StreamMessageRenderer();
2779
2829
  this.drive = true;
2780
2830
  this.enabled = true;
2781
2831
  this.progressBarDelay = 500;
@@ -2819,7 +2869,7 @@ class Session {
2819
2869
  this.adapter = adapter;
2820
2870
  }
2821
2871
  visit(location, options = {}) {
2822
- const frameElement = document.getElementById(options.frame || "");
2872
+ const frameElement = options.frame ? document.getElementById(options.frame) : null;
2823
2873
  if (frameElement instanceof FrameElement) {
2824
2874
  frameElement.src = location.toString();
2825
2875
  return frameElement.loaded;
@@ -2835,7 +2885,7 @@ class Session {
2835
2885
  this.streamObserver.disconnectStreamSource(source);
2836
2886
  }
2837
2887
  renderStreamMessage(message) {
2838
- document.documentElement.appendChild(StreamMessage.wrap(message).fragment);
2888
+ this.streamMessageRenderer.render(StreamMessage.wrap(message));
2839
2889
  }
2840
2890
  clearCache() {
2841
2891
  this.view.clearSnapshotCache();
@@ -2880,22 +2930,27 @@ class Session {
2880
2930
  followedLinkToLocation(link, location) {
2881
2931
  const action = this.getActionForLink(link);
2882
2932
  const acceptsStreamResponse = link.hasAttribute("data-turbo-stream");
2883
- this.visit(location.href, { action, acceptsStreamResponse });
2933
+ this.visit(location.href, { action, acceptsStreamResponse, initiator: link });
2884
2934
  }
2885
- allowsVisitingLocationWithAction(location, action) {
2886
- return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
2935
+ allowsVisitingLocation(location, options = {}) {
2936
+ return (this.locationWithActionIsSamePage(location, options.action) ||
2937
+ this.applicationAllowsVisitingLocation(location, options));
2887
2938
  }
2888
2939
  visitProposedToLocation(location, options) {
2889
2940
  extendURLWithDeprecatedProperties(location);
2890
2941
  return this.adapter.visitProposedToLocation(location, options);
2891
2942
  }
2892
2943
  visitStarted(visit) {
2944
+ if (!visit.acceptsStreamResponse) {
2945
+ markAsBusy(document.documentElement);
2946
+ }
2893
2947
  extendURLWithDeprecatedProperties(visit.location);
2894
2948
  if (!visit.silent) {
2895
- this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
2949
+ this.notifyApplicationAfterVisitingLocation(visit.location, visit.action, visit.initiator);
2896
2950
  }
2897
2951
  }
2898
2952
  visitCompleted(visit) {
2953
+ clearBusyState(document.documentElement);
2899
2954
  this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
2900
2955
  }
2901
2956
  locationWithActionIsSamePage(location, action) {
@@ -2955,16 +3010,12 @@ class Session {
2955
3010
  frameRendered(fetchResponse, frame) {
2956
3011
  this.notifyApplicationAfterFrameRender(fetchResponse, frame);
2957
3012
  }
2958
- frameMissing(frame, fetchResponse) {
2959
- console.warn(`Completing full-page visit as matching frame for #${frame.id} was missing from the response`);
2960
- return this.visit(fetchResponse.location);
2961
- }
2962
3013
  applicationAllowsFollowingLinkToLocation(link, location, ev) {
2963
3014
  const event = this.notifyApplicationAfterClickingLinkToLocation(link, location, ev);
2964
3015
  return !event.defaultPrevented;
2965
3016
  }
2966
- applicationAllowsVisitingLocation(location) {
2967
- const event = this.notifyApplicationBeforeVisitingLocation(location);
3017
+ applicationAllowsVisitingLocation(location, options = {}) {
3018
+ const event = this.notifyApplicationBeforeVisitingLocation(location, options.initiator);
2968
3019
  return !event.defaultPrevented;
2969
3020
  }
2970
3021
  notifyApplicationAfterClickingLinkToLocation(link, location, event) {
@@ -2974,15 +3025,18 @@ class Session {
2974
3025
  cancelable: true,
2975
3026
  });
2976
3027
  }
2977
- notifyApplicationBeforeVisitingLocation(location) {
3028
+ notifyApplicationBeforeVisitingLocation(location, element) {
2978
3029
  return dispatch("turbo:before-visit", {
3030
+ target: element,
2979
3031
  detail: { url: location.href },
2980
3032
  cancelable: true,
2981
3033
  });
2982
3034
  }
2983
- notifyApplicationAfterVisitingLocation(location, action) {
2984
- markAsBusy(document.documentElement);
2985
- return dispatch("turbo:visit", { detail: { url: location.href, action } });
3035
+ notifyApplicationAfterVisitingLocation(location, action, element) {
3036
+ return dispatch("turbo:visit", {
3037
+ target: element,
3038
+ detail: { url: location.href, action },
3039
+ });
2986
3040
  }
2987
3041
  notifyApplicationBeforeCachingSnapshot() {
2988
3042
  return dispatch("turbo:before-cache");
@@ -2997,7 +3051,6 @@ class Session {
2997
3051
  return dispatch("turbo:render");
2998
3052
  }
2999
3053
  notifyApplicationAfterPageLoad(timing = {}) {
3000
- clearBusyState(document.documentElement);
3001
3054
  return dispatch("turbo:load", {
3002
3055
  detail: { url: this.location.href, timing },
3003
3056
  });
@@ -3279,8 +3332,9 @@ class FrameController {
3279
3332
  session.frameLoaded(this.element);
3280
3333
  this.fetchResponseLoaded(fetchResponse);
3281
3334
  }
3282
- else if (this.sessionWillHandleMissingFrame(fetchResponse)) {
3283
- await session.frameMissing(this.element, fetchResponse);
3335
+ else if (this.willHandleFrameMissingFromResponse(fetchResponse)) {
3336
+ console.warn(`A matching frame for #${this.element.id} was missing from the response, transforming into full-page Visit.`);
3337
+ this.visitResponse(fetchResponse.response);
3284
3338
  }
3285
3339
  }
3286
3340
  }
@@ -3338,16 +3392,13 @@ class FrameController {
3338
3392
  await this.loadResponse(response);
3339
3393
  this.resolveVisitPromise();
3340
3394
  }
3341
- requestFailedWithResponse(request, response) {
3395
+ async requestFailedWithResponse(request, response) {
3342
3396
  console.error(response);
3397
+ await this.loadResponse(response);
3343
3398
  this.resolveVisitPromise();
3344
3399
  }
3345
3400
  requestErrored(request, error) {
3346
3401
  console.error(error);
3347
- dispatch("turbo:fetch-request-error", {
3348
- target: this.element,
3349
- detail: { request, error },
3350
- });
3351
3402
  this.resolveVisitPromise();
3352
3403
  }
3353
3404
  requestFinished(_request) {
@@ -3441,15 +3492,30 @@ class FrameController {
3441
3492
  session.history.update(method, expandURL(this.frame.src || ""), this.restorationIdentifier);
3442
3493
  }
3443
3494
  }
3444
- sessionWillHandleMissingFrame(fetchResponse) {
3495
+ willHandleFrameMissingFromResponse(fetchResponse) {
3445
3496
  this.element.setAttribute("complete", "");
3497
+ const response = fetchResponse.response;
3498
+ const visit = async (url, options = {}) => {
3499
+ if (url instanceof Response) {
3500
+ this.visitResponse(url);
3501
+ }
3502
+ else {
3503
+ session.visit(url, options);
3504
+ }
3505
+ };
3446
3506
  const event = dispatch("turbo:frame-missing", {
3447
3507
  target: this.element,
3448
- detail: { fetchResponse },
3508
+ detail: { response, visit },
3449
3509
  cancelable: true,
3450
3510
  });
3451
3511
  return !event.defaultPrevented;
3452
3512
  }
3513
+ async visitResponse(response) {
3514
+ const wrapped = new FetchResponse(response);
3515
+ const responseHTML = await wrapped.responseHTML;
3516
+ const { location, redirected, statusCode } = wrapped;
3517
+ return session.visit(location, { response: { redirected, statusCode, responseHTML } });
3518
+ }
3453
3519
  findFrameElement(element, submitter) {
3454
3520
  var _a;
3455
3521
  const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target");
@@ -3585,6 +3651,9 @@ function activateElement(element, currentURL) {
3585
3651
  }
3586
3652
 
3587
3653
  class StreamElement extends HTMLElement {
3654
+ static async renderElement(newElement) {
3655
+ await newElement.performAction();
3656
+ }
3588
3657
  async connectedCallback() {
3589
3658
  try {
3590
3659
  await this.render();
@@ -3599,9 +3668,10 @@ class StreamElement extends HTMLElement {
3599
3668
  async render() {
3600
3669
  var _a;
3601
3670
  return ((_a = this.renderPromise) !== null && _a !== void 0 ? _a : (this.renderPromise = (async () => {
3602
- if (this.dispatchEvent(this.beforeRenderEvent)) {
3671
+ const event = this.beforeRenderEvent;
3672
+ if (this.dispatchEvent(event)) {
3603
3673
  await nextAnimationFrame();
3604
- this.performAction();
3674
+ await event.detail.render(this);
3605
3675
  }
3606
3676
  })()));
3607
3677
  }
@@ -3645,7 +3715,12 @@ class StreamElement extends HTMLElement {
3645
3715
  return this.templateElement.content.cloneNode(true);
3646
3716
  }
3647
3717
  get templateElement() {
3648
- if (this.firstElementChild instanceof HTMLTemplateElement) {
3718
+ if (this.firstElementChild === null) {
3719
+ const template = this.ownerDocument.createElement("template");
3720
+ this.appendChild(template);
3721
+ return template;
3722
+ }
3723
+ else if (this.firstElementChild instanceof HTMLTemplateElement) {
3649
3724
  return this.firstElementChild;
3650
3725
  }
3651
3726
  this.raise("first child element must be a <template> element");
@@ -3670,7 +3745,7 @@ class StreamElement extends HTMLElement {
3670
3745
  return new CustomEvent("turbo:before-stream-render", {
3671
3746
  bubbles: true,
3672
3747
  cancelable: true,
3673
- detail: { newStream: this },
3748
+ detail: { newStream: this, render: StreamElement.renderElement },
3674
3749
  });
3675
3750
  }
3676
3751
  get targetElementsById() {
@@ -3752,4 +3827,4 @@ if (customElements.get("turbo-stream-source") === undefined) {
3752
3827
  window.Turbo = Turbo;
3753
3828
  start();
3754
3829
 
3755
- export { FrameRenderer, PageRenderer, PageSnapshot, StreamActions, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };
3830
+ export { FrameElement, FrameLoadingStyle, FrameRenderer, PageRenderer, PageSnapshot, StreamActions, StreamElement, StreamSourceElement, cache, clearCache, connectStreamSource, disconnectStreamSource, navigator$1 as navigator, registerAdapter, renderStreamMessage, session, setConfirmMethod, setFormMode, setProgressBarDelay, start, visit };