
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
integrakit-universal
Advanced tools
Universal integration hub: OAuth2 connections, actions, triggers, webhooks, logs. Express & Nest adapters. OpenAPI action generator.
Universal Integration Hub (modeled on the legacy integration_module) with equivalent APIs and behavior:
npm i integrakit-universal simple-oauth2 mongodb js-yaml
{ id, name, actions, triggers, oauth?, webhook? }(ctx, params)=>any(ctx, trigger)=>{ events: any[]; cursor?: any }{ id, ts, level, message, data? }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);
import { nest } from "integrakit-universal/nest";
nest.mountIntegrationRouters(app, hub, { mountBasePath: "/api/integrations" });
hub.startScheduler();
{ id, providerId, name, oauth?, apiKeyAuth?, createdAt }{ id, providerId, appId, tenantId?, userId?, accountLabel?, auth, createdAt, updatedAt? }{ id, providerId, type, connectionId, scheduleMs, cursor?, config?, active, status?, accountId?, userId?, description?, createdAt, updatedAt? }{ id, ts, level, providerId?, subject?, message, data?, tenantId?, userId? }Mongo collections: integration_apps, integration_connections, integration_triggers, integration_logs
/health → { status: "ok" }/healthz → { status: "ok" }/providers → [{ id, name, oauth }]
id string: provider id (e.g. github)name string: human readableoauth boolean: whether provider defines oauth metadata/dynamic/providers/openapi
providerId string (required)name string (required)openapi object (required): OpenAPI v3 document (JSON){ ok: true, provider: { id, name } }/oauth/:provider/start
tenantId string|number (optional){ url: string } (authorization URL)/oauth/:provider/callback
code string (required)tenantId string|number (optional)userId string|number (optional){ ok: true, connectionId: string }/webhook/:provider
{ ok: boolean, events?: any[] } (provider-defined)/connections/:provider
tenantId?, userId?Connection[]
id, providerId, appId?, tenantId?, userId?, accountLabel?, auth ({ accessToken?, refreshToken?, expiresAt?, data? }), createdAt, updatedAt?/connections
providerId string (required)appId string (optional)tenantId string|number (optional)userId string|number (optional)accountLabel string (optional)auth object (optional): { accessToken?, refreshToken?, expiresAt?, data? }Connection/connections/:id
ConnectionConnection/connections/:id
{ success: boolean }Connected-accounts shortcuts:
/connected-accounts/userintegrationapp/:integrationId/connected-accounts/connectionsByUserId/:userId/connected-accounts/:id/connected-accounts/:id/allappsintigrations
{ id?, name, actions?, triggers?, oauth? }
actions object: { [actionName]: (ctx, params)=>Promise<any> } (register programmatically)triggers object: { [triggerType]: (ctx, trigger)=>Promise<{ events, cursor? }>} /allappsintigrations/allApps → provider[]/allappsintigrations/user/:userId → Connection[]/allappsintigrations/:type → provider/allappsintigrations/fetchNodeDefinition/:type → { name, actions: string[] }/allappsintigrations/fetchTriggerDefinition/:type → { name, triggers: string[] }/allappsintigrations/:id → { success: true }/dynamic-api/integrations
{ name: string, openApiSpec: object, description?: string }/dynamic-api/integrations → { name: string }[]/dynamic-api/integrations/:name → provider/dynamic-api/integrations/:name
{ openApiSpec: object }/dynamic-api/integrations/:name → { success: true }/dynamic-api/integrations/:name/auth-config
{ authConfig: any } (persisted as app oauth config){ ok: true }/dynamic-api/integrations/upload
{ name, description?, openApiSpec, authConfig? }{ name, description }/dynamic-oauth/configuren8ntype/:name
{ clientId?, clientSecret?, tokenUrl?, authorizeUrl?, scopes?, redirectPath? }{ ok: true }/dynamic-oauth/configure/:name → same as above/dynamic-oauth/authorize/:id
tenantId? state?{ url }/dynamic-oauth/authorize/:integrationName/:authSchemeName
{ url }/dynamic-oauth/callback
code, state/dynamic-oauth/refresh/:accountId → { ok: true }/dynamic-oauth/apps → App[] (storage-dependent)/dynamic-oauth/apps/:integrationName → App/dynamic-oauth/apps/:integrationName → { success }/dynamic-oauth/apps/:integrationName/auth-schemes → { integration, auth_schemes: any[] }/dynamic-oauth/validate-schema
req.file (YAML/JSON){ valid: boolean, message: string, schema?: any }/dynamic-oauth/store-credentials/:id/:authMethod
{ userName: string, credentials: Record<string,any>, accountName?: string }{ ok: true }/dynamic-oauth/credential-fields/:integrationName/:authSchemeName → { fields: any[] }/dynamic-oauth/test-credentials/:integrationName/:authSchemeName → { ok: true }/dynamic-oauth/accounts/:userId/:integrationName → Connection[]/dynamic-oauth/accounts/:accountId → { success: boolean }/actions/:provider/:action
{ connectionId: string, params?: any }Execution helpers (legacy parity):
/execution/direct/execute
{ accountId, integrationName, method, path, pathParams?, queryParams?, body?, headers? }{ ... } or { status, body }/execution/actions/execute
{ accountId, actionName, integrationName, params?, pathParams?, queryParams?, body?, headers? }{ success: true, data }/execution/endpoints/execute → same shape as direct/execute but endpoint/execution/direct/list
{ integrationName }{ integrationName, paths: string[], totalPaths: number }/execution/actions/list
{ integrationName }{ integrationName, actions: string[], totalActions: number }/execution/actions/:actionName/details → { name: string }/execution/actions/batch
{ accountId, actions: { integrationName, actionName, params }[], parallel?: boolean }{ results: { success:boolean, data?|error? }[], summary: { total, successful, failed, executionMode } }/nodes → [{ name, providerId, type }]/nodes/type/:type → [{ name, providerId }] where type in (action,trigger)/nodes/:name → { name, type, providerId }/nodes/:name/execute
{ inputData?: any, parameters?: any, AccountId?: string, metadata?: any[] }{ success: true, data }/nodes/:nodeName/load-options/:methodName → { success: true, data: any[] }/triggers
Trigger minimal
providerId, type, connectionId, scheduleMscursor, config, active?, status?, accountId?, userId?, description?Trigger/triggers/management/list → { triggers: Trigger[], total }
status? in (active,inactive,paused,error,stopped), userId?, accountId?, limit?/triggers/management/:triggerId → Trigger/triggers/management/:triggerId
Trigger>Trigger/triggers/management/:triggerId → { success }/triggers/management/:triggerId/(start|stop|pause|resume) → { ok: true }/triggers/bulk/(start|stop)
{ triggerIds: string[] }{ success: true, data: { results: { triggerId, success, error? }[], summary: { total, successful, failed } } }/triggers/:triggerId/status → Trigger/triggers/:triggerId/status-with-logs
logLimit? number (default 20){ success, data: { status: Trigger, stats: { totalEvents, uniqueEvents }, recentLogs }, message }/triggers/user/:userId/active → { success, data: { triggers, total } }/triggers/account/:accountId/triggers → { success, data: { triggers, total } }/triggers/types/available → { success, data: { type, name, description, category }[] }/triggers/:type/test
{ providerId: string, connectionId: string }{ success, data: { recentLogs } }/triggers/analytics/overview → { total, active, paused }/triggers/analytics/by-type → { triggersByType: Record<string,number>, total }/triggers/validate
{ ...any }{ success, data: { valid: true, warnings: string[], suggestions: string[] }, message }/trigger-logs
LogEntry without id/ts (server assigns){ ok: true }/trigger-logs
triggerId (required), limit? numberLogEntry[]/trigger-logs/:triggerId/stats → { success: true, data: { totalEvents, uniqueEvents } }/trigger-logs/errors/recent ?limit=10 → { success, data: LogEntry[] }/trigger-logs/:id → { success, data: LogEntry }/trigger-logs/:id → { success }/trigger-logs/trigger/:triggerId → { success, data: { deletedCount } }/trigger-logs/cleanup
{ daysToKeep?: number } (default 30){ success, data: { deletedCount, daysToKeep }, message }/trigger-logs/dashboard/overview → { success, data: { stats, recentErrors, timestamp }, message }/trigger-logs/queue/health → { success, data: { status, message, timestamp }, message }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)create/update/delete/get/find, start/stop/pause/resume, runTrigger, startSchedulerlog, findLogsByTriggerId, getTriggerStats, getRecentErrors, deleteLog, deleteLogsByTriggerIdImplement the Storage interface (Memory and Mongo adapters included). Mongo creates helpful indexes.
MIT
FAQs
Universal integration hub: OAuth2 connections, actions, triggers, webhooks, logs. Express & Nest adapters. OpenAPI action generator.
The npm package integrakit-universal receives a total of 0 weekly downloads. As such, integrakit-universal popularity was classified as not popular.
We found that integrakit-universal demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.