@patch-adams/core 1.5.24 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -564,6 +564,53 @@ interface CourseMetadata {
564
564
  */
565
565
  declare function extractCourseMetadata(zip: AdmZip, format: string): CourseMetadata;
566
566
 
567
+ /**
568
+ * Parsed course structure types for Rise SCORM packages.
569
+ * Used by the parse-structure API and the frontend tree view.
570
+ */
571
+ /** A single block within a Rise lesson */
572
+ interface CourseStructureBlock {
573
+ /** Rise block ID (e.g. "cmk2wzn2p007q3b7hsiobz031") */
574
+ id: string;
575
+ /** Block type: "text", "image", "interactive", "knowledgeCheck", etc. */
576
+ type: string;
577
+ /** Block family: "text", "media", "interactive", etc. */
578
+ family?: string;
579
+ /** Block variant: "paragraph", "accordion", "hero", etc. */
580
+ variant?: string;
581
+ /** Human-readable title derived from block content or type label */
582
+ title?: string;
583
+ }
584
+ /** A lesson (section) in a Rise course */
585
+ interface CourseStructureLesson {
586
+ /** Rise lesson ID */
587
+ id: string;
588
+ /** Lesson title */
589
+ title: string;
590
+ /** Lesson type (e.g. "blocks") */
591
+ type: string;
592
+ /** Blocks within this lesson */
593
+ blocks: CourseStructureBlock[];
594
+ }
595
+ /** Parsed course structure returned by extractCourseStructure() */
596
+ interface CourseStructure {
597
+ /** Course title */
598
+ courseTitle: string;
599
+ /** Course identifier from manifest */
600
+ courseId?: string;
601
+ /** Lessons in the course */
602
+ lessons: CourseStructureLesson[];
603
+ }
604
+ /** User-assigned class mappings at course, lesson, and block levels */
605
+ interface CourseClassMappings {
606
+ /** Space-separated class names for the <html> element (course level) */
607
+ course?: string;
608
+ /** Lesson ID -> space-separated class names */
609
+ lessons?: Record<string, string>;
610
+ /** Block ID -> space-separated class names */
611
+ blocks?: Record<string, string>;
612
+ }
613
+
567
614
  /**
568
615
  * Base configuration for any plugin
569
616
  */
@@ -880,6 +927,8 @@ interface PatchOptions {
880
927
  skin?: string;
881
928
  /** Original package filename — baked into LRS bridge for statement searchability */
882
929
  packageName?: string;
930
+ /** Custom CSS class mappings for course, lesson, and block levels */
931
+ classMappings?: CourseClassMappings;
883
932
  }
884
933
  /**
885
934
  * Main Patcher class for patching e-learning course packages
@@ -1113,6 +1162,31 @@ declare function generateJsAfterLoader(options: JsAfterOptions): string;
1113
1162
  */
1114
1163
  declare function buildJsAfterOptions(config: PatchAdamsConfig): JsAfterOptions;
1115
1164
 
1165
+ /**
1166
+ * Class Mappings Template
1167
+ *
1168
+ * Generates an inline JavaScript snippet that applies user-defined CSS classes
1169
+ * to course, lesson, and block elements at runtime.
1170
+ *
1171
+ * Timing: This script is injected inline (via plugin assets) so it executes
1172
+ * synchronously BEFORE the pa-loading class is removed. The user never sees
1173
+ * an un-styled flash — classes are applied while content is still hidden.
1174
+ *
1175
+ * - Course classes: added to <html> element immediately
1176
+ * - Block classes: added to [data-block-id] elements via MutationObserver
1177
+ * - Lesson classes: toggled on <html> via hashchange (Rise SPA routing)
1178
+ */
1179
+
1180
+ /**
1181
+ * Check if class mappings contain any actual assignments
1182
+ */
1183
+ declare function hasClassMappings(mappings?: CourseClassMappings): boolean;
1184
+ /**
1185
+ * Generate an inline JavaScript snippet that applies class mappings at runtime.
1186
+ * Returns an empty string if there are no mappings to apply.
1187
+ */
1188
+ declare function generateClassMappingsScript(mappings: CourseClassMappings): string;
1189
+
1116
1190
  type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
1117
1191
  /**
1118
1192
  * Simple logger utility for Patch-Adams
@@ -1129,6 +1203,19 @@ declare class Logger {
1129
1203
  setLevel(level: LogLevel): void;
1130
1204
  }
1131
1205
 
1206
+ /**
1207
+ * Rise Course Structure Parser
1208
+ *
1209
+ * Extracts the hierarchical course structure (lessons + blocks) from a
1210
+ * Rise SCORM ZIP package by parsing the embedded base64-encoded course JSON.
1211
+ */
1212
+
1213
+ /**
1214
+ * Extract course structure from a Rise SCORM ZIP buffer.
1215
+ * Returns null if the ZIP is not a Rise package or parsing fails.
1216
+ */
1217
+ declare function extractCourseStructure(zipBuffer: Buffer): CourseStructure | null;
1218
+
1132
1219
  /**
1133
1220
  * Plugin Registry for Patch-Adams
1134
1221
  *
@@ -1198,4 +1285,4 @@ declare class PluginRegistry {
1198
1285
  */
1199
1286
  declare const pluginRegistry: PluginRegistry;
1200
1287
 
1201
- export { type AsyncAssetConfig, AsyncAssetConfigSchema, type AuthoringTool, AuthoringToolDetector, type AuthoringToolInfo, type BlockingAssetConfig, BlockingAssetConfigSchema, type CourseFingerprint, type CourseMetadata, type CssAfterOptions, type CssBeforeOptions, DEFAULT_LRS_OPTIONS, FormatDetector, type GeneratedPluginAssets, HtmlInjector, type JsAfterOptions, type JsBeforeOptions, type LocalFoldersConfig, LocalFoldersConfigSchema, type LogLevel, Logger, type LrsBridgeConfig, LrsBridgeConfigSchema, type LrsBridgeOptions, type ManifestPaths, ManifestUpdater, type PackageFormat, type PatchAdamsConfig, PatchAdamsConfigSchema, type PatchAdamsPlugin, type PatchOptions, type PatchResult, Patcher, type PluginAssets, type PluginConfig, PluginRegistry, type PluginsConfig, StorylineHtmlInjector, buildCssAfterOptions, buildCssBeforeOptions, buildJsAfterOptions, buildJsBeforeOptions, defaultConfig, extractCourseFingerprint, extractCourseMetadata, generateConfigTemplate, generateCssAfterLoader, generateCssBeforeLoader, generateJsAfterLoader, generateJsBeforeLoader, generateLrsBridgeCode, loadConfig, mergeWithDefaults, pluginRegistry };
1288
+ export { type AsyncAssetConfig, AsyncAssetConfigSchema, type AuthoringTool, AuthoringToolDetector, type AuthoringToolInfo, type BlockingAssetConfig, BlockingAssetConfigSchema, type CourseClassMappings, type CourseFingerprint, type CourseMetadata, type CourseStructure, type CourseStructureBlock, type CourseStructureLesson, type CssAfterOptions, type CssBeforeOptions, DEFAULT_LRS_OPTIONS, FormatDetector, type GeneratedPluginAssets, HtmlInjector, type JsAfterOptions, type JsBeforeOptions, type LocalFoldersConfig, LocalFoldersConfigSchema, type LogLevel, Logger, type LrsBridgeConfig, LrsBridgeConfigSchema, type LrsBridgeOptions, type ManifestPaths, ManifestUpdater, type PackageFormat, type PatchAdamsConfig, PatchAdamsConfigSchema, type PatchAdamsPlugin, type PatchOptions, type PatchResult, Patcher, type PluginAssets, type PluginConfig, PluginRegistry, type PluginsConfig, StorylineHtmlInjector, buildCssAfterOptions, buildCssBeforeOptions, buildJsAfterOptions, buildJsBeforeOptions, defaultConfig, extractCourseFingerprint, extractCourseMetadata, extractCourseStructure, generateClassMappingsScript, generateConfigTemplate, generateCssAfterLoader, generateCssBeforeLoader, generateJsAfterLoader, generateJsBeforeLoader, generateLrsBridgeCode, hasClassMappings, loadConfig, mergeWithDefaults, pluginRegistry };
package/dist/index.d.ts CHANGED
@@ -564,6 +564,53 @@ interface CourseMetadata {
564
564
  */
565
565
  declare function extractCourseMetadata(zip: AdmZip, format: string): CourseMetadata;
566
566
 
567
+ /**
568
+ * Parsed course structure types for Rise SCORM packages.
569
+ * Used by the parse-structure API and the frontend tree view.
570
+ */
571
+ /** A single block within a Rise lesson */
572
+ interface CourseStructureBlock {
573
+ /** Rise block ID (e.g. "cmk2wzn2p007q3b7hsiobz031") */
574
+ id: string;
575
+ /** Block type: "text", "image", "interactive", "knowledgeCheck", etc. */
576
+ type: string;
577
+ /** Block family: "text", "media", "interactive", etc. */
578
+ family?: string;
579
+ /** Block variant: "paragraph", "accordion", "hero", etc. */
580
+ variant?: string;
581
+ /** Human-readable title derived from block content or type label */
582
+ title?: string;
583
+ }
584
+ /** A lesson (section) in a Rise course */
585
+ interface CourseStructureLesson {
586
+ /** Rise lesson ID */
587
+ id: string;
588
+ /** Lesson title */
589
+ title: string;
590
+ /** Lesson type (e.g. "blocks") */
591
+ type: string;
592
+ /** Blocks within this lesson */
593
+ blocks: CourseStructureBlock[];
594
+ }
595
+ /** Parsed course structure returned by extractCourseStructure() */
596
+ interface CourseStructure {
597
+ /** Course title */
598
+ courseTitle: string;
599
+ /** Course identifier from manifest */
600
+ courseId?: string;
601
+ /** Lessons in the course */
602
+ lessons: CourseStructureLesson[];
603
+ }
604
+ /** User-assigned class mappings at course, lesson, and block levels */
605
+ interface CourseClassMappings {
606
+ /** Space-separated class names for the <html> element (course level) */
607
+ course?: string;
608
+ /** Lesson ID -> space-separated class names */
609
+ lessons?: Record<string, string>;
610
+ /** Block ID -> space-separated class names */
611
+ blocks?: Record<string, string>;
612
+ }
613
+
567
614
  /**
568
615
  * Base configuration for any plugin
569
616
  */
@@ -880,6 +927,8 @@ interface PatchOptions {
880
927
  skin?: string;
881
928
  /** Original package filename — baked into LRS bridge for statement searchability */
882
929
  packageName?: string;
930
+ /** Custom CSS class mappings for course, lesson, and block levels */
931
+ classMappings?: CourseClassMappings;
883
932
  }
884
933
  /**
885
934
  * Main Patcher class for patching e-learning course packages
@@ -1113,6 +1162,31 @@ declare function generateJsAfterLoader(options: JsAfterOptions): string;
1113
1162
  */
1114
1163
  declare function buildJsAfterOptions(config: PatchAdamsConfig): JsAfterOptions;
1115
1164
 
1165
+ /**
1166
+ * Class Mappings Template
1167
+ *
1168
+ * Generates an inline JavaScript snippet that applies user-defined CSS classes
1169
+ * to course, lesson, and block elements at runtime.
1170
+ *
1171
+ * Timing: This script is injected inline (via plugin assets) so it executes
1172
+ * synchronously BEFORE the pa-loading class is removed. The user never sees
1173
+ * an un-styled flash — classes are applied while content is still hidden.
1174
+ *
1175
+ * - Course classes: added to <html> element immediately
1176
+ * - Block classes: added to [data-block-id] elements via MutationObserver
1177
+ * - Lesson classes: toggled on <html> via hashchange (Rise SPA routing)
1178
+ */
1179
+
1180
+ /**
1181
+ * Check if class mappings contain any actual assignments
1182
+ */
1183
+ declare function hasClassMappings(mappings?: CourseClassMappings): boolean;
1184
+ /**
1185
+ * Generate an inline JavaScript snippet that applies class mappings at runtime.
1186
+ * Returns an empty string if there are no mappings to apply.
1187
+ */
1188
+ declare function generateClassMappingsScript(mappings: CourseClassMappings): string;
1189
+
1116
1190
  type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
1117
1191
  /**
1118
1192
  * Simple logger utility for Patch-Adams
@@ -1129,6 +1203,19 @@ declare class Logger {
1129
1203
  setLevel(level: LogLevel): void;
1130
1204
  }
1131
1205
 
1206
+ /**
1207
+ * Rise Course Structure Parser
1208
+ *
1209
+ * Extracts the hierarchical course structure (lessons + blocks) from a
1210
+ * Rise SCORM ZIP package by parsing the embedded base64-encoded course JSON.
1211
+ */
1212
+
1213
+ /**
1214
+ * Extract course structure from a Rise SCORM ZIP buffer.
1215
+ * Returns null if the ZIP is not a Rise package or parsing fails.
1216
+ */
1217
+ declare function extractCourseStructure(zipBuffer: Buffer): CourseStructure | null;
1218
+
1132
1219
  /**
1133
1220
  * Plugin Registry for Patch-Adams
1134
1221
  *
@@ -1198,4 +1285,4 @@ declare class PluginRegistry {
1198
1285
  */
1199
1286
  declare const pluginRegistry: PluginRegistry;
1200
1287
 
1201
- export { type AsyncAssetConfig, AsyncAssetConfigSchema, type AuthoringTool, AuthoringToolDetector, type AuthoringToolInfo, type BlockingAssetConfig, BlockingAssetConfigSchema, type CourseFingerprint, type CourseMetadata, type CssAfterOptions, type CssBeforeOptions, DEFAULT_LRS_OPTIONS, FormatDetector, type GeneratedPluginAssets, HtmlInjector, type JsAfterOptions, type JsBeforeOptions, type LocalFoldersConfig, LocalFoldersConfigSchema, type LogLevel, Logger, type LrsBridgeConfig, LrsBridgeConfigSchema, type LrsBridgeOptions, type ManifestPaths, ManifestUpdater, type PackageFormat, type PatchAdamsConfig, PatchAdamsConfigSchema, type PatchAdamsPlugin, type PatchOptions, type PatchResult, Patcher, type PluginAssets, type PluginConfig, PluginRegistry, type PluginsConfig, StorylineHtmlInjector, buildCssAfterOptions, buildCssBeforeOptions, buildJsAfterOptions, buildJsBeforeOptions, defaultConfig, extractCourseFingerprint, extractCourseMetadata, generateConfigTemplate, generateCssAfterLoader, generateCssBeforeLoader, generateJsAfterLoader, generateJsBeforeLoader, generateLrsBridgeCode, loadConfig, mergeWithDefaults, pluginRegistry };
1288
+ export { type AsyncAssetConfig, AsyncAssetConfigSchema, type AuthoringTool, AuthoringToolDetector, type AuthoringToolInfo, type BlockingAssetConfig, BlockingAssetConfigSchema, type CourseClassMappings, type CourseFingerprint, type CourseMetadata, type CourseStructure, type CourseStructureBlock, type CourseStructureLesson, type CssAfterOptions, type CssBeforeOptions, DEFAULT_LRS_OPTIONS, FormatDetector, type GeneratedPluginAssets, HtmlInjector, type JsAfterOptions, type JsBeforeOptions, type LocalFoldersConfig, LocalFoldersConfigSchema, type LogLevel, Logger, type LrsBridgeConfig, LrsBridgeConfigSchema, type LrsBridgeOptions, type ManifestPaths, ManifestUpdater, type PackageFormat, type PatchAdamsConfig, PatchAdamsConfigSchema, type PatchAdamsPlugin, type PatchOptions, type PatchResult, Patcher, type PluginAssets, type PluginConfig, PluginRegistry, type PluginsConfig, StorylineHtmlInjector, buildCssAfterOptions, buildCssBeforeOptions, buildJsAfterOptions, buildJsBeforeOptions, defaultConfig, extractCourseFingerprint, extractCourseMetadata, extractCourseStructure, generateClassMappingsScript, generateConfigTemplate, generateCssAfterLoader, generateCssBeforeLoader, generateJsAfterLoader, generateJsBeforeLoader, generateLrsBridgeCode, hasClassMappings, loadConfig, mergeWithDefaults, pluginRegistry };
package/dist/index.js CHANGED
@@ -5687,6 +5687,139 @@ function buildSkinJsOptions(config) {
5687
5687
  };
5688
5688
  }
5689
5689
 
5690
+ // src/templates/class-mappings.ts
5691
+ function hasClassMappings(mappings) {
5692
+ if (!mappings) return false;
5693
+ if (mappings.course?.trim()) return true;
5694
+ if (mappings.lessons && Object.values(mappings.lessons).some((v) => v?.trim())) return true;
5695
+ if (mappings.blocks && Object.values(mappings.blocks).some((v) => v?.trim())) return true;
5696
+ return false;
5697
+ }
5698
+ function generateClassMappingsScript(mappings) {
5699
+ if (!hasClassMappings(mappings)) return "";
5700
+ const courseClasses = (mappings.course || "").trim();
5701
+ const blockMap = {};
5702
+ const lessonMap = {};
5703
+ if (mappings.blocks) {
5704
+ for (const [id, classes] of Object.entries(mappings.blocks)) {
5705
+ const trimmed = classes?.trim();
5706
+ if (trimmed) blockMap[id] = trimmed;
5707
+ }
5708
+ }
5709
+ if (mappings.lessons) {
5710
+ for (const [id, classes] of Object.entries(mappings.lessons)) {
5711
+ const trimmed = classes?.trim();
5712
+ if (trimmed) lessonMap[id] = trimmed;
5713
+ }
5714
+ }
5715
+ const hasBlocks = Object.keys(blockMap).length > 0;
5716
+ const hasLessons = Object.keys(lessonMap).length > 0;
5717
+ const parts = [];
5718
+ parts.push(`// PA-Patcher: Custom Class Mappings`);
5719
+ parts.push(`(function() {`);
5720
+ parts.push(` 'use strict';`);
5721
+ parts.push(``);
5722
+ if (courseClasses) {
5723
+ parts.push(` // Course-level classes (applied to <html>)`);
5724
+ parts.push(` var courseClasses = ${JSON.stringify(courseClasses)};`);
5725
+ parts.push(` courseClasses.split(' ').forEach(function(cls) {`);
5726
+ parts.push(` if (cls) document.documentElement.classList.add(cls);`);
5727
+ parts.push(` });`);
5728
+ parts.push(``);
5729
+ }
5730
+ if (hasBlocks) {
5731
+ parts.push(` // Block-level classes (applied to [data-block-id] elements)`);
5732
+ parts.push(` var blockMap = ${JSON.stringify(blockMap)};`);
5733
+ parts.push(``);
5734
+ parts.push(` function applyBlockClasses() {`);
5735
+ parts.push(` var ids = Object.keys(blockMap);`);
5736
+ parts.push(` for (var i = 0; i < ids.length; i++) {`);
5737
+ parts.push(` var el = document.querySelector('[data-block-id="' + ids[i] + '"]');`);
5738
+ parts.push(` if (el) {`);
5739
+ parts.push(` var classes = blockMap[ids[i]].split(' ');`);
5740
+ parts.push(` for (var j = 0; j < classes.length; j++) {`);
5741
+ parts.push(` if (classes[j]) el.classList.add(classes[j]);`);
5742
+ parts.push(` }`);
5743
+ parts.push(` }`);
5744
+ parts.push(` }`);
5745
+ parts.push(` }`);
5746
+ parts.push(``);
5747
+ }
5748
+ if (hasLessons) {
5749
+ parts.push(` // Lesson-level classes (toggled on <html> via hash navigation)`);
5750
+ parts.push(` var lessonMap = ${JSON.stringify(lessonMap)};`);
5751
+ parts.push(` var allLessonClasses = [];`);
5752
+ parts.push(` Object.keys(lessonMap).forEach(function(id) {`);
5753
+ parts.push(` lessonMap[id].split(' ').forEach(function(cls) {`);
5754
+ parts.push(` if (cls && allLessonClasses.indexOf(cls) === -1) allLessonClasses.push(cls);`);
5755
+ parts.push(` });`);
5756
+ parts.push(` });`);
5757
+ parts.push(``);
5758
+ parts.push(` var currentLessonId = null;`);
5759
+ parts.push(``);
5760
+ parts.push(` function detectCurrentLesson() {`);
5761
+ parts.push(` var hash = window.location.hash || '';`);
5762
+ parts.push(` var match = hash.match(/#\\/lessons\\/([^/]+)/);`);
5763
+ parts.push(` return match ? match[1] : null;`);
5764
+ parts.push(` }`);
5765
+ parts.push(``);
5766
+ parts.push(` function applyLessonClasses() {`);
5767
+ parts.push(` var lessonId = detectCurrentLesson();`);
5768
+ parts.push(` if (lessonId === currentLessonId) return;`);
5769
+ parts.push(` currentLessonId = lessonId;`);
5770
+ parts.push(` for (var i = 0; i < allLessonClasses.length; i++) {`);
5771
+ parts.push(` document.documentElement.classList.remove(allLessonClasses[i]);`);
5772
+ parts.push(` }`);
5773
+ parts.push(` if (lessonId && lessonMap[lessonId]) {`);
5774
+ parts.push(` var classes = lessonMap[lessonId].split(' ');`);
5775
+ parts.push(` for (var j = 0; j < classes.length; j++) {`);
5776
+ parts.push(` if (classes[j]) document.documentElement.classList.add(classes[j]);`);
5777
+ parts.push(` }`);
5778
+ parts.push(` }`);
5779
+ parts.push(` }`);
5780
+ parts.push(``);
5781
+ }
5782
+ parts.push(` // Initialization`);
5783
+ parts.push(` function init() {`);
5784
+ if (hasBlocks) {
5785
+ parts.push(` applyBlockClasses();`);
5786
+ }
5787
+ if (hasLessons) {
5788
+ parts.push(` applyLessonClasses();`);
5789
+ parts.push(``);
5790
+ parts.push(` // Watch for Rise SPA navigation`);
5791
+ parts.push(` window.addEventListener('hashchange', function() {`);
5792
+ parts.push(` applyLessonClasses();`);
5793
+ if (hasBlocks) {
5794
+ parts.push(` setTimeout(applyBlockClasses, 200);`);
5795
+ }
5796
+ parts.push(` });`);
5797
+ }
5798
+ if (hasBlocks) {
5799
+ parts.push(``);
5800
+ parts.push(` // MutationObserver for lazily-rendered Rise blocks`);
5801
+ parts.push(` var observer = new MutationObserver(function(mutations) {`);
5802
+ parts.push(` for (var i = 0; i < mutations.length; i++) {`);
5803
+ parts.push(` if (mutations[i].addedNodes.length > 0) {`);
5804
+ parts.push(` applyBlockClasses();`);
5805
+ parts.push(` return;`);
5806
+ parts.push(` }`);
5807
+ parts.push(` }`);
5808
+ parts.push(` });`);
5809
+ parts.push(` var container = document.querySelector('#app') || document.body;`);
5810
+ parts.push(` observer.observe(container, { childList: true, subtree: true });`);
5811
+ }
5812
+ parts.push(` }`);
5813
+ parts.push(``);
5814
+ parts.push(` if (document.readyState === 'loading') {`);
5815
+ parts.push(` document.addEventListener('DOMContentLoaded', init);`);
5816
+ parts.push(` } else {`);
5817
+ parts.push(` init();`);
5818
+ parts.push(` }`);
5819
+ parts.push(`})();`);
5820
+ return parts.join("\n");
5821
+ }
5822
+
5690
5823
  // src/patcher/html-injector.ts
5691
5824
  var HtmlInjector = class {
5692
5825
  config;
@@ -7077,6 +7210,13 @@ var Patcher = class {
7077
7210
  console.log(`[Patcher] Fetched ${fetchedCount} remote files`);
7078
7211
  }
7079
7212
  const pluginAssets = this.generatePluginAssets();
7213
+ if (hasClassMappings(options.classMappings)) {
7214
+ const classMappingsJs = generateClassMappingsScript(options.classMappings);
7215
+ if (classMappingsJs) {
7216
+ pluginAssets.jsAfter = classMappingsJs + (pluginAssets.jsAfter ? "\n" + pluginAssets.jsAfter : "");
7217
+ console.log("[Patcher] Class mappings JS will be injected inline into HTML");
7218
+ }
7219
+ }
7080
7220
  const hasPluginAssets = pluginAssets.cssBefore || pluginAssets.cssAfter || pluginAssets.jsBefore || pluginAssets.jsAfter;
7081
7221
  if (hasPluginAssets) {
7082
7222
  htmlInjector.setPluginAssets(pluginAssets);
@@ -7436,7 +7576,88 @@ var Logger = class {
7436
7576
  this.level = level;
7437
7577
  }
7438
7578
  };
7579
+ var DESERIALIZE_REGEX = /deserialize\s*\(\s*["']([A-Za-z0-9+/=]+)["']\s*\)/;
7580
+ var RISE_HTML_PATHS = [
7581
+ "scormcontent/index.html",
7582
+ "index.html"
7583
+ ];
7584
+ function extractCourseStructure(zipBuffer) {
7585
+ try {
7586
+ const zip = new AdmZip(zipBuffer);
7587
+ let htmlContent = null;
7588
+ for (const path of RISE_HTML_PATHS) {
7589
+ const entry = zip.getEntry(path);
7590
+ if (entry) {
7591
+ htmlContent = entry.getData().toString("utf-8");
7592
+ if (DESERIALIZE_REGEX.test(htmlContent)) break;
7593
+ htmlContent = null;
7594
+ }
7595
+ }
7596
+ if (!htmlContent) {
7597
+ return null;
7598
+ }
7599
+ const match = htmlContent.match(DESERIALIZE_REGEX);
7600
+ if (!match || !match[1]) {
7601
+ return null;
7602
+ }
7603
+ const jsonStr = Buffer.from(match[1], "base64").toString("utf-8");
7604
+ const data = JSON.parse(jsonStr);
7605
+ const course = data.course;
7606
+ if (!course || !Array.isArray(course.lessons)) {
7607
+ return null;
7608
+ }
7609
+ const lessons = course.lessons.filter((lesson) => !lesson.deleted).sort((a, b) => (a.position ?? 0) - (b.position ?? 0)).map((lesson) => {
7610
+ const blocks = (lesson.items || []).map((block) => {
7611
+ const structBlock = {
7612
+ id: block.id,
7613
+ type: block.type || "unknown"
7614
+ };
7615
+ if (block.family) structBlock.family = block.family;
7616
+ if (block.variant) structBlock.variant = block.variant;
7617
+ const title = deriveBlockTitle(block);
7618
+ if (title) structBlock.title = title;
7619
+ return structBlock;
7620
+ });
7621
+ return {
7622
+ id: lesson.id,
7623
+ title: lesson.title || "Untitled Lesson",
7624
+ type: lesson.type || "blocks",
7625
+ blocks
7626
+ };
7627
+ });
7628
+ return {
7629
+ courseTitle: course.title || course.name || "Untitled Course",
7630
+ courseId: course.id || void 0,
7631
+ lessons
7632
+ };
7633
+ } catch {
7634
+ return null;
7635
+ }
7636
+ }
7637
+ function deriveBlockTitle(block) {
7638
+ if (block.title && typeof block.title === "string") {
7639
+ return stripHtml(block.title).substring(0, 80);
7640
+ }
7641
+ const items = block.items;
7642
+ if (!Array.isArray(items) || items.length === 0) return void 0;
7643
+ const firstItem = items[0];
7644
+ if (!firstItem) return void 0;
7645
+ if (firstItem.heading) {
7646
+ return stripHtml(firstItem.heading).substring(0, 80);
7647
+ }
7648
+ if (firstItem.title && typeof firstItem.title === "string") {
7649
+ return stripHtml(firstItem.title).substring(0, 80);
7650
+ }
7651
+ if (firstItem.paragraph) {
7652
+ const text = stripHtml(firstItem.paragraph).substring(0, 60);
7653
+ if (text) return text + (firstItem.paragraph.length > 60 ? "..." : "");
7654
+ }
7655
+ return void 0;
7656
+ }
7657
+ function stripHtml(html) {
7658
+ return html.replace(/<[^>]*>/g, "").trim();
7659
+ }
7439
7660
 
7440
- export { AsyncAssetConfigSchema, AuthoringToolDetector, BlockingAssetConfigSchema, DEFAULT_LRS_OPTIONS, FormatDetector, HtmlInjector, LocalFoldersConfigSchema, Logger, LrsBridgeConfigSchema, ManifestUpdater, PatchAdamsConfigSchema, Patcher, PluginRegistry, StorylineHtmlInjector, buildCssAfterOptions, buildCssBeforeOptions, buildJsAfterOptions, buildJsBeforeOptions, defaultConfig, extractCourseFingerprint, extractCourseMetadata, generateConfigTemplate, generateCssAfterLoader, generateCssBeforeLoader, generateJsAfterLoader, generateJsBeforeLoader, generateLrsBridgeCode, loadConfig, mergeWithDefaults, pluginRegistry };
7661
+ export { AsyncAssetConfigSchema, AuthoringToolDetector, BlockingAssetConfigSchema, DEFAULT_LRS_OPTIONS, FormatDetector, HtmlInjector, LocalFoldersConfigSchema, Logger, LrsBridgeConfigSchema, ManifestUpdater, PatchAdamsConfigSchema, Patcher, PluginRegistry, StorylineHtmlInjector, buildCssAfterOptions, buildCssBeforeOptions, buildJsAfterOptions, buildJsBeforeOptions, defaultConfig, extractCourseFingerprint, extractCourseMetadata, extractCourseStructure, generateClassMappingsScript, generateConfigTemplate, generateCssAfterLoader, generateCssBeforeLoader, generateJsAfterLoader, generateJsBeforeLoader, generateLrsBridgeCode, hasClassMappings, loadConfig, mergeWithDefaults, pluginRegistry };
7441
7662
  //# sourceMappingURL=index.js.map
7442
7663
  //# sourceMappingURL=index.js.map