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

@patch-adams/core

v1.3.1

Published

Core patching engine for e-learning course packages (Rise, Storyline, Captivate, Xyleme) - inject CSS/JS into SCORM, cmi5, and xAPI courses

Readme

@patch-adams/core

Patch Rise course packages (SCORM 1.2, SCORM 2004, cmi5, xAPI) to inject remote CSS/JS with local fallback. Enables centralized control over deployed e-learning courses.

Features

  • 4 injection points: CSS before/after, JS before/after
  • Remote-first with local fallback: Try CDN first, fallback to bundled files
  • FOUC prevention: Blocking "before" scripts hide content until ready
  • Format detection: Auto-detects SCORM 1.2, SCORM 2004, cmi5, xAPI
  • Manifest updates: Updates imsmanifest.xml for SCORM compliance

Installation

npm install @patch-adams/core

Quick Start

CLI Usage

# Patch a single file
npx patch-adams patch course.zip --remote-domain https://cdn.example.com

# Patch with custom output
npx patch-adams patch course.zip -o patched-course.zip --remote-domain https://cdn.example.com

# Initialize a config file
npx patch-adams init

Programmatic Usage

import { Patcher } from '@patch-adams/core';

const patcher = new Patcher({
  remoteDomain: 'https://cdn.example.com/rise-overrides',

  // HTML classes added to <html> tag
  htmlClass: 'pa-patched',      // Always present
  loadingClass: 'pa-loading',   // Removed when ready

  // CSS injection
  cssBefore: { filename: 'before.css', enabled: true },
  cssAfter: { filename: 'after.css', enabled: true, timeout: 5000 },

  // JS injection
  jsBefore: { filename: 'before.js', enabled: true },
  jsAfter: { filename: 'after.js', enabled: true, timeout: 5000 },

  // Local fallback folders (inside scormcontent/)
  localFolders: { css: 'css', js: 'js' },

  // Update manifests for SCORM compliance
  updateManifests: true,
});

// Patch a file
const patchedBuffer = await patcher.patchFile('course.zip');

// Or patch a buffer directly
const inputBuffer = fs.readFileSync('course.zip');
const outputBuffer = await patcher.patchBuffer(inputBuffer);

Configuration

Create a patch-adams.config.json file:

{
  "remoteDomain": "https://cdn.example.com/rise-overrides",
  "htmlClass": "pa-patched",
  "loadingClass": "pa-loading",
  "cssBefore": {
    "filename": "before.css",
    "enabled": true
  },
  "cssAfter": {
    "filename": "after.css",
    "enabled": true,
    "timeout": 5000
  },
  "jsBefore": {
    "filename": "before.js",
    "enabled": true
  },
  "jsAfter": {
    "filename": "after.js",
    "enabled": true,
    "timeout": 5000
  },
  "localFolders": {
    "css": "css",
    "js": "js"
  },
  "updateManifests": true
}

How It Works

Injection Points

| Slot | Location | Blocking? | Purpose | |------|----------|-----------|---------| | CSS Before | Start of <head> | Yes | Hide content, prevent FOUC | | JS Before | After CSS Before | Yes | Setup, intercept APIs | | CSS After | End of <head> | No (async) | Override Rise styles | | JS After | End of <body> | No (async) | Modify after Rise loads |

Loading Sequence

  1. Browser loads scormcontent/index.html
  2. <html class="pa-patched pa-loading"> applied immediately
  3. CSS Before loads (blocking) - hides content with .pa-loading
  4. JS Before loads (blocking) - setup code runs
  5. Rise CSS/JS loads and initializes
  6. CSS After loads (async with timeout fallback)
  7. JS After loads (async with timeout fallback)
  8. JS After removes .pa-loading class - content reveals

Remote Override Files

Host these files on your CDN at remoteDomain:

https://cdn.example.com/rise-overrides/
├── before.css    # Blocking CSS (hide content)
├── after.css     # Style overrides
├── before.js     # Setup scripts
└── after.js      # Post-initialization scripts

Example CSS Override

/* before.css - Hide content until ready */
html.pa-patched.pa-loading body {
  visibility: hidden !important;
}

/* after.css - Style overrides */
html.pa-patched .blocks-text {
  font-family: 'Your Font', sans-serif !important;
}

html.pa-patched .nav__logo {
  display: none !important;
}

Example JS Override

// before.js - Setup
window.PatchAdams = {
  version: '1.0.0',
  config: { /* custom config */ }
};

// after.js - Post-initialization
document.querySelectorAll('.blocks-text').forEach(el => {
  // Modify elements after Rise renders
});

Patched Package Structure

course.zip
└── scormcontent/
    ├── index.html          # Patched with injection points
    ├── css/
    │   ├── before.css      # Local fallback
    │   └── after.css       # Local fallback
    ├── js/
    │   ├── before.js       # Local fallback
    │   └── after.js        # Local fallback
    └── lib/                # Original Rise files (unchanged)

CLI Commands

# Patch a course
patch-adams patch <input.zip> [options]

Options:
  -o, --output <file>       Output file path
  -r, --remote-domain <url> Remote domain for CSS/JS
  -c, --config <file>       Config file path
  --no-css-before          Disable CSS before injection
  --no-css-after           Disable CSS after injection
  --no-js-before           Disable JS before injection
  --no-js-after            Disable JS after injection

# Initialize config file
patch-adams init

# Show version
patch-adams --version

API Reference

Patcher

class Patcher {
  constructor(config: PatchAdamsConfig)

  // Patch a file on disk
  patchFile(inputPath: string, outputPath?: string): Promise<Buffer>

  // Patch a buffer in memory
  patchBuffer(buffer: Buffer): Promise<Buffer>
}

PatchAdamsConfig

interface PatchAdamsConfig {
  remoteDomain: string;
  htmlClass?: string;           // default: 'pa-patched'
  loadingClass?: string;        // default: 'pa-loading'
  cssBefore?: {
    filename: string;
    enabled: boolean;
  };
  cssAfter?: {
    filename: string;
    enabled: boolean;
    timeout?: number;           // default: 5000
  };
  jsBefore?: {
    filename: string;
    enabled: boolean;
  };
  jsAfter?: {
    filename: string;
    enabled: boolean;
    timeout?: number;           // default: 5000
  };
  localFolders?: {
    css: string;                // default: 'css'
    js: string;                 // default: 'js'
  };
  updateManifests?: boolean;    // default: true
}

License

MIT