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

fat-notes-extension-cli

v0.1.4

Published

Scaffolding CLI for building FatNotes bespoke plugins via Module Federation.

Readme

fat-notes-extension-cli

fat-notes 高级定制插件脚手架 — Scaffolding CLI for FatNotes bespoke plugins.

FatNotes bespoke plugins are VSCode-style extensions loaded into the desktop host via Module Federation at runtime. A plugin can contribute elements to every UI block (activity bar, primary sidebar, panel, footer, routes, widget types) except the secondary sidebar — which is reserved for the chat block.

When FatNotes loads a plugin, the host injects a FatNotesPluginSDK carrying the current user/auth info and helper APIs for reading data sources and executing queries/mutations.

Install

npm install -g fat-notes-extension-cli

Or use it through npx:

npx fat-notes-extension-cli create my-plugin

Commands

fn-ext create <pluginId> [--name "Display Name"] [--dir <target>]
fn-ext build                 # inside a scaffolded project
fn-ext dev                   # watch mode
  • <pluginId> must match ^[a-z0-9][a-z0-9-]*$.
  • --name sets the display name (defaults to <pluginId>).
  • --dir sets an alternate output directory (defaults to ./<pluginId>).

Scaffolded structure

my-plugin/
├── plugin.json              # manifest read by the FatNotes installer
├── package.json
├── tsconfig.json
├── webpack.config.js        # Module Federation build
├── public/
│   └── index.html           # optional standalone preview shell
├── src/
│   ├── bootstrap.ts         # async entry required by MF
│   ├── index.tsx            # exports activate(sdk) / deactivate()
│   └── PluginView.tsx       # example contribution component
└── types/
    └── fat-notes-sdk.d.ts   # SDK types (mirror of host)

Module Federation contract

  • Container name: <plugin-id with dashes replaced by underscores>.
  • Exposed module: ./pluginsrc/index.tsx.
  • Shared (singleton, non-eager) — versions must match the FatNotes host:
    • react 18.2.0
    • react-dom 18.2.0
    • react/jsx-runtime 18.2.0
    • react-redux 9.1.2
    • react-intl 6.8.4
    • antd 6.3.1

Plugin entry

src/index.tsx must export:

export async function activate(sdk: FatNotesPluginSDK): Promise<void> { ... }
export function deactivate(): void { ... }

SDK capabilities

| Namespace | Purpose | | ---------------- | ------------------------------------------------------ | | sdk.host | Host version + mode ("dev" \| "prod") | | sdk.auth | Current user, token, auth-change subscription | | sdk.dataSource | List / read data sources | | sdk.query | Run saved queries or raw SQL | | sdk.mutation | Run saved mutations or insert rows | | sdk.ui | toast / modal / theme / i18n | | sdk.commands | Register + execute commands | | sdk.store | Read-only access to host Redux state | | sdk.config | Read / write current plugin runtime config | | sdk.contributions | Register UI contributions (see below) | | sdk.events | Plugin/host event bus |

Contribution points

  • registerActivityBarItem — icon in the left activity bar
  • registerSidebarView("primary", def) — primary sidebar view (secondary blocked)
  • registerPanelTab — tab in the bottom panel
  • registerFooterItem — inline status in the footer
  • registerRoutePage — full-page route
  • registerWidgetType — custom dashboard widget type
  • registerMenu — contextual menu items

Every register* call returns a Disposable; your deactivate() must dispose every one it collected.

Backend host helpers

When plugin.json enables backend, the scaffold includes backend/plugin_runtime.py helpers that can call host common APIs:

  • host_get_login_info() — returns current { token, user }
  • host_get_plugin_config() — returns this plugin's runtime config
  • host_set_plugin_config(config, merge=True) — persists runtime config

Install a built plugin into FatNotes

  1. npm run build — emits dist/ with remoteEntry.js.
  2. Zip plugin.json + the contents of dist/ as install.zip.
  3. In FatNotes → ExtensionsCustom Bespoke PluginsNew Plugin, or drop the zip via the standard apps-style installer. Files land under extensions/bespokePlugins/ (mirroring the apps installation layout).

Host-side wiring (for reference)

  • Desktop app code: fat-notes/src/views/pluginSdk/*
  • Python service: fat-notes/src/services/extensionService/bespokePluginService/*
  • Server-side type enum: fat-notes-server/backend/internal/models/extension.go (ExtensionTypeBespokePlugin)