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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@curiouslearning/analytics

v1.3.0

Published

A wrapper project for curious learning analytics

Readme

Introduction

A wrapper project for curious learning analytics.

Setup

Simply add as dependency.

$ npm i @curiouslearning/analytics

CircleCI

Usage

import { AnalyticsService, FirebaseStrategy } from '@curiouslearning/analytics';

const analytics = new AnalyticsService();

// each strategy will have its own set of configuration options.


// using firebase strategy
const firebaseStrategy = new FirebaseStrategy({
  firebaseOptions: {
    apiKey: 'api-key',
    authDomain: 'my-domain.firebaseapp.com',
    databaseURL: 'https://my-db.firebaseio.com',
    projectId: 'project-id',
    storageBucket: 'my-bucket.appspot.com',
    messagingSenderId: 'sender-id',
    appId: 'app-id',
    measurementId: 'G-measurement-id',
  },
  userProperties: {
    campaign_id: 'my-campaign',
    source: 'my-source'
  }
});

await firebaseStrategy.initialize();
analytics.register('firebase', firebaseStrategy);
analytics.track('initialized', { test: 'test' }); // fires a tracking event to firebase

// using statsig strategy
const statsigStrategy = new StatsigStrategy({
  clientKey: 'statsig-api-key';
  statsigUser: {
    userId: 'my-psuedo-user-id'
  };
});

await statsigStrategy.initialize();
analytics.register('statsig', statsigStrategy);
analytics.track('initialized', { test: 'test' }); // fires a tracking event to firebase and statsig

Event Schemas & Validation

The Analytics package is using Zod for event validation. Built-in event schemas are provided (FTM), and you can also define your own custom schemas.

Using Built-in Event Schemas

You can import the default event schemas and event names:

import { DefaultEventSchemas, EventNames } from '@curiouslearning/analytics';

// Example: Use EventNames to ensure correct event names
const eventName = EventNames.SESSION_START;

// Example: Validate event params manually (optional)
import { z } from 'zod';
const params = { /* ... */ };
const result = DefaultEventSchemas[eventName].safeParse(params);
if (!result.success) {
  console.error(result.error);
}

Using StatsigStrategy with Validation

When you use the StatsigStrategy, event parameters are automatically validated against the built-in schemas (and any custom schemas you provide). Invalid events will include validation errors in the metadata.

import { StatsigStrategy, EventNames } from '@curiouslearning/analytics';

const statsigStrategy = new StatsigStrategy({
  clientKey: 'your-statsig-key',
  statsigUser: { userID: 'user-123' },
  debug: true // enables validation error logging
});

await statsigStrategy.initialize();

// This will be validated against the SESSION_START schema
statsigStrategy.track(EventNames.SESSION_START, {
  profile_number: 123,
  cr_user_id: 'user123',
  ftm_language: 'en_US',
  version_number: '1.2.3',
  json_version_number: '2.0.0',
  days_since_last: 2
});

Common Validators (common-params)

This package provides reusable validators in common-params for required fields, such as requiredString, requiredNumber, and requiredDate. These helpers wrap Zod's types to provide consistent required field validation and clear error messages. They are used in all built-in event schemas and are recommended for custom schemas as well.

Example:

import { requiredString, requiredNumber } from '@curiouslearning/analytics/validators';

const schema = z.object({
  event_date: requiredString('Event date'),
  duration: requiredNumber('Duration in seconds')
});
  • requiredString('Field Name') ensures the field is present and a string, with a custom error message.
  • requiredNumber('Field Name') ensures the field is present and a number, with a custom error message.
  • See common-params.ts for more helpers like requiredDate, requiredNonNegativeNumber, and requiredDateInFormat.

Defining and Using Custom Schemas

You can define your own Zod schemas and pass them to the strategy. Custom schemas will override or extend the built-in ones. For consistency and better error messages, use the provided validators from common-params:

import { z } from 'zod';
import { StatsigStrategy, EventNames } from '@curiouslearning/analytics';
import { requiredString } from '@curiouslearning/analytics/validators';

const customSchemas = {
  [EventNames.SESSION_START]: z.object({
    event_date: requiredString('Event date'), // uses the shared validator
    custom_field: requiredString('Custom field')
  })
};

const statsigStrategy = new StatsigStrategy({
  clientKey: 'your-statsig-key',
  statsigUser: { userID: 'user-123' },
  customEventSchemas: customSchemas,
  debug: true
});

await statsigStrategy.initialize();

// This will be validated against your custom SESSION_START schema
statsigStrategy.track(EventNames.SESSION_START, {
  profile_number: 123,
  cr_user_id: 'user123',
  ftm_language: 'en_US',
  version_number: '1.2.3',
  json_version_number: '2.0.0',
  days_since_last: 2,
  custom_field: 'my-value'
});

Other Use Cases

A single strategy trigger

const firebaseStrategy = analytics.getRegistry('firebase');
firebaseStrategy.track('test', { test: 'test' });

A custom strategy

Make sure to extend the abstract strategy to making sure all necessary implementations are present.

import { AbstractAnalyticsStrategy } from '@curiouslearning/analytics';

export class CustomStrategy extends AbstractAnalyticsStrategy {
  async initialize() {
    // initialization
  }

  track(evetName: string, data: any) {
    // do something with eventName and data
  }
}

// ..
const customStrategy = new CustomStrategy();
await customStrategy.initialize();
analytics.register('custom', customStrategy);

analytics.track('test', { foo: 'baz' });

Resource Cleanup (v1.3.0+)

Both Firebase and Statsig strategies provide a dispose method to properly clean up resources when they are no longer needed. This is particularly important to prevent memory leaks and ensure proper shutdown of connections.

dispose has been added in version 1.3.0.

Firebase Strategy

const firebaseStrategy = new FirebaseStrategy({
  // ... configuration ...
});

// When done with the strategy
firebaseStrategy.dispose();  // This will delete the Firebase app instance if it exists

Statsig Strategy

const statsigStrategy = new StatsigStrategy({
  // ... configuration ...
});

// When done with the strategy
statsigStrategy.dispose();  // This will shut down the Statsig client if it exists

It's recommended to call dispose() when:

  • Your application is shutting down
  • You're switching to a different analytics strategy
  • You need to reinitialize the strategy with different configuration

Caveats

We are leaving the configuration option definition on the consuming app layer since we may want to have different buckets or measurement ids for these apps.