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

@havena/esm-core-api

v1.0.1

Published

Core API library for ESM

Readme

@havena/esm-core-api

Core ESM API library providing authentication, HTTP client utilities, React hooks, and type definitions for the Haven ESM platform.

Installation

npm install @havena/esm-core-api
# or
yarn add @havena/esm-core-api

Overview

This package provides a comprehensive set of APIs and utilities for building applications on the Haven ESM platform:

  • Authentication: Better Auth integration with organization, admin, and multi-session support
  • HTTP Client: Axios-based API client with SWR integration
  • React Hooks: Custom hooks for session management, permissions, and data fetching
  • Storage: Local storage utilities
  • Utilities: URL construction, object manipulation, and helper functions
  • Types: TypeScript type definitions for the platform

Core Features

Authentication API

The authentication API is built on top of Better Auth and provides a comprehensive authentication system with organization support, admin roles, and multi-session capabilities.

Auth Client

import { Auth } from "@havena/esm-core-api";

// Access the auth client
const { data: session } = Auth.client.useSession();

// Sign out with confirmation
Auth.confirmedSignOut();

// Delete account with confirmation
Auth.confirmedDeleteAccount();

Features:

  • Organization management with teams
  • Admin role-based access control
  • Username-based authentication
  • Anonymous user support
  • API key authentication
  • Multi-session support
  • JWT token support

Auth ACL (Access Control Lists)

The package includes predefined ACL configurations for organization and admin plugins, providing role-based permission management.

Hive API

The Hive API provides HTTP client functionality, data fetching hooks, and API utilities.

API Configuration Provider

Wrap your application with ApiConfigProvider to configure SWR with the API fetcher:

import { ApiConfigProvider } from "@havena/esm-core-api";

function App() {
  return <ApiConfigProvider>{/* Your app components */}</ApiConfigProvider>;
}

API Fetch

Make HTTP requests using the apiFetch function:

import { apiFetch } from "@havena/esm-core-api";

// GET request
const response = await apiFetch("/users", {
  method: "GET",
});

// POST request
const response = await apiFetch("/users", {
  method: "POST",
  data: { name: "John Doe", email: "[email protected]" },
});

// With custom headers
const response = await apiFetch("/protected", {
  method: "GET",
  headers: {
    Authorization: "Bearer token",
  },
});

Use API Hook

A wrapper around SWR for data fetching:

import { useApi } from "@havena/esm-core-api";

function UserList() {
  const { data, error, isLoading } = useApi("/users");

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <ul>
      {data?.data?.results?.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

URL Construction

Build URLs with query parameters:

import { constructUrl } from "@havena/esm-core-api";

// Basic usage
const url = constructUrl("/api/users", { id: 123, active: true });
// → '/api/users?id=123&active=true'

// Array values
const url = constructUrl("/api/search", {
  tags: ["javascript", "typescript"],
  limit: 10,
});
// → '/api/search?tags=javascript&tags=typescript&limit=10'

// With merge strategy
const url = constructUrl(
  "/api/data?sort=name",
  { sort: "date", limit: 5 },
  { mergeStrategy: "preserve" }
);
// → '/api/data?sort=name&limit=5' (preserves existing 'sort')

UrlBuilder Class:

import { UrlBuilder } from "@havena/esm-core-api";

const url = new UrlBuilder("/api/users")
  .param("page", 1)
  .param("limit", 10)
  .paramIf(includeInactive, "includeInactive", true)
  .build();

File Upload

Upload files with progress tracking:

import { uploadFiles } from "@havena/esm-core-api";

const result = await uploadFiles({
  files: {
    avatar: [file1, file2],
    documents: [file3],
  },
  onProgress: (progress) => {
    console.log(`Upload progress: ${progress}%`);
  },
  relatedModelName: "User",
  relatedModelId: "123",
  purpose: "profile",
  tags: ["avatar", "profile"],
});

// Get file URL
import { getHiveFileUrl } from "@havena/esm-core-api";
const fileUrl = getHiveFileUrl(result.files[0].blob.storagePath);

HTTP Client

Direct access to the configured Axios instance:

import { httpClient } from "@havena/esm-core-api";

const response = await httpClient.get("/users");
const response = await httpClient.post("/users", { name: "John" });

React Hooks

Session Management

import { useSession, SessionProvider } from "@havena/esm-core-api";

// Wrap your app with SessionProvider
function App() {
  return (
    <SessionProvider value={{ user, session }}>
      <YourComponents />
    </SessionProvider>
  );
}

// Use session in components
function Profile() {
  const { user, session } = useSession();

  return <div>Welcome, {user?.name}</div>;
}

User Search

import { useSearchUser } from "@havena/esm-core-api";

function UserSearch() {
  const { users, isLoading, setFilters, filters } = useSearchUser();

  return (
    <div>
      <input
        onChange={(e) => setFilters({ search: e.target.value })}
        placeholder="Search users..."
      />
      {isLoading && <div>Loading...</div>}
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

System Access Control

Check system-level permissions (server-side validation):

import { useUserHasSystemAccess } from "@havena/esm-core-api";

function AdminPanel() {
  const { hasAccess, isLoading } = useUserHasSystemAccess({
    user: ["impersonate", "delete"],
    category: ["create"],
  });

  if (isLoading) return <Loader />;

  return (
    <>
      {hasAccess && <ImpersonateUserButton />}
      {hasAccess && <DeleteUserButton />}
    </>
  );
}

Client-side permission check (for UI optimization only):

import { useUserHasSystemAccessSync } from "@havena/esm-core-api";

function Menu() {
  const { hasAccess, isLoading } = useUserHasSystemAccessSync({
    user: ["impersonate"],
  });

  // This is for UI only - backend must validate!
  return <Menu>{hasAccess && <ImpersonateMenuItem />}</Menu>;
}

Organization Access Control

Check organization-level permissions:

import { useUserHasOrganizationAccess } from "@havena/esm-core-api";

function PropertyManagement() {
  const { hasAccess, isLoading } = useUserHasOrganizationAccess({
    property: ["create", "update"],
    file: ["upload"],
  });

  if (isLoading) return <Loader />;

  return (
    <>
      {hasAccess && <CreatePropertyButton />}
      {hasAccess && <UploadFileButton />}
    </>
  );
}

Search Parameters

Manage URL search parameters (React Router v5 compatible):

import { useSearchParams } from "@havena/esm-core-api";

function FilterComponent() {
  const [searchParams, setSearchParams] = useSearchParams();

  const page = searchParams.get("page") || "1";
  const sort = searchParams.get("sort") || "name";

  const handleSort = (newSort: string) => {
    setSearchParams({ ...Object.fromEntries(searchParams), sort: newSort });
  };

  return (
    <select value={sort} onChange={(e) => handleSort(e.target.value)}>
      <option value="name">Name</option>
      <option value="date">Date</option>
    </select>
  );
}

Extended version with utility methods:

import { useSearchParamsExtended } from "@havena/esm-core-api";

function AdvancedFilters() {
  const {
    searchParams,
    getParam,
    setParam,
    deleteParam,
    updateParams,
    clearParams,
    getParamsObject,
  } = useSearchParamsExtended();

  return (
    <div>
      <button onClick={() => setParam("filter", "active")}>Set Filter</button>
      <button onClick={() => deleteParam("filter")}>Remove Filter</button>
      <button onClick={() => updateParams({ page: "1", sort: "name" })}>
        Update Multiple
      </button>
      <button onClick={clearParams}>Clear All</button>
    </div>
  );
}

Typed search parameters:

import { useTypedSearchParams, searchParamUtils } from "@havena/esm-core-api";

function TypedFilters() {
  const [params, setParams, clearParams] = useTypedSearchParams({
    page: {
      parse: searchParamUtils.parseNumber,
      serialize: String,
      defaultValue: 1,
    },
    active: {
      parse: searchParamUtils.parseBoolean,
      serialize: String,
      defaultValue: false,
    },
    tags: {
      parse: searchParamUtils.parseArray,
      serialize: searchParamUtils.serializeArray,
      defaultValue: [],
    },
  });

  return (
    <div>
      <p>Page: {params.page}</p>
      <p>Active: {params.active ? "Yes" : "No"}</p>
      <p>Tags: {params.tags.join(", ")}</p>
      <button onClick={() => setParams({ page: params.page + 1 })}>
        Next Page
      </button>
    </div>
  );
}

Storage

Local Storage Hook

import { useLocalStorage } from "@havena/esm-core-api";

function Preferences() {
  const [theme, setTheme] = useLocalStorage<string>("theme");

  return (
    <select value={theme || "light"} onChange={(e) => setTheme(e.target.value)}>
      <option value="light">Light</option>
      <option value="dark">Dark</option>
    </select>
  );
}

Utilities

Object Manipulation

import {
  flattenObject,
  unflattenArray,
  flattenObjectToPairs,
  unflattenPairsToObject,
} from "@havena/esm-core-api";

// Flatten nested objects
const nested = { user: { name: "John", age: 30 } };
const flattened = flattenObject(nested);
// → ['user.name', '"John"', 'user.age', '30']

// Unflatten back
const restored = unflattenArray(flattened);
// → { user: { name: 'John', age: 30 } }

// Flatten to key-value pairs
const pairs = flattenObjectToPairs(nested);
// → [['user.name', 'John'], ['user.age', 30]]

String Utilities

import {
  getNameInitials,
  getNameInitialsFromEmail,
  parseDate,
} from "@havena/esm-core-api";

const initials = getNameInitials("John Doe"); // → 'JD'
const emailInitials = getNameInitialsFromEmail("[email protected]"); // → 'JD'
const date = parseDate("2024-01-01"); // → Date object

Path Utilities

import { extractIdFromPath } from "@havena/esm-core-api";

// Extract ID from path
const id = extractIdFromPath("/dashboard/users/123", "/dashboard/users");
// → '123'

Type Utilities

import { Path, FieldPath } from "@havena/esm-core-api";

type User = {
  name: string;
  address: {
    city: string;
    zip: number;
  };
};

type UserPaths = Path<User>;
// → 'name' | 'address' | 'address.city' | 'address.zip'

Types

The package exports comprehensive TypeScript types:

import type {
  User,
  Session,
  UserSession,
  Organization,
  OrganizationRole,
  Member,
  Team,
  TeamMember,
  Invitation,
  APIFetchInit,
  APIFetchResponse,
  APIFetchError,
  PaginationControls,
  APIListResponse,
  FileUploadPayload,
  FileUploadResultsPayload,
  FileBlob,
  FileMetadata,
} from "@havena/esm-core-api";

API Reference

Authentication

  • Auth.client - Better Auth client instance
  • Auth.confirmedSignOut() - Sign out with confirmation modal
  • Auth.confirmedDeleteAccount() - Delete account with confirmation modal
  • Auth.pluginOptions - Plugin configuration options

HTTP Client

  • apiFetch<T, K>(url, options?) - Make HTTP requests
  • httpClient - Axios instance with base URL configured
  • ApiConfigProvider - React provider for SWR configuration
  • useApi<T>(resource, fetcher?, options?) - SWR hook wrapper
  • mutate - SWR mutate function
  • handleApiErrors - Error handling utility

URL Utilities

  • constructUrl(path, params?, options?) - Build URLs with query parameters
  • UrlBuilder - Fluent URL builder class

File Operations

  • uploadFiles(options, requestParams?) - Upload files with progress tracking
  • cleanFiles(paths) - Delete files by paths
  • getHiveFileUrl(path) - Get streaming URL for media files

Hooks

  • useSession() - Access user session context
  • useSearchUser() - Search users with debouncing
  • useUserHasSystemAccess(permissions) - Server-side system permission check
  • useUserHasSystemAccessSync(permissions) - Client-side system permission check
  • useUserHasOrganizationAccess(permissions) - Server-side organization permission check
  • useSearchParams() - Manage URL search parameters
  • useSearchParamsExtended() - Extended search params with utilities
  • useTypedSearchParams(schema) - Typed search parameters

Storage

  • useLocalStorage<T>(key) - Local storage hook

Utilities

  • flattenObject(obj, parentKey?) - Flatten nested objects to array
  • unflattenArray(arr) - Restore object from flattened array
  • flattenObjectToPairs(obj, parentKey?) - Flatten to key-value pairs
  • unflattenPairsToObject(pairs) - Restore object from pairs
  • getNameInitials(name) - Get initials from name
  • getNameInitialsFromEmail(email) - Get initials from email
  • parseDate(date?, defaultNow?) - Parse date string
  • extractIdFromPath(pathname, basePath?) - Extract ID from URL path
  • sleep(milliseconds) - Promise-based delay
  • Path<T> - Type utility for object paths
  • FieldPath<T> - Type utility for form field paths

Peer Dependencies

This package requires the following peer dependencies:

  • react ^18.3.1
  • react-dom ^18.3.1
  • react-router ^5.3.4
  • react-router-dom ^5.3.4
  • @mantine/core ^8.3.4
  • @mantine/hooks ^8.3.4
  • @mantine/notifications ^8.3.4
  • @mantine/modals ^8.3.4
  • swr ^2.3.6
  • zustand ^5.0.8

Security Notes

⚠️ Important Security Considerations:

  1. Permission Checks: Always use server-side permission checks (useUserHasSystemAccess, useUserHasOrganizationAccess) for security-critical operations. Client-side checks (useUserHasSystemAccessSync) are for UI optimization only and can be bypassed.

  2. API Validation: The backend MUST validate all permissions and access control before executing any operations. Never rely solely on client-side checks.

  3. Authentication: The auth client handles authentication tokens automatically. Ensure your backend properly validates these tokens.

License