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/contentful-sync-service

v1.0.6

Published

Full export + incremental webhook sync to local storage for Contentful content

Readme

@bernierllc/contentful-sync-service

Full export + incremental webhook sync to local storage for Contentful content.

Overview

ContentfulSyncService syncs Contentful content to local storage for ML training pipelines. It performs an initial full export using contentful-export, then maintains sync via CDA Sync API + webhook processing. Provides a structured content mirror for training without hitting Contentful APIs during training.

Features

  • 🔄 Initial Full Export - Complete space export to local storage
  • 📦 Incremental Sync - Delta sync using CDA Sync API
  • 🪝 Webhook Processing - Real-time updates via webhooks
  • 💾 Abstract Storage - Pluggable storage adapters
  • 📊 Sync Statistics - Track sync performance
  • 🔌 NeverHub Integration - Optional integration with NeverHub
  • 📝 Comprehensive Logging - Detailed operation logs

Installation

npm install @bernierllc/contentful-sync-service

Quick Start

import {
  ContentfulSyncService,
  InMemoryContentStorage,
  InMemorySyncStateStorage
} from '@bernierllc/contentful-sync-service';

// Create storage instances
const contentStorage = new InMemoryContentStorage();
const syncStateStorage = new InMemorySyncStateStorage();

// Create service
const service = new ContentfulSyncService({
  spaceId: 'your-space-id',
  environmentId: 'master',
  accessToken: 'your-cda-token',
  contentStorage,
  syncStateStorage,
  webhookSecret: 'your-webhook-secret' // Optional
});

// Initialize
await service.initialize();

// Perform initial sync
const result = await service.initialSync('your-management-token');
console.log(`Synced ${result.stats.entriesSynced} entries`);

// Start auto-sync
service.startAutoSync();

Storage Adapters

Built-in Adapters

The package provides in-memory adapters for testing:

import {
  InMemoryContentStorage,
  InMemorySyncStateStorage
} from '@bernierllc/contentful-sync-service';

const contentStorage = new InMemoryContentStorage();
const syncStateStorage = new InMemorySyncStateStorage();

Custom Storage Adapters

Implement the storage interfaces for your database:

import type { ContentStorage, SyncStateStorage } from '@bernierllc/contentful-sync-service';
import type { Entry, Asset, ContentType } from '@bernierllc/contentful-types';

class PostgreSQLContentStorage implements ContentStorage {
  async storeContentTypes(contentTypes: ContentType[]): Promise<void> {
    // Store in PostgreSQL
  }

  async storeEntries(entries: Entry[]): Promise<void> {
    // Store in PostgreSQL
  }

  async storeAssets(assets: Asset[]): Promise<void> {
    // Store in PostgreSQL
  }

  async updateEntry(entry: Entry): Promise<void> {
    // Update in PostgreSQL
  }

  async updateAsset(asset: Asset): Promise<void> {
    // Update in PostgreSQL
  }

  async deleteEntry(entryId: string): Promise<void> {
    // Delete from PostgreSQL
  }

  async deleteAsset(assetId: string): Promise<void> {
    // Delete from PostgreSQL
  }

  async getEntry(entryId: string): Promise<Entry | null> {
    // Retrieve from PostgreSQL
  }

  async getAsset(assetId: string): Promise<Asset | null> {
    // Retrieve from PostgreSQL
  }

  async clearSpace(spaceId: string): Promise<void> {
    // Clear space data from PostgreSQL
  }
}

class PostgreSQLSyncStateStorage implements SyncStateStorage {
  async getSyncToken(spaceId: string): Promise<string | null> {
    // Get from PostgreSQL
  }

  async setSyncToken(spaceId: string, token: string): Promise<void> {
    // Store in PostgreSQL
  }

  async clearSyncToken(spaceId: string): Promise<void> {
    // Clear from PostgreSQL
  }
}

API Reference

ContentfulSyncService

Constructor

new ContentfulSyncService(config: SyncServiceConfig)

Config Options:

  • spaceId (string) - Contentful space ID
  • environmentId (string) - Contentful environment ID
  • accessToken (string) - CDA access token
  • contentStorage (ContentStorage) - Content storage implementation
  • syncStateStorage (SyncStateStorage) - Sync state storage implementation
  • webhookSecret (string, optional) - Webhook validation secret
  • syncInterval (number, optional) - Auto-sync interval in ms (default: 300000)
  • neverhubEnabled (boolean, optional) - Enable NeverHub integration

Methods

initialize(): Promise<void>

Initialize the service and optionally connect to NeverHub.

await service.initialize();
initialSync(managementToken: string): Promise<SyncResult>

Perform initial full export from Contentful.

const result = await service.initialSync('cma-token');
console.log(result.stats.entriesSynced); // Number of entries synced
incrementalSync(): Promise<SyncResult>

Perform incremental sync using the CDA Sync API.

const result = await service.incrementalSync();
console.log(result.stats.itemsDeleted); // Number of items deleted
processWebhook(body: string, headers: Record<string, string>): Promise<void>

Process a webhook event from Contentful.

app.post('/webhook', async (req, res) => {
  await service.processWebhook(
    JSON.stringify(req.body),
    req.headers
  );
  res.sendStatus(200);
});
startAutoSync(): void

Start automatic incremental sync at the configured interval.

service.startAutoSync();
stopAutoSync(): void

Stop automatic sync.

service.stopAutoSync();
shutdown(): Promise<void>

Shutdown the service cleanly.

await service.shutdown();

Usage Examples

Basic Sync

import {
  ContentfulSyncService,
  InMemoryContentStorage,
  InMemorySyncStateStorage
} from '@bernierllc/contentful-sync-service';

const service = new ContentfulSyncService({
  spaceId: 'my-space',
  environmentId: 'master',
  accessToken: 'cda-token',
  contentStorage: new InMemoryContentStorage(),
  syncStateStorage: new InMemorySyncStateStorage()
});

await service.initialize();
await service.initialSync('cma-token');
await service.incrementalSync();

Auto-Sync

// Start auto-sync every 5 minutes
service.startAutoSync();

// Later...
service.stopAutoSync();

Webhook Processing

import express from 'express';

const app = express();

const service = new ContentfulSyncService({
  spaceId: 'my-space',
  environmentId: 'master',
  accessToken: 'cda-token',
  contentStorage: new InMemoryContentStorage(),
  syncStateStorage: new InMemorySyncStateStorage(),
  webhookSecret: 'webhook-secret'
});

await service.initialize();

app.post('/contentful-webhook', express.json(), async (req, res) => {
  try {
    await service.processWebhook(
      JSON.stringify(req.body),
      req.headers as Record<string, string>
    );
    res.sendStatus(200);
  } catch (error) {
    console.error('Webhook processing failed:', error);
    res.sendStatus(500);
  }
});

app.listen(3000);

NeverHub Integration

const service = new ContentfulSyncService({
  spaceId: 'my-space',
  environmentId: 'master',
  accessToken: 'cda-token',
  contentStorage: new InMemoryContentStorage(),
  syncStateStorage: new InMemorySyncStateStorage(),
  neverhubEnabled: true // Enable NeverHub
});

await service.initialize();
// NeverHub registration happens automatically if available

TypeScript Support

Full TypeScript support with comprehensive type definitions:

import type {
  ContentStorage,
  SyncStateStorage,
  SyncServiceConfig,
  SyncResult,
  SyncStats
} from '@bernierllc/contentful-sync-service';

Error Handling

const result = await service.initialSync('cma-token');

if (!result.success) {
  console.error('Sync failed:', result.error);
} else {
  console.log('Sync succeeded:', result.stats);
}

License

Copyright (c) 2025 Bernier LLC

This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.