Skip to content

Leak — pointercancel listener not removed on connection-drag end #20

@ContractorHUB-dev

Description

@ContractorHUB-dev

Version: getartisanflow/wireflow ^0.1.2-alpha (bundle file: dist/alpineflow.bundle.esm.js)

What's happening

When the user drags from a source handle to draw an edge, three document-level pointer listeners are registered (pointermove, pointerup, pointercancel). The normal-drop cleanup handler Q removes pointermove and pointerup, but not pointercancel. Each subsequent edge-drag adds a fresh triplet on top, leaving an orphan pointercancel listener accumulating on document after each drop.

Code

In vendor/getartisanflow/wireflow/dist/alpineflow.bundle.esm.js around line 3789, the connection-drag pointerup handler:

```js
}, Q = (Y) => {
if (x?.stop(), x = null,
document.removeEventListener("pointermove", R),
document.removeEventListener("pointerup", Q),
// ❌ missing: document.removeEventListener("pointercancel", Q)
L = null, G) {
```

Compare to the full-cleanup function L defined ~20 lines below (line ~3911), which correctly removes all three:

```js
L = () => {
document.removeEventListener("pointermove", R),
document.removeEventListener("pointerup", Q),
document.removeEventListener("pointercancel", Q),
...
}
```

And to the node-drag handler ie near line 4090, which also correctly removes all three on pointerup. So the omission appears to be an oversight specific to the edge-drag handler.

Proposed fix

One line — add pointercancel removal to Q:

```diff
}, Q = (Y) => {
if (x?.stop(), x = null,
document.removeEventListener("pointermove", R),
document.removeEventListener("pointerup", Q),

  •   document.removeEventListener(\"pointercancel\", Q),
      L = null, G) {
    

```

Reproduction

  1. Open any wireflow canvas with the connection feature enabled.
  2. Drag from a node's source handle to a target handle and drop, repeatedly (10+ times).
  3. In Chrome DevTools console: getEventListeners(document).
  4. You'll see an increasing number of pointercancel entries — one per successful edge drag — none from prior drags removed.

Impact

  • Memory leak: each orphan listener holds the closure (including refs to _, se, etc.) so they accumulate over a session.
  • If a real pointercancel ever fires later (touch interrupted, OS gesture, modal stealing focus), every accumulated handler runs — most internal state is idempotent via optional chaining, but _._emit('connect-end', { connection: null, ... }) would fire N times in a row.

For typical mouse-driven usage on desktop this isn't catastrophic (drag counts per session are low and pointercancel rarely fires), but on touch devices where pointercancel happens more often, the duplicate connect-end events could be observable downstream.

Environment

  • Laravel 12.42.0 / Livewire 3.7.1
  • WireFlow integrated via the docs' static-mode pattern + render-hook plugin registration for Filament panels
  • Reproduced on Chrome 131 / macOS

Happy to PR the one-line fix if the source repo is open and you'd prefer that path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions