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

plugline

v0.2.0

Published

Vendor-agnostic integration SDK: declarative connectors, pluggable auth strategies, OAuth 2.0 authorization-code flow, retries and rate-limit handling, and a host adapter interface. Zero runtime dependencies.

Readme

plugline

Vendor-agnostic integration SDK for production TypeScript backends. Declarative connectors, pluggable auth strategies, OAuth 2.0, retries, rate-limit handling, webhooks. Zero runtime dependencies.

bun add plugline
npm install plugline

Why plugline

A TMS that integrates with 100 vendors eventually notices the same pattern: every vendor has a different auth scheme, a different error shape, a different pagination convention, and the connector that talks to them is mostly boilerplate around that. plugline is the framework you only write once.

A third-party TMS that wants to reuse your connectors on their own models can also bun add plugline, write a bind function against their own schema, and inherit the same auth, refresh, retry and rate-limit behaviour for free.


Minimal example

import {
  defineConnector,
  apiKey,
  Registry,
  type IntegrationHost,
} from "plugline";

// 1. Tell the SDK how to talk to your app (database, env, clock, fetch).
const host: IntegrationHost = {
  fetch: (input, init) => fetch(input, init),
  now: () => Date.now(),
  env: (key) => process.env[key],
  saveSecrets: async () => {},
  loadSecrets: async () => null,
  upsertIntegration: async () => ({ integrationId: "int_1" }),
  getIntegration: async () => null,
  recordRun: async () => {},
  markRun: async () => {},
};

// 2. Declare a manifest and a connector.
const acme = defineConnector<{ id: string; lat: number; lng: number }>({
  manifest: {
    id: "acme",
    name: "Acme Tracking",
    provider: "Acme Inc.",
    tagline: "Live trailer positions",
    description: "Reads trailer locations from the Acme telematics API.",
    category: "ELD & Telematics",
    docsUrl: "https://developer.acme.example",
    authType: "apiKey",
    credentialFields: [
      {
        key: "api_key",
        label: "Acme API Key",
        type: "password",
        required: true,
      },
    ],
    syncEntities: [
      {
        id: "gps",
        label: "GPS",
        description: "Live positions",
        defaultEnabled: true,
      },
    ],
    syncCadence: "every_15_mins",
    features: ["Live GPS"],
  },
  auth: apiKey({ headerName: "X-Acme-Key" }),
  baseUrl: "https://api.acme.example",
  itemsPath: "data.trailers",
  probe: { path: "/v1/trailers", query: { limit: 1 } },
  list: { path: "/v1/trailers" },
  paginate: {
    cursorParam: "cursor",
    nextCursorPath: "meta.next_cursor",
    hasMorePath: "meta.has_more",
  },
  mapping: [
    { target: "id", source: "id" },
    { target: "lat", source: "position.lat" },
    { target: "lng", source: "position.lng" },
  ],
  // 3. Tell the SDK how to write records to your models. Everything else is automatic.
  bind: async (ctx, records) => {
    // ctx.tenantId scopes every call. records is your normalised shape.
    return { persisted: records.length };
  },
});

// 4. Register. A card can only exist in your settings UI for connectors
//    that are actually registered — the catalog is derived from the registry.
const registry = new Registry();
registry.register(acme);

What the SDK owns

  • Auth strategies: apiKey, basicAuth, bearerToken, oauth2AuthCode
  • OAuth 2.0 authorization code flow: CSRF state with HMAC, code exchange, just-in-time refresh, re-encrypted token persistence
  • Vendor error parsing: Google, RFC 6749, Samsara, and plain-text bodies
  • Request loop: timeouts, retries on 5xx + 429 with Retry-After honoured, 4xx passed through
  • Pagination: cursor-based, with hasMore flag and max-page guard
  • Mapping: dot-path extraction with transforms and fallbacks
  • Webhooks: HMAC-SHA256 verification, idempotent dispatch
  • Sync logging: every connector call produces an audit row, success or failure
  • Run measurements: latency is always measured, never invented

What the host owns

  • Persistence (database, encryption, multi-tenant scoping)
  • The fetch transport (and any caching or tracing you wrap around it)
  • The clock (so tests can be deterministic)
  • env(key) for server-side secrets
  • One bind per connector that maps normalised records to your models

The boundary is one interface (IntegrationHost). The SDK never imports your database, your session, or your ORM.


Built-in recipes

| Recipe | Status | Notes | | ------------------------- | ------ | ------------------------------------------------------------------------- | | googleMapsRecipe | live | verified against routes.googleapis.com and the Geocoding API | | samsaraRecipe | live | /fleet/vehicles/locations (50 req/s), real Dallas→Atlanta mileage check | | samsaraHosRecipe | live | /fleet/hos/clocks (5 req/s) for compliance-aware dispatch | | samsaraViolationsRecipe | live | /fleet/hos/violations for active compliance violations |

Each recipe verifies against the live vendor API in scripts/test-sdk.ts.


API surface

  • Types — IntegrationManifest, IntegrationConnector, IntegrationContext, IntegrationHost, OAuthConfig, RecipeOptions, EndpointSpec, FieldMapping
  • Strategies — apiKey, basicAuth, bearerToken, oauth2AuthCode
  • Engine — defineConnector, Registry
  • Helpers — validate, validateManifest, assertCredentialsPresent, parseDurationSeconds, parseVendorError, redact, maskSecret, metersToMiles, formatLocation
  • Runtime — fetchWithRetry, parseRateLimitHeaders, handleWebhook, readWebhook, verifyHmacSha256

Running the tests

bun run test                # 114+ assertions, all offline, no network
bun run typecheck           # tsc, 0 errors
bun run build               # bundles dist/

The tests use a mock host — they prove the SDK never silently fabricates a success, surfaces real vendor errors verbatim, and survives 5xx, 4xx, 429 and network errors with the right policy each time.


License

MIT