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

@hoover-institution/hubspot-lib

v2.1.2

Published

A toolkit for deep integration with HubSpot's Marketing Events API with a plugin-based architecture.

Downloads

7

Readme

HubSpot SDK (hoover-institution/hubspot-lib)

This toolkit enables deep integration with HubSpot's Marketing Events API and introduces a flexible plugin system to customize behavior across the event lifecycle.

⚠️ This is version 2.0.0+ — includes breaking changes to method return structures. See Returning Plugin Information for details.

💡 Plugins are automatically cached after the first call to loadPlugins(). You can reuse the same plugin map across your app without manually caching or exporting anything.


Table of Contents


What's New

This release introduces a full-featured plugin system that includes:

  • ⚠️ Update 2.1.0. Some library methods pass additional context or data to plugins, allowing them to access more information about the operation being performed.

  • Plugins can now return values. Each plugin handler may return a result (or error), and the SDK will return these values alongside the core HubSpot operation output. This allows plugins to report inserted IDs, custom status, debug info, and more.

  • 🔁 MarketingEvent method return values have changedcreateEvent, deleteEvent, and others now return a { hubspot, plugins } object instead of a flat response. This is a breaking change.

  • Plugin loading is now cached by default — the first call to loadPlugins() stores the plugin map. All future calls return the same result for consistency and performance. Manual caching is no longer required.

  • 🧩 Built-in PLUGINS.ALL constant — Pass PLUGINS.ALL when creating a MarketingEvent instance to activate every loaded plugin automatically, without manually listing them.

  • Inline plugins defined in code

  • File-based plugins loaded from disk

  • Bitmask-based lifecycle hooks for efficient execution

  • A CLI to scaffold plugin templates

  • Typed payloads and full IDE integration


Installation

npm install @hoover-institution/hubspot-lib

To load file-based plugins, define the plugin directory in your consumer package.json:

"hubspot-lib": {
  "pluginDir": "src/plugins"
}

MarketingEvent Class

The MarketingEvent class provides the core API for creating, managing, and tracking events and their associated contacts. It is the heart of the SDK.

Constructor

new MarketingEvent(accountId, { plugins: [...] })
  • accountId: Your HubSpot portal's account ID.
  • plugins: A list of plugin identifiers from the PLUGINS object, such as PLUGINS.MY_PLUGIN

⚠️ You must call loadPlugins() before instantiating the MarketingEvent class. If you don't, the plugins will not be available.


Lifecycle Hook Events (EVENTS)

CREATE_EVENT;
GET_EVENT;
GET_EVENTS;
DELETE_EVENT;
REGISTER_EMAIL;
GET_CONTACTS_BY_STATE;
CREATE_OR_FIND_CONTACT_LIST;
GET_CONTACT_EVENT_STATE;
ADD_CONTACT_TO_LIST;
REMOVE_CONTACT_FROM_LIST;
ASSOCIATE_LIST_WITH_EVENT;
MARKETING_EVENT_ERROR;

Methods

createEvent(payload)

Creates a marketing event. Triggers CREATE_EVENT plugins.

Returns: { hubspot: { objectId, status, ... }, plugins: [...] }

 

getEvent(externalEventId?)

Fetches the marketing event by external ID.

Returns: { objectId, eventName, ... } | null

 

deleteEvent(externalEventId?)

Deletes an event and triggers DELETE_EVENT plugins.

Returns: { hubspot: { success: boolean }, plugins: [...] }

 

createOrFindContactList(name)

Creates or returns an existing contact list by name.

Returns: { listId: string }

 

associateListWithEvent(objectId, listId)

Links a contact list to a marketing event.

Returns: { hubspot: { success: boolean }, plugins: [...] }

 

registerEmail(email, externalEventId, subscriberState [, fullName])

Adds a contact to the appropriate list. If a contact does not exist, it will automatically create one with the provided email and optional full name.

Returns: { hubspot: { status: string, listId: string }, plugins: [...] }

 

getContactEventState(email, externalEventId)

Gets the subscriber state for a contact on an event.

Returns: number (from SUBSCRIBER_STATE)

 

getSubscriberStateName(code)

Maps a subscriber state code to a label.

Returns: "REGISTERED" | "CANCELED" | "ATTENDED"


Plugin System Overview

Plugins can be attached to lifecycle hook points such as:

  • CREATE_EVENT
  • GET_EVENT
  • DELETE_EVENT
  • REGISTER_EMAIL
  • MARKETING_EVENT_ERROR

Plugins can be either:

  • Inline (defined directly in JS)
  • File-based (loaded from a directory)

Plugin Payloads

Each hook receives a typed payload. For example:

[EVENTS.CREATE_EVENT]: ({ eventName, externalEventId, status }) => { ... }

 

Plugin Loading Options

Plugins are typically loaded once at startup and reused throughout the application.

You can load plugins in two ways:

 

1. Load specific plugins by name:

const PLUGINS = loadPlugins(["MY_PLUGIN"], { useDirectory: false });

2. Automatically discover all inline and file-based plugins:

const PLUGINS = loadPlugins([], { useDirectory: true });

By default, loadPlugins() caches the plugin map the first time it's called. Subsequent calls return the same result, regardless of names or useDirectory. To force a reload, pass { cache: false }.

💡 You can safely call loadPlugins() multiple times in your server code — the system ensures plugins are loaded only once.

 

Returning Plugin Information

When a plugin function returns a value, the SDK automatically wraps that value in a structured result object as part of the main return payload. You do not need to return metadata like pluginId, pluginName, or success yourself — the system adds those for you.

Each plugin's result is included in the plugins array of the overall return value:

ℹ️ Avoid returning full HubSpot responses inside plugins — the core hubspot field already includes that. Plugin results should be minimal (status codes, flags, debug info).

{
  hubspot: { ... },
  plugins: [
    {
      pluginId: number,
      pluginName: string,
      success: boolean,
      result?: any,
      error?: string
    },
    ...
  ]
}

A plugin is considered success: true if it completes without throwing. If it throws an error, success will be false, and the error field will contain the message. You can return any custom shape from your plugin — it will be captured under result.

Example plugin return:

return {
  pluginEventId: "abc-123",
  statusNote: "Successfully synced to MongoDB",
};

To indicate failure, use throw:

throw new Error("MongoDB insert failed.");

This will be returned as:

{
  success: false,
  error: "MongoDB insert failed."
}

If you return a shape like { ok: false }, but do not throw, the SDK will still treat the plugin as successful from an execution standpoint. Use this only for soft-failures or metadata.

Accessing Individual Plugin Results

You can access each plugin's return using the getPluginResults() utility, which converts the plugin array into a fast lookup map:

import { getPluginResults } from "@hoover-institution/hubspot-lib";

const result = await instance.createEvent(payload);
const pluginResults = getPluginResults(result.plugins);

// Access result of a specific plugin by name
const mongo = pluginResults[PLUGINS.MONGO_SYNC];

if (mongo?.success) {
  console.log("✅ Mongo inserted:", mongo.result.pluginEventId);
} else {
  console.error("❌ Mongo plugin failed:", mongo.error);
}

💡 All plugin results are wrapped and indexed. Using getPluginResults() helps you avoid .find() calls and access specific plugin outcomes by pluginId.


Creating Inline Plugins

import { createPlugin, EVENTS } from "@hoover-institution/hubspot-lib";

createPlugin("INLINE_PLUGIN", {
  [EVENTS.CREATE_EVENT]: ({ eventName }) => {
    console.log(`[INLINE_PLUGIN] Created: ${eventName}`);
    return { note: "Event processed by INLINE_PLUGIN" };
  },
});

Creating File-Based Plugins

You must define your plugin directory in your consumer package.json:

"hubspot-lib": {
  "pluginDir": "src/plugins"
}

Then add files in that folder:

// src/plugins/MY_PLUGIN.js
import { EVENTS, createPlugin } from "@hoover-institution/hubspot-lib";

export default createPlugin("MY_PLUGIN", {
  [EVENTS.DELETE_EVENT]: ({ externalEventId }) => {
    console.log(`[MY_PLUGIN] Deleted event: ${externalEventId}`);
    return { deleted: true };
  },
});

Native Plugins

Currently available native plugins:

  • LOG_TO_CONSOLE — Logs all lifecycle event activity to the console.
  • ALL — Loads and registers all plugins found. Use this to automatically include every plugin you’ve added, without specifying each one individually.

CLI: Generate Plugin Templates

You can scaffold new plugins interactively using the built-in CLI.

Step 1: Configure CLI in package.json

"bin": {
  "create-plugin": "./node_modules/@hoover-institution/hubspot-lib/lib/cli/hook-init.js"
}

Step 2: Run the CLI

npx create-plugin

The CLI is interactive:

🚀 HubSpot Plugin Generator

? 📂 How do you want to select the plugin directory? (Use arrow keys)
❯ Browse folders
  Manually enter path

? 🔌 Select your plugin directory: ./plugins
? 📁 Do you want to create a new subfolder? No

? 🪝 Plugin names (comma-separated, use ALL_CAPS)
📌 Usage: PLUGIN_1, PLUGIN_2
MY_CUSTOM_PLUGIN

✅ Created: plugins/MY_CUSTOM_PLUGIN.js
✅ Scaffolding complete

Full Testing Example with Plugins

import dotenv from "dotenv";
dotenv.config();

import {
  createPlugin,
  EVENTS,
  loadPlugins,
  SUBSCRIBER_STATE,
  getPluginResults,
} from "@hoover-institution/hubspot-lib";

import { MarketingEvent } from "@hoover-institution/hubspot-lib";
import { payload } from "./payload.js";

createPlugin("INLINE_FULL", {
  [EVENTS.CREATE_EVENT]: ({ eventName }) => {
    console.log(`[INLINE_FULL][CreateEvent] ${eventName}`);
    return { inline: true };
  },
  [EVENTS.GET_EVENT]: ({ externalEventId, found }) => {
    console.log(
      `[INLINE_FULL][GetEvent] ID: ${externalEventId}, Found: ${found}`
    );
  },
  [EVENTS.DELETE_EVENT]: ({ externalEventId, success }) => {
    console.log(
      `[INLINE_FULL][DeleteEvent] ID: ${externalEventId}, Deleted: ${success}`
    );
  },
  [EVENTS.MARKETING_EVENT_ERROR]: ({ action, error }) => {
    console.error(`[INLINE_FULL][Error] ${action}:`, error.message);
  },
});

const PLUGINS = await loadPlugins([], { useDirectory: true });

const instance = new MarketingEvent(process.env.HUBSPOT_ACCOUNT_ID, {
  plugins: [PLUGINS.TEST_PLUGIN, PLUGINS.LOG_TO_CONSOLE, PLUGINS.INLINE_FULL],
});

const found = await instance.getEvent(payload.externalEventId);
console.log(`Get event result: ${found ? "Found" : "Not Found"}`);

const result = await instance.createEvent(payload);
console.log("✅ Event created:", result.hubspot.eventName);

const pluginResults = getPluginResults(result.plugins);
const inline = pluginResults[PLUGINS.INLINE_FULL];
if (inline?.success) console.log("🔌 Inline plugin returned:", inline.result);

await instance.deleteEvent();
console.log("✅ Event deleted:", payload.eventName);

payload.js

export const payload = {
  eventName: "My Webinar",
  eventOrganizer: "Dante Ielceanu",
  externalAccountId: process.env.HUBSPOT_ACCOUNT_ID,
  externalEventId: 1234567890,
  startDateTime: new Date().toISOString(),
  eventType: "WEBINAR",
  url: "https://example.com/event",
};

Feature Overview

| Feature | Status | | -------------------------- | ------ | | MarketingEvent Core API | ✔ | | Inline Plugins | ✔ | | File-Based Plugins | ✔ | | Bitmask Hook Engine | ✔ | | CLI Plugin Scaffolding | ✔ | | Full IDE Support via Types | ✔ |