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

@websandhq/contracts

v1.1.0

Published

JSON Schema contracts for the Websand V4 data ingestion API. Used by external integrations (Shopify marketing app, Zapier, future partners) to validate that their outgoing payloads will be accepted by Websand before the request is sent.

Readme

@websandhq/contracts

JSON Schema contracts for the Websand V4 data ingestion API. Used by external integrations (Shopify marketing app, Zapier, future partners) to validate that their outgoing payloads will be accepted by Websand before the request is sent.

The schemas are descriptive — they match what the platform's JSONata preprocessing currently accepts, not a stricter ideal. When the ingestion behavior changes, schemas change with it; consumers track a caret range (e.g. "@websandhq/contracts": "^1.2.0") so patches and minors flow in automatically through Dependabot, and majors are opened as separate PRs for deliberate review. CI gates the merge in both cases — see the consumer runbook.

Exports

| Schema | Endpoint | Purpose | |---|---|---| | shopifyTransactionInputSchema | POST /data/transaction | Raw payload shape for the Shopify transaction resource (pre-JSONata) | | subscriberInputSchema | POST /data/subscriber | Raw payload shape for the subscriber resource (pre-JSONata) | | directInputSchema | POST /data/:directType | Canonical profile event shape for clients that skip preprocessing |

All three are plain JSON Schema constants (as const literals). The package has zero runtime dependencies — bring your own validator (AJV recommended, to match Websand's own validation).

Publishing Runbook

Published to public npm at https://www.npmjs.com/package/@websandhq/contracts. Consumers install with a plain pnpm add -D @websandhq/contracts — no registry override, no auth token.

How publishing happens

The publish-contracts.yml workflow triggers on every push to main that touches packages/contracts/**. The workflow:

  1. Installs the workspace and builds @websandhq/contracts.
  2. Reads version from packages/contracts/package.json.
  3. Queries the npm registry — if that version already exists, the publish step is skipped (re-runs on the same commit are safe; no-op).
  4. Otherwise publishes via pnpm publish --no-git-checks --provenance --access=public.

The --provenance flag attaches a sigstore attestation that the package was built in this exact GitHub Actions workflow from this exact commit. It shows up as a "Verified" badge on the npm package page and lets consumers verify the supply chain — free for public packages, requires id-token: write permission (already declared in the workflow).

Auth: uses an NPM_TOKEN repo secret (npm automation token, not a PAT). See "First-time setup" below — this is the only credential to manage.

First-time setup (already done — kept here for disaster recovery)

If the NPM_TOKEN secret is ever lost, rotated, or this needs to be re-bootstrapped from scratch:

  1. npm org membership. The @websandhq scope on npmjs.com must exist and the publishing identity must have publish access. Confirm at https://www.npmjs.com/settings/websandhq/members. If the org doesn't exist, create it at https://www.npmjs.com/org/create (free for public packages).

    Provenance prerequisite. For the --provenance flag to attach a sigstore attestation, the source repo must be public on GitHub AND packages/contracts/package.json repository.url must match the canonical GitHub URL (it currently does: https://github.com/WebsandHQ/websand-v4.git). Don't rename or move the repo without updating both.

  2. Create an automation token. npmjs.com → profile → Access Tokens → Generate New Token → Granular Access Token. Configure:

    • Packages and scopes permission: write access to @websandhq/* scope only.
    • Bypass Two-Factor Authentication: ✅ check this. The publishing identity has 2FA enforced (it should — npm requires it for publishers); without this checkbox the workflow will fail with EOTP (one-time password required) or hang on the publish step. Granular tokens with this flag set are the documented npm path for CI publishing.
    • Expiration: set to the maximum the UI allows. As of late 2025, npm caps write-enabled granular tokens at significantly less than a calendar year (the exact cap moves; the UI shows the current limit when you select the token scope). Set a calendar reminder for rotation at the cap shown in the UI, not a year out.
  3. Store the token in GitHub. websand-v4 repo → Settings → Secrets and variables → Actions → New repository secret. Name: NPM_TOKEN. Paste the token value. Save.

  4. Verify with a no-op publish. Bump the contracts patch version, merge a PR, watch the workflow log for the "Publish to npmjs.com" step succeeding. Note: the npm view step before it is an anonymous public-registry query — it does NOT validate NPM_TOKEN. A bad, missing, or scope-wrong token will only surface at the publish step itself with E401 / E403. Watch for that step's exit code, not the npm view step's.

Releasing a new version

The version bump is manual and deliberate — the workflow does not bump automatically. This forces a conscious decision about semver impact every time schemas change.

  1. Make the schema change in packages/contracts/src/.

  2. Run the chain tests — these prove the schema still matches the live ingestion path and that directInputSchema stays in lockstep with the canonical ingestionProfileEventJsonSchema from @repo/shared:

    cd apps/server && pnpm exec vitest run src/scripts/platform-endpoint-resources.test.mts

    All three describe blocks must pass: input→preprocess→resource→canonical, negative cases with explicit error-path assertions, and the parity matrix. If parity fails, the standalone copy in direct-input.mts has drifted from the SSOT — sync it before bumping the version.

  3. Bump the version in packages/contracts/package.json following semver:

    | Change | Bump | Examples | |---|---|---| | New optional field, new required-on-output field that JSONata fills in, doc-only edits | patch | adding a new properties.country entry; documenting an existing field | | New endpoint schema export, new oneOf branch in directInputSchema (additive) | minor | adding a new marketing event name; exporting a new *InputSchema | | Tightened type or format on an existing field, removed/renamed field, narrowed required semantics | major | tightening orderdate from string to string + format: "date-time"; removing a deprecated field |

    Run from inside packages/contracts/:

    pnpm version <patch|minor|major> --no-git-tag-version

    The --no-git-tag-version flag is required. Without it, pnpm version creates a git commit AND a tag pointing at HEAD on your local branch. That tag is on the PR branch, not on main — by the time the PR merges, the tag points at an orphan commit and pollutes the tag namespace. The publish workflow keys off the manifest version, not git tags, so tags add no value and create cleanup work. If you prefer, edit the version field in package.json directly — same effect, no flag to remember.

  4. Update the Schema changelog section (below in this README) if it's a major or minor bump. Patch bumps don't need a changelog entry.

  5. Commit, PR, merge. The workflow runs on merge to main and publishes to npmjs.com within ~2 minutes.

  6. Verify the new version is live:

    # any machine — public registry, no auth needed
    npm view @websandhq/contracts@<new-version> version

    Or open https://www.npmjs.com/package/@websandhq/contracts and check the versions list. The "Provenance" badge should be present on every version published via this workflow.

Common publish failures

| Symptom | Cause | Fix | |---|---|---| | Workflow log: npm error code E401 or Unable to authenticate | NPM_TOKEN secret is missing, expired, or scoped wrong | Regenerate per "First-time setup" step 2. Granular tokens expire — set a rotation reminder. | | Workflow log: npm error code EOTP or step hangs waiting for one-time password | The granular token was created without "Bypass Two-Factor Authentication" checked | Regenerate the token with that checkbox set; update the NPM_TOKEN secret in repo settings. | | Workflow log: npm error code E403 — You must be logged in to publish packages | Token lacks publish access for @websandhq/* | Granular token's package-permission scope is wrong. Recreate with write access to the scope. | | Workflow log: npm error code EPUBLISHCONFLICT | A version equal to or older than package.json is already published — and the npm view guard somehow didn't catch it (e.g. registry replication lag) | Bump to the next patch and re-merge. The skip-if-exists guard is best-effort, not transactional. | | Workflow log: npm error code EPROVENANCE or "provenance attestation failed" | id-token: write permission missing, or workflow not eligible (e.g. running on a fork PR) | Confirm the permissions: block declares id-token: write. Provenance only works on push to the canonical repo, not on forked PRs (which is fine — the workflow only runs on push to main). | | Publish step skipped but you bumped the version | The version in package.json already matches a published version on npm | Re-bump the version (you likely cherry-picked an old commit, or someone else published manually). Each merge to main must bring a strictly-greater version, or it no-ops. | | Type errors at consumer install (Cannot find module '@websandhq/contracts/dist/index.d.mts') | Build step didn't run, or files: ["dist"] was edited | Check the workflow's build step logs. The published tarball must contain dist/ — verify with cd packages/contracts && npm pack --dry-run locally. |

Rolling back a bad publish

Don't npm unpublish. Two reasons, both worse than they sound:

  1. The version number is permanently burned. Per npm policy (docs), package-name@version is unique and cannot be reused by unpublishing and re-publishing it. So if you unpublish 1.4.0 planning to fix the bug and re-issue 1.4.0, you can't — 1.4.0 is gone forever and any fix has to ship as 1.4.1 or higher. This is the same end state as fix-forward, but with extra steps and a confusing version-history hole.
  2. Reproducible builds break for cached installs. Anyone whose pnpm install already pulled the bad version still has it locked in their lockfile; their builds keep working off the cache, so they don't know to upgrade until something else triggers a re-resolve. Then they suddenly hit 404 on the version their lockfile points at.

The only valid uses of unpublish are accidental publishes of secrets (which can't happen here — pure JSON Schema, no environment config) and DMCA takedowns. If you unpublish all versions of the package, npm additionally blocks re-creating the package name for 24 hours — separate policy from the per-version burn above.

Instead, publish a fix-forward version:

  1. Bump again (e.g. 1.4.1 over a broken 1.4.0) with the schema fix.

  2. Open a PR in websand-email-marketing-v2 bumping the dependency past the broken version. (Today that's the only consumer; future consumers discover the fix via Dependabot.)

  3. Optional: deprecate the bad version so consumers see a warning at install time:

    npm deprecate @websandhq/[email protected] "Bad release — use 1.4.1+"

    Run from any machine logged in as a publisher of the scope. Deprecation doesn't remove the version (so cached installs keep working) but warns anyone resolving it fresh.

Schema changelog

Document any minor or major bump here. Patch-only changes don't need a row.

| Version | Date | Change | Migration | |---|---|---|---| | 1.1.0 | 2026-05-15 | shopifyTransactionInputSchema: orderId and total.total now declare type: ["string", "number"] instead of unconstrained {}. Catches null/boolean/array values at the consumer-side validator before they hit Websand. | None — JSONata still coerces both shapes on the platform side, so existing payloads with strings or numbers continue to pass. Payloads with null/boolean/array values were already rejected downstream; this surfaces the rejection earlier. | | 1.0.0 | 2026-05-14 | Initial release. shopifyTransactionInputSchema, subscriberInputSchema, directInputSchema all match canonical ingestion behavior as of 00cbc151 (v2.6.0). | — |

How consumers use this

See the consumer-side runbook in the marketing repo: websand-email-marketing-v2/docs/websand-contracts.md.

Short version: install with pnpm add -D @websandhq/contracts, validate the output of your transform functions against the schemas with AJV in a test, fail CI when transforms drift from the contract.