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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@micahgoodman/notes

v1.1.1

Published

A complete notes module SDK for React with backend API client, adapter system, and UI components

Readme

@micahgoodman/notes

A complete notes module SDK for React with backend API client, adapter system, and reusable UI components. Can be used as a library, standalone app, or embedded with federated authentication.

✨ Three Modes of Operation

📦 Library Mode (npm package)

Import into your React app as a module with full control over UI and auth.

🚀 Standalone Mode (self-contained app)

Deploy as a complete notes application with built-in authentication.

🔗 Embedded Mode (SDK + OAuth)

Import components and authenticate against this module's backend.

Features

  • Complete API Client - CRUD operations for notes with Supabase backend support
  • Module Adapter System - Flexible adapter pattern for integrating notes into any app
  • React Components - Ready-to-use UI components for list, whiteboard, and detail views
  • TypeScript - Full type safety throughout
  • Realtime Updates - Optional Supabase subscriptions for live data
  • Context-Aware - Support for associating notes with other entities
  • Federated Auth - Uses @micahgoodman/auth for Keycloak integration and multi-app SSO
  • Flexible Deployment - Works as library, standalone app, or embedded widget

Quick Start

Choose your mode:

Library Mode (npm package)

npm install @micahgoodman/notes react @supabase/supabase-js

→ Library Mode Guide

Standalone Mode (complete app)

git clone https://github.com/micahgoodman/notes-module
cd notes-module
npm install
cp .env.example .env
# Edit .env with your credentials
npm run dev:standalone

→ Standalone Setup Guide

Embedded Mode (SDK with remote backend)

# In your parent app
npm install @micahgoodman/notes @micahgoodman/auth keycloak-js

# Wrap components with RemoteAuthProvider from @micahgoodman/auth

→ Embedded SDK Guide


Library Mode Usage

1. Configure the API Client

import { createClient } from '@supabase/supabase-js';
import { configureNotesApi, createNotesAdapter } from '@micahgoodman/notes';

const supabase = createClient(
  process.env.VITE_SUPABASE_URL,
  process.env.VITE_SUPABASE_ANON_KEY
);

// Configure the notes API
configureNotesApi({
  apiBase: process.env.VITE_API_BASE || '/api',
  supabaseClient: supabase,
  supabaseAnonKey: process.env.VITE_SUPABASE_ANON_KEY
});

// Create the notes adapter with realtime support
export const NoteAdapter = createNotesAdapter(supabase);

2. Use the Adapter Hook

import { useModuleList } from '@micahgoodman/notes';
import { NoteAdapter } from './config';

function MyNotesComponent() {
  const { items: notes, loading, error, refresh } = useModuleList(NoteAdapter);

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

  return (
    <div>
      {notes.map(note => (
        <div key={note.id}>{note.text}</div>
      ))}
    </div>
  );
}

3. Use the UI Components

import { NoteListItem, WhiteboardNoteCard, NoteDetailView } from '@micahgoodman/notes';

// In a list view
<NoteListItem
  item={note}
  isSelected={selectedId === note.id}
  onSelect={() => setSelectedId(note.id)}
  index={0}
/>

// On a whiteboard
<WhiteboardNoteCard
  note={note}
  isSelected={selectedId === note.id}
  onClick={() => setSelectedId(note.id)}
  onCardClick={handleCardClick}
/>

// Detail view
<NoteDetailView
  note={selectedNote}
  onUpdated={refresh}
  onDeleted={() => { setSelectedNote(null); refresh(); }}
  onShowToast={(msg) => console.log(msg)}
/>

API Reference

Configuration

configureNotesApi(config: ApiConfig)

Configure the notes API client before using any API functions.

configureNotesApi({
  apiBase: '/api',                    // Your API base URL
  supabaseClient: supabaseClient,     // Optional: for auth headers
  supabaseAnonKey: 'your-anon-key'    // Optional: for auth headers
});

API Functions

fetchNotes(): Promise<Note[]>

Fetch all notes.

fetchNotesByContext(contextType: string, contextId: string): Promise<Note[]>

Fetch notes associated with a specific context.

createNote(input: CreateNoteInput): Promise<{ id: string }>

Create a new note.

updateNote(id: string, input: UpdateNoteInput): Promise<{ ok: boolean }>

Update an existing note.

deleteNote(id: string): Promise<{ ok: boolean }>

Delete a note.

Adapter System

createNotesAdapter(supabaseClient?: SupabaseClient)

Create a notes adapter instance with optional realtime subscriptions.

const NoteAdapter = createNotesAdapter(supabase);

useModuleList(adapter: ModuleAdapter, opts?: { context?: Context })

React hook for managing a list of items with the adapter.

// All notes
const { items, loading, error, refresh } = useModuleList(NoteAdapter);

// Notes for a specific context
const { items } = useModuleList(NoteAdapter, {
  context: { type: 'project', id: '123' }
});

Components

<NoteListItem />

Renders a note in a list view (for use with @micahgoodman/sidebar or custom lists).

Props:

  • item: Note - The note to display
  • isSelected: boolean - Whether the note is selected
  • onSelect: () => void - Callback when note is clicked
  • index: number - Index in the list

<WhiteboardNoteCard />

Renders a note as a card (for use with @micahgoodman/whiteboard or custom canvases).

Props:

  • note: Note - The note to display
  • isSelected: boolean - Whether the note is selected
  • onClick: () => void - Callback when card content is clicked
  • onCardClick: (e: React.MouseEvent) => void - Callback for card wrapper clicks

<NoteDetailView />

Renders a detailed view/editor for a note.

Props:

  • note: Note | null - The note to display
  • onUpdated: () => void - Callback after successful update
  • onDeleted: () => void - Callback after successful deletion
  • onShowToast: (message: string) => void - Callback for showing notifications
  • contextChain?: any[] - Optional context chain for embedded display
  • hideEmbedded?: boolean - Whether to hide embedded content

<CreateNoteModal />

Modal for creating a new note.

Props:

  • onClose: () => void - Callback to close the modal
  • onCreate: () => void - Callback after successful creation
  • context?: Context - Optional context to associate the note with

Types

type Note = {
  id: string;
  text: string;
  createdAt: string;
  updatedAt: string;
};

type Context = {
  type: string;
  id: string;
};

type CreateNoteInput = {
  text: string;
  context?: Context;
};

type UpdateNoteInput = {
  text?: string;
};

Complete Example: Notes App

import React, { useState } from 'react';
import { createClient } from '@supabase/supabase-js';
import {
  configureNotesApi,
  createNotesAdapter,
  useModuleList,
  NoteListItem,
  NoteDetailView,
  CreateNoteModal,
} from '@micahgoodman/notes';

// Configure
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
configureNotesApi({
  apiBase: '/api',
  supabaseClient: supabase,
  supabaseAnonKey: SUPABASE_KEY
});
const NoteAdapter = createNotesAdapter(supabase);

// App
function NotesApp() {
  const { items: notes, loading, error, refresh } = useModuleList(NoteAdapter);
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [showCreate, setShowCreate] = useState(false);
  
  const selectedNote = notes.find(n => n.id === selectedId) || null;

  return (
    <div style={{ display: 'flex', height: '100vh' }}>
      {/* Sidebar */}
      <div style={{ width: '300px', borderRight: '1px solid #ccc', overflowY: 'auto' }}>
        <button onClick={() => setShowCreate(true)}>+ New Note</button>
        {notes.map((note, index) => (
          <NoteListItem
            key={note.id}
            item={note}
            isSelected={selectedId === note.id}
            onSelect={() => setSelectedId(note.id)}
            index={index}
          />
        ))}
      </div>

      {/* Detail */}
      <div style={{ flex: 1, overflowY: 'auto' }}>
        {selectedNote && (
          <NoteDetailView
            note={selectedNote}
            onUpdated={refresh}
            onDeleted={() => { setSelectedId(null); refresh(); }}
            onShowToast={(msg) => alert(msg)}
          />
        )}
      </div>

      {/* Create Modal */}
      {showCreate && (
        <CreateNoteModal
          onClose={() => setShowCreate(false)}
          onCreate={() => { setShowCreate(false); refresh(); }}
        />
      )}
    </div>
  );
}

Integration with Other Packages

This package is designed to work seamlessly with:

  • @micahgoodman/whiteboard - For whiteboard/canvas layouts
  • @micahgoodman/sidebar - For sidebar/list layouts

Example with both:

import { Whiteboard } from '@micahgoodman/whiteboard';
import { SidebarLayout, SidebarList } from '@micahgoodman/sidebar';
import { NoteListItem, WhiteboardNoteCard, NoteDetailView } from '@micahgoodman/notes';

// List View
<SidebarLayout
  SidebarComponent={
    <SidebarList
      items={notes}
      ItemComponent={NoteListItem}
      // ... other props
    />
  }
  DetailComponent={<NoteDetailView note={selectedNote} />}
/>

// Whiteboard View
<Whiteboard
  items={notes.map(n => ({ ...n, type: 'note' }))}
  componentMap={{
    note: {
      CardComponent: (props) => <WhiteboardNoteCard note={props.item} ... />,
      DetailComponent: (props) => <NoteDetailView note={props.item} ... />
    }
  }}
/>

Backend Requirements

This package includes a complete Supabase backend:

  • Database schema - module_data table with RLS policies
  • Edge Functions - /functions/v1/concepts API endpoint
  • Migrations - In supabase/migrations/
  • Realtime - Optional subscriptions for live updates

API Endpoints

  • GET /concepts?filter=note - List all notes
  • GET /concepts?filter=note&contextType=X&contextId=Y - List notes by context
  • POST /concepts?filter=note - Create note
  • PATCH /concepts/:id - Update note
  • DELETE /concepts/:id - Delete note

Authentication

Library Mode

Consumer provides authentication (any auth system).

Standalone Mode

Built-in Supabase Auth with Keycloak OAuth provider.

Embedded SDK Mode

Parent app handles Keycloak OAuth, components authenticate silently against remote backend. User logs in once.

→ Full Auth Documentation

Documentation

Project Structure

notes-module/
├── src/
│   ├── api.ts              # API client
│   ├── adapter.ts          # Notes adapter
│   ├── types.ts            # TypeScript types
│   ├── auth/               # Authentication (uses @micahgoodman/auth)
│   │   ├── AuthProvider.tsx  # Mode-based auth wrapper
│   │   ├── types.ts          # AppMode type
│   │   ├── utils.ts          # Mode detection utils
│   │   └── index.ts          # Re-exports from @micahgoodman/auth
│   ├── standalone/         # Standalone app
│   │   ├── App.tsx
│   │   └── pages/
│   └── components/         # Shared UI components
├── auth/                   # @micahgoodman/auth package (local)
│   ├── src/                # Auth provider implementations
│   └── README.md           # Auth package documentation
├── supabase/
│   ├── migrations/         # Database migrations
│   └── functions/          # Edge functions
├── standalone.html         # Standalone entry point
└── SINGLE_SIGN_ON.md      # SSO explanation

Contributing

Contributions welcome! Please read our contributing guidelines.

License

MIT