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

@danainnovations/directory

v4.1.0

Published

Sonance employee directory — zero-config React hooks and async functions

Readme

@danainnovations/directory

Sonance employee directory SDK. Zero-config access to the company's active directory (synced live from Okta/AD via SCIM).

Install

npm install @danainnovations/directory

No environment variables or API keys needed. Just install and use.

User identity — the one rule

The canonical user identifier is oktaSub (the Okta OIDC sub). It is stable, immutable, and opaque.

// ✅ Correct — join on the canonical identifier
const link = { userOktaSub: user.oktaSub, projectId }

// ❌ Wrong — emails and display names change
const link = { userEmail: user.email, projectId }

For SSO flows: match jwt.sub === user.oktaSub.

For manager relationships: prefer oManagerId (the manager's oktaSub) over managerId (which is actually the manager's email despite the legacy name).

externalId is kept as an alias of oktaSub for v4.0.x consumers — same value, prefer oktaSub in new code.

React Hooks

import { useUsers, useUser, useDepartments, useOrgChart } from '@danainnovations/directory/react'

// Search and browse employees
const { users, total, loading, error } = useUsers({ search: 'john', department: 'Engineering', page: 1, limit: 20 })

// Look up a single user by email or ID
const { user, loading, error } = useUser('[email protected]')

// Get all departments with employee counts
const { departments, loading, error } = useDepartments()

// Get the full org chart (manager hierarchy tree)
const { tree, loading, error } = useOrgChart()

Async Functions

For server-side, API routes, scripts, or non-React code:

import { getUsers, getUser, getDepartments, getOrgChart } from '@danainnovations/directory'

const result = await getUsers({ search: 'sales', department: 'Sales', page: 1, limit: 50 })
// result = { data: DirectoryUser[], total: number, page: number, limit: number, totalPages: number }

const user = await getUser('[email protected]') // or pass a user ID
// user = DirectoryUser | null

const departments = await getDepartments()
// departments = [{ name: 'Engineering', count: 42 }, ...]

const tree = await getOrgChart()
// tree = OrgNode[] (recursive tree with .reports[])

Types

interface DirectoryUser {
  id: string
  email: string
  full_name: string | null
  given_name: string | null
  family_name: string | null
  department: string | null
  job_title: string | null
  location: string | null
  phone_number: string | null
  avatar_url: string | null
  manager_email: string | null
  role: string | null
}

interface Department {
  name: string
  count: number
}

interface OrgNode {
  id: string
  email: string
  name: string
  title: string | null
  department: string | null
  reports: OrgNode[]  // direct reports (recursive)
}

interface UserSearchParams {
  search?: string      // searches name, email, department
  department?: string  // exact department match
  page?: number        // default: 1
  limit?: number       // default: 50, max: 100
}

How It Works

This package connects directly to a Supabase database that receives live SCIM provisioning from Okta. The data updates automatically whenever IT makes changes in Okta/Active Directory (new hires, terminations, department changes, etc.). Only active employees are returned — non-person accounts (service accounts, rooms, test users) are filtered out by row-level security.

AI Integration Notes

When integrating this package into a project:

  • Use the React hooks (useUsers, useUser, useDepartments, useOrgChart) for client components
  • Use the async functions (getUsers, getUser, getDepartments, getOrgChart) for server components, API routes, or scripts
  • All hooks return { loading, error } alongside the data
  • The useUsers hook re-fetches automatically when search params change
  • No providers, context, or setup needed — just import and use
  • For people pickers / autocomplete, use useUsers({ search: inputValue }) with a debounced input
  • For org charts, useOrgChart() returns a tree — each node has a .reports[] array of direct reports
  • For department filters, useDepartments() returns departments sorted alphabetically with employee counts