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

@admicaa/netpress-permissions

v0.2.3

Published

Laravel-style roles, permissions, policies, and auth guard package for NetPress.

Readme

NetPress Permissions

Laravel-style roles, permissions, morph assignments, and can() integration for NetPress.

What It Includes

  • Role and Permission models
  • Polymorphic model assignments for roles and direct permissions
  • Trait-style mixins for authorizable models
  • can() support that works with both registered policies and direct permissions
  • Migration helpers for creating the package tables
  • Guard-scoped roles and permissions (guardName) — the same role/permission name can coexist across multiple auth guards (web, api, admin, …)
  • AuthGuard route middleware for authenticated/can/role/permission/guest gates
  • A publishable config, migration, and service provider via vendor:publish

Publish Into An App

From a NetPress application:

npm run artisan -- vendor:publish --provider=NetpressPermissionsServiceProvider

The publish flow will:

  • ask which configured database connection should store the permissions data
  • publish config/permissions.js
  • publish database/migrations/*_create_permission_tables.js
  • publish app/Providers/PermissionsServiceProvider.js
  • register the provider in bootstrap/providers.js

If you pick a Mongo connection, the published migration targets that Mongo connection automatically.

Quick Start

import { BaseModel, registerPolicy } from '@admicaa/netpress';
import {
  Authorizable,
  Role,
  Permission,
  createPermissionTables,
} from '@admicaa/netpress-permissions';

class User extends Authorizable(BaseModel) {
  static table = 'users';
}

class Post extends BaseModel {
  static table = 'posts';
}

class PostPolicy {
  async update(authUser, post) {
    return authUser.id === post.userId;
  }
}

registerPolicy(Post, PostPolicy);

Migrations

Create an application migration that delegates to the package helper:

import { createPermissionTables, dropPermissionTables } from '@admicaa/netpress-permissions';

export async function up(schema) {
  await createPermissionTables(schema);
}

export async function down(schema) {
  await dropPermissionTables(schema);
}

Usage

const editor = await Role.findOrCreate('editor');
const publishPosts = await Permission.findOrCreate('posts.publish');

await editor.givePermissionTo(publishPosts);

const user = await User.create({ name: 'Amina' });
await user.assignRole(editor);

await user.can('posts.publish'); // true
await user.can('update', post);  // policy-aware

Guards (guardName)

Every role and permission has a guardName column, mirroring Laravel Spatie. It lets the same name live in different auth guards (for example admin under web and admin under api).

The default guard is resolved in this order:

  1. config.permissions.defaultGuard (if set)
  2. The core Auth.defaultGuard() (i.e. whatever you called Auth.setDefaultGuard(...) with)
  3. 'web' as a final fallback
// Create role/permission scoped to a specific guard:
const webAdmin = await Role.findOrCreate('admin', 'web');
const apiAdmin = await Role.findOrCreate('admin', 'api');

// Second argument may be guard *or* values (Spatie-style convenience):
const perm = await Permission.findOrCreate('posts.publish', { group: 'posts' });

Pin a user model to a specific guard by declaring it on the class — this is the hook the traits use (getDefaultGuardName()), equivalent to Spatie's model method of the same name:

class ApiUser extends Authorizable(BaseModel) {
  static table = 'users';
  static guardName = 'api'; // roles/permissions resolved under the 'api' guard
}

const apiUser = await ApiUser.create({ name: 'Amina' });
await apiUser.assignRole('admin');       // resolves 'admin' in the 'api' guard
await apiUser.hasRole('admin');          // only matches api-scoped admin rows
await apiUser.hasPermissionTo('posts.publish');

String-based lookups (assignRole('admin'), hasRole('admin'), hasPermissionTo('posts.publish'), …) are all filtered by the user's resolved guard, so a role named admin under web will not satisfy a check made by an api user.

AuthGuard middleware

Express-compatible middleware factories for the most common authorization gates. All middleware resolves the current user via the core Auth layer and responds with the conventional 401/403 status codes.

import { AuthGuard } from '@admicaa/netpress-permissions';

router.get('/dashboard',     AuthGuard.authenticated(),      showDashboard);
router.get('/posts/:id/edit', AuthGuard.can('posts.update', 'id'), editPost);
router.post('/posts',         AuthGuard.permission('posts.publish'), createPost);
router.delete('/users/:id',   AuthGuard.role('admin', 'owner'), deleteUser);
router.get('/billing',        AuthGuard.roles('admin', 'billing'),   billing);
router.get('/login',          AuthGuard.guest(),              loginPage);

Available gates:

| Gate | Allows a request through when… | | --- | --- | | AuthGuard.authenticated() | a user is authenticated on the active guard (else 401) | | AuthGuard.can(ability, resource?) | the user passes can(ability, resource) — integrates with registered policies (else 403) | | AuthGuard.canAny(abilities, resource?) | any of the listed abilities succeed | | AuthGuard.canAll(abilities, resource?) | every listed ability succeeds | | AuthGuard.role(...names) | user has any of the given roles | | AuthGuard.roles(...names) | user has all of the given roles | | AuthGuard.permission(...names) | user has any of the given permissions | | AuthGuard.permissions(...names) | user has all of the given permissions | | AuthGuard.guest() | no authenticated user on the active guard (else 403) |

The resource argument of can/canAny/canAll may be:

  • a string — read as req.params[resource] (e.g. 'id'req.params.id)
  • a function — called with req, return the resource to authorize against
  • any value — passed through as-is

Because the role/permission checks flow through the same getDefaultGuardName() hook on the user model, AuthGuard naturally respects guard scoping: an api user will only match api-scoped roles and permissions.