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

@bernierllc/social-media-manager

v1.2.0

Published

Platform-agnostic social media orchestration service for multi-platform posting with content transformation and scheduling

Readme

@bernierllc/social-media-manager

Platform-agnostic social media orchestration service for multi-platform posting with content transformation and scheduling.

Features

  • Multi-Platform Posting: Post to Twitter, LinkedIn, Facebook, Instagram, Mastodon, and Bluesky simultaneously
  • Content Transformation: Automatically transform generic content to platform-specific formats
  • Intelligent Scheduling: Schedule posts with jitter (uniform, normal, or exponential distribution)
  • Content Validation: Validate content against platform constraints before posting
  • Platform Management: Connect, disconnect, and monitor platform connections
  • Batch Operations: Post or schedule multiple posts at once
  • NeverHub Integration: Content-type matching and transformation pipeline system

Installation

npm install @bernierllc/social-media-manager

Quick Start

import { SocialMediaManager } from '@bernierllc/social-media-manager';

const manager = new SocialMediaManager({
  platforms: {
    twitter: { enabled: true },
    linkedin: { enabled: true },
  },
  schedulerConfig: {
    enableJitter: true,
    defaultJitter: {
      enabled: true,
      minMinutes: -5,
      maxMinutes: 5,
      distribution: 'uniform',
    },
    timezone: 'UTC',
  },
});

// Post to all connected platforms
const result = await manager.postToAll({
  content: {
    text: 'Hello from all platforms!',
    images: [{ url: 'https://example.com/image.jpg' }],
    hashtags: ['social', 'automation'],
  },
  platforms: ['twitter', 'linkedin'],
});

console.log(`Posted to ${result.summary.successCount} platforms`);

Multi-Platform Posting

Post to All Platforms

const result = await manager.postToAll({
  content: {
    text: 'Universal message for all platforms',
    images: [{ url: 'image.jpg' }],
    hashtags: ['announcement'],
  },
  platforms: ['twitter', 'linkedin', 'facebook'],
});

if (result.success) {
  console.log('Posted to all platforms successfully!');
} else if (result.summary.partialSuccess) {
  console.log(`Posted to ${result.summary.successCount} out of ${result.summary.totalPlatforms} platforms`);
  console.log('Errors:', result.errors);
}

Post to Specific Platforms

const result = await manager.postToPlatforms(
  ['twitter', 'mastodon'],
  {
    content: {
      text: 'Short-form post for Twitter and Mastodon',
      hashtags: ['tech', 'news'],
    },
    platforms: ['twitter', 'mastodon'],
  }
);

Platform-Specific Content

const result = await manager.postToAll({
  content: {
    text: 'Generic content',
  },
  platforms: ['twitter', 'linkedin'],
  platformSpecificContent: {
    twitter: {
      text: 'Short tweet optimized for Twitter',
      hashtags: ['twitter'],
    },
    linkedin: {
      text: 'Professional post optimized for LinkedIn with more detail and context.',
      hashtags: ['linkedin', 'professional'],
    },
  },
});

Scheduling with Jitter

Schedule a Post

const result = await manager.schedulePost({
  content: {
    text: 'Scheduled post',
  },
  platforms: ['twitter', 'linkedin'],
  scheduledTime: new Date('2025-12-01T12:00:00Z'),
  jitter: {
    enabled: true,
    minMinutes: -5,
    maxMinutes: 5,
    distribution: 'uniform',
  },
});

console.log('Schedule ID:', result.scheduleId);
console.log('Scheduled times:', result.scheduledTimes);

Jitter Distributions

// Uniform distribution (equal probability)
const uniform = await manager.schedulePost({
  content: { text: 'Post' },
  platforms: ['twitter'],
  scheduledTime: new Date('2025-12-01T12:00:00Z'),
  jitter: {
    enabled: true,
    minMinutes: -10,
    maxMinutes: 10,
    distribution: 'uniform',
  },
});

// Normal distribution (bell curve)
const normal = await manager.schedulePost({
  content: { text: 'Post' },
  platforms: ['twitter'],
  scheduledTime: new Date('2025-12-01T12:00:00Z'),
  jitter: {
    enabled: true,
    minMinutes: -10,
    maxMinutes: 10,
    distribution: 'normal',
  },
});

// Exponential distribution (weighted toward minimum)
const exponential = await manager.schedulePost({
  content: { text: 'Post' },
  platforms: ['twitter'],
  scheduledTime: new Date('2025-12-01T12:00:00Z'),
  jitter: {
    enabled: true,
    minMinutes: 0,
    maxMinutes: 15,
    distribution: 'exponential',
  },
});

Manage Scheduled Posts

// List all scheduled posts
const allPosts = await manager.listScheduledPosts();

// Filter by status
const pendingPosts = await manager.listScheduledPosts({
  status: ['pending'],
});

// Filter by platform
const twitterPosts = await manager.listScheduledPosts({
  platforms: ['twitter'],
});

// Cancel a scheduled post
await manager.cancelScheduledPost(scheduleId);

Content Transformation

Transform Content

const genericContent = {
  text: 'Long article text that needs to be adapted for each platform...',
  images: [{ url: 'large-image.jpg', width: 5000, height: 5000 }],
  hashtags: Array(50).fill('tag'),
};

// Transform for Twitter (will truncate text, resize images, limit hashtags)
const twitterContent = await manager.transformContent(genericContent, 'twitter');

// Transform for LinkedIn (will apply professional tone)
const linkedinContent = await manager.transformContent(genericContent, 'linkedin');

// Transform for Instagram (will ensure media is present)
const instagramContent = await manager.transformContent(genericContent, 'instagram');

Validate Content

const content = {
  text: 'Test post',
  images: [{ url: 'image.jpg' }],
};

const validation = await manager.validateContentForPlatform(content, 'twitter');

if (!validation.valid) {
  console.error('Validation errors:', validation.errors);
  console.warn('Validation warnings:', validation.warnings);
}

Optimize Content

const optimized = await manager.optimizeForPlatform(
  {
    text: 'Post with too many hashtags',
    hashtags: Array(50).fill('tag'),
  },
  'twitter'
);

Platform Management

Connect Platform

const connection = await manager.connectPlatform('twitter', {
  apiKey: 'your-api-key',
  apiSecret: 'your-api-secret',
  accessToken: 'your-access-token',
  accessTokenSecret: 'your-access-token-secret',
});

console.log('Connected:', connection.connected);
console.log('Username:', connection.username);

Disconnect Platform

await manager.disconnectPlatform('twitter');

Test Connection

const test = await manager.testPlatformConnection('twitter');

console.log('Connected:', test.connected);
console.log('Healthy:', test.healthy);
console.log('Response time:', test.responseTime, 'ms');

List Connections

const connections = await manager.listConnectedPlatforms();

for (const conn of connections) {
  console.log(`${conn.platform}: ${conn.connected ? 'Connected' : 'Disconnected'}`);
}

Get Platform Status

const status = await manager.getPlatformStatus();

for (const [platform, platformStatus] of Object.entries(status)) {
  console.log(`${platform}:`, {
    connected: platformStatus.connected,
    healthy: platformStatus.healthy,
  });
}

Batch Operations

Batch Post

const result = await manager.postBatch({
  requests: [
    {
      content: { text: 'Post 1' },
      platforms: ['twitter'],
    },
    {
      content: { text: 'Post 2' },
      platforms: ['linkedin'],
    },
    {
      content: { text: 'Post 3' },
      platforms: ['facebook'],
    },
  ],
  parallelExecution: true,
  maxConcurrency: 3,
});

console.log(`${result.summary.successful} out of ${result.summary.total} posts succeeded`);

Batch Schedule

const result = await manager.scheduleBatch([
  {
    content: { text: 'Post 1' },
    platforms: ['twitter'],
    scheduledTime: new Date('2025-12-01T12:00:00Z'),
  },
  {
    content: { text: 'Post 2' },
    platforms: ['linkedin'],
    scheduledTime: new Date('2025-12-01T13:00:00Z'),
  },
]);

Content Type Registry

The package includes a content-type registry system for NeverHub integration:

import { CONTENT_TYPE_REGISTRY, ContentTypeMatching } from '@bernierllc/social-media-manager';

const matching = new ContentTypeMatching();

// Find compatible platforms
const platforms = await matching.findCompatibleTypes('generic-social-content');
console.log('Supported platforms:', platforms);

// Get transformation pipeline
const pipeline = await matching.getTransformationPipeline(
  'generic-social-content',
  'social-media-content:twitter'
);
console.log('Transformations:', pipeline);

Statistics and Cleanup

Get Schedule Statistics

const stats = manager.getScheduleStatistics();

console.log('Total scheduled:', stats.total);
console.log('Pending:', stats.pending);
console.log('Posted:', stats.posted);
console.log('Failed:', stats.failed);
console.log('Cancelled:', stats.cancelled);

Clean Up Old Posts

// Remove completed/cancelled posts older than 30 days
const removed = manager.cleanupOldScheduledPosts(30);
console.log(`Removed ${removed} old posts`);

TypeScript Support

Full TypeScript support with comprehensive type definitions:

import type {
  SocialMediaManagerConfig,
  GenericSocialContent,
  MultiPlatformPostRequest,
  SchedulePostRequest,
  JitterConfig,
} from '@bernierllc/social-media-manager';

License

Copyright (c) 2025 Bernier LLC. See LICENSE for details.