fix(wire): extract Livewire bridge to /wire subpath addon - #81
Open
zachiler wants to merge 11 commits into
Open
Conversation
…tegration test registerWireEvents wrote its on<Event> wrappers to canvas._config, but core's _emit reads callbacks from the closure config — a different object (the stripped copy) — so every wireEvents client→server forward was silently dead. Add _liveConfig() to core returning the live closure config (not stored as reactive data, so it stays collab-safe) and have the wire addon target it. This restores the pre-extraction behavior, where the in-core bridge mutated the closure config directly. Add a real-canvas integration test (mounts flowCanvas + wire addon + $wire) that asserts a node-click reaches $wire; the prior fakeCanvas unit tests could not catch the config-object mismatch. Also fix a stale [wire-bridge] log string and document the init-ordering and selection round-trip edge cases in code.
Remove lib/wire.cdn.ts and the "default": alpineflow-wire.js export condition. Nothing built that .js and no consumer used it: WireFlow loads addons via the .esm.js (import) condition from its vendored dist, never the CDN/require fallback. Leaves ./wire as types + import, matching how the structured addons are actually consumed. CDN support for the structured addons (wire/schema/workflow) is a deliberate follow-up, tracked separately — not dropped as an idea, just deferred until after the v0.3.0-alpha extraction ships and is verified.
When core is too old to expose _liveConfig(), the addon previously fell back to canvas._config — the stripped copy _emit does NOT read — silently reintroducing the dead client→server forwarding bug. Because WireFlow vendors AlpineFlow's bundle as a separate artifact, this addon can genuinely meet an older core, so the failure must be loud. Now: warn and skip event forwarding (server→client commands still register); never mutate the copy.
selectNodes/selectEdges called deselectAll() (which emits) then emitted again, so a single call could fire up to two selection-change events, and re-selecting the same ids still emitted. Route both (and deselectAll) through a shared guard that clears silently and emits once, only when the resulting selection differs from the current one. Beyond removing the redundant/no-op emits, this defuses the wire round-trip: a server-driven flow:selectNodes that maps selection-change in wireEvents now forwards the result once, and re-issuing identical ids does not re-emit — so an idempotent server→client→server path cannot loop.
…tion API Document the breaking move of the Livewire bridge from core into the /wire addon (no longer in the vendored bundle; wireEvents type relocated) and the new public selectNodes/selectEdges/setNodeLocked/setNodeHidden methods with their emit-on-actual-change semantics.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wire-bridge extraction (v0.3.0-alpha)
Moves the Livewire integration out of core into a self-contained
/wiresubpath addon, so the engine stays framework-agnostic and the downstream bridge can evolve on its own. Part of the v0.3.0-alpha decoupling chapter.What changed
AlpineFlowWireaddon (src/wire/index.ts) — registered viaAlpine.plugin(AlpineFlowWire), activates throughsetup(canvas)only when a Livewire$wireproxy is present. Mapsflow:*server→client commands to canvas methods and AlpineFlow events to$wiremethod calls.src/core/wire-bridge.ts→src/wire/bridge.ts(core no longer imports it)./wiresubpath is ESM-only:exportsentry istypes+import(alpineflow-wire.esm.js). The deaddefault/CDN target andlib/wire.cdn.tswere dropped — WireFlow consumes the.esm.js, never the CDN/requirevariant. (CDN/no-build support for the structured addons is a deliberate follow-up, not a no.)selectNodes/selectEdges/setNodeLocked/setNodeHidden(also declared onCanvasContext). The bridge's lock/hide/select composites route through these public methods instead of reaching into internals (TDD'd).setup()may return a cleanup fn, now invoked on canvas destroy.wireEventsremoved from coreFlowCanvasConfig; re-declared via module augmentation inside the addon so consumers keep the type. Runtime-additive.Correctness fixes found in review
on<Event>wrappers tocanvas._config, but core's_emitreads callbacks from the closureconfig(a different object —_configis a one-time stripped copy). Fix: added_liveConfig()to core returning the live closure config (not stored as reactive data, so it stays collab-safe); the addon targets it. Restores the exact pre-extraction behavior._liveConfig()(realistic — WireFlow vendors the bundle as a separate artifact), it nowconsole.warns and skips forwarding rather than silently writing the stale copy and re-introducing the dead-forward bug.selectNodes/selectEdgesnow emitselection-changeonly when the selection truly changes, at most once per call. Client listeners (and the server, ifselection-changeis mapped inwireEvents) observe server-driven selections, but re-issuing the same ids is a no-op that does not re-emit — so an idempotent server→client→server round-trip cannot loop. Also collapses the old clear-then-set double emit and skips unknown ids.Known limitation (documented, not fixed)
wireEvents:{init:…}does not forward — core emitsinitbefore_initAddons()runs the wiresetup(). Reordering core init is riskier than the edge case warrants (other addons rely on_container/ResizeObserver being ready first). Documented in code.Tests
wire-events-integration.test.ts) mountsflowCanvas+ the wire addon + a$wireand asserts anode-clickreaches$wire(fails before the_liveConfigfix, passes after). Plus tests for the version-skew warn path and the selection-change guard.tsc --noEmitclean.Not in this PR
The WireFlow-side migration (vendor
alpineflow-wire.esm.js+ default-load it, since the bridge is baseline for WireFlow, not opt-in) happens in the wireflow repo at resync.