npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@perf-labs/appium-driver

v0.1.0-alpha.11

Published

PerfLab Appium driver: write a journey module, run it on BrowserStack with `perflab farm run --driver appium --journey-module`.

Readme

@perf-labs/appium-driver

PerfLab's built-in Appium driver for perflab farm run --driver appium --journey-module <file> (BrowserStack App Automate, iOS and Android; --platform ios|android, default ios). A thin step vocabulary over W3C WebDriver: you write the journey, the driver owns the session, run tokens, step markers, the readiness wait (expectNext) after a touch and the result files. Design: docs/plans/54b-appium-driver.md.

A journey module default-exports defineJourney({ app, home, options?, setup?, journeys }); see the plan for the full shape. A journey needs no import from this package: selectors are plain objects ({ id }, { text }, { ...sel, childOf }, or a bare string) and everything else is on the t it is given (t.acceptAlert, t.openLink, ...). defineJourney is only an identity helper for editor types. The CLI runs its own bundled copy, so do not instanceof driver errors (WdError) from your own copy; test e.wdError.

Run directly (what farm run does): node dist/src/cli.js --journey-module j.mjs --app bs://… --tokens-js tokens.js --journeys a,b --repeat 3 --out DIR [--local-identifier ID], with BROWSERSTACK_USERNAME / BROWSERSTACK_ACCESS_KEY. Exit 0, or 1 when the session or an iteration failed (the result files are written either way).

Step checks and Android

t.waitAll([{ shown: sel }, { gone: sel }, { keyboard: 'up' }, ...]) waits until all conditions hold in one poll (the poll asks them one lookup after another). On Android the ids are asked in one UiAutomator2 lookup (a walk of the whole tree costs the same for one id and for ten); elsewhere each condition is waited for in turn. The keyboard condition is answered by the platform (Android mobile: isKeyboardShown, iOS the Return key), so a journey needs no platform branch. t.swipeFromUntilVisible(from, direction, target, times) swipes until a target shows.

Android specifics (UiAutomator2, measured on a Galaxy S23): a lookup that finds nothing walks the whole tree (about 1.2 s), one that finds a shown element takes 0.1 to 0.2 s, so findShown asks for one element first. ignoreUnimportantViews is on (about 30 % faster lookups).

Inverted bounds: when the keyboard opens, TapIn translates the chat up while Android still cuts the chat nodes by the container's untranslated rect. The chat's scroll container and its nodes come back with top below bottom and displayed=false, and are left out of lookups. While the keyboard is up (and at most every 3 s) waitAll reads the page source with allowInvisibleElements and counts such a node as shown when it sits in an inverted scroll container, its real bottom edge is on screen and below the edge the clipped-away nodes pile up on (at least two clipped messages are needed to find the edge). See src/snapshot.ts.

How to write journeys that run fast on Android, with measured costs: docs/writing-fast-journeys.md.

GC before the action (--action-gc on|off, default off)

With --action-gc on the driver asks the app for a full garbage collection before every declared step, after the step's readiness wait and before its start marker, and once more before the run's flush. Design: docs/plans/gc-before-action.md; device checks: docs/plans/gc-before-action-measurement.md.

  • Each command has a number n, unique in the session. Android sends it as a broadcast (mobile: broadcast, action <bundleId>.perflab.COMMAND, extras cmd=gc, n) and reads the answer gc:<n> ... from the broadcast's result. iOS opens <scheme>devtools/perf-lab?cmd=gc&n=<n> and waits up to 10 s for the element perflab.ack whose label starts with gc:<n> .
  • The step's start marker carries gc_n, so the backend (derivation 18) pairs the step with its GC record without mapping clocks.
  • No answer turns the command off for the rest of the session (actionGc: unsupported in driver-log.json); the test still passes. Safari link mode sends none (safari).
  • The GC command ends the app's open windows: runs with and without it are a different instrument (actionGc aspect), never compared as an app change.

GC at the run end (--end-gc on|off, default off)

One GC command at the end of every run, after the journey's last step and its readiness wait, before the markers and the flush, also when --action-gc is off. Its record gives the end reading after a GC (derivation 19: mem.end_after_gc_mb, mem.retained_growth_mb, mem.js_heap_end_mb; setup fact sdk.end_gc). End the journey on its home screen (docs/writing-fast-journeys.md), so the reading holds what the journey kept, not an open heavy screen; sdk.journey_ends_home = 0 flags a run that did not.

  • Same command and fallbacks as above; an app that does not answer turns it off for the rest of the session.
  • The command ends the last open window (end reason gc-command) instead of the flush: that one window per run is shorter than on a driver without it. Driver 0.2.0 carries both commands, so runs before and after it are a different instrument already (driverVersion aspect).
  • Off by default: with an SDK that answers it, the run sends a PERF_LAB gc line, which the production API before feat/gc-before-action rejects (400 bad_item, the batch is lost). Turn it on (--end-gc on, endGc: true) only after the migration and the API are deployed. Older SDKs do not answer and send no line.