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

@ostrio/analytics-middleware

v1.0.0

Published

First-party Connect middleware proxy for ostr.io web analytics beacons

Readme

@ostrio/analytics-middleware

First-party Connect-compatible middleware that proxies ostr.io web analytics GIF beacons through your app origin. Cookies and paths stay on your domain; upstream traffic goes to analytics.ostr.io.

Why first-party tracking?

Third-party beacons (analytics.ostr.io loaded from the page) set cookies on the analytics host. Browsers treat those as third-party: Safari ITP, Firefox ETP, and Chrome’s third-party cookie cutoff drop or partition them. Ad blockers also list known analytics hosts.

This middleware serves the GIF from your origin (GET {endpoint}/{trackingId}.gif). The browser sees a first-party request.

  • Session cookie survives. Set-Cookie is rewritten to your hostname and scoped to the beacon path, so Safari/Firefox/Chrome keep the visitor id.
  • Fewer blocked hits. The request URL is your site, not analytics.ostr.io, so host-based blocker lists miss it.
  • Cookie is not on every page request. Path is the beacon only, not /.
  • IP and Referer stay accurate. The server forwards X-Forwarded-For / X-Connecting-IP and Referer. The tracker does not depend on the browser talking to a third-party host.
  • Controller boundary. Collection happens on your origin; ostr.io receives the proxied beacon. That matches a first-party analytics setup for GDPR/CCPA docs.

Without the proxy, ostrio-analytics still works as a third-party tracker. Use this package when cookie durability and blocker resistance matter.

Install

npm install @ostrio/analytics-middleware

For browser tracking, also install optional peer dependency:

npm install ostrio-analytics

Requires Node.js 18+ (CI tests Node 20 / 22 / 24 and Bun).

AI skill:

npx skills add ostr-io/ostrio-analytics-middleware -g --skill ostrio-analytics-middleware

Server usage

Express / Connect

import express from 'express';
import { OstrioAnalyticsMiddleware } from '@ostrio/analytics-middleware';

const analytics = new OstrioAnalyticsMiddleware({
  trackingId: '72Dymb73P94vgPYeB', // 17-character URL-safe ostr.io tracking id
  endpoint: '/service/__a',        // mount path, no trailing slash
  hostname: 'example.com'            // public hostname for cookie Domain rewrite
});

const app = express();
app.use(analytics.middleware());

Any Connect-compatible stack works the same way: call analytics.middleware() early in the chain so beacon requests are intercepted before your routes.

Meteor.js: docs/meteor.md.

Manual handle(req, res)

When you cannot use Connect middleware (custom HTTP server, selective routing):

import { createServer } from 'node:http';
import { OstrioAnalyticsMiddleware } from '@ostrio/analytics-middleware';

const analytics = new OstrioAnalyticsMiddleware({ /* config */ });

createServer((req, res) => {
  if (analytics.handle(req, res) !== false) {
    return; // beacon handled
  }
  // ...your normal request handling
}).listen(3000);

handle returns false when the request is not a beacon (wrong method, path, etc.) — continue your pipeline. Returns void when the response was written.

Client usage (createTracker)

Import from the ./client subpath (re-exports ostrio-analytics types).

import { createTracker } from '@ostrio/analytics-middleware/client';

const tracker = createTracker({
  trackingId: '72Dymb73P94vgPYeB',
  endpoint: '/service/__a' // must match server endpoint
});

tracker.track(); // call on each route change when `auto` is false

createTracker presets:

| Option | Default | |--------|---------| | serviceUrl | {endpoint}/ (trailing slash added if missing) | | transport | fetch | | auto | false |

All other ostrio-analytics options pass through.

Configuration

Required

| Field | Description | |-------|-------------| | trackingId | ostr.io tracking id (exactly 17 URL-safe characters: letters, digits, _, -) | | endpoint | Beacon mount path, e.g. /service/__a. Trailing slashes are stripped | | hostname | Public hostname used as Set-Cookie Domain |

Beacons are matched at GET {endpoint}/{trackingId}.gif.

Optional

| Field | Default | Description | |-------|---------|-------------| | serviceOrigin | https://analytics.ostr.io | Upstream HTTPS origin (path/query ignored) | | defaultReferer | https://{hostname}/ | Referer sent when the browser omits one | | forwardedCookies | ['ot'] | Outbound cookie names allowlist | | responseHeaders | content-type, cache-control, expires, pragma | Upstream response headers allowlist | | upstreamTimeoutMs | 3072 | Upstream socket timeout | | wallTimeoutMs | 4000 | Total handler wall-clock timeout | | maxSearchLen | 4096 | Max query string length; longer → 204 without upstream | | resolveClientIp | see below | Client IP forwarded to ostr.io |

Invalid config (bad trackingId shape, empty endpoint/hostname, non-HTTPS serviceOrigin) throws at construction time.

resolveConfig(config) is the same validation used by the constructor; it returns the resolved runtime config.

Security and proxy failure behavior: docs/security.md. Meteor resolver compatibility: docs/meteor.md.

Package exports

| Import | Purpose | |--------|---------| | @ostrio/analytics-middleware | OstrioAnalyticsMiddleware, resolveConfig, config types | | @ostrio/analytics-middleware/client | createTracker, OstrioWebAnalytics, CreateTrackerOptions, client types |

Dual ESM and CJS builds with TypeScript declarations.

Publishing checklist and npm organization requirements: docs/publishing.md.

License

BSD-3-Clause — see LICENSE.