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

la-analytics

v0.2.5

Published

LA Analytics is a lightweight, privacy-friendly analytics SDK for tracking events, identifying users, and powering marketing integrations.

Readme

LA Analytics is a lightweight, privacy-friendly analytics SDK for tracking events, identifying users, and powering marketing integrations.


Privacy

By default LA Analytics is privacy friendly, obscuring all activity behind Anonymous IDs. However if you want to power more complex user tracking you can identify users and then all events they do will be attributed to them in your CRM. This can assist with customer support but also for things like drip campaigns and other marketing activities.

The server-side ingestion pipeline also applies additional privacy protections:

  • IP addresses are hashed into an $identityHash for de-duplication, never stored raw for EU visitors (GDPR).
  • Geo data (city, country, region) is derived from Cloudflare's edge network, not stored IP lookups.

Getting Started

Install by running: npm install la-analytics

Copy your Project ID from the project settings page.

Then import Analytics and initialize the lib with your Project ID.

// lib/analytics.ts
import { Analytics } from "la-analytics";

export const analytics = new Analytics({ projectId: "YOUR-PROJECT-ID" });

analytics.init();

Configuration

| Option | Type | Default | Description | | ------------------ | ------- | ------------------------------------------------------------ | ---------------------------------------------------------------- | | projectId | string | — | Your project ID (required) | | proxy | string | https://analytics.tscull.workers.dev/ | Base URL for the analytics API | | enabled | boolean | true | Set false to disable sending data | | debug | boolean | false | Log events to console | | debugVerbose | boolean | false | Verbose debug logging (more detail than debug) | | autoPageTracking | object | { enabled: true, trackQueryParams: true, trackHash: true } | Auto page view config | | integrations | array | — | Facebook Pixel, Pinterest Tag, TikTok Pixel configs |

Automatic Page View Tracking

When you initialize the lib it will automatically start tracking all page views in your app. By default, it tracks all URL changes, including query parameters and hash changes. You can customize this behavior using the autoPageTracking configuration object:

const analytics = new Analytics({
  projectId: "your-project-id",
  autoPageTracking: {
    enabled: true, // Set to false to disable automatic page tracking
    trackQueryParams: true, // Set to false to ignore URL query parameter changes
    trackHash: true, // Set to false to ignore URL hash changes
  },
});

analytics.init();

You can also manually track pages by disabling automatic page tracking (enabled: false) and calling analytics.page() yourself. For most use cases you don't need to do this.

Tracking Events

With events you can track custom actions users are taking on your site with event properties. The track method supports both client-side (parameter-style) and server-side (object-style) usage patterns.

Each event has 2 components:

Name - this is the name of the action that was taken.

We like to follow Segment's Object Action Framework for naming events.

Properties - This is an object with the values of the event. Such as the value, currency, or query.

We currently have limited support for properties, but in the future we will support complex querying and actions based on event properties.

Client Usage

For client-side tracking, use the Segment-compatible syntax. This will automatically assign userId based on identify calls during the session

import { analytics } from "../lib/analytics.ts";

// Track an event
analytics.track("Searched Movies");

// Track an event with properties
analytics.track("Searched Movies", {
  query: "batman",
  sortBy: "relevancy",
});

Server Usage (Object Style)

For server-side tracking, use the object-style syntax. You must provide a userId or anonymousId for server side tracking events so there is a contact to connect the events to.

import { analytics } from "../lib/analytics.ts";

// Track an event and associate it with a user
analytics.track({
  event: "Signed Up",
  userId: "user_123",
  properties: {
    provider: "email",
  },
});

// Track with an anonymous ID from the client
analytics.track({
  event: "Form Submitted",
  anonymousId: "anon_abc",
  properties: {
    formId: "contact-us",
  },
});

Identifying Users

By default LA Analytics does not track any identifiable data about users, just anonymous user ids and session ids. However if you want to tie events to users you can identify users which will connect events to users in your CRM and power tools like drip campaigns and customer support.

To do this just identify a user with their DistinctId (usually the user ID from your database) and then any properties on the user.

import { analytics } from "../lib/analytics.ts";

// Inside your user logged in function identify a user
// You should only identify users when they login, or their properties change
function signedIn() {
  // Identify a user
  analytics.identify("USER-ID-FROM-YOUR-DB", {
    // Any user properties you want to record with the user
    name: "Tony Hawk",
    email: "[email protected]",
    bestTrick: 900,
  });
}

Identifying users who don't have an ID

Often times you have a user's contact details before they sign up for your site. This could be part of the signup flow, or if they were to subscribe to marketing emails before they sign up as a customer

LA Analytics allows identifying users without an id by simply skipping the ID part of the identify call. This will add contacts to the CRM for email marketing and other tools before users register.

For example:

// Identify a user only by their email
analytics.identify({
  name: "Tony Hawk",
  email: "[email protected]",
  bestTrick: 900,
});

By doing this users will get tracked and added to your contacts, and as soon as they do login, assuming they use the same email address, all their properties will be connected.

Soft vs Full identify

When users are identified without an ID we call this a "Soft Identify". Users who are soft identified can also be called "Leads". Once a user signs up and you identify them with an ID they will be fully registered and can be called "Customers"

In the CRM when you identify a user, either soft or full, they become a contact. The type of the contact depends on whether they were fully identified, "Leads" are soft and "Customers" are full.

Logout

When a user logs out, call logout() to reset the session, anonymous ID, and user ID. All subsequent events will be tracked as a new anonymous user.

analytics.logout();

This is equivalent to calling analytics.identify(null), which also resets the session. Use whichever reads better in your code.

Debugging and Dev

When setting up analytics you probably want to exclude your local environment.

To do so simply turn on dev mode.

export const analytics = new Analytics({
  projectId: "YOUR-PROJECT-ID",

  // use an env to only set enabled to true on production
  enabled: process.env["NODE_ENV"] === "production",
});

Debugging

You can turn on debugging when you initialize the SDK or by running analytics.enableDebug() in your code or the browser.

export const analytics = new Analytics({
  projectId: "YOUR-PROJECT-ID",
  enabled: process.env["NODE_ENV"] === "production",
  // Set to true to console log analytics events.
  // This works even when enabled is false - it just won't send the data
  debug: true,
});

For even more detail, use debugVerbose:

export const analytics = new Analytics({
  projectId: "YOUR-PROJECT-ID",
  debugVerbose: true,
});

You can also enable debug mode at runtime:

analytics.enableDebug();
// will console log "debug mode activated"

analytics.disableDebug();
// will console log "debug mode deactivated"

URL Debug Parameters

In the browser you can enable debug mode via URL query parameters without changing code:

  • ?la-debug=true — enables standard debug logging
  • ?la-debug-verbose=true — enables verbose debug logging

URL parameters override config values, so you can debug a production build without redeploying.

Cross-Subdomain Tracking

The SDK stores session and identity data in cookies scoped to the root domain (e.g. .example.com). This means a user navigating between app.example.com and www.example.com maintains the same session and anonymous ID.

On first initialization the SDK migrates any existing data from localStorage to cookies automatically.

Ad Network Attribution

The SDK automatically detects ad click IDs from URL parameters for the following networks:

| URL Parameter | Ad Network | $utmAdSource value | | ------------- | ---------- | -------------------- | | fbadid | Facebook | facebook | | gadid | Google | google | | xadid | X | x | | padid | Pinterest | pinterest | | ttadid | TikTok | tiktok | | scadid | Snapchat | snapchat | | radid | Reddit | reddit | | liadid | LinkedIn | linkedin |

If utm_ad_id and utm_ad_source are set explicitly, they take priority over network-specific parameters. Standard UTM parameters (utm_source, utm_medium, utm_campaign, utm_term, utm_content) are always captured, along with extended params: utm_id, utm_source_platform, utm_campaign_id, utm_creative_format, utm_marketing_tactic.

UTM and referrer data persists for the duration of a session so it's attributed to all events, not just the landing page.

Reserved Properties

LA Analytics has reserved some properties that have semantic meanings for contacts and will handle them in special ways. For example, email is always expected to be a string of the user's email address, this is important for when sending emails using the marketing app. You must use the exact property keys listed below.

Reserved Contact Properties

These properties have semantic meaning and are used by features like email marketing. Use the exact key names below in your identify calls.

| Property | Type | Description | | ----------- | ------ | ---------------------------------------------------------------------------- | | displayName | String | The preferred display name, defaults to "firstName lastName" | | email | String | Email address of the user | | firstName | String | First name of the user | | lastName | String | Last name of the user | | name | String | Full name of the user | | title | String | Job title or position (e.g. "VP of Engineering") | | phone | String | Phone number of the user | | avatar | String | URL to an avatar image for the user | | gender | String | Gender of the user | | age | Number | Age of the user | | birthday | Date | The users date of birth | | website | String | Personal or company website | | username | String | Username, should be unique per user (like Twitter or GitHub usernames) | | description | String | Bio or about text | | createdAt | Date | Date the user account was first created. We recommend ISO-8601 date strings. |

Default Contact Properties

Our SDKs automatically collect certain properties on every user profile. Any property that starts with $ is generated by the SDK or ingestion pipeline. These properties are readonly and cannot be set via identify calls.

| Property | Display Name | Description | | ----------------------- | --------------------------- | --------------------------------------------------------------------------- | | $id | Distinct ID | Unique identifier from your application, coerced to a string | | $lastSeen | Last Seen | Last activity timestamp | | $lastIdentifiedAt | Last Identified | Timestamp of the most recent identify() call | | $processedAt | Processed At | Timestamp when the most recent event was processed | | $subscribed | Subscribed | Email subscription status | | $unsubscribeReason | Unsubscribe Reason | Reason the user was unsubscribed (e.g. complained, bounced) | | $unsubscribeSource | Unsubscribe Source | Source of the unsubscribe action | | $os | Operating System | OS from the most recent session | | $browser | Browser | Browser from the most recent session | | $browserVersion | Browser Version | Browser version from the most recent session | | $screenHeight | Screen Height | Height of the device screen in pixels | | $screenWidth | Screen Width | Width of the device screen in pixels | | $screenDpi | Screen DPI | Pixel density of the device screen | | $userAgent | User Agent | Browser user agent from the most recent session | | $ip | IP Address | IP address from the most recent session | | $timezone | Timezone | Timezone from the most recent session (e.g. America/New_York) | | $city | City | City from the most recent session | | $country | Country | Country from the most recent session | | $countryCode | Country Code | Country code from the most recent session | | $continent | Continent | Continent from the most recent session | | $region | Region | State or province from the most recent session | | $locale | Locale | Preferred language of the user | | $latitude | Latitude | Latitude from the most recent session | | $longitude | Longitude | Longitude from the most recent session | | $referrer | Last Touch Referrer | Referring URL from the most recent session. Defaults to "direct" | | $referringDomain | Last Touch Referring Domain | Referring domain from the most recent session. Defaults to "direct" | | $initialReferrer | Initial Referrer | Referring URL from the users first tracked session. Defaults to "direct" | | $initialReferringDomain | Initial Referring Domain | Referring domain from the users first tracked session. Defaults to "direct" | | $utmSource | Last Touch UTM Source | UTM source from the most recent session | | $utmMedium | Last Touch UTM Medium | UTM medium from the most recent session | | $utmCampaign | Last Touch UTM Campaign | UTM campaign from the most recent session | | $utmTerm | Last Touch UTM Term | UTM term from the most recent session | | $utmContent | Last Touch UTM Content | UTM content from the most recent session | | $initialUtmSource | Initial UTM Source | UTM source from the users first tracked session | | $initialUtmMedium | Initial UTM Medium | UTM medium from the users first tracked session | | $initialUtmCampaign | Initial UTM Campaign | UTM campaign from the users first tracked session | | $initialUtmTerm | Initial UTM Term | UTM term from the users first tracked session | | $initialUtmContent | Initial UTM Content | UTM content from the users first tracked session |

Reserved Integration Properties

| Property | Display Name | Description | | -------------------- | --------------------- | ------------------------------------------------ | | $stripeCustomerId | Stripe Customer ID | The Stripe customer ID associated with this user | | $stripeTotalPayments | Stripe Total Payments | Cumulative count of successful payments | | $stripeTotalSpent | Stripe Total Spent | Cumulative amount across all payments |

Reserved event properties

Properties used to calculate revenue for different traffic sources and LTV for users.

| PROPERTY | TYPE | DESCRIPTION | | -------- | ------ | ----------------------------------------------------------------------------------------- | | revenue | Number | Amount of revenue an event resulted in. This should be a decimal value | | currency | String | Currency of the revenue an event resulted in. This should be sent in the ISO 4127 format. | | value | Number | An abstract numerical value used internally to score events, such as lead scoring. |

Default Event Properties

Our SDKs automatically collect certain properties on every event or user profile. The default properties begin with a $ and can be overwritten with your own identify calls, so we recommend avoiding leading $ in your identify call properties to avoid conflicts.

| Property | Display Name | Description | | ---------------- | --------------------------- | ------------------------------------------------------------------------- | | $city | City | The city of the user parsed from the IP. | | $countryCode | Country Code | The country of the user parsed from the IP property. | | $latitude | Latitude | Latitude of the user's IP location. | | $longitude | Longitude | Longitude of the user's IP location. | | $timezone | Timezone | Timezone of the user parsed from the IP. | | $locale | Locale | The preferred language of the user. | | $pathname | Pathname | The path of the page on which the event was tracked. | | $title | Page Title | The title of the page on which the event was tracked. | | $userAgent | User Agent | The user agent string of the browser. | | $screenHeight | Screen Height | The height of the device screen in pixels | | $screenWidth | Screen Width | The width of the device screen in pixels | | $screenDpi | Screen DPI | Pixel density of the device screen. | | $href | Current URL | The URL of the page on which the event was tracked. | | $os | Operating System | The most recent OS of the user. | | $browser | Browser | The most recent browser of the user. | | $browserVersion | Browser Version | The most recent browser version of the user. | | $device | Device Type | The most recent device type of the user. eg Mobile, Tablet, Desktop | | $referrer | Last Touch Referrer | Referring URL from the most recent session. Defaults to "direct" | | $referringDomain | Last Touch Referring Domain | Referring domain from the most recent session. Defaults to "direct" | | $utmSource | UTM Source | UTM source from the most recent session | | $utmMedium | UTM Medium | UTM medium from the most recent session | | $utmCampaign | UTM Campaign | UTM campaign from the most recent session | | $utmTerm | UTM Term | UTM term from the most recent session | | $utmContent | UTM Content | UTM content from the most recent session | | $utmAdSource | Ad Source | Ad network that drove the click (auto-detected or from utm_ad_source) | | $utmAdId | Ad ID | Ad click ID (auto-detected or from utm_ad_id) |

Reserved Events with Optional Properties

These event names have semantic meaning and may be used by future dashboard features. Using them now future-proofs your implementation.

Reserved events are common events that happen during the lifecycle of a user, with optional properties to capture more detailed information, including revenue data where appropriate.

Signed Up

Event triggered when a user signs up. | Property | Description | |----------|----------------------------------| | source | How the user found your site. | | value | Track an estimated value of the signup|

Example:

analytics.track("Signed Up", {
  source: "Referral by friend",
});

Signed In

Event triggered when a user signs in. | Property | Description | |----------|--------------------------------------------------| | provider | The provider used for signing in (e.g., email, github, google). |

Example:

analytics.track("Signed In", {
  provider: "email",
});

Note: When tracking Signed In events call identify('user_id', {email: '...'}) before the event to connect to the user.

Signed Out

Event triggered when a user signs out. No optional properties.

Example:

analytics.track("Signed Out");

Note: When tracking Signed Out events call analytics.logout() after tracking the event to reset the session.

Trial Started

Event triggered when a user starts a trial. | Property | Description | |-------------------|---------------------------------------------------------------------| | duration | The duration of the trial in days | | type| Values can be optIn for when the user didn't provide a cc or optOut when the user provided a cc and it will automatically start at the end of the trial period|

Example:

analytics.track("Trial Started", { duration: 14, type: "optOut" });

Trial Ended

Event triggered when a user's trial ends. | Property | Description | |-------------------|---------------------------------------------------------------------| | daysLeftInTrial| If a user manually upgrades before the end of the trial you can record this here|

Example:

analytics.track("Trial Ended", { daysLeftInTrial: 4 });

Payment Completed

Event triggered when a payment is completed. This may be called on the server side after a confirmation webhook. This is helpful for recording ongoing subscription payments.

| Property | Description | | ---------- | --------------------------------------------------- | | revenue | The initial payment amount. | | currency | The currency of the payment, otherwise assumed USD. |

Example:

analytics.track("Payment Completed", { revenue: 29.99, currency: "GBP" });

Subscription Started

Event triggered when a user starts a subscription. If you have integrated with stripe for revenue tracking or are doing server side revenue tracking with webhooks you should skip the revenue and currency properties to prevent duplicate revenue tracking.

| Property | Description | | ---------- | ----------------------------------------------------------------------------------------------------------------------- | | plan | The name or ID of the subscription plan. | | revenue | The initial payment amount. Exclude if triggering Payment Completed to avoid double counting. | | currency | The currency of the payment, otherwise assumed USD. Exclude if triggering Payment Completed to avoid double counting. |

Example:

analytics.track("Subscription Started", {
  plan: "Monthly",
});

Subscription Cancelled

Event triggered when a subscription is cancelled.

| Property | Description | | -------- | -------------------------------------------------------------------------------------------------- | | reason | The reason for cancellation. This can be used in the feedback and analysis of cancellation reasons |

Example:

analytics.track("Subscription Cancelled", {
  reason: "Not using it any more",
});

Subscription Upgraded

Event triggered when a subscription is upgraded. | Property | Description | |-----------------|-----------------------------------------------------------------------------------------------| | fromPlan | The previous plan. | | toPlan | The new plan. | | previousRevenue | The revenue from the previous plan. | | newRevenue | The revenue from the new plan. | | revenue | If the customer is charged immediately then include a revenue number here | | currency | The currency of the payment, assumed to be USD unless otherwise specified. |

Example:

analytics.track("Subscription Upgraded", {
  fromPlan: "Monthly",
  toPlan: "Annual",
  previousRevenue: 29,
  newRevenue: 129,
});

Subscription Downgraded

Event triggered when a subscription is downgraded. | Property | Description | |------------|-------------------| | fromPlan | The previous plan. | | toPlan | The new plan. | | previousRevenue | The revenue from the previous plan. | | newRevenue | The revenue from the new plan. | | revenue | If the customer is refunded immediately then include a revenue number here, in the case of a downgrade this will be a negative value. Only include the revenue here if it's not tracked on your backend or with a webhook | | currency | The currency of the payment, assumed to be USD unless otherwise specified. |

Example:

analytics.track("Subscription Downgraded", {
  fromPlan: "Annual",
  toPlan: "Monthly",
  previousRevenue: 129,
  newRevenue: 29,
  revenue: -36,
  currency: "USD",
});

Use the hosted SDK

To use the hosted JS SDK, add the following script to your HTML header: All properties should be added as data attributes in the format data-[prop name]

<script
  src="https://analytics.tscull.workers.dev/sdk/la-analytics.js"
  data-projectId="YOUR-PROJECT-ID"
></script>

All the properties for initializing the sdk can be passed in like this:

<script
  src="https://analytics.tscull.workers.dev/sdk/la-analytics.js"
  data-projectId="YOUR-PROJECT-ID"
  data-debug="true"
  data-enabled="true"
  data-proxy="https://your-proxy.com/"
  data-autoPageTracking="true"
></script>

CDN Integrations

You can configure marketing integrations via data attributes on the script tag:

<script
  src="https://analytics.tscull.workers.dev/sdk/la-analytics.js"
  data-projectId="YOUR-PROJECT-ID"
  data-facebook-pixel-id="YOUR_PIXEL_ID"
  data-facebook-pixel-event-mapping='{"Signed Up":"CompleteRegistration"}'
  data-tiktok-pixel-id="YOUR_TIKTOK_PIXEL_ID"
  data-tiktok-pixel-event-mapping='{"Signed Up":"CompleteRegistration"}'
  data-pinterest-tag-id="YOUR_PINTEREST_TAG_ID"
  data-pinterest-tag-event-mapping='{"Signed Up":"signup"}'
></script>

Calling methods

You can then call the track and identify methods like so:

laAnalytics.track("Searched Movies");
laAnalytics.identify("user_123", { email: "[email protected]" });
laAnalytics.logout();

For type safety in your TypeScript project, you can use the following in an index.d.ts file

import { Analytics } from "la-analytics";

declare global {
  interface Window {
    laAnalytics?: Analytics;
  }
}