@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
Maintainers
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.xmlfor SCORM compliance
Installation
npm install @patch-adams/coreQuick 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 initProgrammatic 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
- Browser loads
scormcontent/index.html <html class="pa-patched pa-loading">applied immediately- CSS Before loads (blocking) - hides content with
.pa-loading - JS Before loads (blocking) - setup code runs
- Rise CSS/JS loads and initializes
- CSS After loads (async with timeout fallback)
- JS After loads (async with timeout fallback)
- JS After removes
.pa-loadingclass - 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 scriptsExample 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 --versionAPI 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
