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-scan-resource-controls

v0.0.2

Published

Registers every checkRole permission code in the database automatically: a CLI that scans the source at build time, a runtime scan on route changes, and an optional per-control element that also replaces checkRole.

Readme

eiam-scan-resource-controls

Add a checkRole in the markup — the permission registers itself. No manual entry in Roles Manager, no code change anywhere else.

Three ways to use it — pick one (or combine 1 + 2)

| # | Way | What you change | Coverage | | --- | --- | --- | --- | | 1 | CLI at build time — npx eiam-scan-permissions | one npm script | every code in the source, even on pages nobody opened | | 2 | Runtime scan — startScanning() in main.ts | 2 lines, once | only the pages users actually visit | | 3 | Per control — <eiam-scan-resource-controls code="CADP"> | every control (also replaces checkRole) | the controls you converted |

Recommended: 1 as the base, 2 on top. The CLI guarantees complete coverage and runs in seconds; the runtime scan catches codes that are only known at runtime.


1. CLI — the fast, complete way

npx eiam-scan-permissions --dir ./src --api-url https://localhost:5000 --application-id 50

It reads every .html and .ts under --dir, pulls out every code declared on the directive, and posts them all in one request. On this project that is 206 codes from 806 files in a couple of seconds, without opening a single page.

Scanned 806 file(s) under D:\Git\EIAM-Git-New\Frontend\EIAM -Setup\src
Directives: checkRole, check-role, data-permission
Found 206 permission code(s) in 36 file(s)

Wire it into the app's package.json so it runs on every build and you never think about it again:

"scripts": {
  "build": "ng build && npm run scan:permissions",
  "scan:permissions": "eiam-scan-permissions --dir ./src --api-url https://localhost:5000 --application-id 50"
}

Options

| Option | Default | Description | | --- | --- | --- | | --dir | cwd | Folder to scan. | | --directives | checkRole,check-role,data-permission | The directive name(s) to look for — change it if you rename the directive. | | --extensions | .html,.ts | File types to read. | | --api-url | $EIAM_API_URL | Where to post. Without it nothing is sent. | | --application-id | — | Sent as the header and in the body. | | --endpoint | /api/RolesManagement/ScanPermissions | Scan API path. | | --permission-type | 1 | EPermissionType. | | --group-id | — | Permission group. | | --out | — | Write the codes to a JSON file instead of / as well as posting. | | --dry-run | — | Print what was found and send nothing. | | --verbose | — | List the codes per file. | | --insecure | — | Accept a self-signed certificate (local https servers). |

It reads checkRole="CADP", checkRole='CADP', checkRole="A,B" and [checkRole]="'CADP'". Values that only exist at runtime (checkRole="{{ code }}", [checkRole]="user.perm") cannot be read from the source, so they are reported separately rather than guessed — those are the ones way 2 covers.


2. Runtime scan — for codes that are built at runtime

Two lines in src/main.ts, nothing else in the whole application:

import { startScanning } from 'eiam-scan-resource-controls';

startScanning({ apiUrl: window['config']?.apiUrl, applicationId: environment.eiamSetupAppId });

On every route change it reads the rendered markup and registers anything new. Codes already sent are cached in sessionStorage, so navigating around costs nothing.

Because the checkRole directive removes an element when the user lacks the code, the scanner watches removedNodes as well as addedNodes — otherwise it would only ever see the permissions the signed-in user already holds.

| Option | Default | Description | | --- | --- | --- | | permissionAttributes | checkRole,checkrole,check-role,code,data-permission | Attributes to scan. | | scanDelay | 800 | Wait after a route change before scanning. | | observeWindow | 8000 | Keep watching the route for lazily rendered content. |

Plus every option from Shared configuration below. stopScanning() and scanNow() are exported too.


3. Per control — optional

Replaces checkRole and registers the permission at the same time.

Wrap a control, give it a code, and the package does both jobs:

  1. registers the code in the database, so it appears in Roles Manager and can be attached to a role in Add/Edit Role without anyone typing it in by hand;
  2. hides the control when the signed-in user does not hold the code — the same rule the checkRole directive applies today.

It is the localization scan (Localization/ScanResource) idea applied to authorization controls: the markup is the source of truth, the database follows it.

<!-- before -->
<button checkRole="CADP">Add</button>

<!-- after -->
<eiam-scan-resource-controls code="CADP">
  <button>Add</button>
</eiam-scan-resource-controls>

Nothing else changes in the component: no imports, no ngOnInit, no service.


Install

npm i eiam-scan-resource-controls

The CLI (way 1) needs nothing else — npx eiam-scan-permissions works straight away.

Ways 2 and 3 need the element registered and the API configured once, in src/main.ts:

import { defineCustomElements as defineScanResourceControls } from 'eiam-scan-resource-controls/loader';

// Read by every control on every page; no control repeats these settings.
(window as any).EIAMScanResourceControls = {
  apiUrl: window['config']?.apiUrl || '',
  applicationId: environment.eiamSetupAppId,
  cultureCode: 'en-US',
  isMicroservice: false,
};

platformBrowserDynamic().bootstrapModule(AppModule).then(() => {
  defineFavoritePagesElements(window);
  defineUserPreferenceElements(window);
  defineScanResourceControls(window);          // <-- add this
}).catch(err => console.log(err));

AppModule already declares CUSTOM_ELEMENTS_SCHEMA, so no other Angular change is needed.

The tag. The class is EIAMScanResourceControls; the HTML tag is eiam-scan-resource-controls. It has to be: the HTML parser lower-cases tag names and the custom element registry rejects a name without a hyphen, so <EIAMScanResourceControls> would be parsed as an unknown element and never upgrade — it would silently do nothing. This is the same name in the only casing the platform accepts, and it matches eiam-favorite-pages / eiam-user-preference.

Migrating from checkRole

| Today | With the package | | --- | --- | | <button checkRole="CADP">Add</button> | <eiam-scan-resource-controls code="CADP"><button>Add</button></eiam-scan-resource-controls> | | <a checkRole="CEORG,CVORGB">Edit</a> (all required) | <eiam-scan-resource-controls code="CEORG,CVORGB"><a>Edit</a></eiam-scan-resource-controls> | | <a checkRole="CEORG,CVORGB" [minRoleCheck]="true"> (any) | <eiam-scan-resource-controls code="CEORG,CVORGB" any="true"> |

Behaviour is matched deliberately: codes are trimmed and upper-cased, every code is required unless any is set, and a denied control is removed from the DOM, not merely hidden — exactly what RolePermissionDirective does with nativeElement.remove().

Migrating gradually? Add scan-only="true" and keep the existing checkRole on the inner element. The package then only registers the code and lets the old directive keep doing the hiding, so a page can be converted with zero behavioural risk:

<eiam-scan-resource-controls code="CADP" scan-only="true">
  <button checkRole="CADP">Add</button>
</eiam-scan-resource-controls>

Properties

| Property | Attribute | Type | Default | Description | | --- | --- | --- | --- | --- | | code | code | string | — | The permission code, or several separated by commas. | | name | name | string | — | Friendly name stored with the permission. Defaults to the code. | | any | any | boolean | false | With several codes, require only one (like minRoleCheck). | | permissionSource | permission-source | 'roleInfo' \| 'userPermissions' \| 'both' | 'roleInfo' | Which granted-code list is checked. | | scanOnly | scan-only | boolean | false | Register the code but never hide the control. | | denyMode | deny-mode | 'remove' \| 'hide' | 'remove' | How a denied control disappears. | | disabled | disabled | boolean | false | Skips the check and the registration entirely. |

Methods

| Method | Description | | --- | --- | | refresh(): Promise<boolean> | Re-runs the permission check, e.g. after permissions were reloaded. | | flushRegistrations(): Promise<void> | Sends queued codes now instead of waiting for the batch delay. | | getCodes(): Promise<string[]> | The normalised codes this control requires. |

Window events

| Event | Detail | | --- | --- | | eiamPermissionsScanned | { slug, permissions: [{ code, name }] } | | eiamPermissionsScanFailed | { slug, error } |

Shared configuration

Set window.EIAMScanResourceControls (as above) or call configure() once:

import { configure } from 'eiam-scan-resource-controls';

configure({ apiUrl: '...', applicationId: 50, grantedCodes: myPermissions });

| Option | Default | Description | | --- | --- | --- | | apiUrl | — | Base API url. Without it nothing is sent; codes stay queued. | | applicationId | — | Sent as the applicationId header and in the body. | | cultureCode | 'en-US' | Sent as the CultureCode header. | | isMicroservice | false | Builds the url as {apiUrl}/{service}/{endpoint}. | | endpoint | '/api/RolesManagement/ScanPermissions' | Where codes are registered. | | service | 'security' | Service segment, used only when isMicroservice is true. | | permissionType | 1 | EPermissionType stored with each code. | | groupId | — | Optional permission group for the scanned codes. | | grantedCodes | — | The user's codes. When omitted they are read from storage (see below). | | batchDelay | 300 | Milliseconds to collect controls before sending one request. | | disabled | false | Turns registration off; permission checking still works. | | debug | false | Logs every batch to the console. |

Where granted codes come from

Unless grantedCodes is configured, the component reads the same places the application already writes to:

  • permissionSource="roleInfo" (default) — localStorage.user.RoleInfo, what RolePermissionDirective uses today.
  • permissionSource="userPermissions" — sessionStorage.userPermissions, written by RolePermisssionService from the Applications/GetSlugInfo response.
  • permissionSource="both" — the union.

Feeding configure({ grantedCodes }) from RolePermisssionService.permissions$ is the cleanest option: it removes the dependency on storage key names entirely.

How registration works

Every control on a page queues its code into one shared queue. After batchDelay (300 ms) the queue posts one request for the whole page — a page with forty controls costs one call, not forty. Registered codes are cached in sessionStorage.eiamScannedPermissions, so revisiting a page sends nothing at all. A failed request drops the cache entry and the codes are retried on the next page.

POST {apiUrl}/api/RolesManagement/ScanPermissions
Content-Type: application/json
CultureCode: en-US
Source: 2
applicationId: 50
credentials: include
{
  "slug": "users-permissions/roles",
  "applicationId": 50,
  "type": 1,
  "groupId": null,
  "permissions": [
    { "code": "CADP", "name": "CADP" },
    { "code": "CEP",  "name": "Edit page" }
  ]
}

Backend action this needs — NOT YET IMPLEMENTED

RolesManagementController has AddPermission (one permission per call, AddPermissionsViewModel = Name, Code, Type, GroupId, RelatedActionIds) but no batch scan action. Add one that upserts by Code and ignores codes that already exist, mirroring LocalizationController.ScanResource:

[HttpPost]
[Route("ScanPermissions")]
public async Task<IActionResult> ScanPermissions(ScanPermissionsViewModel obj)
    => Ok(await _permissionService.ScanPermissionsAsync(obj));

public class ScanPermissionsViewModel
{
    public string Slug { get; set; }
    public long? ApplicationId { get; set; }
    public EPermissionType Type { get; set; }
    public long? GroupId { get; set; }
    public List<ScanPermissionVM> Permissions { get; set; }
}

public class ScanPermissionVM
{
    public string Code { get; set; }   // max 50, matches Permission.Code
    public string Name { get; set; }   // max 450
}

Until that action exists the component still hides and shows controls correctly; the registration call just fails and emits eiamPermissionsScanFailed. Nothing on the page breaks. If you would rather not add an endpoint, point endpoint at an existing action and adjust the body in flush().

Layout

The element is display: contents, so wrapping an <li> inside a <ul>, or a flex/grid child, does not change the layout. If a stylesheet targets a direct child selector across the wrapper (ul > li, .btn-group > .btn), that one selector will no longer match — those are the only cases that need a CSS touch-up.

Develop

npm install          # PUPPETEER_SKIP_DOWNLOAD=1 if the Chrome download is blocked
npm run build
npm test             # 23 unit tests
npm start            # dev server; src/index.html shows every mode side by side