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

@dobu/sdk

v0.2.0

Published

Universal SDK for Dobu Mergen: browser tracker, edge-safe server client, React hooks, and Next.js integration.

Downloads

230

Readme

@dobu/sdk

Browser SDK for the Dobu Mergen recommendation engine. Tracks views, clicks, impressions, cart events, and search; stitches anonymous sessions to logged-in users; attaches request_id attribution so conversions flow back to the model that served them.

  • Size: ~4 kB gzipped (IIFE min bundle)
  • Runtime: evergreen browsers (ES2020). Node 18+ only for types/bundling.
  • Storage: localStorage for ids, IndexedDB for offline replay (both optional).
  • Transport: batched fetch with sendBeacon on page-hide.

Install

npm install @dobu/sdk
# or
pnpm add @dobu/sdk
# or
yarn add @dobu/sdk

Or drop it in with a <script> tag — see Script tag below.

Quick start

import * as dobu from "@dobu/sdk";

dobu.init({
    key: "pub_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    projectId: "shoppy",
    endpoint: "https://dobu.example.com", // optional; defaults to the hosted Dobu
});

dobu.page({itemId: "product-123"});
dobu.track("click", {itemId: "product-123", position: 0});
dobu.identify("user_42");

Publishable keys (pub_…) are safe to ship to the browser: they are origin-locked and cannot emit server-only event types (purchase, rate, refund).

Using with Next.js / React

@dobu/sdk is a browser-only SDK. It exports are safe to import from a server component or RSC boundary — init() / track() / identify() are no-ops when window is unavailable, so SSR won't crash — but no event will actually be sent from the server. Always call the SDK from a client component:

// app/DobuProvider.tsx
"use client";
import {useEffect} from "react";
import * as dobu from "@dobu/sdk";

export function DobuProvider() {
    useEffect(() => {
        dobu.init({
            key: process.env.NEXT_PUBLIC_DOBU_KEY!,
            projectId: "shoppy",
            autoTrackImpressions: true,
        });
    }, []);
    return null;
}
// app/layout.tsx
import {DobuProvider} from "./DobuProvider";

export default function RootLayout({children}: { children: React.ReactNode }) {
    return (
        <html>
        <body>
        <DobuProvider/>
        {children}
        </body>
        </html>
    );
}

Tracking in a page component:

"use client";
import {useEffect} from "react";
import * as dobu from "@dobu/sdk";

export default function ProductPage({sku}: { sku: string }) {
    useEffect(() => {
        dobu.page({itemId: sku});
    }, [sku]);
    return <>...</>;
}

Notes:

  • Put the publishable key in NEXT_PUBLIC_DOBU_KEY so Next ships it to the browser. pub_… keys are safe to expose — they are origin-locked and cannot emit purchase / rate.
  • React Strict Mode double-invokes useEffect in dev. dobu.init() is idempotent (re-init flushes the previous transport via sendBeacon before swapping), so this is harmless — just noisy in the network tab.
  • Call dobu.flush() before a programmatic router.push(...) if you want deterministic delivery before navigation.

Script tag


<script async
        src="https://dobu.example.com/sdk/v1/dobu.min.js"
        data-project="shoppy"
        data-key="pub_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
        data-auto-impressions="true"></script>

The SDK auto-initializes from data-* attributes and is globally available as window.dobu.

API

init(config)

| Option | Type | Default | Notes | |------------------------|-----------|------------------------|------------------------------------------------------------------------| | key | string | — | Required. Must start with pub_. | | projectId | string | — | Required. | | endpoint | string | https://dobu.cody.mn | Your Dobu server. | | flushIntervalMs | number | 2000 | Max delay between flushes. | | maxBatchSize | number | 20 | Flush when the queue reaches this size. | | sessionIdleMs | number | 1_800_000 (30 min) | Idle window before session_id rotates. | | offlineReplay | boolean | true | Persist envelopes to IndexedDB; replay on next init. | | autoTrackImpressions | boolean | false | Fire impression for [data-dobu-item-id] elements in view. | | autoCartAbandon | boolean | false | Fire cart_abandon on tab background after an uncompleted cart event. | | debug | boolean | false | Mirror queue/flush activity to console.log. |

track(eventType, opts?)

dobu.track("click", {
    itemId: "product-123",
    position: 2,
    value: 49.99,
    currency: "USD",
    sku: "red-M",
    variantProperties: {color: "red", size: "M"},
    properties: {shelf: "home_for_you"},
});

Allowed event types: impression, view, click, add_to_cart, cart_abandon, wishlist, search, custom. Server-only types (purchase, rate, refund) throw synchronously — those must be sent from your backend with a secret key.

request_id is auto-attached to attribution events (view, click, impression, add_to_cart, wishlist) from the last recommendation response; override per-call via opts.requestId.

page(opts?)

Shorthand for track("view", opts).

search(opts)

dobu.search({
    query: "red shoes",
    resultsCount: 12,
    resultsShown: ["product-1", "product-2", "product-3"],
    filters: {brand: "Nike", size: "M"},
});

identify(userId)

Binds an anonymous browser session to a real user. Emits an identify record that the server stitches into ClickHouse so pre-login events are attributed post-hoc.

reset(opts?)

dobu.reset();                  // clear user id + experiment, keep anon id
dobu.reset({rotate: true});  // also mint a new anon id (shared devices)

disable() / enable()

Consent gate. disable() drops the in-memory queue and ignores subsequent track/identify calls; enable() resumes.

setRequestId(rid) / setExperimentId(experimentId, variantKey?)

Attach attribution context for subsequent events.

applyRecommendationResponse(resp)

const body = await fetch(...).then(r => r.json());
dobu.applyRecommendationResponse(body);
// = setRequestId(body.request_id) + setExperimentId(body.experiment_id, body.model_used)

flush()

await dobu.flush();

Deterministic flush. Useful right before a programmatic navigation.

requestDelete(userId)

const ok = await dobu.requestDelete("user_42");

GDPR erasure — POST /projects/{id}/ingest/forget/{userId}. Returns true on 2xx.

Session helpers

dobu.getAnonId();        // "anon_…"
dobu.getSessionId();     // "sess_…"
dobu.rotateSessionId();  // force a fresh session

debug()

Returns { endpoint, projectId, queued, pendingIdentify, disabled, lastBatch }.

Auto impressions

Opt in at init time, then mark recommendation slots:


<div data-dobu-item-id="product-1" data-dobu-position="0">…</div>

One impression fires per (itemId, sessionId) after the element is >50% visible for ≥1 s. Optional attributes: data-dobu-sku, data-dobu-currency, data-dobu-value.

Offline replay

Outbound batches are persisted to IndexedDB before being sent and deleted on a 2xx response. On the next init(), anything stuck in the queue (network down, tab crash) is replayed oldest-first. Stale entries (>7 days) are dropped. Falls back to in-memory in environments without IndexedDB (Safari private mode, SSR).

CSP

script-src https://dobu.example.com;
connect-src https://dobu.example.com;

License

MIT — see LICENSE.