Repository navigation
Releases: feder-cr/invisible_puppeteer
Release list
v0.11.0
The pin moves to invisible-core 50.48.0, on the same firefox-50 engine.
Changed
- The user-install job runs on the ledger pull request, not on
release: published, which never fired: the Release is created with
GITHUB_TOKEN, and an event that token causes starts no other workflow.
CI checks the wiring withpython -m invisible_core.install_job. - The install test keeps out of the venv it builds every variable the core's
library reads, asinvisible_core.install_job.LEAKSlists them: an engine
folder, a seal file or a GitHub token in the caller's shell no longer
changes what it measures. - The publish workflow checks the version and the ledger against the index
before it uploads (invisible_core.release check --verify-index), as the
pre-push hook does: a clone without the hook can no longer skip it.
v0.10.0
The pin moves to invisible-core 50.47.0, on the firefox-50 engine. From
firefox-50 every realm, worker and worklet takes the session's zone from the
engine's own override, so the launch no longer writes TZ into the browser's
environment. On Windows no TZ could satisfy both the C runtime and ICU, so
some zones showed the machine's time inside a Worker, and zones outside a
ten-row table were never covered; that is gone with it.
Changed
- The IANA-to-POSIX conversion is no longer imported or used. Code that took
IANA_TO_POSIX_TZfrom the core has to stop: the core 50.47.0 removes it.
v0.9.0
The pin moves to invisible-core 49.46.0. The desktop settings a page can read -
scrollbars, text scale, drag threshold, double-click selection, the system
colours in both schemes, the refresh rate, the form-control font and more -
are declared by the persona instead of read from the machine it runs on, and
each of them can be pinned (the keys are the ones invisible_playwright documents in its pinning page).
Changed
- The way in is uv. The README installs with one complete block per
system: uv from its own installer,uv init --python ">=3.11" my-project,
uv add invisible-puppeteer,uv run invisible-puppeteer fetch. CONTRIBUTING teaches the same,
and CI refuses an install instruction that is not uv
(python -m invisible_core.way_in, core 49.45.0). - A remedy this package prints is the command for your environment. In a
venv uv made, which has no pip, a broken install was told to run
pip install --force-reinstall invisible-puppeteer; it is now told the uv command, and
an environment pip made is still told pip, through its own interpreter.
Fixed
- CONTRIBUTING.md was a copy of invisible_playwright's: it cloned that
repository and described its API. It now describes this one. - Nothing ran the README's install block. A user-install workflow now runs it
as written, on Linux and Windows, and starts the browser from the project
it makes.
v0.8.0
The pin moves to invisible-core 49.44.0, on the firefox-49 engine.
Changed
- Your code runs in a world of its own, where a page that replaced a
standard API cannot count your calls.evaluate,evaluateHandle,
waitForFunction,$evalandsetContentran in the page's own
realm, so a call todocument.querySelectorwent through whatever
the page had put in its place. They now run in a world with the
page's principal where a name the standard defines is the native one,
and every other name is the page's.
On what the page created (its classes, their instances, its plain objects)
the page's definitions win,toStringandvalueOfincluded, and an
object your code hands to the page keeps its identity and looks to the
page like its own (instanceof Arrayholds, itsconstructoris the
page's). What changes, and it is declared rather than accidental: a name
the standard defines for one of the browser's own objects wins even where
the page redefined it on that object itself (a setter the page put on one
input'svaluedoes not run;.wrappedJSObjectgives the page's own
view); a bare assignment to an undeclared name creates a global of your
world, not of the page (window.x = 1still reaches the page); and your
code's ownevalis not governed by the page's Content-Security-Policy,
which governs the page's (window.eval) as before. Needs firefox-49.
v0.7.2
The pin moves to invisible-core 48.43.0, still on the firefox-48 engine.
Fixed
- The e2e tests that measure time run alone. Nine of them assert an upper
bound on a wall-clock duration ("refused at once", "returns before the page
loads"), a bound that four browsers at once can cross with nothing wrong in
the engine. They carry the newwall_clockmarker, and the core's e2e run
gives them the machine alone after the parallel tests.
v0.7.1
The pin moves to invisible-core 48.42.0, on the same firefox-48 engine.
Fixed
waitForFunctionno longer ends onNaN. The predicate's result was
judged after conversion to Python, where NaN is true; it is now judged as
JavaScript judges it, with the same rule as invisible-playwright's
wait_for_function.- A GeoIP database being downloaded is never read half-written. Two
sessions starting together on a cold cache could have one open the database
while the other was still writing it, and fail with "Is this a valid MaxMind
DB file?". The core now extracts it outside the cache, checks it with the
real reader and moves it into place in one step (invisible-core 48.42.0).
v0.7.0
The pin moves to invisible-core 48.41.0, on the firefox-48 engine.
Added
- The branding a page reads is a profile field.
pin={"branding": ...}
takes"official"(the default, the retail Firefox's files),"unofficial",
or the absolute path of a zip archive of your own with the same 16 files at
its root. The engine serves the declared files atchrome://branding/content/
andresource://usercontext-content/branding/instead of its own, from inside
the archive as Firefox serves its own branding. Needs firefox-48.
Changed
- The speechSynthesis voices follow the session's language. An it-IT session
lists the voices of a Windows with Italian added instead of only the American
ones; a language without a measured set keeps the English one, and a
hardware.voicespin still wins.
Fixed
- A persistent profile stays the same machine across launches. With
profile_dirand noseed, every launch used to draw a new seed, so the same
profile came back with another GPU, screen and fonts. The first launch now
records its seed in the profile and later ones reuse it; an explicitseed
that differs from the recorded one is refused.
v0.6.4
The pin moves to invisible-core 47.41.0, on the same firefox-47 engine.
Added
- The engine can live on a read-only disk.
INVISIBLE_PLAYWRIGHT_ENGINE_DIR
puts the sealed engine trees somewhere of their own, written only by a
download;INVISIBLE_PLAYWRIGHT_CACHE_DIRkeeps the machine's writable state
(the GeoIP database, the font manifest). Unset, nothing moves. A launch on an
engine already fetched by the same core writes nothing into the store.
Changed
timezone="auto"that cannot be resolved now fails the launch without a
proxy too. It used to fall back to the host's timezone in silence when the
exit could not be discovered or the GeoIP database could not be obtained,
which on a server kept in UTC gives a timezone that contradicts its own IP.
The error names the cause; pass an explicit IANA zone to launch offline.
v0.6.3
The pin moves to invisible-core 47.40.0, on the same firefox-47 engine.
Fixed
- Closing a browser no longer closes the pipe of another browser in the
same process. The connection closed its two descriptor numbers on
every call, and a released number goes to the next pipe the process
opens: a second close, or a write racing the close, could reach another
browser launched by the same script. The connection now releases each
end once and never touches the number again.
v0.6.2
The pin moves to invisible-core 47.39.0, which seals the firefox-47 engine.
Fixed
- A page can no longer tell that the engine carries the automation protocol.
Every engine until firefox-47 registered the protocol as a chrome package named
juggler, and a page tells a registered package from an absent one with one
line,new Worker("chrome://juggler/..."): it returns on our engine and throws
SecurityErroron Firefox. Captcha vendors started probing it in October 2026,
which is why a challenge solved by hand was still refused. firefox-47 loads the
protocol fromresource://gre/modules/juggler/, a namespace every Firefox has
and inside which a page cannot tell which files exist; the engine now registers
exactly the chrome packages and resource substitutions Firefox 151 does. - A
gotothe page stops on its way no longer answers on the old page, nor
waits out its timeout. Stopped with nothing in its place
(document.open(),window.stop()), it raises PageError
"NS_BINDING_ABORTED at " at once; replaced by another document, it
waits on that document, as pyppeteer's NavigatorWatcher does, and answers
with its response, so the answer always names the page it is on. - The wheel is born before the page sees it, as keys and mouse moves are
since 0.35.7:event.timeStampwas the instant the wheel reached the page,
0 to 2 ms belowperformance.now()in the handler where a real wheel is 6
to 14, and half of the wheel events failed the filter a tracker applies.
Not yet as the original
- Firefox lets the page being left stop a navigation the browser started
(document.open, window.stop, location.replace, a form submit, an iframe
navigating the top); pyppeteer's Chromium lets the browser's navigation win
over all of them but going back, so theregotolands on the page asked
for. The engines differ, and this client answers with Firefox's events.