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

stoobly

v0.7.0

Published

Javacript library for stoobly-agent

Readme

Stoobly Node.js library

Node.js Version npm version CI TypeScript Cypress Playwright License Docs

The Stoobly Javascript library provides convenient access to stoobly-agent API.

Requirements

Node 18 or higher.

Installation

Install the package with:

npm install stoobly --save-dev

Usage

const Stoobly = require('stoobly');

Or using ES modules:

import Stoobly from 'stoobly';

Examples

Setting a scenario

Configures requests with origin https://docs.stoobly.com to specify a scenario. sessionId defaults to current time.

import Stoobly from 'stoobly';

const stoobly = new Stoobly();
const interceptor = stoobly.interceptor({
    scenarioKey: '<SCENARIO-KEY>',
    scenarioName: '<SCENARIO-NAME>', // If scenario name is used instead of key, it should be unique
    urls: [{ pattern: new RegExp('https://docs.stoobly.com/.*') }]
});

const sessionId = interceptor.apply();

Configures requests with origin https://docs.stoobly.com to specify a scenario. After a session has started, change sessions with withSessionId().

import Stoobly from 'stoobly';

const stoobly = new Stoobly();
const interceptor = stoobly.interceptor({
    scenarioKey: '<SCENARIO-KEY>',
    sessionId: '<SESSION-ID>',
    urls: [{ pattern: new RegExp('https://docs.stoobly.com/.*') }]
});

const sessionId = interceptor.apply();
interceptor.withSessionId('<NEW-SESSION-ID>');

Configures requests https://docs.stoobly.com/use-cases and https://docs.stoobly.com/getting-started to specify a scenario.

import Stoobly from 'stoobly';

const stoobly = new Stoobly();
const interceptor = stoobly.interceptor({
    scenarioKey: '<SCENARIO-KEY>',
    urls: [
        { pattern: 'https://docs.stoobly.com/use-cases' },
        { pattern: 'https://docs.stoobly.com/getting-started' }
    ]
});

interceptor.apply();

Recording requests

Record requests with specific policy, order, and strategy options:

import Stoobly from 'stoobly';
import {
  RecordPolicy,
  RecordOrder,
  RecordStrategy,

  // Additional constants available:
  //
  // InterceptMode,   // mock, record, replay
  // MockPolicy,      // All, Found
  // ReplayPolicy,    // All
  // TestPolicy,      // All, Found
  // TestStrategy,    // Diff, Fuzzy, Custom
  // FirewallAction,  // Exclude, Include
  // RequestParameter // Header, BodyParam, QueryParam
} from 'stoobly/constants';

const stoobly = new Stoobly();
const interceptor = stoobly.interceptor({
    urls: [{ pattern: 'https://docs.stoobly.com/use-cases' }],
    record: {
        policy: RecordPolicy.All,
        order: RecordOrder.Overwrite, // Defaults to RecordOrder.Append
        strategy: RecordStrategy.Full,
    }
});

interceptor.applyRecord();

Stop recording requests:

interceptor.clearRecord();

Stop all interception (recording, mocking, etc.):

interceptor.clear();

Test Framework Integration

  • Using Cypress? See Integrating with Cypress and use cypressInterceptor()
  • Using Playwright? See Integrating with Playwright and use playwrightInterceptor()
  • Not using a test framework? The examples above use interceptor(), which patches fetch and XMLHttpRequest directly

Integrating with Cypress

Set the Stoobly interception mode in your cypress.config.*:

import { defineConfig } from 'cypress'

export default defineConfig({
  e2e: {
    env: {
      STOOBLY_INTERCEPT_MODE: process.env.STOOBLY_INTERCEPT_MODE, // 'mock' | 'record' | 'replay'
    },
  },
})
import Stoobly from 'stoobly';
import { RecordPolicy, RecordOrder, RecordStrategy } from 'stoobly/constants';

const stoobly = new Stoobly();
const stooblyInterceptor = () => stoobly.cypressInterceptor({
    mode: Cypress.env('STOOBLY_INTERCEPT_MODE'),
    record: {
        policy: RecordPolicy.All,
        order: RecordOrder.Overwrite, // Defaults to RecordOrder.Append
        strategy: RecordStrategy.Full,
    },
    urls: [{ pattern: '<URLS>' }],
});

describe('Scenario', () => {
    beforeEach(() => {
        // WARNING: if a synchronous request is used, this will cause Cypress to hang. See: https://github.com/cypress-io/cypress/issues/29566
        // Example of a synchronous request: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Synchronous_and_Asynchronous_Requests#synchronous_request

        const interceptor = stooblyInterceptor();
        interceptor.withScenarioNameFromTest().apply();

        // Use the following instead to record requests
        // interceptor.applyRecord();
    });
});

Key Points:

  • The Stoobly instance and interceptor are created once outside the describe block
  • Test titles are automatically detected at request interception time for each test, and withScenarioNameFromTest() can be used to derive a scenario name from the Cypress test title path
  • interceptor.apply() must be called in beforeEach because it uses cy.intercept. cy.intercept gets reset before every test. See: https://docs.cypress.io/api/commands/intercept#:~:text=All%20intercepts%20are%20automatically%20cleared%20before%20every%20test.

Integrating with Playwright

Recommended: define a Playwright fixture to manage the interceptor lifecycle per test. This keeps setup/teardown consistent and avoids repeating boilerplate.

// tests/fixtures/stoobly.js
import { test as base } from '@playwright/test';
import Stoobly from 'stoobly';
import { RecordPolicy, RecordOrder, RecordStrategy } from 'stoobly/constants';

const stoobly = new Stoobly();
const interceptor = stoobly.playwrightInterceptor({
    mode: process.env.STOOBLY_INTERCEPT_MODE, // 'mock' | 'record' | 'replay'
    record: {
        policy: RecordPolicy.All,
        order: RecordOrder.Overwrite, // Defaults to RecordOrder.Append
        strategy: RecordStrategy.Full,
    },
    urls: [{ pattern: '<URLS>' }],
});

export const test = base.extend({
    stooblyInterceptor: async ({ page }, use, testInfo) => {
        interceptor
            .withPage(page)
            .withScenarioNameFromTest(testInfo)
            .withTestTitle(testInfo.title);

        await interceptor.apply();
        await use(interceptor);
    },
});
export const expect = test.expect;

Use the fixture in tests:

// tests/example.spec.js
import { test, expect } from './fixtures/stoobly';

test.beforeEach(async ({ page, stooblyInterceptor }) => {
    // In the fixture, the scenario is set to the test path, to override:
    // stooblyInterceptor.withScenarioName('<SCENARIO-NAME'>);
});

test('Scenario', async ({ page, stooblyInterceptor }) => {
    await page.goto('https://docs.stoobly.com');
    // Requests are already intercepted by the fixture
    await expect(page).toHaveTitle(/Stoobly/);
});

You can also set up interception at the context level inside the same fixture if you need to capture extension or service worker traffic (see the withContext() section below).

import { test } from '@playwright/test';
import Stoobly from 'stoobly';
import { RecordPolicy, RecordOrder, RecordStrategy } from 'stoobly/constants';

const stoobly = new Stoobly();
const stooblyInterceptor = stoobly.playwrightInterceptor({
    record: {
        policy: RecordPolicy.All,
        order: RecordOrder.Overwrite, // Defaults to RecordOrder.Append
        strategy: RecordStrategy.Full,
    },
    urls: [{ pattern: '<URLS>' }],
});

test.describe('Scenario', () => {
    test.beforeEach(async ({ page }, testInfo) => {
        await stooblyInterceptor.withPage(page).apply();

        // Use the following instead to record requests
        // await stooblyInterceptor.withPage(page).applyRecord();

        stooblyInterceptor.withTestTitle(testInfo.title);
    });
});

Key Points:

  • The Stoobly instance and interceptor are created once outside the describe block
  • withPage() and withTestTitle() must be called in beforeEach() to update the page and test titles for each test because Playwright does not provide a global API to auto-detect test titles
  • Test titles are applied at request interception time

Intercepting Browser Extension Requests with Playwright

By default, Playwright intercepts requests at the page level using withPage(). To intercept requests from browser extensions, service workers, or all pages in a browser context, use withContext():

test.beforeEach(async ({ context, page }, testInfo) => {
    await stooblyInterceptor
        .withContext(context)  // Intercept all requests in the browser context
        .apply();
    stooblyInterceptor.withTestTitle(testInfo.title);
});

Using withContext() enables:

  • Intercepting requests from browser extensions
  • Intercepting requests from service workers
  • Intercepting requests across all pages in the same browser context

Note: You can use withContext() alone, withPage() alone, or both together. When both are used, routes are applied to both the page and context, ensuring comprehensive request interception.

Testing

Run unit tests:

npm test

Test Cypress Integration

Run Cypress end-to-end tests:

npm run test:cypress

Test Playwright Integration

Run Playwright end-to-end tests:

npm run test:playwright

Documentation

Full API documentation is available at: https://stoobly.github.io/stoobly-js/

To regenerate TypeDoc docs locally:

npx typedoc