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

eiam-resource-scanner

v4.1.3

Published

Reusable Angular resource scanner: discovers the permission codes a page declares through checkRole, enriches them from a resource manifest, and synchronises the resource definition with a backend Resource Scan API. Discovery only - never an authorization

Downloads

78

Readme

eiam-resource-scanner

Your pages already declare their permission codes:

<button checkRole="CANADD">Add User</button>
<button checkRole="CANEDIT" matTooltip="Edit user">Edit</button>

This package reads them and tells IAM they exist — so nobody has to type them in by hand.

// one request per page, sent automatically
{
  "Slug": "users/list",
  "Component": "ListUsersComponent",
  "ExcludedTypes": [],
  "Permissions": [
    { "Code": "CANADD",  "Name": "CANADD",    "IsEnabled": true, "Type": 1 },
    { "Code": "CANEDIT", "Name": "Edit user", "IsEnabled": true, "Type": 1 }
  ]
}

No template changes. No page-by-page setup.

Shared infrastructure: EIAM, GO, EXIM, DCMS and any future project use the same package and differ only in configuration.


Setup — three steps

1. Install

npm install eiam-resource-scanner

2. Configure once, in the root

import { ResourceScannerModule } from 'eiam-resource-scanner';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    HttpClientModule,
    ResourceScannerModule.forRoot({
      application: 'EIAM',                              // your application key
      apiUrl: '/PagePermissionApi/SyncPermissions',     // your backend endpoint
      autoDiscover: true,                               // scan every page
      enabled: !environment.production,                 // the ON/OFF switch
    }),
  ],
})
export class AppModule {}

Standalone application? Use provideResourceScanner({ ... }) with the same options.

3. Import the module wherever checkRole is used

@NgModule({
  imports: [CommonModule, ResourceScannerModule],   // <- one line, no forRoot
})
export class UserManagementModule {}

That is the whole integration.


The ON/OFF switch

enabled decides whether the scanner does anything at all. Off means nothing is registered and no request is ever made — as if the package were not installed.

Give it a boolean, or a function that returns one. Where that answer comes from is entirely up to you.

enabled: true                                     // always
enabled: !environment.production                  // recommended
enabled: () => this.flags.isOn('scanner')         // from a service
enabled: () => currentUser.isAdministrator        // from the user

A boolean is read once at startup. A function is asked every time, so a flag that changes later takes effect with no rebuild and no reload.

You can also flip it from anywhere in the application:

const scanner = inject(ResourceScannerService);

scanner.setEnabled(false);      // stop now
scanner.setEnabled(true);       // resume
scanner.setEnabled(undefined);  // back to the configured value
scanner.isEnabled();            // what is in force right now

Three things worth knowing:

  • Turning it off never deletes anything. It is a pause, not a rollback.
  • Turning it back on sends anything discovered but not yet accepted. If everything was already accepted there is nothing to send — that is correct, not a fault.
  • If your function throws, the scanner treats that as off, says so once, and carries on. Your page keeps working.

Control types and excludedTypes

Every permission is sent with the Type of the control that declared it. The scanner works it out from the element itself — nothing to add to your templates:

| Type | Value | Detected from | | --- | --- | --- | | BTN | 1 | <button>, <input type="button/submit/reset">, mat-*-button, role="button" | | DropDown | 2 | <select>, mat-select, ng-select, p-dropdown, role="combobox" | | TextBox | 3 | <input> (text, email, number, …), <textarea>, matInput | | CheckBox | 4 | <input type="checkbox">, mat-checkbox, mat-slide-toggle | | RadioButton | 5 | <input type="radio">, mat-radio-button, mat-radio-group | | Link | 6 | <a>, role="link" | | Tab | 7 | mat-tab, mat-tab-link, role="tab" | | Grid | 8 | <table>, mat-table, ag-grid-angular, p-table, kendo-grid | | GridColumn | 9 | <th>, <td>, matColumnDef, mat-header-cell, mat-cell | | Menu | 10 | <nav>, <menu>, mat-menu-item, <li> inside a menu, role="menu" | | Section | 11 | <div>, <section>, <fieldset>, <form>, mat-card, mat-expansion-panel | | Field | 12 | <label>, mat-form-field, and any control not listed above |

To stop a type from being scanned, list it in excludedTypes:

import { ResourceElementType } from 'eiam-resource-scanner';

ResourceScannerModule.forRoot({
  // ...
  excludedTypes: [ResourceElementType.GridColumn, ResourceElementType.Field],
})

The list is sent with every request as ExcludedTypes; the backend skips every permission whose Type is in it. Only values of the enum above are accepted — anything else is dropped.


What it will never do

  • It is not an authorization authority. It never grants, revokes or checks access.
  • It never hides, disables, removes or styles anything on your page.
  • It never deletes a permission.
  • It never changes your existing checkRole behaviour.
  • It never throws an error into your application.

Good to know

Names are a suggestion, not an identity. The code is the identity. The name comes from scanName, then a tooltip / title / aria-label, then the control's own label, then the code itself. IAM writes the name only when it creates a permission — so rename it there once and no scan will ever overwrite it.

One request, not a hundred. Fifty buttons sharing CANEDIT produce one entry, and a page with 150 controls produces one request.

The component name is real. Component is the actual routed class, ListUsersComponent, worked out from the application itself — in development and in production alike. Nothing to configure, no map to generate.


Optional: scan at build time too

The directive only sees a control once Angular creates it, so a page nobody opened is never discovered. The CLI reads your source instead:

npx eiam-resource-scan --dir ./src --dry-run --verbose

Use both: the CLI makes the catalogue complete, the directive catches what only exists at runtime.

Keep scanning and writing apart. Writing to IAM is a deployment step. Never put it in npm run build, or every developer machine and every CI job would change the permissions database without anyone intending it.

"scripts": {
  "build": "ng build",
  "scan:permissions": "eiam-resource-scan --dir ./src --dry-run",             // safe anywhere
  "sync:permissions": "eiam-resource-scan --dir ./src --api-url $SCAN_URL"    // deployment only
}

Options

| Option | Default | What it does | | --- | --- | --- | | application | required | Your application key, e.g. EIAM. | | apiUrl | required | Your endpoint. A relative URL goes through your own HttpClient, so your interceptors and auth apply. | | enabled | true | ON/OFF. A boolean, or a function returning one. | | autoDiscover | false | Scan every page. Turn this on. | | resources | — | Optional: nicer names up front, for the pages you care about. | | batchDelay | 250 | How long to wait before sending, in ms. | | validation | 'warn' | What to say about a code that is not in your manifest. | | nameAttributes | matTooltip, tooltip, ngbTooltip, title, aria-label, data-name | Where to look for a readable name. | | componentMap | — | Rarely needed: override Component for one page. | | excludedTypes | [] | Control types the backend should not scan. See Control types. | | debug | false | Log what is happening, while you integrate. |

Requirements

Angular 16+, RxJS 7+, HttpClient and Router provided, and an existing directive with the selector [checkRole].

Backend

POST to your apiUrl with { Slug, Component, ExcludedTypes[], Permissions[{ Code, Name, IsEnabled, Type }] }. The operation must be additive — it never deletes. ResourceScannerTransport is replaceable if your contract differs.


Full walkthrough, checklist and troubleshooting: USER-MANUAL.md Changes between versions: CHANGELOG.md