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

aaspai-integrakit

v0.0.1

Published

Universal integration hub: OAuth2 connections, actions, triggers, webhooks, logs. Express & Nest adapters. OpenAPI action generator.

Readme

integrakit-universal

Universal Integration Hub (modeled on the legacy integration_module) with equivalent APIs and behavior:

  • Providers registry and dynamic registration from OpenAPI
  • OAuth2 apps and user Connections
  • Actions and Triggers (poll/webhook) with scheduler
  • Trigger Logs, analytics, cleanup
  • Dynamic OAuth helper endpoints
  • Dynamic API endpoints and Execution helpers
  • Nodes facade (list/describe/execute, load-options)
  • Express & Nest adapters; Storage (Memory, MongoDB)

Install

npm i integrakit-universal simple-oauth2 mongodb js-yaml

Core concepts

  • App: OAuth client (e.g., Google/GitHub app)
  • Connection: per-user/tenant account with tokens
  • Provider: { id, name, actions, triggers, oauth?, webhook? }
  • Action: async function (ctx, params)=>any
  • Trigger: async function (ctx, trigger)=>{ events: any[]; cursor?: any }
  • Log: audit entry { id, ts, level, message, data? }

Quick start (Express)

import express from "express";
import { MongoClient } from "mongodb";
import { createHub, MongoStorage } from "integrakit-universal/core";
import { createIntegrationRouter, createWebhookRouter } from "integrakit-universal/express";

const client = new MongoClient(process.env.MONGO_URI!);
await client.connect();
const storage = new MongoStorage(client.db(process.env.MONGO_DB));

const hub = createHub({
  storage,
  baseUrl: process.env.PUBLIC_BASE_URL!,
  providers: [],
});

const app = express();
app.use(express.json());
app.use("/api/integrations", createIntegrationRouter(hub));
app.use("/api/integrations/webhook", createWebhookRouter(hub));

hub.startScheduler();
app.listen(3000);

Nest

import { nest } from "integrakit-universal/nest";
nest.mountIntegrationRouters(app, hub, { mountBasePath: "/api/integrations" });
hub.startScheduler();

Data model (Storage)

  • App: { id, providerId, name, oauth?, apiKeyAuth?, createdAt }
  • Connection: { id, providerId, appId, tenantId?, userId?, accountLabel?, auth, createdAt, updatedAt? }
  • Trigger: { id, providerId, type, connectionId, scheduleMs, cursor?, config?, active, status?, accountId?, userId?, description?, createdAt, updatedAt? }
  • Log: { id, ts, level, providerId?, subject?, message, data?, tenantId?, userId? }

Mongo collections: integration_apps, integration_connections, integration_triggers, integration_logs

Express Router API

Health

  • GET /health{ status: "ok" }
  • GET /healthz{ status: "ok" }

Providers

  • GET /providers[{ id, name, oauth }]
    • Response item:
      • id string: provider id (e.g. github)
      • name string: human readable
      • oauth boolean: whether provider defines oauth metadata
  • POST /dynamic/providers/openapi
    • Request body:
      • providerId string (required)
      • name string (required)
      • openapi object (required): OpenAPI v3 document (JSON)
    • Response: { ok: true, provider: { id, name } }

OAuth and Webhooks

  • GET /oauth/:provider/start
    • Query:
      • tenantId string|number (optional)
    • Response: { url: string } (authorization URL)
  • GET /oauth/:provider/callback
    • Query:
      • code string (required)
      • tenantId string|number (optional)
      • userId string|number (optional)
    • Response: { ok: true, connectionId: string }
  • POST /webhook/:provider
    • Headers: provider-specific signature headers
    • Body: raw Buffer (any content type)
    • Response: { ok: boolean, events?: any[] } (provider-defined)

Connections (Connected Accounts parity)

  • GET /connections/:provider
    • Query: tenantId?, userId?
    • Response: Connection[]
      • id, providerId, appId?, tenantId?, userId?, accountLabel?, auth ({ accessToken?, refreshToken?, expiresAt?, data? }), createdAt, updatedAt?
  • POST /connections
    • Body:
      • providerId string (required)
      • appId string (optional)
      • tenantId string|number (optional)
      • userId string|number (optional)
      • accountLabel string (optional)
      • auth object (optional): { accessToken?, refreshToken?, expiresAt?, data? }
    • Response: Connection
  • PUT /connections/:id
    • Body: any subset of Connection
    • Response: updated Connection
  • DELETE /connections/:id
    • Response: { success: boolean }

Connected-accounts shortcuts:

  • GET /connected-accounts/userintegrationapp/:integrationId
  • GET /connected-accounts/connectionsByUserId/:userId
  • GET /connected-accounts/:id
  • DELETE /connected-accounts/:id

Integrations (allappsintigrations parity)

  • POST /allappsintigrations
    • Body: { id?, name, actions?, triggers?, oauth? }
      • actions object: { [actionName]: (ctx, params)=>Promise<any> } (register programmatically)
      • triggers object: { [triggerType]: (ctx, trigger)=>Promise<{ events, cursor? }>}
    • Response: provider object
  • GET /allappsintigrations/allApps → provider[]
  • GET /allappsintigrations/user/:userIdConnection[]
  • GET /allappsintigrations/:type → provider
  • GET /allappsintigrations/fetchNodeDefinition/:type{ name, actions: string[] }
  • GET /allappsintigrations/fetchTriggerDefinition/:type{ name, triggers: string[] }
  • DELETE /allappsintigrations/:id{ success: true }

Dynamic API (OpenAPI registry)

  • POST /dynamic-api/integrations
    • Body: { name: string, openApiSpec: object, description?: string }
  • GET /dynamic-api/integrations{ name: string }[]
  • GET /dynamic-api/integrations/:name → provider
  • PUT /dynamic-api/integrations/:name
    • Body: { openApiSpec: object }
    • Response: provider
  • DELETE /dynamic-api/integrations/:name{ success: true }
  • PUT /dynamic-api/integrations/:name/auth-config
    • Body: { authConfig: any } (persisted as app oauth config)
    • Response: { ok: true }
  • POST /dynamic-api/integrations/upload
    • Body (JSON; for multipart, attach multer in host app): { name, description?, openApiSpec, authConfig? }
    • Response: { name, description }

Dynamic OAuth

  • POST /dynamic-oauth/configuren8ntype/:name
    • Body: { clientId?, clientSecret?, tokenUrl?, authorizeUrl?, scopes?, redirectPath? }
    • Response: { ok: true }
  • POST /dynamic-oauth/configure/:name → same as above
  • GET /dynamic-oauth/authorize/:id
    • Query: tenantId? state?
    • Response: { url }
  • GET /dynamic-oauth/authorize/:integrationName/:authSchemeName
    • Query: same
    • Response: { url }
  • GET /dynamic-oauth/callback
    • Query: code, state
    • Response: HTML page (success/error)
  • POST /dynamic-oauth/refresh/:accountId{ ok: true }
  • GET /dynamic-oauth/apps → App[] (storage-dependent)
  • GET /dynamic-oauth/apps/:integrationName → App
  • DELETE /dynamic-oauth/apps/:integrationName{ success }
  • GET /dynamic-oauth/apps/:integrationName/auth-schemes{ integration, auth_schemes: any[] }
  • POST /dynamic-oauth/validate-schema
    • Expects req.file (YAML/JSON)
    • Response: { valid: boolean, message: string, schema?: any }
  • POST /dynamic-oauth/store-credentials/:id/:authMethod
    • Body: { userName: string, credentials: Record<string,any>, accountName?: string }
    • Response: { ok: true }
  • GET /dynamic-oauth/credential-fields/:integrationName/:authSchemeName{ fields: any[] }
  • POST /dynamic-oauth/test-credentials/:integrationName/:authSchemeName{ ok: true }
  • GET /dynamic-oauth/accounts/:userId/:integrationNameConnection[]
  • DELETE /dynamic-oauth/accounts/:accountId{ success: boolean }

Actions & Execution

  • POST /actions/:provider/:action
    • Body: { connectionId: string, params?: any }
    • Response: any (provider-defined)

Execution helpers (legacy parity):

  • POST /execution/direct/execute
    • Body: { accountId, integrationName, method, path, pathParams?, queryParams?, body?, headers? }
    • Response: backend response { ... } or { status, body }
  • POST /execution/actions/execute
    • Body: { accountId, actionName, integrationName, params?, pathParams?, queryParams?, body?, headers? }
    • Response: { success: true, data }
  • POST /execution/endpoints/execute → same shape as direct/execute but endpoint
  • POST /execution/direct/list
    • Body: { integrationName }
    • Response: { integrationName, paths: string[], totalPaths: number }
  • POST /execution/actions/list
    • Body: { integrationName }
    • Response: { integrationName, actions: string[], totalActions: number }
  • POST /execution/actions/:actionName/details{ name: string }
  • POST /execution/actions/batch
    • Body: { accountId, actions: { integrationName, actionName, params }[], parallel?: boolean }
    • Response: { results: { success:boolean, data?|error? }[], summary: { total, successful, failed, executionMode } }

Nodes Facade

  • GET /nodes[{ name, providerId, type }]
  • GET /nodes/type/:type[{ name, providerId }] where type in (action,trigger)
  • GET /nodes/:name{ name, type, providerId }
  • POST /nodes/:name/execute
    • Body: { inputData?: any, parameters?: any, AccountId?: string, metadata?: any[] }
    • Response: { success: true, data }
  • POST /nodes/:nodeName/load-options/:methodName{ success: true, data: any[] }

Triggers

  • POST /triggers
    • Body: Trigger minimal
      • required: providerId, type, connectionId, scheduleMs
      • optional: cursor, config, active?, status?, accountId?, userId?, description?
    • Response: created Trigger
  • GET /triggers/management/list{ triggers: Trigger[], total }
    • Query: status? in (active,inactive,paused,error,stopped), userId?, accountId?, limit?
  • GET /triggers/management/:triggerIdTrigger
  • PUT /triggers/management/:triggerId
    • Body: Partial<Trigger>
    • Response: updated Trigger
  • DELETE /triggers/management/:triggerId{ success }
  • POST /triggers/management/:triggerId/(start|stop|pause|resume){ ok: true }
  • POST /triggers/bulk/(start|stop)
    • Body: { triggerIds: string[] }
    • Response: { success: true, data: { results: { triggerId, success, error? }[], summary: { total, successful, failed } } }
  • GET /triggers/:triggerId/statusTrigger
  • GET /triggers/:triggerId/status-with-logs
    • Query: logLimit? number (default 20)
    • Response: { success, data: { status: Trigger, stats: { totalEvents, uniqueEvents }, recentLogs }, message }
  • GET /triggers/user/:userId/active{ success, data: { triggers, total } }
  • GET /triggers/account/:accountId/triggers{ success, data: { triggers, total } }
  • GET /triggers/types/available{ success, data: { type, name, description, category }[] }
  • POST /triggers/:type/test
    • Body: { providerId: string, connectionId: string }
    • Response: { success, data: { recentLogs } }
  • GET /triggers/analytics/overview{ total, active, paused }
  • GET /triggers/analytics/by-type{ triggersByType: Record<string,number>, total }
  • POST /triggers/validate
    • Body: { ...any }
    • Response: { success, data: { valid: true, warnings: string[], suggestions: string[] }, message }

Trigger Logs

  • POST /trigger-logs
    • Body: LogEntry without id/ts (server assigns)
    • Response: { ok: true }
  • GET /trigger-logs
    • Query: triggerId (required), limit? number
    • Response: LogEntry[]
  • GET /trigger-logs/:triggerId/stats{ success: true, data: { totalEvents, uniqueEvents } }
  • GET /trigger-logs/errors/recent ?limit=10{ success, data: LogEntry[] }
  • GET /trigger-logs/:id{ success, data: LogEntry }
  • DELETE /trigger-logs/:id{ success }
  • DELETE /trigger-logs/trigger/:triggerId{ success, data: { deletedCount } }
  • POST /trigger-logs/cleanup
    • Body: { daysToKeep?: number } (default 30)
    • Response: { success, data: { deletedCount, daysToKeep }, message }
  • GET /trigger-logs/dashboard/overview{ success, data: { stats, recentErrors, timestamp }, message }
  • GET /trigger-logs/queue/health{ success, data: { status, message, timestamp }, message }

Programmatic API (Core)

  • createHub(options): returns Hub
    • hub.addProvider(p), hub.removeProvider(id), hub.listProviders()
    • hub.oauthStart(id, tenantId?, state?), hub.oauthCallback(id, code, tenantId?, userId?)
    • hub.callAction(providerId, action, connectionId, params)
    • Triggers: create/update/delete/get/find, start/stop/pause/resume, runTrigger, startScheduler
    • Logs: log, findLogsByTriggerId, getTriggerStats, getRecentErrors, deleteLog, deleteLogsByTriggerId

Storage

Implement the Storage interface (Memory and Mongo adapters included). Mongo creates helpful indexes.

Notes on parity with legacy integration_module

  • Route surfaces are equivalent. Authentication/guards are intentionally excluded; add them in your host app.
  • Nodes runtime is provided as a facade mapping providers’ actions/triggers; extend if you rely on node-specific metadata/behaviors.
  • Dynamic API multipart uploads can be enabled by adding multer on your app and passing files to the same endpoints.

MIT