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

@glidevvr/storage-payload-error-logger-pkg

v0.4.0

Published

Centralized error capture for GLI storage products (theme, unit-table, reservation, rental, login). Publishes a runtime helper for browsers and Node, a React error boundary, and a Payload v3 endpoint handler.

Downloads

554

Readme

@glidevvr/storage-payload-error-logger-pkg

Centralized error capture for GLI storage products. See the WEB-1413 design doc in gli-payload-multitenant/docs/plans/2026-05-05-web-1413-error-logger-design.md for details.

Subpaths

  • @glidevvr/storage-payload-error-logger-pkg — core browser/Node helper (initLogger, logError, setLogContext, installGlobalHandlers, fingerprint)
  • @glidevvr/storage-payload-error-logger-pkg/react<ErrorBoundary>
  • @glidevvr/storage-payload-error-logger-pkg/payload-endpoint — Payload v3 Endpoint handler (the HTTP receiver for browser-side logError calls)

Init

initLogger({ endpoint, repo, release, recaptchaSiteKey })

PII contract

The logger is built on a strict rule: never accept user-input data into the pipeline in the first place. Scrubbing PII downstream is too late — the goal is to never receive it.

This contract is enforced on five surfaces:

1. LogContext is a closed allowlist

LogContext has no index signature. TypeScript rejects unknown keys at the call site. The allowlist is grouped by domain — see types.ts for the full list and per-field JSDoc. Headline categories:

| Category | Examples | | --- | --- | | Identity | source, sessionId, componentStack | | Organization (storage org) | organizationId (Payload Mongo ID), organizationName, seCompanyId (SE numeric ID) | | Renter (anonymous, never name/email/phone) | seTenantId, vendorTenantId, vendorType | | Facility | seFacilityId, vendorFacilityId, siteLocationCode | | Unit | seUnitId, vendorUnitId, unitCategorySlug, unitTier, ratePlan | | Reservation (pre-move-in) | seReservationId, vendorReservationId, reservationType | | Rental (post-move-in) | seRentalId, vendorRentalId, seTransactionId | | Ledger / billing | seLedgerId, seReceiptId, chargeId | | Charges + recurring fees | charges[], recurringFees[] (sub-keys: id, name, amount, cycle only — memo/note stripped) | | Selections (codes, never typed values) | promotionId, promotionSlug, insurancePlanId, paymentMethodType, autopayEnabled, billingCycle, moveInDate, quotedRate | | Flow context | flow (closed: reservation / rental / cancellation / payment-update / onboarding), step (paired with flow), slug, apiMethod, apiEndpoint, httpStatus, cacheKey, errorTag, validationFieldNames[], expectedCount, actualCount | | Payload-only | collectionSlug, documentId, hookName, userRole, scraperBatchId, buildId |

Naming convention:

  • se* prefix → identifier from the SE API (matches SE's wire term)
  • vendor* prefix → identifier from the underlying property-management system (SiteLink / StorEdge / DoorSwap)
  • everything else → user-facing terms shared with the admin UI (e.g. organizationId matches the "Organization" label in CMS)

Need a new field? Add it intentionally to LogContext in types.ts AND to ALLOWED_CONTEXT_KEYS in gli-payload-multitenant/src/collections/IssueEvents/hooks/sanitizeContext.ts (the server-side defense). Confirm it carries no PII before adding.

2. reason is a closed enum — the WHY, never user input

The Reason type in types.ts is the closed vocabulary, mirrored by ISSUE_REASON_VALUES in gli-payload-multitenant/src/utilities/errorLogger/reasonValues.ts. The Issues + IssueEvents collection schemas use Payload's select field type with this enum, so direct REST POSTs that send an unknown reason are rejected with a 400 — closing the gap that the helper-side TypeScript guard alone can't cover.

15 values mirror the errorTag enum in @glidevvr/se-components; 9 internal values cover failure modes outside the SE API:

// SE errorTags (mirror se-components)
"unit_not_found" | "unit_not_available" | "unit_unavailable" | "facility_not_found"
| "invalid_tenant_credentials" | "unauthorized" | "session_expired"
| "tenant_not_found" | "reservation_not_found" | "ledger_not_found" | "promotion_not_found"
| "payment_failed" | "payment_declined" | "invalid_payment_option"
| "validation_error"

// Internal additions
| "unit_list_empty" | "expected_data_missing" | "cache_stale"
| "build_failure" | "hook_error" | "scraper_failure"
| "data_integrity" | "unhandled_exception" | "network_timeout"

Pass via LogErrorOptions.reason:

logError(err, { reason: "invalid_cvv", context: { step: "payment" } })
logError(err, { reason: "unit_list_empty", context: { seFacilityId, expectedCount: 1, actualCount: 0 } })

reason is part of the event's fingerprint, so the same code path failing for different reasons becomes distinct Issues.

3. severity defaults from reason

Severity convention:

  • error — blocks the user / unrecoverable / 5xx (default for uncaught and most reasons)
  • warning — recoverable, user-facing flow signal (validation_error, payment_declined, unit_list_empty, cache_stale, etc.)
  • info — informational signal (session_expired)

getDefaultSeverity(reason) does the mapping. logError() uses it automatically; callers can override via LogErrorOptions.severity. Wire getDefaultSeverity into integration points (e.g. RTK Query error middleware) so individual call sites don't think about it.

4. Error.message must not contain user input

logError() reads err.message and persists it. Throw errors with constant messages:

// ✅ good — fixed message, classification via `reason`
if (!isValidCvv(cvv)) {
  logError(new Error("payment validation failed"), {
    reason: "invalid_payment_option",
    context: { step: "payment", validationFieldNames: ["cvv"] },
  });
}

// ❌ bad — user input lands in the persisted message
throw new Error(`Card ${cardNumber} has invalid CVV ${cvv}`);

Code review is the only guard for this surface; the helper has no automated way to detect interpolated PII inside an Error.message string.

5. URL is path-only

logError() strips the query string and fragment from window.location.href before sending. Only origin + pathname is logged. This protects against tokens or emails appearing in URL params (e.g. ?token=…, ?email=…).

Patterns

Soft-assert empty / unexpected results

For "should-have-data-but-doesn't" cases, log a warning at the boundary where bad data hits the UI:

if (facility.isActive && units.length === 0) {
  logError(new Error("Unit list empty for active facility"), {
    reason: "unit_list_empty",
    context: {
      seFacilityId,
      apiMethod: "getUnits",
      expectedCount: 1,
      actualCount: 0,
    },
  });
}

CMS / scraper / build hook errors

On the server side, wrap the suspect spot in try/catch and re-throw after logging:

try {
  await runScraper(batchId);
} catch (err) {
  logError(err, {
    source: "payload",
    reason: "scraper_failure",
    context: { scraperBatchId: batchId, apiMethod: "runScraper" },
  });
  throw err;
}

The helper's server transport writes via the api-user key. Don't wrap collection-internal hooks with logError — they'd loop on themselves.

RTK Query middleware (apps using @glidevvr/se-components)

Map errorTagreason + default severity:

// inside rtkQueryErrorMiddleware
logError(err, {
  reason: payload.errorTag,                       // 1:1 with se-components
  context: {
    apiMethod: action.meta.arg.endpointName,
    httpStatus: err.status,
    validationFieldNames: Object.keys(payload.validationErrors ?? {}),
  },
})

Server-side defense

The CMS-side IssueEvents.beforeValidate hook (sanitizeIssueEventContext) is the matching runtime guard for the LogContext allowlist. Direct REST POSTs that bypass the helper still get unknown keys stripped before persistence — and per-row keys inside charges / recurringFees are filtered too (memo/note dropped).


Local Development

npm install
npm test            # vitest
npm run build       # tsup → dist/{esm,cjs,d.ts} for all three subpaths

Prerequisites

  • Node.js 20.x or later
  • npm 10.x or later

The published package's wire shape (entry/exports/peerDeps) is defined in package.json. The CMS server defense lives in gli-payload-multitenant/src/collections/IssueEvents/hooks/sanitizeContext.ts — keep ALLOWED_CONTEXT_KEYS there in sync with the LogContext interface in src/types.ts.


Branching Strategy

This package follows a Dirty Trunk repo structure to enable fast, continuous integration.

Trunk (Main Branch)

  • master is the trunk where all development merges.
  • Trunk may contain work-in-progress code and is not guaranteed to be in a release-ready state at all times.

Feature Branches

  • Use short-lived branches for features or fixes:
    • Naming convention: feature/<feature-name> or bugfix/<issue-description>.
  • Merge into master frequently to avoid long-lived branches and reduce conflicts.
  • Use squash merges to keep the history clean.

Release Branches

  • Create release/x.y.z branches from master when ready to stabilize for a release.
  • Only bug fixes and release preparation changes go into the release branch.
  • After release, merge back into master and tag with the version.

Hotfixes

  • Create hotfix/x.y.z branches from the latest tagged release.
  • Merge back into master and the active release branch.

Managing CI/CD

This package uses Bitbucket Pipelines for testing and publishing:

  • Branch Tests: Automated build and test run on all branches (feature, bugfix, release, hotfix).
  • Publishing: npm releases are triggered manually via the npm-publish custom pipeline in Bitbucket.
  • Versioning: Uses semantic versioning:
    • Increment minor versions on releases.
    • Increment patch versions for hotfixes.

Publishing a new version

  1. Update the version in package.json (npm version minor / npm version patch) on the release or hotfix branch.
  2. Push the branch and tag.
  3. In Bitbucket, run the npm-publish custom pipeline from the release/hotfix branch. Requires NPM_TOKEN to be set as a Repository Variable.

Consumers

  • gli-payload-multitenant: hosts the /api/log-error endpoint via the payload-endpoint subpath. Server-only.
  • storage-theme-payload: Tier 1 + 2 retrofit (Phase 3 of WEB-1413). Wraps the app root with <ErrorBoundary> and calls initLogger + installGlobalHandlers; explicit logError(...) at high-value catch paths (SE rate-limiter, tenantInfo, form submission).
  • unit-table: Tier 1 + 2 retrofit (Phase 5 of WEB-1413). Augments the existing widget ErrorBoundary; does NOT install global handlers (the host page owns those).
  • Future consumers: reservation-app, rental-app, npm-golocal-cloud-wrapper.

See the WEB-1413 design doc in gli-payload-multitenant/docs/plans/2026-05-05-web-1413-error-logger-design.md for the full rollout plan.