Releases: lynx-family/preact-devtools
Release list
Release 5.0.1-20260827072047-5f77e12
Release 5.0.1-20260827072047-5f77e12
Commit: 5f77e12
Published: Thu Aug 27 07:21:04 UTC 2026
Commit Message
fix(react-lynx): acknowledge correlated refresh (#18)
Emit a request-scoped terminal event after all renderer refresh work so hosts can distinguish a complete snapshot from a quiet transport.
Assisted-by: Codex
Release 5.0.1-20260821134614-21e65a4
Release 5.0.1-20260821134614-21e65a4
Commit: 21e65a4
Published: Fri Aug 21 13:46:31 UTC 2026
Commit Message
fix(react-lynx): acknowledge correlated refresh (#18)
Emit a request-scoped terminal event after all renderer refresh work so hosts can distinguish a complete snapshot from a quiet transport.
Assisted-by: Codex
Release 5.0.1-20260821133753-7eae581
Release 5.0.1-20260821133753-7eae581
Commit: 7eae581
Published: Fri Aug 21 13:38:08 UTC 2026
Commit Message
Fix Preact 11 support (preactjs#522) (#20)
-
fix: detect context providers without private fields
-
fix: support current Preact 11 internals
-
fix: isolate Preact 11 portal bindings
-
ci: forward retries to Playwright
-
fix: exclude experimental Preact 11 renderer
-
test: cover Preact 11 Suspense
Co-authored-by: Jovi De Croock decroockjovi@gmail.com
Release 5.0.1-20260721114100-d7da994
Release 5.0.1-20260716151326-abdb87c
Release 5.0.1-20260716151326-abdb87c
Commit: abdb87c
Published: Thu Jul 16 15:13:41 UTC 2026
Commit Message
feat(react-lynx): support the web platform via BroadcastChannel transport (#16)
- feat(react-lynx): support web platform via BroadcastChannel transport
On the web platform there is no lynx.getDevtool(); fall back to a
BroadcastChannel('preact-devtools') that a page-side bridge can relay to
the Preact Devtools browser extension.
Also survive async modules: on web, ./setup (and installHook /
preact/devtools) become async modules because @lynx-js/react/internal and
preact are async externals there, so a CJS require() returns a Promise of
the namespace. Await it instead of crashing the whole background chunk
('setupReactLynx is not a function'), and chain installHook before
preact/devtools to keep hook-install order.
-
docs: document the web platform transport and extension bridge
-
address review: demo web target, async-module comment, transport error, no-console
- demo: build a web platform bundle (environments.web) and add a browser
host (npm run dev:web) with the extension bridge preinstalled - index.ts: attribute the async-module behavior to async external bundles
(web is just the always-async case), not the web platform itself - setup.ts: report both missing transports in the setup error instead of
only lynx.getDevtool - oxlint: allow console in src/react-lynx (user-facing diagnostics)
-
demo: nudge the devtools client so a late-attaching extension connects
-
demo: keep devtools in prod builds
react-alias-rsbuild-plugin 0.17.0 unconditionally aliases
@lynx-js/preact-devtools to false in production, so the built demo
bundle silently lost the devtools client. Bump react-rsbuild-plugin to
0.17.2 (whose react-alias supports the REACT_DEVTOOL opt-out) and bake
REACT_DEVTOOL=1 into the demo build/dev scripts — keeping devtools in
the bundle is the whole point of this demo.
- fix: skip unique-id mapping on hosts without the lepus debug method
The web platform's main thread does not implement
getUniqueIdListBySnapshotId, so the per-vnode callLepusMethod spammed
'Method not found on mainThreadGlobalThis' on every create/update.
Gate the Elements/screencast id mapping behind a new
preactDevtoolsCtx.supportsUniqueIdMapping capability (true on native
Lynx, false on the web transport, overridable by the host).
- demo: align @lynx-js/react with the runtime line the adapter targets
The store-extension panel rendered an empty tree against the demo's
pinned react 0.121.2 while the identical client/bridge produced a
working tree on 0.122/0.123 apps; bump the demo to 0.122.1.
- fix: survive host-selection messages on hosts without a ctx hook
Chrome fires elements-selection changes as soon as devtools opens, so the
panel sends load-host-selection immediately. On ReactLynx the hook lives
on globalThis of the background worker, not on the devtools ctx, so the
handler crashed with 'Cannot read properties of undefined (reading $0)'
before the tree could be announced. Guard the handlers that dereference
the ctx hook ($0 selection mapping is host-DOM-only anyway) and isolate
listener failures in the transport dispatch so one faulty handler can no
longer break devtools message delivery.
- fix: attach synchronously when modules are sync, and only instrument a preact instance once
Two defects kept the devtools tree empty on the web platform:
-
The installHook/preact-devtools requires were unconditionally chained
through Promise.resolve, deferring attachPreact to a microtask. With
synchronous modules the app's first root.render() ran before the
options hooks were wrapped, and on a static app that first commit is
the only one - roots stayed empty and no operation_v2 was ever
emitted. Attach synchronously unless the modules are actually async. -
attachPreact registered a new renderer on every call. HMR update
loops re-evaluate the devtools init, ballooning the renderers map
(observed: ~19k renderers, turning one refresh into a 56k-message
storm). Deduplicate per preact options object via a WeakMap.
Also build the demo bundles unminified for readability.
- Revert "demo: nudge the devtools client so a late-attaching extension connects"
The nudge papered over an upstream extension defect: the content script
does not reconnect after the MV3 background service worker is suspended,
so a fully static page deadlocks until any client message arrives. That
belongs in preactjs/preact-devtools (content-script auto-reconnect), not
in every hosting page. With the synchronous-attach fix the boot messages
connect the content script reliably, so the demo no longer needs it.
- fix: allow scoping the web channel per view via globalProps
BroadcastChannel broadcasts across the whole origin, so two tabs (or two
lynx-views) debugged at once cross-talked on the fixed channel name: each
panel received both clients' trees and every panel refresh fanned out to
every client, duplicating roots until the browser hung. The client now
honors globalProps.preactDevtoolsChannel (default unchanged), and the
demo host scopes the channel per page through lynx-view global-props.
- feat: add a web-host bridge entry for the browser extension
importing @lynx-js/preact-devtools/web-host once in the hosting page
discovers every lynx-view and relays its devtools protocol to the
official Preact Devtools browser extension — replacing the hand-written
per-page bridge. Prefers the per-card devtool MessagePort exposed by
@lynx-js/web-core (lynx-family/lynx-stack#2986) and falls back to the
client's BroadcastChannel transport (honoring per-view channel scoping
via globalProps) on older web-core versions.
- demo: bump to the released reactlynx/rspeedy line
@lynx-js/react 0.123.0, @lynx-js/rspeedy 0.16.0,
@lynx-js/react-rsbuild-plugin 0.18.0, @lynx-js/qrcode-rsbuild-plugin
0.6.0. Also document why the web environment keeps its own
output.minify override: the top-level one does not reach the web
bundle (verified on rspeedy 0.15 and 0.16).
-
fix: ship a type companion for the web-host subpath entry
-
fix: add an exports map so the web-host subpath resolves under node16
moduleResolution node16 rejects extensionless subpaths on packages
without an exports map, so 'import "@lynx-js/preact-devtools/web-host"'
did not type-resolve. Map '.', './web-host' (with side-effect-only type
companions) and keep './lib/' / './dist/' passthroughs. Also pin the
demo tsconfig to module node16 to match its TypeScript version.
- refactor: emit declarations into lib and point exports there
Turn on declaration output for build:lib so the exports map can
reference generated lib/react-lynx/{index,web-host}.d.ts instead of
hand-written root stubs; drop the root web-host.js shim as well, since
the exports map makes it unnecessary.
-
style: strip narrative comments, keep one-line constraints only
-
refactor(web-host): bridge over BroadcastChannel only
Drop the lynxView.devtoolMessagePort path — it assumed an unmerged
web-core API (lynx-family/lynx-stack#2986). The bridge now joins the
client's BroadcastChannel directly (per-view scoping via global-props
still honored), which also removes the port-polling delay.
-
demo: use crypto.randomUUID for the channel suffix
-
demo: gate minify-off behind NO_MINIFY env
-
demo: restore module node18 and bump typescript to a version that supports it
-
demo: align typescript with the repo root (6.x)
-
revert unnecessary guard in the inspect handler
globalThis.PREACT_DEVTOOLS is assigned before the adapter's
listeners can ever run (adapter init is deferred to attach), so the
guard protected an impossible state. The real defect was the ctx-hook
reads in load-host-selection/view-source, which keep their guards.
- fix: mirror the hook onto the devtools ctx instead of guarding reads
The adapter's 'window' is the lynx.preactDevtoolsCtx shim, but the hook
was only assigned to globalThis (where preact/devtools discovers it), so
window.PREACT_DEVTOOLS reads crashed. Complete the shim by mirroring
the hook onto the ctx and drop the per-read guards — adapter.ts is back
to upstream verbatim.
- fix: only mount the hook on globalThis while preact/devtools initializes
globalThis is shared across cards in Lynx's shared-context mode, so the
hook must not live there durably. It now lives on the per-card devtools
ctx; right before require('preact/devtools') it is mirrored onto
globalThis so initDevTools can discover it, and removed as soon as
initialization finishes. The inspect handler reads the hook through the
ctx window like its sibling handlers.
- revert unique-id mapping guard, let unsupported hosts error
Restores idMapper.ts to upstream: an explicit error from
getUniqueIdListBySnapshotId on hosts that lack it is more useful than
silently skipping the vnode-to-native-UI mapping.
- feat: cleanup code
Release 5.0.1-20260715085604-04751c4
Release 5.0.1-20260714130028-3bd2ab8
Release 5.0.1-20260714130028-3bd2ab8
Commit: 3bd2ab8
Published: Tue Jul 14 13:00:44 UTC 2026
Commit Message
refactor: store devtools context on lynx instead of globalThis (#14)
- refactor: store devtools context on lynx instead of globalThis
The background JS context is reused across page navigations, so a
context hung off globalThis persisted and the next page reused the
previous page's stale context. Hang the devtools context off the
per-page lynx object instead, so every page starts fresh.
All preactDevtoolsCtx references become lynx.preactDevtoolsCtx, the
global type declaration is replaced by an augmentation of the lynx
Lynx interface, and both unit-test setups attach the context to
lynx (on both emulated threads in the ReactLynx env, since thread
switching swaps lynx).
- test(e2e): provide lynx.preactDevtoolsCtx in the browser polyfill
The e2e harness fakes the Lynx env in the browser. After moving the
context onto lynx, installHook -> createHook reads lynx.preactDevtoolsCtx,
so the polyfill must set it there instead of window.preactDevtoolsCtx.
Release 5.0.1-20260714085124-343a84d
Release 5.0.1-20260624133547-6641319
Release 5.0.1-20260624133547-6641319
Commit: 6641319
Published: Wed Jun 24 13:36:00 UTC 2026
Commit Message
chore: upgrade the devtools' Lynx dev dependencies to latest (#10)
- @lynx-js/react 0.121.0 -> 0.121.2
- @lynx-js/types ^3.3.0 -> ^4.0.0
- @lynx-js/testing-environment 0.1.5 -> 0.3.0
Adapt the lynx test setup to @lynx-js/testing-environment >=0.2, which takes
the host window via the constructor instead of reading global.jsdom.
type-check, vitest, and vitest:lynx all pass.
Release 5.0.1-20260624133506-d97520d
Release 5.0.1-20260624133506-d97520d
Commit: d97520d
Published: Wed Jun 24 13:35:19 UTC 2026
Commit Message
chore(demo): upgrade Lynx toolchain (#9)
Bring the demo's Lynx dependencies up to current:
- @lynx-js/rspeedy 0.10.6 -> 0.15.0
- @lynx-js/react-rsbuild-plugin 0.10.11 -> 0.17.0
- @lynx-js/react 0.112.2 -> 0.121.2
- @lynx-js/qrcode-rsbuild-plugin 0.4.0 -> 0.5.0
- @lynx-js/types 3.3.0 -> 3.7.0