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

enigmatic

v0.38.0

Published

![Version](https://img.shields.io/npm/v/enigmatic)

Readme

Enigmatic

Version

A lightweight client-side JavaScript library for DOM manipulation, reactive state management, and API interactions, with an optional Bun server for backend functionality.

Architecture

Client-Server Architecture

The diagram above shows the interaction between the client (browser), Bun server, and external services (Auth0 and Cloudflare R2/S3).

Quick Start

Using client.js via CDN

Include client.js in any HTML file using the unpkg CDN:

<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/enigmatic"></script>
  <script src="https://unpkg.com/enigmatic/client/public/custom.js"></script>
  <script>
    window.api_url = 'https://your-server.com';
    window.state.message = 'Hello World';
  </script>
</head>
<body>
  <hello-world data="message"></hello-world>
</body>
</html>

Note: Use [email protected] (or latest) in the URL to pin a version.

Using the Bun Server

The Bun server provides a complete backend with:

  • Key-value storage – Per-user KV persisted as append-only JSONL (server/kv/{user}.jsonl) with update/delete actions and timestamps
  • File storage – Per-user files via Cloudflare R2 (or S3-compatible API)
  • Authentication – Auth0 OAuth2 login/logout
  • Static files – Served from client/public/
  • LLM proxy – Proxies chat requests to OpenRouter; no auth required. Set OPENROUTER_API_KEY to use.

Installation

  1. Install Bun:

    curl -fsSL https://bun.sh/install | bash
  2. Install dependencies (if any):

    bun install
  3. TLS certificates: place cert.pem and key.pem in server/certs/ for HTTPS (required for Auth0 in production).

Environment Variables

Create a .env file in the project root (or set env vars):

# Auth0
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=your-client-id
AUTH0_CLIENT_SECRET=your-client-secret

# Cloudflare R2 (optional, for file storage)
CLOUDFLARE_ACCESS_KEY_ID=your-access-key-id
CLOUDFLARE_SECRET_ACCESS_KEY=your-secret-access-key
CLOUDFLARE_BUCKET_NAME=your-bucket-name
CLOUDFLARE_PUBLIC_URL=https://your-account-id.r2.cloudflarestorage.com

# OpenRouter (optional, for LLM proxy)
OPENROUTER_API_KEY=sk-or-v1-...

Running the Server

npm start
# or
npx enigmatic
# or with hot reload
npm run hot

Server runs at https://localhost:3000 (HTTPS is required for Auth0 cookies).

Server Endpoints

| Method | Path | Description | |----------|------------|-------------| | GET | / | Serves client/public/index.html | | GET | /index.html, /*.js, etc. | Static files from client/public/ | | GET | /login | Redirects to Auth0 login | | GET | /callback| Auth0 OAuth callback | | GET | /logout | Logs out and clears session | | GET | /me | Current user or 401 (no auth) | | POST | /llm/chat | LLM proxy: forwards body to OpenRouter chat completions (no auth). Body: { model, messages }. | | GET | /{key} | KV get (auth required) | | POST | /{key} | KV set (auth required) | | DELETE | /{key} | KV delete (auth required) | | PUT | /{key} | Upload file to R2 (auth required) | | PURGE | /{key} | Delete file from R2 (auth required) | | PROPFIND | / | List R2 files (auth required) | | PATCH | /{key} | Download file from R2 (auth required) |

LLM proxy

The server can proxy chat requests to OpenRouter. Set OPENROUTER_API_KEY in the environment. No auth is required for /llm/chat; the endpoint forwards the request body to OpenRouter and returns the response.

Request: POST {api_url}/llm/chat with JSON body (OpenRouter chat completions format):

{ "model": "openai/gpt-3.5-turbo", "messages": [{ "role": "user", "content": "Hello" }] }

Example (curl):

curl -X POST "https://localhost:3000/llm/chat" -k \
  -H "Content-Type: application/json" \
  -d '{"model":"openai/gpt-3.5-turbo","messages":[{"role":"user","content":"Say hi."}]}'

Example (client): From a page, fetch(api_url + '/llm/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model, messages }) }). See client/public/api.html for a chat UI that uses this endpoint.

Optional app convention (skill.md)

The repo includes an optional minimal convention in client/public/skill.md for building enigmatic web apps. It’s a small, human- and bot-friendly standard you can follow when creating or generating apps:

  • Strict file set — Only three files: index.html, custom.js, style.css. No package.json, tests, or extra files unless requested.
  • index.html — One script for enigmatic (CDN), then custom.js. Body uses custom elements whose names match keys in window.custom (e.g. <hw></hw>, <app-root></app-root>).
  • custom.jswindow.custom = window.custom || {}; each component is a function on window.custom that returns a string (HTML or text). Element names match keys exactly (e.g. window.custom.hw for <hw></hw>, window.custom["app-root"] for <app-root></app-root>).
  • style.css — All app styles in one file.
  • REQUIREMENTS.md — If present, use it as the source of truth; implement every applicable section and any “Definition of done” or “Acceptance criteria.”

This keeps apps minimal and predictable. See client/public/skill.md for the full spec, examples, and checklist.

Overview

client.js is a client-side JavaScript library that provides utilities for DOM manipulation, reactive state management, and API interactions with a backend server. It automatically initializes custom HTML elements and provides a simple API for key-value storage, file operations, and authentication.

Core Utilities

DOM Selectors

window.$   // Alias for document.querySelector
window.$$  // Alias for document.querySelectorAll
window.$c  // Alias for element.closest (requires $0 context)

Usage:

const element = window.$('#my-id');
const elements = window.$$('.my-class');

API Base URL

window.api_url = "https://localhost:3001"

Configures the base URL for all API requests. Modify this to point to your server.

Reactive State Management

window.state is a Proxy object that automatically updates DOM elements when properties change.

How it works:

  • Set a property: window.state.myKey = 'value'
  • Elements with data="myKey" attribute are automatically updated
  • The system looks for custom element handlers in window.custom[tagName]
  • Only elements with matching custom element handlers are updated
  • Supports both function and object-based custom elements

Example:

<my-element data="message">Initial</my-element>
<script>
  window.custom['my-element'] = (data) => `<div>${data}</div>`;
  window.state.message = "Updated!"; // Automatically updates the element
</script>

Custom Element Integration:

  • If window.custom[tagName] is a function: calls f(value) and sets innerHTML
  • If window.custom[tagName] is an object: calls f.render(value) and sets innerHTML

API Functions

All API functions are async and return Promises. They use window.api_url as the base URL.

KV Storage Operations

window.get(key)

Retrieves a value from the server's key-value store.

const value = await window.get('my-key');

HTTP Method: GET
Endpoint: {api_url}/{key}
Returns: Parsed JSON response

window.set(key, value)

Stores a value in the server's key-value store.

await window.set('my-key', 'my-value');
await window.set('my-key', { json: 'object' });

HTTP Method: POST
Endpoint: {api_url}/{key}
Body: String values sent as-is, objects are JSON stringified
Returns: Parsed JSON response

window.delete(key)

Deletes a key from the server's key-value store.

await window.delete('my-key');

HTTP Method: DELETE
Endpoint: {api_url}/{key}
Returns: Parsed JSON response

R2 Storage Operations (File Storage)

window.put(key, body)

Uploads a file or data to R2 storage.

await window.put('filename.txt', 'file content');
await window.put('image.png', blob);
await window.put('data.json', { json: 'data' });

HTTP Method: PUT
Endpoint: {api_url}/{key}
Body: Accepts Blob, string, or JSON-serializable objects
Returns: Parsed JSON response

window.purge(key)

Deletes a file from R2 storage.

await window.purge('filename.txt');

HTTP Method: PURGE
Endpoint: {api_url}/{key}
Returns: Parsed JSON response

window.list()

Lists all files in the current user's R2 storage.

const files = await window.list();
// Returns: [{ name: 'file1.txt', lastModified: '...', size: 123 }, ...]

HTTP Method: PROPFIND
Endpoint: {api_url}/ (base URL, no key)
Returns: Array of file objects with name, lastModified, and size properties

window.download(key)

Downloads a file from R2 storage and triggers browser download.

await window.download('filename.txt');

HTTP Method: PATCH
Endpoint: {api_url}/{key}
Behavior:

  • Fetches file as blob
  • Creates temporary download URL
  • Triggers browser download
  • Cleans up temporary URL

Note: Uses PATCH method due to browser limitations with custom HTTP methods.

Authentication

window.login()

Redirects to the server's login endpoint.

window.login();

Behavior: Sets window.location.href to {api_url}/login

window.logout()

Redirects to the server's logout endpoint.

window.logout();

Behavior: Sets window.location.href to {api_url}/logout

window.me()

Returns the current user if authenticated, or null if not (e.g. 401).

const user = await window.me();
// user is { sub, email, ... } or null

Endpoint: GET {api_url}/me (with credentials)

Custom Elements System

Custom elements are defined in window.custom object and automatically initialized when the DOM loads or when elements are added dynamically.

Initialization

The library automatically:

  1. Waits for DOM to be ready (DOMContentLoaded or immediate if already loaded)
  2. Iterates through all keys in window.custom
  3. Finds all matching HTML elements by tag name
  4. Calls the custom element handler and sets innerHTML
  5. Watches for new elements added to the DOM via MutationObserver and initializes them automatically

Proxy Behavior

window.custom is a Proxy that automatically initializes matching elements when you add a new custom element definition:

// Adding a new custom element automatically initializes all matching elements in the DOM
window.custom['my-element'] = (data) => `<div>${data}</div>`;
// All <my-element> tags are immediately initialized

Defining Custom Elements

Function-based Custom Element

window.custom = {
  "my-element": async (data) => {
    return `<div>Content: ${data}</div>`;
  }
};

HTML Usage:

<my-element></my-element>

When used with reactive state, the function receives the state value:

<my-element data="myKey"></my-element>
<script>
  window.state.myKey = 'value'; // Function is called with 'value'
</script>

The function receives the state value as the first parameter. If no state value is set, it receives undefined.

Object-based Custom Element

window.custom = {
  "my-element": {
    prop: (data) => `Processed: ${data}`,
    render: function(data) {
      return `<div>${this.prop(data)}</div>`;
    }
  }
};

HTML Usage:

<my-element></my-element>

When used with reactive state, the render method is called with the state value.

Example: File Widget

window.custom = {
  "file-widget": async () => {
    const list = await window.list();
    // Returns HTML string with file list and upload button
    return `<div>...</div>`;
  }
};

HTML Usage:

<file-widget></file-widget>

This custom element:

  • Fetches file list using window.list()
  • Renders file items with download and delete buttons
  • Includes an upload button
  • Uses inline event handlers that call window.download(), window.purge(), and window.put()

Error Handling

All API functions throw errors if the request fails. Use try-catch or .catch():

try {
  await window.get('nonexistent');
} catch (err) {
  console.error('Error:', err);
}

// Or with promises
window.get('key').catch(err => console.error(err));

Complete Example

<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/enigmatic"></script>
  <script src="https://unpkg.com/enigmatic/client/public/custom.js"></script>
  <script>
    window.api_url = window.api_url || window.location.origin;
    window.state.message = 'World';
  </script>
</head>
<body>
  <hello-world data="message"></hello-world>
  <file-widget></file-widget>
  <script>
    window.me().then(u => console.log(u ? 'Logged in as ' + u.email : 'Not logged in'));
  </script>
</body>
</html>

Dependencies

  • Requires a backend server that implements the API endpoints (or use the included Bun server)
  • Requires browser support for:
    • fetch API
    • Proxy API
    • Blob API
    • URL.createObjectURL
    • MutationObserver API

Note: Load client.js first, then your custom element definitions (e.g. custom.js); the Proxy initializes elements when definitions are assigned.

Notes

  • All API functions automatically encode keys using encodeURIComponent
  • The window.download() function uses PATCH method internally (browsers don't support custom HTTP methods)
  • Custom elements are automatically initialized:
    • On page load (when DOM is ready)
    • When new custom element definitions are added to window.custom
    • When new matching elements are added to the DOM (via MutationObserver)
  • The reactive state system only updates elements with matching data attributes
  • Custom element handlers can be async functions
  • When a custom element has a data attribute, it automatically reads from window.state[dataValue] if no explicit value is provided

Development

  • Start server: npm start or npx enigmatic
  • Hot reload: npm run hot
  • Client: Load client.js from unpkg or from client/public/client.js when serving locally. Load custom.js (or your definitions) after client.js; set window.api_url before making API calls.

License

MIT