Skip to content

Releases: Shopify/remote-dom

@remote-dom/signals@2.1.1

08 May 13:56
9eff4af

Choose a tag to compare

Patch Changes

  • #611 f20f6e7 Thanks @eddiechan! - Guard receivers against late mutations on detached nodes

    RemoteReceiver and SignalRemoteReceiver now drop insertChild, removeChild, updateProperty, and updateText mutations whose target node is no longer in the receiver's attached map, instead of throwing a TypeError while dereferencing the missing node.

    This race surfaces in production as unhandled promise rejections such as TypeError: undefined is not an object (evaluating 'x.properties') (Safari) / TypeError: Cannot read properties of undefined (reading 'properties') (V8) when a remote sender dispatches a mutation for a node whose host-side state has just been removed (for example, a removeChild for an ancestor was processed earlier in the same batch, or arrived first from a separate batched payload).

    PR #533 previously added a similar guard to removeChild for the case where the child slot at a given index was empty, but did not handle the case where the parent itself was missing, and did not touch updateProperty or updateText. This change applies the same defensive pattern uniformly to every connection callback that dereferences attached.get(id).

    Late mutations targeting a detached subtree are by definition no-ops — there is nothing left to mutate.

  • Updated dependencies [f20f6e7]:

    • @remote-dom/core@1.11.1

@remote-dom/core@1.11.1

08 May 13:56
9eff4af

Choose a tag to compare

Patch Changes

  • #611 f20f6e7 Thanks @eddiechan! - Guard receivers against late mutations on detached nodes

    RemoteReceiver and SignalRemoteReceiver now drop insertChild, removeChild, updateProperty, and updateText mutations whose target node is no longer in the receiver's attached map, instead of throwing a TypeError while dereferencing the missing node.

    This race surfaces in production as unhandled promise rejections such as TypeError: undefined is not an object (evaluating 'x.properties') (Safari) / TypeError: Cannot read properties of undefined (reading 'properties') (V8) when a remote sender dispatches a mutation for a node whose host-side state has just been removed (for example, a removeChild for an ancestor was processed earlier in the same batch, or arrived first from a separate batched payload).

    PR #533 previously added a similar guard to removeChild for the case where the child slot at a given index was empty, but did not handle the case where the parent itself was missing, and did not touch updateProperty or updateText. This change applies the same defensive pattern uniformly to every connection callback that dereferences attached.get(id).

    Late mutations targeting a detached subtree are by definition no-ops — there is nothing left to mutate.

@remote-dom/core@1.11.0

01 May 13:21
3665d97

Choose a tag to compare

Minor Changes

  • #604 7177edb Thanks @justinhenricks! - Flush BatchingRemoteConnection mutations on a microtask by default

    The default batch function used by BatchingRemoteConnection now schedules flushes on a microtask (via queueMicrotask, falling back to Promise.resolve().then(...)) instead of a macrotask via MessageChannel/setTimeout.

    This ensures that DOM mutations made in the remote environment are delivered to the host before any awaited RPC response resolves, avoiding a class of bugs where the host sees an RPC result before the element updates that produced it (for example, a perform() callback setting an error on a field, only to have the host run its post-await logic before that error mutation arrives).

    If you were relying on the previous macrotask behavior, you can restore it by passing a custom batch option, e.g.:

    new BatchingRemoteConnection(connection, {
      batch: (flush) => {
        const channel = new MessageChannel();
        channel.port1.onmessage = () => flush();
        channel.port2.postMessage(null);
      },
    });

@remote-dom/polyfill@1.5.1

10 Oct 14:31
c4e950b

Choose a tag to compare

Patch Changes

@remote-dom/core@1.10.1

10 Oct 14:31
c4e950b

Choose a tag to compare

Patch Changes

@remote-dom/core@1.10.0

06 Oct 16:00
877d99e

Choose a tag to compare

Minor Changes

@remote-dom/polyfill@1.5.0

29 Aug 14:52
834c9d2

Choose a tag to compare

Minor Changes

@remote-dom/polyfill@1.4.7

29 Aug 13:50
aa00798

Choose a tag to compare

Patch Changes

@remote-dom/polyfill@1.4.6

25 Aug 09:30
85e0e45

Choose a tag to compare

Patch Changes

@remote-dom/core@1.9.0

04 Jul 19:03
e16a89c

Choose a tag to compare

Minor Changes

  • #583 a7be991 Thanks @lemonmade! - Improved RemoteMutationObserver to support automatic emptying and observing multiple nodes

    If you are observing a multi-node list — such as a DocumentFragment or <template> element — you can now provide a custom id option when observing each node. This allows you to treat the observer as a kind of "virtual root" for a list of nodes, similar to the role the DocumentFragment plays in the DOM. You are responsible for giving each node a unique ID, and this class will take care of correctly attaching that node to the root of the remote tree.

    const observer = new RemoteMutationObserver(connection);
    
    let id = 0;
    for (const child of documentFragment.childNodes) {
      observer.observe(child, {
        id: `DocumentFragment:${id++}`,
      });
    }

    You can also now provide an empty option to RemoteMutationObserver.disconnect() in order to clear out children in remote environment:

    const observer = new RemoteMutationObserver(connection);
    
    observer.observe(container);
    
    observer.disconnect({empty: true});
  • #583 a7be991 Thanks @lemonmade! - Expose remoteId() and setRemoteId() for getting and setting a Node's remote ID