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

tryfeature

v0.0.7

Published

Try Feature as Ability and Usage. Track consumptions.

Readme

TryFeature

TryFeature is an easy-to-use library for managing users and their associated features. It provides a simple, yet flexible API to handle both ability-typed features (e.g., permissions) and usage-typed features (e.g., quota-based actions). Use this library to easily migrate your data models, create users, define features, organize them into groups, and control feature grants, revocations, and consumption.

TryFeature is inspired by Laravel package called UseIt, and under the hood, sutando is used to interact with database.

📝 Table of Contents

✨ Features

Feature Management

  • Create features with Feature.firstOrCreate.
  • Support for two feature types:
    • Ability-typed features: Represent permissions or capabilities.
    • Usage-typed features: Represent quota-based actions (requires a specified quantity).
  • Error handling for incorrect feature setup (e.g., missing quantity for usage features).

Feature Grouping

  • Create feature groups with FeatureGroup.firstOrCreate.
  • Add and remove features from groups using featureGroup.addFeatures and featureGroup.removeFeatures.
  • Grant all features of a feature group by name or object using user.grantFeatureGroup

Granting/Revoking Feature Or Feature Group

  • Grant feature using user.grantFeature or user.grantFeatureGroup
  • Revoke feature using user.revokeFeature or user.revokeFeatures

Usage Consumption

  • Consume usage-based features with user.try.
  • Track every consumption of an usage.

And more

  • Include migrations for all core models such as User, Feature, Ability, FeatureGroup, Usage, and Consumption.
  • Easy to extend

🤔 How it works

Flowchart

🔌 Installation

npm i tryfeature
# or
pnpm i tryfeature
# or
yarn add tryfeature

👋 Getting started

  • Migration

Before we get started, we need to migrate required tables first.

  • Connection: Since sutando is used under the hood, we need to setup database connection first. Please consult its documentation page for more details.
// Example
import { sutando } from 'sutando';

sutando.addConnection({
    client: 'sqlite3',
    connection: {
        filename: ":memory:"
    },
    useNullAsDefault: true,
});
  • Migrate Tables: In order to save time, you can use migration helper function migrate of each models. You can pass prefix for the table in case there is already existing table.

You can also reference this sql file.

// set up connection
// ...
// migrate
const prefix = ''
await User.migrate(prefix);
await Feature.migrate(prefix);
await Ability.migrate(prefix);
await FeatureGroup.migrate(prefix);
await Usage.migrate(prefix);
await Consumption.migrate(prefix);

By default, migrate function checks if there is already existing table. If exists, it skips the migraiton. Pass false as second parameter in migrate function such as

const skipOnExist = false;
await User.migrate('', skipOnExist)
  • User Model

By default, User model has the following schema structure. In order to add more columns, please extend the model and add more columns in migrate function.

table.increments('id').primary();
table.string('email').unique();
table.timestamps();

To create an user, you can use User.firstOrCreate async function.

import { User } from 'tryfeature';

const email = '[email protected]'

const user = await User.firstOrCreate(email);
  • Feature Model

Schema structure:

table.increments('id').primary();
table.string('name').unique();
table.enum('type', ['ability', 'usage']);
table.text('description').nullable();
table.bigInteger('quantity').nullable();
table.timestamps();

To create a feature, you can use Feature.firstOrCreate. It has four parameters.

Feature.firstOrCreate(name: string, type: FeatureType, quantity?: number, description?: string)
// Examples
import { Feature, FeatureType } from 'tryfeature';

const abilityFeature = await Feature.firstOrCreate('can-view', FeatureType.ability);

const usageFeature = await Feature.firstOrCreate('api-call', FeatureType.usage, 100);
  • Feature Group Model

Feature and Feature Group has many-to-many relationship type. Schema structure:

table.increments('id').primary();
table.string('name').unique();
table.text('description').nullable();
table.timestamps();

To create a feature group, you can use FeatureGroup.firstOrCreate. It has two parameters.

FeatureGroup.firstOrCreate(name: string, description?: string)
// Examples
import { FeatureGroup } from 'tryfeature';

const basicUser = await FeatureGroup.firstOrCreate('basic-user');

To add feature to feature group, you can use either FeatureGroup.addFeature or FeatureGroup.addFeatures.

import { Feature, FeatureGroup } from 'tryfeature';

const feature = await Feature.query().first();

await FeatureGroup.addFeature(feature);

await FeatureGroup.addFeatures([feature]);

To remove feature from feature group, use FeatureGroup.removeFeature.

import { Feature, FeatureGroup } from 'tryfeature';

const feature = await Feature.query().first();

await FeatureGroup.removeFeature(feature);

🔎 Usage

  • Granting Feature

You can grant a user to a feature by passing feature name or feature object and expire date.

import { User } from 'tryfeature';

const user = await User.firstOrCreate('[email protected]')

await user.grantFeature('can-view', new Date((new Date).getTime() + 10000));

await user.grantFeature('api-call', new Date((new Date).getTime() + 10000));

In order to grant a feature group,

import { User } from 'tryfeature';

await user.grantFeatureGroup('basic-user');
  • Revoking Feature

Like granting feature, you can pass feature name or feature object.

import { User } from 'tryfeature';

const user = await User.firstOrCreate('[email protected]')

await user.revokeFeature('can-view');

await user.revokeFeatureGroup('basic-user');
  • Checking if you can try feature

Before you actually use feature, especially usage typed feature, you can check whether you can try feature.

import { User } from 'tryfeature';

const user = await User.firstOrCreate('[email protected]')

await user.canTry('can-view'); // return boolean

await user.canTry('api-call', 10); // return boolean

Please pass amount to be consumed as second parameter for usage typed feature.

  • Trying feature

    • Ability feature: Trying the feature returns boolean.
    • Usage feature: Trying the feature returns an array of Consumption that are consumed.
import { User } from 'tryfeature';

const user = await User.firstOrCreate('[email protected]')

await user.try('can-view'); // return boolean

await user.try('api-call', 10); // return boolean

When multiple usage entries exist, those expiring sooner will be prioritized for consumption.

  • Listing current available features

You can list current available abilities and usages.

await user.getAvailableFeatures();

The method accepts three parameters:

  • ability: true or false to include ability typed features
  • usage: true or false to include usage typed features
  • include_expire: true or false to include expired features
// To get only ability typed features
await user.getAvailableFeatures(true);

// To get only usage typed features
await user.getAvailableFeatures(false, true);

// To include expired features
await user.getAvailableFeatures(true, true, true);

📄 License

MIT

💖 Show Your Support

Please support me on github if this project helped you.